diff --git a/Atlas/.clang-format b/Atlas/.clang-format new file mode 100644 index 00000000..3f4c0682 --- /dev/null +++ b/Atlas/.clang-format @@ -0,0 +1,126 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Allman +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 140 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +FixNamespaceComments: true +ForEachMacros: + - for +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '.*\.generated\.h' + Priority: 100 + - Regex: '.*(PCH).*' + Priority: -1 + - Regex: '".*"' + Priority: 1 + - Regex: '^<.*\.(h)>' + Priority: 3 + - Regex: '^<.*>' + Priority: 4 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 4 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 4 +UseTab: Never diff --git a/Atlas/.gitignore b/Atlas/.gitignore new file mode 100644 index 00000000..7b73f281 --- /dev/null +++ b/Atlas/.gitignore @@ -0,0 +1,14 @@ +# Directories +.vs/ +bin/ +bin-int/ +x64/ + + +DXSDK11 +.idea +x64/ +Debug/ + +# Exclude +!vendor/bin \ No newline at end of file diff --git a/Atlas/Atlas.cpp b/Atlas/Atlas.cpp new file mode 100644 index 00000000..b390fad2 --- /dev/null +++ b/Atlas/Atlas.cpp @@ -0,0 +1,95 @@ +#include "Camera.h" +#include "Input.h" +#include "NativeWindow.h" +#include "Renderer.h" + +#include +#include +#include +#include + +using namespace std::chrono; + +float getTimeDeltaMs(const time_point& begin, const time_point& end) +{ + return std::chrono::duration_cast>(end - begin).count(); +} + +float getTimeDeltaSecs(const time_point& begin, const time_point& end) +{ + return std::chrono::duration_cast>(end - begin).count(); +} + +int main(int argc, char* argv[]) +{ + std::shared_ptr window = std::make_shared(L"ATLAS", 720, 480); + // Input::InputWindow = window; + DX11Renderer* renderer = new DX11Renderer(); + Camera* camera = new Camera(90.0f, 0.1f, 1000.0f, window->GetAspectRatio()); + HRESULT hr = renderer->InitialiseGeneral(window); + if (FAILED(hr)) + { + printf("Failed to initialise renderer: 0x%llx", hr); + return 1; + } + hr = renderer->Initialise(); + if (FAILED(hr)) + { + printf("Failed to initialise renderer: 0x%llx", hr); + return 1; + } + printf("Renderer initialised\n"); + + time_point tickTimeBefore; + time_point tickTimeAfter = system_clock::now(); + float tickDelta = 0.0f; // in seconds + + // Main message loop + MSG msg = {0}; + std::queue frametimes = std::queue(); + int frameCount = 0; + + float targetFrameTime = static_cast(1) / 144; + + while (WM_QUIT != msg.message) + { + if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + { + // if (tickDelta < targetFrameTime) + // Sleep((targetFrameTime - tickDelta) * 1000); + tickTimeBefore = tickTimeAfter; + camera->Update(tickDelta); + Input::UpdatePrevious(window); + renderer->Render(camera, tickDelta); + tickTimeAfter = system_clock::now(); + tickDelta = getTimeDeltaSecs(tickTimeBefore, tickTimeAfter); + if (frameCount % 10 == 0) + { + frametimes.push(tickDelta); + } + frameCount++; + if (frameCount > 1000) + { + float total = 0.0f; + for (int i = 0; i < 100; i++) + { + total += frametimes.front(); + frametimes.pop(); + } + float averageFrametimeMs = 1000.0f * total / 100.0f; + std::cout << "Average frametime: " << std::to_string(averageFrametimeMs) + << ", Average FPS: " << std::to_string(1000 / averageFrametimeMs) << std::endl; + frameCount = 0; + } + } + } + + renderer = nullptr; + + return 0; +} diff --git a/Atlas/Atlas.vcxproj b/Atlas/Atlas.vcxproj new file mode 100644 index 00000000..521d6455 --- /dev/null +++ b/Atlas/Atlas.vcxproj @@ -0,0 +1,194 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {1132E6A9-9AD9-405D-936A-7643392B0202} + Win32Proj + Atlas + 10.0 + + + + x64 + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + true + v143 + Unicode + DynamicLibrary + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + %(AdditionalIncludeDirectories);$(_ZVcpkgCurrentInstalledDir)include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.20348.0\um + stdcpp20 + /FS + + + Console + true + %(AdditionalDependencies);$(_ZVcpkgCurrentInstalledDir)$(VcpkgConfigSubdir)lib\*.lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\d3d11.lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\d3dcompiler.lib + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + true + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + stdcpp20 + %(AdditionalIncludeDirectories);$(_ZVcpkgCurrentInstalledDir)include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.20348.0\um + + + Console + true + true + true + %(AdditionalDependencies);$(_ZVcpkgCurrentInstalledDir)$(VcpkgConfigSubdir)lib\*.lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\d3d11.lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\d3dcompiler.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Atlas/Atlas.vcxproj.filters b/Atlas/Atlas.vcxproj.filters new file mode 100644 index 00000000..98708dd9 --- /dev/null +++ b/Atlas/Atlas.vcxproj.filters @@ -0,0 +1,81 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/Atlas/Camera.cpp b/Atlas/Camera.cpp new file mode 100644 index 00000000..19833979 --- /dev/null +++ b/Atlas/Camera.cpp @@ -0,0 +1,238 @@ +#include "Camera.h" + +#include "Input.h" + +#include +#include +#include + +Camera::Camera(float fovDegrees, float nearDistance, float farDistance, float aspectRatio) + : FovDegrees(fovDegrees) + , FovRadians(XMConvertToRadians(fovDegrees)) + , NearDistance(nearDistance) + , FarDistance(farDistance) + , AspectRatio(aspectRatio) +{ + Reset(); + Update(0); + OnWindowSizeChanged = [&](int width, int height) { OnWindowSizeChangedImpl(width, height); }; +} + +void Camera::Update(float tickDelta) +{ + // bool Moved = UpdateFromInput(tickDelta); + if (XMMatrixIsIdentity(ViewMatrix) || true) + { + UpdateViewMatrix(); + } +} + +bool Camera::UpdateFromInput(float tickDelta) +{ + // Mouse should be done first to get correct RightDirection vector + bool MouseMoved = UpdateFromMouse(tickDelta); + bool KeyboardMoved = UpdateFromKeyboard(tickDelta); + return KeyboardMoved | MouseMoved; +} + +void Camera::UpdateFromKeyboard(MoveDirection direction, float tickDelta) +{ + // if (Mode == CameraMode::Orbit) + // { + // return; + // } + switch (direction) + { + case MoveDirection::Forward: + Position += ForwardDirection * MovementSpeed * tickDelta; + break; + case MoveDirection::Backward: + Position -= ForwardDirection * MovementSpeed * tickDelta; + break; + case MoveDirection::Left: + Position += RightDirection * MovementSpeed * tickDelta; + break; + case MoveDirection::Right: + Position -= RightDirection * MovementSpeed * tickDelta; + break; + case MoveDirection::Up: + Position -= UpDirection * MovementSpeed * tickDelta; + break; + case MoveDirection::Down: + Position += UpDirection * MovementSpeed * tickDelta; + break; + } +} + +bool Camera::UpdateFromKeyboard(float tickDelta) +{ + bool Moved = false; + if (!Input::IsMouseCaptured()) // || Mode == CameraMode::Orbit) + { + return Moved; + } + if (Input::IsKeyDown('W')) + { + UpdateFromKeyboard(MoveDirection::Forward, tickDelta); + Moved = true; + } + else if (Input::IsKeyDown('S')) + { + UpdateFromKeyboard(MoveDirection::Backward, tickDelta); + Moved = true; + } + if (Input::IsKeyDown('A')) + { + UpdateFromKeyboard(MoveDirection::Left, tickDelta); + Moved = true; + } + else if (Input::IsKeyDown('D')) + { + UpdateFromKeyboard(MoveDirection::Right, tickDelta); + Moved = true; + } + if (Input::IsKeyDown('Q')) + { + UpdateFromKeyboard(MoveDirection::Up, tickDelta); + Moved = true; + } + else if (Input::IsKeyDown('E')) + { + UpdateFromKeyboard(MoveDirection::Down, tickDelta); + Moved = true; + } + return Moved; +} + +bool Camera::UpdateFromMouse(DXSM::Vector2 mouseDelta, float tickDelta, bool isMouseCaptured) +{ + bool Moved = false; + + if (XMMatrixIsIdentity(ViewMatrix) || isMouseCaptured && (mouseDelta.x != 0 || mouseDelta.y != 0)) + { + if (isMouseCaptured) + { + // This seems inefficient + const float thetaDelta = mouseDelta.x * RotationSpeed * 0.005; + const float phiDelta = -mouseDelta.y * RotationSpeed * 0.005; + + Theta += thetaDelta; + Phi += phiDelta; + } + + Moved = true; + } + return Moved; +} + +void Camera::SetMode(CameraMode mode) +{ + Mode = mode; +} + +void Camera::UpdateScroll(int delta) +{ + if (Mode == CameraMode::Orbit) + { + Radius += -delta * 0.001f; + Radius = max(0.01f, Radius); + } +} + +void Camera::Reset() +{ + Position = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); + Theta = -50.f; + Phi = 30.f; + + Radius = 3.f; + OrbitOrigin = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); + + RightDirection = XMVectorSet(-1.0f, 0.0f, 0.0f, 0.0f); + ForwardDirection = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); +} + +void Camera::MoveOrbitOrigin(float mouseX, float mouseY) +{ + if (Mode != CameraMode::Orbit) + { + return; + } + + OrbitOrigin += RightDirection * mouseX * 0.01f; + OrbitOrigin += UpDirection * mouseY * 0.01f; +} + +void Camera::OnWindowSizeChangedImpl(int width, int height) +{ + AspectRatio = (float) width / height; +} + +bool Camera::UpdateFromMouse(float tickDelta) +{ + const DXSM::Vector2 MouseDelta = Input::GetMouseDelta(); + return UpdateFromMouse(MouseDelta, tickDelta, Input::IsMouseCaptured()); +} + +void Camera::UpdateViewMatrix() +{ + const float theta = XMConvertToRadians(Theta); + const float phi = XMConvertToRadians(Phi); + + switch (Mode) + { + case CameraMode::Orbit: + { + // spherical coordinate calculation for position of point on sphere + Position = XMVectorSet(Radius * sinf(phi) * cosf(theta), Radius * sinf(phi) * -sinf(theta), Radius * cosf(phi), 0.f); + ViewMatrix = XMMatrixLookAtRH(Position, OrbitOrigin, UpDirection); + break; + } + case CameraMode::Free: + { + ForwardDirection = XMVectorSet(cosf(phi) * sinf(theta), cosf(phi) * cosf(theta), sinf(phi), 0); + ViewMatrix = XMMatrixLookToRH(Position, ForwardDirection, UpDirection); + + RightDirection = XMVector3Cross(UpDirection, ForwardDirection); + break; + } + } + + XMVector3Normalize(ForwardDirection); + XMVector3Normalize(UpDirection); + XMVector3Normalize(RightDirection); + + // std::cout << "CameraPos: " << Position.m128_f32[0] << ", " << Position.m128_f32[1] << ", " << Position.m128_f32[2] << std::endl; + // std::cout << "CameraDir: " << Theta << ", " << Phi << std::endl; + // std::cout << "Radius: " << Radius << std::endl; +} + +XMMATRIX Camera::GetViewMatrix() const +{ + return ViewMatrix; +} + +XMVECTOR Camera::GetPosition() const +{ + return Position; +} + +XMVECTOR Camera::GetDirection() const +{ + return ForwardDirection; +} + +float Camera::GetFOVDegrees() const +{ + return FovDegrees; +} + +float Camera::GetFOVRadians() const +{ + return FovRadians; +} + +float Camera::GetAspectRatio() const +{ + return AspectRatio; +} diff --git a/Atlas/Camera.h b/Atlas/Camera.h new file mode 100644 index 00000000..12a5af8a --- /dev/null +++ b/Atlas/Camera.h @@ -0,0 +1,80 @@ +#pragma once +#include "SimpleMath.h" + +using namespace DirectX; + +enum class MoveDirection : int +{ + None, + Forward, + Backward, + Left, + Right, + Up, + Down +}; + +enum class CameraMode : int +{ + Orbit, + Free +}; + +class Camera +{ +public: + Camera(float fovDegrees, float nearDistance, float farDistance, float aspectRatio); + void Update(float tickDelta); + XMMATRIX GetViewMatrix() const; + XMVECTOR GetPosition() const; + XMVECTOR GetDirection() const; + float GetFOVDegrees() const; + float GetFOVRadians() const; + float GetAspectRatio() const; + + void UpdateFromKeyboard(MoveDirection direction, float tickDelta); + bool UpdateFromMouse(SimpleMath::Vector2 mouseDelta, float tickDelta, bool isMouseCaptured); + void SetMode(CameraMode mode); + void UpdateScroll(int delta); + void Reset(); + void MoveOrbitOrigin(float mouseX, float mouseY); + + std::function OnWindowSizeChanged; + void OnWindowSizeChangedImpl(int width, int height); + +private: + float FovDegrees; + float FovRadians; + float NearDistance; + float FarDistance; + float AspectRatio; + // todo consider changing this to be polymorphic + CameraMode Mode = CameraMode::Orbit; + + // XMVECTOR Position = XMVectorSet(2.0f, -3.0f, 2.0f, 0.0f); + XMVECTOR Position; + // XMVECTOR Position = XMVectorSet(350.553864f, -878.668212f, 10.7369051f, 1.0f); + // XMVECTOR Position = XMVectorSet(344.f, -867.f, 11.f, 1.0f); + // XMVECTOR RotationEulerDegrees = XMVectorSet(14.0f, -74.0f, 0.0f, 0.0f); + + // Spherical coordinates for orbit camera - theta is XY plane + // In freecam, only theta and phi are used + float Theta; + float Phi; + float Radius; + + const XMVECTOR UpDirection = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); + XMVECTOR RightDirection; + XMVECTOR ForwardDirection; + const float MovementSpeed = 3.0f; + const double RotationSpeed = 10.0f; + XMMATRIX ViewMatrix = XMMatrixIdentity(); + + XMVECTOR OrbitOrigin; + + bool UpdateFromInput(float tickDelta); + bool UpdateFromKeyboard(float tickDelta); + bool UpdateFromMouse(float tickDelta); + + void UpdateViewMatrix(); +}; diff --git a/Atlas/DDSTextureLoader.cpp b/Atlas/DDSTextureLoader.cpp new file mode 100644 index 00000000..08ac7bb3 --- /dev/null +++ b/Atlas/DDSTextureLoader.cpp @@ -0,0 +1,1733 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader.cpp +// +// Functions for loading a DDS texture and creating a Direct3D runtime resource for it +// +// Note these functions are useful as a light-weight runtime loader for DDS files. For +// a full-featured DDS file reader, writer, and texture processing pipeline see +// the 'Texconv' sample and the 'DirectXTex' library. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License (MIT). +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#include "DDSTextureLoader.h" + +#include + +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif +#pragma comment(lib, "dxguid.lib") +using namespace DirectX; + +//-------------------------------------------------------------------------------------- +// Macros +//-------------------------------------------------------------------------------------- +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((uint32_t) (uint8_t) (ch0) | ((uint32_t) (uint8_t) (ch1) << 8) | ((uint32_t) (uint8_t) (ch2) << 16) | \ + ((uint32_t) (uint8_t) (ch3) << 24)) +#endif /* defined(MAKEFOURCC) */ + +//-------------------------------------------------------------------------------------- +// DDS file structure definitions +// +// See DDS.h in the 'Texconv' sample and the 'DirectXTex' library +//-------------------------------------------------------------------------------------- +#pragma pack(push, 1) + +const uint32_t DDS_MAGIC = 0x20534444; // "DDS " + +struct DDS_PIXELFORMAT +{ + uint32_t size; + uint32_t flags; + uint32_t fourCC; + uint32_t RGBBitCount; + uint32_t RBitMask; + uint32_t GBitMask; + uint32_t BBitMask; + uint32_t ABitMask; +}; + +#define DDS_FOURCC 0x00000004 // DDPF_FOURCC +#define DDS_RGB 0x00000040 // DDPF_RGB +#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE +#define DDS_ALPHA 0x00000002 // DDPF_ALPHA +#define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV + +#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH + +#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT + +#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + +#define DDS_CUBEMAP_ALLFACES \ + (DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX | DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY | DDS_CUBEMAP_POSITIVEZ | \ + DDS_CUBEMAP_NEGATIVEZ) + +#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP + +enum DDS_MISC_FLAGS2 +{ + DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L, +}; + +struct DDS_HEADER +{ + uint32_t size; + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitchOrLinearSize; + uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags + uint32_t mipMapCount; + uint32_t reserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t caps; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; +}; + +struct DDS_HEADER_DXT10 +{ + DXGI_FORMAT dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t miscFlags2; +}; + +#pragma pack(pop) + +//-------------------------------------------------------------------------------------- +namespace +{ +struct handle_closer +{ + void operator()(HANDLE h) noexcept + { + if (h) + CloseHandle(h); + } +}; + +using ScopedHandle = std::unique_ptr; + +inline HANDLE safe_handle(HANDLE h) noexcept +{ + return (h == INVALID_HANDLE_VALUE) ? nullptr : h; +} + +template +inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_ const char (&name)[TNameLength]) noexcept +{ +#if defined(_DEBUG) || defined(PROFILE) + resource->SetPrivateData(WKPDID_D3DDebugObjectName, TNameLength - 1, name); +#else + UNREFERENCED_PARAMETER(resource); + UNREFERENCED_PARAMETER(name); +#endif +} + +//-------------------------------------------------------------------------------------- +HRESULT LoadTextureDataFromMemory(_In_reads_(ddsDataSize) const uint8_t* ddsData, size_t ddsDataSize, const DDS_HEADER** header, + const uint8_t** bitData, size_t* bitSize) noexcept +{ + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + if (ddsDataSize > UINT32_MAX) + { + return E_FAIL; + } + + if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + // DDS files always start with the same magic number ("DDS ") + auto dwMagicNumber = *reinterpret_cast(ddsData); + if (dwMagicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + auto hdr = reinterpret_cast(ddsData + sizeof(uint32_t)); + + // Verify header to validate DDS file + if (hdr->size != sizeof(DDS_HEADER) || hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + return E_FAIL; + } + + // Check for DX10 extension + bool bDXT10Header = false; + if ((hdr->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) + { + // Must be long enough for both headers and magic value + if (ddsDataSize < (sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10))) + { + return E_FAIL; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + *header = hdr; + auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER) + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0); + *bitData = ddsData + offset; + *bitSize = ddsDataSize - offset; + + return S_OK; +} + +//-------------------------------------------------------------------------------------- +HRESULT LoadTextureDataFromFile(_In_z_ const wchar_t* fileName, std::unique_ptr& ddsData, const DDS_HEADER** header, + const uint8_t** bitData, size_t* bitSize) noexcept +{ + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + // open the file +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + ScopedHandle hFile(safe_handle(CreateFile2(fileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr))); +#else + ScopedHandle hFile( + safe_handle(CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr))); +#endif + + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // Get the file size + FILE_STANDARD_INFO fileInfo; + if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // File is too big for 32-bit allocation, so reject read + if (fileInfo.EndOfFile.HighPart > 0) + { + return E_FAIL; + } + + // Need at least enough data to fill the header and magic number to be a valid DDS + if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + // create enough space for the file data + ddsData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); + if (!ddsData) + { + return E_OUTOFMEMORY; + } + + // read the data in + DWORD BytesRead = 0; + if (!ReadFile(hFile.get(), ddsData.get(), fileInfo.EndOfFile.LowPart, &BytesRead, nullptr)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (BytesRead < fileInfo.EndOfFile.LowPart) + { + return E_FAIL; + } + + // DDS files always start with the same magic number ("DDS ") + auto dwMagicNumber = *reinterpret_cast(ddsData.get()); + if (dwMagicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + auto hdr = reinterpret_cast(ddsData.get() + sizeof(uint32_t)); + + // Verify header to validate DDS file + if (hdr->size != sizeof(DDS_HEADER) || hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + return E_FAIL; + } + + // Check for DX10 extension + bool bDXT10Header = false; + if ((hdr->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) + { + // Must be long enough for both headers and magic value + if (fileInfo.EndOfFile.LowPart < (sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10))) + { + return E_FAIL; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + *header = hdr; + auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER) + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0); + *bitData = ddsData.get() + offset; + *bitSize = fileInfo.EndOfFile.LowPart - offset; + + return S_OK; +} + +//-------------------------------------------------------------------------------------- +// Return the BPP for a particular format +//-------------------------------------------------------------------------------------- +size_t BitsPerPixel(_In_ DXGI_FORMAT fmt) noexcept +{ + switch (fmt) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 128; + + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 96; + + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + case DXGI_FORMAT_Y416: + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + return 64; + + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + case DXGI_FORMAT_AYUV: + case DXGI_FORMAT_Y410: + case DXGI_FORMAT_YUY2: + return 32; + + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + return 24; + + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + case DXGI_FORMAT_A8P8: + case DXGI_FORMAT_B4G4R4A4_UNORM: + return 16; + + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_420_OPAQUE: + case DXGI_FORMAT_NV11: + return 12; + + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: + return 8; + + case DXGI_FORMAT_R1_UNORM: + return 1; + + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + return 4; + + default: + return 0; + } +} + +//-------------------------------------------------------------------------------------- +// Get surface information for a particular format +//-------------------------------------------------------------------------------------- +HRESULT GetSurfaceInfo(_In_ size_t width, _In_ size_t height, _In_ DXGI_FORMAT fmt, size_t* outNumBytes, _Out_opt_ size_t* outRowBytes, + _Out_opt_ size_t* outNumRows) noexcept +{ + uint64_t numBytes = 0; + uint64_t rowBytes = 0; + uint64_t numRows = 0; + + bool bc = false; + bool packed = false; + bool planar = false; + size_t bpe = 0; + switch (fmt) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + bc = true; + bpe = 8; + break; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + bc = true; + bpe = 16; + break; + + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_YUY2: + packed = true; + bpe = 4; + break; + + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + packed = true; + bpe = 8; + break; + + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_420_OPAQUE: + planar = true; + bpe = 2; + break; + + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + planar = true; + bpe = 4; + break; + + default: + break; + } + + if (bc) + { + uint64_t numBlocksWide = 0; + if (width > 0) + { + numBlocksWide = std::max(1u, (uint64_t(width) + 3u) / 4u); + } + uint64_t numBlocksHigh = 0; + if (height > 0) + { + numBlocksHigh = std::max(1u, (uint64_t(height) + 3u) / 4u); + } + rowBytes = numBlocksWide * bpe; + numRows = numBlocksHigh; + numBytes = rowBytes * numBlocksHigh; + } + else if (packed) + { + rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; + numRows = uint64_t(height); + numBytes = rowBytes * height; + } + else if (fmt == DXGI_FORMAT_NV11) + { + rowBytes = ((uint64_t(width) + 3u) >> 2) * 4u; + numRows = uint64_t(height) * 2u; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data + numBytes = rowBytes * numRows; + } + else if (planar) + { + rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; + numBytes = (rowBytes * uint64_t(height)) + ((rowBytes * uint64_t(height) + 1u) >> 1); + numRows = height + ((uint64_t(height) + 1u) >> 1); + } + else + { + size_t bpp = BitsPerPixel(fmt); + if (!bpp) + return E_INVALIDARG; + + rowBytes = (uint64_t(width) * bpp + 7u) / 8u; // round up to nearest byte + numRows = uint64_t(height); + numBytes = rowBytes * height; + } + +#if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64) + static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!"); + if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); +#else + static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!"); +#endif + + if (outNumBytes) + { + *outNumBytes = static_cast(numBytes); + } + if (outRowBytes) + { + *outRowBytes = static_cast(rowBytes); + } + if (outNumRows) + { + *outNumRows = static_cast(numRows); + } + + return S_OK; +} + +//-------------------------------------------------------------------------------------- +#define ISBITMASK(r, g, b, a) (ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a) + +DXGI_FORMAT GetDXGIFormat(const DDS_PIXELFORMAT& ddpf) noexcept +{ + if (ddpf.flags & DDS_RGB) + { + // Note that sRGB formats are written using the "DX10" extended header + + switch (ddpf.RGBBitCount) + { + case 32: + if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_UNORM; + } + + if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000)) + { + return DXGI_FORMAT_B8G8R8A8_UNORM; + } + + if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0)) + { + return DXGI_FORMAT_B8G8R8X8_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0) aka D3DFMT_X8B8G8R8 + + // Note that many common DDS reader/writers (including D3DX) swap the + // the RED/BLUE masks for 10:10:10:2 formats. We assume + // below that the 'backwards' header mask is being used since it is most + // likely written by D3DX. The more robust solution is to use the 'DX10' + // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly + + // For 'correct' writers, this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data + if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000)) + { + return DXGI_FORMAT_R10G10B10A2_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10 + + if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) + { + return DXGI_FORMAT_R16G16_UNORM; + } + + if (ISBITMASK(0xffffffff, 0, 0, 0)) + { + // Only 32-bit color channel format in D3D9 was R32F + return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 + } + break; + + case 24: + // No 24bpp DXGI formats aka D3DFMT_R8G8B8 + break; + + case 16: + if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x8000)) + { + return DXGI_FORMAT_B5G5R5A1_UNORM; + } + if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0)) + { + return DXGI_FORMAT_B5G6R5_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0) aka D3DFMT_X1R5G5B5 + + if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000)) + { + return DXGI_FORMAT_B4G4R4A4_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0) aka D3DFMT_X4R4G4B4 + + // No 3:3:2, 3:3:2:8, or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8, etc. + break; + } + } + else if (ddpf.flags & DDS_LUMINANCE) + { + if (8 == ddpf.RGBBitCount) + { + if (ISBITMASK(0xff, 0, 0, 0)) + { + return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + + // No DXGI format maps to ISBITMASK(0x0f,0x00,0x00,0xf0) aka D3DFMT_A4L4 + + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16 + } + } + + if (16 == ddpf.RGBBitCount) + { + if (ISBITMASK(0xffff, 0, 0, 0)) + { + return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + } + } + else if (ddpf.flags & DDS_ALPHA) + { + if (8 == ddpf.RGBBitCount) + { + return DXGI_FORMAT_A8_UNORM; + } + } + else if (ddpf.flags & DDS_BUMPDUDV) + { + if (16 == ddpf.RGBBitCount) + { + if (ISBITMASK(0x00ff, 0xff00, 0, 0)) + { + return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + } + + if (32 == ddpf.RGBBitCount) + { + if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) + { + return DXGI_FORMAT_R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension + } + + // No DXGI format maps to ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10 + } + + // No DXGI format maps to DDPF_BUMPLUMINANCE aka D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 + } + else if (ddpf.flags & DDS_FOURCC) + { + if (MAKEFOURCC('D', 'X', 'T', '1') == ddpf.fourCC) + { + return DXGI_FORMAT_BC1_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '3') == ddpf.fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '5') == ddpf.fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + // While pre-multiplied alpha isn't directly supported by the DXGI formats, + // they are basically the same as these BC formats so they can be mapped + if (MAKEFOURCC('D', 'X', 'T', '2') == ddpf.fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '4') == ddpf.fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + if (MAKEFOURCC('A', 'T', 'I', '1') == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC('B', 'C', '4', 'U') == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC('B', 'C', '4', 'S') == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_SNORM; + } + + if (MAKEFOURCC('A', 'T', 'I', '2') == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC('B', 'C', '5', 'U') == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC('B', 'C', '5', 'S') == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_SNORM; + } + + // BC6H and BC7 are written using the "DX10" extended header + + if (MAKEFOURCC('R', 'G', 'B', 'G') == ddpf.fourCC) + { + return DXGI_FORMAT_R8G8_B8G8_UNORM; + } + if (MAKEFOURCC('G', 'R', 'G', 'B') == ddpf.fourCC) + { + return DXGI_FORMAT_G8R8_G8B8_UNORM; + } + + if (MAKEFOURCC('Y', 'U', 'Y', '2') == ddpf.fourCC) + { + return DXGI_FORMAT_YUY2; + } + + // Check for D3DFORMAT enums being set here + switch (ddpf.fourCC) + { + case 36: // D3DFMT_A16B16G16R16 + return DXGI_FORMAT_R16G16B16A16_UNORM; + + case 110: // D3DFMT_Q16W16V16U16 + return DXGI_FORMAT_R16G16B16A16_SNORM; + + case 111: // D3DFMT_R16F + return DXGI_FORMAT_R16_FLOAT; + + case 112: // D3DFMT_G16R16F + return DXGI_FORMAT_R16G16_FLOAT; + + case 113: // D3DFMT_A16B16G16R16F + return DXGI_FORMAT_R16G16B16A16_FLOAT; + + case 114: // D3DFMT_R32F + return DXGI_FORMAT_R32_FLOAT; + + case 115: // D3DFMT_G32R32F + return DXGI_FORMAT_R32G32_FLOAT; + + case 116: // D3DFMT_A32B32G32R32F + return DXGI_FORMAT_R32G32B32A32_FLOAT; + + // No DXGI format maps to D3DFMT_CxV8U8 + } + } + + return DXGI_FORMAT_UNKNOWN; +} + +#undef ISBITMASK + +//-------------------------------------------------------------------------------------- +DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format) noexcept +{ + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + + case DXGI_FORMAT_BC1_UNORM: + return DXGI_FORMAT_BC1_UNORM_SRGB; + + case DXGI_FORMAT_BC2_UNORM: + return DXGI_FORMAT_BC2_UNORM_SRGB; + + case DXGI_FORMAT_BC3_UNORM: + return DXGI_FORMAT_BC3_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; + + case DXGI_FORMAT_BC7_UNORM: + return DXGI_FORMAT_BC7_UNORM_SRGB; + + default: + return format; + } +} + +//-------------------------------------------------------------------------------------- +HRESULT FillInitData(_In_ size_t width, _In_ size_t height, _In_ size_t depth, _In_ size_t mipCount, _In_ size_t arraySize, + _In_ DXGI_FORMAT format, _In_ size_t maxsize, _In_ size_t bitSize, _In_reads_bytes_(bitSize) const uint8_t* bitData, + _Out_ size_t& twidth, _Out_ size_t& theight, _Out_ size_t& tdepth, _Out_ size_t& skipMip, + _Out_writes_(mipCount* arraySize) D3D11_SUBRESOURCE_DATA* initData) noexcept +{ + if (!bitData || !initData) + { + return E_POINTER; + } + + skipMip = 0; + twidth = 0; + theight = 0; + tdepth = 0; + + size_t NumBytes = 0; + size_t RowBytes = 0; + const uint8_t* pSrcBits = bitData; + const uint8_t* pEndBits = bitData + bitSize; + + size_t index = 0; + for (size_t j = 0; j < arraySize; j++) + { + size_t w = width; + size_t h = height; + size_t d = depth; + for (size_t i = 0; i < mipCount; i++) + { + HRESULT hr = GetSurfaceInfo(w, h, format, &NumBytes, &RowBytes, nullptr); + if (FAILED(hr)) + return hr; + + if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + if ((mipCount <= 1) || !maxsize || (w <= maxsize && h <= maxsize && d <= maxsize)) + { + if (!twidth) + { + twidth = w; + theight = h; + tdepth = d; + } + + assert(index < mipCount * arraySize); + _Analysis_assume_(index < mipCount * arraySize); + initData[index].pSysMem = pSrcBits; + initData[index].SysMemPitch = static_cast(RowBytes); + initData[index].SysMemSlicePitch = static_cast(NumBytes); + ++index; + } + else if (!j) + { + // Count number of skipped mipmaps (first item only) + ++skipMip; + } + + if (pSrcBits + (NumBytes * d) > pEndBits) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + pSrcBits += NumBytes * d; + + w = w >> 1; + h = h >> 1; + d = d >> 1; + if (w == 0) + { + w = 1; + } + if (h == 0) + { + h = 1; + } + if (d == 0) + { + d = 1; + } + } + } + + return (index > 0) ? S_OK : E_FAIL; +} + +//-------------------------------------------------------------------------------------- +HRESULT CreateD3DResources(_In_ ID3D11Device* d3dDevice, _In_ uint32_t resDim, _In_ size_t width, _In_ size_t height, _In_ size_t depth, + _In_ size_t mipCount, _In_ size_t arraySize, _In_ DXGI_FORMAT format, _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, + _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ bool forceSRGB, _In_ bool isCubeMap, + _In_reads_opt_(mipCount* arraySize) D3D11_SUBRESOURCE_DATA* initData, _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView) noexcept +{ + if (!d3dDevice) + return E_POINTER; + + HRESULT hr = E_FAIL; + + if (forceSRGB) + { + format = MakeSRGB(format); + } + + switch (resDim) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + { + D3D11_TEXTURE1D_DESC desc; + desc.Width = static_cast(width); + desc.MipLevels = static_cast(mipCount); + desc.ArraySize = static_cast(arraySize); + desc.Format = format; + desc.Usage = usage; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = cpuAccessFlags; + desc.MiscFlags = miscFlags & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); + + ID3D11Texture1D* tex = nullptr; + hr = d3dDevice->CreateTexture1D(&desc, initData, &tex); + if (SUCCEEDED(hr) && tex) + { + if (textureView) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; + SRVDesc.Format = format; + + if (arraySize > 1) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; + SRVDesc.Texture1DArray.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + SRVDesc.Texture1DArray.ArraySize = static_cast(arraySize); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; + SRVDesc.Texture1D.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + } + + hr = d3dDevice->CreateShaderResourceView(tex, &SRVDesc, textureView); + if (FAILED(hr)) + { + tex->Release(); + return hr; + } + } + + if (texture) + { + *texture = tex; + } + else + { + SetDebugObjectName(tex, "DDSTextureLoader"); + tex->Release(); + } + } + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: + { + D3D11_TEXTURE2D_DESC desc; + desc.Width = static_cast(width); + desc.Height = static_cast(height); + desc.MipLevels = static_cast(mipCount); + desc.ArraySize = static_cast(arraySize); + desc.Format = format; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = usage; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = cpuAccessFlags; + if (isCubeMap) + { + desc.MiscFlags = miscFlags | D3D11_RESOURCE_MISC_TEXTURECUBE; + } + else + { + desc.MiscFlags = miscFlags & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); + } + + ID3D11Texture2D* tex = nullptr; + hr = d3dDevice->CreateTexture2D(&desc, initData, &tex); + if (SUCCEEDED(hr) && tex) + { + if (textureView) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; + SRVDesc.Format = format; + + if (isCubeMap) + { + if (arraySize > 6) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; + SRVDesc.TextureCubeArray.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + + // Earlier we set arraySize to (NumCubes * 6) + SRVDesc.TextureCubeArray.NumCubes = static_cast(arraySize / 6); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + SRVDesc.TextureCube.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + } + } + else if (arraySize > 1) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + SRVDesc.Texture2DArray.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + SRVDesc.Texture2DArray.ArraySize = static_cast(arraySize); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + SRVDesc.Texture2D.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + } + + hr = d3dDevice->CreateShaderResourceView(tex, &SRVDesc, textureView); + if (FAILED(hr)) + { + tex->Release(); + return hr; + } + } + + if (texture) + { + *texture = tex; + } + else + { + SetDebugObjectName(tex, "DDSTextureLoader"); + tex->Release(); + } + } + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE3D: + { + D3D11_TEXTURE3D_DESC desc; + desc.Width = static_cast(width); + desc.Height = static_cast(height); + desc.Depth = static_cast(depth); + desc.MipLevels = static_cast(mipCount); + desc.Format = format; + desc.Usage = usage; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = cpuAccessFlags; + desc.MiscFlags = miscFlags & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); + + ID3D11Texture3D* tex = nullptr; + hr = d3dDevice->CreateTexture3D(&desc, initData, &tex); + if (SUCCEEDED(hr) && tex) + { + if (textureView) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; + SRVDesc.Format = format; + + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + SRVDesc.Texture3D.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + + hr = d3dDevice->CreateShaderResourceView(tex, &SRVDesc, textureView); + if (FAILED(hr)) + { + tex->Release(); + return hr; + } + } + + if (texture) + { + *texture = tex; + } + else + { + SetDebugObjectName(tex, "DDSTextureLoader"); + tex->Release(); + } + } + } + break; + } + + return hr; +} + +//-------------------------------------------------------------------------------------- +HRESULT CreateTextureFromDDS(_In_ ID3D11Device* d3dDevice, _In_opt_ ID3D11DeviceContext* d3dContext, _In_ const DDS_HEADER* header, + _In_reads_bytes_(bitSize) const uint8_t* bitData, _In_ size_t bitSize, _In_ size_t maxsize, _In_ D3D11_USAGE usage, + _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ bool forceSRGB, + _Outptr_opt_ ID3D11Resource** texture, _Outptr_opt_ ID3D11ShaderResourceView** textureView) noexcept +{ + HRESULT hr = S_OK; + + UINT width = header->width; + UINT height = header->height; + UINT depth = header->depth; + + uint32_t resDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; + UINT arraySize = 1; + DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; + bool isCubeMap = false; + + size_t mipCount = header->mipMapCount; + if (0 == mipCount) + { + mipCount = 1; + } + + if ((header->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) + { + auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); + + arraySize = d3d10ext->arraySize; + if (arraySize == 0) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + switch (d3d10ext->dxgiFormat) + { + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: + case DXGI_FORMAT_A8P8: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + default: + if (BitsPerPixel(d3d10ext->dxgiFormat) == 0) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + } + + format = d3d10ext->dxgiFormat; + + switch (d3d10ext->resourceDimension) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + // D3DX writes 1D textures with a fixed Height of 1 + if ((header->flags & DDS_HEIGHT) && height != 1) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + height = depth = 1; + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: + if (d3d10ext->miscFlag & D3D11_RESOURCE_MISC_TEXTURECUBE) + { + arraySize *= 6; + isCubeMap = true; + } + depth = 1; + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE3D: + if (!(header->flags & DDS_HEADER_FLAGS_VOLUME)) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + if (arraySize > 1) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + default: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + resDim = d3d10ext->resourceDimension; + } + else + { + format = GetDXGIFormat(header->ddspf); + + if (format == DXGI_FORMAT_UNKNOWN) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (header->flags & DDS_HEADER_FLAGS_VOLUME) + { + resDim = D3D11_RESOURCE_DIMENSION_TEXTURE3D; + } + else + { + if (header->caps2 & DDS_CUBEMAP) + { + // We require all six faces to be defined + if ((header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + arraySize = 6; + isCubeMap = true; + } + + depth = 1; + resDim = D3D11_RESOURCE_DIMENSION_TEXTURE2D; + + // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture + } + + assert(BitsPerPixel(format) != 0); + } + + // Bound sizes (for security purposes we don't trust DDS file metadata larger than the D3D 11.x hardware requirements) + if (mipCount > D3D11_REQ_MIP_LEVELS) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + switch (resDim) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + if ((arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || (width > D3D11_REQ_TEXTURE1D_U_DIMENSION)) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: + if (isCubeMap) + { + // This is the right bound because we set arraySize to (NumCubes*6) above + if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || (width > D3D11_REQ_TEXTURECUBE_DIMENSION) || + (height > D3D11_REQ_TEXTURECUBE_DIMENSION)) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + } + else if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || (width > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION) || + (height > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION)) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE3D: + if ((arraySize > 1) || (width > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || (height > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (depth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + default: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + bool autogen = false; + if (mipCount == 1 && d3dContext && textureView) // Must have context and shader-view to auto generate mipmaps + { + // See if format is supported for auto-gen mipmaps (varies by feature level) + UINT fmtSupport = 0; + hr = d3dDevice->CheckFormatSupport(format, &fmtSupport); + if (SUCCEEDED(hr) && (fmtSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN)) + { + // 10level9 feature levels do not support auto-gen mipgen for volume textures + if ((resDim != D3D11_RESOURCE_DIMENSION_TEXTURE3D) || (d3dDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0)) + { + autogen = true; + } + } + } + + if (autogen) + { + // Create texture with auto-generated mipmaps + ID3D11Resource* tex = nullptr; + hr = CreateD3DResources(d3dDevice, resDim, width, height, depth, 0, arraySize, format, usage, bindFlags | D3D11_BIND_RENDER_TARGET, + cpuAccessFlags, miscFlags | D3D11_RESOURCE_MISC_GENERATE_MIPS, forceSRGB, isCubeMap, nullptr, &tex, textureView); + if (SUCCEEDED(hr)) + { + size_t numBytes = 0; + size_t rowBytes = 0; + hr = GetSurfaceInfo(width, height, format, &numBytes, &rowBytes, nullptr); + if (FAILED(hr)) + return hr; + + if (numBytes > bitSize) + { + (*textureView)->Release(); + *textureView = nullptr; + tex->Release(); + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + D3D11_SHADER_RESOURCE_VIEW_DESC desc; + (*textureView)->GetDesc(&desc); + + UINT mipLevels = 1; + + switch (desc.ViewDimension) + { + case D3D_SRV_DIMENSION_TEXTURE1D: + mipLevels = desc.Texture1D.MipLevels; + break; + case D3D_SRV_DIMENSION_TEXTURE1DARRAY: + mipLevels = desc.Texture1DArray.MipLevels; + break; + case D3D_SRV_DIMENSION_TEXTURE2D: + mipLevels = desc.Texture2D.MipLevels; + break; + case D3D_SRV_DIMENSION_TEXTURE2DARRAY: + mipLevels = desc.Texture2DArray.MipLevels; + break; + case D3D_SRV_DIMENSION_TEXTURECUBE: + mipLevels = desc.TextureCube.MipLevels; + break; + case D3D_SRV_DIMENSION_TEXTURECUBEARRAY: + mipLevels = desc.TextureCubeArray.MipLevels; + break; + case D3D_SRV_DIMENSION_TEXTURE3D: + mipLevels = desc.Texture3D.MipLevels; + break; + default: + (*textureView)->Release(); + *textureView = nullptr; + tex->Release(); + return E_UNEXPECTED; + } + + if (arraySize > 1) + { + const uint8_t* pSrcBits = bitData; + const uint8_t* pEndBits = bitData + bitSize; + for (UINT item = 0; item < arraySize; ++item) + { + if ((pSrcBits + numBytes) > pEndBits) + { + (*textureView)->Release(); + *textureView = nullptr; + tex->Release(); + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + UINT res = D3D11CalcSubresource(0, item, mipLevels); + d3dContext->UpdateSubresource(tex, res, nullptr, pSrcBits, static_cast(rowBytes), static_cast(numBytes)); + pSrcBits += numBytes; + } + } + else + { + d3dContext->UpdateSubresource(tex, 0, nullptr, bitData, static_cast(rowBytes), static_cast(numBytes)); + } + + d3dContext->GenerateMips(*textureView); + + if (texture) + { + *texture = tex; + } + else + { + tex->Release(); + } + } + } + else + { + // Create the texture + std::unique_ptr initData(new (std::nothrow) D3D11_SUBRESOURCE_DATA[mipCount * arraySize]); + if (!initData) + { + return E_OUTOFMEMORY; + } + + size_t skipMip = 0; + size_t twidth = 0; + size_t theight = 0; + size_t tdepth = 0; + hr = FillInitData( + width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData, twidth, theight, tdepth, skipMip, initData.get()); + + if (SUCCEEDED(hr)) + { + hr = CreateD3DResources(d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, format, usage, bindFlags, + cpuAccessFlags, miscFlags, forceSRGB, isCubeMap, initData.get(), texture, textureView); + + if (FAILED(hr) && !maxsize && (mipCount > 1)) + { + // Retry with a maxsize determined by feature level + switch (d3dDevice->GetFeatureLevel()) + { + case D3D_FEATURE_LEVEL_9_1: + case D3D_FEATURE_LEVEL_9_2: + if (isCubeMap) + { + maxsize = 512u /*D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION*/; + } + else + { + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) ? 256u /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ + : 2048u /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + } + break; + + case D3D_FEATURE_LEVEL_9_3: + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) ? 256u /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ + : 4096u /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + break; + + default: // D3D_FEATURE_LEVEL_10_0 & D3D_FEATURE_LEVEL_10_1 + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) ? 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ + : 8192u /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + break; + } + + hr = FillInitData(width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData, twidth, theight, tdepth, + skipMip, initData.get()); + if (SUCCEEDED(hr)) + { + hr = CreateD3DResources(d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, format, usage, + bindFlags, cpuAccessFlags, miscFlags, forceSRGB, isCubeMap, initData.get(), texture, textureView); + } + } + } + } + + return hr; +} + +//-------------------------------------------------------------------------------------- +DDS_ALPHA_MODE GetAlphaMode(_In_ const DDS_HEADER* header) noexcept +{ + if (header->ddspf.flags & DDS_FOURCC) + { + if (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC) + { + auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); + auto mode = static_cast(d3d10ext->miscFlags2 & DDS_MISC_FLAGS2_ALPHA_MODE_MASK); + switch (mode) + { + case DDS_ALPHA_MODE_STRAIGHT: + case DDS_ALPHA_MODE_PREMULTIPLIED: + case DDS_ALPHA_MODE_OPAQUE: + case DDS_ALPHA_MODE_CUSTOM: + return mode; + + case DDS_ALPHA_MODE_UNKNOWN: + default: + break; + } + } + else if ((MAKEFOURCC('D', 'X', 'T', '2') == header->ddspf.fourCC) || (MAKEFOURCC('D', 'X', 'T', '4') == header->ddspf.fourCC)) + { + return DDS_ALPHA_MODE_PREMULTIPLIED; + } + } + + return DDS_ALPHA_MODE_UNKNOWN; +} + +//-------------------------------------------------------------------------------------- +void SetDebugTextureInfo( + _In_z_ const wchar_t* fileName, _In_opt_ ID3D11Resource** texture, _In_opt_ ID3D11ShaderResourceView** textureView) noexcept +{ +#if !defined(NO_D3D11_DEBUG_NAME) && (defined(_DEBUG) || defined(PROFILE)) + if (texture || textureView) + { + CHAR strFileA[MAX_PATH]; + int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS, fileName, -1, strFileA, MAX_PATH, nullptr, nullptr); + if (result > 0) + { + const char* pstrName = strrchr(strFileA, '\\'); + if (!pstrName) + { + pstrName = strFileA; + } + else + { + pstrName++; + } + + if (texture && *texture) + { + (*texture)->SetPrivateData(WKPDID_D3DDebugObjectName, static_cast(strnlen_s(pstrName, MAX_PATH)), pstrName); + } + + if (textureView && *textureView) + { + (*textureView)->SetPrivateData(WKPDID_D3DDebugObjectName, static_cast(strnlen_s(pstrName, MAX_PATH)), pstrName); + } + } + } +#else + UNREFERENCED_PARAMETER(fileName); + UNREFERENCED_PARAMETER(texture); + UNREFERENCED_PARAMETER(textureView); +#endif +} +} // anonymous namespace + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemory(ID3D11Device* d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, + ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, size_t maxsize, DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromMemoryEx(d3dDevice, nullptr, ddsData, ddsDataSize, maxsize, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, + 0, 0, false, texture, textureView, alphaMode); +} + +_Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemory(ID3D11Device* d3dDevice, ID3D11DeviceContext* d3dContext, + const uint8_t* ddsData, size_t ddsDataSize, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, size_t maxsize, + DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromMemoryEx(d3dDevice, d3dContext, ddsData, ddsDataSize, maxsize, D3D11_USAGE_DEFAULT, + D3D11_BIND_SHADER_RESOURCE, 0, 0, false, texture, textureView, alphaMode); +} + +_Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemoryEx(ID3D11Device* d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, + size_t maxsize, D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, unsigned int miscFlags, bool forceSRGB, + ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromMemoryEx(d3dDevice, nullptr, ddsData, ddsDataSize, maxsize, usage, bindFlags, cpuAccessFlags, miscFlags, + forceSRGB, texture, textureView, alphaMode); +} + +_Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemoryEx(ID3D11Device* d3dDevice, ID3D11DeviceContext* d3dContext, + const uint8_t* ddsData, size_t ddsDataSize, size_t maxsize, D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, + unsigned int miscFlags, bool forceSRGB, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, + DDS_ALPHA_MODE* alphaMode) noexcept +{ + if (texture) + { + *texture = nullptr; + } + if (textureView) + { + *textureView = nullptr; + } + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + + if (!d3dDevice || !ddsData || (!texture && !textureView)) + { + return E_INVALIDARG; + } + + if (textureView && !(bindFlags & D3D11_BIND_SHADER_RESOURCE)) + { + return E_INVALIDARG; + } + + // Validate DDS file in memory + const DDS_HEADER* header = nullptr; + const uint8_t* bitData = nullptr; + size_t bitSize = 0; + + HRESULT hr = LoadTextureDataFromMemory(ddsData, ddsDataSize, &header, &bitData, &bitSize); + if (FAILED(hr)) + { + return hr; + } + + hr = CreateTextureFromDDS(d3dDevice, d3dContext, header, bitData, bitSize, maxsize, usage, bindFlags, cpuAccessFlags, miscFlags, + forceSRGB, texture, textureView); + if (SUCCEEDED(hr)) + { + if (texture && *texture) + { + SetDebugObjectName(*texture, "DDSTextureLoader"); + } + + if (textureView && *textureView) + { + SetDebugObjectName(*textureView, "DDSTextureLoader"); + } + + if (alphaMode) + *alphaMode = GetAlphaMode(header); + } + + return hr; +} + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFile(ID3D11Device* d3dDevice, const wchar_t* fileName, ID3D11Resource** texture, + ID3D11ShaderResourceView** textureView, size_t maxsize, DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromFileEx(d3dDevice, nullptr, fileName, maxsize, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, + texture, textureView, alphaMode); +} + +_Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFile(ID3D11Device* d3dDevice, ID3D11DeviceContext* d3dContext, + const wchar_t* fileName, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, size_t maxsize, + DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromFileEx(d3dDevice, d3dContext, fileName, maxsize, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, + false, texture, textureView, alphaMode); +} + +_Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFileEx(ID3D11Device* d3dDevice, const wchar_t* fileName, size_t maxsize, + D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, unsigned int miscFlags, bool forceSRGB, + ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromFileEx( + d3dDevice, nullptr, fileName, maxsize, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, texture, textureView, alphaMode); +} + +_Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFileEx(ID3D11Device* d3dDevice, ID3D11DeviceContext* d3dContext, + const wchar_t* fileName, size_t maxsize, D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, unsigned int miscFlags, + bool forceSRGB, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, DDS_ALPHA_MODE* alphaMode) noexcept +{ + if (texture) + { + *texture = nullptr; + } + if (textureView) + { + *textureView = nullptr; + } + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + + if (!d3dDevice || !fileName || (!texture && !textureView)) + { + return E_INVALIDARG; + } + + if (textureView && !(bindFlags & D3D11_BIND_SHADER_RESOURCE)) + { + return E_INVALIDARG; + } + + const DDS_HEADER* header = nullptr; + const uint8_t* bitData = nullptr; + size_t bitSize = 0; + + std::unique_ptr ddsData; + HRESULT hr = LoadTextureDataFromFile(fileName, ddsData, &header, &bitData, &bitSize); + if (FAILED(hr)) + { + return hr; + } + + hr = CreateTextureFromDDS(d3dDevice, d3dContext, header, bitData, bitSize, maxsize, usage, bindFlags, cpuAccessFlags, miscFlags, + forceSRGB, texture, textureView); + + if (SUCCEEDED(hr)) + { + SetDebugTextureInfo(fileName, texture, textureView); + + if (alphaMode) + *alphaMode = GetAlphaMode(header); + } + + return hr; +} diff --git a/Atlas/DDSTextureLoader.h b/Atlas/DDSTextureLoader.h new file mode 100644 index 00000000..c1d784ec --- /dev/null +++ b/Atlas/DDSTextureLoader.h @@ -0,0 +1,77 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader.h +// +// Functions for loading a DDS texture and creating a Direct3D runtime resource for it +// +// Note these functions are useful as a light-weight runtime loader for DDS files. For +// a full-featured DDS file reader, writer, and texture processing pipeline see +// the 'Texconv' sample and the 'DirectXTex' library. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License (MIT). +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include + +#include + +namespace DirectX +{ +#ifndef DDS_ALPHA_MODE_DEFINED +#define DDS_ALPHA_MODE_DEFINED +enum DDS_ALPHA_MODE : uint32_t +{ + DDS_ALPHA_MODE_UNKNOWN = 0, + DDS_ALPHA_MODE_STRAIGHT = 1, + DDS_ALPHA_MODE_PREMULTIPLIED = 2, + DDS_ALPHA_MODE_OPAQUE = 3, + DDS_ALPHA_MODE_CUSTOM = 4, +}; +#endif + +// Standard version +HRESULT CreateDDSTextureFromMemory(_In_ ID3D11Device* d3dDevice, _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, _Outptr_opt_ ID3D11Resource** texture, _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize = 0, _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + +HRESULT CreateDDSTextureFromFile(_In_ ID3D11Device* d3dDevice, _In_z_ const wchar_t* szFileName, _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, _In_ size_t maxsize = 0, _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + +// Standard version with optional auto-gen mipmap support +HRESULT CreateDDSTextureFromMemory(_In_ ID3D11Device* d3dDevice, _In_opt_ ID3D11DeviceContext* d3dContext, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, _In_ size_t ddsDataSize, _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, _In_ size_t maxsize = 0, _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + +HRESULT CreateDDSTextureFromFile(_In_ ID3D11Device* d3dDevice, _In_opt_ ID3D11DeviceContext* d3dContext, _In_z_ const wchar_t* szFileName, + _Outptr_opt_ ID3D11Resource** texture, _Outptr_opt_ ID3D11ShaderResourceView** textureView, _In_ size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + +// Extended version +HRESULT CreateDDSTextureFromMemoryEx(_In_ ID3D11Device* d3dDevice, _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, _In_ size_t maxsize, _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, + _In_ unsigned int miscFlags, _In_ bool forceSRGB, _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + +HRESULT CreateDDSTextureFromFileEx(_In_ ID3D11Device* d3dDevice, _In_z_ const wchar_t* szFileName, _In_ size_t maxsize, + _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ bool forceSRGB, + _Outptr_opt_ ID3D11Resource** texture, _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + +// Extended version with optional auto-gen mipmap support +HRESULT CreateDDSTextureFromMemoryEx(_In_ ID3D11Device* d3dDevice, _In_opt_ ID3D11DeviceContext* d3dContext, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, _In_ size_t ddsDataSize, _In_ size_t maxsize, _In_ D3D11_USAGE usage, + _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ bool forceSRGB, + _Outptr_opt_ ID3D11Resource** texture, _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + +HRESULT CreateDDSTextureFromFileEx(_In_ ID3D11Device* d3dDevice, _In_opt_ ID3D11DeviceContext* d3dContext, _In_z_ const wchar_t* szFileName, + _In_ size_t maxsize, _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, + _In_ bool forceSRGB, _Outptr_opt_ ID3D11Resource** texture, _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; +} // namespace DirectX diff --git a/Atlas/Debug.cpp b/Atlas/Debug.cpp new file mode 100644 index 00000000..28901c1a --- /dev/null +++ b/Atlas/Debug.cpp @@ -0,0 +1,160 @@ +#include "Debug.h" + +#include "Logger.h" +#include "Renderer.h" + +struct LineVertex +{ + float x, y, z; + SimpleMath::Color colour; +}; + +// clang-format off +// LineVertex Vertices[] = +// { +// {-1, -1, 0}, +// {1, -1, 0}, +// +// {1, -1, 0}, +// {1, 1, 0}, +// +// {1, 1, 0}, +// {1, -1, 0}, +// +// {1, -1, 0}, +// {-1, -1, 0}, +// +// {0, -1, 0}, +// {0, 1, 0}, +// +// {-1, 0, 0}, +// {1, 0, 0}, +// }; +// clang-format on + +void Debug::DrawGrid(ID3D11DeviceContext* deviceContext) +{ + if (!bInitialised) + { + Logger::Log("Cannot debug render as has not been initialised"); + return; + } + + SetupRender(deviceContext); + + deviceContext->Draw(NumVertices, 0); +} + +HRESULT Debug::Initialise(ID3D11Device* device) +{ + HRESULT hr = S_OK; + ID3D10Blob* VertexShaderBlob = nullptr; + hr = DX11Renderer::CreateShaderFromHlslFile(L"Shaders/Debug.hlsl", "VS", &VertexShader, VertexShaderBlob, device); + if (FAILED(hr)) + { + Logger::Log("Failed to create debug vertex shader"); + return hr; + } + hr = DX11Renderer::CreateShaderFromHlslFile(L"Shaders/Debug.hlsl", "PS", &PixelShader, device); + if (FAILED(hr)) + { + Logger::Log("Failed to create debug pixel shader"); + return hr; + } + + D3D11_INPUT_ELEMENT_DESC layout[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + UINT numElements = ARRAYSIZE(layout); + + hr = device->CreateInputLayout( + layout, numElements, VertexShaderBlob->GetBufferPointer(), VertexShaderBlob->GetBufferSize(), &InputLayout); + if (FAILED(hr)) + { + Logger::Log("Failed to create debug input layout"); + return hr; + } + + VertexShaderBlob->Release(); + + // define grid size + constexpr float gridLengthX = 5.f, gridLengthY = 5.f; + constexpr float macroDivisionsSpacing = 0.5f; // inclusive + constexpr float microDivisionsSpacing = 0.1f; + + // For simplicity, we draw each line in each axis. There's duplicates but doesn't really matter that much. + // constexpr int numLines = (gridLengthX + gridLengthY) * (macroDivisionsSpacing + numMicroDivisionsPerMetre); + std::vector vertices; + // for every line, we need two vertex definitions + // vertices.reserve(numLines * 2); + + constexpr int numLinesMacroX = gridLengthX * 2 / macroDivisionsSpacing; + for (int i = 0; i < numLinesMacroX; i++) + { + const float x = -gridLengthX + i * macroDivisionsSpacing; + constexpr float c = 0.7f; + vertices.push_back({x, -gridLengthY, 0, {c, c, c, 1.f}}); + vertices.push_back({x, gridLengthY, 0, {c, c, c, 1.f}}); + } + + constexpr int numLinesMacroY = gridLengthY * 2 / macroDivisionsSpacing; + for (int i = 0; i < numLinesMacroY; i++) + { + const float y = -gridLengthY + i * macroDivisionsSpacing; + constexpr float c = 0.7f; + vertices.push_back({-gridLengthX, y, 0, {c, c, c, 1.f}}); + vertices.push_back({gridLengthX, y, 0, {c, c, c, 1.f}}); + } + + constexpr int numLinesMicroX = gridLengthX * 2 / microDivisionsSpacing; + for (int i = 0; i < numLinesMicroX; i++) + { + const float x = -gridLengthX + i * microDivisionsSpacing; + constexpr float c = 0.4f; + vertices.push_back({x, -gridLengthY, 0, {c, c, c, 1.f}}); + vertices.push_back({x, gridLengthY, 0, {c, c, c, 1.f}}); + } + + constexpr int numLinesMicroY = gridLengthY * 2 / microDivisionsSpacing; + for (int i = 0; i < numLinesMicroY; i++) + { + const float y = -gridLengthY + i * microDivisionsSpacing; + constexpr float c = 0.4f; + vertices.push_back({-gridLengthX, y, 0, {c, c, c, 1.f}}); + vertices.push_back({gridLengthX, y, 0, {c, c, c, 1.f}}); + } + + NumVertices = vertices.size(); + + D3D11_BUFFER_DESC bd = {}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = NumVertices * sizeof(LineVertex); + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA InitData = {}; + InitData.pSysMem = vertices.data(); + hr = device->CreateBuffer(&bd, &InitData, &VertexBuffer); + if (FAILED(hr)) + { + Logger::Log("Failed to create debug vertex buffer"); + return hr; + } + + bInitialised = true; + + return hr; +} + +void Debug::SetupRender(ID3D11DeviceContext* deviceContext) +{ + deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); + deviceContext->VSSetShader(VertexShader, nullptr, 0); + deviceContext->IASetInputLayout(InputLayout); + deviceContext->PSSetShader(PixelShader, nullptr, 0); + + UINT strides[1] = {4 * 7}; + UINT offsets[1] = {0}; + deviceContext->IASetVertexBuffers(0, 1, &VertexBuffer, strides, offsets); +} diff --git a/Atlas/Debug.h b/Atlas/Debug.h new file mode 100644 index 00000000..deaa3c42 --- /dev/null +++ b/Atlas/Debug.h @@ -0,0 +1,19 @@ +#pragma once +#include "DDSTextureLoader.h" + +class Debug +{ +public: + static HRESULT Initialise(ID3D11Device* device); + static void DrawGrid(ID3D11DeviceContext* deviceContext); + +private: + inline static ID3D11VertexShader* VertexShader; + inline static ID3D11PixelShader* PixelShader; + inline static ID3D11InputLayout* InputLayout; + inline static ID3D11Buffer* VertexBuffer; + inline static bool bInitialised = false; + inline static int NumVertices; + + static void SetupRender(ID3D11DeviceContext* deviceContext); +}; diff --git a/Atlas/Entity.cpp-8ede30a6 b/Atlas/Entity.cpp-8ede30a6 new file mode 100644 index 00000000..d57e2247 --- /dev/null +++ b/Atlas/Entity.cpp-8ede30a6 @@ -0,0 +1,424 @@ +#include "Entity.h" + +#include "DDSTextureLoader.h" +#include "Renderer.h" + +#include + +struct cb1_InstanceData +{ + XMVECTOR MeshTransform; + XMVECTOR UVTransform; + XMVECTOR InstanceTransformMatrices[]; +}; + +struct cb12_View +{ + XMVECTOR CameraPosition; // cb12[7] + XMVECTOR ViewportDimensions; // cb12[8], 0/1 is width/height, 2/3 is 1/width and 1/height + XMVECTOR CameraPosition2; // cb12[10] +}; + +Entity::Entity(LPCWSTR FileHash) +{ +} + +ID3D11VertexShader* Entity::GetVertexShader() +{ + return VertexShader; +} + +ID3D11PixelShader* Entity::GetPixelShader() +{ + return PixelShader; +} + +ID3D11InputLayout* Entity::GetVertexLayout() const +{ + return VertexLayout; +} + +ID3D11Buffer* Entity::GetIndexBuffer() const +{ + return IndexBuffer; +} + +ID3D11Buffer* const* Entity::GetVertexBuffers() const +{ + return VertexBuffers.data(); +} + +HRESULT Entity::Initialise(ID3D11Device* Device) +{ + HRESULT hr; + hr = CreateVertexShader(Device); + if (FAILED(hr)) + return hr; + + hr = CreateVertexLayout(Device); + if (FAILED(hr)) + return hr; + + hr = CreatePixelShader(Device); + if (FAILED(hr)) + return hr; + + hr = CreateVertexBuffers(Device); + if (FAILED(hr)) + return hr; + + hr = CreateIndexBuffer(Device); + if (FAILED(hr)) + return hr; + + hr = CreateConstantBuffers(Device); + if (FAILED(hr)) + return hr; + + hr = CreateTextureResources(Device); + if (FAILED(hr)) + return hr; + + return S_OK; +} + +void Entity::Render(ID3D11DeviceContext* DeviceContext, Camera* Camera, float DeltaTime) +{ + DeviceContext->VSSetShader(GetVertexShader(), nullptr, 0); + DeviceContext->IASetInputLayout(GetVertexLayout()); + + DeviceContext->PSSetShader(GetPixelShader(), nullptr, 0); + + UINT strides[2] = {16, 4}; + UINT offsets[2] = {0, 0}; + DeviceContext->IASetVertexBuffers(0, 2, GetVertexBuffers(), strides, offsets); + DeviceContext->IASetIndexBuffer(GetIndexBuffer(), DXGI_FORMAT_R16_UINT, 0); + + DeviceContext->PSSetShaderResources(0, TextureSRVs.size(), TextureSRVs.data()); + for (const auto& SamplerState : SamplerStates) + { + DeviceContext->PSSetSamplers(SamplerState->Slot, 1, &SamplerState->ResourcePointer); + } + + cb12_View View; + View.CameraPosition = Camera->GetPosition(); + D3D11_BOX Box{}; + Box.left = 112; + Box.top = 0; + Box.front = 0; + + Box.right = 112 + 16; + Box.bottom = 1; + Box.back = 1; + + DeviceContext->UpdateSubresource(ViewBuffer, 0, &Box, &View, 0, 0); + + for (const auto& ConstantBuffer : VSConstantBuffers) + { + if (ConstantBuffer->Slot == 12) + { + DeviceContext->CopyResource(ConstantBuffer->ResourcePointer, ViewBuffer); + } + DeviceContext->VSSetConstantBuffers(ConstantBuffer->Slot, 1, &ConstantBuffer->ResourcePointer); + } + for (const auto& ConstantBuffer : PSConstantBuffers) + { + DeviceContext->PSSetConstantBuffers(ConstantBuffer->Slot, 1, &ConstantBuffer->ResourcePointer); + } + + // DeviceContext->DrawIndexed(1116, 0, 0); // 11529 entire thing, 1116 first part lod0 + // DeviceContext->DrawIndexed(3606, 1116, 0); + DeviceContext->DrawIndexed(1503, 5718, 0); +} + +HRESULT Entity::CreateVertexShader(ID3D11Device* Device) +{ + // HRESULT hr = DX11Renderer::CreateShaderFromCompiledFile(L"6C24BB80/VS_CBEBD680.bin", &VertexShader, VertexShaderBlob, Device); + HRESULT hr = DX11Renderer::CreateShaderFromCompiledFile(L"C325BB80/VS_DC6BBA80.bin", &VertexShader, VertexShaderBlob, Device); + // const HRESULT hr = DX11Renderer::CreateShaderFromHlslFile(L"Shaders/Main.hlsl", "VSPole", &VertexShader, VertexShaderBlob, Device); + return hr; +} + +HRESULT Entity::CreateVertexLayout(ID3D11Device* Device) +{ + D3D11_INPUT_ELEMENT_DESC layout[] = { + {"POSITION", 0, DXGI_FORMAT_R16G16B16A16_SNORM, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TANGENT", 0, DXGI_FORMAT_R16G16B16A16_SNORM, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R16G16_SNORM, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + UINT numElements = ARRAYSIZE(layout); + + HRESULT hr = Device->CreateInputLayout( + layout, numElements, VertexShaderBlob->GetBufferPointer(), VertexShaderBlob->GetBufferSize(), &VertexLayout); + VertexShaderBlob->Release(); + + return hr; +} + +HRESULT Entity::CreatePixelShader(ID3D11Device* Device) +{ + const HRESULT hr = DX11Renderer::CreateShaderFromCompiledFile(L"C325BB80/PS_1864BA80.bin", &PixelShader, Device); + // const HRESULT hr = DX11Renderer::CreateShaderFromHlslFile(L"Shaders/Main.hlsl", "PSTexture", &PixelShader, Device); + return hr; +} + +HRESULT Entity::CreateIndexBuffer(ID3D11Device* Device) +{ + std::ifstream IndexBufferFile("C325BB80/IndexBuffer_CEFBBA80.bin", std::ios::in | std::ios::binary); + if (!IndexBufferFile) + { + return ERROR_INTERNAL_ERROR; + } + IndexBufferFile.seekg(0, std::ios::end); + const UINT FileLength = IndexBufferFile.tellg(); + IndexBufferFile.seekg(0, std::ios::beg); + WORD* Indices = new WORD[FileLength / sizeof(WORD)]; + IndexBufferFile.read((char*) Indices, FileLength); + + D3D11_BUFFER_DESC bd{}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = FileLength; + bd.BindFlags = D3D11_BIND_INDEX_BUFFER; + bd.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA InitData = {}; + InitData.pSysMem = Indices; + const HRESULT hr = Device->CreateBuffer(&bd, &InitData, &IndexBuffer); + return hr; +} + +HRESULT Entity::CreateVertexBuffers(ID3D11Device* Device) +{ + std::ifstream VertexBufferFile0("C325BB80/VertexBuffer0_D0FBBA80.bin", std::ios::in | std::ios::binary); + if (!VertexBufferFile0) + { + return MK_E_CANTOPENFILE; + } + VertexBufferFile0.seekg(0, std::ios::end); + const UINT FileLength0 = VertexBufferFile0.tellg(); + VertexBufferFile0.seekg(0, std::ios::beg); + WORD* Vertices0 = new WORD[FileLength0 / sizeof(WORD)]; + VertexBufferFile0.read((char*) Vertices0, FileLength0); + + std::ifstream VertexBufferFile1("C325BB80/VertexBuffer1_D2FBBA80.bin", std::ios::in | std::ios::binary); + if (!VertexBufferFile1) + { + return MK_E_CANTOPENFILE; + } + VertexBufferFile1.seekg(0, std::ios::end); + const UINT FileLength1 = VertexBufferFile1.tellg(); + VertexBufferFile1.seekg(0, std::ios::beg); + WORD* Vertices1 = new WORD[FileLength1 / sizeof(WORD)]; + VertexBufferFile1.read((char*) Vertices1, FileLength1); + + D3D11_BUFFER_DESC bd{}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = FileLength0; + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA InitData = {}; + InitData.pSysMem = Vertices0; + ID3D11Buffer* VertexBuffer0; + HRESULT hr = Device->CreateBuffer(&bd, &InitData, &VertexBuffer0); + if (FAILED(hr)) + return hr; + VertexBuffers.push_back(VertexBuffer0); + + bd.ByteWidth = FileLength1; + InitData.pSysMem = Vertices1; + ID3D11Buffer* VertexBuffer1; + hr = Device->CreateBuffer(&bd, &InitData, &VertexBuffer1); + if (FAILED(hr)) + return hr; + VertexBuffers.push_back(VertexBuffer1); + + return S_OK; +} + +HRESULT Entity::CreateConstantBuffers(ID3D11Device* Device) +{ + std::ifstream ConstantBufferFile1("C325BB80/VS_cb1.bin", std::ios::in | std::ios::binary); + if (!ConstantBufferFile1) + { + return MK_E_CANTOPENFILE; + } + ConstantBufferFile1.seekg(0, std::ios::end); + const UINT FileLength1 = ConstantBufferFile1.tellg(); + ConstantBufferFile1.seekg(0, std::ios::beg); + BYTE* cb1 = new BYTE[FileLength1]; + ConstantBufferFile1.read((char*) cb1, FileLength1); + + D3D11_BUFFER_DESC bd{}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = FileLength1; + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bd.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA InitData = {}; + InitData.pSysMem = cb1; + + ID3D11Buffer* ConstantBuffer1; + HRESULT hr = Device->CreateBuffer(&bd, &InitData, &ConstantBuffer1); + if (FAILED(hr)) + return hr; + + VSConstantBuffers.push_back(new Resource(1, ConstantBuffer1)); + + std::ifstream ConstantBufferFile12("C325BB80/VS_cb12.bin", std::ios::in | std::ios::binary); + if (!ConstantBufferFile12) + { + return MK_E_CANTOPENFILE; + } + ConstantBufferFile12.seekg(0, std::ios::end); + const UINT FileLength12 = ConstantBufferFile12.tellg(); + ConstantBufferFile12.seekg(0, std::ios::beg); + BYTE* cb12 = new BYTE[FileLength1]; + ConstantBufferFile12.read((char*) cb12, FileLength12); + + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = FileLength1; + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bd.CPUAccessFlags = 0; + + InitData.pSysMem = cb12; + + ID3D11Buffer* ConstantBuffer12; + hr = Device->CreateBuffer(&bd, &InitData, &ConstantBuffer12); + if (FAILED(hr)) + return hr; + + VSConstantBuffers.push_back(new Resource(12, ConstantBuffer12)); + + // to be able to update the VS, then we copy over to the resource + bd.BindFlags = 0; + hr = hr = Device->CreateBuffer(&bd, &InitData, &ViewBuffer); + if (FAILED(hr)) + return hr; + + std::ifstream ConstantBufferFile0("C325BB80/PS_cb0_75FFBA80.bin", std::ios::in | std::ios::binary); + if (!ConstantBufferFile0) + { + return MK_E_CANTOPENFILE; + } + ConstantBufferFile0.seekg(0, std::ios::end); + const UINT FileLength0 = ConstantBufferFile0.tellg(); + ConstantBufferFile0.seekg(0, std::ios::beg); + BYTE* cb0 = new BYTE[FileLength0]; + ConstantBufferFile0.read((char*) cb0, FileLength0); + + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = FileLength0; + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bd.CPUAccessFlags = 0; + + InitData.pSysMem = cb0; + + ID3D11Buffer* ConstantBuffer; + hr = Device->CreateBuffer(&bd, &InitData, &ConstantBuffer); + if (FAILED(hr)) + return hr; + + PSConstantBuffers.push_back(new Resource(0, ConstantBuffer)); + + std::ifstream PSConstantBufferFile12("C325BB80/PS_cb12.bin", std::ios::in | std::ios::binary); + if (!PSConstantBufferFile12) + { + return MK_E_CANTOPENFILE; + } + PSConstantBufferFile12.seekg(0, std::ios::end); + const UINT PSFileLength12 = PSConstantBufferFile12.tellg(); + PSConstantBufferFile12.seekg(0, std::ios::beg); + BYTE* PScb12 = new BYTE[PSFileLength12]; + PSConstantBufferFile12.read((char*) cb0, PSFileLength12); + + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = PSFileLength12; + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bd.CPUAccessFlags = 0; + + InitData.pSysMem = PScb12; + + ID3D11Buffer* PSConstantBuffer12; + hr = Device->CreateBuffer(&bd, &InitData, &PSConstantBuffer12); + if (FAILED(hr)) + return hr; + + PSConstantBuffers.push_back(new Resource(12, PSConstantBuffer12)); + + return S_OK; +} + +namespace DirectX +{ +HRESULT CreateTextureSRVsFromFiles( + ID3D11Device* Device, std::vector FileNames, std::vector& TextureSRVs) +{ + std::vector Output; + for (const auto& FileName : FileNames) + { + ID3D11ShaderResourceView* TextureSRV; + HRESULT hr = CreateDDSTextureFromFile(Device, FileName, nullptr, &TextureSRV); + if (FAILED(hr)) + return hr; + Output.push_back(TextureSRV); + } + + TextureSRVs = Output; + return S_OK; +} +} // namespace DirectX + +HRESULT Entity::CreateTextureResources(ID3D11Device* Device) +{ + const std::vector TextureFiles = {L"C325BB80/PS_0_AFC5BC80.dds", L"C325BB80/PS_1_8848A380.dds", L"C325BB80/PS_2_53C7BC80.dds", + L"C325BB80/PS_3_9742BD80.dds", L"C325BB80/PS_4_9342BD80.dds", L"C325BB80/PS_5_7DC5BC80.dds", L"C325BB80/PS_6_7D63BC80.dds", + L"C325BB80/PS_7_9B42BD80.dds", L"C325BB80/PS_8_85C5BC80.dds"}; + + HRESULT hr = CreateTextureSRVsFromFiles(Device, TextureFiles, TextureSRVs); + if (FAILED(hr)) + return hr; + + hr = CreateSamplers(Device); + if (FAILED(hr)) + return hr; + + return S_OK; +} + +HRESULT Entity::CreateSamplers(ID3D11Device* Device) +{ + D3D11_SAMPLER_DESC sampDesc = {}; + sampDesc.Filter = D3D11_FILTER_ANISOTROPIC; + sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + sampDesc.MaxAnisotropy = 4; + sampDesc.MinLOD = 0; + sampDesc.MaxLOD = D3D11_FLOAT32_MAX; + sampDesc.MipLODBias = 0; + ID3D11SamplerState* SamplerState; + HRESULT hr = Device->CreateSamplerState(&sampDesc, &SamplerState); + if (FAILED(hr)) + return hr; + SamplerStates.push_back(new Resource(1, SamplerState)); + + sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + hr = Device->CreateSamplerState(&sampDesc, &SamplerState); + if (FAILED(hr)) + return hr; + SamplerStates.push_back(new Resource(2, SamplerState)); + + sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + hr = Device->CreateSamplerState(&sampDesc, &SamplerState); + if (FAILED(hr)) + return hr; + SamplerStates.push_back(new Resource(3, SamplerState)); + + return S_OK; +} \ No newline at end of file diff --git a/Atlas/Input.cpp b/Atlas/Input.cpp new file mode 100644 index 00000000..bd59900e --- /dev/null +++ b/Atlas/Input.cpp @@ -0,0 +1,70 @@ +#include "Input.h" + +#include "NativeWindow.h" + +#include +#include + +DXSM::Vector2 Input::PreviousMousePosition = DXSM::Vector2(0, 0); +bool Input::bIsMouseCaptured = false; + +const DXSM::Vector2 Input::GetMouseDelta() +{ + const DXSM::Vector2& CurrentMousePosition = GetMousePosition(); + const DXSM::Vector2 Delta = CurrentMousePosition - PreviousMousePosition; + return Delta; +} + +void Input::UpdatePrevious(std::shared_ptr window) +{ + // PreviousMousePosition = GetMousePosition(); + if (IsMouseCaptured()) + { + const Rect& WindowRect = window->GetRect(); + const float MiddleRectX = WindowRect.X + WindowRect.Width / 2.0f; + const float MiddleRectY = WindowRect.Y + WindowRect.Height / 2.0f; + SetCursorPos(MiddleRectX, MiddleRectY); + auto x = GetMousePosition(); + PreviousMousePosition = DXSM::Vector2(MiddleRectX, MiddleRectY); + } + else + { + PreviousMousePosition = GetMousePosition(); + } +} + +bool Input::IsKeyDown(const char& key) +{ + return (GetAsyncKeyState(key) & 0x8000) != 0; +} + +bool Input::IsMouseCaptured() +{ + return bIsMouseCaptured; +} + +void Input::SetMouseCaptured(bool captured, HWND hwnd) +{ + bIsMouseCaptured = captured; + if (IsMouseCaptured()) + { + SetCapture(hwnd); + RECT rect; + GetWindowRect(hwnd, &rect); + ClipCursor(&rect); + ShowCursor(false); + } + else + { + ReleaseCapture(); + ClipCursor(NULL); + ShowCursor(true); + } +} + +const DXSM::Vector2 Input::GetMousePosition() +{ + POINT p; + GetCursorPos(&p); + return DXSM::Vector2((float) p.x, (float) p.y); +} diff --git a/Atlas/Input.h b/Atlas/Input.h new file mode 100644 index 00000000..a02b624a --- /dev/null +++ b/Atlas/Input.h @@ -0,0 +1,26 @@ +#pragma once +#include "NativeWindow.h" +#include "SimpleMath.h" + +#include + +#include + +#define DXSM DirectX::SimpleMath + +class Input +{ +public: + // static NativeWindow* InputWindow; + static const DXSM::Vector2 GetMouseDelta(); + static void UpdatePrevious(std::shared_ptr window); + static bool IsKeyDown(const char& key); + static bool IsMouseCaptured(); + static void SetMouseCaptured(bool captured, HWND hwnd); + +private: + static DXSM::Vector2 PreviousMousePosition; + static bool bIsMouseCaptured; + + static const DXSM::Vector2 GetMousePosition(); +}; diff --git a/Atlas/Interop.cpp b/Atlas/Interop.cpp new file mode 100644 index 00000000..4fea8ded --- /dev/null +++ b/Atlas/Interop.cpp @@ -0,0 +1,206 @@ +#include "Interop.h" + +#include "Logger.h" +#include "NativeWindow.h" +#include "Renderer.h" +#include "StaticMesh.h" +#include "renderdoc_app.h" + +#include + +#include + +RENDERDOC_API_1_1_2* rdoc_api = NULL; +static int frameCaptureCount = 0; +static constexpr int maxFrameCaptureCount = 1; +DX11Renderer* renderer; +std::shared_ptr window; +Camera* camera; + +extern HRESULT __cdecl Init(HWND hwnd, int width, int height) +{ + HRESULT hr = S_OK; + + if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) + { + pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI) GetProcAddress(mod, "RENDERDOC_GetAPI"); + int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void**) &rdoc_api); + assert(ret == 1); + } + + if (hwnd == nullptr) + { + return E_INVALIDARG; + } + + window = std::make_shared(hwnd, width, height); + + renderer = new DX11Renderer(false); + camera = new Camera(90.0f, 0.1f, 1000.0f, window->GetAspectRatio()); + window->OnSizeChanged.Add(camera->OnWindowSizeChanged); + // cube = new CCube(); + // + renderer->InitialiseGeneral(window); + // cube->InitDevice(); + return hr; +} + +extern HRESULT __cdecl Render(void* pResource, bool isNewSurface) +{ + HRESULT hr = S_OK; + if (rdoc_api && frameCaptureCount < maxFrameCaptureCount) + rdoc_api->StartFrameCapture(NULL, NULL); + + if (isNewSurface) + { + if (FAILED(hr = renderer->Initialise())) + { + Logger::Log("Failed to initialise renderer"); + return hr; + } + + hr = renderer->InitRenderTarget(pResource); + if (FAILED(hr)) + { + return hr; + } + } + + camera->Update(0.01f); + hr = renderer->Render(camera, 0.01f); + + if (rdoc_api && frameCaptureCount++ < maxFrameCaptureCount) + rdoc_api->EndFrameCapture(NULL, NULL); + + return hr; +} + +extern void __cdecl RegisterMouseDelta(float mouseX, float mouseY) +{ + if (camera == nullptr) + { + return; + } + + camera->UpdateFromMouse(SimpleMath::Vector2(mouseX, mouseY), 0.01f, true); +} + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fwdReason, LPVOID lpvReserved) +{ + return TRUE; +} + +extern void __cdecl MoveCamera(MoveDirection direction) +{ + if (camera == nullptr) + { + return; + } + + camera->UpdateFromKeyboard(direction, 0.1f); +} + +extern void __cdecl SetCameraMode(CameraMode mode) +{ + if (camera == nullptr) + { + return; + } + + camera->SetMode(mode); +} + +extern void __cdecl CreateStaticMesh(uint32_t hash, Blob staticMeshTransforms) +{ + if (renderer == nullptr) + { + return; + } + + renderer->StaticMesh = std::make_shared(hash, staticMeshTransforms); + renderer->StaticMesh->Initialise(renderer->Device); +} + +extern HRESULT __cdecl AddStaticMeshBufferGroup(uint32_t hash, BufferGroup bufferGroup) +{ + if (renderer == nullptr) + { + return E_FAIL; + } + + HRESULT hr = renderer->StaticMesh->AddStaticMeshBufferGroup(bufferGroup); + + if (FAILED(hr)) + { + Logger::Log("Failed to add buffer group for static mesh %x with error %x", hash, hr); + } + + return hr; +} + +// do we have to copy on an extern? or can we use const ref +extern HRESULT __cdecl CreateStaticMeshPart(uint32_t hash, PartInfo partInfo) +{ + if (renderer == nullptr) + { + return E_FAIL; + } + + HRESULT hr = renderer->StaticMesh->AddPart(partInfo); + + if (FAILED(hr)) + { + Logger::Log("Failed to create static mesh part for static mesh %x with error %x", hash, hr); + } + + return hr; +} + +extern void __cdecl ResizeWindow(int width, int height) +{ + if (window == nullptr || renderer == nullptr) + { + return; + } + + window->SetRect(width, height); + renderer->SetRasterizerViewport(); +} + +extern void __cdecl RegisterMouseScroll(int delta) +{ + if (camera == nullptr) + { + return; + } + camera->UpdateScroll(delta); +} + +extern void __cdecl ResetCamera() +{ + if (camera == nullptr) + { + return; + } + camera->Reset(); +} + +extern void __cdecl MoveOrbitOrigin(float mouseX, float mouseY) +{ + if (camera == nullptr) + { + return; + } + + camera->MoveOrbitOrigin(mouseX, mouseY); +} + +extern void __cdecl Cleanup() +{ + if (renderer == nullptr) + { + return; + } + + renderer->Cleanup(); +} diff --git a/Atlas/Interop.h b/Atlas/Interop.h new file mode 100644 index 00000000..b6c854f9 --- /dev/null +++ b/Atlas/Interop.h @@ -0,0 +1,31 @@ +#pragma once + +#include "DDSTextureLoader.h" +#include "Renderer.h" + +#include +#include + +struct Blob; +enum class MoveDirection : int; +enum class CameraMode : int; +struct PartInfo; +struct BufferGroup; +struct StaticMeshInfo; + +extern "C" +{ + __declspec(dllexport) HRESULT __cdecl Init(HWND hwnd, int width, int height); + __declspec(dllexport) void __cdecl Cleanup(); + __declspec(dllexport) HRESULT __cdecl Render(void* pResource, bool isNewSurface); + __declspec(dllexport) void __cdecl MoveCamera(MoveDirection direction); + __declspec(dllexport) void __cdecl SetCameraMode(CameraMode mode); + __declspec(dllexport) void __cdecl RegisterMouseDelta(float mouseX, float mouseY); + __declspec(dllexport) void __cdecl MoveOrbitOrigin(float mouseX, float mouseY); + __declspec(dllexport) void __cdecl RegisterMouseScroll(int delta); + __declspec(dllexport) void __cdecl CreateStaticMesh(uint32_t hash, Blob staticMeshTransforms); + __declspec(dllexport) HRESULT __cdecl AddStaticMeshBufferGroup(uint32_t hash, BufferGroup bufferGroup); + __declspec(dllexport) HRESULT __cdecl CreateStaticMeshPart(uint32_t hash, PartInfo partInfo); + __declspec(dllexport) void __cdecl ResizeWindow(int width, int height); + __declspec(dllexport) void __cdecl ResetCamera(); +} diff --git a/Atlas/Light.cpp b/Atlas/Light.cpp new file mode 100644 index 00000000..c0851926 --- /dev/null +++ b/Atlas/Light.cpp @@ -0,0 +1 @@ +#include "Light.h" diff --git a/Atlas/Light.h b/Atlas/Light.h new file mode 100644 index 00000000..2b020423 --- /dev/null +++ b/Atlas/Light.h @@ -0,0 +1,10 @@ +#pragma once + +class ILight +{ +public: +}; + +class PointLight : ILight +{ +}; \ No newline at end of file diff --git a/Atlas/Logger.cpp b/Atlas/Logger.cpp new file mode 100644 index 00000000..3234dd9d --- /dev/null +++ b/Atlas/Logger.cpp @@ -0,0 +1,29 @@ +#include "Logger.h" + +#include + +#include +#include + +void Logger::Log(const char* format, ...) +{ + va_list args; + va_start(args, format); + LogImpl(format, args); + va_end(args); +} + +void Logger::Verbose(const char* format, ...) +{ + va_list args; + va_start(args, format); + LogImpl(format, args); + va_end(args); +} + +void Logger::LogImpl(const char* format, va_list args) +{ + char buffer[512]; + vsprintf_s(buffer, format, args); + std::cout << buffer << std::endl; +} diff --git a/Atlas/Logger.h b/Atlas/Logger.h new file mode 100644 index 00000000..034438f7 --- /dev/null +++ b/Atlas/Logger.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct Logger +{ + static void Log(const char* format, ...); + static void Verbose(const char* format, ...); + +private: + static void LogImpl(const char* format, va_list args); +}; diff --git a/Atlas/NativeWindow.cpp b/Atlas/NativeWindow.cpp new file mode 100644 index 00000000..afafc109 --- /dev/null +++ b/Atlas/NativeWindow.cpp @@ -0,0 +1,110 @@ +#include "NativeWindow.h" + +#include "Camera.h" +#include "Input.h" + +RenderPanel::RenderPanel(UINT width, UINT height) : WindowRect({100, 100, width, height}) +{ +} + +RenderPanel::RenderPanel(HWND handle, UINT width, UINT height) : Handle(handle), WindowRect({100, 100, width, height}) +{ +} + +ExternalWindow::ExternalWindow(HWND handle, UINT width, UINT height) : RenderPanel(handle, width, height) +{ +} + +HWND RenderPanel::GetHandle() const +{ + return Handle; +} + +Rect RenderPanel::GetRect() const +{ + return WindowRect; +} + +float RenderPanel::GetAspectRatio() const +{ + return static_cast(WindowRect.Width) / static_cast(WindowRect.Height); +} + +void RenderPanel::SetRect(UINT width, UINT height) +{ + WindowRect.Width = width; + WindowRect.Height = height; + OnSizeChanged.Execute(width, height); +} + +NativeWindow::NativeWindow(LPCWSTR title, UINT width, UINT height) : RenderPanel(width, height), Title(title) +{ + Instance = GetModuleHandle(NULL); + MakeClass(); + MakeWindow(); +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT ps; + HDC hdc; + + switch (message) + { + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + // Note that this tutorial does not handle resizing (WM_SIZE) requests, + // so we created the window without the resize border. + + case WM_LBUTTONDOWN: + Input::SetMouseCaptured(true, hWnd); + + break; + + case WM_RBUTTONDOWN: + Input::SetMouseCaptured(false, hWnd); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return 0; +} + +void NativeWindow::MakeClass() const +{ + WNDCLASSEX wc = {0}; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WndProc; + wc.hInstance = Instance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpszClassName = Title; + wc.cbSize = sizeof(WNDCLASSEX); + ATOM atom = RegisterClassEx(&wc); +} + +void NativeWindow::MakeWindow() +{ + Handle = CreateWindowEx(0, Title, Title, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, WindowRect.X, WindowRect.Y, WindowRect.Width, + WindowRect.Height, 0, 0, Instance, nullptr); + ShowWindow(Handle, SW_SHOW); + SetForegroundWindow(Handle); + SetFocus(Handle); +} + +NativeWindow::~NativeWindow() +{ + if (Handle) + { + UnregisterClass(Title, Instance); + DestroyWindow(Handle); + } +} diff --git a/Atlas/NativeWindow.h b/Atlas/NativeWindow.h new file mode 100644 index 00000000..12db227b --- /dev/null +++ b/Atlas/NativeWindow.h @@ -0,0 +1,65 @@ +#pragma once +#include "Camera.h" + +struct Rect +{ + UINT X; + UINT Y; + UINT Width; + UINT Height; +}; + +// todo generalise +struct Delegate +{ + std::list> functions; + +public: + void Add(std::function function) { functions.push_back(function); } + + void Execute(int width, int height) + { + for (const auto& function : functions) + { + function(width, height); + } + } +}; + +class RenderPanel +{ +public: + Delegate OnSizeChanged; + + HWND GetHandle() const; + Rect GetRect() const; + float GetAspectRatio() const; + void SetRect(UINT width, UINT height); + +protected: + HWND Handle; + Rect WindowRect; + + RenderPanel(UINT width, UINT height); + RenderPanel(HWND handle, UINT width, UINT height); +}; + +class ExternalWindow : public RenderPanel +{ +public: + ExternalWindow(HWND handle, UINT width, UINT height); +}; + +class NativeWindow : public RenderPanel +{ +public: + NativeWindow(LPCWSTR title, UINT width, UINT height); + ~NativeWindow(); + +private: + LPCWSTR Title; + HINSTANCE Instance; + + void MakeClass() const; + void MakeWindow(); +}; diff --git a/Atlas/Renderer.cpp b/Atlas/Renderer.cpp new file mode 100644 index 00000000..2f11a191 --- /dev/null +++ b/Atlas/Renderer.cpp @@ -0,0 +1,793 @@ +#include "Renderer.h" + +#include "DDSTextureLoader.h" +#include "Debug.h" +#include "Logger.h" +#include "StaticMesh.h" + +#include + +#include +#include +#include + +DX11Renderer::DX11Renderer(bool useSwapchain) : UseSwapchain(useSwapchain) +{ +} + +struct GeometryVertex0 +{ + XMFLOAT3 Pos; + XMFLOAT3 Normal; +}; + +struct GeometryVertex1 +{ + XMFLOAT2 Tex; +}; + +struct QuadVertex +{ + XMFLOAT4 Pos; + XMFLOAT2 Tex; +}; + +// struct CBNeverChanges +// { +// }; + +struct CBChangeOnResize +{ + XMMATRIX Projection; +}; + +struct ScopeView +{ + XMMATRIX WorldToProjective; + XMMATRIX CameraToWorld; + XMFLOAT4 Target; + XMFLOAT4 ViewMiscellaneous; +}; + +struct CBLightingChangesEveryFrame +{ + XMFLOAT4 LightDir[2]; + XMFLOAT4 LightColor[2]; +}; + +struct SimpleVertex +{ + XMFLOAT3 Pos; + XMFLOAT4 Color; +}; + +struct ConstantBuffer +{ + XMMATRIX mWorld; + XMMATRIX mView; + XMMATRIX mProjection; +}; + +HRESULT DX11Renderer::Initialise() +{ + // Initialize the projection matrix - todo camera + + // HRESULT hr = InitialiseGeneral(); + // if (FAILED(hr)) + // return hr; + + HRESULT hr = CreateDepthStencilView(); + if (FAILED(hr)) + { + Logger::Log("Failed to initialise depth stencil"); + return hr; + } + + hr = InitialiseLightingPass(); + if (FAILED(hr)) + { + Logger::Log("Failed to initialise lighting pass"); + return hr; + } + + hr = Debug::Initialise(Device); + if (FAILED(hr)) + { + Logger::Log("Failed to initialise debug pass"); + return hr; + } + + return S_OK; +} + +HRESULT DX11Renderer::CreateShaderFromCompiledFile(LPCWSTR FileName, ID3D11PixelShader** PixelShader, ID3D11Device* Device) +{ + ID3D10Blob* ShaderBlob; + HRESULT hr = D3DReadFileToBlob(FileName, &ShaderBlob); + if (FAILED(hr)) + return hr; + + hr = Device->CreatePixelShader(ShaderBlob->GetBufferPointer(), ShaderBlob->GetBufferSize(), nullptr, PixelShader); + if (FAILED(hr)) + return hr; + + return hr; +} + +HRESULT DX11Renderer::CreateShaderFromCompiledFile( + LPCWSTR FileName, ID3D11VertexShader** VertexShader, ID3D10Blob*& ShaderBlob, ID3D11Device* Device) +{ + HRESULT hr = D3DReadFileToBlob(FileName, &ShaderBlob); + if (FAILED(hr)) + return hr; + + hr = Device->CreateVertexShader(ShaderBlob->GetBufferPointer(), ShaderBlob->GetBufferSize(), nullptr, VertexShader); + if (FAILED(hr)) + return hr; + + return hr; +} + +HRESULT DX11Renderer::CreateShaderFromHlslFile( + LPCWSTR FileName, LPCSTR EntryPoint, ID3D11VertexShader** VertexShader, ID3D10Blob*& ShaderBlob, ID3D11Device* Device) +{ + const LPWSTR path = new WCHAR[MAX_PATH]; + GetCurrentDirectoryW(MAX_PATH, path); + wcscat_s(path, MAX_PATH, L"\\"); + wcscat_s(path, MAX_PATH, FileName); + + Logger::Log("Creating shader from file %ws", path); + + HRESULT hr = CompileShaderFromFile(path, EntryPoint, "vs_5_0", &ShaderBlob); + if (FAILED(hr)) + return hr; + + hr = Device->CreateVertexShader(ShaderBlob->GetBufferPointer(), ShaderBlob->GetBufferSize(), nullptr, VertexShader); + if (FAILED(hr)) + return hr; + + return hr; +} + +HRESULT DX11Renderer::CreateShaderFromHlslFile(LPCWSTR FileName, LPCSTR EntryPoint, ID3D11PixelShader** PixelShader, ID3D11Device* Device) +{ + ID3DBlob* ShaderBlob = nullptr; + HRESULT hr = CompileShaderFromFile(FileName, EntryPoint, "ps_5_0", &ShaderBlob); + if (FAILED(hr)) + return hr; + + hr = Device->CreatePixelShader(ShaderBlob->GetBufferPointer(), ShaderBlob->GetBufferSize(), nullptr, PixelShader); + ShaderBlob->Release(); + if (FAILED(hr)) + return hr; + + return hr; +} + +HRESULT DX11Renderer::CompileShaderFromFile(LPCWSTR FileName, LPCSTR EntryPoint, LPCSTR ShaderModel, ID3DBlob** ShaderBlob) +{ + ID3DBlob* ErrorShaderBlob = nullptr; + DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; +#ifdef _DEBUG + dwShaderFlags |= D3DCOMPILE_DEBUG; + dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION; +#endif + + HRESULT hr = D3DCompileFromFile(FileName, nullptr, nullptr, EntryPoint, ShaderModel, dwShaderFlags, 0, ShaderBlob, &ErrorShaderBlob); + if (hr != S_OK && ErrorShaderBlob != nullptr) + { + const char* error = static_cast(ErrorShaderBlob->GetBufferPointer()); + std::cout << error << std::endl; + } + + return hr; +} + +HRESULT DX11Renderer::InitialiseGeneral(std::shared_ptr window) +{ + this->window = window; + + HRESULT hr = S_OK; + + if (UseSwapchain) + { + hr = CreateDeviceAndSwapChain(); + if (FAILED(hr)) + return hr; + + hr = CreateBackBufferView(); + if (FAILED(hr)) + return hr; + + SetRasterizerViewport(); + } + else + { + hr = CreateDevice(); + if (FAILED(hr)) + return hr; + } + + // SetRasterizerViewport(); + + // Initialize the world matrix + World1 = XMMatrixIdentity(); + World2 = XMMatrixIdentity(); + + return hr; +} + +HRESULT DX11Renderer::CreateDeviceAndSwapChain() +{ + DXGI_SWAP_CHAIN_DESC Desc{}; + Desc.BufferCount = 1; + Desc.BufferDesc.Width = window->GetRect().Width; + Desc.BufferDesc.Height = window->GetRect().Height; + Desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + Desc.BufferDesc.RefreshRate.Numerator = 240; + Desc.BufferDesc.RefreshRate.Denominator = 1; + Desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + Desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + Desc.OutputWindow = window->GetHandle(); + Desc.SampleDesc.Count = 1; + Desc.SampleDesc.Quality = 0; + Desc.Windowed = TRUE; + // Desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // enables vsync + + UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; +#ifdef _DEBUG + createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + D3D_FEATURE_LEVEL featureLevel; + const D3D_FEATURE_LEVEL featureLevelArray[1] = {D3D_FEATURE_LEVEL_11_0}; + + HRESULT hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevelArray, 1, + D3D11_SDK_VERSION, &Desc, &SwapChain, &Device, &featureLevel, &DeviceContext); + + return hr; +} + +HRESULT DX11Renderer::CreateDevice() +{ + UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; +#ifdef _DEBUG + createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + D3D_FEATURE_LEVEL featureLevel; + const D3D_FEATURE_LEVEL featureLevelArray[1] = {D3D_FEATURE_LEVEL_11_0}; + + HRESULT hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevelArray, 1, D3D11_SDK_VERSION, + &Device, &featureLevel, &DeviceContext); + + return hr; +} + +HRESULT DX11Renderer::CreateBackBufferView() +{ + ID3D11Texture2D* pBackBuffer = nullptr; + HRESULT hr = SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast(&pBackBuffer)); + if (FAILED(hr)) + return hr; + + hr = Device->CreateRenderTargetView(pBackBuffer, nullptr, &BackBufferView); + pBackBuffer->Release(); + if (FAILED(hr)) + return hr; + + return hr; +} + +HRESULT DX11Renderer::SetRasterizerViewport() +{ + D3D11_VIEWPORT vp; + vp.Width = static_cast(window->GetRect().Width); + vp.Height = static_cast(window->GetRect().Height); + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + DeviceContext->RSSetViewports(1, &vp); + + D3D11_RASTERIZER_DESC rasterizerDesc{}; + rasterizerDesc.CullMode = D3D11_CULL_NONE; + rasterizerDesc.FillMode = D3D11_FILL_SOLID; + rasterizerDesc.FrontCounterClockwise = true; + rasterizerDesc.DepthClipEnable = true; + + ID3D11RasterizerState* rasterizerState; + HRESULT hr = Device->CreateRasterizerState(&rasterizerDesc, &rasterizerState); + if (FAILED(hr)) + return hr; + + DeviceContext->RSSetState(rasterizerState); + + return S_OK; +} + +HRESULT DX11Renderer::CreateDepthStencilView() +{ + HRESULT hr; + D3D11_TEXTURE2D_DESC descDepth{}; + descDepth.Width = window->GetRect().Width; + descDepth.Height = window->GetRect().Height; + descDepth.MipLevels = 1; + descDepth.ArraySize = 1; + descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + descDepth.SampleDesc.Count = 1; + descDepth.SampleDesc.Quality = 0; + descDepth.Usage = D3D11_USAGE_DEFAULT; + descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL; + descDepth.CPUAccessFlags = 0; + descDepth.MiscFlags = 0; + // todo DepthStencil texture might not need to be global? unsure how views work here if the texture goes out of scope + hr = Device->CreateTexture2D(&descDepth, nullptr, &DepthStencil); + if (FAILED(hr)) + return hr; + + D3D11_DEPTH_STENCIL_VIEW_DESC descDSV = {}; + descDSV.Format = descDepth.Format; + descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + descDSV.Texture2D.MipSlice = 0; + hr = Device->CreateDepthStencilView(DepthStencil, &descDSV, &DepthStencilView); + if (FAILED(hr)) + return hr; + + D3D11_DEPTH_STENCIL_DESC dsDesc; + + // Depth test parameters + dsDesc.DepthEnable = true; + dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + dsDesc.DepthFunc = D3D11_COMPARISON_GREATER; + + // Stencil test parameters + dsDesc.StencilEnable = false; + dsDesc.StencilReadMask = 0xFF; + dsDesc.StencilWriteMask = 0xFF; + + // Stencil operations if pixel is front-facing + dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR; + dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + + // Stencil operations if pixel is back-facing + dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR; + dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + + // Create depth stencil state + ID3D11DepthStencilState* depthStencilState; + Device->CreateDepthStencilState(&dsDesc, &depthStencilState); + DeviceContext->OMSetDepthStencilState(depthStencilState, 1); + + return hr; +} + +HRESULT DX11Renderer::InitialiseLightingPass() +{ + HRESULT hr; + + hr = CreateLightingRenderTargets(); + if (FAILED(hr)) + { + Logger::Log("Failed to create lighting RTs"); + return hr; + } + + ID3D10Blob* VertexShaderBlob = nullptr; + hr = CreateLightingVertexShader(VertexShaderBlob); + if (FAILED(hr)) + { + Logger::Log("Failed to create lighting vertex shader"); + return hr; + } + + hr = CreateLightingVertexLayout(VertexShaderBlob); + if (FAILED(hr)) + { + Logger::Log("Failed to create lighting vertex layout"); + return hr; + } + + VertexShaderBlob->Release(); + + hr = CreateLightingPixelShader(); + if (FAILED(hr)) + { + Logger::Log("Failed to create lighting pixel shader"); + return hr; + } + + hr = CreateLightingVertexBuffer(); + if (FAILED(hr)) + { + Logger::Log("Failed to create lighting vertex buffer"); + return hr; + } + + hr = CreateLightingIndexBuffer(); + if (FAILED(hr)) + { + Logger::Log("Failed to create lighting index buffer"); + return hr; + } + + hr = CreateLightingConstantBuffer(); + if (FAILED(hr)) + { + Logger::Log("Failed to create lighting constant buffer"); + return hr; + } + + return hr; +} + +HRESULT DX11Renderer::CreateLightingRenderTargets() +{ + HRESULT hr; + + D3D11_TEXTURE2D_DESC descDepth = {}; + descDepth.Width = window->GetRect().Width; + descDepth.Height = window->GetRect().Height; + descDepth.MipLevels = 1; + descDepth.ArraySize = 1; + descDepth.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + descDepth.SampleDesc.Count = 1; + descDepth.SampleDesc.Quality = 0; + descDepth.Usage = D3D11_USAGE_DEFAULT; + descDepth.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + descDepth.CPUAccessFlags = 0; + descDepth.MiscFlags = 0; + + D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc; + renderTargetViewDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + renderTargetViewDesc.Texture2D.MipSlice = 0; + + D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc; + shaderResourceViewDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + shaderResourceViewDesc.Texture2D.MostDetailedMip = 0; + shaderResourceViewDesc.Texture2D.MipLevels = 1; + + hr = Device->CreateTexture2D(&descDepth, nullptr, &RT0Texture); + if (FAILED(hr)) + return hr; + hr = Device->CreateRenderTargetView(RT0Texture, &renderTargetViewDesc, &RT0View); + if (FAILED(hr)) + return hr; + hr = Device->CreateShaderResourceView(RT0Texture, &shaderResourceViewDesc, &RT0SRV); + // RT0Texture->Release(); // todo test if this is allowed or not + if (FAILED(hr)) + return hr; + + hr = Device->CreateTexture2D(&descDepth, nullptr, &RT1Texture); + if (FAILED(hr)) + return hr; + hr = Device->CreateRenderTargetView(RT1Texture, nullptr, &RT1View); + if (FAILED(hr)) + return hr; + hr = Device->CreateShaderResourceView(RT1Texture, &shaderResourceViewDesc, &RT1SRV); + // RT1Texture->Release(); + if (FAILED(hr)) + return hr; + + hr = Device->CreateTexture2D(&descDepth, nullptr, &RT2Texture); + if (FAILED(hr)) + return hr; + hr = Device->CreateRenderTargetView(RT2Texture, nullptr, &RT2View); + if (FAILED(hr)) + return hr; + hr = Device->CreateShaderResourceView(RT2Texture, &shaderResourceViewDesc, &RT2SRV); + // RT1Texture->Release(); + if (FAILED(hr)) + return hr; + + return hr; +} + +HRESULT DX11Renderer::CreateLightingVertexShader(ID3D10Blob*& VertexShaderBlob) +{ + const HRESULT hr = CreateShaderFromHlslFile(L"Shaders/Lighting.hlsl", "VS2", &LightingVertexShader, VertexShaderBlob, Device); + return hr; +} + +HRESULT DX11Renderer::CreateLightingPixelShader() +{ + const HRESULT hr = CreateShaderFromHlslFile(L"Shaders/Lighting.hlsl", "PS2", &LightingPixelShader, Device); + return hr; +} + +HRESULT DX11Renderer::CreateLightingVertexLayout(ID3D10Blob* VertexShaderBlob) +{ + D3D11_INPUT_ELEMENT_DESC layout[] = { + {"SV_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + UINT numElements = ARRAYSIZE(layout); + + HRESULT hr = Device->CreateInputLayout( + layout, numElements, VertexShaderBlob->GetBufferPointer(), VertexShaderBlob->GetBufferSize(), &QuadVertexLayout); + + VertexShaderBlob->Release(); + + return hr; +} + +HRESULT DX11Renderer::CreateLightingVertexBuffer() +{ + QuadVertex vertices[] = { + {XMFLOAT4(-1.0f, 1.0f, 0.0f, 1.0f), XMFLOAT2(0.0f, 0.0f)}, + {XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f), XMFLOAT2(1.0f, 0.0f)}, + {XMFLOAT4(1.0f, -1.0f, 0.0f, 1.0f), XMFLOAT2(1.0f, 1.0f)}, + {XMFLOAT4(-1.0f, -1.0f, 0.0f, 1.0f), XMFLOAT2(0.0f, 1.0f)}, + }; + D3D11_BUFFER_DESC bd = {}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = ARRAYSIZE(vertices) * sizeof(QuadVertex); + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA InitData = {}; + InitData.pSysMem = vertices; + HRESULT hr = Device->CreateBuffer(&bd, &InitData, &QuadVertexBuffer); + + return hr; +} + +HRESULT DX11Renderer::CreateLightingIndexBuffer() +{ + WORD indices[] = {0, 2, 1, 0, 3, 2}; + + D3D11_BUFFER_DESC bd{}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = ARRAYSIZE(indices) * sizeof(WORD); + bd.BindFlags = D3D11_BIND_INDEX_BUFFER; + bd.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA InitData = {}; + InitData.pSysMem = indices; + const HRESULT hr = Device->CreateBuffer(&bd, &InitData, &QuadIndexBuffer); + + return hr; +} + +HRESULT DX11Renderer::CreateLightingConstantBuffer() +{ + D3D11_BUFFER_DESC bd{}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bd.CPUAccessFlags = 0; + bd.ByteWidth = sizeof(CBLightingChangesEveryFrame); + HRESULT hr = Device->CreateBuffer(&bd, nullptr, &pCBLightingChangesEveryFrame); + + return hr; +} + +HRESULT DX11Renderer::Render(Camera* camera, float deltaTime) +{ + // todo put this into a function + DeviceContext->ClearRenderTargetView(BackBufferView, new FLOAT[4]{0.0f, 0.0f, 0.0f, 0.0f}); + DeviceContext->ClearRenderTargetView(RT0View, new FLOAT[4]{0.1f, 0.1f, 0.1f, 0.0f}); + DeviceContext->ClearRenderTargetView(RT1View, new FLOAT[4]{0.0f, 0.0f, 0.0f, 0.0f}); + DeviceContext->ClearRenderTargetView(RT2View, new FLOAT[4]{0.0f, 0.0f, 0.0f, 0.0f}); + DeviceContext->ClearDepthStencilView(DepthStencilView, D3D11_CLEAR_DEPTH, 0.0f, 0); + + HRESULT hr = RenderGeometry(camera, deltaTime); + if (FAILED(hr)) + { + return hr; + } + RenderLightingPass(camera, deltaTime); + + if (UseSwapchain) + { + SwapChain->Present(0, 0); + } + else + { + if (DeviceContext != NULL) + { + DeviceContext->Flush(); + } + } + + return S_OK; +} + +void DX11Renderer::Cleanup() +{ + // if (DeviceContext) + // { + // DeviceContext->ClearState(); + // } + // Logger::Log("Cleaning up renderer"); + + if (StaticMesh) + { + StaticMesh.reset(); + } +} + +struct SVertexPositionColor +{ + XMFLOAT3 position; + XMFLOAT4 color; +}; + +HRESULT DX11Renderer::RenderGeometry(Camera* camera, float deltaTime) +{ + ID3D11RenderTargetView* renderTargets[3] = {RT0View, RT1View, RT2View}; + DeviceContext->OMSetRenderTargets(3, renderTargets, DepthStencilView); + + static const XMVECTORF32 xAxis = {20.f, 0.f, 0.f, 0.f}; + static const XMVECTORF32 yAxis = {0.f, 0.f, 20.f, 0.f}; + // Debug::DrawGrid(xAxis, yAxis, XMVectorSet(0, 0, 0, 0), 20, 20, Colors::Gray); + + HRESULT hr = S_OK; + if (StaticMesh) + { + hr = StaticMesh->Render(DeviceContext, camera, deltaTime); + } + + // draw after so we can use the cb12 thats already bound for now + Debug::DrawGrid(DeviceContext); + + return hr; +} + +void DX11Renderer::RenderLightingPass(Camera* camera, float deltaTime) +{ + DeviceContext->OMSetRenderTargets(1, &BackBufferView, nullptr); + + DeviceContext->VSSetShader(LightingVertexShader, nullptr, 0); + DeviceContext->IASetInputLayout(QuadVertexLayout); + DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + ID3D11ShaderResourceView* RenderTargets[3] = {RT0SRV, RT1SRV, RT2SRV}; + DeviceContext->PSSetShaderResources(0, 3, RenderTargets); + DeviceContext->PSSetShader(LightingPixelShader, nullptr, 0); + + UINT stride = sizeof(QuadVertex); + UINT offset = 0; + DeviceContext->IASetVertexBuffers(0, 1, &QuadVertexBuffer, &stride, &offset); + DeviceContext->IASetIndexBuffer(QuadIndexBuffer, DXGI_FORMAT_R16_UINT, 0); + + CBLightingChangesEveryFrame cb1; + static float t = 0.0f; + t += (float) XM_PI * deltaTime * 0.1f; + XMFLOAT4 vLightDirs[2] = { + XMFLOAT4(-0.577f, 0.577f, -0.577f, 1.0f), + XMFLOAT4(0.0f, 0.0f, -1.0f, 1.0f), + }; + XMFLOAT4 vLightColors[2] = {XMFLOAT4(0.5f, 1.0f, 1.0f, 1.0f), XMFLOAT4(0.5f, 0.0f, 0.0f, 1.0f)}; + cb1.LightDir[0] = vLightDirs[0]; + cb1.LightDir[1] = vLightDirs[1]; + cb1.LightColor[0] = vLightColors[0]; + cb1.LightColor[1] = vLightColors[1]; + // DeviceContext->UpdateSubresource(pCBLightingChangesEveryFrame, 0, nullptr, &cb1, 0, 0); + // DeviceContext->PSSetConstantBuffers(0, 1, &pCBLightingChangesEveryFrame); + + DeviceContext->DrawIndexed(6, 0, 0); + + ID3D11ShaderResourceView* pSRV[3] = {NULL, NULL, NULL}; + DeviceContext->PSSetShaderResources(0, 3, pSRV); +} + +DX11Renderer::~DX11Renderer() +{ + // if (DeviceContext) + // DeviceContext->ClearState(); + // + // // if( pCBNeverChanges ) pCBNeverChanges->Release(); + // if (pCBChangeOnResize) + // pCBChangeOnResize->Release(); + // if (pCBChangesEveryFrame) + // pCBChangesEveryFrame->Release(); + // if (TextureRV) + // TextureRV->Release(); + // if (SamplerLinear) + // SamplerLinear->Release(); + // if (VertexBuffer) + // VertexBuffer->Release(); + // if (IndexBuffer) + // IndexBuffer->Release(); + // if (VertexLayout) + // VertexLayout->Release(); + // if (VertexShader) + // VertexShader->Release(); + // if (PixelShader) + // PixelShader->Release(); + // if (PixelShaderSolid) + // PixelShaderSolid->Release(); + // if (DepthStencil) + // DepthStencil->Release(); + // if (DepthStencilView) + // DepthStencilView->Release(); + // if (BackBufferView) + // BackBufferView->Release(); + // if (SwapChain) + // SwapChain->Release(); + // if (DeviceContext) + // DeviceContext->Release(); + // if (Device) + // Device->Release(); +} + +HRESULT DX11Renderer::InitRenderTarget(void* pResource) +{ + DeviceContext->OMSetRenderTargets(0, NULL, NULL); + + HRESULT hr = S_OK; + + IUnknown* pUnk = (IUnknown*) pResource; + + IDXGIResource* pDXGIResource; + hr = pUnk->QueryInterface(__uuidof(IDXGIResource), (void**) &pDXGIResource); + if (FAILED(hr)) + { + return hr; + } + + HANDLE sharedHandle; + hr = pDXGIResource->GetSharedHandle(&sharedHandle); + if (FAILED(hr)) + { + return hr; + } + + pDXGIResource->Release(); + + IUnknown* tempResource11; + hr = Device->OpenSharedResource(sharedHandle, __uuidof(ID3D11Resource), (void**) (&tempResource11)); + if (FAILED(hr)) + { + return hr; + } + + ID3D11Texture2D* pOutputResource; + hr = tempResource11->QueryInterface(__uuidof(ID3D11Texture2D), (void**) (&pOutputResource)); + if (FAILED(hr)) + { + return hr; + } + tempResource11->Release(); + + D3D11_RENDER_TARGET_VIEW_DESC rtDesc; + // DXGI_FORMAT_R8G8B8A8_UNORM + rtDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtDesc.Texture2D.MipSlice = 0; + + hr = Device->CreateRenderTargetView(pOutputResource, &rtDesc, &BackBufferView); + if (FAILED(hr)) + { + return hr; + } + + D3D11_TEXTURE2D_DESC outputResourceDesc; + pOutputResource->GetDesc(&outputResourceDesc); + if (outputResourceDesc.Width != window->GetRect().Width || outputResourceDesc.Height != window->GetRect().Height) + { + // this should never happen + // todo add const back + window->SetRect(outputResourceDesc.Width, outputResourceDesc.Height); + } + + hr = SetRasterizerViewport(); + if (FAILED(hr)) + { + return hr; + } + + // this happens when we do the lighting pass + DeviceContext->OMSetRenderTargets(1, &BackBufferView, NULL); + + if (NULL != pOutputResource) + { + pOutputResource->Release(); + } + + return hr; +} diff --git a/Atlas/Renderer.h b/Atlas/Renderer.h new file mode 100644 index 00000000..a86d8cbc --- /dev/null +++ b/Atlas/Renderer.h @@ -0,0 +1,145 @@ +#pragma once +#include "Camera.h" +#include "NativeWindow.h" + +#include + +#include + +namespace DirectX::DX11 +{ +struct VertexPositionColor; +} + +class StaticMesh; +using namespace DirectX; + +enum ShaderType +{ + Vertex, + Pixel +}; + +#ifndef SAFE_DELETE +#define SAFE_DELETE(p) \ + { \ + if (p) \ + { \ + delete (p); \ + (p) = NULL; \ + } \ + } +#endif +#ifndef SAFE_DELETE_ARRAY +#define SAFE_DELETE_ARRAY(p) \ + { \ + if (p) \ + { \ + delete[] (p); \ + (p) = NULL; \ + } \ + } +#endif +#ifndef SAFE_RELEASE +#define SAFE_RELEASE(p) \ + { \ + if (p) \ + { \ + (p)->Release(); \ + (p) = NULL; \ + } \ + } +#endif + +class IRenderer +{ +public: + virtual ~IRenderer() = default; + virtual void Render(Camera* camera) = 0; +}; + +class DX11Renderer +{ +public: + std::shared_ptr StaticMesh; + + DX11Renderer(bool useSwapchain = true); + ~DX11Renderer(); + HRESULT InitRenderTarget(void* pResource); + HRESULT Initialise(); + HRESULT Render(Camera* camera, float deltaTime); + void Cleanup(); + + static HRESULT CreateShaderFromCompiledFile( + LPCWSTR FileName, ID3D11VertexShader** VertexShader, ID3D10Blob*& ShaderBlob, ID3D11Device* Device); + static HRESULT CreateShaderFromCompiledFile(LPCWSTR FileName, ID3D11PixelShader** PixelShader, ID3D11Device* Device); + static HRESULT CreateShaderFromHlslFile( + LPCWSTR FileName, LPCSTR EntryPoint, ID3D11VertexShader** VertexShader, ID3D10Blob*& ShaderBlob, ID3D11Device* Device); + static HRESULT CreateShaderFromHlslFile(LPCWSTR FileName, LPCSTR EntryPoint, ID3D11PixelShader** PixelShader, ID3D11Device* Device); + static HRESULT CompileShaderFromFile(LPCWSTR FileName, LPCSTR EntryPoint, LPCSTR ShaderModel, ID3DBlob** ShaderBlob); + +private: + std::shared_ptr window; + + XMMATRIX World1; + XMMATRIX World2; + XMMATRIX View; + XMMATRIX Projection; + + ID3D11VertexShader* LightingVertexShader; + ID3D11PixelShader* LightingPixelShader; + ID3D11InputLayout* QuadVertexLayout; + ID3D11InputLayout* BatchVertexLayout; + ID3D11Buffer* QuadIndexBuffer; + ID3D11Buffer* QuadVertexBuffer; + + // ID3D11Buffer* pCBNeverChanges = nullptr; + ID3D11Buffer* pCBChangeOnResize = nullptr; + ID3D11Buffer* pScopeView = nullptr; + ID3D11Buffer* pCBLightingChangesEveryFrame = nullptr; + + ID3D11ShaderResourceView* TextureRV = nullptr; + ID3D11SamplerState* SamplerLinear = nullptr; + + IDXGISwapChain* SwapChain; + +public: + ID3D11Device* Device; + + HRESULT InitialiseGeneral(std::shared_ptr window); + HRESULT SetRasterizerViewport(); + +private: + ID3D11DeviceContext* DeviceContext; + ID3D11Texture2D* RT0Texture; + ID3D11Texture2D* RT1Texture; + ID3D11Texture2D* RT2Texture; + ID3D11RenderTargetView* RT0View; + ID3D11RenderTargetView* RT1View; + ID3D11RenderTargetView* RT2View; + ID3D11ShaderResourceView* RT0SRV = nullptr; + ID3D11ShaderResourceView* RT1SRV = nullptr; + ID3D11ShaderResourceView* RT2SRV = nullptr; + ID3D11RenderTargetView* BackBufferView; + ID3D11Texture2D* DepthStencil; + ID3D11DepthStencilView* DepthStencilView; + + bool UseSwapchain; + + HRESULT CreateDeviceAndSwapChain(); + HRESULT CreateDevice(); + HRESULT CreateBackBufferView(); + + HRESULT CreateDepthStencilView(); + + HRESULT InitialiseLightingPass(); + HRESULT CreateLightingRenderTargets(); + HRESULT CreateLightingVertexShader(ID3D10Blob*& VertexShaderBlob); + HRESULT CreateLightingPixelShader(); + HRESULT CreateLightingVertexLayout(ID3D10Blob* VertexShaderBlob); + HRESULT CreateLightingVertexBuffer(); + HRESULT CreateLightingIndexBuffer(); + HRESULT CreateLightingConstantBuffer(); + HRESULT RenderGeometry(Camera* camera, float deltaTime); + void RenderLightingPass(Camera* camera, float deltaTime); +}; diff --git a/Atlas/Shaders/2125bb80.hlsl b/Atlas/Shaders/2125bb80.hlsl new file mode 100644 index 00000000..1e598cb0 --- /dev/null +++ b/Atlas/Shaders/2125bb80.hlsl @@ -0,0 +1,76 @@ +Texture2D txDiffuse : register( t0 ); +SamplerState samLinear : register( s0 ); + +//-------------------------------------------------------------------------------------- +// Constant Buffer Variables +//-------------------------------------------------------------------------------------- + +cbuffer cbChangeOnResize : register( b0 ) +{ + matrix Projection; +}; + +cbuffer cbGeometryChangesEveryFrame : register( b1 ) +{ + matrix View; + matrix World; + float4 MeshColor; +} + +//-------------------------------------------------------------------------------------- +struct VS_INPUT +{ + float3 Position : POSITION; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; +}; + +struct PS_INPUT +{ + float4 Position : SV_POSITION; + float3 Normal : TEXCOORD0; + float2 TexCoord : TEXCOORD1; +}; + +struct PS_OUTPUT +{ + float4 RT0_Albedo : SV_TARGET0; // albedo xyz + float4 RT1_Normal : SV_TARGET1; // normal xyz +}; + +//-------------------------------------------------------------------------------------- +// Vertex Shader +//-------------------------------------------------------------------------------------- +PS_INPUT VS( VS_INPUT input ) +{ + PS_INPUT output = (PS_INPUT)0; + output.Position = mul( float4(input.Position, 1), World ); + output.Position = mul( output.Position, View ); + output.Position = mul( output.Position, Projection ); + output.Normal = mul( float4( input.Normal, 1 ), World ).xyz; + output.TexCoord = input.TexCoord; + return output; +} + + +//-------------------------------------------------------------------------------------- +// Pixel Shader +//-------------------------------------------------------------------------------------- +PS_OUTPUT PSTexture(PS_INPUT input) +{ + PS_OUTPUT output = (PS_OUTPUT)0; + output.RT0_Albedo.xyz = txDiffuse.Sample( samLinear, input.TexCoord ).xyz; + output.RT1_Normal.xyz = input.Normal.xyz; + return output; +} + +//-------------------------------------------------------------------------------------- +// PSSolid - render a solid color +//-------------------------------------------------------------------------------------- +PS_OUTPUT PSSolid(PS_INPUT input) +{ + PS_OUTPUT output = (PS_OUTPUT)0; + output.RT0_Albedo.xyz = MeshColor.xyz; + output.RT1_Normal.xyz = input.Normal.xyz; + return output; +} \ No newline at end of file diff --git a/Atlas/Shaders/Debug.hlsl b/Atlas/Shaders/Debug.hlsl new file mode 100644 index 00000000..94cf119c --- /dev/null +++ b/Atlas/Shaders/Debug.hlsl @@ -0,0 +1,58 @@ +cbuffer cb12_View : register(b12) +{ + matrix WorldToProjective; // cb12[0-3] + matrix CameraToWorld; // cb12[4-7] + float4 Target; // cb12[8], viewport dimensions 0/1 is width/height, 2/3 is 1/width and 1/height + float4 Unk09; + float4 CameraPosition; + matrix Unk11; // idk why but cb12[14].z must not be zero ever + float4 ViewMiscellaneous; // cb12[15]; cb12[10] is camera position +} + +struct VertexShaderInput +{ + float3 Position : POSITION; + float4 Colour : COLOR; +}; + +struct VertexShaderOutput +{ + float4 Position : SV_POSITION; + float4 Colour : COLOR; +}; + + +//-------------------------------------------------------------------------------------- +// Vertex Shader +//-------------------------------------------------------------------------------------- + +VertexShaderOutput VS( VertexShaderInput input) +{ + VertexShaderOutput output = (VertexShaderOutput)0; + + float4 pos = float4(input.Position.xyz, 1); + + pos = mul(WorldToProjective, pos); + + output.Position = pos; + output.Colour = input.Colour; + + + return output; +} + + +//-------------------------------------------------------------------------------------- +// Pixel Shader +//-------------------------------------------------------------------------------------- + +void PS( + VertexShaderOutput input, + out float4 o0 : SV_TARGET0, + out float4 o1 : SV_TARGET1, + out float4 o2 : SV_TARGET2) +{ + o0 = input.Colour; + o1 = float4(0, 0, 0, 0); + o2 = float4(0, 0, 0, 0); +} diff --git a/Atlas/Shaders/Lighting.hlsl b/Atlas/Shaders/Lighting.hlsl new file mode 100644 index 00000000..819a5d5d --- /dev/null +++ b/Atlas/Shaders/Lighting.hlsl @@ -0,0 +1,105 @@ +Texture2D RT0 : register( t0 ); +Texture2D RT1 : register( t1 ); +Texture2D RT2 : register( t2 ); + + +//-------------------------------------------------------------------------------------- +// Constant Buffer Variables +//-------------------------------------------------------------------------------------- + +cbuffer cbLightingChangesEveryFrame : register( b0 ) +{ + float4 LightDir[2]; + float4 LightColor[2]; +} + +//-------------------------------------------------------------------------------------- + +struct VertexShaderInput +{ + float4 Position : SV_POSITION; + float2 TextureUV : TEXCOORD0; +}; + +struct VertexShaderOutput +{ + float4 Position : SV_POSITION; + float2 TextureUV : TEXCOORD0; +}; + + +struct VertexShaderInput2 +{ + float4 Position : SV_POSITION; +}; + +struct VertexShaderOutput2 +{ + float4 Position : SV_POSITION; +}; + + + +//-------------------------------------------------------------------------------------- +// Vertex Shader +//-------------------------------------------------------------------------------------- + +VertexShaderOutput VS( VertexShaderInput input) +{ + VertexShaderOutput output = (VertexShaderOutput)0; + + output.Position = input.Position; + output.TextureUV = input.TextureUV; + + return output; +} + +VertexShaderOutput2 VS2( VertexShaderInput2 input) +{ + VertexShaderOutput2 output = (VertexShaderOutput2)0; + + output.Position = input.Position; + + return output; +} + +//-------------------------------------------------------------------------------------- +// Pixel Shader +//-------------------------------------------------------------------------------------- +float4 PS(VertexShaderOutput input) : SV_Target +{ + int3 sampleIndices = int3( input.Position.xy, 0 ); + float3 albedo = RT0.Load(sampleIndices).xyz; + float3 normal = RT1.Load(sampleIndices).xyz; + // + float4 finalColor = 0; + // + // //do NdotL lighting for 2 lights + for(int i=0; i<2; i++) + { + finalColor += saturate( dot( (float3)LightDir[i], normal) * LightColor[i] ); + } + finalColor.a = 1; + finalColor.xyz *= albedo.xyz; + // finalColor.xyz = normal.xyz; + return finalColor; +} + +//-------------------------------------------------------------------------------------- +// PSSolid - render a solid color +//-------------------------------------------------------------------------------------- +float4 PSSolid(VertexShaderOutput input) : SV_Target +{ + // return float4(input.TextureUV.x, input.TextureUV.y, 0, 1); + // return float4(1,0,1,1); + int3 sampleIndices = int3( input.Position.xy, 0 ); + float3 albedo = RT0.Load(sampleIndices).xyz; + // return float4(1,0,1,1); + return float4(albedo*4, 1); +} + +float4 PS2(VertexShaderOutput2 input) : SV_Target +{ + int3 sampleIndices = int3( input.Position.xy, 0 ); + return float4(RT0.Load(sampleIndices).xyz, 1); +} \ No newline at end of file diff --git a/Atlas/Shaders/Main.hlsl b/Atlas/Shaders/Main.hlsl new file mode 100644 index 00000000..cebaed98 --- /dev/null +++ b/Atlas/Shaders/Main.hlsl @@ -0,0 +1,173 @@ +Texture2D txDiffuse : register( t0 ); +SamplerState samLinear : register( s0 ); + +//-------------------------------------------------------------------------------------- +// Constant Buffer Variables +//-------------------------------------------------------------------------------------- + +cbuffer cbChangeOnResize : register( b0 ) +{ + matrix Projection; +}; + +cbuffer cbGeometryChangesEveryFrame : register( b1 ) +{ + matrix View; + matrix World; + float4 MeshColor; +} + +//-------------------------------------------------------------------------------------- +struct VS_INPUT +{ + float3 Position : POSITION; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; +}; + +struct VS_INPUTENTITY +{ + float4 Position : POSITION; + float4 Tangent : TANGENT; + float2 TexCoord : TEXCOORD0; +}; + +struct VS_INPUTCAR +{ + float4 Position : POSITION; + float4 Tangent : TANGENT; + float2 TexCoord : TEXCOORD0; +}; + + +struct PS_INPUTCAR +{ + float4 v0 : TEXCOORD0; + float4 v1 : TEXCOORD1; + float4 v2 : TEXCOORD2; + float4 TexCoord : TEXCOORD3; + float3 v4 : TEXCOORD4; + float4 Position : SV_POSITION0; +}; + +struct PS_INPUTPOLE +{ + float4 v0 : TEXCOORD0; + float4 v1 : TEXCOORD1; + float4 v2 : TEXCOORD2; + float4 TexCoord : TEXCOORD3; + float3 v4 : TEXCOORD4; + float4 v5 : TEXCOORD8; + float4 Position : SV_POSITION0; +}; + +struct PS_INPUT +{ + float4 Position : SV_POSITION; + float3 Normal : TEXCOORD0; + float2 TexCoord : TEXCOORD1; +}; + +struct PS_OUTPUT +{ + float4 RT0_Albedo : SV_TARGET0; // albedo xyz + float4 RT1_Normal : SV_TARGET1; // normal xyz +}; + +//-------------------------------------------------------------------------------------- +// Vertex Shader +//-------------------------------------------------------------------------------------- +PS_INPUT VS( VS_INPUT input ) +{ + PS_INPUT output = (PS_INPUT)0; + output.Position = mul( float4(input.Position, 1), World ); + output.Position = mul( output.Position, View ); + output.Position = mul( output.Position, Projection ); + output.Normal = mul( float4( input.Normal, 1 ), World ).xyz; + output.TexCoord = input.TexCoord; + return output; +} + +// first thing to try is to use real PS but fake VS + +PS_INPUT VSEntity( VS_INPUTENTITY input ) +{ + PS_INPUT output = (PS_INPUT)0; + input.Position.xyz = input.Position.xzy; + input.Position.x = -input.Position.x; + // output.Position = input.Position; + output.Position = mul( input.Position, World ); + output.Position = mul( output.Position, View ); + output.Position = mul( output.Position, Projection ); + // output.Normal = mul( input.Tangent, World ).xyz; // wrong + //output.TexCoord.x = (input.TexCoord.x * 5.56146240234375) + -3.03619384765625; + //output.TexCoord.y = (input.TexCoord.y * 5.56146240234375) - (1 - 5.20550537109375); + output.TexCoord.x = (input.TexCoord.x * 5.56146240234375) + -3.03619384765625; + output.TexCoord.y = (input.TexCoord.y * 5.56146240234375) + 5.20550537109375; + return output; +} + +PS_INPUTCAR VSCar( VS_INPUTCAR input ) +{ + PS_INPUTCAR output = (PS_INPUTCAR)0; + input.Position.xyz = input.Position.xzy; + input.Position.x = -input.Position.x; + // output.Position = input.Position; + output.Position = mul( input.Position, World ); + output.Position = mul( output.Position, View ); + output.Position = mul( output.Position, Projection ); + // output.Normal = mul( input.Tangent, World ).xyz; // wrong + //output.TexCoord.x = (input.TexCoord.x * 5.56146240234375) + -3.03619384765625; + //output.TexCoord.y = (input.TexCoord.y * 5.56146240234375) - (1 - 5.20550537109375); + output.v0 = float4(0,0,1,0); + output.v1 = float4(1,1,1,1); + output.v2 = float4(1,1,1,1); + output.v4 = float4(1,1,1,1); + output.TexCoord.x = (input.TexCoord.x * 5.56146240234375) + -3.03619384765625; + output.TexCoord.y = (input.TexCoord.y * 5.56146240234375) + 5.20550537109375; + return output; +} + +PS_INPUTPOLE VSPole( VS_INPUTCAR input ) +{ + PS_INPUTPOLE output = (PS_INPUTPOLE)0; + input.Position.xyz = input.Position.xzy; + input.Position.x = -input.Position.x; + // output.Position = input.Position; + output.Position = mul( input.Position, World ); + output.Position = mul( output.Position, View ); + output.Position = mul( output.Position, Projection ); + // output.Normal = mul( input.Tangent, World ).xyz; // wrong + //output.TexCoord.x = (input.TexCoord.x * 5.56146240234375) + -3.03619384765625; + //output.TexCoord.y = (input.TexCoord.y * 5.56146240234375) - (1 - 5.20550537109375); + output.v0 = float4(0,0,1,0); + output.v1 = float4(1,1,1,1); + output.v2 = float4(1,1,1,1); + output.v4 = float4(1,1,1,1); + output.v5 = float4(1,1,1,1); + output.TexCoord.x = (input.TexCoord.x * 9.33603668212891) + 2.78580379486084; + output.TexCoord.y = (input.TexCoord.y * 9.33603668212891) + 8.54026317596436; + return output; +} + +//-------------------------------------------------------------------------------------- +// Pixel Shader +//-------------------------------------------------------------------------------------- +PS_OUTPUT PSTexture(PS_INPUT input) +{ + PS_OUTPUT output = (PS_OUTPUT)0; + output.RT0_Albedo.xyz = txDiffuse.Sample( samLinear, input.TexCoord ).xyz; + output.RT1_Normal.xyz = input.Normal.xyz; + return output; +} + +//-------------------------------------------------------------------------------------- +// PSSolid - render a solid color +//-------------------------------------------------------------------------------------- +PS_OUTPUT PSSolid(PS_INPUT input) +{ + PS_OUTPUT output = (PS_OUTPUT)0; + output.RT0_Albedo.xyz = MeshColor.xyz; + output.RT1_Normal.xyz = input.Normal.xyz; + return output; +} \ No newline at end of file diff --git a/Atlas/SimpleMath.cpp b/Atlas/SimpleMath.cpp new file mode 100644 index 00000000..88e83a8f --- /dev/null +++ b/Atlas/SimpleMath.cpp @@ -0,0 +1,243 @@ +//------------------------------------------------------------------------------------- +// SimpleMath.cpp -- Simplified C++ Math wrapper for DirectXMath +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#include "SimpleMath.h" + +/**************************************************************************** + * + * Constants + * + ****************************************************************************/ + +namespace DirectX +{ +namespace SimpleMath +{ +const Vector2 Vector2::Zero = {0.f, 0.f}; +const Vector2 Vector2::One = {1.f, 1.f}; +const Vector2 Vector2::UnitX = {1.f, 0.f}; +const Vector2 Vector2::UnitY = {0.f, 1.f}; + +const Vector3 Vector3::Zero = {0.f, 0.f, 0.f}; +const Vector3 Vector3::One = {1.f, 1.f, 1.f}; +const Vector3 Vector3::UnitX = {1.f, 0.f, 0.f}; +const Vector3 Vector3::UnitY = {0.f, 1.f, 0.f}; +const Vector3 Vector3::UnitZ = {0.f, 0.f, 1.f}; +const Vector3 Vector3::Up = {0.f, 1.f, 0.f}; +const Vector3 Vector3::Down = {0.f, -1.f, 0.f}; +const Vector3 Vector3::Right = {1.f, 0.f, 0.f}; +const Vector3 Vector3::Left = {-1.f, 0.f, 0.f}; +const Vector3 Vector3::Forward = {0.f, 0.f, -1.f}; +const Vector3 Vector3::Backward = {0.f, 0.f, 1.f}; + +const Vector4 Vector4::Zero = {0.f, 0.f, 0.f, 0.f}; +const Vector4 Vector4::One = {1.f, 1.f, 1.f, 1.f}; +const Vector4 Vector4::UnitX = {1.f, 0.f, 0.f, 0.f}; +const Vector4 Vector4::UnitY = {0.f, 1.f, 0.f, 0.f}; +const Vector4 Vector4::UnitZ = {0.f, 0.f, 1.f, 0.f}; +const Vector4 Vector4::UnitW = {0.f, 0.f, 0.f, 1.f}; + +const Matrix Matrix::Identity = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f}; + +const Quaternion Quaternion::Identity = {0.f, 0.f, 0.f, 1.f}; +} // namespace SimpleMath +} // namespace DirectX + +using namespace DirectX; +using namespace DirectX::SimpleMath; + +/**************************************************************************** + * + * Quaternion + * + ****************************************************************************/ + +void Quaternion::RotateTowards(const Quaternion& target, float maxAngle, Quaternion& result) const noexcept +{ + const XMVECTOR T = XMLoadFloat4(this); + + // We can use the conjugate here instead of inverse assuming q1 & q2 are normalized. + const XMVECTOR R = XMQuaternionMultiply(XMQuaternionConjugate(T), target); + + const float rs = XMVectorGetW(R); + const XMVECTOR L = XMVector3Length(R); + const float angle = 2.f * atan2f(XMVectorGetX(L), rs); + if (angle > maxAngle) + { + const XMVECTOR delta = XMQuaternionRotationAxis(R, maxAngle); + const XMVECTOR Q = XMQuaternionMultiply(delta, T); + XMStoreFloat4(&result, Q); + } + else + { + // Don't overshoot. + result = target; + } +} + +void Quaternion::FromToRotation(const Vector3& fromDir, const Vector3& toDir, Quaternion& result) noexcept +{ + // Melax, "The Shortest Arc Quaternion", Game Programming Gems, Charles River Media (2000). + + const XMVECTOR F = XMVector3Normalize(fromDir); + const XMVECTOR T = XMVector3Normalize(toDir); + + const float dot = XMVectorGetX(XMVector3Dot(F, T)); + if (dot >= 1.f) + { + result = Identity; + } + else if (dot <= -1.f) + { + XMVECTOR axis = XMVector3Cross(F, Vector3::Right); + if (XMVector3NearEqual(XMVector3LengthSq(axis), g_XMZero, g_XMEpsilon)) + { + axis = XMVector3Cross(F, Vector3::Up); + } + + const XMVECTOR Q = XMQuaternionRotationAxis(axis, XM_PI); + XMStoreFloat4(&result, Q); + } + else + { + const XMVECTOR C = XMVector3Cross(F, T); + XMStoreFloat4(&result, C); + + const float s = sqrtf((1.f + dot) * 2.f); + result.x /= s; + result.y /= s; + result.z /= s; + result.w = s * 0.5f; + } +} + +void Quaternion::LookRotation(const Vector3& forward, const Vector3& up, Quaternion& result) noexcept +{ + Quaternion q1; + FromToRotation(Vector3::Forward, forward, q1); + + const XMVECTOR C = XMVector3Cross(forward, up); + if (XMVector3NearEqual(XMVector3LengthSq(C), g_XMZero, g_XMEpsilon)) + { + // forward and up are co-linear + result = q1; + return; + } + + const XMVECTOR U = XMQuaternionMultiply(q1, Vector3::Up); + + Quaternion q2; + FromToRotation(U, up, q2); + + XMStoreFloat4(&result, XMQuaternionMultiply(q2, q1)); +} + +/**************************************************************************** + * + * Viewport + * + ****************************************************************************/ + +#if defined(__d3d11_h__) || defined(__d3d11_x_h__) +static_assert(sizeof(DirectX::SimpleMath::Viewport) == sizeof(D3D11_VIEWPORT), "Size mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, x) == offsetof(D3D11_VIEWPORT, TopLeftX), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, y) == offsetof(D3D11_VIEWPORT, TopLeftY), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, width) == offsetof(D3D11_VIEWPORT, Width), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, height) == offsetof(D3D11_VIEWPORT, Height), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, minDepth) == offsetof(D3D11_VIEWPORT, MinDepth), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, maxDepth) == offsetof(D3D11_VIEWPORT, MaxDepth), "Layout mismatch"); +#endif + +#if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) +static_assert(sizeof(DirectX::SimpleMath::Viewport) == sizeof(D3D12_VIEWPORT), "Size mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, x) == offsetof(D3D12_VIEWPORT, TopLeftX), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, y) == offsetof(D3D12_VIEWPORT, TopLeftY), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, width) == offsetof(D3D12_VIEWPORT, Width), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, height) == offsetof(D3D12_VIEWPORT, Height), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, minDepth) == offsetof(D3D12_VIEWPORT, MinDepth), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, maxDepth) == offsetof(D3D12_VIEWPORT, MaxDepth), "Layout mismatch"); +#endif + +#if defined(__dxgi1_2_h__) || defined(__d3d11_x_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) +RECT Viewport::ComputeDisplayArea( + DXGI_SCALING scaling, UINT backBufferWidth, UINT backBufferHeight, int outputWidth, int outputHeight) noexcept +{ + RECT rct = {}; + + switch (int(scaling)) + { + case DXGI_SCALING_STRETCH: + // Output fills the entire window area + rct.top = 0; + rct.left = 0; + rct.right = outputWidth; + rct.bottom = outputHeight; + break; + + case 2 /*DXGI_SCALING_ASPECT_RATIO_STRETCH*/: + // Output fills the window area but respects the original aspect ratio, using pillar boxing or letter boxing as required + // Note: This scaling option is not supported for legacy Win32 windows swap chains + { + assert(backBufferHeight > 0); + const float aspectRatio = float(backBufferWidth) / float(backBufferHeight); + + // Horizontal fill + float scaledWidth = float(outputWidth); + float scaledHeight = float(outputWidth) / aspectRatio; + if (scaledHeight >= float(outputHeight)) + { + // Do vertical fill + scaledWidth = float(outputHeight) * aspectRatio; + scaledHeight = float(outputHeight); + } + + const float offsetX = (float(outputWidth) - scaledWidth) * 0.5f; + const float offsetY = (float(outputHeight) - scaledHeight) * 0.5f; + + rct.left = static_cast(offsetX); + rct.top = static_cast(offsetY); + rct.right = static_cast(offsetX + scaledWidth); + rct.bottom = static_cast(offsetY + scaledHeight); + + // Clip to display window + rct.left = std::max(0, rct.left); + rct.top = std::max(0, rct.top); + rct.right = std::min(outputWidth, rct.right); + rct.bottom = std::min(outputHeight, rct.bottom); + } + break; + + case DXGI_SCALING_NONE: + default: + // Output is displayed in the upper left corner of the window area + rct.top = 0; + rct.left = 0; + rct.right = std::min(static_cast(backBufferWidth), outputWidth); + rct.bottom = std::min(static_cast(backBufferHeight), outputHeight); + break; + } + + return rct; +} +#endif + +RECT Viewport::ComputeTitleSafeArea(UINT backBufferWidth, UINT backBufferHeight) noexcept +{ + const float safew = (float(backBufferWidth) + 19.f) / 20.f; + const float safeh = (float(backBufferHeight) + 19.f) / 20.f; + + RECT rct; + rct.left = static_cast(safew); + rct.top = static_cast(safeh); + rct.right = static_cast(float(backBufferWidth) - safew + 0.5f); + rct.bottom = static_cast(float(backBufferHeight) - safeh + 0.5f); + + return rct; +} diff --git a/Atlas/SimpleMath.h b/Atlas/SimpleMath.h new file mode 100644 index 00000000..41f350e4 --- /dev/null +++ b/Atlas/SimpleMath.h @@ -0,0 +1,1331 @@ +//------------------------------------------------------------------------------------- +// SimpleMath.h -- Simplified C++ Math wrapper for DirectXMath +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#pragma once + +#if (defined(_WIN32) || defined(WINAPI_FAMILY)) && !(defined(_XBOX_ONE) && defined(_TITLE)) && !defined(_GAMING_XBOX) +#include +#endif + +#include +#include +#include +#include + +#if (__cplusplus >= 202002L) +#include +#endif + +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfloat-equal" +#endif + +namespace DirectX +{ +namespace SimpleMath +{ +struct Vector2; +struct Vector4; +struct Matrix; +struct Quaternion; +struct Plane; + +//------------------------------------------------------------------------------ +// 2D rectangle +struct Rectangle +{ + long x; + long y; + long width; + long height; + + // Creators + Rectangle() noexcept : x(0), y(0), width(0), height(0) {} + constexpr Rectangle(long ix, long iy, long iw, long ih) noexcept : x(ix), y(iy), width(iw), height(ih) {} + explicit Rectangle(const RECT& rct) noexcept : x(rct.left), y(rct.top), width(rct.right - rct.left), height(rct.bottom - rct.top) {} + + Rectangle(const Rectangle&) = default; + Rectangle& operator=(const Rectangle&) = default; + + Rectangle(Rectangle&&) = default; + Rectangle& operator=(Rectangle&&) = default; + + operator RECT() noexcept + { + RECT rct; + rct.left = x; + rct.top = y; + rct.right = (x + width); + rct.bottom = (y + height); + return rct; + } +#ifdef __cplusplus_winrt + operator Windows::Foundation::Rect() noexcept { return Windows::Foundation::Rect(float(x), float(y), float(width), float(height)); } +#endif + + // Comparison operators +#if (__cplusplus >= 202002L) + bool operator==(const Rectangle&) const = default; + auto operator<=>(const Rectangle&) const = default; +#else + bool operator==(const Rectangle& r) const noexcept { return (x == r.x) && (y == r.y) && (width == r.width) && (height == r.height); } + bool operator!=(const Rectangle& r) const noexcept { return (x != r.x) || (y != r.y) || (width != r.width) || (height != r.height); } +#endif + bool operator==(const RECT& rct) const noexcept + { + return (x == rct.left) && (y == rct.top) && (width == (rct.right - rct.left)) && (height == (rct.bottom - rct.top)); + } + bool operator!=(const RECT& rct) const noexcept + { + return (x != rct.left) || (y != rct.top) || (width != (rct.right - rct.left)) || (height != (rct.bottom - rct.top)); + } + + // Assignment operators + Rectangle& operator=(_In_ const RECT& rct) noexcept + { + x = rct.left; + y = rct.top; + width = (rct.right - rct.left); + height = (rct.bottom - rct.top); + return *this; + } + + // Rectangle operations + Vector2 Location() const noexcept; + Vector2 Center() const noexcept; + + bool IsEmpty() const noexcept { return (width == 0 && height == 0 && x == 0 && y == 0); } + + bool Contains(long ix, long iy) const noexcept { return (x <= ix) && (ix < (x + width)) && (y <= iy) && (iy < (y + height)); } + bool Contains(const Vector2& point) const noexcept; + bool Contains(const Rectangle& r) const noexcept + { + return (x <= r.x) && ((r.x + r.width) <= (x + width)) && (y <= r.y) && ((r.y + r.height) <= (y + height)); + } + bool Contains(const RECT& rct) const noexcept + { + return (x <= rct.left) && (rct.right <= (x + width)) && (y <= rct.top) && (rct.bottom <= (y + height)); + } + + void Inflate(long horizAmount, long vertAmount) noexcept; + + bool Intersects(const Rectangle& r) const noexcept + { + return (r.x < (x + width)) && (x < (r.x + r.width)) && (r.y < (y + height)) && (y < (r.y + r.height)); + } + bool Intersects(const RECT& rct) const noexcept + { + return (rct.left < (x + width)) && (x < rct.right) && (rct.top < (y + height)) && (y < rct.bottom); + } + + void Offset(long ox, long oy) noexcept + { + x += ox; + y += oy; + } + + // Static functions + static Rectangle Intersect(const Rectangle& ra, const Rectangle& rb) noexcept; + static RECT Intersect(const RECT& rcta, const RECT& rctb) noexcept; + + static Rectangle Union(const Rectangle& ra, const Rectangle& rb) noexcept; + static RECT Union(const RECT& rcta, const RECT& rctb) noexcept; +}; + +//------------------------------------------------------------------------------ +// 2D vector +struct Vector2 : public XMFLOAT2 +{ + Vector2() noexcept : XMFLOAT2(0.f, 0.f) {} + constexpr explicit Vector2(float ix) noexcept : XMFLOAT2(ix, ix) {} + constexpr Vector2(float ix, float iy) noexcept : XMFLOAT2(ix, iy) {} + explicit Vector2(_In_reads_(2) const float* pArray) noexcept : XMFLOAT2(pArray) {} + Vector2(FXMVECTOR V) noexcept { XMStoreFloat2(this, V); } + Vector2(const XMFLOAT2& V) noexcept + { + this->x = V.x; + this->y = V.y; + } + explicit Vector2(const XMVECTORF32& F) noexcept + { + this->x = F.f[0]; + this->y = F.f[1]; + } + + Vector2(const Vector2&) = default; + Vector2& operator=(const Vector2&) = default; + + Vector2(Vector2&&) = default; + Vector2& operator=(Vector2&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat2(this); } + + // Comparison operators + bool operator==(const Vector2& V) const noexcept; + bool operator!=(const Vector2& V) const noexcept; + + // Assignment operators + Vector2& operator=(const XMVECTORF32& F) noexcept + { + x = F.f[0]; + y = F.f[1]; + return *this; + } + Vector2& operator+=(const Vector2& V) noexcept; + Vector2& operator-=(const Vector2& V) noexcept; + Vector2& operator*=(const Vector2& V) noexcept; + Vector2& operator*=(float S) noexcept; + Vector2& operator/=(float S) noexcept; + + // Unary operators + Vector2 operator+() const noexcept { return *this; } + Vector2 operator-() const noexcept { return Vector2(-x, -y); } + + // Vector operations + bool InBounds(const Vector2& Bounds) const noexcept; + + float Length() const noexcept; + float LengthSquared() const noexcept; + + float Dot(const Vector2& V) const noexcept; + void Cross(const Vector2& V, Vector2& result) const noexcept; + Vector2 Cross(const Vector2& V) const noexcept; + + void Normalize() noexcept; + void Normalize(Vector2& result) const noexcept; + + void Clamp(const Vector2& vmin, const Vector2& vmax) noexcept; + void Clamp(const Vector2& vmin, const Vector2& vmax, Vector2& result) const noexcept; + + // Static functions + static float Distance(const Vector2& v1, const Vector2& v2) noexcept; + static float DistanceSquared(const Vector2& v1, const Vector2& v2) noexcept; + + static void Min(const Vector2& v1, const Vector2& v2, Vector2& result) noexcept; + static Vector2 Min(const Vector2& v1, const Vector2& v2) noexcept; + + static void Max(const Vector2& v1, const Vector2& v2, Vector2& result) noexcept; + static Vector2 Max(const Vector2& v1, const Vector2& v2) noexcept; + + static void Lerp(const Vector2& v1, const Vector2& v2, float t, Vector2& result) noexcept; + static Vector2 Lerp(const Vector2& v1, const Vector2& v2, float t) noexcept; + + static void SmoothStep(const Vector2& v1, const Vector2& v2, float t, Vector2& result) noexcept; + static Vector2 SmoothStep(const Vector2& v1, const Vector2& v2, float t) noexcept; + + static void Barycentric(const Vector2& v1, const Vector2& v2, const Vector2& v3, float f, float g, Vector2& result) noexcept; + static Vector2 Barycentric(const Vector2& v1, const Vector2& v2, const Vector2& v3, float f, float g) noexcept; + + static void CatmullRom(const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float t, Vector2& result) noexcept; + static Vector2 CatmullRom(const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float t) noexcept; + + static void Hermite(const Vector2& v1, const Vector2& t1, const Vector2& v2, const Vector2& t2, float t, Vector2& result) noexcept; + static Vector2 Hermite(const Vector2& v1, const Vector2& t1, const Vector2& v2, const Vector2& t2, float t) noexcept; + + static void Reflect(const Vector2& ivec, const Vector2& nvec, Vector2& result) noexcept; + static Vector2 Reflect(const Vector2& ivec, const Vector2& nvec) noexcept; + + static void Refract(const Vector2& ivec, const Vector2& nvec, float refractionIndex, Vector2& result) noexcept; + static Vector2 Refract(const Vector2& ivec, const Vector2& nvec, float refractionIndex) noexcept; + + static void Transform(const Vector2& v, const Quaternion& quat, Vector2& result) noexcept; + static Vector2 Transform(const Vector2& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector2& v, const Matrix& m, Vector2& result) noexcept; + static Vector2 Transform(const Vector2& v, const Matrix& m) noexcept; + static void Transform( + _In_reads_(count) const Vector2* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector2* resultArray) noexcept; + + static void Transform(const Vector2& v, const Matrix& m, Vector4& result) noexcept; + static void Transform( + _In_reads_(count) const Vector2* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector4* resultArray) noexcept; + + static void TransformNormal(const Vector2& v, const Matrix& m, Vector2& result) noexcept; + static Vector2 TransformNormal(const Vector2& v, const Matrix& m) noexcept; + static void TransformNormal( + _In_reads_(count) const Vector2* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector2* resultArray) noexcept; + + // Constants + static const Vector2 Zero; + static const Vector2 One; + static const Vector2 UnitX; + static const Vector2 UnitY; +}; + +// Binary operators +Vector2 operator+(const Vector2& V1, const Vector2& V2) noexcept; +Vector2 operator-(const Vector2& V1, const Vector2& V2) noexcept; +Vector2 operator*(const Vector2& V1, const Vector2& V2) noexcept; +Vector2 operator*(const Vector2& V, float S) noexcept; +Vector2 operator/(const Vector2& V1, const Vector2& V2) noexcept; +Vector2 operator/(const Vector2& V, float S) noexcept; +Vector2 operator*(float S, const Vector2& V) noexcept; + +//------------------------------------------------------------------------------ +// 3D vector +struct Vector3 : public XMFLOAT3 +{ + Vector3() noexcept : XMFLOAT3(0.f, 0.f, 0.f) {} + constexpr explicit Vector3(float ix) noexcept : XMFLOAT3(ix, ix, ix) {} + constexpr Vector3(float ix, float iy, float iz) noexcept : XMFLOAT3(ix, iy, iz) {} + explicit Vector3(_In_reads_(3) const float* pArray) noexcept : XMFLOAT3(pArray) {} + Vector3(FXMVECTOR V) noexcept { XMStoreFloat3(this, V); } + Vector3(const XMFLOAT3& V) noexcept + { + this->x = V.x; + this->y = V.y; + this->z = V.z; + } + explicit Vector3(const XMVECTORF32& F) noexcept + { + this->x = F.f[0]; + this->y = F.f[1]; + this->z = F.f[2]; + } + + Vector3(const Vector3&) = default; + Vector3& operator=(const Vector3&) = default; + + Vector3(Vector3&&) = default; + Vector3& operator=(Vector3&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat3(this); } + + // Comparison operators + bool operator==(const Vector3& V) const noexcept; + bool operator!=(const Vector3& V) const noexcept; + + // Assignment operators + Vector3& operator=(const XMVECTORF32& F) noexcept + { + x = F.f[0]; + y = F.f[1]; + z = F.f[2]; + return *this; + } + Vector3& operator+=(const Vector3& V) noexcept; + Vector3& operator-=(const Vector3& V) noexcept; + Vector3& operator*=(const Vector3& V) noexcept; + Vector3& operator*=(float S) noexcept; + Vector3& operator/=(float S) noexcept; + + // Unary operators + Vector3 operator+() const noexcept { return *this; } + Vector3 operator-() const noexcept; + + // Vector operations + bool InBounds(const Vector3& Bounds) const noexcept; + + float Length() const noexcept; + float LengthSquared() const noexcept; + + float Dot(const Vector3& V) const noexcept; + void Cross(const Vector3& V, Vector3& result) const noexcept; + Vector3 Cross(const Vector3& V) const noexcept; + + void Normalize() noexcept; + void Normalize(Vector3& result) const noexcept; + + void Clamp(const Vector3& vmin, const Vector3& vmax) noexcept; + void Clamp(const Vector3& vmin, const Vector3& vmax, Vector3& result) const noexcept; + + // Static functions + static float Distance(const Vector3& v1, const Vector3& v2) noexcept; + static float DistanceSquared(const Vector3& v1, const Vector3& v2) noexcept; + + static void Min(const Vector3& v1, const Vector3& v2, Vector3& result) noexcept; + static Vector3 Min(const Vector3& v1, const Vector3& v2) noexcept; + + static void Max(const Vector3& v1, const Vector3& v2, Vector3& result) noexcept; + static Vector3 Max(const Vector3& v1, const Vector3& v2) noexcept; + + static void Lerp(const Vector3& v1, const Vector3& v2, float t, Vector3& result) noexcept; + static Vector3 Lerp(const Vector3& v1, const Vector3& v2, float t) noexcept; + + static void SmoothStep(const Vector3& v1, const Vector3& v2, float t, Vector3& result) noexcept; + static Vector3 SmoothStep(const Vector3& v1, const Vector3& v2, float t) noexcept; + + static void Barycentric(const Vector3& v1, const Vector3& v2, const Vector3& v3, float f, float g, Vector3& result) noexcept; + static Vector3 Barycentric(const Vector3& v1, const Vector3& v2, const Vector3& v3, float f, float g) noexcept; + + static void CatmullRom(const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& v4, float t, Vector3& result) noexcept; + static Vector3 CatmullRom(const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& v4, float t) noexcept; + + static void Hermite(const Vector3& v1, const Vector3& t1, const Vector3& v2, const Vector3& t2, float t, Vector3& result) noexcept; + static Vector3 Hermite(const Vector3& v1, const Vector3& t1, const Vector3& v2, const Vector3& t2, float t) noexcept; + + static void Reflect(const Vector3& ivec, const Vector3& nvec, Vector3& result) noexcept; + static Vector3 Reflect(const Vector3& ivec, const Vector3& nvec) noexcept; + + static void Refract(const Vector3& ivec, const Vector3& nvec, float refractionIndex, Vector3& result) noexcept; + static Vector3 Refract(const Vector3& ivec, const Vector3& nvec, float refractionIndex) noexcept; + + static void Transform(const Vector3& v, const Quaternion& quat, Vector3& result) noexcept; + static Vector3 Transform(const Vector3& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector3& v, const Matrix& m, Vector3& result) noexcept; + static Vector3 Transform(const Vector3& v, const Matrix& m) noexcept; + static void Transform( + _In_reads_(count) const Vector3* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector3* resultArray) noexcept; + + static void Transform(const Vector3& v, const Matrix& m, Vector4& result) noexcept; + static void Transform( + _In_reads_(count) const Vector3* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector4* resultArray) noexcept; + + static void TransformNormal(const Vector3& v, const Matrix& m, Vector3& result) noexcept; + static Vector3 TransformNormal(const Vector3& v, const Matrix& m) noexcept; + static void TransformNormal( + _In_reads_(count) const Vector3* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector3* resultArray) noexcept; + + // Constants + static const Vector3 Zero; + static const Vector3 One; + static const Vector3 UnitX; + static const Vector3 UnitY; + static const Vector3 UnitZ; + static const Vector3 Up; + static const Vector3 Down; + static const Vector3 Right; + static const Vector3 Left; + static const Vector3 Forward; + static const Vector3 Backward; +}; + +// Binary operators +Vector3 operator+(const Vector3& V1, const Vector3& V2) noexcept; +Vector3 operator-(const Vector3& V1, const Vector3& V2) noexcept; +Vector3 operator*(const Vector3& V1, const Vector3& V2) noexcept; +Vector3 operator*(const Vector3& V, float S) noexcept; +Vector3 operator/(const Vector3& V1, const Vector3& V2) noexcept; +Vector3 operator/(const Vector3& V, float S) noexcept; +Vector3 operator*(float S, const Vector3& V) noexcept; + +//------------------------------------------------------------------------------ +// 4D vector +struct Vector4 : public XMFLOAT4 +{ + Vector4() noexcept : XMFLOAT4(0.f, 0.f, 0.f, 0.f) {} + constexpr explicit Vector4(float ix) noexcept : XMFLOAT4(ix, ix, ix, ix) {} + constexpr Vector4(float ix, float iy, float iz, float iw) noexcept : XMFLOAT4(ix, iy, iz, iw) {} + explicit Vector4(_In_reads_(4) const float* pArray) noexcept : XMFLOAT4(pArray) {} + Vector4(FXMVECTOR V) noexcept { XMStoreFloat4(this, V); } + Vector4(const XMFLOAT4& V) noexcept + { + this->x = V.x; + this->y = V.y; + this->z = V.z; + this->w = V.w; + } + explicit Vector4(const XMVECTORF32& F) noexcept + { + this->x = F.f[0]; + this->y = F.f[1]; + this->z = F.f[2]; + this->w = F.f[3]; + } + + Vector4(const Vector4&) = default; + Vector4& operator=(const Vector4&) = default; + + Vector4(Vector4&&) = default; + Vector4& operator=(Vector4&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat4(this); } + + // Comparison operators + bool operator==(const Vector4& V) const noexcept; + bool operator!=(const Vector4& V) const noexcept; + + // Assignment operators + Vector4& operator=(const XMVECTORF32& F) noexcept + { + x = F.f[0]; + y = F.f[1]; + z = F.f[2]; + w = F.f[3]; + return *this; + } + Vector4& operator+=(const Vector4& V) noexcept; + Vector4& operator-=(const Vector4& V) noexcept; + Vector4& operator*=(const Vector4& V) noexcept; + Vector4& operator*=(float S) noexcept; + Vector4& operator/=(float S) noexcept; + + // Unary operators + Vector4 operator+() const noexcept { return *this; } + Vector4 operator-() const noexcept; + + // Vector operations + bool InBounds(const Vector4& Bounds) const noexcept; + + float Length() const noexcept; + float LengthSquared() const noexcept; + + float Dot(const Vector4& V) const noexcept; + void Cross(const Vector4& v1, const Vector4& v2, Vector4& result) const noexcept; + Vector4 Cross(const Vector4& v1, const Vector4& v2) const noexcept; + + void Normalize() noexcept; + void Normalize(Vector4& result) const noexcept; + + void Clamp(const Vector4& vmin, const Vector4& vmax) noexcept; + void Clamp(const Vector4& vmin, const Vector4& vmax, Vector4& result) const noexcept; + + // Static functions + static float Distance(const Vector4& v1, const Vector4& v2) noexcept; + static float DistanceSquared(const Vector4& v1, const Vector4& v2) noexcept; + + static void Min(const Vector4& v1, const Vector4& v2, Vector4& result) noexcept; + static Vector4 Min(const Vector4& v1, const Vector4& v2) noexcept; + + static void Max(const Vector4& v1, const Vector4& v2, Vector4& result) noexcept; + static Vector4 Max(const Vector4& v1, const Vector4& v2) noexcept; + + static void Lerp(const Vector4& v1, const Vector4& v2, float t, Vector4& result) noexcept; + static Vector4 Lerp(const Vector4& v1, const Vector4& v2, float t) noexcept; + + static void SmoothStep(const Vector4& v1, const Vector4& v2, float t, Vector4& result) noexcept; + static Vector4 SmoothStep(const Vector4& v1, const Vector4& v2, float t) noexcept; + + static void Barycentric(const Vector4& v1, const Vector4& v2, const Vector4& v3, float f, float g, Vector4& result) noexcept; + static Vector4 Barycentric(const Vector4& v1, const Vector4& v2, const Vector4& v3, float f, float g) noexcept; + + static void CatmullRom(const Vector4& v1, const Vector4& v2, const Vector4& v3, const Vector4& v4, float t, Vector4& result) noexcept; + static Vector4 CatmullRom(const Vector4& v1, const Vector4& v2, const Vector4& v3, const Vector4& v4, float t) noexcept; + + static void Hermite(const Vector4& v1, const Vector4& t1, const Vector4& v2, const Vector4& t2, float t, Vector4& result) noexcept; + static Vector4 Hermite(const Vector4& v1, const Vector4& t1, const Vector4& v2, const Vector4& t2, float t) noexcept; + + static void Reflect(const Vector4& ivec, const Vector4& nvec, Vector4& result) noexcept; + static Vector4 Reflect(const Vector4& ivec, const Vector4& nvec) noexcept; + + static void Refract(const Vector4& ivec, const Vector4& nvec, float refractionIndex, Vector4& result) noexcept; + static Vector4 Refract(const Vector4& ivec, const Vector4& nvec, float refractionIndex) noexcept; + + static void Transform(const Vector2& v, const Quaternion& quat, Vector4& result) noexcept; + static Vector4 Transform(const Vector2& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector3& v, const Quaternion& quat, Vector4& result) noexcept; + static Vector4 Transform(const Vector3& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector4& v, const Quaternion& quat, Vector4& result) noexcept; + static Vector4 Transform(const Vector4& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector4& v, const Matrix& m, Vector4& result) noexcept; + static Vector4 Transform(const Vector4& v, const Matrix& m) noexcept; + static void Transform( + _In_reads_(count) const Vector4* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector4* resultArray) noexcept; + + // Constants + static const Vector4 Zero; + static const Vector4 One; + static const Vector4 UnitX; + static const Vector4 UnitY; + static const Vector4 UnitZ; + static const Vector4 UnitW; +}; + +// Binary operators +Vector4 operator+(const Vector4& V1, const Vector4& V2) noexcept; +Vector4 operator-(const Vector4& V1, const Vector4& V2) noexcept; +Vector4 operator*(const Vector4& V1, const Vector4& V2) noexcept; +Vector4 operator*(const Vector4& V, float S) noexcept; +Vector4 operator/(const Vector4& V1, const Vector4& V2) noexcept; +Vector4 operator/(const Vector4& V, float S) noexcept; +Vector4 operator*(float S, const Vector4& V) noexcept; + +//------------------------------------------------------------------------------ +// 4x4 Matrix (assumes right-handed cooordinates) +struct Matrix : public XMFLOAT4X4 +{ + Matrix() noexcept : XMFLOAT4X4(1.f, 0, 0, 0, 0, 1.f, 0, 0, 0, 0, 1.f, 0, 0, 0, 0, 1.f) {} + constexpr Matrix(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, + float m22, float m23, float m30, float m31, float m32, float m33) noexcept + : XMFLOAT4X4(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) + { + } + explicit Matrix(const Vector3& r0, const Vector3& r1, const Vector3& r2) noexcept + : XMFLOAT4X4(r0.x, r0.y, r0.z, 0, r1.x, r1.y, r1.z, 0, r2.x, r2.y, r2.z, 0, 0, 0, 0, 1.f) + { + } + explicit Matrix(const Vector4& r0, const Vector4& r1, const Vector4& r2, const Vector4& r3) noexcept + : XMFLOAT4X4(r0.x, r0.y, r0.z, r0.w, r1.x, r1.y, r1.z, r1.w, r2.x, r2.y, r2.z, r2.w, r3.x, r3.y, r3.z, r3.w) + { + } + Matrix(const XMFLOAT4X4& M) noexcept { memcpy(this, &M, sizeof(XMFLOAT4X4)); } + Matrix(const XMFLOAT3X3& M) noexcept; + Matrix(const XMFLOAT4X3& M) noexcept; + + explicit Matrix(_In_reads_(16) const float* pArray) noexcept : XMFLOAT4X4(pArray) {} + Matrix(CXMMATRIX M) noexcept { XMStoreFloat4x4(this, M); } + + Matrix(const Matrix&) = default; + Matrix& operator=(const Matrix&) = default; + + Matrix(Matrix&&) = default; + Matrix& operator=(Matrix&&) = default; + + operator XMMATRIX() const noexcept { return XMLoadFloat4x4(this); } + + // Comparison operators + bool operator==(const Matrix& M) const noexcept; + bool operator!=(const Matrix& M) const noexcept; + + // Assignment operators + Matrix& operator=(const XMFLOAT3X3& M) noexcept; + Matrix& operator=(const XMFLOAT4X3& M) noexcept; + Matrix& operator+=(const Matrix& M) noexcept; + Matrix& operator-=(const Matrix& M) noexcept; + Matrix& operator*=(const Matrix& M) noexcept; + Matrix& operator*=(float S) noexcept; + Matrix& operator/=(float S) noexcept; + + Matrix& operator/=(const Matrix& M) noexcept; + // Element-wise divide + + // Unary operators + Matrix operator+() const noexcept { return *this; } + Matrix operator-() const noexcept; + + // Properties + Vector3 Up() const noexcept { return Vector3(_21, _22, _23); } + void Up(const Vector3& v) noexcept + { + _21 = v.x; + _22 = v.y; + _23 = v.z; + } + + Vector3 Down() const noexcept { return Vector3(-_21, -_22, -_23); } + void Down(const Vector3& v) noexcept + { + _21 = -v.x; + _22 = -v.y; + _23 = -v.z; + } + + Vector3 Right() const noexcept { return Vector3(_11, _12, _13); } + void Right(const Vector3& v) noexcept + { + _11 = v.x; + _12 = v.y; + _13 = v.z; + } + + Vector3 Left() const noexcept { return Vector3(-_11, -_12, -_13); } + void Left(const Vector3& v) noexcept + { + _11 = -v.x; + _12 = -v.y; + _13 = -v.z; + } + + Vector3 Forward() const noexcept { return Vector3(-_31, -_32, -_33); } + void Forward(const Vector3& v) noexcept + { + _31 = -v.x; + _32 = -v.y; + _33 = -v.z; + } + + Vector3 Backward() const noexcept { return Vector3(_31, _32, _33); } + void Backward(const Vector3& v) noexcept + { + _31 = v.x; + _32 = v.y; + _33 = v.z; + } + + Vector3 Translation() const noexcept { return Vector3(_41, _42, _43); } + void Translation(const Vector3& v) noexcept + { + _41 = v.x; + _42 = v.y; + _43 = v.z; + } + + // Matrix operations + bool Decompose(Vector3& scale, Quaternion& rotation, Vector3& translation) noexcept; + + Matrix Transpose() const noexcept; + void Transpose(Matrix& result) const noexcept; + + Matrix Invert() const noexcept; + void Invert(Matrix& result) const noexcept; + + float Determinant() const noexcept; + + // Computes rotation about y-axis (y), then x-axis (x), then z-axis (z) + Vector3 ToEuler() const noexcept; + + // Static functions + static Matrix CreateBillboard(const Vector3& object, const Vector3& cameraPosition, const Vector3& cameraUp, + _In_opt_ const Vector3* cameraForward = nullptr) noexcept; + + static Matrix CreateConstrainedBillboard(const Vector3& object, const Vector3& cameraPosition, const Vector3& rotateAxis, + _In_opt_ const Vector3* cameraForward = nullptr, _In_opt_ const Vector3* objectForward = nullptr) noexcept; + + static Matrix CreateTranslation(const Vector3& position) noexcept; + static Matrix CreateTranslation(float x, float y, float z) noexcept; + + static Matrix CreateScale(const Vector3& scales) noexcept; + static Matrix CreateScale(float xs, float ys, float zs) noexcept; + static Matrix CreateScale(float scale) noexcept; + + static Matrix CreateRotationX(float radians) noexcept; + static Matrix CreateRotationY(float radians) noexcept; + static Matrix CreateRotationZ(float radians) noexcept; + + static Matrix CreateFromAxisAngle(const Vector3& axis, float angle) noexcept; + + static Matrix CreatePerspectiveFieldOfView(float fov, float aspectRatio, float nearPlane, float farPlane) noexcept; + static Matrix CreatePerspective(float width, float height, float nearPlane, float farPlane) noexcept; + static Matrix CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlane, float farPlane) noexcept; + static Matrix CreateOrthographic(float width, float height, float zNearPlane, float zFarPlane) noexcept; + static Matrix CreateOrthographicOffCenter(float left, float right, float bottom, float top, float zNearPlane, float zFarPlane) noexcept; + + static Matrix CreateLookAt(const Vector3& position, const Vector3& target, const Vector3& up) noexcept; + static Matrix CreateWorld(const Vector3& position, const Vector3& forward, const Vector3& up) noexcept; + + static Matrix CreateFromQuaternion(const Quaternion& quat) noexcept; + + // Rotates about y-axis (yaw), then x-axis (pitch), then z-axis (roll) + static Matrix CreateFromYawPitchRoll(float yaw, float pitch, float roll) noexcept; + + // Rotates about y-axis (angles.y), then x-axis (angles.x), then z-axis (angles.z) + static Matrix CreateFromYawPitchRoll(const Vector3& angles) noexcept; + + static Matrix CreateShadow(const Vector3& lightDir, const Plane& plane) noexcept; + + static Matrix CreateReflection(const Plane& plane) noexcept; + + static void Lerp(const Matrix& M1, const Matrix& M2, float t, Matrix& result) noexcept; + static Matrix Lerp(const Matrix& M1, const Matrix& M2, float t) noexcept; + + static void Transform(const Matrix& M, const Quaternion& rotation, Matrix& result) noexcept; + static Matrix Transform(const Matrix& M, const Quaternion& rotation) noexcept; + + // Constants + static const Matrix Identity; +}; + +// Binary operators +Matrix operator+(const Matrix& M1, const Matrix& M2) noexcept; +Matrix operator-(const Matrix& M1, const Matrix& M2) noexcept; +Matrix operator*(const Matrix& M1, const Matrix& M2) noexcept; +Matrix operator*(const Matrix& M, float S) noexcept; +Matrix operator/(const Matrix& M, float S) noexcept; +Matrix operator/(const Matrix& M1, const Matrix& M2) noexcept; +// Element-wise divide +Matrix operator*(float S, const Matrix& M) noexcept; + +//----------------------------------------------------------------------------- +// Plane +struct Plane : public XMFLOAT4 +{ + Plane() noexcept : XMFLOAT4(0.f, 1.f, 0.f, 0.f) {} + constexpr Plane(float ix, float iy, float iz, float iw) noexcept : XMFLOAT4(ix, iy, iz, iw) {} + Plane(const Vector3& normal, float d) noexcept : XMFLOAT4(normal.x, normal.y, normal.z, d) {} + Plane(const Vector3& point1, const Vector3& point2, const Vector3& point3) noexcept; + Plane(const Vector3& point, const Vector3& normal) noexcept; + explicit Plane(const Vector4& v) noexcept : XMFLOAT4(v.x, v.y, v.z, v.w) {} + explicit Plane(_In_reads_(4) const float* pArray) noexcept : XMFLOAT4(pArray) {} + Plane(FXMVECTOR V) noexcept { XMStoreFloat4(this, V); } + Plane(const XMFLOAT4& p) noexcept + { + this->x = p.x; + this->y = p.y; + this->z = p.z; + this->w = p.w; + } + explicit Plane(const XMVECTORF32& F) noexcept + { + this->x = F.f[0]; + this->y = F.f[1]; + this->z = F.f[2]; + this->w = F.f[3]; + } + + Plane(const Plane&) = default; + Plane& operator=(const Plane&) = default; + + Plane(Plane&&) = default; + Plane& operator=(Plane&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat4(this); } + + // Comparison operators + bool operator==(const Plane& p) const noexcept; + bool operator!=(const Plane& p) const noexcept; + + // Assignment operators + Plane& operator=(const XMVECTORF32& F) noexcept + { + x = F.f[0]; + y = F.f[1]; + z = F.f[2]; + w = F.f[3]; + return *this; + } + + // Properties + Vector3 Normal() const noexcept { return Vector3(x, y, z); } + void Normal(const Vector3& normal) noexcept + { + x = normal.x; + y = normal.y; + z = normal.z; + } + + float D() const noexcept { return w; } + void D(float d) noexcept { w = d; } + + // Plane operations + void Normalize() noexcept; + void Normalize(Plane& result) const noexcept; + + float Dot(const Vector4& v) const noexcept; + float DotCoordinate(const Vector3& position) const noexcept; + float DotNormal(const Vector3& normal) const noexcept; + + // Static functions + static void Transform(const Plane& plane, const Matrix& M, Plane& result) noexcept; + static Plane Transform(const Plane& plane, const Matrix& M) noexcept; + + static void Transform(const Plane& plane, const Quaternion& rotation, Plane& result) noexcept; + static Plane Transform(const Plane& plane, const Quaternion& rotation) noexcept; + // Input quaternion must be the inverse transpose of the transformation +}; + +//------------------------------------------------------------------------------ +// Quaternion +struct Quaternion : public XMFLOAT4 +{ + Quaternion() noexcept : XMFLOAT4(0, 0, 0, 1.f) {} + constexpr Quaternion(float ix, float iy, float iz, float iw) noexcept : XMFLOAT4(ix, iy, iz, iw) {} + Quaternion(const Vector3& v, float scalar) noexcept : XMFLOAT4(v.x, v.y, v.z, scalar) {} + explicit Quaternion(const Vector4& v) noexcept : XMFLOAT4(v.x, v.y, v.z, v.w) {} + explicit Quaternion(_In_reads_(4) const float* pArray) noexcept : XMFLOAT4(pArray) {} + Quaternion(FXMVECTOR V) noexcept { XMStoreFloat4(this, V); } + Quaternion(const XMFLOAT4& q) noexcept + { + this->x = q.x; + this->y = q.y; + this->z = q.z; + this->w = q.w; + } + explicit Quaternion(const XMVECTORF32& F) noexcept + { + this->x = F.f[0]; + this->y = F.f[1]; + this->z = F.f[2]; + this->w = F.f[3]; + } + + Quaternion(const Quaternion&) = default; + Quaternion& operator=(const Quaternion&) = default; + + Quaternion(Quaternion&&) = default; + Quaternion& operator=(Quaternion&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat4(this); } + + // Comparison operators + bool operator==(const Quaternion& q) const noexcept; + bool operator!=(const Quaternion& q) const noexcept; + + // Assignment operators + Quaternion& operator=(const XMVECTORF32& F) noexcept + { + x = F.f[0]; + y = F.f[1]; + z = F.f[2]; + w = F.f[3]; + return *this; + } + Quaternion& operator+=(const Quaternion& q) noexcept; + Quaternion& operator-=(const Quaternion& q) noexcept; + Quaternion& operator*=(const Quaternion& q) noexcept; + Quaternion& operator*=(float S) noexcept; + Quaternion& operator/=(const Quaternion& q) noexcept; + + // Unary operators + Quaternion operator+() const noexcept { return *this; } + Quaternion operator-() const noexcept; + + // Quaternion operations + float Length() const noexcept; + float LengthSquared() const noexcept; + + void Normalize() noexcept; + void Normalize(Quaternion& result) const noexcept; + + void Conjugate() noexcept; + void Conjugate(Quaternion& result) const noexcept; + + void Inverse(Quaternion& result) const noexcept; + + float Dot(const Quaternion& Q) const noexcept; + + void RotateTowards(const Quaternion& target, float maxAngle) noexcept; + void __cdecl RotateTowards(const Quaternion& target, float maxAngle, Quaternion& result) const noexcept; + + // Computes rotation about y-axis (y), then x-axis (x), then z-axis (z) + Vector3 ToEuler() const noexcept; + + // Static functions + static Quaternion CreateFromAxisAngle(const Vector3& axis, float angle) noexcept; + + // Rotates about y-axis (yaw), then x-axis (pitch), then z-axis (roll) + static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float roll) noexcept; + + // Rotates about y-axis (angles.y), then x-axis (angles.x), then z-axis (angles.z) + static Quaternion CreateFromYawPitchRoll(const Vector3& angles) noexcept; + + static Quaternion CreateFromRotationMatrix(const Matrix& M) noexcept; + + static void Lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion& result) noexcept; + static Quaternion Lerp(const Quaternion& q1, const Quaternion& q2, float t) noexcept; + + static void Slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion& result) noexcept; + static Quaternion Slerp(const Quaternion& q1, const Quaternion& q2, float t) noexcept; + + static void Concatenate(const Quaternion& q1, const Quaternion& q2, Quaternion& result) noexcept; + static Quaternion Concatenate(const Quaternion& q1, const Quaternion& q2) noexcept; + + static void __cdecl FromToRotation(const Vector3& fromDir, const Vector3& toDir, Quaternion& result) noexcept; + static Quaternion FromToRotation(const Vector3& fromDir, const Vector3& toDir) noexcept; + + static void __cdecl LookRotation(const Vector3& forward, const Vector3& up, Quaternion& result) noexcept; + static Quaternion LookRotation(const Vector3& forward, const Vector3& up) noexcept; + + static float Angle(const Quaternion& q1, const Quaternion& q2) noexcept; + + // Constants + static const Quaternion Identity; +}; + +// Binary operators +Quaternion operator+(const Quaternion& Q1, const Quaternion& Q2) noexcept; +Quaternion operator-(const Quaternion& Q1, const Quaternion& Q2) noexcept; +Quaternion operator*(const Quaternion& Q1, const Quaternion& Q2) noexcept; +Quaternion operator*(const Quaternion& Q, float S) noexcept; +Quaternion operator/(const Quaternion& Q1, const Quaternion& Q2) noexcept; +Quaternion operator*(float S, const Quaternion& Q) noexcept; + +//------------------------------------------------------------------------------ +// Color +struct Color : public XMFLOAT4 +{ + Color() noexcept : XMFLOAT4(0, 0, 0, 1.f) {} + constexpr Color(float _r, float _g, float _b) noexcept : XMFLOAT4(_r, _g, _b, 1.f) {} + constexpr Color(float _r, float _g, float _b, float _a) noexcept : XMFLOAT4(_r, _g, _b, _a) {} + explicit Color(const Vector3& clr) noexcept : XMFLOAT4(clr.x, clr.y, clr.z, 1.f) {} + explicit Color(const Vector4& clr) noexcept : XMFLOAT4(clr.x, clr.y, clr.z, clr.w) {} + explicit Color(_In_reads_(4) const float* pArray) noexcept : XMFLOAT4(pArray) {} + Color(FXMVECTOR V) noexcept { XMStoreFloat4(this, V); } + Color(const XMFLOAT4& c) noexcept + { + this->x = c.x; + this->y = c.y; + this->z = c.z; + this->w = c.w; + } + explicit Color(const XMVECTORF32& F) noexcept + { + this->x = F.f[0]; + this->y = F.f[1]; + this->z = F.f[2]; + this->w = F.f[3]; + } + + // BGRA Direct3D 9 D3DCOLOR packed color + explicit Color(const DirectX::PackedVector::XMCOLOR& Packed) noexcept; + + // RGBA XNA Game Studio packed color + explicit Color(const DirectX::PackedVector::XMUBYTEN4& Packed) noexcept; + + Color(const Color&) = default; + Color& operator=(const Color&) = default; + + Color(Color&&) = default; + Color& operator=(Color&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat4(this); } + operator const float*() const noexcept { return reinterpret_cast(this); } + + // Comparison operators + bool operator==(const Color& c) const noexcept; + bool operator!=(const Color& c) const noexcept; + + // Assignment operators + Color& operator=(const XMVECTORF32& F) noexcept + { + x = F.f[0]; + y = F.f[1]; + z = F.f[2]; + w = F.f[3]; + return *this; + } + Color& operator=(const DirectX::PackedVector::XMCOLOR& Packed) noexcept; + Color& operator=(const DirectX::PackedVector::XMUBYTEN4& Packed) noexcept; + Color& operator+=(const Color& c) noexcept; + Color& operator-=(const Color& c) noexcept; + Color& operator*=(const Color& c) noexcept; + Color& operator*=(float S) noexcept; + Color& operator/=(const Color& c) noexcept; + + // Unary operators + Color operator+() const noexcept { return *this; } + Color operator-() const noexcept; + + // Properties + float R() const noexcept { return x; } + void R(float r) noexcept { x = r; } + + float G() const noexcept { return y; } + void G(float g) noexcept { y = g; } + + float B() const noexcept { return z; } + void B(float b) noexcept { z = b; } + + float A() const noexcept { return w; } + void A(float a) noexcept { w = a; } + + // Color operations + DirectX::PackedVector::XMCOLOR BGRA() const noexcept; + DirectX::PackedVector::XMUBYTEN4 RGBA() const noexcept; + + Vector3 ToVector3() const noexcept; + Vector4 ToVector4() const noexcept; + + void Negate() noexcept; + void Negate(Color& result) const noexcept; + + void Saturate() noexcept; + void Saturate(Color& result) const noexcept; + + void Premultiply() noexcept; + void Premultiply(Color& result) const noexcept; + + void AdjustSaturation(float sat) noexcept; + void AdjustSaturation(float sat, Color& result) const noexcept; + + void AdjustContrast(float contrast) noexcept; + void AdjustContrast(float contrast, Color& result) const noexcept; + + // Static functions + static void Modulate(const Color& c1, const Color& c2, Color& result) noexcept; + static Color Modulate(const Color& c1, const Color& c2) noexcept; + + static void Lerp(const Color& c1, const Color& c2, float t, Color& result) noexcept; + static Color Lerp(const Color& c1, const Color& c2, float t) noexcept; +}; + +// Binary operators +Color operator+(const Color& C1, const Color& C2) noexcept; +Color operator-(const Color& C1, const Color& C2) noexcept; +Color operator*(const Color& C1, const Color& C2) noexcept; +Color operator*(const Color& C, float S) noexcept; +Color operator/(const Color& C1, const Color& C2) noexcept; +Color operator*(float S, const Color& C) noexcept; + +//------------------------------------------------------------------------------ +// Ray +class Ray +{ +public: + Vector3 position; + Vector3 direction; + + Ray() noexcept : position(0, 0, 0), direction(0, 0, 1) {} + Ray(const Vector3& pos, const Vector3& dir) noexcept : position(pos), direction(dir) {} + + Ray(const Ray&) = default; + Ray& operator=(const Ray&) = default; + + Ray(Ray&&) = default; + Ray& operator=(Ray&&) = default; + + // Comparison operators + bool operator==(const Ray& r) const noexcept; + bool operator!=(const Ray& r) const noexcept; + + // Ray operations + bool Intersects(const BoundingSphere& sphere, _Out_ float& Dist) const noexcept; + bool Intersects(const BoundingBox& box, _Out_ float& Dist) const noexcept; + bool Intersects(const Vector3& tri0, const Vector3& tri1, const Vector3& tri2, _Out_ float& Dist) const noexcept; + bool Intersects(const Plane& plane, _Out_ float& Dist) const noexcept; +}; + +//------------------------------------------------------------------------------ +// Viewport +class Viewport +{ +public: + float x; + float y; + float width; + float height; + float minDepth; + float maxDepth; + + Viewport() noexcept : x(0.f), y(0.f), width(0.f), height(0.f), minDepth(0.f), maxDepth(1.f) {} + constexpr Viewport(float ix, float iy, float iw, float ih, float iminz = 0.f, float imaxz = 1.f) noexcept + : x(ix), y(iy), width(iw), height(ih), minDepth(iminz), maxDepth(imaxz) + { + } + explicit Viewport(const RECT& rct) noexcept + : x(float(rct.left)) + , y(float(rct.top)) + , width(float(rct.right - rct.left)) + , height(float(rct.bottom - rct.top)) + , minDepth(0.f) + , maxDepth(1.f) + { + } + +#if defined(__d3d11_h__) || defined(__d3d11_x_h__) + // Direct3D 11 interop + explicit Viewport(const D3D11_VIEWPORT& vp) noexcept + : x(vp.TopLeftX), y(vp.TopLeftY), width(vp.Width), height(vp.Height), minDepth(vp.MinDepth), maxDepth(vp.MaxDepth) + { + } + + operator D3D11_VIEWPORT() noexcept { return *reinterpret_cast(this); } + const D3D11_VIEWPORT* Get11() const noexcept { return reinterpret_cast(this); } + Viewport& operator=(const D3D11_VIEWPORT& vp) noexcept; +#endif + +#if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) + // Direct3D 12 interop + explicit Viewport(const D3D12_VIEWPORT& vp) noexcept + : x(vp.TopLeftX), y(vp.TopLeftY), width(vp.Width), height(vp.Height), minDepth(vp.MinDepth), maxDepth(vp.MaxDepth) + { + } + + operator D3D12_VIEWPORT() noexcept { return *reinterpret_cast(this); } + const D3D12_VIEWPORT* Get12() const noexcept { return reinterpret_cast(this); } + Viewport& operator=(const D3D12_VIEWPORT& vp) noexcept; +#endif + + Viewport(const Viewport&) = default; + Viewport& operator=(const Viewport&) = default; + + Viewport(Viewport&&) = default; + Viewport& operator=(Viewport&&) = default; + + // Comparison operators +#if (__cplusplus >= 202002L) + bool operator==(const Viewport&) const = default; + auto operator<=>(const Viewport&) const = default; +#else + bool operator==(const Viewport& vp) const noexcept; + bool operator!=(const Viewport& vp) const noexcept; +#endif + + // Assignment operators + Viewport& operator=(const RECT& rct) noexcept; + + // Viewport operations + float AspectRatio() const noexcept; + + Vector3 Project(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world) const noexcept; + void Project(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world, Vector3& result) const noexcept; + + Vector3 Unproject(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world) const noexcept; + void Unproject(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world, Vector3& result) const noexcept; + + // Static methods +#if defined(__dxgi1_2_h__) || defined(__d3d11_x_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) + static RECT __cdecl ComputeDisplayArea( + DXGI_SCALING scaling, UINT backBufferWidth, UINT backBufferHeight, int outputWidth, int outputHeight) noexcept; +#endif + static RECT __cdecl ComputeTitleSafeArea(UINT backBufferWidth, UINT backBufferHeight) noexcept; +}; + +#include "SimpleMath.inl" + +} // namespace SimpleMath + +} // namespace DirectX + +//------------------------------------------------------------------------------ +// Support for SimpleMath and Standard C++ Library containers +namespace std +{ + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Rectangle& r1, const DirectX::SimpleMath::Rectangle& r2) const noexcept + { + return ((r1.x < r2.x) || ((r1.x == r2.x) && (r1.y < r2.y)) || ((r1.x == r2.x) && (r1.y == r2.y) && (r1.width < r2.width)) || + ((r1.x == r2.x) && (r1.y == r2.y) && (r1.width == r2.width) && (r1.height < r2.height))); + } +}; + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Vector2& V1, const DirectX::SimpleMath::Vector2& V2) const noexcept + { + return ((V1.x < V2.x) || ((V1.x == V2.x) && (V1.y < V2.y))); + } +}; + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Vector3& V1, const DirectX::SimpleMath::Vector3& V2) const noexcept + { + return ((V1.x < V2.x) || ((V1.x == V2.x) && (V1.y < V2.y)) || ((V1.x == V2.x) && (V1.y == V2.y) && (V1.z < V2.z))); + } +}; + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Vector4& V1, const DirectX::SimpleMath::Vector4& V2) const noexcept + { + return ((V1.x < V2.x) || ((V1.x == V2.x) && (V1.y < V2.y)) || ((V1.x == V2.x) && (V1.y == V2.y) && (V1.z < V2.z)) || + ((V1.x == V2.x) && (V1.y == V2.y) && (V1.z == V2.z) && (V1.w < V2.w))); + } +}; + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Matrix& M1, const DirectX::SimpleMath::Matrix& M2) const noexcept + { + if (M1._11 != M2._11) + return M1._11 < M2._11; + if (M1._12 != M2._12) + return M1._12 < M2._12; + if (M1._13 != M2._13) + return M1._13 < M2._13; + if (M1._14 != M2._14) + return M1._14 < M2._14; + if (M1._21 != M2._21) + return M1._21 < M2._21; + if (M1._22 != M2._22) + return M1._22 < M2._22; + if (M1._23 != M2._23) + return M1._23 < M2._23; + if (M1._24 != M2._24) + return M1._24 < M2._24; + if (M1._31 != M2._31) + return M1._31 < M2._31; + if (M1._32 != M2._32) + return M1._32 < M2._32; + if (M1._33 != M2._33) + return M1._33 < M2._33; + if (M1._34 != M2._34) + return M1._34 < M2._34; + if (M1._41 != M2._41) + return M1._41 < M2._41; + if (M1._42 != M2._42) + return M1._42 < M2._42; + if (M1._43 != M2._43) + return M1._43 < M2._43; + if (M1._44 != M2._44) + return M1._44 < M2._44; + + return false; + } +}; + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Plane& P1, const DirectX::SimpleMath::Plane& P2) const noexcept + { + return ((P1.x < P2.x) || ((P1.x == P2.x) && (P1.y < P2.y)) || ((P1.x == P2.x) && (P1.y == P2.y) && (P1.z < P2.z)) || + ((P1.x == P2.x) && (P1.y == P2.y) && (P1.z == P2.z) && (P1.w < P2.w))); + } +}; + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Quaternion& Q1, const DirectX::SimpleMath::Quaternion& Q2) const noexcept + { + return ((Q1.x < Q2.x) || ((Q1.x == Q2.x) && (Q1.y < Q2.y)) || ((Q1.x == Q2.x) && (Q1.y == Q2.y) && (Q1.z < Q2.z)) || + ((Q1.x == Q2.x) && (Q1.y == Q2.y) && (Q1.z == Q2.z) && (Q1.w < Q2.w))); + } +}; + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Color& C1, const DirectX::SimpleMath::Color& C2) const noexcept + { + return ((C1.x < C2.x) || ((C1.x == C2.x) && (C1.y < C2.y)) || ((C1.x == C2.x) && (C1.y == C2.y) && (C1.z < C2.z)) || + ((C1.x == C2.x) && (C1.y == C2.y) && (C1.z == C2.z) && (C1.w < C2.w))); + } +}; + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Ray& R1, const DirectX::SimpleMath::Ray& R2) const noexcept + { + if (R1.position.x != R2.position.x) + return R1.position.x < R2.position.x; + if (R1.position.y != R2.position.y) + return R1.position.y < R2.position.y; + if (R1.position.z != R2.position.z) + return R1.position.z < R2.position.z; + + if (R1.direction.x != R2.direction.x) + return R1.direction.x < R2.direction.x; + if (R1.direction.y != R2.direction.y) + return R1.direction.y < R2.direction.y; + if (R1.direction.z != R2.direction.z) + return R1.direction.z < R2.direction.z; + + return false; + } +}; + +template <> +struct less +{ + bool operator()(const DirectX::SimpleMath::Viewport& vp1, const DirectX::SimpleMath::Viewport& vp2) const noexcept + { + if (vp1.x != vp2.x) + return (vp1.x < vp2.x); + if (vp1.y != vp2.y) + return (vp1.y < vp2.y); + + if (vp1.width != vp2.width) + return (vp1.width < vp2.width); + if (vp1.height != vp2.height) + return (vp1.height < vp2.height); + + if (vp1.minDepth != vp2.minDepth) + return (vp1.minDepth < vp2.minDepth); + if (vp1.maxDepth != vp2.maxDepth) + return (vp1.maxDepth < vp2.maxDepth); + + return false; + } +}; + +} // namespace std + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/Atlas/SimpleMath.inl b/Atlas/SimpleMath.inl new file mode 100644 index 00000000..fc783525 --- /dev/null +++ b/Atlas/SimpleMath.inl @@ -0,0 +1,3857 @@ +//------------------------------------------------------------------------------------- +// SimpleMath.inl -- Simplified C++ Math wrapper for DirectXMath +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#pragma once + +/**************************************************************************** + * + * Rectangle + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Rectangle operations +//------------------------------------------------------------------------------ +inline Vector2 Rectangle::Location() const noexcept +{ + return Vector2(float(x), float(y)); +} + +inline Vector2 Rectangle::Center() const noexcept +{ + return Vector2(float(x) + (float(width) / 2.f), float(y) + (float(height) / 2.f)); +} + +inline bool Rectangle::Contains(const Vector2& point) const noexcept +{ + return (float(x) <= point.x) && (point.x < float(x + width)) && (float(y) <= point.y) && (point.y < float(y + height)); +} + +inline void Rectangle::Inflate(long horizAmount, long vertAmount) noexcept +{ + x -= horizAmount; + y -= vertAmount; + width += horizAmount; + height += vertAmount; +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline Rectangle Rectangle::Intersect(const Rectangle& ra, const Rectangle& rb) noexcept +{ + const long righta = ra.x + ra.width; + const long rightb = rb.x + rb.width; + + const long bottoma = ra.y + ra.height; + const long bottomb = rb.y + rb.height; + + const long maxX = ra.x > rb.x ? ra.x : rb.x; + const long maxY = ra.y > rb.y ? ra.y : rb.y; + + const long minRight = righta < rightb ? righta : rightb; + const long minBottom = bottoma < bottomb ? bottoma : bottomb; + + Rectangle result; + + if ((minRight > maxX) && (minBottom > maxY)) + { + result.x = maxX; + result.y = maxY; + result.width = minRight - maxX; + result.height = minBottom - maxY; + } + else + { + result.x = 0; + result.y = 0; + result.width = 0; + result.height = 0; + } + + return result; +} + +inline RECT Rectangle::Intersect(const RECT& rcta, const RECT& rctb) noexcept +{ + const long maxX = rcta.left > rctb.left ? rcta.left : rctb.left; + const long maxY = rcta.top > rctb.top ? rcta.top : rctb.top; + + const long minRight = rcta.right < rctb.right ? rcta.right : rctb.right; + const long minBottom = rcta.bottom < rctb.bottom ? rcta.bottom : rctb.bottom; + + RECT result; + + if ((minRight > maxX) && (minBottom > maxY)) + { + result.left = maxX; + result.top = maxY; + result.right = minRight; + result.bottom = minBottom; + } + else + { + result.left = 0; + result.top = 0; + result.right = 0; + result.bottom = 0; + } + + return result; +} + +inline Rectangle Rectangle::Union(const Rectangle& ra, const Rectangle& rb) noexcept +{ + const long righta = ra.x + ra.width; + const long rightb = rb.x + rb.width; + + const long bottoma = ra.y + ra.height; + const long bottomb = rb.y + rb.height; + + const int minX = ra.x < rb.x ? ra.x : rb.x; + const int minY = ra.y < rb.y ? ra.y : rb.y; + + const int maxRight = righta > rightb ? righta : rightb; + const int maxBottom = bottoma > bottomb ? bottoma : bottomb; + + Rectangle result; + result.x = minX; + result.y = minY; + result.width = maxRight - minX; + result.height = maxBottom - minY; + return result; +} + +inline RECT Rectangle::Union(const RECT& rcta, const RECT& rctb) noexcept +{ + RECT result; + result.left = rcta.left < rctb.left ? rcta.left : rctb.left; + result.top = rcta.top < rctb.top ? rcta.top : rctb.top; + result.right = rcta.right > rctb.right ? rcta.right : rctb.right; + result.bottom = rcta.bottom > rctb.bottom ? rcta.bottom : rctb.bottom; + return result; +} + +/**************************************************************************** + * + * Vector2 + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Vector2::operator==(const Vector2& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + return XMVector2Equal(v1, v2); +} + +inline bool Vector2::operator!=(const Vector2& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + return XMVector2NotEqual(v1, v2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Vector2& Vector2::operator+=(const Vector2& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorAdd(v1, v2); + XMStoreFloat2(this, X); + return *this; +} + +inline Vector2& Vector2::operator-=(const Vector2& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorSubtract(v1, v2); + XMStoreFloat2(this, X); + return *this; +} + +inline Vector2& Vector2::operator*=(const Vector2& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorMultiply(v1, v2); + XMStoreFloat2(this, X); + return *this; +} + +inline Vector2& Vector2::operator*=(float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVectorScale(v1, S); + XMStoreFloat2(this, X); + return *this; +} + +inline Vector2& Vector2::operator/=(float S) noexcept +{ + using namespace DirectX; + assert(S != 0.0f); + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + XMStoreFloat2(this, X); + return *this; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Vector2 operator+(const Vector2& V1, const Vector2& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V1); + const XMVECTOR v2 = XMLoadFloat2(&V2); + const XMVECTOR X = XMVectorAdd(v1, v2); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator-(const Vector2& V1, const Vector2& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V1); + const XMVECTOR v2 = XMLoadFloat2(&V2); + const XMVECTOR X = XMVectorSubtract(v1, v2); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator*(const Vector2& V1, const Vector2& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V1); + const XMVECTOR v2 = XMLoadFloat2(&V2); + const XMVECTOR X = XMVectorMultiply(v1, v2); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator*(const Vector2& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator/(const Vector2& V1, const Vector2& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V1); + const XMVECTOR v2 = XMLoadFloat2(&V2); + const XMVECTOR X = XMVectorDivide(v1, v2); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator/(const Vector2& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator*(float S, const Vector2& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Vector operations +//------------------------------------------------------------------------------ + +inline bool Vector2::InBounds(const Vector2& Bounds) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&Bounds); + return XMVector2InBounds(v1, v2); +} + +inline float Vector2::Length() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVector2Length(v1); + return XMVectorGetX(X); +} + +inline float Vector2::LengthSquared() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVector2LengthSq(v1); + return XMVectorGetX(X); +} + +inline float Vector2::Dot(const Vector2& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR X = XMVector2Dot(v1, v2); + return XMVectorGetX(X); +} + +inline void Vector2::Cross(const Vector2& V, Vector2& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR R = XMVector2Cross(v1, v2); + XMStoreFloat2(&result, R); +} + +inline Vector2 Vector2::Cross(const Vector2& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR R = XMVector2Cross(v1, v2); + + Vector2 result; + XMStoreFloat2(&result, R); + return result; +} + +inline void Vector2::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVector2Normalize(v1); + XMStoreFloat2(this, X); +} + +inline void Vector2::Normalize(Vector2& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVector2Normalize(v1); + XMStoreFloat2(&result, X); +} + +inline void Vector2::Clamp(const Vector2& vmin, const Vector2& vmax) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&vmin); + const XMVECTOR v3 = XMLoadFloat2(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat2(this, X); +} + +inline void Vector2::Clamp(const Vector2& vmin, const Vector2& vmax, Vector2& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&vmin); + const XMVECTOR v3 = XMLoadFloat2(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat2(&result, X); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline float Vector2::Distance(const Vector2& v1, const Vector2& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector2Length(V); + return XMVectorGetX(X); +} + +inline float Vector2::DistanceSquared(const Vector2& v1, const Vector2& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector2LengthSq(V); + return XMVectorGetX(X); +} + +inline void Vector2::Min(const Vector2& v1, const Vector2& v2, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Min(const Vector2& v1, const Vector2& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Max(const Vector2& v1, const Vector2& v2, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Max(const Vector2& v1, const Vector2& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Lerp(const Vector2& v1, const Vector2& v2, float t, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Lerp(const Vector2& v1, const Vector2& v2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::SmoothStep(const Vector2& v1, const Vector2& v2, float t, Vector2& result) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t * (3.f - 2.f * t); + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::SmoothStep(const Vector2& v1, const Vector2& v2, float t) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t * (3.f - 2.f * t); + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Barycentric(const Vector2& v1, const Vector2& v2, const Vector2& v3, float f, float g, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR x3 = XMLoadFloat2(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Barycentric(const Vector2& v1, const Vector2& v2, const Vector2& v3, float f, float g) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR x3 = XMLoadFloat2(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::CatmullRom( + const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float t, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR x3 = XMLoadFloat2(&v3); + const XMVECTOR x4 = XMLoadFloat2(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::CatmullRom(const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR x3 = XMLoadFloat2(&v3); + const XMVECTOR x4 = XMLoadFloat2(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Hermite(const Vector2& v1, const Vector2& t1, const Vector2& v2, const Vector2& t2, float t, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&t1); + const XMVECTOR x3 = XMLoadFloat2(&v2); + const XMVECTOR x4 = XMLoadFloat2(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Hermite(const Vector2& v1, const Vector2& t1, const Vector2& v2, const Vector2& t2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&t1); + const XMVECTOR x3 = XMLoadFloat2(&v2); + const XMVECTOR x4 = XMLoadFloat2(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Reflect(const Vector2& ivec, const Vector2& nvec, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat2(&ivec); + const XMVECTOR n = XMLoadFloat2(&nvec); + const XMVECTOR X = XMVector2Reflect(i, n); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Reflect(const Vector2& ivec, const Vector2& nvec) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat2(&ivec); + const XMVECTOR n = XMLoadFloat2(&nvec); + const XMVECTOR X = XMVector2Reflect(i, n); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Refract(const Vector2& ivec, const Vector2& nvec, float refractionIndex, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat2(&ivec); + const XMVECTOR n = XMLoadFloat2(&nvec); + const XMVECTOR X = XMVector2Refract(i, n, refractionIndex); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Refract(const Vector2& ivec, const Vector2& nvec, float refractionIndex) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat2(&ivec); + const XMVECTOR n = XMLoadFloat2(&nvec); + const XMVECTOR X = XMVector2Refract(i, n, refractionIndex); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Transform(const Vector2& v, const Quaternion& quat, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + const XMVECTOR X = XMVector3Rotate(v1, q); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Transform(const Vector2& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + const XMVECTOR X = XMVector3Rotate(v1, q); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Transform(const Vector2& v, const Matrix& m, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2TransformCoord(v1, M); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Transform(const Vector2& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2TransformCoord(v1, M); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +_Use_decl_annotations_ inline void Vector2::Transform(const Vector2* varray, size_t count, const Matrix& m, Vector2* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector2TransformCoordStream(resultArray, sizeof(XMFLOAT2), varray, sizeof(XMFLOAT2), count, M); +} + +inline void Vector2::Transform(const Vector2& v, const Matrix& m, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2Transform(v1, M); + XMStoreFloat4(&result, X); +} + +_Use_decl_annotations_ inline void Vector2::Transform(const Vector2* varray, size_t count, const Matrix& m, Vector4* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector2TransformStream(resultArray, sizeof(XMFLOAT4), varray, sizeof(XMFLOAT2), count, M); +} + +inline void Vector2::TransformNormal(const Vector2& v, const Matrix& m, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2TransformNormal(v1, M); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::TransformNormal(const Vector2& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2TransformNormal(v1, M); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +_Use_decl_annotations_ inline void Vector2::TransformNormal( + const Vector2* varray, size_t count, const Matrix& m, Vector2* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector2TransformNormalStream(resultArray, sizeof(XMFLOAT2), varray, sizeof(XMFLOAT2), count, M); +} + +/**************************************************************************** + * + * Vector3 + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Vector3::operator==(const Vector3& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + return XMVector3Equal(v1, v2); +} + +inline bool Vector3::operator!=(const Vector3& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + return XMVector3NotEqual(v1, v2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Vector3& Vector3::operator+=(const Vector3& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorAdd(v1, v2); + XMStoreFloat3(this, X); + return *this; +} + +inline Vector3& Vector3::operator-=(const Vector3& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorSubtract(v1, v2); + XMStoreFloat3(this, X); + return *this; +} + +inline Vector3& Vector3::operator*=(const Vector3& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorMultiply(v1, v2); + XMStoreFloat3(this, X); + return *this; +} + +inline Vector3& Vector3::operator*=(float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVectorScale(v1, S); + XMStoreFloat3(this, X); + return *this; +} + +inline Vector3& Vector3::operator/=(float S) noexcept +{ + using namespace DirectX; + assert(S != 0.0f); + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + XMStoreFloat3(this, X); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Vector3 Vector3::operator-() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVectorNegate(v1); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Vector3 operator+(const Vector3& V1, const Vector3& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V1); + const XMVECTOR v2 = XMLoadFloat3(&V2); + const XMVECTOR X = XMVectorAdd(v1, v2); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator-(const Vector3& V1, const Vector3& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V1); + const XMVECTOR v2 = XMLoadFloat3(&V2); + const XMVECTOR X = XMVectorSubtract(v1, v2); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator*(const Vector3& V1, const Vector3& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V1); + const XMVECTOR v2 = XMLoadFloat3(&V2); + const XMVECTOR X = XMVectorMultiply(v1, v2); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator*(const Vector3& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator/(const Vector3& V1, const Vector3& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V1); + const XMVECTOR v2 = XMLoadFloat3(&V2); + const XMVECTOR X = XMVectorDivide(v1, v2); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator/(const Vector3& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator*(float S, const Vector3& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Vector operations +//------------------------------------------------------------------------------ + +inline bool Vector3::InBounds(const Vector3& Bounds) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&Bounds); + return XMVector3InBounds(v1, v2); +} + +inline float Vector3::Length() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVector3Length(v1); + return XMVectorGetX(X); +} + +inline float Vector3::LengthSquared() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVector3LengthSq(v1); + return XMVectorGetX(X); +} + +inline float Vector3::Dot(const Vector3& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR X = XMVector3Dot(v1, v2); + return XMVectorGetX(X); +} + +inline void Vector3::Cross(const Vector3& V, Vector3& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR R = XMVector3Cross(v1, v2); + XMStoreFloat3(&result, R); +} + +inline Vector3 Vector3::Cross(const Vector3& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR R = XMVector3Cross(v1, v2); + + Vector3 result; + XMStoreFloat3(&result, R); + return result; +} + +inline void Vector3::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVector3Normalize(v1); + XMStoreFloat3(this, X); +} + +inline void Vector3::Normalize(Vector3& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVector3Normalize(v1); + XMStoreFloat3(&result, X); +} + +inline void Vector3::Clamp(const Vector3& vmin, const Vector3& vmax) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&vmin); + const XMVECTOR v3 = XMLoadFloat3(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat3(this, X); +} + +inline void Vector3::Clamp(const Vector3& vmin, const Vector3& vmax, Vector3& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&vmin); + const XMVECTOR v3 = XMLoadFloat3(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat3(&result, X); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline float Vector3::Distance(const Vector3& v1, const Vector3& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector3Length(V); + return XMVectorGetX(X); +} + +inline float Vector3::DistanceSquared(const Vector3& v1, const Vector3& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector3LengthSq(V); + return XMVectorGetX(X); +} + +inline void Vector3::Min(const Vector3& v1, const Vector3& v2, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Min(const Vector3& v1, const Vector3& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Max(const Vector3& v1, const Vector3& v2, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Max(const Vector3& v1, const Vector3& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Lerp(const Vector3& v1, const Vector3& v2, float t, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Lerp(const Vector3& v1, const Vector3& v2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::SmoothStep(const Vector3& v1, const Vector3& v2, float t, Vector3& result) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t * (3.f - 2.f * t); + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::SmoothStep(const Vector3& v1, const Vector3& v2, float t) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t * (3.f - 2.f * t); + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Barycentric(const Vector3& v1, const Vector3& v2, const Vector3& v3, float f, float g, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR x3 = XMLoadFloat3(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Barycentric(const Vector3& v1, const Vector3& v2, const Vector3& v3, float f, float g) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR x3 = XMLoadFloat3(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::CatmullRom( + const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& v4, float t, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR x3 = XMLoadFloat3(&v3); + const XMVECTOR x4 = XMLoadFloat3(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::CatmullRom(const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& v4, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR x3 = XMLoadFloat3(&v3); + const XMVECTOR x4 = XMLoadFloat3(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Hermite(const Vector3& v1, const Vector3& t1, const Vector3& v2, const Vector3& t2, float t, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&t1); + const XMVECTOR x3 = XMLoadFloat3(&v2); + const XMVECTOR x4 = XMLoadFloat3(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Hermite(const Vector3& v1, const Vector3& t1, const Vector3& v2, const Vector3& t2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&t1); + const XMVECTOR x3 = XMLoadFloat3(&v2); + const XMVECTOR x4 = XMLoadFloat3(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Reflect(const Vector3& ivec, const Vector3& nvec, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat3(&ivec); + const XMVECTOR n = XMLoadFloat3(&nvec); + const XMVECTOR X = XMVector3Reflect(i, n); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Reflect(const Vector3& ivec, const Vector3& nvec) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat3(&ivec); + const XMVECTOR n = XMLoadFloat3(&nvec); + const XMVECTOR X = XMVector3Reflect(i, n); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Refract(const Vector3& ivec, const Vector3& nvec, float refractionIndex, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat3(&ivec); + const XMVECTOR n = XMLoadFloat3(&nvec); + const XMVECTOR X = XMVector3Refract(i, n, refractionIndex); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Refract(const Vector3& ivec, const Vector3& nvec, float refractionIndex) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat3(&ivec); + const XMVECTOR n = XMLoadFloat3(&nvec); + const XMVECTOR X = XMVector3Refract(i, n, refractionIndex); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Transform(const Vector3& v, const Quaternion& quat, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + const XMVECTOR X = XMVector3Rotate(v1, q); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Transform(const Vector3& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + const XMVECTOR X = XMVector3Rotate(v1, q); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Transform(const Vector3& v, const Matrix& m, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3TransformCoord(v1, M); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Transform(const Vector3& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3TransformCoord(v1, M); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +_Use_decl_annotations_ inline void Vector3::Transform(const Vector3* varray, size_t count, const Matrix& m, Vector3* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector3TransformCoordStream(resultArray, sizeof(XMFLOAT3), varray, sizeof(XMFLOAT3), count, M); +} + +inline void Vector3::Transform(const Vector3& v, const Matrix& m, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3Transform(v1, M); + XMStoreFloat4(&result, X); +} + +_Use_decl_annotations_ inline void Vector3::Transform(const Vector3* varray, size_t count, const Matrix& m, Vector4* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector3TransformStream(resultArray, sizeof(XMFLOAT4), varray, sizeof(XMFLOAT3), count, M); +} + +inline void Vector3::TransformNormal(const Vector3& v, const Matrix& m, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3TransformNormal(v1, M); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::TransformNormal(const Vector3& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3TransformNormal(v1, M); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +_Use_decl_annotations_ inline void Vector3::TransformNormal( + const Vector3* varray, size_t count, const Matrix& m, Vector3* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector3TransformNormalStream(resultArray, sizeof(XMFLOAT3), varray, sizeof(XMFLOAT3), count, M); +} + +/**************************************************************************** + * + * Vector4 + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Vector4::operator==(const Vector4& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + return XMVector4Equal(v1, v2); +} + +inline bool Vector4::operator!=(const Vector4& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + return XMVector4NotEqual(v1, v2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Vector4& Vector4::operator+=(const Vector4& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorAdd(v1, v2); + XMStoreFloat4(this, X); + return *this; +} + +inline Vector4& Vector4::operator-=(const Vector4& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorSubtract(v1, v2); + XMStoreFloat4(this, X); + return *this; +} + +inline Vector4& Vector4::operator*=(const Vector4& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorMultiply(v1, v2); + XMStoreFloat4(this, X); + return *this; +} + +inline Vector4& Vector4::operator*=(float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVectorScale(v1, S); + XMStoreFloat4(this, X); + return *this; +} + +inline Vector4& Vector4::operator/=(float S) noexcept +{ + using namespace DirectX; + assert(S != 0.0f); + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + XMStoreFloat4(this, X); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Vector4 Vector4::operator-() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVectorNegate(v1); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Vector4 operator+(const Vector4& V1, const Vector4& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V1); + const XMVECTOR v2 = XMLoadFloat4(&V2); + const XMVECTOR X = XMVectorAdd(v1, v2); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator-(const Vector4& V1, const Vector4& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V1); + const XMVECTOR v2 = XMLoadFloat4(&V2); + const XMVECTOR X = XMVectorSubtract(v1, v2); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator*(const Vector4& V1, const Vector4& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V1); + const XMVECTOR v2 = XMLoadFloat4(&V2); + const XMVECTOR X = XMVectorMultiply(v1, v2); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator*(const Vector4& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator/(const Vector4& V1, const Vector4& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V1); + const XMVECTOR v2 = XMLoadFloat4(&V2); + const XMVECTOR X = XMVectorDivide(v1, v2); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator/(const Vector4& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator*(float S, const Vector4& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Vector operations +//------------------------------------------------------------------------------ + +inline bool Vector4::InBounds(const Vector4& Bounds) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&Bounds); + return XMVector4InBounds(v1, v2); +} + +inline float Vector4::Length() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVector4Length(v1); + return XMVectorGetX(X); +} + +inline float Vector4::LengthSquared() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVector4LengthSq(v1); + return XMVectorGetX(X); +} + +inline float Vector4::Dot(const Vector4& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + const XMVECTOR X = XMVector4Dot(v1, v2); + return XMVectorGetX(X); +} + +inline void Vector4::Cross(const Vector4& v1, const Vector4& v2, Vector4& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(this); + const XMVECTOR x2 = XMLoadFloat4(&v1); + const XMVECTOR x3 = XMLoadFloat4(&v2); + const XMVECTOR R = XMVector4Cross(x1, x2, x3); + XMStoreFloat4(&result, R); +} + +inline Vector4 Vector4::Cross(const Vector4& v1, const Vector4& v2) const noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(this); + const XMVECTOR x2 = XMLoadFloat4(&v1); + const XMVECTOR x3 = XMLoadFloat4(&v2); + const XMVECTOR R = XMVector4Cross(x1, x2, x3); + + Vector4 result; + XMStoreFloat4(&result, R); + return result; +} + +inline void Vector4::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVector4Normalize(v1); + XMStoreFloat4(this, X); +} + +inline void Vector4::Normalize(Vector4& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVector4Normalize(v1); + XMStoreFloat4(&result, X); +} + +inline void Vector4::Clamp(const Vector4& vmin, const Vector4& vmax) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&vmin); + const XMVECTOR v3 = XMLoadFloat4(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat4(this, X); +} + +inline void Vector4::Clamp(const Vector4& vmin, const Vector4& vmax, Vector4& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&vmin); + const XMVECTOR v3 = XMLoadFloat4(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat4(&result, X); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline float Vector4::Distance(const Vector4& v1, const Vector4& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector4Length(V); + return XMVectorGetX(X); +} + +inline float Vector4::DistanceSquared(const Vector4& v1, const Vector4& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector4LengthSq(V); + return XMVectorGetX(X); +} + +inline void Vector4::Min(const Vector4& v1, const Vector4& v2, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Min(const Vector4& v1, const Vector4& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Max(const Vector4& v1, const Vector4& v2, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Max(const Vector4& v1, const Vector4& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Lerp(const Vector4& v1, const Vector4& v2, float t, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Lerp(const Vector4& v1, const Vector4& v2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::SmoothStep(const Vector4& v1, const Vector4& v2, float t, Vector4& result) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t * (3.f - 2.f * t); + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::SmoothStep(const Vector4& v1, const Vector4& v2, float t) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t * (3.f - 2.f * t); + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Barycentric(const Vector4& v1, const Vector4& v2, const Vector4& v3, float f, float g, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR x3 = XMLoadFloat4(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Barycentric(const Vector4& v1, const Vector4& v2, const Vector4& v3, float f, float g) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR x3 = XMLoadFloat4(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::CatmullRom( + const Vector4& v1, const Vector4& v2, const Vector4& v3, const Vector4& v4, float t, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR x3 = XMLoadFloat4(&v3); + const XMVECTOR x4 = XMLoadFloat4(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::CatmullRom(const Vector4& v1, const Vector4& v2, const Vector4& v3, const Vector4& v4, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR x3 = XMLoadFloat4(&v3); + const XMVECTOR x4 = XMLoadFloat4(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Hermite(const Vector4& v1, const Vector4& t1, const Vector4& v2, const Vector4& t2, float t, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&t1); + const XMVECTOR x3 = XMLoadFloat4(&v2); + const XMVECTOR x4 = XMLoadFloat4(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Hermite(const Vector4& v1, const Vector4& t1, const Vector4& v2, const Vector4& t2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&t1); + const XMVECTOR x3 = XMLoadFloat4(&v2); + const XMVECTOR x4 = XMLoadFloat4(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Reflect(const Vector4& ivec, const Vector4& nvec, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat4(&ivec); + const XMVECTOR n = XMLoadFloat4(&nvec); + const XMVECTOR X = XMVector4Reflect(i, n); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Reflect(const Vector4& ivec, const Vector4& nvec) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat4(&ivec); + const XMVECTOR n = XMLoadFloat4(&nvec); + const XMVECTOR X = XMVector4Reflect(i, n); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Refract(const Vector4& ivec, const Vector4& nvec, float refractionIndex, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat4(&ivec); + const XMVECTOR n = XMLoadFloat4(&nvec); + const XMVECTOR X = XMVector4Refract(i, n, refractionIndex); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Refract(const Vector4& ivec, const Vector4& nvec, float refractionIndex) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat4(&ivec); + const XMVECTOR n = XMLoadFloat4(&nvec); + const XMVECTOR X = XMVector4Refract(i, n, refractionIndex); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Transform(const Vector2& v, const Quaternion& quat, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(g_XMIdentityR3, X, g_XMSelect1110); // result.w = 1.f + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Transform(const Vector2& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(g_XMIdentityR3, X, g_XMSelect1110); // result.w = 1.f + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Transform(const Vector3& v, const Quaternion& quat, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(g_XMIdentityR3, X, g_XMSelect1110); // result.w = 1.f + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Transform(const Vector3& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(g_XMIdentityR3, X, g_XMSelect1110); // result.w = 1.f + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Transform(const Vector4& v, const Quaternion& quat, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(v1, X, g_XMSelect1110); // result.w = v.w + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Transform(const Vector4& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(v1, X, g_XMSelect1110); // result.w = v.w + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Transform(const Vector4& v, const Matrix& m, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector4Transform(v1, M); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Transform(const Vector4& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector4Transform(v1, M); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +_Use_decl_annotations_ inline void Vector4::Transform(const Vector4* varray, size_t count, const Matrix& m, Vector4* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector4TransformStream(resultArray, sizeof(XMFLOAT4), varray, sizeof(XMFLOAT4), count, M); +} + +/**************************************************************************** + * + * Matrix + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Matrix::operator==(const Matrix& M) const noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + const XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + const XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + const XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + return (XMVector4Equal(x1, y1) && XMVector4Equal(x2, y2) && XMVector4Equal(x3, y3) && XMVector4Equal(x4, y4)) != 0; +} + +inline bool Matrix::operator!=(const Matrix& M) const noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + const XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + const XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + const XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + return (XMVector4NotEqual(x1, y1) || XMVector4NotEqual(x2, y2) || XMVector4NotEqual(x3, y3) || XMVector4NotEqual(x4, y4)) != 0; +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Matrix::Matrix(const XMFLOAT3X3& M) noexcept +{ + _11 = M._11; + _12 = M._12; + _13 = M._13; + _14 = 0.f; + _21 = M._21; + _22 = M._22; + _23 = M._23; + _24 = 0.f; + _31 = M._31; + _32 = M._32; + _33 = M._33; + _34 = 0.f; + _41 = 0.f; + _42 = 0.f; + _43 = 0.f; + _44 = 1.f; +} + +inline Matrix::Matrix(const XMFLOAT4X3& M) noexcept +{ + _11 = M._11; + _12 = M._12; + _13 = M._13; + _14 = 0.f; + _21 = M._21; + _22 = M._22; + _23 = M._23; + _24 = 0.f; + _31 = M._31; + _32 = M._32; + _33 = M._33; + _34 = 0.f; + _41 = M._41; + _42 = M._42; + _43 = M._43; + _44 = 1.f; +} + +inline Matrix& Matrix::operator=(const XMFLOAT3X3& M) noexcept +{ + _11 = M._11; + _12 = M._12; + _13 = M._13; + _14 = 0.f; + _21 = M._21; + _22 = M._22; + _23 = M._23; + _24 = 0.f; + _31 = M._31; + _32 = M._32; + _33 = M._33; + _34 = 0.f; + _41 = 0.f; + _42 = 0.f; + _43 = 0.f; + _44 = 1.f; + return *this; +} + +inline Matrix& Matrix::operator=(const XMFLOAT4X3& M) noexcept +{ + _11 = M._11; + _12 = M._12; + _13 = M._13; + _14 = 0.f; + _21 = M._21; + _22 = M._22; + _23 = M._23; + _24 = 0.f; + _31 = M._31; + _32 = M._32; + _33 = M._33; + _34 = 0.f; + _41 = M._41; + _42 = M._42; + _43 = M._43; + _44 = 1.f; + return *this; +} + +inline Matrix& Matrix::operator+=(const Matrix& M) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorAdd(x1, y1); + x2 = XMVectorAdd(x2, y2); + x3 = XMVectorAdd(x3, y3); + x4 = XMVectorAdd(x4, y4); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +inline Matrix& Matrix::operator-=(const Matrix& M) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorSubtract(x1, y1); + x2 = XMVectorSubtract(x2, y2); + x3 = XMVectorSubtract(x3, y3); + x4 = XMVectorSubtract(x4, y4); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +inline Matrix& Matrix::operator*=(const Matrix& M) noexcept +{ + using namespace DirectX; + const XMMATRIX M1 = XMLoadFloat4x4(this); + const XMMATRIX M2 = XMLoadFloat4x4(&M); + const XMMATRIX X = XMMatrixMultiply(M1, M2); + XMStoreFloat4x4(this, X); + return *this; +} + +inline Matrix& Matrix::operator*=(float S) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + x1 = XMVectorScale(x1, S); + x2 = XMVectorScale(x2, S); + x3 = XMVectorScale(x3, S); + x4 = XMVectorScale(x4, S); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +inline Matrix& Matrix::operator/=(float S) noexcept +{ + using namespace DirectX; + assert(S != 0.f); + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const float rs = 1.f / S; + + x1 = XMVectorScale(x1, rs); + x2 = XMVectorScale(x2, rs); + x3 = XMVectorScale(x3, rs); + x4 = XMVectorScale(x4, rs); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +inline Matrix& Matrix::operator/=(const Matrix& M) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorDivide(x1, y1); + x2 = XMVectorDivide(x2, y2); + x3 = XMVectorDivide(x3, y3); + x4 = XMVectorDivide(x4, y4); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Matrix Matrix::operator-() const noexcept +{ + using namespace DirectX; + XMVECTOR v1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR v2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR v3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR v4 = XMLoadFloat4(reinterpret_cast(&_41)); + + v1 = XMVectorNegate(v1); + v2 = XMVectorNegate(v2); + v3 = XMVectorNegate(v3); + v4 = XMVectorNegate(v4); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), v1); + XMStoreFloat4(reinterpret_cast(&R._21), v2); + XMStoreFloat4(reinterpret_cast(&R._31), v3); + XMStoreFloat4(reinterpret_cast(&R._41), v4); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Matrix operator+(const Matrix& M1, const Matrix& M2) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorAdd(x1, y1); + x2 = XMVectorAdd(x2, y2); + x3 = XMVectorAdd(x3, y3); + x4 = XMVectorAdd(x4, y4); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator-(const Matrix& M1, const Matrix& M2) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorSubtract(x1, y1); + x2 = XMVectorSubtract(x2, y2); + x3 = XMVectorSubtract(x3, y3); + x4 = XMVectorSubtract(x4, y4); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator*(const Matrix& M1, const Matrix& M2) noexcept +{ + using namespace DirectX; + const XMMATRIX m1 = XMLoadFloat4x4(&M1); + const XMMATRIX m2 = XMLoadFloat4x4(&M2); + const XMMATRIX X = XMMatrixMultiply(m1, m2); + + Matrix R; + XMStoreFloat4x4(&R, X); + return R; +} + +inline Matrix operator*(const Matrix& M, float S) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorScale(x1, S); + x2 = XMVectorScale(x2, S); + x3 = XMVectorScale(x3, S); + x4 = XMVectorScale(x4, S); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator/(const Matrix& M, float S) noexcept +{ + using namespace DirectX; + assert(S != 0.f); + + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + const float rs = 1.f / S; + + x1 = XMVectorScale(x1, rs); + x2 = XMVectorScale(x2, rs); + x3 = XMVectorScale(x3, rs); + x4 = XMVectorScale(x4, rs); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator/(const Matrix& M1, const Matrix& M2) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorDivide(x1, y1); + x2 = XMVectorDivide(x2, y2); + x3 = XMVectorDivide(x3, y3); + x4 = XMVectorDivide(x4, y4); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator*(float S, const Matrix& M) noexcept +{ + using namespace DirectX; + + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorScale(x1, S); + x2 = XMVectorScale(x2, S); + x3 = XMVectorScale(x3, S); + x4 = XMVectorScale(x4, S); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +//------------------------------------------------------------------------------ +// Matrix operations +//------------------------------------------------------------------------------ + +inline bool Matrix::Decompose(Vector3& scale, Quaternion& rotation, Vector3& translation) noexcept +{ + using namespace DirectX; + + XMVECTOR s, r, t; + + if (!XMMatrixDecompose(&s, &r, &t, *this)) + return false; + + XMStoreFloat3(&scale, s); + XMStoreFloat4(&rotation, r); + XMStoreFloat3(&translation, t); + + return true; +} + +inline Matrix Matrix::Transpose() const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + Matrix R; + XMStoreFloat4x4(&R, XMMatrixTranspose(M)); + return R; +} + +inline void Matrix::Transpose(Matrix& result) const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + XMStoreFloat4x4(&result, XMMatrixTranspose(M)); +} + +inline Matrix Matrix::Invert() const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + Matrix R; + XMVECTOR det; + XMStoreFloat4x4(&R, XMMatrixInverse(&det, M)); + return R; +} + +inline void Matrix::Invert(Matrix& result) const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + XMVECTOR det; + XMStoreFloat4x4(&result, XMMatrixInverse(&det, M)); +} + +inline float Matrix::Determinant() const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + return XMVectorGetX(XMMatrixDeterminant(M)); +} + +inline Vector3 Matrix::ToEuler() const noexcept +{ + const float cy = sqrtf(_33 * _33 + _31 * _31); + const float cx = atan2f(-_32, cy); + if (cy > 16.f * FLT_EPSILON) + { + return Vector3(cx, atan2f(_31, _33), atan2f(_12, _22)); + } + else + { + return Vector3(cx, 0.f, atan2f(-_21, _11)); + } +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ inline Matrix Matrix::CreateBillboard( + const Vector3& object, const Vector3& cameraPosition, const Vector3& cameraUp, const Vector3* cameraForward) noexcept +{ + using namespace DirectX; + const XMVECTOR O = XMLoadFloat3(&object); + const XMVECTOR C = XMLoadFloat3(&cameraPosition); + XMVECTOR Z = XMVectorSubtract(O, C); + + const XMVECTOR N = XMVector3LengthSq(Z); + if (XMVector3Less(N, g_XMEpsilon)) + { + if (cameraForward) + { + const XMVECTOR F = XMLoadFloat3(cameraForward); + Z = XMVectorNegate(F); + } + else + Z = g_XMNegIdentityR2; + } + else + { + Z = XMVector3Normalize(Z); + } + + const XMVECTOR up = XMLoadFloat3(&cameraUp); + XMVECTOR X = XMVector3Cross(up, Z); + X = XMVector3Normalize(X); + + const XMVECTOR Y = XMVector3Cross(Z, X); + + XMMATRIX M; + M.r[0] = X; + M.r[1] = Y; + M.r[2] = Z; + M.r[3] = XMVectorSetW(O, 1.f); + + Matrix R; + XMStoreFloat4x4(&R, M); + return R; +} + +_Use_decl_annotations_ inline Matrix Matrix::CreateConstrainedBillboard(const Vector3& object, const Vector3& cameraPosition, + const Vector3& rotateAxis, const Vector3* cameraForward, const Vector3* objectForward) noexcept +{ + using namespace DirectX; + + static const XMVECTORF32 s_minAngle = { + {{0.99825467075f, 0.99825467075f, 0.99825467075f, 0.99825467075f}}}; // 1.0 - XMConvertToRadians( 0.1f ); + + const XMVECTOR O = XMLoadFloat3(&object); + const XMVECTOR C = XMLoadFloat3(&cameraPosition); + XMVECTOR faceDir = XMVectorSubtract(O, C); + + const XMVECTOR N = XMVector3LengthSq(faceDir); + if (XMVector3Less(N, g_XMEpsilon)) + { + if (cameraForward) + { + const XMVECTOR F = XMLoadFloat3(cameraForward); + faceDir = XMVectorNegate(F); + } + else + faceDir = g_XMNegIdentityR2; + } + else + { + faceDir = XMVector3Normalize(faceDir); + } + + const XMVECTOR Y = XMLoadFloat3(&rotateAxis); + XMVECTOR X, Z; + + XMVECTOR dot = XMVectorAbs(XMVector3Dot(Y, faceDir)); + if (XMVector3Greater(dot, s_minAngle)) + { + if (objectForward) + { + Z = XMLoadFloat3(objectForward); + dot = XMVectorAbs(XMVector3Dot(Y, Z)); + if (XMVector3Greater(dot, s_minAngle)) + { + dot = XMVectorAbs(XMVector3Dot(Y, g_XMNegIdentityR2)); + Z = (XMVector3Greater(dot, s_minAngle)) ? g_XMIdentityR0 : g_XMNegIdentityR2; + } + } + else + { + dot = XMVectorAbs(XMVector3Dot(Y, g_XMNegIdentityR2)); + Z = (XMVector3Greater(dot, s_minAngle)) ? g_XMIdentityR0 : g_XMNegIdentityR2; + } + + X = XMVector3Cross(Y, Z); + X = XMVector3Normalize(X); + + Z = XMVector3Cross(X, Y); + Z = XMVector3Normalize(Z); + } + else + { + X = XMVector3Cross(Y, faceDir); + X = XMVector3Normalize(X); + + Z = XMVector3Cross(X, Y); + Z = XMVector3Normalize(Z); + } + + XMMATRIX M; + M.r[0] = X; + M.r[1] = Y; + M.r[2] = Z; + M.r[3] = XMVectorSetW(O, 1.f); + + Matrix R; + XMStoreFloat4x4(&R, M); + return R; +} + +inline Matrix Matrix::CreateTranslation(const Vector3& position) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixTranslation(position.x, position.y, position.z)); + return R; +} + +inline Matrix Matrix::CreateTranslation(float x, float y, float z) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixTranslation(x, y, z)); + return R; +} + +inline Matrix Matrix::CreateScale(const Vector3& scales) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixScaling(scales.x, scales.y, scales.z)); + return R; +} + +inline Matrix Matrix::CreateScale(float xs, float ys, float zs) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixScaling(xs, ys, zs)); + return R; +} + +inline Matrix Matrix::CreateScale(float scale) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixScaling(scale, scale, scale)); + return R; +} + +inline Matrix Matrix::CreateRotationX(float radians) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationX(radians)); + return R; +} + +inline Matrix Matrix::CreateRotationY(float radians) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationY(radians)); + return R; +} + +inline Matrix Matrix::CreateRotationZ(float radians) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationZ(radians)); + return R; +} + +inline Matrix Matrix::CreateFromAxisAngle(const Vector3& axis, float angle) noexcept +{ + using namespace DirectX; + Matrix R; + const XMVECTOR a = XMLoadFloat3(&axis); + XMStoreFloat4x4(&R, XMMatrixRotationAxis(a, angle)); + return R; +} + +inline Matrix Matrix::CreatePerspectiveFieldOfView(float fov, float aspectRatio, float nearPlane, float farPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixPerspectiveFovRH(fov, aspectRatio, nearPlane, farPlane)); + return R; +} + +inline Matrix Matrix::CreatePerspective(float width, float height, float nearPlane, float farPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixPerspectiveRH(width, height, nearPlane, farPlane)); + return R; +} + +inline Matrix Matrix::CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlane, float farPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixPerspectiveOffCenterRH(left, right, bottom, top, nearPlane, farPlane)); + return R; +} + +inline Matrix Matrix::CreateOrthographic(float width, float height, float zNearPlane, float zFarPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixOrthographicRH(width, height, zNearPlane, zFarPlane)); + return R; +} + +inline Matrix Matrix::CreateOrthographicOffCenter( + float left, float right, float bottom, float top, float zNearPlane, float zFarPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixOrthographicOffCenterRH(left, right, bottom, top, zNearPlane, zFarPlane)); + return R; +} + +inline Matrix Matrix::CreateLookAt(const Vector3& eye, const Vector3& target, const Vector3& up) noexcept +{ + using namespace DirectX; + Matrix R; + const XMVECTOR eyev = XMLoadFloat3(&eye); + const XMVECTOR targetv = XMLoadFloat3(&target); + const XMVECTOR upv = XMLoadFloat3(&up); + XMStoreFloat4x4(&R, XMMatrixLookAtRH(eyev, targetv, upv)); + return R; +} + +inline Matrix Matrix::CreateWorld(const Vector3& position, const Vector3& forward, const Vector3& up) noexcept +{ + using namespace DirectX; + const XMVECTOR zaxis = XMVector3Normalize(XMVectorNegate(XMLoadFloat3(&forward))); + XMVECTOR yaxis = XMLoadFloat3(&up); + const XMVECTOR xaxis = XMVector3Normalize(XMVector3Cross(yaxis, zaxis)); + yaxis = XMVector3Cross(zaxis, xaxis); + + Matrix R; + XMStoreFloat3(reinterpret_cast(&R._11), xaxis); + XMStoreFloat3(reinterpret_cast(&R._21), yaxis); + XMStoreFloat3(reinterpret_cast(&R._31), zaxis); + R._14 = R._24 = R._34 = 0.f; + R._41 = position.x; + R._42 = position.y; + R._43 = position.z; + R._44 = 1.f; + return R; +} + +inline Matrix Matrix::CreateFromQuaternion(const Quaternion& rotation) noexcept +{ + using namespace DirectX; + const XMVECTOR quatv = XMLoadFloat4(&rotation); + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationQuaternion(quatv)); + return R; +} + +inline Matrix Matrix::CreateFromYawPitchRoll(float yaw, float pitch, float roll) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationRollPitchYaw(pitch, yaw, roll)); + return R; +} + +inline Matrix Matrix::CreateFromYawPitchRoll(const Vector3& angles) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationRollPitchYawFromVector(angles)); + return R; +} + +inline Matrix Matrix::CreateShadow(const Vector3& lightDir, const Plane& plane) noexcept +{ + using namespace DirectX; + const XMVECTOR light = XMLoadFloat3(&lightDir); + const XMVECTOR planev = XMLoadFloat4(&plane); + Matrix R; + XMStoreFloat4x4(&R, XMMatrixShadow(planev, light)); + return R; +} + +inline Matrix Matrix::CreateReflection(const Plane& plane) noexcept +{ + using namespace DirectX; + const XMVECTOR planev = XMLoadFloat4(&plane); + Matrix R; + XMStoreFloat4x4(&R, XMMatrixReflect(planev)); + return R; +} + +inline void Matrix::Lerp(const Matrix& M1, const Matrix& M2, float t, Matrix& result) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorLerp(x1, y1, t); + x2 = XMVectorLerp(x2, y2, t); + x3 = XMVectorLerp(x3, y3, t); + x4 = XMVectorLerp(x4, y4, t); + + XMStoreFloat4(reinterpret_cast(&result._11), x1); + XMStoreFloat4(reinterpret_cast(&result._21), x2); + XMStoreFloat4(reinterpret_cast(&result._31), x3); + XMStoreFloat4(reinterpret_cast(&result._41), x4); +} + +inline Matrix Matrix::Lerp(const Matrix& M1, const Matrix& M2, float t) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorLerp(x1, y1, t); + x2 = XMVectorLerp(x2, y2, t); + x3 = XMVectorLerp(x3, y3, t); + x4 = XMVectorLerp(x4, y4, t); + + Matrix result; + XMStoreFloat4(reinterpret_cast(&result._11), x1); + XMStoreFloat4(reinterpret_cast(&result._21), x2); + XMStoreFloat4(reinterpret_cast(&result._31), x3); + XMStoreFloat4(reinterpret_cast(&result._41), x4); + return result; +} + +inline void Matrix::Transform(const Matrix& M, const Quaternion& rotation, Matrix& result) noexcept +{ + using namespace DirectX; + const XMVECTOR quatv = XMLoadFloat4(&rotation); + + const XMMATRIX M0 = XMLoadFloat4x4(&M); + const XMMATRIX M1 = XMMatrixRotationQuaternion(quatv); + + XMStoreFloat4x4(&result, XMMatrixMultiply(M0, M1)); +} + +inline Matrix Matrix::Transform(const Matrix& M, const Quaternion& rotation) noexcept +{ + using namespace DirectX; + const XMVECTOR quatv = XMLoadFloat4(&rotation); + + const XMMATRIX M0 = XMLoadFloat4x4(&M); + const XMMATRIX M1 = XMMatrixRotationQuaternion(quatv); + + Matrix result; + XMStoreFloat4x4(&result, XMMatrixMultiply(M0, M1)); + return result; +} + +/**************************************************************************** + * + * Plane + * + ****************************************************************************/ + +inline Plane::Plane(const Vector3& point1, const Vector3& point2, const Vector3& point3) noexcept +{ + using namespace DirectX; + const XMVECTOR P0 = XMLoadFloat3(&point1); + const XMVECTOR P1 = XMLoadFloat3(&point2); + const XMVECTOR P2 = XMLoadFloat3(&point3); + XMStoreFloat4(this, XMPlaneFromPoints(P0, P1, P2)); +} + +inline Plane::Plane(const Vector3& point, const Vector3& normal) noexcept +{ + using namespace DirectX; + const XMVECTOR P = XMLoadFloat3(&point); + const XMVECTOR N = XMLoadFloat3(&normal); + XMStoreFloat4(this, XMPlaneFromPointNormal(P, N)); +} + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Plane::operator==(const Plane& p) const noexcept +{ + using namespace DirectX; + const XMVECTOR p1 = XMLoadFloat4(this); + const XMVECTOR p2 = XMLoadFloat4(&p); + return XMPlaneEqual(p1, p2); +} + +inline bool Plane::operator!=(const Plane& p) const noexcept +{ + using namespace DirectX; + const XMVECTOR p1 = XMLoadFloat4(this); + const XMVECTOR p2 = XMLoadFloat4(&p); + return XMPlaneNotEqual(p1, p2); +} + +//------------------------------------------------------------------------------ +// Plane operations +//------------------------------------------------------------------------------ + +inline void Plane::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + XMStoreFloat4(this, XMPlaneNormalize(p)); +} + +inline void Plane::Normalize(Plane& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + XMStoreFloat4(&result, XMPlaneNormalize(p)); +} + +inline float Plane::Dot(const Vector4& v) const noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + const XMVECTOR v0 = XMLoadFloat4(&v); + return XMVectorGetX(XMPlaneDot(p, v0)); +} + +inline float Plane::DotCoordinate(const Vector3& position) const noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + const XMVECTOR v0 = XMLoadFloat3(&position); + return XMVectorGetX(XMPlaneDotCoord(p, v0)); +} + +inline float Plane::DotNormal(const Vector3& normal) const noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + const XMVECTOR n0 = XMLoadFloat3(&normal); + return XMVectorGetX(XMPlaneDotNormal(p, n0)); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline void Plane::Transform(const Plane& plane, const Matrix& M, Plane& result) noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(&plane); + const XMMATRIX m0 = XMLoadFloat4x4(&M); + XMStoreFloat4(&result, XMPlaneTransform(p, m0)); +} + +inline Plane Plane::Transform(const Plane& plane, const Matrix& M) noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(&plane); + const XMMATRIX m0 = XMLoadFloat4x4(&M); + + Plane result; + XMStoreFloat4(&result, XMPlaneTransform(p, m0)); + return result; +} + +inline void Plane::Transform(const Plane& plane, const Quaternion& rotation, Plane& result) noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(&plane); + const XMVECTOR q = XMLoadFloat4(&rotation); + XMVECTOR X = XMVector3Rotate(p, q); + X = XMVectorSelect(p, X, g_XMSelect1110); // result.d = plane.d + XMStoreFloat4(&result, X); +} + +inline Plane Plane::Transform(const Plane& plane, const Quaternion& rotation) noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(&plane); + const XMVECTOR q = XMLoadFloat4(&rotation); + XMVECTOR X = XMVector3Rotate(p, q); + X = XMVectorSelect(p, X, g_XMSelect1110); // result.d = plane.d + + Plane result; + XMStoreFloat4(&result, X); + return result; +} + +/**************************************************************************** + * + * Quaternion + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Quaternion::operator==(const Quaternion& q) const noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + return XMQuaternionEqual(q1, q2); +} + +inline bool Quaternion::operator!=(const Quaternion& q) const noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + return XMQuaternionNotEqual(q1, q2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Quaternion& Quaternion::operator+=(const Quaternion& q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + XMStoreFloat4(this, XMVectorAdd(q1, q2)); + return *this; +} + +inline Quaternion& Quaternion::operator-=(const Quaternion& q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + XMStoreFloat4(this, XMVectorSubtract(q1, q2)); + return *this; +} + +inline Quaternion& Quaternion::operator*=(const Quaternion& q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + XMStoreFloat4(this, XMQuaternionMultiply(q1, q2)); + return *this; +} + +inline Quaternion& Quaternion::operator*=(float S) noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(this, XMVectorScale(q, S)); + return *this; +} + +inline Quaternion& Quaternion::operator/=(const Quaternion& q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + XMVECTOR q2 = XMLoadFloat4(&q); + q2 = XMQuaternionInverse(q2); + XMStoreFloat4(this, XMQuaternionMultiply(q1, q2)); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Quaternion Quaternion::operator-() const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + + Quaternion R; + XMStoreFloat4(&R, XMVectorNegate(q)); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Quaternion operator+(const Quaternion& Q1, const Quaternion& Q2) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q1); + const XMVECTOR q2 = XMLoadFloat4(&Q2); + + Quaternion R; + XMStoreFloat4(&R, XMVectorAdd(q1, q2)); + return R; +} + +inline Quaternion operator-(const Quaternion& Q1, const Quaternion& Q2) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q1); + const XMVECTOR q2 = XMLoadFloat4(&Q2); + + Quaternion R; + XMStoreFloat4(&R, XMVectorSubtract(q1, q2)); + return R; +} + +inline Quaternion operator*(const Quaternion& Q1, const Quaternion& Q2) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q1); + const XMVECTOR q2 = XMLoadFloat4(&Q2); + + Quaternion R; + XMStoreFloat4(&R, XMQuaternionMultiply(q1, q2)); + return R; +} + +inline Quaternion operator*(const Quaternion& Q, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(&Q); + + Quaternion R; + XMStoreFloat4(&R, XMVectorScale(q, S)); + return R; +} + +inline Quaternion operator/(const Quaternion& Q1, const Quaternion& Q2) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q1); + XMVECTOR q2 = XMLoadFloat4(&Q2); + q2 = XMQuaternionInverse(q2); + + Quaternion R; + XMStoreFloat4(&R, XMQuaternionMultiply(q1, q2)); + return R; +} + +inline Quaternion operator*(float S, const Quaternion& Q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q); + + Quaternion R; + XMStoreFloat4(&R, XMVectorScale(q1, S)); + return R; +} + +//------------------------------------------------------------------------------ +// Quaternion operations +//------------------------------------------------------------------------------ + +inline float Quaternion::Length() const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + return XMVectorGetX(XMQuaternionLength(q)); +} + +inline float Quaternion::LengthSquared() const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + return XMVectorGetX(XMQuaternionLengthSq(q)); +} + +inline void Quaternion::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(this, XMQuaternionNormalize(q)); +} + +inline void Quaternion::Normalize(Quaternion& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(&result, XMQuaternionNormalize(q)); +} + +inline void Quaternion::Conjugate() noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(this, XMQuaternionConjugate(q)); +} + +inline void Quaternion::Conjugate(Quaternion& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(&result, XMQuaternionConjugate(q)); +} + +inline void Quaternion::Inverse(Quaternion& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(&result, XMQuaternionInverse(q)); +} + +inline float Quaternion::Dot(const Quaternion& q) const noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + return XMVectorGetX(XMQuaternionDot(q1, q2)); +} + +inline void Quaternion::RotateTowards(const Quaternion& target, float maxAngle) noexcept +{ + RotateTowards(target, maxAngle, *this); +} + +inline Vector3 Quaternion::ToEuler() const noexcept +{ + const float xx = x * x; + const float yy = y * y; + const float zz = z * z; + + const float m31 = 2.f * x * z + 2.f * y * w; + const float m32 = 2.f * y * z - 2.f * x * w; + const float m33 = 1.f - 2.f * xx - 2.f * yy; + + const float cy = sqrtf(m33 * m33 + m31 * m31); + const float cx = atan2f(-m32, cy); + if (cy > 16.f * FLT_EPSILON) + { + const float m12 = 2.f * x * y + 2.f * z * w; + const float m22 = 1.f - 2.f * xx - 2.f * zz; + + return Vector3(cx, atan2f(m31, m33), atan2f(m12, m22)); + } + else + { + const float m11 = 1.f - 2.f * yy - 2.f * zz; + const float m21 = 2.f * x * y - 2.f * z * w; + + return Vector3(cx, 0.f, atan2f(-m21, m11)); + } +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline Quaternion Quaternion::CreateFromAxisAngle(const Vector3& axis, float angle) noexcept +{ + using namespace DirectX; + const XMVECTOR a = XMLoadFloat3(&axis); + + Quaternion R; + XMStoreFloat4(&R, XMQuaternionRotationAxis(a, angle)); + return R; +} + +inline Quaternion Quaternion::CreateFromYawPitchRoll(float yaw, float pitch, float roll) noexcept +{ + using namespace DirectX; + Quaternion R; + XMStoreFloat4(&R, XMQuaternionRotationRollPitchYaw(pitch, yaw, roll)); + return R; +} + +inline Quaternion Quaternion::CreateFromYawPitchRoll(const Vector3& angles) noexcept +{ + using namespace DirectX; + Quaternion R; + XMStoreFloat4(&R, XMQuaternionRotationRollPitchYawFromVector(angles)); + return R; +} + +inline Quaternion Quaternion::CreateFromRotationMatrix(const Matrix& M) noexcept +{ + using namespace DirectX; + const XMMATRIX M0 = XMLoadFloat4x4(&M); + + Quaternion R; + XMStoreFloat4(&R, XMQuaternionRotationMatrix(M0)); + return R; +} + +inline void Quaternion::Lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion& result) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + const XMVECTOR dot = XMVector4Dot(Q0, Q1); + + XMVECTOR R; + if (XMVector4GreaterOrEqual(dot, XMVectorZero())) + { + R = XMVectorLerp(Q0, Q1, t); + } + else + { + const XMVECTOR tv = XMVectorReplicate(t); + const XMVECTOR t1v = XMVectorReplicate(1.f - t); + const XMVECTOR X0 = XMVectorMultiply(Q0, t1v); + const XMVECTOR X1 = XMVectorMultiply(Q1, tv); + R = XMVectorSubtract(X0, X1); + } + + XMStoreFloat4(&result, XMQuaternionNormalize(R)); +} + +inline Quaternion Quaternion::Lerp(const Quaternion& q1, const Quaternion& q2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + const XMVECTOR dot = XMVector4Dot(Q0, Q1); + + XMVECTOR R; + if (XMVector4GreaterOrEqual(dot, XMVectorZero())) + { + R = XMVectorLerp(Q0, Q1, t); + } + else + { + const XMVECTOR tv = XMVectorReplicate(t); + const XMVECTOR t1v = XMVectorReplicate(1.f - t); + const XMVECTOR X0 = XMVectorMultiply(Q0, t1v); + const XMVECTOR X1 = XMVectorMultiply(Q1, tv); + R = XMVectorSubtract(X0, X1); + } + + Quaternion result; + XMStoreFloat4(&result, XMQuaternionNormalize(R)); + return result; +} + +inline void Quaternion::Slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion& result) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + XMStoreFloat4(&result, XMQuaternionSlerp(Q0, Q1, t)); +} + +inline Quaternion Quaternion::Slerp(const Quaternion& q1, const Quaternion& q2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + Quaternion result; + XMStoreFloat4(&result, XMQuaternionSlerp(Q0, Q1, t)); + return result; +} + +inline void Quaternion::Concatenate(const Quaternion& q1, const Quaternion& q2, Quaternion& result) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + XMStoreFloat4(&result, XMQuaternionMultiply(Q1, Q0)); +} + +inline Quaternion Quaternion::Concatenate(const Quaternion& q1, const Quaternion& q2) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + Quaternion result; + XMStoreFloat4(&result, XMQuaternionMultiply(Q1, Q0)); + return result; +} + +inline Quaternion Quaternion::FromToRotation(const Vector3& fromDir, const Vector3& toDir) noexcept +{ + Quaternion result; + FromToRotation(fromDir, toDir, result); + return result; +} + +inline Quaternion Quaternion::LookRotation(const Vector3& forward, const Vector3& up) noexcept +{ + Quaternion result; + LookRotation(forward, up, result); + return result; +} + +inline float Quaternion::Angle(const Quaternion& q1, const Quaternion& q2) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + // We can use the conjugate here instead of inverse assuming q1 & q2 are normalized. + XMVECTOR R = XMQuaternionMultiply(XMQuaternionConjugate(Q0), Q1); + + const float rs = XMVectorGetW(R); + R = XMVector3Length(R); + return 2.f * atan2f(XMVectorGetX(R), rs); +} + +/**************************************************************************** + * + * Color + * + ****************************************************************************/ + +inline Color::Color(const DirectX::PackedVector::XMCOLOR& Packed) noexcept +{ + using namespace DirectX; + XMStoreFloat4(this, PackedVector::XMLoadColor(&Packed)); +} + +inline Color::Color(const DirectX::PackedVector::XMUBYTEN4& Packed) noexcept +{ + using namespace DirectX; + XMStoreFloat4(this, PackedVector::XMLoadUByteN4(&Packed)); +} + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ +inline bool Color::operator==(const Color& c) const noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + return XMColorEqual(c1, c2); +} + +inline bool Color::operator!=(const Color& c) const noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + return XMColorNotEqual(c1, c2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Color& Color::operator=(const DirectX::PackedVector::XMCOLOR& Packed) noexcept +{ + using namespace DirectX; + XMStoreFloat4(this, PackedVector::XMLoadColor(&Packed)); + return *this; +} + +inline Color& Color::operator=(const DirectX::PackedVector::XMUBYTEN4& Packed) noexcept +{ + using namespace DirectX; + XMStoreFloat4(this, PackedVector::XMLoadUByteN4(&Packed)); + return *this; +} + +inline Color& Color::operator+=(const Color& c) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + XMStoreFloat4(this, XMVectorAdd(c1, c2)); + return *this; +} + +inline Color& Color::operator-=(const Color& c) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + XMStoreFloat4(this, XMVectorSubtract(c1, c2)); + return *this; +} + +inline Color& Color::operator*=(const Color& c) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + XMStoreFloat4(this, XMVectorMultiply(c1, c2)); + return *this; +} + +inline Color& Color::operator*=(float S) noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMVectorScale(c, S)); + return *this; +} + +inline Color& Color::operator/=(const Color& c) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + XMStoreFloat4(this, XMVectorDivide(c1, c2)); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Color Color::operator-() const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + Color R; + XMStoreFloat4(&R, XMVectorNegate(c)); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Color operator+(const Color& C1, const Color& C2) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C1); + const XMVECTOR c2 = XMLoadFloat4(&C2); + Color R; + XMStoreFloat4(&R, XMVectorAdd(c1, c2)); + return R; +} + +inline Color operator-(const Color& C1, const Color& C2) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C1); + const XMVECTOR c2 = XMLoadFloat4(&C2); + Color R; + XMStoreFloat4(&R, XMVectorSubtract(c1, c2)); + return R; +} + +inline Color operator*(const Color& C1, const Color& C2) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C1); + const XMVECTOR c2 = XMLoadFloat4(&C2); + Color R; + XMStoreFloat4(&R, XMVectorMultiply(c1, c2)); + return R; +} + +inline Color operator*(const Color& C, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(&C); + Color R; + XMStoreFloat4(&R, XMVectorScale(c, S)); + return R; +} + +inline Color operator/(const Color& C1, const Color& C2) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C1); + const XMVECTOR c2 = XMLoadFloat4(&C2); + Color R; + XMStoreFloat4(&R, XMVectorDivide(c1, c2)); + return R; +} + +inline Color operator*(float S, const Color& C) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C); + Color R; + XMStoreFloat4(&R, XMVectorScale(c1, S)); + return R; +} + +//------------------------------------------------------------------------------ +// Color operations +//------------------------------------------------------------------------------ + +inline DirectX::PackedVector::XMCOLOR Color::BGRA() const noexcept +{ + using namespace DirectX; + const XMVECTOR clr = XMLoadFloat4(this); + PackedVector::XMCOLOR Packed; + PackedVector::XMStoreColor(&Packed, clr); + return Packed; +} + +inline DirectX::PackedVector::XMUBYTEN4 Color::RGBA() const noexcept +{ + using namespace DirectX; + const XMVECTOR clr = XMLoadFloat4(this); + PackedVector::XMUBYTEN4 Packed; + PackedVector::XMStoreUByteN4(&Packed, clr); + return Packed; +} + +inline Vector3 Color::ToVector3() const noexcept +{ + return Vector3(x, y, z); +} + +inline Vector4 Color::ToVector4() const noexcept +{ + return Vector4(x, y, z, w); +} + +inline void Color::Negate() noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMColorNegative(c)); +} + +inline void Color::Negate(Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(&result, XMColorNegative(c)); +} + +inline void Color::Saturate() noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMVectorSaturate(c)); +} + +inline void Color::Saturate(Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(&result, XMVectorSaturate(c)); +} + +inline void Color::Premultiply() noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMVECTOR a = XMVectorSplatW(c); + a = XMVectorSelect(g_XMIdentityR3, a, g_XMSelect1110); + XMStoreFloat4(this, XMVectorMultiply(c, a)); +} + +inline void Color::Premultiply(Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMVECTOR a = XMVectorSplatW(c); + a = XMVectorSelect(g_XMIdentityR3, a, g_XMSelect1110); + XMStoreFloat4(&result, XMVectorMultiply(c, a)); +} + +inline void Color::AdjustSaturation(float sat) noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMColorAdjustSaturation(c, sat)); +} + +inline void Color::AdjustSaturation(float sat, Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(&result, XMColorAdjustSaturation(c, sat)); +} + +inline void Color::AdjustContrast(float contrast) noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMColorAdjustContrast(c, contrast)); +} + +inline void Color::AdjustContrast(float contrast, Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(&result, XMColorAdjustContrast(c, contrast)); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline void Color::Modulate(const Color& c1, const Color& c2, Color& result) noexcept +{ + using namespace DirectX; + const XMVECTOR C0 = XMLoadFloat4(&c1); + const XMVECTOR C1 = XMLoadFloat4(&c2); + XMStoreFloat4(&result, XMColorModulate(C0, C1)); +} + +inline Color Color::Modulate(const Color& c1, const Color& c2) noexcept +{ + using namespace DirectX; + const XMVECTOR C0 = XMLoadFloat4(&c1); + const XMVECTOR C1 = XMLoadFloat4(&c2); + + Color result; + XMStoreFloat4(&result, XMColorModulate(C0, C1)); + return result; +} + +inline void Color::Lerp(const Color& c1, const Color& c2, float t, Color& result) noexcept +{ + using namespace DirectX; + const XMVECTOR C0 = XMLoadFloat4(&c1); + const XMVECTOR C1 = XMLoadFloat4(&c2); + XMStoreFloat4(&result, XMVectorLerp(C0, C1, t)); +} + +inline Color Color::Lerp(const Color& c1, const Color& c2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR C0 = XMLoadFloat4(&c1); + const XMVECTOR C1 = XMLoadFloat4(&c2); + + Color result; + XMStoreFloat4(&result, XMVectorLerp(C0, C1, t)); + return result; +} + +/**************************************************************************** + * + * Ray + * + ****************************************************************************/ + +//----------------------------------------------------------------------------- +// Comparision operators +//------------------------------------------------------------------------------ +inline bool Ray::operator==(const Ray& r) const noexcept +{ + using namespace DirectX; + const XMVECTOR r1p = XMLoadFloat3(&position); + const XMVECTOR r2p = XMLoadFloat3(&r.position); + const XMVECTOR r1d = XMLoadFloat3(&direction); + const XMVECTOR r2d = XMLoadFloat3(&r.direction); + return XMVector3Equal(r1p, r2p) && XMVector3Equal(r1d, r2d); +} + +inline bool Ray::operator!=(const Ray& r) const noexcept +{ + using namespace DirectX; + const XMVECTOR r1p = XMLoadFloat3(&position); + const XMVECTOR r2p = XMLoadFloat3(&r.position); + const XMVECTOR r1d = XMLoadFloat3(&direction); + const XMVECTOR r2d = XMLoadFloat3(&r.direction); + return XMVector3NotEqual(r1p, r2p) && XMVector3NotEqual(r1d, r2d); +} + +//----------------------------------------------------------------------------- +// Ray operators +//------------------------------------------------------------------------------ + +inline bool Ray::Intersects(const BoundingSphere& sphere, _Out_ float& Dist) const noexcept +{ + return sphere.Intersects(position, direction, Dist); +} + +inline bool Ray::Intersects(const BoundingBox& box, _Out_ float& Dist) const noexcept +{ + return box.Intersects(position, direction, Dist); +} + +inline bool Ray::Intersects(const Vector3& tri0, const Vector3& tri1, const Vector3& tri2, _Out_ float& Dist) const noexcept +{ + return DirectX::TriangleTests::Intersects(position, direction, tri0, tri1, tri2, Dist); +} + +inline bool Ray::Intersects(const Plane& plane, _Out_ float& Dist) const noexcept +{ + using namespace DirectX; + + const XMVECTOR p = XMLoadFloat4(&plane); + const XMVECTOR dir = XMLoadFloat3(&direction); + + const XMVECTOR nd = XMPlaneDotNormal(p, dir); + + if (XMVector3LessOrEqual(XMVectorAbs(nd), g_RayEpsilon)) + { + Dist = 0.f; + return false; + } + else + { + // t = -(dot(n,origin) + D) / dot(n,dir) + const XMVECTOR pos = XMLoadFloat3(&position); + XMVECTOR v = XMPlaneDotNormal(p, pos); + v = XMVectorAdd(v, XMVectorSplatW(p)); + v = XMVectorDivide(v, nd); + float dist = -XMVectorGetX(v); + if (dist < 0) + { + Dist = 0.f; + return false; + } + else + { + Dist = dist; + return true; + } + } +} + +/**************************************************************************** + * + * Viewport + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +#if (__cplusplus < 202002L) +inline bool Viewport::operator==(const Viewport& vp) const noexcept +{ + return (x == vp.x && y == vp.y && width == vp.width && height == vp.height && minDepth == vp.minDepth && maxDepth == vp.maxDepth); +} + +inline bool Viewport::operator!=(const Viewport& vp) const noexcept +{ + return (x != vp.x || y != vp.y || width != vp.width || height != vp.height || minDepth != vp.minDepth || maxDepth != vp.maxDepth); +} +#endif + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Viewport& Viewport::operator=(const RECT& rct) noexcept +{ + x = float(rct.left); + y = float(rct.top); + width = float(rct.right - rct.left); + height = float(rct.bottom - rct.top); + minDepth = 0.f; + maxDepth = 1.f; + return *this; +} + +#if defined(__d3d11_h__) || defined(__d3d11_x_h__) +inline Viewport& Viewport::operator=(const D3D11_VIEWPORT& vp) noexcept +{ + x = vp.TopLeftX; + y = vp.TopLeftY; + width = vp.Width; + height = vp.Height; + minDepth = vp.MinDepth; + maxDepth = vp.MaxDepth; + return *this; +} +#endif + +#if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) +inline Viewport& Viewport::operator=(const D3D12_VIEWPORT& vp) noexcept +{ + x = vp.TopLeftX; + y = vp.TopLeftY; + width = vp.Width; + height = vp.Height; + minDepth = vp.MinDepth; + maxDepth = vp.MaxDepth; + return *this; +} +#endif + +//------------------------------------------------------------------------------ +// Viewport operations +//------------------------------------------------------------------------------ + +inline float Viewport::AspectRatio() const noexcept +{ + if (width == 0.f || height == 0.f) + return 0.f; + + return (width / height); +} + +inline Vector3 Viewport::Project(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world) const noexcept +{ + using namespace DirectX; + XMVECTOR v = XMLoadFloat3(&p); + const XMMATRIX projection = XMLoadFloat4x4(&proj); + v = XMVector3Project(v, x, y, width, height, minDepth, maxDepth, projection, view, world); + Vector3 result; + XMStoreFloat3(&result, v); + return result; +} + +inline void Viewport::Project(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world, Vector3& result) const noexcept +{ + using namespace DirectX; + XMVECTOR v = XMLoadFloat3(&p); + const XMMATRIX projection = XMLoadFloat4x4(&proj); + v = XMVector3Project(v, x, y, width, height, minDepth, maxDepth, projection, view, world); + XMStoreFloat3(&result, v); +} + +inline Vector3 Viewport::Unproject(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world) const noexcept +{ + using namespace DirectX; + XMVECTOR v = XMLoadFloat3(&p); + const XMMATRIX projection = XMLoadFloat4x4(&proj); + v = XMVector3Unproject(v, x, y, width, height, minDepth, maxDepth, projection, view, world); + Vector3 result; + XMStoreFloat3(&result, v); + return result; +} + +inline void Viewport::Unproject( + const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world, Vector3& result) const noexcept +{ + using namespace DirectX; + XMVECTOR v = XMLoadFloat3(&p); + const XMMATRIX projection = XMLoadFloat4x4(&proj); + v = XMVector3Unproject(v, x, y, width, height, minDepth, maxDepth, projection, view, world); + XMStoreFloat3(&result, v); +} diff --git a/Atlas/StaticMesh.cpp b/Atlas/StaticMesh.cpp new file mode 100644 index 00000000..aa2f29b0 --- /dev/null +++ b/Atlas/StaticMesh.cpp @@ -0,0 +1,628 @@ +#include "StaticMesh.h" + +#include "DDSTextureLoader.h" +#include "Logger.h" +#include "Renderer.h" + +#include +#include + +struct cb1_InstanceData +{ + XMVECTOR MeshTransform; + XMVECTOR UVTransform; + XMVECTOR InstanceTransformMatrices[]; +}; + +struct cb12_View +{ + XMMATRIX WorldToProjective; // cb12[0-3] + XMMATRIX CameraToWorld; // cb12[4-7] + XMVECTOR Target; // cb12[8], viewport dimensions 0/1 is width/height, 2/3 is 1/width and 1/height + XMVECTOR Unk09; + XMVECTOR CameraPosition; + XMMATRIX Unk11; // idk why but cb12[14].z must not be zero ever + XMVECTOR ViewMiscellaneous; // cb12[15]; cb12[10] is camera position +}; + +StaticMesh::~StaticMesh() +{ + Logger::Log("Deleting static mesh"); + + SAFE_RELEASE(IndexBuffer); + + for (auto& buffer : VertexBuffers) + { + SAFE_RELEASE(buffer); + } + VertexBuffers.clear(); + + // Logger::Log("There are %d parts to destroy", Parts.size()); + + for (auto& part : Parts) + { + part.reset(); + } + Parts.clear(); +} + +ID3D11Buffer* StaticMesh::GetIndexBuffer() const +{ + return IndexBuffer; +} + +ID3D11Buffer* const* StaticMesh::GetVertexBuffers() const +{ + return VertexBuffers.data(); +} + +HRESULT StaticMesh::Initialise(ID3D11Device* Device) +{ + this->Device = Device; + + return S_OK; +} + +HRESULT StaticMesh::AddStaticMeshBufferGroup(const BufferGroup& bufferGroup) +{ + HRESULT hr = S_OK; + + hr = CreateVertexBuffers(bufferGroup.VertexBuffers); + if (FAILED(hr)) + return hr; + + hr = CreateIndexBuffer(bufferGroup.IndexBuffer); + if (FAILED(hr)) + return hr; + + return hr; +} + +HRESULT StaticMesh::Render(ID3D11DeviceContext* DeviceContext, Camera* Camera, float DeltaTime) +{ + if (GetVertexBuffers() == nullptr || GetIndexBuffer() == nullptr) + { + Logger::Log("No vertex or index buffers set."); + return E_FAIL; + } + + // todo fix this how tf does it work?? maybe its ignored idk + UINT strides[2] = {16, 4}; + UINT offsets[2] = {0, 0}; + DeviceContext->IASetVertexBuffers(0, 2, GetVertexBuffers(), strides, offsets); + DeviceContext->IASetIndexBuffer(GetIndexBuffer(), DXGI_FORMAT_R16_UINT, 0); + DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + // DeviceContext->DrawIndexed(1116, 0, 0); // 11529 entire thing, 1116 first part lod0 + // DeviceContext->DrawIndexed(3606, 1116, 0); + // DeviceContext->DrawIndexed(1503, 5718, 0); + + HRESULT hr = S_OK; + for (auto& part : Parts) + { + hr = part->Render(DeviceContext, Camera, DeltaTime); + if (FAILED(hr)) + { + return hr; + } + } + + return S_OK; +} + +HRESULT StaticMesh::CreateIndexBuffer(const Blob& indexBlob) +{ + byte* IndexData = new byte[indexBlob.Size]; + memcpy(IndexData, indexBlob.Data, indexBlob.Size); + + D3D11_BUFFER_DESC bd{}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = indexBlob.Size; + bd.BindFlags = D3D11_BIND_INDEX_BUFFER; + bd.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA InitData = {}; + InitData.pSysMem = std::move(IndexData); + const HRESULT hr = Device->CreateBuffer(&bd, &InitData, &IndexBuffer); + return hr; +} + +HRESULT StaticMesh::CreateVertexBuffers(const Blob vertexBufferBlobs[3]) +{ + D3D11_BUFFER_DESC bd{}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = 0; + + for (int i = 0; i < 3; i++) + { + const Blob& VertexBlob = vertexBufferBlobs[i]; + if (VertexBlob.IsInvalid()) + { + continue; + } + + // copy the memory so we can free the GCHandle in c# + byte* VertexData = new byte[VertexBlob.Size]; + memcpy(VertexData, VertexBlob.Data, VertexBlob.Size); + + bd.ByteWidth = VertexBlob.Size; + + D3D11_SUBRESOURCE_DATA InitData = {}; + // actually we probably dont need manualy copy as this should copy too? or maybe not idk + // idk what assignment operator does to a byte array + InitData.pSysMem = std::move(VertexData); + ID3D11Buffer* VertexBuffer; + HRESULT hr = Device->CreateBuffer(&bd, &InitData, &VertexBuffer); + if (FAILED(hr)) + return hr; + VertexBuffers.push_back(VertexBuffer); + } + + return S_OK; +} + +Part::~Part() +{ + SAFE_RELEASE(VertexLayout); + SAFE_RELEASE(VertexShader); + SAFE_RELEASE(PixelShader); + + for (auto& buffer : VSConstantBuffers) + { + SAFE_RELEASE(buffer->ResourcePointer); + } + VSConstantBuffers.clear(); + + for (auto& buffer : PSConstantBuffers) + { + SAFE_RELEASE(buffer->ResourcePointer); + } + PSConstantBuffers.clear(); + + for (auto& srv : VSTextureSRVs) + { + SAFE_RELEASE(srv); + } + VSTextureSRVs.clear(); + + for (auto& srv : PSTextureSRVs) + { + SAFE_RELEASE(srv); + } + PSTextureSRVs.clear(); + + for (auto& state : VSSamplerStates) + { + SAFE_RELEASE(state->ResourcePointer); + } + VSSamplerStates.clear(); + + for (auto& state : PSSamplerStates) + { + SAFE_RELEASE(state->ResourcePointer); + } + PSSamplerStates.clear(); + + // Logger::Log("Part destroyed"); +} + +static XMMATRIX CreatePerspectiveInfiniteReverseRH(const float fov, const float aspectRatio, const float zNear) +{ + assert(zNear > 0); + const float yScale = 1.0f / tan(fov * 0.5); + return {yScale / aspectRatio, 0, 0, 0, 0, yScale, 0, 0, 0, 0, 0, -1, 0, 0, zNear, 0}; +} + +HRESULT Part::CreateConstantBuffers(const Blob& psCb0) +{ + byte cb1[0x500]; + + memcpy(cb1, Parent->StaticMeshTransforms.Data, Parent->StaticMeshTransforms.Size); + const XMMATRIX transformMatrix = XMMatrixIdentity(); + memcpy(cb1 + 0x20, &transformMatrix, sizeof(transformMatrix)); + + D3D11_BUFFER_DESC bd{}; + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = 0x500; + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bd.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA InitData = {}; + InitData.pSysMem = cb1; + + ID3D11Buffer* ConstantBuffer1; + HRESULT hr = Device->CreateBuffer(&bd, &InitData, &ConstantBuffer1); + if (FAILED(hr)) + return hr; + + VSConstantBuffers.push_back(new Resource(1, ConstantBuffer1)); + + cb12_View View; + View.WorldToProjective = XMMatrixIdentity(); + View.CameraToWorld = XMMatrixIdentity(); + View.Target = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); + View.Unk09 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); + View.CameraPosition = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); + View.Unk11 = XMMatrixIdentity(); + View.Unk11.r[3].m128_f32[2] = 0.15f; + View.ViewMiscellaneous = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); + + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = sizeof(View); + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bd.CPUAccessFlags = 0; + + InitData.pSysMem = &View; + + ID3D11Buffer* ConstantBuffer12; + hr = Device->CreateBuffer(&bd, &InitData, &ConstantBuffer12); + if (FAILED(hr)) + return hr; + + VSConstantBuffers.push_back(new Resource(12, ConstantBuffer12)); + + // to be able to update the VS, then we copy over to the resource + bd.BindFlags = 0; + hr = hr = Device->CreateBuffer(&bd, &InitData, &ViewBuffer); + if (FAILED(hr)) + return hr; + + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = psCb0.Size; + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bd.CPUAccessFlags = 0; + + InitData.pSysMem = psCb0.Data; + + if (psCb0.IsInvalid()) + { + return S_OK; + } + + ID3D11Buffer* ConstantBuffer; + hr = Device->CreateBuffer(&bd, &InitData, &ConstantBuffer); + if (FAILED(hr)) + return hr; + + PSConstantBuffers.push_back(new Resource(0, ConstantBuffer)); + // + // std::ifstream PSConstantBufferFile12( + // "C:/Users/monta/Desktop/Projects/Charm/Charm/bin/x64/Debug/net7.0-windows/C325BB80/PS_cb12.bin", std::ios::in | + // std::ios::binary); + // if (!PSConstantBufferFile12) + // { + // return MK_E_CANTOPENFILE; + // } + // PSConstantBufferFile12.seekg(0, std::ios::end); + // const UINT PSFileLength12 = PSConstantBufferFile12.tellg(); + // PSConstantBufferFile12.seekg(0, std::ios::beg); + // BYTE* PScb12 = new BYTE[PSFileLength12]; + // PSConstantBufferFile12.read((char*) cb0, PSFileLength12); + // + // bd.Usage = D3D11_USAGE_DEFAULT; + // bd.ByteWidth = PSFileLength12; + // bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + // bd.CPUAccessFlags = 0; + // + // InitData.pSysMem = PScb12; + // + // ID3D11Buffer* PSConstantBuffer12; + // hr = Device->CreateBuffer(&bd, &InitData, &PSConstantBuffer12); + // if (FAILED(hr)) + // return hr; + // + // PSConstantBuffers.push_back(new Resource(12, PSConstantBuffer12)); + + return S_OK; +} + +HRESULT Part::CreateTextureResources(const Blob vsTextures[16], const Blob psTextures[32]) +{ + HRESULT hr = S_OK; + for (int i = 0; i < 16; i++) + { + const Blob& vsTexture = vsTextures[i]; + if (vsTexture.Size == 0) + { + continue; + } + + ID3D11ShaderResourceView* TextureSRV; + if (FAILED( + hr = CreateDDSTextureFromMemory(Device, static_cast(vsTexture.Data), vsTexture.Size, nullptr, &TextureSRV))) + { + return hr; + } + VSTextureSRVs.push_back(TextureSRV); + } + for (int i = 0; i < 32; i++) + { + const Blob& psTexture = psTextures[i]; + if (psTexture.Size == 0) + { + continue; + } + + ID3D11ShaderResourceView* TextureSRV; + if (FAILED( + hr = CreateDDSTextureFromMemory(Device, static_cast(psTexture.Data), psTexture.Size, nullptr, &TextureSRV))) + { + return hr; + } + PSTextureSRVs.push_back(TextureSRV); + } + + return S_OK; +} + +HRESULT Part::CreateSamplers(const Blob vsSamplers[16], const Blob psSamplers[16]) +{ + HRESULT hr = S_OK; + + for (int i = 0; i < 16; i++) + { + const Blob& vsSampler = vsSamplers[i]; + if (vsSampler.IsInvalid()) + { + continue; + } + + D3D11_SAMPLER_DESC sampDesc = *static_cast(vsSampler.Data); + ID3D11SamplerState* samplerState; + hr = Device->CreateSamplerState(&sampDesc, &samplerState); + if (FAILED(hr)) + { + Logger::Log( + "Failed to create VS static mesh part sampler %d (%d, %d) with error %x", i, sampDesc.AddressU, sampDesc.MipLODBias, hr); + return hr; + } + + VSSamplerStates.push_back(new Resource(i + 1, samplerState)); + } + + for (int i = 0; i < 16; i++) + { + const Blob& psSampler = psSamplers[i]; + if (psSampler.IsInvalid()) + { + continue; + } + + D3D11_SAMPLER_DESC sampDesc = *static_cast(psSampler.Data); + ID3D11SamplerState* samplerState; + hr = Device->CreateSamplerState(&sampDesc, &samplerState); + if (FAILED(hr)) + { + Logger::Log( + "Failed to create PS static mesh part sampler %d (%d, %d) with error %x", i, sampDesc.AddressU, sampDesc.MipLODBias, hr); + return hr; + } + + PSSamplerStates.push_back(new Resource(i + 1, samplerState)); + } + + return hr; +} + +ID3D11VertexShader* Part::GetVertexShader() const +{ + return VertexShader; +} + +ID3D11PixelShader* Part::GetPixelShader() const +{ + return PixelShader; +} + +ID3D11InputLayout* Part::GetVertexLayout() const +{ + return VertexLayout; +} + +HRESULT Part::Render(ID3D11DeviceContext* DeviceContext, Camera* Camera, float DeltaTime) +{ + if (GetVertexShader() == nullptr || GetPixelShader() == nullptr || GetVertexLayout() == nullptr) + { + Logger::Log("No vertex layour or shaders set."); + return E_FAIL; + } + + DeviceContext->VSSetShader(GetVertexShader(), nullptr, 0); + DeviceContext->VSSetShaderResources(0, VSTextureSRVs.size(), VSTextureSRVs.data()); + for (const auto& SamplerState : VSSamplerStates) + { + DeviceContext->VSSetSamplers(SamplerState->Slot, 1, &SamplerState->ResourcePointer); + } + + DeviceContext->PSSetShader(GetPixelShader(), nullptr, 0); + DeviceContext->PSSetShaderResources(0, PSTextureSRVs.size(), PSTextureSRVs.data()); + for (const auto& SamplerState : PSSamplerStates) + { + DeviceContext->PSSetSamplers(SamplerState->Slot, 1, &SamplerState->ResourcePointer); + } + + DeviceContext->IASetInputLayout(GetVertexLayout()); + + cb12_View View; + + auto projection = CreatePerspectiveInfiniteReverseRH(Camera->GetFOVRadians(), Camera->GetAspectRatio(), 0.01f); + + View.WorldToProjective = Camera->GetViewMatrix() * projection; + View.CameraToWorld = XMMatrixIdentity(); + View.CameraToWorld.r[3].m128_f32[0] = Camera->GetPosition().m128_f32[0]; + View.CameraToWorld.r[3].m128_f32[1] = Camera->GetPosition().m128_f32[1]; + View.CameraToWorld.r[3].m128_f32[2] = Camera->GetPosition().m128_f32[2]; + View.CameraToWorld.r[3].m128_f32[3] = 1.0f; + + // std::cout << "Camera Position: " << View.CameraPosition.m128_f32[0] << ", " << View.CameraPosition.m128_f32[1] << ", " + // << View.CameraPosition.m128_f32[2] << std::endl; + // std::cout << "Camera Direction: " << Camera->GetDirection().m128_f32[0] << ", " << Camera->GetDirection().m128_f32[1] << ", " + // << Camera->GetDirection().m128_f32[2] << std::endl; + + // view matrix + D3D11_BOX Box; + Box.left = 0; + Box.top = 0; + Box.front = 0; + Box.right = 0 + 16 * 8; // 2 matrices = 8 vectors + Box.bottom = 1; + Box.back = 1; + DeviceContext->UpdateSubresource(ViewBuffer, 0, &Box, &View, 0, 0); + + // // player position + // Box.left = 112; + // Box.top = 0; + // Box.front = 0; + // Box.right = 112 + 16; + // Box.bottom = 1; + // Box.back = 1; + // DeviceContext->UpdateSubresource(ViewBuffer, 0, &Box, &View.CameraPosition, 0, 0); + + for (const auto& ConstantBuffer : VSConstantBuffers) + { + if (ConstantBuffer->Slot == 12) + { + DeviceContext->CopyResource(ConstantBuffer->ResourcePointer, ViewBuffer); + } + DeviceContext->VSSetConstantBuffers(ConstantBuffer->Slot, 1, &ConstantBuffer->ResourcePointer); + } + for (const auto& ConstantBuffer : PSConstantBuffers) + { + DeviceContext->PSSetConstantBuffers(ConstantBuffer->Slot, 1, &ConstantBuffer->ResourcePointer); + } + + DeviceContext->DrawIndexed(PartInfo.IndexCount, PartInfo.IndexOffset, 0); + // Logger::Log("%d", PartInfo.IndexOffset); + + return S_OK; +} + +HRESULT Part::Initialise(ID3D11Device* device) +{ + Device = device; + + HRESULT hr; + hr = CreateVertexShader(PartInfo.PartMaterial.VSBytecode); + if (FAILED(hr)) + { + Logger::Log("Failed to create static mesh part vertex shader with error %x", hr); + return hr; + } + + hr = CreatePixelShader(PartInfo.PartMaterial.PSBytecode); + if (FAILED(hr)) + { + Logger::Log("Failed to create static mesh part pixel shader with error %x", hr); + return hr; + } + + hr = CreateVertexLayout(PartInfo.PartMaterial.InputSignatures, PartInfo.PartMaterial.VSBytecode); + if (FAILED(hr)) + { + Logger::Log("Failed to create static mesh part vertex layout with error %x", hr); + return hr; + } + + hr = CreateConstantBuffers(PartInfo.PartMaterial.PScb0); + if (FAILED(hr)) + { + Logger::Log("Failed to create static mesh part constant buffers with error %x", hr); + return hr; + } + + hr = CreateTextureResources(PartInfo.PartMaterial.VSTextures, PartInfo.PartMaterial.PSTextures); + if (FAILED(hr)) + { + Logger::Log("Failed to create static mesh part texture resources with error %x", hr); + return hr; + } + + hr = CreateSamplers(PartInfo.PartMaterial.VSSamplers, PartInfo.PartMaterial.PSSamplers); + if (FAILED(hr)) + { + Logger::Log("Failed to create static mesh part samplers with error %x", hr); + return hr; + } + + return hr; +} + +HRESULT Part::CreateVertexShader(const Blob& shaderBlob) +{ + // does this copy? want to know if its safe or not + HRESULT hr = Device->CreateVertexShader(shaderBlob.Data, shaderBlob.Size, nullptr, &VertexShader); + return hr; +} + +HRESULT Part::CreatePixelShader(const Blob& shaderBlob) +{ + HRESULT hr = Device->CreatePixelShader(shaderBlob.Data, shaderBlob.Size, nullptr, &PixelShader); + return hr; +} + +static LPCSTR GetSemanticName(InputSemantic semantic) +{ + switch (semantic) + { + case Position: + return "POSITION"; + case Normal: + return "NORMAL"; + case Tangent: + return "TANGENT"; + case Colour: + return "COLOR"; + case Texcoord: + return "TEXCOORD"; + case BlendIndices: + return "BLENDINDICES"; + case BlendWeight: + return "BLENDWEIGHT"; + default: + return ""; + } +} + +HRESULT Part::CreateVertexLayout(const InputSignature inputSignatures[8], const Blob& shaderBlob) +{ + D3D11_INPUT_ELEMENT_DESC layout[8]; + + int numElements = 0; + + for (int i = 0; i < 8; i++) + { + if (inputSignatures[i].Semantic == InputSemantic::None) + { + continue; + } + + numElements++; + layout[i] = {}; + layout[i].SemanticName = GetSemanticName(inputSignatures[i].Semantic); + layout[i].SemanticIndex = inputSignatures[i].SemanticIndex; + layout[i].Format = (DXGI_FORMAT) inputSignatures[i].DxgiFormat; + layout[i].InputSlot = inputSignatures[i].BufferIndex; + layout[i].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + layout[i].InstanceDataStepRate = 0; + layout[i].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; + } + + HRESULT hr = Device->CreateInputLayout(layout, numElements, shaderBlob.Data, shaderBlob.Size, &VertexLayout); + // VertexShaderBlob->Release(); + + return hr; +} + +HRESULT StaticMesh::AddPart(const PartInfo& partInfo) +{ + std::unique_ptr part = std::make_unique(this, partInfo); + HRESULT hr = part->Initialise(Device); + if (FAILED(hr)) + { + return hr; + } + Parts.push_back(std::move(part)); + // Logger::Log("Added new part to static mesh"); + return hr; +} diff --git a/Atlas/StaticMesh.h b/Atlas/StaticMesh.h new file mode 100644 index 00000000..dc120108 --- /dev/null +++ b/Atlas/StaticMesh.h @@ -0,0 +1,167 @@ +#pragma once +#include "DDSTextureLoader.h" + +#include + +#include +#include + +class Camera; + +struct Blob +{ + void* Data; + int Size; + + bool IsInvalid() const { return Data == nullptr || Size == 0; } +}; + +enum InputSemantic : int +{ + None, + Position, + Texcoord, + Normal, + Tangent, + BlendIndices, + BlendWeight, + Colour +}; + +struct InputSignature +{ + InputSemantic Semantic; + int SemanticIndex; + int DxgiFormat; + int BufferIndex; +}; + +struct PartMaterial +{ + Blob VSBytecode; + Blob PSBytecode; + InputSignature InputSignatures[8]; + Blob VSTextures[16]; + Blob PSTextures[32]; + Blob PScb0; + Blob VSSamplers[16]; + Blob PSSamplers[16]; +}; + +struct PartInfo +{ + uint32_t IndexOffset; + uint32_t IndexCount; + PartMaterial PartMaterial; +}; + +struct BufferGroup +{ + Blob IndexBuffer; + Blob VertexBuffers[3]; + uint32_t IndexOffset; +}; + +struct Vector4 +{ + float X; + float Y; + float Z; + float W; +}; + +template +struct Resource +{ + int Slot; + T* ResourcePointer; +}; + +struct Texture +{ + LPCWSTR FileName; +}; + +namespace DirectX +{ +inline HRESULT CreateTextureSRVsFromFiles( + ID3D11Device* Device, std::vector FileNames, std::vector& TextureSRVs) +{ + std::vector Output; + for (const auto& FileName : FileNames) + { + ID3D11ShaderResourceView* TextureSRV; + HRESULT hr = CreateDDSTextureFromFile(Device, FileName, nullptr, &TextureSRV); + if (FAILED(hr)) + return hr; + Output.push_back(TextureSRV); + } + + TextureSRVs = Output; + return S_OK; +} +} // namespace DirectX + +class StaticMesh +{ +public: + explicit StaticMesh(uint32_t hash, const Blob& staticMeshTransforms) : StaticMeshTransforms(staticMeshTransforms){}; + ~StaticMesh(); + ID3D11Device* Device; + Blob StaticMeshTransforms; + + ID3D11Buffer* GetIndexBuffer() const; + ID3D11Buffer* const* GetVertexBuffers() const; + HRESULT Initialise(ID3D11Device* Device); + HRESULT AddStaticMeshBufferGroup(const BufferGroup& bufferGroup); + HRESULT AddPart(const PartInfo& partInfo); + HRESULT Render(ID3D11DeviceContext* DeviceContext, Camera* Camera, float DeltaTime); + +private: + ID3D11Buffer* IndexBuffer = nullptr; + std::vector VertexBuffers; + std::vector> Parts; + + HRESULT CreateIndexBuffer(const Blob& indexBlob); + HRESULT CreateVertexBuffers(const Blob vertexBufferBlobs[3]); +}; + +class Part +{ +public: + Part(StaticMesh* staticMesh, const PartInfo& partInfo) : Parent(staticMesh), PartInfo(partInfo) {} + ~Part(); + + ID3D11Device* Device; + + ID3D11VertexShader* GetVertexShader() const; + ID3D11PixelShader* GetPixelShader() const; + ID3D11InputLayout* GetVertexLayout() const; + HRESULT Render(ID3D11DeviceContext* DeviceContext, Camera* Camera, float DeltaTime); + HRESULT Initialise(ID3D11Device* device); + +private: + ID3D11InputLayout* VertexLayout = nullptr; + ID3D11VertexShader* VertexShader = nullptr; + ID3D11PixelShader* PixelShader = nullptr; + std::vector*> VSConstantBuffers; + std::vector*> PSConstantBuffers; + std::vector VSTextureSRVs; + std::vector PSTextureSRVs; + std::vector*> VSSamplerStates; + std::vector*> PSSamplerStates; + + StaticMesh* Parent = nullptr; + + ID3D11Buffer* ViewBuffer = nullptr; + + PartInfo PartInfo; + + HRESULT CreateVertexShader(const Blob& shaderBlob); + HRESULT CreatePixelShader(const Blob& shaderBlob); + HRESULT CreateVertexLayout(const InputSignature inputSignatures[8], const Blob& shaderBlob); + + HRESULT CreateConstantBuffers(const Blob& psCb0); + HRESULT CreateTextureResources(const Blob vsTextures[16], const Blob psTextures[32]); + HRESULT CreateSamplers(const Blob vsSamplers[16], const Blob psSamplers[16]); +}; diff --git a/Atlas/renderdoc_app.h b/Atlas/renderdoc_app.h new file mode 100644 index 00000000..a4514c6b --- /dev/null +++ b/Atlas/renderdoc_app.h @@ -0,0 +1,737 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2023 Baldur Karlsson + * + * 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. + ******************************************************************************/ + +#pragma once + +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Documentation for the API is available at https://renderdoc.org/docs/in_application_api.html +// + +#if !defined(RENDERDOC_NO_STDINT) +#include +#endif + +#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define RENDERDOC_CC __cdecl +#elif defined(__linux__) +#define RENDERDOC_CC +#elif defined(__APPLE__) +#define RENDERDOC_CC +#else +#error "Unknown platform" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////// +// Constants not used directly in below API + +// This is a GUID/magic value used for when applications pass a path where shader debug +// information can be found to match up with a stripped shader. +// the define can be used like so: const GUID RENDERDOC_ShaderDebugMagicValue = +// RENDERDOC_ShaderDebugMagicValue_value +#define RENDERDOC_ShaderDebugMagicValue_struct \ + { \ + 0xeab25520, 0x6670, 0x4865, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \ + } + +// as an alternative when you want a byte array (assuming x86 endianness): +#define RENDERDOC_ShaderDebugMagicValue_bytearray \ + { \ + 0x20, 0x55, 0xb2, 0xea, 0x70, 0x66, 0x65, 0x48, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \ + } + +// truncated version when only a uint64_t is available (e.g. Vulkan tags): +#define RENDERDOC_ShaderDebugMagicValue_truncated 0x48656670eab25520ULL + +////////////////////////////////////////////////////////////////////////////////////////////////// +// RenderDoc capture options +// + +typedef enum RENDERDOC_CaptureOption { + // Allow the application to enable vsync + // + // Default - enabled + // + // 1 - The application can enable or disable vsync at will + // 0 - vsync is force disabled + eRENDERDOC_Option_AllowVSync = 0, + + // Allow the application to enable fullscreen + // + // Default - enabled + // + // 1 - The application can enable or disable fullscreen at will + // 0 - fullscreen is force disabled + eRENDERDOC_Option_AllowFullscreen = 1, + + // Record API debugging events and messages + // + // Default - disabled + // + // 1 - Enable built-in API debugging features and records the results into + // the capture, which is matched up with events on replay + // 0 - no API debugging is forcibly enabled + eRENDERDOC_Option_APIValidation = 2, + eRENDERDOC_Option_DebugDeviceMode = 2, // deprecated name of this enum + + // Capture CPU callstacks for API events + // + // Default - disabled + // + // 1 - Enables capturing of callstacks + // 0 - no callstacks are captured + eRENDERDOC_Option_CaptureCallstacks = 3, + + // When capturing CPU callstacks, only capture them from actions. + // This option does nothing without the above option being enabled + // + // Default - disabled + // + // 1 - Only captures callstacks for actions. + // Ignored if CaptureCallstacks is disabled + // 0 - Callstacks, if enabled, are captured for every event. + eRENDERDOC_Option_CaptureCallstacksOnlyDraws = 4, + eRENDERDOC_Option_CaptureCallstacksOnlyActions = 4, + + // Specify a delay in seconds to wait for a debugger to attach, after + // creating or injecting into a process, before continuing to allow it to run. + // + // 0 indicates no delay, and the process will run immediately after injection + // + // Default - 0 seconds + // + eRENDERDOC_Option_DelayForDebugger = 5, + + // Verify buffer access. This includes checking the memory returned by a Map() call to + // detect any out-of-bounds modification, as well as initialising buffers with undefined contents + // to a marker value to catch use of uninitialised memory. + // + // NOTE: This option is only valid for OpenGL and D3D11. Explicit APIs such as D3D12 and Vulkan do + // not do the same kind of interception & checking and undefined contents are really undefined. + // + // Default - disabled + // + // 1 - Verify buffer access + // 0 - No verification is performed, and overwriting bounds may cause crashes or corruption in + // RenderDoc. + eRENDERDOC_Option_VerifyBufferAccess = 6, + + // The old name for eRENDERDOC_Option_VerifyBufferAccess was eRENDERDOC_Option_VerifyMapWrites. + // This option now controls the filling of uninitialised buffers with 0xdddddddd which was + // previously always enabled + eRENDERDOC_Option_VerifyMapWrites = eRENDERDOC_Option_VerifyBufferAccess, + + // Hooks any system API calls that create child processes, and injects + // RenderDoc into them recursively with the same options. + // + // Default - disabled + // + // 1 - Hooks into spawned child processes + // 0 - Child processes are not hooked by RenderDoc + eRENDERDOC_Option_HookIntoChildren = 7, + + // By default RenderDoc only includes resources in the final capture necessary + // for that frame, this allows you to override that behaviour. + // + // Default - disabled + // + // 1 - all live resources at the time of capture are included in the capture + // and available for inspection + // 0 - only the resources referenced by the captured frame are included + eRENDERDOC_Option_RefAllResources = 8, + + // **NOTE**: As of RenderDoc v1.1 this option has been deprecated. Setting or + // getting it will be ignored, to allow compatibility with older versions. + // In v1.1 the option acts as if it's always enabled. + // + // By default RenderDoc skips saving initial states for resources where the + // previous contents don't appear to be used, assuming that writes before + // reads indicate previous contents aren't used. + // + // Default - disabled + // + // 1 - initial contents at the start of each captured frame are saved, even if + // they are later overwritten or cleared before being used. + // 0 - unless a read is detected, initial contents will not be saved and will + // appear as black or empty data. + eRENDERDOC_Option_SaveAllInitials = 9, + + // In APIs that allow for the recording of command lists to be replayed later, + // RenderDoc may choose to not capture command lists before a frame capture is + // triggered, to reduce overheads. This means any command lists recorded once + // and replayed many times will not be available and may cause a failure to + // capture. + // + // NOTE: This is only true for APIs where multithreading is difficult or + // discouraged. Newer APIs like Vulkan and D3D12 will ignore this option + // and always capture all command lists since the API is heavily oriented + // around it and the overheads have been reduced by API design. + // + // 1 - All command lists are captured from the start of the application + // 0 - Command lists are only captured if their recording begins during + // the period when a frame capture is in progress. + eRENDERDOC_Option_CaptureAllCmdLists = 10, + + // Mute API debugging output when the API validation mode option is enabled + // + // Default - enabled + // + // 1 - Mute any API debug messages from being displayed or passed through + // 0 - API debugging is displayed as normal + eRENDERDOC_Option_DebugOutputMute = 11, + + // Option to allow vendor extensions to be used even when they may be + // incompatible with RenderDoc and cause corrupted replays or crashes. + // + // Default - inactive + // + // No values are documented, this option should only be used when absolutely + // necessary as directed by a RenderDoc developer. + eRENDERDOC_Option_AllowUnsupportedVendorExtensions = 12, + + // Define a soft memory limit which some APIs may aim to keep overhead under where + // possible. Anything above this limit will where possible be saved directly to disk during + // capture. + // This will cause increased disk space use (which may cause a capture to fail if disk space is + // exhausted) as well as slower capture times. + // + // Not all memory allocations may be deferred like this so it is not a guarantee of a memory + // limit. + // + // Units are in MBs, suggested values would range from 200MB to 1000MB. + // + // Default - 0 Megabytes + eRENDERDOC_Option_SoftMemoryLimit = 13, +} RENDERDOC_CaptureOption; + +// Sets an option that controls how RenderDoc behaves on capture. +// +// Returns 1 if the option and value are valid +// Returns 0 if either is invalid and the option is unchanged +typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionU32)(RENDERDOC_CaptureOption opt, uint32_t val); +typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionF32)(RENDERDOC_CaptureOption opt, float val); + +// Gets the current value of an option as a uint32_t +// +// If the option is invalid, 0xffffffff is returned +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionU32)(RENDERDOC_CaptureOption opt); + +// Gets the current value of an option as a float +// +// If the option is invalid, -FLT_MAX is returned +typedef float(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionF32)(RENDERDOC_CaptureOption opt); + +typedef enum RENDERDOC_InputButton { + // '0' - '9' matches ASCII values + eRENDERDOC_Key_0 = 0x30, + eRENDERDOC_Key_1 = 0x31, + eRENDERDOC_Key_2 = 0x32, + eRENDERDOC_Key_3 = 0x33, + eRENDERDOC_Key_4 = 0x34, + eRENDERDOC_Key_5 = 0x35, + eRENDERDOC_Key_6 = 0x36, + eRENDERDOC_Key_7 = 0x37, + eRENDERDOC_Key_8 = 0x38, + eRENDERDOC_Key_9 = 0x39, + + // 'A' - 'Z' matches ASCII values + eRENDERDOC_Key_A = 0x41, + eRENDERDOC_Key_B = 0x42, + eRENDERDOC_Key_C = 0x43, + eRENDERDOC_Key_D = 0x44, + eRENDERDOC_Key_E = 0x45, + eRENDERDOC_Key_F = 0x46, + eRENDERDOC_Key_G = 0x47, + eRENDERDOC_Key_H = 0x48, + eRENDERDOC_Key_I = 0x49, + eRENDERDOC_Key_J = 0x4A, + eRENDERDOC_Key_K = 0x4B, + eRENDERDOC_Key_L = 0x4C, + eRENDERDOC_Key_M = 0x4D, + eRENDERDOC_Key_N = 0x4E, + eRENDERDOC_Key_O = 0x4F, + eRENDERDOC_Key_P = 0x50, + eRENDERDOC_Key_Q = 0x51, + eRENDERDOC_Key_R = 0x52, + eRENDERDOC_Key_S = 0x53, + eRENDERDOC_Key_T = 0x54, + eRENDERDOC_Key_U = 0x55, + eRENDERDOC_Key_V = 0x56, + eRENDERDOC_Key_W = 0x57, + eRENDERDOC_Key_X = 0x58, + eRENDERDOC_Key_Y = 0x59, + eRENDERDOC_Key_Z = 0x5A, + + // leave the rest of the ASCII range free + // in case we want to use it later + eRENDERDOC_Key_NonPrintable = 0x100, + + eRENDERDOC_Key_Divide, + eRENDERDOC_Key_Multiply, + eRENDERDOC_Key_Subtract, + eRENDERDOC_Key_Plus, + + eRENDERDOC_Key_F1, + eRENDERDOC_Key_F2, + eRENDERDOC_Key_F3, + eRENDERDOC_Key_F4, + eRENDERDOC_Key_F5, + eRENDERDOC_Key_F6, + eRENDERDOC_Key_F7, + eRENDERDOC_Key_F8, + eRENDERDOC_Key_F9, + eRENDERDOC_Key_F10, + eRENDERDOC_Key_F11, + eRENDERDOC_Key_F12, + + eRENDERDOC_Key_Home, + eRENDERDOC_Key_End, + eRENDERDOC_Key_Insert, + eRENDERDOC_Key_Delete, + eRENDERDOC_Key_PageUp, + eRENDERDOC_Key_PageDn, + + eRENDERDOC_Key_Backspace, + eRENDERDOC_Key_Tab, + eRENDERDOC_Key_PrtScrn, + eRENDERDOC_Key_Pause, + + eRENDERDOC_Key_Max, +} RENDERDOC_InputButton; + +// Sets which key or keys can be used to toggle focus between multiple windows +// +// If keys is NULL or num is 0, toggle keys will be disabled +typedef void(RENDERDOC_CC *pRENDERDOC_SetFocusToggleKeys)(RENDERDOC_InputButton *keys, int num); + +// Sets which key or keys can be used to capture the next frame +// +// If keys is NULL or num is 0, captures keys will be disabled +typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureKeys)(RENDERDOC_InputButton *keys, int num); + +typedef enum RENDERDOC_OverlayBits { + // This single bit controls whether the overlay is enabled or disabled globally + eRENDERDOC_Overlay_Enabled = 0x1, + + // Show the average framerate over several seconds as well as min/max + eRENDERDOC_Overlay_FrameRate = 0x2, + + // Show the current frame number + eRENDERDOC_Overlay_FrameNumber = 0x4, + + // Show a list of recent captures, and how many captures have been made + eRENDERDOC_Overlay_CaptureList = 0x8, + + // Default values for the overlay mask + eRENDERDOC_Overlay_Default = (eRENDERDOC_Overlay_Enabled | eRENDERDOC_Overlay_FrameRate | + eRENDERDOC_Overlay_FrameNumber | eRENDERDOC_Overlay_CaptureList), + + // Enable all bits + eRENDERDOC_Overlay_All = ~0U, + + // Disable all bits + eRENDERDOC_Overlay_None = 0, +} RENDERDOC_OverlayBits; + +// returns the overlay bits that have been set +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetOverlayBits)(); +// sets the overlay bits with an and & or mask +typedef void(RENDERDOC_CC *pRENDERDOC_MaskOverlayBits)(uint32_t And, uint32_t Or); + +// this function will attempt to remove RenderDoc's hooks in the application. +// +// Note: that this can only work correctly if done immediately after +// the module is loaded, before any API work happens. RenderDoc will remove its +// injected hooks and shut down. Behaviour is undefined if this is called +// after any API functions have been called, and there is still no guarantee of +// success. +typedef void(RENDERDOC_CC *pRENDERDOC_RemoveHooks)(); + +// DEPRECATED: compatibility for code compiled against pre-1.4.1 headers. +typedef pRENDERDOC_RemoveHooks pRENDERDOC_Shutdown; + +// This function will unload RenderDoc's crash handler. +// +// If you use your own crash handler and don't want RenderDoc's handler to +// intercede, you can call this function to unload it and any unhandled +// exceptions will pass to the next handler. +typedef void(RENDERDOC_CC *pRENDERDOC_UnloadCrashHandler)(); + +// Sets the capture file path template +// +// pathtemplate is a UTF-8 string that gives a template for how captures will be named +// and where they will be saved. +// +// Any extension is stripped off the path, and captures are saved in the directory +// specified, and named with the filename and the frame number appended. If the +// directory does not exist it will be created, including any parent directories. +// +// If pathtemplate is NULL, the template will remain unchanged +// +// Example: +// +// SetCaptureFilePathTemplate("my_captures/example"); +// +// Capture #1 -> my_captures/example_frame123.rdc +// Capture #2 -> my_captures/example_frame456.rdc +typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFilePathTemplate)(const char *pathtemplate); + +// returns the current capture path template, see SetCaptureFileTemplate above, as a UTF-8 string +typedef const char *(RENDERDOC_CC *pRENDERDOC_GetCaptureFilePathTemplate)(); + +// DEPRECATED: compatibility for code compiled against pre-1.1.2 headers. +typedef pRENDERDOC_SetCaptureFilePathTemplate pRENDERDOC_SetLogFilePathTemplate; +typedef pRENDERDOC_GetCaptureFilePathTemplate pRENDERDOC_GetLogFilePathTemplate; + +// returns the number of captures that have been made +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetNumCaptures)(); + +// This function returns the details of a capture, by index. New captures are added +// to the end of the list. +// +// filename will be filled with the absolute path to the capture file, as a UTF-8 string +// pathlength will be written with the length in bytes of the filename string +// timestamp will be written with the time of the capture, in seconds since the Unix epoch +// +// Any of the parameters can be NULL and they'll be skipped. +// +// The function will return 1 if the capture index is valid, or 0 if the index is invalid +// If the index is invalid, the values will be unchanged +// +// Note: when captures are deleted in the UI they will remain in this list, so the +// capture path may not exist anymore. +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *filename, + uint32_t *pathlength, uint64_t *timestamp); + +// Sets the comments associated with a capture file. These comments are displayed in the +// UI program when opening. +// +// filePath should be a path to the capture file to add comments to. If set to NULL or "" +// the most recent capture file created made will be used instead. +// comments should be a NULL-terminated UTF-8 string to add as comments. +// +// Any existing comments will be overwritten. +typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFileComments)(const char *filePath, + const char *comments); + +// returns 1 if the RenderDoc UI is connected to this application, 0 otherwise +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsTargetControlConnected)(); + +// DEPRECATED: compatibility for code compiled against pre-1.1.1 headers. +// This was renamed to IsTargetControlConnected in API 1.1.1, the old typedef is kept here for +// backwards compatibility with old code, it is castable either way since it's ABI compatible +// as the same function pointer type. +typedef pRENDERDOC_IsTargetControlConnected pRENDERDOC_IsRemoteAccessConnected; + +// This function will launch the Replay UI associated with the RenderDoc library injected +// into the running application. +// +// if connectTargetControl is 1, the Replay UI will be launched with a command line parameter +// to connect to this application +// cmdline is the rest of the command line, as a UTF-8 string. E.g. a captures to open +// if cmdline is NULL, the command line will be empty. +// +// returns the PID of the replay UI if successful, 0 if not successful. +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_LaunchReplayUI)(uint32_t connectTargetControl, + const char *cmdline); + +// RenderDoc can return a higher version than requested if it's backwards compatible, +// this function returns the actual version returned. If a parameter is NULL, it will be +// ignored and the others will be filled out. +typedef void(RENDERDOC_CC *pRENDERDOC_GetAPIVersion)(int *major, int *minor, int *patch); + +// Requests that the replay UI show itself (if hidden or not the current top window). This can be +// used in conjunction with IsTargetControlConnected and LaunchReplayUI to intelligently handle +// showing the UI after making a capture. +// +// This will return 1 if the request was successfully passed on, though it's not guaranteed that +// the UI will be on top in all cases depending on OS rules. It will return 0 if there is no current +// target control connection to make such a request, or if there was another error +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_ShowReplayUI)(); + +////////////////////////////////////////////////////////////////////////// +// Capturing functions +// + +// A device pointer is a pointer to the API's root handle. +// +// This would be an ID3D11Device, HGLRC/GLXContext, ID3D12Device, etc +typedef void *RENDERDOC_DevicePointer; + +// A window handle is the OS's native window handle +// +// This would be an HWND, GLXDrawable, etc +typedef void *RENDERDOC_WindowHandle; + +// A helper macro for Vulkan, where the device handle cannot be used directly. +// +// Passing the VkInstance to this macro will return the RENDERDOC_DevicePointer to use. +// +// Specifically, the value needed is the dispatch table pointer, which sits as the first +// pointer-sized object in the memory pointed to by the VkInstance. Thus we cast to a void** and +// indirect once. +#define RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(inst) (*((void **)(inst))) + +// This sets the RenderDoc in-app overlay in the API/window pair as 'active' and it will +// respond to keypresses. Neither parameter can be NULL +typedef void(RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(RENDERDOC_DevicePointer device, + RENDERDOC_WindowHandle wndHandle); + +// capture the next frame on whichever window and API is currently considered active +typedef void(RENDERDOC_CC *pRENDERDOC_TriggerCapture)(); + +// capture the next N frames on whichever window and API is currently considered active +typedef void(RENDERDOC_CC *pRENDERDOC_TriggerMultiFrameCapture)(uint32_t numFrames); + +// When choosing either a device pointer or a window handle to capture, you can pass NULL. +// Passing NULL specifies a 'wildcard' match against anything. This allows you to specify +// any API rendering to a specific window, or a specific API instance rendering to any window, +// or in the simplest case of one window and one API, you can just pass NULL for both. +// +// In either case, if there are two or more possible matching (device,window) pairs it +// is undefined which one will be captured. +// +// Note: for headless rendering you can pass NULL for the window handle and either specify +// a device pointer or leave it NULL as above. + +// Immediately starts capturing API calls on the specified device pointer and window handle. +// +// If there is no matching thing to capture (e.g. no supported API has been initialised), +// this will do nothing. +// +// The results are undefined (including crashes) if two captures are started overlapping, +// even on separate devices and/oror windows. +typedef void(RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(RENDERDOC_DevicePointer device, + RENDERDOC_WindowHandle wndHandle); + +// Returns whether or not a frame capture is currently ongoing anywhere. +// +// This will return 1 if a capture is ongoing, and 0 if there is no capture running +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsFrameCapturing)(); + +// Ends capturing immediately. +// +// This will return 1 if the capture succeeded, and 0 if there was an error capturing. +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(RENDERDOC_DevicePointer device, + RENDERDOC_WindowHandle wndHandle); + +// Ends capturing immediately and discard any data stored without saving to disk. +// +// This will return 1 if the capture was discarded, and 0 if there was an error or no capture +// was in progress +typedef uint32_t(RENDERDOC_CC *pRENDERDOC_DiscardFrameCapture)(RENDERDOC_DevicePointer device, + RENDERDOC_WindowHandle wndHandle); + +// Only valid to be called between a call to StartFrameCapture and EndFrameCapture. Gives a custom +// title to the capture produced which will be displayed in the UI. +// +// If multiple captures are ongoing, this title will be applied to the first capture to end after +// this call. The second capture to end will have no title, unless this function is called again. +// +// Calling this function has no effect if no capture is currently running, and if it is called +// multiple times only the last title will be used. +typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureTitle)(const char *title); + +////////////////////////////////////////////////////////////////////////////////////////////////// +// RenderDoc API versions +// + +// RenderDoc uses semantic versioning (http://semver.org/). +// +// MAJOR version is incremented when incompatible API changes happen. +// MINOR version is incremented when functionality is added in a backwards-compatible manner. +// PATCH version is incremented when backwards-compatible bug fixes happen. +// +// Note that this means the API returned can be higher than the one you might have requested. +// e.g. if you are running against a newer RenderDoc that supports 1.0.1, it will be returned +// instead of 1.0.0. You can check this with the GetAPIVersion entry point +typedef enum RENDERDOC_Version { + eRENDERDOC_API_Version_1_0_0 = 10000, // RENDERDOC_API_1_0_0 = 1 00 00 + eRENDERDOC_API_Version_1_0_1 = 10001, // RENDERDOC_API_1_0_1 = 1 00 01 + eRENDERDOC_API_Version_1_0_2 = 10002, // RENDERDOC_API_1_0_2 = 1 00 02 + eRENDERDOC_API_Version_1_1_0 = 10100, // RENDERDOC_API_1_1_0 = 1 01 00 + eRENDERDOC_API_Version_1_1_1 = 10101, // RENDERDOC_API_1_1_1 = 1 01 01 + eRENDERDOC_API_Version_1_1_2 = 10102, // RENDERDOC_API_1_1_2 = 1 01 02 + eRENDERDOC_API_Version_1_2_0 = 10200, // RENDERDOC_API_1_2_0 = 1 02 00 + eRENDERDOC_API_Version_1_3_0 = 10300, // RENDERDOC_API_1_3_0 = 1 03 00 + eRENDERDOC_API_Version_1_4_0 = 10400, // RENDERDOC_API_1_4_0 = 1 04 00 + eRENDERDOC_API_Version_1_4_1 = 10401, // RENDERDOC_API_1_4_1 = 1 04 01 + eRENDERDOC_API_Version_1_4_2 = 10402, // RENDERDOC_API_1_4_2 = 1 04 02 + eRENDERDOC_API_Version_1_5_0 = 10500, // RENDERDOC_API_1_5_0 = 1 05 00 + eRENDERDOC_API_Version_1_6_0 = 10600, // RENDERDOC_API_1_6_0 = 1 06 00 +} RENDERDOC_Version; + +// API version changelog: +// +// 1.0.0 - initial release +// 1.0.1 - Bugfix: IsFrameCapturing() was returning false for captures that were triggered +// by keypress or TriggerCapture, instead of Start/EndFrameCapture. +// 1.0.2 - Refactor: Renamed eRENDERDOC_Option_DebugDeviceMode to eRENDERDOC_Option_APIValidation +// 1.1.0 - Add feature: TriggerMultiFrameCapture(). Backwards compatible with 1.0.x since the new +// function pointer is added to the end of the struct, the original layout is identical +// 1.1.1 - Refactor: Renamed remote access to target control (to better disambiguate from remote +// replay/remote server concept in replay UI) +// 1.1.2 - Refactor: Renamed "log file" in function names to just capture, to clarify that these +// are captures and not debug logging files. This is the first API version in the v1.0 +// branch. +// 1.2.0 - Added feature: SetCaptureFileComments() to add comments to a capture file that will be +// displayed in the UI program on load. +// 1.3.0 - Added feature: New capture option eRENDERDOC_Option_AllowUnsupportedVendorExtensions +// which allows users to opt-in to allowing unsupported vendor extensions to function. +// Should be used at the user's own risk. +// Refactor: Renamed eRENDERDOC_Option_VerifyMapWrites to +// eRENDERDOC_Option_VerifyBufferAccess, which now also controls initialisation to +// 0xdddddddd of uninitialised buffer contents. +// 1.4.0 - Added feature: DiscardFrameCapture() to discard a frame capture in progress and stop +// capturing without saving anything to disk. +// 1.4.1 - Refactor: Renamed Shutdown to RemoveHooks to better clarify what is happening +// 1.4.2 - Refactor: Renamed 'draws' to 'actions' in callstack capture option. +// 1.5.0 - Added feature: ShowReplayUI() to request that the replay UI show itself if connected +// 1.6.0 - Added feature: SetCaptureTitle() which can be used to set a title for a +// capture made with StartFrameCapture() or EndFrameCapture() + +typedef struct RENDERDOC_API_1_6_0 +{ + pRENDERDOC_GetAPIVersion GetAPIVersion; + + pRENDERDOC_SetCaptureOptionU32 SetCaptureOptionU32; + pRENDERDOC_SetCaptureOptionF32 SetCaptureOptionF32; + + pRENDERDOC_GetCaptureOptionU32 GetCaptureOptionU32; + pRENDERDOC_GetCaptureOptionF32 GetCaptureOptionF32; + + pRENDERDOC_SetFocusToggleKeys SetFocusToggleKeys; + pRENDERDOC_SetCaptureKeys SetCaptureKeys; + + pRENDERDOC_GetOverlayBits GetOverlayBits; + pRENDERDOC_MaskOverlayBits MaskOverlayBits; + + // Shutdown was renamed to RemoveHooks in 1.4.1. + // These unions allow old code to continue compiling without changes + union + { + pRENDERDOC_Shutdown Shutdown; + pRENDERDOC_RemoveHooks RemoveHooks; + }; + pRENDERDOC_UnloadCrashHandler UnloadCrashHandler; + + // Get/SetLogFilePathTemplate was renamed to Get/SetCaptureFilePathTemplate in 1.1.2. + // These unions allow old code to continue compiling without changes + union + { + // deprecated name + pRENDERDOC_SetLogFilePathTemplate SetLogFilePathTemplate; + // current name + pRENDERDOC_SetCaptureFilePathTemplate SetCaptureFilePathTemplate; + }; + union + { + // deprecated name + pRENDERDOC_GetLogFilePathTemplate GetLogFilePathTemplate; + // current name + pRENDERDOC_GetCaptureFilePathTemplate GetCaptureFilePathTemplate; + }; + + pRENDERDOC_GetNumCaptures GetNumCaptures; + pRENDERDOC_GetCapture GetCapture; + + pRENDERDOC_TriggerCapture TriggerCapture; + + // IsRemoteAccessConnected was renamed to IsTargetControlConnected in 1.1.1. + // This union allows old code to continue compiling without changes + union + { + // deprecated name + pRENDERDOC_IsRemoteAccessConnected IsRemoteAccessConnected; + // current name + pRENDERDOC_IsTargetControlConnected IsTargetControlConnected; + }; + pRENDERDOC_LaunchReplayUI LaunchReplayUI; + + pRENDERDOC_SetActiveWindow SetActiveWindow; + + pRENDERDOC_StartFrameCapture StartFrameCapture; + pRENDERDOC_IsFrameCapturing IsFrameCapturing; + pRENDERDOC_EndFrameCapture EndFrameCapture; + + // new function in 1.1.0 + pRENDERDOC_TriggerMultiFrameCapture TriggerMultiFrameCapture; + + // new function in 1.2.0 + pRENDERDOC_SetCaptureFileComments SetCaptureFileComments; + + // new function in 1.4.0 + pRENDERDOC_DiscardFrameCapture DiscardFrameCapture; + + // new function in 1.5.0 + pRENDERDOC_ShowReplayUI ShowReplayUI; + + // new function in 1.6.0 + pRENDERDOC_SetCaptureTitle SetCaptureTitle; +} RENDERDOC_API_1_6_0; + +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_0; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_1; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_2; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_0; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_1; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_2; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_2_0; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_3_0; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_0; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_1; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_2; +typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_5_0; + +////////////////////////////////////////////////////////////////////////////////////////////////// +// RenderDoc API entry point +// +// This entry point can be obtained via GetProcAddress/dlsym if RenderDoc is available. +// +// The name is the same as the typedef - "RENDERDOC_GetAPI" +// +// This function is not thread safe, and should not be called on multiple threads at once. +// Ideally, call this once as early as possible in your application's startup, before doing +// any API work, since some configuration functionality etc has to be done also before +// initialising any APIs. +// +// Parameters: +// version is a single value from the RENDERDOC_Version above. +// +// outAPIPointers will be filled out with a pointer to the corresponding struct of function +// pointers. +// +// Returns: +// 1 - if the outAPIPointers has been filled with a pointer to the API struct requested +// 0 - if the requested version is not supported or the arguments are invalid. +// +typedef int(RENDERDOC_CC *pRENDERDOC_GetAPI)(RENDERDOC_Version version, void **outAPIPointers); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/AtlasSharp/App.xaml b/AtlasSharp/App.xaml new file mode 100644 index 00000000..defed628 --- /dev/null +++ b/AtlasSharp/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/AtlasSharp/App.xaml.cs b/AtlasSharp/App.xaml.cs new file mode 100644 index 00000000..5f12c7dd --- /dev/null +++ b/AtlasSharp/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace Atlas +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/AtlasSharp/AssemblyInfo.cs b/AtlasSharp/AssemblyInfo.cs new file mode 100644 index 00000000..b9d746b1 --- /dev/null +++ b/AtlasSharp/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/AtlasSharp/AtlasSharp.csproj b/AtlasSharp/AtlasSharp.csproj new file mode 100644 index 00000000..7567f31f --- /dev/null +++ b/AtlasSharp/AtlasSharp.csproj @@ -0,0 +1,99 @@ + + + + WinExe + net7.0-windows + enable + true + AtlasSharp + AtlasSharp + true + Always + true + + + + x64 + + + + x64 + + + + + + + + + + + + + + Always + + + + + + + + + + MSBuild:Compile + Wpf + Designer + + + + + + ThirdParty\3dmigoto_shader_decomp.exe + Always + + + ThirdParty\d3dcompiler_46.dll + Always + + + ThirdParty\FbxWrapperNative.dll + Always + + + ThirdParty\librevorb.dll + Always + + + ThirdParty\oo2core_3_win64.dll + Always + + + ThirdParty\oo2core_9_win64.dll + Always + + + ThirdParty\packed_codebooks_aoTuV_603.bin + Always + + + + + + Shaders\Lighting.hlsl + Always + + + + + + Shaders\Debug.hlsl + Always + + + + + + + + diff --git a/AtlasSharp/AtlasView.xaml b/AtlasSharp/AtlasView.xaml new file mode 100644 index 00000000..4f233c7e --- /dev/null +++ b/AtlasSharp/AtlasView.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/AtlasSharp/MainWindow.xaml b/AtlasSharp/MainWindow.xaml new file mode 100644 index 00000000..6df34265 --- /dev/null +++ b/AtlasSharp/MainWindow.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/AtlasSharp/MainWindow.xaml.cs b/AtlasSharp/MainWindow.xaml.cs new file mode 100644 index 00000000..0667f4a9 --- /dev/null +++ b/AtlasSharp/MainWindow.xaml.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Cryptography.Xml; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; +using DirectXTexNet; +using SharpDX; +using SharpDX.D3DCompiler; +using SharpDX.Direct3D; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using SharpDX.Windows; +using Tiger; +using Tiger.Schema; +using Tiger.Schema.Shaders; +using Tiger.Schema.Static; +using Blob = Tiger.Blob; +using Device = SharpDX.Direct3D11.Device; +using Point = System.Windows.Point; +using RegisterComponentType = Tiger.Schema.RegisterComponentType; +using Vector4 = System.Numerics.Vector4; + +namespace AtlasSharp; + + + +/// +/// Interaction logic for MainWindow.xaml +/// +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); + InitTiger(); + + AtlasView.Loaded += (sender, args) => { + // uint staticHash = 0x80bce840; // 40E8BC80 + // uint staticHash = 0x80bce912; // 12e9bc80 + string staticHash = "40E8BC80"; + AtlasView.LoadStatic(new FileHash(staticHash));}; + } + + private void InitTiger() + { + HashSet lazyStrategistSingletons = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes()) + .Select(t => t.GetNonGenericParent(typeof(Strategy.LazyStrategistSingleton<>))) + .Where(t => t is { ContainsGenericParameters: false }) + .Select(t => t.GetNonGenericParent(typeof(Strategy.StrategistSingleton<>))) + .ToHashSet(); + + // Get all classes that inherit from StrategistSingleton<> + // Then call RegisterEvents() on each of them + HashSet allStrategistSingletons = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes()) + .Select(t => t.GetNonGenericParent(typeof(Strategy.StrategistSingleton<>))) + .Where(t => t is { ContainsGenericParameters: false }) + .ToHashSet(); + + allStrategistSingletons.ExceptWith(lazyStrategistSingletons); + + // order dependencies from InitializesAfterAttribute + List strategistSingletons = SortByInitializationOrder(allStrategistSingletons.ToList()).ToList(); + + foreach (Type strategistSingleton in strategistSingletons) + { + strategistSingleton.GetMethod("RegisterEvents", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null); + } + + string[] args = Environment.GetCommandLineArgs(); + CharmInstance.Args = new CharmArgs(args); + CharmInstance.InitialiseSubsystems(); + } + + private static IEnumerable SortByInitializationOrder(IEnumerable types) + { + var dependencyMap = new Dictionary>(); + var dependencyCount = new Dictionary(); + + // Build dependency map and count dependencies + foreach (var type in types) + { + var attributes = type.GenericTypeArguments[0].GetCustomAttributes(typeof(InitializeAfterAttribute), true); + foreach (InitializeAfterAttribute attribute in attributes) + { + var dependentType = attribute.TypeToInitializeAfter.GetNonGenericParent( + typeof(Strategy.StrategistSingleton<>)); + if (!dependencyMap.ContainsKey(dependentType)) + { + dependencyMap[dependentType] = new List(); + dependencyCount[dependentType] = 0; + } + dependencyMap[dependentType].Add(type); + dependencyCount[type] = dependencyCount.ContainsKey(type) ? dependencyCount[type] + 1 : 1; + } + } + + // Perform topological sorting + var sortedTypes = types.Where(t => !dependencyCount.ContainsKey(t)).ToList(); + var queue = new Queue(dependencyMap.Keys.Where(k => dependencyCount[k] == 0)); + while (queue.Count > 0) + { + var type = queue.Dequeue(); + sortedTypes.Add(type); + + if (dependencyMap.ContainsKey(type)) + { + foreach (var dependentType in dependencyMap[type]) + { + dependencyCount[dependentType]--; + if (dependencyCount[dependentType] == 0) + { + queue.Enqueue(dependentType); + } + } + } + } + + if (sortedTypes.Count < types.Count()) + { + throw new InvalidOperationException("Circular dependency detected."); + } + + return sortedTypes; + } +} + diff --git a/AtlasSharp/MiniTri.fx b/AtlasSharp/MiniTri.fx new file mode 100644 index 00000000..6fc32dee --- /dev/null +++ b/AtlasSharp/MiniTri.fx @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------------- +// Original code from SlimDX project. +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +struct VS_IN +{ + float4 pos : POSITION; + float4 col : COLOR; +}; + +struct PS_IN +{ + float4 pos : SV_POSITION; + float4 col : COLOR; +}; + +PS_IN VS( VS_IN input ) +{ + PS_IN output = (PS_IN)0; + + output.pos = input.pos; + output.col = input.col; + + return output; +} + +float4 PS( PS_IN input ) : SV_Target +{ + return input.col; +} + +technique10 Render +{ + pass P0 + { + SetGeometryShader( 0 ); + SetVertexShader( CompileShader( vs_4_0, VS() ) ); + SetPixelShader( CompileShader( ps_4_0, PS() ) ); + } +} \ No newline at end of file diff --git a/AtlasSharp/NativeMethods.cs b/AtlasSharp/NativeMethods.cs new file mode 100644 index 00000000..21ca543b --- /dev/null +++ b/AtlasSharp/NativeMethods.cs @@ -0,0 +1,107 @@ +using System; +using System.Runtime.InteropServices; +using System.Windows; +using Tiger; +using Tiger.Schema.Static; + +namespace AtlasSharp; + +public enum CameraMode +{ + Orbit, + Free, +} + +public static class NativeMethods +{ + /// + /// Variable used to track whether the missing dependency dialog has been displayed, + /// used to prevent multiple notifications of the same failure. + /// + private static bool errorHasDisplayed; + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern long Init(nint hwnd, int width, int height); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void ResizeWindow(int width, int height); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void Cleanup(); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern long Render(IntPtr resourcePointer, bool isNewSurface); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void MoveCamera(MoveDirection direction); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void SetCameraMode(CameraMode mode); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void ResetCamera(); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern long RegisterMouseDelta(float mouseX, float mouseY); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern long MoveOrbitOrigin(float mouseX, float mouseY); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern long RegisterMouseScroll(int delta); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern long CreateStaticMesh(uint hash, Blob staticMeshTransforms); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern long AddStaticMeshBufferGroup(uint hash, BufferGroup bufferGroup); + + [DllImport("Atlas.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern long CreateStaticMeshPart(uint hash, AtlasView.PartInfo partInfo); + + /// + /// Method used to invoke an Action that will catch DllNotFoundExceptions and display a warning dialog. + /// + /// The Action to invoke. + public static void InvokeWithDllProtection(Action action) + { + InvokeWithDllProtection( + () => + { + action.Invoke(); + return 0; + }); + } + + /// + /// Method used to invoke A Func that will catch DllNotFoundExceptions and display a warning dialog. + /// + /// The Func to invoke. + /// The return value of func, or default(T) if a DllNotFoundException was caught. + /// The return type of the func. + public static T InvokeWithDllProtection(Func func) + { + try + { + return func.Invoke(); + } + catch (DllNotFoundException e) + { + if (!errorHasDisplayed) + { + MessageBox.Show("This sample requires:\nManual build of the D3DVisualization project, which requires installation of Windows 10 SDK or DirectX SDK.\n" + + "Installation of the DirectX runtime on non-build machines.\n\n"+ + "Detailed exception message: " + e.Message, "WPF D3D11 Interop", + MessageBoxButton.OK, MessageBoxImage.Error); + errorHasDisplayed = true; + + if (Application.Current != null) + { + Application.Current.Shutdown(); + } + } + } + + return default(T); + } +} diff --git a/AtlasSharp/Properties/launchSettings.json b/AtlasSharp/Properties/launchSettings.json new file mode 100644 index 00000000..7027a41c --- /dev/null +++ b/AtlasSharp/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "AtlasSharp": { + "commandName": "Project", + "nativeDebugging": true, + "hotReloadEnabled": false + } + } +} \ No newline at end of file diff --git a/Charm/UnrealConfigView.xaml b/Charm/UnrealConfigView.xaml new file mode 100644 index 00000000..c7172b19 --- /dev/null +++ b/Charm/UnrealConfigView.xaml @@ -0,0 +1,13 @@ + + + + + diff --git a/Charm/UnrealConfigView.xaml.cs b/Charm/UnrealConfigView.xaml.cs new file mode 100644 index 00000000..ce67e440 --- /dev/null +++ b/Charm/UnrealConfigView.xaml.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using Tiger; + +namespace Charm; + +public partial class UnrealConfigView : UserControl +{ + public UnrealConfigView() + { + InitializeComponent(); + _config = CharmInstance.GetSubsystem(); + } + + private ConfigSubsystem _config; + + + public void OnControlLoaded(object sender, RoutedEventArgs e) + { + PopulateConfigPanel(); + } + + private void PopulateConfigPanel() + { + UnrealConfigPanel.Children.Clear(); + + TextBlock header = new TextBlock(); + header.Text = "Unreal Engine Settings"; + header.FontSize = 30; + UnrealConfigPanel.Children.Add(header); + + // Unreal interop path + ConfigSettingControl cui = new ConfigSettingControl(); + cui.SettingName = "Unreal content path"; + var val = _config.GetUnrealInteropPath(); + cui.SettingValue = val == "" ? "Not set" : val; + cui.ChangeButton.Click += UnrealInteropPath_OnClick; + UnrealConfigPanel.Children.Add(cui); + + // Enable UE5 interop + ConfigSettingControl cii = new ConfigSettingControl(); + cii.SettingName = "Generate Unreal Engine importing files"; + bool bval = _config.GetUnrealInteropEnabled(); + cii.SettingValue = bval.ToString(); + cii.ChangeButton.Click += UnrealInteropEnabled_OnClick; + UnrealConfigPanel.Children.Add(cii); + } + + private void UnrealInteropPath_OnClick(object sender, RoutedEventArgs e) + { + OpenUnrealInteropPathDialog(); + PopulateConfigPanel(); + } + + public void OpenUnrealInteropPathDialog() + { + using (var dialog = new System.Windows.Forms.FolderBrowserDialog()) + { + bool success = false; + while (!success) + { + dialog.Description = "Select the folder where you want to import to unreal engine (eg Content folder)"; + System.Windows.Forms.DialogResult result = dialog.ShowDialog(); + if (result == System.Windows.Forms.DialogResult.OK) + { + success = _config.TrySetUnrealInteropPath(dialog.SelectedPath); + } + else + { + return; + } + } + } + } + + private void UnrealInteropEnabled_OnClick(object sender, RoutedEventArgs e) + { + if (_config.GetUnrealInteropPath() == "") + { + MessageBox.Show("Please set the path to the Unreal Engine content folder first."); + return; + } + _config.SetUnrealInteropEnabled(!_config.GetUnrealInteropEnabled()); + PopulateConfigPanel(); + } +} diff --git a/Tiger/Exporters/import_to_blender.py b/Tiger/Exporters/import_to_blender.py new file mode 100644 index 00000000..6bd4887c --- /dev/null +++ b/Tiger/Exporters/import_to_blender.py @@ -0,0 +1,312 @@ +import bpy +import json +import mathutils +import os +#!!!DO NOT MANUALLY IMPORT THE FBX, THE SCRIPT WILL DO IT FOR YOU!!! + +#Adapted from Monteven's UE5 import script + +#Globally gets all the objects in the scene +objects = bpy.data.objects +scene = bpy.context.scene + +#Info +Type = "IMPORT_TYPE" +Name = "HASH" +Filepath = os.path.abspath(bpy.context.space_data.text.filepath+"/..") #"OUTPUT_DIR" +# + +#Files to open +info_name = Name + "_info.cfg" +config = json.load(open(Filepath + f"\\{info_name}")) +FileName = Filepath + "\\" + Name + ".fbx" +# + +static_names = {} #original static objects + +def assemble_map(): + print(f"Starting import on {Type}: {Name}") + + #make a collection with the name of the imported fbx for the objects + bpy.data.collections.new(str(Name)) + bpy.context.scene.collection.children.link(bpy.data.collections[str(Name)]) + bpy.context.view_layer.active_layer_collection = bpy.context.view_layer.layer_collection.children[str(Name)] + + bpy.ops.import_scene.fbx(filepath=FileName, use_custom_normals=True, ignore_leaf_bones=True, automatic_bone_orientation=True) #Just imports the fbx, no special settings needed + + assign_materials() + add_to_collection() + + newobjects = bpy.data.collections[str(Name)].objects + + print(f"Imported {Type}: {Name}") + + #Merge statics, create instances for maps only + if Is_Map(): + print("Merging Map Statics... ") + tmp = [] + for obj in newobjects: + #deselect all objects + bpy.ops.object.select_all(action='DESELECT') + tmp.append(obj.name[:8]) + + #merge static parts into one object + for obj in tmp: + bpy.ops.object.select_all(action='DESELECT') + for meshes, mats in config["Parts"].items(): + if meshes[:8] == obj and meshes in bpy.context.view_layer.objects: + print(meshes + " belongs to " + obj) + bpy.data.objects[meshes].select_set(True) + bpy.context.view_layer.objects.active = bpy.data.objects[meshes] + bpy.ops.object.join() + bpy.ops.outliner.orphans_purge() + + #merge static parts into one object, Old method + # for x in range(0, 4): #For some reason one pass doesnt work, this slows the import down a bit, idk a better fix + # for obj in tmp: + # bpy.ops.object.select_all(action='DESELECT') + # #print(obj) + # for obj2 in newobjects: + # if obj2.name[:8] == obj and obj in tmp: + # tmp.remove(obj) + # obj2.select_set(True) + # bpy.context.view_layer.objects.active = obj2 + # bpy.ops.object.join() + # bpy.ops.outliner.orphans_purge() + + newobjects = [] #Clears the list just in case + newobjects = bpy.data.collections[str(Name)].objects #Readds the objects in the collection to the list + + for x in newobjects: + if len(config["Instances"].items()) <= 1 and len(config["Parts"].items()) <= 1: #Fix for error that occurs when theres only 1 object in the fbx + for newname, value in config["Instances"].items(): + x.name = newname + + obj_name = x.name[:8] + if obj_name not in static_names.keys(): + static_names[obj_name] = [] + static_names[obj_name].append(x.name) + + print("Instancing...") + + for static, instances in config["Instances"].items(): + try: # fix this + parts = static_names[static] + except: + print(f"Failed on {static}. FBX may contain only 1 object") + continue + + for part in parts: + for instance in instances: + ob_copy = bpy.data.objects[part].copy() + bpy.context.collection.objects.link(ob_copy) #makes the instances + + location = [instance["Translation"][0], instance["Translation"][1], instance["Translation"][2]] + #Reminder that blender uses WXYZ, the order in the confing file is XYZW, so W is always first + quat = mathutils.Quaternion([instance["Rotation"][3], instance["Rotation"][0], instance["Rotation"][1], instance["Rotation"][2]]) + + ob_copy.location = location + ob_copy.rotation_mode = 'QUATERNION' + ob_copy.rotation_quaternion = quat + ob_copy.scale = [instance["Scale"]]*3 + + if "Terrain" in Type: + for x in newobjects: + x.select_set(True) + bpy.ops.object.rotation_clear(clear_delta=False) #Clears the rotation of the terrain + + if not Is_Map(): + for x in newobjects: + x.select_set(True) + #Clear the scale and rotation of the entity + bpy.ops.object.rotation_clear(clear_delta=False) + bpy.ops.object.scale_clear(clear_delta=False) + + cleanup() + +def assign_materials(): + print("Assigning materials...") + + materials = bpy.data.materials + for k in materials: #Removes the last _ and anything after it in the material name, so the name matches the config files + if k.name.count("_") > 1: + k.name = k.name[:k.name.rfind("_")] + + for staticname, matname in config["Parts"].items(): #Renames the materials to the actual material hash in the config file + for mats in materials: + if mats.name == staticname: + mats.name = matname + else: + if len(config["Parts"].items()) <= 1: + for name, mat in config["Parts"].items(): + bpy.data.objects[name].active_material.name = mat + + for obj in bpy.data.objects: #remove any duplicate materials that may have been created + for slt in obj.material_slots: + part = slt.name.rpartition('.') + if part[2].isnumeric() and part[0] in materials: + slt.material = materials.get(part[0]) + + #Get all the images in the directory and load them + for img in os.listdir(Filepath + "/Textures/"): + if img.endswith("TEX_EXT"): + bpy.data.images.load(Filepath + "/Textures/" + f"/{img}", check_existing = True) + print(f"Loaded {img}") + + #New way of getting info from cfg, thank you Mont + d = {x : y["PS"] for x, y in config["Materials"].items()} + + for k, mat in d.items(): + matnodes = bpy.data.materials[k].node_tree.nodes + if matnodes.find('Principled BSDF') != -1: + matnodes['Principled BSDF'].inputs['Metallic'].default_value = 0 + + #To make sure the current material already doesnt have at least one texture node + if not len(find_nodes_by_type(bpy.data.materials[k], 'TEX_IMAGE')) > 0: # + tex_num = 0 #To keep track of the current position in the list + for n, info in mat.items(): + #current_image = "PS_" + str(n) + "_" + info["Hash"] + "TEX_EXT" + current_image = info["Hash"] + "TEX_EXT" + + if info["SRGB"]: + colorspace = "sRGB" + else: + colorspace = "Non-Color" + + texnode = matnodes.new('ShaderNodeTexImage') + texnode.hide = True + texnode.location = (-370.0, 200.0 + (float(tex_num)*-1.1)*50) #shitty offsetting + + texture = bpy.data.images.get(current_image) + if texture: + texnode.label = texture.name + texture.colorspace_settings.name = colorspace + texture.alpha_mode = "CHANNEL_PACKED" + texnode.image = texture #Assign the texture to the node + + #assign a texture to material's diffuse and normal just to help a little + if texture.colorspace_settings.name == "sRGB": + link_diffuse(bpy.data.materials[k]) + if texture.colorspace_settings.name == "Non-Color": + if int(tex_num) == 0: + link_diffuse(bpy.data.materials[k]) + else: + link_normal(bpy.data.materials[k], int(tex_num)) + tex_num += 1 + +def find_nodes_by_type(material, node_type): + """ Return a list of all of the nodes in the material + that match the node type. + Return an empty list if the material doesn't use + nodes or doesn't have a tree. + """ + node_list = [] + if material.use_nodes and material.node_tree: + for n in material.node_tree.nodes: + if n.type == node_type: + node_list.append(n) + return node_list + +def link_diffuse(material): + """ Finds at least one image texture in the material + and at least one Principled shader. + If they both exist and neither have a link to + the relevant input socket, connect them. + There are many ways this can fail. + if there's no image; if there's no principled + shader; if the selected image/principled sockets + are already in use. + Returns false on any detected error. + Does not try alternatives if there are multiple + images or multiple principled shaders. + """ + it_list = find_nodes_by_type(material, 'TEX_IMAGE') + s_list = find_nodes_by_type(material, 'BSDF_PRINCIPLED') + if len(s_list) == 0: + return False + image_node = it_list[0] + shader_node = s_list[0] + image_socket = image_node.outputs['Color'] + shader_socket = shader_node.inputs['Base Color'] + if shader_socket.is_linked: + return + material.node_tree.links.new(shader_socket, image_socket) + + +def link_normal(material, num = 0): + it_list = find_nodes_by_type(material, 'TEX_IMAGE') + s_list = find_nodes_by_type(material, 'NORMAL_MAP') + if len(s_list) == 0: + return False + image_node = it_list[num] + #print(len(image_node.items())) + shader_node = s_list[0] + if image_node.image.colorspace_settings.name == "Non-Color": + image_socket = image_node.outputs['Color'] + shader_socket = shader_node.inputs['Color'] + if shader_socket.is_linked: + return + material.node_tree.links.new(shader_socket, image_socket) + +def cleanup(): + print(f"Cleaning up...") + #Delete all the objects in static_names + if Is_Map(): + for name in static_names.values(): + bpy.data.objects.remove(bpy.data.objects[name[0]]) + + #Removes unused data such as duplicate images, materials, etc. + for block in bpy.data.meshes: + if block.users == 0: + bpy.data.meshes.remove(block) + + for block in bpy.data.materials: + if block.users == 0: + bpy.data.materials.remove(block) + + for block in bpy.data.textures: + if block.users == 0: + bpy.data.textures.remove(block) + + for block in bpy.data.images: + if block.users == 0: + bpy.data.images.remove(block) + print("Done cleaning up!") + +def add_to_collection(): + # List of object references + objs = bpy.context.selected_objects + # Set target collection to a known collection + coll_target = bpy.context.scene.collection.children.get(str(Name)) + # If target found and object list not empty + if coll_target and objs: + # Loop through all objects + for ob in objs: + # Loop through all collections the obj is linked to + for coll in ob.users_collection: + # Unlink the object + coll.objects.unlink(ob) + # Link each object to the target collection + coll_target.objects.link(ob) + +def Is_Map(): + if "Map" in Type: + return True + if "Terrain" in Type: + return True + else: + return False + +def ShowMessageBox(message = "", title = "Message Box", icon = 'INFO'): + def draw(self, context): + self.layout.label(text=message) + bpy.context.window_manager.popup_menu(draw, title = title, icon = icon) + + +if __name__ == "__main__": + #Shows a message box with a message, custom title, and a specific icon + ShowMessageBox(f"Importing {Name}", "This might take some time! (Especially on multiple imports)", 'ERROR') + #To give the message box a chance to show up + bpy.app.timers.register(assemble_map, first_interval=0.3) + #Deselect all objects just in case + bpy.ops.object.select_all(action='DESELECT') diff --git a/Tiger/Exporters/import_to_ue5.py b/Tiger/Exporters/import_to_ue5.py new file mode 100644 index 00000000..80bae1a7 --- /dev/null +++ b/Tiger/Exporters/import_to_ue5.py @@ -0,0 +1,366 @@ +import unreal +import os +import json + + +class CharmImporter: + def __init__(self, folder_path: str, b_unique_folder: bool) -> None: + self.folder_path = folder_path + info_name = f"{__file__.split('/')[-1].split('_')[0]}_info.cfg" + self.config = json.load(open(self.folder_path + f"/{info_name}")) + if b_unique_folder: + self.content_path = f"{self.config['UnrealInteropPath']}/{self.config['MeshName']}" + else: + self.content_path = f"{self.config['UnrealInteropPath']}" + if not unreal.EditorAssetLibrary.does_directory_exist(self.content_path): + unreal.EditorAssetLibrary.make_directory(self.content_path) + + def import_entity(self): + self.make_materials() + self.import_entity_mesh() + self.assign_entity_materials() + unreal.EditorAssetLibrary.save_directory(f"/Game/{self.content_path}/", False) + + def import_static(self): + self.make_materials() + self.import_static_mesh(combine=True) + self.assign_static_materials() + unreal.EditorAssetLibrary.save_directory(f"/Game/{self.content_path}/", False) + + def import_map(self): + self.make_materials() + self.import_static_mesh(combine=False) + self.assign_map_materials() + self.assemble_map() + unreal.EditorAssetLibrary.save_directory(f"/Game/{self.content_path}/", False) + + def assemble_map(self) -> None: + # Create new level asset + unreal.EditorLevelLibrary.new_level(f'/Game/{self.content_path}/map_{self.config["MeshName"]}') + + static_names = {} + for x in unreal.EditorAssetLibrary.list_assets(f'/Game/{self.content_path}/Statics/', recursive=False): + if "Group" in x: + name = x.split('/')[-1].split("_")[1] + else: + name = x.split('/')[-1].split(".")[0] + if name not in static_names.keys(): + static_names[name] = [] + static_names[name].append(x) + + for static, instances in self.config["Instances"].items(): + try: # fix this + parts = static_names[static] + except: + print(f"Failed on {static}") + for part in parts: + sm = unreal.EditorAssetLibrary.load_asset(part) + for instance in instances: + quat = unreal.Quat(instance["Rotation"][0], instance["Rotation"][1], instance["Rotation"][2], instance["Rotation"][3]) + euler = quat.euler() + rotator = unreal.Rotator(-euler.x+180, -euler.y+180, -euler.z) + location = [-instance["Translation"][0]*100, instance["Translation"][1]*100, instance["Translation"][2]*100] + s = unreal.EditorLevelLibrary.spawn_actor_from_object(sm, location=location, rotation=rotator) # l must be UE4 Object + s.set_actor_label(s.get_actor_label() + f"_{instance['Scale']}") + s.set_actor_relative_scale3d([instance["Scale"]]*3) + + + # for i, a in enumerate(assets): + # name = a.split('.')[0].split('/')[-1].split(origin_folder)[-1][1:] + # # s = name.split('_') + # # print(s) + # # continue + # sm = unreal.EditorAssetLibrary.load_asset(a) + # # instance_component = unreal.HierarchicalInstancedStaticMeshComponent() + # # instance_component.set_editor_property("static_mesh", sm) + # try: + # data = helper[name] + # except KeyError: + # continue + # # transforms = unreal.Array(unreal.Transform) + # for d in data: + # r = d[1] + # l = d[0] + # l = [-l[0]*100, l[1]*100, l[2]*100] + # rotator = unreal.Rotator(-r[0], r[1], -r[2]) + # # transform = rotator.transform() + # # transform.set_editor_property("translation", l) + # # transform.set_editor_property("scale3d", [d[2]]*3) + # # transforms.append(transform) + # s = unreal.EditorLevelLibrary.spawn_actor_from_object(sm, location=l, rotation=rotator) # l must be UE4 Object + # s.set_actor_scale3d([d[2]]*3) + # + # # instance_component.add_instances(transforms, False) + # # unreal.EditorAssetLibrary.duplicate_asset(template_path + "HLODTemplate", f"/Game/{data_path}/actors/{name}") + # # actorbp = unreal.EditorAssetLibrary.load_asset(f"/Game/{data_path}/actors/{name}") + # # actor_spawn = unreal.EditorAssetLibrary.load_blueprint_class(f"/Game/{data_path}/actors/{name}") + # # actor = unreal.EditorLevelLibrary.spawn_actor_from_class(actor_spawn, location=[0, 0, 0]) + # # actor.set_actor_label(name, True) + # + # # instance_component.attach_to_component(actor.root_component, ' ', unreal.AttachmentRule.KEEP_WORLD, + # # unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, + # # False) + # # actor.set_editor_property('root_component', instance_component) + unreal.EditorLevelLibrary.save_current_level() + + def assign_map_materials(self) -> None: + for x in unreal.EditorAssetLibrary.list_assets(f'/Game/{self.content_path}/Statics/', recursive=False): + # Identify static mesh + mesh = unreal.load_asset(x) + + # Check material slots and compare names from config + mesh_materials = mesh.get_editor_property("static_materials") + material_slot_name_dict = {x: unreal.load_asset(f"/Game/{self.config['UnrealInteropPath']}/Materials/M_{y}") for x, y in self.config["Parts"].items()} + new_mesh_materials = [] + for skeletal_material in mesh_materials: + slot_name = skeletal_material.get_editor_property("material_slot_name").__str__() + slot_name = '_'.join(slot_name.split('_')[:-1]) + if slot_name in material_slot_name_dict.keys(): + if material_slot_name_dict[slot_name] != None: + skeletal_material.set_editor_property("material_interface", material_slot_name_dict[slot_name]) + new_mesh_materials.append(skeletal_material) + print(new_mesh_materials) + mesh.set_editor_property("static_materials", new_mesh_materials) + + def assign_static_materials(self) -> None: + # Identify static mesh + mesh = unreal.load_asset(f"/Game/{self.content_path}/{self.config['MeshName']}") + + # Check material slots and compare names from config + mesh_materials = mesh.get_editor_property("static_materials") + material_slot_name_dict = {x: unreal.load_asset(f"/Game/{self.config['UnrealInteropPath']}/Materials/M_{y}") for x, y in self.config["Parts"].items()} + new_mesh_materials = [] + for skeletal_material in mesh_materials: + slot_name = skeletal_material.get_editor_property("material_slot_name").__str__() + slot_name = '_'.join(slot_name.split('_')[:-1]) + if slot_name in material_slot_name_dict.keys(): + if material_slot_name_dict[slot_name] != None: + skeletal_material.set_editor_property("material_interface", material_slot_name_dict[slot_name]) + new_mesh_materials.append(skeletal_material) + print(new_mesh_materials) + mesh.set_editor_property("static_materials", new_mesh_materials) + + def assign_entity_materials(self) -> None: + # Identify entity mesh + mesh = unreal.load_asset(f"/Game/{self.content_path}/{self.config['MeshName']}") + + # Check material slots and compare names from config + mesh_materials = mesh.get_editor_property("materials") + material_slot_name_dict = {x: unreal.load_asset(f"/Game/{self.config['UnrealInteropPath']}/Materials/M_{y}") for x, y in self.config["Parts"].items()} + new_mesh_materials = [] + for skeletal_material in mesh_materials: + slot_name = skeletal_material.get_editor_property("material_slot_name").__str__() + slot_name = '_'.join(slot_name.split('_')[:-1]) + if slot_name in material_slot_name_dict.keys(): + if material_slot_name_dict[slot_name] != None: + skeletal_material.set_editor_property("material_interface", material_slot_name_dict[slot_name]) + new_mesh_materials.append(skeletal_material) + print(new_mesh_materials) + mesh.set_editor_property("materials", new_mesh_materials) + + def import_entity_mesh(self) -> None: + task = unreal.AssetImportTask() + task.set_editor_property("automated", True) + task.set_editor_property("destination_path", f"/Game/{self.content_path}/") + task.set_editor_property("filename", f"{self.folder_path}/{self.config['MeshName']}.fbx") + task.set_editor_property("replace_existing", True) + task.set_editor_property("save", True) + + options = unreal.FbxImportUI() + options.set_editor_property('import_mesh', True) + options.set_editor_property('import_textures', False) + options.set_editor_property('import_materials', False) + options.set_editor_property('import_as_skeletal', True) + # todo fix this, not static mesh import data + options.static_mesh_import_data.set_editor_property('convert_scene', False) + options.static_mesh_import_data.set_editor_property('combine_meshes', False) + options.static_mesh_import_data.set_editor_property('generate_lightmap_u_vs', False) + options.static_mesh_import_data.set_editor_property('auto_generate_collision', False) + options.static_mesh_import_data.set_editor_property("vertex_color_import_option", unreal.VertexColorImportOption.REPLACE) + options.static_mesh_import_data.set_editor_property("build_nanite", False) # todo add nanite option + task.set_editor_property("options", options) + + unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) + + def import_static_mesh(self, combine) -> None: + task = unreal.AssetImportTask() + task.set_editor_property("automated", True) + task.set_editor_property("destination_path", f"/Game/{self.content_path}/Statics/") + task.set_editor_property("filename", f"{self.folder_path}/{self.config['MeshName']}.fbx") + task.set_editor_property("replace_existing", True) + task.set_editor_property("save", True) + + options = unreal.FbxImportUI() + options.set_editor_property('import_mesh', True) + options.set_editor_property('import_textures', False) + options.set_editor_property('import_materials', False) + options.set_editor_property('import_as_skeletal', False) + options.static_mesh_import_data.set_editor_property('convert_scene', False) + options.static_mesh_import_data.set_editor_property('import_uniform_scale', 100.0) + options.static_mesh_import_data.set_editor_property('combine_meshes', combine) + options.static_mesh_import_data.set_editor_property('generate_lightmap_u_vs', False) + options.static_mesh_import_data.set_editor_property('auto_generate_collision', False) + options.static_mesh_import_data.set_editor_property('normal_import_method', unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS) + options.static_mesh_import_data.set_editor_property("vertex_color_import_option", unreal.VertexColorImportOption.REPLACE) + options.static_mesh_import_data.set_editor_property("build_nanite", False) # todo add nanite option + task.set_editor_property("options", options) + + unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) + + def make_materials(self) -> None: + # Get all materials we need + materials = list(self.config["Materials"].keys()) + + # Check if materials exist already + existing_materials = [x.split('/')[-1].split('.')[0][2:] for x in unreal.EditorAssetLibrary.list_assets(f'/Game/{self.config["UnrealInteropPath"]}/Materials/', recursive=False) if unreal.EditorAssetLibrary.find_asset_data(x).asset_class == 'Material'] + materials_to_make = list(set(materials)-set(existing_materials)) + + # If doesn't exist, make + for mat in materials_to_make: + material = self.make_material(mat) + unreal.MaterialEditingLibrary.recompile_material(material) + + def make_material(self, matstr: str) -> unreal.Material: + # Make base material + material = unreal.AssetToolsHelpers.get_asset_tools().create_asset("M_" + matstr, f"/Game/{self.config['UnrealInteropPath']}/Materials", unreal.Material, unreal.MaterialFactoryNew()) + + if os.path.exists(f"{self.folder_path}/Shaders/PS_{matstr}.usf"): + # Add textures + texture_samples = self.add_textures(material, matstr) + + # Add custom node + custom_node = self.add_custom_node(material, texture_samples, matstr) + + # Set output, not using in-built custom expression system because I want to leave it open for manual control + self.create_output(material, custom_node) + else: + material.set_editor_property("blend_mode", unreal.BlendMode.BLEND_MASKED) + const = unreal.MaterialEditingLibrary.create_material_expression(material, unreal.MaterialExpressionConstant, -300, 0) + unreal.MaterialEditingLibrary.connect_material_property(const, "", unreal.MaterialProperty.MP_OPACITY_MASK) + + return material + + def create_output(self, material: unreal.Material, custom_node: unreal.MaterialExpressionCustom) -> None: + mat_att = unreal.MaterialEditingLibrary.create_material_expression(material, unreal.MaterialExpressionBreakMaterialAttributes, -300, 0) + # Connect custom node to the new break + unreal.MaterialEditingLibrary.connect_material_expressions(custom_node, '', mat_att, 'Attr') + # Connect all outputs + unreal.MaterialEditingLibrary.connect_material_property(mat_att, "BaseColor", unreal.MaterialProperty.MP_BASE_COLOR) + unreal.MaterialEditingLibrary.connect_material_property(mat_att, "Metallic", unreal.MaterialProperty.MP_METALLIC) + unreal.MaterialEditingLibrary.connect_material_property(mat_att, "Roughness", unreal.MaterialProperty.MP_ROUGHNESS) + unreal.MaterialEditingLibrary.connect_material_property(mat_att, "EmissiveColor", unreal.MaterialProperty.MP_EMISSIVE_COLOR) + unreal.MaterialEditingLibrary.connect_material_property(mat_att, "OpacityMask", unreal.MaterialProperty.MP_OPACITY_MASK) + unreal.MaterialEditingLibrary.connect_material_property(mat_att, "Normal", unreal.MaterialProperty.MP_NORMAL) + unreal.MaterialEditingLibrary.connect_material_property(mat_att, "AmbientOcclusion", unreal.MaterialProperty.MP_AMBIENT_OCCLUSION) + + def add_custom_node(self, material: unreal.Material, texture_samples: list, matstr: str) -> unreal.MaterialExpressionCustom: + # Get sorted list of textures + sorted_texture_indices = list(sorted([int(x) for x in self.config["Materials"][matstr]["PS"].keys()])) + sorted_texture_vars = [f"t{x}" for x in sorted_texture_indices] + + custom_node = unreal.MaterialEditingLibrary.create_material_expression(material, unreal.MaterialExpressionCustom, -500, 0) + + # Definitions + + # Check the material shader exists + code = open(f"{self.folder_path}/Shaders/PS_{matstr}.usf", "r").read() + + # If the material is masked, change its blend mode for alpha + make it two-sided + if "// masked" in code: + material.set_editor_property("blend_mode", unreal.BlendMode.BLEND_MASKED) + material.set_editor_property("two_sided", True) + + + inputs = [] + for tvar in sorted_texture_vars: + ci = unreal.CustomInput() + ci.set_editor_property('input_name', tvar) + inputs.append(ci) + ci = unreal.CustomInput() + ci.set_editor_property('input_name', 'tx') + inputs.append(ci) + + custom_node.set_editor_property('code', code) + custom_node.set_editor_property('inputs', inputs) + custom_node.set_editor_property('output_type', unreal.CustomMaterialOutputType.CMOT_MATERIAL_ATTRIBUTES) + + for i, t in texture_samples.items(): + unreal.MaterialEditingLibrary.connect_material_expressions(t, 'RGBA', custom_node, f't{i}') + texcoord = unreal.MaterialEditingLibrary.create_material_expression(material, unreal.MaterialExpressionTextureCoordinate, -500, 300) + unreal.MaterialEditingLibrary.connect_material_expressions(texcoord, '', custom_node, 'tx') + + return custom_node + + def add_textures(self, material: unreal.Material, matstr: str) -> dict: + texture_samples = {} + + # Import texture list for the material + + tex_factory = unreal.TextureFactory() + tex_factory.set_editor_property('supported_class', unreal.Texture2D) + # Only pixel shader for now, todo replace .dds with the extension + names = [f"{self.folder_path}/Textures/PS_{i}_{texstruct['Hash']}.dds" for i, texstruct in self.config["Materials"][matstr]["PS"].items()] + srgbs = {int(i): texstruct['SRGB'] for i, texstruct in self.config["Materials"][matstr]["PS"].items()} + import_tasks = [] + for name in names: + asset_import_task = unreal.AssetImportTask() + asset_import_task.set_editor_property('filename', name) + asset_import_task.set_editor_property('destination_path', f'/Game/{self.content_path}/Textures') + asset_import_task.set_editor_property('save', True) + asset_import_task.set_editor_property('replace_existing', False) # dont do extra work if we dont need to + asset_import_task.set_editor_property('automated', True) + import_tasks.append(asset_import_task) + + unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(import_tasks) + + # Make texture samples + for i, texstruct in self.config["Materials"][matstr]["PS"].items(): + i = int(i) + texture_sample = unreal.MaterialEditingLibrary.create_material_expression(material, unreal.MaterialExpressionTextureSample, -1000, -500 + 250 * i) + + ts_TextureUePath = f"/Game/{self.content_path}/Textures/PS_{i}_{texstruct['Hash']}.PS_{i}_{texstruct['Hash']}" + ts_LoadedTexture = unreal.EditorAssetLibrary.load_asset(ts_TextureUePath) + if not ts_LoadedTexture: # some cubemaps and 3d textures cannot be loaded for now + continue + ts_LoadedTexture.set_editor_property('srgb', srgbs[i]) + if srgbs[i] == True: + ts_LoadedTexture.set_editor_property('compression_settings', unreal.TextureCompressionSettings.TC_DEFAULT) + else: + ts_LoadedTexture.set_editor_property('compression_settings', unreal.TextureCompressionSettings.TC_VECTOR_DISPLACEMENTMAP) + + texture_sample.set_editor_property('texture', ts_LoadedTexture) + if texstruct['SRGB'] == True: + texture_sample.set_editor_property("sampler_type", unreal.MaterialSamplerType.SAMPLERTYPE_COLOR) + else: + texture_sample.set_editor_property("sampler_type", unreal.MaterialSamplerType.SAMPLERTYPE_LINEAR_COLOR) + texture_samples[i] = texture_sample + + unreal.EditorAssetLibrary.save_loaded_asset(ts_LoadedTexture) + + return texture_samples + + """ + Updates all materials used by this model to the latest .usfs found in the Shaders/ folder. + Very useful for improving the material quality without much manual work. + """ + def update_material_code(self) -> None: + # Get all materials to update + materials = list(self.config["Materials"].keys()) + + # For each material, find the code node and update it + mats = {unreal.EditorAssetLibrary.load_asset(f"/Game/{self.config['UnrealInteropPath']}/Materials/M_{matstr}"): matstr for matstr in materials} + it = unreal.ObjectIterator() + for x in it: + if x.get_outer() in mats: + if isinstance(x, unreal.MaterialExpressionCustom): + code = open(f"{self.folder_path}/Shaders/PS_{mats[x.get_outer()]}.usf", "r").read() + x.set_editor_property('code', code) + print(f"Updated material {mats[x.get_outer()]}") + + unreal.EditorAssetLibrary.save_directory(f"/Game/{self.content_path}/Materials/", False) + + +if __name__ == "__main__": + importer = CharmImporter(os.path.dirname(os.path.realpath(__file__)), b_unique_folder=False) + importer.import_entity() + # importer.update_material_code() \ No newline at end of file