diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f1c284ac..3d1c5503e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,6 +190,12 @@ option( ON ) +option( + ORBITER_BUILD_VULKANCLIENT + "Build VulkanClient" + ON +) + option( ORBITER_BUILD_XRSOUND "Build XRSound module to enable sound in Orbiter" @@ -222,10 +228,21 @@ if(ORBITER_BUILD_D3D9CLIENT) CACHE PATH "Installation directory of DirectX SDK (June 2010)" + ) + find_package(VULKAN REQUIRED) + set(VULKAN_DIR + ${VULKAN_DIR} + CACHE + PATH + "Root directory of Vulkan SDK (containing include/lib directories)" ) + if(${DXSDK_DIR} STREQUAL "DXSDK_DIR-NOTFOUND") message(FATAL_ERROR "DirectX SDK not found. The D3D9 graphics client will not be built.") endif() + if(${VULKAN_DIR} STREQUAL "VULKAN_DIR-NOTFOUND") + message(FATAL_ERROR "Vulkan SDK not found. The Vulkan graphics will not be built.") + endif() endif() if (ORBITER_MAKE_DOC) diff --git a/Extern/CMakeLists.txt b/Extern/CMakeLists.txt index a8690b14e..1dbd4b64f 100644 --- a/Extern/CMakeLists.txt +++ b/Extern/CMakeLists.txt @@ -27,3 +27,15 @@ add_custom_target(CopyLDoc ALL COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/ldoc/ ${ORBITER_BINARY_ROOT_DIR}/packages/ldoc COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Penlight/lua/ ${ORBITER_BINARY_ROOT_DIR}/ ) + + + +#file(GLOB DXMATH_INL_FILES "${dxmath_SOURCE_DIR}/Inc/*.inl") +#file(GLOB DXMATH_H_FILES "${dxmath_SOURCE_DIR}/Inc/*.h") +#file(GLOB DXMATH_EXT_FILES "${dxmath_SOURCE_DIR}/Extensions/*.h") +#file(GLOB DXMATH_SH_FILES "${dxmath_SOURCE_DIR}/SHMath/*.*") + +#file(COPY ${DXMATH_INL_FILES} DESTINATION ${DXMATH_DIR}) +#file(COPY ${DXMATH_H_FILES} DESTINATION ${DXMATH_DIR}) +#file(COPY ${DXMATH_EXT_FILES} DESTINATION ${DXMATH_DIR}) +#file(COPY ${DXMATH_SH_FILES} DESTINATION ${DXMATH_DIR}) \ No newline at end of file diff --git a/Extern/dxmath/CHANGELOG.md b/Extern/dxmath/CHANGELOG.md new file mode 100644 index 000000000..49a03ad7e --- /dev/null +++ b/Extern/dxmath/CHANGELOG.md @@ -0,0 +1,217 @@ +# DirectXMath + +https://github.com/Microsoft/DirectXMath + +Release available for download on [GitHub](https://github.com/microsoft/DirectXMath/releases) + +## Release History + +### April 2025 (3.20b) +* `XM_DEPRECATED` macro uses C++14 ``[[deprecated]]`` standard attribute when supported +* Cmake project updates with build options for XDSP and SHMath + +### October 2024 (3.20) +* Fixed close-to-zero bug in the implementation of `TriangleTests::Intersects` +* Renamed implementation namespace from `DirectX::Internal` to `DirectX::MathInternal` to avoid some conformance issues with other libraries +* CMake project updates including support for ARM64EC +* Added GitHub Actions YAML files + +### February 2024 (3.19) +* Fix to address MinGW issue with ``__cpuid`` in cpuid.h vs. intrin.h +* Additional updates for clang/LLVM and GNUC +* Minor comment updates + +### December 2023 (3.18b) +* Hot-fix to address ``-Wunsafe-buffer-usage`` warnings from clang v16 +* Hot-fix to address MinGW issue with ``__cpuid`` in cpuid.h vs. intrin.h +* CMake project updates including pkg-config file generation + +### December 2022 (3.18) +* C++20 spaceship operators for XMFLOAT2, XMFLOAT3, etc. when building with ``/std:c++20 /Zc:_cplusplus`` +* Improved conformance for ARM64 when using `/Zc:arm64-aliased-neon-types-` +* Minor code review +* CMake project updated to require 3.20 or later +* Added Azure Dev Ops Pipeline YAML files + +### May 2022 (3.17b) +* Hot-fix to address ``-Wreserved-identifier`` warnings with clang v13 +* C++20 spaceship operators for XMFLOAT2, XMFLOAT3, etc. when building with ``/std:c++20 /Zc:_cplusplus`` +* Minor CMake project update + +### January 2022 (3.17) +* Added ColorsLinear namespace to DirectXColors.h with linear versions of .NET colors +* Optimized the ``XMMatrixRotationRollPitchYaw(FromVector)`` functions +* Fixed overread problem for 16bpp GPU types Load functions: + * ``XMUNIBBLE4``, ``XMU555``, ``XMU565``, ``XMBYTEN2``, ``XMBYTE2``, ``XMUBYTEN2``, ``XMUBYTE2`` +* ``XM_CACHE_LINE_SIZE`` updated for ARM/ARM64 targets to 128 bytes +* A few comments added to improve IntelliSense experience +* Conformance improvements for GNU compiler +* Minor code cleanup + +### January 2021 (3.16b) +* Hot-fixes to resolve build breaks for clang/LLVM and GCC on ARM64 +* ``XM_ALIGNED_DATA`` and ``XM_ALIGNED_STRUCT`` macros updated to use C++17 ``alignas`` when available + +### December 2020 (3.16) +* Added ``XMVectorLog10`` / ``XMVectorExp10`` +* Added ``XMColorRGBToYUV_UHD`` / ``XMColorYUVToRGB_UHD`` for Rec. 2020 YUV +* Added optional ``rhcoords`` parameter for BoundingFrustum ``CreateFromMatrix`` +* Added use of Intel® Short Vector Matrix Library (SVML) supported by VS 2019 + * Opt-in with ``_XM_SVML_INTRINSICS_``; opt-out with ``_XM_DISABLE_INTEL_SVML_`` +* Fixed denorm handling for ``XMConvertFloatToHalf`` +* Fixed flush (too small for denorm) handling for ``XMStoreFloat3PK`` +* Fixed clamping bug in ``XMStoreByteN4`` +* Cleaned up ARM-NEON intrinsics type issues for improved portability on GNUC +* Fixed ``GXMVECTOR`` for x86 ``__vectorcall`` +* Code review + +### April 2020 (3.15) +* Added ``XMMatrixVectorTensorProduct`` for creating a matrix from two vectors +* Use of m256 registers and FMA3 with ``/arch:AVX2`` for stream and some matrix functions +* Optimized load/stores for SSE2 float2 & float3 functions +* Optimized some instruction choices for better AMD CPU support +* Improved conformance for clang/LLVM, GCC, and MinGW compilers +* Code review (``constexpr`` / ``noexcept`` usage) +* Retired VS 2015 support + +### August 2019 (3.14) +* Added float control around IsNan functions to resolve issue with VS 2019 with ``/fp:fast`` +* XMVerifyCPUSupport updated for clang/LLVM cpuid implementation on x86/x64 +* Added support for clang/LLVM built-in platform defines as well as the MSVC ones +* Cleaned up ARM-NEON intrinsics type issues for improved portability +* Removed unneeded malloc.h include in DirectXMath.h +* Whitespace cleanup + +### July 2018 (3.13) +* ``XMFLOAT3X4``, ``XMFLOAT3X4A``, and associated Load/Store functions +* Move/copy constructors and assignment operators for C++ types +* Minor fix for XMVectorClamp behavior with NaN +* Fixed compilation warnings with VS 2017 (15.7 update), Intel C++ 18.0 compiler, and clang 6 +* Retired VS 2013 support +* Minor code cleanup + +### February 2018 (3.12) +* ARM64 use of fused multiply-accumulate intriniscs +* Conformance fix for XMConvertFloatToHalf +* Minor code cleanup + +### June 2017 (3.11) +* AVX optimization of XMMatrixMultiply and XMMatrixMultiplyTranspose +* AVX2 optimization for XMVectorSplatX +* FMA3 optimization of XMVectorMultiplyAdd and XMVectorNegativeMultiplySubtract (implied by /arch:AVX2) +* Conformance fixes to support compilation with Clang 3.7 + +### January 2017 (3.10) +* Added XMVectorSum for horizontal adds +* ARMv8 intrinsics use for ARM64 platform (division, rounding, half-precision conversion) +* Added SSE3 codepaths using opt-in ``_XM_SSE3_INTRINSICS_`` +* XMVectorRound fix for no-intrinsics to match round to nearest (even) +* XMStoreFloat3SE fix when max channel isn't a perfect power of 2 +* constexpr conformance fix and workaround for compiler bug in VS 2015 RTM +* Remove support for VS 2012 compilers +* Remove ``__vector4i`` deprecated type + +### June 2016 (3.09) +* Includes support for additional optimizations when built with /arch:AVX or /arch:AVX2 +* Added use of constexpr for type constructors, XMConvertToRadians, and XMConvertToDegrees +* Marked ``__vector4i``, ``XMXDEC4``, ``XMDECN4``, ``XMDEC4``, and associated Load & Store functions as deprecated. + * These are vestiges of Xbox 360 support and will be removed in a future release +* Renamed parameter in XMMatrixPerspectiveFov* to reduce user confusion when relying on IntelliSense +* XMU565, XMUNIBBLE4 constructors take uint8_t instead of int8_t + +### May 2016 +* DirectXMath 3.08 released under the MIT license + +### November 2015 (3.08) +* Added use of ``_mm_sfence`` for Stream methods +* Fixed bug with non-uniform scaling transforms for BoundingOrientedBox +* Added asserts for Near/FarZ in XMMatrix* methods +* Added use of ``=default`` for PODs with VS 2013/2015 +* Additional SSE and ARM-NEON optimizations for PackedVector functions + +### April 2015 (3.07) +* Fix customer reported bugs in BoundingBox methods +* Fix customer reported bug in XMStoreFloat3SE +* Fix customer reported bug in XMVectorATan2, XMVectorATan2Est +* Fix customer reported bug in XMVectorRound + +### October 2013 (3.06) +* Fixed load/store of XMFLOAT3SE to properly match the ``DXGI_FORMAT_R9G9B9E5_SHAREDEXP`` +* Added ``XMLoadUDecN4_XR`` and ``XMStoreUDecN4_XR`` to match ``DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM`` +* Added ``XMColorRGBToSRGB`` and ``XMColorSRGBToRGB`` to convert linear RGB <-> sRGB + +### July 2013 (3.05) +* Use x86/x64 ``__vectorcall`` calling-convention when available (``XM_CALLCONV``, ``HXMVECTOR``, ``FXMMATRIX`` introduced) +* Fixed bug with XMVectorFloor and XMVectorCeiling when given whole odd numbers (i.e. 105.0) +* Improved XMVectorRound algorithm +* ARM-NEON optimizations for XMVectorExp2, XMVectorLog2, XMVectorExpE, and XMVectorLogE +* ARM-NEON code paths use multiply-by-scalar intrinsics when supported +* Additional optimizations for ARM-NEON Stream functions +* Fixed potential warning C4723 using ``operator/`` or ``operator/=`` + +### March 2013 (3.04) +* ``XMVectorExp2``, ``XMVectorLog2``, ``XMVectorExpE``, and ``XMVectorLogE`` functions added to provide base-e support in addition to the existing base-2 support +* ``XMVectorExp`` and ``XMVectorLog`` are now aliases for XMVectorExp2 and XMVectorLog2 +* Additional optimizations for Stream functions +* XMVector3Cross now ensures w component is zero on ARM +* XMConvertHalfToFloat and XMConvertFloatToHalf now use IEEE 754 standard float16 behavior for INF/QNAN +* Updated matrix version Transform for BoundingOrientedBox and BoundingFrustum to handle scaling + +### March 2012 (3.03) +* *breaking change* Removed union members from XMMATRIX type to make it a fully 'opaque' type +* Marked single-parameter C++ constructors for XMFLOAT2, XMFLOAT2A, XMFLOAT3, XMFLOAT3A, XMFLOAT4, and XMFLOAT4A explicit + +### February 2012 (3.02) +* ARM-NEON intrinsics (selected by default for the ARM platform) +* Reworked XMVectorPermute, change of ``XM_PERMUTE_`` defines, removal of XMVectorPermuteControl +* Addition of ``XM_SWIZZLE_`` defines +* Optimizations for transcendental functions +* Template forms for permute, swizzle, shift-left, rotate-left, rotation-right, and insert +* Removal of deprecated types and functions + * ``XM_CACHE_LINE_SIZE`` define, XMVectorExpEst, XMVectorLogEst, XMVectorPowEst, XMVectorSinHEs, XMVectorCosHEst, XMVectorTanHEst, XMVector2InBoundsR, XMVector3InBoundsR, XMVector4InBoundsR +* Removed ``XM_STRICT_VECTOR4``; XMVECTOR in NO-INTRINSICS always defined without .x, .y, .z, .w, .v, or .u +* Additional bounding types +* SAL fixes and improvements + +### September 2011 (3.00) +* Renamed and reorganized the headers +* Introduced C++ namespaces +* Removed the Xbox 360-specific GPU types + * HENDN3, XMHEND3, XMUHENDN3, XMUHEND3, XMDHENN3, XMDHEN3, XMUDHENN3, XMUDHEN3, XMXICON4, XMXICO4, XMICON4, XMICO4, XMUICON4, XMUICO4 + +### July 2012 (XNAMath 2.05) +* Template forms have been added for `XMVectorPermute`, `XMVectorSwizzle`, `XMVectorShiftLeft`, `XMVectorRotateLeft`, `XMVectorRotateRight`, and `XMVectorInsert` +* The `XM_STRICT_XMMATRIX` compilation define has been added for opaque `XMMATRIX`. +* Stream stride and count arguments have been changed to `size_t` +* The ``pDeterminant`` parameter of `XMMatrixInverse` is now optional +* Additional operator= overloads for `XMBYTEN4`, `XMBYTE4`, `XMUBYTEN4`, and `XMUBYTE4` types are now available + +### February 2011 (XNAMath 2.04) +* Addition of new data types and associated load-store functions: + * `XMBYTEN2, XMBYTE2, XMUBYTEN2, XMUBYTE2` + * `XMLoadByteN2, XMLoadByte2, XMLoadUByteN2, XMLoadUByte2` + * `XMStoreByteN2, XMStoreByte2, XMStoreUByteN2, XMStoreUByte2` + * `XMINT2, XMUINT2, XMINT3, XMUINT3, XMINT4, XMUINT4` + * `XMLoadSInt2, XMLoadUInt2, XMLoadSInt3, XMLoadUInt3, XMLoadSInt4, XMLoadUInt4` + * `XMStoreSInt2, XMStoreUInt2, XMStoreSInt3, XMStoreUInt3, XMStoreSInt4, XMStoreUInt4` +* Marked most single-parameter C++ constructors with `explicit` keyword +* Corrected range issues with SSE implementations of `XMVectorFloor` and `XMVectorCeiling` + +### June 2010 (XNAMath 2.03) +* Addition of ``XMVectorDivide`` to optimize SSE2 vector division operations +* Unified handling of floating-point specials between the Windows SSE2 and no-intrinsics implementations +* Use of Visual Studio style SAL annotations +* Modifications to the C++ declarations for `XMFLOAT2A/3A/4A/4X3A/4X4A` to better support these types in C++ templates + +### February 2010 (XNAMath 2.02) +* Fixes to `XMStoreColor`, `XMQuaternionRotationMatrix`, `XMVectorATan2`, and `XMVectorATan2Est` + +### August 2009 (XNAMath 2.01) +* Adds ``XM_STRICT_VECTOR4``. This opt-in directive disallows the usage of XboxMath-like member accessors such as .x, .y, and .z. This makes it easier to write portable XNA Math code. +* Added conversion support for the following Windows graphics formats: + * 16-bit color formats (565, 555X, 5551) + * 4-bits per channel color formats (4444) + * Unique Direct3D 10/11 formats (``DXGI_FORMAT_R9G9B9E5_SHAREDEXP`` and ``DXGI_FORMAT_R11G11B10_FLOAT``) + +### March 2009 (XNAMath 2.00) +* Initial release (based on the Xbox 360 Xbox math library) diff --git a/Extern/dxmath/CODE_OF_CONDUCT.md b/Extern/dxmath/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..686e5e7a0 --- /dev/null +++ b/Extern/dxmath/CODE_OF_CONDUCT.md @@ -0,0 +1,10 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns +- Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) diff --git a/Extern/dxmath/Extensions/DirectXMathAVX.h b/Extern/dxmath/Extensions/DirectXMathAVX.h new file mode 100644 index 000000000..a0eaf3b96 --- /dev/null +++ b/Extern/dxmath/Extensions/DirectXMathAVX.h @@ -0,0 +1,275 @@ +//------------------------------------------------------------------------------------- +// DirectXMathAVX.h -- AVX (version 1) extensions for SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __arm__ || __aarch64__ +#error AVX not supported on ARM platform +#endif + +#include + +namespace DirectX +{ + + namespace AVX + { + + inline bool XMVerifyAVXSupport() + { + // Should return true for AMD Bulldozer, Intel "Sandy Bridge", and Intel "Ivy Bridge" or later processors + // with OS support for AVX (Windows 7 Service Pack 1, Windows Server 2008 R2 Service Pack 1, Windows 8, Windows Server 2012) + + // See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + int CPUInfo[4] = { -1 }; + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0); + #endif + + if (CPUInfo[0] < 1) + return false; + + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 1); + #endif + + // We check for AVX, OSXSAVE, SSSE4.1, and SSE3 + return ((CPUInfo[2] & 0x18080001) == 0x18080001); + } + + + //------------------------------------------------------------------------------------- + // Vector + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVectorReplicatePtr(_In_ const float *pValue) + { + return _mm_broadcast_ss(pValue); + } + + inline XMVECTOR XM_CALLCONV XMVectorSplatX(FXMVECTOR V) + { + return _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); + } + + inline XMVECTOR XM_CALLCONV XMVectorSplatY(FXMVECTOR V) + { + return _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); + } + + inline XMVECTOR XM_CALLCONV XMVectorSplatZ(FXMVECTOR V) + { + return _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); + } + + inline XMVECTOR XM_CALLCONV XMVectorSplatW(FXMVECTOR V) + { + return _mm_permute_ps(V, _MM_SHUFFLE(3, 3, 3, 3)); + } + + inline XMVECTOR XM_CALLCONV XMVectorSwizzle(FXMVECTOR V, uint32_t E0, uint32_t E1, uint32_t E2, uint32_t E3) + { + assert((E0 < 4) && (E1 < 4) && (E2 < 4) && (E3 < 4)); + _Analysis_assume_((E0 < 4) && (E1 < 4) && (E2 < 4) && (E3 < 4)); + + unsigned int elem[4] = { E0, E1, E2, E3 }; + __m128i vControl = _mm_loadu_si128(reinterpret_cast(&elem[0])); + return _mm_permutevar_ps(V, vControl); + } + + inline XMVECTOR XM_CALLCONV XMVectorPermute(FXMVECTOR V1, FXMVECTOR V2, uint32_t PermuteX, uint32_t PermuteY, uint32_t PermuteZ, uint32_t PermuteW) + { + assert(PermuteX <= 7 && PermuteY <= 7 && PermuteZ <= 7 && PermuteW <= 7); + _Analysis_assume_(PermuteX <= 7 && PermuteY <= 7 && PermuteZ <= 7 && PermuteW <= 7); + + static const XMVECTORU32 three = { { { 3, 3, 3, 3 } } }; + + XM_ALIGNED_DATA(16) unsigned int elem[4] = { PermuteX, PermuteY, PermuteZ, PermuteW }; + __m128i vControl = _mm_load_si128(reinterpret_cast(&elem[0])); + + __m128i vSelect = _mm_cmpgt_epi32(vControl, three); + vControl = _mm_castps_si128(_mm_and_ps(_mm_castsi128_ps(vControl), three)); + + __m128 shuffled1 = _mm_permutevar_ps(V1, vControl); + __m128 shuffled2 = _mm_permutevar_ps(V2, vControl); + + __m128 masked1 = _mm_andnot_ps(_mm_castsi128_ps(vSelect), shuffled1); + __m128 masked2 = _mm_and_ps(_mm_castsi128_ps(vSelect), shuffled2); + + return _mm_or_ps(masked1, masked2); + } + + inline XMVECTOR XM_CALLCONV XMVectorShiftLeft(FXMVECTOR V1, FXMVECTOR V2, uint32_t Elements) + { + assert(Elements < 4); + _Analysis_assume_(Elements < 4); + return AVX::XMVectorPermute(V1, V2, Elements, ((Elements)+ 1), ((Elements)+ 2), ((Elements)+ 3)); + } + + inline XMVECTOR XM_CALLCONV XMVectorRotateLeft(FXMVECTOR V, uint32_t Elements) + { + assert(Elements < 4); + _Analysis_assume_(Elements < 4); + return AVX::XMVectorSwizzle(V, Elements & 3, (Elements + 1) & 3, (Elements + 2) & 3, (Elements + 3) & 3); + } + + inline XMVECTOR XM_CALLCONV XMVectorRotateRight(FXMVECTOR V, uint32_t Elements) + { + assert(Elements < 4); + _Analysis_assume_(Elements < 4); + return AVX::XMVectorSwizzle(V, (4 - (Elements)) & 3, (5 - (Elements)) & 3, (6 - (Elements)) & 3, (7 - (Elements)) & 3); + } + + + //------------------------------------------------------------------------------------- + // Permute Templates + //------------------------------------------------------------------------------------- + + namespace MathInternal + { + // Slow path fallback for permutes that do not map to a single SSE opcode. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) + { + static const XMVECTORU32 selectMask = + { { { + WhichX ? 0xFFFFFFFF : 0, + WhichY ? 0xFFFFFFFF : 0, + WhichZ ? 0xFFFFFFFF : 0, + WhichW ? 0xFFFFFFFF : 0, + } } }; + + XMVECTOR shuffled1 = _mm_permute_ps(v1, Shuffle); + XMVECTOR shuffled2 = _mm_permute_ps(v2, Shuffle); + + XMVECTOR masked1 = _mm_andnot_ps(selectMask, shuffled1); + XMVECTOR masked2 = _mm_and_ps(selectMask, shuffled2); + + return _mm_or_ps(masked1, masked2); + } + }; + + // Fast path for permutes that only read from the first vector. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) { (v2); return _mm_permute_ps(v1, Shuffle); } + }; + + // Fast path for permutes that only read from the second vector. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) { (v1); return _mm_permute_ps(v2, Shuffle); } + }; + + // Fast path for permutes that read XY from the first vector, ZW from the second. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) { return _mm_shuffle_ps(v1, v2, Shuffle); } + }; + + // Fast path for permutes that read XY from the second vector, ZW from the first. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) { return _mm_shuffle_ps(v2, v1, Shuffle); } + }; + }; + + // General permute template + template + inline XMVECTOR XM_CALLCONV XMVectorPermute(FXMVECTOR V1, FXMVECTOR V2) + { + static_assert(PermuteX <= 7, "PermuteX template parameter out of range"); + static_assert(PermuteY <= 7, "PermuteY template parameter out of range"); + static_assert(PermuteZ <= 7, "PermuteZ template parameter out of range"); + static_assert(PermuteW <= 7, "PermuteW template parameter out of range"); + + const uint32_t Shuffle = _MM_SHUFFLE(PermuteW & 3, PermuteZ & 3, PermuteY & 3, PermuteX & 3); + + const bool WhichX = PermuteX > 3; + const bool WhichY = PermuteY > 3; + const bool WhichZ = PermuteZ > 3; + const bool WhichW = PermuteW > 3; + + return AVX::MathInternal::PermuteHelper::Permute(V1, V2); + } + + // Special-case permute templates + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 2, 3>(FXMVECTOR V1, FXMVECTOR) { return V1; } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 6, 7>(FXMVECTOR, FXMVECTOR V2) { return V2; } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x1); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x2); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x3); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x4); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x5); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x6); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x7); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x8); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x9); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xA); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xB); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xC); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xD); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xE); } + + + //------------------------------------------------------------------------------------- + // Swizzle Templates + //------------------------------------------------------------------------------------- + + // General swizzle template + template + inline XMVECTOR XM_CALLCONV XMVectorSwizzle(FXMVECTOR V) + { + static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); + static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); + static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); + static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); + + return _mm_permute_ps(V, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX)); + } + + // Specialized swizzles + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 1, 2, 3>(FXMVECTOR V) { return V; } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 0, 2, 2>(FXMVECTOR V) { return _mm_moveldup_ps(V); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 1, 3, 3>(FXMVECTOR V) { return _mm_movehdup_ps(V); } + + + //------------------------------------------------------------------------------------- + // Other Templates + //------------------------------------------------------------------------------------- + + template + inline XMVECTOR XM_CALLCONV XMVectorShiftLeft(FXMVECTOR V1, FXMVECTOR V2) + { + static_assert(Elements < 4, "Elements template parameter out of range"); + return AVX::XMVectorPermute(V1, V2); + } + + template + inline XMVECTOR XM_CALLCONV XMVectorRotateLeft(FXMVECTOR V) + { + static_assert(Elements < 4, "Elements template parameter out of range"); + return AVX::XMVectorSwizzle(V); + } + + template + inline XMVECTOR XM_CALLCONV XMVectorRotateRight(FXMVECTOR V) + { + static_assert(Elements < 4, "Elements template parameter out of range"); + return AVX::XMVectorSwizzle<(4 - Elements) & 3, (5 - Elements) & 3, (6 - Elements) & 3, (7 - Elements) & 3>(V); + } + + } // namespace AVX + +} // namespace DirectX; diff --git a/Extern/dxmath/Extensions/DirectXMathAVX2.h b/Extern/dxmath/Extensions/DirectXMathAVX2.h new file mode 100644 index 000000000..efbacc2aa --- /dev/null +++ b/Extern/dxmath/Extensions/DirectXMathAVX2.h @@ -0,0 +1,1037 @@ +//------------------------------------------------------------------------------------- +// DirectXMathAVX2.h -- AVX2 extensions for SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __arm__ || __aarch64__ +#error AVX2 not supported on ARM platform +#endif + +#include +#include + +namespace DirectX +{ + + namespace AVX2 + { + + inline bool XMVerifyAVX2Support() + { + // Should return true for AMD "Excavator", Intel "Haswell" or later processors + // with OS support for AVX (Windows 7 Service Pack 1, Windows Server 2008 R2 Service Pack 1, Windows 8, Windows Server 2012) + + // See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + int CPUInfo[4] = { -1 }; + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0); + #endif + + if (CPUInfo[0] < 7) + return false; + + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 1); + #endif + + // We check for F16C, FMA3, AVX, OSXSAVE, SSSE4.1, and SSE3 + if ((CPUInfo[2] & 0x38081001) != 0x38081001) + return false; + + #if defined(__clang__) || defined(__GNUC__) + __cpuid_count(7, 0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuidex(CPUInfo, 7, 0); + #endif + + return ((CPUInfo[1] & 0x20) == 0x20); + } + + + //------------------------------------------------------------------------------------- + // Vector + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVectorReplicatePtr(_In_ const float *pValue) + { + return _mm_broadcast_ss(pValue); + } + + inline XMVECTOR XM_CALLCONV XMVectorSplatX(FXMVECTOR V) + { + return _mm_broadcastss_ps(V); + } + + inline XMVECTOR XM_CALLCONV XMVectorSplatY(FXMVECTOR V) + { + return _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); + } + + inline XMVECTOR XM_CALLCONV XMVectorSplatZ(FXMVECTOR V) + { + return _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); + } + + inline XMVECTOR XM_CALLCONV XMVectorSplatW(FXMVECTOR V) + { + return _mm_permute_ps(V, _MM_SHUFFLE(3, 3, 3, 3)); + } + + inline XMVECTOR XM_CALLCONV XMVectorMultiplyAdd + ( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR V3 + ) + { + return _mm_fmadd_ps(V1, V2, V3); + } + + inline XMVECTOR XM_CALLCONV XMVectorNegativeMultiplySubtract + ( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR V3 + ) + { + return _mm_fnmadd_ps(V1, V2, V3); + } + + inline XMVECTOR XM_CALLCONV XMVectorSwizzle(FXMVECTOR V, uint32_t E0, uint32_t E1, uint32_t E2, uint32_t E3) + { + assert((E0 < 4) && (E1 < 4) && (E2 < 4) && (E3 < 4)); + _Analysis_assume_((E0 < 4) && (E1 < 4) && (E2 < 4) && (E3 < 4)); + + unsigned int elem[4] = { E0, E1, E2, E3 }; + __m128i vControl = _mm_loadu_si128(reinterpret_cast(&elem[0])); + return _mm_permutevar_ps(V, vControl); + } + + inline XMVECTOR XM_CALLCONV XMVectorPermute(FXMVECTOR V1, FXMVECTOR V2, uint32_t PermuteX, uint32_t PermuteY, uint32_t PermuteZ, uint32_t PermuteW) + { + assert(PermuteX <= 7 && PermuteY <= 7 && PermuteZ <= 7 && PermuteW <= 7); + _Analysis_assume_(PermuteX <= 7 && PermuteY <= 7 && PermuteZ <= 7 && PermuteW <= 7); + + static const XMVECTORU32 three = { { { 3, 3, 3, 3 } } }; + + XM_ALIGNED_DATA(16) unsigned int elem[4] = { PermuteX, PermuteY, PermuteZ, PermuteW }; + __m128i vControl = _mm_load_si128(reinterpret_cast(&elem[0])); + + __m128i vSelect = _mm_cmpgt_epi32(vControl, three); + vControl = _mm_castps_si128(_mm_and_ps(_mm_castsi128_ps(vControl), three)); + + __m128 shuffled1 = _mm_permutevar_ps(V1, vControl); + __m128 shuffled2 = _mm_permutevar_ps(V2, vControl); + + __m128 masked1 = _mm_andnot_ps(_mm_castsi128_ps(vSelect), shuffled1); + __m128 masked2 = _mm_and_ps(_mm_castsi128_ps(vSelect), shuffled2); + + return _mm_or_ps(masked1, masked2); + } + + inline XMVECTOR XM_CALLCONV XMVectorShiftLeft(FXMVECTOR V1, FXMVECTOR V2, uint32_t Elements) + { + assert(Elements < 4); + _Analysis_assume_(Elements < 4); + return AVX2::XMVectorPermute(V1, V2, Elements, ((Elements)+ 1), ((Elements)+ 2), ((Elements)+ 3)); + } + + inline XMVECTOR XM_CALLCONV XMVectorRotateLeft(FXMVECTOR V, uint32_t Elements) + { + assert(Elements < 4); + _Analysis_assume_(Elements < 4); + return AVX2::XMVectorSwizzle(V, Elements & 3, (Elements + 1) & 3, (Elements + 2) & 3, (Elements + 3) & 3); + } + + inline XMVECTOR XM_CALLCONV XMVectorRotateRight(FXMVECTOR V, uint32_t Elements) + { + assert(Elements < 4); + _Analysis_assume_(Elements < 4); + return AVX2::XMVectorSwizzle(V, (4 - (Elements)) & 3, (5 - (Elements)) & 3, (6 - (Elements)) & 3, (7 - (Elements)) & 3); + } + + + //------------------------------------------------------------------------------------- + // Vector2 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector2Transform + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vResult, M.r[1], M.r[3]); + XMVECTOR vTemp = _mm_broadcastss_ps(V); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector2TransformCoord + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vResult, M.r[1], M.r[3]); + XMVECTOR vTemp = _mm_broadcastss_ps(V); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + XMVECTOR W = _mm_permute_ps(vResult, _MM_SHUFFLE(3, 3, 3, 3)); + vResult = _mm_div_ps(vResult, W); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector2TransformNormal + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_mul_ps(vResult, M.r[1]); + XMVECTOR vTemp = _mm_broadcastss_ps(V); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + + //------------------------------------------------------------------------------------- + // Vector3 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector3Transform + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_fmadd_ps(vResult, M.r[2], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vTemp, M.r[1], vResult); + vTemp = _mm_broadcastss_ps(V); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector3TransformCoord + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_fmadd_ps(vResult, M.r[2], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vTemp, M.r[1], vResult); + vTemp = _mm_broadcastss_ps(V); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + XMVECTOR W = _mm_permute_ps(vResult, _MM_SHUFFLE(3, 3, 3, 3)); + vResult = _mm_div_ps(vResult, W); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector3TransformNormal + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_mul_ps(vResult, M.r[2]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vTemp, M.r[1], vResult); + vTemp = _mm_broadcastss_ps(V); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + XMMATRIX XM_CALLCONV XMMatrixMultiply(CXMMATRIX M1, CXMMATRIX M2); + + inline XMVECTOR XM_CALLCONV XMVector3Project + ( + FXMVECTOR V, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + CXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World + ) + { + const float HalfViewportWidth = ViewportWidth * 0.5f; + const float HalfViewportHeight = ViewportHeight * 0.5f; + + XMVECTOR Scale = XMVectorSet(HalfViewportWidth, -HalfViewportHeight, ViewportMaxZ - ViewportMinZ, 0.0f); + XMVECTOR Offset = XMVectorSet(ViewportX + HalfViewportWidth, ViewportY + HalfViewportHeight, ViewportMinZ, 0.0f); + + XMMATRIX Transform = AVX2::XMMatrixMultiply(World, View); + Transform = AVX2::XMMatrixMultiply(Transform, Projection); + + XMVECTOR Result = AVX2::XMVector3TransformCoord(V, Transform); + + Result = AVX2::XMVectorMultiplyAdd(Result, Scale, Offset); + + return Result; + } + + inline XMVECTOR XM_CALLCONV XMVector3Unproject + ( + FXMVECTOR V, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + CXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World + ) + { + static const XMVECTORF32 D = { { { -1.0f, 1.0f, 0.0f, 0.0f } } }; + + XMVECTOR Scale = XMVectorSet(ViewportWidth * 0.5f, -ViewportHeight * 0.5f, ViewportMaxZ - ViewportMinZ, 1.0f); + Scale = XMVectorReciprocal(Scale); + + XMVECTOR Offset = XMVectorSet(-ViewportX, -ViewportY, -ViewportMinZ, 0.0f); + Offset = AVX2::XMVectorMultiplyAdd(Scale, Offset, D.v); + + XMMATRIX Transform = AVX2::XMMatrixMultiply(World, View); + Transform = AVX2::XMMatrixMultiply(Transform, Projection); + Transform = XMMatrixInverse(nullptr, Transform); + + XMVECTOR Result = AVX2::XMVectorMultiplyAdd(V, Scale, Offset); + + return AVX2::XMVector3TransformCoord(Result, Transform); + } + + + //------------------------------------------------------------------------------------- + // Vector4 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector4Transform + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(3, 3, 3, 3)); // W + vResult = _mm_mul_ps(vResult, M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_fmadd_ps(vTemp, M.r[2], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vTemp, M.r[1], vResult); + vTemp = _mm_broadcastss_ps(V); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + + //------------------------------------------------------------------------------------- + // Matrix + //------------------------------------------------------------------------------------- + + inline XMMATRIX XM_CALLCONV XMMatrixMultiply + ( + CXMMATRIX M1, + CXMMATRIX M2 + ) + { + XMMATRIX mResult; + // Use vW to hold the original row + XMVECTOR vW = M1.r[0]; + // Splat the component X,Y,Z then W + XMVECTOR vX = _mm_broadcastss_ps(vW); + XMVECTOR vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + // Perform the operation on the first row + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + mResult.r[0] = vX; + // Repeat for the other 3 rows + vW = M1.r[1]; + vX = _mm_broadcastss_ps(vW); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + mResult.r[1] = vX; + vW = M1.r[2]; + vX = _mm_broadcastss_ps(vW); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + mResult.r[2] = vX; + vW = M1.r[3]; + vX = _mm_broadcastss_ps(vW); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + mResult.r[3] = vX; + return mResult; + } + + inline XMMATRIX XM_CALLCONV XMMatrixMultiplyTranspose + ( + FXMMATRIX M1, + CXMMATRIX M2 + ) + { + // Use vW to hold the original row + XMVECTOR vW = M1.r[0]; + // Splat the component X,Y,Z then W + XMVECTOR vX = _mm_broadcastss_ps(vW); + XMVECTOR vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + // Perform the operation on the first row + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + __m128 r0 = vX; + // Repeat for the other 3 rows + vW = M1.r[1]; + vX = _mm_broadcastss_ps(vW); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + __m128 r1 = vX; + vW = M1.r[2]; + vX = _mm_broadcastss_ps(vW); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + __m128 r2 = vX; + vW = M1.r[3]; + vX = _mm_broadcastss_ps(vW); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + __m128 r3 = vX; + + // x.x,x.y,y.x,y.y + XMVECTOR vTemp1 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(1, 0, 1, 0)); + // x.z,x.w,y.z,y.w + XMVECTOR vTemp3 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(3, 2, 3, 2)); + // z.x,z.y,w.x,w.y + XMVECTOR vTemp2 = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(1, 0, 1, 0)); + // z.z,z.w,w.z,w.w + XMVECTOR vTemp4 = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(3, 2, 3, 2)); + + XMMATRIX mResult; + // x.x,y.x,z.x,w.x + mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + // x.y,y.y,z.y,w.y + mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + // x.z,y.z,z.z,w.z + mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + // x.w,y.w,z.w,w.w + mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1)); + return mResult; + } + + + //------------------------------------------------------------------------------------- + // Permute Templates + //------------------------------------------------------------------------------------- + + namespace MathInternal + { + // Slow path fallback for permutes that do not map to a single SSE opcode. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) + { + static const XMVECTORU32 selectMask = + { { { + WhichX ? 0xFFFFFFFF : 0, + WhichY ? 0xFFFFFFFF : 0, + WhichZ ? 0xFFFFFFFF : 0, + WhichW ? 0xFFFFFFFF : 0, + } } }; + + XMVECTOR shuffled1 = _mm_permute_ps(v1, Shuffle); + XMVECTOR shuffled2 = _mm_permute_ps(v2, Shuffle); + + XMVECTOR masked1 = _mm_andnot_ps(selectMask, shuffled1); + XMVECTOR masked2 = _mm_and_ps(selectMask, shuffled2); + + return _mm_or_ps(masked1, masked2); + } + }; + + // Fast path for permutes that only read from the first vector. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) { (v2); return _mm_permute_ps(v1, Shuffle); } + }; + + // Fast path for permutes that only read from the second vector. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) { (v1); return _mm_permute_ps(v2, Shuffle); } + }; + + // Fast path for permutes that read XY from the first vector, ZW from the second. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) { return _mm_shuffle_ps(v1, v2, Shuffle); } + }; + + // Fast path for permutes that read XY from the second vector, ZW from the first. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) { return _mm_shuffle_ps(v2, v1, Shuffle); } + }; + }; + + // General permute template + template + inline XMVECTOR XM_CALLCONV XMVectorPermute(FXMVECTOR V1, FXMVECTOR V2) + { + static_assert(PermuteX <= 7, "PermuteX template parameter out of range"); + static_assert(PermuteY <= 7, "PermuteY template parameter out of range"); + static_assert(PermuteZ <= 7, "PermuteZ template parameter out of range"); + static_assert(PermuteW <= 7, "PermuteW template parameter out of range"); + + const uint32_t Shuffle = _MM_SHUFFLE(PermuteW & 3, PermuteZ & 3, PermuteY & 3, PermuteX & 3); + + const bool WhichX = PermuteX > 3; + const bool WhichY = PermuteY > 3; + const bool WhichZ = PermuteZ > 3; + const bool WhichW = PermuteW > 3; + + return AVX2::MathInternal::PermuteHelper::Permute(V1, V2); + } + + // Special-case permute templates + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 2, 3>(FXMVECTOR V1, FXMVECTOR) { return V1; } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 6, 7>(FXMVECTOR, FXMVECTOR V2) { return V2; } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x1); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x2); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x3); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x4); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x5); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x6); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x7); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x8); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0x9); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xA); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xB); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xC); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xD); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) { return _mm_blend_ps(V1, V2, 0xE); } + + + //------------------------------------------------------------------------------------- + // Swizzle Templates + //------------------------------------------------------------------------------------- + + // General swizzle template + template + inline XMVECTOR XM_CALLCONV XMVectorSwizzle(FXMVECTOR V) + { + static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); + static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); + static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); + static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); + + return _mm_permute_ps(V, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX)); + } + + // Specialized swizzles + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 1, 2, 3>(FXMVECTOR V) { return V; } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 0, 0, 0>(FXMVECTOR V) { return _mm_broadcastss_ps(V); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 0, 2, 2>(FXMVECTOR V) { return _mm_moveldup_ps(V); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 1, 3, 3>(FXMVECTOR V) { return _mm_movehdup_ps(V); } + + + //------------------------------------------------------------------------------------- + // Other Templates + //------------------------------------------------------------------------------------- + + template + inline XMVECTOR XM_CALLCONV XMVectorShiftLeft(FXMVECTOR V1, FXMVECTOR V2) + { + static_assert(Elements < 4, "Elements template parameter out of range"); + return AVX2::XMVectorPermute(V1, V2); + } + + template + inline XMVECTOR XM_CALLCONV XMVectorRotateLeft(FXMVECTOR V) + { + static_assert(Elements < 4, "Elements template parameter out of range"); + return AVX2::XMVectorSwizzle(V); + } + + template + inline XMVECTOR XM_CALLCONV XMVectorRotateRight(FXMVECTOR V) + { + static_assert(Elements < 4, "Elements template parameter out of range"); + return AVX2::XMVectorSwizzle<(4 - Elements) & 3, (5 - Elements) & 3, (6 - Elements) & 3, (7 - Elements) & 3>(V); + } + + //------------------------------------------------------------------------------------- + // Data conversion + //------------------------------------------------------------------------------------- + + inline float XMConvertHalfToFloat(PackedVector::HALF Value) + { + __m128i V1 = _mm_cvtsi32_si128(static_cast(Value)); + __m128 V2 = _mm_cvtph_ps(V1); + return _mm_cvtss_f32(V2); + } + + inline PackedVector::HALF XMConvertFloatToHalf(float Value) + { + __m128 V1 = _mm_set_ss(Value); + __m128i V2 = _mm_cvtps_ph(V1, 0); + return static_cast(_mm_cvtsi128_si32(V2)); + } + + inline float* XMConvertHalfToFloatStream + ( + _Out_writes_bytes_(sizeof(float)+OutputStride*(HalfCount-1)) float* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(2+InputStride*(HalfCount-1)) const PackedVector::HALF* pInputStream, + _In_ size_t InputStride, + _In_ size_t HalfCount + ) + { + using namespace PackedVector; + + assert(pOutputStream); + assert(pInputStream); + + assert(InputStride >= sizeof(HALF)); + assert(OutputStride >= sizeof(float)); + + auto pHalf = reinterpret_cast(pInputStream); + auto pFloat = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = HalfCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(HALF)) + { + if (OutputStride == sizeof(float)) + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128i HV = _mm_loadl_epi64(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + __m128 FV = _mm_cvtph_ps(HV); + + _mm_stream_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128i HV = _mm_loadl_epi64(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + __m128 FV = _mm_cvtph_ps(HV); + + _mm_storeu_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + } + else + { + // Packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128i HV = _mm_loadl_epi64(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + __m128 FV = _mm_cvtph_ps(HV); + + _mm_store_ss(reinterpret_cast(pFloat), FV); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 1); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 2); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 3); + pFloat += OutputStride; + i += 4; + } + } + } + else if (OutputStride == sizeof(float)) + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Scattered input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + __m128i HV = _mm_setzero_si128(); + HV = _mm_insert_epi16(HV, H1, 0); + HV = _mm_insert_epi16(HV, H2, 1); + HV = _mm_insert_epi16(HV, H3, 2); + HV = _mm_insert_epi16(HV, H4, 3); + __m128 FV = _mm_cvtph_ps(HV); + + _mm_stream_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + else + { + // Scattered input, packed output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + __m128i HV = _mm_setzero_si128(); + HV = _mm_insert_epi16(HV, H1, 0); + HV = _mm_insert_epi16(HV, H2, 1); + HV = _mm_insert_epi16(HV, H3, 2); + HV = _mm_insert_epi16(HV, H4, 3); + __m128 FV = _mm_cvtph_ps(HV); + + _mm_storeu_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + + } + } + else + { + // Scattered input, scattered output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + __m128i HV = _mm_setzero_si128(); + HV = _mm_insert_epi16(HV, H1, 0); + HV = _mm_insert_epi16(HV, H2, 1); + HV = _mm_insert_epi16(HV, H3, 2); + HV = _mm_insert_epi16(HV, H4, 3); + __m128 FV = _mm_cvtph_ps(HV); + + _mm_store_ss(reinterpret_cast(pFloat), FV); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 1); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 2); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 3); + pFloat += OutputStride; + i += 4; + } + } + } + + for (; i < HalfCount; ++i) + { + *reinterpret_cast(pFloat) = XMConvertHalfToFloat(reinterpret_cast(pHalf)[0]); + pHalf += InputStride; + pFloat += OutputStride; + } + + return pOutputStream; + } + + + inline PackedVector::HALF* XMConvertFloatToHalfStream + ( + _Out_writes_bytes_(2+OutputStride*(FloatCount-1)) PackedVector::HALF* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(float)+InputStride*(FloatCount-1)) const float* pInputStream, + _In_ size_t InputStride, + _In_ size_t FloatCount + ) + { + using namespace PackedVector; + + assert(pOutputStream); + assert(pInputStream); + + assert(InputStride >= sizeof(float)); + assert(OutputStride >= sizeof(HALF)); + + auto pFloat = reinterpret_cast(pInputStream); + auto pHalf = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = FloatCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(float)) + { + if (OutputStride == sizeof(HALF)) + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Aligned and packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_load_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, 0); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(pHalf), HV); + pHalf += OutputStride * 4; + i += 4; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_loadu_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, 0); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(pHalf), HV); + pHalf += OutputStride * 4; + i += 4; + } + } + } + else + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Aligned & packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_load_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, 0); + + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 0)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 1)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 2)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 3)); + pHalf += OutputStride; + i += 4; + } + } + else + { + // Packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_loadu_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, 0); + + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 0)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 1)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 2)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 3)); + pHalf += OutputStride; + i += 4; + } + } + } + } + else if (OutputStride == sizeof(HALF)) + { + // Scattered input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128 FV1 = _mm_load_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV2 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV3 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV4 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV = _mm_blend_ps(FV1, FV2, 0x2); + __m128 FT = _mm_blend_ps(FV3, FV4, 0x8); + FV = _mm_blend_ps(FV, FT, 0xC); + + __m128i HV = _mm_cvtps_ph(FV, 0); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(pHalf), HV); + pHalf += OutputStride * 4; + i += 4; + } + } + else + { + // Scattered input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128 FV1 = _mm_load_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV2 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV3 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV4 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV = _mm_blend_ps(FV1, FV2, 0x2); + __m128 FT = _mm_blend_ps(FV3, FV4, 0x8); + FV = _mm_blend_ps(FV, FT, 0xC); + + __m128i HV = _mm_cvtps_ph(FV, 0); + + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 0)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 1)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 2)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 3)); + pHalf += OutputStride; + i += 4; + } + } + } + + for (; i < FloatCount; ++i) + { + *reinterpret_cast(pHalf) = XMConvertFloatToHalf(reinterpret_cast(pFloat)[0]); + pFloat += InputStride; + pHalf += OutputStride; + } + + return pOutputStream; + } + + + //------------------------------------------------------------------------------------- + // Half2 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMLoadHalf2(_In_ const PackedVector::XMHALF2* pSource) + { + assert(pSource); + __m128 V = _mm_load_ss(reinterpret_cast(pSource)); + return _mm_cvtph_ps(_mm_castps_si128(V)); + } + + inline void XM_CALLCONV XMStoreHalf2(_Out_ PackedVector::XMHALF2* pDestination, _In_ FXMVECTOR V) + { + assert(pDestination); + __m128i V1 = _mm_cvtps_ph(V, 0); + _mm_store_ss(reinterpret_cast(pDestination), _mm_castsi128_ps(V1)); + } + + + //------------------------------------------------------------------------------------- + // Half4 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMLoadHalf4(_In_ const PackedVector::XMHALF4* pSource) + { + assert(pSource); + __m128i V = _mm_loadl_epi64(reinterpret_cast(pSource)); + return _mm_cvtph_ps(V); + } + + inline void XM_CALLCONV XMStoreHalf4(_Out_ PackedVector::XMHALF4* pDestination, _In_ FXMVECTOR V) + { + assert(pDestination); + __m128i V1 = _mm_cvtps_ph(V, 0); + _mm_storel_epi64(reinterpret_cast<__m128i*>(pDestination), V1); + } + + } // namespace AVX2 + +} // namespace DirectX; diff --git a/Extern/dxmath/Extensions/DirectXMathBE.h b/Extern/dxmath/Extensions/DirectXMathBE.h new file mode 100644 index 000000000..ebc59fbac --- /dev/null +++ b/Extern/dxmath/Extensions/DirectXMathBE.h @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------------- +// DirectXMathBE.h -- Big-endian swap extensions for SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#if (defined(_M_IX86) || defined(_M_X64) || __i386__ || __x86_64__) && !defined(_M_HYBRID_X86_ARM64) && !defined(_M_ARM64EC) +#include +#endif + +#include + +namespace DirectX +{ + + inline XMVECTOR XM_CALLCONV XMVectorEndian + ( + FXMVECTOR V + ) + { + #if defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + static const XMVECTORU32 idx = { { { 0x00010203u, 0x04050607u, 0x08090A0Bu, 0x0C0D0E0Fu } } }; + + uint8x8x2_t tbl; + tbl.val[0] = vreinterpret_u8_f32(vget_low_f32(V)); + tbl.val[1] = vreinterpret_u8_f32(vget_high_f32(V)); + + const uint8x8_t rL = vtbl2_u8(tbl, vget_low_u32(idx)); + const uint8x8_t rH = vtbl2_u8(tbl, vget_high_u32(idx)); + return vcombine_f32(vreinterpret_f32_u8(rL), vreinterpret_f32_u8(rH)); + #else + XMVECTORU32 E; + E.v = V; + uint32_t value = E.u[0]; + E.u[0] = ((value << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | (value >> 24)); + value = E.u[1]; + E.u[1] = ((value << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | (value >> 24)); + value = E.u[2]; + E.u[2] = ((value << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | (value >> 24)); + value = E.u[3]; + E.u[3] = ((value << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | (value >> 24)); + return E.v; + #endif + } + + +#if (defined(_M_IX86) || defined(_M_X64) || __i386__ || __x86_64__) && !defined(_M_HYBRID_X86_ARM64) + namespace SSSE3 + { + + inline bool XMVerifySSSE3Support() + { + // Should return true on AMD Bulldozer, Intel Core i7/i5/i3, Intel Atom, or later processors + + // See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + int CPUInfo[4] = { -1 }; + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0); + #endif + + if (CPUInfo[0] < 1) + return false; + + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 1); + #endif + + // Check for SSSE3 instruction set. + return ((CPUInfo[2] & 0x200) != 0); + } + + inline XMVECTOR XM_CALLCONV XMVectorEndian + ( + FXMVECTOR V + ) + { + static const XMVECTORU32 idx = { { { 0x00010203u, 0x04050607u, 0x08090A0Bu, 0x0C0D0E0Fu } } }; + + __m128i Result = _mm_shuffle_epi8(_mm_castps_si128(V), idx); + return _mm_castsi128_ps(Result); + } + + } // namespace SSSE3 +#endif // X86 || X64 + +} // namespace DirectX diff --git a/Extern/dxmath/Extensions/DirectXMathF16C.h b/Extern/dxmath/Extensions/DirectXMathF16C.h new file mode 100644 index 000000000..1141929fd --- /dev/null +++ b/Extern/dxmath/Extensions/DirectXMathF16C.h @@ -0,0 +1,471 @@ +//------------------------------------------------------------------------------------- +// DirectXMathF16C.h -- F16C/CVT16 extensions for SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __arm__ || __aarch64__ +#error F16C not supported on ARM platform +#endif + +#include +#include + +namespace DirectX +{ + + namespace F16C + { + + inline bool XMVerifyF16CSupport() + { + // Should return true for AMD "Piledriver" and Intel "Ivy Bridge" processors + // with OS support for AVX (Windows 7 Service Pack 1, Windows Server 2008 R2 Service Pack 1, Windows 8, Windows Server 2012) + + // See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + int CPUInfo[4] = { -1 }; + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0); + #endif + + if (CPUInfo[0] < 1) + return false; + + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 1); + #endif + + // We check for F16C, AVX, OSXSAVE, and SSE4.1 + return ((CPUInfo[2] & 0x38080000) == 0x38080000); + } + + + //------------------------------------------------------------------------------------- + // Data conversion + //------------------------------------------------------------------------------------- + + inline float XMConvertHalfToFloat(PackedVector::HALF Value) + { + __m128i V1 = _mm_cvtsi32_si128(static_cast(Value)); + __m128 V2 = _mm_cvtph_ps(V1); + return _mm_cvtss_f32(V2); + } + + inline PackedVector::HALF XMConvertFloatToHalf(float Value) + { + __m128 V1 = _mm_set_ss(Value); + __m128i V2 = _mm_cvtps_ph(V1, 0); + return static_cast(_mm_cvtsi128_si32(V2)); + } + + inline float* XMConvertHalfToFloatStream + ( + _Out_writes_bytes_(sizeof(float) + OutputStride * (HalfCount - 1)) float* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(2 + InputStride * (HalfCount - 1)) const PackedVector::HALF* pInputStream, + _In_ size_t InputStride, + _In_ size_t HalfCount + ) + { + using namespace PackedVector; + + assert(pOutputStream); + assert(pInputStream); + + assert(InputStride >= sizeof(HALF)); + assert(OutputStride >= sizeof(float)); + + auto pHalf = reinterpret_cast(pInputStream); + auto pFloat = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = HalfCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(HALF)) + { + if (OutputStride == sizeof(float)) + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128i HV = _mm_loadl_epi64(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + __m128 FV = _mm_cvtph_ps(HV); + + _mm_stream_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128i HV = _mm_loadl_epi64(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + __m128 FV = _mm_cvtph_ps(HV); + + _mm_storeu_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + } + else + { + // Packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128i HV = _mm_loadl_epi64(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + __m128 FV = _mm_cvtph_ps(HV); + + _mm_store_ss(reinterpret_cast(pFloat), FV); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 1); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 2); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 3); + pFloat += OutputStride; + i += 4; + } + } + } + else if (OutputStride == sizeof(float)) + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Scattered input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + __m128i HV = _mm_setzero_si128(); + HV = _mm_insert_epi16(HV, H1, 0); + HV = _mm_insert_epi16(HV, H2, 1); + HV = _mm_insert_epi16(HV, H3, 2); + HV = _mm_insert_epi16(HV, H4, 3); + __m128 FV = _mm_cvtph_ps(HV); + + _mm_stream_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + else + { + // Scattered input, packed output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + __m128i HV = _mm_setzero_si128(); + HV = _mm_insert_epi16(HV, H1, 0); + HV = _mm_insert_epi16(HV, H2, 1); + HV = _mm_insert_epi16(HV, H3, 2); + HV = _mm_insert_epi16(HV, H4, 3); + __m128 FV = _mm_cvtph_ps(HV); + + _mm_storeu_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + + } + } + else + { + // Scattered input, scattered output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + __m128i HV = _mm_setzero_si128(); + HV = _mm_insert_epi16(HV, H1, 0); + HV = _mm_insert_epi16(HV, H2, 1); + HV = _mm_insert_epi16(HV, H3, 2); + HV = _mm_insert_epi16(HV, H4, 3); + __m128 FV = _mm_cvtph_ps(HV); + + _mm_store_ss(reinterpret_cast(pFloat), FV); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 1); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 2); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 3); + pFloat += OutputStride; + i += 4; + } + } + } + + for (; i < HalfCount; ++i) + { + *reinterpret_cast(pFloat) = XMConvertHalfToFloat(reinterpret_cast(pHalf)[0]); + pHalf += InputStride; + pFloat += OutputStride; + } + + return pOutputStream; + } + + + inline PackedVector::HALF* XMConvertFloatToHalfStream + ( + _Out_writes_bytes_(2 + OutputStride * (FloatCount - 1)) PackedVector::HALF* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(float) + InputStride * (FloatCount - 1)) const float* pInputStream, + _In_ size_t InputStride, + _In_ size_t FloatCount + ) + { + using namespace PackedVector; + + assert(pOutputStream); + assert(pInputStream); + + assert(InputStride >= sizeof(float)); + assert(OutputStride >= sizeof(HALF)); + + auto pFloat = reinterpret_cast(pInputStream); + auto pHalf = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = FloatCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(float)) + { + if (OutputStride == sizeof(HALF)) + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Aligned and packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_load_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, 0); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(pHalf), HV); + pHalf += OutputStride * 4; + i += 4; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_loadu_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, 0); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(pHalf), HV); + pHalf += OutputStride * 4; + i += 4; + } + } + } + else + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Aligned & packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_load_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, 0); + + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 0)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 1)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 2)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 3)); + pHalf += OutputStride; + i += 4; + } + } + else + { + // Packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_loadu_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, 0); + + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 0)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 1)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 2)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 3)); + pHalf += OutputStride; + i += 4; + } + } + } + } + else if (OutputStride == sizeof(HALF)) + { + // Scattered input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128 FV1 = _mm_load_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV2 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV3 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV4 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV = _mm_blend_ps(FV1, FV2, 0x2); + __m128 FT = _mm_blend_ps(FV3, FV4, 0x8); + FV = _mm_blend_ps(FV, FT, 0xC); + + __m128i HV = _mm_cvtps_ph(FV, 0); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(pHalf), HV); + pHalf += OutputStride * 4; + i += 4; + } + } + else + { + // Scattered input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128 FV1 = _mm_load_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV2 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV3 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV4 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV = _mm_blend_ps(FV1, FV2, 0x2); + __m128 FT = _mm_blend_ps(FV3, FV4, 0x8); + FV = _mm_blend_ps(FV, FT, 0xC); + + __m128i HV = _mm_cvtps_ph(FV, 0); + + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 0)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 1)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 2)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 3)); + pHalf += OutputStride; + i += 4; + } + } + } + + for (; i < FloatCount; ++i) + { + *reinterpret_cast(pHalf) = XMConvertFloatToHalf(reinterpret_cast(pFloat)[0]); + pFloat += InputStride; + pHalf += OutputStride; + } + + return pOutputStream; + } + + + //------------------------------------------------------------------------------------- + // Half2 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMLoadHalf2(_In_ const PackedVector::XMHALF2* pSource) + { + assert(pSource); + __m128 V = _mm_load_ss(reinterpret_cast(pSource)); + return _mm_cvtph_ps(_mm_castps_si128(V)); + } + + inline void XM_CALLCONV XMStoreHalf2(_Out_ PackedVector::XMHALF2* pDestination, _In_ FXMVECTOR V) + { + assert(pDestination); + __m128i V1 = _mm_cvtps_ph(V, 0); + _mm_store_ss(reinterpret_cast(pDestination), _mm_castsi128_ps(V1)); + } + + + //------------------------------------------------------------------------------------- + // Half4 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMLoadHalf4(_In_ const PackedVector::XMHALF4* pSource) + { + assert(pSource); + __m128i V = _mm_loadl_epi64(reinterpret_cast(pSource)); + return _mm_cvtph_ps(V); + } + + inline void XM_CALLCONV XMStoreHalf4(_Out_ PackedVector::XMHALF4* pDestination, _In_ FXMVECTOR V) + { + assert(pDestination); + __m128i V1 = _mm_cvtps_ph(V, 0); + _mm_storel_epi64(reinterpret_cast<__m128i*>(pDestination), V1); + } + + } // namespace F16C + +} // namespace DirectX diff --git a/Extern/dxmath/Extensions/DirectXMathFMA3.h b/Extern/dxmath/Extensions/DirectXMathFMA3.h new file mode 100644 index 000000000..9359e2a59 --- /dev/null +++ b/Extern/dxmath/Extensions/DirectXMathFMA3.h @@ -0,0 +1,391 @@ +//------------------------------------------------------------------------------------- +// DirectXMathFMA3.h -- FMA3 extensions for SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __arm__ || __aarch64__ +#error FMA3 not supported on ARM platform +#endif + +#include + +namespace DirectX +{ + + namespace FMA3 + { + + inline bool XMVerifyFMA3Support() + { + // Should return true for AMD "Pildriver" and Intel "Haswell" processors + // with OS support for AVX (Windows 7 Service Pack 1, Windows Server 2008 R2 Service Pack 1, Windows 8, Windows Server 2012) + + // See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + int CPUInfo[4] = { -1 }; + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0); + #endif + + if (CPUInfo[0] < 1) + return false; + + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 1); + #endif + + // We check for FMA3, AVX, OSXSAVE + return ((CPUInfo[2] & 0x18001000) == 0x18001000); + } + + + //------------------------------------------------------------------------------------- + // Vector + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVectorMultiplyAdd + ( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR V3 + ) + { + return _mm_fmadd_ps(V1, V2, V3); + } + + inline XMVECTOR XM_CALLCONV XMVectorNegativeMultiplySubtract + ( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR V3 + ) + { + return _mm_fnmadd_ps(V1, V2, V3); + } + + + //------------------------------------------------------------------------------------- + // Vector2 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector2Transform + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vResult, M.r[1], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector2TransformCoord + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vResult, M.r[1], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + XMVECTOR W = _mm_permute_ps(vResult, _MM_SHUFFLE(3, 3, 3, 3)); + vResult = _mm_div_ps(vResult, W); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector2TransformNormal + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_mul_ps(vResult, M.r[1]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + + //------------------------------------------------------------------------------------- + // Vector3 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector3Transform + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_fmadd_ps(vResult, M.r[2], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vTemp, M.r[1], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector3TransformCoord + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_fmadd_ps(vResult, M.r[2], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vTemp, M.r[1], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + XMVECTOR W = _mm_permute_ps(vResult, _MM_SHUFFLE(3, 3, 3, 3)); + vResult = _mm_div_ps(vResult, W); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector3TransformNormal + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_mul_ps(vResult, M.r[2]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vTemp, M.r[1], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + XMMATRIX XM_CALLCONV XMMatrixMultiply(CXMMATRIX M1, CXMMATRIX M2); + + inline XMVECTOR XM_CALLCONV XMVector3Project + ( + FXMVECTOR V, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + CXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World + ) + { + const float HalfViewportWidth = ViewportWidth * 0.5f; + const float HalfViewportHeight = ViewportHeight * 0.5f; + + XMVECTOR Scale = XMVectorSet(HalfViewportWidth, -HalfViewportHeight, ViewportMaxZ - ViewportMinZ, 0.0f); + XMVECTOR Offset = XMVectorSet(ViewportX + HalfViewportWidth, ViewportY + HalfViewportHeight, ViewportMinZ, 0.0f); + + XMMATRIX Transform = FMA3::XMMatrixMultiply(World, View); + Transform = FMA3::XMMatrixMultiply(Transform, Projection); + + XMVECTOR Result = FMA3::XMVector3TransformCoord(V, Transform); + + Result = FMA3::XMVectorMultiplyAdd(Result, Scale, Offset); + + return Result; + } + + inline XMVECTOR XM_CALLCONV XMVector3Unproject + ( + FXMVECTOR V, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + CXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World + ) + { + static const XMVECTORF32 D = { { { -1.0f, 1.0f, 0.0f, 0.0f } } }; + + XMVECTOR Scale = XMVectorSet(ViewportWidth * 0.5f, -ViewportHeight * 0.5f, ViewportMaxZ - ViewportMinZ, 1.0f); + Scale = XMVectorReciprocal(Scale); + + XMVECTOR Offset = XMVectorSet(-ViewportX, -ViewportY, -ViewportMinZ, 0.0f); + Offset = FMA3::XMVectorMultiplyAdd(Scale, Offset, D.v); + + XMMATRIX Transform = FMA3::XMMatrixMultiply(World, View); + Transform = FMA3::XMMatrixMultiply(Transform, Projection); + Transform = XMMatrixInverse(nullptr, Transform); + + XMVECTOR Result = FMA3::XMVectorMultiplyAdd(V, Scale, Offset); + + return FMA3::XMVector3TransformCoord(Result, Transform); + } + + + //------------------------------------------------------------------------------------- + // Vector4 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector4Transform + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(3, 3, 3, 3)); // W + vResult = _mm_mul_ps(vResult, M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_fmadd_ps(vTemp, M.r[2], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_fmadd_ps(vTemp, M.r[1], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_fmadd_ps(vTemp, M.r[0], vResult); + return vResult; + } + + + //------------------------------------------------------------------------------------- + // Matrix + //------------------------------------------------------------------------------------- + + inline XMMATRIX XM_CALLCONV XMMatrixMultiply + ( + CXMMATRIX M1, + CXMMATRIX M2 + ) + { + XMMATRIX mResult; + // Use vW to hold the original row + XMVECTOR vW = M1.r[0]; + // Splat the component X,Y,Z then W + XMVECTOR vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + // Perform the operation on the first row + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + mResult.r[0] = vX; + // Repeat for the other 3 rows + vW = M1.r[1]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + mResult.r[1] = vX; + vW = M1.r[2]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + mResult.r[2] = vX; + vW = M1.r[3]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + mResult.r[3] = vX; + return mResult; + } + + inline XMMATRIX XM_CALLCONV XMMatrixMultiplyTranspose + ( + FXMMATRIX M1, + CXMMATRIX M2 + ) + { + // Use vW to hold the original row + XMVECTOR vW = M1.r[0]; + // Splat the component X,Y,Z then W + XMVECTOR vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + // Perform the operation on the first row + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + __m128 r0 = vX; + // Repeat for the other 3 rows + vW = M1.r[1]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + __m128 r1 = vX; + vW = M1.r[2]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + __m128 r2 = vX; + vW = M1.r[3]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_fmadd_ps(vY, M2.r[1], vX); + vX = _mm_fmadd_ps(vZ, M2.r[2], vX); + vX = _mm_fmadd_ps(vW, M2.r[3], vX); + __m128 r3 = vX; + + // x.x,x.y,y.x,y.y + XMVECTOR vTemp1 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(1, 0, 1, 0)); + // x.z,x.w,y.z,y.w + XMVECTOR vTemp3 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(3, 2, 3, 2)); + // z.x,z.y,w.x,w.y + XMVECTOR vTemp2 = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(1, 0, 1, 0)); + // z.z,z.w,w.z,w.w + XMVECTOR vTemp4 = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(3, 2, 3, 2)); + + XMMATRIX mResult; + // x.x,y.x,z.x,w.x + mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + // x.y,y.y,z.y,w.y + mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + // x.z,y.z,z.z,w.z + mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + // x.w,y.w,z.w,w.w + mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1)); + return mResult; + } + + } // namespace FMA3 + +} // namespace DirectX; diff --git a/Extern/dxmath/Extensions/DirectXMathFMA4.h b/Extern/dxmath/Extensions/DirectXMathFMA4.h new file mode 100644 index 000000000..45f4d0d32 --- /dev/null +++ b/Extern/dxmath/Extensions/DirectXMathFMA4.h @@ -0,0 +1,415 @@ +//------------------------------------------------------------------------------------- +// DirectXMathFMA4.h -- FMA4 extensions for SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __arm__ || __aarch64__ +#error FMA4 not supported on ARM platform +#endif + +#include +#include + +#ifdef __GNUC__ +#include +#endif + +namespace DirectX +{ + + namespace FMA4 + { + + inline bool XMVerifyFMA4Support() + { + // Should return true for AMD Bulldozer processors + // with OS support for AVX (Windows 7 Service Pack 1, Windows Server 2008 R2 Service Pack 1, Windows 8, Windows Server 2012) + + // See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + int CPUInfo[4] = { -1 }; + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0); + #endif + + if (CPUInfo[0] < 1) + return false; + + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 1); + #endif + + // We check for AVX, OSXSAVE (required to access FMA4) + if ((CPUInfo[2] & 0x18000000) != 0x18000000) + return false; + + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0x80000000, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0x80000000); + #endif + + if (uint32_t(CPUInfo[0]) < 0x80000001u) + return false; + + // We check for FMA4 + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0x80000001, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0x80000001); + #endif + + return (CPUInfo[2] & 0x10000); + } + + + //------------------------------------------------------------------------------------- + // Vector + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVectorMultiplyAdd + ( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR V3 + ) + { + return _mm_macc_ps(V1, V2, V3); + } + + inline XMVECTOR XM_CALLCONV XMVectorNegativeMultiplySubtract + ( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR V3 + ) + { + return _mm_nmacc_ps(V1, V2, V3); + } + + + //------------------------------------------------------------------------------------- + // Vector2 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector2Transform + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_macc_ps(vResult, M.r[1], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_macc_ps(vTemp, M.r[0], vResult); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector2TransformCoord + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_macc_ps(vResult, M.r[1], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_macc_ps(vTemp, M.r[0], vResult); + XMVECTOR W = _mm_permute_ps(vResult, _MM_SHUFFLE(3, 3, 3, 3)); + vResult = _mm_div_ps(vResult, W); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector2TransformNormal + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_mul_ps(vResult, M.r[1]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_macc_ps(vTemp, M.r[0], vResult); + return vResult; + } + + + //------------------------------------------------------------------------------------- + // Vector3 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector3Transform + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_macc_ps(vResult, M.r[2], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_macc_ps(vTemp, M.r[1], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_macc_ps(vTemp, M.r[0], vResult); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector3TransformCoord + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_macc_ps(vResult, M.r[2], M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_macc_ps(vTemp, M.r[1], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_macc_ps(vTemp, M.r[0], vResult); + XMVECTOR W = _mm_permute_ps(vResult, _MM_SHUFFLE(3, 3, 3, 3)); + vResult = _mm_div_ps(vResult, W); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVector3TransformNormal + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_mul_ps(vResult, M.r[2]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_macc_ps(vTemp, M.r[1], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_macc_ps(vTemp, M.r[0], vResult); + return vResult; + } + + XMMATRIX XM_CALLCONV XMMatrixMultiply(CXMMATRIX M1, CXMMATRIX M2); + + inline XMVECTOR XM_CALLCONV XMVector3Project + ( + FXMVECTOR V, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + CXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World + ) + { + const float HalfViewportWidth = ViewportWidth * 0.5f; + const float HalfViewportHeight = ViewportHeight * 0.5f; + + XMVECTOR Scale = XMVectorSet(HalfViewportWidth, -HalfViewportHeight, ViewportMaxZ - ViewportMinZ, 0.0f); + XMVECTOR Offset = XMVectorSet(ViewportX + HalfViewportWidth, ViewportY + HalfViewportHeight, ViewportMinZ, 0.0f); + + XMMATRIX Transform = FMA4::XMMatrixMultiply(World, View); + Transform = FMA4::XMMatrixMultiply(Transform, Projection); + + XMVECTOR Result = FMA4::XMVector3TransformCoord(V, Transform); + + Result = FMA4::XMVectorMultiplyAdd(Result, Scale, Offset); + + return Result; + } + + inline XMVECTOR XM_CALLCONV XMVector3Unproject + ( + FXMVECTOR V, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + CXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World + ) + { + static const XMVECTORF32 D = { { { -1.0f, 1.0f, 0.0f, 0.0f } } }; + + XMVECTOR Scale = XMVectorSet(ViewportWidth * 0.5f, -ViewportHeight * 0.5f, ViewportMaxZ - ViewportMinZ, 1.0f); + Scale = XMVectorReciprocal(Scale); + + XMVECTOR Offset = XMVectorSet(-ViewportX, -ViewportY, -ViewportMinZ, 0.0f); + Offset = FMA4::XMVectorMultiplyAdd(Scale, Offset, D.v); + + XMMATRIX Transform = FMA4::XMMatrixMultiply(World, View); + Transform = FMA4::XMMatrixMultiply(Transform, Projection); + Transform = XMMatrixInverse(nullptr, Transform); + + XMVECTOR Result = FMA4::XMVectorMultiplyAdd(V, Scale, Offset); + + return FMA4::XMVector3TransformCoord(Result, Transform); + } + + + //------------------------------------------------------------------------------------- + // Vector4 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector4Transform + ( + FXMVECTOR V, + CXMMATRIX M + ) + { + XMVECTOR vResult = _mm_permute_ps(V, _MM_SHUFFLE(3, 3, 3, 3)); // W + vResult = _mm_mul_ps(vResult, M.r[3]); + XMVECTOR vTemp = _mm_permute_ps(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_macc_ps(vTemp, M.r[2], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_macc_ps(vTemp, M.r[1], vResult); + vTemp = _mm_permute_ps(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = _mm_macc_ps(vTemp, M.r[0], vResult); + return vResult; + } + + + //------------------------------------------------------------------------------------- + // Matrix + //------------------------------------------------------------------------------------- + + inline XMMATRIX XM_CALLCONV XMMatrixMultiply + ( + CXMMATRIX M1, + CXMMATRIX M2 + ) + { + XMMATRIX mResult; + // Use vW to hold the original row + XMVECTOR vW = M1.r[0]; + // Splat the component X,Y,Z then W + XMVECTOR vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + // Perform the operation on the first row + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_macc_ps(vY, M2.r[1], vX); + vX = _mm_macc_ps(vZ, M2.r[2], vX); + vX = _mm_macc_ps(vW, M2.r[3], vX); + mResult.r[0] = vX; + // Repeat for the other 3 rows + vW = M1.r[1]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_macc_ps(vY, M2.r[1], vX); + vX = _mm_macc_ps(vZ, M2.r[2], vX); + vX = _mm_macc_ps(vW, M2.r[3], vX); + mResult.r[1] = vX; + vW = M1.r[2]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_macc_ps(vY, M2.r[1], vX); + vX = _mm_macc_ps(vZ, M2.r[2], vX); + vX = _mm_macc_ps(vW, M2.r[3], vX); + mResult.r[2] = vX; + vW = M1.r[3]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_macc_ps(vY, M2.r[1], vX); + vX = _mm_macc_ps(vZ, M2.r[2], vX); + vX = _mm_macc_ps(vW, M2.r[3], vX); + mResult.r[3] = vX; + return mResult; + } + + inline XMMATRIX XM_CALLCONV XMMatrixMultiplyTranspose + ( + FXMMATRIX M1, + CXMMATRIX M2 + ) + { + // Use vW to hold the original row + XMVECTOR vW = M1.r[0]; + // Splat the component X,Y,Z then W + XMVECTOR vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + // Perform the operation on the first row + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_macc_ps(vY, M2.r[1], vX); + vX = _mm_macc_ps(vZ, M2.r[2], vX); + vX = _mm_macc_ps(vW, M2.r[3], vX); + __m128 r0 = vX; + // Repeat for the other 3 rows + vW = M1.r[1]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_macc_ps(vY, M2.r[1], vX); + vX = _mm_macc_ps(vZ, M2.r[2], vX); + vX = _mm_macc_ps(vW, M2.r[3], vX); + __m128 r1 = vX; + vW = M1.r[2]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_macc_ps(vY, M2.r[1], vX); + vX = _mm_macc_ps(vZ, M2.r[2], vX); + vX = _mm_macc_ps(vW, M2.r[3], vX); + __m128 r2 = vX; + vW = M1.r[3]; + vX = _mm_permute_ps(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = _mm_permute_ps(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = _mm_permute_ps(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = _mm_permute_ps(vW, _MM_SHUFFLE(3, 3, 3, 3)); + vX = _mm_mul_ps(vX, M2.r[0]); + vX = _mm_macc_ps(vY, M2.r[1], vX); + vX = _mm_macc_ps(vZ, M2.r[2], vX); + vX = _mm_macc_ps(vW, M2.r[3], vX); + __m128 r3 = vX; + + // x.x,x.y,y.x,y.y + XMVECTOR vTemp1 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(1, 0, 1, 0)); + // x.z,x.w,y.z,y.w + XMVECTOR vTemp3 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(3, 2, 3, 2)); + // z.x,z.y,w.x,w.y + XMVECTOR vTemp2 = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(1, 0, 1, 0)); + // z.z,z.w,w.z,w.w + XMVECTOR vTemp4 = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(3, 2, 3, 2)); + + XMMATRIX mResult; + // x.x,y.x,z.x,w.x + mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + // x.y,y.y,z.y,w.y + mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + // x.z,y.z,z.z,w.z + mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + // x.w,y.w,z.w,w.w + mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1)); + return mResult; + } + + } // namespace FMA4 + +} // namespace DirectX; diff --git a/Extern/dxmath/Extensions/DirectXMathSSE3.h b/Extern/dxmath/Extensions/DirectXMathSSE3.h new file mode 100644 index 000000000..ae293027c --- /dev/null +++ b/Extern/dxmath/Extensions/DirectXMathSSE3.h @@ -0,0 +1,111 @@ +//------------------------------------------------------------------------------------- +// DirectXMathSSE3.h -- SSE3 extensions for SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __arm__ || __aarch64__ +#error SSE3 not supported on ARM platform +#endif + +#include + +#include + +namespace DirectX +{ + + namespace SSE3 + { + + inline bool XMVerifySSE3Support() + { + // Should return true on AMD Athlon 64, AMD Phenom, and Intel Pentium 4 or later processors + + // See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + int CPUInfo[4] = { -1 }; + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0); + #endif + if (CPUInfo[0] < 1) + return false; + + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 1); + #endif + + // We only check for SSE3 instruction set. SSSE3 instructions are not used. + return ((CPUInfo[2] & 0x1) != 0); + } + + inline XMVECTOR XM_CALLCONV XMVector2Dot + ( + FXMVECTOR V1, + FXMVECTOR V2 + ) + { + XMVECTOR vTemp = _mm_mul_ps(V1, V2); + vTemp = _mm_hadd_ps(vTemp, vTemp); + return _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(0, 0, 0, 0)); + } + + inline XMVECTOR XM_CALLCONV XMVector2LengthSq(FXMVECTOR V) + { + return SSE3::XMVector2Dot(V, V); + } + + inline XMVECTOR XM_CALLCONV XMVector3Dot + ( + FXMVECTOR V1, + FXMVECTOR V2 + ) + { + XMVECTOR vTemp = _mm_mul_ps(V1, V2); + vTemp = _mm_and_ps(vTemp, g_XMMask3); + vTemp = _mm_hadd_ps(vTemp, vTemp); + return _mm_hadd_ps(vTemp, vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector3LengthSq(FXMVECTOR V) + { + return SSE3::XMVector3Dot(V, V); + } + + inline XMVECTOR XM_CALLCONV XMVector4Dot + ( + FXMVECTOR V1, + FXMVECTOR V2 + ) + { + XMVECTOR vTemp = _mm_mul_ps(V1, V2); + vTemp = _mm_hadd_ps(vTemp, vTemp); + return _mm_hadd_ps(vTemp, vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector4LengthSq(FXMVECTOR V) + { + return SSE3::XMVector4Dot(V, V); + } + + inline XMVECTOR XM_CALLCONV XMVectorSwizzle_0022(FXMVECTOR V) + { + return _mm_moveldup_ps(V); + } + + inline XMVECTOR XM_CALLCONV XMVectorSwizzle_1133(FXMVECTOR V) + { + return _mm_movehdup_ps(V); + } + + } // namespace SSE3 + +} // namespace DirectX diff --git a/Extern/dxmath/Extensions/DirectXMathSSE4.h b/Extern/dxmath/Extensions/DirectXMathSSE4.h new file mode 100644 index 000000000..2b51e7684 --- /dev/null +++ b/Extern/dxmath/Extensions/DirectXMathSSE4.h @@ -0,0 +1,417 @@ +//------------------------------------------------------------------------------------- +// DirectXMathSSE4.h -- SSE4.1 extensions for SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __arm__ || __aarch64__ +#error SSE4 not supported on ARM platform +#endif + +#include + +#include + +namespace DirectX +{ + + namespace SSE4 + { + + inline bool XMVerifySSE4Support() + { + // Should return true on AMD Bulldozer, Intel Core 2 ("Penryn"), and Intel Core i7 ("Nehalem") or later processors + + // See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + int CPUInfo[4] = { -1 }; + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 0); + #endif + if (CPUInfo[0] < 1) + return false; + + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); + #else + __cpuid(CPUInfo, 1); + #endif + + // We only check for SSE4.1 instruction set. SSE4.2 instructions are not used. + return ((CPUInfo[2] & 0x80000) == 0x80000); + } + + + //------------------------------------------------------------------------------------- + // Vector + //------------------------------------------------------------------------------------- + + #ifdef __clang__ + #pragma clang diagnostic ignored "-Wundefined-reinterpret-cast" + #endif + + inline void XM_CALLCONV XMVectorGetYPtr(_Out_ float *y, _In_ FXMVECTOR V) + { + assert(y != nullptr); + *reinterpret_cast(y) = _mm_extract_ps(V, 1); + } + + inline void XM_CALLCONV XMVectorGetZPtr(_Out_ float *z, _In_ FXMVECTOR V) + { + assert(z != nullptr); + *reinterpret_cast(z) = _mm_extract_ps(V, 2); + } + + inline void XM_CALLCONV XMVectorGetWPtr(_Out_ float *w, _In_ FXMVECTOR V) + { + assert(w != nullptr); + *reinterpret_cast(w) = _mm_extract_ps(V, 3); + } + + inline uint32_t XM_CALLCONV XMVectorGetIntY(FXMVECTOR V) + { + __m128i V1 = _mm_castps_si128(V); + return static_cast(_mm_extract_epi32(V1, 1)); + } + + inline uint32_t XM_CALLCONV XMVectorGetIntZ(FXMVECTOR V) + { + __m128i V1 = _mm_castps_si128(V); + return static_cast(_mm_extract_epi32(V1, 2)); + } + + inline uint32_t XM_CALLCONV XMVectorGetIntW(FXMVECTOR V) + { + __m128i V1 = _mm_castps_si128(V); + return static_cast(_mm_extract_epi32(V1, 3)); + } + + inline void XM_CALLCONV XMVectorGetIntYPtr(_Out_ uint32_t *y, _In_ FXMVECTOR V) + { + assert(y != nullptr); + __m128i V1 = _mm_castps_si128(V); + *y = static_cast(_mm_extract_epi32(V1, 1)); + } + + inline void XM_CALLCONV XMVectorGetIntZPtr(_Out_ uint32_t *z, _In_ FXMVECTOR V) + { + assert(z != nullptr); + __m128i V1 = _mm_castps_si128(V); + *z = static_cast(_mm_extract_epi32(V1, 2)); + } + + inline void XM_CALLCONV XMVectorGetIntWPtr(_Out_ uint32_t *w, _In_ FXMVECTOR V) + { + assert(w != nullptr); + __m128i V1 = _mm_castps_si128(V); + *w = static_cast(_mm_extract_epi32(V1, 3)); + } + + inline XMVECTOR XM_CALLCONV XMVectorSetY(FXMVECTOR V, float y) + { + XMVECTOR vResult = _mm_set_ss(y); + vResult = _mm_insert_ps(V, vResult, 0x10); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVectorSetZ(FXMVECTOR V, float z) + { + XMVECTOR vResult = _mm_set_ss(z); + vResult = _mm_insert_ps(V, vResult, 0x20); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVectorSetW(FXMVECTOR V, float w) + { + XMVECTOR vResult = _mm_set_ss(w); + vResult = _mm_insert_ps(V, vResult, 0x30); + return vResult; + } + + inline XMVECTOR XM_CALLCONV XMVectorSetIntY(FXMVECTOR V, uint32_t y) + { + __m128i vResult = _mm_castps_si128(V); + vResult = _mm_insert_epi32(vResult, static_cast(y), 1); + return _mm_castsi128_ps(vResult); + } + + inline XMVECTOR XM_CALLCONV XMVectorSetIntZ(FXMVECTOR V, uint32_t z) + { + __m128i vResult = _mm_castps_si128(V); + vResult = _mm_insert_epi32(vResult, static_cast(z), 2); + return _mm_castsi128_ps(vResult); + } + + inline XMVECTOR XM_CALLCONV XMVectorSetIntW(FXMVECTOR V, uint32_t w) + { + __m128i vResult = _mm_castps_si128(V); + vResult = _mm_insert_epi32(vResult, static_cast(w), 3); + return _mm_castsi128_ps(vResult); + } + + inline XMVECTOR XM_CALLCONV XMVectorRound(FXMVECTOR V) + { + return _mm_round_ps(V, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); + } + + inline XMVECTOR XM_CALLCONV XMVectorTruncate(FXMVECTOR V) + { + return _mm_round_ps(V, _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC); + } + + inline XMVECTOR XM_CALLCONV XMVectorFloor(FXMVECTOR V) + { + return _mm_floor_ps(V); + } + + inline XMVECTOR XM_CALLCONV XMVectorCeiling(FXMVECTOR V) + { + return _mm_ceil_ps(V); + } + + + //------------------------------------------------------------------------------------- + // Vector2 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector2Dot(FXMVECTOR V1, FXMVECTOR V2) + { + return _mm_dp_ps(V1, V2, 0x3f); + } + + inline XMVECTOR XM_CALLCONV XMVector2LengthSq(FXMVECTOR V) + { + return SSE4::XMVector2Dot(V, V); + } + + inline XMVECTOR XM_CALLCONV XMVector2ReciprocalLengthEst(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + return _mm_rsqrt_ps(vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector2ReciprocalLength(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + XMVECTOR vLengthSq = _mm_sqrt_ps(vTemp); + return _mm_div_ps(g_XMOne, vLengthSq); + } + + inline XMVECTOR XM_CALLCONV XMVector2LengthEst(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + return _mm_sqrt_ps(vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector2Length(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + return _mm_sqrt_ps(vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector2NormalizeEst(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + XMVECTOR vResult = _mm_rsqrt_ps(vTemp); + return _mm_mul_ps(vResult, V); + } + + inline XMVECTOR XM_CALLCONV XMVector2Normalize(FXMVECTOR V) + { + XMVECTOR vLengthSq = _mm_dp_ps(V, V, 0x3f); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Reciprocal mul to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; + } + + + //------------------------------------------------------------------------------------- + // Vector3 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector3Dot(FXMVECTOR V1, FXMVECTOR V2) + { + return _mm_dp_ps(V1, V2, 0x7f); + } + + inline XMVECTOR XM_CALLCONV XMVector3LengthSq(FXMVECTOR V) + { + return SSE4::XMVector3Dot(V, V); + } + + inline XMVECTOR XM_CALLCONV XMVector3ReciprocalLengthEst(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + return _mm_rsqrt_ps(vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector3ReciprocalLength(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + XMVECTOR vLengthSq = _mm_sqrt_ps(vTemp); + return _mm_div_ps(g_XMOne, vLengthSq); + } + + inline XMVECTOR XM_CALLCONV XMVector3LengthEst(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + return _mm_sqrt_ps(vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector3Length(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + return _mm_sqrt_ps(vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector3NormalizeEst(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + XMVECTOR vResult = _mm_rsqrt_ps(vTemp); + return _mm_mul_ps(vResult, V); + } + + inline XMVECTOR XM_CALLCONV XMVector3Normalize(FXMVECTOR V) + { + XMVECTOR vLengthSq = _mm_dp_ps(V, V, 0x7f); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Divide to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; + } + + + //------------------------------------------------------------------------------------- + // Vector4 + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMVector4Dot(FXMVECTOR V1, FXMVECTOR V2) + { + return _mm_dp_ps(V1, V2, 0xff); + } + + inline XMVECTOR XM_CALLCONV XMVector4LengthSq(FXMVECTOR V) + { + return SSE4::XMVector4Dot(V, V); + } + + inline XMVECTOR XM_CALLCONV XMVector4ReciprocalLengthEst(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + return _mm_rsqrt_ps(vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector4ReciprocalLength(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + XMVECTOR vLengthSq = _mm_sqrt_ps(vTemp); + return _mm_div_ps(g_XMOne, vLengthSq); + } + + inline XMVECTOR XM_CALLCONV XMVector4LengthEst(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + return _mm_sqrt_ps(vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector4Length(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + return _mm_sqrt_ps(vTemp); + } + + inline XMVECTOR XM_CALLCONV XMVector4NormalizeEst(FXMVECTOR V) + { + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + XMVECTOR vResult = _mm_rsqrt_ps(vTemp); + return _mm_mul_ps(vResult, V); + } + + inline XMVECTOR XM_CALLCONV XMVector4Normalize(FXMVECTOR V) + { + XMVECTOR vLengthSq = _mm_dp_ps(V, V, 0xff); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Divide to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; + } + + + //------------------------------------------------------------------------------------- + // Plane + //------------------------------------------------------------------------------------- + + inline XMVECTOR XM_CALLCONV XMPlaneNormalizeEst(FXMVECTOR P) + { + XMVECTOR vTemp = _mm_dp_ps(P, P, 0x7f); + XMVECTOR vResult = _mm_rsqrt_ps(vTemp); + return _mm_mul_ps(vResult, P); + } + + inline XMVECTOR XM_CALLCONV XMPlaneNormalize(FXMVECTOR P) + { + XMVECTOR vLengthSq = _mm_dp_ps(P, P, 0x7f); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Reciprocal mul to perform the normalization + vResult = _mm_div_ps(P, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vLengthSq); + return vResult; + } + + } // namespace SSE4 + +} // namespace DirectX diff --git a/Extern/dxmath/Inc/DirectXCollision.h b/Extern/dxmath/Inc/DirectXCollision.h new file mode 100644 index 000000000..17f514af4 --- /dev/null +++ b/Extern/dxmath/Inc/DirectXCollision.h @@ -0,0 +1,370 @@ +//------------------------------------------------------------------------------------- +// DirectXCollision.h -- C++ Collision Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#include "DirectXMath.h" + +namespace DirectX +{ + + enum ContainmentType + { + DISJOINT = 0, + INTERSECTS = 1, + CONTAINS = 2 + }; + + enum PlaneIntersectionType + { + FRONT = 0, + INTERSECTING = 1, + BACK = 2 + }; + + struct BoundingBox; + struct BoundingOrientedBox; + struct BoundingFrustum; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4324 4820) + // C4324: alignment padding warnings + // C4820: Off by default noise +#endif + + //------------------------------------------------------------------------------------- + // Bounding sphere + //------------------------------------------------------------------------------------- + struct BoundingSphere + { + XMFLOAT3 Center; // Center of the sphere. + float Radius; // Radius of the sphere. + + // Creators + BoundingSphere() noexcept : Center(0, 0, 0), Radius(1.f) {} + + BoundingSphere(const BoundingSphere&) = default; + BoundingSphere& operator=(const BoundingSphere&) = default; + + BoundingSphere(BoundingSphere&&) = default; + BoundingSphere& operator=(BoundingSphere&&) = default; + + constexpr BoundingSphere(_In_ const XMFLOAT3& center, _In_ float radius) noexcept + : Center(center), Radius(radius) {} + + // Methods + void XM_CALLCONV Transform(_Out_ BoundingSphere& Out, _In_ FXMMATRIX M) const noexcept; + void XM_CALLCONV Transform(_Out_ BoundingSphere& Out, _In_ float Scale, _In_ FXMVECTOR Rotation, _In_ FXMVECTOR Translation) const noexcept; + // Transform the sphere + + ContainmentType XM_CALLCONV Contains(_In_ FXMVECTOR Point) const noexcept; + ContainmentType XM_CALLCONV Contains(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) const noexcept; + ContainmentType Contains(_In_ const BoundingSphere& sh) const noexcept; + ContainmentType Contains(_In_ const BoundingBox& box) const noexcept; + ContainmentType Contains(_In_ const BoundingOrientedBox& box) const noexcept; + ContainmentType Contains(_In_ const BoundingFrustum& fr) const noexcept; + + bool Intersects(_In_ const BoundingSphere& sh) const noexcept; + bool Intersects(_In_ const BoundingBox& box) const noexcept; + bool Intersects(_In_ const BoundingOrientedBox& box) const noexcept; + bool Intersects(_In_ const BoundingFrustum& fr) const noexcept; + + bool XM_CALLCONV Intersects(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) const noexcept; + // Triangle-sphere test + + PlaneIntersectionType XM_CALLCONV Intersects(_In_ FXMVECTOR Plane) const noexcept; + // Plane-sphere test + + bool XM_CALLCONV Intersects(_In_ FXMVECTOR Origin, _In_ FXMVECTOR Direction, _Out_ float& Dist) const noexcept; + // Ray-sphere test + + ContainmentType XM_CALLCONV ContainedBy(_In_ FXMVECTOR Plane0, _In_ FXMVECTOR Plane1, _In_ FXMVECTOR Plane2, + _In_ GXMVECTOR Plane3, _In_ HXMVECTOR Plane4, _In_ HXMVECTOR Plane5) const noexcept; + // Test sphere against six planes (see BoundingFrustum::GetPlanes) + + // Static methods + static void CreateMerged(_Out_ BoundingSphere& Out, _In_ const BoundingSphere& S1, _In_ const BoundingSphere& S2) noexcept; + + static void CreateFromBoundingBox(_Out_ BoundingSphere& Out, _In_ const BoundingBox& box) noexcept; + static void CreateFromBoundingBox(_Out_ BoundingSphere& Out, _In_ const BoundingOrientedBox& box) noexcept; + + static void CreateFromPoints(_Out_ BoundingSphere& Out, _In_ size_t Count, + _In_reads_bytes_(sizeof(XMFLOAT3) + Stride * (Count - 1)) const XMFLOAT3* pPoints, _In_ size_t Stride) noexcept; + + static void CreateFromFrustum(_Out_ BoundingSphere& Out, _In_ const BoundingFrustum& fr) noexcept; + }; + + //------------------------------------------------------------------------------------- + // Axis-aligned bounding box + //------------------------------------------------------------------------------------- + struct BoundingBox + { + static constexpr size_t CORNER_COUNT = 8; + + XMFLOAT3 Center; // Center of the box. + XMFLOAT3 Extents; // Distance from the center to each side. + + // Creators + BoundingBox() noexcept : Center(0, 0, 0), Extents(1.f, 1.f, 1.f) {} + + BoundingBox(const BoundingBox&) = default; + BoundingBox& operator=(const BoundingBox&) = default; + + BoundingBox(BoundingBox&&) = default; + BoundingBox& operator=(BoundingBox&&) = default; + + constexpr BoundingBox(_In_ const XMFLOAT3& center, _In_ const XMFLOAT3& extents) noexcept + : Center(center), Extents(extents) {} + + // Methods + void XM_CALLCONV Transform(_Out_ BoundingBox& Out, _In_ FXMMATRIX M) const noexcept; + void XM_CALLCONV Transform(_Out_ BoundingBox& Out, _In_ float Scale, _In_ FXMVECTOR Rotation, _In_ FXMVECTOR Translation) const noexcept; + + void GetCorners(_Out_writes_(8) XMFLOAT3* Corners) const noexcept; + // Gets the 8 corners of the box + + ContainmentType XM_CALLCONV Contains(_In_ FXMVECTOR Point) const noexcept; + ContainmentType XM_CALLCONV Contains(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) const noexcept; + ContainmentType Contains(_In_ const BoundingSphere& sh) const noexcept; + ContainmentType Contains(_In_ const BoundingBox& box) const noexcept; + ContainmentType Contains(_In_ const BoundingOrientedBox& box) const noexcept; + ContainmentType Contains(_In_ const BoundingFrustum& fr) const noexcept; + + bool Intersects(_In_ const BoundingSphere& sh) const noexcept; + bool Intersects(_In_ const BoundingBox& box) const noexcept; + bool Intersects(_In_ const BoundingOrientedBox& box) const noexcept; + bool Intersects(_In_ const BoundingFrustum& fr) const noexcept; + + bool XM_CALLCONV Intersects(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) const noexcept; + // Triangle-Box test + + PlaneIntersectionType XM_CALLCONV Intersects(_In_ FXMVECTOR Plane) const noexcept; + // Plane-box test + + bool XM_CALLCONV Intersects(_In_ FXMVECTOR Origin, _In_ FXMVECTOR Direction, _Out_ float& Dist) const noexcept; + // Ray-Box test + + ContainmentType XM_CALLCONV ContainedBy(_In_ FXMVECTOR Plane0, _In_ FXMVECTOR Plane1, _In_ FXMVECTOR Plane2, + _In_ GXMVECTOR Plane3, _In_ HXMVECTOR Plane4, _In_ HXMVECTOR Plane5) const noexcept; + // Test box against six planes (see BoundingFrustum::GetPlanes) + + // Static methods + static void CreateMerged(_Out_ BoundingBox& Out, _In_ const BoundingBox& b1, _In_ const BoundingBox& b2) noexcept; + + static void CreateFromSphere(_Out_ BoundingBox& Out, _In_ const BoundingSphere& sh) noexcept; + + static void XM_CALLCONV CreateFromPoints(_Out_ BoundingBox& Out, _In_ FXMVECTOR pt1, _In_ FXMVECTOR pt2) noexcept; + static void CreateFromPoints(_Out_ BoundingBox& Out, _In_ size_t Count, + _In_reads_bytes_(sizeof(XMFLOAT3) + Stride * (Count - 1)) const XMFLOAT3* pPoints, _In_ size_t Stride) noexcept; + }; + + //------------------------------------------------------------------------------------- + // Oriented bounding box + //------------------------------------------------------------------------------------- + struct BoundingOrientedBox + { + static constexpr size_t CORNER_COUNT = 8; + + XMFLOAT3 Center; // Center of the box. + XMFLOAT3 Extents; // Distance from the center to each side. + XMFLOAT4 Orientation; // Unit quaternion representing rotation (box -> world). + + // Creators + BoundingOrientedBox() noexcept : Center(0, 0, 0), Extents(1.f, 1.f, 1.f), Orientation(0, 0, 0, 1.f) {} + + BoundingOrientedBox(const BoundingOrientedBox&) = default; + BoundingOrientedBox& operator=(const BoundingOrientedBox&) = default; + + BoundingOrientedBox(BoundingOrientedBox&&) = default; + BoundingOrientedBox& operator=(BoundingOrientedBox&&) = default; + + constexpr BoundingOrientedBox(_In_ const XMFLOAT3& center, _In_ const XMFLOAT3& extents, _In_ const XMFLOAT4& orientation) noexcept + : Center(center), Extents(extents), Orientation(orientation) {} + + // Methods + void XM_CALLCONV Transform(_Out_ BoundingOrientedBox& Out, _In_ FXMMATRIX M) const noexcept; + void XM_CALLCONV Transform(_Out_ BoundingOrientedBox& Out, _In_ float Scale, _In_ FXMVECTOR Rotation, _In_ FXMVECTOR Translation) const noexcept; + + void GetCorners(_Out_writes_(8) XMFLOAT3* Corners) const noexcept; + // Gets the 8 corners of the box + + ContainmentType XM_CALLCONV Contains(_In_ FXMVECTOR Point) const noexcept; + ContainmentType XM_CALLCONV Contains(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) const noexcept; + ContainmentType Contains(_In_ const BoundingSphere& sh) const noexcept; + ContainmentType Contains(_In_ const BoundingBox& box) const noexcept; + ContainmentType Contains(_In_ const BoundingOrientedBox& box) const noexcept; + ContainmentType Contains(_In_ const BoundingFrustum& fr) const noexcept; + + bool Intersects(_In_ const BoundingSphere& sh) const noexcept; + bool Intersects(_In_ const BoundingBox& box) const noexcept; + bool Intersects(_In_ const BoundingOrientedBox& box) const noexcept; + bool Intersects(_In_ const BoundingFrustum& fr) const noexcept; + + bool XM_CALLCONV Intersects(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) const noexcept; + // Triangle-OrientedBox test + + PlaneIntersectionType XM_CALLCONV Intersects(_In_ FXMVECTOR Plane) const noexcept; + // Plane-OrientedBox test + + bool XM_CALLCONV Intersects(_In_ FXMVECTOR Origin, _In_ FXMVECTOR Direction, _Out_ float& Dist) const noexcept; + // Ray-OrientedBox test + + ContainmentType XM_CALLCONV ContainedBy(_In_ FXMVECTOR Plane0, _In_ FXMVECTOR Plane1, _In_ FXMVECTOR Plane2, + _In_ GXMVECTOR Plane3, _In_ HXMVECTOR Plane4, _In_ HXMVECTOR Plane5) const noexcept; + // Test OrientedBox against six planes (see BoundingFrustum::GetPlanes) + + // Static methods + static void CreateFromBoundingBox(_Out_ BoundingOrientedBox& Out, _In_ const BoundingBox& box) noexcept; + + static void CreateFromPoints(_Out_ BoundingOrientedBox& Out, _In_ size_t Count, + _In_reads_bytes_(sizeof(XMFLOAT3) + Stride * (Count - 1)) const XMFLOAT3* pPoints, _In_ size_t Stride) noexcept; + }; + + //------------------------------------------------------------------------------------- + // Bounding frustum + //------------------------------------------------------------------------------------- + struct BoundingFrustum + { + static constexpr size_t CORNER_COUNT = 8; + + XMFLOAT3 Origin; // Origin of the frustum (and projection). + XMFLOAT4 Orientation; // Quaternion representing rotation. + + float RightSlope; // Positive X (X/Z) + float LeftSlope; // Negative X + float TopSlope; // Positive Y (Y/Z) + float BottomSlope; // Negative Y + float Near, Far; // Z of the near plane and far plane. + + // Creators + BoundingFrustum() noexcept : + Origin(0, 0, 0), Orientation(0, 0, 0, 1.f), RightSlope(1.f), LeftSlope(-1.f), + TopSlope(1.f), BottomSlope(-1.f), Near(0), Far(1.f) {} + + BoundingFrustum(const BoundingFrustum&) = default; + BoundingFrustum& operator=(const BoundingFrustum&) = default; + + BoundingFrustum(BoundingFrustum&&) = default; + BoundingFrustum& operator=(BoundingFrustum&&) = default; + + constexpr BoundingFrustum(_In_ const XMFLOAT3& origin, _In_ const XMFLOAT4& orientation, + _In_ float rightSlope, _In_ float leftSlope, _In_ float topSlope, _In_ float bottomSlope, + _In_ float nearPlane, _In_ float farPlane) noexcept + : Origin(origin), Orientation(orientation), + RightSlope(rightSlope), LeftSlope(leftSlope), TopSlope(topSlope), BottomSlope(bottomSlope), + Near(nearPlane), Far(farPlane) {} + BoundingFrustum(_In_ CXMMATRIX Projection, bool rhcoords = false) noexcept; + + // Methods + void XM_CALLCONV Transform(_Out_ BoundingFrustum& Out, _In_ FXMMATRIX M) const noexcept; + void XM_CALLCONV Transform(_Out_ BoundingFrustum& Out, _In_ float Scale, _In_ FXMVECTOR Rotation, _In_ FXMVECTOR Translation) const noexcept; + + void GetCorners(_Out_writes_(8) XMFLOAT3* Corners) const noexcept; + // Gets the 8 corners of the frustum + + ContainmentType XM_CALLCONV Contains(_In_ FXMVECTOR Point) const noexcept; + ContainmentType XM_CALLCONV Contains(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) const noexcept; + ContainmentType Contains(_In_ const BoundingSphere& sp) const noexcept; + ContainmentType Contains(_In_ const BoundingBox& box) const noexcept; + ContainmentType Contains(_In_ const BoundingOrientedBox& box) const noexcept; + ContainmentType Contains(_In_ const BoundingFrustum& fr) const noexcept; + // Frustum-Frustum test + + bool Intersects(_In_ const BoundingSphere& sh) const noexcept; + bool Intersects(_In_ const BoundingBox& box) const noexcept; + bool Intersects(_In_ const BoundingOrientedBox& box) const noexcept; + bool Intersects(_In_ const BoundingFrustum& fr) const noexcept; + + bool XM_CALLCONV Intersects(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) const noexcept; + // Triangle-Frustum test + + PlaneIntersectionType XM_CALLCONV Intersects(_In_ FXMVECTOR Plane) const noexcept; + // Plane-Frustum test + + bool XM_CALLCONV Intersects(_In_ FXMVECTOR rayOrigin, _In_ FXMVECTOR Direction, _Out_ float& Dist) const noexcept; + // Ray-Frustum test + + ContainmentType XM_CALLCONV ContainedBy(_In_ FXMVECTOR Plane0, _In_ FXMVECTOR Plane1, _In_ FXMVECTOR Plane2, + _In_ GXMVECTOR Plane3, _In_ HXMVECTOR Plane4, _In_ HXMVECTOR Plane5) const noexcept; + // Test frustum against six planes (see BoundingFrustum::GetPlanes) + + void GetPlanes(_Out_opt_ XMVECTOR* NearPlane, _Out_opt_ XMVECTOR* FarPlane, _Out_opt_ XMVECTOR* RightPlane, + _Out_opt_ XMVECTOR* LeftPlane, _Out_opt_ XMVECTOR* TopPlane, _Out_opt_ XMVECTOR* BottomPlane) const noexcept; + // Create 6 Planes representation of Frustum + + // Static methods + static void XM_CALLCONV CreateFromMatrix(_Out_ BoundingFrustum& Out, _In_ FXMMATRIX Projection, bool rhcoords = false) noexcept; + }; + + //----------------------------------------------------------------------------- + // Triangle intersection testing routines. + //----------------------------------------------------------------------------- + namespace TriangleTests + { + bool XM_CALLCONV Intersects(_In_ FXMVECTOR Origin, _In_ FXMVECTOR Direction, _In_ FXMVECTOR V0, _In_ GXMVECTOR V1, _In_ HXMVECTOR V2, _Out_ float& Dist) noexcept; + // Ray-Triangle + + bool XM_CALLCONV Intersects(_In_ FXMVECTOR A0, _In_ FXMVECTOR A1, _In_ FXMVECTOR A2, _In_ GXMVECTOR B0, _In_ HXMVECTOR B1, _In_ HXMVECTOR B2) noexcept; + // Triangle-Triangle + + PlaneIntersectionType XM_CALLCONV Intersects(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2, _In_ GXMVECTOR Plane) noexcept; + // Plane-Triangle + + ContainmentType XM_CALLCONV ContainedBy(_In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2, + _In_ GXMVECTOR Plane0, _In_ HXMVECTOR Plane1, _In_ HXMVECTOR Plane2, + _In_ CXMVECTOR Plane3, _In_ CXMVECTOR Plane4, _In_ CXMVECTOR Plane5) noexcept; + // Test a triangle against six planes at once (see BoundingFrustum::GetPlanes) + } + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + /**************************************************************************** + * + * Implementation + * + ****************************************************************************/ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4068 4365 4616 6001) + // C4068/4616: ignore unknown pragmas + // C4365: Off by default noise + // C6001: False positives +#endif + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable : 25000, "FXMVECTOR is 16 bytes") +#pragma prefast(disable : 26495, "Union initialization confuses /analyze") +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + +#include "DirectXCollision.inl" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +} // namespace DirectX + diff --git a/Extern/dxmath/Inc/DirectXCollision.inl b/Extern/dxmath/Inc/DirectXCollision.inl new file mode 100644 index 000000000..3ebf02cec --- /dev/null +++ b/Extern/dxmath/Inc/DirectXCollision.inl @@ -0,0 +1,4816 @@ +//------------------------------------------------------------------------------------- +// DirectXCollision.inl -- C++ Collision Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +XMGLOBALCONST XMVECTORF32 g_BoxOffset[8] = +{ + { { { -1.0f, -1.0f, 1.0f, 0.0f } } }, + { { { 1.0f, -1.0f, 1.0f, 0.0f } } }, + { { { 1.0f, 1.0f, 1.0f, 0.0f } } }, + { { { -1.0f, 1.0f, 1.0f, 0.0f } } }, + { { { -1.0f, -1.0f, -1.0f, 0.0f } } }, + { { { 1.0f, -1.0f, -1.0f, 0.0f } } }, + { { { 1.0f, 1.0f, -1.0f, 0.0f } } }, + { { { -1.0f, 1.0f, -1.0f, 0.0f } } }, +}; + +XMGLOBALCONST XMVECTORF32 g_RayEpsilon = { { { 1e-20f, 1e-20f, 1e-20f, 1e-20f } } }; +XMGLOBALCONST XMVECTORF32 g_RayNegEpsilon = { { { -1e-20f, -1e-20f, -1e-20f, -1e-20f } } }; +XMGLOBALCONST XMVECTORF32 g_FltMin = { { { -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX } } }; +XMGLOBALCONST XMVECTORF32 g_FltMax = { { { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX } } }; + +namespace MathInternal +{ + + //----------------------------------------------------------------------------- + // Return true if any of the elements of a 3 vector are equal to 0xffffffff. + // Slightly more efficient than using XMVector3EqualInt. + //----------------------------------------------------------------------------- + inline bool XMVector3AnyTrue(_In_ FXMVECTOR V) noexcept + { + // Duplicate the fourth element from the first element. + XMVECTOR C = XMVectorSwizzle(V); + + return XMComparisonAnyTrue(XMVector4EqualIntR(C, XMVectorTrueInt())); + } + + + //----------------------------------------------------------------------------- + // Return true if all of the elements of a 3 vector are equal to 0xffffffff. + // Slightly more efficient than using XMVector3EqualInt. + //----------------------------------------------------------------------------- + inline bool XMVector3AllTrue(_In_ FXMVECTOR V) noexcept + { + // Duplicate the fourth element from the first element. + XMVECTOR C = XMVectorSwizzle(V); + + return XMComparisonAllTrue(XMVector4EqualIntR(C, XMVectorTrueInt())); + } + +#if defined(_PREFAST_) || !defined(NDEBUG) + + XMGLOBALCONST XMVECTORF32 g_UnitVectorEpsilon = { { { 1.0e-4f, 1.0e-4f, 1.0e-4f, 1.0e-4f } } }; + XMGLOBALCONST XMVECTORF32 g_UnitQuaternionEpsilon = { { { 1.0e-4f, 1.0e-4f, 1.0e-4f, 1.0e-4f } } }; + XMGLOBALCONST XMVECTORF32 g_UnitPlaneEpsilon = { { { 1.0e-4f, 1.0e-4f, 1.0e-4f, 1.0e-4f } } }; + + //----------------------------------------------------------------------------- + // Return true if the vector is a unit vector (length == 1). + //----------------------------------------------------------------------------- + inline bool XMVector3IsUnit(_In_ FXMVECTOR V) noexcept + { + XMVECTOR Difference = XMVectorSubtract(XMVector3Length(V), XMVectorSplatOne()); + return XMVector4Less(XMVectorAbs(Difference), g_UnitVectorEpsilon); + } + + //----------------------------------------------------------------------------- + // Return true if the quaterion is a unit quaternion. + //----------------------------------------------------------------------------- + inline bool XMQuaternionIsUnit(_In_ FXMVECTOR Q) noexcept + { + XMVECTOR Difference = XMVectorSubtract(XMVector4Length(Q), XMVectorSplatOne()); + return XMVector4Less(XMVectorAbs(Difference), g_UnitQuaternionEpsilon); + } + + //----------------------------------------------------------------------------- + // Return true if the plane is a unit plane. + //----------------------------------------------------------------------------- + inline bool XMPlaneIsUnit(_In_ FXMVECTOR Plane) noexcept + { + XMVECTOR Difference = XMVectorSubtract(XMVector3Length(Plane), XMVectorSplatOne()); + return XMVector4Less(XMVectorAbs(Difference), g_UnitPlaneEpsilon); + } + +#endif // _PREFAST_ || !NDEBUG + + //----------------------------------------------------------------------------- + inline XMVECTOR XMPlaneTransform(_In_ FXMVECTOR Plane, _In_ FXMVECTOR Rotation, _In_ FXMVECTOR Translation) noexcept + { + XMVECTOR vNormal = XMVector3Rotate(Plane, Rotation); + XMVECTOR vD = XMVectorSubtract(XMVectorSplatW(Plane), XMVector3Dot(vNormal, Translation)); + + return XMVectorInsert<0, 0, 0, 0, 1>(vNormal, vD); + } + + //----------------------------------------------------------------------------- + // Return the point on the line segement (S1, S2) nearest the point P. + //----------------------------------------------------------------------------- + inline XMVECTOR PointOnLineSegmentNearestPoint(_In_ FXMVECTOR S1, _In_ FXMVECTOR S2, _In_ FXMVECTOR P) noexcept + { + XMVECTOR Dir = XMVectorSubtract(S2, S1); + XMVECTOR Projection = XMVectorSubtract(XMVector3Dot(P, Dir), XMVector3Dot(S1, Dir)); + XMVECTOR LengthSq = XMVector3Dot(Dir, Dir); + + XMVECTOR t = XMVectorMultiply(Projection, XMVectorReciprocal(LengthSq)); + XMVECTOR Point = XMVectorMultiplyAdd(t, Dir, S1); + + // t < 0 + XMVECTOR SelectS1 = XMVectorLess(Projection, XMVectorZero()); + Point = XMVectorSelect(Point, S1, SelectS1); + + // t > 1 + XMVECTOR SelectS2 = XMVectorGreater(Projection, LengthSq); + Point = XMVectorSelect(Point, S2, SelectS2); + + return Point; + } + + //----------------------------------------------------------------------------- + // Test if the point (P) on the plane of the triangle is inside the triangle + // (V0, V1, V2). + //----------------------------------------------------------------------------- + inline XMVECTOR XM_CALLCONV PointOnPlaneInsideTriangle(_In_ FXMVECTOR P, _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ GXMVECTOR V2) noexcept + { + // Compute the triangle normal. + XMVECTOR N = XMVector3Cross(XMVectorSubtract(V2, V0), XMVectorSubtract(V1, V0)); + + // Compute the cross products of the vector from the base of each edge to + // the point with each edge vector. + XMVECTOR C0 = XMVector3Cross(XMVectorSubtract(P, V0), XMVectorSubtract(V1, V0)); + XMVECTOR C1 = XMVector3Cross(XMVectorSubtract(P, V1), XMVectorSubtract(V2, V1)); + XMVECTOR C2 = XMVector3Cross(XMVectorSubtract(P, V2), XMVectorSubtract(V0, V2)); + + // If the cross product points in the same direction as the normal the the + // point is inside the edge (it is zero if is on the edge). + XMVECTOR Zero = XMVectorZero(); + XMVECTOR Inside0 = XMVectorGreaterOrEqual(XMVector3Dot(C0, N), Zero); + XMVECTOR Inside1 = XMVectorGreaterOrEqual(XMVector3Dot(C1, N), Zero); + XMVECTOR Inside2 = XMVectorGreaterOrEqual(XMVector3Dot(C2, N), Zero); + + // If the point inside all of the edges it is inside. + return XMVectorAndInt(XMVectorAndInt(Inside0, Inside1), Inside2); + } + + //----------------------------------------------------------------------------- + inline bool SolveCubic(_In_ float e, _In_ float f, _In_ float g, _Out_ float* t, _Out_ float* u, _Out_ float* v) noexcept + { + float p, q, h, rc, d, theta, costh3, sinth3; + + p = f - e * e / 3.0f; + q = g - e * f / 3.0f + e * e * e * 2.0f / 27.0f; + h = q * q / 4.0f + p * p * p / 27.0f; + + if (h > 0) + { + *t = *u = *v = 0.f; + return false; // only one real root + } + + if ((h == 0) && (q == 0)) // all the same root + { + *t = -e / 3; + *u = -e / 3; + *v = -e / 3; + + return true; + } + + d = sqrtf(q * q / 4.0f - h); + if (d < 0) + rc = -powf(-d, 1.0f / 3.0f); + else + rc = powf(d, 1.0f / 3.0f); + + theta = XMScalarACos(-q / (2.0f * d)); + costh3 = XMScalarCos(theta / 3.0f); + sinth3 = sqrtf(3.0f) * XMScalarSin(theta / 3.0f); + *t = 2.0f * rc * costh3 - e / 3.0f; + *u = -rc * (costh3 + sinth3) - e / 3.0f; + *v = -rc * (costh3 - sinth3) - e / 3.0f; + + return true; + } + + //----------------------------------------------------------------------------- + inline XMVECTOR CalculateEigenVector(_In_ float m11, _In_ float m12, _In_ float m13, + _In_ float m22, _In_ float m23, _In_ float m33, _In_ float e) noexcept + { + float fTmp[3]; + fTmp[0] = m12 * m23 - m13 * (m22 - e); + fTmp[1] = m13 * m12 - m23 * (m11 - e); + fTmp[2] = (m11 - e) * (m22 - e) - m12 * m12; + + XMVECTOR vTmp = XMLoadFloat3(reinterpret_cast(fTmp)); + + if (XMVector3Equal(vTmp, XMVectorZero())) // planar or linear + { + float f1, f2, f3; + + // we only have one equation - find a valid one + if ((m11 - e != 0) || (m12 != 0) || (m13 != 0)) + { + f1 = m11 - e; f2 = m12; f3 = m13; + } + else if ((m12 != 0) || (m22 - e != 0) || (m23 != 0)) + { + f1 = m12; f2 = m22 - e; f3 = m23; + } + else if ((m13 != 0) || (m23 != 0) || (m33 - e != 0)) + { + f1 = m13; f2 = m23; f3 = m33 - e; + } + else + { + // error, we'll just make something up - we have NO context + f1 = 1.0f; f2 = 0.0f; f3 = 0.0f; + } + + if (f1 == 0) + vTmp = XMVectorSetX(vTmp, 0.0f); + else + vTmp = XMVectorSetX(vTmp, 1.0f); + + if (f2 == 0) + vTmp = XMVectorSetY(vTmp, 0.0f); + else + vTmp = XMVectorSetY(vTmp, 1.0f); + + if (f3 == 0) + { + vTmp = XMVectorSetZ(vTmp, 0.0f); + // recalculate y to make equation work + if (m12 != 0) + vTmp = XMVectorSetY(vTmp, -f1 / f2); + } + else + { + vTmp = XMVectorSetZ(vTmp, (f2 - f1) / f3); + } + } + + if (XMVectorGetX(XMVector3LengthSq(vTmp)) > 1e-5f) + { + return XMVector3Normalize(vTmp); + } + else + { + // Multiply by a value large enough to make the vector non-zero. + vTmp = XMVectorScale(vTmp, 1e5f); + return XMVector3Normalize(vTmp); + } + } + + //----------------------------------------------------------------------------- + inline bool CalculateEigenVectors(_In_ float m11, _In_ float m12, _In_ float m13, + _In_ float m22, _In_ float m23, _In_ float m33, + _In_ float e1, _In_ float e2, _In_ float e3, + _Out_ XMVECTOR* pV1, _Out_ XMVECTOR* pV2, _Out_ XMVECTOR* pV3) noexcept + { + *pV1 = DirectX::MathInternal::CalculateEigenVector(m11, m12, m13, m22, m23, m33, e1); + *pV2 = DirectX::MathInternal::CalculateEigenVector(m11, m12, m13, m22, m23, m33, e2); + *pV3 = DirectX::MathInternal::CalculateEigenVector(m11, m12, m13, m22, m23, m33, e3); + + bool v1z = false; + bool v2z = false; + bool v3z = false; + + XMVECTOR Zero = XMVectorZero(); + + if (XMVector3Equal(*pV1, Zero)) + v1z = true; + + if (XMVector3Equal(*pV2, Zero)) + v2z = true; + + if (XMVector3Equal(*pV3, Zero)) + v3z = true; + + bool e12 = (fabsf(XMVectorGetX(XMVector3Dot(*pV1, *pV2))) > 0.1f); // check for non-orthogonal vectors + bool e13 = (fabsf(XMVectorGetX(XMVector3Dot(*pV1, *pV3))) > 0.1f); + bool e23 = (fabsf(XMVectorGetX(XMVector3Dot(*pV2, *pV3))) > 0.1f); + + if ((v1z && v2z && v3z) || (e12 && e13 && e23) || + (e12 && v3z) || (e13 && v2z) || (e23 && v1z)) // all eigenvectors are 0- any basis set + { + *pV1 = g_XMIdentityR0.v; + *pV2 = g_XMIdentityR1.v; + *pV3 = g_XMIdentityR2.v; + return true; + } + + if (v1z && v2z) + { + XMVECTOR vTmp = XMVector3Cross(g_XMIdentityR1, *pV3); + if (XMVectorGetX(XMVector3LengthSq(vTmp)) < 1e-5f) + { + vTmp = XMVector3Cross(g_XMIdentityR0, *pV3); + } + *pV1 = XMVector3Normalize(vTmp); + *pV2 = XMVector3Cross(*pV3, *pV1); + return true; + } + + if (v3z && v1z) + { + XMVECTOR vTmp = XMVector3Cross(g_XMIdentityR1, *pV2); + if (XMVectorGetX(XMVector3LengthSq(vTmp)) < 1e-5f) + { + vTmp = XMVector3Cross(g_XMIdentityR0, *pV2); + } + *pV3 = XMVector3Normalize(vTmp); + *pV1 = XMVector3Cross(*pV2, *pV3); + return true; + } + + if (v2z && v3z) + { + XMVECTOR vTmp = XMVector3Cross(g_XMIdentityR1, *pV1); + if (XMVectorGetX(XMVector3LengthSq(vTmp)) < 1e-5f) + { + vTmp = XMVector3Cross(g_XMIdentityR0, *pV1); + } + *pV2 = XMVector3Normalize(vTmp); + *pV3 = XMVector3Cross(*pV1, *pV2); + return true; + } + + if ((v1z) || e12) + { + *pV1 = XMVector3Cross(*pV2, *pV3); + return true; + } + + if ((v2z) || e23) + { + *pV2 = XMVector3Cross(*pV3, *pV1); + return true; + } + + if ((v3z) || e13) + { + *pV3 = XMVector3Cross(*pV1, *pV2); + return true; + } + + return true; + } + + //----------------------------------------------------------------------------- + inline bool CalculateEigenVectorsFromCovarianceMatrix(_In_ float Cxx, _In_ float Cyy, _In_ float Czz, + _In_ float Cxy, _In_ float Cxz, _In_ float Cyz, + _Out_ XMVECTOR* pV1, _Out_ XMVECTOR* pV2, _Out_ XMVECTOR* pV3) noexcept + { + // Calculate the eigenvalues by solving a cubic equation. + float e = -(Cxx + Cyy + Czz); + float f = Cxx * Cyy + Cyy * Czz + Czz * Cxx - Cxy * Cxy - Cxz * Cxz - Cyz * Cyz; + float g = Cxy * Cxy * Czz + Cxz * Cxz * Cyy + Cyz * Cyz * Cxx - Cxy * Cyz * Cxz * 2.0f - Cxx * Cyy * Czz; + + float ev1, ev2, ev3; + if (!DirectX::MathInternal::SolveCubic(e, f, g, &ev1, &ev2, &ev3)) + { + // set them to arbitrary orthonormal basis set + *pV1 = g_XMIdentityR0.v; + *pV2 = g_XMIdentityR1.v; + *pV3 = g_XMIdentityR2.v; + return false; + } + + return DirectX::MathInternal::CalculateEigenVectors(Cxx, Cxy, Cxz, Cyy, Cyz, Czz, ev1, ev2, ev3, pV1, pV2, pV3); + } + + //----------------------------------------------------------------------------- + inline void XM_CALLCONV FastIntersectTrianglePlane( + FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2, + GXMVECTOR Plane, + XMVECTOR& Outside, XMVECTOR& Inside) noexcept + { + // Plane0 + XMVECTOR Dist0 = XMVector4Dot(V0, Plane); + XMVECTOR Dist1 = XMVector4Dot(V1, Plane); + XMVECTOR Dist2 = XMVector4Dot(V2, Plane); + + XMVECTOR MinDist = XMVectorMin(Dist0, Dist1); + MinDist = XMVectorMin(MinDist, Dist2); + + XMVECTOR MaxDist = XMVectorMax(Dist0, Dist1); + MaxDist = XMVectorMax(MaxDist, Dist2); + + XMVECTOR Zero = XMVectorZero(); + + // Outside the plane? + Outside = XMVectorGreater(MinDist, Zero); + + // Fully inside the plane? + Inside = XMVectorLess(MaxDist, Zero); + } + + //----------------------------------------------------------------------------- + inline void FastIntersectSpherePlane(_In_ FXMVECTOR Center, _In_ FXMVECTOR Radius, _In_ FXMVECTOR Plane, + _Out_ XMVECTOR& Outside, _Out_ XMVECTOR& Inside) noexcept + { + XMVECTOR Dist = XMVector4Dot(Center, Plane); + + // Outside the plane? + Outside = XMVectorGreater(Dist, Radius); + + // Fully inside the plane? + Inside = XMVectorLess(Dist, XMVectorNegate(Radius)); + } + + //----------------------------------------------------------------------------- + inline void FastIntersectAxisAlignedBoxPlane(_In_ FXMVECTOR Center, _In_ FXMVECTOR Extents, _In_ FXMVECTOR Plane, + _Out_ XMVECTOR& Outside, _Out_ XMVECTOR& Inside) noexcept + { + // Compute the distance to the center of the box. + XMVECTOR Dist = XMVector4Dot(Center, Plane); + + // Project the axes of the box onto the normal of the plane. Half the + // length of the projection (sometime called the "radius") is equal to + // h(u) * abs(n dot b(u))) + h(v) * abs(n dot b(v)) + h(w) * abs(n dot b(w)) + // where h(i) are extents of the box, n is the plane normal, and b(i) are the + // axes of the box. In this case b(i) = [(1,0,0), (0,1,0), (0,0,1)]. + XMVECTOR Radius = XMVector3Dot(Extents, XMVectorAbs(Plane)); + + // Outside the plane? + Outside = XMVectorGreater(Dist, Radius); + + // Fully inside the plane? + Inside = XMVectorLess(Dist, XMVectorNegate(Radius)); + } + + //----------------------------------------------------------------------------- + inline void XM_CALLCONV FastIntersectOrientedBoxPlane( + _In_ FXMVECTOR Center, _In_ FXMVECTOR Extents, _In_ FXMVECTOR Axis0, + _In_ GXMVECTOR Axis1, + _In_ HXMVECTOR Axis2, _In_ HXMVECTOR Plane, + _Out_ XMVECTOR& Outside, _Out_ XMVECTOR& Inside) noexcept + { + // Compute the distance to the center of the box. + XMVECTOR Dist = XMVector4Dot(Center, Plane); + + // Project the axes of the box onto the normal of the plane. Half the + // length of the projection (sometime called the "radius") is equal to + // h(u) * abs(n dot b(u))) + h(v) * abs(n dot b(v)) + h(w) * abs(n dot b(w)) + // where h(i) are extents of the box, n is the plane normal, and b(i) are the + // axes of the box. + XMVECTOR Radius = XMVector3Dot(Plane, Axis0); + Radius = XMVectorInsert<0, 0, 1, 0, 0>(Radius, XMVector3Dot(Plane, Axis1)); + Radius = XMVectorInsert<0, 0, 0, 1, 0>(Radius, XMVector3Dot(Plane, Axis2)); + Radius = XMVector3Dot(Extents, XMVectorAbs(Radius)); + + // Outside the plane? + Outside = XMVectorGreater(Dist, Radius); + + // Fully inside the plane? + Inside = XMVectorLess(Dist, XMVectorNegate(Radius)); + } + + //----------------------------------------------------------------------------- + inline void XM_CALLCONV FastIntersectFrustumPlane( + _In_ FXMVECTOR Point0, _In_ FXMVECTOR Point1, _In_ FXMVECTOR Point2, + _In_ GXMVECTOR Point3, + _In_ HXMVECTOR Point4, _In_ HXMVECTOR Point5, + _In_ CXMVECTOR Point6, _In_ CXMVECTOR Point7, _In_ CXMVECTOR Plane, + _Out_ XMVECTOR& Outside, _Out_ XMVECTOR& Inside) noexcept + { + // Find the min/max projection of the frustum onto the plane normal. + XMVECTOR Min, Max, Dist; + + Min = Max = XMVector3Dot(Plane, Point0); + + Dist = XMVector3Dot(Plane, Point1); + Min = XMVectorMin(Min, Dist); + Max = XMVectorMax(Max, Dist); + + Dist = XMVector3Dot(Plane, Point2); + Min = XMVectorMin(Min, Dist); + Max = XMVectorMax(Max, Dist); + + Dist = XMVector3Dot(Plane, Point3); + Min = XMVectorMin(Min, Dist); + Max = XMVectorMax(Max, Dist); + + Dist = XMVector3Dot(Plane, Point4); + Min = XMVectorMin(Min, Dist); + Max = XMVectorMax(Max, Dist); + + Dist = XMVector3Dot(Plane, Point5); + Min = XMVectorMin(Min, Dist); + Max = XMVectorMax(Max, Dist); + + Dist = XMVector3Dot(Plane, Point6); + Min = XMVectorMin(Min, Dist); + Max = XMVectorMax(Max, Dist); + + Dist = XMVector3Dot(Plane, Point7); + Min = XMVectorMin(Min, Dist); + Max = XMVectorMax(Max, Dist); + + XMVECTOR PlaneDist = XMVectorNegate(XMVectorSplatW(Plane)); + + // Outside the plane? + Outside = XMVectorGreater(Min, PlaneDist); + + // Fully inside the plane? + Inside = XMVectorLess(Max, PlaneDist); + } + +} // namespace MathInternal + + +/**************************************************************************** + * + * BoundingSphere + * + ****************************************************************************/ + +//----------------------------------------------------------------------------- +// Transform a sphere by an angle preserving transform. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingSphere::Transform(BoundingSphere& Out, FXMMATRIX M) const noexcept +{ + // Load the center of the sphere. + XMVECTOR vCenter = XMLoadFloat3(&Center); + + // Transform the center of the sphere. + XMVECTOR C = XMVector3Transform(vCenter, M); + + XMVECTOR dX = XMVector3Dot(M.r[0], M.r[0]); + XMVECTOR dY = XMVector3Dot(M.r[1], M.r[1]); + XMVECTOR dZ = XMVector3Dot(M.r[2], M.r[2]); + + XMVECTOR d = XMVectorMax(dX, XMVectorMax(dY, dZ)); + + // Store the center sphere. + XMStoreFloat3(&Out.Center, C); + + // Scale the radius of the pshere. + float Scale = sqrtf(XMVectorGetX(d)); + Out.Radius = Radius * Scale; +} + +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingSphere::Transform(BoundingSphere& Out, float Scale, FXMVECTOR Rotation, FXMVECTOR Translation) const noexcept +{ + // Load the center of the sphere. + XMVECTOR vCenter = XMLoadFloat3(&Center); + + // Transform the center of the sphere. + vCenter = XMVectorAdd(XMVector3Rotate(XMVectorScale(vCenter, Scale), Rotation), Translation); + + // Store the center sphere. + XMStoreFloat3(&Out.Center, vCenter); + + // Scale the radius of the pshere. + Out.Radius = Radius * Scale; +} + + +//----------------------------------------------------------------------------- +// Point in sphere test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingSphere::Contains(FXMVECTOR Point) const noexcept +{ + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&Radius); + + XMVECTOR DistanceSquared = XMVector3LengthSq(XMVectorSubtract(Point, vCenter)); + XMVECTOR RadiusSquared = XMVectorMultiply(vRadius, vRadius); + + return XMVector3LessOrEqual(DistanceSquared, RadiusSquared) ? CONTAINS : DISJOINT; +} + + +//----------------------------------------------------------------------------- +// Triangle in sphere test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingSphere::Contains(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2) const noexcept +{ + if (!Intersects(V0, V1, V2)) + return DISJOINT; + + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&Radius); + XMVECTOR RadiusSquared = XMVectorMultiply(vRadius, vRadius); + + XMVECTOR DistanceSquared = XMVector3LengthSq(XMVectorSubtract(V0, vCenter)); + XMVECTOR Inside = XMVectorLessOrEqual(DistanceSquared, RadiusSquared); + + DistanceSquared = XMVector3LengthSq(XMVectorSubtract(V1, vCenter)); + Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(DistanceSquared, RadiusSquared)); + + DistanceSquared = XMVector3LengthSq(XMVectorSubtract(V2, vCenter)); + Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(DistanceSquared, RadiusSquared)); + + return (XMVector3EqualInt(Inside, XMVectorTrueInt())) ? CONTAINS : INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Sphere in sphere test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingSphere::Contains(const BoundingSphere& sh) const noexcept +{ + XMVECTOR Center1 = XMLoadFloat3(&Center); + float r1 = Radius; + + XMVECTOR Center2 = XMLoadFloat3(&sh.Center); + float r2 = sh.Radius; + + XMVECTOR V = XMVectorSubtract(Center2, Center1); + + XMVECTOR Dist = XMVector3Length(V); + + float d = XMVectorGetX(Dist); + + return (r1 + r2 >= d) ? ((r1 - r2 >= d) ? CONTAINS : INTERSECTS) : DISJOINT; +} + + +//----------------------------------------------------------------------------- +// Axis-aligned box in sphere test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingSphere::Contains(const BoundingBox& box) const noexcept +{ + if (!box.Intersects(*this)) + return DISJOINT; + + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&Radius); + XMVECTOR RadiusSq = XMVectorMultiply(vRadius, vRadius); + + XMVECTOR boxCenter = XMLoadFloat3(&box.Center); + XMVECTOR boxExtents = XMLoadFloat3(&box.Extents); + + XMVECTOR InsideAll = XMVectorTrueInt(); + + XMVECTOR offset = XMVectorSubtract(boxCenter, vCenter); + + for (size_t i = 0; i < BoundingBox::CORNER_COUNT; ++i) + { + XMVECTOR C = XMVectorMultiplyAdd(boxExtents, g_BoxOffset[i], offset); + XMVECTOR d = XMVector3LengthSq(C); + InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(d, RadiusSq)); + } + + return (XMVector3EqualInt(InsideAll, XMVectorTrueInt())) ? CONTAINS : INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Oriented box in sphere test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingSphere::Contains(const BoundingOrientedBox& box) const noexcept +{ + if (!box.Intersects(*this)) + return DISJOINT; + + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&Radius); + XMVECTOR RadiusSq = XMVectorMultiply(vRadius, vRadius); + + XMVECTOR boxCenter = XMLoadFloat3(&box.Center); + XMVECTOR boxExtents = XMLoadFloat3(&box.Extents); + XMVECTOR boxOrientation = XMLoadFloat4(&box.Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(boxOrientation)); + + XMVECTOR InsideAll = XMVectorTrueInt(); + + for (size_t i = 0; i < BoundingOrientedBox::CORNER_COUNT; ++i) + { + XMVECTOR C = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(boxExtents, g_BoxOffset[i]), boxOrientation), boxCenter); + XMVECTOR d = XMVector3LengthSq(XMVectorSubtract(vCenter, C)); + InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(d, RadiusSq)); + } + + return (XMVector3EqualInt(InsideAll, XMVectorTrueInt())) ? CONTAINS : INTERSECTS; + +} + + +//----------------------------------------------------------------------------- +// Frustum in sphere test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingSphere::Contains(const BoundingFrustum& fr) const noexcept +{ + if (!fr.Intersects(*this)) + return DISJOINT; + + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&Radius); + XMVECTOR RadiusSq = XMVectorMultiply(vRadius, vRadius); + + XMVECTOR vOrigin = XMLoadFloat3(&fr.Origin); + XMVECTOR vOrientation = XMLoadFloat4(&fr.Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Build the corners of the frustum. + XMVECTOR vRightTop = XMVectorSet(fr.RightSlope, fr.TopSlope, 1.0f, 0.0f); + XMVECTOR vRightBottom = XMVectorSet(fr.RightSlope, fr.BottomSlope, 1.0f, 0.0f); + XMVECTOR vLeftTop = XMVectorSet(fr.LeftSlope, fr.TopSlope, 1.0f, 0.0f); + XMVECTOR vLeftBottom = XMVectorSet(fr.LeftSlope, fr.BottomSlope, 1.0f, 0.0f); + XMVECTOR vNear = XMVectorReplicatePtr(&fr.Near); + XMVECTOR vFar = XMVectorReplicatePtr(&fr.Far); + + XMVECTOR Corners[BoundingFrustum::CORNER_COUNT]; + Corners[0] = XMVectorMultiply(vRightTop, vNear); + Corners[1] = XMVectorMultiply(vRightBottom, vNear); + Corners[2] = XMVectorMultiply(vLeftTop, vNear); + Corners[3] = XMVectorMultiply(vLeftBottom, vNear); + Corners[4] = XMVectorMultiply(vRightTop, vFar); + Corners[5] = XMVectorMultiply(vRightBottom, vFar); + Corners[6] = XMVectorMultiply(vLeftTop, vFar); + Corners[7] = XMVectorMultiply(vLeftBottom, vFar); + + XMVECTOR InsideAll = XMVectorTrueInt(); + for (size_t i = 0; i < BoundingFrustum::CORNER_COUNT; ++i) + { + XMVECTOR C = XMVectorAdd(XMVector3Rotate(Corners[i], vOrientation), vOrigin); + XMVECTOR d = XMVector3LengthSq(XMVectorSubtract(vCenter, C)); + InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(d, RadiusSq)); + } + + return (XMVector3EqualInt(InsideAll, XMVectorTrueInt())) ? CONTAINS : INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Sphere vs. sphere test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingSphere::Intersects(const BoundingSphere& sh) const noexcept +{ + // Load A. + XMVECTOR vCenterA = XMLoadFloat3(&Center); + XMVECTOR vRadiusA = XMVectorReplicatePtr(&Radius); + + // Load B. + XMVECTOR vCenterB = XMLoadFloat3(&sh.Center); + XMVECTOR vRadiusB = XMVectorReplicatePtr(&sh.Radius); + + // Distance squared between centers. + XMVECTOR Delta = XMVectorSubtract(vCenterB, vCenterA); + XMVECTOR DistanceSquared = XMVector3LengthSq(Delta); + + // Sum of the radii squared. + XMVECTOR RadiusSquared = XMVectorAdd(vRadiusA, vRadiusB); + RadiusSquared = XMVectorMultiply(RadiusSquared, RadiusSquared); + + return XMVector3LessOrEqual(DistanceSquared, RadiusSquared); +} + + +//----------------------------------------------------------------------------- +// Box vs. sphere test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingSphere::Intersects(const BoundingBox& box) const noexcept +{ + return box.Intersects(*this); +} + +_Use_decl_annotations_ +inline bool BoundingSphere::Intersects(const BoundingOrientedBox& box) const noexcept +{ + return box.Intersects(*this); +} + + +//----------------------------------------------------------------------------- +// Frustum vs. sphere test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingSphere::Intersects(const BoundingFrustum& fr) const noexcept +{ + return fr.Intersects(*this); +} + + +//----------------------------------------------------------------------------- +// Triangle vs sphere test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool XM_CALLCONV BoundingSphere::Intersects(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2) const noexcept +{ + // Load the sphere. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&Radius); + + // Compute the plane of the triangle (has to be normalized). + XMVECTOR N = XMVector3Normalize(XMVector3Cross(XMVectorSubtract(V1, V0), XMVectorSubtract(V2, V0))); + + // Assert that the triangle is not degenerate. + assert(!XMVector3Equal(N, XMVectorZero())); + + // Find the nearest feature on the triangle to the sphere. + XMVECTOR Dist = XMVector3Dot(XMVectorSubtract(vCenter, V0), N); + + // If the center of the sphere is farther from the plane of the triangle than + // the radius of the sphere, then there cannot be an intersection. + XMVECTOR NoIntersection = XMVectorLess(Dist, XMVectorNegate(vRadius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Dist, vRadius)); + + // Project the center of the sphere onto the plane of the triangle. + XMVECTOR Point = XMVectorNegativeMultiplySubtract(N, Dist, vCenter); + + // Is it inside all the edges? If so we intersect because the distance + // to the plane is less than the radius. + XMVECTOR Intersection = DirectX::MathInternal::PointOnPlaneInsideTriangle(Point, V0, V1, V2); + + // Find the nearest point on each edge. + XMVECTOR RadiusSq = XMVectorMultiply(vRadius, vRadius); + + // Edge 0,1 + Point = DirectX::MathInternal::PointOnLineSegmentNearestPoint(V0, V1, vCenter); + + // If the distance to the center of the sphere to the point is less than + // the radius of the sphere then it must intersect. + Intersection = XMVectorOrInt(Intersection, XMVectorLessOrEqual(XMVector3LengthSq(XMVectorSubtract(vCenter, Point)), RadiusSq)); + + // Edge 1,2 + Point = DirectX::MathInternal::PointOnLineSegmentNearestPoint(V1, V2, vCenter); + + // If the distance to the center of the sphere to the point is less than + // the radius of the sphere then it must intersect. + Intersection = XMVectorOrInt(Intersection, XMVectorLessOrEqual(XMVector3LengthSq(XMVectorSubtract(vCenter, Point)), RadiusSq)); + + // Edge 2,0 + Point = DirectX::MathInternal::PointOnLineSegmentNearestPoint(V2, V0, vCenter); + + // If the distance to the center of the sphere to the point is less than + // the radius of the sphere then it must intersect. + Intersection = XMVectorOrInt(Intersection, XMVectorLessOrEqual(XMVector3LengthSq(XMVectorSubtract(vCenter, Point)), RadiusSq)); + + return XMVector4EqualInt(XMVectorAndCInt(Intersection, NoIntersection), XMVectorTrueInt()); +} + + +//----------------------------------------------------------------------------- +// Sphere-plane intersection +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline PlaneIntersectionType XM_CALLCONV BoundingSphere::Intersects(FXMVECTOR Plane) const noexcept +{ + assert(DirectX::MathInternal::XMPlaneIsUnit(Plane)); + + // Load the sphere. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&Radius); + + // Set w of the center to one so we can dot4 with a plane. + vCenter = XMVectorInsert<0, 0, 0, 0, 1>(vCenter, XMVectorSplatOne()); + + XMVECTOR Outside, Inside; + DirectX::MathInternal::FastIntersectSpherePlane(vCenter, vRadius, Plane, Outside, Inside); + + // If the sphere is outside any plane it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return FRONT; + + // If the sphere is inside all planes it is inside. + if (XMVector4EqualInt(Inside, XMVectorTrueInt())) + return BACK; + + // The sphere is not inside all planes or outside a plane it intersects. + return INTERSECTING; +} + + +//----------------------------------------------------------------------------- +// Compute the intersection of a ray (Origin, Direction) with a sphere. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool XM_CALLCONV BoundingSphere::Intersects(FXMVECTOR Origin, FXMVECTOR Direction, float& Dist) const noexcept +{ + assert(DirectX::MathInternal::XMVector3IsUnit(Direction)); + + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&Radius); + + // l is the vector from the ray origin to the center of the sphere. + XMVECTOR l = XMVectorSubtract(vCenter, Origin); + + // s is the projection of the l onto the ray direction. + XMVECTOR s = XMVector3Dot(l, Direction); + + XMVECTOR l2 = XMVector3Dot(l, l); + + XMVECTOR r2 = XMVectorMultiply(vRadius, vRadius); + + // m2 is squared distance from the center of the sphere to the projection. + XMVECTOR m2 = XMVectorNegativeMultiplySubtract(s, s, l2); + + XMVECTOR NoIntersection; + + // If the ray origin is outside the sphere and the center of the sphere is + // behind the ray origin there is no intersection. + NoIntersection = XMVectorAndInt(XMVectorLess(s, XMVectorZero()), XMVectorGreater(l2, r2)); + + // If the squared distance from the center of the sphere to the projection + // is greater than the radius squared the ray will miss the sphere. + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(m2, r2)); + + // The ray hits the sphere, compute the nearest intersection point. + XMVECTOR q = XMVectorSqrt(XMVectorSubtract(r2, m2)); + XMVECTOR t1 = XMVectorSubtract(s, q); + XMVECTOR t2 = XMVectorAdd(s, q); + + XMVECTOR OriginInside = XMVectorLessOrEqual(l2, r2); + XMVECTOR t = XMVectorSelect(t1, t2, OriginInside); + + if (XMVector4NotEqualInt(NoIntersection, XMVectorTrueInt())) + { + // Store the x-component to *pDist. + XMStoreFloat(&Dist, t); + return true; + } + + Dist = 0.f; + return false; +} + + +//----------------------------------------------------------------------------- +// Test a sphere vs 6 planes (typically forming a frustum). +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingSphere::ContainedBy( + FXMVECTOR Plane0, FXMVECTOR Plane1, FXMVECTOR Plane2, + GXMVECTOR Plane3, + HXMVECTOR Plane4, HXMVECTOR Plane5) const noexcept +{ + // Load the sphere. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&Radius); + + // Set w of the center to one so we can dot4 with a plane. + vCenter = XMVectorInsert<0, 0, 0, 0, 1>(vCenter, XMVectorSplatOne()); + + XMVECTOR Outside, Inside; + + // Test against each plane. + DirectX::MathInternal::FastIntersectSpherePlane(vCenter, vRadius, Plane0, Outside, Inside); + + XMVECTOR AnyOutside = Outside; + XMVECTOR AllInside = Inside; + + DirectX::MathInternal::FastIntersectSpherePlane(vCenter, vRadius, Plane1, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectSpherePlane(vCenter, vRadius, Plane2, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectSpherePlane(vCenter, vRadius, Plane3, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectSpherePlane(vCenter, vRadius, Plane4, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectSpherePlane(vCenter, vRadius, Plane5, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + // If the sphere is outside any plane it is outside. + if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) + return DISJOINT; + + // If the sphere is inside all planes it is inside. + if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) + return CONTAINS; + + // The sphere is not inside all planes or outside a plane, it may intersect. + return INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Creates a bounding sphere that contains two other bounding spheres +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingSphere::CreateMerged(BoundingSphere& Out, const BoundingSphere& S1, const BoundingSphere& S2) noexcept +{ + XMVECTOR Center1 = XMLoadFloat3(&S1.Center); + float r1 = S1.Radius; + + XMVECTOR Center2 = XMLoadFloat3(&S2.Center); + float r2 = S2.Radius; + + XMVECTOR V = XMVectorSubtract(Center2, Center1); + + XMVECTOR Dist = XMVector3Length(V); + + float d = XMVectorGetX(Dist); + + if (r1 + r2 >= d) + { + if (r1 - r2 >= d) + { + Out = S1; + return; + } + else if (r2 - r1 >= d) + { + Out = S2; + return; + } + } + + XMVECTOR N = XMVectorDivide(V, Dist); + + float t1 = XMMin(-r1, d - r2); + float t2 = XMMax(r1, d + r2); + float t_5 = (t2 - t1) * 0.5f; + + XMVECTOR NCenter = XMVectorAdd(Center1, XMVectorMultiply(N, XMVectorReplicate(t_5 + t1))); + + XMStoreFloat3(&Out.Center, NCenter); + Out.Radius = t_5; +} + + +//----------------------------------------------------------------------------- +// Create sphere enscribing bounding box +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingSphere::CreateFromBoundingBox(BoundingSphere& Out, const BoundingBox& box) noexcept +{ + Out.Center = box.Center; + XMVECTOR vExtents = XMLoadFloat3(&box.Extents); + Out.Radius = XMVectorGetX(XMVector3Length(vExtents)); +} + +_Use_decl_annotations_ +inline void BoundingSphere::CreateFromBoundingBox(BoundingSphere& Out, const BoundingOrientedBox& box) noexcept +{ + // Bounding box orientation is irrelevant because a sphere is rotationally invariant + Out.Center = box.Center; + XMVECTOR vExtents = XMLoadFloat3(&box.Extents); + Out.Radius = XMVectorGetX(XMVector3Length(vExtents)); +} + + +//----------------------------------------------------------------------------- +// Find the approximate smallest enclosing bounding sphere for a set of +// points. Exact computation of the smallest enclosing bounding sphere is +// possible but is slower and requires a more complex algorithm. +// The algorithm is based on Jack Ritter, "An Efficient Bounding Sphere", +// Graphics Gems. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingSphere::CreateFromPoints(BoundingSphere& Out, size_t Count, const XMFLOAT3* pPoints, size_t Stride) noexcept +{ + assert(Count > 0); + assert(pPoints); + + // Find the points with minimum and maximum x, y, and z + XMVECTOR MinX, MaxX, MinY, MaxY, MinZ, MaxZ; + + MinX = MaxX = MinY = MaxY = MinZ = MaxZ = XMLoadFloat3(pPoints); + + for (size_t i = 1; i < Count; ++i) + { + XMVECTOR Point = XMLoadFloat3(reinterpret_cast(reinterpret_cast(pPoints) + i * Stride)); + + float px = XMVectorGetX(Point); + float py = XMVectorGetY(Point); + float pz = XMVectorGetZ(Point); + + if (px < XMVectorGetX(MinX)) + MinX = Point; + + if (px > XMVectorGetX(MaxX)) + MaxX = Point; + + if (py < XMVectorGetY(MinY)) + MinY = Point; + + if (py > XMVectorGetY(MaxY)) + MaxY = Point; + + if (pz < XMVectorGetZ(MinZ)) + MinZ = Point; + + if (pz > XMVectorGetZ(MaxZ)) + MaxZ = Point; + } + + // Use the min/max pair that are farthest apart to form the initial sphere. + XMVECTOR DeltaX = XMVectorSubtract(MaxX, MinX); + XMVECTOR DistX = XMVector3Length(DeltaX); + + XMVECTOR DeltaY = XMVectorSubtract(MaxY, MinY); + XMVECTOR DistY = XMVector3Length(DeltaY); + + XMVECTOR DeltaZ = XMVectorSubtract(MaxZ, MinZ); + XMVECTOR DistZ = XMVector3Length(DeltaZ); + + XMVECTOR vCenter; + XMVECTOR vRadius; + + if (XMVector3Greater(DistX, DistY)) + { + if (XMVector3Greater(DistX, DistZ)) + { + // Use min/max x. + vCenter = XMVectorLerp(MaxX, MinX, 0.5f); + vRadius = XMVectorScale(DistX, 0.5f); + } + else + { + // Use min/max z. + vCenter = XMVectorLerp(MaxZ, MinZ, 0.5f); + vRadius = XMVectorScale(DistZ, 0.5f); + } + } + else // Y >= X + { + if (XMVector3Greater(DistY, DistZ)) + { + // Use min/max y. + vCenter = XMVectorLerp(MaxY, MinY, 0.5f); + vRadius = XMVectorScale(DistY, 0.5f); + } + else + { + // Use min/max z. + vCenter = XMVectorLerp(MaxZ, MinZ, 0.5f); + vRadius = XMVectorScale(DistZ, 0.5f); + } + } + + // Add any points not inside the sphere. + for (size_t i = 0; i < Count; ++i) + { + XMVECTOR Point = XMLoadFloat3(reinterpret_cast(reinterpret_cast(pPoints) + i * Stride)); + + XMVECTOR Delta = XMVectorSubtract(Point, vCenter); + + XMVECTOR Dist = XMVector3Length(Delta); + + if (XMVector3Greater(Dist, vRadius)) + { + // Adjust sphere to include the new point. + vRadius = XMVectorScale(XMVectorAdd(vRadius, Dist), 0.5f); + vCenter = XMVectorAdd(vCenter, XMVectorMultiply(XMVectorSubtract(XMVectorReplicate(1.0f), XMVectorDivide(vRadius, Dist)), Delta)); + } + } + + XMStoreFloat3(&Out.Center, vCenter); + XMStoreFloat(&Out.Radius, vRadius); +} + + +//----------------------------------------------------------------------------- +// Create sphere containing frustum +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingSphere::CreateFromFrustum(BoundingSphere& Out, const BoundingFrustum& fr) noexcept +{ + XMFLOAT3 Corners[BoundingFrustum::CORNER_COUNT]; + fr.GetCorners(Corners); + CreateFromPoints(Out, BoundingFrustum::CORNER_COUNT, Corners, sizeof(XMFLOAT3)); +} + + +/**************************************************************************** + * + * BoundingBox + * + ****************************************************************************/ + +//----------------------------------------------------------------------------- +// Transform an axis aligned box by an angle preserving transform. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingBox::Transform(BoundingBox& Out, FXMMATRIX M) const noexcept +{ + // Load center and extents. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + // Compute and transform the corners and find new min/max bounds. + XMVECTOR Corner = XMVectorMultiplyAdd(vExtents, g_BoxOffset[0], vCenter); + Corner = XMVector3Transform(Corner, M); + + XMVECTOR Min, Max; + Min = Max = Corner; + + for (size_t i = 1; i < CORNER_COUNT; ++i) + { + Corner = XMVectorMultiplyAdd(vExtents, g_BoxOffset[i], vCenter); + Corner = XMVector3Transform(Corner, M); + + Min = XMVectorMin(Min, Corner); + Max = XMVectorMax(Max, Corner); + } + + // Store center and extents. + XMStoreFloat3(&Out.Center, XMVectorScale(XMVectorAdd(Min, Max), 0.5f)); + XMStoreFloat3(&Out.Extents, XMVectorScale(XMVectorSubtract(Max, Min), 0.5f)); +} + +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingBox::Transform(BoundingBox& Out, float Scale, FXMVECTOR Rotation, FXMVECTOR Translation) const noexcept +{ + assert(DirectX::MathInternal::XMQuaternionIsUnit(Rotation)); + + // Load center and extents. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + XMVECTOR VectorScale = XMVectorReplicate(Scale); + + // Compute and transform the corners and find new min/max bounds. + XMVECTOR Corner = XMVectorMultiplyAdd(vExtents, g_BoxOffset[0], vCenter); + Corner = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(Corner, VectorScale), Rotation), Translation); + + XMVECTOR Min, Max; + Min = Max = Corner; + + for (size_t i = 1; i < CORNER_COUNT; ++i) + { + Corner = XMVectorMultiplyAdd(vExtents, g_BoxOffset[i], vCenter); + Corner = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(Corner, VectorScale), Rotation), Translation); + + Min = XMVectorMin(Min, Corner); + Max = XMVectorMax(Max, Corner); + } + + // Store center and extents. + XMStoreFloat3(&Out.Center, XMVectorScale(XMVectorAdd(Min, Max), 0.5f)); + XMStoreFloat3(&Out.Extents, XMVectorScale(XMVectorSubtract(Max, Min), 0.5f)); +} + + +//----------------------------------------------------------------------------- +// Get the corner points of the box +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingBox::GetCorners(XMFLOAT3* Corners) const noexcept +{ + assert(Corners != nullptr); + + // Load the box + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + for (size_t i = 0; i < CORNER_COUNT; ++i) + { + XMVECTOR C = XMVectorMultiplyAdd(vExtents, g_BoxOffset[i], vCenter); + XMStoreFloat3(&Corners[i], C); + } +} + + +//----------------------------------------------------------------------------- +// Point in axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingBox::Contains(FXMVECTOR Point) const noexcept +{ + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + return XMVector3InBounds(XMVectorSubtract(Point, vCenter), vExtents) ? CONTAINS : DISJOINT; +} + + +//----------------------------------------------------------------------------- +// Triangle in axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingBox::Contains(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2) const noexcept +{ + if (!Intersects(V0, V1, V2)) + return DISJOINT; + + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + XMVECTOR d = XMVectorAbs(XMVectorSubtract(V0, vCenter)); + XMVECTOR Inside = XMVectorLessOrEqual(d, vExtents); + + d = XMVectorAbs(XMVectorSubtract(V1, vCenter)); + Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(d, vExtents)); + + d = XMVectorAbs(XMVectorSubtract(V2, vCenter)); + Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(d, vExtents)); + + return (XMVector3EqualInt(Inside, XMVectorTrueInt())) ? CONTAINS : INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Sphere in axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingBox::Contains(const BoundingSphere& sh) const noexcept +{ + XMVECTOR SphereCenter = XMLoadFloat3(&sh.Center); + XMVECTOR SphereRadius = XMVectorReplicatePtr(&sh.Radius); + + XMVECTOR BoxCenter = XMLoadFloat3(&Center); + XMVECTOR BoxExtents = XMLoadFloat3(&Extents); + + XMVECTOR BoxMin = XMVectorSubtract(BoxCenter, BoxExtents); + XMVECTOR BoxMax = XMVectorAdd(BoxCenter, BoxExtents); + + // Find the distance to the nearest point on the box. + // for each i in (x, y, z) + // if (SphereCenter(i) < BoxMin(i)) d2 += (SphereCenter(i) - BoxMin(i)) ^ 2 + // else if (SphereCenter(i) > BoxMax(i)) d2 += (SphereCenter(i) - BoxMax(i)) ^ 2 + + XMVECTOR d = XMVectorZero(); + + // Compute d for each dimension. + XMVECTOR LessThanMin = XMVectorLess(SphereCenter, BoxMin); + XMVECTOR GreaterThanMax = XMVectorGreater(SphereCenter, BoxMax); + + XMVECTOR MinDelta = XMVectorSubtract(SphereCenter, BoxMin); + XMVECTOR MaxDelta = XMVectorSubtract(SphereCenter, BoxMax); + + // Choose value for each dimension based on the comparison. + d = XMVectorSelect(d, MinDelta, LessThanMin); + d = XMVectorSelect(d, MaxDelta, GreaterThanMax); + + // Use a dot-product to square them and sum them together. + XMVECTOR d2 = XMVector3Dot(d, d); + + if (XMVector3Greater(d2, XMVectorMultiply(SphereRadius, SphereRadius))) + return DISJOINT; + + XMVECTOR InsideAll = XMVectorLessOrEqual(XMVectorAdd(BoxMin, SphereRadius), SphereCenter); + InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(SphereCenter, XMVectorSubtract(BoxMax, SphereRadius))); + InsideAll = XMVectorAndInt(InsideAll, XMVectorGreater(XMVectorSubtract(BoxMax, BoxMin), SphereRadius)); + + return (XMVector3EqualInt(InsideAll, XMVectorTrueInt())) ? CONTAINS : INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Axis-aligned box in axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingBox::Contains(const BoundingBox& box) const noexcept +{ + XMVECTOR CenterA = XMLoadFloat3(&Center); + XMVECTOR ExtentsA = XMLoadFloat3(&Extents); + + XMVECTOR CenterB = XMLoadFloat3(&box.Center); + XMVECTOR ExtentsB = XMLoadFloat3(&box.Extents); + + XMVECTOR MinA = XMVectorSubtract(CenterA, ExtentsA); + XMVECTOR MaxA = XMVectorAdd(CenterA, ExtentsA); + + XMVECTOR MinB = XMVectorSubtract(CenterB, ExtentsB); + XMVECTOR MaxB = XMVectorAdd(CenterB, ExtentsB); + + // for each i in (x, y, z) if a_min(i) > b_max(i) or b_min(i) > a_max(i) then return false + XMVECTOR Disjoint = XMVectorOrInt(XMVectorGreater(MinA, MaxB), XMVectorGreater(MinB, MaxA)); + + if (DirectX::MathInternal::XMVector3AnyTrue(Disjoint)) + return DISJOINT; + + // for each i in (x, y, z) if a_min(i) <= b_min(i) and b_max(i) <= a_max(i) then A contains B + XMVECTOR Inside = XMVectorAndInt(XMVectorLessOrEqual(MinA, MinB), XMVectorLessOrEqual(MaxB, MaxA)); + + return DirectX::MathInternal::XMVector3AllTrue(Inside) ? CONTAINS : INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Oriented box in axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingBox::Contains(const BoundingOrientedBox& box) const noexcept +{ + if (!box.Intersects(*this)) + return DISJOINT; + + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + // Subtract off the AABB center to remove a subtract below + XMVECTOR oCenter = XMVectorSubtract(XMLoadFloat3(&box.Center), vCenter); + + XMVECTOR oExtents = XMLoadFloat3(&box.Extents); + XMVECTOR oOrientation = XMLoadFloat4(&box.Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(oOrientation)); + + XMVECTOR Inside = XMVectorTrueInt(); + + for (size_t i = 0; i < BoundingOrientedBox::CORNER_COUNT; ++i) + { + XMVECTOR C = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(oExtents, g_BoxOffset[i]), oOrientation), oCenter); + XMVECTOR d = XMVectorAbs(C); + Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(d, vExtents)); + } + + return (XMVector3EqualInt(Inside, XMVectorTrueInt())) ? CONTAINS : INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Frustum in axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingBox::Contains(const BoundingFrustum& fr) const noexcept +{ + if (!fr.Intersects(*this)) + return DISJOINT; + + XMFLOAT3 Corners[BoundingFrustum::CORNER_COUNT]; + fr.GetCorners(Corners); + + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + XMVECTOR Inside = XMVectorTrueInt(); + + for (size_t i = 0; i < BoundingFrustum::CORNER_COUNT; ++i) + { + XMVECTOR Point = XMLoadFloat3(&Corners[i]); + XMVECTOR d = XMVectorAbs(XMVectorSubtract(Point, vCenter)); + Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(d, vExtents)); + } + + return (XMVector3EqualInt(Inside, XMVectorTrueInt())) ? CONTAINS : INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Sphere vs axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingBox::Intersects(const BoundingSphere& sh) const noexcept +{ + XMVECTOR SphereCenter = XMLoadFloat3(&sh.Center); + XMVECTOR SphereRadius = XMVectorReplicatePtr(&sh.Radius); + + XMVECTOR BoxCenter = XMLoadFloat3(&Center); + XMVECTOR BoxExtents = XMLoadFloat3(&Extents); + + XMVECTOR BoxMin = XMVectorSubtract(BoxCenter, BoxExtents); + XMVECTOR BoxMax = XMVectorAdd(BoxCenter, BoxExtents); + + // Find the distance to the nearest point on the box. + // for each i in (x, y, z) + // if (SphereCenter(i) < BoxMin(i)) d2 += (SphereCenter(i) - BoxMin(i)) ^ 2 + // else if (SphereCenter(i) > BoxMax(i)) d2 += (SphereCenter(i) - BoxMax(i)) ^ 2 + + XMVECTOR d = XMVectorZero(); + + // Compute d for each dimension. + XMVECTOR LessThanMin = XMVectorLess(SphereCenter, BoxMin); + XMVECTOR GreaterThanMax = XMVectorGreater(SphereCenter, BoxMax); + + XMVECTOR MinDelta = XMVectorSubtract(SphereCenter, BoxMin); + XMVECTOR MaxDelta = XMVectorSubtract(SphereCenter, BoxMax); + + // Choose value for each dimension based on the comparison. + d = XMVectorSelect(d, MinDelta, LessThanMin); + d = XMVectorSelect(d, MaxDelta, GreaterThanMax); + + // Use a dot-product to square them and sum them together. + XMVECTOR d2 = XMVector3Dot(d, d); + + return XMVector3LessOrEqual(d2, XMVectorMultiply(SphereRadius, SphereRadius)); +} + + +//----------------------------------------------------------------------------- +// Axis-aligned box vs. axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingBox::Intersects(const BoundingBox& box) const noexcept +{ + XMVECTOR CenterA = XMLoadFloat3(&Center); + XMVECTOR ExtentsA = XMLoadFloat3(&Extents); + + XMVECTOR CenterB = XMLoadFloat3(&box.Center); + XMVECTOR ExtentsB = XMLoadFloat3(&box.Extents); + + XMVECTOR MinA = XMVectorSubtract(CenterA, ExtentsA); + XMVECTOR MaxA = XMVectorAdd(CenterA, ExtentsA); + + XMVECTOR MinB = XMVectorSubtract(CenterB, ExtentsB); + XMVECTOR MaxB = XMVectorAdd(CenterB, ExtentsB); + + // for each i in (x, y, z) if a_min(i) > b_max(i) or b_min(i) > a_max(i) then return false + XMVECTOR Disjoint = XMVectorOrInt(XMVectorGreater(MinA, MaxB), XMVectorGreater(MinB, MaxA)); + + return !DirectX::MathInternal::XMVector3AnyTrue(Disjoint); +} + + +//----------------------------------------------------------------------------- +// Oriented box vs. axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingBox::Intersects(const BoundingOrientedBox& box) const noexcept +{ + return box.Intersects(*this); +} + + +//----------------------------------------------------------------------------- +// Frustum vs. axis-aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingBox::Intersects(const BoundingFrustum& fr) const noexcept +{ + return fr.Intersects(*this); +} + + +//----------------------------------------------------------------------------- +// Triangle vs. axis aligned box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool XM_CALLCONV BoundingBox::Intersects(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2) const noexcept +{ + XMVECTOR Zero = XMVectorZero(); + + // Load the box. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + XMVECTOR BoxMin = XMVectorSubtract(vCenter, vExtents); + XMVECTOR BoxMax = XMVectorAdd(vCenter, vExtents); + + // Test the axes of the box (in effect test the AAB against the minimal AAB + // around the triangle). + XMVECTOR TriMin = XMVectorMin(XMVectorMin(V0, V1), V2); + XMVECTOR TriMax = XMVectorMax(XMVectorMax(V0, V1), V2); + + // for each i in (x, y, z) if a_min(i) > b_max(i) or b_min(i) > a_max(i) then disjoint + XMVECTOR Disjoint = XMVectorOrInt(XMVectorGreater(TriMin, BoxMax), XMVectorGreater(BoxMin, TriMax)); + if (DirectX::MathInternal::XMVector3AnyTrue(Disjoint)) + return false; + + // Test the plane of the triangle. + XMVECTOR Normal = XMVector3Cross(XMVectorSubtract(V1, V0), XMVectorSubtract(V2, V0)); + XMVECTOR Dist = XMVector3Dot(Normal, V0); + + // Assert that the triangle is not degenerate. + assert(!XMVector3Equal(Normal, Zero)); + + // for each i in (x, y, z) if n(i) >= 0 then v_min(i)=b_min(i), v_max(i)=b_max(i) + // else v_min(i)=b_max(i), v_max(i)=b_min(i) + XMVECTOR NormalSelect = XMVectorGreater(Normal, Zero); + XMVECTOR V_Min = XMVectorSelect(BoxMax, BoxMin, NormalSelect); + XMVECTOR V_Max = XMVectorSelect(BoxMin, BoxMax, NormalSelect); + + // if n dot v_min + d > 0 || n dot v_max + d < 0 then disjoint + XMVECTOR MinDist = XMVector3Dot(V_Min, Normal); + XMVECTOR MaxDist = XMVector3Dot(V_Max, Normal); + + XMVECTOR NoIntersection = XMVectorGreater(MinDist, Dist); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(MaxDist, Dist)); + + // Move the box center to zero to simplify the following tests. + XMVECTOR TV0 = XMVectorSubtract(V0, vCenter); + XMVECTOR TV1 = XMVectorSubtract(V1, vCenter); + XMVECTOR TV2 = XMVectorSubtract(V2, vCenter); + + // Test the edge/edge axes (3*3). + XMVECTOR e0 = XMVectorSubtract(TV1, TV0); + XMVECTOR e1 = XMVectorSubtract(TV2, TV1); + XMVECTOR e2 = XMVectorSubtract(TV0, TV2); + + // Make w zero. + e0 = XMVectorInsert<0, 0, 0, 0, 1>(e0, Zero); + e1 = XMVectorInsert<0, 0, 0, 0, 1>(e1, Zero); + e2 = XMVectorInsert<0, 0, 0, 0, 1>(e2, Zero); + + XMVECTOR Axis; + XMVECTOR p0, p1, p2; + XMVECTOR Min, Max; + XMVECTOR Radius; + + // Axis == (1,0,0) x e0 = (0, -e0.z, e0.y) + Axis = XMVectorPermute(e0, XMVectorNegate(e0)); + p0 = XMVector3Dot(TV0, Axis); + // p1 = XMVector3Dot( V1, Axis ); // p1 = p0; + p2 = XMVector3Dot(TV2, Axis); + Min = XMVectorMin(p0, p2); + Max = XMVectorMax(p0, p2); + Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius))); + + // Axis == (1,0,0) x e1 = (0, -e1.z, e1.y) + Axis = XMVectorPermute(e1, XMVectorNegate(e1)); + p0 = XMVector3Dot(TV0, Axis); + p1 = XMVector3Dot(TV1, Axis); + // p2 = XMVector3Dot( V2, Axis ); // p2 = p1; + Min = XMVectorMin(p0, p1); + Max = XMVectorMax(p0, p1); + Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius))); + + // Axis == (1,0,0) x e2 = (0, -e2.z, e2.y) + Axis = XMVectorPermute(e2, XMVectorNegate(e2)); + p0 = XMVector3Dot(TV0, Axis); + p1 = XMVector3Dot(TV1, Axis); + // p2 = XMVector3Dot( V2, Axis ); // p2 = p0; + Min = XMVectorMin(p0, p1); + Max = XMVectorMax(p0, p1); + Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius))); + + // Axis == (0,1,0) x e0 = (e0.z, 0, -e0.x) + Axis = XMVectorPermute(e0, XMVectorNegate(e0)); + p0 = XMVector3Dot(TV0, Axis); + // p1 = XMVector3Dot( V1, Axis ); // p1 = p0; + p2 = XMVector3Dot(TV2, Axis); + Min = XMVectorMin(p0, p2); + Max = XMVectorMax(p0, p2); + Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius))); + + // Axis == (0,1,0) x e1 = (e1.z, 0, -e1.x) + Axis = XMVectorPermute(e1, XMVectorNegate(e1)); + p0 = XMVector3Dot(TV0, Axis); + p1 = XMVector3Dot(TV1, Axis); + // p2 = XMVector3Dot( V2, Axis ); // p2 = p1; + Min = XMVectorMin(p0, p1); + Max = XMVectorMax(p0, p1); + Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius))); + + // Axis == (0,0,1) x e2 = (e2.z, 0, -e2.x) + Axis = XMVectorPermute(e2, XMVectorNegate(e2)); + p0 = XMVector3Dot(TV0, Axis); + p1 = XMVector3Dot(TV1, Axis); + // p2 = XMVector3Dot( V2, Axis ); // p2 = p0; + Min = XMVectorMin(p0, p1); + Max = XMVectorMax(p0, p1); + Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius))); + + // Axis == (0,0,1) x e0 = (-e0.y, e0.x, 0) + Axis = XMVectorPermute(e0, XMVectorNegate(e0)); + p0 = XMVector3Dot(TV0, Axis); + // p1 = XMVector3Dot( V1, Axis ); // p1 = p0; + p2 = XMVector3Dot(TV2, Axis); + Min = XMVectorMin(p0, p2); + Max = XMVectorMax(p0, p2); + Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius))); + + // Axis == (0,0,1) x e1 = (-e1.y, e1.x, 0) + Axis = XMVectorPermute(e1, XMVectorNegate(e1)); + p0 = XMVector3Dot(TV0, Axis); + p1 = XMVector3Dot(TV1, Axis); + // p2 = XMVector3Dot( V2, Axis ); // p2 = p1; + Min = XMVectorMin(p0, p1); + Max = XMVectorMax(p0, p1); + Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius))); + + // Axis == (0,0,1) x e2 = (-e2.y, e2.x, 0) + Axis = XMVectorPermute(e2, XMVectorNegate(e2)); + p0 = XMVector3Dot(TV0, Axis); + p1 = XMVector3Dot(TV1, Axis); + // p2 = XMVector3Dot( V2, Axis ); // p2 = p0; + Min = XMVectorMin(p0, p1); + Max = XMVectorMax(p0, p1); + Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius))); + + return XMVector4NotEqualInt(NoIntersection, XMVectorTrueInt()); +} + + +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline PlaneIntersectionType XM_CALLCONV BoundingBox::Intersects(FXMVECTOR Plane) const noexcept +{ + assert(DirectX::MathInternal::XMPlaneIsUnit(Plane)); + + // Load the box. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + // Set w of the center to one so we can dot4 with a plane. + vCenter = XMVectorInsert<0, 0, 0, 0, 1>(vCenter, XMVectorSplatOne()); + + XMVECTOR Outside, Inside; + DirectX::MathInternal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane, Outside, Inside); + + // If the box is outside any plane it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return FRONT; + + // If the box is inside all planes it is inside. + if (XMVector4EqualInt(Inside, XMVectorTrueInt())) + return BACK; + + // The box is not inside all planes or outside a plane it intersects. + return INTERSECTING; +} + + +//----------------------------------------------------------------------------- +// Compute the intersection of a ray (Origin, Direction) with an axis aligned +// box using the slabs method. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool XM_CALLCONV BoundingBox::Intersects(FXMVECTOR Origin, FXMVECTOR Direction, float& Dist) const noexcept +{ + assert(DirectX::MathInternal::XMVector3IsUnit(Direction)); + + // Load the box. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + // Adjust ray origin to be relative to center of the box. + XMVECTOR TOrigin = XMVectorSubtract(vCenter, Origin); + + // Compute the dot product againt each axis of the box. + // Since the axii are (1,0,0), (0,1,0), (0,0,1) no computation is necessary. + XMVECTOR AxisDotOrigin = TOrigin; + XMVECTOR AxisDotDirection = Direction; + + // if (fabs(AxisDotDirection) <= Epsilon) the ray is nearly parallel to the slab. + XMVECTOR IsParallel = XMVectorLessOrEqual(XMVectorAbs(AxisDotDirection), g_RayEpsilon); + + // Test against all three axii simultaneously. + XMVECTOR InverseAxisDotDirection = XMVectorReciprocal(AxisDotDirection); + XMVECTOR t1 = XMVectorMultiply(XMVectorSubtract(AxisDotOrigin, vExtents), InverseAxisDotDirection); + XMVECTOR t2 = XMVectorMultiply(XMVectorAdd(AxisDotOrigin, vExtents), InverseAxisDotDirection); + + // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't + // use the results from any directions parallel to the slab. + XMVECTOR t_min = XMVectorSelect(XMVectorMin(t1, t2), g_FltMin, IsParallel); + XMVECTOR t_max = XMVectorSelect(XMVectorMax(t1, t2), g_FltMax, IsParallel); + + // t_min.x = maximum( t_min.x, t_min.y, t_min.z ); + // t_max.x = minimum( t_max.x, t_max.y, t_max.z ); + t_min = XMVectorMax(t_min, XMVectorSplatY(t_min)); // x = max(x,y) + t_min = XMVectorMax(t_min, XMVectorSplatZ(t_min)); // x = max(max(x,y),z) + t_max = XMVectorMin(t_max, XMVectorSplatY(t_max)); // x = min(x,y) + t_max = XMVectorMin(t_max, XMVectorSplatZ(t_max)); // x = min(min(x,y),z) + + // if ( t_min > t_max ) return false; + XMVECTOR NoIntersection = XMVectorGreater(XMVectorSplatX(t_min), XMVectorSplatX(t_max)); + + // if ( t_max < 0.0f ) return false; + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(XMVectorSplatX(t_max), XMVectorZero())); + + // if (IsParallel && (-Extents > AxisDotOrigin || Extents < AxisDotOrigin)) return false; + XMVECTOR ParallelOverlap = XMVectorInBounds(AxisDotOrigin, vExtents); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorAndCInt(IsParallel, ParallelOverlap)); + + if (!DirectX::MathInternal::XMVector3AnyTrue(NoIntersection)) + { + // Store the x-component to *pDist + XMStoreFloat(&Dist, t_min); + return true; + } + + Dist = 0.f; + return false; +} + + +//----------------------------------------------------------------------------- +// Test an axis alinged box vs 6 planes (typically forming a frustum). +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingBox::ContainedBy( + FXMVECTOR Plane0, FXMVECTOR Plane1, FXMVECTOR Plane2, + GXMVECTOR Plane3, + HXMVECTOR Plane4, HXMVECTOR Plane5) const noexcept +{ + // Load the box. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + + // Set w of the center to one so we can dot4 with a plane. + vCenter = XMVectorInsert<0, 0, 0, 0, 1>(vCenter, XMVectorSplatOne()); + + XMVECTOR Outside, Inside; + + // Test against each plane. + DirectX::MathInternal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane0, Outside, Inside); + + XMVECTOR AnyOutside = Outside; + XMVECTOR AllInside = Inside; + + DirectX::MathInternal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane1, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane2, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane3, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane4, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane5, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + // If the box is outside any plane it is outside. + if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) + return DISJOINT; + + // If the box is inside all planes it is inside. + if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) + return CONTAINS; + + // The box is not inside all planes or outside a plane, it may intersect. + return INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Create axis-aligned box that contains two other bounding boxes +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingBox::CreateMerged(BoundingBox& Out, const BoundingBox& b1, const BoundingBox& b2) noexcept +{ + XMVECTOR b1Center = XMLoadFloat3(&b1.Center); + XMVECTOR b1Extents = XMLoadFloat3(&b1.Extents); + + XMVECTOR b2Center = XMLoadFloat3(&b2.Center); + XMVECTOR b2Extents = XMLoadFloat3(&b2.Extents); + + XMVECTOR Min = XMVectorSubtract(b1Center, b1Extents); + Min = XMVectorMin(Min, XMVectorSubtract(b2Center, b2Extents)); + + XMVECTOR Max = XMVectorAdd(b1Center, b1Extents); + Max = XMVectorMax(Max, XMVectorAdd(b2Center, b2Extents)); + + assert(XMVector3LessOrEqual(Min, Max)); + + XMStoreFloat3(&Out.Center, XMVectorScale(XMVectorAdd(Min, Max), 0.5f)); + XMStoreFloat3(&Out.Extents, XMVectorScale(XMVectorSubtract(Max, Min), 0.5f)); +} + + +//----------------------------------------------------------------------------- +// Create axis-aligned box that contains a bounding sphere +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingBox::CreateFromSphere(BoundingBox& Out, const BoundingSphere& sh) noexcept +{ + XMVECTOR spCenter = XMLoadFloat3(&sh.Center); + XMVECTOR shRadius = XMVectorReplicatePtr(&sh.Radius); + + XMVECTOR Min = XMVectorSubtract(spCenter, shRadius); + XMVECTOR Max = XMVectorAdd(spCenter, shRadius); + + assert(XMVector3LessOrEqual(Min, Max)); + + XMStoreFloat3(&Out.Center, XMVectorScale(XMVectorAdd(Min, Max), 0.5f)); + XMStoreFloat3(&Out.Extents, XMVectorScale(XMVectorSubtract(Max, Min), 0.5f)); +} + + +//----------------------------------------------------------------------------- +// Create axis-aligned box from min/max points +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingBox::CreateFromPoints(BoundingBox& Out, FXMVECTOR pt1, FXMVECTOR pt2) noexcept +{ + XMVECTOR Min = XMVectorMin(pt1, pt2); + XMVECTOR Max = XMVectorMax(pt1, pt2); + + // Store center and extents. + XMStoreFloat3(&Out.Center, XMVectorScale(XMVectorAdd(Min, Max), 0.5f)); + XMStoreFloat3(&Out.Extents, XMVectorScale(XMVectorSubtract(Max, Min), 0.5f)); +} + + +//----------------------------------------------------------------------------- +// Find the minimum axis aligned bounding box containing a set of points. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingBox::CreateFromPoints(BoundingBox& Out, size_t Count, const XMFLOAT3* pPoints, size_t Stride) noexcept +{ + assert(Count > 0); + assert(pPoints); + + // Find the minimum and maximum x, y, and z + XMVECTOR vMin, vMax; + + vMin = vMax = XMLoadFloat3(pPoints); + + for (size_t i = 1; i < Count; ++i) + { + XMVECTOR Point = XMLoadFloat3(reinterpret_cast(reinterpret_cast(pPoints) + i * Stride)); + + vMin = XMVectorMin(vMin, Point); + vMax = XMVectorMax(vMax, Point); + } + + // Store center and extents. + XMStoreFloat3(&Out.Center, XMVectorScale(XMVectorAdd(vMin, vMax), 0.5f)); + XMStoreFloat3(&Out.Extents, XMVectorScale(XMVectorSubtract(vMax, vMin), 0.5f)); +} + + +/**************************************************************************** + * + * BoundingOrientedBox + * + ****************************************************************************/ + +//----------------------------------------------------------------------------- +// Transform an oriented box by an angle preserving transform. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingOrientedBox::Transform(BoundingOrientedBox& Out, FXMMATRIX M) const noexcept +{ + // Load the box. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Composite the box rotation and the transform rotation. + XMMATRIX nM; + nM.r[0] = XMVector3Normalize(M.r[0]); + nM.r[1] = XMVector3Normalize(M.r[1]); + nM.r[2] = XMVector3Normalize(M.r[2]); + nM.r[3] = g_XMIdentityR3; + XMVECTOR Rotation = XMQuaternionRotationMatrix(nM); + vOrientation = XMQuaternionMultiply(vOrientation, Rotation); + + // Transform the center. + vCenter = XMVector3Transform(vCenter, M); + + // Scale the box extents. + XMVECTOR dX = XMVector3Length(M.r[0]); + XMVECTOR dY = XMVector3Length(M.r[1]); + XMVECTOR dZ = XMVector3Length(M.r[2]); + + XMVECTOR VectorScale = XMVectorSelect(dY, dX, g_XMSelect1000); + VectorScale = XMVectorSelect(dZ, VectorScale, g_XMSelect1100); + vExtents = XMVectorMultiply(vExtents, VectorScale); + + // Store the box. + XMStoreFloat3(&Out.Center, vCenter); + XMStoreFloat3(&Out.Extents, vExtents); + XMStoreFloat4(&Out.Orientation, vOrientation); +} + +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingOrientedBox::Transform(BoundingOrientedBox& Out, float Scale, FXMVECTOR Rotation, FXMVECTOR Translation) const noexcept +{ + assert(DirectX::MathInternal::XMQuaternionIsUnit(Rotation)); + + // Load the box. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Composite the box rotation and the transform rotation. + vOrientation = XMQuaternionMultiply(vOrientation, Rotation); + + // Transform the center. + XMVECTOR VectorScale = XMVectorReplicate(Scale); + vCenter = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(vCenter, VectorScale), Rotation), Translation); + + // Scale the box extents. + vExtents = XMVectorMultiply(vExtents, VectorScale); + + // Store the box. + XMStoreFloat3(&Out.Center, vCenter); + XMStoreFloat3(&Out.Extents, vExtents); + XMStoreFloat4(&Out.Orientation, vOrientation); +} + + +//----------------------------------------------------------------------------- +// Get the corner points of the box +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingOrientedBox::GetCorners(XMFLOAT3* Corners) const noexcept +{ + assert(Corners != nullptr); + + // Load the box + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + for (size_t i = 0; i < CORNER_COUNT; ++i) + { + XMVECTOR C = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(vExtents, g_BoxOffset[i]), vOrientation), vCenter); + XMStoreFloat3(&Corners[i], C); + } +} + + +//----------------------------------------------------------------------------- +// Point in oriented box test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingOrientedBox::Contains(FXMVECTOR Point) const noexcept +{ + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + // Transform the point to be local to the box. + XMVECTOR TPoint = XMVector3InverseRotate(XMVectorSubtract(Point, vCenter), vOrientation); + + return XMVector3InBounds(TPoint, vExtents) ? CONTAINS : DISJOINT; +} + + +//----------------------------------------------------------------------------- +// Triangle in oriented bounding box +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingOrientedBox::Contains(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2) const noexcept +{ + // Load the box center & orientation. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + // Transform the triangle vertices into the space of the box. + XMVECTOR TV0 = XMVector3InverseRotate(XMVectorSubtract(V0, vCenter), vOrientation); + XMVECTOR TV1 = XMVector3InverseRotate(XMVectorSubtract(V1, vCenter), vOrientation); + XMVECTOR TV2 = XMVector3InverseRotate(XMVectorSubtract(V2, vCenter), vOrientation); + + BoundingBox box; + box.Center = XMFLOAT3(0.0f, 0.0f, 0.0f); + box.Extents = Extents; + + // Use the triangle vs axis aligned box intersection routine. + return box.Contains(TV0, TV1, TV2); +} + + +//----------------------------------------------------------------------------- +// Sphere in oriented bounding box +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingOrientedBox::Contains(const BoundingSphere& sh) const noexcept +{ + XMVECTOR SphereCenter = XMLoadFloat3(&sh.Center); + XMVECTOR SphereRadius = XMVectorReplicatePtr(&sh.Radius); + + XMVECTOR BoxCenter = XMLoadFloat3(&Center); + XMVECTOR BoxExtents = XMLoadFloat3(&Extents); + XMVECTOR BoxOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(BoxOrientation)); + + // Transform the center of the sphere to be local to the box. + // BoxMin = -BoxExtents + // BoxMax = +BoxExtents + SphereCenter = XMVector3InverseRotate(XMVectorSubtract(SphereCenter, BoxCenter), BoxOrientation); + + // Find the distance to the nearest point on the box. + // for each i in (x, y, z) + // if (SphereCenter(i) < BoxMin(i)) d2 += (SphereCenter(i) - BoxMin(i)) ^ 2 + // else if (SphereCenter(i) > BoxMax(i)) d2 += (SphereCenter(i) - BoxMax(i)) ^ 2 + + XMVECTOR d = XMVectorZero(); + + // Compute d for each dimension. + XMVECTOR LessThanMin = XMVectorLess(SphereCenter, XMVectorNegate(BoxExtents)); + XMVECTOR GreaterThanMax = XMVectorGreater(SphereCenter, BoxExtents); + + XMVECTOR MinDelta = XMVectorAdd(SphereCenter, BoxExtents); + XMVECTOR MaxDelta = XMVectorSubtract(SphereCenter, BoxExtents); + + // Choose value for each dimension based on the comparison. + d = XMVectorSelect(d, MinDelta, LessThanMin); + d = XMVectorSelect(d, MaxDelta, GreaterThanMax); + + // Use a dot-product to square them and sum them together. + XMVECTOR d2 = XMVector3Dot(d, d); + XMVECTOR SphereRadiusSq = XMVectorMultiply(SphereRadius, SphereRadius); + + if (XMVector4Greater(d2, SphereRadiusSq)) + return DISJOINT; + + // See if we are completely inside the box + XMVECTOR SMin = XMVectorSubtract(SphereCenter, SphereRadius); + XMVECTOR SMax = XMVectorAdd(SphereCenter, SphereRadius); + + return (XMVector3InBounds(SMin, BoxExtents) && XMVector3InBounds(SMax, BoxExtents)) ? CONTAINS : INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Axis aligned box vs. oriented box. Constructs an oriented box and uses +// the oriented box vs. oriented box test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingOrientedBox::Contains(const BoundingBox& box) const noexcept +{ + // Make the axis aligned box oriented and do an OBB vs OBB test. + BoundingOrientedBox obox(box.Center, box.Extents, XMFLOAT4(0.f, 0.f, 0.f, 1.f)); + return Contains(obox); +} + + +//----------------------------------------------------------------------------- +// Oriented bounding box in oriented bounding box +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingOrientedBox::Contains(const BoundingOrientedBox& box) const noexcept +{ + if (!Intersects(box)) + return DISJOINT; + + // Load the boxes + XMVECTOR aCenter = XMLoadFloat3(&Center); + XMVECTOR aExtents = XMLoadFloat3(&Extents); + XMVECTOR aOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(aOrientation)); + + XMVECTOR bCenter = XMLoadFloat3(&box.Center); + XMVECTOR bExtents = XMLoadFloat3(&box.Extents); + XMVECTOR bOrientation = XMLoadFloat4(&box.Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(bOrientation)); + + XMVECTOR offset = XMVectorSubtract(bCenter, aCenter); + + for (size_t i = 0; i < CORNER_COUNT; ++i) + { + // Cb = rotate( bExtents * corneroffset[i], bOrientation ) + bcenter + // Ca = invrotate( Cb - aCenter, aOrientation ) + + XMVECTOR C = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(bExtents, g_BoxOffset[i]), bOrientation), offset); + C = XMVector3InverseRotate(C, aOrientation); + + if (!XMVector3InBounds(C, aExtents)) + return INTERSECTS; + } + + return CONTAINS; +} + + +//----------------------------------------------------------------------------- +// Frustum in oriented bounding box +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingOrientedBox::Contains(const BoundingFrustum& fr) const noexcept +{ + if (!fr.Intersects(*this)) + return DISJOINT; + + XMFLOAT3 Corners[BoundingFrustum::CORNER_COUNT]; + fr.GetCorners(Corners); + + // Load the box + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + for (size_t i = 0; i < BoundingFrustum::CORNER_COUNT; ++i) + { + XMVECTOR C = XMVector3InverseRotate(XMVectorSubtract(XMLoadFloat3(&Corners[i]), vCenter), vOrientation); + + if (!XMVector3InBounds(C, vExtents)) + return INTERSECTS; + } + + return CONTAINS; +} + + +//----------------------------------------------------------------------------- +// Sphere vs. oriented box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingOrientedBox::Intersects(const BoundingSphere& sh) const noexcept +{ + XMVECTOR SphereCenter = XMLoadFloat3(&sh.Center); + XMVECTOR SphereRadius = XMVectorReplicatePtr(&sh.Radius); + + XMVECTOR BoxCenter = XMLoadFloat3(&Center); + XMVECTOR BoxExtents = XMLoadFloat3(&Extents); + XMVECTOR BoxOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(BoxOrientation)); + + // Transform the center of the sphere to be local to the box. + // BoxMin = -BoxExtents + // BoxMax = +BoxExtents + SphereCenter = XMVector3InverseRotate(XMVectorSubtract(SphereCenter, BoxCenter), BoxOrientation); + + // Find the distance to the nearest point on the box. + // for each i in (x, y, z) + // if (SphereCenter(i) < BoxMin(i)) d2 += (SphereCenter(i) - BoxMin(i)) ^ 2 + // else if (SphereCenter(i) > BoxMax(i)) d2 += (SphereCenter(i) - BoxMax(i)) ^ 2 + + XMVECTOR d = XMVectorZero(); + + // Compute d for each dimension. + XMVECTOR LessThanMin = XMVectorLess(SphereCenter, XMVectorNegate(BoxExtents)); + XMVECTOR GreaterThanMax = XMVectorGreater(SphereCenter, BoxExtents); + + XMVECTOR MinDelta = XMVectorAdd(SphereCenter, BoxExtents); + XMVECTOR MaxDelta = XMVectorSubtract(SphereCenter, BoxExtents); + + // Choose value for each dimension based on the comparison. + d = XMVectorSelect(d, MinDelta, LessThanMin); + d = XMVectorSelect(d, MaxDelta, GreaterThanMax); + + // Use a dot-product to square them and sum them together. + XMVECTOR d2 = XMVector3Dot(d, d); + + return XMVector4LessOrEqual(d2, XMVectorMultiply(SphereRadius, SphereRadius)) ? true : false; +} + + +//----------------------------------------------------------------------------- +// Axis aligned box vs. oriented box. Constructs an oriented box and uses +// the oriented box vs. oriented box test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingOrientedBox::Intersects(const BoundingBox& box) const noexcept +{ + // Make the axis aligned box oriented and do an OBB vs OBB test. + BoundingOrientedBox obox(box.Center, box.Extents, XMFLOAT4(0.f, 0.f, 0.f, 1.f)); + return Intersects(obox); +} + + +//----------------------------------------------------------------------------- +// Fast oriented box / oriented box intersection test using the separating axis +// theorem. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingOrientedBox::Intersects(const BoundingOrientedBox& box) const noexcept +{ + // Build the 3x3 rotation matrix that defines the orientation of B relative to A. + XMVECTOR A_quat = XMLoadFloat4(&Orientation); + XMVECTOR B_quat = XMLoadFloat4(&box.Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(A_quat)); + assert(DirectX::MathInternal::XMQuaternionIsUnit(B_quat)); + + XMVECTOR Q = XMQuaternionMultiply(A_quat, XMQuaternionConjugate(B_quat)); + XMMATRIX R = XMMatrixRotationQuaternion(Q); + + // Compute the translation of B relative to A. + XMVECTOR A_cent = XMLoadFloat3(&Center); + XMVECTOR B_cent = XMLoadFloat3(&box.Center); + XMVECTOR t = XMVector3InverseRotate(XMVectorSubtract(B_cent, A_cent), A_quat); + + // + // h(A) = extents of A. + // h(B) = extents of B. + // + // a(u) = axes of A = (1,0,0), (0,1,0), (0,0,1) + // b(u) = axes of B relative to A = (r00,r10,r20), (r01,r11,r21), (r02,r12,r22) + // + // For each possible separating axis l: + // d(A) = sum (for i = u,v,w) h(A)(i) * abs( a(i) dot l ) + // d(B) = sum (for i = u,v,w) h(B)(i) * abs( b(i) dot l ) + // if abs( t dot l ) > d(A) + d(B) then disjoint + // + + // Load extents of A and B. + XMVECTOR h_A = XMLoadFloat3(&Extents); + XMVECTOR h_B = XMLoadFloat3(&box.Extents); + + // Rows. Note R[0,1,2]X.w = 0. + XMVECTOR R0X = R.r[0]; + XMVECTOR R1X = R.r[1]; + XMVECTOR R2X = R.r[2]; + + R = XMMatrixTranspose(R); + + // Columns. Note RX[0,1,2].w = 0. + XMVECTOR RX0 = R.r[0]; + XMVECTOR RX1 = R.r[1]; + XMVECTOR RX2 = R.r[2]; + + // Absolute value of rows. + XMVECTOR AR0X = XMVectorAbs(R0X); + XMVECTOR AR1X = XMVectorAbs(R1X); + XMVECTOR AR2X = XMVectorAbs(R2X); + + // Absolute value of columns. + XMVECTOR ARX0 = XMVectorAbs(RX0); + XMVECTOR ARX1 = XMVectorAbs(RX1); + XMVECTOR ARX2 = XMVectorAbs(RX2); + + // Test each of the 15 possible seperating axii. + XMVECTOR d, d_A, d_B; + + // l = a(u) = (1, 0, 0) + // t dot l = t.x + // d(A) = h(A).x + // d(B) = h(B) dot abs(r00, r01, r02) + d = XMVectorSplatX(t); + d_A = XMVectorSplatX(h_A); + d_B = XMVector3Dot(h_B, AR0X); + XMVECTOR NoIntersection = XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)); + + // l = a(v) = (0, 1, 0) + // t dot l = t.y + // d(A) = h(A).y + // d(B) = h(B) dot abs(r10, r11, r12) + d = XMVectorSplatY(t); + d_A = XMVectorSplatY(h_A); + d_B = XMVector3Dot(h_B, AR1X); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(w) = (0, 0, 1) + // t dot l = t.z + // d(A) = h(A).z + // d(B) = h(B) dot abs(r20, r21, r22) + d = XMVectorSplatZ(t); + d_A = XMVectorSplatZ(h_A); + d_B = XMVector3Dot(h_B, AR2X); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = b(u) = (r00, r10, r20) + // d(A) = h(A) dot abs(r00, r10, r20) + // d(B) = h(B).x + d = XMVector3Dot(t, RX0); + d_A = XMVector3Dot(h_A, ARX0); + d_B = XMVectorSplatX(h_B); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = b(v) = (r01, r11, r21) + // d(A) = h(A) dot abs(r01, r11, r21) + // d(B) = h(B).y + d = XMVector3Dot(t, RX1); + d_A = XMVector3Dot(h_A, ARX1); + d_B = XMVectorSplatY(h_B); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = b(w) = (r02, r12, r22) + // d(A) = h(A) dot abs(r02, r12, r22) + // d(B) = h(B).z + d = XMVector3Dot(t, RX2); + d_A = XMVector3Dot(h_A, ARX2); + d_B = XMVectorSplatZ(h_B); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(u) x b(u) = (0, -r20, r10) + // d(A) = h(A) dot abs(0, r20, r10) + // d(B) = h(B) dot abs(0, r02, r01) + d = XMVector3Dot(t, XMVectorPermute(RX0, XMVectorNegate(RX0))); + d_A = XMVector3Dot(h_A, XMVectorSwizzle(ARX0)); + d_B = XMVector3Dot(h_B, XMVectorSwizzle(AR0X)); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(u) x b(v) = (0, -r21, r11) + // d(A) = h(A) dot abs(0, r21, r11) + // d(B) = h(B) dot abs(r02, 0, r00) + d = XMVector3Dot(t, XMVectorPermute(RX1, XMVectorNegate(RX1))); + d_A = XMVector3Dot(h_A, XMVectorSwizzle(ARX1)); + d_B = XMVector3Dot(h_B, XMVectorSwizzle(AR0X)); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(u) x b(w) = (0, -r22, r12) + // d(A) = h(A) dot abs(0, r22, r12) + // d(B) = h(B) dot abs(r01, r00, 0) + d = XMVector3Dot(t, XMVectorPermute(RX2, XMVectorNegate(RX2))); + d_A = XMVector3Dot(h_A, XMVectorSwizzle(ARX2)); + d_B = XMVector3Dot(h_B, XMVectorSwizzle(AR0X)); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(v) x b(u) = (r20, 0, -r00) + // d(A) = h(A) dot abs(r20, 0, r00) + // d(B) = h(B) dot abs(0, r12, r11) + d = XMVector3Dot(t, XMVectorPermute(RX0, XMVectorNegate(RX0))); + d_A = XMVector3Dot(h_A, XMVectorSwizzle(ARX0)); + d_B = XMVector3Dot(h_B, XMVectorSwizzle(AR1X)); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(v) x b(v) = (r21, 0, -r01) + // d(A) = h(A) dot abs(r21, 0, r01) + // d(B) = h(B) dot abs(r12, 0, r10) + d = XMVector3Dot(t, XMVectorPermute(RX1, XMVectorNegate(RX1))); + d_A = XMVector3Dot(h_A, XMVectorSwizzle(ARX1)); + d_B = XMVector3Dot(h_B, XMVectorSwizzle(AR1X)); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(v) x b(w) = (r22, 0, -r02) + // d(A) = h(A) dot abs(r22, 0, r02) + // d(B) = h(B) dot abs(r11, r10, 0) + d = XMVector3Dot(t, XMVectorPermute(RX2, XMVectorNegate(RX2))); + d_A = XMVector3Dot(h_A, XMVectorSwizzle(ARX2)); + d_B = XMVector3Dot(h_B, XMVectorSwizzle(AR1X)); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(w) x b(u) = (-r10, r00, 0) + // d(A) = h(A) dot abs(r10, r00, 0) + // d(B) = h(B) dot abs(0, r22, r21) + d = XMVector3Dot(t, XMVectorPermute(RX0, XMVectorNegate(RX0))); + d_A = XMVector3Dot(h_A, XMVectorSwizzle(ARX0)); + d_B = XMVector3Dot(h_B, XMVectorSwizzle(AR2X)); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(w) x b(v) = (-r11, r01, 0) + // d(A) = h(A) dot abs(r11, r01, 0) + // d(B) = h(B) dot abs(r22, 0, r20) + d = XMVector3Dot(t, XMVectorPermute(RX1, XMVectorNegate(RX1))); + d_A = XMVector3Dot(h_A, XMVectorSwizzle(ARX1)); + d_B = XMVector3Dot(h_B, XMVectorSwizzle(AR2X)); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // l = a(w) x b(w) = (-r12, r02, 0) + // d(A) = h(A) dot abs(r12, r02, 0) + // d(B) = h(B) dot abs(r21, r20, 0) + d = XMVector3Dot(t, XMVectorPermute(RX2, XMVectorNegate(RX2))); + d_A = XMVector3Dot(h_A, XMVectorSwizzle(ARX2)); + d_B = XMVector3Dot(h_B, XMVectorSwizzle(AR2X)); + NoIntersection = XMVectorOrInt(NoIntersection, + XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B))); + + // No seperating axis found, boxes must intersect. + return XMVector4NotEqualInt(NoIntersection, XMVectorTrueInt()) ? true : false; +} + + +//----------------------------------------------------------------------------- +// Frustum vs. oriented box test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingOrientedBox::Intersects(const BoundingFrustum& fr) const noexcept +{ + return fr.Intersects(*this); +} + + +//----------------------------------------------------------------------------- +// Triangle vs. oriented box test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool XM_CALLCONV BoundingOrientedBox::Intersects(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2) const noexcept +{ + // Load the box center & orientation. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + // Transform the triangle vertices into the space of the box. + XMVECTOR TV0 = XMVector3InverseRotate(XMVectorSubtract(V0, vCenter), vOrientation); + XMVECTOR TV1 = XMVector3InverseRotate(XMVectorSubtract(V1, vCenter), vOrientation); + XMVECTOR TV2 = XMVector3InverseRotate(XMVectorSubtract(V2, vCenter), vOrientation); + + BoundingBox box; + box.Center = XMFLOAT3(0.0f, 0.0f, 0.0f); + box.Extents = Extents; + + // Use the triangle vs axis aligned box intersection routine. + return box.Intersects(TV0, TV1, TV2); +} + + +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline PlaneIntersectionType XM_CALLCONV BoundingOrientedBox::Intersects(FXMVECTOR Plane) const noexcept +{ + assert(DirectX::MathInternal::XMPlaneIsUnit(Plane)); + + // Load the box. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + XMVECTOR BoxOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(BoxOrientation)); + + // Set w of the center to one so we can dot4 with a plane. + vCenter = XMVectorInsert<0, 0, 0, 0, 1>(vCenter, XMVectorSplatOne()); + + // Build the 3x3 rotation matrix that defines the box axes. + XMMATRIX R = XMMatrixRotationQuaternion(BoxOrientation); + + XMVECTOR Outside, Inside; + DirectX::MathInternal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane, Outside, Inside); + + // If the box is outside any plane it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return FRONT; + + // If the box is inside all planes it is inside. + if (XMVector4EqualInt(Inside, XMVectorTrueInt())) + return BACK; + + // The box is not inside all planes or outside a plane it intersects. + return INTERSECTING; +} + + +//----------------------------------------------------------------------------- +// Compute the intersection of a ray (Origin, Direction) with an oriented box +// using the slabs method. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool XM_CALLCONV BoundingOrientedBox::Intersects(FXMVECTOR Origin, FXMVECTOR Direction, float& Dist) const noexcept +{ + assert(DirectX::MathInternal::XMVector3IsUnit(Direction)); + + static const XMVECTORU32 SelectY = { { { XM_SELECT_0, XM_SELECT_1, XM_SELECT_0, XM_SELECT_0 } } }; + static const XMVECTORU32 SelectZ = { { { XM_SELECT_0, XM_SELECT_0, XM_SELECT_1, XM_SELECT_0 } } }; + + // Load the box. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Get the boxes normalized side directions. + XMMATRIX R = XMMatrixRotationQuaternion(vOrientation); + + // Adjust ray origin to be relative to center of the box. + XMVECTOR TOrigin = XMVectorSubtract(vCenter, Origin); + + // Compute the dot product againt each axis of the box. + XMVECTOR AxisDotOrigin = XMVector3Dot(R.r[0], TOrigin); + AxisDotOrigin = XMVectorSelect(AxisDotOrigin, XMVector3Dot(R.r[1], TOrigin), SelectY); + AxisDotOrigin = XMVectorSelect(AxisDotOrigin, XMVector3Dot(R.r[2], TOrigin), SelectZ); + + XMVECTOR AxisDotDirection = XMVector3Dot(R.r[0], Direction); + AxisDotDirection = XMVectorSelect(AxisDotDirection, XMVector3Dot(R.r[1], Direction), SelectY); + AxisDotDirection = XMVectorSelect(AxisDotDirection, XMVector3Dot(R.r[2], Direction), SelectZ); + + // if (fabs(AxisDotDirection) <= Epsilon) the ray is nearly parallel to the slab. + XMVECTOR IsParallel = XMVectorLessOrEqual(XMVectorAbs(AxisDotDirection), g_RayEpsilon); + + // Test against all three axes simultaneously. + XMVECTOR InverseAxisDotDirection = XMVectorReciprocal(AxisDotDirection); + XMVECTOR t1 = XMVectorMultiply(XMVectorSubtract(AxisDotOrigin, vExtents), InverseAxisDotDirection); + XMVECTOR t2 = XMVectorMultiply(XMVectorAdd(AxisDotOrigin, vExtents), InverseAxisDotDirection); + + // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't + // use the results from any directions parallel to the slab. + XMVECTOR t_min = XMVectorSelect(XMVectorMin(t1, t2), g_FltMin, IsParallel); + XMVECTOR t_max = XMVectorSelect(XMVectorMax(t1, t2), g_FltMax, IsParallel); + + // t_min.x = maximum( t_min.x, t_min.y, t_min.z ); + // t_max.x = minimum( t_max.x, t_max.y, t_max.z ); + t_min = XMVectorMax(t_min, XMVectorSplatY(t_min)); // x = max(x,y) + t_min = XMVectorMax(t_min, XMVectorSplatZ(t_min)); // x = max(max(x,y),z) + t_max = XMVectorMin(t_max, XMVectorSplatY(t_max)); // x = min(x,y) + t_max = XMVectorMin(t_max, XMVectorSplatZ(t_max)); // x = min(min(x,y),z) + + // if ( t_min > t_max ) return false; + XMVECTOR NoIntersection = XMVectorGreater(XMVectorSplatX(t_min), XMVectorSplatX(t_max)); + + // if ( t_max < 0.0f ) return false; + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(XMVectorSplatX(t_max), XMVectorZero())); + + // if (IsParallel && (-Extents > AxisDotOrigin || Extents < AxisDotOrigin)) return false; + XMVECTOR ParallelOverlap = XMVectorInBounds(AxisDotOrigin, vExtents); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorAndCInt(IsParallel, ParallelOverlap)); + + if (!DirectX::MathInternal::XMVector3AnyTrue(NoIntersection)) + { + // Store the x-component to *pDist + XMStoreFloat(&Dist, t_min); + return true; + } + + Dist = 0.f; + return false; +} + + +//----------------------------------------------------------------------------- +// Test an oriented box vs 6 planes (typically forming a frustum). +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingOrientedBox::ContainedBy( + FXMVECTOR Plane0, FXMVECTOR Plane1, FXMVECTOR Plane2, + GXMVECTOR Plane3, + HXMVECTOR Plane4, HXMVECTOR Plane5) const noexcept +{ + // Load the box. + XMVECTOR vCenter = XMLoadFloat3(&Center); + XMVECTOR vExtents = XMLoadFloat3(&Extents); + XMVECTOR BoxOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(BoxOrientation)); + + // Set w of the center to one so we can dot4 with a plane. + vCenter = XMVectorInsert<0, 0, 0, 0, 1>(vCenter, XMVectorSplatOne()); + + // Build the 3x3 rotation matrix that defines the box axes. + XMMATRIX R = XMMatrixRotationQuaternion(BoxOrientation); + + XMVECTOR Outside, Inside; + + // Test against each plane. + DirectX::MathInternal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane0, Outside, Inside); + + XMVECTOR AnyOutside = Outside; + XMVECTOR AllInside = Inside; + + DirectX::MathInternal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane1, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane2, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane3, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane4, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane5, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + // If the box is outside any plane it is outside. + if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) + return DISJOINT; + + // If the box is inside all planes it is inside. + if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) + return CONTAINS; + + // The box is not inside all planes or outside a plane, it may intersect. + return INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Create oriented bounding box from axis-aligned bounding box +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingOrientedBox::CreateFromBoundingBox(BoundingOrientedBox& Out, const BoundingBox& box) noexcept +{ + Out.Center = box.Center; + Out.Extents = box.Extents; + Out.Orientation = XMFLOAT4(0.f, 0.f, 0.f, 1.f); +} + + +//----------------------------------------------------------------------------- +// Find the approximate minimum oriented bounding box containing a set of +// points. Exact computation of minimum oriented bounding box is possible but +// is slower and requires a more complex algorithm. +// The algorithm works by computing the inertia tensor of the points and then +// using the eigenvectors of the intertia tensor as the axes of the box. +// Computing the intertia tensor of the convex hull of the points will usually +// result in better bounding box but the computation is more complex. +// Exact computation of the minimum oriented bounding box is possible but the +// best know algorithm is O(N^3) and is significanly more complex to implement. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingOrientedBox::CreateFromPoints(BoundingOrientedBox& Out, size_t Count, const XMFLOAT3* pPoints, size_t Stride) noexcept +{ + assert(Count > 0); + assert(pPoints != nullptr); + + XMVECTOR CenterOfMass = XMVectorZero(); + + // Compute the center of mass and inertia tensor of the points. + for (size_t i = 0; i < Count; ++i) + { + XMVECTOR Point = XMLoadFloat3(reinterpret_cast(reinterpret_cast(pPoints) + i * Stride)); + + CenterOfMass = XMVectorAdd(CenterOfMass, Point); + } + + CenterOfMass = XMVectorMultiply(CenterOfMass, XMVectorReciprocal(XMVectorReplicate(float(Count)))); + + // Compute the inertia tensor of the points around the center of mass. + // Using the center of mass is not strictly necessary, but will hopefully + // improve the stability of finding the eigenvectors. + XMVECTOR XX_YY_ZZ = XMVectorZero(); + XMVECTOR XY_XZ_YZ = XMVectorZero(); + + for (size_t i = 0; i < Count; ++i) + { + XMVECTOR Point = XMVectorSubtract(XMLoadFloat3(reinterpret_cast(reinterpret_cast(pPoints) + i * Stride)), CenterOfMass); + + XX_YY_ZZ = XMVectorAdd(XX_YY_ZZ, XMVectorMultiply(Point, Point)); + + XMVECTOR XXY = XMVectorSwizzle(Point); + XMVECTOR YZZ = XMVectorSwizzle(Point); + + XY_XZ_YZ = XMVectorAdd(XY_XZ_YZ, XMVectorMultiply(XXY, YZZ)); + } + + XMVECTOR v1, v2, v3; + + // Compute the eigenvectors of the inertia tensor. + DirectX::MathInternal::CalculateEigenVectorsFromCovarianceMatrix(XMVectorGetX(XX_YY_ZZ), XMVectorGetY(XX_YY_ZZ), + XMVectorGetZ(XX_YY_ZZ), + XMVectorGetX(XY_XZ_YZ), XMVectorGetY(XY_XZ_YZ), + XMVectorGetZ(XY_XZ_YZ), + &v1, &v2, &v3); + + // Put them in a matrix. + XMMATRIX R; + + R.r[0] = XMVectorSetW(v1, 0.f); + R.r[1] = XMVectorSetW(v2, 0.f); + R.r[2] = XMVectorSetW(v3, 0.f); + R.r[3] = g_XMIdentityR3.v; + + // Multiply by -1 to convert the matrix into a right handed coordinate + // system (Det ~= 1) in case the eigenvectors form a left handed + // coordinate system (Det ~= -1) because XMQuaternionRotationMatrix only + // works on right handed matrices. + XMVECTOR Det = XMMatrixDeterminant(R); + + if (XMVector4Less(Det, XMVectorZero())) + { + R.r[0] = XMVectorMultiply(R.r[0], g_XMNegativeOne.v); + R.r[1] = XMVectorMultiply(R.r[1], g_XMNegativeOne.v); + R.r[2] = XMVectorMultiply(R.r[2], g_XMNegativeOne.v); + } + + // Get the rotation quaternion from the matrix. + XMVECTOR vOrientation = XMQuaternionRotationMatrix(R); + + // Make sure it is normal (in case the vectors are slightly non-orthogonal). + vOrientation = XMQuaternionNormalize(vOrientation); + + // Rebuild the rotation matrix from the quaternion. + R = XMMatrixRotationQuaternion(vOrientation); + + // Build the rotation into the rotated space. + XMMATRIX InverseR = XMMatrixTranspose(R); + + // Find the minimum OBB using the eigenvectors as the axes. + XMVECTOR vMin, vMax; + + vMin = vMax = XMVector3TransformNormal(XMLoadFloat3(pPoints), InverseR); + + for (size_t i = 1; i < Count; ++i) + { + XMVECTOR Point = XMVector3TransformNormal(XMLoadFloat3(reinterpret_cast(reinterpret_cast(pPoints) + i * Stride)), + InverseR); + + vMin = XMVectorMin(vMin, Point); + vMax = XMVectorMax(vMax, Point); + } + + // Rotate the center into world space. + XMVECTOR vCenter = XMVectorScale(XMVectorAdd(vMin, vMax), 0.5f); + vCenter = XMVector3TransformNormal(vCenter, R); + + // Store center, extents, and orientation. + XMStoreFloat3(&Out.Center, vCenter); + XMStoreFloat3(&Out.Extents, XMVectorScale(XMVectorSubtract(vMax, vMin), 0.5f)); + XMStoreFloat4(&Out.Orientation, vOrientation); +} + + +/**************************************************************************** + * + * BoundingFrustum + * + ****************************************************************************/ + +_Use_decl_annotations_ +inline BoundingFrustum::BoundingFrustum(CXMMATRIX Projection, bool rhcoords) noexcept +{ + CreateFromMatrix(*this, Projection, rhcoords); +} + + +//----------------------------------------------------------------------------- +// Transform a frustum by an angle preserving transform. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingFrustum::Transform(BoundingFrustum& Out, FXMMATRIX M) const noexcept +{ + // Load the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Composite the frustum rotation and the transform rotation + XMMATRIX nM; + nM.r[0] = XMVector3Normalize(M.r[0]); + nM.r[1] = XMVector3Normalize(M.r[1]); + nM.r[2] = XMVector3Normalize(M.r[2]); + nM.r[3] = g_XMIdentityR3; + XMVECTOR Rotation = XMQuaternionRotationMatrix(nM); + vOrientation = XMQuaternionMultiply(vOrientation, Rotation); + + // Transform the center. + vOrigin = XMVector3Transform(vOrigin, M); + + // Store the frustum. + XMStoreFloat3(&Out.Origin, vOrigin); + XMStoreFloat4(&Out.Orientation, vOrientation); + + // Scale the near and far distances (the slopes remain the same). + XMVECTOR dX = XMVector3Dot(M.r[0], M.r[0]); + XMVECTOR dY = XMVector3Dot(M.r[1], M.r[1]); + XMVECTOR dZ = XMVector3Dot(M.r[2], M.r[2]); + + XMVECTOR d = XMVectorMax(dX, XMVectorMax(dY, dZ)); + float Scale = sqrtf(XMVectorGetX(d)); + + Out.Near = Near * Scale; + Out.Far = Far * Scale; + + // Copy the slopes. + Out.RightSlope = RightSlope; + Out.LeftSlope = LeftSlope; + Out.TopSlope = TopSlope; + Out.BottomSlope = BottomSlope; +} + +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingFrustum::Transform(BoundingFrustum& Out, float Scale, FXMVECTOR Rotation, FXMVECTOR Translation) const noexcept +{ + assert(DirectX::MathInternal::XMQuaternionIsUnit(Rotation)); + + // Load the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Composite the frustum rotation and the transform rotation. + vOrientation = XMQuaternionMultiply(vOrientation, Rotation); + + // Transform the origin. + vOrigin = XMVectorAdd(XMVector3Rotate(XMVectorScale(vOrigin, Scale), Rotation), Translation); + + // Store the frustum. + XMStoreFloat3(&Out.Origin, vOrigin); + XMStoreFloat4(&Out.Orientation, vOrientation); + + // Scale the near and far distances (the slopes remain the same). + Out.Near = Near * Scale; + Out.Far = Far * Scale; + + // Copy the slopes. + Out.RightSlope = RightSlope; + Out.LeftSlope = LeftSlope; + Out.TopSlope = TopSlope; + Out.BottomSlope = BottomSlope; +} + + +//----------------------------------------------------------------------------- +// Get the corner points of the frustum +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingFrustum::GetCorners(XMFLOAT3* Corners) const noexcept +{ + assert(Corners != nullptr); + + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Build the corners of the frustum. + XMVECTOR vRightTop = XMVectorSet(RightSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR vRightBottom = XMVectorSet(RightSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vLeftTop = XMVectorSet(LeftSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR vLeftBottom = XMVectorSet(LeftSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vNear = XMVectorReplicatePtr(&Near); + XMVECTOR vFar = XMVectorReplicatePtr(&Far); + + // Returns 8 corners position of bounding frustum. + // Near Far + // 0----1 4----5 + // | | | | + // | | | | + // 3----2 7----6 + + XMVECTOR vCorners[CORNER_COUNT]; + vCorners[0] = XMVectorMultiply(vLeftTop, vNear); + vCorners[1] = XMVectorMultiply(vRightTop, vNear); + vCorners[2] = XMVectorMultiply(vRightBottom, vNear); + vCorners[3] = XMVectorMultiply(vLeftBottom, vNear); + vCorners[4] = XMVectorMultiply(vLeftTop, vFar); + vCorners[5] = XMVectorMultiply(vRightTop, vFar); + vCorners[6] = XMVectorMultiply(vRightBottom, vFar); + vCorners[7] = XMVectorMultiply(vLeftBottom, vFar); + + for (size_t i = 0; i < CORNER_COUNT; ++i) + { + XMVECTOR C = XMVectorAdd(XMVector3Rotate(vCorners[i], vOrientation), vOrigin); + XMStoreFloat3(&Corners[i], C); + } +} + + +//----------------------------------------------------------------------------- +// Point in frustum test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingFrustum::Contains(FXMVECTOR Point) const noexcept +{ + // Build frustum planes. + XMVECTOR Planes[6]; + Planes[0] = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + Planes[1] = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + Planes[2] = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + Planes[3] = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + Planes[4] = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + Planes[5] = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + + // Load origin and orientation. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Transform point into local space of frustum. + XMVECTOR TPoint = XMVector3InverseRotate(XMVectorSubtract(Point, vOrigin), vOrientation); + + // Set w to one. + TPoint = XMVectorInsert<0, 0, 0, 0, 1>(TPoint, XMVectorSplatOne()); + + XMVECTOR Zero = XMVectorZero(); + XMVECTOR Outside = Zero; + + // Test point against each plane of the frustum. + for (size_t i = 0; i < 6; ++i) + { + XMVECTOR Dot = XMVector4Dot(TPoint, Planes[i]); + Outside = XMVectorOrInt(Outside, XMVectorGreater(Dot, Zero)); + } + + return XMVector4NotEqualInt(Outside, XMVectorTrueInt()) ? CONTAINS : DISJOINT; +} + + +//----------------------------------------------------------------------------- +// Triangle vs frustum test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingFrustum::Contains(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2) const noexcept +{ + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + // Create 6 planes (do it inline to encourage use of registers) + XMVECTOR NearPlane = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + NearPlane = DirectX::MathInternal::XMPlaneTransform(NearPlane, vOrientation, vOrigin); + NearPlane = XMPlaneNormalize(NearPlane); + + XMVECTOR FarPlane = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + FarPlane = DirectX::MathInternal::XMPlaneTransform(FarPlane, vOrientation, vOrigin); + FarPlane = XMPlaneNormalize(FarPlane); + + XMVECTOR RightPlane = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + RightPlane = DirectX::MathInternal::XMPlaneTransform(RightPlane, vOrientation, vOrigin); + RightPlane = XMPlaneNormalize(RightPlane); + + XMVECTOR LeftPlane = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + LeftPlane = DirectX::MathInternal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin); + LeftPlane = XMPlaneNormalize(LeftPlane); + + XMVECTOR TopPlane = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + TopPlane = DirectX::MathInternal::XMPlaneTransform(TopPlane, vOrientation, vOrigin); + TopPlane = XMPlaneNormalize(TopPlane); + + XMVECTOR BottomPlane = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + BottomPlane = DirectX::MathInternal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin); + BottomPlane = XMPlaneNormalize(BottomPlane); + + return TriangleTests::ContainedBy(V0, V1, V2, NearPlane, FarPlane, RightPlane, LeftPlane, TopPlane, BottomPlane); +} + + +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingFrustum::Contains(const BoundingSphere& sh) const noexcept +{ + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + // Create 6 planes (do it inline to encourage use of registers) + XMVECTOR NearPlane = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + NearPlane = DirectX::MathInternal::XMPlaneTransform(NearPlane, vOrientation, vOrigin); + NearPlane = XMPlaneNormalize(NearPlane); + + XMVECTOR FarPlane = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + FarPlane = DirectX::MathInternal::XMPlaneTransform(FarPlane, vOrientation, vOrigin); + FarPlane = XMPlaneNormalize(FarPlane); + + XMVECTOR RightPlane = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + RightPlane = DirectX::MathInternal::XMPlaneTransform(RightPlane, vOrientation, vOrigin); + RightPlane = XMPlaneNormalize(RightPlane); + + XMVECTOR LeftPlane = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + LeftPlane = DirectX::MathInternal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin); + LeftPlane = XMPlaneNormalize(LeftPlane); + + XMVECTOR TopPlane = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + TopPlane = DirectX::MathInternal::XMPlaneTransform(TopPlane, vOrientation, vOrigin); + TopPlane = XMPlaneNormalize(TopPlane); + + XMVECTOR BottomPlane = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + BottomPlane = DirectX::MathInternal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin); + BottomPlane = XMPlaneNormalize(BottomPlane); + + return sh.ContainedBy(NearPlane, FarPlane, RightPlane, LeftPlane, TopPlane, BottomPlane); +} + + +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingFrustum::Contains(const BoundingBox& box) const noexcept +{ + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + // Create 6 planes (do it inline to encourage use of registers) + XMVECTOR NearPlane = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + NearPlane = DirectX::MathInternal::XMPlaneTransform(NearPlane, vOrientation, vOrigin); + NearPlane = XMPlaneNormalize(NearPlane); + + XMVECTOR FarPlane = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + FarPlane = DirectX::MathInternal::XMPlaneTransform(FarPlane, vOrientation, vOrigin); + FarPlane = XMPlaneNormalize(FarPlane); + + XMVECTOR RightPlane = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + RightPlane = DirectX::MathInternal::XMPlaneTransform(RightPlane, vOrientation, vOrigin); + RightPlane = XMPlaneNormalize(RightPlane); + + XMVECTOR LeftPlane = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + LeftPlane = DirectX::MathInternal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin); + LeftPlane = XMPlaneNormalize(LeftPlane); + + XMVECTOR TopPlane = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + TopPlane = DirectX::MathInternal::XMPlaneTransform(TopPlane, vOrientation, vOrigin); + TopPlane = XMPlaneNormalize(TopPlane); + + XMVECTOR BottomPlane = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + BottomPlane = DirectX::MathInternal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin); + BottomPlane = XMPlaneNormalize(BottomPlane); + + return box.ContainedBy(NearPlane, FarPlane, RightPlane, LeftPlane, TopPlane, BottomPlane); +} + + +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingFrustum::Contains(const BoundingOrientedBox& box) const noexcept +{ + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + // Create 6 planes (do it inline to encourage use of registers) + XMVECTOR NearPlane = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + NearPlane = DirectX::MathInternal::XMPlaneTransform(NearPlane, vOrientation, vOrigin); + NearPlane = XMPlaneNormalize(NearPlane); + + XMVECTOR FarPlane = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + FarPlane = DirectX::MathInternal::XMPlaneTransform(FarPlane, vOrientation, vOrigin); + FarPlane = XMPlaneNormalize(FarPlane); + + XMVECTOR RightPlane = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + RightPlane = DirectX::MathInternal::XMPlaneTransform(RightPlane, vOrientation, vOrigin); + RightPlane = XMPlaneNormalize(RightPlane); + + XMVECTOR LeftPlane = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + LeftPlane = DirectX::MathInternal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin); + LeftPlane = XMPlaneNormalize(LeftPlane); + + XMVECTOR TopPlane = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + TopPlane = DirectX::MathInternal::XMPlaneTransform(TopPlane, vOrientation, vOrigin); + TopPlane = XMPlaneNormalize(TopPlane); + + XMVECTOR BottomPlane = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + BottomPlane = DirectX::MathInternal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin); + BottomPlane = XMPlaneNormalize(BottomPlane); + + return box.ContainedBy(NearPlane, FarPlane, RightPlane, LeftPlane, TopPlane, BottomPlane); +} + + +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType BoundingFrustum::Contains(const BoundingFrustum& fr) const noexcept +{ + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + // Create 6 planes (do it inline to encourage use of registers) + XMVECTOR NearPlane = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + NearPlane = DirectX::MathInternal::XMPlaneTransform(NearPlane, vOrientation, vOrigin); + NearPlane = XMPlaneNormalize(NearPlane); + + XMVECTOR FarPlane = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + FarPlane = DirectX::MathInternal::XMPlaneTransform(FarPlane, vOrientation, vOrigin); + FarPlane = XMPlaneNormalize(FarPlane); + + XMVECTOR RightPlane = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + RightPlane = DirectX::MathInternal::XMPlaneTransform(RightPlane, vOrientation, vOrigin); + RightPlane = XMPlaneNormalize(RightPlane); + + XMVECTOR LeftPlane = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + LeftPlane = DirectX::MathInternal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin); + LeftPlane = XMPlaneNormalize(LeftPlane); + + XMVECTOR TopPlane = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + TopPlane = DirectX::MathInternal::XMPlaneTransform(TopPlane, vOrientation, vOrigin); + TopPlane = XMPlaneNormalize(TopPlane); + + XMVECTOR BottomPlane = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + BottomPlane = DirectX::MathInternal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin); + BottomPlane = XMPlaneNormalize(BottomPlane); + + return fr.ContainedBy(NearPlane, FarPlane, RightPlane, LeftPlane, TopPlane, BottomPlane); +} + + +//----------------------------------------------------------------------------- +// Exact sphere vs frustum test. The algorithm first checks the sphere against +// the planes of the frustum, then if the plane checks were indeterminate finds +// the nearest feature (plane, line, point) on the frustum to the center of the +// sphere and compares the distance to the nearest feature to the radius of the +// sphere +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingFrustum::Intersects(const BoundingSphere& sh) const noexcept +{ + XMVECTOR Zero = XMVectorZero(); + + // Build the frustum planes. + XMVECTOR Planes[6]; + Planes[0] = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + Planes[1] = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + Planes[2] = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + Planes[3] = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + Planes[4] = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + Planes[5] = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + + // Normalize the planes so we can compare to the sphere radius. + Planes[2] = XMVector3Normalize(Planes[2]); + Planes[3] = XMVector3Normalize(Planes[3]); + Planes[4] = XMVector3Normalize(Planes[4]); + Planes[5] = XMVector3Normalize(Planes[5]); + + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Load the sphere. + XMVECTOR vCenter = XMLoadFloat3(&sh.Center); + XMVECTOR vRadius = XMVectorReplicatePtr(&sh.Radius); + + // Transform the center of the sphere into the local space of frustum. + vCenter = XMVector3InverseRotate(XMVectorSubtract(vCenter, vOrigin), vOrientation); + + // Set w of the center to one so we can dot4 with the plane. + vCenter = XMVectorInsert<0, 0, 0, 0, 1>(vCenter, XMVectorSplatOne()); + + // Check against each plane of the frustum. + XMVECTOR Outside = XMVectorFalseInt(); + XMVECTOR InsideAll = XMVectorTrueInt(); + XMVECTOR CenterInsideAll = XMVectorTrueInt(); + + XMVECTOR Dist[6]; + + for (size_t i = 0; i < 6; ++i) + { + Dist[i] = XMVector4Dot(vCenter, Planes[i]); + + // Outside the plane? + Outside = XMVectorOrInt(Outside, XMVectorGreater(Dist[i], vRadius)); + + // Fully inside the plane? + InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(Dist[i], XMVectorNegate(vRadius))); + + // Check if the center is inside the plane. + CenterInsideAll = XMVectorAndInt(CenterInsideAll, XMVectorLessOrEqual(Dist[i], Zero)); + } + + // If the sphere is outside any of the planes it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return false; + + // If the sphere is inside all planes it is fully inside. + if (XMVector4EqualInt(InsideAll, XMVectorTrueInt())) + return true; + + // If the center of the sphere is inside all planes and the sphere intersects + // one or more planes then it must intersect. + if (XMVector4EqualInt(CenterInsideAll, XMVectorTrueInt())) + return true; + + // The sphere may be outside the frustum or intersecting the frustum. + // Find the nearest feature (face, edge, or corner) on the frustum + // to the sphere. + + // The faces adjacent to each face are: + static const size_t adjacent_faces[6][4] = + { + { 2, 3, 4, 5 }, // 0 + { 2, 3, 4, 5 }, // 1 + { 0, 1, 4, 5 }, // 2 + { 0, 1, 4, 5 }, // 3 + { 0, 1, 2, 3 }, // 4 + { 0, 1, 2, 3 } + }; // 5 + + XMVECTOR Intersects = XMVectorFalseInt(); + + // Check to see if the nearest feature is one of the planes. + for (size_t i = 0; i < 6; ++i) + { + // Find the nearest point on the plane to the center of the sphere. + XMVECTOR Point = XMVectorNegativeMultiplySubtract(Planes[i], Dist[i], vCenter); + + // Set w of the point to one. + Point = XMVectorInsert<0, 0, 0, 0, 1>(Point, XMVectorSplatOne()); + + // If the point is inside the face (inside the adjacent planes) then + // this plane is the nearest feature. + XMVECTOR InsideFace = XMVectorTrueInt(); + + for (size_t j = 0; j < 4; j++) + { + size_t plane_index = adjacent_faces[i][j]; + + InsideFace = XMVectorAndInt(InsideFace, + XMVectorLessOrEqual(XMVector4Dot(Point, Planes[plane_index]), Zero)); + } + + // Since we have already checked distance from the plane we know that the + // sphere must intersect if this plane is the nearest feature. + Intersects = XMVectorOrInt(Intersects, + XMVectorAndInt(XMVectorGreater(Dist[i], Zero), InsideFace)); + } + + if (XMVector4EqualInt(Intersects, XMVectorTrueInt())) + return true; + + // Build the corners of the frustum. + XMVECTOR vRightTop = XMVectorSet(RightSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR vRightBottom = XMVectorSet(RightSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vLeftTop = XMVectorSet(LeftSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR vLeftBottom = XMVectorSet(LeftSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vNear = XMVectorReplicatePtr(&Near); + XMVECTOR vFar = XMVectorReplicatePtr(&Far); + + XMVECTOR Corners[CORNER_COUNT]; + Corners[0] = XMVectorMultiply(vRightTop, vNear); + Corners[1] = XMVectorMultiply(vRightBottom, vNear); + Corners[2] = XMVectorMultiply(vLeftTop, vNear); + Corners[3] = XMVectorMultiply(vLeftBottom, vNear); + Corners[4] = XMVectorMultiply(vRightTop, vFar); + Corners[5] = XMVectorMultiply(vRightBottom, vFar); + Corners[6] = XMVectorMultiply(vLeftTop, vFar); + Corners[7] = XMVectorMultiply(vLeftBottom, vFar); + + // The Edges are: + static const size_t edges[12][2] = + { + { 0, 1 }, { 2, 3 }, { 0, 2 }, { 1, 3 }, // Near plane + { 4, 5 }, { 6, 7 }, { 4, 6 }, { 5, 7 }, // Far plane + { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 }, + }; // Near to far + + XMVECTOR RadiusSq = XMVectorMultiply(vRadius, vRadius); + + // Check to see if the nearest feature is one of the edges (or corners). + for (size_t i = 0; i < 12; ++i) + { + size_t ei0 = edges[i][0]; + size_t ei1 = edges[i][1]; + + // Find the nearest point on the edge to the center of the sphere. + // The corners of the frustum are included as the endpoints of the edges. + XMVECTOR Point = DirectX::MathInternal::PointOnLineSegmentNearestPoint(Corners[ei0], Corners[ei1], vCenter); + + XMVECTOR Delta = XMVectorSubtract(vCenter, Point); + + XMVECTOR DistSq = XMVector3Dot(Delta, Delta); + + // If the distance to the center of the sphere to the point is less than + // the radius of the sphere then it must intersect. + Intersects = XMVectorOrInt(Intersects, XMVectorLessOrEqual(DistSq, RadiusSq)); + } + + if (XMVector4EqualInt(Intersects, XMVectorTrueInt())) + return true; + + // The sphere must be outside the frustum. + return false; +} + + +//----------------------------------------------------------------------------- +// Exact axis aligned box vs frustum test. Constructs an oriented box and uses +// the oriented box vs frustum test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingFrustum::Intersects(const BoundingBox& box) const noexcept +{ + // Make the axis aligned box oriented and do an OBB vs frustum test. + BoundingOrientedBox obox(box.Center, box.Extents, XMFLOAT4(0.f, 0.f, 0.f, 1.f)); + return Intersects(obox); +} + + +//----------------------------------------------------------------------------- +// Exact oriented box vs frustum test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingFrustum::Intersects(const BoundingOrientedBox& box) const noexcept +{ + static const XMVECTORU32 SelectY = { { { XM_SELECT_0, XM_SELECT_1, XM_SELECT_0, XM_SELECT_0 } } }; + static const XMVECTORU32 SelectZ = { { { XM_SELECT_0, XM_SELECT_0, XM_SELECT_1, XM_SELECT_0 } } }; + + XMVECTOR Zero = XMVectorZero(); + + // Build the frustum planes. + XMVECTOR Planes[6]; + Planes[0] = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + Planes[1] = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + Planes[2] = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + Planes[3] = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + Planes[4] = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + Planes[5] = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR FrustumOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(FrustumOrientation)); + + // Load the box. + XMVECTOR Center = XMLoadFloat3(&box.Center); + XMVECTOR Extents = XMLoadFloat3(&box.Extents); + XMVECTOR BoxOrientation = XMLoadFloat4(&box.Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(BoxOrientation)); + + // Transform the oriented box into the space of the frustum in order to + // minimize the number of transforms we have to do. + Center = XMVector3InverseRotate(XMVectorSubtract(Center, vOrigin), FrustumOrientation); + BoxOrientation = XMQuaternionMultiply(BoxOrientation, XMQuaternionConjugate(FrustumOrientation)); + + // Set w of the center to one so we can dot4 with the plane. + Center = XMVectorInsert<0, 0, 0, 0, 1>(Center, XMVectorSplatOne()); + + // Build the 3x3 rotation matrix that defines the box axes. + XMMATRIX R = XMMatrixRotationQuaternion(BoxOrientation); + + // Check against each plane of the frustum. + XMVECTOR Outside = XMVectorFalseInt(); + XMVECTOR InsideAll = XMVectorTrueInt(); + XMVECTOR CenterInsideAll = XMVectorTrueInt(); + + for (size_t i = 0; i < 6; ++i) + { + // Compute the distance to the center of the box. + XMVECTOR Dist = XMVector4Dot(Center, Planes[i]); + + // Project the axes of the box onto the normal of the plane. Half the + // length of the projection (sometime called the "radius") is equal to + // h(u) * abs(n dot b(u))) + h(v) * abs(n dot b(v)) + h(w) * abs(n dot b(w)) + // where h(i) are extents of the box, n is the plane normal, and b(i) are the + // axes of the box. + XMVECTOR Radius = XMVector3Dot(Planes[i], R.r[0]); + Radius = XMVectorSelect(Radius, XMVector3Dot(Planes[i], R.r[1]), SelectY); + Radius = XMVectorSelect(Radius, XMVector3Dot(Planes[i], R.r[2]), SelectZ); + Radius = XMVector3Dot(Extents, XMVectorAbs(Radius)); + + // Outside the plane? + Outside = XMVectorOrInt(Outside, XMVectorGreater(Dist, Radius)); + + // Fully inside the plane? + InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(Dist, XMVectorNegate(Radius))); + + // Check if the center is inside the plane. + CenterInsideAll = XMVectorAndInt(CenterInsideAll, XMVectorLessOrEqual(Dist, Zero)); + } + + // If the box is outside any of the planes it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return false; + + // If the box is inside all planes it is fully inside. + if (XMVector4EqualInt(InsideAll, XMVectorTrueInt())) + return true; + + // If the center of the box is inside all planes and the box intersects + // one or more planes then it must intersect. + if (XMVector4EqualInt(CenterInsideAll, XMVectorTrueInt())) + return true; + + // Build the corners of the frustum. + XMVECTOR vRightTop = XMVectorSet(RightSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR vRightBottom = XMVectorSet(RightSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vLeftTop = XMVectorSet(LeftSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR vLeftBottom = XMVectorSet(LeftSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vNear = XMVectorReplicatePtr(&Near); + XMVECTOR vFar = XMVectorReplicatePtr(&Far); + + XMVECTOR Corners[CORNER_COUNT]; + Corners[0] = XMVectorMultiply(vRightTop, vNear); + Corners[1] = XMVectorMultiply(vRightBottom, vNear); + Corners[2] = XMVectorMultiply(vLeftTop, vNear); + Corners[3] = XMVectorMultiply(vLeftBottom, vNear); + Corners[4] = XMVectorMultiply(vRightTop, vFar); + Corners[5] = XMVectorMultiply(vRightBottom, vFar); + Corners[6] = XMVectorMultiply(vLeftTop, vFar); + Corners[7] = XMVectorMultiply(vLeftBottom, vFar); + + // Test against box axes (3) + { + // Find the min/max values of the projection of the frustum onto each axis. + XMVECTOR FrustumMin, FrustumMax; + + FrustumMin = XMVector3Dot(Corners[0], R.r[0]); + FrustumMin = XMVectorSelect(FrustumMin, XMVector3Dot(Corners[0], R.r[1]), SelectY); + FrustumMin = XMVectorSelect(FrustumMin, XMVector3Dot(Corners[0], R.r[2]), SelectZ); + FrustumMax = FrustumMin; + + for (size_t i = 1; i < BoundingOrientedBox::CORNER_COUNT; ++i) + { + XMVECTOR Temp = XMVector3Dot(Corners[i], R.r[0]); + Temp = XMVectorSelect(Temp, XMVector3Dot(Corners[i], R.r[1]), SelectY); + Temp = XMVectorSelect(Temp, XMVector3Dot(Corners[i], R.r[2]), SelectZ); + + FrustumMin = XMVectorMin(FrustumMin, Temp); + FrustumMax = XMVectorMax(FrustumMax, Temp); + } + + // Project the center of the box onto the axes. + XMVECTOR BoxDist = XMVector3Dot(Center, R.r[0]); + BoxDist = XMVectorSelect(BoxDist, XMVector3Dot(Center, R.r[1]), SelectY); + BoxDist = XMVectorSelect(BoxDist, XMVector3Dot(Center, R.r[2]), SelectZ); + + // The projection of the box onto the axis is just its Center and Extents. + // if (min > box_max || max < box_min) reject; + XMVECTOR Result = XMVectorOrInt(XMVectorGreater(FrustumMin, XMVectorAdd(BoxDist, Extents)), + XMVectorLess(FrustumMax, XMVectorSubtract(BoxDist, Extents))); + + if (DirectX::MathInternal::XMVector3AnyTrue(Result)) + return false; + } + + // Test against edge/edge axes (3*6). + XMVECTOR FrustumEdgeAxis[6]; + + FrustumEdgeAxis[0] = vRightTop; + FrustumEdgeAxis[1] = vRightBottom; + FrustumEdgeAxis[2] = vLeftTop; + FrustumEdgeAxis[3] = vLeftBottom; + FrustumEdgeAxis[4] = XMVectorSubtract(vRightTop, vLeftTop); + FrustumEdgeAxis[5] = XMVectorSubtract(vLeftBottom, vLeftTop); + + for (size_t i = 0; i < 3; ++i) + { + for (size_t j = 0; j < 6; j++) + { + // Compute the axis we are going to test. + XMVECTOR Axis = XMVector3Cross(R.r[i], FrustumEdgeAxis[j]); + + // Find the min/max values of the projection of the frustum onto the axis. + XMVECTOR FrustumMin, FrustumMax; + + FrustumMin = FrustumMax = XMVector3Dot(Axis, Corners[0]); + + for (size_t k = 1; k < CORNER_COUNT; k++) + { + XMVECTOR Temp = XMVector3Dot(Axis, Corners[k]); + FrustumMin = XMVectorMin(FrustumMin, Temp); + FrustumMax = XMVectorMax(FrustumMax, Temp); + } + + // Project the center of the box onto the axis. + XMVECTOR Dist = XMVector3Dot(Center, Axis); + + // Project the axes of the box onto the axis to find the "radius" of the box. + XMVECTOR Radius = XMVector3Dot(Axis, R.r[0]); + Radius = XMVectorSelect(Radius, XMVector3Dot(Axis, R.r[1]), SelectY); + Radius = XMVectorSelect(Radius, XMVector3Dot(Axis, R.r[2]), SelectZ); + Radius = XMVector3Dot(Extents, XMVectorAbs(Radius)); + + // if (center > max + radius || center < min - radius) reject; + Outside = XMVectorOrInt(Outside, XMVectorGreater(Dist, XMVectorAdd(FrustumMax, Radius))); + Outside = XMVectorOrInt(Outside, XMVectorLess(Dist, XMVectorSubtract(FrustumMin, Radius))); + } + } + + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return false; + + // If we did not find a separating plane then the box must intersect the frustum. + return true; +} + + +//----------------------------------------------------------------------------- +// Exact frustum vs frustum test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool BoundingFrustum::Intersects(const BoundingFrustum& fr) const noexcept +{ + // Load origin and orientation of frustum B. + XMVECTOR OriginB = XMLoadFloat3(&Origin); + XMVECTOR OrientationB = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(OrientationB)); + + // Build the planes of frustum B. + XMVECTOR AxisB[6]; + AxisB[0] = XMVectorSet(0.0f, 0.0f, -1.0f, 0.0f); + AxisB[1] = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); + AxisB[2] = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + AxisB[3] = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + AxisB[4] = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + AxisB[5] = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + + XMVECTOR PlaneDistB[6]; + PlaneDistB[0] = XMVectorNegate(XMVectorReplicatePtr(&Near)); + PlaneDistB[1] = XMVectorReplicatePtr(&Far); + PlaneDistB[2] = XMVectorZero(); + PlaneDistB[3] = XMVectorZero(); + PlaneDistB[4] = XMVectorZero(); + PlaneDistB[5] = XMVectorZero(); + + // Load origin and orientation of frustum A. + XMVECTOR OriginA = XMLoadFloat3(&fr.Origin); + XMVECTOR OrientationA = XMLoadFloat4(&fr.Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(OrientationA)); + + // Transform frustum A into the space of the frustum B in order to + // minimize the number of transforms we have to do. + OriginA = XMVector3InverseRotate(XMVectorSubtract(OriginA, OriginB), OrientationB); + OrientationA = XMQuaternionMultiply(OrientationA, XMQuaternionConjugate(OrientationB)); + + // Build the corners of frustum A (in the local space of B). + XMVECTOR RightTopA = XMVectorSet(fr.RightSlope, fr.TopSlope, 1.0f, 0.0f); + XMVECTOR RightBottomA = XMVectorSet(fr.RightSlope, fr.BottomSlope, 1.0f, 0.0f); + XMVECTOR LeftTopA = XMVectorSet(fr.LeftSlope, fr.TopSlope, 1.0f, 0.0f); + XMVECTOR LeftBottomA = XMVectorSet(fr.LeftSlope, fr.BottomSlope, 1.0f, 0.0f); + XMVECTOR NearA = XMVectorReplicatePtr(&fr.Near); + XMVECTOR FarA = XMVectorReplicatePtr(&fr.Far); + + RightTopA = XMVector3Rotate(RightTopA, OrientationA); + RightBottomA = XMVector3Rotate(RightBottomA, OrientationA); + LeftTopA = XMVector3Rotate(LeftTopA, OrientationA); + LeftBottomA = XMVector3Rotate(LeftBottomA, OrientationA); + + XMVECTOR CornersA[CORNER_COUNT]; + CornersA[0] = XMVectorMultiplyAdd(RightTopA, NearA, OriginA); + CornersA[1] = XMVectorMultiplyAdd(RightBottomA, NearA, OriginA); + CornersA[2] = XMVectorMultiplyAdd(LeftTopA, NearA, OriginA); + CornersA[3] = XMVectorMultiplyAdd(LeftBottomA, NearA, OriginA); + CornersA[4] = XMVectorMultiplyAdd(RightTopA, FarA, OriginA); + CornersA[5] = XMVectorMultiplyAdd(RightBottomA, FarA, OriginA); + CornersA[6] = XMVectorMultiplyAdd(LeftTopA, FarA, OriginA); + CornersA[7] = XMVectorMultiplyAdd(LeftBottomA, FarA, OriginA); + + // Check frustum A against each plane of frustum B. + XMVECTOR Outside = XMVectorFalseInt(); + XMVECTOR InsideAll = XMVectorTrueInt(); + + for (size_t i = 0; i < 6; ++i) + { + // Find the min/max projection of the frustum onto the plane normal. + XMVECTOR Min, Max; + + Min = Max = XMVector3Dot(AxisB[i], CornersA[0]); + + for (size_t j = 1; j < CORNER_COUNT; j++) + { + XMVECTOR Temp = XMVector3Dot(AxisB[i], CornersA[j]); + Min = XMVectorMin(Min, Temp); + Max = XMVectorMax(Max, Temp); + } + + // Outside the plane? + Outside = XMVectorOrInt(Outside, XMVectorGreater(Min, PlaneDistB[i])); + + // Fully inside the plane? + InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(Max, PlaneDistB[i])); + } + + // If the frustum A is outside any of the planes of frustum B it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return false; + + // If frustum A is inside all planes of frustum B it is fully inside. + if (XMVector4EqualInt(InsideAll, XMVectorTrueInt())) + return true; + + // Build the corners of frustum B. + XMVECTOR RightTopB = XMVectorSet(RightSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR RightBottomB = XMVectorSet(RightSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR LeftTopB = XMVectorSet(LeftSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR LeftBottomB = XMVectorSet(LeftSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR NearB = XMVectorReplicatePtr(&Near); + XMVECTOR FarB = XMVectorReplicatePtr(&Far); + + XMVECTOR CornersB[BoundingFrustum::CORNER_COUNT]; + CornersB[0] = XMVectorMultiply(RightTopB, NearB); + CornersB[1] = XMVectorMultiply(RightBottomB, NearB); + CornersB[2] = XMVectorMultiply(LeftTopB, NearB); + CornersB[3] = XMVectorMultiply(LeftBottomB, NearB); + CornersB[4] = XMVectorMultiply(RightTopB, FarB); + CornersB[5] = XMVectorMultiply(RightBottomB, FarB); + CornersB[6] = XMVectorMultiply(LeftTopB, FarB); + CornersB[7] = XMVectorMultiply(LeftBottomB, FarB); + + // Build the planes of frustum A (in the local space of B). + XMVECTOR AxisA[6]; + XMVECTOR PlaneDistA[6]; + + AxisA[0] = XMVectorSet(0.0f, 0.0f, -1.0f, 0.0f); + AxisA[1] = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); + AxisA[2] = XMVectorSet(1.0f, 0.0f, -fr.RightSlope, 0.0f); + AxisA[3] = XMVectorSet(-1.0f, 0.0f, fr.LeftSlope, 0.0f); + AxisA[4] = XMVectorSet(0.0f, 1.0f, -fr.TopSlope, 0.0f); + AxisA[5] = XMVectorSet(0.0f, -1.0f, fr.BottomSlope, 0.0f); + + AxisA[0] = XMVector3Rotate(AxisA[0], OrientationA); + AxisA[1] = XMVectorNegate(AxisA[0]); + AxisA[2] = XMVector3Rotate(AxisA[2], OrientationA); + AxisA[3] = XMVector3Rotate(AxisA[3], OrientationA); + AxisA[4] = XMVector3Rotate(AxisA[4], OrientationA); + AxisA[5] = XMVector3Rotate(AxisA[5], OrientationA); + + PlaneDistA[0] = XMVector3Dot(AxisA[0], CornersA[0]); // Re-use corner on near plane. + PlaneDistA[1] = XMVector3Dot(AxisA[1], CornersA[4]); // Re-use corner on far plane. + PlaneDistA[2] = XMVector3Dot(AxisA[2], OriginA); + PlaneDistA[3] = XMVector3Dot(AxisA[3], OriginA); + PlaneDistA[4] = XMVector3Dot(AxisA[4], OriginA); + PlaneDistA[5] = XMVector3Dot(AxisA[5], OriginA); + + // Check each axis of frustum A for a seperating plane (5). + for (size_t i = 0; i < 6; ++i) + { + // Find the minimum projection of the frustum onto the plane normal. + XMVECTOR Min; + + Min = XMVector3Dot(AxisA[i], CornersB[0]); + + for (size_t j = 1; j < CORNER_COUNT; j++) + { + XMVECTOR Temp = XMVector3Dot(AxisA[i], CornersB[j]); + Min = XMVectorMin(Min, Temp); + } + + // Outside the plane? + Outside = XMVectorOrInt(Outside, XMVectorGreater(Min, PlaneDistA[i])); + } + + // If the frustum B is outside any of the planes of frustum A it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return false; + + // Check edge/edge axes (6 * 6). + XMVECTOR FrustumEdgeAxisA[6]; + FrustumEdgeAxisA[0] = RightTopA; + FrustumEdgeAxisA[1] = RightBottomA; + FrustumEdgeAxisA[2] = LeftTopA; + FrustumEdgeAxisA[3] = LeftBottomA; + FrustumEdgeAxisA[4] = XMVectorSubtract(RightTopA, LeftTopA); + FrustumEdgeAxisA[5] = XMVectorSubtract(LeftBottomA, LeftTopA); + + XMVECTOR FrustumEdgeAxisB[6]; + FrustumEdgeAxisB[0] = RightTopB; + FrustumEdgeAxisB[1] = RightBottomB; + FrustumEdgeAxisB[2] = LeftTopB; + FrustumEdgeAxisB[3] = LeftBottomB; + FrustumEdgeAxisB[4] = XMVectorSubtract(RightTopB, LeftTopB); + FrustumEdgeAxisB[5] = XMVectorSubtract(LeftBottomB, LeftTopB); + + for (size_t i = 0; i < 6; ++i) + { + for (size_t j = 0; j < 6; j++) + { + // Compute the axis we are going to test. + XMVECTOR Axis = XMVector3Cross(FrustumEdgeAxisA[i], FrustumEdgeAxisB[j]); + + // Find the min/max values of the projection of both frustums onto the axis. + XMVECTOR MinA, MaxA; + XMVECTOR MinB, MaxB; + + MinA = MaxA = XMVector3Dot(Axis, CornersA[0]); + MinB = MaxB = XMVector3Dot(Axis, CornersB[0]); + + for (size_t k = 1; k < CORNER_COUNT; k++) + { + XMVECTOR TempA = XMVector3Dot(Axis, CornersA[k]); + MinA = XMVectorMin(MinA, TempA); + MaxA = XMVectorMax(MaxA, TempA); + + XMVECTOR TempB = XMVector3Dot(Axis, CornersB[k]); + MinB = XMVectorMin(MinB, TempB); + MaxB = XMVectorMax(MaxB, TempB); + } + + // if (MinA > MaxB || MinB > MaxA) reject + Outside = XMVectorOrInt(Outside, XMVectorGreater(MinA, MaxB)); + Outside = XMVectorOrInt(Outside, XMVectorGreater(MinB, MaxA)); + } + } + + // If there is a seperating plane, then the frustums do not intersect. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return false; + + // If we did not find a separating plane then the frustums intersect. + return true; +} + + +//----------------------------------------------------------------------------- +// Triangle vs frustum test. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool XM_CALLCONV BoundingFrustum::Intersects(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2) const noexcept +{ + // Build the frustum planes (NOTE: D is negated from the usual). + XMVECTOR Planes[6]; + Planes[0] = XMVectorSet(0.0f, 0.0f, -1.0f, -Near); + Planes[1] = XMVectorSet(0.0f, 0.0f, 1.0f, Far); + Planes[2] = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + Planes[3] = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + Planes[4] = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + Planes[5] = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Transform triangle into the local space of frustum. + XMVECTOR TV0 = XMVector3InverseRotate(XMVectorSubtract(V0, vOrigin), vOrientation); + XMVECTOR TV1 = XMVector3InverseRotate(XMVectorSubtract(V1, vOrigin), vOrientation); + XMVECTOR TV2 = XMVector3InverseRotate(XMVectorSubtract(V2, vOrigin), vOrientation); + + // Test each vertex of the triangle against the frustum planes. + XMVECTOR Outside = XMVectorFalseInt(); + XMVECTOR InsideAll = XMVectorTrueInt(); + + for (size_t i = 0; i < 6; ++i) + { + XMVECTOR Dist0 = XMVector3Dot(TV0, Planes[i]); + XMVECTOR Dist1 = XMVector3Dot(TV1, Planes[i]); + XMVECTOR Dist2 = XMVector3Dot(TV2, Planes[i]); + + XMVECTOR MinDist = XMVectorMin(Dist0, Dist1); + MinDist = XMVectorMin(MinDist, Dist2); + XMVECTOR MaxDist = XMVectorMax(Dist0, Dist1); + MaxDist = XMVectorMax(MaxDist, Dist2); + + XMVECTOR PlaneDist = XMVectorSplatW(Planes[i]); + + // Outside the plane? + Outside = XMVectorOrInt(Outside, XMVectorGreater(MinDist, PlaneDist)); + + // Fully inside the plane? + InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(MaxDist, PlaneDist)); + } + + // If the triangle is outside any of the planes it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return false; + + // If the triangle is inside all planes it is fully inside. + if (XMVector4EqualInt(InsideAll, XMVectorTrueInt())) + return true; + + // Build the corners of the frustum. + XMVECTOR vRightTop = XMVectorSet(RightSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR vRightBottom = XMVectorSet(RightSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vLeftTop = XMVectorSet(LeftSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR vLeftBottom = XMVectorSet(LeftSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vNear = XMVectorReplicatePtr(&Near); + XMVECTOR vFar = XMVectorReplicatePtr(&Far); + + XMVECTOR Corners[CORNER_COUNT]; + Corners[0] = XMVectorMultiply(vRightTop, vNear); + Corners[1] = XMVectorMultiply(vRightBottom, vNear); + Corners[2] = XMVectorMultiply(vLeftTop, vNear); + Corners[3] = XMVectorMultiply(vLeftBottom, vNear); + Corners[4] = XMVectorMultiply(vRightTop, vFar); + Corners[5] = XMVectorMultiply(vRightBottom, vFar); + Corners[6] = XMVectorMultiply(vLeftTop, vFar); + Corners[7] = XMVectorMultiply(vLeftBottom, vFar); + + // Test the plane of the triangle. + XMVECTOR Normal = XMVector3Cross(XMVectorSubtract(V1, V0), XMVectorSubtract(V2, V0)); + XMVECTOR Dist = XMVector3Dot(Normal, V0); + + XMVECTOR MinDist, MaxDist; + MinDist = MaxDist = XMVector3Dot(Corners[0], Normal); + for (size_t i = 1; i < CORNER_COUNT; ++i) + { + XMVECTOR Temp = XMVector3Dot(Corners[i], Normal); + MinDist = XMVectorMin(MinDist, Temp); + MaxDist = XMVectorMax(MaxDist, Temp); + } + + Outside = XMVectorOrInt(XMVectorGreater(MinDist, Dist), XMVectorLess(MaxDist, Dist)); + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return false; + + // Check the edge/edge axes (3*6). + XMVECTOR TriangleEdgeAxis[3]; + TriangleEdgeAxis[0] = XMVectorSubtract(V1, V0); + TriangleEdgeAxis[1] = XMVectorSubtract(V2, V1); + TriangleEdgeAxis[2] = XMVectorSubtract(V0, V2); + + XMVECTOR FrustumEdgeAxis[6]; + FrustumEdgeAxis[0] = vRightTop; + FrustumEdgeAxis[1] = vRightBottom; + FrustumEdgeAxis[2] = vLeftTop; + FrustumEdgeAxis[3] = vLeftBottom; + FrustumEdgeAxis[4] = XMVectorSubtract(vRightTop, vLeftTop); + FrustumEdgeAxis[5] = XMVectorSubtract(vLeftBottom, vLeftTop); + + for (size_t i = 0; i < 3; ++i) + { + for (size_t j = 0; j < 6; j++) + { + // Compute the axis we are going to test. + XMVECTOR Axis = XMVector3Cross(TriangleEdgeAxis[i], FrustumEdgeAxis[j]); + + // Find the min/max of the projection of the triangle onto the axis. + XMVECTOR MinA, MaxA; + + XMVECTOR Dist0 = XMVector3Dot(V0, Axis); + XMVECTOR Dist1 = XMVector3Dot(V1, Axis); + XMVECTOR Dist2 = XMVector3Dot(V2, Axis); + + MinA = XMVectorMin(Dist0, Dist1); + MinA = XMVectorMin(MinA, Dist2); + MaxA = XMVectorMax(Dist0, Dist1); + MaxA = XMVectorMax(MaxA, Dist2); + + // Find the min/max of the projection of the frustum onto the axis. + XMVECTOR MinB, MaxB; + + MinB = MaxB = XMVector3Dot(Axis, Corners[0]); + + for (size_t k = 1; k < CORNER_COUNT; k++) + { + XMVECTOR Temp = XMVector3Dot(Axis, Corners[k]); + MinB = XMVectorMin(MinB, Temp); + MaxB = XMVectorMax(MaxB, Temp); + } + + // if (MinA > MaxB || MinB > MaxA) reject; + Outside = XMVectorOrInt(Outside, XMVectorGreater(MinA, MaxB)); + Outside = XMVectorOrInt(Outside, XMVectorGreater(MinB, MaxA)); + } + } + + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return false; + + // If we did not find a separating plane then the triangle must intersect the frustum. + return true; +} + + +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline PlaneIntersectionType XM_CALLCONV BoundingFrustum::Intersects(FXMVECTOR Plane) const noexcept +{ + assert(DirectX::MathInternal::XMPlaneIsUnit(Plane)); + + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Set w of the origin to one so we can dot4 with a plane. + vOrigin = XMVectorInsert<0, 0, 0, 0, 1>(vOrigin, XMVectorSplatOne()); + + // Build the corners of the frustum (in world space). + XMVECTOR RightTop = XMVectorSet(RightSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR RightBottom = XMVectorSet(RightSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR LeftTop = XMVectorSet(LeftSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR LeftBottom = XMVectorSet(LeftSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vNear = XMVectorReplicatePtr(&Near); + XMVECTOR vFar = XMVectorReplicatePtr(&Far); + + RightTop = XMVector3Rotate(RightTop, vOrientation); + RightBottom = XMVector3Rotate(RightBottom, vOrientation); + LeftTop = XMVector3Rotate(LeftTop, vOrientation); + LeftBottom = XMVector3Rotate(LeftBottom, vOrientation); + + XMVECTOR Corners0 = XMVectorMultiplyAdd(RightTop, vNear, vOrigin); + XMVECTOR Corners1 = XMVectorMultiplyAdd(RightBottom, vNear, vOrigin); + XMVECTOR Corners2 = XMVectorMultiplyAdd(LeftTop, vNear, vOrigin); + XMVECTOR Corners3 = XMVectorMultiplyAdd(LeftBottom, vNear, vOrigin); + XMVECTOR Corners4 = XMVectorMultiplyAdd(RightTop, vFar, vOrigin); + XMVECTOR Corners5 = XMVectorMultiplyAdd(RightBottom, vFar, vOrigin); + XMVECTOR Corners6 = XMVectorMultiplyAdd(LeftTop, vFar, vOrigin); + XMVECTOR Corners7 = XMVectorMultiplyAdd(LeftBottom, vFar, vOrigin); + + XMVECTOR Outside, Inside; + DirectX::MathInternal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3, + Corners4, Corners5, Corners6, Corners7, + Plane, Outside, Inside); + + // If the frustum is outside any plane it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return FRONT; + + // If the frustum is inside all planes it is inside. + if (XMVector4EqualInt(Inside, XMVectorTrueInt())) + return BACK; + + // The frustum is not inside all planes or outside a plane it intersects. + return INTERSECTING; +} + + +//----------------------------------------------------------------------------- +// Ray vs. frustum test +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline bool XM_CALLCONV BoundingFrustum::Intersects(FXMVECTOR rayOrigin, FXMVECTOR Direction, float& Dist) const noexcept +{ + // If ray starts inside the frustum, return a distance of 0 for the hit + if (Contains(rayOrigin) == CONTAINS) + { + Dist = 0.0f; + return true; + } + + // Build the frustum planes. + XMVECTOR Planes[6]; + Planes[0] = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + Planes[1] = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + Planes[2] = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + Planes[3] = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + Planes[4] = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + Planes[5] = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + + // Load origin and orientation of the frustum. + XMVECTOR frOrigin = XMLoadFloat3(&Origin); + XMVECTOR frOrientation = XMLoadFloat4(&Orientation); + + // This algorithm based on "Fast Ray-Convex Polyhedron Intersectin," in James Arvo, ed., Graphics Gems II pp. 247-250 + float tnear = -FLT_MAX; + float tfar = FLT_MAX; + + for (size_t i = 0; i < 6; ++i) + { + XMVECTOR Plane = DirectX::MathInternal::XMPlaneTransform(Planes[i], frOrientation, frOrigin); + Plane = XMPlaneNormalize(Plane); + + XMVECTOR AxisDotOrigin = XMPlaneDotCoord(Plane, rayOrigin); + XMVECTOR AxisDotDirection = XMVector3Dot(Plane, Direction); + + if (XMVector3LessOrEqual(XMVectorAbs(AxisDotDirection), g_RayEpsilon)) + { + // Ray is parallel to plane - check if ray origin is inside plane's + if (XMVector3Greater(AxisDotOrigin, g_XMZero)) + { + // Ray origin is outside half-space. + Dist = 0.f; + return false; + } + } + else + { + // Ray not parallel - get distance to plane. + float vd = XMVectorGetX(AxisDotDirection); + float vn = XMVectorGetX(AxisDotOrigin); + float t = -vn / vd; + if (vd < 0.0f) + { + // Front face - T is a near point. + if (t > tfar) + { + Dist = 0.f; + return false; + } + if (t > tnear) + { + // Hit near face. + tnear = t; + } + } + else + { + // back face - T is far point. + if (t < tnear) + { + Dist = 0.f; + return false; + } + if (t < tfar) + { + // Hit far face. + tfar = t; + } + } + } + } + + // Survived all tests. + // Note: if ray originates on polyhedron, may want to change 0.0f to some + // epsilon to avoid intersecting the originating face. + float distance = (tnear >= 0.0f) ? tnear : tfar; + if (distance >= 0.0f) + { + Dist = distance; + return true; + } + + Dist = 0.f; + return false; +} + + +//----------------------------------------------------------------------------- +// Test a frustum vs 6 planes (typically forming another frustum). +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline ContainmentType XM_CALLCONV BoundingFrustum::ContainedBy( + FXMVECTOR Plane0, FXMVECTOR Plane1, FXMVECTOR Plane2, + GXMVECTOR Plane3, + HXMVECTOR Plane4, HXMVECTOR Plane5) const noexcept +{ + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + assert(DirectX::MathInternal::XMQuaternionIsUnit(vOrientation)); + + // Set w of the origin to one so we can dot4 with a plane. + vOrigin = XMVectorInsert<0, 0, 0, 0, 1>(vOrigin, XMVectorSplatOne()); + + // Build the corners of the frustum (in world space). + XMVECTOR RightTop = XMVectorSet(RightSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR RightBottom = XMVectorSet(RightSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR LeftTop = XMVectorSet(LeftSlope, TopSlope, 1.0f, 0.0f); + XMVECTOR LeftBottom = XMVectorSet(LeftSlope, BottomSlope, 1.0f, 0.0f); + XMVECTOR vNear = XMVectorReplicatePtr(&Near); + XMVECTOR vFar = XMVectorReplicatePtr(&Far); + + RightTop = XMVector3Rotate(RightTop, vOrientation); + RightBottom = XMVector3Rotate(RightBottom, vOrientation); + LeftTop = XMVector3Rotate(LeftTop, vOrientation); + LeftBottom = XMVector3Rotate(LeftBottom, vOrientation); + + XMVECTOR Corners0 = XMVectorMultiplyAdd(RightTop, vNear, vOrigin); + XMVECTOR Corners1 = XMVectorMultiplyAdd(RightBottom, vNear, vOrigin); + XMVECTOR Corners2 = XMVectorMultiplyAdd(LeftTop, vNear, vOrigin); + XMVECTOR Corners3 = XMVectorMultiplyAdd(LeftBottom, vNear, vOrigin); + XMVECTOR Corners4 = XMVectorMultiplyAdd(RightTop, vFar, vOrigin); + XMVECTOR Corners5 = XMVectorMultiplyAdd(RightBottom, vFar, vOrigin); + XMVECTOR Corners6 = XMVectorMultiplyAdd(LeftTop, vFar, vOrigin); + XMVECTOR Corners7 = XMVectorMultiplyAdd(LeftBottom, vFar, vOrigin); + + XMVECTOR Outside, Inside; + + // Test against each plane. + DirectX::MathInternal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3, + Corners4, Corners5, Corners6, Corners7, + Plane0, Outside, Inside); + + XMVECTOR AnyOutside = Outside; + XMVECTOR AllInside = Inside; + + DirectX::MathInternal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3, + Corners4, Corners5, Corners6, Corners7, + Plane1, Outside, Inside); + + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3, + Corners4, Corners5, Corners6, Corners7, + Plane2, Outside, Inside); + + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3, + Corners4, Corners5, Corners6, Corners7, + Plane3, Outside, Inside); + + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3, + Corners4, Corners5, Corners6, Corners7, + Plane4, Outside, Inside); + + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3, + Corners4, Corners5, Corners6, Corners7, + Plane5, Outside, Inside); + + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + // If the frustum is outside any plane it is outside. + if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) + return DISJOINT; + + // If the frustum is inside all planes it is inside. + if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) + return CONTAINS; + + // The frustum is not inside all planes or outside a plane, it may intersect. + return INTERSECTS; +} + + +//----------------------------------------------------------------------------- +// Build the 6 frustum planes from a frustum. +// +// The intended use for these routines is for fast culling to a view frustum. +// When the volume being tested against a view frustum is small relative to the +// view frustum it is usually either inside all six planes of the frustum +// (CONTAINS) or outside one of the planes of the frustum (DISJOINT). If neither +// of these cases is true then it may or may not be intersecting the frustum +// (INTERSECTS) +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void BoundingFrustum::GetPlanes(XMVECTOR* NearPlane, XMVECTOR* FarPlane, XMVECTOR* RightPlane, + XMVECTOR* LeftPlane, XMVECTOR* TopPlane, XMVECTOR* BottomPlane) const noexcept +{ + // Load origin and orientation of the frustum. + XMVECTOR vOrigin = XMLoadFloat3(&Origin); + XMVECTOR vOrientation = XMLoadFloat4(&Orientation); + + if (NearPlane) + { + XMVECTOR vNearPlane = XMVectorSet(0.0f, 0.0f, -1.0f, Near); + vNearPlane = DirectX::MathInternal::XMPlaneTransform(vNearPlane, vOrientation, vOrigin); + *NearPlane = XMPlaneNormalize(vNearPlane); + } + + if (FarPlane) + { + XMVECTOR vFarPlane = XMVectorSet(0.0f, 0.0f, 1.0f, -Far); + vFarPlane = DirectX::MathInternal::XMPlaneTransform(vFarPlane, vOrientation, vOrigin); + *FarPlane = XMPlaneNormalize(vFarPlane); + } + + if (RightPlane) + { + XMVECTOR vRightPlane = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f); + vRightPlane = DirectX::MathInternal::XMPlaneTransform(vRightPlane, vOrientation, vOrigin); + *RightPlane = XMPlaneNormalize(vRightPlane); + } + + if (LeftPlane) + { + XMVECTOR vLeftPlane = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f); + vLeftPlane = DirectX::MathInternal::XMPlaneTransform(vLeftPlane, vOrientation, vOrigin); + *LeftPlane = XMPlaneNormalize(vLeftPlane); + } + + if (TopPlane) + { + XMVECTOR vTopPlane = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f); + vTopPlane = DirectX::MathInternal::XMPlaneTransform(vTopPlane, vOrientation, vOrigin); + *TopPlane = XMPlaneNormalize(vTopPlane); + } + + if (BottomPlane) + { + XMVECTOR vBottomPlane = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f); + vBottomPlane = DirectX::MathInternal::XMPlaneTransform(vBottomPlane, vOrientation, vOrigin); + *BottomPlane = XMPlaneNormalize(vBottomPlane); + } +} + + +//----------------------------------------------------------------------------- +// Build a frustum from a persepective projection matrix. The matrix may only +// contain a projection; any rotation, translation or scale will cause the +// constructed frustum to be incorrect. +//----------------------------------------------------------------------------- +_Use_decl_annotations_ +inline void XM_CALLCONV BoundingFrustum::CreateFromMatrix(BoundingFrustum& Out, FXMMATRIX Projection, bool rhcoords) noexcept +{ + // Corners of the projection frustum in NDC space. + static XMVECTORF32 NDCPoints[6] = + { + { { { 1.0f, 0.0f, 1.0f, 1.0f } } }, // right (at far plane) + { { { -1.0f, 0.0f, 1.0f, 1.0f } } }, // left + { { { 0.0f, 1.0f, 1.0f, 1.0f } } }, // top + { { { 0.0f, -1.0f, 1.0f, 1.0f } } }, // bottom + + { { { 0.0f, 0.0f, 0.0f, 1.0f } } }, // near + { { { 0.0f, 0.0f, 1.0f, 1.0f } } } // far + }; + + XMVECTOR Determinant; + XMMATRIX matInverse = XMMatrixInverse(&Determinant, Projection); + + // Compute the frustum corners in world space. + XMVECTOR Points[6]; + + for (size_t i = 0; i < 6; ++i) + { + // Transform point. + Points[i] = XMVector4Transform(NDCPoints[i], matInverse); + } + + Out.Origin = XMFLOAT3(0.0f, 0.0f, 0.0f); + Out.Orientation = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f); + + // Compute the slopes. + Points[0] = XMVectorMultiply(Points[0], XMVectorReciprocal(XMVectorSplatZ(Points[0]))); + Points[1] = XMVectorMultiply(Points[1], XMVectorReciprocal(XMVectorSplatZ(Points[1]))); + Points[2] = XMVectorMultiply(Points[2], XMVectorReciprocal(XMVectorSplatZ(Points[2]))); + Points[3] = XMVectorMultiply(Points[3], XMVectorReciprocal(XMVectorSplatZ(Points[3]))); + + Out.RightSlope = XMVectorGetX(Points[0]); + Out.LeftSlope = XMVectorGetX(Points[1]); + Out.TopSlope = XMVectorGetY(Points[2]); + Out.BottomSlope = XMVectorGetY(Points[3]); + + // Compute near and far. + Points[4] = XMVectorMultiply(Points[4], XMVectorReciprocal(XMVectorSplatW(Points[4]))); + Points[5] = XMVectorMultiply(Points[5], XMVectorReciprocal(XMVectorSplatW(Points[5]))); + + if (rhcoords) + { + Out.Near = XMVectorGetZ(Points[5]); + Out.Far = XMVectorGetZ(Points[4]); + } + else + { + Out.Near = XMVectorGetZ(Points[4]); + Out.Far = XMVectorGetZ(Points[5]); + } +} + + +/**************************************************************************** + * + * TriangleTests + * + ****************************************************************************/ + +namespace TriangleTests +{ + + //----------------------------------------------------------------------------- + // Compute the intersection of a ray (Origin, Direction) with a triangle + // (V0, V1, V2). Return true if there is an intersection and also set *pDist + // to the distance along the ray to the intersection. + // + // The algorithm is based on Moller, Tomas and Trumbore, "Fast, Minimum Storage + // Ray-Triangle Intersection", Journal of Graphics Tools, vol. 2, no. 1, + // pp 21-28, 1997. + //----------------------------------------------------------------------------- + _Use_decl_annotations_ + inline bool XM_CALLCONV Intersects( + FXMVECTOR Origin, FXMVECTOR Direction, FXMVECTOR V0, + GXMVECTOR V1, + HXMVECTOR V2, float& Dist) noexcept + { + assert(DirectX::MathInternal::XMVector3IsUnit(Direction)); + + XMVECTOR Zero = XMVectorZero(); + + XMVECTOR e1 = XMVectorSubtract(V1, V0); + XMVECTOR e2 = XMVectorSubtract(V2, V0); + + // p = Direction ^ e2; + XMVECTOR p = XMVector3Cross(Direction, e2); + + // det = e1 * p; + XMVECTOR det = XMVector3Dot(e1, p); + + XMVECTOR u, v, t; + + if (XMVector3GreaterOrEqual(det, g_RayEpsilon)) + { + // Determinate is positive (front side of the triangle). + XMVECTOR s = XMVectorSubtract(Origin, V0); + + // u = s * p; + u = XMVector3Dot(s, p); + + XMVECTOR NoIntersection = XMVectorLess(u, Zero); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(u, det)); + + // q = s ^ e1; + XMVECTOR q = XMVector3Cross(s, e1); + + // v = Direction * q; + v = XMVector3Dot(Direction, q); + + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(v, Zero)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(XMVectorAdd(u, v), det)); + + // t = e2 * q; + t = XMVector3Dot(e2, q); + + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(t, Zero)); + + if (XMVector4EqualInt(NoIntersection, XMVectorTrueInt())) + { + Dist = 0.f; + return false; + } + } + else if (XMVector3LessOrEqual(det, g_RayNegEpsilon)) + { + // Determinate is negative (back side of the triangle). + XMVECTOR s = XMVectorSubtract(Origin, V0); + + // u = s * p; + u = XMVector3Dot(s, p); + + XMVECTOR NoIntersection = XMVectorGreater(u, Zero); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(u, det)); + + // q = s ^ e1; + XMVECTOR q = XMVector3Cross(s, e1); + + // v = Direction * q; + v = XMVector3Dot(Direction, q); + + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(v, Zero)); + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(XMVectorAdd(u, v), det)); + + // t = e2 * q; + t = XMVector3Dot(e2, q); + + NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(t, Zero)); + + if (XMVector4EqualInt(NoIntersection, XMVectorTrueInt())) + { + Dist = 0.f; + return false; + } + } + else + { + // Parallel ray. + Dist = 0.f; + return false; + } + + t = XMVectorDivide(t, det); + + // (u / det) and (v / dev) are the barycentric cooridinates of the intersection. + + // Store the x-component to *pDist + XMStoreFloat(&Dist, t); + + return true; + } + + + //----------------------------------------------------------------------------- + // Test if two triangles intersect. + // + // The final test of algorithm is based on Shen, Heng, and Tang, "A Fast + // Triangle-Triangle Overlap Test Using Signed Distances", Journal of Graphics + // Tools, vol. 8, no. 1, pp 17-23, 2003 and Guigue and Devillers, "Fast and + // Robust Triangle-Triangle Overlap Test Using Orientation Predicates", Journal + // of Graphics Tools, vol. 8, no. 1, pp 25-32, 2003. + // + // The final test could be considered an edge-edge separating plane test with + // the 9 possible cases narrowed down to the only two pairs of edges that can + // actaully result in a seperation. + //----------------------------------------------------------------------------- + _Use_decl_annotations_ + inline bool XM_CALLCONV Intersects(FXMVECTOR A0, FXMVECTOR A1, FXMVECTOR A2, GXMVECTOR B0, HXMVECTOR B1, HXMVECTOR B2) noexcept + { + static const XMVECTORU32 SelectY = { { { XM_SELECT_0, XM_SELECT_1, XM_SELECT_0, XM_SELECT_0 } } }; + static const XMVECTORU32 SelectZ = { { { XM_SELECT_0, XM_SELECT_0, XM_SELECT_1, XM_SELECT_0 } } }; + static const XMVECTORU32 Select0111 = { { { XM_SELECT_0, XM_SELECT_1, XM_SELECT_1, XM_SELECT_1 } } }; + static const XMVECTORU32 Select1011 = { { { XM_SELECT_1, XM_SELECT_0, XM_SELECT_1, XM_SELECT_1 } } }; + static const XMVECTORU32 Select1101 = { { { XM_SELECT_1, XM_SELECT_1, XM_SELECT_0, XM_SELECT_1 } } }; + + XMVECTOR Zero = XMVectorZero(); + + // Compute the normal of triangle A. + XMVECTOR N1 = XMVector3Cross(XMVectorSubtract(A1, A0), XMVectorSubtract(A2, A0)); + + // Assert that the triangle is not degenerate. + assert(!XMVector3Equal(N1, Zero)); + + // Test points of B against the plane of A. + XMVECTOR BDist = XMVector3Dot(N1, XMVectorSubtract(B0, A0)); + BDist = XMVectorSelect(BDist, XMVector3Dot(N1, XMVectorSubtract(B1, A0)), SelectY); + BDist = XMVectorSelect(BDist, XMVector3Dot(N1, XMVectorSubtract(B2, A0)), SelectZ); + + // Ensure robustness with co-planar triangles by zeroing small distances. + uint32_t BDistIsZeroCR; + XMVECTOR BDistIsZero = XMVectorGreaterR(&BDistIsZeroCR, g_RayEpsilon, XMVectorAbs(BDist)); + BDist = XMVectorSelect(BDist, Zero, BDistIsZero); + + uint32_t BDistIsLessCR; + XMVECTOR BDistIsLess = XMVectorGreaterR(&BDistIsLessCR, Zero, BDist); + + uint32_t BDistIsGreaterCR; + XMVECTOR BDistIsGreater = XMVectorGreaterR(&BDistIsGreaterCR, BDist, Zero); + + // If all the points are on the same side we don't intersect. + if (XMComparisonAllTrue(BDistIsLessCR) || XMComparisonAllTrue(BDistIsGreaterCR)) + return false; + + // Compute the normal of triangle B. + XMVECTOR N2 = XMVector3Cross(XMVectorSubtract(B1, B0), XMVectorSubtract(B2, B0)); + + // Assert that the triangle is not degenerate. + assert(!XMVector3Equal(N2, Zero)); + + // Test points of A against the plane of B. + XMVECTOR ADist = XMVector3Dot(N2, XMVectorSubtract(A0, B0)); + ADist = XMVectorSelect(ADist, XMVector3Dot(N2, XMVectorSubtract(A1, B0)), SelectY); + ADist = XMVectorSelect(ADist, XMVector3Dot(N2, XMVectorSubtract(A2, B0)), SelectZ); + + // Ensure robustness with co-planar triangles by zeroing small distances. + uint32_t ADistIsZeroCR; + XMVECTOR ADistIsZero = XMVectorGreaterR(&ADistIsZeroCR, g_RayEpsilon, XMVectorAbs(ADist)); + ADist = XMVectorSelect(ADist, Zero, ADistIsZero); + + uint32_t ADistIsLessCR; + XMVECTOR ADistIsLess = XMVectorGreaterR(&ADistIsLessCR, Zero, ADist); + + uint32_t ADistIsGreaterCR; + XMVECTOR ADistIsGreater = XMVectorGreaterR(&ADistIsGreaterCR, ADist, Zero); + + // If all the points are on the same side we don't intersect. + if (XMComparisonAllTrue(ADistIsLessCR) || XMComparisonAllTrue(ADistIsGreaterCR)) + return false; + + // Special case for co-planar triangles. + if (XMComparisonAllTrue(ADistIsZeroCR) || XMComparisonAllTrue(BDistIsZeroCR)) + { + XMVECTOR Axis, Dist, MinDist; + + // Compute an axis perpindicular to the edge (points out). + Axis = XMVector3Cross(N1, XMVectorSubtract(A1, A0)); + Dist = XMVector3Dot(Axis, A0); + + // Test points of B against the axis. + MinDist = XMVector3Dot(B0, Axis); + MinDist = XMVectorMin(MinDist, XMVector3Dot(B1, Axis)); + MinDist = XMVectorMin(MinDist, XMVector3Dot(B2, Axis)); + if (XMVector4GreaterOrEqual(MinDist, Dist)) + return false; + + // Edge (A1, A2) + Axis = XMVector3Cross(N1, XMVectorSubtract(A2, A1)); + Dist = XMVector3Dot(Axis, A1); + + MinDist = XMVector3Dot(B0, Axis); + MinDist = XMVectorMin(MinDist, XMVector3Dot(B1, Axis)); + MinDist = XMVectorMin(MinDist, XMVector3Dot(B2, Axis)); + if (XMVector4GreaterOrEqual(MinDist, Dist)) + return false; + + // Edge (A2, A0) + Axis = XMVector3Cross(N1, XMVectorSubtract(A0, A2)); + Dist = XMVector3Dot(Axis, A2); + + MinDist = XMVector3Dot(B0, Axis); + MinDist = XMVectorMin(MinDist, XMVector3Dot(B1, Axis)); + MinDist = XMVectorMin(MinDist, XMVector3Dot(B2, Axis)); + if (XMVector4GreaterOrEqual(MinDist, Dist)) + return false; + + // Edge (B0, B1) + Axis = XMVector3Cross(N2, XMVectorSubtract(B1, B0)); + Dist = XMVector3Dot(Axis, B0); + + MinDist = XMVector3Dot(A0, Axis); + MinDist = XMVectorMin(MinDist, XMVector3Dot(A1, Axis)); + MinDist = XMVectorMin(MinDist, XMVector3Dot(A2, Axis)); + if (XMVector4GreaterOrEqual(MinDist, Dist)) + return false; + + // Edge (B1, B2) + Axis = XMVector3Cross(N2, XMVectorSubtract(B2, B1)); + Dist = XMVector3Dot(Axis, B1); + + MinDist = XMVector3Dot(A0, Axis); + MinDist = XMVectorMin(MinDist, XMVector3Dot(A1, Axis)); + MinDist = XMVectorMin(MinDist, XMVector3Dot(A2, Axis)); + if (XMVector4GreaterOrEqual(MinDist, Dist)) + return false; + + // Edge (B2,B0) + Axis = XMVector3Cross(N2, XMVectorSubtract(B0, B2)); + Dist = XMVector3Dot(Axis, B2); + + MinDist = XMVector3Dot(A0, Axis); + MinDist = XMVectorMin(MinDist, XMVector3Dot(A1, Axis)); + MinDist = XMVectorMin(MinDist, XMVector3Dot(A2, Axis)); + if (XMVector4GreaterOrEqual(MinDist, Dist)) + return false; + + return true; + } + + // + // Find the single vertex of A and B (ie the vertex on the opposite side + // of the plane from the other two) and reorder the edges so we can compute + // the signed edge/edge distances. + // + // if ( (V0 >= 0 && V1 < 0 && V2 < 0) || + // (V0 > 0 && V1 <= 0 && V2 <= 0) || + // (V0 <= 0 && V1 > 0 && V2 > 0) || + // (V0 < 0 && V1 >= 0 && V2 >= 0) ) then V0 is singular; + // + // If our singular vertex is not on the positive side of the plane we reverse + // the triangle winding so that the overlap comparisons will compare the + // correct edges with the correct signs. + // + XMVECTOR ADistIsLessEqual = XMVectorOrInt(ADistIsLess, ADistIsZero); + XMVECTOR ADistIsGreaterEqual = XMVectorOrInt(ADistIsGreater, ADistIsZero); + + XMVECTOR AA0, AA1, AA2; + bool bPositiveA; + + if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsGreaterEqual, ADistIsLess, Select0111)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsGreater, ADistIsLessEqual, Select0111))) + { + // A0 is singular, crossing from positive to negative. + AA0 = A0; AA1 = A1; AA2 = A2; + bPositiveA = true; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsLessEqual, ADistIsGreater, Select0111)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsLess, ADistIsGreaterEqual, Select0111))) + { + // A0 is singular, crossing from negative to positive. + AA0 = A0; AA1 = A2; AA2 = A1; + bPositiveA = false; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsGreaterEqual, ADistIsLess, Select1011)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsGreater, ADistIsLessEqual, Select1011))) + { + // A1 is singular, crossing from positive to negative. + AA0 = A1; AA1 = A2; AA2 = A0; + bPositiveA = true; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsLessEqual, ADistIsGreater, Select1011)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsLess, ADistIsGreaterEqual, Select1011))) + { + // A1 is singular, crossing from negative to positive. + AA0 = A1; AA1 = A0; AA2 = A2; + bPositiveA = false; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsGreaterEqual, ADistIsLess, Select1101)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsGreater, ADistIsLessEqual, Select1101))) + { + // A2 is singular, crossing from positive to negative. + AA0 = A2; AA1 = A0; AA2 = A1; + bPositiveA = true; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsLessEqual, ADistIsGreater, Select1101)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(ADistIsLess, ADistIsGreaterEqual, Select1101))) + { + // A2 is singular, crossing from negative to positive. + AA0 = A2; AA1 = A1; AA2 = A0; + bPositiveA = false; + } + else + { + assert(false); + return false; + } + + XMVECTOR BDistIsLessEqual = XMVectorOrInt(BDistIsLess, BDistIsZero); + XMVECTOR BDistIsGreaterEqual = XMVectorOrInt(BDistIsGreater, BDistIsZero); + + XMVECTOR BB0, BB1, BB2; + bool bPositiveB; + + if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsGreaterEqual, BDistIsLess, Select0111)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsGreater, BDistIsLessEqual, Select0111))) + { + // B0 is singular, crossing from positive to negative. + BB0 = B0; BB1 = B1; BB2 = B2; + bPositiveB = true; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsLessEqual, BDistIsGreater, Select0111)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsLess, BDistIsGreaterEqual, Select0111))) + { + // B0 is singular, crossing from negative to positive. + BB0 = B0; BB1 = B2; BB2 = B1; + bPositiveB = false; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsGreaterEqual, BDistIsLess, Select1011)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsGreater, BDistIsLessEqual, Select1011))) + { + // B1 is singular, crossing from positive to negative. + BB0 = B1; BB1 = B2; BB2 = B0; + bPositiveB = true; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsLessEqual, BDistIsGreater, Select1011)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsLess, BDistIsGreaterEqual, Select1011))) + { + // B1 is singular, crossing from negative to positive. + BB0 = B1; BB1 = B0; BB2 = B2; + bPositiveB = false; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsGreaterEqual, BDistIsLess, Select1101)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsGreater, BDistIsLessEqual, Select1101))) + { + // B2 is singular, crossing from positive to negative. + BB0 = B2; BB1 = B0; BB2 = B1; + bPositiveB = true; + } + else if (DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsLessEqual, BDistIsGreater, Select1101)) || + DirectX::MathInternal::XMVector3AllTrue(XMVectorSelect(BDistIsLess, BDistIsGreaterEqual, Select1101))) + { + // B2 is singular, crossing from negative to positive. + BB0 = B2; BB1 = B1; BB2 = B0; + bPositiveB = false; + } + else + { + assert(false); + return false; + } + + XMVECTOR Delta0, Delta1; + + // Reverse the direction of the test depending on whether the singular vertices are + // the same sign or different signs. + if (bPositiveA ^ bPositiveB) + { + Delta0 = XMVectorSubtract(BB0, AA0); + Delta1 = XMVectorSubtract(AA0, BB0); + } + else + { + Delta0 = XMVectorSubtract(AA0, BB0); + Delta1 = XMVectorSubtract(BB0, AA0); + } + + // Check if the triangles overlap on the line of intersection between the + // planes of the two triangles by finding the signed line distances. + XMVECTOR Dist0 = XMVector3Dot(Delta0, XMVector3Cross(XMVectorSubtract(BB2, BB0), XMVectorSubtract(AA2, AA0))); + if (XMVector4Greater(Dist0, Zero)) + return false; + + XMVECTOR Dist1 = XMVector3Dot(Delta1, XMVector3Cross(XMVectorSubtract(BB1, BB0), XMVectorSubtract(AA1, AA0))); + if (XMVector4Greater(Dist1, Zero)) + return false; + + return true; + } + + + //----------------------------------------------------------------------------- + // Ray-triangle test + //----------------------------------------------------------------------------- + _Use_decl_annotations_ + inline PlaneIntersectionType XM_CALLCONV Intersects(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2, GXMVECTOR Plane) noexcept + { + XMVECTOR One = XMVectorSplatOne(); + + assert(DirectX::MathInternal::XMPlaneIsUnit(Plane)); + + // Set w of the points to one so we can dot4 with a plane. + XMVECTOR TV0 = XMVectorInsert<0, 0, 0, 0, 1>(V0, One); + XMVECTOR TV1 = XMVectorInsert<0, 0, 0, 0, 1>(V1, One); + XMVECTOR TV2 = XMVectorInsert<0, 0, 0, 0, 1>(V2, One); + + XMVECTOR Outside, Inside; + DirectX::MathInternal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane, Outside, Inside); + + // If the triangle is outside any plane it is outside. + if (XMVector4EqualInt(Outside, XMVectorTrueInt())) + return FRONT; + + // If the triangle is inside all planes it is inside. + if (XMVector4EqualInt(Inside, XMVectorTrueInt())) + return BACK; + + // The triangle is not inside all planes or outside a plane it intersects. + return INTERSECTING; + } + + + //----------------------------------------------------------------------------- + // Test a triangle vs 6 planes (typically forming a frustum). + //----------------------------------------------------------------------------- + _Use_decl_annotations_ + inline ContainmentType XM_CALLCONV ContainedBy( + FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR V2, + GXMVECTOR Plane0, + HXMVECTOR Plane1, HXMVECTOR Plane2, + CXMVECTOR Plane3, CXMVECTOR Plane4, CXMVECTOR Plane5) noexcept + { + XMVECTOR One = XMVectorSplatOne(); + + // Set w of the points to one so we can dot4 with a plane. + XMVECTOR TV0 = XMVectorInsert<0, 0, 0, 0, 1>(V0, One); + XMVECTOR TV1 = XMVectorInsert<0, 0, 0, 0, 1>(V1, One); + XMVECTOR TV2 = XMVectorInsert<0, 0, 0, 0, 1>(V2, One); + + XMVECTOR Outside, Inside; + + // Test against each plane. + DirectX::MathInternal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane0, Outside, Inside); + + XMVECTOR AnyOutside = Outside; + XMVECTOR AllInside = Inside; + + DirectX::MathInternal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane1, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane2, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane3, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane4, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + DirectX::MathInternal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane5, Outside, Inside); + AnyOutside = XMVectorOrInt(AnyOutside, Outside); + AllInside = XMVectorAndInt(AllInside, Inside); + + // If the triangle is outside any plane it is outside. + if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) + return DISJOINT; + + // If the triangle is inside all planes it is inside. + if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) + return CONTAINS; + + // The triangle is not inside all planes or outside a plane, it may intersect. + return INTERSECTS; + } + +} // namespace TriangleTests + diff --git a/Extern/dxmath/Inc/DirectXColors.h b/Extern/dxmath/Inc/DirectXColors.h new file mode 100644 index 000000000..83fa21093 --- /dev/null +++ b/Extern/dxmath/Inc/DirectXColors.h @@ -0,0 +1,312 @@ +//------------------------------------------------------------------------------------- +// DirectXColors.h -- C++ Color Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#include "DirectXMath.h" + +namespace DirectX +{ + + namespace Colors + { + // Standard colors (Red/Green/Blue/Alpha) in sRGB colorspace + XMGLOBALCONST XMVECTORF32 AliceBlue = { { { 0.941176534f, 0.972549081f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 AntiqueWhite = { { { 0.980392218f, 0.921568692f, 0.843137324f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Aqua = { { { 0.f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Aquamarine = { { { 0.498039246f, 1.f, 0.831372619f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Azure = { { { 0.941176534f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Beige = { { { 0.960784376f, 0.960784376f, 0.862745166f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Bisque = { { { 1.f, 0.894117713f, 0.768627524f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Black = { { { 0.f, 0.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 BlanchedAlmond = { { { 1.f, 0.921568692f, 0.803921640f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Blue = { { { 0.f, 0.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 BlueViolet = { { { 0.541176498f, 0.168627456f, 0.886274576f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Brown = { { { 0.647058845f, 0.164705887f, 0.164705887f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 BurlyWood = { { { 0.870588303f, 0.721568644f, 0.529411793f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 CadetBlue = { { { 0.372549027f, 0.619607866f, 0.627451003f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Chartreuse = { { { 0.498039246f, 1.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Chocolate = { { { 0.823529482f, 0.411764741f, 0.117647067f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Coral = { { { 1.f, 0.498039246f, 0.313725501f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 CornflowerBlue = { { { 0.392156899f, 0.584313750f, 0.929411829f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Cornsilk = { { { 1.f, 0.972549081f, 0.862745166f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Crimson = { { { 0.862745166f, 0.078431375f, 0.235294133f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Cyan = { { { 0.f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkBlue = { { { 0.f, 0.f, 0.545098066f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkCyan = { { { 0.f, 0.545098066f, 0.545098066f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkGoldenrod = { { { 0.721568644f, 0.525490224f, 0.043137256f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkGray = { { { 0.662745118f, 0.662745118f, 0.662745118f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkGreen = { { { 0.f, 0.392156899f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkKhaki = { { { 0.741176486f, 0.717647076f, 0.419607878f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkMagenta = { { { 0.545098066f, 0.f, 0.545098066f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkOliveGreen = { { { 0.333333343f, 0.419607878f, 0.184313729f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkOrange = { { { 1.f, 0.549019635f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkOrchid = { { { 0.600000024f, 0.196078449f, 0.800000072f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkRed = { { { 0.545098066f, 0.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkSalmon = { { { 0.913725555f, 0.588235319f, 0.478431404f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkSeaGreen = { { { 0.560784340f, 0.737254918f, 0.545098066f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkSlateBlue = { { { 0.282352954f, 0.239215702f, 0.545098066f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkSlateGray = { { { 0.184313729f, 0.309803933f, 0.309803933f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkTurquoise = { { { 0.f, 0.807843208f, 0.819607913f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkViolet = { { { 0.580392182f, 0.f, 0.827451050f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DeepPink = { { { 1.f, 0.078431375f, 0.576470613f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DeepSkyBlue = { { { 0.f, 0.749019623f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DimGray = { { { 0.411764741f, 0.411764741f, 0.411764741f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DodgerBlue = { { { 0.117647067f, 0.564705908f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Firebrick = { { { 0.698039234f, 0.133333340f, 0.133333340f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 FloralWhite = { { { 1.f, 0.980392218f, 0.941176534f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 ForestGreen = { { { 0.133333340f, 0.545098066f, 0.133333340f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Fuchsia = { { { 1.f, 0.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Gainsboro = { { { 0.862745166f, 0.862745166f, 0.862745166f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 GhostWhite = { { { 0.972549081f, 0.972549081f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Gold = { { { 1.f, 0.843137324f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Goldenrod = { { { 0.854902029f, 0.647058845f, 0.125490203f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Gray = { { { 0.501960814f, 0.501960814f, 0.501960814f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Green = { { { 0.f, 0.501960814f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 GreenYellow = { { { 0.678431392f, 1.f, 0.184313729f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Honeydew = { { { 0.941176534f, 1.f, 0.941176534f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 HotPink = { { { 1.f, 0.411764741f, 0.705882370f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 IndianRed = { { { 0.803921640f, 0.360784322f, 0.360784322f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Indigo = { { { 0.294117659f, 0.f, 0.509803951f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Ivory = { { { 1.f, 1.f, 0.941176534f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Khaki = { { { 0.941176534f, 0.901960850f, 0.549019635f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Lavender = { { { 0.901960850f, 0.901960850f, 0.980392218f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LavenderBlush = { { { 1.f, 0.941176534f, 0.960784376f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LawnGreen = { { { 0.486274540f, 0.988235354f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LemonChiffon = { { { 1.f, 0.980392218f, 0.803921640f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightBlue = { { { 0.678431392f, 0.847058892f, 0.901960850f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightCoral = { { { 0.941176534f, 0.501960814f, 0.501960814f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightCyan = { { { 0.878431439f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightGoldenrodYellow = { { { 0.980392218f, 0.980392218f, 0.823529482f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightGray = { { { 0.827451050f, 0.827451050f, 0.827451050f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightGreen = { { { 0.564705908f, 0.933333397f, 0.564705908f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightPink = { { { 1.f, 0.713725507f, 0.756862819f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSalmon = { { { 1.f, 0.627451003f, 0.478431404f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSeaGreen = { { { 0.125490203f, 0.698039234f, 0.666666687f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSkyBlue = { { { 0.529411793f, 0.807843208f, 0.980392218f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSlateGray = { { { 0.466666698f, 0.533333361f, 0.600000024f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSteelBlue = { { { 0.690196097f, 0.768627524f, 0.870588303f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightYellow = { { { 1.f, 1.f, 0.878431439f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Lime = { { { 0.f, 1.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LimeGreen = { { { 0.196078449f, 0.803921640f, 0.196078449f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Linen = { { { 0.980392218f, 0.941176534f, 0.901960850f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Magenta = { { { 1.f, 0.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Maroon = { { { 0.501960814f, 0.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumAquamarine = { { { 0.400000036f, 0.803921640f, 0.666666687f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumBlue = { { { 0.f, 0.f, 0.803921640f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumOrchid = { { { 0.729411781f, 0.333333343f, 0.827451050f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumPurple = { { { 0.576470613f, 0.439215720f, 0.858823597f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumSeaGreen = { { { 0.235294133f, 0.701960802f, 0.443137288f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumSlateBlue = { { { 0.482352972f, 0.407843173f, 0.933333397f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumSpringGreen = { { { 0.f, 0.980392218f, 0.603921592f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumTurquoise = { { { 0.282352954f, 0.819607913f, 0.800000072f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumVioletRed = { { { 0.780392230f, 0.082352944f, 0.521568656f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MidnightBlue = { { { 0.098039225f, 0.098039225f, 0.439215720f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MintCream = { { { 0.960784376f, 1.f, 0.980392218f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MistyRose = { { { 1.f, 0.894117713f, 0.882353008f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Moccasin = { { { 1.f, 0.894117713f, 0.709803939f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 NavajoWhite = { { { 1.f, 0.870588303f, 0.678431392f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Navy = { { { 0.f, 0.f, 0.501960814f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 OldLace = { { { 0.992156923f, 0.960784376f, 0.901960850f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Olive = { { { 0.501960814f, 0.501960814f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 OliveDrab = { { { 0.419607878f, 0.556862772f, 0.137254909f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Orange = { { { 1.f, 0.647058845f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 OrangeRed = { { { 1.f, 0.270588249f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Orchid = { { { 0.854902029f, 0.439215720f, 0.839215755f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PaleGoldenrod = { { { 0.933333397f, 0.909803987f, 0.666666687f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PaleGreen = { { { 0.596078455f, 0.984313786f, 0.596078455f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PaleTurquoise = { { { 0.686274529f, 0.933333397f, 0.933333397f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PaleVioletRed = { { { 0.858823597f, 0.439215720f, 0.576470613f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PapayaWhip = { { { 1.f, 0.937254965f, 0.835294187f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PeachPuff = { { { 1.f, 0.854902029f, 0.725490212f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Peru = { { { 0.803921640f, 0.521568656f, 0.247058839f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Pink = { { { 1.f, 0.752941251f, 0.796078503f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Plum = { { { 0.866666734f, 0.627451003f, 0.866666734f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PowderBlue = { { { 0.690196097f, 0.878431439f, 0.901960850f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Purple = { { { 0.501960814f, 0.f, 0.501960814f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Red = { { { 1.f, 0.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 RosyBrown = { { { 0.737254918f, 0.560784340f, 0.560784340f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 RoyalBlue = { { { 0.254901975f, 0.411764741f, 0.882353008f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SaddleBrown = { { { 0.545098066f, 0.270588249f, 0.074509807f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Salmon = { { { 0.980392218f, 0.501960814f, 0.447058856f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SandyBrown = { { { 0.956862807f, 0.643137276f, 0.376470625f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SeaGreen = { { { 0.180392161f, 0.545098066f, 0.341176480f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SeaShell = { { { 1.f, 0.960784376f, 0.933333397f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Sienna = { { { 0.627451003f, 0.321568638f, 0.176470593f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Silver = { { { 0.752941251f, 0.752941251f, 0.752941251f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SkyBlue = { { { 0.529411793f, 0.807843208f, 0.921568692f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SlateBlue = { { { 0.415686309f, 0.352941185f, 0.803921640f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SlateGray = { { { 0.439215720f, 0.501960814f, 0.564705908f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Snow = { { { 1.f, 0.980392218f, 0.980392218f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SpringGreen = { { { 0.f, 1.f, 0.498039246f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SteelBlue = { { { 0.274509817f, 0.509803951f, 0.705882370f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Tan = { { { 0.823529482f, 0.705882370f, 0.549019635f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Teal = { { { 0.f, 0.501960814f, 0.501960814f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Thistle = { { { 0.847058892f, 0.749019623f, 0.847058892f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Tomato = { { { 1.f, 0.388235331f, 0.278431386f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Transparent = { { { 0.f, 0.f, 0.f, 0.f } } }; + XMGLOBALCONST XMVECTORF32 Turquoise = { { { 0.250980407f, 0.878431439f, 0.815686345f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Violet = { { { 0.933333397f, 0.509803951f, 0.933333397f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Wheat = { { { 0.960784376f, 0.870588303f, 0.701960802f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 White = { { { 1.f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 WhiteSmoke = { { { 0.960784376f, 0.960784376f, 0.960784376f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Yellow = { { { 1.f, 1.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 YellowGreen = { { { 0.603921592f, 0.803921640f, 0.196078449f, 1.f } } }; + + } // namespace Colors + + namespace ColorsLinear + { + // Standard colors (Red/Green/Blue/Alpha) in linear colorspace + XMGLOBALCONST XMVECTORF32 AliceBlue = { { { 0.871367335f, 0.938685894f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 AntiqueWhite = { { { 0.955973506f, 0.830770075f, 0.679542601f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Aqua = { { { 0.f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Aquamarine = { { { 0.212230787f, 1.f, 0.658374965f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Azure = { { { 0.871367335f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Beige = { { { 0.913098991f, 0.913098991f, 0.715693772f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Bisque = { { { 1.f, 0.775822461f, 0.552011609f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Black = { { { 0.f, 0.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 BlanchedAlmond = { { { 1.f, 0.830770075f, 0.610495746f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Blue = { { { 0.f, 0.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 BlueViolet = { { { 0.254152179f, 0.024157630f, 0.760524750f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Brown = { { { 0.376262218f, 0.023153365f, 0.023153365f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 BurlyWood = { { { 0.730461001f, 0.479320228f, 0.242281199f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 CadetBlue = { { { 0.114435382f, 0.341914445f, 0.351532698f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Chartreuse = { { { 0.212230787f, 1.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Chocolate = { { { 0.644479871f, 0.141263321f, 0.012983031f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Coral = { { { 1.f, 0.212230787f, 0.080219828f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 CornflowerBlue = { { { 0.127437726f, 0.300543845f, 0.846873462f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Cornsilk = { { { 1.f, 0.938685894f, 0.715693772f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Crimson = { { { 0.715693772f, 0.006995410f, 0.045186214f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Cyan = { { { 0.f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkBlue = { { { 0.f, 0.f, 0.258182913f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkCyan = { { { 0.f, 0.258182913f, 0.258182913f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkGoldenrod = { { { 0.479320228f, 0.238397658f, 0.003346536f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkGray = { { { 0.396755308f, 0.396755308f, 0.396755308f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkGreen = { { { 0.f, 0.127437726f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkKhaki = { { { 0.508881450f, 0.473531544f, 0.147027299f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkMagenta = { { { 0.258182913f, 0.f, 0.258182913f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkOliveGreen = { { { 0.090841733f, 0.147027299f, 0.028426038f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkOrange = { { { 1.f, 0.262250721f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkOrchid = { { { 0.318546832f, 0.031896040f, 0.603827536f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkRed = { { { 0.258182913f, 0.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkSalmon = { { { 0.814846814f, 0.304987371f, 0.194617867f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkSeaGreen = { { { 0.274677366f, 0.502886593f, 0.258182913f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkSlateBlue = { { { 0.064803280f, 0.046665095f, 0.258182913f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkSlateGray = { { { 0.028426038f, 0.078187428f, 0.078187428f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkTurquoise = { { { 0.f, 0.617206752f, 0.637597024f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DarkViolet = { { { 0.296138316f, 0.f, 0.651405811f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DeepPink = { { { 1.f, 0.006995410f, 0.291770697f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DeepSkyBlue = { { { 0.f, 0.520995677f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DimGray = { { { 0.141263321f, 0.141263321f, 0.141263321f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 DodgerBlue = { { { 0.012983031f, 0.278894335f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Firebrick = { { { 0.445201248f, 0.015996292f, 0.015996292f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 FloralWhite = { { { 1.f, 0.955973506f, 0.871367335f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 ForestGreen = { { { 0.015996292f, 0.258182913f, 0.015996292f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Fuchsia = { { { 1.f, 0.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Gainsboro = { { { 0.715693772f, 0.715693772f, 0.715693772f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 GhostWhite = { { { 0.938685894f, 0.938685894f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Gold = { { { 1.f, 0.679542601f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Goldenrod = { { { 0.701102138f, 0.376262218f, 0.014443844f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Gray = { { { 0.215860531f, 0.215860531f, 0.215860531f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Green = { { { 0.f, 0.215860531f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 GreenYellow = { { { 0.417885154f, 1.f, 0.028426038f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Honeydew = { { { 0.871367335f, 1.f, 0.871367335f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 HotPink = { { { 1.f, 0.141263321f, 0.456411064f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 IndianRed = { { { 0.610495746f, 0.107023112f, 0.107023112f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Indigo = { { { 0.070360109f, 0.f, 0.223227978f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Ivory = { { { 1.f, 1.f, 0.871367335f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Khaki = { { { 0.871367335f, 0.791298151f, 0.262250721f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Lavender = { { { 0.791298151f, 0.791298151f, 0.955973506f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LavenderBlush = { { { 1.f, 0.871367335f, 0.913098991f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LawnGreen = { { { 0.201556295f, 0.973445475f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LemonChiffon = { { { 1.f, 0.955973506f, 0.610495746f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightBlue = { { { 0.417885154f, 0.686685443f, 0.791298151f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightCoral = { { { 0.871367335f, 0.215860531f, 0.215860531f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightCyan = { { { 0.745404482f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightGoldenrodYellow = { { { 0.955973506f, 0.955973506f, 0.644479871f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightGray = { { { 0.651405811f, 0.651405811f, 0.651405811f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightGreen = { { { 0.278894335f, 0.854992807f, 0.278894335f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightPink = { { { 1.f, 0.467783839f, 0.533276618f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSalmon = { { { 1.f, 0.351532698f, 0.194617867f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSeaGreen = { { { 0.014443844f, 0.445201248f, 0.401977867f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSkyBlue = { { { 0.242281199f, 0.617206752f, 0.955973506f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSlateGray = { { { 0.184475034f, 0.246201396f, 0.318546832f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightSteelBlue = { { { 0.434153706f, 0.552011609f, 0.730461001f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LightYellow = { { { 1.f, 1.f, 0.745404482f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Lime = { { { 0.f, 1.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 LimeGreen = { { { 0.031896040f, 0.610495746f, 0.031896040f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Linen = { { { 0.955973506f, 0.871367335f, 0.791298151f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Magenta = { { { 1.f, 0.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Maroon = { { { 0.215860531f, 0.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumAquamarine = { { { 0.132868364f, 0.610495746f, 0.401977867f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumBlue = { { { 0.f, 0.f, 0.610495746f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumOrchid = { { { 0.491020888f, 0.090841733f, 0.651405811f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumPurple = { { { 0.291770697f, 0.162029430f, 0.708376050f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumSeaGreen = { { { 0.045186214f, 0.450785846f, 0.165132239f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumSlateBlue = { { { 0.198069349f, 0.138431653f, 0.854992807f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumSpringGreen = { { { 0.f, 0.955973506f, 0.323143244f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumTurquoise = { { { 0.064803280f, 0.637597024f, 0.603827536f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MediumVioletRed = { { { 0.571125031f, 0.007499032f, 0.234550655f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MidnightBlue = { { { 0.009721218f, 0.009721218f, 0.162029430f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MintCream = { { { 0.913098991f, 1.f, 0.955973506f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 MistyRose = { { { 1.f, 0.775822461f, 0.752942443f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Moccasin = { { { 1.f, 0.775822461f, 0.462077051f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 NavajoWhite = { { { 1.f, 0.730461001f, 0.417885154f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Navy = { { { 0.f, 0.f, 0.215860531f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 OldLace = { { { 0.982250869f, 0.913098991f, 0.791298151f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Olive = { { { 0.215860531f, 0.215860531f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 OliveDrab = { { { 0.147027299f, 0.270497859f, 0.016807375f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Orange = { { { 1.f, 0.376262218f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 OrangeRed = { { { 1.f, 0.059511241f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Orchid = { { { 0.701102138f, 0.162029430f, 0.672443330f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PaleGoldenrod = { { { 0.854992807f, 0.806952477f, 0.401977867f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PaleGreen = { { { 0.313988745f, 0.964686573f, 0.313988745f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PaleTurquoise = { { { 0.428690553f, 0.854992807f, 0.854992807f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PaleVioletRed = { { { 0.708376050f, 0.162029430f, 0.291770697f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PapayaWhip = { { { 1.f, 0.863157392f, 0.665387452f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PeachPuff = { { { 1.f, 0.701102138f, 0.485149980f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Peru = { { { 0.610495746f, 0.234550655f, 0.049706575f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Pink = { { { 1.f, 0.527115345f, 0.597202003f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Plum = { { { 0.723055363f, 0.351532698f, 0.723055363f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 PowderBlue = { { { 0.434153706f, 0.745404482f, 0.791298151f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Purple = { { { 0.215860531f, 0.f, 0.215860531f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Red = { { { 1.f, 0.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 RosyBrown = { { { 0.502886593f, 0.274677366f, 0.274677366f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 RoyalBlue = { { { 0.052860655f, 0.141263321f, 0.752942443f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SaddleBrown = { { { 0.258182913f, 0.059511241f, 0.006512091f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Salmon = { { { 0.955973506f, 0.215860531f, 0.168269455f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SandyBrown = { { { 0.904661357f, 0.371237785f, 0.116970696f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SeaGreen = { { { 0.027320892f, 0.258182913f, 0.095307484f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SeaShell = { { { 1.f, 0.913098991f, 0.854992807f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Sienna = { { { 0.351532698f, 0.084376216f, 0.026241222f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Silver = { { { 0.527115345f, 0.527115345f, 0.527115345f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SkyBlue = { { { 0.242281199f, 0.617206752f, 0.830770075f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SlateBlue = { { { 0.144128501f, 0.102241747f, 0.610495746f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SlateGray = { { { 0.162029430f, 0.215860531f, 0.278894335f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Snow = { { { 1.f, 0.955973506f, 0.955973506f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SpringGreen = { { { 0.f, 1.f, 0.212230787f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 SteelBlue = { { { 0.061246071f, 0.223227978f, 0.456411064f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Tan = { { { 0.644479871f, 0.456411064f, 0.262250721f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Teal = { { { 0.f, 0.215860531f, 0.215860531f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Thistle = { { { 0.686685443f, 0.520995677f, 0.686685443f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Tomato = { { { 1.f, 0.124771863f, 0.063010029f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Transparent = { { { 0.f, 0.f, 0.f, 0.f } } }; + XMGLOBALCONST XMVECTORF32 Turquoise = { { { 0.051269468f, 0.745404482f, 0.630757332f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Violet = { { { 0.854992807f, 0.223227978f, 0.854992807f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Wheat = { { { 0.913098991f, 0.730461001f, 0.450785846f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 White = { { { 1.f, 1.f, 1.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 WhiteSmoke = { { { 0.913098991f, 0.913098991f, 0.913098991f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 Yellow = { { { 1.f, 1.f, 0.f, 1.f } } }; + XMGLOBALCONST XMVECTORF32 YellowGreen = { { { 0.323143244f, 0.610495746f, 0.031896040f, 1.f } } }; + + } // namespace ColorsLinear + +} // namespace DirectX + diff --git a/Extern/dxmath/Inc/DirectXMath.h b/Extern/dxmath/Inc/DirectXMath.h new file mode 100644 index 000000000..6228e7d80 --- /dev/null +++ b/Extern/dxmath/Inc/DirectXMath.h @@ -0,0 +1,2295 @@ +//------------------------------------------------------------------------------------- +// DirectXMath.h -- SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#ifndef __cplusplus +#error DirectX Math requires C++ +#endif + +#define DIRECTX_MATH_VERSION 320 + +#if defined(_MSC_VER) && (_MSC_VER < 1910) +#error DirectX Math requires Visual C++ 2017 or later. +#endif + +#if defined(_MSC_VER) && !defined(_M_ARM) && !defined(_M_ARM64) && !defined(_M_HYBRID_X86_ARM64) && !defined(_M_ARM64EC) && (!_MANAGED) && (!_M_CEE) && (!defined(_M_IX86_FP) || (_M_IX86_FP > 1)) && !defined(_XM_NO_INTRINSICS_) && !defined(_XM_VECTORCALL_) +#define _XM_VECTORCALL_ 1 +#endif + +#if _XM_VECTORCALL_ +#define XM_CALLCONV __vectorcall +#elif defined(__GNUC__) +#define XM_CALLCONV +#else +#define XM_CALLCONV __fastcall +#endif + +#ifndef XM_DEPRECATED +#if (__cplusplus >= 201402L) +#define XM_DEPRECATED [[deprecated]] +#elif defined(__GNUC__) +#define XM_DEPRECATED __attribute__ ((deprecated)) +#else +#define XM_DEPRECATED __declspec(deprecated("This is deprecated and will be removed in a future version.")) +#endif +#endif + +#if !defined(_XM_AVX2_INTRINSICS_) && defined(__AVX2__) && !defined(_XM_NO_INTRINSICS_) +#define _XM_AVX2_INTRINSICS_ +#endif + +#if !defined(_XM_FMA3_INTRINSICS_) && defined(_XM_AVX2_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) +#define _XM_FMA3_INTRINSICS_ +#endif + +#if !defined(_XM_F16C_INTRINSICS_) && defined(_XM_AVX2_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) +#define _XM_F16C_INTRINSICS_ +#endif + +#if !defined(_XM_F16C_INTRINSICS_) && defined(__F16C__) && !defined(_XM_NO_INTRINSICS_) +#define _XM_F16C_INTRINSICS_ +#endif + +#if defined(_XM_FMA3_INTRINSICS_) && !defined(_XM_AVX_INTRINSICS_) +#define _XM_AVX_INTRINSICS_ +#endif + +#if defined(_XM_F16C_INTRINSICS_) && !defined(_XM_AVX_INTRINSICS_) +#define _XM_AVX_INTRINSICS_ +#endif + +#if !defined(_XM_AVX_INTRINSICS_) && defined(__AVX__) && !defined(_XM_NO_INTRINSICS_) +#define _XM_AVX_INTRINSICS_ +#endif + +#if defined(_XM_AVX_INTRINSICS_) && !defined(_XM_SSE4_INTRINSICS_) +#define _XM_SSE4_INTRINSICS_ +#endif + +#if defined(_XM_SSE4_INTRINSICS_) && !defined(_XM_SSE3_INTRINSICS_) +#define _XM_SSE3_INTRINSICS_ +#endif + +#if defined(_XM_SSE3_INTRINSICS_) && !defined(_XM_SSE_INTRINSICS_) +#define _XM_SSE_INTRINSICS_ +#endif + +#if !defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) +#if (defined(_M_IX86) || defined(_M_X64) || __i386__ || __x86_64__) && !defined(_M_HYBRID_X86_ARM64) && !defined(_M_ARM64EC) +#define _XM_SSE_INTRINSICS_ +#elif defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __arm__ || __aarch64__ +#define _XM_ARM_NEON_INTRINSICS_ +#elif !defined(_XM_NO_INTRINSICS_) +#error DirectX Math does not support this target +#endif +#endif // !_XM_ARM_NEON_INTRINSICS_ && !_XM_SSE_INTRINSICS_ && !_XM_NO_INTRINSICS_ + +#if defined(_XM_SSE_INTRINSICS_) && defined(_MSC_VER) && (_MSC_VER >= 1920) && !defined(__clang__) && !defined(_XM_SVML_INTRINSICS_) && !defined(_XM_DISABLE_INTEL_SVML_) +#define _XM_SVML_INTRINSICS_ +#endif + +#if !defined(_XM_NO_XMVECTOR_OVERLOADS_) && (defined(__clang__) || defined(__GNUC__)) && !defined(_XM_NO_INTRINSICS_) +#define _XM_NO_XMVECTOR_OVERLOADS_ +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4514 4820) +// C4514/4820: Off by default noise +#endif +#include +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifndef _XM_NO_INTRINSICS_ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4987) +// C4987: Off by default noise +#endif +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#if (defined(__clang__) || defined(__GNUC__)) && (__x86_64__ || __i386__) && !defined(__MINGW32__) +#include +#endif + +#ifdef _XM_SSE_INTRINSICS_ +#include +#include + +#ifdef _XM_SSE3_INTRINSICS_ +#include +#endif + +#ifdef _XM_SSE4_INTRINSICS_ +#include +#endif + +#ifdef _XM_AVX_INTRINSICS_ +#include +#endif + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC)) +#include +#else +#include +#endif +#endif +#endif // !_XM_NO_INTRINSICS_ + +#include "sal.h" +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4005 4668) +// C4005/4668: Old header issue +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#if (__cplusplus >= 201703L) +#define XM_ALIGNED_DATA(x) alignas(x) +#define XM_ALIGNED_STRUCT(x) struct alignas(x) +#elif defined(__GNUC__) +#define XM_ALIGNED_DATA(x) __attribute__ ((aligned(x))) +#define XM_ALIGNED_STRUCT(x) struct __attribute__ ((aligned(x))) +#else +#define XM_ALIGNED_DATA(x) __declspec(align(x)) +#define XM_ALIGNED_STRUCT(x) __declspec(align(x)) struct +#endif + +#if (__cplusplus >= 202002L) +#include +#endif + +/**************************************************************************** + * + * Conditional intrinsics + * + ****************************************************************************/ + +#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + +#if defined(_XM_NO_MOVNT_) +#define XM_STREAM_PS( p, a ) _mm_store_ps((p), (a)) +#define XM256_STREAM_PS( p, a ) _mm256_store_ps((p), (a)) +#define XM_SFENCE() +#else +#define XM_STREAM_PS( p, a ) _mm_stream_ps((p), (a)) +#define XM256_STREAM_PS( p, a ) _mm256_stream_ps((p), (a)) +#define XM_SFENCE() _mm_sfence() +#endif + +#if defined(_XM_FMA3_INTRINSICS_) +#define XM_FMADD_PS( a, b, c ) _mm_fmadd_ps((a), (b), (c)) +#define XM_FNMADD_PS( a, b, c ) _mm_fnmadd_ps((a), (b), (c)) +#else +#define XM_FMADD_PS( a, b, c ) _mm_add_ps(_mm_mul_ps((a), (b)), (c)) +#define XM_FNMADD_PS( a, b, c ) _mm_sub_ps((c), _mm_mul_ps((a), (b))) +#endif + +#if defined(_XM_AVX_INTRINSICS_) && defined(_XM_FAVOR_INTEL_) +#define XM_PERMUTE_PS( v, c ) _mm_permute_ps((v), c ) +#else +#define XM_PERMUTE_PS( v, c ) _mm_shuffle_ps((v), (v), c ) +#endif + +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 11) +#define XM_LOADU_SI16( p ) _mm_cvtsi32_si128(*reinterpret_cast(p)) +#else +#define XM_LOADU_SI16( p ) _mm_loadu_si16(p) +#endif + +#endif // _XM_SSE_INTRINSICS_ && !_XM_NO_INTRINSICS_ + +#if defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + +#if defined(__clang__) || defined(__GNUC__) +#define XM_PREFETCH( a ) __builtin_prefetch(a) +#elif defined(_MSC_VER) +#define XM_PREFETCH( a ) __prefetch(a) +#else +#define XM_PREFETCH( a ) +#endif + +#endif // _XM_ARM_NEON_INTRINSICS_ && !_XM_NO_INTRINSICS_ + +namespace DirectX +{ + + /**************************************************************************** + * + * Constant definitions + * + ****************************************************************************/ + +#if defined(__XNAMATH_H__) && defined(XM_PI) +#undef XM_PI +#undef XM_2PI +#undef XM_1DIVPI +#undef XM_1DIV2PI +#undef XM_PIDIV2 +#undef XM_PIDIV4 +#undef XM_SELECT_0 +#undef XM_SELECT_1 +#undef XM_PERMUTE_0X +#undef XM_PERMUTE_0Y +#undef XM_PERMUTE_0Z +#undef XM_PERMUTE_0W +#undef XM_PERMUTE_1X +#undef XM_PERMUTE_1Y +#undef XM_PERMUTE_1Z +#undef XM_PERMUTE_1W +#undef XM_CRMASK_CR6 +#undef XM_CRMASK_CR6TRUE +#undef XM_CRMASK_CR6FALSE +#undef XM_CRMASK_CR6BOUNDS +#undef XM_CACHE_LINE_SIZE +#endif + + constexpr float XM_PI = 3.141592654f; + constexpr float XM_2PI = 6.283185307f; + constexpr float XM_1DIVPI = 0.318309886f; + constexpr float XM_1DIV2PI = 0.159154943f; + constexpr float XM_PIDIV2 = 1.570796327f; + constexpr float XM_PIDIV4 = 0.785398163f; + + constexpr uint32_t XM_SELECT_0 = 0x00000000; + constexpr uint32_t XM_SELECT_1 = 0xFFFFFFFF; + + constexpr uint32_t XM_PERMUTE_0X = 0; + constexpr uint32_t XM_PERMUTE_0Y = 1; + constexpr uint32_t XM_PERMUTE_0Z = 2; + constexpr uint32_t XM_PERMUTE_0W = 3; + constexpr uint32_t XM_PERMUTE_1X = 4; + constexpr uint32_t XM_PERMUTE_1Y = 5; + constexpr uint32_t XM_PERMUTE_1Z = 6; + constexpr uint32_t XM_PERMUTE_1W = 7; + + constexpr uint32_t XM_SWIZZLE_X = 0; + constexpr uint32_t XM_SWIZZLE_Y = 1; + constexpr uint32_t XM_SWIZZLE_Z = 2; + constexpr uint32_t XM_SWIZZLE_W = 3; + + constexpr uint32_t XM_CRMASK_CR6 = 0x000000F0; + constexpr uint32_t XM_CRMASK_CR6TRUE = 0x00000080; + constexpr uint32_t XM_CRMASK_CR6FALSE = 0x00000020; + constexpr uint32_t XM_CRMASK_CR6BOUNDS = XM_CRMASK_CR6FALSE; + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __arm__ || __aarch64__ + constexpr size_t XM_CACHE_LINE_SIZE = 128; +#else + constexpr size_t XM_CACHE_LINE_SIZE = 64; +#endif + + + /**************************************************************************** + * + * Macros + * + ****************************************************************************/ + +#if defined(__XNAMATH_H__) && defined(XMComparisonAllTrue) +#undef XMComparisonAllTrue +#undef XMComparisonAnyTrue +#undef XMComparisonAllFalse +#undef XMComparisonAnyFalse +#undef XMComparisonMixed +#undef XMComparisonAllInBounds +#undef XMComparisonAnyOutOfBounds +#endif + + // Unit conversion + + constexpr float XMConvertToRadians(float fDegrees) noexcept { return fDegrees * (XM_PI / 180.0f); } + constexpr float XMConvertToDegrees(float fRadians) noexcept { return fRadians * (180.0f / XM_PI); } + + // Condition register evaluation proceeding a recording (R) comparison + + constexpr bool XMComparisonAllTrue(uint32_t CR) noexcept { return (CR & XM_CRMASK_CR6TRUE) == XM_CRMASK_CR6TRUE; } + constexpr bool XMComparisonAnyTrue(uint32_t CR) noexcept { return (CR & XM_CRMASK_CR6FALSE) != XM_CRMASK_CR6FALSE; } + constexpr bool XMComparisonAllFalse(uint32_t CR) noexcept { return (CR & XM_CRMASK_CR6FALSE) == XM_CRMASK_CR6FALSE; } + constexpr bool XMComparisonAnyFalse(uint32_t CR) noexcept { return (CR & XM_CRMASK_CR6TRUE) != XM_CRMASK_CR6TRUE; } + constexpr bool XMComparisonMixed(uint32_t CR) noexcept { return (CR & XM_CRMASK_CR6) == 0; } + constexpr bool XMComparisonAllInBounds(uint32_t CR) noexcept { return (CR & XM_CRMASK_CR6BOUNDS) == XM_CRMASK_CR6BOUNDS; } + constexpr bool XMComparisonAnyOutOfBounds(uint32_t CR) noexcept { return (CR & XM_CRMASK_CR6BOUNDS) != XM_CRMASK_CR6BOUNDS; } + + + /**************************************************************************** + * + * Data types + * + ****************************************************************************/ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4068 4201 4365 4324 4820) + // C4068: ignore unknown pragmas + // C4201: nonstandard extension used : nameless struct/union + // C4365: Off by default noise + // C4324/4820: padding warnings +#endif + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable : 25000, "FXMVECTOR is 16 bytes") +#endif + +//------------------------------------------------------------------------------ +#if defined(_XM_NO_INTRINSICS_) + struct __vector4 + { + union + { + float vector4_f32[4]; + uint32_t vector4_u32[4]; + }; + }; +#endif // _XM_NO_INTRINSICS_ + + //------------------------------------------------------------------------------ + // Vector intrinsic: Four 32 bit floating point components aligned on a 16 byte + // boundary and mapped to hardware vector registers +#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + using XMVECTOR = __m128; +#elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + using XMVECTOR = float32x4_t; +#else + using XMVECTOR = __vector4; +#endif + + // Fix-up for (1st-3rd) XMVECTOR parameters that are pass-in-register for x86, ARM, ARM64, and vector call; by reference otherwise +#if ( defined(_M_IX86) || defined(_M_ARM) || defined(_M_ARM64) || _XM_VECTORCALL_ || __i386__ || __arm__ || __aarch64__ ) && !defined(_XM_NO_INTRINSICS_) + typedef const XMVECTOR FXMVECTOR; +#else + typedef const XMVECTOR& FXMVECTOR; +#endif + + // Fix-up for (4th) XMVECTOR parameter to pass in-register for ARM, ARM64, and vector call; by reference otherwise +#if ( defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || _XM_VECTORCALL_ || __arm__ || __aarch64__ ) && !defined(_XM_NO_INTRINSICS_) + typedef const XMVECTOR GXMVECTOR; +#else + typedef const XMVECTOR& GXMVECTOR; +#endif + + // Fix-up for (5th & 6th) XMVECTOR parameter to pass in-register for ARM64 and vector call; by reference otherwise +#if ( defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || _XM_VECTORCALL_ || __aarch64__ ) && !defined(_XM_NO_INTRINSICS_) + typedef const XMVECTOR HXMVECTOR; +#else + typedef const XMVECTOR& HXMVECTOR; +#endif + + // Fix-up for (7th+) XMVECTOR parameters to pass by reference + typedef const XMVECTOR& CXMVECTOR; + + //------------------------------------------------------------------------------ + // Conversion types for constants + XM_ALIGNED_STRUCT(16) XMVECTORF32 + { + union + { + float f[4]; + XMVECTOR v; + }; + + inline operator XMVECTOR() const noexcept { return v; } + inline operator const float* () const noexcept { return f; } + #ifdef _XM_NO_INTRINSICS_ + #elif defined(_XM_SSE_INTRINSICS_) + inline operator __m128i() const noexcept { return _mm_castps_si128(v); } + inline operator __m128d() const noexcept { return _mm_castps_pd(v); } + #elif defined(_XM_ARM_NEON_INTRINSICS_) && (defined(__GNUC__) || defined(_ARM64_DISTINCT_NEON_TYPES)) + inline operator int32x4_t() const noexcept { return vreinterpretq_s32_f32(v); } + inline operator uint32x4_t() const noexcept { return vreinterpretq_u32_f32(v); } + #endif + }; + + XM_ALIGNED_STRUCT(16) XMVECTORI32 + { + union + { + int32_t i[4]; + XMVECTOR v; + }; + + inline operator XMVECTOR() const noexcept { return v; } + #ifdef _XM_NO_INTRINSICS_ + #elif defined(_XM_SSE_INTRINSICS_) + inline operator __m128i() const noexcept { return _mm_castps_si128(v); } + inline operator __m128d() const noexcept { return _mm_castps_pd(v); } + #elif defined(_XM_ARM_NEON_INTRINSICS_) && (defined(__GNUC__) || defined(_ARM64_DISTINCT_NEON_TYPES)) + inline operator int32x4_t() const noexcept { return vreinterpretq_s32_f32(v); } + inline operator uint32x4_t() const noexcept { return vreinterpretq_u32_f32(v); } + #endif + }; + + XM_ALIGNED_STRUCT(16) XMVECTORU8 + { + union + { + uint8_t u[16]; + XMVECTOR v; + }; + + inline operator XMVECTOR() const noexcept { return v; } + #ifdef _XM_NO_INTRINSICS_ + #elif defined(_XM_SSE_INTRINSICS_) + inline operator __m128i() const noexcept { return _mm_castps_si128(v); } + inline operator __m128d() const noexcept { return _mm_castps_pd(v); } + #elif defined(_XM_ARM_NEON_INTRINSICS_) && (defined(__GNUC__) || defined(_ARM64_DISTINCT_NEON_TYPES)) + inline operator int32x4_t() const noexcept { return vreinterpretq_s32_f32(v); } + inline operator uint32x4_t() const noexcept { return vreinterpretq_u32_f32(v); } + #endif + }; + + XM_ALIGNED_STRUCT(16) XMVECTORU32 + { + union + { + uint32_t u[4]; + XMVECTOR v; + }; + + inline operator XMVECTOR() const noexcept { return v; } + #ifdef _XM_NO_INTRINSICS_ + #elif defined(_XM_SSE_INTRINSICS_) + inline operator __m128i() const noexcept { return _mm_castps_si128(v); } + inline operator __m128d() const noexcept { return _mm_castps_pd(v); } + #elif defined(_XM_ARM_NEON_INTRINSICS_) && (defined(__GNUC__) || defined(_ARM64_DISTINCT_NEON_TYPES)) + inline operator int32x4_t() const noexcept { return vreinterpretq_s32_f32(v); } + inline operator uint32x4_t() const noexcept { return vreinterpretq_u32_f32(v); } + #endif + }; + + //------------------------------------------------------------------------------ + // Vector operators + +#ifndef _XM_NO_XMVECTOR_OVERLOADS_ + XMVECTOR XM_CALLCONV operator+ (FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV operator- (FXMVECTOR V) noexcept; + + XMVECTOR& XM_CALLCONV operator+= (XMVECTOR& V1, FXMVECTOR V2) noexcept; + XMVECTOR& XM_CALLCONV operator-= (XMVECTOR& V1, FXMVECTOR V2) noexcept; + XMVECTOR& XM_CALLCONV operator*= (XMVECTOR& V1, FXMVECTOR V2) noexcept; + XMVECTOR& XM_CALLCONV operator/= (XMVECTOR& V1, FXMVECTOR V2) noexcept; + + XMVECTOR& operator*= (XMVECTOR& V, float S) noexcept; + XMVECTOR& operator/= (XMVECTOR& V, float S) noexcept; + + XMVECTOR XM_CALLCONV operator+ (FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV operator- (FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV operator* (FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV operator/ (FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV operator* (FXMVECTOR V, float S) noexcept; + XMVECTOR XM_CALLCONV operator* (float S, FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV operator/ (FXMVECTOR V, float S) noexcept; +#endif /* !_XM_NO_XMVECTOR_OVERLOADS_ */ + + //------------------------------------------------------------------------------ + // Matrix type: Sixteen 32 bit floating point components aligned on a + // 16 byte boundary and mapped to four hardware vector registers + + struct XMMATRIX; + + // Fix-up for (1st) XMMATRIX parameter to pass in-register for ARM64 and vector call; by reference otherwise +#if ( defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || _XM_VECTORCALL_ || __aarch64__ ) && !defined(_XM_NO_INTRINSICS_) + typedef const XMMATRIX FXMMATRIX; +#else + typedef const XMMATRIX& FXMMATRIX; +#endif + + // Fix-up for (2nd+) XMMATRIX parameters to pass by reference + typedef const XMMATRIX& CXMMATRIX; + +#ifdef _XM_NO_INTRINSICS_ + struct XMMATRIX + #else + XM_ALIGNED_STRUCT(16) XMMATRIX + #endif + { + #ifdef _XM_NO_INTRINSICS_ + union + { + XMVECTOR r[4]; + struct + { + float _11, _12, _13, _14; + float _21, _22, _23, _24; + float _31, _32, _33, _34; + float _41, _42, _43, _44; + }; + float m[4][4]; + }; + #else + XMVECTOR r[4]; + #endif + + XMMATRIX() = default; + + XMMATRIX(const XMMATRIX&) = default; + + #if defined(_MSC_VER) && (_MSC_FULL_VER < 191426431) + XMMATRIX& operator= (const XMMATRIX& M) noexcept { r[0] = M.r[0]; r[1] = M.r[1]; r[2] = M.r[2]; r[3] = M.r[3]; return *this; } + #else + XMMATRIX& operator=(const XMMATRIX&) = default; + + XMMATRIX(XMMATRIX&&) = default; + XMMATRIX& operator=(XMMATRIX&&) = default; + #endif + + constexpr XMMATRIX(FXMVECTOR R0, FXMVECTOR R1, FXMVECTOR R2, CXMVECTOR R3) noexcept : r{ R0,R1,R2,R3 } {} + XMMATRIX(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; + explicit XMMATRIX(_In_reads_(16) const float* pArray) noexcept; + + #ifdef _XM_NO_INTRINSICS_ + float operator() (size_t Row, size_t Column) const noexcept { return m[Row][Column]; } + float& operator() (size_t Row, size_t Column) noexcept { return m[Row][Column]; } + #endif + + XMMATRIX operator+ () const noexcept { return *this; } + XMMATRIX operator- () const noexcept; + + XMMATRIX& XM_CALLCONV operator+= (FXMMATRIX M) noexcept; + XMMATRIX& XM_CALLCONV operator-= (FXMMATRIX M) noexcept; + XMMATRIX& XM_CALLCONV operator*= (FXMMATRIX M) noexcept; + XMMATRIX& operator*= (float S) noexcept; + XMMATRIX& operator/= (float S) noexcept; + + XMMATRIX XM_CALLCONV operator+ (FXMMATRIX M) const noexcept; + XMMATRIX XM_CALLCONV operator- (FXMMATRIX M) const noexcept; + XMMATRIX XM_CALLCONV operator* (FXMMATRIX M) const noexcept; + XMMATRIX operator* (float S) const noexcept; + XMMATRIX operator/ (float S) const noexcept; + + friend XMMATRIX XM_CALLCONV operator* (float S, FXMMATRIX M) noexcept; + }; + + //------------------------------------------------------------------------------ + // 2D Vector; 32 bit floating point components + struct XMFLOAT2 + { + float x; + float y; + + XMFLOAT2() = default; + + XMFLOAT2(const XMFLOAT2&) = default; + XMFLOAT2& operator=(const XMFLOAT2&) = default; + + XMFLOAT2(XMFLOAT2&&) = default; + XMFLOAT2& operator=(XMFLOAT2&&) = default; + + constexpr XMFLOAT2(float _x, float _y) noexcept : x(_x), y(_y) {} + explicit XMFLOAT2(_In_reads_(2) const float* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + + #if (__cplusplus >= 202002L) + bool operator == (const XMFLOAT2&) const = default; + auto operator <=> (const XMFLOAT2&) const = default; + #endif + }; + + // 2D Vector; 32 bit floating point components aligned on a 16 byte boundary + XM_ALIGNED_STRUCT(16) XMFLOAT2A : public XMFLOAT2 + { + using XMFLOAT2::XMFLOAT2; + }; + + //------------------------------------------------------------------------------ + // 2D Vector; 32 bit signed integer components + struct XMINT2 + { + int32_t x; + int32_t y; + + XMINT2() = default; + + XMINT2(const XMINT2&) = default; + XMINT2& operator=(const XMINT2&) = default; + + XMINT2(XMINT2&&) = default; + XMINT2& operator=(XMINT2&&) = default; + + constexpr XMINT2(int32_t _x, int32_t _y) noexcept : x(_x), y(_y) {} + explicit XMINT2(_In_reads_(2) const int32_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + + #if (__cplusplus >= 202002L) + bool operator == (const XMINT2&) const = default; + auto operator <=> (const XMINT2&) const = default; + #endif + }; + + // 2D Vector; 32 bit unsigned integer components + struct XMUINT2 + { + uint32_t x; + uint32_t y; + + XMUINT2() = default; + + XMUINT2(const XMUINT2&) = default; + XMUINT2& operator=(const XMUINT2&) = default; + + XMUINT2(XMUINT2&&) = default; + XMUINT2& operator=(XMUINT2&&) = default; + + constexpr XMUINT2(uint32_t _x, uint32_t _y) noexcept : x(_x), y(_y) {} + explicit XMUINT2(_In_reads_(2) const uint32_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + + #if (__cplusplus >= 202002L) + bool operator == (const XMUINT2&) const = default; + auto operator <=> (const XMUINT2&) const = default; + #endif + }; + + //------------------------------------------------------------------------------ + // 3D Vector; 32 bit floating point components + struct XMFLOAT3 + { + float x; + float y; + float z; + + XMFLOAT3() = default; + + XMFLOAT3(const XMFLOAT3&) = default; + XMFLOAT3& operator=(const XMFLOAT3&) = default; + + XMFLOAT3(XMFLOAT3&&) = default; + XMFLOAT3& operator=(XMFLOAT3&&) = default; + + constexpr XMFLOAT3(float _x, float _y, float _z) noexcept : x(_x), y(_y), z(_z) {} + explicit XMFLOAT3(_In_reads_(3) const float* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]) {} + }; + + // 3D Vector; 32 bit floating point components aligned on a 16 byte boundary + XM_ALIGNED_STRUCT(16) XMFLOAT3A : public XMFLOAT3 + { + using XMFLOAT3::XMFLOAT3; + }; + + //------------------------------------------------------------------------------ + // 3D Vector; 32 bit signed integer components + struct XMINT3 + { + int32_t x; + int32_t y; + int32_t z; + + XMINT3() = default; + + XMINT3(const XMINT3&) = default; + XMINT3& operator=(const XMINT3&) = default; + + XMINT3(XMINT3&&) = default; + XMINT3& operator=(XMINT3&&) = default; + + constexpr XMINT3(int32_t _x, int32_t _y, int32_t _z) noexcept : x(_x), y(_y), z(_z) {} + explicit XMINT3(_In_reads_(3) const int32_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]) {} + + #if (__cplusplus >= 202002L) + bool operator == (const XMINT3&) const = default; + auto operator <=> (const XMINT3&) const = default; + #endif + }; + + // 3D Vector; 32 bit unsigned integer components + struct XMUINT3 + { + uint32_t x; + uint32_t y; + uint32_t z; + + XMUINT3() = default; + + XMUINT3(const XMUINT3&) = default; + XMUINT3& operator=(const XMUINT3&) = default; + + XMUINT3(XMUINT3&&) = default; + XMUINT3& operator=(XMUINT3&&) = default; + + constexpr XMUINT3(uint32_t _x, uint32_t _y, uint32_t _z) noexcept : x(_x), y(_y), z(_z) {} + explicit XMUINT3(_In_reads_(3) const uint32_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]) {} + + #if (__cplusplus >= 202002L) + bool operator == (const XMUINT3&) const = default; + auto operator <=> (const XMUINT3&) const = default; + #endif + }; + + //------------------------------------------------------------------------------ + // 4D Vector; 32 bit floating point components + struct XMFLOAT4 + { + float x; + float y; + float z; + float w; + + XMFLOAT4() = default; + + XMFLOAT4(const XMFLOAT4&) = default; + XMFLOAT4& operator=(const XMFLOAT4&) = default; + + XMFLOAT4(XMFLOAT4&&) = default; + XMFLOAT4& operator=(XMFLOAT4&&) = default; + + constexpr XMFLOAT4(float _x, float _y, float _z, float _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit XMFLOAT4(_In_reads_(4) const float* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + + #if (__cplusplus >= 202002L) + bool operator == (const XMFLOAT4&) const = default; + auto operator <=> (const XMFLOAT4&) const = default; + #endif + }; + + // 4D Vector; 32 bit floating point components aligned on a 16 byte boundary + XM_ALIGNED_STRUCT(16) XMFLOAT4A : public XMFLOAT4 + { + using XMFLOAT4::XMFLOAT4; + }; + + //------------------------------------------------------------------------------ + // 4D Vector; 32 bit signed integer components + struct XMINT4 + { + int32_t x; + int32_t y; + int32_t z; + int32_t w; + + XMINT4() = default; + + XMINT4(const XMINT4&) = default; + XMINT4& operator=(const XMINT4&) = default; + + XMINT4(XMINT4&&) = default; + XMINT4& operator=(XMINT4&&) = default; + + constexpr XMINT4(int32_t _x, int32_t _y, int32_t _z, int32_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit XMINT4(_In_reads_(4) const int32_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + + #if (__cplusplus >= 202002L) + bool operator == (const XMINT4&) const = default; + auto operator <=> (const XMINT4&) const = default; + #endif + }; + + // 4D Vector; 32 bit unsigned integer components + struct XMUINT4 + { + uint32_t x; + uint32_t y; + uint32_t z; + uint32_t w; + + XMUINT4() = default; + + XMUINT4(const XMUINT4&) = default; + XMUINT4& operator=(const XMUINT4&) = default; + + XMUINT4(XMUINT4&&) = default; + XMUINT4& operator=(XMUINT4&&) = default; + + constexpr XMUINT4(uint32_t _x, uint32_t _y, uint32_t _z, uint32_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit XMUINT4(_In_reads_(4) const uint32_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + + #if (__cplusplus >= 202002L) + bool operator == (const XMUINT4&) const = default; + auto operator <=> (const XMUINT4&) const = default; + #endif + }; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + + //------------------------------------------------------------------------------ + // 3x3 Matrix: 32 bit floating point components + struct XMFLOAT3X3 + { + union + { + struct + { + float _11, _12, _13; + float _21, _22, _23; + float _31, _32, _33; + }; + float m[3][3]; + }; + + XMFLOAT3X3() = default; + + XMFLOAT3X3(const XMFLOAT3X3&) = default; + XMFLOAT3X3& operator=(const XMFLOAT3X3&) = default; + + XMFLOAT3X3(XMFLOAT3X3&&) = default; + XMFLOAT3X3& operator=(XMFLOAT3X3&&) = default; + + constexpr XMFLOAT3X3(float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22) noexcept + : _11(m00), _12(m01), _13(m02), + _21(m10), _22(m11), _23(m12), + _31(m20), _32(m21), _33(m22) + {} + explicit XMFLOAT3X3(_In_reads_(9) const float* pArray) noexcept; + + float operator() (size_t Row, size_t Column) const noexcept { return m[Row][Column]; } + float& operator() (size_t Row, size_t Column) noexcept { return m[Row][Column]; } + + #if (__cplusplus >= 202002L) + bool operator == (const XMFLOAT3X3&) const = default; + auto operator <=> (const XMFLOAT3X3&) const = default; + #endif + }; + + //------------------------------------------------------------------------------ + // 4x3 Row-major Matrix: 32 bit floating point components + struct XMFLOAT4X3 + { + union + { + struct + { + float _11, _12, _13; + float _21, _22, _23; + float _31, _32, _33; + float _41, _42, _43; + }; + float m[4][3]; + float f[12]; + }; + + XMFLOAT4X3() = default; + + XMFLOAT4X3(const XMFLOAT4X3&) = default; + XMFLOAT4X3& operator=(const XMFLOAT4X3&) = default; + + XMFLOAT4X3(XMFLOAT4X3&&) = default; + XMFLOAT4X3& operator=(XMFLOAT4X3&&) = default; + + constexpr XMFLOAT4X3(float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22, + float m30, float m31, float m32) noexcept + : _11(m00), _12(m01), _13(m02), + _21(m10), _22(m11), _23(m12), + _31(m20), _32(m21), _33(m22), + _41(m30), _42(m31), _43(m32) + {} + explicit XMFLOAT4X3(_In_reads_(12) const float* pArray) noexcept; + + float operator() (size_t Row, size_t Column) const noexcept { return m[Row][Column]; } + float& operator() (size_t Row, size_t Column) noexcept { return m[Row][Column]; } + + #if (__cplusplus >= 202002L) + bool operator == (const XMFLOAT4X3&) const = default; + auto operator <=> (const XMFLOAT4X3&) const = default; + #endif + }; + + // 4x3 Row-major Matrix: 32 bit floating point components aligned on a 16 byte boundary + XM_ALIGNED_STRUCT(16) XMFLOAT4X3A : public XMFLOAT4X3 + { + using XMFLOAT4X3::XMFLOAT4X3; + }; + + //------------------------------------------------------------------------------ + // 3x4 Column-major Matrix: 32 bit floating point components + struct XMFLOAT3X4 + { + union + { + struct + { + float _11, _12, _13, _14; + float _21, _22, _23, _24; + float _31, _32, _33, _34; + }; + float m[3][4]; + float f[12]; + }; + + XMFLOAT3X4() = default; + + XMFLOAT3X4(const XMFLOAT3X4&) = default; + XMFLOAT3X4& operator=(const XMFLOAT3X4&) = default; + + XMFLOAT3X4(XMFLOAT3X4&&) = default; + XMFLOAT3X4& operator=(XMFLOAT3X4&&) = default; + + constexpr XMFLOAT3X4(float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23) noexcept + : _11(m00), _12(m01), _13(m02), _14(m03), + _21(m10), _22(m11), _23(m12), _24(m13), + _31(m20), _32(m21), _33(m22), _34(m23) + {} + explicit XMFLOAT3X4(_In_reads_(12) const float* pArray) noexcept; + + float operator() (size_t Row, size_t Column) const noexcept { return m[Row][Column]; } + float& operator() (size_t Row, size_t Column) noexcept { return m[Row][Column]; } + + #if (__cplusplus >= 202002L) + bool operator == (const XMFLOAT3X4&) const = default; + auto operator <=> (const XMFLOAT3X4&) const = default; + #endif + }; + + // 3x4 Column-major Matrix: 32 bit floating point components aligned on a 16 byte boundary + XM_ALIGNED_STRUCT(16) XMFLOAT3X4A : public XMFLOAT3X4 + { + using XMFLOAT3X4::XMFLOAT3X4; + }; + + //------------------------------------------------------------------------------ + // 4x4 Matrix: 32 bit floating point components + struct XMFLOAT4X4 + { + union + { + struct + { + float _11, _12, _13, _14; + float _21, _22, _23, _24; + float _31, _32, _33, _34; + float _41, _42, _43, _44; + }; + float m[4][4]; + }; + + XMFLOAT4X4() = default; + + XMFLOAT4X4(const XMFLOAT4X4&) = default; + XMFLOAT4X4& operator=(const XMFLOAT4X4&) = default; + + XMFLOAT4X4(XMFLOAT4X4&&) = default; + XMFLOAT4X4& operator=(XMFLOAT4X4&&) = default; + + constexpr XMFLOAT4X4(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 + : _11(m00), _12(m01), _13(m02), _14(m03), + _21(m10), _22(m11), _23(m12), _24(m13), + _31(m20), _32(m21), _33(m22), _34(m23), + _41(m30), _42(m31), _43(m32), _44(m33) + {} + explicit XMFLOAT4X4(_In_reads_(16) const float* pArray) noexcept; + + float operator() (size_t Row, size_t Column) const noexcept { return m[Row][Column]; } + float& operator() (size_t Row, size_t Column) noexcept { return m[Row][Column]; } + + #if (__cplusplus >= 202002L) + bool operator == (const XMFLOAT4X4&) const = default; + auto operator <=> (const XMFLOAT4X4&) const = default; + #endif + }; + + // 4x4 Matrix: 32 bit floating point components aligned on a 16 byte boundary + XM_ALIGNED_STRUCT(16) XMFLOAT4X4A : public XMFLOAT4X4 + { + using XMFLOAT4X4::XMFLOAT4X4; + }; + + //////////////////////////////////////////////////////////////////////////////// + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +/**************************************************************************** + * + * Data conversion operations + * + ****************************************************************************/ + + XMVECTOR XM_CALLCONV XMConvertVectorIntToFloat(FXMVECTOR VInt, uint32_t DivExponent) noexcept; + XMVECTOR XM_CALLCONV XMConvertVectorFloatToInt(FXMVECTOR VFloat, uint32_t MulExponent) noexcept; + XMVECTOR XM_CALLCONV XMConvertVectorUIntToFloat(FXMVECTOR VUInt, uint32_t DivExponent) noexcept; + XMVECTOR XM_CALLCONV XMConvertVectorFloatToUInt(FXMVECTOR VFloat, uint32_t MulExponent) noexcept; + +#if defined(__XNAMATH_H__) && defined(XMVectorSetBinaryConstant) +#undef XMVectorSetBinaryConstant +#undef XMVectorSplatConstant +#undef XMVectorSplatConstantInt +#endif + + XMVECTOR XM_CALLCONV XMVectorSetBinaryConstant(uint32_t C0, uint32_t C1, uint32_t C2, uint32_t C3) noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatConstant(int32_t IntConstant, uint32_t DivExponent) noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatConstantInt(int32_t IntConstant) noexcept; + + /**************************************************************************** + * + * Load operations + * + ****************************************************************************/ + + XMVECTOR XM_CALLCONV XMLoadInt(_In_ const uint32_t* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadFloat(_In_ const float* pSource) noexcept; + + XMVECTOR XM_CALLCONV XMLoadInt2(_In_reads_(2) const uint32_t* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadInt2A(_In_reads_(2) const uint32_t* PSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadFloat2(_In_ const XMFLOAT2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadFloat2A(_In_ const XMFLOAT2A* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadSInt2(_In_ const XMINT2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUInt2(_In_ const XMUINT2* pSource) noexcept; + + XMVECTOR XM_CALLCONV XMLoadInt3(_In_reads_(3) const uint32_t* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadInt3A(_In_reads_(3) const uint32_t* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadFloat3(_In_ const XMFLOAT3* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadFloat3A(_In_ const XMFLOAT3A* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadSInt3(_In_ const XMINT3* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUInt3(_In_ const XMUINT3* pSource) noexcept; + + XMVECTOR XM_CALLCONV XMLoadInt4(_In_reads_(4) const uint32_t* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadInt4A(_In_reads_(4) const uint32_t* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadFloat4(_In_ const XMFLOAT4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadFloat4A(_In_ const XMFLOAT4A* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadSInt4(_In_ const XMINT4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUInt4(_In_ const XMUINT4* pSource) noexcept; + + XMMATRIX XM_CALLCONV XMLoadFloat3x3(_In_ const XMFLOAT3X3* pSource) noexcept; + XMMATRIX XM_CALLCONV XMLoadFloat4x3(_In_ const XMFLOAT4X3* pSource) noexcept; + XMMATRIX XM_CALLCONV XMLoadFloat4x3A(_In_ const XMFLOAT4X3A* pSource) noexcept; + XMMATRIX XM_CALLCONV XMLoadFloat3x4(_In_ const XMFLOAT3X4* pSource) noexcept; + XMMATRIX XM_CALLCONV XMLoadFloat3x4A(_In_ const XMFLOAT3X4A* pSource) noexcept; + XMMATRIX XM_CALLCONV XMLoadFloat4x4(_In_ const XMFLOAT4X4* pSource) noexcept; + XMMATRIX XM_CALLCONV XMLoadFloat4x4A(_In_ const XMFLOAT4X4A* pSource) noexcept; + + /**************************************************************************** + * + * Store operations + * + ****************************************************************************/ + + void XM_CALLCONV XMStoreInt(_Out_ uint32_t* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreFloat(_Out_ float* pDestination, _In_ FXMVECTOR V) noexcept; + + void XM_CALLCONV XMStoreInt2(_Out_writes_(2) uint32_t* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreInt2A(_Out_writes_(2) uint32_t* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreFloat2(_Out_ XMFLOAT2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreFloat2A(_Out_ XMFLOAT2A* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreSInt2(_Out_ XMINT2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUInt2(_Out_ XMUINT2* pDestination, _In_ FXMVECTOR V) noexcept; + + void XM_CALLCONV XMStoreInt3(_Out_writes_(3) uint32_t* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreInt3A(_Out_writes_(3) uint32_t* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreFloat3(_Out_ XMFLOAT3* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreFloat3A(_Out_ XMFLOAT3A* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreSInt3(_Out_ XMINT3* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUInt3(_Out_ XMUINT3* pDestination, _In_ FXMVECTOR V) noexcept; + + void XM_CALLCONV XMStoreInt4(_Out_writes_(4) uint32_t* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreInt4A(_Out_writes_(4) uint32_t* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreFloat4(_Out_ XMFLOAT4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreFloat4A(_Out_ XMFLOAT4A* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreSInt4(_Out_ XMINT4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUInt4(_Out_ XMUINT4* pDestination, _In_ FXMVECTOR V) noexcept; + + void XM_CALLCONV XMStoreFloat3x3(_Out_ XMFLOAT3X3* pDestination, _In_ FXMMATRIX M) noexcept; + void XM_CALLCONV XMStoreFloat4x3(_Out_ XMFLOAT4X3* pDestination, _In_ FXMMATRIX M) noexcept; + void XM_CALLCONV XMStoreFloat4x3A(_Out_ XMFLOAT4X3A* pDestination, _In_ FXMMATRIX M) noexcept; + void XM_CALLCONV XMStoreFloat3x4(_Out_ XMFLOAT3X4* pDestination, _In_ FXMMATRIX M) noexcept; + void XM_CALLCONV XMStoreFloat3x4A(_Out_ XMFLOAT3X4A* pDestination, _In_ FXMMATRIX M) noexcept; + void XM_CALLCONV XMStoreFloat4x4(_Out_ XMFLOAT4X4* pDestination, _In_ FXMMATRIX M) noexcept; + void XM_CALLCONV XMStoreFloat4x4A(_Out_ XMFLOAT4X4A* pDestination, _In_ FXMMATRIX M) noexcept; + + /**************************************************************************** + * + * General vector operations + * + ****************************************************************************/ + + XMVECTOR XM_CALLCONV XMVectorZero() noexcept; + XMVECTOR XM_CALLCONV XMVectorSet(float x, float y, float z, float w) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetInt(uint32_t x, uint32_t y, uint32_t z, uint32_t w) noexcept; + XMVECTOR XM_CALLCONV XMVectorReplicate(float Value) noexcept; + XMVECTOR XM_CALLCONV XMVectorReplicatePtr(_In_ const float* pValue) noexcept; + XMVECTOR XM_CALLCONV XMVectorReplicateInt(uint32_t Value) noexcept; + XMVECTOR XM_CALLCONV XMVectorReplicateIntPtr(_In_ const uint32_t* pValue) noexcept; + XMVECTOR XM_CALLCONV XMVectorTrueInt() noexcept; + XMVECTOR XM_CALLCONV XMVectorFalseInt() noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatX(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatY(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatZ(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatW(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatOne() noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatInfinity() noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatQNaN() noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatEpsilon() noexcept; + XMVECTOR XM_CALLCONV XMVectorSplatSignMask() noexcept; + + float XM_CALLCONV XMVectorGetByIndex(FXMVECTOR V, size_t i) noexcept; + float XM_CALLCONV XMVectorGetX(FXMVECTOR V) noexcept; + float XM_CALLCONV XMVectorGetY(FXMVECTOR V) noexcept; + float XM_CALLCONV XMVectorGetZ(FXMVECTOR V) noexcept; + float XM_CALLCONV XMVectorGetW(FXMVECTOR V) noexcept; + + void XM_CALLCONV XMVectorGetByIndexPtr(_Out_ float* f, _In_ FXMVECTOR V, _In_ size_t i) noexcept; + void XM_CALLCONV XMVectorGetXPtr(_Out_ float* x, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMVectorGetYPtr(_Out_ float* y, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMVectorGetZPtr(_Out_ float* z, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMVectorGetWPtr(_Out_ float* w, _In_ FXMVECTOR V) noexcept; + + uint32_t XM_CALLCONV XMVectorGetIntByIndex(FXMVECTOR V, size_t i) noexcept; + uint32_t XM_CALLCONV XMVectorGetIntX(FXMVECTOR V) noexcept; + uint32_t XM_CALLCONV XMVectorGetIntY(FXMVECTOR V) noexcept; + uint32_t XM_CALLCONV XMVectorGetIntZ(FXMVECTOR V) noexcept; + uint32_t XM_CALLCONV XMVectorGetIntW(FXMVECTOR V) noexcept; + + void XM_CALLCONV XMVectorGetIntByIndexPtr(_Out_ uint32_t* x, _In_ FXMVECTOR V, _In_ size_t i) noexcept; + void XM_CALLCONV XMVectorGetIntXPtr(_Out_ uint32_t* x, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMVectorGetIntYPtr(_Out_ uint32_t* y, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMVectorGetIntZPtr(_Out_ uint32_t* z, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMVectorGetIntWPtr(_Out_ uint32_t* w, _In_ FXMVECTOR V) noexcept; + + XMVECTOR XM_CALLCONV XMVectorSetByIndex(FXMVECTOR V, float f, size_t i) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetX(FXMVECTOR V, float x) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetY(FXMVECTOR V, float y) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetZ(FXMVECTOR V, float z) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetW(FXMVECTOR V, float w) noexcept; + + XMVECTOR XM_CALLCONV XMVectorSetByIndexPtr(_In_ FXMVECTOR V, _In_ const float* f, _In_ size_t i) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetXPtr(_In_ FXMVECTOR V, _In_ const float* x) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetYPtr(_In_ FXMVECTOR V, _In_ const float* y) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetZPtr(_In_ FXMVECTOR V, _In_ const float* z) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetWPtr(_In_ FXMVECTOR V, _In_ const float* w) noexcept; + + XMVECTOR XM_CALLCONV XMVectorSetIntByIndex(FXMVECTOR V, uint32_t x, size_t i) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetIntX(FXMVECTOR V, uint32_t x) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetIntY(FXMVECTOR V, uint32_t y) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetIntZ(FXMVECTOR V, uint32_t z) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetIntW(FXMVECTOR V, uint32_t w) noexcept; + + XMVECTOR XM_CALLCONV XMVectorSetIntByIndexPtr(_In_ FXMVECTOR V, _In_ const uint32_t* x, _In_ size_t i) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetIntXPtr(_In_ FXMVECTOR V, _In_ const uint32_t* x) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetIntYPtr(_In_ FXMVECTOR V, _In_ const uint32_t* y) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetIntZPtr(_In_ FXMVECTOR V, _In_ const uint32_t* z) noexcept; + XMVECTOR XM_CALLCONV XMVectorSetIntWPtr(_In_ FXMVECTOR V, _In_ const uint32_t* w) noexcept; + +#if defined(__XNAMATH_H__) && defined(XMVectorSwizzle) +#undef XMVectorSwizzle +#endif + + XMVECTOR XM_CALLCONV XMVectorSwizzle(FXMVECTOR V, uint32_t E0, uint32_t E1, uint32_t E2, uint32_t E3) noexcept; + XMVECTOR XM_CALLCONV XMVectorPermute(FXMVECTOR V1, FXMVECTOR V2, uint32_t PermuteX, uint32_t PermuteY, uint32_t PermuteZ, uint32_t PermuteW) noexcept; + XMVECTOR XM_CALLCONV XMVectorSelectControl(uint32_t VectorIndex0, uint32_t VectorIndex1, uint32_t VectorIndex2, uint32_t VectorIndex3) noexcept; + XMVECTOR XM_CALLCONV XMVectorSelect(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR Control) noexcept; + XMVECTOR XM_CALLCONV XMVectorMergeXY(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorMergeZW(FXMVECTOR V1, FXMVECTOR V2) noexcept; + +#if defined(__XNAMATH_H__) && defined(XMVectorShiftLeft) +#undef XMVectorShiftLeft +#undef XMVectorRotateLeft +#undef XMVectorRotateRight +#undef XMVectorInsert +#endif + + XMVECTOR XM_CALLCONV XMVectorShiftLeft(FXMVECTOR V1, FXMVECTOR V2, uint32_t Elements) noexcept; + XMVECTOR XM_CALLCONV XMVectorRotateLeft(FXMVECTOR V, uint32_t Elements) noexcept; + XMVECTOR XM_CALLCONV XMVectorRotateRight(FXMVECTOR V, uint32_t Elements) noexcept; + XMVECTOR XM_CALLCONV XMVectorInsert(FXMVECTOR VD, FXMVECTOR VS, uint32_t VSLeftRotateElements, + uint32_t Select0, uint32_t Select1, uint32_t Select2, uint32_t Select3) noexcept; + + XMVECTOR XM_CALLCONV XMVectorEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorEqualR(_Out_ uint32_t* pCR, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorEqualInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorEqualIntR(_Out_ uint32_t* pCR, _In_ FXMVECTOR V, _In_ FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorNearEqual(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR Epsilon) noexcept; + XMVECTOR XM_CALLCONV XMVectorNotEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorNotEqualInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorGreater(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorGreaterR(_Out_ uint32_t* pCR, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorGreaterOrEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorGreaterOrEqualR(_Out_ uint32_t* pCR, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorLess(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorLessOrEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorInBounds(FXMVECTOR V, FXMVECTOR Bounds) noexcept; + XMVECTOR XM_CALLCONV XMVectorInBoundsR(_Out_ uint32_t* pCR, _In_ FXMVECTOR V, _In_ FXMVECTOR Bounds) noexcept; + + XMVECTOR XM_CALLCONV XMVectorIsNaN(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorIsInfinite(FXMVECTOR V) noexcept; + + XMVECTOR XM_CALLCONV XMVectorMin(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorMax(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorRound(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorTruncate(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorFloor(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorCeiling(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorClamp(FXMVECTOR V, FXMVECTOR Min, FXMVECTOR Max) noexcept; + XMVECTOR XM_CALLCONV XMVectorSaturate(FXMVECTOR V) noexcept; + + XMVECTOR XM_CALLCONV XMVectorAndInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorAndCInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorOrInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorNorInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorXorInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + + XMVECTOR XM_CALLCONV XMVectorNegate(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorAdd(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorSum(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorAddAngles(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorSubtract(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorSubtractAngles(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorMultiply(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorMultiplyAdd(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR V3) noexcept; + XMVECTOR XM_CALLCONV XMVectorDivide(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorNegativeMultiplySubtract(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR V3) noexcept; + XMVECTOR XM_CALLCONV XMVectorScale(FXMVECTOR V, float ScaleFactor) noexcept; + XMVECTOR XM_CALLCONV XMVectorReciprocalEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorReciprocal(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorSqrtEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorSqrt(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorReciprocalSqrtEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorReciprocalSqrt(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorExp2(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorExp10(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorExpE(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorExp(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorLog2(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorLog10(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorLogE(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorLog(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorPow(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorAbs(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorMod(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVectorModAngles(FXMVECTOR Angles) noexcept; + XMVECTOR XM_CALLCONV XMVectorSin(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorSinEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorCos(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorCosEst(FXMVECTOR V) noexcept; + void XM_CALLCONV XMVectorSinCos(_Out_ XMVECTOR* pSin, _Out_ XMVECTOR* pCos, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMVectorSinCosEst(_Out_ XMVECTOR* pSin, _Out_ XMVECTOR* pCos, _In_ FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorTan(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorTanEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorSinH(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorCosH(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorTanH(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorASin(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorASinEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorACos(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorACosEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorATan(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorATanEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVectorATan2(FXMVECTOR Y, FXMVECTOR X) noexcept; + XMVECTOR XM_CALLCONV XMVectorATan2Est(FXMVECTOR Y, FXMVECTOR X) noexcept; + XMVECTOR XM_CALLCONV XMVectorLerp(FXMVECTOR V0, FXMVECTOR V1, float t) noexcept; + XMVECTOR XM_CALLCONV XMVectorLerpV(FXMVECTOR V0, FXMVECTOR V1, FXMVECTOR T) noexcept; + XMVECTOR XM_CALLCONV XMVectorHermite(FXMVECTOR Position0, FXMVECTOR Tangent0, FXMVECTOR Position1, GXMVECTOR Tangent1, float t) noexcept; + XMVECTOR XM_CALLCONV XMVectorHermiteV(FXMVECTOR Position0, FXMVECTOR Tangent0, FXMVECTOR Position1, GXMVECTOR Tangent1, HXMVECTOR T) noexcept; + XMVECTOR XM_CALLCONV XMVectorCatmullRom(FXMVECTOR Position0, FXMVECTOR Position1, FXMVECTOR Position2, GXMVECTOR Position3, float t) noexcept; + XMVECTOR XM_CALLCONV XMVectorCatmullRomV(FXMVECTOR Position0, FXMVECTOR Position1, FXMVECTOR Position2, GXMVECTOR Position3, HXMVECTOR T) noexcept; + XMVECTOR XM_CALLCONV XMVectorBaryCentric(FXMVECTOR Position0, FXMVECTOR Position1, FXMVECTOR Position2, float f, float g) noexcept; + XMVECTOR XM_CALLCONV XMVectorBaryCentricV(FXMVECTOR Position0, FXMVECTOR Position1, FXMVECTOR Position2, GXMVECTOR F, HXMVECTOR G) noexcept; + + /**************************************************************************** + * + * 2D vector operations + * + ****************************************************************************/ + + bool XM_CALLCONV XMVector2Equal(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector2EqualR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector2EqualInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector2EqualIntR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector2NearEqual(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR Epsilon) noexcept; + bool XM_CALLCONV XMVector2NotEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector2NotEqualInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector2Greater(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector2GreaterR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector2GreaterOrEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector2GreaterOrEqualR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector2Less(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector2LessOrEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector2InBounds(FXMVECTOR V, FXMVECTOR Bounds) noexcept; + + bool XM_CALLCONV XMVector2IsNaN(FXMVECTOR V) noexcept; + bool XM_CALLCONV XMVector2IsInfinite(FXMVECTOR V) noexcept; + + XMVECTOR XM_CALLCONV XMVector2Dot(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVector2Cross(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVector2LengthSq(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector2ReciprocalLengthEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector2ReciprocalLength(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector2LengthEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector2Length(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector2NormalizeEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector2Normalize(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector2ClampLength(FXMVECTOR V, float LengthMin, float LengthMax) noexcept; + XMVECTOR XM_CALLCONV XMVector2ClampLengthV(FXMVECTOR V, FXMVECTOR LengthMin, FXMVECTOR LengthMax) noexcept; + XMVECTOR XM_CALLCONV XMVector2Reflect(FXMVECTOR Incident, FXMVECTOR Normal) noexcept; + XMVECTOR XM_CALLCONV XMVector2Refract(FXMVECTOR Incident, FXMVECTOR Normal, float RefractionIndex) noexcept; + XMVECTOR XM_CALLCONV XMVector2RefractV(FXMVECTOR Incident, FXMVECTOR Normal, FXMVECTOR RefractionIndex) noexcept; + XMVECTOR XM_CALLCONV XMVector2Orthogonal(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector2AngleBetweenNormalsEst(FXMVECTOR N1, FXMVECTOR N2) noexcept; + XMVECTOR XM_CALLCONV XMVector2AngleBetweenNormals(FXMVECTOR N1, FXMVECTOR N2) noexcept; + XMVECTOR XM_CALLCONV XMVector2AngleBetweenVectors(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVector2LinePointDistance(FXMVECTOR LinePoint1, FXMVECTOR LinePoint2, FXMVECTOR Point) noexcept; + XMVECTOR XM_CALLCONV XMVector2IntersectLine(FXMVECTOR Line1Point1, FXMVECTOR Line1Point2, FXMVECTOR Line2Point1, GXMVECTOR Line2Point2) noexcept; + XMVECTOR XM_CALLCONV XMVector2Transform(FXMVECTOR V, FXMMATRIX M) noexcept; + XMFLOAT4* XM_CALLCONV XMVector2TransformStream(_Out_writes_bytes_(sizeof(XMFLOAT4) + OutputStride * (VectorCount - 1)) XMFLOAT4* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT2) + InputStride * (VectorCount - 1)) const XMFLOAT2* pInputStream, + _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M) noexcept; + XMVECTOR XM_CALLCONV XMVector2TransformCoord(FXMVECTOR V, FXMMATRIX M) noexcept; + XMFLOAT2* XM_CALLCONV XMVector2TransformCoordStream(_Out_writes_bytes_(sizeof(XMFLOAT2) + OutputStride * (VectorCount - 1)) XMFLOAT2* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT2) + InputStride * (VectorCount - 1)) const XMFLOAT2* pInputStream, + _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M) noexcept; + XMVECTOR XM_CALLCONV XMVector2TransformNormal(FXMVECTOR V, FXMMATRIX M) noexcept; + XMFLOAT2* XM_CALLCONV XMVector2TransformNormalStream(_Out_writes_bytes_(sizeof(XMFLOAT2) + OutputStride * (VectorCount - 1)) XMFLOAT2* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT2) + InputStride * (VectorCount - 1)) const XMFLOAT2* pInputStream, + _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M) noexcept; + + /**************************************************************************** + * + * 3D vector operations + * + ****************************************************************************/ + + bool XM_CALLCONV XMVector3Equal(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector3EqualR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector3EqualInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector3EqualIntR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector3NearEqual(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR Epsilon) noexcept; + bool XM_CALLCONV XMVector3NotEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector3NotEqualInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector3Greater(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector3GreaterR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector3GreaterOrEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector3GreaterOrEqualR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector3Less(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector3LessOrEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector3InBounds(FXMVECTOR V, FXMVECTOR Bounds) noexcept; + + bool XM_CALLCONV XMVector3IsNaN(FXMVECTOR V) noexcept; + bool XM_CALLCONV XMVector3IsInfinite(FXMVECTOR V) noexcept; + + XMVECTOR XM_CALLCONV XMVector3Dot(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVector3Cross(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVector3LengthSq(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector3ReciprocalLengthEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector3ReciprocalLength(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector3LengthEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector3Length(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector3NormalizeEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector3Normalize(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector3ClampLength(FXMVECTOR V, float LengthMin, float LengthMax) noexcept; + XMVECTOR XM_CALLCONV XMVector3ClampLengthV(FXMVECTOR V, FXMVECTOR LengthMin, FXMVECTOR LengthMax) noexcept; + XMVECTOR XM_CALLCONV XMVector3Reflect(FXMVECTOR Incident, FXMVECTOR Normal) noexcept; + XMVECTOR XM_CALLCONV XMVector3Refract(FXMVECTOR Incident, FXMVECTOR Normal, float RefractionIndex) noexcept; + XMVECTOR XM_CALLCONV XMVector3RefractV(FXMVECTOR Incident, FXMVECTOR Normal, FXMVECTOR RefractionIndex) noexcept; + XMVECTOR XM_CALLCONV XMVector3Orthogonal(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector3AngleBetweenNormalsEst(FXMVECTOR N1, FXMVECTOR N2) noexcept; + XMVECTOR XM_CALLCONV XMVector3AngleBetweenNormals(FXMVECTOR N1, FXMVECTOR N2) noexcept; + XMVECTOR XM_CALLCONV XMVector3AngleBetweenVectors(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVector3LinePointDistance(FXMVECTOR LinePoint1, FXMVECTOR LinePoint2, FXMVECTOR Point) noexcept; + void XM_CALLCONV XMVector3ComponentsFromNormal(_Out_ XMVECTOR* pParallel, _Out_ XMVECTOR* pPerpendicular, _In_ FXMVECTOR V, _In_ FXMVECTOR Normal) noexcept; + XMVECTOR XM_CALLCONV XMVector3Rotate(FXMVECTOR V, FXMVECTOR RotationQuaternion) noexcept; + XMVECTOR XM_CALLCONV XMVector3InverseRotate(FXMVECTOR V, FXMVECTOR RotationQuaternion) noexcept; + XMVECTOR XM_CALLCONV XMVector3Transform(FXMVECTOR V, FXMMATRIX M) noexcept; + XMFLOAT4* XM_CALLCONV XMVector3TransformStream(_Out_writes_bytes_(sizeof(XMFLOAT4) + OutputStride * (VectorCount - 1)) XMFLOAT4* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT3) + InputStride * (VectorCount - 1)) const XMFLOAT3* pInputStream, + _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M) noexcept; + XMVECTOR XM_CALLCONV XMVector3TransformCoord(FXMVECTOR V, FXMMATRIX M) noexcept; + XMFLOAT3* XM_CALLCONV XMVector3TransformCoordStream(_Out_writes_bytes_(sizeof(XMFLOAT3) + OutputStride * (VectorCount - 1)) XMFLOAT3* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT3) + InputStride * (VectorCount - 1)) const XMFLOAT3* pInputStream, + _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M) noexcept; + XMVECTOR XM_CALLCONV XMVector3TransformNormal(FXMVECTOR V, FXMMATRIX M) noexcept; + XMFLOAT3* XM_CALLCONV XMVector3TransformNormalStream(_Out_writes_bytes_(sizeof(XMFLOAT3) + OutputStride * (VectorCount - 1)) XMFLOAT3* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT3) + InputStride * (VectorCount - 1)) const XMFLOAT3* pInputStream, + _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M) noexcept; + XMVECTOR XM_CALLCONV XMVector3Project(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ, + FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World) noexcept; + XMFLOAT3* XM_CALLCONV XMVector3ProjectStream(_Out_writes_bytes_(sizeof(XMFLOAT3) + OutputStride * (VectorCount - 1)) XMFLOAT3* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT3) + InputStride * (VectorCount - 1)) const XMFLOAT3* pInputStream, + _In_ size_t InputStride, _In_ size_t VectorCount, + _In_ float ViewportX, _In_ float ViewportY, _In_ float ViewportWidth, _In_ float ViewportHeight, _In_ float ViewportMinZ, _In_ float ViewportMaxZ, + _In_ FXMMATRIX Projection, _In_ CXMMATRIX View, _In_ CXMMATRIX World) noexcept; + XMVECTOR XM_CALLCONV XMVector3Unproject(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ, + FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World) noexcept; + XMFLOAT3* XM_CALLCONV XMVector3UnprojectStream(_Out_writes_bytes_(sizeof(XMFLOAT3) + OutputStride * (VectorCount - 1)) XMFLOAT3* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT3) + InputStride * (VectorCount - 1)) const XMFLOAT3* pInputStream, + _In_ size_t InputStride, _In_ size_t VectorCount, + _In_ float ViewportX, _In_ float ViewportY, _In_ float ViewportWidth, _In_ float ViewportHeight, _In_ float ViewportMinZ, _In_ float ViewportMaxZ, + _In_ FXMMATRIX Projection, _In_ CXMMATRIX View, _In_ CXMMATRIX World) noexcept; + + /**************************************************************************** + * + * 4D vector operations + * + ****************************************************************************/ + + bool XM_CALLCONV XMVector4Equal(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector4EqualR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector4EqualInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector4EqualIntR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector4NearEqual(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR Epsilon) noexcept; + bool XM_CALLCONV XMVector4NotEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector4NotEqualInt(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector4Greater(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector4GreaterR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector4GreaterOrEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + uint32_t XM_CALLCONV XMVector4GreaterOrEqualR(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector4Less(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector4LessOrEqual(FXMVECTOR V1, FXMVECTOR V2) noexcept; + bool XM_CALLCONV XMVector4InBounds(FXMVECTOR V, FXMVECTOR Bounds) noexcept; + + bool XM_CALLCONV XMVector4IsNaN(FXMVECTOR V) noexcept; + bool XM_CALLCONV XMVector4IsInfinite(FXMVECTOR V) noexcept; + + XMVECTOR XM_CALLCONV XMVector4Dot(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVector4Cross(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR V3) noexcept; + XMVECTOR XM_CALLCONV XMVector4LengthSq(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector4ReciprocalLengthEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector4ReciprocalLength(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector4LengthEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector4Length(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector4NormalizeEst(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector4Normalize(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector4ClampLength(FXMVECTOR V, float LengthMin, float LengthMax) noexcept; + XMVECTOR XM_CALLCONV XMVector4ClampLengthV(FXMVECTOR V, FXMVECTOR LengthMin, FXMVECTOR LengthMax) noexcept; + XMVECTOR XM_CALLCONV XMVector4Reflect(FXMVECTOR Incident, FXMVECTOR Normal) noexcept; + XMVECTOR XM_CALLCONV XMVector4Refract(FXMVECTOR Incident, FXMVECTOR Normal, float RefractionIndex) noexcept; + XMVECTOR XM_CALLCONV XMVector4RefractV(FXMVECTOR Incident, FXMVECTOR Normal, FXMVECTOR RefractionIndex) noexcept; + XMVECTOR XM_CALLCONV XMVector4Orthogonal(FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMVector4AngleBetweenNormalsEst(FXMVECTOR N1, FXMVECTOR N2) noexcept; + XMVECTOR XM_CALLCONV XMVector4AngleBetweenNormals(FXMVECTOR N1, FXMVECTOR N2) noexcept; + XMVECTOR XM_CALLCONV XMVector4AngleBetweenVectors(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMVector4Transform(FXMVECTOR V, FXMMATRIX M) noexcept; + XMFLOAT4* XM_CALLCONV XMVector4TransformStream(_Out_writes_bytes_(sizeof(XMFLOAT4) + OutputStride * (VectorCount - 1)) XMFLOAT4* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT4) + InputStride * (VectorCount - 1)) const XMFLOAT4* pInputStream, + _In_ size_t InputStride, _In_ size_t VectorCount, _In_ FXMMATRIX M) noexcept; + + /**************************************************************************** + * + * Matrix operations + * + ****************************************************************************/ + + bool XM_CALLCONV XMMatrixIsNaN(FXMMATRIX M) noexcept; + bool XM_CALLCONV XMMatrixIsInfinite(FXMMATRIX M) noexcept; + bool XM_CALLCONV XMMatrixIsIdentity(FXMMATRIX M) noexcept; + + XMMATRIX XM_CALLCONV XMMatrixMultiply(FXMMATRIX M1, CXMMATRIX M2) noexcept; + XMMATRIX XM_CALLCONV XMMatrixMultiplyTranspose(FXMMATRIX M1, CXMMATRIX M2) noexcept; + XMMATRIX XM_CALLCONV XMMatrixTranspose(FXMMATRIX M) noexcept; + XMMATRIX XM_CALLCONV XMMatrixInverse(_Out_opt_ XMVECTOR* pDeterminant, _In_ FXMMATRIX M) noexcept; + XMMATRIX XM_CALLCONV XMMatrixVectorTensorProduct(FXMVECTOR V1, FXMVECTOR V2) noexcept; + XMVECTOR XM_CALLCONV XMMatrixDeterminant(FXMMATRIX M) noexcept; + + _Success_(return) + bool XM_CALLCONV XMMatrixDecompose(_Out_ XMVECTOR* outScale, _Out_ XMVECTOR* outRotQuat, _Out_ XMVECTOR* outTrans, _In_ FXMMATRIX M) noexcept; + + XMMATRIX XM_CALLCONV XMMatrixIdentity() noexcept; + XMMATRIX XM_CALLCONV XMMatrixSet(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; + XMMATRIX XM_CALLCONV XMMatrixTranslation(float OffsetX, float OffsetY, float OffsetZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixTranslationFromVector(FXMVECTOR Offset) noexcept; + XMMATRIX XM_CALLCONV XMMatrixScaling(float ScaleX, float ScaleY, float ScaleZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixScalingFromVector(FXMVECTOR Scale) noexcept; + XMMATRIX XM_CALLCONV XMMatrixRotationX(float Angle) noexcept; + XMMATRIX XM_CALLCONV XMMatrixRotationY(float Angle) noexcept; + XMMATRIX XM_CALLCONV XMMatrixRotationZ(float Angle) noexcept; + + // Rotates about y-axis (Yaw), then x-axis (Pitch), then z-axis (Roll) + XMMATRIX XM_CALLCONV XMMatrixRotationRollPitchYaw(float Pitch, float Yaw, float Roll) noexcept; + + // Rotates about y-axis (Angles.y), then x-axis (Angles.x), then z-axis (Angles.z) + XMMATRIX XM_CALLCONV XMMatrixRotationRollPitchYawFromVector(FXMVECTOR Angles) noexcept; + + XMMATRIX XM_CALLCONV XMMatrixRotationNormal(FXMVECTOR NormalAxis, float Angle) noexcept; + XMMATRIX XM_CALLCONV XMMatrixRotationAxis(FXMVECTOR Axis, float Angle) noexcept; + XMMATRIX XM_CALLCONV XMMatrixRotationQuaternion(FXMVECTOR Quaternion) noexcept; + XMMATRIX XM_CALLCONV XMMatrixTransformation2D(FXMVECTOR ScalingOrigin, float ScalingOrientation, FXMVECTOR Scaling, + FXMVECTOR RotationOrigin, float Rotation, GXMVECTOR Translation) noexcept; + XMMATRIX XM_CALLCONV XMMatrixTransformation(FXMVECTOR ScalingOrigin, FXMVECTOR ScalingOrientationQuaternion, FXMVECTOR Scaling, + GXMVECTOR RotationOrigin, HXMVECTOR RotationQuaternion, HXMVECTOR Translation) noexcept; + XMMATRIX XM_CALLCONV XMMatrixAffineTransformation2D(FXMVECTOR Scaling, FXMVECTOR RotationOrigin, float Rotation, FXMVECTOR Translation) noexcept; + XMMATRIX XM_CALLCONV XMMatrixAffineTransformation(FXMVECTOR Scaling, FXMVECTOR RotationOrigin, FXMVECTOR RotationQuaternion, GXMVECTOR Translation) noexcept; + XMMATRIX XM_CALLCONV XMMatrixReflect(FXMVECTOR ReflectionPlane) noexcept; + XMMATRIX XM_CALLCONV XMMatrixShadow(FXMVECTOR ShadowPlane, FXMVECTOR LightPosition) noexcept; + + XMMATRIX XM_CALLCONV XMMatrixLookAtLH(FXMVECTOR EyePosition, FXMVECTOR FocusPosition, FXMVECTOR UpDirection) noexcept; + XMMATRIX XM_CALLCONV XMMatrixLookAtRH(FXMVECTOR EyePosition, FXMVECTOR FocusPosition, FXMVECTOR UpDirection) noexcept; + XMMATRIX XM_CALLCONV XMMatrixLookToLH(FXMVECTOR EyePosition, FXMVECTOR EyeDirection, FXMVECTOR UpDirection) noexcept; + XMMATRIX XM_CALLCONV XMMatrixLookToRH(FXMVECTOR EyePosition, FXMVECTOR EyeDirection, FXMVECTOR UpDirection) noexcept; + XMMATRIX XM_CALLCONV XMMatrixPerspectiveLH(float ViewWidth, float ViewHeight, float NearZ, float FarZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixPerspectiveRH(float ViewWidth, float ViewHeight, float NearZ, float FarZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovLH(float FovAngleY, float AspectRatio, float NearZ, float FarZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovRH(float FovAngleY, float AspectRatio, float NearZ, float FarZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixPerspectiveOffCenterLH(float ViewLeft, float ViewRight, float ViewBottom, float ViewTop, float NearZ, float FarZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixPerspectiveOffCenterRH(float ViewLeft, float ViewRight, float ViewBottom, float ViewTop, float NearZ, float FarZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixOrthographicLH(float ViewWidth, float ViewHeight, float NearZ, float FarZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixOrthographicRH(float ViewWidth, float ViewHeight, float NearZ, float FarZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixOrthographicOffCenterLH(float ViewLeft, float ViewRight, float ViewBottom, float ViewTop, float NearZ, float FarZ) noexcept; + XMMATRIX XM_CALLCONV XMMatrixOrthographicOffCenterRH(float ViewLeft, float ViewRight, float ViewBottom, float ViewTop, float NearZ, float FarZ) noexcept; + + + /**************************************************************************** + * + * Quaternion operations + * + ****************************************************************************/ + + bool XM_CALLCONV XMQuaternionEqual(FXMVECTOR Q1, FXMVECTOR Q2) noexcept; + bool XM_CALLCONV XMQuaternionNotEqual(FXMVECTOR Q1, FXMVECTOR Q2) noexcept; + + bool XM_CALLCONV XMQuaternionIsNaN(FXMVECTOR Q) noexcept; + bool XM_CALLCONV XMQuaternionIsInfinite(FXMVECTOR Q) noexcept; + bool XM_CALLCONV XMQuaternionIsIdentity(FXMVECTOR Q) noexcept; + + XMVECTOR XM_CALLCONV XMQuaternionDot(FXMVECTOR Q1, FXMVECTOR Q2) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionMultiply(FXMVECTOR Q1, FXMVECTOR Q2) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionLengthSq(FXMVECTOR Q) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionReciprocalLength(FXMVECTOR Q) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionLength(FXMVECTOR Q) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionNormalizeEst(FXMVECTOR Q) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionNormalize(FXMVECTOR Q) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionConjugate(FXMVECTOR Q) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionInverse(FXMVECTOR Q) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionLn(FXMVECTOR Q) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionExp(FXMVECTOR Q) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionSlerp(FXMVECTOR Q0, FXMVECTOR Q1, float t) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionSlerpV(FXMVECTOR Q0, FXMVECTOR Q1, FXMVECTOR T) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionSquad(FXMVECTOR Q0, FXMVECTOR Q1, FXMVECTOR Q2, GXMVECTOR Q3, float t) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionSquadV(FXMVECTOR Q0, FXMVECTOR Q1, FXMVECTOR Q2, GXMVECTOR Q3, HXMVECTOR T) noexcept; + void XM_CALLCONV XMQuaternionSquadSetup(_Out_ XMVECTOR* pA, _Out_ XMVECTOR* pB, _Out_ XMVECTOR* pC, _In_ FXMVECTOR Q0, _In_ FXMVECTOR Q1, _In_ FXMVECTOR Q2, _In_ GXMVECTOR Q3) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionBaryCentric(FXMVECTOR Q0, FXMVECTOR Q1, FXMVECTOR Q2, float f, float g) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionBaryCentricV(FXMVECTOR Q0, FXMVECTOR Q1, FXMVECTOR Q2, GXMVECTOR F, HXMVECTOR G) noexcept; + + XMVECTOR XM_CALLCONV XMQuaternionIdentity() noexcept; + + // Rotates about y-axis (Yaw), then x-axis (Pitch), then z-axis (Roll) + XMVECTOR XM_CALLCONV XMQuaternionRotationRollPitchYaw(float Pitch, float Yaw, float Roll) noexcept; + + // Rotates about y-axis (Angles.y), then x-axis (Angles.x), then z-axis (Angles.z) + XMVECTOR XM_CALLCONV XMQuaternionRotationRollPitchYawFromVector(FXMVECTOR Angles) noexcept; + + XMVECTOR XM_CALLCONV XMQuaternionRotationNormal(FXMVECTOR NormalAxis, float Angle) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionRotationAxis(FXMVECTOR Axis, float Angle) noexcept; + XMVECTOR XM_CALLCONV XMQuaternionRotationMatrix(FXMMATRIX M) noexcept; + + void XM_CALLCONV XMQuaternionToAxisAngle(_Out_ XMVECTOR* pAxis, _Out_ float* pAngle, _In_ FXMVECTOR Q) noexcept; + + /**************************************************************************** + * + * Plane operations + * + ****************************************************************************/ + + bool XM_CALLCONV XMPlaneEqual(FXMVECTOR P1, FXMVECTOR P2) noexcept; + bool XM_CALLCONV XMPlaneNearEqual(FXMVECTOR P1, FXMVECTOR P2, FXMVECTOR Epsilon) noexcept; + bool XM_CALLCONV XMPlaneNotEqual(FXMVECTOR P1, FXMVECTOR P2) noexcept; + + bool XM_CALLCONV XMPlaneIsNaN(FXMVECTOR P) noexcept; + bool XM_CALLCONV XMPlaneIsInfinite(FXMVECTOR P) noexcept; + + XMVECTOR XM_CALLCONV XMPlaneDot(FXMVECTOR P, FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMPlaneDotCoord(FXMVECTOR P, FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMPlaneDotNormal(FXMVECTOR P, FXMVECTOR V) noexcept; + XMVECTOR XM_CALLCONV XMPlaneNormalizeEst(FXMVECTOR P) noexcept; + XMVECTOR XM_CALLCONV XMPlaneNormalize(FXMVECTOR P) noexcept; + XMVECTOR XM_CALLCONV XMPlaneIntersectLine(FXMVECTOR P, FXMVECTOR LinePoint1, FXMVECTOR LinePoint2) noexcept; + void XM_CALLCONV XMPlaneIntersectPlane(_Out_ XMVECTOR* pLinePoint1, _Out_ XMVECTOR* pLinePoint2, _In_ FXMVECTOR P1, _In_ FXMVECTOR P2) noexcept; + + // Transforms a plane given an inverse transpose matrix + XMVECTOR XM_CALLCONV XMPlaneTransform(FXMVECTOR P, FXMMATRIX ITM) noexcept; + + // Transforms an array of planes given an inverse transpose matrix + XMFLOAT4* XM_CALLCONV XMPlaneTransformStream(_Out_writes_bytes_(sizeof(XMFLOAT4) + OutputStride * (PlaneCount - 1)) XMFLOAT4* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(XMFLOAT4) + InputStride * (PlaneCount - 1)) const XMFLOAT4* pInputStream, + _In_ size_t InputStride, _In_ size_t PlaneCount, _In_ FXMMATRIX ITM) noexcept; + + XMVECTOR XM_CALLCONV XMPlaneFromPointNormal(FXMVECTOR Point, FXMVECTOR Normal) noexcept; + XMVECTOR XM_CALLCONV XMPlaneFromPoints(FXMVECTOR Point1, FXMVECTOR Point2, FXMVECTOR Point3) noexcept; + + /**************************************************************************** + * + * Color operations + * + ****************************************************************************/ + + bool XM_CALLCONV XMColorEqual(FXMVECTOR C1, FXMVECTOR C2) noexcept; + bool XM_CALLCONV XMColorNotEqual(FXMVECTOR C1, FXMVECTOR C2) noexcept; + bool XM_CALLCONV XMColorGreater(FXMVECTOR C1, FXMVECTOR C2) noexcept; + bool XM_CALLCONV XMColorGreaterOrEqual(FXMVECTOR C1, FXMVECTOR C2) noexcept; + bool XM_CALLCONV XMColorLess(FXMVECTOR C1, FXMVECTOR C2) noexcept; + bool XM_CALLCONV XMColorLessOrEqual(FXMVECTOR C1, FXMVECTOR C2) noexcept; + + bool XM_CALLCONV XMColorIsNaN(FXMVECTOR C) noexcept; + bool XM_CALLCONV XMColorIsInfinite(FXMVECTOR C) noexcept; + + XMVECTOR XM_CALLCONV XMColorNegative(FXMVECTOR C) noexcept; + XMVECTOR XM_CALLCONV XMColorModulate(FXMVECTOR C1, FXMVECTOR C2) noexcept; + XMVECTOR XM_CALLCONV XMColorAdjustSaturation(FXMVECTOR C, float Saturation) noexcept; + XMVECTOR XM_CALLCONV XMColorAdjustContrast(FXMVECTOR C, float Contrast) noexcept; + + XMVECTOR XM_CALLCONV XMColorRGBToHSL(FXMVECTOR rgb) noexcept; + XMVECTOR XM_CALLCONV XMColorHSLToRGB(FXMVECTOR hsl) noexcept; + + XMVECTOR XM_CALLCONV XMColorRGBToHSV(FXMVECTOR rgb) noexcept; + XMVECTOR XM_CALLCONV XMColorHSVToRGB(FXMVECTOR hsv) noexcept; + + XMVECTOR XM_CALLCONV XMColorRGBToYUV(FXMVECTOR rgb) noexcept; + XMVECTOR XM_CALLCONV XMColorYUVToRGB(FXMVECTOR yuv) noexcept; + + XMVECTOR XM_CALLCONV XMColorRGBToYUV_HD(FXMVECTOR rgb) noexcept; + XMVECTOR XM_CALLCONV XMColorYUVToRGB_HD(FXMVECTOR yuv) noexcept; + + XMVECTOR XM_CALLCONV XMColorRGBToYUV_UHD(FXMVECTOR rgb) noexcept; + XMVECTOR XM_CALLCONV XMColorYUVToRGB_UHD(FXMVECTOR yuv) noexcept; + + XMVECTOR XM_CALLCONV XMColorRGBToXYZ(FXMVECTOR rgb) noexcept; + XMVECTOR XM_CALLCONV XMColorXYZToRGB(FXMVECTOR xyz) noexcept; + + XMVECTOR XM_CALLCONV XMColorXYZToSRGB(FXMVECTOR xyz) noexcept; + XMVECTOR XM_CALLCONV XMColorSRGBToXYZ(FXMVECTOR srgb) noexcept; + + XMVECTOR XM_CALLCONV XMColorRGBToSRGB(FXMVECTOR rgb) noexcept; + XMVECTOR XM_CALLCONV XMColorSRGBToRGB(FXMVECTOR srgb) noexcept; + + + /**************************************************************************** + * + * Miscellaneous operations + * + ****************************************************************************/ + + bool XMVerifyCPUSupport() noexcept; + + XMVECTOR XM_CALLCONV XMFresnelTerm(FXMVECTOR CosIncidentAngle, FXMVECTOR RefractionIndex) noexcept; + + bool XMScalarNearEqual(float S1, float S2, float Epsilon) noexcept; + float XMScalarModAngle(float Value) noexcept; + + float XMScalarSin(float Value) noexcept; + float XMScalarSinEst(float Value) noexcept; + + float XMScalarCos(float Value) noexcept; + float XMScalarCosEst(float Value) noexcept; + + void XMScalarSinCos(_Out_ float* pSin, _Out_ float* pCos, float Value) noexcept; + void XMScalarSinCosEst(_Out_ float* pSin, _Out_ float* pCos, float Value) noexcept; + + float XMScalarASin(float Value) noexcept; + float XMScalarASinEst(float Value) noexcept; + + float XMScalarACos(float Value) noexcept; + float XMScalarACosEst(float Value) noexcept; + + /**************************************************************************** + * + * Templates + * + ****************************************************************************/ + +#if defined(__XNAMATH_H__) && defined(XMMin) +#undef XMMin +#undef XMMax +#endif + + template inline T XMMin(T a, T b) noexcept { return (a < b) ? a : b; } + template inline T XMMax(T a, T b) noexcept { return (a > b) ? a : b; } + + //------------------------------------------------------------------------------ + +#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + +// PermuteHelper internal template (SSE only) + namespace MathInternal + { + // Slow path fallback for permutes that do not map to a single SSE shuffle opcode. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) noexcept + { + static const XMVECTORU32 selectMask = + { { { + WhichX ? 0xFFFFFFFF : 0, + WhichY ? 0xFFFFFFFF : 0, + WhichZ ? 0xFFFFFFFF : 0, + WhichW ? 0xFFFFFFFF : 0, + } } }; + + XMVECTOR shuffled1 = XM_PERMUTE_PS(v1, Shuffle); + XMVECTOR shuffled2 = XM_PERMUTE_PS(v2, Shuffle); + + XMVECTOR masked1 = _mm_andnot_ps(selectMask, shuffled1); + XMVECTOR masked2 = _mm_and_ps(selectMask, shuffled2); + + return _mm_or_ps(masked1, masked2); + } + }; + + // Fast path for permutes that only read from the first vector. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR) noexcept { return XM_PERMUTE_PS(v1, Shuffle); } + }; + + // Fast path for permutes that only read from the second vector. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR, FXMVECTOR v2) noexcept { return XM_PERMUTE_PS(v2, Shuffle); } + }; + + // Fast path for permutes that read XY from the first vector, ZW from the second. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) noexcept { return _mm_shuffle_ps(v1, v2, Shuffle); } + }; + + // Fast path for permutes that read XY from the second vector, ZW from the first. + template struct PermuteHelper + { + static XMVECTOR XM_CALLCONV Permute(FXMVECTOR v1, FXMVECTOR v2) noexcept { return _mm_shuffle_ps(v2, v1, Shuffle); } + }; + } + +#endif // _XM_SSE_INTRINSICS_ && !_XM_NO_INTRINSICS_ + + // General permute template + template + inline XMVECTOR XM_CALLCONV XMVectorPermute(FXMVECTOR V1, FXMVECTOR V2) noexcept + { + static_assert(PermuteX <= 7, "PermuteX template parameter out of range"); + static_assert(PermuteY <= 7, "PermuteY template parameter out of range"); + static_assert(PermuteZ <= 7, "PermuteZ template parameter out of range"); + static_assert(PermuteW <= 7, "PermuteW template parameter out of range"); + + #if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + constexpr uint32_t Shuffle = _MM_SHUFFLE(PermuteW & 3, PermuteZ & 3, PermuteY & 3, PermuteX & 3); + + constexpr bool WhichX = PermuteX > 3; + constexpr bool WhichY = PermuteY > 3; + constexpr bool WhichZ = PermuteZ > 3; + constexpr bool WhichW = PermuteW > 3; + + return MathInternal::PermuteHelper::Permute(V1, V2); + #else + + return XMVectorPermute(V1, V2, PermuteX, PermuteY, PermuteZ, PermuteW); + + #endif + } + + // Special-case permute templates + template<> constexpr XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 2, 3>(FXMVECTOR V1, FXMVECTOR) noexcept { return V1; } + template<> constexpr XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 6, 7>(FXMVECTOR, FXMVECTOR V2) noexcept { return V2; } + +#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 4, 5>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_movelh_ps(V1, V2); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<6, 7, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_movehl_ps(V1, V2); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 4, 1, 5>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_unpacklo_ps(V1, V2); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<2, 6, 3, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_unpackhi_ps(V1, V2); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<2, 3, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_castpd_ps(_mm_unpackhi_pd(_mm_castps_pd(V1), _mm_castps_pd(V2))); } +#endif + +#if defined(_XM_SSE4_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0x1); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0x2); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 2, 3>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0x3); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0x4); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0x5); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0x6); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 6, 3>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0x7); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0x8); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0x9); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0xA); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 5, 2, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0xB); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0xC); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<4, 1, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0xD); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 5, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return _mm_blend_ps(V1, V2, 0xE); } +#endif + +#if defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + + // If the indices are all in the range 0-3 or 4-7, then use XMVectorSwizzle instead + // The mirror cases are not spelled out here as the programmer can always swap the arguments + // (i.e. prefer permutes where the X element comes from the V1 vector instead of the V2 vector) + + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 4, 5>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vget_low_f32(V1), vget_low_f32(V2)); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<1, 0, 4, 5>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vrev64_f32(vget_low_f32(V1)), vget_low_f32(V2)); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 5, 4>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vget_low_f32(V1), vrev64_f32(vget_low_f32(V2))); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<1, 0, 5, 4>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vrev64_f32(vget_low_f32(V1)), vrev64_f32(vget_low_f32(V2))); } + + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<2, 3, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vget_high_f32(V1), vget_high_f32(V2)); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<3, 2, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vrev64_f32(vget_high_f32(V1)), vget_high_f32(V2)); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<2, 3, 7, 6>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vget_high_f32(V1), vrev64_f32(vget_high_f32(V2))); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<3, 2, 7, 6>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vrev64_f32(vget_high_f32(V1)), vrev64_f32(vget_high_f32(V2))); } + + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vget_low_f32(V1), vget_high_f32(V2)); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<1, 0, 6, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vrev64_f32(vget_low_f32(V1)), vget_high_f32(V2)); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 1, 7, 6>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vget_low_f32(V1), vrev64_f32(vget_high_f32(V2))); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<1, 0, 7, 6>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vrev64_f32(vget_low_f32(V1)), vrev64_f32(vget_high_f32(V2))); } + + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<3, 2, 4, 5>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vrev64_f32(vget_high_f32(V1)), vget_low_f32(V2)); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<2, 3, 5, 4>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vget_high_f32(V1), vrev64_f32(vget_low_f32(V2))); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<3, 2, 5, 4>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vcombine_f32(vrev64_f32(vget_high_f32(V1)), vrev64_f32(vget_low_f32(V2))); } + + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 4, 2, 6>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vtrnq_f32(V1, V2).val[0]; } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<1, 5, 3, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vtrnq_f32(V1, V2).val[1]; } + + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 4, 1, 5>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vzipq_f32(V1, V2).val[0]; } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<2, 6, 3, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vzipq_f32(V1, V2).val[1]; } + + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<0, 2, 4, 6>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vuzpq_f32(V1, V2).val[0]; } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<1, 3, 5, 7>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vuzpq_f32(V1, V2).val[1]; } + + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<1, 2, 3, 4>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vextq_f32(V1, V2, 1); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<2, 3, 4, 5>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vextq_f32(V1, V2, 2); } + template<> inline XMVECTOR XM_CALLCONV XMVectorPermute<3, 4, 5, 6>(FXMVECTOR V1, FXMVECTOR V2) noexcept { return vextq_f32(V1, V2, 3); } + +#endif // _XM_ARM_NEON_INTRINSICS_ && !_XM_NO_INTRINSICS_ + + //------------------------------------------------------------------------------ + + // General swizzle template + template + inline XMVECTOR XM_CALLCONV XMVectorSwizzle(FXMVECTOR V) noexcept + { + static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); + static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); + static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); + static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); + + #if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + return XM_PERMUTE_PS(V, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX)); + #else + + return XMVectorSwizzle(V, SwizzleX, SwizzleY, SwizzleZ, SwizzleW); + + #endif + } + + // Specialized swizzles + template<> constexpr XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 1, 2, 3>(FXMVECTOR V) noexcept { return V; } + +#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 1, 0, 1>(FXMVECTOR V) noexcept { return _mm_movelh_ps(V, V); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<2, 3, 2, 3>(FXMVECTOR V) noexcept { return _mm_movehl_ps(V, V); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 0, 1, 1>(FXMVECTOR V) noexcept { return _mm_unpacklo_ps(V, V); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<2, 2, 3, 3>(FXMVECTOR V) noexcept { return _mm_unpackhi_ps(V, V); } +#endif + +#if defined(_XM_SSE3_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 0, 2, 2>(FXMVECTOR V) noexcept { return _mm_moveldup_ps(V); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 1, 3, 3>(FXMVECTOR V) noexcept { return _mm_movehdup_ps(V); } +#endif + +#if defined(_XM_AVX2_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) && defined(_XM_FAVOR_INTEL_) + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 0, 0, 0>(FXMVECTOR V) noexcept { return _mm_broadcastss_ps(V); } +#endif + +#if defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 0, 0, 0>(FXMVECTOR V) noexcept { return vdupq_lane_f32(vget_low_f32(V), 0); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 1, 1, 1>(FXMVECTOR V) noexcept { return vdupq_lane_f32(vget_low_f32(V), 1); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<2, 2, 2, 2>(FXMVECTOR V) noexcept { return vdupq_lane_f32(vget_high_f32(V), 0); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<3, 3, 3, 3>(FXMVECTOR V) noexcept { return vdupq_lane_f32(vget_high_f32(V), 1); } + + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 0, 3, 2>(FXMVECTOR V) noexcept { return vrev64q_f32(V); } + + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 1, 0, 1>(FXMVECTOR V) noexcept { float32x2_t vt = vget_low_f32(V); return vcombine_f32(vt, vt); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<2, 3, 2, 3>(FXMVECTOR V) noexcept { float32x2_t vt = vget_high_f32(V); return vcombine_f32(vt, vt); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 0, 1, 0>(FXMVECTOR V) noexcept { float32x2_t vt = vrev64_f32(vget_low_f32(V)); return vcombine_f32(vt, vt); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<3, 2, 3, 2>(FXMVECTOR V) noexcept { float32x2_t vt = vrev64_f32(vget_high_f32(V)); return vcombine_f32(vt, vt); } + + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 1, 3, 2>(FXMVECTOR V) noexcept { return vcombine_f32(vget_low_f32(V), vrev64_f32(vget_high_f32(V))); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 0, 2, 3>(FXMVECTOR V) noexcept { return vcombine_f32(vrev64_f32(vget_low_f32(V)), vget_high_f32(V)); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<2, 3, 1, 0>(FXMVECTOR V) noexcept { return vcombine_f32(vget_high_f32(V), vrev64_f32(vget_low_f32(V))); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<3, 2, 0, 1>(FXMVECTOR V) noexcept { return vcombine_f32(vrev64_f32(vget_high_f32(V)), vget_low_f32(V)); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<3, 2, 1, 0>(FXMVECTOR V) noexcept { return vcombine_f32(vrev64_f32(vget_high_f32(V)), vrev64_f32(vget_low_f32(V))); } + + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 0, 2, 2>(FXMVECTOR V) noexcept { return vtrnq_f32(V, V).val[0]; } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 1, 3, 3>(FXMVECTOR V) noexcept { return vtrnq_f32(V, V).val[1]; } + + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 0, 1, 1>(FXMVECTOR V) noexcept { return vzipq_f32(V, V).val[0]; } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<2, 2, 3, 3>(FXMVECTOR V) noexcept { return vzipq_f32(V, V).val[1]; } + + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<0, 2, 0, 2>(FXMVECTOR V) noexcept { return vuzpq_f32(V, V).val[0]; } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 3, 1, 3>(FXMVECTOR V) noexcept { return vuzpq_f32(V, V).val[1]; } + + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<1, 2, 3, 0>(FXMVECTOR V) noexcept { return vextq_f32(V, V, 1); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<2, 3, 0, 1>(FXMVECTOR V) noexcept { return vextq_f32(V, V, 2); } + template<> inline XMVECTOR XM_CALLCONV XMVectorSwizzle<3, 0, 1, 2>(FXMVECTOR V) noexcept { return vextq_f32(V, V, 3); } + +#endif // _XM_ARM_NEON_INTRINSICS_ && !_XM_NO_INTRINSICS_ + + //------------------------------------------------------------------------------ + + template + inline XMVECTOR XM_CALLCONV XMVectorShiftLeft(FXMVECTOR V1, FXMVECTOR V2) noexcept + { + static_assert(Elements < 4, "Elements template parameter out of range"); + return XMVectorPermute(V1, V2); + } + + template + inline XMVECTOR XM_CALLCONV XMVectorRotateLeft(FXMVECTOR V) noexcept + { + static_assert(Elements < 4, "Elements template parameter out of range"); + return XMVectorSwizzle(V); + } + + template + inline XMVECTOR XM_CALLCONV XMVectorRotateRight(FXMVECTOR V) noexcept + { + static_assert(Elements < 4, "Elements template parameter out of range"); + return XMVectorSwizzle<(4 - Elements) & 3, (5 - Elements) & 3, (6 - Elements) & 3, (7 - Elements) & 3>(V); + } + + template + inline XMVECTOR XM_CALLCONV XMVectorInsert(FXMVECTOR VD, FXMVECTOR VS) noexcept + { + XMVECTOR Control = XMVectorSelectControl(Select0 & 1, Select1 & 1, Select2 & 1, Select3 & 1); + return XMVectorSelect(VD, XMVectorRotateLeft(VS), Control); + } + + /**************************************************************************** + * + * Globals + * + ****************************************************************************/ + + // The purpose of the following global constants is to prevent redundant + // reloading of the constants when they are referenced by more than one + // separate inline math routine called within the same function. Declaring + // a constant locally within a routine is sufficient to prevent redundant + // reloads of that constant when that single routine is called multiple + // times in a function, but if the constant is used (and declared) in a + // separate math routine it would be reloaded. + +#ifndef XMGLOBALCONST +#if defined(__GNUC__) && !defined(__MINGW32__) +#define XMGLOBALCONST extern const __attribute__((weak)) +#else +#define XMGLOBALCONST extern const __declspec(selectany) +#endif +#endif + + XMGLOBALCONST XMVECTORF32 g_XMSinCoefficients0 = { { { -0.16666667f, +0.0083333310f, -0.00019840874f, +2.7525562e-06f } } }; + XMGLOBALCONST XMVECTORF32 g_XMSinCoefficients1 = { { { -2.3889859e-08f, -0.16665852f /*Est1*/, +0.0083139502f /*Est2*/, -0.00018524670f /*Est3*/ } } }; + XMGLOBALCONST XMVECTORF32 g_XMCosCoefficients0 = { { { -0.5f, +0.041666638f, -0.0013888378f, +2.4760495e-05f } } }; + XMGLOBALCONST XMVECTORF32 g_XMCosCoefficients1 = { { { -2.6051615e-07f, -0.49992746f /*Est1*/, +0.041493919f /*Est2*/, -0.0012712436f /*Est3*/ } } }; + XMGLOBALCONST XMVECTORF32 g_XMTanCoefficients0 = { { { 1.0f, 0.333333333f, 0.133333333f, 5.396825397e-2f } } }; + XMGLOBALCONST XMVECTORF32 g_XMTanCoefficients1 = { { { 2.186948854e-2f, 8.863235530e-3f, 3.592128167e-3f, 1.455834485e-3f } } }; + XMGLOBALCONST XMVECTORF32 g_XMTanCoefficients2 = { { { 5.900274264e-4f, 2.391290764e-4f, 9.691537707e-5f, 3.927832950e-5f } } }; + XMGLOBALCONST XMVECTORF32 g_XMArcCoefficients0 = { { { +1.5707963050f, -0.2145988016f, +0.0889789874f, -0.0501743046f } } }; + XMGLOBALCONST XMVECTORF32 g_XMArcCoefficients1 = { { { +0.0308918810f, -0.0170881256f, +0.0066700901f, -0.0012624911f } } }; + XMGLOBALCONST XMVECTORF32 g_XMATanCoefficients0 = { { { -0.3333314528f, +0.1999355085f, -0.1420889944f, +0.1065626393f } } }; + XMGLOBALCONST XMVECTORF32 g_XMATanCoefficients1 = { { { -0.0752896400f, +0.0429096138f, -0.0161657367f, +0.0028662257f } } }; + XMGLOBALCONST XMVECTORF32 g_XMATanEstCoefficients0 = { { { +0.999866f, +0.999866f, +0.999866f, +0.999866f } } }; + XMGLOBALCONST XMVECTORF32 g_XMATanEstCoefficients1 = { { { -0.3302995f, +0.180141f, -0.085133f, +0.0208351f } } }; + XMGLOBALCONST XMVECTORF32 g_XMTanEstCoefficients = { { { 2.484f, -1.954923183e-1f, 2.467401101f, XM_1DIVPI } } }; + XMGLOBALCONST XMVECTORF32 g_XMArcEstCoefficients = { { { +1.5707288f, -0.2121144f, +0.0742610f, -0.0187293f } } }; + XMGLOBALCONST XMVECTORF32 g_XMPiConstants0 = { { { XM_PI, XM_2PI, XM_1DIVPI, XM_1DIV2PI } } }; + XMGLOBALCONST XMVECTORF32 g_XMIdentityR0 = { { { 1.0f, 0.0f, 0.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMIdentityR1 = { { { 0.0f, 1.0f, 0.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMIdentityR2 = { { { 0.0f, 0.0f, 1.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMIdentityR3 = { { { 0.0f, 0.0f, 0.0f, 1.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR0 = { { { -1.0f, 0.0f, 0.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR1 = { { { 0.0f, -1.0f, 0.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR2 = { { { 0.0f, 0.0f, -1.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR3 = { { { 0.0f, 0.0f, 0.0f, -1.0f } } }; + XMGLOBALCONST XMVECTORU32 g_XMNegativeZero = { { { 0x80000000, 0x80000000, 0x80000000, 0x80000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMNegate3 = { { { 0x80000000, 0x80000000, 0x80000000, 0x00000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskXY = { { { 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMMask3 = { { { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskX = { { { 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskY = { { { 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskZ = { { { 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskW = { { { 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF } } }; + XMGLOBALCONST XMVECTORF32 g_XMOne = { { { 1.0f, 1.0f, 1.0f, 1.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMOne3 = { { { 1.0f, 1.0f, 1.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMZero = { { { 0.0f, 0.0f, 0.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMTwo = { { { 2.f, 2.f, 2.f, 2.f } } }; + XMGLOBALCONST XMVECTORF32 g_XMFour = { { { 4.f, 4.f, 4.f, 4.f } } }; + XMGLOBALCONST XMVECTORF32 g_XMSix = { { { 6.f, 6.f, 6.f, 6.f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegativeOne = { { { -1.0f, -1.0f, -1.0f, -1.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMOneHalf = { { { 0.5f, 0.5f, 0.5f, 0.5f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegativeOneHalf = { { { -0.5f, -0.5f, -0.5f, -0.5f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegativeTwoPi = { { { -XM_2PI, -XM_2PI, -XM_2PI, -XM_2PI } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegativePi = { { { -XM_PI, -XM_PI, -XM_PI, -XM_PI } } }; + XMGLOBALCONST XMVECTORF32 g_XMHalfPi = { { { XM_PIDIV2, XM_PIDIV2, XM_PIDIV2, XM_PIDIV2 } } }; + XMGLOBALCONST XMVECTORF32 g_XMPi = { { { XM_PI, XM_PI, XM_PI, XM_PI } } }; + XMGLOBALCONST XMVECTORF32 g_XMReciprocalPi = { { { XM_1DIVPI, XM_1DIVPI, XM_1DIVPI, XM_1DIVPI } } }; + XMGLOBALCONST XMVECTORF32 g_XMTwoPi = { { { XM_2PI, XM_2PI, XM_2PI, XM_2PI } } }; + XMGLOBALCONST XMVECTORF32 g_XMReciprocalTwoPi = { { { XM_1DIV2PI, XM_1DIV2PI, XM_1DIV2PI, XM_1DIV2PI } } }; + XMGLOBALCONST XMVECTORF32 g_XMEpsilon = { { { 1.192092896e-7f, 1.192092896e-7f, 1.192092896e-7f, 1.192092896e-7f } } }; + XMGLOBALCONST XMVECTORI32 g_XMInfinity = { { { 0x7F800000, 0x7F800000, 0x7F800000, 0x7F800000 } } }; + XMGLOBALCONST XMVECTORI32 g_XMQNaN = { { { 0x7FC00000, 0x7FC00000, 0x7FC00000, 0x7FC00000 } } }; + XMGLOBALCONST XMVECTORI32 g_XMQNaNTest = { { { 0x007FFFFF, 0x007FFFFF, 0x007FFFFF, 0x007FFFFF } } }; + XMGLOBALCONST XMVECTORI32 g_XMAbsMask = { { { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF } } }; + XMGLOBALCONST XMVECTORI32 g_XMFltMin = { { { 0x00800000, 0x00800000, 0x00800000, 0x00800000 } } }; + XMGLOBALCONST XMVECTORI32 g_XMFltMax = { { { 0x7F7FFFFF, 0x7F7FFFFF, 0x7F7FFFFF, 0x7F7FFFFF } } }; + XMGLOBALCONST XMVECTORU32 g_XMNegOneMask = { { { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskA8R8G8B8 = { { { 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMFlipA8R8G8B8 = { { { 0x00000000, 0x00000000, 0x00000000, 0x80000000 } } }; + XMGLOBALCONST XMVECTORF32 g_XMFixAA8R8G8B8 = { { { 0.0f, 0.0f, 0.0f, float(0x80000000U) } } }; + XMGLOBALCONST XMVECTORF32 g_XMNormalizeA8R8G8B8 = { { { 1.0f / (255.0f * float(0x10000)), 1.0f / (255.0f * float(0x100)), 1.0f / 255.0f, 1.0f / (255.0f * float(0x1000000)) } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskA2B10G10R10 = { { { 0x000003FF, 0x000FFC00, 0x3FF00000, 0xC0000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMFlipA2B10G10R10 = { { { 0x00000200, 0x00080000, 0x20000000, 0x80000000 } } }; + XMGLOBALCONST XMVECTORF32 g_XMFixAA2B10G10R10 = { { { -512.0f, -512.0f * float(0x400), -512.0f * float(0x100000), float(0x80000000U) } } }; + XMGLOBALCONST XMVECTORF32 g_XMNormalizeA2B10G10R10 = { { { 1.0f / 511.0f, 1.0f / (511.0f * float(0x400)), 1.0f / (511.0f * float(0x100000)), 1.0f / (3.0f * float(0x40000000)) } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskX16Y16 = { { { 0x0000FFFF, 0xFFFF0000, 0x00000000, 0x00000000 } } }; + XMGLOBALCONST XMVECTORI32 g_XMFlipX16Y16 = { { { 0x00008000, 0x00000000, 0x00000000, 0x00000000 } } }; + XMGLOBALCONST XMVECTORF32 g_XMFixX16Y16 = { { { -32768.0f, 0.0f, 0.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNormalizeX16Y16 = { { { 1.0f / 32767.0f, 1.0f / (32767.0f * 65536.0f), 0.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskX16Y16Z16W16 = { { { 0x0000FFFF, 0x0000FFFF, 0xFFFF0000, 0xFFFF0000 } } }; + XMGLOBALCONST XMVECTORI32 g_XMFlipX16Y16Z16W16 = { { { 0x00008000, 0x00008000, 0x00000000, 0x00000000 } } }; + XMGLOBALCONST XMVECTORF32 g_XMFixX16Y16Z16W16 = { { { -32768.0f, -32768.0f, 0.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNormalizeX16Y16Z16W16 = { { { 1.0f / 32767.0f, 1.0f / 32767.0f, 1.0f / (32767.0f * 65536.0f), 1.0f / (32767.0f * 65536.0f) } } }; + XMGLOBALCONST XMVECTORF32 g_XMNoFraction = { { { 8388608.0f, 8388608.0f, 8388608.0f, 8388608.0f } } }; + XMGLOBALCONST XMVECTORI32 g_XMMaskByte = { { { 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegateX = { { { -1.0f, 1.0f, 1.0f, 1.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegateY = { { { 1.0f, -1.0f, 1.0f, 1.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegateZ = { { { 1.0f, 1.0f, -1.0f, 1.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMNegateW = { { { 1.0f, 1.0f, 1.0f, -1.0f } } }; + XMGLOBALCONST XMVECTORU32 g_XMSelect0101 = { { { XM_SELECT_0, XM_SELECT_1, XM_SELECT_0, XM_SELECT_1 } } }; + XMGLOBALCONST XMVECTORU32 g_XMSelect1010 = { { { XM_SELECT_1, XM_SELECT_0, XM_SELECT_1, XM_SELECT_0 } } }; + XMGLOBALCONST XMVECTORI32 g_XMOneHalfMinusEpsilon = { { { 0x3EFFFFFD, 0x3EFFFFFD, 0x3EFFFFFD, 0x3EFFFFFD } } }; + XMGLOBALCONST XMVECTORU32 g_XMSelect1000 = { { { XM_SELECT_1, XM_SELECT_0, XM_SELECT_0, XM_SELECT_0 } } }; + XMGLOBALCONST XMVECTORU32 g_XMSelect1100 = { { { XM_SELECT_1, XM_SELECT_1, XM_SELECT_0, XM_SELECT_0 } } }; + XMGLOBALCONST XMVECTORU32 g_XMSelect1110 = { { { XM_SELECT_1, XM_SELECT_1, XM_SELECT_1, XM_SELECT_0 } } }; + XMGLOBALCONST XMVECTORU32 g_XMSelect1011 = { { { XM_SELECT_1, XM_SELECT_0, XM_SELECT_1, XM_SELECT_1 } } }; + XMGLOBALCONST XMVECTORF32 g_XMFixupY16 = { { { 1.0f, 1.0f / 65536.0f, 0.0f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMFixupY16W16 = { { { 1.0f, 1.0f, 1.0f / 65536.0f, 1.0f / 65536.0f } } }; + XMGLOBALCONST XMVECTORU32 g_XMFlipY = { { { 0, 0x80000000, 0, 0 } } }; + XMGLOBALCONST XMVECTORU32 g_XMFlipZ = { { { 0, 0, 0x80000000, 0 } } }; + XMGLOBALCONST XMVECTORU32 g_XMFlipW = { { { 0, 0, 0, 0x80000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMFlipYZ = { { { 0, 0x80000000, 0x80000000, 0 } } }; + XMGLOBALCONST XMVECTORU32 g_XMFlipZW = { { { 0, 0, 0x80000000, 0x80000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMFlipYW = { { { 0, 0x80000000, 0, 0x80000000 } } }; + XMGLOBALCONST XMVECTORI32 g_XMMaskDec4 = { { { 0x3FF, 0x3FF << 10, 0x3FF << 20, static_cast(0xC0000000) } } }; + XMGLOBALCONST XMVECTORI32 g_XMXorDec4 = { { { 0x200, 0x200 << 10, 0x200 << 20, 0 } } }; + XMGLOBALCONST XMVECTORF32 g_XMAddUDec4 = { { { 0, 0, 0, 32768.0f * 65536.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMAddDec4 = { { { -512.0f, -512.0f * 1024.0f, -512.0f * 1024.0f * 1024.0f, 0 } } }; + XMGLOBALCONST XMVECTORF32 g_XMMulDec4 = { { { 1.0f, 1.0f / 1024.0f, 1.0f / (1024.0f * 1024.0f), 1.0f / (1024.0f * 1024.0f * 1024.0f) } } }; + XMGLOBALCONST XMVECTORU32 g_XMMaskByte4 = { { { 0xFF, 0xFF00, 0xFF0000, 0xFF000000 } } }; + XMGLOBALCONST XMVECTORI32 g_XMXorByte4 = { { { 0x80, 0x8000, 0x800000, 0x00000000 } } }; + XMGLOBALCONST XMVECTORF32 g_XMAddByte4 = { { { -128.0f, -128.0f * 256.0f, -128.0f * 65536.0f, 0 } } }; + XMGLOBALCONST XMVECTORF32 g_XMFixUnsigned = { { { 32768.0f * 65536.0f, 32768.0f * 65536.0f, 32768.0f * 65536.0f, 32768.0f * 65536.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMMaxInt = { { { 65536.0f * 32768.0f - 128.0f, 65536.0f * 32768.0f - 128.0f, 65536.0f * 32768.0f - 128.0f, 65536.0f * 32768.0f - 128.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMMaxUInt = { { { 65536.0f * 65536.0f - 256.0f, 65536.0f * 65536.0f - 256.0f, 65536.0f * 65536.0f - 256.0f, 65536.0f * 65536.0f - 256.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMUnsignedFix = { { { 32768.0f * 65536.0f, 32768.0f * 65536.0f, 32768.0f * 65536.0f, 32768.0f * 65536.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMsrgbScale = { { { 12.92f, 12.92f, 12.92f, 1.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMsrgbA = { { { 0.055f, 0.055f, 0.055f, 0.0f } } }; + XMGLOBALCONST XMVECTORF32 g_XMsrgbA1 = { { { 1.055f, 1.055f, 1.055f, 1.0f } } }; + XMGLOBALCONST XMVECTORI32 g_XMExponentBias = { { { 127, 127, 127, 127 } } }; + XMGLOBALCONST XMVECTORI32 g_XMSubnormalExponent = { { { -126, -126, -126, -126 } } }; + XMGLOBALCONST XMVECTORI32 g_XMNumTrailing = { { { 23, 23, 23, 23 } } }; + XMGLOBALCONST XMVECTORI32 g_XMMinNormal = { { { 0x00800000, 0x00800000, 0x00800000, 0x00800000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMNegInfinity = { { { 0xFF800000, 0xFF800000, 0xFF800000, 0xFF800000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMNegQNaN = { { { 0xFFC00000, 0xFFC00000, 0xFFC00000, 0xFFC00000 } } }; + XMGLOBALCONST XMVECTORI32 g_XMBin128 = { { { 0x43000000, 0x43000000, 0x43000000, 0x43000000 } } }; + XMGLOBALCONST XMVECTORU32 g_XMBinNeg150 = { { { 0xC3160000, 0xC3160000, 0xC3160000, 0xC3160000 } } }; + XMGLOBALCONST XMVECTORI32 g_XM253 = { { { 253, 253, 253, 253 } } }; + XMGLOBALCONST XMVECTORF32 g_XMExpEst1 = { { { -6.93147182e-1f, -6.93147182e-1f, -6.93147182e-1f, -6.93147182e-1f } } }; + XMGLOBALCONST XMVECTORF32 g_XMExpEst2 = { { { +2.40226462e-1f, +2.40226462e-1f, +2.40226462e-1f, +2.40226462e-1f } } }; + XMGLOBALCONST XMVECTORF32 g_XMExpEst3 = { { { -5.55036440e-2f, -5.55036440e-2f, -5.55036440e-2f, -5.55036440e-2f } } }; + XMGLOBALCONST XMVECTORF32 g_XMExpEst4 = { { { +9.61597636e-3f, +9.61597636e-3f, +9.61597636e-3f, +9.61597636e-3f } } }; + XMGLOBALCONST XMVECTORF32 g_XMExpEst5 = { { { -1.32823968e-3f, -1.32823968e-3f, -1.32823968e-3f, -1.32823968e-3f } } }; + XMGLOBALCONST XMVECTORF32 g_XMExpEst6 = { { { +1.47491097e-4f, +1.47491097e-4f, +1.47491097e-4f, +1.47491097e-4f } } }; + XMGLOBALCONST XMVECTORF32 g_XMExpEst7 = { { { -1.08635004e-5f, -1.08635004e-5f, -1.08635004e-5f, -1.08635004e-5f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLogEst0 = { { { +1.442693f, +1.442693f, +1.442693f, +1.442693f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLogEst1 = { { { -0.721242f, -0.721242f, -0.721242f, -0.721242f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLogEst2 = { { { +0.479384f, +0.479384f, +0.479384f, +0.479384f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLogEst3 = { { { -0.350295f, -0.350295f, -0.350295f, -0.350295f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLogEst4 = { { { +0.248590f, +0.248590f, +0.248590f, +0.248590f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLogEst5 = { { { -0.145700f, -0.145700f, -0.145700f, -0.145700f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLogEst6 = { { { +0.057148f, +0.057148f, +0.057148f, +0.057148f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLogEst7 = { { { -0.010578f, -0.010578f, -0.010578f, -0.010578f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLgE = { { { +1.442695f, +1.442695f, +1.442695f, +1.442695f } } }; + XMGLOBALCONST XMVECTORF32 g_XMInvLgE = { { { +6.93147182e-1f, +6.93147182e-1f, +6.93147182e-1f, +6.93147182e-1f } } }; + XMGLOBALCONST XMVECTORF32 g_XMLg10 = { { { +3.321928f, +3.321928f, +3.321928f, +3.321928f } } }; + XMGLOBALCONST XMVECTORF32 g_XMInvLg10 = { { { +3.010299956e-1f, +3.010299956e-1f, +3.010299956e-1f, +3.010299956e-1f } } }; + XMGLOBALCONST XMVECTORF32 g_UByteMax = { { { 255.0f, 255.0f, 255.0f, 255.0f } } }; + XMGLOBALCONST XMVECTORF32 g_ByteMin = { { { -127.0f, -127.0f, -127.0f, -127.0f } } }; + XMGLOBALCONST XMVECTORF32 g_ByteMax = { { { 127.0f, 127.0f, 127.0f, 127.0f } } }; + XMGLOBALCONST XMVECTORF32 g_ShortMin = { { { -32767.0f, -32767.0f, -32767.0f, -32767.0f } } }; + XMGLOBALCONST XMVECTORF32 g_ShortMax = { { { 32767.0f, 32767.0f, 32767.0f, 32767.0f } } }; + XMGLOBALCONST XMVECTORF32 g_UShortMax = { { { 65535.0f, 65535.0f, 65535.0f, 65535.0f } } }; + + /**************************************************************************** + * + * Implementation + * + ****************************************************************************/ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4068 4214 4204 4365 4616 4640 6001 6101) + // C4068/4616: ignore unknown pragmas + // C4214/4204: nonstandard extension used + // C4365/4640: Off by default noise + // C6001/6101: False positives +#endif + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable : 25000, "FXMVECTOR is 16 bytes") +#pragma prefast(disable : 26495, "Union initialization confuses /analyze") +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wundefined-reinterpret-cast" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + +//------------------------------------------------------------------------------ + + inline XMVECTOR XM_CALLCONV XMVectorSetBinaryConstant(uint32_t C0, uint32_t C1, uint32_t C2, uint32_t C3) noexcept + { + #if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 vResult; + vResult.u[0] = (0 - (C0 & 1)) & 0x3F800000; + vResult.u[1] = (0 - (C1 & 1)) & 0x3F800000; + vResult.u[2] = (0 - (C2 & 1)) & 0x3F800000; + vResult.u[3] = (0 - (C3 & 1)) & 0x3F800000; + return vResult.v; + #elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTORU32 vResult; + vResult.u[0] = (0 - (C0 & 1)) & 0x3F800000; + vResult.u[1] = (0 - (C1 & 1)) & 0x3F800000; + vResult.u[2] = (0 - (C2 & 1)) & 0x3F800000; + vResult.u[3] = (0 - (C3 & 1)) & 0x3F800000; + return vResult.v; + #else // XM_SSE_INTRINSICS_ + static const XMVECTORU32 g_vMask1 = { { { 1, 1, 1, 1 } } }; + // Move the parms to a vector + __m128i vTemp = _mm_set_epi32(static_cast(C3), static_cast(C2), static_cast(C1), static_cast(C0)); + // Mask off the low bits + vTemp = _mm_and_si128(vTemp, g_vMask1); + // 0xFFFFFFFF on true bits + vTemp = _mm_cmpeq_epi32(vTemp, g_vMask1); + // 0xFFFFFFFF -> 1.0f, 0x00000000 -> 0.0f + vTemp = _mm_and_si128(vTemp, g_XMOne); + return _mm_castsi128_ps(vTemp); + #endif + } + + //------------------------------------------------------------------------------ + + inline XMVECTOR XM_CALLCONV XMVectorSplatConstant(int32_t IntConstant, uint32_t DivExponent) noexcept + { + assert(IntConstant >= -16 && IntConstant <= 15); + assert(DivExponent < 32); + #if defined(_XM_NO_INTRINSICS_) + + using DirectX::XMConvertVectorIntToFloat; + + XMVECTORI32 V = { { { IntConstant, IntConstant, IntConstant, IntConstant } } }; + return XMConvertVectorIntToFloat(V.v, DivExponent); + + #elif defined(_XM_ARM_NEON_INTRINSICS_) + // Splat the int + int32x4_t vScale = vdupq_n_s32(IntConstant); + // Convert to a float + XMVECTOR vResult = vcvtq_f32_s32(vScale); + // Convert DivExponent into 1.0f/(1<(&vScale)[0]); + return vResult; + #else // XM_SSE_INTRINSICS_ + // Splat the int + __m128i vScale = _mm_set1_epi32(IntConstant); + // Convert to a float + XMVECTOR vResult = _mm_cvtepi32_ps(vScale); + // Convert DivExponent into 1.0f/(1<(uScale)); + // Multiply by the reciprocal (Perform a right shift by DivExponent) + vResult = _mm_mul_ps(vResult, _mm_castsi128_ps(vScale)); + return vResult; + #endif + } + + //------------------------------------------------------------------------------ + + inline XMVECTOR XM_CALLCONV XMVectorSplatConstantInt(int32_t IntConstant) noexcept + { + assert(IntConstant >= -16 && IntConstant <= 15); + #if defined(_XM_NO_INTRINSICS_) + + XMVECTORI32 V = { { { IntConstant, IntConstant, IntConstant, IntConstant } } }; + return V.v; + + #elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x4_t V = vdupq_n_s32(IntConstant); + return reinterpret_cast(&V)[0]; + #else // XM_SSE_INTRINSICS_ + __m128i V = _mm_set1_epi32(IntConstant); + return _mm_castsi128_ps(V); + #endif + } + +#include "DirectXMathConvert.inl" +#include "DirectXMathVector.inl" +#include "DirectXMathMatrix.inl" +#include "DirectXMathMisc.inl" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +} // namespace DirectX + diff --git a/Extern/dxmath/Inc/DirectXMathConvert.inl b/Extern/dxmath/Inc/DirectXMathConvert.inl new file mode 100644 index 000000000..aaf9ef119 --- /dev/null +++ b/Extern/dxmath/Inc/DirectXMathConvert.inl @@ -0,0 +1,2201 @@ +//------------------------------------------------------------------------------------- +// DirectXMathConvert.inl -- SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +/**************************************************************************** + * + * Data conversion + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4701) +// C4701: false positives +#endif + +inline XMVECTOR XM_CALLCONV XMConvertVectorIntToFloat +( + FXMVECTOR VInt, + uint32_t DivExponent +) noexcept +{ + assert(DivExponent < 32); +#if defined(_XM_NO_INTRINSICS_) + float fScale = 1.0f / static_cast(1U << DivExponent); + uint32_t ElementIndex = 0; + XMVECTOR Result; + do + { + auto iTemp = static_cast(VInt.vector4_u32[ElementIndex]); + Result.vector4_f32[ElementIndex] = static_cast(iTemp)* fScale; + } + while (++ElementIndex < 4); + return Result; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float fScale = 1.0f / static_cast(1U << DivExponent); + float32x4_t vResult = vcvtq_f32_s32(vreinterpretq_s32_f32(VInt)); + return vmulq_n_f32(vResult, fScale); +#else // _XM_SSE_INTRINSICS_ + // Convert to floats + XMVECTOR vResult = _mm_cvtepi32_ps(_mm_castps_si128(VInt)); + // Convert DivExponent into 1.0f/(1<(uScale)); + vResult = _mm_mul_ps(vResult, _mm_castsi128_ps(vScale)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMConvertVectorFloatToInt +( + FXMVECTOR VFloat, + uint32_t MulExponent +) noexcept +{ + assert(MulExponent < 32); +#if defined(_XM_NO_INTRINSICS_) + // Get the scalar factor. + auto fScale = static_cast(1U << MulExponent); + uint32_t ElementIndex = 0; + XMVECTOR Result; + do + { + int32_t iResult; + float fTemp = VFloat.vector4_f32[ElementIndex] * fScale; + if (fTemp <= -(65536.0f * 32768.0f)) + { + iResult = (-0x7FFFFFFF) - 1; + } + else if (fTemp > (65536.0f * 32768.0f) - 128.0f) + { + iResult = 0x7FFFFFFF; + } + else + { + iResult = static_cast(fTemp); + } + Result.vector4_u32[ElementIndex] = static_cast(iResult); + } + while (++ElementIndex < 4); + return Result; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vResult = vmulq_n_f32(VFloat, static_cast(1U << MulExponent)); + // In case of positive overflow, detect it + uint32x4_t vOverflow = vcgtq_f32(vResult, g_XMMaxInt); + // Float to int conversion + int32x4_t vResulti = vcvtq_s32_f32(vResult); + // If there was positive overflow, set to 0x7FFFFFFF + vResult = vreinterpretq_f32_u32(vandq_u32(vOverflow, g_XMAbsMask)); + vOverflow = vbicq_u32(vreinterpretq_u32_s32(vResulti), vOverflow); + vOverflow = vorrq_u32(vOverflow, vreinterpretq_u32_f32(vResult)); + return vreinterpretq_f32_u32(vOverflow); +#else // _XM_SSE_INTRINSICS_ + XMVECTOR vResult = _mm_set_ps1(static_cast(1U << MulExponent)); + vResult = _mm_mul_ps(vResult, VFloat); + // In case of positive overflow, detect it + XMVECTOR vOverflow = _mm_cmpgt_ps(vResult, g_XMMaxInt); + // Float to int conversion + __m128i vResulti = _mm_cvttps_epi32(vResult); + // If there was positive overflow, set to 0x7FFFFFFF + vResult = _mm_and_ps(vOverflow, g_XMAbsMask); + vOverflow = _mm_andnot_ps(vOverflow, _mm_castsi128_ps(vResulti)); + vOverflow = _mm_or_ps(vOverflow, vResult); + return vOverflow; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMConvertVectorUIntToFloat +( + FXMVECTOR VUInt, + uint32_t DivExponent +) noexcept +{ + assert(DivExponent < 32); +#if defined(_XM_NO_INTRINSICS_) + float fScale = 1.0f / static_cast(1U << DivExponent); + uint32_t ElementIndex = 0; + XMVECTOR Result; + do + { + Result.vector4_f32[ElementIndex] = static_cast(VUInt.vector4_u32[ElementIndex])* fScale; + } + while (++ElementIndex < 4); + return Result; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float fScale = 1.0f / static_cast(1U << DivExponent); + float32x4_t vResult = vcvtq_f32_u32(vreinterpretq_u32_f32(VUInt)); + return vmulq_n_f32(vResult, fScale); +#else // _XM_SSE_INTRINSICS_ + // For the values that are higher than 0x7FFFFFFF, a fixup is needed + // Determine which ones need the fix. + XMVECTOR vMask = _mm_and_ps(VUInt, g_XMNegativeZero); + // Force all values positive + XMVECTOR vResult = _mm_xor_ps(VUInt, vMask); + // Convert to floats + vResult = _mm_cvtepi32_ps(_mm_castps_si128(vResult)); + // Convert 0x80000000 -> 0xFFFFFFFF + __m128i iMask = _mm_srai_epi32(_mm_castps_si128(vMask), 31); + // For only the ones that are too big, add the fixup + vMask = _mm_and_ps(_mm_castsi128_ps(iMask), g_XMFixUnsigned); + vResult = _mm_add_ps(vResult, vMask); + // Convert DivExponent into 1.0f/(1<(uScale)); + vResult = _mm_mul_ps(vResult, _mm_castsi128_ps(iMask)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMConvertVectorFloatToUInt +( + FXMVECTOR VFloat, + uint32_t MulExponent +) noexcept +{ + assert(MulExponent < 32); +#if defined(_XM_NO_INTRINSICS_) + // Get the scalar factor. + auto fScale = static_cast(1U << MulExponent); + uint32_t ElementIndex = 0; + XMVECTOR Result; + do + { + uint32_t uResult; + float fTemp = VFloat.vector4_f32[ElementIndex] * fScale; + if (fTemp <= 0.0f) + { + uResult = 0; + } + else if (fTemp >= (65536.0f * 65536.0f)) + { + uResult = 0xFFFFFFFFU; + } + else + { + uResult = static_cast(fTemp); + } + Result.vector4_u32[ElementIndex] = uResult; + } + while (++ElementIndex < 4); + return Result; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vResult = vmulq_n_f32(VFloat, static_cast(1U << MulExponent)); + // In case of overflow, detect it + uint32x4_t vOverflow = vcgtq_f32(vResult, g_XMMaxUInt); + // Float to int conversion + uint32x4_t vResulti = vcvtq_u32_f32(vResult); + // If there was overflow, set to 0xFFFFFFFFU + vResult = vreinterpretq_f32_u32(vbicq_u32(vResulti, vOverflow)); + vOverflow = vorrq_u32(vOverflow, vreinterpretq_u32_f32(vResult)); + return vreinterpretq_f32_u32(vOverflow); +#else // _XM_SSE_INTRINSICS_ + XMVECTOR vResult = _mm_set_ps1(static_cast(1U << MulExponent)); + vResult = _mm_mul_ps(vResult, VFloat); + // Clamp to >=0 + vResult = _mm_max_ps(vResult, g_XMZero); + // Any numbers that are too big, set to 0xFFFFFFFFU + XMVECTOR vOverflow = _mm_cmpgt_ps(vResult, g_XMMaxUInt); + XMVECTOR vValue = g_XMUnsignedFix; + // Too large for a signed integer? + XMVECTOR vMask = _mm_cmpge_ps(vResult, vValue); + // Zero for number's lower than 0x80000000, 32768.0f*65536.0f otherwise + vValue = _mm_and_ps(vValue, vMask); + // Perform fixup only on numbers too large (Keeps low bit precision) + vResult = _mm_sub_ps(vResult, vValue); + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Convert from signed to unsigned pnly if greater than 0x80000000 + vMask = _mm_and_ps(vMask, g_XMNegativeZero); + vResult = _mm_xor_ps(_mm_castsi128_ps(vResulti), vMask); + // On those that are too large, set to 0xFFFFFFFF + vResult = _mm_or_ps(vResult, vOverflow); + return vResult; +#endif +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +/**************************************************************************** + * + * Vector and matrix load operations + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadInt(const uint32_t* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_u32[0] = *pSource; + V.vector4_u32[1] = 0; + V.vector4_u32[2] = 0; + V.vector4_u32[3] = 0; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t zero = vdupq_n_u32(0); + return vreinterpretq_f32_u32(vld1q_lane_u32(pSource, zero, 0)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_load_ss(reinterpret_cast(pSource)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadFloat(const float* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = *pSource; + V.vector4_f32[1] = 0.f; + V.vector4_f32[2] = 0.f; + V.vector4_f32[3] = 0.f; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t zero = vdupq_n_f32(0); + return vld1q_lane_f32(pSource, zero, 0); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_load_ss(pSource); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadInt2(const uint32_t* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_u32[0] = pSource[0]; + V.vector4_u32[1] = pSource[1]; + V.vector4_u32[2] = 0; + V.vector4_u32[3] = 0; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t x = vld1_u32(pSource); + uint32x2_t zero = vdup_n_u32(0); + return vreinterpretq_f32_u32(vcombine_u32(x, zero)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadInt2A(const uint32_t* pSource) noexcept +{ + assert(pSource); + assert((reinterpret_cast(pSource) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_u32[0] = pSource[0]; + V.vector4_u32[1] = pSource[1]; + V.vector4_u32[2] = 0; + V.vector4_u32[3] = 0; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + uint32x2_t x = vld1_u32_ex(pSource, 64); +#else + uint32x2_t x = vld1_u32(pSource); +#endif + uint32x2_t zero = vdup_n_u32(0); + return vreinterpretq_f32_u32(vcombine_u32(x, zero)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadFloat2(const XMFLOAT2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = pSource->x; + V.vector4_f32[1] = pSource->y; + V.vector4_f32[2] = 0.f; + V.vector4_f32[3] = 0.f; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t x = vld1_f32(reinterpret_cast(pSource)); + float32x2_t zero = vdup_n_f32(0); + return vcombine_f32(x, zero); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadFloat2A(const XMFLOAT2A* pSource) noexcept +{ + assert(pSource); + assert((reinterpret_cast(pSource) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = pSource->x; + V.vector4_f32[1] = pSource->y; + V.vector4_f32[2] = 0.f; + V.vector4_f32[3] = 0.f; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + float32x2_t x = vld1_f32_ex(reinterpret_cast(pSource), 64); +#else + float32x2_t x = vld1_f32(reinterpret_cast(pSource)); +#endif + float32x2_t zero = vdup_n_f32(0); + return vcombine_f32(x, zero); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadSInt2(const XMINT2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = static_cast(pSource->x); + V.vector4_f32[1] = static_cast(pSource->y); + V.vector4_f32[2] = 0.f; + V.vector4_f32[3] = 0.f; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x2_t x = vld1_s32(reinterpret_cast(pSource)); + float32x2_t v = vcvt_f32_s32(x); + float32x2_t zero = vdup_n_f32(0); + return vcombine_f32(v, zero); +#elif defined(_XM_SSE_INTRINSICS_) + __m128 V = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + return _mm_cvtepi32_ps(_mm_castps_si128(V)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUInt2(const XMUINT2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = static_cast(pSource->x); + V.vector4_f32[1] = static_cast(pSource->y); + V.vector4_f32[2] = 0.f; + V.vector4_f32[3] = 0.f; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t x = vld1_u32(reinterpret_cast(pSource)); + float32x2_t v = vcvt_f32_u32(x); + float32x2_t zero = vdup_n_f32(0); + return vcombine_f32(v, zero); +#elif defined(_XM_SSE_INTRINSICS_) + __m128 V = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + // For the values that are higher than 0x7FFFFFFF, a fixup is needed + // Determine which ones need the fix. + XMVECTOR vMask = _mm_and_ps(V, g_XMNegativeZero); + // Force all values positive + XMVECTOR vResult = _mm_xor_ps(V, vMask); + // Convert to floats + vResult = _mm_cvtepi32_ps(_mm_castps_si128(vResult)); + // Convert 0x80000000 -> 0xFFFFFFFF + __m128i iMask = _mm_srai_epi32(_mm_castps_si128(vMask), 31); + // For only the ones that are too big, add the fixup + vMask = _mm_and_ps(_mm_castsi128_ps(iMask), g_XMFixUnsigned); + vResult = _mm_add_ps(vResult, vMask); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadInt3(const uint32_t* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_u32[0] = pSource[0]; + V.vector4_u32[1] = pSource[1]; + V.vector4_u32[2] = pSource[2]; + V.vector4_u32[3] = 0; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t x = vld1_u32(pSource); + uint32x2_t zero = vdup_n_u32(0); + uint32x2_t y = vld1_lane_u32(pSource + 2, zero, 0); + return vreinterpretq_f32_u32(vcombine_u32(x, y)); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + __m128 z = _mm_load_ss(reinterpret_cast(pSource + 2)); + return _mm_insert_ps(xy, z, 0x20); +#elif defined(_XM_SSE_INTRINSICS_) + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + __m128 z = _mm_load_ss(reinterpret_cast(pSource + 2)); + return _mm_movelh_ps(xy, z); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadInt3A(const uint32_t* pSource) noexcept +{ + assert(pSource); + assert((reinterpret_cast(pSource) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_u32[0] = pSource[0]; + V.vector4_u32[1] = pSource[1]; + V.vector4_u32[2] = pSource[2]; + V.vector4_u32[3] = 0; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Reads an extra integer which is zero'd +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + uint32x4_t V = vld1q_u32_ex(pSource, 128); +#else + uint32x4_t V = vld1q_u32(pSource); +#endif + return vreinterpretq_f32_u32(vsetq_lane_u32(0, V, 3)); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + __m128 z = _mm_load_ss(reinterpret_cast(pSource + 2)); + return _mm_insert_ps(xy, z, 0x20); +#elif defined(_XM_SSE_INTRINSICS_) + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + __m128 z = _mm_load_ss(reinterpret_cast(pSource + 2)); + return _mm_movelh_ps(xy, z); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadFloat3(const XMFLOAT3* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = pSource->x; + V.vector4_f32[1] = pSource->y; + V.vector4_f32[2] = pSource->z; + V.vector4_f32[3] = 0.f; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t x = vld1_f32(reinterpret_cast(pSource)); + float32x2_t zero = vdup_n_f32(0); + float32x2_t y = vld1_lane_f32(reinterpret_cast(pSource) + 2, zero, 0); + return vcombine_f32(x, y); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + __m128 z = _mm_load_ss(&pSource->z); + return _mm_insert_ps(xy, z, 0x20); +#elif defined(_XM_SSE_INTRINSICS_) + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + __m128 z = _mm_load_ss(&pSource->z); + return _mm_movelh_ps(xy, z); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadFloat3A(const XMFLOAT3A* pSource) noexcept +{ + assert(pSource); + assert((reinterpret_cast(pSource) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = pSource->x; + V.vector4_f32[1] = pSource->y; + V.vector4_f32[2] = pSource->z; + V.vector4_f32[3] = 0.f; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Reads an extra float which is zero'd +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + float32x4_t V = vld1q_f32_ex(reinterpret_cast(pSource), 128); +#else + float32x4_t V = vld1q_f32(reinterpret_cast(pSource)); +#endif + return vsetq_lane_f32(0, V, 3); +#elif defined(_XM_SSE_INTRINSICS_) + // Reads an extra float which is zero'd + __m128 V = _mm_load_ps(&pSource->x); + return _mm_and_ps(V, g_XMMask3); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadSInt3(const XMINT3* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR V; + V.vector4_f32[0] = static_cast(pSource->x); + V.vector4_f32[1] = static_cast(pSource->y); + V.vector4_f32[2] = static_cast(pSource->z); + V.vector4_f32[3] = 0.f; + return V; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x2_t x = vld1_s32(reinterpret_cast(pSource)); + int32x2_t zero = vdup_n_s32(0); + int32x2_t y = vld1_lane_s32(reinterpret_cast(pSource) + 2, zero, 0); + int32x4_t v = vcombine_s32(x, y); + return vcvtq_f32_s32(v); +#elif defined(_XM_SSE_INTRINSICS_) + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + __m128 z = _mm_load_ss(reinterpret_cast(&pSource->z)); + __m128 V = _mm_movelh_ps(xy, z); + return _mm_cvtepi32_ps(_mm_castps_si128(V)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUInt3(const XMUINT3* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = static_cast(pSource->x); + V.vector4_f32[1] = static_cast(pSource->y); + V.vector4_f32[2] = static_cast(pSource->z); + V.vector4_f32[3] = 0.f; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t x = vld1_u32(reinterpret_cast(pSource)); + uint32x2_t zero = vdup_n_u32(0); + uint32x2_t y = vld1_lane_u32(reinterpret_cast(pSource) + 2, zero, 0); + uint32x4_t v = vcombine_u32(x, y); + return vcvtq_f32_u32(v); +#elif defined(_XM_SSE_INTRINSICS_) + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pSource))); + __m128 z = _mm_load_ss(reinterpret_cast(&pSource->z)); + __m128 V = _mm_movelh_ps(xy, z); + // For the values that are higher than 0x7FFFFFFF, a fixup is needed + // Determine which ones need the fix. + XMVECTOR vMask = _mm_and_ps(V, g_XMNegativeZero); + // Force all values positive + XMVECTOR vResult = _mm_xor_ps(V, vMask); + // Convert to floats + vResult = _mm_cvtepi32_ps(_mm_castps_si128(vResult)); + // Convert 0x80000000 -> 0xFFFFFFFF + __m128i iMask = _mm_srai_epi32(_mm_castps_si128(vMask), 31); + // For only the ones that are too big, add the fixup + vMask = _mm_and_ps(_mm_castsi128_ps(iMask), g_XMFixUnsigned); + vResult = _mm_add_ps(vResult, vMask); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadInt4(const uint32_t* pSource) noexcept +{ + assert(pSource); + +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_u32[0] = pSource[0]; + V.vector4_u32[1] = pSource[1]; + V.vector4_u32[2] = pSource[2]; + V.vector4_u32[3] = pSource[3]; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vld1q_u32(pSource)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_loadu_si128(reinterpret_cast(pSource)); + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadInt4A(const uint32_t* pSource) noexcept +{ + assert(pSource); + assert((reinterpret_cast(pSource) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_u32[0] = pSource[0]; + V.vector4_u32[1] = pSource[1]; + V.vector4_u32[2] = pSource[2]; + V.vector4_u32[3] = pSource[3]; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + return vld1q_u32_ex(pSource, 128); +#else + return vreinterpretq_f32_u32(vld1q_u32(pSource)); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_load_si128(reinterpret_cast(pSource)); + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadFloat4(const XMFLOAT4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = pSource->x; + V.vector4_f32[1] = pSource->y; + V.vector4_f32[2] = pSource->z; + V.vector4_f32[3] = pSource->w; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vld1q_f32(reinterpret_cast(pSource)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_loadu_ps(&pSource->x); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadFloat4A(const XMFLOAT4A* pSource) noexcept +{ + assert(pSource); + assert((reinterpret_cast(pSource) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = pSource->x; + V.vector4_f32[1] = pSource->y; + V.vector4_f32[2] = pSource->z; + V.vector4_f32[3] = pSource->w; + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + return vld1q_f32_ex(reinterpret_cast(pSource), 128); +#else + return vld1q_f32(reinterpret_cast(pSource)); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_load_ps(&pSource->x); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadSInt4(const XMINT4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR V; + V.vector4_f32[0] = static_cast(pSource->x); + V.vector4_f32[1] = static_cast(pSource->y); + V.vector4_f32[2] = static_cast(pSource->z); + V.vector4_f32[3] = static_cast(pSource->w); + return V; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x4_t v = vld1q_s32(reinterpret_cast(pSource)); + return vcvtq_f32_s32(v); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_loadu_si128(reinterpret_cast(pSource)); + return _mm_cvtepi32_ps(V); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUInt4(const XMUINT4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR V; + V.vector4_f32[0] = static_cast(pSource->x); + V.vector4_f32[1] = static_cast(pSource->y); + V.vector4_f32[2] = static_cast(pSource->z); + V.vector4_f32[3] = static_cast(pSource->w); + return V; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t v = vld1q_u32(reinterpret_cast(pSource)); + return vcvtq_f32_u32(v); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_loadu_si128(reinterpret_cast(pSource)); + // For the values that are higher than 0x7FFFFFFF, a fixup is needed + // Determine which ones need the fix. + XMVECTOR vMask = _mm_and_ps(_mm_castsi128_ps(V), g_XMNegativeZero); + // Force all values positive + XMVECTOR vResult = _mm_xor_ps(_mm_castsi128_ps(V), vMask); + // Convert to floats + vResult = _mm_cvtepi32_ps(_mm_castps_si128(vResult)); + // Convert 0x80000000 -> 0xFFFFFFFF + __m128i iMask = _mm_srai_epi32(_mm_castps_si128(vMask), 31); + // For only the ones that are too big, add the fixup + vMask = _mm_and_ps(_mm_castsi128_ps(iMask), g_XMFixUnsigned); + vResult = _mm_add_ps(vResult, vMask); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMMATRIX XM_CALLCONV XMLoadFloat3x3(const XMFLOAT3X3* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.r[0].vector4_f32[0] = pSource->m[0][0]; + M.r[0].vector4_f32[1] = pSource->m[0][1]; + M.r[0].vector4_f32[2] = pSource->m[0][2]; + M.r[0].vector4_f32[3] = 0.0f; + + M.r[1].vector4_f32[0] = pSource->m[1][0]; + M.r[1].vector4_f32[1] = pSource->m[1][1]; + M.r[1].vector4_f32[2] = pSource->m[1][2]; + M.r[1].vector4_f32[3] = 0.0f; + + M.r[2].vector4_f32[0] = pSource->m[2][0]; + M.r[2].vector4_f32[1] = pSource->m[2][1]; + M.r[2].vector4_f32[2] = pSource->m[2][2]; + M.r[2].vector4_f32[3] = 0.0f; + M.r[3].vector4_f32[0] = 0.0f; + M.r[3].vector4_f32[1] = 0.0f; + M.r[3].vector4_f32[2] = 0.0f; + M.r[3].vector4_f32[3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t v0 = vld1q_f32(&pSource->m[0][0]); + float32x4_t v1 = vld1q_f32(&pSource->m[1][1]); + float32x2_t v2 = vcreate_f32(static_cast(*reinterpret_cast(&pSource->m[2][2]))); + float32x4_t T = vextq_f32(v0, v1, 3); + + XMMATRIX M; + M.r[0] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(v0), g_XMMask3)); + M.r[1] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T), g_XMMask3)); + M.r[2] = vcombine_f32(vget_high_f32(v1), v2); + M.r[3] = g_XMIdentityR3; + return M; +#elif defined(_XM_SSE_INTRINSICS_) + __m128 Z = _mm_setzero_ps(); + + __m128 V1 = _mm_loadu_ps(&pSource->m[0][0]); + __m128 V2 = _mm_loadu_ps(&pSource->m[1][1]); + __m128 V3 = _mm_load_ss(&pSource->m[2][2]); + + __m128 T1 = _mm_unpackhi_ps(V1, Z); + __m128 T2 = _mm_unpacklo_ps(V2, Z); + __m128 T3 = _mm_shuffle_ps(V3, T2, _MM_SHUFFLE(0, 1, 0, 0)); + __m128 T4 = _mm_movehl_ps(T2, T3); + __m128 T5 = _mm_movehl_ps(Z, T1); + + XMMATRIX M; + M.r[0] = _mm_movelh_ps(V1, T1); + M.r[1] = _mm_add_ps(T4, T5); + M.r[2] = _mm_shuffle_ps(V2, V3, _MM_SHUFFLE(1, 0, 3, 2)); + M.r[3] = g_XMIdentityR3; + return M; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMMATRIX XM_CALLCONV XMLoadFloat4x3(const XMFLOAT4X3* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.r[0].vector4_f32[0] = pSource->m[0][0]; + M.r[0].vector4_f32[1] = pSource->m[0][1]; + M.r[0].vector4_f32[2] = pSource->m[0][2]; + M.r[0].vector4_f32[3] = 0.0f; + + M.r[1].vector4_f32[0] = pSource->m[1][0]; + M.r[1].vector4_f32[1] = pSource->m[1][1]; + M.r[1].vector4_f32[2] = pSource->m[1][2]; + M.r[1].vector4_f32[3] = 0.0f; + + M.r[2].vector4_f32[0] = pSource->m[2][0]; + M.r[2].vector4_f32[1] = pSource->m[2][1]; + M.r[2].vector4_f32[2] = pSource->m[2][2]; + M.r[2].vector4_f32[3] = 0.0f; + + M.r[3].vector4_f32[0] = pSource->m[3][0]; + M.r[3].vector4_f32[1] = pSource->m[3][1]; + M.r[3].vector4_f32[2] = pSource->m[3][2]; + M.r[3].vector4_f32[3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t v0 = vld1q_f32(&pSource->m[0][0]); + float32x4_t v1 = vld1q_f32(&pSource->m[1][1]); + float32x4_t v2 = vld1q_f32(&pSource->m[2][2]); + + float32x4_t T1 = vextq_f32(v0, v1, 3); + float32x4_t T2 = vcombine_f32(vget_high_f32(v1), vget_low_f32(v2)); + float32x4_t T3 = vextq_f32(v2, v2, 1); + + XMMATRIX M; + M.r[0] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(v0), g_XMMask3)); + M.r[1] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T1), g_XMMask3)); + M.r[2] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T2), g_XMMask3)); + M.r[3] = vsetq_lane_f32(1.f, T3, 3); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + // Use unaligned load instructions to + // load the 12 floats + // vTemp1 = x1,y1,z1,x2 + XMVECTOR vTemp1 = _mm_loadu_ps(&pSource->m[0][0]); + // vTemp2 = y2,z2,x3,y3 + XMVECTOR vTemp2 = _mm_loadu_ps(&pSource->m[1][1]); + // vTemp4 = z3,x4,y4,z4 + XMVECTOR vTemp4 = _mm_loadu_ps(&pSource->m[2][2]); + // vTemp3 = x3,y3,z3,z3 + XMVECTOR vTemp3 = _mm_shuffle_ps(vTemp2, vTemp4, _MM_SHUFFLE(0, 0, 3, 2)); + // vTemp2 = y2,z2,x2,x2 + vTemp2 = _mm_shuffle_ps(vTemp2, vTemp1, _MM_SHUFFLE(3, 3, 1, 0)); + // vTemp2 = x2,y2,z2,z2 + vTemp2 = XM_PERMUTE_PS(vTemp2, _MM_SHUFFLE(1, 1, 0, 2)); + // vTemp1 = x1,y1,z1,0 + vTemp1 = _mm_and_ps(vTemp1, g_XMMask3); + // vTemp2 = x2,y2,z2,0 + vTemp2 = _mm_and_ps(vTemp2, g_XMMask3); + // vTemp3 = x3,y3,z3,0 + vTemp3 = _mm_and_ps(vTemp3, g_XMMask3); + // vTemp4i = x4,y4,z4,0 + __m128i vTemp4i = _mm_srli_si128(_mm_castps_si128(vTemp4), 32 / 8); + // vTemp4i = x4,y4,z4,1.0f + vTemp4i = _mm_or_si128(vTemp4i, g_XMIdentityR3); + XMMATRIX M(vTemp1, + vTemp2, + vTemp3, + _mm_castsi128_ps(vTemp4i)); + return M; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMMATRIX XM_CALLCONV XMLoadFloat4x3A(const XMFLOAT4X3A* pSource) noexcept +{ + assert(pSource); + assert((reinterpret_cast(pSource) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.r[0].vector4_f32[0] = pSource->m[0][0]; + M.r[0].vector4_f32[1] = pSource->m[0][1]; + M.r[0].vector4_f32[2] = pSource->m[0][2]; + M.r[0].vector4_f32[3] = 0.0f; + + M.r[1].vector4_f32[0] = pSource->m[1][0]; + M.r[1].vector4_f32[1] = pSource->m[1][1]; + M.r[1].vector4_f32[2] = pSource->m[1][2]; + M.r[1].vector4_f32[3] = 0.0f; + + M.r[2].vector4_f32[0] = pSource->m[2][0]; + M.r[2].vector4_f32[1] = pSource->m[2][1]; + M.r[2].vector4_f32[2] = pSource->m[2][2]; + M.r[2].vector4_f32[3] = 0.0f; + + M.r[3].vector4_f32[0] = pSource->m[3][0]; + M.r[3].vector4_f32[1] = pSource->m[3][1]; + M.r[3].vector4_f32[2] = pSource->m[3][2]; + M.r[3].vector4_f32[3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + float32x4_t v0 = vld1q_f32_ex(&pSource->m[0][0], 128); + float32x4_t v1 = vld1q_f32_ex(&pSource->m[1][1], 128); + float32x4_t v2 = vld1q_f32_ex(&pSource->m[2][2], 128); +#else + float32x4_t v0 = vld1q_f32(&pSource->m[0][0]); + float32x4_t v1 = vld1q_f32(&pSource->m[1][1]); + float32x4_t v2 = vld1q_f32(&pSource->m[2][2]); +#endif + + float32x4_t T1 = vextq_f32(v0, v1, 3); + float32x4_t T2 = vcombine_f32(vget_high_f32(v1), vget_low_f32(v2)); + float32x4_t T3 = vextq_f32(v2, v2, 1); + + XMMATRIX M; + M.r[0] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(v0), g_XMMask3)); + M.r[1] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T1), g_XMMask3)); + M.r[2] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T2), g_XMMask3)); + M.r[3] = vsetq_lane_f32(1.f, T3, 3); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + // Use aligned load instructions to + // load the 12 floats + // vTemp1 = x1,y1,z1,x2 + XMVECTOR vTemp1 = _mm_load_ps(&pSource->m[0][0]); + // vTemp2 = y2,z2,x3,y3 + XMVECTOR vTemp2 = _mm_load_ps(&pSource->m[1][1]); + // vTemp4 = z3,x4,y4,z4 + XMVECTOR vTemp4 = _mm_load_ps(&pSource->m[2][2]); + // vTemp3 = x3,y3,z3,z3 + XMVECTOR vTemp3 = _mm_shuffle_ps(vTemp2, vTemp4, _MM_SHUFFLE(0, 0, 3, 2)); + // vTemp2 = y2,z2,x2,x2 + vTemp2 = _mm_shuffle_ps(vTemp2, vTemp1, _MM_SHUFFLE(3, 3, 1, 0)); + // vTemp2 = x2,y2,z2,z2 + vTemp2 = XM_PERMUTE_PS(vTemp2, _MM_SHUFFLE(1, 1, 0, 2)); + // vTemp1 = x1,y1,z1,0 + vTemp1 = _mm_and_ps(vTemp1, g_XMMask3); + // vTemp2 = x2,y2,z2,0 + vTemp2 = _mm_and_ps(vTemp2, g_XMMask3); + // vTemp3 = x3,y3,z3,0 + vTemp3 = _mm_and_ps(vTemp3, g_XMMask3); + // vTemp4i = x4,y4,z4,0 + __m128i vTemp4i = _mm_srli_si128(_mm_castps_si128(vTemp4), 32 / 8); + // vTemp4i = x4,y4,z4,1.0f + vTemp4i = _mm_or_si128(vTemp4i, g_XMIdentityR3); + XMMATRIX M(vTemp1, + vTemp2, + vTemp3, + _mm_castsi128_ps(vTemp4i)); + return M; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMMATRIX XM_CALLCONV XMLoadFloat3x4(const XMFLOAT3X4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.r[0].vector4_f32[0] = pSource->m[0][0]; + M.r[0].vector4_f32[1] = pSource->m[1][0]; + M.r[0].vector4_f32[2] = pSource->m[2][0]; + M.r[0].vector4_f32[3] = 0.0f; + + M.r[1].vector4_f32[0] = pSource->m[0][1]; + M.r[1].vector4_f32[1] = pSource->m[1][1]; + M.r[1].vector4_f32[2] = pSource->m[2][1]; + M.r[1].vector4_f32[3] = 0.0f; + + M.r[2].vector4_f32[0] = pSource->m[0][2]; + M.r[2].vector4_f32[1] = pSource->m[1][2]; + M.r[2].vector4_f32[2] = pSource->m[2][2]; + M.r[2].vector4_f32[3] = 0.0f; + + M.r[3].vector4_f32[0] = pSource->m[0][3]; + M.r[3].vector4_f32[1] = pSource->m[1][3]; + M.r[3].vector4_f32[2] = pSource->m[2][3]; + M.r[3].vector4_f32[3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2x4_t vTemp0 = vld4_f32(&pSource->_11); + float32x4_t vTemp1 = vld1q_f32(&pSource->_31); + + float32x2_t l = vget_low_f32(vTemp1); + float32x4_t T0 = vcombine_f32(vTemp0.val[0], l); + float32x2_t rl = vrev64_f32(l); + float32x4_t T1 = vcombine_f32(vTemp0.val[1], rl); + + float32x2_t h = vget_high_f32(vTemp1); + float32x4_t T2 = vcombine_f32(vTemp0.val[2], h); + float32x2_t rh = vrev64_f32(h); + float32x4_t T3 = vcombine_f32(vTemp0.val[3], rh); + + XMMATRIX M = {}; + M.r[0] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T0), g_XMMask3)); + M.r[1] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T1), g_XMMask3)); + M.r[2] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T2), g_XMMask3)); + M.r[3] = vsetq_lane_f32(1.f, T3, 3); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + M.r[0] = _mm_loadu_ps(&pSource->_11); + M.r[1] = _mm_loadu_ps(&pSource->_21); + M.r[2] = _mm_loadu_ps(&pSource->_31); + M.r[3] = g_XMIdentityR3; + + // x.x,x.y,y.x,y.y + XMVECTOR vTemp1 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(1, 0, 1, 0)); + // x.z,x.w,y.z,y.w + XMVECTOR vTemp3 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(3, 2, 3, 2)); + // z.x,z.y,w.x,w.y + XMVECTOR vTemp2 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(1, 0, 1, 0)); + // z.z,z.w,w.z,w.w + XMVECTOR vTemp4 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(3, 2, 3, 2)); + XMMATRIX mResult; + + // x.x,y.x,z.x,w.x + mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + // x.y,y.y,z.y,w.y + mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + // x.z,y.z,z.z,w.z + mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + // x.w,y.w,z.w,w.w + mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1)); + return mResult; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMMATRIX XM_CALLCONV XMLoadFloat3x4A(const XMFLOAT3X4A* pSource) noexcept +{ + assert(pSource); + assert((reinterpret_cast(pSource) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.r[0].vector4_f32[0] = pSource->m[0][0]; + M.r[0].vector4_f32[1] = pSource->m[1][0]; + M.r[0].vector4_f32[2] = pSource->m[2][0]; + M.r[0].vector4_f32[3] = 0.0f; + + M.r[1].vector4_f32[0] = pSource->m[0][1]; + M.r[1].vector4_f32[1] = pSource->m[1][1]; + M.r[1].vector4_f32[2] = pSource->m[2][1]; + M.r[1].vector4_f32[3] = 0.0f; + + M.r[2].vector4_f32[0] = pSource->m[0][2]; + M.r[2].vector4_f32[1] = pSource->m[1][2]; + M.r[2].vector4_f32[2] = pSource->m[2][2]; + M.r[2].vector4_f32[3] = 0.0f; + + M.r[3].vector4_f32[0] = pSource->m[0][3]; + M.r[3].vector4_f32[1] = pSource->m[1][3]; + M.r[3].vector4_f32[2] = pSource->m[2][3]; + M.r[3].vector4_f32[3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + float32x2x4_t vTemp0 = vld4_f32_ex(&pSource->_11, 128); + float32x4_t vTemp1 = vld1q_f32_ex(&pSource->_31, 128); +#else + float32x2x4_t vTemp0 = vld4_f32(&pSource->_11); + float32x4_t vTemp1 = vld1q_f32(&pSource->_31); +#endif + + float32x2_t l = vget_low_f32(vTemp1); + float32x4_t T0 = vcombine_f32(vTemp0.val[0], l); + float32x2_t rl = vrev64_f32(l); + float32x4_t T1 = vcombine_f32(vTemp0.val[1], rl); + + float32x2_t h = vget_high_f32(vTemp1); + float32x4_t T2 = vcombine_f32(vTemp0.val[2], h); + float32x2_t rh = vrev64_f32(h); + float32x4_t T3 = vcombine_f32(vTemp0.val[3], rh); + + XMMATRIX M = {}; + M.r[0] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T0), g_XMMask3)); + M.r[1] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T1), g_XMMask3)); + M.r[2] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T2), g_XMMask3)); + M.r[3] = vsetq_lane_f32(1.f, T3, 3); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + M.r[0] = _mm_load_ps(&pSource->_11); + M.r[1] = _mm_load_ps(&pSource->_21); + M.r[2] = _mm_load_ps(&pSource->_31); + M.r[3] = g_XMIdentityR3; + + // x.x,x.y,y.x,y.y + XMVECTOR vTemp1 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(1, 0, 1, 0)); + // x.z,x.w,y.z,y.w + XMVECTOR vTemp3 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(3, 2, 3, 2)); + // z.x,z.y,w.x,w.y + XMVECTOR vTemp2 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(1, 0, 1, 0)); + // z.z,z.w,w.z,w.w + XMVECTOR vTemp4 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(3, 2, 3, 2)); + XMMATRIX mResult; + + // x.x,y.x,z.x,w.x + mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + // x.y,y.y,z.y,w.y + mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + // x.z,y.z,z.z,w.z + mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + // x.w,y.w,z.w,w.w + mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1)); + return mResult; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMMATRIX XM_CALLCONV XMLoadFloat4x4(const XMFLOAT4X4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.r[0].vector4_f32[0] = pSource->m[0][0]; + M.r[0].vector4_f32[1] = pSource->m[0][1]; + M.r[0].vector4_f32[2] = pSource->m[0][2]; + M.r[0].vector4_f32[3] = pSource->m[0][3]; + + M.r[1].vector4_f32[0] = pSource->m[1][0]; + M.r[1].vector4_f32[1] = pSource->m[1][1]; + M.r[1].vector4_f32[2] = pSource->m[1][2]; + M.r[1].vector4_f32[3] = pSource->m[1][3]; + + M.r[2].vector4_f32[0] = pSource->m[2][0]; + M.r[2].vector4_f32[1] = pSource->m[2][1]; + M.r[2].vector4_f32[2] = pSource->m[2][2]; + M.r[2].vector4_f32[3] = pSource->m[2][3]; + + M.r[3].vector4_f32[0] = pSource->m[3][0]; + M.r[3].vector4_f32[1] = pSource->m[3][1]; + M.r[3].vector4_f32[2] = pSource->m[3][2]; + M.r[3].vector4_f32[3] = pSource->m[3][3]; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMMATRIX M; + M.r[0] = vld1q_f32(reinterpret_cast(&pSource->_11)); + M.r[1] = vld1q_f32(reinterpret_cast(&pSource->_21)); + M.r[2] = vld1q_f32(reinterpret_cast(&pSource->_31)); + M.r[3] = vld1q_f32(reinterpret_cast(&pSource->_41)); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + M.r[0] = _mm_loadu_ps(&pSource->_11); + M.r[1] = _mm_loadu_ps(&pSource->_21); + M.r[2] = _mm_loadu_ps(&pSource->_31); + M.r[3] = _mm_loadu_ps(&pSource->_41); + return M; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMMATRIX XM_CALLCONV XMLoadFloat4x4A(const XMFLOAT4X4A* pSource) noexcept +{ + assert(pSource); + assert((reinterpret_cast(pSource) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.r[0].vector4_f32[0] = pSource->m[0][0]; + M.r[0].vector4_f32[1] = pSource->m[0][1]; + M.r[0].vector4_f32[2] = pSource->m[0][2]; + M.r[0].vector4_f32[3] = pSource->m[0][3]; + + M.r[1].vector4_f32[0] = pSource->m[1][0]; + M.r[1].vector4_f32[1] = pSource->m[1][1]; + M.r[1].vector4_f32[2] = pSource->m[1][2]; + M.r[1].vector4_f32[3] = pSource->m[1][3]; + + M.r[2].vector4_f32[0] = pSource->m[2][0]; + M.r[2].vector4_f32[1] = pSource->m[2][1]; + M.r[2].vector4_f32[2] = pSource->m[2][2]; + M.r[2].vector4_f32[3] = pSource->m[2][3]; + + M.r[3].vector4_f32[0] = pSource->m[3][0]; + M.r[3].vector4_f32[1] = pSource->m[3][1]; + M.r[3].vector4_f32[2] = pSource->m[3][2]; + M.r[3].vector4_f32[3] = pSource->m[3][3]; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMMATRIX M; +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + M.r[0] = vld1q_f32_ex(reinterpret_cast(&pSource->_11), 128); + M.r[1] = vld1q_f32_ex(reinterpret_cast(&pSource->_21), 128); + M.r[2] = vld1q_f32_ex(reinterpret_cast(&pSource->_31), 128); + M.r[3] = vld1q_f32_ex(reinterpret_cast(&pSource->_41), 128); +#else + M.r[0] = vld1q_f32(reinterpret_cast(&pSource->_11)); + M.r[1] = vld1q_f32(reinterpret_cast(&pSource->_21)); + M.r[2] = vld1q_f32(reinterpret_cast(&pSource->_31)); + M.r[3] = vld1q_f32(reinterpret_cast(&pSource->_41)); +#endif + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + M.r[0] = _mm_load_ps(&pSource->_11); + M.r[1] = _mm_load_ps(&pSource->_21); + M.r[2] = _mm_load_ps(&pSource->_31); + M.r[3] = _mm_load_ps(&pSource->_41); + return M; +#endif +} + +/**************************************************************************** + * + * Vector and matrix store operations + * + ****************************************************************************/ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreInt +( + uint32_t* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + *pDestination = XMVectorGetIntX(V); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_u32(pDestination, *reinterpret_cast(&V), 0); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_ss(reinterpret_cast(pDestination), V); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat +( + float* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + *pDestination = XMVectorGetX(V); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_f32(pDestination, V, 0); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_ss(pDestination, V); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreInt2 +( + uint32_t* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination[0] = V.vector4_u32[0]; + pDestination[1] = V.vector4_u32[1]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t VL = vget_low_u32(vreinterpretq_u32_f32(V)); + vst1_u32(pDestination, VL); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(V)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreInt2A +( + uint32_t* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + assert((reinterpret_cast(pDestination) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + pDestination[0] = V.vector4_u32[0]; + pDestination[1] = V.vector4_u32[1]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t VL = vget_low_u32(vreinterpretq_u32_f32(V)); +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + vst1_u32_ex(pDestination, VL, 64); +#else + vst1_u32(pDestination, VL); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(V)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat2 +( + XMFLOAT2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = V.vector4_f32[0]; + pDestination->y = V.vector4_f32[1]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + vst1_f32(reinterpret_cast(pDestination), VL); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(V)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat2A +( + XMFLOAT2A* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + assert((reinterpret_cast(pDestination) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = V.vector4_f32[0]; + pDestination->y = V.vector4_f32[1]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + vst1_f32_ex(reinterpret_cast(pDestination), VL, 64); +#else + vst1_f32(reinterpret_cast(pDestination), VL); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(V)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreSInt2 +( + XMINT2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = static_cast(V.vector4_f32[0]); + pDestination->y = static_cast(V.vector4_f32[1]); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t v = vget_low_f32(V); + int32x2_t iv = vcvt_s32_f32(v); + vst1_s32(reinterpret_cast(pDestination), iv); +#elif defined(_XM_SSE_INTRINSICS_) + // In case of positive overflow, detect it + XMVECTOR vOverflow = _mm_cmpgt_ps(V, g_XMMaxInt); + // Float to int conversion + __m128i vResulti = _mm_cvttps_epi32(V); + // If there was positive overflow, set to 0x7FFFFFFF + XMVECTOR vResult = _mm_and_ps(vOverflow, g_XMAbsMask); + vOverflow = _mm_andnot_ps(vOverflow, _mm_castsi128_ps(vResulti)); + vOverflow = _mm_or_ps(vOverflow, vResult); + // Write two ints + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(vOverflow)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUInt2 +( + XMUINT2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = static_cast(V.vector4_f32[0]); + pDestination->y = static_cast(V.vector4_f32[1]); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t v = vget_low_f32(V); + uint32x2_t iv = vcvt_u32_f32(v); + vst1_u32(reinterpret_cast(pDestination), iv); +#elif defined(_XM_SSE_INTRINSICS_) + // Clamp to >=0 + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + // Any numbers that are too big, set to 0xFFFFFFFFU + XMVECTOR vOverflow = _mm_cmpgt_ps(vResult, g_XMMaxUInt); + XMVECTOR vValue = g_XMUnsignedFix; + // Too large for a signed integer? + XMVECTOR vMask = _mm_cmpge_ps(vResult, vValue); + // Zero for number's lower than 0x80000000, 32768.0f*65536.0f otherwise + vValue = _mm_and_ps(vValue, vMask); + // Perform fixup only on numbers too large (Keeps low bit precision) + vResult = _mm_sub_ps(vResult, vValue); + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Convert from signed to unsigned pnly if greater than 0x80000000 + vMask = _mm_and_ps(vMask, g_XMNegativeZero); + vResult = _mm_xor_ps(_mm_castsi128_ps(vResulti), vMask); + // On those that are too large, set to 0xFFFFFFFF + vResult = _mm_or_ps(vResult, vOverflow); + // Write two uints + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(vResult)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreInt3 +( + uint32_t* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination[0] = V.vector4_u32[0]; + pDestination[1] = V.vector4_u32[1]; + pDestination[2] = V.vector4_u32[2]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t VL = vget_low_u32(vreinterpretq_u32_f32(V)); + vst1_u32(pDestination, VL); + vst1q_lane_u32(pDestination + 2, *reinterpret_cast(&V), 2); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(V)); + __m128 z = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + _mm_store_ss(reinterpret_cast(&pDestination[2]), z); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreInt3A +( + uint32_t* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + assert((reinterpret_cast(pDestination) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + pDestination[0] = V.vector4_u32[0]; + pDestination[1] = V.vector4_u32[1]; + pDestination[2] = V.vector4_u32[2]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t VL = vget_low_u32(vreinterpretq_u32_f32(V)); +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + vst1_u32_ex(pDestination, VL, 64); +#else + vst1_u32(pDestination, VL); +#endif + vst1q_lane_u32(pDestination + 2, *reinterpret_cast(&V), 2); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(V)); + __m128 z = _mm_movehl_ps(V, V); + _mm_store_ss(reinterpret_cast(&pDestination[2]), z); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat3 +( + XMFLOAT3* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = V.vector4_f32[0]; + pDestination->y = V.vector4_f32[1]; + pDestination->z = V.vector4_f32[2]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + vst1_f32(reinterpret_cast(pDestination), VL); + vst1q_lane_f32(reinterpret_cast(pDestination) + 2, V, 2); +#elif defined(_XM_SSE4_INTRINSICS_) + * reinterpret_cast(&pDestination->x) = _mm_extract_ps(V, 0); + *reinterpret_cast(&pDestination->y) = _mm_extract_ps(V, 1); + *reinterpret_cast(&pDestination->z) = _mm_extract_ps(V, 2); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(V)); + __m128 z = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + _mm_store_ss(&pDestination->z, z); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat3A +( + XMFLOAT3A* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + assert((reinterpret_cast(pDestination) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = V.vector4_f32[0]; + pDestination->y = V.vector4_f32[1]; + pDestination->z = V.vector4_f32[2]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + vst1_f32_ex(reinterpret_cast(pDestination), VL, 64); +#else + vst1_f32(reinterpret_cast(pDestination), VL); +#endif + vst1q_lane_f32(reinterpret_cast(pDestination) + 2, V, 2); +#elif defined(_XM_SSE4_INTRINSICS_) + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(V)); + *reinterpret_cast(&pDestination->z) = _mm_extract_ps(V, 2); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(V)); + __m128 z = _mm_movehl_ps(V, V); + _mm_store_ss(&pDestination->z, z); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreSInt3 +( + XMINT3* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = static_cast(V.vector4_f32[0]); + pDestination->y = static_cast(V.vector4_f32[1]); + pDestination->z = static_cast(V.vector4_f32[2]); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x4_t v = vcvtq_s32_f32(V); + int32x2_t vL = vget_low_s32(v); + vst1_s32(reinterpret_cast(pDestination), vL); + vst1q_lane_s32(reinterpret_cast(pDestination) + 2, v, 2); +#elif defined(_XM_SSE_INTRINSICS_) + // In case of positive overflow, detect it + XMVECTOR vOverflow = _mm_cmpgt_ps(V, g_XMMaxInt); + // Float to int conversion + __m128i vResulti = _mm_cvttps_epi32(V); + // If there was positive overflow, set to 0x7FFFFFFF + XMVECTOR vResult = _mm_and_ps(vOverflow, g_XMAbsMask); + vOverflow = _mm_andnot_ps(vOverflow, _mm_castsi128_ps(vResulti)); + vOverflow = _mm_or_ps(vOverflow, vResult); + // Write 3 uints + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(vOverflow)); + __m128 z = XM_PERMUTE_PS(vOverflow, _MM_SHUFFLE(2, 2, 2, 2)); + _mm_store_ss(reinterpret_cast(&pDestination->z), z); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUInt3 +( + XMUINT3* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = static_cast(V.vector4_f32[0]); + pDestination->y = static_cast(V.vector4_f32[1]); + pDestination->z = static_cast(V.vector4_f32[2]); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t v = vcvtq_u32_f32(V); + uint32x2_t vL = vget_low_u32(v); + vst1_u32(reinterpret_cast(pDestination), vL); + vst1q_lane_u32(reinterpret_cast(pDestination) + 2, v, 2); +#elif defined(_XM_SSE_INTRINSICS_) + // Clamp to >=0 + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + // Any numbers that are too big, set to 0xFFFFFFFFU + XMVECTOR vOverflow = _mm_cmpgt_ps(vResult, g_XMMaxUInt); + XMVECTOR vValue = g_XMUnsignedFix; + // Too large for a signed integer? + XMVECTOR vMask = _mm_cmpge_ps(vResult, vValue); + // Zero for number's lower than 0x80000000, 32768.0f*65536.0f otherwise + vValue = _mm_and_ps(vValue, vMask); + // Perform fixup only on numbers too large (Keeps low bit precision) + vResult = _mm_sub_ps(vResult, vValue); + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Convert from signed to unsigned pnly if greater than 0x80000000 + vMask = _mm_and_ps(vMask, g_XMNegativeZero); + vResult = _mm_xor_ps(_mm_castsi128_ps(vResulti), vMask); + // On those that are too large, set to 0xFFFFFFFF + vResult = _mm_or_ps(vResult, vOverflow); + // Write 3 uints + _mm_store_sd(reinterpret_cast(pDestination), _mm_castps_pd(vResult)); + __m128 z = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(2, 2, 2, 2)); + _mm_store_ss(reinterpret_cast(&pDestination->z), z); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreInt4 +( + uint32_t* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination[0] = V.vector4_u32[0]; + pDestination[1] = V.vector4_u32[1]; + pDestination[2] = V.vector4_u32[2]; + pDestination[3] = V.vector4_u32[3]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_u32(pDestination, vreinterpretq_u32_f32(V)); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_storeu_si128(reinterpret_cast<__m128i*>(pDestination), _mm_castps_si128(V)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreInt4A +( + uint32_t* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + assert((reinterpret_cast(pDestination) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + pDestination[0] = V.vector4_u32[0]; + pDestination[1] = V.vector4_u32[1]; + pDestination[2] = V.vector4_u32[2]; + pDestination[3] = V.vector4_u32[3]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + vst1q_u32_ex(pDestination, V, 128); +#else + vst1q_u32(pDestination, vreinterpretq_u32_f32(V)); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_si128(reinterpret_cast<__m128i*>(pDestination), _mm_castps_si128(V)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat4 +( + XMFLOAT4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = V.vector4_f32[0]; + pDestination->y = V.vector4_f32[1]; + pDestination->z = V.vector4_f32[2]; + pDestination->w = V.vector4_f32[3]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_f32(reinterpret_cast(pDestination), V); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_storeu_ps(&pDestination->x, V); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat4A +( + XMFLOAT4A* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + assert((reinterpret_cast(pDestination) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = V.vector4_f32[0]; + pDestination->y = V.vector4_f32[1]; + pDestination->z = V.vector4_f32[2]; + pDestination->w = V.vector4_f32[3]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + vst1q_f32_ex(reinterpret_cast(pDestination), V, 128); +#else + vst1q_f32(reinterpret_cast(pDestination), V); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_ps(&pDestination->x, V); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreSInt4 +( + XMINT4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = static_cast(V.vector4_f32[0]); + pDestination->y = static_cast(V.vector4_f32[1]); + pDestination->z = static_cast(V.vector4_f32[2]); + pDestination->w = static_cast(V.vector4_f32[3]); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x4_t v = vcvtq_s32_f32(V); + vst1q_s32(reinterpret_cast(pDestination), v); +#elif defined(_XM_SSE_INTRINSICS_) + // In case of positive overflow, detect it + XMVECTOR vOverflow = _mm_cmpgt_ps(V, g_XMMaxInt); + // Float to int conversion + __m128i vResulti = _mm_cvttps_epi32(V); + // If there was positive overflow, set to 0x7FFFFFFF + XMVECTOR vResult = _mm_and_ps(vOverflow, g_XMAbsMask); + vOverflow = _mm_andnot_ps(vOverflow, _mm_castsi128_ps(vResulti)); + vOverflow = _mm_or_ps(vOverflow, vResult); + _mm_storeu_si128(reinterpret_cast<__m128i*>(pDestination), _mm_castps_si128(vOverflow)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUInt4 +( + XMUINT4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + pDestination->x = static_cast(V.vector4_f32[0]); + pDestination->y = static_cast(V.vector4_f32[1]); + pDestination->z = static_cast(V.vector4_f32[2]); + pDestination->w = static_cast(V.vector4_f32[3]); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t v = vcvtq_u32_f32(V); + vst1q_u32(reinterpret_cast(pDestination), v); +#elif defined(_XM_SSE_INTRINSICS_) + // Clamp to >=0 + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + // Any numbers that are too big, set to 0xFFFFFFFFU + XMVECTOR vOverflow = _mm_cmpgt_ps(vResult, g_XMMaxUInt); + XMVECTOR vValue = g_XMUnsignedFix; + // Too large for a signed integer? + XMVECTOR vMask = _mm_cmpge_ps(vResult, vValue); + // Zero for number's lower than 0x80000000, 32768.0f*65536.0f otherwise + vValue = _mm_and_ps(vValue, vMask); + // Perform fixup only on numbers too large (Keeps low bit precision) + vResult = _mm_sub_ps(vResult, vValue); + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Convert from signed to unsigned pnly if greater than 0x80000000 + vMask = _mm_and_ps(vMask, g_XMNegativeZero); + vResult = _mm_xor_ps(_mm_castsi128_ps(vResulti), vMask); + // On those that are too large, set to 0xFFFFFFFF + vResult = _mm_or_ps(vResult, vOverflow); + _mm_storeu_si128(reinterpret_cast<__m128i*>(pDestination), _mm_castps_si128(vResult)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat3x3 +( + XMFLOAT3X3* pDestination, + FXMMATRIX M +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + pDestination->m[0][0] = M.r[0].vector4_f32[0]; + pDestination->m[0][1] = M.r[0].vector4_f32[1]; + pDestination->m[0][2] = M.r[0].vector4_f32[2]; + + pDestination->m[1][0] = M.r[1].vector4_f32[0]; + pDestination->m[1][1] = M.r[1].vector4_f32[1]; + pDestination->m[1][2] = M.r[1].vector4_f32[2]; + + pDestination->m[2][0] = M.r[2].vector4_f32[0]; + pDestination->m[2][1] = M.r[2].vector4_f32[1]; + pDestination->m[2][2] = M.r[2].vector4_f32[2]; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t T1 = vextq_f32(M.r[0], M.r[1], 1); + float32x4_t T2 = vbslq_f32(g_XMMask3, M.r[0], T1); + vst1q_f32(&pDestination->m[0][0], T2); + + T1 = vextq_f32(M.r[1], M.r[1], 1); + T2 = vcombine_f32(vget_low_f32(T1), vget_low_f32(M.r[2])); + vst1q_f32(&pDestination->m[1][1], T2); + + vst1q_lane_f32(&pDestination->m[2][2], M.r[2], 2); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp1 = M.r[0]; + XMVECTOR vTemp2 = M.r[1]; + XMVECTOR vTemp3 = M.r[2]; + XMVECTOR vWork = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(0, 0, 2, 2)); + vTemp1 = _mm_shuffle_ps(vTemp1, vWork, _MM_SHUFFLE(2, 0, 1, 0)); + _mm_storeu_ps(&pDestination->m[0][0], vTemp1); + vTemp2 = _mm_shuffle_ps(vTemp2, vTemp3, _MM_SHUFFLE(1, 0, 2, 1)); + _mm_storeu_ps(&pDestination->m[1][1], vTemp2); + vTemp3 = XM_PERMUTE_PS(vTemp3, _MM_SHUFFLE(2, 2, 2, 2)); + _mm_store_ss(&pDestination->m[2][2], vTemp3); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat4x3 +( + XMFLOAT4X3* pDestination, + FXMMATRIX M +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + pDestination->m[0][0] = M.r[0].vector4_f32[0]; + pDestination->m[0][1] = M.r[0].vector4_f32[1]; + pDestination->m[0][2] = M.r[0].vector4_f32[2]; + + pDestination->m[1][0] = M.r[1].vector4_f32[0]; + pDestination->m[1][1] = M.r[1].vector4_f32[1]; + pDestination->m[1][2] = M.r[1].vector4_f32[2]; + + pDestination->m[2][0] = M.r[2].vector4_f32[0]; + pDestination->m[2][1] = M.r[2].vector4_f32[1]; + pDestination->m[2][2] = M.r[2].vector4_f32[2]; + + pDestination->m[3][0] = M.r[3].vector4_f32[0]; + pDestination->m[3][1] = M.r[3].vector4_f32[1]; + pDestination->m[3][2] = M.r[3].vector4_f32[2]; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t T1 = vextq_f32(M.r[0], M.r[1], 1); + float32x4_t T2 = vbslq_f32(g_XMMask3, M.r[0], T1); + vst1q_f32(&pDestination->m[0][0], T2); + + T1 = vextq_f32(M.r[1], M.r[1], 1); + T2 = vcombine_f32(vget_low_f32(T1), vget_low_f32(M.r[2])); + vst1q_f32(&pDestination->m[1][1], T2); + + T1 = vdupq_lane_f32(vget_high_f32(M.r[2]), 0); + T2 = vextq_f32(T1, M.r[3], 3); + vst1q_f32(&pDestination->m[2][2], T2); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp1 = M.r[0]; + XMVECTOR vTemp2 = M.r[1]; + XMVECTOR vTemp3 = M.r[2]; + XMVECTOR vTemp4 = M.r[3]; + XMVECTOR vTemp2x = _mm_shuffle_ps(vTemp2, vTemp3, _MM_SHUFFLE(1, 0, 2, 1)); + vTemp2 = _mm_shuffle_ps(vTemp2, vTemp1, _MM_SHUFFLE(2, 2, 0, 0)); + vTemp1 = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(0, 2, 1, 0)); + vTemp3 = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(0, 0, 2, 2)); + vTemp3 = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 1, 2, 0)); + _mm_storeu_ps(&pDestination->m[0][0], vTemp1); + _mm_storeu_ps(&pDestination->m[1][1], vTemp2x); + _mm_storeu_ps(&pDestination->m[2][2], vTemp3); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat4x3A +( + XMFLOAT4X3A* pDestination, + FXMMATRIX M +) noexcept +{ + assert(pDestination); + assert((reinterpret_cast(pDestination) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + + pDestination->m[0][0] = M.r[0].vector4_f32[0]; + pDestination->m[0][1] = M.r[0].vector4_f32[1]; + pDestination->m[0][2] = M.r[0].vector4_f32[2]; + + pDestination->m[1][0] = M.r[1].vector4_f32[0]; + pDestination->m[1][1] = M.r[1].vector4_f32[1]; + pDestination->m[1][2] = M.r[1].vector4_f32[2]; + + pDestination->m[2][0] = M.r[2].vector4_f32[0]; + pDestination->m[2][1] = M.r[2].vector4_f32[1]; + pDestination->m[2][2] = M.r[2].vector4_f32[2]; + + pDestination->m[3][0] = M.r[3].vector4_f32[0]; + pDestination->m[3][1] = M.r[3].vector4_f32[1]; + pDestination->m[3][2] = M.r[3].vector4_f32[2]; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + float32x4_t T1 = vextq_f32(M.r[0], M.r[1], 1); + float32x4_t T2 = vbslq_f32(g_XMMask3, M.r[0], T1); + vst1q_f32_ex(&pDestination->m[0][0], T2, 128); + + T1 = vextq_f32(M.r[1], M.r[1], 1); + T2 = vcombine_f32(vget_low_f32(T1), vget_low_f32(M.r[2])); + vst1q_f32_ex(&pDestination->m[1][1], T2, 128); + + T1 = vdupq_lane_f32(vget_high_f32(M.r[2]), 0); + T2 = vextq_f32(T1, M.r[3], 3); + vst1q_f32_ex(&pDestination->m[2][2], T2, 128); +#else + float32x4_t T1 = vextq_f32(M.r[0], M.r[1], 1); + float32x4_t T2 = vbslq_f32(g_XMMask3, M.r[0], T1); + vst1q_f32(&pDestination->m[0][0], T2); + + T1 = vextq_f32(M.r[1], M.r[1], 1); + T2 = vcombine_f32(vget_low_f32(T1), vget_low_f32(M.r[2])); + vst1q_f32(&pDestination->m[1][1], T2); + + T1 = vdupq_lane_f32(vget_high_f32(M.r[2]), 0); + T2 = vextq_f32(T1, M.r[3], 3); + vst1q_f32(&pDestination->m[2][2], T2); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + // x1,y1,z1,w1 + XMVECTOR vTemp1 = M.r[0]; + // x2,y2,z2,w2 + XMVECTOR vTemp2 = M.r[1]; + // x3,y3,z3,w3 + XMVECTOR vTemp3 = M.r[2]; + // x4,y4,z4,w4 + XMVECTOR vTemp4 = M.r[3]; + // z1,z1,x2,y2 + XMVECTOR vTemp = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(1, 0, 2, 2)); + // y2,z2,x3,y3 (Final) + vTemp2 = _mm_shuffle_ps(vTemp2, vTemp3, _MM_SHUFFLE(1, 0, 2, 1)); + // x1,y1,z1,x2 (Final) + vTemp1 = _mm_shuffle_ps(vTemp1, vTemp, _MM_SHUFFLE(2, 0, 1, 0)); + // z3,z3,x4,x4 + vTemp3 = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(0, 0, 2, 2)); + // z3,x4,y4,z4 (Final) + vTemp3 = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 1, 2, 0)); + // Store in 3 operations + _mm_store_ps(&pDestination->m[0][0], vTemp1); + _mm_store_ps(&pDestination->m[1][1], vTemp2); + _mm_store_ps(&pDestination->m[2][2], vTemp3); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat3x4 +( + XMFLOAT3X4* pDestination, + FXMMATRIX M +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + pDestination->m[0][0] = M.r[0].vector4_f32[0]; + pDestination->m[0][1] = M.r[1].vector4_f32[0]; + pDestination->m[0][2] = M.r[2].vector4_f32[0]; + pDestination->m[0][3] = M.r[3].vector4_f32[0]; + + pDestination->m[1][0] = M.r[0].vector4_f32[1]; + pDestination->m[1][1] = M.r[1].vector4_f32[1]; + pDestination->m[1][2] = M.r[2].vector4_f32[1]; + pDestination->m[1][3] = M.r[3].vector4_f32[1]; + + pDestination->m[2][0] = M.r[0].vector4_f32[2]; + pDestination->m[2][1] = M.r[1].vector4_f32[2]; + pDestination->m[2][2] = M.r[2].vector4_f32[2]; + pDestination->m[2][3] = M.r[3].vector4_f32[2]; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4x2_t P0 = vzipq_f32(M.r[0], M.r[2]); + float32x4x2_t P1 = vzipq_f32(M.r[1], M.r[3]); + + float32x4x2_t T0 = vzipq_f32(P0.val[0], P1.val[0]); + float32x4x2_t T1 = vzipq_f32(P0.val[1], P1.val[1]); + + vst1q_f32(&pDestination->m[0][0], T0.val[0]); + vst1q_f32(&pDestination->m[1][0], T0.val[1]); + vst1q_f32(&pDestination->m[2][0], T1.val[0]); +#elif defined(_XM_SSE_INTRINSICS_) + // x.x,x.y,y.x,y.y + XMVECTOR vTemp1 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(1, 0, 1, 0)); + // x.z,x.w,y.z,y.w + XMVECTOR vTemp3 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(3, 2, 3, 2)); + // z.x,z.y,w.x,w.y + XMVECTOR vTemp2 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(1, 0, 1, 0)); + // z.z,z.w,w.z,w.w + XMVECTOR vTemp4 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(3, 2, 3, 2)); + + // x.x,y.x,z.x,w.x + XMVECTOR r0 = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + // x.y,y.y,z.y,w.y + XMVECTOR r1 = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + // x.z,y.z,z.z,w.z + XMVECTOR r2 = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + + _mm_storeu_ps(&pDestination->m[0][0], r0); + _mm_storeu_ps(&pDestination->m[1][0], r1); + _mm_storeu_ps(&pDestination->m[2][0], r2); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat3x4A +( + XMFLOAT3X4A* pDestination, + FXMMATRIX M +) noexcept +{ + assert(pDestination); + assert((reinterpret_cast(pDestination) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + + pDestination->m[0][0] = M.r[0].vector4_f32[0]; + pDestination->m[0][1] = M.r[1].vector4_f32[0]; + pDestination->m[0][2] = M.r[2].vector4_f32[0]; + pDestination->m[0][3] = M.r[3].vector4_f32[0]; + + pDestination->m[1][0] = M.r[0].vector4_f32[1]; + pDestination->m[1][1] = M.r[1].vector4_f32[1]; + pDestination->m[1][2] = M.r[2].vector4_f32[1]; + pDestination->m[1][3] = M.r[3].vector4_f32[1]; + + pDestination->m[2][0] = M.r[0].vector4_f32[2]; + pDestination->m[2][1] = M.r[1].vector4_f32[2]; + pDestination->m[2][2] = M.r[2].vector4_f32[2]; + pDestination->m[2][3] = M.r[3].vector4_f32[2]; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4x2_t P0 = vzipq_f32(M.r[0], M.r[2]); + float32x4x2_t P1 = vzipq_f32(M.r[1], M.r[3]); + + float32x4x2_t T0 = vzipq_f32(P0.val[0], P1.val[0]); + float32x4x2_t T1 = vzipq_f32(P0.val[1], P1.val[1]); + +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + vst1q_f32_ex(&pDestination->m[0][0], T0.val[0], 128); + vst1q_f32_ex(&pDestination->m[1][0], T0.val[1], 128); + vst1q_f32_ex(&pDestination->m[2][0], T1.val[0], 128); +#else + vst1q_f32(&pDestination->m[0][0], T0.val[0]); + vst1q_f32(&pDestination->m[1][0], T0.val[1]); + vst1q_f32(&pDestination->m[2][0], T1.val[0]); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + // x.x,x.y,y.x,y.y + XMVECTOR vTemp1 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(1, 0, 1, 0)); + // x.z,x.w,y.z,y.w + XMVECTOR vTemp3 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(3, 2, 3, 2)); + // z.x,z.y,w.x,w.y + XMVECTOR vTemp2 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(1, 0, 1, 0)); + // z.z,z.w,w.z,w.w + XMVECTOR vTemp4 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(3, 2, 3, 2)); + + // x.x,y.x,z.x,w.x + XMVECTOR r0 = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + // x.y,y.y,z.y,w.y + XMVECTOR r1 = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + // x.z,y.z,z.z,w.z + XMVECTOR r2 = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + + _mm_store_ps(&pDestination->m[0][0], r0); + _mm_store_ps(&pDestination->m[1][0], r1); + _mm_store_ps(&pDestination->m[2][0], r2); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat4x4 +( + XMFLOAT4X4* pDestination, + FXMMATRIX M +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + pDestination->m[0][0] = M.r[0].vector4_f32[0]; + pDestination->m[0][1] = M.r[0].vector4_f32[1]; + pDestination->m[0][2] = M.r[0].vector4_f32[2]; + pDestination->m[0][3] = M.r[0].vector4_f32[3]; + + pDestination->m[1][0] = M.r[1].vector4_f32[0]; + pDestination->m[1][1] = M.r[1].vector4_f32[1]; + pDestination->m[1][2] = M.r[1].vector4_f32[2]; + pDestination->m[1][3] = M.r[1].vector4_f32[3]; + + pDestination->m[2][0] = M.r[2].vector4_f32[0]; + pDestination->m[2][1] = M.r[2].vector4_f32[1]; + pDestination->m[2][2] = M.r[2].vector4_f32[2]; + pDestination->m[2][3] = M.r[2].vector4_f32[3]; + + pDestination->m[3][0] = M.r[3].vector4_f32[0]; + pDestination->m[3][1] = M.r[3].vector4_f32[1]; + pDestination->m[3][2] = M.r[3].vector4_f32[2]; + pDestination->m[3][3] = M.r[3].vector4_f32[3]; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_f32(reinterpret_cast(&pDestination->_11), M.r[0]); + vst1q_f32(reinterpret_cast(&pDestination->_21), M.r[1]); + vst1q_f32(reinterpret_cast(&pDestination->_31), M.r[2]); + vst1q_f32(reinterpret_cast(&pDestination->_41), M.r[3]); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_storeu_ps(&pDestination->_11, M.r[0]); + _mm_storeu_ps(&pDestination->_21, M.r[1]); + _mm_storeu_ps(&pDestination->_31, M.r[2]); + _mm_storeu_ps(&pDestination->_41, M.r[3]); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat4x4A +( + XMFLOAT4X4A* pDestination, + FXMMATRIX M +) noexcept +{ + assert(pDestination); + assert((reinterpret_cast(pDestination) & 0xF) == 0); +#if defined(_XM_NO_INTRINSICS_) + + pDestination->m[0][0] = M.r[0].vector4_f32[0]; + pDestination->m[0][1] = M.r[0].vector4_f32[1]; + pDestination->m[0][2] = M.r[0].vector4_f32[2]; + pDestination->m[0][3] = M.r[0].vector4_f32[3]; + + pDestination->m[1][0] = M.r[1].vector4_f32[0]; + pDestination->m[1][1] = M.r[1].vector4_f32[1]; + pDestination->m[1][2] = M.r[1].vector4_f32[2]; + pDestination->m[1][3] = M.r[1].vector4_f32[3]; + + pDestination->m[2][0] = M.r[2].vector4_f32[0]; + pDestination->m[2][1] = M.r[2].vector4_f32[1]; + pDestination->m[2][2] = M.r[2].vector4_f32[2]; + pDestination->m[2][3] = M.r[2].vector4_f32[3]; + + pDestination->m[3][0] = M.r[3].vector4_f32[0]; + pDestination->m[3][1] = M.r[3].vector4_f32[1]; + pDestination->m[3][2] = M.r[3].vector4_f32[2]; + pDestination->m[3][3] = M.r[3].vector4_f32[3]; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + vst1q_f32_ex(reinterpret_cast(&pDestination->_11), M.r[0], 128); + vst1q_f32_ex(reinterpret_cast(&pDestination->_21), M.r[1], 128); + vst1q_f32_ex(reinterpret_cast(&pDestination->_31), M.r[2], 128); + vst1q_f32_ex(reinterpret_cast(&pDestination->_41), M.r[3], 128); +#else + vst1q_f32(reinterpret_cast(&pDestination->_11), M.r[0]); + vst1q_f32(reinterpret_cast(&pDestination->_21), M.r[1]); + vst1q_f32(reinterpret_cast(&pDestination->_31), M.r[2]); + vst1q_f32(reinterpret_cast(&pDestination->_41), M.r[3]); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_ps(&pDestination->_11, M.r[0]); + _mm_store_ps(&pDestination->_21, M.r[1]); + _mm_store_ps(&pDestination->_31, M.r[2]); + _mm_store_ps(&pDestination->_41, M.r[3]); +#endif +} + diff --git a/Extern/dxmath/Inc/DirectXMathMatrix.inl b/Extern/dxmath/Inc/DirectXMathMatrix.inl new file mode 100644 index 000000000..36f4f9a74 --- /dev/null +++ b/Extern/dxmath/Inc/DirectXMathMatrix.inl @@ -0,0 +1,3554 @@ +//------------------------------------------------------------------------------------- +// DirectXMathMatrix.inl -- SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +/**************************************************************************** + * + * Matrix + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparison operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(push) +#pragma float_control(precise, on) +#endif + +// Return true if any entry in the matrix is NaN +inline bool XM_CALLCONV XMMatrixIsNaN(FXMMATRIX M) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + size_t i = 16; + auto pWork = reinterpret_cast(&M.m[0][0]); + do + { + // Fetch value into integer unit + uint32_t uTest = pWork[0]; + // Remove sign + uTest &= 0x7FFFFFFFU; + // NaN is 0x7F800001 through 0x7FFFFFFF inclusive + uTest -= 0x7F800001U; + if (uTest < 0x007FFFFFU) + { + break; // NaN found + } + ++pWork; // Next entry + } + while (--i); + return (i != 0); // i == 0 if nothing matched +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Load in registers + float32x4_t vX = M.r[0]; + float32x4_t vY = M.r[1]; + float32x4_t vZ = M.r[2]; + float32x4_t vW = M.r[3]; + // Test themselves to check for NaN + uint32x4_t xmask = vmvnq_u32(vceqq_f32(vX, vX)); + uint32x4_t ymask = vmvnq_u32(vceqq_f32(vY, vY)); + uint32x4_t zmask = vmvnq_u32(vceqq_f32(vZ, vZ)); + uint32x4_t wmask = vmvnq_u32(vceqq_f32(vW, vW)); + // Or all the results + xmask = vorrq_u32(xmask, zmask); + ymask = vorrq_u32(ymask, wmask); + xmask = vorrq_u32(xmask, ymask); + // If any tested true, return true + uint8x8x2_t vTemp = vzip_u8( + vget_low_u8(vreinterpretq_u8_u32(xmask)), + vget_high_u8(vreinterpretq_u8_u32(xmask))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + return (r != 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Load in registers + XMVECTOR vX = M.r[0]; + XMVECTOR vY = M.r[1]; + XMVECTOR vZ = M.r[2]; + XMVECTOR vW = M.r[3]; + // Test themselves to check for NaN + vX = _mm_cmpneq_ps(vX, vX); + vY = _mm_cmpneq_ps(vY, vY); + vZ = _mm_cmpneq_ps(vZ, vZ); + vW = _mm_cmpneq_ps(vW, vW); + // Or all the results + vX = _mm_or_ps(vX, vZ); + vY = _mm_or_ps(vY, vW); + vX = _mm_or_ps(vX, vY); + // If any tested true, return true + return (_mm_movemask_ps(vX) != 0); +#else +#endif +} + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(pop) +#endif + +//------------------------------------------------------------------------------ + +// Return true if any entry in the matrix is +/-INF +inline bool XM_CALLCONV XMMatrixIsInfinite(FXMMATRIX M) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + size_t i = 16; + auto pWork = reinterpret_cast(&M.m[0][0]); + do + { + // Fetch value into integer unit + uint32_t uTest = pWork[0]; + // Remove sign + uTest &= 0x7FFFFFFFU; + // INF is 0x7F800000 + if (uTest == 0x7F800000U) + { + break; // INF found + } + ++pWork; // Next entry + } + while (--i); + return (i != 0); // i == 0 if nothing matched +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Load in registers + float32x4_t vX = M.r[0]; + float32x4_t vY = M.r[1]; + float32x4_t vZ = M.r[2]; + float32x4_t vW = M.r[3]; + // Mask off the sign bits + vX = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(vX), g_XMAbsMask)); + vY = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(vY), g_XMAbsMask)); + vZ = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(vZ), g_XMAbsMask)); + vW = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(vW), g_XMAbsMask)); + // Compare to infinity + uint32x4_t xmask = vceqq_f32(vX, g_XMInfinity); + uint32x4_t ymask = vceqq_f32(vY, g_XMInfinity); + uint32x4_t zmask = vceqq_f32(vZ, g_XMInfinity); + uint32x4_t wmask = vceqq_f32(vW, g_XMInfinity); + // Or the answers together + xmask = vorrq_u32(xmask, zmask); + ymask = vorrq_u32(ymask, wmask); + xmask = vorrq_u32(xmask, ymask); + // If any tested true, return true + uint8x8x2_t vTemp = vzip_u8( + vget_low_u8(vreinterpretq_u8_u32(xmask)), + vget_high_u8(vreinterpretq_u8_u32(xmask))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + return (r != 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Mask off the sign bits + XMVECTOR vTemp1 = _mm_and_ps(M.r[0], g_XMAbsMask); + XMVECTOR vTemp2 = _mm_and_ps(M.r[1], g_XMAbsMask); + XMVECTOR vTemp3 = _mm_and_ps(M.r[2], g_XMAbsMask); + XMVECTOR vTemp4 = _mm_and_ps(M.r[3], g_XMAbsMask); + // Compare to infinity + vTemp1 = _mm_cmpeq_ps(vTemp1, g_XMInfinity); + vTemp2 = _mm_cmpeq_ps(vTemp2, g_XMInfinity); + vTemp3 = _mm_cmpeq_ps(vTemp3, g_XMInfinity); + vTemp4 = _mm_cmpeq_ps(vTemp4, g_XMInfinity); + // Or the answers together + vTemp1 = _mm_or_ps(vTemp1, vTemp2); + vTemp3 = _mm_or_ps(vTemp3, vTemp4); + vTemp1 = _mm_or_ps(vTemp1, vTemp3); + // If any are infinity, the signs are true. + return (_mm_movemask_ps(vTemp1) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +// Return true if the XMMatrix is equal to identity +inline bool XM_CALLCONV XMMatrixIsIdentity(FXMMATRIX M) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + // Use the integer pipeline to reduce branching to a minimum + auto pWork = reinterpret_cast(&M.m[0][0]); + // Convert 1.0f to zero and or them together + uint32_t uOne = pWork[0] ^ 0x3F800000U; + // Or all the 0.0f entries together + uint32_t uZero = pWork[1]; + uZero |= pWork[2]; + uZero |= pWork[3]; + // 2nd row + uZero |= pWork[4]; + uOne |= pWork[5] ^ 0x3F800000U; + uZero |= pWork[6]; + uZero |= pWork[7]; + // 3rd row + uZero |= pWork[8]; + uZero |= pWork[9]; + uOne |= pWork[10] ^ 0x3F800000U; + uZero |= pWork[11]; + // 4th row + uZero |= pWork[12]; + uZero |= pWork[13]; + uZero |= pWork[14]; + uOne |= pWork[15] ^ 0x3F800000U; + // If all zero entries are zero, the uZero==0 + uZero &= 0x7FFFFFFF; // Allow -0.0f + // If all 1.0f entries are 1.0f, then uOne==0 + uOne |= uZero; + return (uOne == 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t xmask = vceqq_f32(M.r[0], g_XMIdentityR0); + uint32x4_t ymask = vceqq_f32(M.r[1], g_XMIdentityR1); + uint32x4_t zmask = vceqq_f32(M.r[2], g_XMIdentityR2); + uint32x4_t wmask = vceqq_f32(M.r[3], g_XMIdentityR3); + xmask = vandq_u32(xmask, zmask); + ymask = vandq_u32(ymask, wmask); + xmask = vandq_u32(xmask, ymask); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(xmask)), vget_high_u8(vreinterpretq_u8_u32(xmask))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + return (r == 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp1 = _mm_cmpeq_ps(M.r[0], g_XMIdentityR0); + XMVECTOR vTemp2 = _mm_cmpeq_ps(M.r[1], g_XMIdentityR1); + XMVECTOR vTemp3 = _mm_cmpeq_ps(M.r[2], g_XMIdentityR2); + XMVECTOR vTemp4 = _mm_cmpeq_ps(M.r[3], g_XMIdentityR3); + vTemp1 = _mm_and_ps(vTemp1, vTemp2); + vTemp3 = _mm_and_ps(vTemp3, vTemp4); + vTemp1 = _mm_and_ps(vTemp1, vTemp3); + return (_mm_movemask_ps(vTemp1) == 0x0f); +#endif +} + +//------------------------------------------------------------------------------ +// Computation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Perform a 4x4 matrix multiply by a 4x4 matrix +inline XMMATRIX XM_CALLCONV XMMatrixMultiply +( + FXMMATRIX M1, + CXMMATRIX M2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMMATRIX mResult; + // Cache the invariants in registers + float x = M1.m[0][0]; + float y = M1.m[0][1]; + float z = M1.m[0][2]; + float w = M1.m[0][3]; + // Perform the operation on the first row + mResult.m[0][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w); + mResult.m[0][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w); + mResult.m[0][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w); + mResult.m[0][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w); + // Repeat for all the other rows + x = M1.m[1][0]; + y = M1.m[1][1]; + z = M1.m[1][2]; + w = M1.m[1][3]; + mResult.m[1][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w); + mResult.m[1][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w); + mResult.m[1][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w); + mResult.m[1][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w); + x = M1.m[2][0]; + y = M1.m[2][1]; + z = M1.m[2][2]; + w = M1.m[2][3]; + mResult.m[2][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w); + mResult.m[2][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w); + mResult.m[2][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w); + mResult.m[2][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w); + x = M1.m[3][0]; + y = M1.m[3][1]; + z = M1.m[3][2]; + w = M1.m[3][3]; + mResult.m[3][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w); + mResult.m[3][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w); + mResult.m[3][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w); + mResult.m[3][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w); + return mResult; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMMATRIX mResult; + float32x2_t VL = vget_low_f32(M1.r[0]); + float32x2_t VH = vget_high_f32(M1.r[0]); + // Perform the operation on the first row + float32x4_t vX = vmulq_lane_f32(M2.r[0], VL, 0); + float32x4_t vY = vmulq_lane_f32(M2.r[1], VL, 1); + float32x4_t vZ = vmlaq_lane_f32(vX, M2.r[2], VH, 0); + float32x4_t vW = vmlaq_lane_f32(vY, M2.r[3], VH, 1); + mResult.r[0] = vaddq_f32(vZ, vW); + // Repeat for the other 3 rows + VL = vget_low_f32(M1.r[1]); + VH = vget_high_f32(M1.r[1]); + vX = vmulq_lane_f32(M2.r[0], VL, 0); + vY = vmulq_lane_f32(M2.r[1], VL, 1); + vZ = vmlaq_lane_f32(vX, M2.r[2], VH, 0); + vW = vmlaq_lane_f32(vY, M2.r[3], VH, 1); + mResult.r[1] = vaddq_f32(vZ, vW); + VL = vget_low_f32(M1.r[2]); + VH = vget_high_f32(M1.r[2]); + vX = vmulq_lane_f32(M2.r[0], VL, 0); + vY = vmulq_lane_f32(M2.r[1], VL, 1); + vZ = vmlaq_lane_f32(vX, M2.r[2], VH, 0); + vW = vmlaq_lane_f32(vY, M2.r[3], VH, 1); + mResult.r[2] = vaddq_f32(vZ, vW); + VL = vget_low_f32(M1.r[3]); + VH = vget_high_f32(M1.r[3]); + vX = vmulq_lane_f32(M2.r[0], VL, 0); + vY = vmulq_lane_f32(M2.r[1], VL, 1); + vZ = vmlaq_lane_f32(vX, M2.r[2], VH, 0); + vW = vmlaq_lane_f32(vY, M2.r[3], VH, 1); + mResult.r[3] = vaddq_f32(vZ, vW); + return mResult; +#elif defined(_XM_AVX2_INTRINSICS_) + __m256 t0 = _mm256_castps128_ps256(M1.r[0]); + t0 = _mm256_insertf128_ps(t0, M1.r[1], 1); + __m256 t1 = _mm256_castps128_ps256(M1.r[2]); + t1 = _mm256_insertf128_ps(t1, M1.r[3], 1); + + __m256 u0 = _mm256_castps128_ps256(M2.r[0]); + u0 = _mm256_insertf128_ps(u0, M2.r[1], 1); + __m256 u1 = _mm256_castps128_ps256(M2.r[2]); + u1 = _mm256_insertf128_ps(u1, M2.r[3], 1); + + __m256 a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(0, 0, 0, 0)); + __m256 a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(0, 0, 0, 0)); + __m256 b0 = _mm256_permute2f128_ps(u0, u0, 0x00); + __m256 c0 = _mm256_mul_ps(a0, b0); + __m256 c1 = _mm256_mul_ps(a1, b0); + + a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(1, 1, 1, 1)); + a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(1, 1, 1, 1)); + b0 = _mm256_permute2f128_ps(u0, u0, 0x11); + __m256 c2 = _mm256_fmadd_ps(a0, b0, c0); + __m256 c3 = _mm256_fmadd_ps(a1, b0, c1); + + a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(2, 2, 2, 2)); + a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 b1 = _mm256_permute2f128_ps(u1, u1, 0x00); + __m256 c4 = _mm256_mul_ps(a0, b1); + __m256 c5 = _mm256_mul_ps(a1, b1); + + a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(3, 3, 3, 3)); + a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(3, 3, 3, 3)); + b1 = _mm256_permute2f128_ps(u1, u1, 0x11); + __m256 c6 = _mm256_fmadd_ps(a0, b1, c4); + __m256 c7 = _mm256_fmadd_ps(a1, b1, c5); + + t0 = _mm256_add_ps(c2, c6); + t1 = _mm256_add_ps(c3, c7); + + XMMATRIX mResult; + mResult.r[0] = _mm256_castps256_ps128(t0); + mResult.r[1] = _mm256_extractf128_ps(t0, 1); + mResult.r[2] = _mm256_castps256_ps128(t1); + mResult.r[3] = _mm256_extractf128_ps(t1, 1); + return mResult; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX mResult; + // Splat the component X,Y,Z then W +#if defined(_XM_AVX_INTRINSICS_) + XMVECTOR vX = _mm_broadcast_ss(reinterpret_cast(&M1.r[0]) + 0); + XMVECTOR vY = _mm_broadcast_ss(reinterpret_cast(&M1.r[0]) + 1); + XMVECTOR vZ = _mm_broadcast_ss(reinterpret_cast(&M1.r[0]) + 2); + XMVECTOR vW = _mm_broadcast_ss(reinterpret_cast(&M1.r[0]) + 3); +#else + // Use vW to hold the original row + XMVECTOR vW = M1.r[0]; + XMVECTOR vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3)); +#endif + // Perform the operation on the first row + vX = _mm_mul_ps(vX, M2.r[0]); + vY = _mm_mul_ps(vY, M2.r[1]); + vZ = _mm_mul_ps(vZ, M2.r[2]); + vW = _mm_mul_ps(vW, M2.r[3]); + // Perform a binary add to reduce cumulative errors + vX = _mm_add_ps(vX, vZ); + vY = _mm_add_ps(vY, vW); + vX = _mm_add_ps(vX, vY); + mResult.r[0] = vX; + // Repeat for the other 3 rows +#if defined(_XM_AVX_INTRINSICS_) + vX = _mm_broadcast_ss(reinterpret_cast(&M1.r[1]) + 0); + vY = _mm_broadcast_ss(reinterpret_cast(&M1.r[1]) + 1); + vZ = _mm_broadcast_ss(reinterpret_cast(&M1.r[1]) + 2); + vW = _mm_broadcast_ss(reinterpret_cast(&M1.r[1]) + 3); +#else + vW = M1.r[1]; + vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3)); +#endif + vX = _mm_mul_ps(vX, M2.r[0]); + vY = _mm_mul_ps(vY, M2.r[1]); + vZ = _mm_mul_ps(vZ, M2.r[2]); + vW = _mm_mul_ps(vW, M2.r[3]); + vX = _mm_add_ps(vX, vZ); + vY = _mm_add_ps(vY, vW); + vX = _mm_add_ps(vX, vY); + mResult.r[1] = vX; +#if defined(_XM_AVX_INTRINSICS_) + vX = _mm_broadcast_ss(reinterpret_cast(&M1.r[2]) + 0); + vY = _mm_broadcast_ss(reinterpret_cast(&M1.r[2]) + 1); + vZ = _mm_broadcast_ss(reinterpret_cast(&M1.r[2]) + 2); + vW = _mm_broadcast_ss(reinterpret_cast(&M1.r[2]) + 3); +#else + vW = M1.r[2]; + vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3)); +#endif + vX = _mm_mul_ps(vX, M2.r[0]); + vY = _mm_mul_ps(vY, M2.r[1]); + vZ = _mm_mul_ps(vZ, M2.r[2]); + vW = _mm_mul_ps(vW, M2.r[3]); + vX = _mm_add_ps(vX, vZ); + vY = _mm_add_ps(vY, vW); + vX = _mm_add_ps(vX, vY); + mResult.r[2] = vX; +#if defined(_XM_AVX_INTRINSICS_) + vX = _mm_broadcast_ss(reinterpret_cast(&M1.r[3]) + 0); + vY = _mm_broadcast_ss(reinterpret_cast(&M1.r[3]) + 1); + vZ = _mm_broadcast_ss(reinterpret_cast(&M1.r[3]) + 2); + vW = _mm_broadcast_ss(reinterpret_cast(&M1.r[3]) + 3); +#else + vW = M1.r[3]; + vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3)); +#endif + vX = _mm_mul_ps(vX, M2.r[0]); + vY = _mm_mul_ps(vY, M2.r[1]); + vZ = _mm_mul_ps(vZ, M2.r[2]); + vW = _mm_mul_ps(vW, M2.r[3]); + vX = _mm_add_ps(vX, vZ); + vY = _mm_add_ps(vY, vW); + vX = _mm_add_ps(vX, vY); + mResult.r[3] = vX; + return mResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixMultiplyTranspose +( + FXMMATRIX M1, + CXMMATRIX M2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMMATRIX mResult; + // Cache the invariants in registers + float x = M2.m[0][0]; + float y = M2.m[1][0]; + float z = M2.m[2][0]; + float w = M2.m[3][0]; + // Perform the operation on the first row + mResult.m[0][0] = (M1.m[0][0] * x) + (M1.m[0][1] * y) + (M1.m[0][2] * z) + (M1.m[0][3] * w); + mResult.m[0][1] = (M1.m[1][0] * x) + (M1.m[1][1] * y) + (M1.m[1][2] * z) + (M1.m[1][3] * w); + mResult.m[0][2] = (M1.m[2][0] * x) + (M1.m[2][1] * y) + (M1.m[2][2] * z) + (M1.m[2][3] * w); + mResult.m[0][3] = (M1.m[3][0] * x) + (M1.m[3][1] * y) + (M1.m[3][2] * z) + (M1.m[3][3] * w); + // Repeat for all the other rows + x = M2.m[0][1]; + y = M2.m[1][1]; + z = M2.m[2][1]; + w = M2.m[3][1]; + mResult.m[1][0] = (M1.m[0][0] * x) + (M1.m[0][1] * y) + (M1.m[0][2] * z) + (M1.m[0][3] * w); + mResult.m[1][1] = (M1.m[1][0] * x) + (M1.m[1][1] * y) + (M1.m[1][2] * z) + (M1.m[1][3] * w); + mResult.m[1][2] = (M1.m[2][0] * x) + (M1.m[2][1] * y) + (M1.m[2][2] * z) + (M1.m[2][3] * w); + mResult.m[1][3] = (M1.m[3][0] * x) + (M1.m[3][1] * y) + (M1.m[3][2] * z) + (M1.m[3][3] * w); + x = M2.m[0][2]; + y = M2.m[1][2]; + z = M2.m[2][2]; + w = M2.m[3][2]; + mResult.m[2][0] = (M1.m[0][0] * x) + (M1.m[0][1] * y) + (M1.m[0][2] * z) + (M1.m[0][3] * w); + mResult.m[2][1] = (M1.m[1][0] * x) + (M1.m[1][1] * y) + (M1.m[1][2] * z) + (M1.m[1][3] * w); + mResult.m[2][2] = (M1.m[2][0] * x) + (M1.m[2][1] * y) + (M1.m[2][2] * z) + (M1.m[2][3] * w); + mResult.m[2][3] = (M1.m[3][0] * x) + (M1.m[3][1] * y) + (M1.m[3][2] * z) + (M1.m[3][3] * w); + x = M2.m[0][3]; + y = M2.m[1][3]; + z = M2.m[2][3]; + w = M2.m[3][3]; + mResult.m[3][0] = (M1.m[0][0] * x) + (M1.m[0][1] * y) + (M1.m[0][2] * z) + (M1.m[0][3] * w); + mResult.m[3][1] = (M1.m[1][0] * x) + (M1.m[1][1] * y) + (M1.m[1][2] * z) + (M1.m[1][3] * w); + mResult.m[3][2] = (M1.m[2][0] * x) + (M1.m[2][1] * y) + (M1.m[2][2] * z) + (M1.m[2][3] * w); + mResult.m[3][3] = (M1.m[3][0] * x) + (M1.m[3][1] * y) + (M1.m[3][2] * z) + (M1.m[3][3] * w); + return mResult; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(M1.r[0]); + float32x2_t VH = vget_high_f32(M1.r[0]); + // Perform the operation on the first row + float32x4_t vX = vmulq_lane_f32(M2.r[0], VL, 0); + float32x4_t vY = vmulq_lane_f32(M2.r[1], VL, 1); + float32x4_t vZ = vmlaq_lane_f32(vX, M2.r[2], VH, 0); + float32x4_t vW = vmlaq_lane_f32(vY, M2.r[3], VH, 1); + float32x4_t r0 = vaddq_f32(vZ, vW); + // Repeat for the other 3 rows + VL = vget_low_f32(M1.r[1]); + VH = vget_high_f32(M1.r[1]); + vX = vmulq_lane_f32(M2.r[0], VL, 0); + vY = vmulq_lane_f32(M2.r[1], VL, 1); + vZ = vmlaq_lane_f32(vX, M2.r[2], VH, 0); + vW = vmlaq_lane_f32(vY, M2.r[3], VH, 1); + float32x4_t r1 = vaddq_f32(vZ, vW); + VL = vget_low_f32(M1.r[2]); + VH = vget_high_f32(M1.r[2]); + vX = vmulq_lane_f32(M2.r[0], VL, 0); + vY = vmulq_lane_f32(M2.r[1], VL, 1); + vZ = vmlaq_lane_f32(vX, M2.r[2], VH, 0); + vW = vmlaq_lane_f32(vY, M2.r[3], VH, 1); + float32x4_t r2 = vaddq_f32(vZ, vW); + VL = vget_low_f32(M1.r[3]); + VH = vget_high_f32(M1.r[3]); + vX = vmulq_lane_f32(M2.r[0], VL, 0); + vY = vmulq_lane_f32(M2.r[1], VL, 1); + vZ = vmlaq_lane_f32(vX, M2.r[2], VH, 0); + vW = vmlaq_lane_f32(vY, M2.r[3], VH, 1); + float32x4_t r3 = vaddq_f32(vZ, vW); + + // Transpose result + float32x4x2_t P0 = vzipq_f32(r0, r2); + float32x4x2_t P1 = vzipq_f32(r1, r3); + + float32x4x2_t T0 = vzipq_f32(P0.val[0], P1.val[0]); + float32x4x2_t T1 = vzipq_f32(P0.val[1], P1.val[1]); + + XMMATRIX mResult; + mResult.r[0] = T0.val[0]; + mResult.r[1] = T0.val[1]; + mResult.r[2] = T1.val[0]; + mResult.r[3] = T1.val[1]; + return mResult; +#elif defined(_XM_AVX2_INTRINSICS_) + __m256 t0 = _mm256_castps128_ps256(M1.r[0]); + t0 = _mm256_insertf128_ps(t0, M1.r[1], 1); + __m256 t1 = _mm256_castps128_ps256(M1.r[2]); + t1 = _mm256_insertf128_ps(t1, M1.r[3], 1); + + __m256 u0 = _mm256_castps128_ps256(M2.r[0]); + u0 = _mm256_insertf128_ps(u0, M2.r[1], 1); + __m256 u1 = _mm256_castps128_ps256(M2.r[2]); + u1 = _mm256_insertf128_ps(u1, M2.r[3], 1); + + __m256 a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(0, 0, 0, 0)); + __m256 a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(0, 0, 0, 0)); + __m256 b0 = _mm256_permute2f128_ps(u0, u0, 0x00); + __m256 c0 = _mm256_mul_ps(a0, b0); + __m256 c1 = _mm256_mul_ps(a1, b0); + + a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(1, 1, 1, 1)); + a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(1, 1, 1, 1)); + b0 = _mm256_permute2f128_ps(u0, u0, 0x11); + __m256 c2 = _mm256_fmadd_ps(a0, b0, c0); + __m256 c3 = _mm256_fmadd_ps(a1, b0, c1); + + a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(2, 2, 2, 2)); + a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 b1 = _mm256_permute2f128_ps(u1, u1, 0x00); + __m256 c4 = _mm256_mul_ps(a0, b1); + __m256 c5 = _mm256_mul_ps(a1, b1); + + a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(3, 3, 3, 3)); + a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(3, 3, 3, 3)); + b1 = _mm256_permute2f128_ps(u1, u1, 0x11); + __m256 c6 = _mm256_fmadd_ps(a0, b1, c4); + __m256 c7 = _mm256_fmadd_ps(a1, b1, c5); + + t0 = _mm256_add_ps(c2, c6); + t1 = _mm256_add_ps(c3, c7); + + // Transpose result + __m256 vTemp = _mm256_unpacklo_ps(t0, t1); + __m256 vTemp2 = _mm256_unpackhi_ps(t0, t1); + __m256 vTemp3 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x20); + __m256 vTemp4 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x31); + vTemp = _mm256_unpacklo_ps(vTemp3, vTemp4); + vTemp2 = _mm256_unpackhi_ps(vTemp3, vTemp4); + t0 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x20); + t1 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x31); + + XMMATRIX mResult; + mResult.r[0] = _mm256_castps256_ps128(t0); + mResult.r[1] = _mm256_extractf128_ps(t0, 1); + mResult.r[2] = _mm256_castps256_ps128(t1); + mResult.r[3] = _mm256_extractf128_ps(t1, 1); + return mResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Splat the component X,Y,Z then W +#if defined(_XM_AVX_INTRINSICS_) + XMVECTOR vX = _mm_broadcast_ss(reinterpret_cast(&M1.r[0]) + 0); + XMVECTOR vY = _mm_broadcast_ss(reinterpret_cast(&M1.r[0]) + 1); + XMVECTOR vZ = _mm_broadcast_ss(reinterpret_cast(&M1.r[0]) + 2); + XMVECTOR vW = _mm_broadcast_ss(reinterpret_cast(&M1.r[0]) + 3); +#else + // Use vW to hold the original row + XMVECTOR vW = M1.r[0]; + XMVECTOR vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3)); +#endif + // Perform the operation on the first row + vX = _mm_mul_ps(vX, M2.r[0]); + vY = _mm_mul_ps(vY, M2.r[1]); + vZ = _mm_mul_ps(vZ, M2.r[2]); + vW = _mm_mul_ps(vW, M2.r[3]); + // Perform a binary add to reduce cumulative errors + vX = _mm_add_ps(vX, vZ); + vY = _mm_add_ps(vY, vW); + vX = _mm_add_ps(vX, vY); + XMVECTOR r0 = vX; + // Repeat for the other 3 rows +#if defined(_XM_AVX_INTRINSICS_) + vX = _mm_broadcast_ss(reinterpret_cast(&M1.r[1]) + 0); + vY = _mm_broadcast_ss(reinterpret_cast(&M1.r[1]) + 1); + vZ = _mm_broadcast_ss(reinterpret_cast(&M1.r[1]) + 2); + vW = _mm_broadcast_ss(reinterpret_cast(&M1.r[1]) + 3); +#else + vW = M1.r[1]; + vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3)); +#endif + vX = _mm_mul_ps(vX, M2.r[0]); + vY = _mm_mul_ps(vY, M2.r[1]); + vZ = _mm_mul_ps(vZ, M2.r[2]); + vW = _mm_mul_ps(vW, M2.r[3]); + vX = _mm_add_ps(vX, vZ); + vY = _mm_add_ps(vY, vW); + vX = _mm_add_ps(vX, vY); + XMVECTOR r1 = vX; +#if defined(_XM_AVX_INTRINSICS_) + vX = _mm_broadcast_ss(reinterpret_cast(&M1.r[2]) + 0); + vY = _mm_broadcast_ss(reinterpret_cast(&M1.r[2]) + 1); + vZ = _mm_broadcast_ss(reinterpret_cast(&M1.r[2]) + 2); + vW = _mm_broadcast_ss(reinterpret_cast(&M1.r[2]) + 3); +#else + vW = M1.r[2]; + vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3)); +#endif + vX = _mm_mul_ps(vX, M2.r[0]); + vY = _mm_mul_ps(vY, M2.r[1]); + vZ = _mm_mul_ps(vZ, M2.r[2]); + vW = _mm_mul_ps(vW, M2.r[3]); + vX = _mm_add_ps(vX, vZ); + vY = _mm_add_ps(vY, vW); + vX = _mm_add_ps(vX, vY); + XMVECTOR r2 = vX; +#if defined(_XM_AVX_INTRINSICS_) + vX = _mm_broadcast_ss(reinterpret_cast(&M1.r[3]) + 0); + vY = _mm_broadcast_ss(reinterpret_cast(&M1.r[3]) + 1); + vZ = _mm_broadcast_ss(reinterpret_cast(&M1.r[3]) + 2); + vW = _mm_broadcast_ss(reinterpret_cast(&M1.r[3]) + 3); +#else + vW = M1.r[3]; + vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0)); + vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1)); + vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2)); + vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3)); +#endif + vX = _mm_mul_ps(vX, M2.r[0]); + vY = _mm_mul_ps(vY, M2.r[1]); + vZ = _mm_mul_ps(vZ, M2.r[2]); + vW = _mm_mul_ps(vW, M2.r[3]); + vX = _mm_add_ps(vX, vZ); + vY = _mm_add_ps(vY, vW); + vX = _mm_add_ps(vX, vY); + XMVECTOR r3 = vX; + + // Transpose result + // x.x,x.y,y.x,y.y + XMVECTOR vTemp1 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(1, 0, 1, 0)); + // x.z,x.w,y.z,y.w + XMVECTOR vTemp3 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(3, 2, 3, 2)); + // z.x,z.y,w.x,w.y + XMVECTOR vTemp2 = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(1, 0, 1, 0)); + // z.z,z.w,w.z,w.w + XMVECTOR vTemp4 = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(3, 2, 3, 2)); + + XMMATRIX mResult; + // x.x,y.x,z.x,w.x + mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + // x.y,y.y,z.y,w.y + mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + // x.z,y.z,z.z,w.z + mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + // x.w,y.w,z.w,w.w + mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1)); + return mResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixTranspose(FXMMATRIX M) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + // Original matrix: + // + // m00m01m02m03 + // m10m11m12m13 + // m20m21m22m23 + // m30m31m32m33 + + XMMATRIX P; + P.r[0] = XMVectorMergeXY(M.r[0], M.r[2]); // m00m20m01m21 + P.r[1] = XMVectorMergeXY(M.r[1], M.r[3]); // m10m30m11m31 + P.r[2] = XMVectorMergeZW(M.r[0], M.r[2]); // m02m22m03m23 + P.r[3] = XMVectorMergeZW(M.r[1], M.r[3]); // m12m32m13m33 + + XMMATRIX MT; + MT.r[0] = XMVectorMergeXY(P.r[0], P.r[1]); // m00m10m20m30 + MT.r[1] = XMVectorMergeZW(P.r[0], P.r[1]); // m01m11m21m31 + MT.r[2] = XMVectorMergeXY(P.r[2], P.r[3]); // m02m12m22m32 + MT.r[3] = XMVectorMergeZW(P.r[2], P.r[3]); // m03m13m23m33 + return MT; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4x2_t P0 = vzipq_f32(M.r[0], M.r[2]); + float32x4x2_t P1 = vzipq_f32(M.r[1], M.r[3]); + + float32x4x2_t T0 = vzipq_f32(P0.val[0], P1.val[0]); + float32x4x2_t T1 = vzipq_f32(P0.val[1], P1.val[1]); + + XMMATRIX mResult; + mResult.r[0] = T0.val[0]; + mResult.r[1] = T0.val[1]; + mResult.r[2] = T1.val[0]; + mResult.r[3] = T1.val[1]; + return mResult; +#elif defined(_XM_AVX2_INTRINSICS_) + __m256 t0 = _mm256_castps128_ps256(M.r[0]); + t0 = _mm256_insertf128_ps(t0, M.r[1], 1); + __m256 t1 = _mm256_castps128_ps256(M.r[2]); + t1 = _mm256_insertf128_ps(t1, M.r[3], 1); + + __m256 vTemp = _mm256_unpacklo_ps(t0, t1); + __m256 vTemp2 = _mm256_unpackhi_ps(t0, t1); + __m256 vTemp3 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x20); + __m256 vTemp4 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x31); + vTemp = _mm256_unpacklo_ps(vTemp3, vTemp4); + vTemp2 = _mm256_unpackhi_ps(vTemp3, vTemp4); + t0 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x20); + t1 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x31); + + XMMATRIX mResult; + mResult.r[0] = _mm256_castps256_ps128(t0); + mResult.r[1] = _mm256_extractf128_ps(t0, 1); + mResult.r[2] = _mm256_castps256_ps128(t1); + mResult.r[3] = _mm256_extractf128_ps(t1, 1); + return mResult; +#elif defined(_XM_SSE_INTRINSICS_) + // x.x,x.y,y.x,y.y + XMVECTOR vTemp1 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(1, 0, 1, 0)); + // x.z,x.w,y.z,y.w + XMVECTOR vTemp3 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(3, 2, 3, 2)); + // z.x,z.y,w.x,w.y + XMVECTOR vTemp2 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(1, 0, 1, 0)); + // z.z,z.w,w.z,w.w + XMVECTOR vTemp4 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(3, 2, 3, 2)); + + XMMATRIX mResult; + // x.x,y.x,z.x,w.x + mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + // x.y,y.y,z.y,w.y + mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + // x.z,y.z,z.z,w.z + mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + // x.w,y.w,z.w,w.w + mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1)); + return mResult; +#endif +} + +//------------------------------------------------------------------------------ +// Return the inverse and the determinant of a 4x4 matrix +_Use_decl_annotations_ +inline XMMATRIX XM_CALLCONV XMMatrixInverse +( + XMVECTOR* pDeterminant, + FXMMATRIX M +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + + XMMATRIX MT = XMMatrixTranspose(M); + + XMVECTOR V0[4], V1[4]; + V0[0] = XMVectorSwizzle(MT.r[2]); + V1[0] = XMVectorSwizzle(MT.r[3]); + V0[1] = XMVectorSwizzle(MT.r[0]); + V1[1] = XMVectorSwizzle(MT.r[1]); + V0[2] = XMVectorPermute(MT.r[2], MT.r[0]); + V1[2] = XMVectorPermute(MT.r[3], MT.r[1]); + + XMVECTOR D0 = XMVectorMultiply(V0[0], V1[0]); + XMVECTOR D1 = XMVectorMultiply(V0[1], V1[1]); + XMVECTOR D2 = XMVectorMultiply(V0[2], V1[2]); + + V0[0] = XMVectorSwizzle(MT.r[2]); + V1[0] = XMVectorSwizzle(MT.r[3]); + V0[1] = XMVectorSwizzle(MT.r[0]); + V1[1] = XMVectorSwizzle(MT.r[1]); + V0[2] = XMVectorPermute(MT.r[2], MT.r[0]); + V1[2] = XMVectorPermute(MT.r[3], MT.r[1]); + + D0 = XMVectorNegativeMultiplySubtract(V0[0], V1[0], D0); + D1 = XMVectorNegativeMultiplySubtract(V0[1], V1[1], D1); + D2 = XMVectorNegativeMultiplySubtract(V0[2], V1[2], D2); + + V0[0] = XMVectorSwizzle(MT.r[1]); + V1[0] = XMVectorPermute(D0, D2); + V0[1] = XMVectorSwizzle(MT.r[0]); + V1[1] = XMVectorPermute(D0, D2); + V0[2] = XMVectorSwizzle(MT.r[3]); + V1[2] = XMVectorPermute(D1, D2); + V0[3] = XMVectorSwizzle(MT.r[2]); + V1[3] = XMVectorPermute(D1, D2); + + XMVECTOR C0 = XMVectorMultiply(V0[0], V1[0]); + XMVECTOR C2 = XMVectorMultiply(V0[1], V1[1]); + XMVECTOR C4 = XMVectorMultiply(V0[2], V1[2]); + XMVECTOR C6 = XMVectorMultiply(V0[3], V1[3]); + + V0[0] = XMVectorSwizzle(MT.r[1]); + V1[0] = XMVectorPermute(D0, D2); + V0[1] = XMVectorSwizzle(MT.r[0]); + V1[1] = XMVectorPermute(D0, D2); + V0[2] = XMVectorSwizzle(MT.r[3]); + V1[2] = XMVectorPermute(D1, D2); + V0[3] = XMVectorSwizzle(MT.r[2]); + V1[3] = XMVectorPermute(D1, D2); + + C0 = XMVectorNegativeMultiplySubtract(V0[0], V1[0], C0); + C2 = XMVectorNegativeMultiplySubtract(V0[1], V1[1], C2); + C4 = XMVectorNegativeMultiplySubtract(V0[2], V1[2], C4); + C6 = XMVectorNegativeMultiplySubtract(V0[3], V1[3], C6); + + V0[0] = XMVectorSwizzle(MT.r[1]); + V1[0] = XMVectorPermute(D0, D2); + V0[1] = XMVectorSwizzle(MT.r[0]); + V1[1] = XMVectorPermute(D0, D2); + V0[2] = XMVectorSwizzle(MT.r[3]); + V1[2] = XMVectorPermute(D1, D2); + V0[3] = XMVectorSwizzle(MT.r[2]); + V1[3] = XMVectorPermute(D1, D2); + + XMVECTOR C1 = XMVectorNegativeMultiplySubtract(V0[0], V1[0], C0); + C0 = XMVectorMultiplyAdd(V0[0], V1[0], C0); + XMVECTOR C3 = XMVectorMultiplyAdd(V0[1], V1[1], C2); + C2 = XMVectorNegativeMultiplySubtract(V0[1], V1[1], C2); + XMVECTOR C5 = XMVectorNegativeMultiplySubtract(V0[2], V1[2], C4); + C4 = XMVectorMultiplyAdd(V0[2], V1[2], C4); + XMVECTOR C7 = XMVectorMultiplyAdd(V0[3], V1[3], C6); + C6 = XMVectorNegativeMultiplySubtract(V0[3], V1[3], C6); + + XMMATRIX R; + R.r[0] = XMVectorSelect(C0, C1, g_XMSelect0101.v); + R.r[1] = XMVectorSelect(C2, C3, g_XMSelect0101.v); + R.r[2] = XMVectorSelect(C4, C5, g_XMSelect0101.v); + R.r[3] = XMVectorSelect(C6, C7, g_XMSelect0101.v); + + XMVECTOR Determinant = XMVector4Dot(R.r[0], MT.r[0]); + + if (pDeterminant != nullptr) + *pDeterminant = Determinant; + + XMVECTOR Reciprocal = XMVectorReciprocal(Determinant); + + XMMATRIX Result; + Result.r[0] = XMVectorMultiply(R.r[0], Reciprocal); + Result.r[1] = XMVectorMultiply(R.r[1], Reciprocal); + Result.r[2] = XMVectorMultiply(R.r[2], Reciprocal); + Result.r[3] = XMVectorMultiply(R.r[3], Reciprocal); + return Result; + +#elif defined(_XM_SSE_INTRINSICS_) + // Transpose matrix + XMVECTOR vTemp1 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(1, 0, 1, 0)); + XMVECTOR vTemp3 = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(3, 2, 3, 2)); + XMVECTOR vTemp2 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(1, 0, 1, 0)); + XMVECTOR vTemp4 = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(3, 2, 3, 2)); + + XMMATRIX MT; + MT.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0)); + MT.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1)); + MT.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0)); + MT.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1)); + + XMVECTOR V00 = XM_PERMUTE_PS(MT.r[2], _MM_SHUFFLE(1, 1, 0, 0)); + XMVECTOR V10 = XM_PERMUTE_PS(MT.r[3], _MM_SHUFFLE(3, 2, 3, 2)); + XMVECTOR V01 = XM_PERMUTE_PS(MT.r[0], _MM_SHUFFLE(1, 1, 0, 0)); + XMVECTOR V11 = XM_PERMUTE_PS(MT.r[1], _MM_SHUFFLE(3, 2, 3, 2)); + XMVECTOR V02 = _mm_shuffle_ps(MT.r[2], MT.r[0], _MM_SHUFFLE(2, 0, 2, 0)); + XMVECTOR V12 = _mm_shuffle_ps(MT.r[3], MT.r[1], _MM_SHUFFLE(3, 1, 3, 1)); + + XMVECTOR D0 = _mm_mul_ps(V00, V10); + XMVECTOR D1 = _mm_mul_ps(V01, V11); + XMVECTOR D2 = _mm_mul_ps(V02, V12); + + V00 = XM_PERMUTE_PS(MT.r[2], _MM_SHUFFLE(3, 2, 3, 2)); + V10 = XM_PERMUTE_PS(MT.r[3], _MM_SHUFFLE(1, 1, 0, 0)); + V01 = XM_PERMUTE_PS(MT.r[0], _MM_SHUFFLE(3, 2, 3, 2)); + V11 = XM_PERMUTE_PS(MT.r[1], _MM_SHUFFLE(1, 1, 0, 0)); + V02 = _mm_shuffle_ps(MT.r[2], MT.r[0], _MM_SHUFFLE(3, 1, 3, 1)); + V12 = _mm_shuffle_ps(MT.r[3], MT.r[1], _MM_SHUFFLE(2, 0, 2, 0)); + + D0 = XM_FNMADD_PS(V00, V10, D0); + D1 = XM_FNMADD_PS(V01, V11, D1); + D2 = XM_FNMADD_PS(V02, V12, D2); + // V11 = D0Y,D0W,D2Y,D2Y + V11 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(1, 1, 3, 1)); + V00 = XM_PERMUTE_PS(MT.r[1], _MM_SHUFFLE(1, 0, 2, 1)); + V10 = _mm_shuffle_ps(V11, D0, _MM_SHUFFLE(0, 3, 0, 2)); + V01 = XM_PERMUTE_PS(MT.r[0], _MM_SHUFFLE(0, 1, 0, 2)); + V11 = _mm_shuffle_ps(V11, D0, _MM_SHUFFLE(2, 1, 2, 1)); + // V13 = D1Y,D1W,D2W,D2W + XMVECTOR V13 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(3, 3, 3, 1)); + V02 = XM_PERMUTE_PS(MT.r[3], _MM_SHUFFLE(1, 0, 2, 1)); + V12 = _mm_shuffle_ps(V13, D1, _MM_SHUFFLE(0, 3, 0, 2)); + XMVECTOR V03 = XM_PERMUTE_PS(MT.r[2], _MM_SHUFFLE(0, 1, 0, 2)); + V13 = _mm_shuffle_ps(V13, D1, _MM_SHUFFLE(2, 1, 2, 1)); + + XMVECTOR C0 = _mm_mul_ps(V00, V10); + XMVECTOR C2 = _mm_mul_ps(V01, V11); + XMVECTOR C4 = _mm_mul_ps(V02, V12); + XMVECTOR C6 = _mm_mul_ps(V03, V13); + + // V11 = D0X,D0Y,D2X,D2X + V11 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(0, 0, 1, 0)); + V00 = XM_PERMUTE_PS(MT.r[1], _MM_SHUFFLE(2, 1, 3, 2)); + V10 = _mm_shuffle_ps(D0, V11, _MM_SHUFFLE(2, 1, 0, 3)); + V01 = XM_PERMUTE_PS(MT.r[0], _MM_SHUFFLE(1, 3, 2, 3)); + V11 = _mm_shuffle_ps(D0, V11, _MM_SHUFFLE(0, 2, 1, 2)); + // V13 = D1X,D1Y,D2Z,D2Z + V13 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(2, 2, 1, 0)); + V02 = XM_PERMUTE_PS(MT.r[3], _MM_SHUFFLE(2, 1, 3, 2)); + V12 = _mm_shuffle_ps(D1, V13, _MM_SHUFFLE(2, 1, 0, 3)); + V03 = XM_PERMUTE_PS(MT.r[2], _MM_SHUFFLE(1, 3, 2, 3)); + V13 = _mm_shuffle_ps(D1, V13, _MM_SHUFFLE(0, 2, 1, 2)); + + C0 = XM_FNMADD_PS(V00, V10, C0); + C2 = XM_FNMADD_PS(V01, V11, C2); + C4 = XM_FNMADD_PS(V02, V12, C4); + C6 = XM_FNMADD_PS(V03, V13, C6); + + V00 = XM_PERMUTE_PS(MT.r[1], _MM_SHUFFLE(0, 3, 0, 3)); + // V10 = D0Z,D0Z,D2X,D2Y + V10 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(1, 0, 2, 2)); + V10 = XM_PERMUTE_PS(V10, _MM_SHUFFLE(0, 2, 3, 0)); + V01 = XM_PERMUTE_PS(MT.r[0], _MM_SHUFFLE(2, 0, 3, 1)); + // V11 = D0X,D0W,D2X,D2Y + V11 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(1, 0, 3, 0)); + V11 = XM_PERMUTE_PS(V11, _MM_SHUFFLE(2, 1, 0, 3)); + V02 = XM_PERMUTE_PS(MT.r[3], _MM_SHUFFLE(0, 3, 0, 3)); + // V12 = D1Z,D1Z,D2Z,D2W + V12 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(3, 2, 2, 2)); + V12 = XM_PERMUTE_PS(V12, _MM_SHUFFLE(0, 2, 3, 0)); + V03 = XM_PERMUTE_PS(MT.r[2], _MM_SHUFFLE(2, 0, 3, 1)); + // V13 = D1X,D1W,D2Z,D2W + V13 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(3, 2, 3, 0)); + V13 = XM_PERMUTE_PS(V13, _MM_SHUFFLE(2, 1, 0, 3)); + + V00 = _mm_mul_ps(V00, V10); + V01 = _mm_mul_ps(V01, V11); + V02 = _mm_mul_ps(V02, V12); + V03 = _mm_mul_ps(V03, V13); + XMVECTOR C1 = _mm_sub_ps(C0, V00); + C0 = _mm_add_ps(C0, V00); + XMVECTOR C3 = _mm_add_ps(C2, V01); + C2 = _mm_sub_ps(C2, V01); + XMVECTOR C5 = _mm_sub_ps(C4, V02); + C4 = _mm_add_ps(C4, V02); + XMVECTOR C7 = _mm_add_ps(C6, V03); + C6 = _mm_sub_ps(C6, V03); + + C0 = _mm_shuffle_ps(C0, C1, _MM_SHUFFLE(3, 1, 2, 0)); + C2 = _mm_shuffle_ps(C2, C3, _MM_SHUFFLE(3, 1, 2, 0)); + C4 = _mm_shuffle_ps(C4, C5, _MM_SHUFFLE(3, 1, 2, 0)); + C6 = _mm_shuffle_ps(C6, C7, _MM_SHUFFLE(3, 1, 2, 0)); + C0 = XM_PERMUTE_PS(C0, _MM_SHUFFLE(3, 1, 2, 0)); + C2 = XM_PERMUTE_PS(C2, _MM_SHUFFLE(3, 1, 2, 0)); + C4 = XM_PERMUTE_PS(C4, _MM_SHUFFLE(3, 1, 2, 0)); + C6 = XM_PERMUTE_PS(C6, _MM_SHUFFLE(3, 1, 2, 0)); + // Get the determinant + XMVECTOR vTemp = XMVector4Dot(C0, MT.r[0]); + if (pDeterminant != nullptr) + *pDeterminant = vTemp; + vTemp = _mm_div_ps(g_XMOne, vTemp); + XMMATRIX mResult; + mResult.r[0] = _mm_mul_ps(C0, vTemp); + mResult.r[1] = _mm_mul_ps(C2, vTemp); + mResult.r[2] = _mm_mul_ps(C4, vTemp); + mResult.r[3] = _mm_mul_ps(C6, vTemp); + return mResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixVectorTensorProduct +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + XMMATRIX mResult; + mResult.r[0] = XMVectorMultiply(XMVectorSwizzle<0, 0, 0, 0>(V1), V2); + mResult.r[1] = XMVectorMultiply(XMVectorSwizzle<1, 1, 1, 1>(V1), V2); + mResult.r[2] = XMVectorMultiply(XMVectorSwizzle<2, 2, 2, 2>(V1), V2); + mResult.r[3] = XMVectorMultiply(XMVectorSwizzle<3, 3, 3, 3>(V1), V2); + return mResult; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMMatrixDeterminant(FXMMATRIX M) noexcept +{ + static const XMVECTORF32 Sign = { { { 1.0f, -1.0f, 1.0f, -1.0f } } }; + + XMVECTOR V0 = XMVectorSwizzle(M.r[2]); + XMVECTOR V1 = XMVectorSwizzle(M.r[3]); + XMVECTOR V2 = XMVectorSwizzle(M.r[2]); + XMVECTOR V3 = XMVectorSwizzle(M.r[3]); + XMVECTOR V4 = XMVectorSwizzle(M.r[2]); + XMVECTOR V5 = XMVectorSwizzle(M.r[3]); + + XMVECTOR P0 = XMVectorMultiply(V0, V1); + XMVECTOR P1 = XMVectorMultiply(V2, V3); + XMVECTOR P2 = XMVectorMultiply(V4, V5); + + V0 = XMVectorSwizzle(M.r[2]); + V1 = XMVectorSwizzle(M.r[3]); + V2 = XMVectorSwizzle(M.r[2]); + V3 = XMVectorSwizzle(M.r[3]); + V4 = XMVectorSwizzle(M.r[2]); + V5 = XMVectorSwizzle(M.r[3]); + + P0 = XMVectorNegativeMultiplySubtract(V0, V1, P0); + P1 = XMVectorNegativeMultiplySubtract(V2, V3, P1); + P2 = XMVectorNegativeMultiplySubtract(V4, V5, P2); + + V0 = XMVectorSwizzle(M.r[1]); + V1 = XMVectorSwizzle(M.r[1]); + V2 = XMVectorSwizzle(M.r[1]); + + XMVECTOR S = XMVectorMultiply(M.r[0], Sign.v); + XMVECTOR R = XMVectorMultiply(V0, P0); + R = XMVectorNegativeMultiplySubtract(V1, P1, R); + R = XMVectorMultiplyAdd(V2, P2, R); + + return XMVector4Dot(S, R); +} + +#define XM3RANKDECOMPOSE(a, b, c, x, y, z) \ + if((x) < (y)) \ + { \ + if((y) < (z)) \ + { \ + (a) = 2; \ + (b) = 1; \ + (c) = 0; \ + } \ + else \ + { \ + (a) = 1; \ + \ + if((x) < (z)) \ + { \ + (b) = 2; \ + (c) = 0; \ + } \ + else \ + { \ + (b) = 0; \ + (c) = 2; \ + } \ + } \ + } \ + else \ + { \ + if((x) < (z)) \ + { \ + (a) = 2; \ + (b) = 0; \ + (c) = 1; \ + } \ + else \ + { \ + (a) = 0; \ + \ + if((y) < (z)) \ + { \ + (b) = 2; \ + (c) = 1; \ + } \ + else \ + { \ + (b) = 1; \ + (c) = 2; \ + } \ + } \ + } + +#define XM3_DECOMP_EPSILON 0.0001f + +_Use_decl_annotations_ +inline bool XM_CALLCONV XMMatrixDecompose +( + XMVECTOR* outScale, + XMVECTOR* outRotQuat, + XMVECTOR* outTrans, + FXMMATRIX M +) noexcept +{ + static const XMVECTOR* pvCanonicalBasis[3] = { + &g_XMIdentityR0.v, + &g_XMIdentityR1.v, + &g_XMIdentityR2.v + }; + + assert(outScale != nullptr); + assert(outRotQuat != nullptr); + assert(outTrans != nullptr); + + // Get the translation + outTrans[0] = M.r[3]; + + XMVECTOR* ppvBasis[3]; + XMMATRIX matTemp; + ppvBasis[0] = &matTemp.r[0]; + ppvBasis[1] = &matTemp.r[1]; + ppvBasis[2] = &matTemp.r[2]; + + matTemp.r[0] = M.r[0]; + matTemp.r[1] = M.r[1]; + matTemp.r[2] = M.r[2]; + matTemp.r[3] = g_XMIdentityR3.v; + + auto pfScales = reinterpret_cast(outScale); + + size_t a, b, c; + XMVectorGetXPtr(&pfScales[0], XMVector3Length(ppvBasis[0][0])); + XMVectorGetXPtr(&pfScales[1], XMVector3Length(ppvBasis[1][0])); + XMVectorGetXPtr(&pfScales[2], XMVector3Length(ppvBasis[2][0])); + pfScales[3] = 0.f; + + XM3RANKDECOMPOSE(a, b, c, pfScales[0], pfScales[1], pfScales[2]) + + if (pfScales[a] < XM3_DECOMP_EPSILON) + { + ppvBasis[a][0] = pvCanonicalBasis[a][0]; + } + ppvBasis[a][0] = XMVector3Normalize(ppvBasis[a][0]); + + if (pfScales[b] < XM3_DECOMP_EPSILON) + { + size_t aa, bb, cc; + float fAbsX, fAbsY, fAbsZ; + + fAbsX = fabsf(XMVectorGetX(ppvBasis[a][0])); + fAbsY = fabsf(XMVectorGetY(ppvBasis[a][0])); + fAbsZ = fabsf(XMVectorGetZ(ppvBasis[a][0])); + + XM3RANKDECOMPOSE(aa, bb, cc, fAbsX, fAbsY, fAbsZ) + + ppvBasis[b][0] = XMVector3Cross(ppvBasis[a][0], pvCanonicalBasis[cc][0]); + } + + ppvBasis[b][0] = XMVector3Normalize(ppvBasis[b][0]); + + if (pfScales[c] < XM3_DECOMP_EPSILON) + { + ppvBasis[c][0] = XMVector3Cross(ppvBasis[a][0], ppvBasis[b][0]); + } + + ppvBasis[c][0] = XMVector3Normalize(ppvBasis[c][0]); + + float fDet = XMVectorGetX(XMMatrixDeterminant(matTemp)); + + // use Kramer's rule to check for handedness of coordinate system + if (fDet < 0.0f) + { + // switch coordinate system by negating the scale and inverting the basis vector on the x-axis + pfScales[a] = -pfScales[a]; + ppvBasis[a][0] = XMVectorNegate(ppvBasis[a][0]); + + fDet = -fDet; + } + + fDet -= 1.0f; + fDet *= fDet; + + if (XM3_DECOMP_EPSILON < fDet) + { + // Non-SRT matrix encountered + return false; + } + + // generate the quaternion from the matrix + outRotQuat[0] = XMQuaternionRotationMatrix(matTemp); + return true; +} + +#undef XM3_DECOMP_EPSILON +#undef XM3RANKDECOMPOSE + +//------------------------------------------------------------------------------ +// Transformation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixIdentity() noexcept +{ + XMMATRIX M; + M.r[0] = g_XMIdentityR0.v; + M.r[1] = g_XMIdentityR1.v; + M.r[2] = g_XMIdentityR2.v; + M.r[3] = g_XMIdentityR3.v; + return M; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixSet +( + 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 +{ + XMMATRIX M; +#if defined(_XM_NO_INTRINSICS_) + M.m[0][0] = m00; M.m[0][1] = m01; M.m[0][2] = m02; M.m[0][3] = m03; + M.m[1][0] = m10; M.m[1][1] = m11; M.m[1][2] = m12; M.m[1][3] = m13; + M.m[2][0] = m20; M.m[2][1] = m21; M.m[2][2] = m22; M.m[2][3] = m23; + M.m[3][0] = m30; M.m[3][1] = m31; M.m[3][2] = m32; M.m[3][3] = m33; +#else + M.r[0] = XMVectorSet(m00, m01, m02, m03); + M.r[1] = XMVectorSet(m10, m11, m12, m13); + M.r[2] = XMVectorSet(m20, m21, m22, m23); + M.r[3] = XMVectorSet(m30, m31, m32, m33); +#endif + return M; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixTranslation +( + float OffsetX, + float OffsetY, + float OffsetZ +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.m[0][0] = 1.0f; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = 1.0f; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = 1.0f; + M.m[2][3] = 0.0f; + + M.m[3][0] = OffsetX; + M.m[3][1] = OffsetY; + M.m[3][2] = OffsetZ; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_SSE_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + XMMATRIX M; + M.r[0] = g_XMIdentityR0.v; + M.r[1] = g_XMIdentityR1.v; + M.r[2] = g_XMIdentityR2.v; + M.r[3] = XMVectorSet(OffsetX, OffsetY, OffsetZ, 1.f); + return M; +#endif +} + + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixTranslationFromVector(FXMVECTOR Offset) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.m[0][0] = 1.0f; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = 1.0f; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = 1.0f; + M.m[2][3] = 0.0f; + + M.m[3][0] = Offset.vector4_f32[0]; + M.m[3][1] = Offset.vector4_f32[1]; + M.m[3][2] = Offset.vector4_f32[2]; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_SSE_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + XMMATRIX M; + M.r[0] = g_XMIdentityR0.v; + M.r[1] = g_XMIdentityR1.v; + M.r[2] = g_XMIdentityR2.v; + M.r[3] = XMVectorSelect(g_XMIdentityR3.v, Offset, g_XMSelect1110.v); + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixScaling +( + float ScaleX, + float ScaleY, + float ScaleZ +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.m[0][0] = ScaleX; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = ScaleY; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = ScaleZ; + M.m[2][3] = 0.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = 0.0f; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + const XMVECTOR Zero = vdupq_n_f32(0); + XMMATRIX M; + M.r[0] = vsetq_lane_f32(ScaleX, Zero, 0); + M.r[1] = vsetq_lane_f32(ScaleY, Zero, 1); + M.r[2] = vsetq_lane_f32(ScaleZ, Zero, 2); + M.r[3] = g_XMIdentityR3.v; + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + M.r[0] = _mm_set_ps(0, 0, 0, ScaleX); + M.r[1] = _mm_set_ps(0, 0, ScaleY, 0); + M.r[2] = _mm_set_ps(0, ScaleZ, 0, 0); + M.r[3] = g_XMIdentityR3.v; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixScalingFromVector(FXMVECTOR Scale) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMMATRIX M; + M.m[0][0] = Scale.vector4_f32[0]; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = Scale.vector4_f32[1]; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = Scale.vector4_f32[2]; + M.m[2][3] = 0.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = 0.0f; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMMATRIX M; + M.r[0] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(Scale), g_XMMaskX)); + M.r[1] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(Scale), g_XMMaskY)); + M.r[2] = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(Scale), g_XMMaskZ)); + M.r[3] = g_XMIdentityR3.v; + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + M.r[0] = _mm_and_ps(Scale, g_XMMaskX); + M.r[1] = _mm_and_ps(Scale, g_XMMaskY); + M.r[2] = _mm_and_ps(Scale, g_XMMaskZ); + M.r[3] = g_XMIdentityR3.v; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixRotationX(float Angle) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + float fSinAngle; + float fCosAngle; + XMScalarSinCos(&fSinAngle, &fCosAngle, Angle); + + XMMATRIX M; + M.m[0][0] = 1.0f; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = fCosAngle; + M.m[1][2] = fSinAngle; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = -fSinAngle; + M.m[2][2] = fCosAngle; + M.m[2][3] = 0.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = 0.0f; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float fSinAngle; + float fCosAngle; + XMScalarSinCos(&fSinAngle, &fCosAngle, Angle); + + const float32x4_t Zero = vdupq_n_f32(0); + + float32x4_t T1 = vsetq_lane_f32(fCosAngle, Zero, 1); + T1 = vsetq_lane_f32(fSinAngle, T1, 2); + + float32x4_t T2 = vsetq_lane_f32(-fSinAngle, Zero, 1); + T2 = vsetq_lane_f32(fCosAngle, T2, 2); + + XMMATRIX M; + M.r[0] = g_XMIdentityR0.v; + M.r[1] = T1; + M.r[2] = T2; + M.r[3] = g_XMIdentityR3.v; + return M; +#elif defined(_XM_SSE_INTRINSICS_) + float SinAngle; + float CosAngle; + XMScalarSinCos(&SinAngle, &CosAngle, Angle); + + XMVECTOR vSin = _mm_set_ss(SinAngle); + XMVECTOR vCos = _mm_set_ss(CosAngle); + // x = 0,y = cos,z = sin, w = 0 + vCos = _mm_shuffle_ps(vCos, vSin, _MM_SHUFFLE(3, 0, 0, 3)); + XMMATRIX M; + M.r[0] = g_XMIdentityR0; + M.r[1] = vCos; + // x = 0,y = sin,z = cos, w = 0 + vCos = XM_PERMUTE_PS(vCos, _MM_SHUFFLE(3, 1, 2, 0)); + // x = 0,y = -sin,z = cos, w = 0 + vCos = _mm_mul_ps(vCos, g_XMNegateY); + M.r[2] = vCos; + M.r[3] = g_XMIdentityR3; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixRotationY(float Angle) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + float fSinAngle; + float fCosAngle; + XMScalarSinCos(&fSinAngle, &fCosAngle, Angle); + + XMMATRIX M; + M.m[0][0] = fCosAngle; + M.m[0][1] = 0.0f; + M.m[0][2] = -fSinAngle; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = 1.0f; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = fSinAngle; + M.m[2][1] = 0.0f; + M.m[2][2] = fCosAngle; + M.m[2][3] = 0.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = 0.0f; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float fSinAngle; + float fCosAngle; + XMScalarSinCos(&fSinAngle, &fCosAngle, Angle); + + const float32x4_t Zero = vdupq_n_f32(0); + + float32x4_t T0 = vsetq_lane_f32(fCosAngle, Zero, 0); + T0 = vsetq_lane_f32(-fSinAngle, T0, 2); + + float32x4_t T2 = vsetq_lane_f32(fSinAngle, Zero, 0); + T2 = vsetq_lane_f32(fCosAngle, T2, 2); + + XMMATRIX M; + M.r[0] = T0; + M.r[1] = g_XMIdentityR1.v; + M.r[2] = T2; + M.r[3] = g_XMIdentityR3.v; + return M; +#elif defined(_XM_SSE_INTRINSICS_) + float SinAngle; + float CosAngle; + XMScalarSinCos(&SinAngle, &CosAngle, Angle); + + XMVECTOR vSin = _mm_set_ss(SinAngle); + XMVECTOR vCos = _mm_set_ss(CosAngle); + // x = sin,y = 0,z = cos, w = 0 + vSin = _mm_shuffle_ps(vSin, vCos, _MM_SHUFFLE(3, 0, 3, 0)); + XMMATRIX M; + M.r[2] = vSin; + M.r[1] = g_XMIdentityR1; + // x = cos,y = 0,z = sin, w = 0 + vSin = XM_PERMUTE_PS(vSin, _MM_SHUFFLE(3, 0, 1, 2)); + // x = cos,y = 0,z = -sin, w = 0 + vSin = _mm_mul_ps(vSin, g_XMNegateZ); + M.r[0] = vSin; + M.r[3] = g_XMIdentityR3; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixRotationZ(float Angle) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + float fSinAngle; + float fCosAngle; + XMScalarSinCos(&fSinAngle, &fCosAngle, Angle); + + XMMATRIX M; + M.m[0][0] = fCosAngle; + M.m[0][1] = fSinAngle; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = -fSinAngle; + M.m[1][1] = fCosAngle; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = 1.0f; + M.m[2][3] = 0.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = 0.0f; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float fSinAngle; + float fCosAngle; + XMScalarSinCos(&fSinAngle, &fCosAngle, Angle); + + const float32x4_t Zero = vdupq_n_f32(0); + + float32x4_t T0 = vsetq_lane_f32(fCosAngle, Zero, 0); + T0 = vsetq_lane_f32(fSinAngle, T0, 1); + + float32x4_t T1 = vsetq_lane_f32(-fSinAngle, Zero, 0); + T1 = vsetq_lane_f32(fCosAngle, T1, 1); + + XMMATRIX M; + M.r[0] = T0; + M.r[1] = T1; + M.r[2] = g_XMIdentityR2.v; + M.r[3] = g_XMIdentityR3.v; + return M; +#elif defined(_XM_SSE_INTRINSICS_) + float SinAngle; + float CosAngle; + XMScalarSinCos(&SinAngle, &CosAngle, Angle); + + XMVECTOR vSin = _mm_set_ss(SinAngle); + XMVECTOR vCos = _mm_set_ss(CosAngle); + // x = cos,y = sin,z = 0, w = 0 + vCos = _mm_unpacklo_ps(vCos, vSin); + XMMATRIX M; + M.r[0] = vCos; + // x = sin,y = cos,z = 0, w = 0 + vCos = XM_PERMUTE_PS(vCos, _MM_SHUFFLE(3, 2, 0, 1)); + // x = cos,y = -sin,z = 0, w = 0 + vCos = _mm_mul_ps(vCos, g_XMNegateX); + M.r[1] = vCos; + M.r[2] = g_XMIdentityR2; + M.r[3] = g_XMIdentityR3; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixRotationRollPitchYaw +( + float Pitch, + float Yaw, + float Roll +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float cp = cosf(Pitch); + float sp = sinf(Pitch); + + float cy = cosf(Yaw); + float sy = sinf(Yaw); + + float cr = cosf(Roll); + float sr = sinf(Roll); + + XMMATRIX M; + M.m[0][0] = cr * cy + sr * sp * sy; + M.m[0][1] = sr * cp; + M.m[0][2] = sr * sp * cy - cr * sy; + M.m[0][3] = 0.0f; + + M.m[1][0] = cr * sp * sy - sr * cy; + M.m[1][1] = cr * cp; + M.m[1][2] = sr * sy + cr * sp * cy; + M.m[1][3] = 0.0f; + + M.m[2][0] = cp * sy; + M.m[2][1] = -sp; + M.m[2][2] = cp * cy; + M.m[2][3] = 0.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = 0.0f; + M.m[3][3] = 1.0f; + return M; +#else + XMVECTOR Angles = XMVectorSet(Pitch, Yaw, Roll, 0.0f); + return XMMatrixRotationRollPitchYawFromVector(Angles); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixRotationRollPitchYawFromVector +( + FXMVECTOR Angles // +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float cp = cosf(Angles.vector4_f32[0]); + float sp = sinf(Angles.vector4_f32[0]); + + float cy = cosf(Angles.vector4_f32[1]); + float sy = sinf(Angles.vector4_f32[1]); + + float cr = cosf(Angles.vector4_f32[2]); + float sr = sinf(Angles.vector4_f32[2]); + + XMMATRIX M; + M.m[0][0] = cr * cy + sr * sp * sy; + M.m[0][1] = sr * cp; + M.m[0][2] = sr * sp * cy - cr * sy; + M.m[0][3] = 0.0f; + + M.m[1][0] = cr * sp * sy - sr * cy; + M.m[1][1] = cr * cp; + M.m[1][2] = sr * sy + cr * sp * cy; + M.m[1][3] = 0.0f; + + M.m[2][0] = cp * sy; + M.m[2][1] = -sp; + M.m[2][2] = cp * cy; + M.m[2][3] = 0.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = 0.0f; + M.m[3][3] = 1.0f; + return M; +#else + static const XMVECTORF32 Sign = { { { 1.0f, -1.0f, -1.0f, 1.0f } } }; + + XMVECTOR SinAngles, CosAngles; + XMVectorSinCos(&SinAngles, &CosAngles, Angles); + + XMVECTOR P0 = XMVectorPermute(SinAngles, CosAngles); + XMVECTOR Y0 = XMVectorPermute(SinAngles, CosAngles); + XMVECTOR P1 = XMVectorPermute(SinAngles, CosAngles); + XMVECTOR Y1 = XMVectorPermute(SinAngles, CosAngles); + XMVECTOR P2 = XMVectorPermute(SinAngles, CosAngles); + XMVECTOR P3 = XMVectorPermute(SinAngles, CosAngles); + XMVECTOR Y2 = XMVectorSplatX(SinAngles); + XMVECTOR NS = XMVectorNegate(SinAngles); + + XMVECTOR Q0 = XMVectorMultiply(P0, Y0); + XMVECTOR Q1 = XMVectorMultiply(P1, Sign.v); + Q1 = XMVectorMultiply(Q1, Y1); + XMVECTOR Q2 = XMVectorMultiply(P2, Y2); + Q2 = XMVectorMultiplyAdd(Q2, P3, Q1); + + XMVECTOR V0 = XMVectorPermute(Q0, Q2); + XMVECTOR V1 = XMVectorPermute(Q0, Q2); + XMVECTOR V2 = XMVectorPermute(Q0, NS); + + XMMATRIX M; + M.r[0] = XMVectorSelect(g_XMZero, V0, g_XMSelect1110.v); + M.r[1] = XMVectorSelect(g_XMZero, V1, g_XMSelect1110.v); + M.r[2] = XMVectorSelect(g_XMZero, V2, g_XMSelect1110.v); + M.r[3] = g_XMIdentityR3; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixRotationNormal +( + FXMVECTOR NormalAxis, + float Angle +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + + float fSinAngle; + float fCosAngle; + XMScalarSinCos(&fSinAngle, &fCosAngle, Angle); + + XMVECTOR A = XMVectorSet(fSinAngle, fCosAngle, 1.0f - fCosAngle, 0.0f); + + XMVECTOR C2 = XMVectorSplatZ(A); + XMVECTOR C1 = XMVectorSplatY(A); + XMVECTOR C0 = XMVectorSplatX(A); + + XMVECTOR N0 = XMVectorSwizzle(NormalAxis); + XMVECTOR N1 = XMVectorSwizzle(NormalAxis); + + XMVECTOR V0 = XMVectorMultiply(C2, N0); + V0 = XMVectorMultiply(V0, N1); + + XMVECTOR R0 = XMVectorMultiply(C2, NormalAxis); + R0 = XMVectorMultiplyAdd(R0, NormalAxis, C1); + + XMVECTOR R1 = XMVectorMultiplyAdd(C0, NormalAxis, V0); + XMVECTOR R2 = XMVectorNegativeMultiplySubtract(C0, NormalAxis, V0); + + V0 = XMVectorSelect(A, R0, g_XMSelect1110.v); + XMVECTOR V1 = XMVectorPermute(R1, R2); + XMVECTOR V2 = XMVectorPermute(R1, R2); + + XMMATRIX M; + M.r[0] = XMVectorPermute(V0, V1); + M.r[1] = XMVectorPermute(V0, V1); + M.r[2] = XMVectorPermute(V0, V2); + M.r[3] = g_XMIdentityR3.v; + return M; + +#elif defined(_XM_SSE_INTRINSICS_) + float fSinAngle; + float fCosAngle; + XMScalarSinCos(&fSinAngle, &fCosAngle, Angle); + + XMVECTOR C2 = _mm_set_ps1(1.0f - fCosAngle); + XMVECTOR C1 = _mm_set_ps1(fCosAngle); + XMVECTOR C0 = _mm_set_ps1(fSinAngle); + + XMVECTOR N0 = XM_PERMUTE_PS(NormalAxis, _MM_SHUFFLE(3, 0, 2, 1)); + XMVECTOR N1 = XM_PERMUTE_PS(NormalAxis, _MM_SHUFFLE(3, 1, 0, 2)); + + XMVECTOR V0 = _mm_mul_ps(C2, N0); + V0 = _mm_mul_ps(V0, N1); + + XMVECTOR R0 = _mm_mul_ps(C2, NormalAxis); + R0 = _mm_mul_ps(R0, NormalAxis); + R0 = _mm_add_ps(R0, C1); + + XMVECTOR R1 = _mm_mul_ps(C0, NormalAxis); + R1 = _mm_add_ps(R1, V0); + XMVECTOR R2 = _mm_mul_ps(C0, NormalAxis); + R2 = _mm_sub_ps(V0, R2); + + V0 = _mm_and_ps(R0, g_XMMask3); + XMVECTOR V1 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(2, 1, 2, 0)); + V1 = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 3, 2, 1)); + XMVECTOR V2 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(0, 0, 1, 1)); + V2 = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 0, 2, 0)); + + R2 = _mm_shuffle_ps(V0, V1, _MM_SHUFFLE(1, 0, 3, 0)); + R2 = XM_PERMUTE_PS(R2, _MM_SHUFFLE(1, 3, 2, 0)); + + XMMATRIX M; + M.r[0] = R2; + + R2 = _mm_shuffle_ps(V0, V1, _MM_SHUFFLE(3, 2, 3, 1)); + R2 = XM_PERMUTE_PS(R2, _MM_SHUFFLE(1, 3, 0, 2)); + M.r[1] = R2; + + V2 = _mm_shuffle_ps(V2, V0, _MM_SHUFFLE(3, 2, 1, 0)); + M.r[2] = V2; + M.r[3] = g_XMIdentityR3.v; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixRotationAxis +( + FXMVECTOR Axis, + float Angle +) noexcept +{ + assert(!XMVector3Equal(Axis, XMVectorZero())); + assert(!XMVector3IsInfinite(Axis)); + + XMVECTOR Normal = XMVector3Normalize(Axis); + return XMMatrixRotationNormal(Normal, Angle); +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixRotationQuaternion(FXMVECTOR Quaternion) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + float qx = Quaternion.vector4_f32[0]; + float qxx = qx * qx; + + float qy = Quaternion.vector4_f32[1]; + float qyy = qy * qy; + + float qz = Quaternion.vector4_f32[2]; + float qzz = qz * qz; + + float qw = Quaternion.vector4_f32[3]; + + XMMATRIX M; + M.m[0][0] = 1.f - 2.f * qyy - 2.f * qzz; + M.m[0][1] = 2.f * qx * qy + 2.f * qz * qw; + M.m[0][2] = 2.f * qx * qz - 2.f * qy * qw; + M.m[0][3] = 0.f; + + M.m[1][0] = 2.f * qx * qy - 2.f * qz * qw; + M.m[1][1] = 1.f - 2.f * qxx - 2.f * qzz; + M.m[1][2] = 2.f * qy * qz + 2.f * qx * qw; + M.m[1][3] = 0.f; + + M.m[2][0] = 2.f * qx * qz + 2.f * qy * qw; + M.m[2][1] = 2.f * qy * qz - 2.f * qx * qw; + M.m[2][2] = 1.f - 2.f * qxx - 2.f * qyy; + M.m[2][3] = 0.f; + + M.m[3][0] = 0.f; + M.m[3][1] = 0.f; + M.m[3][2] = 0.f; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Constant1110 = { { { 1.0f, 1.0f, 1.0f, 0.0f } } }; + + XMVECTOR Q0 = XMVectorAdd(Quaternion, Quaternion); + XMVECTOR Q1 = XMVectorMultiply(Quaternion, Q0); + + XMVECTOR V0 = XMVectorPermute(Q1, Constant1110.v); + XMVECTOR V1 = XMVectorPermute(Q1, Constant1110.v); + XMVECTOR R0 = XMVectorSubtract(Constant1110, V0); + R0 = XMVectorSubtract(R0, V1); + + V0 = XMVectorSwizzle(Quaternion); + V1 = XMVectorSwizzle(Q0); + V0 = XMVectorMultiply(V0, V1); + + V1 = XMVectorSplatW(Quaternion); + XMVECTOR V2 = XMVectorSwizzle(Q0); + V1 = XMVectorMultiply(V1, V2); + + XMVECTOR R1 = XMVectorAdd(V0, V1); + XMVECTOR R2 = XMVectorSubtract(V0, V1); + + V0 = XMVectorPermute(R1, R2); + V1 = XMVectorPermute(R1, R2); + + XMMATRIX M; + M.r[0] = XMVectorPermute(R0, V0); + M.r[1] = XMVectorPermute(R0, V0); + M.r[2] = XMVectorPermute(R0, V1); + M.r[3] = g_XMIdentityR3.v; + return M; + +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Constant1110 = { { { 1.0f, 1.0f, 1.0f, 0.0f } } }; + + XMVECTOR Q0 = _mm_add_ps(Quaternion, Quaternion); + XMVECTOR Q1 = _mm_mul_ps(Quaternion, Q0); + + XMVECTOR V0 = XM_PERMUTE_PS(Q1, _MM_SHUFFLE(3, 0, 0, 1)); + V0 = _mm_and_ps(V0, g_XMMask3); + XMVECTOR V1 = XM_PERMUTE_PS(Q1, _MM_SHUFFLE(3, 1, 2, 2)); + V1 = _mm_and_ps(V1, g_XMMask3); + XMVECTOR R0 = _mm_sub_ps(Constant1110, V0); + R0 = _mm_sub_ps(R0, V1); + + V0 = XM_PERMUTE_PS(Quaternion, _MM_SHUFFLE(3, 1, 0, 0)); + V1 = XM_PERMUTE_PS(Q0, _MM_SHUFFLE(3, 2, 1, 2)); + V0 = _mm_mul_ps(V0, V1); + + V1 = XM_PERMUTE_PS(Quaternion, _MM_SHUFFLE(3, 3, 3, 3)); + XMVECTOR V2 = XM_PERMUTE_PS(Q0, _MM_SHUFFLE(3, 0, 2, 1)); + V1 = _mm_mul_ps(V1, V2); + + XMVECTOR R1 = _mm_add_ps(V0, V1); + XMVECTOR R2 = _mm_sub_ps(V0, V1); + + V0 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(1, 0, 2, 1)); + V0 = XM_PERMUTE_PS(V0, _MM_SHUFFLE(1, 3, 2, 0)); + V1 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(2, 2, 0, 0)); + V1 = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 0, 2, 0)); + + Q1 = _mm_shuffle_ps(R0, V0, _MM_SHUFFLE(1, 0, 3, 0)); + Q1 = XM_PERMUTE_PS(Q1, _MM_SHUFFLE(1, 3, 2, 0)); + + XMMATRIX M; + M.r[0] = Q1; + + Q1 = _mm_shuffle_ps(R0, V0, _MM_SHUFFLE(3, 2, 3, 1)); + Q1 = XM_PERMUTE_PS(Q1, _MM_SHUFFLE(1, 3, 0, 2)); + M.r[1] = Q1; + + Q1 = _mm_shuffle_ps(V1, R0, _MM_SHUFFLE(3, 2, 1, 0)); + M.r[2] = Q1; + M.r[3] = g_XMIdentityR3; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixTransformation2D +( + FXMVECTOR ScalingOrigin, + float ScalingOrientation, + FXMVECTOR Scaling, + FXMVECTOR RotationOrigin, + float Rotation, + GXMVECTOR Translation +) noexcept +{ + // M = Inverse(MScalingOrigin) * Transpose(MScalingOrientation) * MScaling * MScalingOrientation * + // MScalingOrigin * Inverse(MRotationOrigin) * MRotation * MRotationOrigin * MTranslation; + + XMVECTOR VScalingOrigin = XMVectorSelect(g_XMSelect1100.v, ScalingOrigin, g_XMSelect1100.v); + XMVECTOR NegScalingOrigin = XMVectorNegate(VScalingOrigin); + + XMMATRIX MScalingOriginI = XMMatrixTranslationFromVector(NegScalingOrigin); + XMMATRIX MScalingOrientation = XMMatrixRotationZ(ScalingOrientation); + XMMATRIX MScalingOrientationT = XMMatrixTranspose(MScalingOrientation); + XMVECTOR VScaling = XMVectorSelect(g_XMOne.v, Scaling, g_XMSelect1100.v); + XMMATRIX MScaling = XMMatrixScalingFromVector(VScaling); + XMVECTOR VRotationOrigin = XMVectorSelect(g_XMSelect1100.v, RotationOrigin, g_XMSelect1100.v); + XMMATRIX MRotation = XMMatrixRotationZ(Rotation); + XMVECTOR VTranslation = XMVectorSelect(g_XMSelect1100.v, Translation, g_XMSelect1100.v); + + XMMATRIX M = XMMatrixMultiply(MScalingOriginI, MScalingOrientationT); + M = XMMatrixMultiply(M, MScaling); + M = XMMatrixMultiply(M, MScalingOrientation); + M.r[3] = XMVectorAdd(M.r[3], VScalingOrigin); + M.r[3] = XMVectorSubtract(M.r[3], VRotationOrigin); + M = XMMatrixMultiply(M, MRotation); + M.r[3] = XMVectorAdd(M.r[3], VRotationOrigin); + M.r[3] = XMVectorAdd(M.r[3], VTranslation); + + return M; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixTransformation +( + FXMVECTOR ScalingOrigin, + FXMVECTOR ScalingOrientationQuaternion, + FXMVECTOR Scaling, + GXMVECTOR RotationOrigin, + HXMVECTOR RotationQuaternion, + HXMVECTOR Translation +) noexcept +{ + // M = Inverse(MScalingOrigin) * Transpose(MScalingOrientation) * MScaling * MScalingOrientation * + // MScalingOrigin * Inverse(MRotationOrigin) * MRotation * MRotationOrigin * MTranslation; + + XMVECTOR VScalingOrigin = XMVectorSelect(g_XMSelect1110.v, ScalingOrigin, g_XMSelect1110.v); + XMVECTOR NegScalingOrigin = XMVectorNegate(ScalingOrigin); + + XMMATRIX MScalingOriginI = XMMatrixTranslationFromVector(NegScalingOrigin); + XMMATRIX MScalingOrientation = XMMatrixRotationQuaternion(ScalingOrientationQuaternion); + XMMATRIX MScalingOrientationT = XMMatrixTranspose(MScalingOrientation); + XMMATRIX MScaling = XMMatrixScalingFromVector(Scaling); + XMVECTOR VRotationOrigin = XMVectorSelect(g_XMSelect1110.v, RotationOrigin, g_XMSelect1110.v); + XMMATRIX MRotation = XMMatrixRotationQuaternion(RotationQuaternion); + XMVECTOR VTranslation = XMVectorSelect(g_XMSelect1110.v, Translation, g_XMSelect1110.v); + + XMMATRIX M; + M = XMMatrixMultiply(MScalingOriginI, MScalingOrientationT); + M = XMMatrixMultiply(M, MScaling); + M = XMMatrixMultiply(M, MScalingOrientation); + M.r[3] = XMVectorAdd(M.r[3], VScalingOrigin); + M.r[3] = XMVectorSubtract(M.r[3], VRotationOrigin); + M = XMMatrixMultiply(M, MRotation); + M.r[3] = XMVectorAdd(M.r[3], VRotationOrigin); + M.r[3] = XMVectorAdd(M.r[3], VTranslation); + return M; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixAffineTransformation2D +( + FXMVECTOR Scaling, + FXMVECTOR RotationOrigin, + float Rotation, + FXMVECTOR Translation +) noexcept +{ + // M = MScaling * Inverse(MRotationOrigin) * MRotation * MRotationOrigin * MTranslation; + + XMVECTOR VScaling = XMVectorSelect(g_XMOne.v, Scaling, g_XMSelect1100.v); + XMMATRIX MScaling = XMMatrixScalingFromVector(VScaling); + XMVECTOR VRotationOrigin = XMVectorSelect(g_XMSelect1100.v, RotationOrigin, g_XMSelect1100.v); + XMMATRIX MRotation = XMMatrixRotationZ(Rotation); + XMVECTOR VTranslation = XMVectorSelect(g_XMSelect1100.v, Translation, g_XMSelect1100.v); + + XMMATRIX M; + M = MScaling; + M.r[3] = XMVectorSubtract(M.r[3], VRotationOrigin); + M = XMMatrixMultiply(M, MRotation); + M.r[3] = XMVectorAdd(M.r[3], VRotationOrigin); + M.r[3] = XMVectorAdd(M.r[3], VTranslation); + return M; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixAffineTransformation +( + FXMVECTOR Scaling, + FXMVECTOR RotationOrigin, + FXMVECTOR RotationQuaternion, + GXMVECTOR Translation +) noexcept +{ + // M = MScaling * Inverse(MRotationOrigin) * MRotation * MRotationOrigin * MTranslation; + + XMMATRIX MScaling = XMMatrixScalingFromVector(Scaling); + XMVECTOR VRotationOrigin = XMVectorSelect(g_XMSelect1110.v, RotationOrigin, g_XMSelect1110.v); + XMMATRIX MRotation = XMMatrixRotationQuaternion(RotationQuaternion); + XMVECTOR VTranslation = XMVectorSelect(g_XMSelect1110.v, Translation, g_XMSelect1110.v); + + XMMATRIX M; + M = MScaling; + M.r[3] = XMVectorSubtract(M.r[3], VRotationOrigin); + M = XMMatrixMultiply(M, MRotation); + M.r[3] = XMVectorAdd(M.r[3], VRotationOrigin); + M.r[3] = XMVectorAdd(M.r[3], VTranslation); + return M; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixReflect(FXMVECTOR ReflectionPlane) noexcept +{ + assert(!XMVector3Equal(ReflectionPlane, XMVectorZero())); + assert(!XMPlaneIsInfinite(ReflectionPlane)); + + static const XMVECTORF32 NegativeTwo = { { { -2.0f, -2.0f, -2.0f, 0.0f } } }; + + XMVECTOR P = XMPlaneNormalize(ReflectionPlane); + XMVECTOR S = XMVectorMultiply(P, NegativeTwo); + + XMVECTOR A = XMVectorSplatX(P); + XMVECTOR B = XMVectorSplatY(P); + XMVECTOR C = XMVectorSplatZ(P); + XMVECTOR D = XMVectorSplatW(P); + + XMMATRIX M; + M.r[0] = XMVectorMultiplyAdd(A, S, g_XMIdentityR0.v); + M.r[1] = XMVectorMultiplyAdd(B, S, g_XMIdentityR1.v); + M.r[2] = XMVectorMultiplyAdd(C, S, g_XMIdentityR2.v); + M.r[3] = XMVectorMultiplyAdd(D, S, g_XMIdentityR3.v); + return M; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixShadow +( + FXMVECTOR ShadowPlane, + FXMVECTOR LightPosition +) noexcept +{ + static const XMVECTORU32 Select0001 = { { { XM_SELECT_0, XM_SELECT_0, XM_SELECT_0, XM_SELECT_1 } } }; + + assert(!XMVector3Equal(ShadowPlane, XMVectorZero())); + assert(!XMPlaneIsInfinite(ShadowPlane)); + + XMVECTOR P = XMPlaneNormalize(ShadowPlane); + XMVECTOR Dot = XMPlaneDot(P, LightPosition); + P = XMVectorNegate(P); + XMVECTOR D = XMVectorSplatW(P); + XMVECTOR C = XMVectorSplatZ(P); + XMVECTOR B = XMVectorSplatY(P); + XMVECTOR A = XMVectorSplatX(P); + Dot = XMVectorSelect(Select0001.v, Dot, Select0001.v); + + XMMATRIX M; + M.r[3] = XMVectorMultiplyAdd(D, LightPosition, Dot); + Dot = XMVectorRotateLeft(Dot, 1); + M.r[2] = XMVectorMultiplyAdd(C, LightPosition, Dot); + Dot = XMVectorRotateLeft(Dot, 1); + M.r[1] = XMVectorMultiplyAdd(B, LightPosition, Dot); + Dot = XMVectorRotateLeft(Dot, 1); + M.r[0] = XMVectorMultiplyAdd(A, LightPosition, Dot); + return M; +} + +//------------------------------------------------------------------------------ +// View and projection initialization operations +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixLookAtLH +( + FXMVECTOR EyePosition, + FXMVECTOR FocusPosition, + FXMVECTOR UpDirection +) noexcept +{ + XMVECTOR EyeDirection = XMVectorSubtract(FocusPosition, EyePosition); + return XMMatrixLookToLH(EyePosition, EyeDirection, UpDirection); +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixLookAtRH +( + FXMVECTOR EyePosition, + FXMVECTOR FocusPosition, + FXMVECTOR UpDirection +) noexcept +{ + XMVECTOR NegEyeDirection = XMVectorSubtract(EyePosition, FocusPosition); + return XMMatrixLookToLH(EyePosition, NegEyeDirection, UpDirection); +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixLookToLH +( + FXMVECTOR EyePosition, + FXMVECTOR EyeDirection, + FXMVECTOR UpDirection +) noexcept +{ + assert(!XMVector3Equal(EyeDirection, XMVectorZero())); + assert(!XMVector3IsInfinite(EyeDirection)); + assert(!XMVector3Equal(UpDirection, XMVectorZero())); + assert(!XMVector3IsInfinite(UpDirection)); + + XMVECTOR R2 = XMVector3Normalize(EyeDirection); + + XMVECTOR R0 = XMVector3Cross(UpDirection, R2); + R0 = XMVector3Normalize(R0); + + XMVECTOR R1 = XMVector3Cross(R2, R0); + + XMVECTOR NegEyePosition = XMVectorNegate(EyePosition); + + XMVECTOR D0 = XMVector3Dot(R0, NegEyePosition); + XMVECTOR D1 = XMVector3Dot(R1, NegEyePosition); + XMVECTOR D2 = XMVector3Dot(R2, NegEyePosition); + + XMMATRIX M; + M.r[0] = XMVectorSelect(D0, R0, g_XMSelect1110.v); + M.r[1] = XMVectorSelect(D1, R1, g_XMSelect1110.v); + M.r[2] = XMVectorSelect(D2, R2, g_XMSelect1110.v); + M.r[3] = g_XMIdentityR3.v; + + M = XMMatrixTranspose(M); + + return M; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixLookToRH +( + FXMVECTOR EyePosition, + FXMVECTOR EyeDirection, + FXMVECTOR UpDirection +) noexcept +{ + XMVECTOR NegEyeDirection = XMVectorNegate(EyeDirection); + return XMMatrixLookToLH(EyePosition, NegEyeDirection, UpDirection); +} + +//------------------------------------------------------------------------------ + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable:28931, "PREfast noise: Esp:1266") +#endif + +inline XMMATRIX XM_CALLCONV XMMatrixPerspectiveLH +( + float ViewWidth, + float ViewHeight, + float NearZ, + float FarZ +) noexcept +{ + assert(NearZ > 0.f && FarZ > 0.f); + assert(!XMScalarNearEqual(ViewWidth, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(ViewHeight, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float TwoNearZ = NearZ + NearZ; + float fRange = FarZ / (FarZ - NearZ); + + XMMATRIX M; + M.m[0][0] = TwoNearZ / ViewWidth; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = TwoNearZ / ViewHeight; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = fRange; + M.m[2][3] = 1.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = -fRange * NearZ; + M.m[3][3] = 0.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float TwoNearZ = NearZ + NearZ; + float fRange = FarZ / (FarZ - NearZ); + const float32x4_t Zero = vdupq_n_f32(0); + XMMATRIX M; + M.r[0] = vsetq_lane_f32(TwoNearZ / ViewWidth, Zero, 0); + M.r[1] = vsetq_lane_f32(TwoNearZ / ViewHeight, Zero, 1); + M.r[2] = vsetq_lane_f32(fRange, g_XMIdentityR3.v, 2); + M.r[3] = vsetq_lane_f32(-fRange * NearZ, Zero, 2); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + float TwoNearZ = NearZ + NearZ; + float fRange = FarZ / (FarZ - NearZ); + // Note: This is recorded on the stack + XMVECTOR rMem = { + TwoNearZ / ViewWidth, + TwoNearZ / ViewHeight, + fRange, + -fRange * NearZ + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // TwoNearZ / ViewWidth,0,0,0 + M.r[0] = vTemp; + // 0,TwoNearZ / ViewHeight,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + M.r[1] = vTemp; + // x=fRange,y=-fRange * NearZ,0,1.0f + vValues = _mm_shuffle_ps(vValues, g_XMIdentityR3, _MM_SHUFFLE(3, 2, 3, 2)); + // 0,0,fRange,1.0f + vTemp = _mm_setzero_ps(); + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 0, 0, 0)); + M.r[2] = vTemp; + // 0,0,-fRange * NearZ,0 + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 1, 0, 0)); + M.r[3] = vTemp; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixPerspectiveRH +( + float ViewWidth, + float ViewHeight, + float NearZ, + float FarZ +) noexcept +{ + assert(NearZ > 0.f && FarZ > 0.f); + assert(!XMScalarNearEqual(ViewWidth, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(ViewHeight, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float TwoNearZ = NearZ + NearZ; + float fRange = FarZ / (NearZ - FarZ); + + XMMATRIX M; + M.m[0][0] = TwoNearZ / ViewWidth; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = TwoNearZ / ViewHeight; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = fRange; + M.m[2][3] = -1.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = fRange * NearZ; + M.m[3][3] = 0.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float TwoNearZ = NearZ + NearZ; + float fRange = FarZ / (NearZ - FarZ); + const float32x4_t Zero = vdupq_n_f32(0); + + XMMATRIX M; + M.r[0] = vsetq_lane_f32(TwoNearZ / ViewWidth, Zero, 0); + M.r[1] = vsetq_lane_f32(TwoNearZ / ViewHeight, Zero, 1); + M.r[2] = vsetq_lane_f32(fRange, g_XMNegIdentityR3.v, 2); + M.r[3] = vsetq_lane_f32(fRange * NearZ, Zero, 2); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + float TwoNearZ = NearZ + NearZ; + float fRange = FarZ / (NearZ - FarZ); + // Note: This is recorded on the stack + XMVECTOR rMem = { + TwoNearZ / ViewWidth, + TwoNearZ / ViewHeight, + fRange, + fRange * NearZ + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // TwoNearZ / ViewWidth,0,0,0 + M.r[0] = vTemp; + // 0,TwoNearZ / ViewHeight,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + M.r[1] = vTemp; + // x=fRange,y=-fRange * NearZ,0,-1.0f + vValues = _mm_shuffle_ps(vValues, g_XMNegIdentityR3, _MM_SHUFFLE(3, 2, 3, 2)); + // 0,0,fRange,-1.0f + vTemp = _mm_setzero_ps(); + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 0, 0, 0)); + M.r[2] = vTemp; + // 0,0,-fRange * NearZ,0 + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 1, 0, 0)); + M.r[3] = vTemp; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovLH +( + float FovAngleY, + float AspectRatio, + float NearZ, + float FarZ +) noexcept +{ + assert(NearZ > 0.f && FarZ > 0.f); + assert(!XMScalarNearEqual(FovAngleY, 0.0f, 0.00001f * 2.0f)); + assert(!XMScalarNearEqual(AspectRatio, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float SinFov; + float CosFov; + XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY); + + float Height = CosFov / SinFov; + float Width = Height / AspectRatio; + float fRange = FarZ / (FarZ - NearZ); + + XMMATRIX M; + M.m[0][0] = Width; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = Height; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = fRange; + M.m[2][3] = 1.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = -fRange * NearZ; + M.m[3][3] = 0.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float SinFov; + float CosFov; + XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY); + + float fRange = FarZ / (FarZ - NearZ); + float Height = CosFov / SinFov; + float Width = Height / AspectRatio; + const float32x4_t Zero = vdupq_n_f32(0); + + XMMATRIX M; + M.r[0] = vsetq_lane_f32(Width, Zero, 0); + M.r[1] = vsetq_lane_f32(Height, Zero, 1); + M.r[2] = vsetq_lane_f32(fRange, g_XMIdentityR3.v, 2); + M.r[3] = vsetq_lane_f32(-fRange * NearZ, Zero, 2); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + float SinFov; + float CosFov; + XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY); + + float fRange = FarZ / (FarZ - NearZ); + // Note: This is recorded on the stack + float Height = CosFov / SinFov; + XMVECTOR rMem = { + Height / AspectRatio, + Height, + fRange, + -fRange * NearZ + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // Height / AspectRatio,0,0,0 + XMMATRIX M; + M.r[0] = vTemp; + // 0,Height,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + M.r[1] = vTemp; + // x=fRange,y=-fRange * NearZ,0,1.0f + vTemp = _mm_setzero_ps(); + vValues = _mm_shuffle_ps(vValues, g_XMIdentityR3, _MM_SHUFFLE(3, 2, 3, 2)); + // 0,0,fRange,1.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 0, 0, 0)); + M.r[2] = vTemp; + // 0,0,-fRange * NearZ,0.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 1, 0, 0)); + M.r[3] = vTemp; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovRH +( + float FovAngleY, + float AspectRatio, + float NearZ, + float FarZ +) noexcept +{ + assert(NearZ > 0.f && FarZ > 0.f); + assert(!XMScalarNearEqual(FovAngleY, 0.0f, 0.00001f * 2.0f)); + assert(!XMScalarNearEqual(AspectRatio, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float SinFov; + float CosFov; + XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY); + + float Height = CosFov / SinFov; + float Width = Height / AspectRatio; + float fRange = FarZ / (NearZ - FarZ); + + XMMATRIX M; + M.m[0][0] = Width; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = Height; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = fRange; + M.m[2][3] = -1.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = fRange * NearZ; + M.m[3][3] = 0.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float SinFov; + float CosFov; + XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY); + float fRange = FarZ / (NearZ - FarZ); + float Height = CosFov / SinFov; + float Width = Height / AspectRatio; + const float32x4_t Zero = vdupq_n_f32(0); + + XMMATRIX M; + M.r[0] = vsetq_lane_f32(Width, Zero, 0); + M.r[1] = vsetq_lane_f32(Height, Zero, 1); + M.r[2] = vsetq_lane_f32(fRange, g_XMNegIdentityR3.v, 2); + M.r[3] = vsetq_lane_f32(fRange * NearZ, Zero, 2); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + float SinFov; + float CosFov; + XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY); + float fRange = FarZ / (NearZ - FarZ); + // Note: This is recorded on the stack + float Height = CosFov / SinFov; + XMVECTOR rMem = { + Height / AspectRatio, + Height, + fRange, + fRange * NearZ + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // Height / AspectRatio,0,0,0 + XMMATRIX M; + M.r[0] = vTemp; + // 0,Height,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + M.r[1] = vTemp; + // x=fRange,y=-fRange * NearZ,0,-1.0f + vTemp = _mm_setzero_ps(); + vValues = _mm_shuffle_ps(vValues, g_XMNegIdentityR3, _MM_SHUFFLE(3, 2, 3, 2)); + // 0,0,fRange,-1.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 0, 0, 0)); + M.r[2] = vTemp; + // 0,0,fRange * NearZ,0.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 1, 0, 0)); + M.r[3] = vTemp; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixPerspectiveOffCenterLH +( + float ViewLeft, + float ViewRight, + float ViewBottom, + float ViewTop, + float NearZ, + float FarZ +) noexcept +{ + assert(NearZ > 0.f && FarZ > 0.f); + assert(!XMScalarNearEqual(ViewRight, ViewLeft, 0.00001f)); + assert(!XMScalarNearEqual(ViewTop, ViewBottom, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float TwoNearZ = NearZ + NearZ; + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = FarZ / (FarZ - NearZ); + + XMMATRIX M; + M.m[0][0] = TwoNearZ * ReciprocalWidth; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = TwoNearZ * ReciprocalHeight; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = -(ViewLeft + ViewRight) * ReciprocalWidth; + M.m[2][1] = -(ViewTop + ViewBottom) * ReciprocalHeight; + M.m[2][2] = fRange; + M.m[2][3] = 1.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = -fRange * NearZ; + M.m[3][3] = 0.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float TwoNearZ = NearZ + NearZ; + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = FarZ / (FarZ - NearZ); + const float32x4_t Zero = vdupq_n_f32(0); + + XMMATRIX M; + M.r[0] = vsetq_lane_f32(TwoNearZ * ReciprocalWidth, Zero, 0); + M.r[1] = vsetq_lane_f32(TwoNearZ * ReciprocalHeight, Zero, 1); + M.r[2] = XMVectorSet(-(ViewLeft + ViewRight) * ReciprocalWidth, + -(ViewTop + ViewBottom) * ReciprocalHeight, + fRange, + 1.0f); + M.r[3] = vsetq_lane_f32(-fRange * NearZ, Zero, 2); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + float TwoNearZ = NearZ + NearZ; + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = FarZ / (FarZ - NearZ); + // Note: This is recorded on the stack + XMVECTOR rMem = { + TwoNearZ * ReciprocalWidth, + TwoNearZ * ReciprocalHeight, + -fRange * NearZ, + 0 + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // TwoNearZ*ReciprocalWidth,0,0,0 + M.r[0] = vTemp; + // 0,TwoNearZ*ReciprocalHeight,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + M.r[1] = vTemp; + // 0,0,fRange,1.0f + M.r[2] = XMVectorSet(-(ViewLeft + ViewRight) * ReciprocalWidth, + -(ViewTop + ViewBottom) * ReciprocalHeight, + fRange, + 1.0f); + // 0,0,-fRange * NearZ,0.0f + vValues = _mm_and_ps(vValues, g_XMMaskZ); + M.r[3] = vValues; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixPerspectiveOffCenterRH +( + float ViewLeft, + float ViewRight, + float ViewBottom, + float ViewTop, + float NearZ, + float FarZ +) noexcept +{ + assert(NearZ > 0.f && FarZ > 0.f); + assert(!XMScalarNearEqual(ViewRight, ViewLeft, 0.00001f)); + assert(!XMScalarNearEqual(ViewTop, ViewBottom, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float TwoNearZ = NearZ + NearZ; + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = FarZ / (NearZ - FarZ); + + XMMATRIX M; + M.m[0][0] = TwoNearZ * ReciprocalWidth; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = TwoNearZ * ReciprocalHeight; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = (ViewLeft + ViewRight) * ReciprocalWidth; + M.m[2][1] = (ViewTop + ViewBottom) * ReciprocalHeight; + M.m[2][2] = fRange; + M.m[2][3] = -1.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = fRange * NearZ; + M.m[3][3] = 0.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float TwoNearZ = NearZ + NearZ; + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = FarZ / (NearZ - FarZ); + const float32x4_t Zero = vdupq_n_f32(0); + + XMMATRIX M; + M.r[0] = vsetq_lane_f32(TwoNearZ * ReciprocalWidth, Zero, 0); + M.r[1] = vsetq_lane_f32(TwoNearZ * ReciprocalHeight, Zero, 1); + M.r[2] = XMVectorSet((ViewLeft + ViewRight) * ReciprocalWidth, + (ViewTop + ViewBottom) * ReciprocalHeight, + fRange, + -1.0f); + M.r[3] = vsetq_lane_f32(fRange * NearZ, Zero, 2); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + float TwoNearZ = NearZ + NearZ; + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = FarZ / (NearZ - FarZ); + // Note: This is recorded on the stack + XMVECTOR rMem = { + TwoNearZ * ReciprocalWidth, + TwoNearZ * ReciprocalHeight, + fRange * NearZ, + 0 + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // TwoNearZ*ReciprocalWidth,0,0,0 + M.r[0] = vTemp; + // 0,TwoNearZ*ReciprocalHeight,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + M.r[1] = vTemp; + // 0,0,fRange,1.0f + M.r[2] = XMVectorSet((ViewLeft + ViewRight) * ReciprocalWidth, + (ViewTop + ViewBottom) * ReciprocalHeight, + fRange, + -1.0f); + // 0,0,-fRange * NearZ,0.0f + vValues = _mm_and_ps(vValues, g_XMMaskZ); + M.r[3] = vValues; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixOrthographicLH +( + float ViewWidth, + float ViewHeight, + float NearZ, + float FarZ +) noexcept +{ + assert(!XMScalarNearEqual(ViewWidth, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(ViewHeight, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float fRange = 1.0f / (FarZ - NearZ); + + XMMATRIX M; + M.m[0][0] = 2.0f / ViewWidth; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = 2.0f / ViewHeight; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = fRange; + M.m[2][3] = 0.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = -fRange * NearZ; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float fRange = 1.0f / (FarZ - NearZ); + + const float32x4_t Zero = vdupq_n_f32(0); + XMMATRIX M; + M.r[0] = vsetq_lane_f32(2.0f / ViewWidth, Zero, 0); + M.r[1] = vsetq_lane_f32(2.0f / ViewHeight, Zero, 1); + M.r[2] = vsetq_lane_f32(fRange, Zero, 2); + M.r[3] = vsetq_lane_f32(-fRange * NearZ, g_XMIdentityR3.v, 2); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + float fRange = 1.0f / (FarZ - NearZ); + // Note: This is recorded on the stack + XMVECTOR rMem = { + 2.0f / ViewWidth, + 2.0f / ViewHeight, + fRange, + -fRange * NearZ + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // 2.0f / ViewWidth,0,0,0 + M.r[0] = vTemp; + // 0,2.0f / ViewHeight,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + M.r[1] = vTemp; + // x=fRange,y=-fRange * NearZ,0,1.0f + vTemp = _mm_setzero_ps(); + vValues = _mm_shuffle_ps(vValues, g_XMIdentityR3, _MM_SHUFFLE(3, 2, 3, 2)); + // 0,0,fRange,0.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 0, 0, 0)); + M.r[2] = vTemp; + // 0,0,-fRange * NearZ,1.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 1, 0, 0)); + M.r[3] = vTemp; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixOrthographicRH +( + float ViewWidth, + float ViewHeight, + float NearZ, + float FarZ +) noexcept +{ + assert(!XMScalarNearEqual(ViewWidth, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(ViewHeight, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float fRange = 1.0f / (NearZ - FarZ); + + XMMATRIX M; + M.m[0][0] = 2.0f / ViewWidth; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = 2.0f / ViewHeight; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = fRange; + M.m[2][3] = 0.0f; + + M.m[3][0] = 0.0f; + M.m[3][1] = 0.0f; + M.m[3][2] = fRange * NearZ; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float fRange = 1.0f / (NearZ - FarZ); + + const float32x4_t Zero = vdupq_n_f32(0); + XMMATRIX M; + M.r[0] = vsetq_lane_f32(2.0f / ViewWidth, Zero, 0); + M.r[1] = vsetq_lane_f32(2.0f / ViewHeight, Zero, 1); + M.r[2] = vsetq_lane_f32(fRange, Zero, 2); + M.r[3] = vsetq_lane_f32(fRange * NearZ, g_XMIdentityR3.v, 2); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + float fRange = 1.0f / (NearZ - FarZ); + // Note: This is recorded on the stack + XMVECTOR rMem = { + 2.0f / ViewWidth, + 2.0f / ViewHeight, + fRange, + fRange * NearZ + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // 2.0f / ViewWidth,0,0,0 + M.r[0] = vTemp; + // 0,2.0f / ViewHeight,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + M.r[1] = vTemp; + // x=fRange,y=fRange * NearZ,0,1.0f + vTemp = _mm_setzero_ps(); + vValues = _mm_shuffle_ps(vValues, g_XMIdentityR3, _MM_SHUFFLE(3, 2, 3, 2)); + // 0,0,fRange,0.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 0, 0, 0)); + M.r[2] = vTemp; + // 0,0,fRange * NearZ,1.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 1, 0, 0)); + M.r[3] = vTemp; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixOrthographicOffCenterLH +( + float ViewLeft, + float ViewRight, + float ViewBottom, + float ViewTop, + float NearZ, + float FarZ +) noexcept +{ + assert(!XMScalarNearEqual(ViewRight, ViewLeft, 0.00001f)); + assert(!XMScalarNearEqual(ViewTop, ViewBottom, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = 1.0f / (FarZ - NearZ); + + XMMATRIX M; + M.m[0][0] = ReciprocalWidth + ReciprocalWidth; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = ReciprocalHeight + ReciprocalHeight; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = fRange; + M.m[2][3] = 0.0f; + + M.m[3][0] = -(ViewLeft + ViewRight) * ReciprocalWidth; + M.m[3][1] = -(ViewTop + ViewBottom) * ReciprocalHeight; + M.m[3][2] = -fRange * NearZ; + M.m[3][3] = 1.0f; + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = 1.0f / (FarZ - NearZ); + const float32x4_t Zero = vdupq_n_f32(0); + XMMATRIX M; + M.r[0] = vsetq_lane_f32(ReciprocalWidth + ReciprocalWidth, Zero, 0); + M.r[1] = vsetq_lane_f32(ReciprocalHeight + ReciprocalHeight, Zero, 1); + M.r[2] = vsetq_lane_f32(fRange, Zero, 2); + M.r[3] = XMVectorSet(-(ViewLeft + ViewRight) * ReciprocalWidth, + -(ViewTop + ViewBottom) * ReciprocalHeight, + -fRange * NearZ, + 1.0f); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + float fReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float fReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = 1.0f / (FarZ - NearZ); + // Note: This is recorded on the stack + XMVECTOR rMem = { + fReciprocalWidth, + fReciprocalHeight, + fRange, + 1.0f + }; + XMVECTOR rMem2 = { + -(ViewLeft + ViewRight), + -(ViewTop + ViewBottom), + -NearZ, + 1.0f + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // fReciprocalWidth*2,0,0,0 + vTemp = _mm_add_ss(vTemp, vTemp); + M.r[0] = vTemp; + // 0,fReciprocalHeight*2,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + vTemp = _mm_add_ps(vTemp, vTemp); + M.r[1] = vTemp; + // 0,0,fRange,0.0f + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskZ); + M.r[2] = vTemp; + // -(ViewLeft + ViewRight)*fReciprocalWidth,-(ViewTop + ViewBottom)*fReciprocalHeight,fRange*-NearZ,1.0f + vValues = _mm_mul_ps(vValues, rMem2); + M.r[3] = vValues; + return M; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMatrixOrthographicOffCenterRH +( + float ViewLeft, + float ViewRight, + float ViewBottom, + float ViewTop, + float NearZ, + float FarZ +) noexcept +{ + assert(!XMScalarNearEqual(ViewRight, ViewLeft, 0.00001f)); + assert(!XMScalarNearEqual(ViewTop, ViewBottom, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + +#if defined(_XM_NO_INTRINSICS_) + + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = 1.0f / (NearZ - FarZ); + + XMMATRIX M; + M.m[0][0] = ReciprocalWidth + ReciprocalWidth; + M.m[0][1] = 0.0f; + M.m[0][2] = 0.0f; + M.m[0][3] = 0.0f; + + M.m[1][0] = 0.0f; + M.m[1][1] = ReciprocalHeight + ReciprocalHeight; + M.m[1][2] = 0.0f; + M.m[1][3] = 0.0f; + + M.m[2][0] = 0.0f; + M.m[2][1] = 0.0f; + M.m[2][2] = fRange; + M.m[2][3] = 0.0f; + + M.r[3] = XMVectorSet(-(ViewLeft + ViewRight) * ReciprocalWidth, + -(ViewTop + ViewBottom) * ReciprocalHeight, + fRange * NearZ, + 1.0f); + return M; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = 1.0f / (NearZ - FarZ); + const float32x4_t Zero = vdupq_n_f32(0); + XMMATRIX M; + M.r[0] = vsetq_lane_f32(ReciprocalWidth + ReciprocalWidth, Zero, 0); + M.r[1] = vsetq_lane_f32(ReciprocalHeight + ReciprocalHeight, Zero, 1); + M.r[2] = vsetq_lane_f32(fRange, Zero, 2); + M.r[3] = XMVectorSet(-(ViewLeft + ViewRight) * ReciprocalWidth, + -(ViewTop + ViewBottom) * ReciprocalHeight, + fRange * NearZ, + 1.0f); + return M; +#elif defined(_XM_SSE_INTRINSICS_) + XMMATRIX M; + float fReciprocalWidth = 1.0f / (ViewRight - ViewLeft); + float fReciprocalHeight = 1.0f / (ViewTop - ViewBottom); + float fRange = 1.0f / (NearZ - FarZ); + // Note: This is recorded on the stack + XMVECTOR rMem = { + fReciprocalWidth, + fReciprocalHeight, + fRange, + 1.0f + }; + XMVECTOR rMem2 = { + -(ViewLeft + ViewRight), + -(ViewTop + ViewBottom), + NearZ, + 1.0f + }; + // Copy from memory to SSE register + XMVECTOR vValues = rMem; + XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // fReciprocalWidth*2,0,0,0 + vTemp = _mm_add_ss(vTemp, vTemp); + M.r[0] = vTemp; + // 0,fReciprocalHeight*2,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskY); + vTemp = _mm_add_ps(vTemp, vTemp); + M.r[1] = vTemp; + // 0,0,fRange,0.0f + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, g_XMMaskZ); + M.r[2] = vTemp; + // -(ViewLeft + ViewRight)*fReciprocalWidth,-(ViewTop + ViewBottom)*fReciprocalHeight,fRange*-NearZ,1.0f + vValues = _mm_mul_ps(vValues, rMem2); + M.r[3] = vValues; + return M; +#endif +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + +/**************************************************************************** + * + * XMMATRIX operators and methods + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMMATRIX::XMMATRIX +( + 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 +{ + r[0] = XMVectorSet(m00, m01, m02, m03); + r[1] = XMVectorSet(m10, m11, m12, m13); + r[2] = XMVectorSet(m20, m21, m22, m23); + r[3] = XMVectorSet(m30, m31, m32, m33); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMMATRIX::XMMATRIX(const float* pArray) noexcept +{ + assert(pArray != nullptr); + r[0] = XMLoadFloat4(reinterpret_cast(pArray)); + r[1] = XMLoadFloat4(reinterpret_cast(pArray + 4)); + r[2] = XMLoadFloat4(reinterpret_cast(pArray + 8)); + r[3] = XMLoadFloat4(reinterpret_cast(pArray + 12)); +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XMMATRIX::operator- () const noexcept +{ + XMMATRIX R; + R.r[0] = XMVectorNegate(r[0]); + R.r[1] = XMVectorNegate(r[1]); + R.r[2] = XMVectorNegate(r[2]); + R.r[3] = XMVectorNegate(r[3]); + return R; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX& XM_CALLCONV XMMATRIX::operator+= (FXMMATRIX M) noexcept +{ + r[0] = XMVectorAdd(r[0], M.r[0]); + r[1] = XMVectorAdd(r[1], M.r[1]); + r[2] = XMVectorAdd(r[2], M.r[2]); + r[3] = XMVectorAdd(r[3], M.r[3]); + return *this; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX& XM_CALLCONV XMMATRIX::operator-= (FXMMATRIX M) noexcept +{ + r[0] = XMVectorSubtract(r[0], M.r[0]); + r[1] = XMVectorSubtract(r[1], M.r[1]); + r[2] = XMVectorSubtract(r[2], M.r[2]); + r[3] = XMVectorSubtract(r[3], M.r[3]); + return *this; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX& XM_CALLCONV XMMATRIX::operator*=(FXMMATRIX M) noexcept +{ + *this = XMMatrixMultiply(*this, M); + return *this; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX& XMMATRIX::operator*= (float S) noexcept +{ + r[0] = XMVectorScale(r[0], S); + r[1] = XMVectorScale(r[1], S); + r[2] = XMVectorScale(r[2], S); + r[3] = XMVectorScale(r[3], S); + return *this; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX& XMMATRIX::operator/= (float S) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR vS = XMVectorReplicate(S); + r[0] = XMVectorDivide(r[0], vS); + r[1] = XMVectorDivide(r[1], vS); + r[2] = XMVectorDivide(r[2], vS); + r[3] = XMVectorDivide(r[3], vS); + return *this; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + float32x4_t vS = vdupq_n_f32(S); + r[0] = vdivq_f32(r[0], vS); + r[1] = vdivq_f32(r[1], vS); + r[2] = vdivq_f32(r[2], vS); + r[3] = vdivq_f32(r[3], vS); +#else + // 2 iterations of Newton-Raphson refinement of reciprocal + float32x2_t vS = vdup_n_f32(S); + float32x2_t R0 = vrecpe_f32(vS); + float32x2_t S0 = vrecps_f32(R0, vS); + R0 = vmul_f32(S0, R0); + S0 = vrecps_f32(R0, vS); + R0 = vmul_f32(S0, R0); + float32x4_t Reciprocal = vcombine_f32(R0, R0); + r[0] = vmulq_f32(r[0], Reciprocal); + r[1] = vmulq_f32(r[1], Reciprocal); + r[2] = vmulq_f32(r[2], Reciprocal); + r[3] = vmulq_f32(r[3], Reciprocal); +#endif + return *this; +#elif defined(_XM_SSE_INTRINSICS_) + __m128 vS = _mm_set_ps1(S); + r[0] = _mm_div_ps(r[0], vS); + r[1] = _mm_div_ps(r[1], vS); + r[2] = _mm_div_ps(r[2], vS); + r[3] = _mm_div_ps(r[3], vS); + return *this; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMATRIX::operator+ (FXMMATRIX M) const noexcept +{ + XMMATRIX R; + R.r[0] = XMVectorAdd(r[0], M.r[0]); + R.r[1] = XMVectorAdd(r[1], M.r[1]); + R.r[2] = XMVectorAdd(r[2], M.r[2]); + R.r[3] = XMVectorAdd(r[3], M.r[3]); + return R; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMATRIX::operator- (FXMMATRIX M) const noexcept +{ + XMMATRIX R; + R.r[0] = XMVectorSubtract(r[0], M.r[0]); + R.r[1] = XMVectorSubtract(r[1], M.r[1]); + R.r[2] = XMVectorSubtract(r[2], M.r[2]); + R.r[3] = XMVectorSubtract(r[3], M.r[3]); + return R; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV XMMATRIX::operator*(FXMMATRIX M) const noexcept +{ + return XMMatrixMultiply(*this, M); +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XMMATRIX::operator* (float S) const noexcept +{ + XMMATRIX R; + R.r[0] = XMVectorScale(r[0], S); + R.r[1] = XMVectorScale(r[1], S); + R.r[2] = XMVectorScale(r[2], S); + R.r[3] = XMVectorScale(r[3], S); + return R; +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XMMATRIX::operator/ (float S) const noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR vS = XMVectorReplicate(S); + XMMATRIX R; + R.r[0] = XMVectorDivide(r[0], vS); + R.r[1] = XMVectorDivide(r[1], vS); + R.r[2] = XMVectorDivide(r[2], vS); + R.r[3] = XMVectorDivide(r[3], vS); + return R; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + float32x4_t vS = vdupq_n_f32(S); + XMMATRIX R; + R.r[0] = vdivq_f32(r[0], vS); + R.r[1] = vdivq_f32(r[1], vS); + R.r[2] = vdivq_f32(r[2], vS); + R.r[3] = vdivq_f32(r[3], vS); +#else + // 2 iterations of Newton-Raphson refinement of reciprocal + float32x2_t vS = vdup_n_f32(S); + float32x2_t R0 = vrecpe_f32(vS); + float32x2_t S0 = vrecps_f32(R0, vS); + R0 = vmul_f32(S0, R0); + S0 = vrecps_f32(R0, vS); + R0 = vmul_f32(S0, R0); + float32x4_t Reciprocal = vcombine_f32(R0, R0); + XMMATRIX R; + R.r[0] = vmulq_f32(r[0], Reciprocal); + R.r[1] = vmulq_f32(r[1], Reciprocal); + R.r[2] = vmulq_f32(r[2], Reciprocal); + R.r[3] = vmulq_f32(r[3], Reciprocal); +#endif + return R; +#elif defined(_XM_SSE_INTRINSICS_) + __m128 vS = _mm_set_ps1(S); + XMMATRIX R; + R.r[0] = _mm_div_ps(r[0], vS); + R.r[1] = _mm_div_ps(r[1], vS); + R.r[2] = _mm_div_ps(r[2], vS); + R.r[3] = _mm_div_ps(r[3], vS); + return R; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMMATRIX XM_CALLCONV operator* +( + float S, + FXMMATRIX M + ) noexcept +{ + XMMATRIX R; + R.r[0] = XMVectorScale(M.r[0], S); + R.r[1] = XMVectorScale(M.r[1], S); + R.r[2] = XMVectorScale(M.r[2], S); + R.r[3] = XMVectorScale(M.r[3], S); + return R; +} + +/**************************************************************************** + * + * XMFLOAT3X3 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMFLOAT3X3::XMFLOAT3X3(const float* pArray) noexcept +{ + assert(pArray != nullptr); + for (size_t Row = 0; Row < 3; Row++) + { + for (size_t Column = 0; Column < 3; Column++) + { + m[Row][Column] = pArray[Row * 3 + Column]; + } + } +} + +/**************************************************************************** + * + * XMFLOAT4X3 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMFLOAT4X3::XMFLOAT4X3(const float* pArray) noexcept +{ + assert(pArray != nullptr); + + m[0][0] = pArray[0]; + m[0][1] = pArray[1]; + m[0][2] = pArray[2]; + + m[1][0] = pArray[3]; + m[1][1] = pArray[4]; + m[1][2] = pArray[5]; + + m[2][0] = pArray[6]; + m[2][1] = pArray[7]; + m[2][2] = pArray[8]; + + m[3][0] = pArray[9]; + m[3][1] = pArray[10]; + m[3][2] = pArray[11]; +} + +/**************************************************************************** +* +* XMFLOAT3X4 operators +* +****************************************************************************/ + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMFLOAT3X4::XMFLOAT3X4(const float* pArray) noexcept +{ + assert(pArray != nullptr); + + m[0][0] = pArray[0]; + m[0][1] = pArray[1]; + m[0][2] = pArray[2]; + m[0][3] = pArray[3]; + + m[1][0] = pArray[4]; + m[1][1] = pArray[5]; + m[1][2] = pArray[6]; + m[1][3] = pArray[7]; + + m[2][0] = pArray[8]; + m[2][1] = pArray[9]; + m[2][2] = pArray[10]; + m[2][3] = pArray[11]; +} + +/**************************************************************************** + * + * XMFLOAT4X4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMFLOAT4X4::XMFLOAT4X4(const float* pArray) noexcept +{ + assert(pArray != nullptr); + + m[0][0] = pArray[0]; + m[0][1] = pArray[1]; + m[0][2] = pArray[2]; + m[0][3] = pArray[3]; + + m[1][0] = pArray[4]; + m[1][1] = pArray[5]; + m[1][2] = pArray[6]; + m[1][3] = pArray[7]; + + m[2][0] = pArray[8]; + m[2][1] = pArray[9]; + m[2][2] = pArray[10]; + m[2][3] = pArray[11]; + + m[3][0] = pArray[12]; + m[3][1] = pArray[13]; + m[3][2] = pArray[14]; + m[3][3] = pArray[15]; +} + diff --git a/Extern/dxmath/Inc/DirectXMathMisc.inl b/Extern/dxmath/Inc/DirectXMathMisc.inl new file mode 100644 index 000000000..7a21dc8b8 --- /dev/null +++ b/Extern/dxmath/Inc/DirectXMathMisc.inl @@ -0,0 +1,2493 @@ +//------------------------------------------------------------------------------------- +// DirectXMathMisc.inl -- SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +/**************************************************************************** + * + * Quaternion + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparison operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMQuaternionEqual +( + FXMVECTOR Q1, + FXMVECTOR Q2 +) noexcept +{ + return XMVector4Equal(Q1, Q2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMQuaternionNotEqual +( + FXMVECTOR Q1, + FXMVECTOR Q2 +) noexcept +{ + return XMVector4NotEqual(Q1, Q2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMQuaternionIsNaN(FXMVECTOR Q) noexcept +{ + return XMVector4IsNaN(Q); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMQuaternionIsInfinite(FXMVECTOR Q) noexcept +{ + return XMVector4IsInfinite(Q); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMQuaternionIsIdentity(FXMVECTOR Q) noexcept +{ + return XMVector4Equal(Q, g_XMIdentityR3.v); +} + +//------------------------------------------------------------------------------ +// Computation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionDot +( + FXMVECTOR Q1, + FXMVECTOR Q2 +) noexcept +{ + return XMVector4Dot(Q1, Q2); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionMultiply +( + FXMVECTOR Q1, + FXMVECTOR Q2 +) noexcept +{ + // Returns the product Q2*Q1 (which is the concatenation of a rotation Q1 followed by the rotation Q2) + + // [ (Q2.w * Q1.x) + (Q2.x * Q1.w) + (Q2.y * Q1.z) - (Q2.z * Q1.y), + // (Q2.w * Q1.y) - (Q2.x * Q1.z) + (Q2.y * Q1.w) + (Q2.z * Q1.x), + // (Q2.w * Q1.z) + (Q2.x * Q1.y) - (Q2.y * Q1.x) + (Q2.z * Q1.w), + // (Q2.w * Q1.w) - (Q2.x * Q1.x) - (Q2.y * Q1.y) - (Q2.z * Q1.z) ] + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + (Q2.vector4_f32[3] * Q1.vector4_f32[0]) + (Q2.vector4_f32[0] * Q1.vector4_f32[3]) + (Q2.vector4_f32[1] * Q1.vector4_f32[2]) - (Q2.vector4_f32[2] * Q1.vector4_f32[1]), + (Q2.vector4_f32[3] * Q1.vector4_f32[1]) - (Q2.vector4_f32[0] * Q1.vector4_f32[2]) + (Q2.vector4_f32[1] * Q1.vector4_f32[3]) + (Q2.vector4_f32[2] * Q1.vector4_f32[0]), + (Q2.vector4_f32[3] * Q1.vector4_f32[2]) + (Q2.vector4_f32[0] * Q1.vector4_f32[1]) - (Q2.vector4_f32[1] * Q1.vector4_f32[0]) + (Q2.vector4_f32[2] * Q1.vector4_f32[3]), + (Q2.vector4_f32[3] * Q1.vector4_f32[3]) - (Q2.vector4_f32[0] * Q1.vector4_f32[0]) - (Q2.vector4_f32[1] * Q1.vector4_f32[1]) - (Q2.vector4_f32[2] * Q1.vector4_f32[2]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 ControlWZYX = { { { 1.0f, -1.0f, 1.0f, -1.0f } } }; + static const XMVECTORF32 ControlZWXY = { { { 1.0f, 1.0f, -1.0f, -1.0f } } }; + static const XMVECTORF32 ControlYXWZ = { { { -1.0f, 1.0f, 1.0f, -1.0f } } }; + + float32x2_t Q2L = vget_low_f32(Q2); + float32x2_t Q2H = vget_high_f32(Q2); + + float32x4_t Q2X = vdupq_lane_f32(Q2L, 0); + float32x4_t Q2Y = vdupq_lane_f32(Q2L, 1); + float32x4_t Q2Z = vdupq_lane_f32(Q2H, 0); + XMVECTOR vResult = vmulq_lane_f32(Q1, Q2H, 1); + + // Mul by Q1WZYX + float32x4_t vTemp = vrev64q_f32(Q1); + vTemp = vcombine_f32(vget_high_f32(vTemp), vget_low_f32(vTemp)); + Q2X = vmulq_f32(Q2X, vTemp); + vResult = vmlaq_f32(vResult, Q2X, ControlWZYX); + + // Mul by Q1ZWXY + vTemp = vreinterpretq_f32_u32(vrev64q_u32(vreinterpretq_u32_f32(vTemp))); + Q2Y = vmulq_f32(Q2Y, vTemp); + vResult = vmlaq_f32(vResult, Q2Y, ControlZWXY); + + // Mul by Q1YXWZ + vTemp = vreinterpretq_f32_u32(vrev64q_u32(vreinterpretq_u32_f32(vTemp))); + vTemp = vcombine_f32(vget_high_f32(vTemp), vget_low_f32(vTemp)); + Q2Z = vmulq_f32(Q2Z, vTemp); + vResult = vmlaq_f32(vResult, Q2Z, ControlYXWZ); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ControlWZYX = { { { 1.0f, -1.0f, 1.0f, -1.0f } } }; + static const XMVECTORF32 ControlZWXY = { { { 1.0f, 1.0f, -1.0f, -1.0f } } }; + static const XMVECTORF32 ControlYXWZ = { { { -1.0f, 1.0f, 1.0f, -1.0f } } }; + // Copy to SSE registers and use as few as possible for x86 + XMVECTOR Q2X = Q2; + XMVECTOR Q2Y = Q2; + XMVECTOR Q2Z = Q2; + XMVECTOR vResult = Q2; + // Splat with one instruction + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 3, 3, 3)); + Q2X = XM_PERMUTE_PS(Q2X, _MM_SHUFFLE(0, 0, 0, 0)); + Q2Y = XM_PERMUTE_PS(Q2Y, _MM_SHUFFLE(1, 1, 1, 1)); + Q2Z = XM_PERMUTE_PS(Q2Z, _MM_SHUFFLE(2, 2, 2, 2)); + // Retire Q1 and perform Q1*Q2W + vResult = _mm_mul_ps(vResult, Q1); + XMVECTOR Q1Shuffle = Q1; + // Shuffle the copies of Q1 + Q1Shuffle = XM_PERMUTE_PS(Q1Shuffle, _MM_SHUFFLE(0, 1, 2, 3)); + // Mul by Q1WZYX + Q2X = _mm_mul_ps(Q2X, Q1Shuffle); + Q1Shuffle = XM_PERMUTE_PS(Q1Shuffle, _MM_SHUFFLE(2, 3, 0, 1)); + // Flip the signs on y and z + vResult = XM_FMADD_PS(Q2X, ControlWZYX, vResult); + // Mul by Q1ZWXY + Q2Y = _mm_mul_ps(Q2Y, Q1Shuffle); + Q1Shuffle = XM_PERMUTE_PS(Q1Shuffle, _MM_SHUFFLE(0, 1, 2, 3)); + // Flip the signs on z and w + Q2Y = _mm_mul_ps(Q2Y, ControlZWXY); + // Mul by Q1YXWZ + Q2Z = _mm_mul_ps(Q2Z, Q1Shuffle); + // Flip the signs on x and w + Q2Y = XM_FMADD_PS(Q2Z, ControlYXWZ, Q2Y); + vResult = _mm_add_ps(vResult, Q2Y); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionLengthSq(FXMVECTOR Q) noexcept +{ + return XMVector4LengthSq(Q); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionReciprocalLength(FXMVECTOR Q) noexcept +{ + return XMVector4ReciprocalLength(Q); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionLength(FXMVECTOR Q) noexcept +{ + return XMVector4Length(Q); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionNormalizeEst(FXMVECTOR Q) noexcept +{ + return XMVector4NormalizeEst(Q); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionNormalize(FXMVECTOR Q) noexcept +{ + return XMVector4Normalize(Q); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionConjugate(FXMVECTOR Q) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + -Q.vector4_f32[0], + -Q.vector4_f32[1], + -Q.vector4_f32[2], + Q.vector4_f32[3] + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 NegativeOne3 = { { { -1.0f, -1.0f, -1.0f, 1.0f } } }; + return vmulq_f32(Q, NegativeOne3.v); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 NegativeOne3 = { { { -1.0f, -1.0f, -1.0f, 1.0f } } }; + return _mm_mul_ps(Q, NegativeOne3); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionInverse(FXMVECTOR Q) noexcept +{ + XMVECTOR L = XMVector4LengthSq(Q); + XMVECTOR Conjugate = XMQuaternionConjugate(Q); + + XMVECTOR Control = XMVectorLessOrEqual(L, g_XMEpsilon.v); + + XMVECTOR Result = XMVectorDivide(Conjugate, L); + + Result = XMVectorSelect(Result, g_XMZero, Control); + + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionLn(FXMVECTOR Q) noexcept +{ + static const XMVECTORF32 OneMinusEpsilon = { { { 1.0f - 0.00001f, 1.0f - 0.00001f, 1.0f - 0.00001f, 1.0f - 0.00001f } } }; + + XMVECTOR QW = XMVectorSplatW(Q); + XMVECTOR Q0 = XMVectorSelect(g_XMSelect1110.v, Q, g_XMSelect1110.v); + + XMVECTOR ControlW = XMVectorInBounds(QW, OneMinusEpsilon.v); + + XMVECTOR Theta = XMVectorACos(QW); + XMVECTOR SinTheta = XMVectorSin(Theta); + + XMVECTOR S = XMVectorDivide(Theta, SinTheta); + + XMVECTOR Result = XMVectorMultiply(Q0, S); + Result = XMVectorSelect(Q0, Result, ControlW); + + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionExp(FXMVECTOR Q) noexcept +{ + XMVECTOR Theta = XMVector3Length(Q); + + XMVECTOR SinTheta, CosTheta; + XMVectorSinCos(&SinTheta, &CosTheta, Theta); + + XMVECTOR S = XMVectorDivide(SinTheta, Theta); + + XMVECTOR Result = XMVectorMultiply(Q, S); + + const XMVECTOR Zero = XMVectorZero(); + XMVECTOR Control = XMVectorNearEqual(Theta, Zero, g_XMEpsilon.v); + Result = XMVectorSelect(Result, Q, Control); + + Result = XMVectorSelect(CosTheta, Result, g_XMSelect1110.v); + + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionSlerp +( + FXMVECTOR Q0, + FXMVECTOR Q1, + float t +) noexcept +{ + XMVECTOR T = XMVectorReplicate(t); + return XMQuaternionSlerpV(Q0, Q1, T); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionSlerpV +( + FXMVECTOR Q0, + FXMVECTOR Q1, + FXMVECTOR T +) noexcept +{ + assert((XMVectorGetY(T) == XMVectorGetX(T)) && (XMVectorGetZ(T) == XMVectorGetX(T)) && (XMVectorGetW(T) == XMVectorGetX(T))); + + // Result = Q0 * sin((1.0 - t) * Omega) / sin(Omega) + Q1 * sin(t * Omega) / sin(Omega) + +#if defined(_XM_NO_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + + const XMVECTORF32 OneMinusEpsilon = { { { 1.0f - 0.00001f, 1.0f - 0.00001f, 1.0f - 0.00001f, 1.0f - 0.00001f } } }; + + XMVECTOR CosOmega = XMQuaternionDot(Q0, Q1); + + const XMVECTOR Zero = XMVectorZero(); + XMVECTOR Control = XMVectorLess(CosOmega, Zero); + XMVECTOR Sign = XMVectorSelect(g_XMOne.v, g_XMNegativeOne.v, Control); + + CosOmega = XMVectorMultiply(CosOmega, Sign); + + Control = XMVectorLess(CosOmega, OneMinusEpsilon); + + XMVECTOR SinOmega = XMVectorNegativeMultiplySubtract(CosOmega, CosOmega, g_XMOne.v); + SinOmega = XMVectorSqrt(SinOmega); + + XMVECTOR Omega = XMVectorATan2(SinOmega, CosOmega); + + XMVECTOR SignMask = XMVectorSplatSignMask(); + XMVECTOR V01 = XMVectorShiftLeft(T, Zero, 2); + SignMask = XMVectorShiftLeft(SignMask, Zero, 3); + V01 = XMVectorXorInt(V01, SignMask); + V01 = XMVectorAdd(g_XMIdentityR0.v, V01); + + XMVECTOR InvSinOmega = XMVectorReciprocal(SinOmega); + + XMVECTOR S0 = XMVectorMultiply(V01, Omega); + S0 = XMVectorSin(S0); + S0 = XMVectorMultiply(S0, InvSinOmega); + + S0 = XMVectorSelect(V01, S0, Control); + + XMVECTOR S1 = XMVectorSplatY(S0); + S0 = XMVectorSplatX(S0); + + S1 = XMVectorMultiply(S1, Sign); + + XMVECTOR Result = XMVectorMultiply(Q0, S0); + Result = XMVectorMultiplyAdd(Q1, S1, Result); + + return Result; + +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 OneMinusEpsilon = { { { 1.0f - 0.00001f, 1.0f - 0.00001f, 1.0f - 0.00001f, 1.0f - 0.00001f } } }; + static const XMVECTORU32 SignMask2 = { { { 0x80000000, 0x00000000, 0x00000000, 0x00000000 } } }; + + XMVECTOR CosOmega = XMQuaternionDot(Q0, Q1); + + const XMVECTOR Zero = XMVectorZero(); + XMVECTOR Control = XMVectorLess(CosOmega, Zero); + XMVECTOR Sign = XMVectorSelect(g_XMOne, g_XMNegativeOne, Control); + + CosOmega = _mm_mul_ps(CosOmega, Sign); + + Control = XMVectorLess(CosOmega, OneMinusEpsilon); + + XMVECTOR SinOmega = _mm_mul_ps(CosOmega, CosOmega); + SinOmega = _mm_sub_ps(g_XMOne, SinOmega); + SinOmega = _mm_sqrt_ps(SinOmega); + + XMVECTOR Omega = XMVectorATan2(SinOmega, CosOmega); + + XMVECTOR V01 = XM_PERMUTE_PS(T, _MM_SHUFFLE(2, 3, 0, 1)); + V01 = _mm_and_ps(V01, g_XMMaskXY); + V01 = _mm_xor_ps(V01, SignMask2); + V01 = _mm_add_ps(g_XMIdentityR0, V01); + + XMVECTOR S0 = _mm_mul_ps(V01, Omega); + S0 = XMVectorSin(S0); + S0 = _mm_div_ps(S0, SinOmega); + + S0 = XMVectorSelect(V01, S0, Control); + + XMVECTOR S1 = XMVectorSplatY(S0); + S0 = XMVectorSplatX(S0); + + S1 = _mm_mul_ps(S1, Sign); + XMVECTOR Result = _mm_mul_ps(Q0, S0); + S1 = _mm_mul_ps(S1, Q1); + Result = _mm_add_ps(Result, S1); + return Result; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionSquad +( + FXMVECTOR Q0, + FXMVECTOR Q1, + FXMVECTOR Q2, + GXMVECTOR Q3, + float t +) noexcept +{ + XMVECTOR T = XMVectorReplicate(t); + return XMQuaternionSquadV(Q0, Q1, Q2, Q3, T); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionSquadV +( + FXMVECTOR Q0, + FXMVECTOR Q1, + FXMVECTOR Q2, + GXMVECTOR Q3, + HXMVECTOR T +) noexcept +{ + assert((XMVectorGetY(T) == XMVectorGetX(T)) && (XMVectorGetZ(T) == XMVectorGetX(T)) && (XMVectorGetW(T) == XMVectorGetX(T))); + + XMVECTOR TP = T; + const XMVECTOR Two = XMVectorSplatConstant(2, 0); + + XMVECTOR Q03 = XMQuaternionSlerpV(Q0, Q3, T); + XMVECTOR Q12 = XMQuaternionSlerpV(Q1, Q2, T); + + TP = XMVectorNegativeMultiplySubtract(TP, TP, TP); + TP = XMVectorMultiply(TP, Two); + + XMVECTOR Result = XMQuaternionSlerpV(Q03, Q12, TP); + + return Result; +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMQuaternionSquadSetup +( + XMVECTOR* pA, + XMVECTOR* pB, + XMVECTOR* pC, + FXMVECTOR Q0, + FXMVECTOR Q1, + FXMVECTOR Q2, + GXMVECTOR Q3 +) noexcept +{ + assert(pA); + assert(pB); + assert(pC); + + XMVECTOR LS12 = XMQuaternionLengthSq(XMVectorAdd(Q1, Q2)); + XMVECTOR LD12 = XMQuaternionLengthSq(XMVectorSubtract(Q1, Q2)); + XMVECTOR SQ2 = XMVectorNegate(Q2); + + XMVECTOR Control1 = XMVectorLess(LS12, LD12); + SQ2 = XMVectorSelect(Q2, SQ2, Control1); + + XMVECTOR LS01 = XMQuaternionLengthSq(XMVectorAdd(Q0, Q1)); + XMVECTOR LD01 = XMQuaternionLengthSq(XMVectorSubtract(Q0, Q1)); + XMVECTOR SQ0 = XMVectorNegate(Q0); + + XMVECTOR LS23 = XMQuaternionLengthSq(XMVectorAdd(SQ2, Q3)); + XMVECTOR LD23 = XMQuaternionLengthSq(XMVectorSubtract(SQ2, Q3)); + XMVECTOR SQ3 = XMVectorNegate(Q3); + + XMVECTOR Control0 = XMVectorLess(LS01, LD01); + XMVECTOR Control2 = XMVectorLess(LS23, LD23); + + SQ0 = XMVectorSelect(Q0, SQ0, Control0); + SQ3 = XMVectorSelect(Q3, SQ3, Control2); + + XMVECTOR InvQ1 = XMQuaternionInverse(Q1); + XMVECTOR InvQ2 = XMQuaternionInverse(SQ2); + + XMVECTOR LnQ0 = XMQuaternionLn(XMQuaternionMultiply(InvQ1, SQ0)); + XMVECTOR LnQ2 = XMQuaternionLn(XMQuaternionMultiply(InvQ1, SQ2)); + XMVECTOR LnQ1 = XMQuaternionLn(XMQuaternionMultiply(InvQ2, Q1)); + XMVECTOR LnQ3 = XMQuaternionLn(XMQuaternionMultiply(InvQ2, SQ3)); + + const XMVECTOR NegativeOneQuarter = XMVectorSplatConstant(-1, 2); + + XMVECTOR ExpQ02 = XMVectorMultiply(XMVectorAdd(LnQ0, LnQ2), NegativeOneQuarter); + XMVECTOR ExpQ13 = XMVectorMultiply(XMVectorAdd(LnQ1, LnQ3), NegativeOneQuarter); + ExpQ02 = XMQuaternionExp(ExpQ02); + ExpQ13 = XMQuaternionExp(ExpQ13); + + *pA = XMQuaternionMultiply(Q1, ExpQ02); + *pB = XMQuaternionMultiply(SQ2, ExpQ13); + *pC = SQ2; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionBaryCentric +( + FXMVECTOR Q0, + FXMVECTOR Q1, + FXMVECTOR Q2, + float f, + float g +) noexcept +{ + float s = f + g; + + XMVECTOR Result; + if ((s < 0.00001f) && (s > -0.00001f)) + { + Result = Q0; + } + else + { + XMVECTOR Q01 = XMQuaternionSlerp(Q0, Q1, s); + XMVECTOR Q02 = XMQuaternionSlerp(Q0, Q2, s); + + Result = XMQuaternionSlerp(Q01, Q02, g / s); + } + + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionBaryCentricV +( + FXMVECTOR Q0, + FXMVECTOR Q1, + FXMVECTOR Q2, + GXMVECTOR F, + HXMVECTOR G +) noexcept +{ + assert((XMVectorGetY(F) == XMVectorGetX(F)) && (XMVectorGetZ(F) == XMVectorGetX(F)) && (XMVectorGetW(F) == XMVectorGetX(F))); + assert((XMVectorGetY(G) == XMVectorGetX(G)) && (XMVectorGetZ(G) == XMVectorGetX(G)) && (XMVectorGetW(G) == XMVectorGetX(G))); + + const XMVECTOR Epsilon = XMVectorSplatConstant(1, 16); + + XMVECTOR S = XMVectorAdd(F, G); + + XMVECTOR Result; + if (XMVector4InBounds(S, Epsilon)) + { + Result = Q0; + } + else + { + XMVECTOR Q01 = XMQuaternionSlerpV(Q0, Q1, S); + XMVECTOR Q02 = XMQuaternionSlerpV(Q0, Q2, S); + XMVECTOR GS = XMVectorReciprocal(S); + GS = XMVectorMultiply(G, GS); + + Result = XMQuaternionSlerpV(Q01, Q02, GS); + } + + return Result; +} + +//------------------------------------------------------------------------------ +// Transformation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionIdentity() noexcept +{ + return g_XMIdentityR3.v; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionRotationRollPitchYaw +( + float Pitch, + float Yaw, + float Roll +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + const float halfpitch = Pitch * 0.5f; + float cp = cosf(halfpitch); + float sp = sinf(halfpitch); + + const float halfyaw = Yaw * 0.5f; + float cy = cosf(halfyaw); + float sy = sinf(halfyaw); + + const float halfroll = Roll * 0.5f; + float cr = cosf(halfroll); + float sr = sinf(halfroll); + + XMVECTORF32 vResult = { { { + cr * sp * cy + sr * cp * sy, + cr * cp * sy - sr * sp * cy, + sr * cp * cy - cr * sp * sy, + cr * cp * cy + sr * sp * sy + } } }; + return vResult; +#else + XMVECTOR Angles = XMVectorSet(Pitch, Yaw, Roll, 0.0f); + return XMQuaternionRotationRollPitchYawFromVector(Angles); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionRotationRollPitchYawFromVector +( + FXMVECTOR Angles // +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + const float halfpitch = Angles.vector4_f32[0] * 0.5f; + float cp = cosf(halfpitch); + float sp = sinf(halfpitch); + + const float halfyaw = Angles.vector4_f32[1] * 0.5f; + float cy = cosf(halfyaw); + float sy = sinf(halfyaw); + + const float halfroll = Angles.vector4_f32[2] * 0.5f; + float cr = cosf(halfroll); + float sr = sinf(halfroll); + + XMVECTORF32 vResult = { { { + cr * sp * cy + sr * cp * sy, + cr * cp * sy - sr * sp * cy, + sr * cp * cy - cr * sp * sy, + cr * cp * cy + sr * sp * sy + } } }; + return vResult; +#else + static const XMVECTORF32 Sign = { { { 1.0f, -1.0f, -1.0f, 1.0f } } }; + + XMVECTOR HalfAngles = XMVectorMultiply(Angles, g_XMOneHalf.v); + + XMVECTOR SinAngles, CosAngles; + XMVectorSinCos(&SinAngles, &CosAngles, HalfAngles); + + XMVECTOR P0 = XMVectorPermute(SinAngles, CosAngles); + XMVECTOR Y0 = XMVectorPermute(SinAngles, CosAngles); + XMVECTOR R0 = XMVectorPermute(SinAngles, CosAngles); + XMVECTOR P1 = XMVectorPermute(CosAngles, SinAngles); + XMVECTOR Y1 = XMVectorPermute(CosAngles, SinAngles); + XMVECTOR R1 = XMVectorPermute(CosAngles, SinAngles); + + XMVECTOR Q1 = XMVectorMultiply(P1, Sign.v); + XMVECTOR Q0 = XMVectorMultiply(P0, Y0); + Q1 = XMVectorMultiply(Q1, Y1); + Q0 = XMVectorMultiply(Q0, R0); + XMVECTOR Q = XMVectorMultiplyAdd(Q1, R1, Q0); + + return Q; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionRotationNormal +( + FXMVECTOR NormalAxis, + float Angle +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + + XMVECTOR N = XMVectorSelect(g_XMOne.v, NormalAxis, g_XMSelect1110.v); + + float SinV, CosV; + XMScalarSinCos(&SinV, &CosV, 0.5f * Angle); + + XMVECTOR Scale = XMVectorSet(SinV, SinV, SinV, CosV); + return XMVectorMultiply(N, Scale); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR N = _mm_and_ps(NormalAxis, g_XMMask3); + N = _mm_or_ps(N, g_XMIdentityR3); + XMVECTOR Scale = _mm_set_ps1(0.5f * Angle); + XMVECTOR vSine; + XMVECTOR vCosine; + XMVectorSinCos(&vSine, &vCosine, Scale); + Scale = _mm_and_ps(vSine, g_XMMask3); + vCosine = _mm_and_ps(vCosine, g_XMMaskW); + Scale = _mm_or_ps(Scale, vCosine); + N = _mm_mul_ps(N, Scale); + return N; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionRotationAxis +( + FXMVECTOR Axis, + float Angle +) noexcept +{ + assert(!XMVector3Equal(Axis, XMVectorZero())); + assert(!XMVector3IsInfinite(Axis)); + + XMVECTOR Normal = XMVector3Normalize(Axis); + XMVECTOR Q = XMQuaternionRotationNormal(Normal, Angle); + return Q; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMQuaternionRotationMatrix(FXMMATRIX M) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 q; + float r22 = M.m[2][2]; + if (r22 <= 0.f) // x^2 + y^2 >= z^2 + w^2 + { + float dif10 = M.m[1][1] - M.m[0][0]; + float omr22 = 1.f - r22; + if (dif10 <= 0.f) // x^2 >= y^2 + { + float fourXSqr = omr22 - dif10; + float inv4x = 0.5f / sqrtf(fourXSqr); + q.f[0] = fourXSqr * inv4x; + q.f[1] = (M.m[0][1] + M.m[1][0]) * inv4x; + q.f[2] = (M.m[0][2] + M.m[2][0]) * inv4x; + q.f[3] = (M.m[1][2] - M.m[2][1]) * inv4x; + } + else // y^2 >= x^2 + { + float fourYSqr = omr22 + dif10; + float inv4y = 0.5f / sqrtf(fourYSqr); + q.f[0] = (M.m[0][1] + M.m[1][0]) * inv4y; + q.f[1] = fourYSqr * inv4y; + q.f[2] = (M.m[1][2] + M.m[2][1]) * inv4y; + q.f[3] = (M.m[2][0] - M.m[0][2]) * inv4y; + } + } + else // z^2 + w^2 >= x^2 + y^2 + { + float sum10 = M.m[1][1] + M.m[0][0]; + float opr22 = 1.f + r22; + if (sum10 <= 0.f) // z^2 >= w^2 + { + float fourZSqr = opr22 - sum10; + float inv4z = 0.5f / sqrtf(fourZSqr); + q.f[0] = (M.m[0][2] + M.m[2][0]) * inv4z; + q.f[1] = (M.m[1][2] + M.m[2][1]) * inv4z; + q.f[2] = fourZSqr * inv4z; + q.f[3] = (M.m[0][1] - M.m[1][0]) * inv4z; + } + else // w^2 >= z^2 + { + float fourWSqr = opr22 + sum10; + float inv4w = 0.5f / sqrtf(fourWSqr); + q.f[0] = (M.m[1][2] - M.m[2][1]) * inv4w; + q.f[1] = (M.m[2][0] - M.m[0][2]) * inv4w; + q.f[2] = (M.m[0][1] - M.m[1][0]) * inv4w; + q.f[3] = fourWSqr * inv4w; + } + } + return q.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 XMPMMP = { { { +1.0f, -1.0f, -1.0f, +1.0f } } }; + static const XMVECTORF32 XMMPMP = { { { -1.0f, +1.0f, -1.0f, +1.0f } } }; + static const XMVECTORF32 XMMMPP = { { { -1.0f, -1.0f, +1.0f, +1.0f } } }; + static const XMVECTORU32 Select0110 = { { { XM_SELECT_0, XM_SELECT_1, XM_SELECT_1, XM_SELECT_0 } } }; + static const XMVECTORU32 Select0010 = { { { XM_SELECT_0, XM_SELECT_0, XM_SELECT_1, XM_SELECT_0 } } }; + + float32x4_t r0 = M.r[0]; + float32x4_t r1 = M.r[1]; + float32x4_t r2 = M.r[2]; + + float32x4_t r00 = vdupq_lane_f32(vget_low_f32(r0), 0); + float32x4_t r11 = vdupq_lane_f32(vget_low_f32(r1), 1); + float32x4_t r22 = vdupq_lane_f32(vget_high_f32(r2), 0); + + // x^2 >= y^2 equivalent to r11 - r00 <= 0 + float32x4_t r11mr00 = vsubq_f32(r11, r00); + uint32x4_t x2gey2 = vcleq_f32(r11mr00, g_XMZero); + + // z^2 >= w^2 equivalent to r11 + r00 <= 0 + float32x4_t r11pr00 = vaddq_f32(r11, r00); + uint32x4_t z2gew2 = vcleq_f32(r11pr00, g_XMZero); + + // x^2 + y^2 >= z^2 + w^2 equivalent to r22 <= 0 + uint32x4_t x2py2gez2pw2 = vcleq_f32(r22, g_XMZero); + + // (4*x^2, 4*y^2, 4*z^2, 4*w^2) + float32x4_t t0 = vmulq_f32(XMPMMP, r00); + float32x4_t x2y2z2w2 = vmlaq_f32(t0, XMMPMP, r11); + x2y2z2w2 = vmlaq_f32(x2y2z2w2, XMMMPP, r22); + x2y2z2w2 = vaddq_f32(x2y2z2w2, g_XMOne); + + // (r01, r02, r12, r11) + t0 = vextq_f32(r0, r0, 1); + float32x4_t t1 = vextq_f32(r1, r1, 1); + t0 = vcombine_f32(vget_low_f32(t0), vrev64_f32(vget_low_f32(t1))); + + // (r10, r20, r21, r10) + t1 = vextq_f32(r2, r2, 3); + float32x4_t r10 = vdupq_lane_f32(vget_low_f32(r1), 0); + t1 = vbslq_f32(Select0110, t1, r10); + + // (4*x*y, 4*x*z, 4*y*z, unused) + float32x4_t xyxzyz = vaddq_f32(t0, t1); + + // (r21, r20, r10, r10) + t0 = vcombine_f32(vrev64_f32(vget_low_f32(r2)), vget_low_f32(r10)); + + // (r12, r02, r01, r12) + float32x4_t t2 = vcombine_f32(vrev64_f32(vget_high_f32(r0)), vrev64_f32(vget_low_f32(r0))); + float32x4_t t3 = vdupq_lane_f32(vget_high_f32(r1), 0); + t1 = vbslq_f32(Select0110, t2, t3); + + // (4*x*w, 4*y*w, 4*z*w, unused) + float32x4_t xwywzw = vsubq_f32(t0, t1); + xwywzw = vmulq_f32(XMMPMP, xwywzw); + + // (4*x*x, 4*x*y, 4*x*z, 4*x*w) + t0 = vextq_f32(xyxzyz, xyxzyz, 3); + t1 = vbslq_f32(Select0110, t0, x2y2z2w2); + t2 = vdupq_lane_f32(vget_low_f32(xwywzw), 0); + float32x4_t tensor0 = vbslq_f32(g_XMSelect1110, t1, t2); + + // (4*y*x, 4*y*y, 4*y*z, 4*y*w) + t0 = vbslq_f32(g_XMSelect1011, xyxzyz, x2y2z2w2); + t1 = vdupq_lane_f32(vget_low_f32(xwywzw), 1); + float32x4_t tensor1 = vbslq_f32(g_XMSelect1110, t0, t1); + + // (4*z*x, 4*z*y, 4*z*z, 4*z*w) + t0 = vextq_f32(xyxzyz, xyxzyz, 1); + t1 = vcombine_f32(vget_low_f32(t0), vrev64_f32(vget_high_f32(xwywzw))); + float32x4_t tensor2 = vbslq_f32(Select0010, x2y2z2w2, t1); + + // (4*w*x, 4*w*y, 4*w*z, 4*w*w) + float32x4_t tensor3 = vbslq_f32(g_XMSelect1110, xwywzw, x2y2z2w2); + + // Select the row of the tensor-product matrix that has the largest + // magnitude. + t0 = vbslq_f32(x2gey2, tensor0, tensor1); + t1 = vbslq_f32(z2gew2, tensor2, tensor3); + t2 = vbslq_f32(x2py2gez2pw2, t0, t1); + + // Normalize the row. No division by zero is possible because the + // quaternion is unit-length (and the row is a nonzero multiple of + // the quaternion). + t0 = XMVector4Length(t2); + return XMVectorDivide(t2, t0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 XMPMMP = { { { +1.0f, -1.0f, -1.0f, +1.0f } } }; + static const XMVECTORF32 XMMPMP = { { { -1.0f, +1.0f, -1.0f, +1.0f } } }; + static const XMVECTORF32 XMMMPP = { { { -1.0f, -1.0f, +1.0f, +1.0f } } }; + + XMVECTOR r0 = M.r[0]; // (r00, r01, r02, 0) + XMVECTOR r1 = M.r[1]; // (r10, r11, r12, 0) + XMVECTOR r2 = M.r[2]; // (r20, r21, r22, 0) + + // (r00, r00, r00, r00) + XMVECTOR r00 = XM_PERMUTE_PS(r0, _MM_SHUFFLE(0, 0, 0, 0)); + // (r11, r11, r11, r11) + XMVECTOR r11 = XM_PERMUTE_PS(r1, _MM_SHUFFLE(1, 1, 1, 1)); + // (r22, r22, r22, r22) + XMVECTOR r22 = XM_PERMUTE_PS(r2, _MM_SHUFFLE(2, 2, 2, 2)); + + // x^2 >= y^2 equivalent to r11 - r00 <= 0 + // (r11 - r00, r11 - r00, r11 - r00, r11 - r00) + XMVECTOR r11mr00 = _mm_sub_ps(r11, r00); + XMVECTOR x2gey2 = _mm_cmple_ps(r11mr00, g_XMZero); + + // z^2 >= w^2 equivalent to r11 + r00 <= 0 + // (r11 + r00, r11 + r00, r11 + r00, r11 + r00) + XMVECTOR r11pr00 = _mm_add_ps(r11, r00); + XMVECTOR z2gew2 = _mm_cmple_ps(r11pr00, g_XMZero); + + // x^2 + y^2 >= z^2 + w^2 equivalent to r22 <= 0 + XMVECTOR x2py2gez2pw2 = _mm_cmple_ps(r22, g_XMZero); + + // (4*x^2, 4*y^2, 4*z^2, 4*w^2) + XMVECTOR t0 = XM_FMADD_PS(XMPMMP, r00, g_XMOne); + XMVECTOR t1 = _mm_mul_ps(XMMPMP, r11); + XMVECTOR t2 = XM_FMADD_PS(XMMMPP, r22, t0); + XMVECTOR x2y2z2w2 = _mm_add_ps(t1, t2); + + // (r01, r02, r12, r11) + t0 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(1, 2, 2, 1)); + // (r10, r10, r20, r21) + t1 = _mm_shuffle_ps(r1, r2, _MM_SHUFFLE(1, 0, 0, 0)); + // (r10, r20, r21, r10) + t1 = XM_PERMUTE_PS(t1, _MM_SHUFFLE(1, 3, 2, 0)); + // (4*x*y, 4*x*z, 4*y*z, unused) + XMVECTOR xyxzyz = _mm_add_ps(t0, t1); + + // (r21, r20, r10, r10) + t0 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(0, 0, 0, 1)); + // (r12, r12, r02, r01) + t1 = _mm_shuffle_ps(r1, r0, _MM_SHUFFLE(1, 2, 2, 2)); + // (r12, r02, r01, r12) + t1 = XM_PERMUTE_PS(t1, _MM_SHUFFLE(1, 3, 2, 0)); + // (4*x*w, 4*y*w, 4*z*w, unused) + XMVECTOR xwywzw = _mm_sub_ps(t0, t1); + xwywzw = _mm_mul_ps(XMMPMP, xwywzw); + + // (4*x^2, 4*y^2, 4*x*y, unused) + t0 = _mm_shuffle_ps(x2y2z2w2, xyxzyz, _MM_SHUFFLE(0, 0, 1, 0)); + // (4*z^2, 4*w^2, 4*z*w, unused) + t1 = _mm_shuffle_ps(x2y2z2w2, xwywzw, _MM_SHUFFLE(0, 2, 3, 2)); + // (4*x*z, 4*y*z, 4*x*w, 4*y*w) + t2 = _mm_shuffle_ps(xyxzyz, xwywzw, _MM_SHUFFLE(1, 0, 2, 1)); + + // (4*x*x, 4*x*y, 4*x*z, 4*x*w) + XMVECTOR tensor0 = _mm_shuffle_ps(t0, t2, _MM_SHUFFLE(2, 0, 2, 0)); + // (4*y*x, 4*y*y, 4*y*z, 4*y*w) + XMVECTOR tensor1 = _mm_shuffle_ps(t0, t2, _MM_SHUFFLE(3, 1, 1, 2)); + // (4*z*x, 4*z*y, 4*z*z, 4*z*w) + XMVECTOR tensor2 = _mm_shuffle_ps(t2, t1, _MM_SHUFFLE(2, 0, 1, 0)); + // (4*w*x, 4*w*y, 4*w*z, 4*w*w) + XMVECTOR tensor3 = _mm_shuffle_ps(t2, t1, _MM_SHUFFLE(1, 2, 3, 2)); + + // Select the row of the tensor-product matrix that has the largest + // magnitude. + t0 = _mm_and_ps(x2gey2, tensor0); + t1 = _mm_andnot_ps(x2gey2, tensor1); + t0 = _mm_or_ps(t0, t1); + t1 = _mm_and_ps(z2gew2, tensor2); + t2 = _mm_andnot_ps(z2gew2, tensor3); + t1 = _mm_or_ps(t1, t2); + t0 = _mm_and_ps(x2py2gez2pw2, t0); + t1 = _mm_andnot_ps(x2py2gez2pw2, t1); + t2 = _mm_or_ps(t0, t1); + + // Normalize the row. No division by zero is possible because the + // quaternion is unit-length (and the row is a nonzero multiple of + // the quaternion). + t0 = XMVector4Length(t2); + return _mm_div_ps(t2, t0); +#endif +} + +//------------------------------------------------------------------------------ +// Conversion operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMQuaternionToAxisAngle +( + XMVECTOR* pAxis, + float* pAngle, + FXMVECTOR Q +) noexcept +{ + assert(pAxis); + assert(pAngle); + + *pAxis = Q; + + *pAngle = 2.0f * XMScalarACos(XMVectorGetW(Q)); +} + +/**************************************************************************** + * + * Plane + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparison operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMPlaneEqual +( + FXMVECTOR P1, + FXMVECTOR P2 +) noexcept +{ + return XMVector4Equal(P1, P2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMPlaneNearEqual +( + FXMVECTOR P1, + FXMVECTOR P2, + FXMVECTOR Epsilon +) noexcept +{ + XMVECTOR NP1 = XMPlaneNormalize(P1); + XMVECTOR NP2 = XMPlaneNormalize(P2); + return XMVector4NearEqual(NP1, NP2, Epsilon); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMPlaneNotEqual +( + FXMVECTOR P1, + FXMVECTOR P2 +) noexcept +{ + return XMVector4NotEqual(P1, P2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMPlaneIsNaN(FXMVECTOR P) noexcept +{ + return XMVector4IsNaN(P); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMPlaneIsInfinite(FXMVECTOR P) noexcept +{ + return XMVector4IsInfinite(P); +} + +//------------------------------------------------------------------------------ +// Computation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMPlaneDot +( + FXMVECTOR P, + FXMVECTOR V +) noexcept +{ + return XMVector4Dot(P, V); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMPlaneDotCoord +( + FXMVECTOR P, + FXMVECTOR V +) noexcept +{ + // Result = P[0] * V[0] + P[1] * V[1] + P[2] * V[2] + P[3] + + XMVECTOR V3 = XMVectorSelect(g_XMOne.v, V, g_XMSelect1110.v); + XMVECTOR Result = XMVector4Dot(P, V3); + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMPlaneDotNormal +( + FXMVECTOR P, + FXMVECTOR V +) noexcept +{ + return XMVector3Dot(P, V); +} + +//------------------------------------------------------------------------------ +// XMPlaneNormalizeEst uses a reciprocal estimate and +// returns QNaN on zero and infinite vectors. + +inline XMVECTOR XM_CALLCONV XMPlaneNormalizeEst(FXMVECTOR P) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + + XMVECTOR Result = XMVector3ReciprocalLengthEst(P); + return XMVectorMultiply(P, Result); + +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(P, P, 0x7f); + XMVECTOR vResult = _mm_rsqrt_ps(vTemp); + return _mm_mul_ps(vResult, P); +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product + XMVECTOR vDot = _mm_mul_ps(P, P); + // x=Dot.y, y=Dot.z + XMVECTOR vTemp = XM_PERMUTE_PS(vDot, _MM_SHUFFLE(2, 1, 2, 1)); + // Result.x = x+y + vDot = _mm_add_ss(vDot, vTemp); + // x=Dot.z + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(1, 1, 1, 1)); + // Result.x = (x+y)+z + vDot = _mm_add_ss(vDot, vTemp); + // Splat x + vDot = XM_PERMUTE_PS(vDot, _MM_SHUFFLE(0, 0, 0, 0)); + // Get the reciprocal + vDot = _mm_rsqrt_ps(vDot); + // Get the reciprocal + vDot = _mm_mul_ps(vDot, P); + return vDot; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMPlaneNormalize(FXMVECTOR P) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float fLengthSq = sqrtf((P.vector4_f32[0] * P.vector4_f32[0]) + (P.vector4_f32[1] * P.vector4_f32[1]) + (P.vector4_f32[2] * P.vector4_f32[2])); + // Prevent divide by zero + if (fLengthSq > 0) + { + fLengthSq = 1.0f / fLengthSq; + } + XMVECTORF32 vResult = { { { + P.vector4_f32[0] * fLengthSq, + P.vector4_f32[1] * fLengthSq, + P.vector4_f32[2] * fLengthSq, + P.vector4_f32[3] * fLengthSq + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR vLength = XMVector3ReciprocalLength(P); + return XMVectorMultiply(P, vLength); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vLengthSq = _mm_dp_ps(P, P, 0x7f); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Reciprocal mul to perform the normalization + vResult = _mm_div_ps(P, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vLengthSq); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y and z only + XMVECTOR vLengthSq = _mm_mul_ps(P, P); + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(2, 1, 2, 1)); + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(1, 1, 1, 1)); + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Reciprocal mul to perform the normalization + vResult = _mm_div_ps(P, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vLengthSq); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMPlaneIntersectLine +( + FXMVECTOR P, + FXMVECTOR LinePoint1, + FXMVECTOR LinePoint2 +) noexcept +{ + XMVECTOR V1 = XMVector3Dot(P, LinePoint1); + XMVECTOR V2 = XMVector3Dot(P, LinePoint2); + XMVECTOR D = XMVectorSubtract(V1, V2); + + XMVECTOR VT = XMPlaneDotCoord(P, LinePoint1); + VT = XMVectorDivide(VT, D); + + XMVECTOR Point = XMVectorSubtract(LinePoint2, LinePoint1); + Point = XMVectorMultiplyAdd(Point, VT, LinePoint1); + + const XMVECTOR Zero = XMVectorZero(); + XMVECTOR Control = XMVectorNearEqual(D, Zero, g_XMEpsilon.v); + + return XMVectorSelect(Point, g_XMQNaN.v, Control); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMPlaneIntersectPlane +( + XMVECTOR* pLinePoint1, + XMVECTOR* pLinePoint2, + FXMVECTOR P1, + FXMVECTOR P2 +) noexcept +{ + assert(pLinePoint1); + assert(pLinePoint2); + + XMVECTOR V1 = XMVector3Cross(P2, P1); + + XMVECTOR LengthSq = XMVector3LengthSq(V1); + + XMVECTOR V2 = XMVector3Cross(P2, V1); + + XMVECTOR P1W = XMVectorSplatW(P1); + XMVECTOR Point = XMVectorMultiply(V2, P1W); + + XMVECTOR V3 = XMVector3Cross(V1, P1); + + XMVECTOR P2W = XMVectorSplatW(P2); + Point = XMVectorMultiplyAdd(V3, P2W, Point); + + XMVECTOR LinePoint1 = XMVectorDivide(Point, LengthSq); + + XMVECTOR LinePoint2 = XMVectorAdd(LinePoint1, V1); + + XMVECTOR Control = XMVectorLessOrEqual(LengthSq, g_XMEpsilon.v); + *pLinePoint1 = XMVectorSelect(LinePoint1, g_XMQNaN.v, Control); + *pLinePoint2 = XMVectorSelect(LinePoint2, g_XMQNaN.v, Control); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMPlaneTransform +( + FXMVECTOR P, + FXMMATRIX ITM +) noexcept +{ + XMVECTOR W = XMVectorSplatW(P); + XMVECTOR Z = XMVectorSplatZ(P); + XMVECTOR Y = XMVectorSplatY(P); + XMVECTOR X = XMVectorSplatX(P); + + XMVECTOR Result = XMVectorMultiply(W, ITM.r[3]); + Result = XMVectorMultiplyAdd(Z, ITM.r[2], Result); + Result = XMVectorMultiplyAdd(Y, ITM.r[1], Result); + Result = XMVectorMultiplyAdd(X, ITM.r[0], Result); + return Result; +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMFLOAT4* XM_CALLCONV XMPlaneTransformStream +( + XMFLOAT4* pOutputStream, + size_t OutputStride, + const XMFLOAT4* pInputStream, + size_t InputStride, + size_t PlaneCount, + FXMMATRIX ITM +) noexcept +{ + return XMVector4TransformStream(pOutputStream, + OutputStride, + pInputStream, + InputStride, + PlaneCount, + ITM); +} + +//------------------------------------------------------------------------------ +// Conversion operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMPlaneFromPointNormal +( + FXMVECTOR Point, + FXMVECTOR Normal +) noexcept +{ + XMVECTOR W = XMVector3Dot(Point, Normal); + W = XMVectorNegate(W); + return XMVectorSelect(W, Normal, g_XMSelect1110.v); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMPlaneFromPoints +( + FXMVECTOR Point1, + FXMVECTOR Point2, + FXMVECTOR Point3 +) noexcept +{ + XMVECTOR V21 = XMVectorSubtract(Point1, Point2); + XMVECTOR V31 = XMVectorSubtract(Point1, Point3); + + XMVECTOR N = XMVector3Cross(V21, V31); + N = XMVector3Normalize(N); + + XMVECTOR D = XMPlaneDotNormal(N, Point1); + D = XMVectorNegate(D); + + XMVECTOR Result = XMVectorSelect(D, N, g_XMSelect1110.v); + + return Result; +} + +/**************************************************************************** + * + * Color + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparison operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMColorEqual +( + FXMVECTOR C1, + FXMVECTOR C2 +) noexcept +{ + return XMVector4Equal(C1, C2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMColorNotEqual +( + FXMVECTOR C1, + FXMVECTOR C2 +) noexcept +{ + return XMVector4NotEqual(C1, C2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMColorGreater +( + FXMVECTOR C1, + FXMVECTOR C2 +) noexcept +{ + return XMVector4Greater(C1, C2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMColorGreaterOrEqual +( + FXMVECTOR C1, + FXMVECTOR C2 +) noexcept +{ + return XMVector4GreaterOrEqual(C1, C2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMColorLess +( + FXMVECTOR C1, + FXMVECTOR C2 +) noexcept +{ + return XMVector4Less(C1, C2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMColorLessOrEqual +( + FXMVECTOR C1, + FXMVECTOR C2 +) noexcept +{ + return XMVector4LessOrEqual(C1, C2); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMColorIsNaN(FXMVECTOR C) noexcept +{ + return XMVector4IsNaN(C); +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMColorIsInfinite(FXMVECTOR C) noexcept +{ + return XMVector4IsInfinite(C); +} + +//------------------------------------------------------------------------------ +// Computation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorNegative(FXMVECTOR vColor) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + 1.0f - vColor.vector4_f32[0], + 1.0f - vColor.vector4_f32[1], + 1.0f - vColor.vector4_f32[2], + vColor.vector4_f32[3] + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vTemp = veorq_u32(vreinterpretq_u32_f32(vColor), g_XMNegate3); + return vaddq_f32(vreinterpretq_f32_u32(vTemp), g_XMOne3); +#elif defined(_XM_SSE_INTRINSICS_) + // Negate only x,y and z. + XMVECTOR vTemp = _mm_xor_ps(vColor, g_XMNegate3); + // Add 1,1,1,0 to -x,-y,-z,w + return _mm_add_ps(vTemp, g_XMOne3); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorModulate +( + FXMVECTOR C1, + FXMVECTOR C2 +) noexcept +{ + return XMVectorMultiply(C1, C2); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorAdjustSaturation +( + FXMVECTOR vColor, + float fSaturation +) noexcept +{ + // Luminance = 0.2125f * C[0] + 0.7154f * C[1] + 0.0721f * C[2]; + // Result = (C - Luminance) * Saturation + Luminance; + + const XMVECTORF32 gvLuminance = { { { 0.2125f, 0.7154f, 0.0721f, 0.0f } } }; +#if defined(_XM_NO_INTRINSICS_) + float fLuminance = (vColor.vector4_f32[0] * gvLuminance.f[0]) + (vColor.vector4_f32[1] * gvLuminance.f[1]) + (vColor.vector4_f32[2] * gvLuminance.f[2]); + XMVECTOR vResult; + vResult.vector4_f32[0] = ((vColor.vector4_f32[0] - fLuminance) * fSaturation) + fLuminance; + vResult.vector4_f32[1] = ((vColor.vector4_f32[1] - fLuminance) * fSaturation) + fLuminance; + vResult.vector4_f32[2] = ((vColor.vector4_f32[2] - fLuminance) * fSaturation) + fLuminance; + vResult.vector4_f32[3] = vColor.vector4_f32[3]; + return vResult; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR vLuminance = XMVector3Dot(vColor, gvLuminance); + XMVECTOR vResult = vsubq_f32(vColor, vLuminance); + vResult = vmlaq_n_f32(vLuminance, vResult, fSaturation); + return vbslq_f32(g_XMSelect1110, vResult, vColor); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vLuminance = XMVector3Dot(vColor, gvLuminance); + // Splat fSaturation + XMVECTOR vSaturation = _mm_set_ps1(fSaturation); + // vResult = ((vColor-vLuminance)*vSaturation)+vLuminance; + XMVECTOR vResult = _mm_sub_ps(vColor, vLuminance); + vResult = XM_FMADD_PS(vResult, vSaturation, vLuminance); + // Retain w from the source color + vLuminance = _mm_shuffle_ps(vResult, vColor, _MM_SHUFFLE(3, 2, 2, 2)); // x = vResult.z,y = vResult.z,z = vColor.z,w=vColor.w + vResult = _mm_shuffle_ps(vResult, vLuminance, _MM_SHUFFLE(3, 0, 1, 0)); // x = vResult.x,y = vResult.y,z = vResult.z,w=vColor.w + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorAdjustContrast +( + FXMVECTOR vColor, + float fContrast +) noexcept +{ + // Result = (vColor - 0.5f) * fContrast + 0.5f; + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + ((vColor.vector4_f32[0] - 0.5f) * fContrast) + 0.5f, + ((vColor.vector4_f32[1] - 0.5f) * fContrast) + 0.5f, + ((vColor.vector4_f32[2] - 0.5f) * fContrast) + 0.5f, + vColor.vector4_f32[3] // Leave W untouched + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR vResult = vsubq_f32(vColor, g_XMOneHalf.v); + vResult = vmlaq_n_f32(g_XMOneHalf.v, vResult, fContrast); + return vbslq_f32(g_XMSelect1110, vResult, vColor); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vScale = _mm_set_ps1(fContrast); // Splat the scale + XMVECTOR vResult = _mm_sub_ps(vColor, g_XMOneHalf); // Subtract 0.5f from the source (Saving source) + vResult = XM_FMADD_PS(vResult, vScale, g_XMOneHalf); +// Retain w from the source color + vScale = _mm_shuffle_ps(vResult, vColor, _MM_SHUFFLE(3, 2, 2, 2)); // x = vResult.z,y = vResult.z,z = vColor.z,w=vColor.w + vResult = _mm_shuffle_ps(vResult, vScale, _MM_SHUFFLE(3, 0, 1, 0)); // x = vResult.x,y = vResult.y,z = vResult.z,w=vColor.w + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorRGBToHSL(FXMVECTOR rgb) noexcept +{ + XMVECTOR r = XMVectorSplatX(rgb); + XMVECTOR g = XMVectorSplatY(rgb); + XMVECTOR b = XMVectorSplatZ(rgb); + + XMVECTOR min = XMVectorMin(r, XMVectorMin(g, b)); + XMVECTOR max = XMVectorMax(r, XMVectorMax(g, b)); + + XMVECTOR l = XMVectorMultiply(XMVectorAdd(min, max), g_XMOneHalf); + + XMVECTOR d = XMVectorSubtract(max, min); + + XMVECTOR la = XMVectorSelect(rgb, l, g_XMSelect1110); + + if (XMVector3Less(d, g_XMEpsilon)) + { + // Achromatic, assume H and S of 0 + return XMVectorSelect(la, g_XMZero, g_XMSelect1100); + } + else + { + XMVECTOR s, h; + + XMVECTOR d2 = XMVectorAdd(min, max); + + if (XMVector3Greater(l, g_XMOneHalf)) + { + // d / (2-max-min) + s = XMVectorDivide(d, XMVectorSubtract(g_XMTwo, d2)); + } + else + { + // d / (max+min) + s = XMVectorDivide(d, d2); + } + + if (XMVector3Equal(r, max)) + { + // Red is max + h = XMVectorDivide(XMVectorSubtract(g, b), d); + } + else if (XMVector3Equal(g, max)) + { + // Green is max + h = XMVectorDivide(XMVectorSubtract(b, r), d); + h = XMVectorAdd(h, g_XMTwo); + } + else + { + // Blue is max + h = XMVectorDivide(XMVectorSubtract(r, g), d); + h = XMVectorAdd(h, g_XMFour); + } + + h = XMVectorDivide(h, g_XMSix); + + if (XMVector3Less(h, g_XMZero)) + h = XMVectorAdd(h, g_XMOne); + + XMVECTOR lha = XMVectorSelect(la, h, g_XMSelect1100); + return XMVectorSelect(s, lha, g_XMSelect1011); + } +} + +//------------------------------------------------------------------------------ + +namespace MathInternal +{ + + inline XMVECTOR XM_CALLCONV XMColorHue2Clr(FXMVECTOR p, FXMVECTOR q, FXMVECTOR h) noexcept + { + static const XMVECTORF32 oneSixth = { { { 1.0f / 6.0f, 1.0f / 6.0f, 1.0f / 6.0f, 1.0f / 6.0f } } }; + static const XMVECTORF32 twoThirds = { { { 2.0f / 3.0f, 2.0f / 3.0f, 2.0f / 3.0f, 2.0f / 3.0f } } }; + + XMVECTOR t = h; + + if (XMVector3Less(t, g_XMZero)) + t = XMVectorAdd(t, g_XMOne); + + if (XMVector3Greater(t, g_XMOne)) + t = XMVectorSubtract(t, g_XMOne); + + if (XMVector3Less(t, oneSixth)) + { + // p + (q - p) * 6 * t + XMVECTOR t1 = XMVectorSubtract(q, p); + XMVECTOR t2 = XMVectorMultiply(g_XMSix, t); + return XMVectorMultiplyAdd(t1, t2, p); + } + + if (XMVector3Less(t, g_XMOneHalf)) + return q; + + if (XMVector3Less(t, twoThirds)) + { + // p + (q - p) * 6 * (2/3 - t) + XMVECTOR t1 = XMVectorSubtract(q, p); + XMVECTOR t2 = XMVectorMultiply(g_XMSix, XMVectorSubtract(twoThirds, t)); + return XMVectorMultiplyAdd(t1, t2, p); + } + + return p; + } + +} // namespace MathInternal + +inline XMVECTOR XM_CALLCONV XMColorHSLToRGB(FXMVECTOR hsl) noexcept +{ + static const XMVECTORF32 oneThird = { { { 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f } } }; + + XMVECTOR s = XMVectorSplatY(hsl); + XMVECTOR l = XMVectorSplatZ(hsl); + + if (XMVector3NearEqual(s, g_XMZero, g_XMEpsilon)) + { + // Achromatic + return XMVectorSelect(hsl, l, g_XMSelect1110); + } + else + { + XMVECTOR h = XMVectorSplatX(hsl); + + XMVECTOR q; + if (XMVector3Less(l, g_XMOneHalf)) + { + q = XMVectorMultiply(l, XMVectorAdd(g_XMOne, s)); + } + else + { + q = XMVectorSubtract(XMVectorAdd(l, s), XMVectorMultiply(l, s)); + } + + XMVECTOR p = XMVectorSubtract(XMVectorMultiply(g_XMTwo, l), q); + + XMVECTOR r = DirectX::MathInternal::XMColorHue2Clr(p, q, XMVectorAdd(h, oneThird)); + XMVECTOR g = DirectX::MathInternal::XMColorHue2Clr(p, q, h); + XMVECTOR b = DirectX::MathInternal::XMColorHue2Clr(p, q, XMVectorSubtract(h, oneThird)); + + XMVECTOR rg = XMVectorSelect(g, r, g_XMSelect1000); + XMVECTOR ba = XMVectorSelect(hsl, b, g_XMSelect1110); + + return XMVectorSelect(ba, rg, g_XMSelect1100); + } +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorRGBToHSV(FXMVECTOR rgb) noexcept +{ + XMVECTOR r = XMVectorSplatX(rgb); + XMVECTOR g = XMVectorSplatY(rgb); + XMVECTOR b = XMVectorSplatZ(rgb); + + XMVECTOR min = XMVectorMin(r, XMVectorMin(g, b)); + XMVECTOR v = XMVectorMax(r, XMVectorMax(g, b)); + + XMVECTOR d = XMVectorSubtract(v, min); + + XMVECTOR s = (XMVector3NearEqual(v, g_XMZero, g_XMEpsilon)) ? g_XMZero : XMVectorDivide(d, v); + + if (XMVector3Less(d, g_XMEpsilon)) + { + // Achromatic, assume H of 0 + XMVECTOR hv = XMVectorSelect(v, g_XMZero, g_XMSelect1000); + XMVECTOR hva = XMVectorSelect(rgb, hv, g_XMSelect1110); + return XMVectorSelect(s, hva, g_XMSelect1011); + } + else + { + XMVECTOR h; + + if (XMVector3Equal(r, v)) + { + // Red is max + h = XMVectorDivide(XMVectorSubtract(g, b), d); + + if (XMVector3Less(g, b)) + h = XMVectorAdd(h, g_XMSix); + } + else if (XMVector3Equal(g, v)) + { + // Green is max + h = XMVectorDivide(XMVectorSubtract(b, r), d); + h = XMVectorAdd(h, g_XMTwo); + } + else + { + // Blue is max + h = XMVectorDivide(XMVectorSubtract(r, g), d); + h = XMVectorAdd(h, g_XMFour); + } + + h = XMVectorDivide(h, g_XMSix); + + XMVECTOR hv = XMVectorSelect(v, h, g_XMSelect1000); + XMVECTOR hva = XMVectorSelect(rgb, hv, g_XMSelect1110); + return XMVectorSelect(s, hva, g_XMSelect1011); + } +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorHSVToRGB(FXMVECTOR hsv) noexcept +{ + XMVECTOR h = XMVectorSplatX(hsv); + XMVECTOR s = XMVectorSplatY(hsv); + XMVECTOR v = XMVectorSplatZ(hsv); + + XMVECTOR h6 = XMVectorMultiply(h, g_XMSix); + + XMVECTOR i = XMVectorFloor(h6); + XMVECTOR f = XMVectorSubtract(h6, i); + + // p = v* (1-s) + XMVECTOR p = XMVectorMultiply(v, XMVectorSubtract(g_XMOne, s)); + + // q = v*(1-f*s) + XMVECTOR q = XMVectorMultiply(v, XMVectorSubtract(g_XMOne, XMVectorMultiply(f, s))); + + // t = v*(1 - (1-f)*s) + XMVECTOR t = XMVectorMultiply(v, XMVectorSubtract(g_XMOne, XMVectorMultiply(XMVectorSubtract(g_XMOne, f), s))); + + auto ii = static_cast(XMVectorGetX(XMVectorMod(i, g_XMSix))); + + XMVECTOR _rgb; + + switch (ii) + { + case 0: // rgb = vtp + { + XMVECTOR vt = XMVectorSelect(t, v, g_XMSelect1000); + _rgb = XMVectorSelect(p, vt, g_XMSelect1100); + } + break; + case 1: // rgb = qvp + { + XMVECTOR qv = XMVectorSelect(v, q, g_XMSelect1000); + _rgb = XMVectorSelect(p, qv, g_XMSelect1100); + } + break; + case 2: // rgb = pvt + { + XMVECTOR pv = XMVectorSelect(v, p, g_XMSelect1000); + _rgb = XMVectorSelect(t, pv, g_XMSelect1100); + } + break; + case 3: // rgb = pqv + { + XMVECTOR pq = XMVectorSelect(q, p, g_XMSelect1000); + _rgb = XMVectorSelect(v, pq, g_XMSelect1100); + } + break; + case 4: // rgb = tpv + { + XMVECTOR tp = XMVectorSelect(p, t, g_XMSelect1000); + _rgb = XMVectorSelect(v, tp, g_XMSelect1100); + } + break; + default: // rgb = vpq + { + XMVECTOR vp = XMVectorSelect(p, v, g_XMSelect1000); + _rgb = XMVectorSelect(q, vp, g_XMSelect1100); + } + break; + } + + return XMVectorSelect(hsv, _rgb, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorRGBToYUV(FXMVECTOR rgb) noexcept +{ + static const XMVECTORF32 Scale0 = { { { 0.299f, -0.147f, 0.615f, 0.0f } } }; + static const XMVECTORF32 Scale1 = { { { 0.587f, -0.289f, -0.515f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { 0.114f, 0.436f, -0.100f, 0.0f } } }; + + XMMATRIX M(Scale0, Scale1, Scale2, g_XMZero); + XMVECTOR clr = XMVector3Transform(rgb, M); + + return XMVectorSelect(rgb, clr, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorYUVToRGB(FXMVECTOR yuv) noexcept +{ + static const XMVECTORF32 Scale1 = { { { 0.0f, -0.395f, 2.032f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { 1.140f, -0.581f, 0.0f, 0.0f } } }; + + XMMATRIX M(g_XMOne, Scale1, Scale2, g_XMZero); + XMVECTOR clr = XMVector3Transform(yuv, M); + + return XMVectorSelect(yuv, clr, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorRGBToYUV_HD(FXMVECTOR rgb) noexcept +{ + static const XMVECTORF32 Scale0 = { { { 0.2126f, -0.0997f, 0.6150f, 0.0f } } }; + static const XMVECTORF32 Scale1 = { { { 0.7152f, -0.3354f, -0.5586f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { 0.0722f, 0.4351f, -0.0564f, 0.0f } } }; + + XMMATRIX M(Scale0, Scale1, Scale2, g_XMZero); + XMVECTOR clr = XMVector3Transform(rgb, M); + + return XMVectorSelect(rgb, clr, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorYUVToRGB_HD(FXMVECTOR yuv) noexcept +{ + static const XMVECTORF32 Scale1 = { { { 0.0f, -0.2153f, 2.1324f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { 1.2803f, -0.3806f, 0.0f, 0.0f } } }; + + XMMATRIX M(g_XMOne, Scale1, Scale2, g_XMZero); + XMVECTOR clr = XMVector3Transform(yuv, M); + + return XMVectorSelect(yuv, clr, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorRGBToYUV_UHD(FXMVECTOR rgb) noexcept +{ + static const XMVECTORF32 Scale0 = { { { 0.2627f, -0.1215f, 0.6150f, 0.0f } } }; + static const XMVECTORF32 Scale1 = { { { 0.6780f, -0.3136f, -0.5655f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { 0.0593f, 0.4351f, -0.0495f, 0.0f } } }; + + XMMATRIX M(Scale0, Scale1, Scale2, g_XMZero); + XMVECTOR clr = XMVector3Transform(rgb, M); + + return XMVectorSelect(rgb, clr, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorYUVToRGB_UHD(FXMVECTOR yuv) noexcept +{ + static const XMVECTORF32 Scale1 = { { { 0.0f, -0.1891f, 2.1620f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { 1.1989f, -0.4645f, 0.0f, 0.0f } } }; + + XMMATRIX M(g_XMOne, Scale1, Scale2, g_XMZero); + XMVECTOR clr = XMVector3Transform(yuv, M); + + return XMVectorSelect(yuv, clr, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorRGBToXYZ(FXMVECTOR rgb) noexcept +{ + static const XMVECTORF32 Scale0 = { { { 0.4887180f, 0.1762044f, 0.0000000f, 0.0f } } }; + static const XMVECTORF32 Scale1 = { { { 0.3106803f, 0.8129847f, 0.0102048f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { 0.2006017f, 0.0108109f, 0.9897952f, 0.0f } } }; + static const XMVECTORF32 Scale = { { { 1.f / 0.17697f, 1.f / 0.17697f, 1.f / 0.17697f, 0.0f } } }; + + XMMATRIX M(Scale0, Scale1, Scale2, g_XMZero); + XMVECTOR clr = XMVectorMultiply(XMVector3Transform(rgb, M), Scale); + + return XMVectorSelect(rgb, clr, g_XMSelect1110); +} + +inline XMVECTOR XM_CALLCONV XMColorXYZToRGB(FXMVECTOR xyz) noexcept +{ + static const XMVECTORF32 Scale0 = { { { 2.3706743f, -0.5138850f, 0.0052982f, 0.0f } } }; + static const XMVECTORF32 Scale1 = { { { -0.9000405f, 1.4253036f, -0.0146949f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { -0.4706338f, 0.0885814f, 1.0093968f, 0.0f } } }; + static const XMVECTORF32 Scale = { { { 0.17697f, 0.17697f, 0.17697f, 0.0f } } }; + + XMMATRIX M(Scale0, Scale1, Scale2, g_XMZero); + XMVECTOR clr = XMVector3Transform(XMVectorMultiply(xyz, Scale), M); + + return XMVectorSelect(xyz, clr, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorXYZToSRGB(FXMVECTOR xyz) noexcept +{ + static const XMVECTORF32 Scale0 = { { { 3.2406f, -0.9689f, 0.0557f, 0.0f } } }; + static const XMVECTORF32 Scale1 = { { { -1.5372f, 1.8758f, -0.2040f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { -0.4986f, 0.0415f, 1.0570f, 0.0f } } }; + static const XMVECTORF32 Cutoff = { { { 0.0031308f, 0.0031308f, 0.0031308f, 0.0f } } }; + static const XMVECTORF32 Exp = { { { 1.0f / 2.4f, 1.0f / 2.4f, 1.0f / 2.4f, 1.0f } } }; + + XMMATRIX M(Scale0, Scale1, Scale2, g_XMZero); + XMVECTOR lclr = XMVector3Transform(xyz, M); + + XMVECTOR sel = XMVectorGreater(lclr, Cutoff); + + // clr = 12.92 * lclr for lclr <= 0.0031308f + XMVECTOR smallC = XMVectorMultiply(lclr, g_XMsrgbScale); + + // clr = (1+a)*pow(lclr, 1/2.4) - a for lclr > 0.0031308 (where a = 0.055) + XMVECTOR largeC = XMVectorSubtract(XMVectorMultiply(g_XMsrgbA1, XMVectorPow(lclr, Exp)), g_XMsrgbA); + + XMVECTOR clr = XMVectorSelect(smallC, largeC, sel); + + return XMVectorSelect(xyz, clr, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorSRGBToXYZ(FXMVECTOR srgb) noexcept +{ + static const XMVECTORF32 Scale0 = { { { 0.4124f, 0.2126f, 0.0193f, 0.0f } } }; + static const XMVECTORF32 Scale1 = { { { 0.3576f, 0.7152f, 0.1192f, 0.0f } } }; + static const XMVECTORF32 Scale2 = { { { 0.1805f, 0.0722f, 0.9505f, 0.0f } } }; + static const XMVECTORF32 Cutoff = { { { 0.04045f, 0.04045f, 0.04045f, 0.0f } } }; + static const XMVECTORF32 Exp = { { { 2.4f, 2.4f, 2.4f, 1.0f } } }; + + XMVECTOR sel = XMVectorGreater(srgb, Cutoff); + + // lclr = clr / 12.92 + XMVECTOR smallC = XMVectorDivide(srgb, g_XMsrgbScale); + + // lclr = pow( (clr + a) / (1+a), 2.4 ) + XMVECTOR largeC = XMVectorPow(XMVectorDivide(XMVectorAdd(srgb, g_XMsrgbA), g_XMsrgbA1), Exp); + + XMVECTOR lclr = XMVectorSelect(smallC, largeC, sel); + + XMMATRIX M(Scale0, Scale1, Scale2, g_XMZero); + XMVECTOR clr = XMVector3Transform(lclr, M); + + return XMVectorSelect(srgb, clr, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorRGBToSRGB(FXMVECTOR rgb) noexcept +{ + static const XMVECTORF32 Cutoff = { { { 0.0031308f, 0.0031308f, 0.0031308f, 1.f } } }; + static const XMVECTORF32 Linear = { { { 12.92f, 12.92f, 12.92f, 1.f } } }; + static const XMVECTORF32 Scale = { { { 1.055f, 1.055f, 1.055f, 1.f } } }; + static const XMVECTORF32 Bias = { { { 0.055f, 0.055f, 0.055f, 0.f } } }; + static const XMVECTORF32 InvGamma = { { { 1.0f / 2.4f, 1.0f / 2.4f, 1.0f / 2.4f, 1.f } } }; + + XMVECTOR V = XMVectorSaturate(rgb); + XMVECTOR V0 = XMVectorMultiply(V, Linear); + XMVECTOR V1 = XMVectorSubtract(XMVectorMultiply(Scale, XMVectorPow(V, InvGamma)), Bias); + XMVECTOR select = XMVectorLess(V, Cutoff); + V = XMVectorSelect(V1, V0, select); + return XMVectorSelect(rgb, V, g_XMSelect1110); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMColorSRGBToRGB(FXMVECTOR srgb) noexcept +{ + static const XMVECTORF32 Cutoff = { { { 0.04045f, 0.04045f, 0.04045f, 1.f } } }; + static const XMVECTORF32 ILinear = { { { 1.f / 12.92f, 1.f / 12.92f, 1.f / 12.92f, 1.f } } }; + static const XMVECTORF32 Scale = { { { 1.f / 1.055f, 1.f / 1.055f, 1.f / 1.055f, 1.f } } }; + static const XMVECTORF32 Bias = { { { 0.055f, 0.055f, 0.055f, 0.f } } }; + static const XMVECTORF32 Gamma = { { { 2.4f, 2.4f, 2.4f, 1.f } } }; + + XMVECTOR V = XMVectorSaturate(srgb); + XMVECTOR V0 = XMVectorMultiply(V, ILinear); + XMVECTOR V1 = XMVectorPow(XMVectorMultiply(XMVectorAdd(V, Bias), Scale), Gamma); + XMVECTOR select = XMVectorGreater(V, Cutoff); + V = XMVectorSelect(V0, V1, select); + return XMVectorSelect(srgb, V, g_XMSelect1110); +} + +/**************************************************************************** + * + * Miscellaneous + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline bool XMVerifyCPUSupport() noexcept +{ +#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + int CPUInfo[4] = { -1 }; +#if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); +#else + __cpuid(CPUInfo, 0); +#endif + +#ifdef __AVX2__ + if (CPUInfo[0] < 7) + return false; +#else + if (CPUInfo[0] < 1) + return false; +#endif + +#if (defined(__clang__) || defined(__GNUC__)) && defined(__cpuid) + __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); +#else + __cpuid(CPUInfo, 1); +#endif + +#if defined(__AVX2__) || defined(_XM_AVX2_INTRINSICS_) + // The compiler can emit FMA3 instructions even without explicit intrinsics use + if ((CPUInfo[2] & 0x38081001) != 0x38081001) + return false; // No F16C/AVX/OSXSAVE/SSE4.1/FMA3/SSE3 support +#elif defined(_XM_FMA3_INTRINSICS_) && defined(_XM_F16C_INTRINSICS_) + if ((CPUInfo[2] & 0x38081001) != 0x38081001) + return false; // No F16C/AVX/OSXSAVE/SSE4.1/FMA3/SSE3 support +#elif defined(_XM_FMA3_INTRINSICS_) + if ((CPUInfo[2] & 0x18081001) != 0x18081001) + return false; // No AVX/OSXSAVE/SSE4.1/FMA3/SSE3 support +#elif defined(_XM_F16C_INTRINSICS_) + if ((CPUInfo[2] & 0x38080001) != 0x38080001) + return false; // No F16C/AVX/OSXSAVE/SSE4.1/SSE3 support +#elif defined(__AVX__) || defined(_XM_AVX_INTRINSICS_) + if ((CPUInfo[2] & 0x18080001) != 0x18080001) + return false; // No AVX/OSXSAVE/SSE4.1/SSE3 support +#elif defined(_XM_SSE4_INTRINSICS_) + if ((CPUInfo[2] & 0x80001) != 0x80001) + return false; // No SSE3/SSE4.1 support +#elif defined(_XM_SSE3_INTRINSICS_) + if (!(CPUInfo[2] & 0x1)) + return false; // No SSE3 support +#endif + + // The x64 processor model requires SSE2 support, but no harm in checking + if ((CPUInfo[3] & 0x6000000) != 0x6000000) + return false; // No SSE2/SSE support + +#if defined(__AVX2__) || defined(_XM_AVX2_INTRINSICS_) +#if defined(__clang__) || defined(__GNUC__) + __cpuid_count(7, 0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]); +#else + __cpuidex(CPUInfo, 7, 0); +#endif + if (!(CPUInfo[1] & 0x20)) + return false; // No AVX2 support +#endif + + return true; +#elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + // ARM-NEON support is required for the Windows on ARM platform + return true; +#else + // No intrinsics path always supported + return true; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMFresnelTerm +( + FXMVECTOR CosIncidentAngle, + FXMVECTOR RefractionIndex +) noexcept +{ + assert(!XMVector4IsInfinite(CosIncidentAngle)); + + // Result = 0.5f * (g - c)^2 / (g + c)^2 * ((c * (g + c) - 1)^2 / (c * (g - c) + 1)^2 + 1) where + // c = CosIncidentAngle + // g = sqrt(c^2 + RefractionIndex^2 - 1) + +#if defined(_XM_NO_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + + XMVECTOR G = XMVectorMultiplyAdd(RefractionIndex, RefractionIndex, g_XMNegativeOne.v); + G = XMVectorMultiplyAdd(CosIncidentAngle, CosIncidentAngle, G); + G = XMVectorAbs(G); + G = XMVectorSqrt(G); + + XMVECTOR S = XMVectorAdd(G, CosIncidentAngle); + XMVECTOR D = XMVectorSubtract(G, CosIncidentAngle); + + XMVECTOR V0 = XMVectorMultiply(D, D); + XMVECTOR V1 = XMVectorMultiply(S, S); + V1 = XMVectorReciprocal(V1); + V0 = XMVectorMultiply(g_XMOneHalf.v, V0); + V0 = XMVectorMultiply(V0, V1); + + XMVECTOR V2 = XMVectorMultiplyAdd(CosIncidentAngle, S, g_XMNegativeOne.v); + XMVECTOR V3 = XMVectorMultiplyAdd(CosIncidentAngle, D, g_XMOne.v); + V2 = XMVectorMultiply(V2, V2); + V3 = XMVectorMultiply(V3, V3); + V3 = XMVectorReciprocal(V3); + V2 = XMVectorMultiplyAdd(V2, V3, g_XMOne.v); + + XMVECTOR Result = XMVectorMultiply(V0, V2); + + Result = XMVectorSaturate(Result); + + return Result; + +#elif defined(_XM_SSE_INTRINSICS_) + // G = sqrt(abs((RefractionIndex^2-1) + CosIncidentAngle^2)) + XMVECTOR G = _mm_mul_ps(RefractionIndex, RefractionIndex); + XMVECTOR vTemp = _mm_mul_ps(CosIncidentAngle, CosIncidentAngle); + G = _mm_sub_ps(G, g_XMOne); + vTemp = _mm_add_ps(vTemp, G); + // max((0-vTemp),vTemp) == abs(vTemp) + // The abs is needed to deal with refraction and cosine being zero + G = _mm_setzero_ps(); + G = _mm_sub_ps(G, vTemp); + G = _mm_max_ps(G, vTemp); + // Last operation, the sqrt() + G = _mm_sqrt_ps(G); + + // Calc G-C and G+C + XMVECTOR GAddC = _mm_add_ps(G, CosIncidentAngle); + XMVECTOR GSubC = _mm_sub_ps(G, CosIncidentAngle); + // Perform the term (0.5f *(g - c)^2) / (g + c)^2 + XMVECTOR vResult = _mm_mul_ps(GSubC, GSubC); + vTemp = _mm_mul_ps(GAddC, GAddC); + vResult = _mm_mul_ps(vResult, g_XMOneHalf); + vResult = _mm_div_ps(vResult, vTemp); + // Perform the term ((c * (g + c) - 1)^2 / (c * (g - c) + 1)^2 + 1) + GAddC = _mm_mul_ps(GAddC, CosIncidentAngle); + GSubC = _mm_mul_ps(GSubC, CosIncidentAngle); + GAddC = _mm_sub_ps(GAddC, g_XMOne); + GSubC = _mm_add_ps(GSubC, g_XMOne); + GAddC = _mm_mul_ps(GAddC, GAddC); + GSubC = _mm_mul_ps(GSubC, GSubC); + GAddC = _mm_div_ps(GAddC, GSubC); + GAddC = _mm_add_ps(GAddC, g_XMOne); + // Multiply the two term parts + vResult = _mm_mul_ps(vResult, GAddC); + // Clamp to 0.0 - 1.0f + vResult = _mm_max_ps(vResult, g_XMZero); + vResult = _mm_min_ps(vResult, g_XMOne); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XMScalarNearEqual +( + float S1, + float S2, + float Epsilon +) noexcept +{ + float Delta = S1 - S2; + return (fabsf(Delta) <= Epsilon); +} + +//------------------------------------------------------------------------------ +// Modulo the range of the given angle such that -XM_PI <= Angle < XM_PI +inline float XMScalarModAngle(float Angle) noexcept +{ + // Note: The modulo is performed with unsigned math only to work + // around a precision error on numbers that are close to PI + + // Normalize the range from 0.0f to XM_2PI + Angle = Angle + XM_PI; + // Perform the modulo, unsigned + float fTemp = fabsf(Angle); + fTemp = fTemp - (XM_2PI * static_cast(static_cast(fTemp / XM_2PI))); + // Restore the number to the range of -XM_PI to XM_PI-epsilon + fTemp = fTemp - XM_PI; + // If the modulo'd value was negative, restore negation + if (Angle < 0.0f) + { + fTemp = -fTemp; + } + return fTemp; +} + +//------------------------------------------------------------------------------ + +inline float XMScalarSin(float Value) noexcept +{ + // Map Value to y in [-pi,pi], x = 2*pi*quotient + remainder. + float quotient = XM_1DIV2PI * Value; + if (Value >= 0.0f) + { + quotient = static_cast(static_cast(quotient + 0.5f)); + } + else + { + quotient = static_cast(static_cast(quotient - 0.5f)); + } + float y = Value - XM_2PI * quotient; + + // Map y to [-pi/2,pi/2] with sin(y) = sin(Value). + if (y > XM_PIDIV2) + { + y = XM_PI - y; + } + else if (y < -XM_PIDIV2) + { + y = -XM_PI - y; + } + + // 11-degree minimax approximation + float y2 = y * y; + return (((((-2.3889859e-08f * y2 + 2.7525562e-06f) * y2 - 0.00019840874f) * y2 + 0.0083333310f) * y2 - 0.16666667f) * y2 + 1.0f) * y; +} + +//------------------------------------------------------------------------------ + +inline float XMScalarSinEst(float Value) noexcept +{ + // Map Value to y in [-pi,pi], x = 2*pi*quotient + remainder. + float quotient = XM_1DIV2PI * Value; + if (Value >= 0.0f) + { + quotient = static_cast(static_cast(quotient + 0.5f)); + } + else + { + quotient = static_cast(static_cast(quotient - 0.5f)); + } + float y = Value - XM_2PI * quotient; + + // Map y to [-pi/2,pi/2] with sin(y) = sin(Value). + if (y > XM_PIDIV2) + { + y = XM_PI - y; + } + else if (y < -XM_PIDIV2) + { + y = -XM_PI - y; + } + + // 7-degree minimax approximation + float y2 = y * y; + return (((-0.00018524670f * y2 + 0.0083139502f) * y2 - 0.16665852f) * y2 + 1.0f) * y; +} + +//------------------------------------------------------------------------------ + +inline float XMScalarCos(float Value) noexcept +{ + // Map Value to y in [-pi,pi], x = 2*pi*quotient + remainder. + float quotient = XM_1DIV2PI * Value; + if (Value >= 0.0f) + { + quotient = static_cast(static_cast(quotient + 0.5f)); + } + else + { + quotient = static_cast(static_cast(quotient - 0.5f)); + } + float y = Value - XM_2PI * quotient; + + // Map y to [-pi/2,pi/2] with cos(y) = sign*cos(x). + float sign; + if (y > XM_PIDIV2) + { + y = XM_PI - y; + sign = -1.0f; + } + else if (y < -XM_PIDIV2) + { + y = -XM_PI - y; + sign = -1.0f; + } + else + { + sign = +1.0f; + } + + // 10-degree minimax approximation + float y2 = y * y; + float p = ((((-2.6051615e-07f * y2 + 2.4760495e-05f) * y2 - 0.0013888378f) * y2 + 0.041666638f) * y2 - 0.5f) * y2 + 1.0f; + return sign * p; +} + +//------------------------------------------------------------------------------ + +inline float XMScalarCosEst(float Value) noexcept +{ + // Map Value to y in [-pi,pi], x = 2*pi*quotient + remainder. + float quotient = XM_1DIV2PI * Value; + if (Value >= 0.0f) + { + quotient = static_cast(static_cast(quotient + 0.5f)); + } + else + { + quotient = static_cast(static_cast(quotient - 0.5f)); + } + float y = Value - XM_2PI * quotient; + + // Map y to [-pi/2,pi/2] with cos(y) = sign*cos(x). + float sign; + if (y > XM_PIDIV2) + { + y = XM_PI - y; + sign = -1.0f; + } + else if (y < -XM_PIDIV2) + { + y = -XM_PI - y; + sign = -1.0f; + } + else + { + sign = +1.0f; + } + + // 6-degree minimax approximation + float y2 = y * y; + float p = ((-0.0012712436f * y2 + 0.041493919f) * y2 - 0.49992746f) * y2 + 1.0f; + return sign * p; +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline void XMScalarSinCos +( + float* pSin, + float* pCos, + float Value +) noexcept +{ + assert(pSin); + assert(pCos); + + // Map Value to y in [-pi,pi], x = 2*pi*quotient + remainder. + float quotient = XM_1DIV2PI * Value; + if (Value >= 0.0f) + { + quotient = static_cast(static_cast(quotient + 0.5f)); + } + else + { + quotient = static_cast(static_cast(quotient - 0.5f)); + } + float y = Value - XM_2PI * quotient; + + // Map y to [-pi/2,pi/2] with sin(y) = sin(Value). + float sign; + if (y > XM_PIDIV2) + { + y = XM_PI - y; + sign = -1.0f; + } + else if (y < -XM_PIDIV2) + { + y = -XM_PI - y; + sign = -1.0f; + } + else + { + sign = +1.0f; + } + + float y2 = y * y; + + // 11-degree minimax approximation + *pSin = (((((-2.3889859e-08f * y2 + 2.7525562e-06f) * y2 - 0.00019840874f) * y2 + 0.0083333310f) * y2 - 0.16666667f) * y2 + 1.0f) * y; + + // 10-degree minimax approximation + float p = ((((-2.6051615e-07f * y2 + 2.4760495e-05f) * y2 - 0.0013888378f) * y2 + 0.041666638f) * y2 - 0.5f) * y2 + 1.0f; + *pCos = sign * p; +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline void XMScalarSinCosEst +( + float* pSin, + float* pCos, + float Value +) noexcept +{ + assert(pSin); + assert(pCos); + + // Map Value to y in [-pi,pi], x = 2*pi*quotient + remainder. + float quotient = XM_1DIV2PI * Value; + if (Value >= 0.0f) + { + quotient = static_cast(static_cast(quotient + 0.5f)); + } + else + { + quotient = static_cast(static_cast(quotient - 0.5f)); + } + float y = Value - XM_2PI * quotient; + + // Map y to [-pi/2,pi/2] with sin(y) = sin(Value). + float sign; + if (y > XM_PIDIV2) + { + y = XM_PI - y; + sign = -1.0f; + } + else if (y < -XM_PIDIV2) + { + y = -XM_PI - y; + sign = -1.0f; + } + else + { + sign = +1.0f; + } + + float y2 = y * y; + + // 7-degree minimax approximation + *pSin = (((-0.00018524670f * y2 + 0.0083139502f) * y2 - 0.16665852f) * y2 + 1.0f) * y; + + // 6-degree minimax approximation + float p = ((-0.0012712436f * y2 + 0.041493919f) * y2 - 0.49992746f) * y2 + 1.0f; + *pCos = sign * p; +} + +//------------------------------------------------------------------------------ + +inline float XMScalarASin(float Value) noexcept +{ + // Clamp input to [-1,1]. + bool nonnegative = (Value >= 0.0f); + float x = fabsf(Value); + float omx = 1.0f - x; + if (omx < 0.0f) + { + omx = 0.0f; + } + float root = sqrtf(omx); + + // 7-degree minimax approximation + float result = ((((((-0.0012624911f * x + 0.0066700901f) * x - 0.0170881256f) * x + 0.0308918810f) * x - 0.0501743046f) * x + 0.0889789874f) * x - 0.2145988016f) * x + 1.5707963050f; + result *= root; // acos(|x|) + + // acos(x) = pi - acos(-x) when x < 0, asin(x) = pi/2 - acos(x) + return (nonnegative ? XM_PIDIV2 - result : result - XM_PIDIV2); +} + +//------------------------------------------------------------------------------ + +inline float XMScalarASinEst(float Value) noexcept +{ + // Clamp input to [-1,1]. + bool nonnegative = (Value >= 0.0f); + float x = fabsf(Value); + float omx = 1.0f - x; + if (omx < 0.0f) + { + omx = 0.0f; + } + float root = sqrtf(omx); + + // 3-degree minimax approximation + float result = ((-0.0187293f * x + 0.0742610f) * x - 0.2121144f) * x + 1.5707288f; + result *= root; // acos(|x|) + + // acos(x) = pi - acos(-x) when x < 0, asin(x) = pi/2 - acos(x) + return (nonnegative ? XM_PIDIV2 - result : result - XM_PIDIV2); +} + +//------------------------------------------------------------------------------ + +inline float XMScalarACos(float Value) noexcept +{ + // Clamp input to [-1,1]. + bool nonnegative = (Value >= 0.0f); + float x = fabsf(Value); + float omx = 1.0f - x; + if (omx < 0.0f) + { + omx = 0.0f; + } + float root = sqrtf(omx); + + // 7-degree minimax approximation + float result = ((((((-0.0012624911f * x + 0.0066700901f) * x - 0.0170881256f) * x + 0.0308918810f) * x - 0.0501743046f) * x + 0.0889789874f) * x - 0.2145988016f) * x + 1.5707963050f; + result *= root; + + // acos(x) = pi - acos(-x) when x < 0 + return (nonnegative ? result : XM_PI - result); +} + +//------------------------------------------------------------------------------ + +inline float XMScalarACosEst(float Value) noexcept +{ + // Clamp input to [-1,1]. + bool nonnegative = (Value >= 0.0f); + float x = fabsf(Value); + float omx = 1.0f - x; + if (omx < 0.0f) + { + omx = 0.0f; + } + float root = sqrtf(omx); + + // 3-degree minimax approximation + float result = ((-0.0187293f * x + 0.0742610f) * x - 0.2121144f) * x + 1.5707288f; + result *= root; + + // acos(x) = pi - acos(-x) when x < 0 + return (nonnegative ? result : XM_PI - result); +} + diff --git a/Extern/dxmath/Inc/DirectXMathVector.inl b/Extern/dxmath/Inc/DirectXMathVector.inl new file mode 100644 index 000000000..c25b902ca --- /dev/null +++ b/Extern/dxmath/Inc/DirectXMathVector.inl @@ -0,0 +1,14869 @@ +//------------------------------------------------------------------------------------- +// DirectXMathVector.inl -- SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#if defined(_XM_NO_INTRINSICS_) +#define XMISNAN(x) isnan(x) +#define XMISINF(x) isinf(x) +#endif + +#if defined(_XM_SSE_INTRINSICS_) + +#define XM3UNPACK3INTO4(l1, l2, l3) \ + XMVECTOR V3 = _mm_shuffle_ps(l2, l3, _MM_SHUFFLE(0, 0, 3, 2));\ + XMVECTOR V2 = _mm_shuffle_ps(l2, l1, _MM_SHUFFLE(3, 3, 1, 0));\ + V2 = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 0, 2));\ + XMVECTOR V4 = _mm_castsi128_ps(_mm_srli_si128(_mm_castps_si128(L3), 32 / 8)) + +#define XM3PACK4INTO3(v2x) \ + v2x = _mm_shuffle_ps(V2, V3, _MM_SHUFFLE(1, 0, 2, 1));\ + V2 = _mm_shuffle_ps(V2, V1, _MM_SHUFFLE(2, 2, 0, 0));\ + V1 = _mm_shuffle_ps(V1, V2, _MM_SHUFFLE(0, 2, 1, 0));\ + V3 = _mm_shuffle_ps(V3, V4, _MM_SHUFFLE(0, 0, 2, 2));\ + V3 = _mm_shuffle_ps(V3, V4, _MM_SHUFFLE(2, 1, 2, 0)) + +#endif + +/**************************************************************************** + * + * General Vector + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Assignment operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Return a vector with all elements equaling zero +inline XMVECTOR XM_CALLCONV XMVectorZero() noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { 0.0f, 0.0f, 0.0f, 0.0f } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vdupq_n_f32(0); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_setzero_ps(); +#endif +} + +//------------------------------------------------------------------------------ +// Initialize a vector with four floating point values +inline XMVECTOR XM_CALLCONV XMVectorSet +( + float x, + float y, + float z, + float w +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { x, y, z, w } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t V0 = vcreate_f32( + static_cast(*reinterpret_cast(&x)) + | (static_cast(*reinterpret_cast(&y)) << 32)); + float32x2_t V1 = vcreate_f32( + static_cast(*reinterpret_cast(&z)) + | (static_cast(*reinterpret_cast(&w)) << 32)); + return vcombine_f32(V0, V1); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_set_ps(w, z, y, x); +#endif +} + +//------------------------------------------------------------------------------ +// Initialize a vector with four integer values +inline XMVECTOR XM_CALLCONV XMVectorSetInt +( + uint32_t x, + uint32_t y, + uint32_t z, + uint32_t w +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 vResult = { { { x, y, z, w } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t V0 = vcreate_u32(static_cast(x) | (static_cast(y) << 32)); + uint32x2_t V1 = vcreate_u32(static_cast(z) | (static_cast(w) << 32)); + return vreinterpretq_f32_u32(vcombine_u32(V0, V1)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_set_epi32(static_cast(w), static_cast(z), static_cast(y), static_cast(x)); + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ +// Initialize a vector with a replicated floating point value +inline XMVECTOR XM_CALLCONV XMVectorReplicate(float Value) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult; + vResult.f[0] = + vResult.f[1] = + vResult.f[2] = + vResult.f[3] = Value; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vdupq_n_f32(Value); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_set_ps1(Value); +#endif +} + +//------------------------------------------------------------------------------ +// Initialize a vector with a replicated floating point value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorReplicatePtr(const float* pValue) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float Value = pValue[0]; + XMVECTORF32 vResult; + vResult.f[0] = + vResult.f[1] = + vResult.f[2] = + vResult.f[3] = Value; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vld1q_dup_f32(pValue); +#elif defined(_XM_AVX_INTRINSICS_) + return _mm_broadcast_ss(pValue); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_load_ps1(pValue); +#endif +} + +//------------------------------------------------------------------------------ +// Initialize a vector with a replicated integer value +inline XMVECTOR XM_CALLCONV XMVectorReplicateInt(uint32_t Value) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 vResult; + vResult.u[0] = + vResult.u[1] = + vResult.u[2] = + vResult.u[3] = Value; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vdupq_n_u32(Value)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_set1_epi32(static_cast(Value)); + return _mm_castsi128_ps(vTemp); +#endif +} + +//------------------------------------------------------------------------------ +// Initialize a vector with a replicated integer value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorReplicateIntPtr(const uint32_t* pValue) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + uint32_t Value = pValue[0]; + XMVECTORU32 vResult; + vResult.u[0] = + vResult.u[1] = + vResult.u[2] = + vResult.u[3] = Value; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vld1q_dup_u32(pValue)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_load_ps1(reinterpret_cast(pValue)); +#endif +} + +//------------------------------------------------------------------------------ +// Initialize a vector with all bits set (true mask) +inline XMVECTOR XM_CALLCONV XMVectorTrueInt() noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 vResult = { { { 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_s32(vdupq_n_s32(-1)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_set1_epi32(-1); + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ +// Initialize a vector with all bits clear (false mask) +inline XMVECTOR XM_CALLCONV XMVectorFalseInt() noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { 0.0f, 0.0f, 0.0f, 0.0f } } }; + return vResult; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vdupq_n_u32(0)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_setzero_ps(); +#endif +} + +//------------------------------------------------------------------------------ +// Replicate the x component of the vector +inline XMVECTOR XM_CALLCONV XMVectorSplatX(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult; + vResult.f[0] = + vResult.f[1] = + vResult.f[2] = + vResult.f[3] = V.vector4_f32[0]; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vdupq_lane_f32(vget_low_f32(V), 0); +#elif defined(_XM_AVX2_INTRINSICS_) && defined(_XM_FAVOR_INTEL_) + return _mm_broadcastss_ps(V); +#elif defined(_XM_SSE_INTRINSICS_) + return XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); +#endif +} + +//------------------------------------------------------------------------------ +// Replicate the y component of the vector +inline XMVECTOR XM_CALLCONV XMVectorSplatY(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult; + vResult.f[0] = + vResult.f[1] = + vResult.f[2] = + vResult.f[3] = V.vector4_f32[1]; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vdupq_lane_f32(vget_low_f32(V), 1); +#elif defined(_XM_SSE_INTRINSICS_) + return XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); +#endif +} + +//------------------------------------------------------------------------------ +// Replicate the z component of the vector +inline XMVECTOR XM_CALLCONV XMVectorSplatZ(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult; + vResult.f[0] = + vResult.f[1] = + vResult.f[2] = + vResult.f[3] = V.vector4_f32[2]; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vdupq_lane_f32(vget_high_f32(V), 0); +#elif defined(_XM_SSE_INTRINSICS_) + return XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); +#endif +} + +//------------------------------------------------------------------------------ +// Replicate the w component of the vector +inline XMVECTOR XM_CALLCONV XMVectorSplatW(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult; + vResult.f[0] = + vResult.f[1] = + vResult.f[2] = + vResult.f[3] = V.vector4_f32[3]; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vdupq_lane_f32(vget_high_f32(V), 1); +#elif defined(_XM_SSE_INTRINSICS_) + return XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); +#endif +} + +//------------------------------------------------------------------------------ +// Return a vector of 1.0f,1.0f,1.0f,1.0f +inline XMVECTOR XM_CALLCONV XMVectorSplatOne() noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult; + vResult.f[0] = + vResult.f[1] = + vResult.f[2] = + vResult.f[3] = 1.0f; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vdupq_n_f32(1.0f); +#elif defined(_XM_SSE_INTRINSICS_) + return g_XMOne; +#endif +} + +//------------------------------------------------------------------------------ +// Return a vector of INF,INF,INF,INF +inline XMVECTOR XM_CALLCONV XMVectorSplatInfinity() noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 vResult; + vResult.u[0] = + vResult.u[1] = + vResult.u[2] = + vResult.u[3] = 0x7F800000; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vdupq_n_u32(0x7F800000)); +#elif defined(_XM_SSE_INTRINSICS_) + return g_XMInfinity; +#endif +} + +//------------------------------------------------------------------------------ +// Return a vector of Q_NAN,Q_NAN,Q_NAN,Q_NAN +inline XMVECTOR XM_CALLCONV XMVectorSplatQNaN() noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 vResult; + vResult.u[0] = + vResult.u[1] = + vResult.u[2] = + vResult.u[3] = 0x7FC00000; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vdupq_n_u32(0x7FC00000)); +#elif defined(_XM_SSE_INTRINSICS_) + return g_XMQNaN; +#endif +} + +//------------------------------------------------------------------------------ +// Return a vector of 1.192092896e-7f,1.192092896e-7f,1.192092896e-7f,1.192092896e-7f +inline XMVECTOR XM_CALLCONV XMVectorSplatEpsilon() noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 vResult; + vResult.u[0] = + vResult.u[1] = + vResult.u[2] = + vResult.u[3] = 0x34000000; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vdupq_n_u32(0x34000000)); +#elif defined(_XM_SSE_INTRINSICS_) + return g_XMEpsilon; +#endif +} + +//------------------------------------------------------------------------------ +// Return a vector of -0.0f (0x80000000),-0.0f,-0.0f,-0.0f +inline XMVECTOR XM_CALLCONV XMVectorSplatSignMask() noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 vResult; + vResult.u[0] = + vResult.u[1] = + vResult.u[2] = + vResult.u[3] = 0x80000000U; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vdupq_n_u32(0x80000000U)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_set1_epi32(static_cast(0x80000000)); + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ +// Return a floating point value via an index. This is not a recommended +// function to use due to performance loss. +inline float XM_CALLCONV XMVectorGetByIndex(FXMVECTOR V, size_t i) noexcept +{ + assert(i < 4); + _Analysis_assume_(i < 4); +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_f32[i]; +#else + XMVECTORF32 U; + U.v = V; + return U.f[i]; +#endif +} + +//------------------------------------------------------------------------------ +// Return the X component in an FPU register. +inline float XM_CALLCONV XMVectorGetX(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_f32[0]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vgetq_lane_f32(V, 0); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_cvtss_f32(V); +#endif +} + +// Return the Y component in an FPU register. +inline float XM_CALLCONV XMVectorGetY(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_f32[1]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vgetq_lane_f32(V, 1); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + return _mm_cvtss_f32(vTemp); +#endif +} + +// Return the Z component in an FPU register. +inline float XM_CALLCONV XMVectorGetZ(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_f32[2]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vgetq_lane_f32(V, 2); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + return _mm_cvtss_f32(vTemp); +#endif +} + +// Return the W component in an FPU register. +inline float XM_CALLCONV XMVectorGetW(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_f32[3]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vgetq_lane_f32(V, 3); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + return _mm_cvtss_f32(vTemp); +#endif +} + +//------------------------------------------------------------------------------ + +// Store a component indexed by i into a 32 bit float location in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetByIndexPtr(float* f, FXMVECTOR V, size_t i) noexcept +{ + assert(f != nullptr); + assert(i < 4); + _Analysis_assume_(i < 4); +#if defined(_XM_NO_INTRINSICS_) + *f = V.vector4_f32[i]; +#else + XMVECTORF32 U; + U.v = V; + *f = U.f[i]; +#endif +} + +//------------------------------------------------------------------------------ + +// Store the X component into a 32 bit float location in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetXPtr(float* x, FXMVECTOR V) noexcept +{ + assert(x != nullptr); +#if defined(_XM_NO_INTRINSICS_) + *x = V.vector4_f32[0]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_f32(x, V, 0); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_ss(x, V); +#endif +} + +// Store the Y component into a 32 bit float location in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetYPtr(float* y, FXMVECTOR V) noexcept +{ + assert(y != nullptr); +#if defined(_XM_NO_INTRINSICS_) + *y = V.vector4_f32[1]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_f32(y, V, 1); +#elif defined(_XM_SSE4_INTRINSICS_) + * (reinterpret_cast(y)) = _mm_extract_ps(V, 1); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + _mm_store_ss(y, vResult); +#endif +} + +// Store the Z component into a 32 bit float location in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetZPtr(float* z, FXMVECTOR V) noexcept +{ + assert(z != nullptr); +#if defined(_XM_NO_INTRINSICS_) + *z = V.vector4_f32[2]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_f32(z, V, 2); +#elif defined(_XM_SSE4_INTRINSICS_) + * (reinterpret_cast(z)) = _mm_extract_ps(V, 2); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + _mm_store_ss(z, vResult); +#endif +} + +// Store the W component into a 32 bit float location in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetWPtr(float* w, FXMVECTOR V) noexcept +{ + assert(w != nullptr); +#if defined(_XM_NO_INTRINSICS_) + *w = V.vector4_f32[3]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_f32(w, V, 3); +#elif defined(_XM_SSE4_INTRINSICS_) + * (reinterpret_cast(w)) = _mm_extract_ps(V, 3); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + _mm_store_ss(w, vResult); +#endif +} + +//------------------------------------------------------------------------------ + +// Return an integer value via an index. This is not a recommended +// function to use due to performance loss. +inline uint32_t XM_CALLCONV XMVectorGetIntByIndex(FXMVECTOR V, size_t i) noexcept +{ + assert(i < 4); + _Analysis_assume_(i < 4); +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_u32[i]; +#else + XMVECTORU32 U; + U.v = V; + return U.u[i]; +#endif +} + +//------------------------------------------------------------------------------ + +// Return the X component in an integer register. +inline uint32_t XM_CALLCONV XMVectorGetIntX(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_u32[0]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vgetq_lane_u32(vreinterpretq_u32_f32(V), 0); +#elif defined(_XM_SSE_INTRINSICS_) + return static_cast(_mm_cvtsi128_si32(_mm_castps_si128(V))); +#endif +} + +// Return the Y component in an integer register. +inline uint32_t XM_CALLCONV XMVectorGetIntY(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_u32[1]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vgetq_lane_u32(vreinterpretq_u32_f32(V), 1); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128i V1 = _mm_castps_si128(V); + return static_cast(_mm_extract_epi32(V1, 1)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vResulti = _mm_shuffle_epi32(_mm_castps_si128(V), _MM_SHUFFLE(1, 1, 1, 1)); + return static_cast(_mm_cvtsi128_si32(vResulti)); +#endif +} + +// Return the Z component in an integer register. +inline uint32_t XM_CALLCONV XMVectorGetIntZ(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_u32[2]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vgetq_lane_u32(vreinterpretq_u32_f32(V), 2); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128i V1 = _mm_castps_si128(V); + return static_cast(_mm_extract_epi32(V1, 2)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vResulti = _mm_shuffle_epi32(_mm_castps_si128(V), _MM_SHUFFLE(2, 2, 2, 2)); + return static_cast(_mm_cvtsi128_si32(vResulti)); +#endif +} + +// Return the W component in an integer register. +inline uint32_t XM_CALLCONV XMVectorGetIntW(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return V.vector4_u32[3]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vgetq_lane_u32(vreinterpretq_u32_f32(V), 3); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128i V1 = _mm_castps_si128(V); + return static_cast(_mm_extract_epi32(V1, 3)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vResulti = _mm_shuffle_epi32(_mm_castps_si128(V), _MM_SHUFFLE(3, 3, 3, 3)); + return static_cast(_mm_cvtsi128_si32(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ + +// Store a component indexed by i into a 32 bit integer location in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetIntByIndexPtr(uint32_t* x, FXMVECTOR V, size_t i) noexcept +{ + assert(x != nullptr); + assert(i < 4); + _Analysis_assume_(i < 4); +#if defined(_XM_NO_INTRINSICS_) + *x = V.vector4_u32[i]; +#else + XMVECTORU32 U; + U.v = V; + *x = U.u[i]; +#endif +} + +//------------------------------------------------------------------------------ + +// Store the X component into a 32 bit integer location in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetIntXPtr(uint32_t* x, FXMVECTOR V) noexcept +{ + assert(x != nullptr); +#if defined(_XM_NO_INTRINSICS_) + *x = V.vector4_u32[0]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_u32(x, *reinterpret_cast(&V), 0); +#elif defined(_XM_SSE_INTRINSICS_) + _mm_store_ss(reinterpret_cast(x), V); +#endif +} + +// Store the Y component into a 32 bit integer location in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetIntYPtr(uint32_t* y, FXMVECTOR V) noexcept +{ + assert(y != nullptr); +#if defined(_XM_NO_INTRINSICS_) + *y = V.vector4_u32[1]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_u32(y, *reinterpret_cast(&V), 1); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128i V1 = _mm_castps_si128(V); + *y = static_cast(_mm_extract_epi32(V1, 1)); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + _mm_store_ss(reinterpret_cast(y), vResult); +#endif +} + +// Store the Z component into a 32 bit integer locaCantion in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetIntZPtr(uint32_t* z, FXMVECTOR V) noexcept +{ + assert(z != nullptr); +#if defined(_XM_NO_INTRINSICS_) + *z = V.vector4_u32[2]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_u32(z, *reinterpret_cast(&V), 2); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128i V1 = _mm_castps_si128(V); + *z = static_cast(_mm_extract_epi32(V1, 2)); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + _mm_store_ss(reinterpret_cast(z), vResult); +#endif +} + +// Store the W component into a 32 bit integer location in memory. +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorGetIntWPtr(uint32_t* w, FXMVECTOR V) noexcept +{ + assert(w != nullptr); +#if defined(_XM_NO_INTRINSICS_) + *w = V.vector4_u32[3]; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + vst1q_lane_u32(w, *reinterpret_cast(&V), 3); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128i V1 = _mm_castps_si128(V); + *w = static_cast(_mm_extract_epi32(V1, 3)); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + _mm_store_ss(reinterpret_cast(w), vResult); +#endif +} + +//------------------------------------------------------------------------------ + +// Set a single indexed floating point component +inline XMVECTOR XM_CALLCONV XMVectorSetByIndex(FXMVECTOR V, float f, size_t i) noexcept +{ + assert(i < 4); + _Analysis_assume_(i < 4); + XMVECTORF32 U; + U.v = V; + U.f[i] = f; + return U.v; +} + +//------------------------------------------------------------------------------ + +// Sets the X component of a vector to a passed floating point value +inline XMVECTOR XM_CALLCONV XMVectorSetX(FXMVECTOR V, float x) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 U = { { { + x, + V.vector4_f32[1], + V.vector4_f32[2], + V.vector4_f32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vsetq_lane_f32(x, V, 0); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = _mm_set_ss(x); + vResult = _mm_move_ss(V, vResult); + return vResult; +#endif +} + +// Sets the Y component of a vector to a passed floating point value +inline XMVECTOR XM_CALLCONV XMVectorSetY(FXMVECTOR V, float y) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 U = { { { + V.vector4_f32[0], + y, + V.vector4_f32[2], + V.vector4_f32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vsetq_lane_f32(y, V, 1); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vResult = _mm_set_ss(y); + vResult = _mm_insert_ps(V, vResult, 0x10); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Swap y and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 2, 0, 1)); + // Convert input to vector + XMVECTOR vTemp = _mm_set_ss(y); + // Replace the x component + vResult = _mm_move_ss(vResult, vTemp); + // Swap y and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 2, 0, 1)); + return vResult; +#endif +} +// Sets the Z component of a vector to a passed floating point value +inline XMVECTOR XM_CALLCONV XMVectorSetZ(FXMVECTOR V, float z) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 U = { { { + V.vector4_f32[0], + V.vector4_f32[1], + z, + V.vector4_f32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vsetq_lane_f32(z, V, 2); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vResult = _mm_set_ss(z); + vResult = _mm_insert_ps(V, vResult, 0x20); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Swap z and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 0, 1, 2)); + // Convert input to vector + XMVECTOR vTemp = _mm_set_ss(z); + // Replace the x component + vResult = _mm_move_ss(vResult, vTemp); + // Swap z and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 0, 1, 2)); + return vResult; +#endif +} + +// Sets the W component of a vector to a passed floating point value +inline XMVECTOR XM_CALLCONV XMVectorSetW(FXMVECTOR V, float w) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 U = { { { + V.vector4_f32[0], + V.vector4_f32[1], + V.vector4_f32[2], + w + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vsetq_lane_f32(w, V, 3); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vResult = _mm_set_ss(w); + vResult = _mm_insert_ps(V, vResult, 0x30); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Swap w and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 2, 1, 3)); + // Convert input to vector + XMVECTOR vTemp = _mm_set_ss(w); + // Replace the x component + vResult = _mm_move_ss(vResult, vTemp); + // Swap w and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(0, 2, 1, 3)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +// Sets a component of a vector to a floating point value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetByIndexPtr(FXMVECTOR V, const float* f, size_t i) noexcept +{ + assert(f != nullptr); + assert(i < 4); + _Analysis_assume_(i < 4); + XMVECTORF32 U; + U.v = V; + U.f[i] = *f; + return U.v; +} + +//------------------------------------------------------------------------------ + +// Sets the X component of a vector to a floating point value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetXPtr(FXMVECTOR V, const float* x) noexcept +{ + assert(x != nullptr); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 U = { { { + *x, + V.vector4_f32[1], + V.vector4_f32[2], + V.vector4_f32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vld1q_lane_f32(x, V, 0); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = _mm_load_ss(x); + vResult = _mm_move_ss(V, vResult); + return vResult; +#endif +} + +// Sets the Y component of a vector to a floating point value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetYPtr(FXMVECTOR V, const float* y) noexcept +{ + assert(y != nullptr); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 U = { { { + V.vector4_f32[0], + *y, + V.vector4_f32[2], + V.vector4_f32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vld1q_lane_f32(y, V, 1); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap y and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 2, 0, 1)); + // Convert input to vector + XMVECTOR vTemp = _mm_load_ss(y); + // Replace the x component + vResult = _mm_move_ss(vResult, vTemp); + // Swap y and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 2, 0, 1)); + return vResult; +#endif +} + +// Sets the Z component of a vector to a floating point value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetZPtr(FXMVECTOR V, const float* z) noexcept +{ + assert(z != nullptr); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 U = { { { + V.vector4_f32[0], + V.vector4_f32[1], + *z, + V.vector4_f32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vld1q_lane_f32(z, V, 2); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap z and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 0, 1, 2)); + // Convert input to vector + XMVECTOR vTemp = _mm_load_ss(z); + // Replace the x component + vResult = _mm_move_ss(vResult, vTemp); + // Swap z and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 0, 1, 2)); + return vResult; +#endif +} + +// Sets the W component of a vector to a floating point value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetWPtr(FXMVECTOR V, const float* w) noexcept +{ + assert(w != nullptr); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 U = { { { + V.vector4_f32[0], + V.vector4_f32[1], + V.vector4_f32[2], + *w + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vld1q_lane_f32(w, V, 3); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap w and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 2, 1, 3)); + // Convert input to vector + XMVECTOR vTemp = _mm_load_ss(w); + // Replace the x component + vResult = _mm_move_ss(vResult, vTemp); + // Swap w and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(0, 2, 1, 3)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +// Sets a component of a vector to an integer passed by value +inline XMVECTOR XM_CALLCONV XMVectorSetIntByIndex(FXMVECTOR V, uint32_t x, size_t i) noexcept +{ + assert(i < 4); + _Analysis_assume_(i < 4); + XMVECTORU32 tmp; + tmp.v = V; + tmp.u[i] = x; + return tmp; +} + +//------------------------------------------------------------------------------ + +// Sets the X component of a vector to an integer passed by value +inline XMVECTOR XM_CALLCONV XMVectorSetIntX(FXMVECTOR V, uint32_t x) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 U = { { { + x, + V.vector4_u32[1], + V.vector4_u32[2], + V.vector4_u32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vsetq_lane_u32(x, vreinterpretq_u32_f32(V), 0)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cvtsi32_si128(static_cast(x)); + XMVECTOR vResult = _mm_move_ss(V, _mm_castsi128_ps(vTemp)); + return vResult; +#endif +} + +// Sets the Y component of a vector to an integer passed by value +inline XMVECTOR XM_CALLCONV XMVectorSetIntY(FXMVECTOR V, uint32_t y) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 U = { { { + V.vector4_u32[0], + y, + V.vector4_u32[2], + V.vector4_u32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vsetq_lane_u32(y, vreinterpretq_u32_f32(V), 1)); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128i vResult = _mm_castps_si128(V); + vResult = _mm_insert_epi32(vResult, static_cast(y), 1); + return _mm_castsi128_ps(vResult); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap y and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 2, 0, 1)); + // Convert input to vector + __m128i vTemp = _mm_cvtsi32_si128(static_cast(y)); + // Replace the x component + vResult = _mm_move_ss(vResult, _mm_castsi128_ps(vTemp)); + // Swap y and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 2, 0, 1)); + return vResult; +#endif +} + +// Sets the Z component of a vector to an integer passed by value +inline XMVECTOR XM_CALLCONV XMVectorSetIntZ(FXMVECTOR V, uint32_t z) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 U = { { { + V.vector4_u32[0], + V.vector4_u32[1], + z, + V.vector4_u32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vsetq_lane_u32(z, vreinterpretq_u32_f32(V), 2)); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128i vResult = _mm_castps_si128(V); + vResult = _mm_insert_epi32(vResult, static_cast(z), 2); + return _mm_castsi128_ps(vResult); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap z and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 0, 1, 2)); + // Convert input to vector + __m128i vTemp = _mm_cvtsi32_si128(static_cast(z)); + // Replace the x component + vResult = _mm_move_ss(vResult, _mm_castsi128_ps(vTemp)); + // Swap z and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 0, 1, 2)); + return vResult; +#endif +} + +// Sets the W component of a vector to an integer passed by value +inline XMVECTOR XM_CALLCONV XMVectorSetIntW(FXMVECTOR V, uint32_t w) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 U = { { { + V.vector4_u32[0], + V.vector4_u32[1], + V.vector4_u32[2], + w + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vsetq_lane_u32(w, vreinterpretq_u32_f32(V), 3)); +#elif defined(_XM_SSE4_INTRINSICS_) + __m128i vResult = _mm_castps_si128(V); + vResult = _mm_insert_epi32(vResult, static_cast(w), 3); + return _mm_castsi128_ps(vResult); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap w and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 2, 1, 3)); + // Convert input to vector + __m128i vTemp = _mm_cvtsi32_si128(static_cast(w)); + // Replace the x component + vResult = _mm_move_ss(vResult, _mm_castsi128_ps(vTemp)); + // Swap w and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(0, 2, 1, 3)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +// Sets a component of a vector to an integer value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetIntByIndexPtr(FXMVECTOR V, const uint32_t* x, size_t i) noexcept +{ + assert(x != nullptr); + assert(i < 4); + _Analysis_assume_(i < 4); + XMVECTORU32 tmp; + tmp.v = V; + tmp.u[i] = *x; + return tmp; +} + +//------------------------------------------------------------------------------ + +// Sets the X component of a vector to an integer value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetIntXPtr(FXMVECTOR V, const uint32_t* x) noexcept +{ + assert(x != nullptr); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 U = { { { + *x, + V.vector4_u32[1], + V.vector4_u32[2], + V.vector4_u32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vld1q_lane_u32(x, *reinterpret_cast(&V), 0)); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_load_ss(reinterpret_cast(x)); + XMVECTOR vResult = _mm_move_ss(V, vTemp); + return vResult; +#endif +} + +// Sets the Y component of a vector to an integer value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetIntYPtr(FXMVECTOR V, const uint32_t* y) noexcept +{ + assert(y != nullptr); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 U = { { { + V.vector4_u32[0], + *y, + V.vector4_u32[2], + V.vector4_u32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vld1q_lane_u32(y, *reinterpret_cast(&V), 1)); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap y and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 2, 0, 1)); + // Convert input to vector + XMVECTOR vTemp = _mm_load_ss(reinterpret_cast(y)); + // Replace the x component + vResult = _mm_move_ss(vResult, vTemp); + // Swap y and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 2, 0, 1)); + return vResult; +#endif +} + +// Sets the Z component of a vector to an integer value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetIntZPtr(FXMVECTOR V, const uint32_t* z) noexcept +{ + assert(z != nullptr); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 U = { { { + V.vector4_u32[0], + V.vector4_u32[1], + *z, + V.vector4_u32[3] + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vld1q_lane_u32(z, *reinterpret_cast(&V), 2)); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap z and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 0, 1, 2)); + // Convert input to vector + XMVECTOR vTemp = _mm_load_ss(reinterpret_cast(z)); + // Replace the x component + vResult = _mm_move_ss(vResult, vTemp); + // Swap z and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 0, 1, 2)); + return vResult; +#endif +} + +// Sets the W component of a vector to an integer value passed by pointer +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorSetIntWPtr(FXMVECTOR V, const uint32_t* w) noexcept +{ + assert(w != nullptr); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORU32 U = { { { + V.vector4_u32[0], + V.vector4_u32[1], + V.vector4_u32[2], + *w + } } }; + return U.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vld1q_lane_u32(w, *reinterpret_cast(&V), 3)); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap w and x + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 2, 1, 3)); + // Convert input to vector + XMVECTOR vTemp = _mm_load_ss(reinterpret_cast(w)); + // Replace the x component + vResult = _mm_move_ss(vResult, vTemp); + // Swap w and x again + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(0, 2, 1, 3)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSwizzle +( + FXMVECTOR V, + uint32_t E0, + uint32_t E1, + uint32_t E2, + uint32_t E3 +) noexcept +{ + assert((E0 < 4) && (E1 < 4) && (E2 < 4) && (E3 < 4)); + _Analysis_assume_((E0 < 4) && (E1 < 4) && (E2 < 4) && (E3 < 4)); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + V.vector4_f32[E0], + V.vector4_f32[E1], + V.vector4_f32[E2], + V.vector4_f32[E3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const uint32_t ControlElement[4] = + { + 0x03020100, // XM_SWIZZLE_X + 0x07060504, // XM_SWIZZLE_Y + 0x0B0A0908, // XM_SWIZZLE_Z + 0x0F0E0D0C, // XM_SWIZZLE_W + }; + + uint8x8x2_t tbl; + tbl.val[0] = vreinterpret_u8_f32(vget_low_f32(V)); + tbl.val[1] = vreinterpret_u8_f32(vget_high_f32(V)); + + uint32x2_t idx = vcreate_u32(static_cast(ControlElement[E0]) | (static_cast(ControlElement[E1]) << 32)); + const uint8x8_t rL = vtbl2_u8(tbl, vreinterpret_u8_u32(idx)); + + idx = vcreate_u32(static_cast(ControlElement[E2]) | (static_cast(ControlElement[E3]) << 32)); + const uint8x8_t rH = vtbl2_u8(tbl, vreinterpret_u8_u32(idx)); + + return vcombine_f32(vreinterpret_f32_u8(rL), vreinterpret_f32_u8(rH)); +#elif defined(_XM_AVX_INTRINSICS_) + unsigned int elem[4] = { E0, E1, E2, E3 }; + __m128i vControl = _mm_loadu_si128(reinterpret_cast(&elem[0])); + return _mm_permutevar_ps(V, vControl); +#else + auto aPtr = reinterpret_cast(&V); + + XMVECTOR Result; + auto pWork = reinterpret_cast(&Result); + + pWork[0] = aPtr[E0]; + pWork[1] = aPtr[E1]; + pWork[2] = aPtr[E2]; + pWork[3] = aPtr[E3]; + + return Result; +#endif +} + +//------------------------------------------------------------------------------ +inline XMVECTOR XM_CALLCONV XMVectorPermute +( + FXMVECTOR V1, + FXMVECTOR V2, + uint32_t PermuteX, + uint32_t PermuteY, + uint32_t PermuteZ, + uint32_t PermuteW +) noexcept +{ + assert(PermuteX <= 7 && PermuteY <= 7 && PermuteZ <= 7 && PermuteW <= 7); + _Analysis_assume_(PermuteX <= 7 && PermuteY <= 7 && PermuteZ <= 7 && PermuteW <= 7); + +#if defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + static const uint32_t ControlElement[8] = + { + 0x03020100, // XM_PERMUTE_0X + 0x07060504, // XM_PERMUTE_0Y + 0x0B0A0908, // XM_PERMUTE_0Z + 0x0F0E0D0C, // XM_PERMUTE_0W + 0x13121110, // XM_PERMUTE_1X + 0x17161514, // XM_PERMUTE_1Y + 0x1B1A1918, // XM_PERMUTE_1Z + 0x1F1E1D1C, // XM_PERMUTE_1W + }; + + uint8x8x4_t tbl; + tbl.val[0] = vreinterpret_u8_f32(vget_low_f32(V1)); + tbl.val[1] = vreinterpret_u8_f32(vget_high_f32(V1)); + tbl.val[2] = vreinterpret_u8_f32(vget_low_f32(V2)); + tbl.val[3] = vreinterpret_u8_f32(vget_high_f32(V2)); + + uint32x2_t idx = vcreate_u32(static_cast(ControlElement[PermuteX]) | (static_cast(ControlElement[PermuteY]) << 32)); + const uint8x8_t rL = vtbl4_u8(tbl, vreinterpret_u8_u32(idx)); + + idx = vcreate_u32(static_cast(ControlElement[PermuteZ]) | (static_cast(ControlElement[PermuteW]) << 32)); + const uint8x8_t rH = vtbl4_u8(tbl, vreinterpret_u8_u32(idx)); + + return vcombine_f32(vreinterpret_f32_u8(rL), vreinterpret_f32_u8(rH)); +#elif defined(_XM_AVX_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + static const XMVECTORU32 three = { { { 3, 3, 3, 3 } } }; + + XM_ALIGNED_DATA(16) unsigned int elem[4] = { PermuteX, PermuteY, PermuteZ, PermuteW }; + __m128i vControl = _mm_load_si128(reinterpret_cast(&elem[0])); + + __m128i vSelect = _mm_cmpgt_epi32(vControl, three); + vControl = _mm_castps_si128(_mm_and_ps(_mm_castsi128_ps(vControl), three)); + + __m128 shuffled1 = _mm_permutevar_ps(V1, vControl); + __m128 shuffled2 = _mm_permutevar_ps(V2, vControl); + + __m128 masked1 = _mm_andnot_ps(_mm_castsi128_ps(vSelect), shuffled1); + __m128 masked2 = _mm_and_ps(_mm_castsi128_ps(vSelect), shuffled2); + + return _mm_or_ps(masked1, masked2); +#else + + const uint32_t* aPtr[2]; + aPtr[0] = reinterpret_cast(&V1); + aPtr[1] = reinterpret_cast(&V2); + + XMVECTOR Result; + auto pWork = reinterpret_cast(&Result); + + const uint32_t i0 = PermuteX & 3; + const uint32_t vi0 = PermuteX >> 2; + pWork[0] = aPtr[vi0][i0]; + + const uint32_t i1 = PermuteY & 3; + const uint32_t vi1 = PermuteY >> 2; + pWork[1] = aPtr[vi1][i1]; + + const uint32_t i2 = PermuteZ & 3; + const uint32_t vi2 = PermuteZ >> 2; + pWork[2] = aPtr[vi2][i2]; + + const uint32_t i3 = PermuteW & 3; + const uint32_t vi3 = PermuteW >> 2; + pWork[3] = aPtr[vi3][i3]; + + return Result; +#endif +} + +//------------------------------------------------------------------------------ +// Define a control vector to be used in XMVectorSelect +// operations. The four integers specified in XMVectorSelectControl +// serve as indices to select between components in two vectors. +// The first index controls selection for the first component of +// the vectors involved in a select operation, the second index +// controls selection for the second component etc. A value of +// zero for an index causes the corresponding component from the first +// vector to be selected whereas a one causes the component from the +// second vector to be selected instead. + +inline XMVECTOR XM_CALLCONV XMVectorSelectControl +( + uint32_t VectorIndex0, + uint32_t VectorIndex1, + uint32_t VectorIndex2, + uint32_t VectorIndex3 +) noexcept +{ +#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + // x=Index0,y=Index1,z=Index2,w=Index3 + __m128i vTemp = _mm_set_epi32(static_cast(VectorIndex3), static_cast(VectorIndex2), static_cast(VectorIndex1), static_cast(VectorIndex0)); + // Any non-zero entries become 0xFFFFFFFF else 0 + vTemp = _mm_cmpgt_epi32(vTemp, g_XMZero); + return _mm_castsi128_ps(vTemp); +#elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + int32x2_t V0 = vcreate_s32(static_cast(VectorIndex0) | (static_cast(VectorIndex1) << 32)); + int32x2_t V1 = vcreate_s32(static_cast(VectorIndex2) | (static_cast(VectorIndex3) << 32)); + int32x4_t vTemp = vcombine_s32(V0, V1); + // Any non-zero entries become 0xFFFFFFFF else 0 + return vreinterpretq_f32_u32(vcgtq_s32(vTemp, g_XMZero)); +#else + XMVECTOR ControlVector; + const uint32_t ControlElement[] = + { + XM_SELECT_0, + XM_SELECT_1 + }; + + assert(VectorIndex0 < 2); + assert(VectorIndex1 < 2); + assert(VectorIndex2 < 2); + assert(VectorIndex3 < 2); + _Analysis_assume_(VectorIndex0 < 2); + _Analysis_assume_(VectorIndex1 < 2); + _Analysis_assume_(VectorIndex2 < 2); + _Analysis_assume_(VectorIndex3 < 2); + + ControlVector.vector4_u32[0] = ControlElement[VectorIndex0]; + ControlVector.vector4_u32[1] = ControlElement[VectorIndex1]; + ControlVector.vector4_u32[2] = ControlElement[VectorIndex2]; + ControlVector.vector4_u32[3] = ControlElement[VectorIndex3]; + + return ControlVector; + +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSelect +( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR Control +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Result = { { { + (V1.vector4_u32[0] & ~Control.vector4_u32[0]) | (V2.vector4_u32[0] & Control.vector4_u32[0]), + (V1.vector4_u32[1] & ~Control.vector4_u32[1]) | (V2.vector4_u32[1] & Control.vector4_u32[1]), + (V1.vector4_u32[2] & ~Control.vector4_u32[2]) | (V2.vector4_u32[2] & Control.vector4_u32[2]), + (V1.vector4_u32[3] & ~Control.vector4_u32[3]) | (V2.vector4_u32[3] & Control.vector4_u32[3]), + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vbslq_f32(vreinterpretq_u32_f32(Control), V2, V1); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp1 = _mm_andnot_ps(Control, V1); + XMVECTOR vTemp2 = _mm_and_ps(V2, Control); + return _mm_or_ps(vTemp1, vTemp2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorMergeXY +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Result = { { { + V1.vector4_u32[0], + V2.vector4_u32[0], + V1.vector4_u32[1], + V2.vector4_u32[1], + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vzipq_f32(V1, V2).val[0]; +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_unpacklo_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorMergeZW +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Result = { { { + V1.vector4_u32[2], + V2.vector4_u32[2], + V1.vector4_u32[3], + V2.vector4_u32[3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vzipq_f32(V1, V2).val[1]; +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_unpackhi_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorShiftLeft(FXMVECTOR V1, FXMVECTOR V2, uint32_t Elements) noexcept +{ + assert(Elements < 4); + _Analysis_assume_(Elements < 4); + return XMVectorPermute(V1, V2, Elements, ((Elements)+1), ((Elements)+2), ((Elements)+3)); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorRotateLeft(FXMVECTOR V, uint32_t Elements) noexcept +{ + assert(Elements < 4); + _Analysis_assume_(Elements < 4); + return XMVectorSwizzle(V, Elements & 3, (Elements + 1) & 3, (Elements + 2) & 3, (Elements + 3) & 3); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorRotateRight(FXMVECTOR V, uint32_t Elements) noexcept +{ + assert(Elements < 4); + _Analysis_assume_(Elements < 4); + return XMVectorSwizzle(V, (4 - (Elements)) & 3, (5 - (Elements)) & 3, (6 - (Elements)) & 3, (7 - (Elements)) & 3); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorInsert( + FXMVECTOR VD, FXMVECTOR VS, + uint32_t VSLeftRotateElements, + uint32_t Select0, uint32_t Select1, uint32_t Select2, uint32_t Select3) noexcept +{ + XMVECTOR Control = XMVectorSelectControl(Select0 & 1, Select1 & 1, Select2 & 1, Select3 & 1); + return XMVectorSelect(VD, XMVectorRotateLeft(VS, VSLeftRotateElements), Control); +} + +//------------------------------------------------------------------------------ +// Comparison operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + (V1.vector4_f32[0] == V2.vector4_f32[0]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[1] == V2.vector4_f32[1]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[2] == V2.vector4_f32[2]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[3] == V2.vector4_f32[3]) ? 0xFFFFFFFF : 0, + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vceqq_f32(V1, V2)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_cmpeq_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorEqualR +( + uint32_t* pCR, + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + assert(pCR != nullptr); +#if defined(_XM_NO_INTRINSICS_) + uint32_t ux = (V1.vector4_f32[0] == V2.vector4_f32[0]) ? 0xFFFFFFFFU : 0; + uint32_t uy = (V1.vector4_f32[1] == V2.vector4_f32[1]) ? 0xFFFFFFFFU : 0; + uint32_t uz = (V1.vector4_f32[2] == V2.vector4_f32[2]) ? 0xFFFFFFFFU : 0; + uint32_t uw = (V1.vector4_f32[3] == V2.vector4_f32[3]) ? 0xFFFFFFFFU : 0; + uint32_t CR = 0; + if (ux & uy & uz & uw) + { + // All elements are greater + CR = XM_CRMASK_CR6TRUE; + } + else if (!(ux | uy | uz | uw)) + { + // All elements are not greater + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + + XMVECTORU32 Control = { { { ux, uy, uz, uw } } }; + return Control; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vreinterpret_u8_u32(vget_low_u32(vResult)), vreinterpret_u8_u32(vget_high_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + uint32_t CR = 0; + if (r == 0xFFFFFFFFU) + { + // All elements are equal + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + // All elements are not equal + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + return vreinterpretq_f32_u32(vResult); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpeq_ps(V1, V2); + uint32_t CR = 0; + int iTest = _mm_movemask_ps(vTemp); + if (iTest == 0xf) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + // All elements are not greater + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ +// Treat the components of the vectors as unsigned integers and +// compare individual bits between the two. This is useful for +// comparing control vectors and result vectors returned from +// other comparison operations. + +inline XMVECTOR XM_CALLCONV XMVectorEqualInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + (V1.vector4_u32[0] == V2.vector4_u32[0]) ? 0xFFFFFFFF : 0, + (V1.vector4_u32[1] == V2.vector4_u32[1]) ? 0xFFFFFFFF : 0, + (V1.vector4_u32[2] == V2.vector4_u32[2]) ? 0xFFFFFFFF : 0, + (V1.vector4_u32[3] == V2.vector4_u32[3]) ? 0xFFFFFFFF : 0, + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vceqq_s32(vreinterpretq_s32_f32(V1), vreinterpretq_s32_f32(V2))); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorEqualIntR +( + uint32_t* pCR, + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + assert(pCR != nullptr); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Control = XMVectorEqualInt(V1, V2); + + *pCR = 0; + if (XMVector4EqualInt(Control, XMVectorTrueInt())) + { + // All elements are equal + *pCR |= XM_CRMASK_CR6TRUE; + } + else if (XMVector4EqualInt(Control, XMVectorFalseInt())) + { + // All elements are not equal + *pCR |= XM_CRMASK_CR6FALSE; + } + return Control; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2)); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + uint32_t CR = 0; + if (r == 0xFFFFFFFFU) + { + // All elements are equal + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + // All elements are not equal + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + return vreinterpretq_f32_u32(vResult); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + int iTemp = _mm_movemask_ps(_mm_castsi128_ps(V)); + uint32_t CR = 0; + if (iTemp == 0x0F) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTemp) + { + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorNearEqual +( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR Epsilon +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + float fDeltax = V1.vector4_f32[0] - V2.vector4_f32[0]; + float fDeltay = V1.vector4_f32[1] - V2.vector4_f32[1]; + float fDeltaz = V1.vector4_f32[2] - V2.vector4_f32[2]; + float fDeltaw = V1.vector4_f32[3] - V2.vector4_f32[3]; + + fDeltax = fabsf(fDeltax); + fDeltay = fabsf(fDeltay); + fDeltaz = fabsf(fDeltaz); + fDeltaw = fabsf(fDeltaw); + + XMVECTORU32 Control = { { { + (fDeltax <= Epsilon.vector4_f32[0]) ? 0xFFFFFFFFU : 0, + (fDeltay <= Epsilon.vector4_f32[1]) ? 0xFFFFFFFFU : 0, + (fDeltaz <= Epsilon.vector4_f32[2]) ? 0xFFFFFFFFU : 0, + (fDeltaw <= Epsilon.vector4_f32[3]) ? 0xFFFFFFFFU : 0, + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vDelta = vsubq_f32(V1, V2); +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + return vacleq_f32(vDelta, Epsilon); +#else + return vreinterpretq_f32_u32(vcleq_f32(vabsq_f32(vDelta), Epsilon)); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + // Get the difference + XMVECTOR vDelta = _mm_sub_ps(V1, V2); + // Get the absolute value of the difference + XMVECTOR vTemp = _mm_setzero_ps(); + vTemp = _mm_sub_ps(vTemp, vDelta); + vTemp = _mm_max_ps(vTemp, vDelta); + vTemp = _mm_cmple_ps(vTemp, Epsilon); + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorNotEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + (V1.vector4_f32[0] != V2.vector4_f32[0]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[1] != V2.vector4_f32[1]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[2] != V2.vector4_f32[2]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[3] != V2.vector4_f32[3]) ? 0xFFFFFFFF : 0, + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(V1, V2))); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_cmpneq_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorNotEqualInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + (V1.vector4_u32[0] != V2.vector4_u32[0]) ? 0xFFFFFFFFU : 0, + (V1.vector4_u32[1] != V2.vector4_u32[1]) ? 0xFFFFFFFFU : 0, + (V1.vector4_u32[2] != V2.vector4_u32[2]) ? 0xFFFFFFFFU : 0, + (V1.vector4_u32[3] != V2.vector4_u32[3]) ? 0xFFFFFFFFU : 0 + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vmvnq_u32( + vceqq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2)))); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return _mm_xor_ps(_mm_castsi128_ps(V), g_XMNegOneMask); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorGreater +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + (V1.vector4_f32[0] > V2.vector4_f32[0]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[1] > V2.vector4_f32[1]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[2] > V2.vector4_f32[2]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[3] > V2.vector4_f32[3]) ? 0xFFFFFFFF : 0 + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vcgtq_f32(V1, V2)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_cmpgt_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorGreaterR +( + uint32_t* pCR, + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + assert(pCR != nullptr); +#if defined(_XM_NO_INTRINSICS_) + + uint32_t ux = (V1.vector4_f32[0] > V2.vector4_f32[0]) ? 0xFFFFFFFFU : 0; + uint32_t uy = (V1.vector4_f32[1] > V2.vector4_f32[1]) ? 0xFFFFFFFFU : 0; + uint32_t uz = (V1.vector4_f32[2] > V2.vector4_f32[2]) ? 0xFFFFFFFFU : 0; + uint32_t uw = (V1.vector4_f32[3] > V2.vector4_f32[3]) ? 0xFFFFFFFFU : 0; + uint32_t CR = 0; + if (ux & uy & uz & uw) + { + // All elements are greater + CR = XM_CRMASK_CR6TRUE; + } + else if (!(ux | uy | uz | uw)) + { + // All elements are not greater + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + + XMVECTORU32 Control = { { { ux, uy, uz, uw } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgtq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + uint32_t CR = 0; + if (r == 0xFFFFFFFFU) + { + // All elements are greater + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + // All elements are not greater + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + return vreinterpretq_f32_u32(vResult); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpgt_ps(V1, V2); + uint32_t CR = 0; + int iTest = _mm_movemask_ps(vTemp); + if (iTest == 0xf) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + // All elements are not greater + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorGreaterOrEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + (V1.vector4_f32[0] >= V2.vector4_f32[0]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[1] >= V2.vector4_f32[1]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[2] >= V2.vector4_f32[2]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[3] >= V2.vector4_f32[3]) ? 0xFFFFFFFF : 0 + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vcgeq_f32(V1, V2)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_cmpge_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorGreaterOrEqualR +( + uint32_t* pCR, + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + assert(pCR != nullptr); +#if defined(_XM_NO_INTRINSICS_) + + uint32_t ux = (V1.vector4_f32[0] >= V2.vector4_f32[0]) ? 0xFFFFFFFFU : 0; + uint32_t uy = (V1.vector4_f32[1] >= V2.vector4_f32[1]) ? 0xFFFFFFFFU : 0; + uint32_t uz = (V1.vector4_f32[2] >= V2.vector4_f32[2]) ? 0xFFFFFFFFU : 0; + uint32_t uw = (V1.vector4_f32[3] >= V2.vector4_f32[3]) ? 0xFFFFFFFFU : 0; + uint32_t CR = 0; + if (ux & uy & uz & uw) + { + // All elements are greater + CR = XM_CRMASK_CR6TRUE; + } + else if (!(ux | uy | uz | uw)) + { + // All elements are not greater + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + + XMVECTORU32 Control = { { { ux, uy, uz, uw } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgeq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + uint32_t CR = 0; + if (r == 0xFFFFFFFFU) + { + // All elements are greater or equal + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + // All elements are not greater or equal + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + return vreinterpretq_f32_u32(vResult); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpge_ps(V1, V2); + uint32_t CR = 0; + int iTest = _mm_movemask_ps(vTemp); + if (iTest == 0xf) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + // All elements are not greater + CR = XM_CRMASK_CR6FALSE; + } + *pCR = CR; + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorLess +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + (V1.vector4_f32[0] < V2.vector4_f32[0]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[1] < V2.vector4_f32[1]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[2] < V2.vector4_f32[2]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[3] < V2.vector4_f32[3]) ? 0xFFFFFFFF : 0 + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vcltq_f32(V1, V2)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_cmplt_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorLessOrEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + (V1.vector4_f32[0] <= V2.vector4_f32[0]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[1] <= V2.vector4_f32[1]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[2] <= V2.vector4_f32[2]) ? 0xFFFFFFFF : 0, + (V1.vector4_f32[3] <= V2.vector4_f32[3]) ? 0xFFFFFFFF : 0 + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vcleq_f32(V1, V2)); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_cmple_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorInBounds +( + FXMVECTOR V, + FXMVECTOR Bounds +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + (V.vector4_f32[0] <= Bounds.vector4_f32[0] && V.vector4_f32[0] >= -Bounds.vector4_f32[0]) ? 0xFFFFFFFF : 0, + (V.vector4_f32[1] <= Bounds.vector4_f32[1] && V.vector4_f32[1] >= -Bounds.vector4_f32[1]) ? 0xFFFFFFFF : 0, + (V.vector4_f32[2] <= Bounds.vector4_f32[2] && V.vector4_f32[2] >= -Bounds.vector4_f32[2]) ? 0xFFFFFFFF : 0, + (V.vector4_f32[3] <= Bounds.vector4_f32[3] && V.vector4_f32[3] >= -Bounds.vector4_f32[3]) ? 0xFFFFFFFF : 0 + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Test if less than or equal + uint32x4_t vTemp1 = vcleq_f32(V, Bounds); + // Negate the bounds + uint32x4_t vTemp2 = vreinterpretq_u32_f32(vnegq_f32(Bounds)); + // Test if greater or equal (Reversed) + vTemp2 = vcleq_f32(vreinterpretq_f32_u32(vTemp2), V); + // Blend answers + vTemp1 = vandq_u32(vTemp1, vTemp2); + return vreinterpretq_f32_u32(vTemp1); +#elif defined(_XM_SSE_INTRINSICS_) + // Test if less than or equal + XMVECTOR vTemp1 = _mm_cmple_ps(V, Bounds); + // Negate the bounds + XMVECTOR vTemp2 = _mm_mul_ps(Bounds, g_XMNegativeOne); + // Test if greater or equal (Reversed) + vTemp2 = _mm_cmple_ps(vTemp2, V); + // Blend answers + vTemp1 = _mm_and_ps(vTemp1, vTemp2); + return vTemp1; +#endif +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMVectorInBoundsR +( + uint32_t* pCR, + FXMVECTOR V, + FXMVECTOR Bounds +) noexcept +{ + assert(pCR != nullptr); +#if defined(_XM_NO_INTRINSICS_) + + uint32_t ux = (V.vector4_f32[0] <= Bounds.vector4_f32[0] && V.vector4_f32[0] >= -Bounds.vector4_f32[0]) ? 0xFFFFFFFFU : 0; + uint32_t uy = (V.vector4_f32[1] <= Bounds.vector4_f32[1] && V.vector4_f32[1] >= -Bounds.vector4_f32[1]) ? 0xFFFFFFFFU : 0; + uint32_t uz = (V.vector4_f32[2] <= Bounds.vector4_f32[2] && V.vector4_f32[2] >= -Bounds.vector4_f32[2]) ? 0xFFFFFFFFU : 0; + uint32_t uw = (V.vector4_f32[3] <= Bounds.vector4_f32[3] && V.vector4_f32[3] >= -Bounds.vector4_f32[3]) ? 0xFFFFFFFFU : 0; + + uint32_t CR = 0; + if (ux & uy & uz & uw) + { + // All elements are in bounds + CR = XM_CRMASK_CR6BOUNDS; + } + *pCR = CR; + + XMVECTORU32 Control = { { { ux, uy, uz, uw } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Test if less than or equal + uint32x4_t vTemp1 = vcleq_f32(V, Bounds); + // Negate the bounds + uint32x4_t vTemp2 = vreinterpretq_u32_f32(vnegq_f32(Bounds)); + // Test if greater or equal (Reversed) + vTemp2 = vcleq_f32(vreinterpretq_f32_u32(vTemp2), V); + // Blend answers + vTemp1 = vandq_u32(vTemp1, vTemp2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vTemp1)), vget_high_u8(vreinterpretq_u8_u32(vTemp1))); + uint16x4x2_t vTemp3 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp3.val[1]), 1); + uint32_t CR = 0; + if (r == 0xFFFFFFFFU) + { + // All elements are in bounds + CR = XM_CRMASK_CR6BOUNDS; + } + *pCR = CR; + return vreinterpretq_f32_u32(vTemp1); +#elif defined(_XM_SSE_INTRINSICS_) + // Test if less than or equal + XMVECTOR vTemp1 = _mm_cmple_ps(V, Bounds); + // Negate the bounds + XMVECTOR vTemp2 = _mm_mul_ps(Bounds, g_XMNegativeOne); + // Test if greater or equal (Reversed) + vTemp2 = _mm_cmple_ps(vTemp2, V); + // Blend answers + vTemp1 = _mm_and_ps(vTemp1, vTemp2); + + uint32_t CR = 0; + if (_mm_movemask_ps(vTemp1) == 0xf) + { + // All elements are in bounds + CR = XM_CRMASK_CR6BOUNDS; + } + *pCR = CR; + return vTemp1; +#endif +} + +//------------------------------------------------------------------------------ + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(push) +#pragma float_control(precise, on) +#endif + +inline XMVECTOR XM_CALLCONV XMVectorIsNaN(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + XMISNAN(V.vector4_f32[0]) ? 0xFFFFFFFFU : 0, + XMISNAN(V.vector4_f32[1]) ? 0xFFFFFFFFU : 0, + XMISNAN(V.vector4_f32[2]) ? 0xFFFFFFFFU : 0, + XMISNAN(V.vector4_f32[3]) ? 0xFFFFFFFFU : 0 + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(__clang__) && defined(__FINITE_MATH_ONLY__) + XMVECTORU32 vResult = { { { + isnan(vgetq_lane_f32(V, 0)) ? 0xFFFFFFFFU : 0, + isnan(vgetq_lane_f32(V, 1)) ? 0xFFFFFFFFU : 0, + isnan(vgetq_lane_f32(V, 2)) ? 0xFFFFFFFFU : 0, + isnan(vgetq_lane_f32(V, 3)) ? 0xFFFFFFFFU : 0 } } }; + return vResult.v; +#else +// Test against itself. NaN is always not equal + uint32x4_t vTempNan = vceqq_f32(V, V); + // Flip results + return vreinterpretq_f32_u32(vmvnq_u32(vTempNan)); +#endif +#elif defined(_XM_SSE_INTRINSICS_) +#if defined(__clang__) && defined(__FINITE_MATH_ONLY__) + XM_ALIGNED_DATA(16) float tmp[4]; + _mm_store_ps(tmp, V); + XMVECTORU32 vResult = { { { + isnan(tmp[0]) ? 0xFFFFFFFFU : 0, + isnan(tmp[1]) ? 0xFFFFFFFFU : 0, + isnan(tmp[2]) ? 0xFFFFFFFFU : 0, + isnan(tmp[3]) ? 0xFFFFFFFFU : 0 } } }; + return vResult.v; +#else +// Test against itself. NaN is always not equal + return _mm_cmpneq_ps(V, V); +#endif +#endif +} + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(pop) +#endif + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorIsInfinite(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Control = { { { + XMISINF(V.vector4_f32[0]) ? 0xFFFFFFFFU : 0, + XMISINF(V.vector4_f32[1]) ? 0xFFFFFFFFU : 0, + XMISINF(V.vector4_f32[2]) ? 0xFFFFFFFFU : 0, + XMISINF(V.vector4_f32[3]) ? 0xFFFFFFFFU : 0 + } } }; + return Control.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Mask off the sign bit + uint32x4_t vTemp = vandq_u32(vreinterpretq_u32_f32(V), g_XMAbsMask); + // Compare to infinity + vTemp = vceqq_f32(vreinterpretq_f32_u32(vTemp), g_XMInfinity); + // If any are infinity, the signs are true. + return vreinterpretq_f32_u32(vTemp); +#elif defined(_XM_SSE_INTRINSICS_) + // Mask off the sign bit + __m128 vTemp = _mm_and_ps(V, g_XMAbsMask); + // Compare to infinity + vTemp = _mm_cmpeq_ps(vTemp, g_XMInfinity); + // If any are infinity, the signs are true. + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ +// Rounding and clamping operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorMin +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + (V1.vector4_f32[0] < V2.vector4_f32[0]) ? V1.vector4_f32[0] : V2.vector4_f32[0], + (V1.vector4_f32[1] < V2.vector4_f32[1]) ? V1.vector4_f32[1] : V2.vector4_f32[1], + (V1.vector4_f32[2] < V2.vector4_f32[2]) ? V1.vector4_f32[2] : V2.vector4_f32[2], + (V1.vector4_f32[3] < V2.vector4_f32[3]) ? V1.vector4_f32[3] : V2.vector4_f32[3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vminq_f32(V1, V2); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_min_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorMax +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + (V1.vector4_f32[0] > V2.vector4_f32[0]) ? V1.vector4_f32[0] : V2.vector4_f32[0], + (V1.vector4_f32[1] > V2.vector4_f32[1]) ? V1.vector4_f32[1] : V2.vector4_f32[1], + (V1.vector4_f32[2] > V2.vector4_f32[2]) ? V1.vector4_f32[2] : V2.vector4_f32[2], + (V1.vector4_f32[3] > V2.vector4_f32[3]) ? V1.vector4_f32[3] : V2.vector4_f32[3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vmaxq_f32(V1, V2); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_max_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +namespace MathInternal +{ + // Round to nearest (even) a.k.a. banker's rounding + inline float round_to_nearest(float x) noexcept + { + float i = floorf(x); + x -= i; + if (x < 0.5f) + return i; + if (x > 0.5f) + return i + 1.f; + + float int_part; + (void)modff(i / 2.f, &int_part); + if ((2.f * int_part) == i) + { + return i; + } + + return i + 1.f; + } +} + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(push) +#pragma float_control(precise, on) +#endif + +inline XMVECTOR XM_CALLCONV XMVectorRound(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + MathInternal::round_to_nearest(V.vector4_f32[0]), + MathInternal::round_to_nearest(V.vector4_f32[1]), + MathInternal::round_to_nearest(V.vector4_f32[2]), + MathInternal::round_to_nearest(V.vector4_f32[3]) + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + return vrndnq_f32(V); +#else + uint32x4_t sign = vandq_u32(vreinterpretq_u32_f32(V), g_XMNegativeZero); + float32x4_t sMagic = vreinterpretq_f32_u32(vorrq_u32(g_XMNoFraction, sign)); + float32x4_t R1 = vaddq_f32(V, sMagic); + R1 = vsubq_f32(R1, sMagic); + float32x4_t R2 = vabsq_f32(V); + uint32x4_t mask = vcleq_f32(R2, g_XMNoFraction); + return vbslq_f32(mask, R1, V); +#endif +#elif defined(_XM_SSE4_INTRINSICS_) + return _mm_round_ps(V, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); +#elif defined(_XM_SSE_INTRINSICS_) + __m128 sign = _mm_and_ps(V, g_XMNegativeZero); + __m128 sMagic = _mm_or_ps(g_XMNoFraction, sign); + __m128 R1 = _mm_add_ps(V, sMagic); + R1 = _mm_sub_ps(R1, sMagic); + __m128 R2 = _mm_and_ps(V, g_XMAbsMask); + __m128 mask = _mm_cmple_ps(R2, g_XMNoFraction); + R2 = _mm_andnot_ps(mask, V); + R1 = _mm_and_ps(R1, mask); + XMVECTOR vResult = _mm_xor_ps(R1, R2); + return vResult; +#endif +} + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(pop) +#endif + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorTruncate(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR Result; + uint32_t i; + + // Avoid C4701 + Result.vector4_f32[0] = 0.0f; + + for (i = 0; i < 4; i++) + { + if (XMISNAN(V.vector4_f32[i])) + { + Result.vector4_u32[i] = 0x7FC00000; + } + else if (fabsf(V.vector4_f32[i]) < 8388608.0f) + { + Result.vector4_f32[i] = static_cast(static_cast(V.vector4_f32[i])); + } + else + { + Result.vector4_f32[i] = V.vector4_f32[i]; + } + } + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + return vrndq_f32(V); +#else + float32x4_t vTest = vabsq_f32(V); + vTest = vreinterpretq_f32_u32(vcltq_f32(vTest, g_XMNoFraction)); + + int32x4_t vInt = vcvtq_s32_f32(V); + float32x4_t vResult = vcvtq_f32_s32(vInt); + + // All numbers less than 8388608 will use the round to int + // All others, use the ORIGINAL value + return vbslq_f32(vreinterpretq_u32_f32(vTest), vResult, V); +#endif +#elif defined(_XM_SSE4_INTRINSICS_) + return _mm_round_ps(V, _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC); +#elif defined(_XM_SSE_INTRINSICS_) + // To handle NAN, INF and numbers greater than 8388608, use masking + // Get the abs value + __m128i vTest = _mm_and_si128(_mm_castps_si128(V), g_XMAbsMask); + // Test for greater than 8388608 (All floats with NO fractionals, NAN and INF + vTest = _mm_cmplt_epi32(vTest, g_XMNoFraction); + // Convert to int and back to float for rounding with truncation + __m128i vInt = _mm_cvttps_epi32(V); + // Convert back to floats + XMVECTOR vResult = _mm_cvtepi32_ps(vInt); + // All numbers less than 8388608 will use the round to int + vResult = _mm_and_ps(vResult, _mm_castsi128_ps(vTest)); + // All others, use the ORIGINAL value + vTest = _mm_andnot_si128(vTest, _mm_castps_si128(V)); + vResult = _mm_or_ps(vResult, _mm_castsi128_ps(vTest)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorFloor(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + floorf(V.vector4_f32[0]), + floorf(V.vector4_f32[1]), + floorf(V.vector4_f32[2]), + floorf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + return vrndmq_f32(V); +#else + float32x4_t vTest = vabsq_f32(V); + vTest = vreinterpretq_f32_u32(vcltq_f32(vTest, g_XMNoFraction)); + // Truncate + int32x4_t vInt = vcvtq_s32_f32(V); + float32x4_t vResult = vcvtq_f32_s32(vInt); + uint32x4_t vLargerMask = vcgtq_f32(vResult, V); + // 0 -> 0, 0xffffffff -> -1.0f + float32x4_t vLarger = vcvtq_f32_s32(vreinterpretq_s32_u32(vLargerMask)); + vResult = vaddq_f32(vResult, vLarger); + // All numbers less than 8388608 will use the round to int + // All others, use the ORIGINAL value + return vbslq_f32(vreinterpretq_u32_f32(vTest), vResult, V); +#endif +#elif defined(_XM_SSE4_INTRINSICS_) + return _mm_floor_ps(V); +#elif defined(_XM_SSE_INTRINSICS_) + // To handle NAN, INF and numbers greater than 8388608, use masking + __m128i vTest = _mm_and_si128(_mm_castps_si128(V), g_XMAbsMask); + vTest = _mm_cmplt_epi32(vTest, g_XMNoFraction); + // Truncate + __m128i vInt = _mm_cvttps_epi32(V); + XMVECTOR vResult = _mm_cvtepi32_ps(vInt); + __m128 vLarger = _mm_cmpgt_ps(vResult, V); + // 0 -> 0, 0xffffffff -> -1.0f + vLarger = _mm_cvtepi32_ps(_mm_castps_si128(vLarger)); + vResult = _mm_add_ps(vResult, vLarger); + // All numbers less than 8388608 will use the round to int + vResult = _mm_and_ps(vResult, _mm_castsi128_ps(vTest)); + // All others, use the ORIGINAL value + vTest = _mm_andnot_si128(vTest, _mm_castps_si128(V)); + vResult = _mm_or_ps(vResult, _mm_castsi128_ps(vTest)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorCeiling(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + ceilf(V.vector4_f32[0]), + ceilf(V.vector4_f32[1]), + ceilf(V.vector4_f32[2]), + ceilf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + return vrndpq_f32(V); +#else + float32x4_t vTest = vabsq_f32(V); + vTest = vreinterpretq_f32_u32(vcltq_f32(vTest, g_XMNoFraction)); + // Truncate + int32x4_t vInt = vcvtq_s32_f32(V); + float32x4_t vResult = vcvtq_f32_s32(vInt); + uint32x4_t vSmallerMask = vcltq_f32(vResult, V); + // 0 -> 0, 0xffffffff -> -1.0f + float32x4_t vSmaller = vcvtq_f32_s32(vreinterpretq_s32_u32(vSmallerMask)); + vResult = vsubq_f32(vResult, vSmaller); + // All numbers less than 8388608 will use the round to int + // All others, use the ORIGINAL value + return vbslq_f32(vreinterpretq_u32_f32(vTest), vResult, V); +#endif +#elif defined(_XM_SSE4_INTRINSICS_) + return _mm_ceil_ps(V); +#elif defined(_XM_SSE_INTRINSICS_) + // To handle NAN, INF and numbers greater than 8388608, use masking + __m128i vTest = _mm_and_si128(_mm_castps_si128(V), g_XMAbsMask); + vTest = _mm_cmplt_epi32(vTest, g_XMNoFraction); + // Truncate + __m128i vInt = _mm_cvttps_epi32(V); + XMVECTOR vResult = _mm_cvtepi32_ps(vInt); + __m128 vSmaller = _mm_cmplt_ps(vResult, V); + // 0 -> 0, 0xffffffff -> -1.0f + vSmaller = _mm_cvtepi32_ps(_mm_castps_si128(vSmaller)); + vResult = _mm_sub_ps(vResult, vSmaller); + // All numbers less than 8388608 will use the round to int + vResult = _mm_and_ps(vResult, _mm_castsi128_ps(vTest)); + // All others, use the ORIGINAL value + vTest = _mm_andnot_si128(vTest, _mm_castps_si128(V)); + vResult = _mm_or_ps(vResult, _mm_castsi128_ps(vTest)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorClamp +( + FXMVECTOR V, + FXMVECTOR Min, + FXMVECTOR Max +) noexcept +{ + assert(XMVector4LessOrEqual(Min, Max)); + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + Result = XMVectorMax(Min, V); + Result = XMVectorMin(Max, Result); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vResult = vmaxq_f32(Min, V); + vResult = vminq_f32(Max, vResult); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult; + vResult = _mm_max_ps(Min, V); + vResult = _mm_min_ps(Max, vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSaturate(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + const XMVECTOR Zero = XMVectorZero(); + + return XMVectorClamp(V, Zero, g_XMOne.v); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Set <0 to 0 + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(0)); + // Set>1 to 1 + return vminq_f32(vResult, vdupq_n_f32(1.0f)); +#elif defined(_XM_SSE_INTRINSICS_) + // Set <0 to 0 + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + // Set>1 to 1 + return _mm_min_ps(vResult, g_XMOne); +#endif +} + +//------------------------------------------------------------------------------ +// Bitwise logical operations +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorAndInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Result = { { { + V1.vector4_u32[0] & V2.vector4_u32[0], + V1.vector4_u32[1] & V2.vector4_u32[1], + V1.vector4_u32[2] & V2.vector4_u32[2], + V1.vector4_u32[3] & V2.vector4_u32[3] + } } }; + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2))); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_and_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorAndCInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Result = { { { + V1.vector4_u32[0] & ~V2.vector4_u32[0], + V1.vector4_u32[1] & ~V2.vector4_u32[1], + V1.vector4_u32[2] & ~V2.vector4_u32[2], + V1.vector4_u32[3] & ~V2.vector4_u32[3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2))); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_andnot_si128(_mm_castps_si128(V2), _mm_castps_si128(V1)); + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorOrInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Result = { { { + V1.vector4_u32[0] | V2.vector4_u32[0], + V1.vector4_u32[1] | V2.vector4_u32[1], + V1.vector4_u32[2] | V2.vector4_u32[2], + V1.vector4_u32[3] | V2.vector4_u32[3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2))); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_or_si128(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorNorInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Result = { { { + ~(V1.vector4_u32[0] | V2.vector4_u32[0]), + ~(V1.vector4_u32[1] | V2.vector4_u32[1]), + ~(V1.vector4_u32[2] | V2.vector4_u32[2]), + ~(V1.vector4_u32[3] | V2.vector4_u32[3]) + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t Result = vorrq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2)); + return vreinterpretq_f32_u32(vbicq_u32(g_XMNegOneMask, Result)); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i Result; + Result = _mm_or_si128(_mm_castps_si128(V1), _mm_castps_si128(V2)); + Result = _mm_andnot_si128(Result, g_XMNegOneMask); + return _mm_castsi128_ps(Result); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorXorInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORU32 Result = { { { + V1.vector4_u32[0] ^ V2.vector4_u32[0], + V1.vector4_u32[1] ^ V2.vector4_u32[1], + V1.vector4_u32[2] ^ V2.vector4_u32[2], + V1.vector4_u32[3] ^ V2.vector4_u32[3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2))); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i V = _mm_xor_si128(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return _mm_castsi128_ps(V); +#endif +} + +//------------------------------------------------------------------------------ +// Computation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorNegate(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + -V.vector4_f32[0], + -V.vector4_f32[1], + -V.vector4_f32[2], + -V.vector4_f32[3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vnegq_f32(V); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR Z; + + Z = _mm_setzero_ps(); + + return _mm_sub_ps(Z, V); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorAdd +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + V1.vector4_f32[0] + V2.vector4_f32[0], + V1.vector4_f32[1] + V2.vector4_f32[1], + V1.vector4_f32[2] + V2.vector4_f32[2], + V1.vector4_f32[3] + V2.vector4_f32[3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vaddq_f32(V1, V2); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_add_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSum(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result; + Result.f[0] = + Result.f[1] = + Result.f[2] = + Result.f[3] = V.vector4_f32[0] + V.vector4_f32[1] + V.vector4_f32[2] + V.vector4_f32[3]; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + float32x4_t vTemp = vpaddq_f32(V, V); + return vpaddq_f32(vTemp, vTemp); +#else + float32x2_t v1 = vget_low_f32(V); + float32x2_t v2 = vget_high_f32(V); + v1 = vadd_f32(v1, v2); + v1 = vpadd_f32(v1, v1); + return vcombine_f32(v1, v1); +#endif +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vTemp = _mm_hadd_ps(V, V); + return _mm_hadd_ps(vTemp, vTemp); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 3, 0, 1)); + XMVECTOR vTemp2 = _mm_add_ps(V, vTemp); + vTemp = XM_PERMUTE_PS(vTemp2, _MM_SHUFFLE(1, 0, 3, 2)); + return _mm_add_ps(vTemp, vTemp2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorAddAngles +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + const XMVECTOR Zero = XMVectorZero(); + + // Add the given angles together. If the range of V1 is such + // that -Pi <= V1 < Pi and the range of V2 is such that + // -2Pi <= V2 <= 2Pi, then the range of the resulting angle + // will be -Pi <= Result < Pi. + XMVECTOR Result = XMVectorAdd(V1, V2); + + XMVECTOR Mask = XMVectorLess(Result, g_XMNegativePi.v); + XMVECTOR Offset = XMVectorSelect(Zero, g_XMTwoPi.v, Mask); + + Mask = XMVectorGreaterOrEqual(Result, g_XMPi.v); + Offset = XMVectorSelect(Offset, g_XMNegativeTwoPi.v, Mask); + + Result = XMVectorAdd(Result, Offset); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Adjust the angles + float32x4_t vResult = vaddq_f32(V1, V2); + // Less than Pi? + uint32x4_t vOffset = vcltq_f32(vResult, g_XMNegativePi); + vOffset = vandq_u32(vOffset, g_XMTwoPi); + // Add 2Pi to all entries less than -Pi + vResult = vaddq_f32(vResult, vreinterpretq_f32_u32(vOffset)); + // Greater than or equal to Pi? + vOffset = vcgeq_f32(vResult, g_XMPi); + vOffset = vandq_u32(vOffset, g_XMTwoPi); + // Sub 2Pi to all entries greater than Pi + vResult = vsubq_f32(vResult, vreinterpretq_f32_u32(vOffset)); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Adjust the angles + XMVECTOR vResult = _mm_add_ps(V1, V2); + // Less than Pi? + XMVECTOR vOffset = _mm_cmplt_ps(vResult, g_XMNegativePi); + vOffset = _mm_and_ps(vOffset, g_XMTwoPi); + // Add 2Pi to all entries less than -Pi + vResult = _mm_add_ps(vResult, vOffset); + // Greater than or equal to Pi? + vOffset = _mm_cmpge_ps(vResult, g_XMPi); + vOffset = _mm_and_ps(vOffset, g_XMTwoPi); + // Sub 2Pi to all entries greater than Pi + vResult = _mm_sub_ps(vResult, vOffset); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSubtract +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + V1.vector4_f32[0] - V2.vector4_f32[0], + V1.vector4_f32[1] - V2.vector4_f32[1], + V1.vector4_f32[2] - V2.vector4_f32[2], + V1.vector4_f32[3] - V2.vector4_f32[3] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vsubq_f32(V1, V2); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_sub_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSubtractAngles +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + const XMVECTOR Zero = XMVectorZero(); + + // Subtract the given angles. If the range of V1 is such + // that -Pi <= V1 < Pi and the range of V2 is such that + // -2Pi <= V2 <= 2Pi, then the range of the resulting angle + // will be -Pi <= Result < Pi. + XMVECTOR Result = XMVectorSubtract(V1, V2); + + XMVECTOR Mask = XMVectorLess(Result, g_XMNegativePi.v); + XMVECTOR Offset = XMVectorSelect(Zero, g_XMTwoPi.v, Mask); + + Mask = XMVectorGreaterOrEqual(Result, g_XMPi.v); + Offset = XMVectorSelect(Offset, g_XMNegativeTwoPi.v, Mask); + + Result = XMVectorAdd(Result, Offset); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Adjust the angles + XMVECTOR vResult = vsubq_f32(V1, V2); + // Less than Pi? + uint32x4_t vOffset = vcltq_f32(vResult, g_XMNegativePi); + vOffset = vandq_u32(vOffset, g_XMTwoPi); + // Add 2Pi to all entries less than -Pi + vResult = vaddq_f32(vResult, vreinterpretq_f32_u32(vOffset)); + // Greater than or equal to Pi? + vOffset = vcgeq_f32(vResult, g_XMPi); + vOffset = vandq_u32(vOffset, g_XMTwoPi); + // Sub 2Pi to all entries greater than Pi + vResult = vsubq_f32(vResult, vreinterpretq_f32_u32(vOffset)); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Adjust the angles + XMVECTOR vResult = _mm_sub_ps(V1, V2); + // Less than Pi? + XMVECTOR vOffset = _mm_cmplt_ps(vResult, g_XMNegativePi); + vOffset = _mm_and_ps(vOffset, g_XMTwoPi); + // Add 2Pi to all entries less than -Pi + vResult = _mm_add_ps(vResult, vOffset); + // Greater than or equal to Pi? + vOffset = _mm_cmpge_ps(vResult, g_XMPi); + vOffset = _mm_and_ps(vOffset, g_XMTwoPi); + // Sub 2Pi to all entries greater than Pi + vResult = _mm_sub_ps(vResult, vOffset); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorMultiply +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + V1.vector4_f32[0] * V2.vector4_f32[0], + V1.vector4_f32[1] * V2.vector4_f32[1], + V1.vector4_f32[2] * V2.vector4_f32[2], + V1.vector4_f32[3] * V2.vector4_f32[3] + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vmulq_f32(V1, V2); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_mul_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorMultiplyAdd +( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR V3 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + V1.vector4_f32[0] * V2.vector4_f32[0] + V3.vector4_f32[0], + V1.vector4_f32[1] * V2.vector4_f32[1] + V3.vector4_f32[1], + V1.vector4_f32[2] * V2.vector4_f32[2] + V3.vector4_f32[2], + V1.vector4_f32[3] * V2.vector4_f32[3] + V3.vector4_f32[3] + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + return vfmaq_f32(V3, V1, V2); +#else + return vmlaq_f32(V3, V1, V2); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + return XM_FMADD_PS(V1, V2, V3); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorDivide +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + V1.vector4_f32[0] / V2.vector4_f32[0], + V1.vector4_f32[1] / V2.vector4_f32[1], + V1.vector4_f32[2] / V2.vector4_f32[2], + V1.vector4_f32[3] / V2.vector4_f32[3] + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + return vdivq_f32(V1, V2); +#else + // 2 iterations of Newton-Raphson refinement of reciprocal + float32x4_t Reciprocal = vrecpeq_f32(V2); + float32x4_t S = vrecpsq_f32(Reciprocal, V2); + Reciprocal = vmulq_f32(S, Reciprocal); + S = vrecpsq_f32(Reciprocal, V2); + Reciprocal = vmulq_f32(S, Reciprocal); + return vmulq_f32(V1, Reciprocal); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_div_ps(V1, V2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorNegativeMultiplySubtract +( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR V3 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + V3.vector4_f32[0] - (V1.vector4_f32[0] * V2.vector4_f32[0]), + V3.vector4_f32[1] - (V1.vector4_f32[1] * V2.vector4_f32[1]), + V3.vector4_f32[2] - (V1.vector4_f32[2] * V2.vector4_f32[2]), + V3.vector4_f32[3] - (V1.vector4_f32[3] * V2.vector4_f32[3]) + } } }; + return Result; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + return vfmsq_f32(V3, V1, V2); +#else + return vmlsq_f32(V3, V1, V2); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + return XM_FNMADD_PS(V1, V2, V3); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorScale +( + FXMVECTOR V, + float ScaleFactor +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + V.vector4_f32[0] * ScaleFactor, + V.vector4_f32[1] * ScaleFactor, + V.vector4_f32[2] * ScaleFactor, + V.vector4_f32[3] * ScaleFactor + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vmulq_n_f32(V, ScaleFactor); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = _mm_set_ps1(ScaleFactor); + return _mm_mul_ps(vResult, V); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorReciprocalEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + 1.f / V.vector4_f32[0], + 1.f / V.vector4_f32[1], + 1.f / V.vector4_f32[2], + 1.f / V.vector4_f32[3] + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vrecpeq_f32(V); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_rcp_ps(V); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorReciprocal(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + 1.f / V.vector4_f32[0], + 1.f / V.vector4_f32[1], + 1.f / V.vector4_f32[2], + 1.f / V.vector4_f32[3] + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + float32x4_t one = vdupq_n_f32(1.0f); + return vdivq_f32(one, V); +#else + // 2 iterations of Newton-Raphson refinement + float32x4_t Reciprocal = vrecpeq_f32(V); + float32x4_t S = vrecpsq_f32(Reciprocal, V); + Reciprocal = vmulq_f32(S, Reciprocal); + S = vrecpsq_f32(Reciprocal, V); + return vmulq_f32(S, Reciprocal); +#endif +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_div_ps(g_XMOne, V); +#endif +} + +//------------------------------------------------------------------------------ +// Return an estimated square root +inline XMVECTOR XM_CALLCONV XMVectorSqrtEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + sqrtf(V.vector4_f32[0]), + sqrtf(V.vector4_f32[1]), + sqrtf(V.vector4_f32[2]), + sqrtf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // 1 iteration of Newton-Raphson refinment of sqrt + float32x4_t S0 = vrsqrteq_f32(V); + float32x4_t P0 = vmulq_f32(V, S0); + float32x4_t R0 = vrsqrtsq_f32(P0, S0); + float32x4_t S1 = vmulq_f32(S0, R0); + + XMVECTOR VEqualsInfinity = XMVectorEqualInt(V, g_XMInfinity.v); + XMVECTOR VEqualsZero = XMVectorEqual(V, vdupq_n_f32(0)); + XMVECTOR Result = vmulq_f32(V, S1); + XMVECTOR Select = XMVectorEqualInt(VEqualsInfinity, VEqualsZero); + return XMVectorSelect(V, Result, Select); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_sqrt_ps(V); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSqrt(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + sqrtf(V.vector4_f32[0]), + sqrtf(V.vector4_f32[1]), + sqrtf(V.vector4_f32[2]), + sqrtf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // 3 iterations of Newton-Raphson refinment of sqrt + float32x4_t S0 = vrsqrteq_f32(V); + float32x4_t P0 = vmulq_f32(V, S0); + float32x4_t R0 = vrsqrtsq_f32(P0, S0); + float32x4_t S1 = vmulq_f32(S0, R0); + float32x4_t P1 = vmulq_f32(V, S1); + float32x4_t R1 = vrsqrtsq_f32(P1, S1); + float32x4_t S2 = vmulq_f32(S1, R1); + float32x4_t P2 = vmulq_f32(V, S2); + float32x4_t R2 = vrsqrtsq_f32(P2, S2); + float32x4_t S3 = vmulq_f32(S2, R2); + + XMVECTOR VEqualsInfinity = XMVectorEqualInt(V, g_XMInfinity.v); + XMVECTOR VEqualsZero = XMVectorEqual(V, vdupq_n_f32(0)); + XMVECTOR Result = vmulq_f32(V, S3); + XMVECTOR Select = XMVectorEqualInt(VEqualsInfinity, VEqualsZero); + return XMVectorSelect(V, Result, Select); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_sqrt_ps(V); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorReciprocalSqrtEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + 1.f / sqrtf(V.vector4_f32[0]), + 1.f / sqrtf(V.vector4_f32[1]), + 1.f / sqrtf(V.vector4_f32[2]), + 1.f / sqrtf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vrsqrteq_f32(V); +#elif defined(_XM_SSE_INTRINSICS_) + return _mm_rsqrt_ps(V); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorReciprocalSqrt(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + 1.f / sqrtf(V.vector4_f32[0]), + 1.f / sqrtf(V.vector4_f32[1]), + 1.f / sqrtf(V.vector4_f32[2]), + 1.f / sqrtf(V.vector4_f32[3]) + } } }; + return Result; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // 2 iterations of Newton-Raphson refinement of reciprocal + float32x4_t S0 = vrsqrteq_f32(V); + + float32x4_t P0 = vmulq_f32(V, S0); + float32x4_t R0 = vrsqrtsq_f32(P0, S0); + + float32x4_t S1 = vmulq_f32(S0, R0); + float32x4_t P1 = vmulq_f32(V, S1); + float32x4_t R1 = vrsqrtsq_f32(P1, S1); + + return vmulq_f32(S1, R1); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = _mm_sqrt_ps(V); + vResult = _mm_div_ps(g_XMOne, vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorExp2(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + exp2f(V.vector4_f32[0]), + exp2f(V.vector4_f32[1]), + exp2f(V.vector4_f32[2]), + exp2f(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x4_t itrunc = vcvtq_s32_f32(V); + float32x4_t ftrunc = vcvtq_f32_s32(itrunc); + float32x4_t y = vsubq_f32(V, ftrunc); + + float32x4_t poly = vmlaq_f32(g_XMExpEst6, g_XMExpEst7, y); + poly = vmlaq_f32(g_XMExpEst5, poly, y); + poly = vmlaq_f32(g_XMExpEst4, poly, y); + poly = vmlaq_f32(g_XMExpEst3, poly, y); + poly = vmlaq_f32(g_XMExpEst2, poly, y); + poly = vmlaq_f32(g_XMExpEst1, poly, y); + poly = vmlaq_f32(g_XMOne, poly, y); + + int32x4_t biased = vaddq_s32(itrunc, g_XMExponentBias); + biased = vshlq_n_s32(biased, 23); + float32x4_t result0 = XMVectorDivide(vreinterpretq_f32_s32(biased), poly); + + biased = vaddq_s32(itrunc, g_XM253); + biased = vshlq_n_s32(biased, 23); + float32x4_t result1 = XMVectorDivide(vreinterpretq_f32_s32(biased), poly); + result1 = vmulq_f32(g_XMMinNormal.v, result1); + + // Use selection to handle the cases + // if (V is NaN) -> QNaN; + // else if (V sign bit set) + // if (V > -150) + // if (V.exponent < -126) -> result1 + // else -> result0 + // else -> +0 + // else + // if (V < 128) -> result0 + // else -> +inf + + uint32x4_t comp = vcltq_s32(vreinterpretq_s32_f32(V), g_XMBin128); + float32x4_t result2 = vbslq_f32(comp, result0, g_XMInfinity); + + comp = vcltq_s32(itrunc, g_XMSubnormalExponent); + float32x4_t result3 = vbslq_f32(comp, result1, result0); + + comp = vcltq_s32(vreinterpretq_s32_f32(V), g_XMBinNeg150); + float32x4_t result4 = vbslq_f32(comp, result3, g_XMZero); + + int32x4_t sign = vandq_s32(vreinterpretq_s32_f32(V), g_XMNegativeZero); + comp = vceqq_s32(sign, g_XMNegativeZero); + float32x4_t result5 = vbslq_f32(comp, result4, result2); + + int32x4_t t0 = vandq_s32(vreinterpretq_s32_f32(V), g_XMQNaNTest); + int32x4_t t1 = vandq_s32(vreinterpretq_s32_f32(V), g_XMInfinity); + t0 = vreinterpretq_s32_u32(vceqq_s32(t0, g_XMZero)); + t1 = vreinterpretq_s32_u32(vceqq_s32(t1, g_XMInfinity)); + int32x4_t isNaN = vbicq_s32(t1, t0); + + float32x4_t vResult = vbslq_f32(vreinterpretq_u32_s32(isNaN), g_XMQNaN, result5); + return vResult; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_exp2_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128i itrunc = _mm_cvttps_epi32(V); + __m128 ftrunc = _mm_cvtepi32_ps(itrunc); + __m128 y = _mm_sub_ps(V, ftrunc); + + __m128 poly = XM_FMADD_PS(g_XMExpEst7, y, g_XMExpEst6); + poly = XM_FMADD_PS(poly, y, g_XMExpEst5); + poly = XM_FMADD_PS(poly, y, g_XMExpEst4); + poly = XM_FMADD_PS(poly, y, g_XMExpEst3); + poly = XM_FMADD_PS(poly, y, g_XMExpEst2); + poly = XM_FMADD_PS(poly, y, g_XMExpEst1); + poly = XM_FMADD_PS(poly, y, g_XMOne); + + __m128i biased = _mm_add_epi32(itrunc, g_XMExponentBias); + biased = _mm_slli_epi32(biased, 23); + __m128 result0 = _mm_div_ps(_mm_castsi128_ps(biased), poly); + + biased = _mm_add_epi32(itrunc, g_XM253); + biased = _mm_slli_epi32(biased, 23); + __m128 result1 = _mm_div_ps(_mm_castsi128_ps(biased), poly); + result1 = _mm_mul_ps(g_XMMinNormal.v, result1); + + // Use selection to handle the cases + // if (V is NaN) -> QNaN; + // else if (V sign bit set) + // if (V > -150) + // if (V.exponent < -126) -> result1 + // else -> result0 + // else -> +0 + // else + // if (V < 128) -> result0 + // else -> +inf + + __m128i comp = _mm_cmplt_epi32(_mm_castps_si128(V), g_XMBin128); + __m128i select0 = _mm_and_si128(comp, _mm_castps_si128(result0)); + __m128i select1 = _mm_andnot_si128(comp, g_XMInfinity); + __m128i result2 = _mm_or_si128(select0, select1); + + comp = _mm_cmplt_epi32(itrunc, g_XMSubnormalExponent); + select1 = _mm_and_si128(comp, _mm_castps_si128(result1)); + select0 = _mm_andnot_si128(comp, _mm_castps_si128(result0)); + __m128i result3 = _mm_or_si128(select0, select1); + + comp = _mm_cmplt_epi32(_mm_castps_si128(V), g_XMBinNeg150); + select0 = _mm_and_si128(comp, result3); + select1 = _mm_andnot_si128(comp, g_XMZero); + __m128i result4 = _mm_or_si128(select0, select1); + + __m128i sign = _mm_and_si128(_mm_castps_si128(V), g_XMNegativeZero); + comp = _mm_cmpeq_epi32(sign, g_XMNegativeZero); + select0 = _mm_and_si128(comp, result4); + select1 = _mm_andnot_si128(comp, result2); + __m128i result5 = _mm_or_si128(select0, select1); + + __m128i t0 = _mm_and_si128(_mm_castps_si128(V), g_XMQNaNTest); + __m128i t1 = _mm_and_si128(_mm_castps_si128(V), g_XMInfinity); + t0 = _mm_cmpeq_epi32(t0, g_XMZero); + t1 = _mm_cmpeq_epi32(t1, g_XMInfinity); + __m128i isNaN = _mm_andnot_si128(t0, t1); + + select0 = _mm_and_si128(isNaN, g_XMQNaN); + select1 = _mm_andnot_si128(isNaN, result5); + __m128i vResult = _mm_or_si128(select0, select1); + + return _mm_castsi128_ps(vResult); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorExp10(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + powf(10.0f, V.vector4_f32[0]), + powf(10.0f, V.vector4_f32[1]), + powf(10.0f, V.vector4_f32[2]), + powf(10.0f, V.vector4_f32[3]) + } } }; + return Result.v; + +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_exp10_ps(V); + return Result; +#else + // exp10(V) = exp2(vin*log2(10)) + XMVECTOR Vten = XMVectorMultiply(g_XMLg10, V); + return XMVectorExp2(Vten); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorExpE(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + expf(V.vector4_f32[0]), + expf(V.vector4_f32[1]), + expf(V.vector4_f32[2]), + expf(V.vector4_f32[3]) + } } }; + return Result.v; + +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_exp_ps(V); + return Result; +#else + // expE(V) = exp2(vin*log2(e)) + XMVECTOR Ve = XMVectorMultiply(g_XMLgE, V); + return XMVectorExp2(Ve); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorExp(FXMVECTOR V) noexcept +{ + return XMVectorExp2(V); +} + +//------------------------------------------------------------------------------ + +#if defined(_XM_SSE_INTRINSICS_) + +namespace MathInternal +{ + inline __m128i multi_sll_epi32(__m128i value, __m128i count) noexcept + { + __m128i v = _mm_shuffle_epi32(value, _MM_SHUFFLE(0, 0, 0, 0)); + __m128i c = _mm_shuffle_epi32(count, _MM_SHUFFLE(0, 0, 0, 0)); + c = _mm_and_si128(c, g_XMMaskX); + __m128i r0 = _mm_sll_epi32(v, c); + + v = _mm_shuffle_epi32(value, _MM_SHUFFLE(1, 1, 1, 1)); + c = _mm_shuffle_epi32(count, _MM_SHUFFLE(1, 1, 1, 1)); + c = _mm_and_si128(c, g_XMMaskX); + __m128i r1 = _mm_sll_epi32(v, c); + + v = _mm_shuffle_epi32(value, _MM_SHUFFLE(2, 2, 2, 2)); + c = _mm_shuffle_epi32(count, _MM_SHUFFLE(2, 2, 2, 2)); + c = _mm_and_si128(c, g_XMMaskX); + __m128i r2 = _mm_sll_epi32(v, c); + + v = _mm_shuffle_epi32(value, _MM_SHUFFLE(3, 3, 3, 3)); + c = _mm_shuffle_epi32(count, _MM_SHUFFLE(3, 3, 3, 3)); + c = _mm_and_si128(c, g_XMMaskX); + __m128i r3 = _mm_sll_epi32(v, c); + + // (r0,r0,r1,r1) + __m128 r01 = _mm_shuffle_ps(_mm_castsi128_ps(r0), _mm_castsi128_ps(r1), _MM_SHUFFLE(0, 0, 0, 0)); + // (r2,r2,r3,r3) + __m128 r23 = _mm_shuffle_ps(_mm_castsi128_ps(r2), _mm_castsi128_ps(r3), _MM_SHUFFLE(0, 0, 0, 0)); + // (r0,r1,r2,r3) + __m128 result = _mm_shuffle_ps(r01, r23, _MM_SHUFFLE(2, 0, 2, 0)); + return _mm_castps_si128(result); + } + + inline __m128i multi_srl_epi32(__m128i value, __m128i count) noexcept + { + __m128i v = _mm_shuffle_epi32(value, _MM_SHUFFLE(0, 0, 0, 0)); + __m128i c = _mm_shuffle_epi32(count, _MM_SHUFFLE(0, 0, 0, 0)); + c = _mm_and_si128(c, g_XMMaskX); + __m128i r0 = _mm_srl_epi32(v, c); + + v = _mm_shuffle_epi32(value, _MM_SHUFFLE(1, 1, 1, 1)); + c = _mm_shuffle_epi32(count, _MM_SHUFFLE(1, 1, 1, 1)); + c = _mm_and_si128(c, g_XMMaskX); + __m128i r1 = _mm_srl_epi32(v, c); + + v = _mm_shuffle_epi32(value, _MM_SHUFFLE(2, 2, 2, 2)); + c = _mm_shuffle_epi32(count, _MM_SHUFFLE(2, 2, 2, 2)); + c = _mm_and_si128(c, g_XMMaskX); + __m128i r2 = _mm_srl_epi32(v, c); + + v = _mm_shuffle_epi32(value, _MM_SHUFFLE(3, 3, 3, 3)); + c = _mm_shuffle_epi32(count, _MM_SHUFFLE(3, 3, 3, 3)); + c = _mm_and_si128(c, g_XMMaskX); + __m128i r3 = _mm_srl_epi32(v, c); + + // (r0,r0,r1,r1) + __m128 r01 = _mm_shuffle_ps(_mm_castsi128_ps(r0), _mm_castsi128_ps(r1), _MM_SHUFFLE(0, 0, 0, 0)); + // (r2,r2,r3,r3) + __m128 r23 = _mm_shuffle_ps(_mm_castsi128_ps(r2), _mm_castsi128_ps(r3), _MM_SHUFFLE(0, 0, 0, 0)); + // (r0,r1,r2,r3) + __m128 result = _mm_shuffle_ps(r01, r23, _MM_SHUFFLE(2, 0, 2, 0)); + return _mm_castps_si128(result); + } + + inline __m128i GetLeadingBit(const __m128i value) noexcept + { + static const XMVECTORI32 g_XM0000FFFF = { { { 0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF } } }; + static const XMVECTORI32 g_XM000000FF = { { { 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF } } }; + static const XMVECTORI32 g_XM0000000F = { { { 0x0000000F, 0x0000000F, 0x0000000F, 0x0000000F } } }; + static const XMVECTORI32 g_XM00000003 = { { { 0x00000003, 0x00000003, 0x00000003, 0x00000003 } } }; + + __m128i v = value, r, c, b, s; + + c = _mm_cmpgt_epi32(v, g_XM0000FFFF); // c = (v > 0xFFFF) + b = _mm_srli_epi32(c, 31); // b = (c ? 1 : 0) + r = _mm_slli_epi32(b, 4); // r = (b << 4) + v = multi_srl_epi32(v, r); // v = (v >> r) + + c = _mm_cmpgt_epi32(v, g_XM000000FF); // c = (v > 0xFF) + b = _mm_srli_epi32(c, 31); // b = (c ? 1 : 0) + s = _mm_slli_epi32(b, 3); // s = (b << 3) + v = multi_srl_epi32(v, s); // v = (v >> s) + r = _mm_or_si128(r, s); // r = (r | s) + + c = _mm_cmpgt_epi32(v, g_XM0000000F); // c = (v > 0xF) + b = _mm_srli_epi32(c, 31); // b = (c ? 1 : 0) + s = _mm_slli_epi32(b, 2); // s = (b << 2) + v = multi_srl_epi32(v, s); // v = (v >> s) + r = _mm_or_si128(r, s); // r = (r | s) + + c = _mm_cmpgt_epi32(v, g_XM00000003); // c = (v > 0x3) + b = _mm_srli_epi32(c, 31); // b = (c ? 1 : 0) + s = _mm_slli_epi32(b, 1); // s = (b << 1) + v = multi_srl_epi32(v, s); // v = (v >> s) + r = _mm_or_si128(r, s); // r = (r | s) + + s = _mm_srli_epi32(v, 1); + r = _mm_or_si128(r, s); + return r; + } +} // namespace MathInternal + +#endif // _XM_SSE_INTRINSICS_ + +#if defined(_XM_ARM_NEON_INTRINSICS_) + +namespace MathInternal +{ + inline int32x4_t GetLeadingBit(const int32x4_t value) noexcept + { + static const XMVECTORI32 g_XM0000FFFF = { { { 0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF } } }; + static const XMVECTORI32 g_XM000000FF = { { { 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF } } }; + static const XMVECTORI32 g_XM0000000F = { { { 0x0000000F, 0x0000000F, 0x0000000F, 0x0000000F } } }; + static const XMVECTORI32 g_XM00000003 = { { { 0x00000003, 0x00000003, 0x00000003, 0x00000003 } } }; + + uint32x4_t c = vcgtq_s32(value, g_XM0000FFFF); // c = (v > 0xFFFF) + int32x4_t b = vshrq_n_s32(vreinterpretq_s32_u32(c), 31); // b = (c ? 1 : 0) + int32x4_t r = vshlq_n_s32(b, 4); // r = (b << 4) + r = vnegq_s32(r); + int32x4_t v = vshlq_s32(value, r); // v = (v >> r) + + c = vcgtq_s32(v, g_XM000000FF); // c = (v > 0xFF) + b = vshrq_n_s32(vreinterpretq_s32_u32(c), 31); // b = (c ? 1 : 0) + int32x4_t s = vshlq_n_s32(b, 3); // s = (b << 3) + s = vnegq_s32(s); + v = vshlq_s32(v, s); // v = (v >> s) + r = vorrq_s32(r, s); // r = (r | s) + + c = vcgtq_s32(v, g_XM0000000F); // c = (v > 0xF) + b = vshrq_n_s32(vreinterpretq_s32_u32(c), 31); // b = (c ? 1 : 0) + s = vshlq_n_s32(b, 2); // s = (b << 2) + s = vnegq_s32(s); + v = vshlq_s32(v, s); // v = (v >> s) + r = vorrq_s32(r, s); // r = (r | s) + + c = vcgtq_s32(v, g_XM00000003); // c = (v > 0x3) + b = vshrq_n_s32(vreinterpretq_s32_u32(c), 31); // b = (c ? 1 : 0) + s = vshlq_n_s32(b, 1); // s = (b << 1) + s = vnegq_s32(s); + v = vshlq_s32(v, s); // v = (v >> s) + r = vorrq_s32(r, s); // r = (r | s) + + s = vshrq_n_s32(v, 1); + r = vorrq_s32(r, s); + return r; + } + +} // namespace MathInternal + +#endif + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorLog2(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + log2f(V.vector4_f32[0]), + log2f(V.vector4_f32[1]), + log2f(V.vector4_f32[2]), + log2f(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x4_t rawBiased = vandq_s32(vreinterpretq_s32_f32(V), g_XMInfinity); + int32x4_t trailing = vandq_s32(vreinterpretq_s32_f32(V), g_XMQNaNTest); + uint32x4_t isExponentZero = vceqq_s32(vreinterpretq_s32_f32(g_XMZero), rawBiased); + + // Compute exponent and significand for normals. + int32x4_t biased = vshrq_n_s32(rawBiased, 23); + int32x4_t exponentNor = vsubq_s32(biased, g_XMExponentBias); + int32x4_t trailingNor = trailing; + + // Compute exponent and significand for subnormals. + int32x4_t leading = MathInternal::GetLeadingBit(trailing); + int32x4_t shift = vsubq_s32(g_XMNumTrailing, leading); + int32x4_t exponentSub = vsubq_s32(g_XMSubnormalExponent, shift); + int32x4_t trailingSub = vshlq_s32(trailing, shift); + trailingSub = vandq_s32(trailingSub, g_XMQNaNTest); + int32x4_t e = vbslq_s32(isExponentZero, exponentSub, exponentNor); + int32x4_t t = vbslq_s32(isExponentZero, trailingSub, trailingNor); + + // Compute the approximation. + int32x4_t tmp = vorrq_s32(vreinterpretq_s32_f32(g_XMOne), t); + float32x4_t y = vsubq_f32(vreinterpretq_f32_s32(tmp), g_XMOne); + + float32x4_t log2 = vmlaq_f32(g_XMLogEst6, g_XMLogEst7, y); + log2 = vmlaq_f32(g_XMLogEst5, log2, y); + log2 = vmlaq_f32(g_XMLogEst4, log2, y); + log2 = vmlaq_f32(g_XMLogEst3, log2, y); + log2 = vmlaq_f32(g_XMLogEst2, log2, y); + log2 = vmlaq_f32(g_XMLogEst1, log2, y); + log2 = vmlaq_f32(g_XMLogEst0, log2, y); + log2 = vmlaq_f32(vcvtq_f32_s32(e), log2, y); + + // if (x is NaN) -> QNaN + // else if (V is positive) + // if (V is infinite) -> +inf + // else -> log2(V) + // else + // if (V is zero) -> -inf + // else -> -QNaN + + uint32x4_t isInfinite = vandq_u32(vreinterpretq_u32_f32(V), g_XMAbsMask); + isInfinite = vceqq_u32(isInfinite, g_XMInfinity); + + uint32x4_t isGreaterZero = vcgtq_f32(V, g_XMZero); + uint32x4_t isNotFinite = vcgtq_f32(V, g_XMInfinity); + uint32x4_t isPositive = vbicq_u32(isGreaterZero, isNotFinite); + + uint32x4_t isZero = vandq_u32(vreinterpretq_u32_f32(V), g_XMAbsMask); + isZero = vceqq_u32(isZero, g_XMZero); + + uint32x4_t t0 = vandq_u32(vreinterpretq_u32_f32(V), g_XMQNaNTest); + uint32x4_t t1 = vandq_u32(vreinterpretq_u32_f32(V), g_XMInfinity); + t0 = vceqq_u32(t0, g_XMZero); + t1 = vceqq_u32(t1, g_XMInfinity); + uint32x4_t isNaN = vbicq_u32(t1, t0); + + float32x4_t result = vbslq_f32(isInfinite, g_XMInfinity, log2); + float32x4_t tmp2 = vbslq_f32(isZero, g_XMNegInfinity, g_XMNegQNaN); + result = vbslq_f32(isPositive, result, tmp2); + result = vbslq_f32(isNaN, g_XMQNaN, result); + return result; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_log2_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128i rawBiased = _mm_and_si128(_mm_castps_si128(V), g_XMInfinity); + __m128i trailing = _mm_and_si128(_mm_castps_si128(V), g_XMQNaNTest); + __m128i isExponentZero = _mm_cmpeq_epi32(g_XMZero, rawBiased); + + // Compute exponent and significand for normals. + __m128i biased = _mm_srli_epi32(rawBiased, 23); + __m128i exponentNor = _mm_sub_epi32(biased, g_XMExponentBias); + __m128i trailingNor = trailing; + + // Compute exponent and significand for subnormals. + __m128i leading = MathInternal::GetLeadingBit(trailing); + __m128i shift = _mm_sub_epi32(g_XMNumTrailing, leading); + __m128i exponentSub = _mm_sub_epi32(g_XMSubnormalExponent, shift); + __m128i trailingSub = MathInternal::multi_sll_epi32(trailing, shift); + trailingSub = _mm_and_si128(trailingSub, g_XMQNaNTest); + + __m128i select0 = _mm_and_si128(isExponentZero, exponentSub); + __m128i select1 = _mm_andnot_si128(isExponentZero, exponentNor); + __m128i e = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isExponentZero, trailingSub); + select1 = _mm_andnot_si128(isExponentZero, trailingNor); + __m128i t = _mm_or_si128(select0, select1); + + // Compute the approximation. + __m128i tmp = _mm_or_si128(g_XMOne, t); + __m128 y = _mm_sub_ps(_mm_castsi128_ps(tmp), g_XMOne); + + __m128 log2 = XM_FMADD_PS(g_XMLogEst7, y, g_XMLogEst6); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst5); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst4); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst3); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst2); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst1); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst0); + log2 = XM_FMADD_PS(log2, y, _mm_cvtepi32_ps(e)); + + // if (x is NaN) -> QNaN + // else if (V is positive) + // if (V is infinite) -> +inf + // else -> log2(V) + // else + // if (V is zero) -> -inf + // else -> -QNaN + + __m128i isInfinite = _mm_and_si128(_mm_castps_si128(V), g_XMAbsMask); + isInfinite = _mm_cmpeq_epi32(isInfinite, g_XMInfinity); + + __m128i isGreaterZero = _mm_cmpgt_epi32(_mm_castps_si128(V), g_XMZero); + __m128i isNotFinite = _mm_cmpgt_epi32(_mm_castps_si128(V), g_XMInfinity); + __m128i isPositive = _mm_andnot_si128(isNotFinite, isGreaterZero); + + __m128i isZero = _mm_and_si128(_mm_castps_si128(V), g_XMAbsMask); + isZero = _mm_cmpeq_epi32(isZero, g_XMZero); + + __m128i t0 = _mm_and_si128(_mm_castps_si128(V), g_XMQNaNTest); + __m128i t1 = _mm_and_si128(_mm_castps_si128(V), g_XMInfinity); + t0 = _mm_cmpeq_epi32(t0, g_XMZero); + t1 = _mm_cmpeq_epi32(t1, g_XMInfinity); + __m128i isNaN = _mm_andnot_si128(t0, t1); + + select0 = _mm_and_si128(isInfinite, g_XMInfinity); + select1 = _mm_andnot_si128(isInfinite, _mm_castps_si128(log2)); + __m128i result = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isZero, g_XMNegInfinity); + select1 = _mm_andnot_si128(isZero, g_XMNegQNaN); + tmp = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isPositive, result); + select1 = _mm_andnot_si128(isPositive, tmp); + result = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isNaN, g_XMQNaN); + select1 = _mm_andnot_si128(isNaN, result); + result = _mm_or_si128(select0, select1); + + return _mm_castsi128_ps(result); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorLog10(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + log10f(V.vector4_f32[0]), + log10f(V.vector4_f32[1]), + log10f(V.vector4_f32[2]), + log10f(V.vector4_f32[3]) + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x4_t rawBiased = vandq_s32(vreinterpretq_s32_f32(V), g_XMInfinity); + int32x4_t trailing = vandq_s32(vreinterpretq_s32_f32(V), g_XMQNaNTest); + uint32x4_t isExponentZero = vceqq_s32(g_XMZero, rawBiased); + + // Compute exponent and significand for normals. + int32x4_t biased = vshrq_n_s32(rawBiased, 23); + int32x4_t exponentNor = vsubq_s32(biased, g_XMExponentBias); + int32x4_t trailingNor = trailing; + + // Compute exponent and significand for subnormals. + int32x4_t leading = MathInternal::GetLeadingBit(trailing); + int32x4_t shift = vsubq_s32(g_XMNumTrailing, leading); + int32x4_t exponentSub = vsubq_s32(g_XMSubnormalExponent, shift); + int32x4_t trailingSub = vshlq_s32(trailing, shift); + trailingSub = vandq_s32(trailingSub, g_XMQNaNTest); + int32x4_t e = vbslq_s32(isExponentZero, exponentSub, exponentNor); + int32x4_t t = vbslq_s32(isExponentZero, trailingSub, trailingNor); + + // Compute the approximation. + int32x4_t tmp = vorrq_s32(g_XMOne, t); + float32x4_t y = vsubq_f32(vreinterpretq_f32_s32(tmp), g_XMOne); + + float32x4_t log2 = vmlaq_f32(g_XMLogEst6, g_XMLogEst7, y); + log2 = vmlaq_f32(g_XMLogEst5, log2, y); + log2 = vmlaq_f32(g_XMLogEst4, log2, y); + log2 = vmlaq_f32(g_XMLogEst3, log2, y); + log2 = vmlaq_f32(g_XMLogEst2, log2, y); + log2 = vmlaq_f32(g_XMLogEst1, log2, y); + log2 = vmlaq_f32(g_XMLogEst0, log2, y); + log2 = vmlaq_f32(vcvtq_f32_s32(e), log2, y); + + log2 = vmulq_f32(g_XMInvLg10, log2); + + // if (x is NaN) -> QNaN + // else if (V is positive) + // if (V is infinite) -> +inf + // else -> log2(V) + // else + // if (V is zero) -> -inf + // else -> -QNaN + + uint32x4_t isInfinite = vandq_u32(vreinterpretq_u32_f32(V), g_XMAbsMask); + isInfinite = vceqq_u32(isInfinite, g_XMInfinity); + + uint32x4_t isGreaterZero = vcgtq_s32(vreinterpretq_s32_f32(V), g_XMZero); + uint32x4_t isNotFinite = vcgtq_s32(vreinterpretq_s32_f32(V), g_XMInfinity); + uint32x4_t isPositive = vbicq_u32(isGreaterZero, isNotFinite); + + uint32x4_t isZero = vandq_u32(vreinterpretq_u32_f32(V), g_XMAbsMask); + isZero = vceqq_u32(isZero, g_XMZero); + + uint32x4_t t0 = vandq_u32(vreinterpretq_u32_f32(V), g_XMQNaNTest); + uint32x4_t t1 = vandq_u32(vreinterpretq_u32_f32(V), g_XMInfinity); + t0 = vceqq_u32(t0, g_XMZero); + t1 = vceqq_u32(t1, g_XMInfinity); + uint32x4_t isNaN = vbicq_u32(t1, t0); + + float32x4_t result = vbslq_f32(isInfinite, g_XMInfinity, log2); + float32x4_t tmp2 = vbslq_f32(isZero, g_XMNegInfinity, g_XMNegQNaN); + result = vbslq_f32(isPositive, result, tmp2); + result = vbslq_f32(isNaN, g_XMQNaN, result); + return result; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_log10_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128i rawBiased = _mm_and_si128(_mm_castps_si128(V), g_XMInfinity); + __m128i trailing = _mm_and_si128(_mm_castps_si128(V), g_XMQNaNTest); + __m128i isExponentZero = _mm_cmpeq_epi32(g_XMZero, rawBiased); + + // Compute exponent and significand for normals. + __m128i biased = _mm_srli_epi32(rawBiased, 23); + __m128i exponentNor = _mm_sub_epi32(biased, g_XMExponentBias); + __m128i trailingNor = trailing; + + // Compute exponent and significand for subnormals. + __m128i leading = MathInternal::GetLeadingBit(trailing); + __m128i shift = _mm_sub_epi32(g_XMNumTrailing, leading); + __m128i exponentSub = _mm_sub_epi32(g_XMSubnormalExponent, shift); + __m128i trailingSub = MathInternal::multi_sll_epi32(trailing, shift); + trailingSub = _mm_and_si128(trailingSub, g_XMQNaNTest); + + __m128i select0 = _mm_and_si128(isExponentZero, exponentSub); + __m128i select1 = _mm_andnot_si128(isExponentZero, exponentNor); + __m128i e = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isExponentZero, trailingSub); + select1 = _mm_andnot_si128(isExponentZero, trailingNor); + __m128i t = _mm_or_si128(select0, select1); + + // Compute the approximation. + __m128i tmp = _mm_or_si128(g_XMOne, t); + __m128 y = _mm_sub_ps(_mm_castsi128_ps(tmp), g_XMOne); + + __m128 log2 = XM_FMADD_PS(g_XMLogEst7, y, g_XMLogEst6); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst5); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst4); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst3); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst2); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst1); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst0); + log2 = XM_FMADD_PS(log2, y, _mm_cvtepi32_ps(e)); + + log2 = _mm_mul_ps(g_XMInvLg10, log2); + + // if (x is NaN) -> QNaN + // else if (V is positive) + // if (V is infinite) -> +inf + // else -> log2(V) + // else + // if (V is zero) -> -inf + // else -> -QNaN + + __m128i isInfinite = _mm_and_si128(_mm_castps_si128(V), g_XMAbsMask); + isInfinite = _mm_cmpeq_epi32(isInfinite, g_XMInfinity); + + __m128i isGreaterZero = _mm_cmpgt_epi32(_mm_castps_si128(V), g_XMZero); + __m128i isNotFinite = _mm_cmpgt_epi32(_mm_castps_si128(V), g_XMInfinity); + __m128i isPositive = _mm_andnot_si128(isNotFinite, isGreaterZero); + + __m128i isZero = _mm_and_si128(_mm_castps_si128(V), g_XMAbsMask); + isZero = _mm_cmpeq_epi32(isZero, g_XMZero); + + __m128i t0 = _mm_and_si128(_mm_castps_si128(V), g_XMQNaNTest); + __m128i t1 = _mm_and_si128(_mm_castps_si128(V), g_XMInfinity); + t0 = _mm_cmpeq_epi32(t0, g_XMZero); + t1 = _mm_cmpeq_epi32(t1, g_XMInfinity); + __m128i isNaN = _mm_andnot_si128(t0, t1); + + select0 = _mm_and_si128(isInfinite, g_XMInfinity); + select1 = _mm_andnot_si128(isInfinite, _mm_castps_si128(log2)); + __m128i result = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isZero, g_XMNegInfinity); + select1 = _mm_andnot_si128(isZero, g_XMNegQNaN); + tmp = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isPositive, result); + select1 = _mm_andnot_si128(isPositive, tmp); + result = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isNaN, g_XMQNaN); + select1 = _mm_andnot_si128(isNaN, result); + result = _mm_or_si128(select0, select1); + + return _mm_castsi128_ps(result); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorLogE(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + logf(V.vector4_f32[0]), + logf(V.vector4_f32[1]), + logf(V.vector4_f32[2]), + logf(V.vector4_f32[3]) + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int32x4_t rawBiased = vandq_s32(vreinterpretq_s32_f32(V), g_XMInfinity); + int32x4_t trailing = vandq_s32(vreinterpretq_s32_f32(V), g_XMQNaNTest); + uint32x4_t isExponentZero = vceqq_s32(g_XMZero, rawBiased); + + // Compute exponent and significand for normals. + int32x4_t biased = vshrq_n_s32(rawBiased, 23); + int32x4_t exponentNor = vsubq_s32(biased, g_XMExponentBias); + int32x4_t trailingNor = trailing; + + // Compute exponent and significand for subnormals. + int32x4_t leading = MathInternal::GetLeadingBit(trailing); + int32x4_t shift = vsubq_s32(g_XMNumTrailing, leading); + int32x4_t exponentSub = vsubq_s32(g_XMSubnormalExponent, shift); + int32x4_t trailingSub = vshlq_s32(trailing, shift); + trailingSub = vandq_s32(trailingSub, g_XMQNaNTest); + int32x4_t e = vbslq_s32(isExponentZero, exponentSub, exponentNor); + int32x4_t t = vbslq_s32(isExponentZero, trailingSub, trailingNor); + + // Compute the approximation. + int32x4_t tmp = vorrq_s32(g_XMOne, t); + float32x4_t y = vsubq_f32(vreinterpretq_f32_s32(tmp), g_XMOne); + + float32x4_t log2 = vmlaq_f32(g_XMLogEst6, g_XMLogEst7, y); + log2 = vmlaq_f32(g_XMLogEst5, log2, y); + log2 = vmlaq_f32(g_XMLogEst4, log2, y); + log2 = vmlaq_f32(g_XMLogEst3, log2, y); + log2 = vmlaq_f32(g_XMLogEst2, log2, y); + log2 = vmlaq_f32(g_XMLogEst1, log2, y); + log2 = vmlaq_f32(g_XMLogEst0, log2, y); + log2 = vmlaq_f32(vcvtq_f32_s32(e), log2, y); + + log2 = vmulq_f32(g_XMInvLgE, log2); + + // if (x is NaN) -> QNaN + // else if (V is positive) + // if (V is infinite) -> +inf + // else -> log2(V) + // else + // if (V is zero) -> -inf + // else -> -QNaN + + uint32x4_t isInfinite = vandq_u32(vreinterpretq_u32_f32(V), g_XMAbsMask); + isInfinite = vceqq_u32(isInfinite, g_XMInfinity); + + uint32x4_t isGreaterZero = vcgtq_s32(vreinterpretq_s32_f32(V), g_XMZero); + uint32x4_t isNotFinite = vcgtq_s32(vreinterpretq_s32_f32(V), g_XMInfinity); + uint32x4_t isPositive = vbicq_u32(isGreaterZero, isNotFinite); + + uint32x4_t isZero = vandq_u32(vreinterpretq_u32_f32(V), g_XMAbsMask); + isZero = vceqq_u32(isZero, g_XMZero); + + uint32x4_t t0 = vandq_u32(vreinterpretq_u32_f32(V), g_XMQNaNTest); + uint32x4_t t1 = vandq_u32(vreinterpretq_u32_f32(V), g_XMInfinity); + t0 = vceqq_u32(t0, g_XMZero); + t1 = vceqq_u32(t1, g_XMInfinity); + uint32x4_t isNaN = vbicq_u32(t1, t0); + + float32x4_t result = vbslq_f32(isInfinite, g_XMInfinity, log2); + float32x4_t tmp2 = vbslq_f32(isZero, g_XMNegInfinity, g_XMNegQNaN); + result = vbslq_f32(isPositive, result, tmp2); + result = vbslq_f32(isNaN, g_XMQNaN, result); + return result; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_log_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128i rawBiased = _mm_and_si128(_mm_castps_si128(V), g_XMInfinity); + __m128i trailing = _mm_and_si128(_mm_castps_si128(V), g_XMQNaNTest); + __m128i isExponentZero = _mm_cmpeq_epi32(g_XMZero, rawBiased); + + // Compute exponent and significand for normals. + __m128i biased = _mm_srli_epi32(rawBiased, 23); + __m128i exponentNor = _mm_sub_epi32(biased, g_XMExponentBias); + __m128i trailingNor = trailing; + + // Compute exponent and significand for subnormals. + __m128i leading = MathInternal::GetLeadingBit(trailing); + __m128i shift = _mm_sub_epi32(g_XMNumTrailing, leading); + __m128i exponentSub = _mm_sub_epi32(g_XMSubnormalExponent, shift); + __m128i trailingSub = MathInternal::multi_sll_epi32(trailing, shift); + trailingSub = _mm_and_si128(trailingSub, g_XMQNaNTest); + + __m128i select0 = _mm_and_si128(isExponentZero, exponentSub); + __m128i select1 = _mm_andnot_si128(isExponentZero, exponentNor); + __m128i e = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isExponentZero, trailingSub); + select1 = _mm_andnot_si128(isExponentZero, trailingNor); + __m128i t = _mm_or_si128(select0, select1); + + // Compute the approximation. + __m128i tmp = _mm_or_si128(g_XMOne, t); + __m128 y = _mm_sub_ps(_mm_castsi128_ps(tmp), g_XMOne); + + __m128 log2 = XM_FMADD_PS(g_XMLogEst7, y, g_XMLogEst6); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst5); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst4); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst3); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst2); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst1); + log2 = XM_FMADD_PS(log2, y, g_XMLogEst0); + log2 = XM_FMADD_PS(log2, y, _mm_cvtepi32_ps(e)); + + log2 = _mm_mul_ps(g_XMInvLgE, log2); + + // if (x is NaN) -> QNaN + // else if (V is positive) + // if (V is infinite) -> +inf + // else -> log2(V) + // else + // if (V is zero) -> -inf + // else -> -QNaN + + __m128i isInfinite = _mm_and_si128(_mm_castps_si128(V), g_XMAbsMask); + isInfinite = _mm_cmpeq_epi32(isInfinite, g_XMInfinity); + + __m128i isGreaterZero = _mm_cmpgt_epi32(_mm_castps_si128(V), g_XMZero); + __m128i isNotFinite = _mm_cmpgt_epi32(_mm_castps_si128(V), g_XMInfinity); + __m128i isPositive = _mm_andnot_si128(isNotFinite, isGreaterZero); + + __m128i isZero = _mm_and_si128(_mm_castps_si128(V), g_XMAbsMask); + isZero = _mm_cmpeq_epi32(isZero, g_XMZero); + + __m128i t0 = _mm_and_si128(_mm_castps_si128(V), g_XMQNaNTest); + __m128i t1 = _mm_and_si128(_mm_castps_si128(V), g_XMInfinity); + t0 = _mm_cmpeq_epi32(t0, g_XMZero); + t1 = _mm_cmpeq_epi32(t1, g_XMInfinity); + __m128i isNaN = _mm_andnot_si128(t0, t1); + + select0 = _mm_and_si128(isInfinite, g_XMInfinity); + select1 = _mm_andnot_si128(isInfinite, _mm_castps_si128(log2)); + __m128i result = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isZero, g_XMNegInfinity); + select1 = _mm_andnot_si128(isZero, g_XMNegQNaN); + tmp = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isPositive, result); + select1 = _mm_andnot_si128(isPositive, tmp); + result = _mm_or_si128(select0, select1); + + select0 = _mm_and_si128(isNaN, g_XMQNaN); + select1 = _mm_andnot_si128(isNaN, result); + result = _mm_or_si128(select0, select1); + + return _mm_castsi128_ps(result); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorLog(FXMVECTOR V) noexcept +{ + return XMVectorLog2(V); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorPow +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + powf(V1.vector4_f32[0], V2.vector4_f32[0]), + powf(V1.vector4_f32[1], V2.vector4_f32[1]), + powf(V1.vector4_f32[2], V2.vector4_f32[2]), + powf(V1.vector4_f32[3], V2.vector4_f32[3]) + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTORF32 vResult = { { { + powf(vgetq_lane_f32(V1, 0), vgetq_lane_f32(V2, 0)), + powf(vgetq_lane_f32(V1, 1), vgetq_lane_f32(V2, 1)), + powf(vgetq_lane_f32(V1, 2), vgetq_lane_f32(V2, 2)), + powf(vgetq_lane_f32(V1, 3), vgetq_lane_f32(V2, 3)) + } } }; + return vResult.v; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_pow_ps(V1, V2); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + XM_ALIGNED_DATA(16) float a[4]; + XM_ALIGNED_DATA(16) float b[4]; + _mm_store_ps(a, V1); + _mm_store_ps(b, V2); + XMVECTOR vResult = _mm_setr_ps( + powf(a[0], b[0]), + powf(a[1], b[1]), + powf(a[2], b[2]), + powf(a[3], b[3])); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorAbs(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + fabsf(V.vector4_f32[0]), + fabsf(V.vector4_f32[1]), + fabsf(V.vector4_f32[2]), + fabsf(V.vector4_f32[3]) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + return vabsq_f32(V); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = _mm_setzero_ps(); + vResult = _mm_sub_ps(vResult, V); + vResult = _mm_max_ps(vResult, V); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorMod +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + // V1 % V2 = V1 - V2 * truncate(V1 / V2) + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Quotient = XMVectorDivide(V1, V2); + Quotient = XMVectorTruncate(Quotient); + XMVECTOR Result = XMVectorNegativeMultiplySubtract(V2, Quotient, V1); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR vResult = XMVectorDivide(V1, V2); + vResult = XMVectorTruncate(vResult); + return vmlsq_f32(V1, vResult, V2); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = _mm_div_ps(V1, V2); + vResult = XMVectorTruncate(vResult); + return XM_FNMADD_PS(vResult, V2, V1); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorModAngles(FXMVECTOR Angles) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR V; + XMVECTOR Result; + + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + V = XMVectorMultiply(Angles, g_XMReciprocalTwoPi.v); + V = XMVectorRound(V); + Result = XMVectorNegativeMultiplySubtract(g_XMTwoPi.v, V, Angles); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + XMVECTOR vResult = vmulq_f32(Angles, g_XMReciprocalTwoPi); + // Use the inline function due to complexity for rounding + vResult = XMVectorRound(vResult); + return vmlsq_f32(Angles, vResult, g_XMTwoPi); +#elif defined(_XM_SSE_INTRINSICS_) + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + XMVECTOR vResult = _mm_mul_ps(Angles, g_XMReciprocalTwoPi); + // Use the inline function due to complexity for rounding + vResult = XMVectorRound(vResult); + return XM_FNMADD_PS(vResult, g_XMTwoPi, Angles); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSin(FXMVECTOR V) noexcept +{ + // 11-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + sinf(V.vector4_f32[0]), + sinf(V.vector4_f32[1]), + sinf(V.vector4_f32[2]), + sinf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Force the value within the bounds of pi + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with sin(y) = sin(x). + uint32x4_t sign = vandq_u32(vreinterpretq_u32_f32(x), g_XMNegativeZero); + uint32x4_t c = vorrq_u32(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + float32x4_t absx = vabsq_f32(x); + float32x4_t rflx = vsubq_f32(vreinterpretq_f32_u32(c), x); + uint32x4_t comp = vcleq_f32(absx, g_XMHalfPi); + x = vbslq_f32(comp, x, rflx); + + float32x4_t x2 = vmulq_f32(x, x); + + // Compute polynomial approximation + const XMVECTOR SC1 = g_XMSinCoefficients1; + const XMVECTOR SC0 = g_XMSinCoefficients0; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(SC0), 1); + XMVECTOR Result = vmlaq_lane_f32(vConstants, x2, vget_low_f32(SC1), 0); + + vConstants = vdupq_lane_f32(vget_high_f32(SC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(SC0), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(SC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + Result = vmlaq_f32(g_XMOne, Result, x2); + Result = vmulq_f32(Result, x); + return Result; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_sin_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + // Force the value within the bounds of pi + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with sin(y) = sin(x). + __m128 sign = _mm_and_ps(x, g_XMNegativeZero); + __m128 c = _mm_or_ps(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + __m128 absx = _mm_andnot_ps(sign, x); // |x| + __m128 rflx = _mm_sub_ps(c, x); + __m128 comp = _mm_cmple_ps(absx, g_XMHalfPi); + __m128 select0 = _mm_and_ps(comp, x); + __m128 select1 = _mm_andnot_ps(comp, rflx); + x = _mm_or_ps(select0, select1); + + __m128 x2 = _mm_mul_ps(x, x); + + // Compute polynomial approximation + const XMVECTOR SC1 = g_XMSinCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(SC1, _MM_SHUFFLE(0, 0, 0, 0)); + const XMVECTOR SC0 = g_XMSinCoefficients0; + __m128 vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(2, 2, 2, 2)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(0, 0, 0, 0)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + Result = XM_FMADD_PS(Result, x2, g_XMOne); + Result = _mm_mul_ps(Result, x); + return Result; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorCos(FXMVECTOR V) noexcept +{ + // 10-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + cosf(V.vector4_f32[0]), + cosf(V.vector4_f32[1]), + cosf(V.vector4_f32[2]), + cosf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Map V to x in [-pi,pi]. + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with cos(y) = sign*cos(x). + uint32x4_t sign = vandq_u32(vreinterpretq_u32_f32(x), g_XMNegativeZero); + uint32x4_t c = vorrq_u32(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + float32x4_t absx = vabsq_f32(x); + float32x4_t rflx = vsubq_f32(vreinterpretq_f32_u32(c), x); + uint32x4_t comp = vcleq_f32(absx, g_XMHalfPi); + x = vbslq_f32(comp, x, rflx); + float32x4_t fsign = vbslq_f32(comp, g_XMOne, g_XMNegativeOne); + + float32x4_t x2 = vmulq_f32(x, x); + + // Compute polynomial approximation + const XMVECTOR CC1 = g_XMCosCoefficients1; + const XMVECTOR CC0 = g_XMCosCoefficients0; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(CC0), 1); + XMVECTOR Result = vmlaq_lane_f32(vConstants, x2, vget_low_f32(CC1), 0); + + vConstants = vdupq_lane_f32(vget_high_f32(CC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(CC0), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(CC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + Result = vmlaq_f32(g_XMOne, Result, x2); + Result = vmulq_f32(Result, fsign); + return Result; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_cos_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + // Map V to x in [-pi,pi]. + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with cos(y) = sign*cos(x). + XMVECTOR sign = _mm_and_ps(x, g_XMNegativeZero); + __m128 c = _mm_or_ps(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + __m128 absx = _mm_andnot_ps(sign, x); // |x| + __m128 rflx = _mm_sub_ps(c, x); + __m128 comp = _mm_cmple_ps(absx, g_XMHalfPi); + __m128 select0 = _mm_and_ps(comp, x); + __m128 select1 = _mm_andnot_ps(comp, rflx); + x = _mm_or_ps(select0, select1); + select0 = _mm_and_ps(comp, g_XMOne); + select1 = _mm_andnot_ps(comp, g_XMNegativeOne); + sign = _mm_or_ps(select0, select1); + + __m128 x2 = _mm_mul_ps(x, x); + + // Compute polynomial approximation + const XMVECTOR CC1 = g_XMCosCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(CC1, _MM_SHUFFLE(0, 0, 0, 0)); + const XMVECTOR CC0 = g_XMCosCoefficients0; + __m128 vConstants = XM_PERMUTE_PS(CC0, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(CC0, _MM_SHUFFLE(2, 2, 2, 2)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(CC0, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(CC0, _MM_SHUFFLE(0, 0, 0, 0)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + Result = XM_FMADD_PS(Result, x2, g_XMOne); + Result = _mm_mul_ps(Result, sign); + return Result; +#endif +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorSinCos +( + XMVECTOR* pSin, + XMVECTOR* pCos, + FXMVECTOR V +) noexcept +{ + assert(pSin != nullptr); + assert(pCos != nullptr); + + // 11/10-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Sin = { { { + sinf(V.vector4_f32[0]), + sinf(V.vector4_f32[1]), + sinf(V.vector4_f32[2]), + sinf(V.vector4_f32[3]) + } } }; + + XMVECTORF32 Cos = { { { + cosf(V.vector4_f32[0]), + cosf(V.vector4_f32[1]), + cosf(V.vector4_f32[2]), + cosf(V.vector4_f32[3]) + } } }; + + *pSin = Sin.v; + *pCos = Cos.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Force the value within the bounds of pi + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with cos(y) = sign*cos(x). + uint32x4_t sign = vandq_u32(vreinterpretq_u32_f32(x), g_XMNegativeZero); + uint32x4_t c = vorrq_u32(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + float32x4_t absx = vabsq_f32(x); + float32x4_t rflx = vsubq_f32(vreinterpretq_f32_u32(c), x); + uint32x4_t comp = vcleq_f32(absx, g_XMHalfPi); + x = vbslq_f32(comp, x, rflx); + float32x4_t fsign = vbslq_f32(comp, g_XMOne, g_XMNegativeOne); + + float32x4_t x2 = vmulq_f32(x, x); + + // Compute polynomial approximation for sine + const XMVECTOR SC1 = g_XMSinCoefficients1; + const XMVECTOR SC0 = g_XMSinCoefficients0; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(SC0), 1); + XMVECTOR Result = vmlaq_lane_f32(vConstants, x2, vget_low_f32(SC1), 0); + + vConstants = vdupq_lane_f32(vget_high_f32(SC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(SC0), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(SC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + Result = vmlaq_f32(g_XMOne, Result, x2); + *pSin = vmulq_f32(Result, x); + + // Compute polynomial approximation for cosine + const XMVECTOR CC1 = g_XMCosCoefficients1; + const XMVECTOR CC0 = g_XMCosCoefficients0; + vConstants = vdupq_lane_f32(vget_high_f32(CC0), 1); + Result = vmlaq_lane_f32(vConstants, x2, vget_low_f32(CC1), 0); + + vConstants = vdupq_lane_f32(vget_high_f32(CC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(CC0), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(CC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + Result = vmlaq_f32(g_XMOne, Result, x2); + *pCos = vmulq_f32(Result, fsign); +#elif defined(_XM_SVML_INTRINSICS_) + *pSin = _mm_sincos_ps(pCos, V); +#elif defined(_XM_SSE_INTRINSICS_) + // Force the value within the bounds of pi + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with sin(y) = sin(x), cos(y) = sign*cos(x). + XMVECTOR sign = _mm_and_ps(x, g_XMNegativeZero); + __m128 c = _mm_or_ps(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + __m128 absx = _mm_andnot_ps(sign, x); // |x| + __m128 rflx = _mm_sub_ps(c, x); + __m128 comp = _mm_cmple_ps(absx, g_XMHalfPi); + __m128 select0 = _mm_and_ps(comp, x); + __m128 select1 = _mm_andnot_ps(comp, rflx); + x = _mm_or_ps(select0, select1); + select0 = _mm_and_ps(comp, g_XMOne); + select1 = _mm_andnot_ps(comp, g_XMNegativeOne); + sign = _mm_or_ps(select0, select1); + + __m128 x2 = _mm_mul_ps(x, x); + + // Compute polynomial approximation of sine + const XMVECTOR SC1 = g_XMSinCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(SC1, _MM_SHUFFLE(0, 0, 0, 0)); + const XMVECTOR SC0 = g_XMSinCoefficients0; + __m128 vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(2, 2, 2, 2)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(0, 0, 0, 0)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + Result = XM_FMADD_PS(Result, x2, g_XMOne); + Result = _mm_mul_ps(Result, x); + *pSin = Result; + + // Compute polynomial approximation of cosine + const XMVECTOR CC1 = g_XMCosCoefficients1; + vConstantsB = XM_PERMUTE_PS(CC1, _MM_SHUFFLE(0, 0, 0, 0)); + const XMVECTOR CC0 = g_XMCosCoefficients0; + vConstants = XM_PERMUTE_PS(CC0, _MM_SHUFFLE(3, 3, 3, 3)); + Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(CC0, _MM_SHUFFLE(2, 2, 2, 2)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(CC0, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(CC0, _MM_SHUFFLE(0, 0, 0, 0)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + Result = XM_FMADD_PS(Result, x2, g_XMOne); + Result = _mm_mul_ps(Result, sign); + *pCos = Result; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorTan(FXMVECTOR V) noexcept +{ + // Cody and Waite algorithm to compute tangent. + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + tanf(V.vector4_f32[0]), + tanf(V.vector4_f32[1]), + tanf(V.vector4_f32[2]), + tanf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_tan_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + + static const XMVECTORF32 TanCoefficients0 = { { { 1.0f, -4.667168334e-1f, 2.566383229e-2f, -3.118153191e-4f } } }; + static const XMVECTORF32 TanCoefficients1 = { { { 4.981943399e-7f, -1.333835001e-1f, 3.424887824e-3f, -1.786170734e-5f } } }; + static const XMVECTORF32 TanConstants = { { { 1.570796371f, 6.077100628e-11f, 0.000244140625f, 0.63661977228f /*2 / Pi*/ } } }; + static const XMVECTORU32 Mask = { { { 0x1, 0x1, 0x1, 0x1 } } }; + + XMVECTOR TwoDivPi = XMVectorSplatW(TanConstants.v); + + XMVECTOR Zero = XMVectorZero(); + + XMVECTOR C0 = XMVectorSplatX(TanConstants.v); + XMVECTOR C1 = XMVectorSplatY(TanConstants.v); + XMVECTOR Epsilon = XMVectorSplatZ(TanConstants.v); + + XMVECTOR VA = XMVectorMultiply(V, TwoDivPi); + + VA = XMVectorRound(VA); + + XMVECTOR VC = XMVectorNegativeMultiplySubtract(VA, C0, V); + + XMVECTOR VB = XMVectorAbs(VA); + + VC = XMVectorNegativeMultiplySubtract(VA, C1, VC); + +#if defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + VB = vreinterpretq_f32_u32(vcvtq_u32_f32(VB)); +#elif defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + reinterpret_cast<__m128i*>(&VB)[0] = _mm_cvttps_epi32(VB); +#else + for (size_t i = 0; i < 4; i++) + { + VB.vector4_u32[i] = static_cast(VB.vector4_f32[i]); + } +#endif + + XMVECTOR VC2 = XMVectorMultiply(VC, VC); + + XMVECTOR T7 = XMVectorSplatW(TanCoefficients1.v); + XMVECTOR T6 = XMVectorSplatZ(TanCoefficients1.v); + XMVECTOR T4 = XMVectorSplatX(TanCoefficients1.v); + XMVECTOR T3 = XMVectorSplatW(TanCoefficients0.v); + XMVECTOR T5 = XMVectorSplatY(TanCoefficients1.v); + XMVECTOR T2 = XMVectorSplatZ(TanCoefficients0.v); + XMVECTOR T1 = XMVectorSplatY(TanCoefficients0.v); + XMVECTOR T0 = XMVectorSplatX(TanCoefficients0.v); + + XMVECTOR VBIsEven = XMVectorAndInt(VB, Mask.v); + VBIsEven = XMVectorEqualInt(VBIsEven, Zero); + + XMVECTOR N = XMVectorMultiplyAdd(VC2, T7, T6); + XMVECTOR D = XMVectorMultiplyAdd(VC2, T4, T3); + N = XMVectorMultiplyAdd(VC2, N, T5); + D = XMVectorMultiplyAdd(VC2, D, T2); + N = XMVectorMultiply(VC2, N); + D = XMVectorMultiplyAdd(VC2, D, T1); + N = XMVectorMultiplyAdd(VC, N, VC); + XMVECTOR VCNearZero = XMVectorInBounds(VC, Epsilon); + D = XMVectorMultiplyAdd(VC2, D, T0); + + N = XMVectorSelect(N, VC, VCNearZero); + D = XMVectorSelect(D, g_XMOne.v, VCNearZero); + + XMVECTOR R0 = XMVectorNegate(N); + XMVECTOR R1 = XMVectorDivide(N, D); + R0 = XMVectorDivide(D, R0); + + XMVECTOR VIsZero = XMVectorEqual(V, Zero); + + XMVECTOR Result = XMVectorSelect(R0, R1, VBIsEven); + + Result = XMVectorSelect(Result, Zero, VIsZero); + + return Result; + +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSinH(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + sinhf(V.vector4_f32[0]), + sinhf(V.vector4_f32[1]), + sinhf(V.vector4_f32[2]), + sinhf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.442695040888963f, 1.442695040888963f, 1.442695040888963f, 1.442695040888963f } } }; // 1.0f / ln(2.0f) + + XMVECTOR V1 = vmlaq_f32(g_XMNegativeOne.v, V, Scale.v); + XMVECTOR V2 = vmlsq_f32(g_XMNegativeOne.v, V, Scale.v); + XMVECTOR E1 = XMVectorExp(V1); + XMVECTOR E2 = XMVectorExp(V2); + + return vsubq_f32(E1, E2); +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_sinh_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.442695040888963f, 1.442695040888963f, 1.442695040888963f, 1.442695040888963f } } }; // 1.0f / ln(2.0f) + + XMVECTOR V1 = XM_FMADD_PS(V, Scale, g_XMNegativeOne); + XMVECTOR V2 = XM_FNMADD_PS(V, Scale, g_XMNegativeOne); + XMVECTOR E1 = XMVectorExp(V1); + XMVECTOR E2 = XMVectorExp(V2); + + return _mm_sub_ps(E1, E2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorCosH(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + coshf(V.vector4_f32[0]), + coshf(V.vector4_f32[1]), + coshf(V.vector4_f32[2]), + coshf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.442695040888963f, 1.442695040888963f, 1.442695040888963f, 1.442695040888963f } } }; // 1.0f / ln(2.0f) + + XMVECTOR V1 = vmlaq_f32(g_XMNegativeOne.v, V, Scale.v); + XMVECTOR V2 = vmlsq_f32(g_XMNegativeOne.v, V, Scale.v); + XMVECTOR E1 = XMVectorExp(V1); + XMVECTOR E2 = XMVectorExp(V2); + return vaddq_f32(E1, E2); +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_cosh_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.442695040888963f, 1.442695040888963f, 1.442695040888963f, 1.442695040888963f } } }; // 1.0f / ln(2.0f) + + XMVECTOR V1 = XM_FMADD_PS(V, Scale.v, g_XMNegativeOne.v); + XMVECTOR V2 = XM_FNMADD_PS(V, Scale.v, g_XMNegativeOne.v); + XMVECTOR E1 = XMVectorExp(V1); + XMVECTOR E2 = XMVectorExp(V2); + return _mm_add_ps(E1, E2); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorTanH(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + tanhf(V.vector4_f32[0]), + tanhf(V.vector4_f32[1]), + tanhf(V.vector4_f32[2]), + tanhf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 2.8853900817779268f, 2.8853900817779268f, 2.8853900817779268f, 2.8853900817779268f } } }; // 2.0f / ln(2.0f) + + XMVECTOR E = vmulq_f32(V, Scale.v); + E = XMVectorExp(E); + E = vmlaq_f32(g_XMOneHalf.v, E, g_XMOneHalf.v); + E = XMVectorReciprocal(E); + return vsubq_f32(g_XMOne.v, E); +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_tanh_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 2.8853900817779268f, 2.8853900817779268f, 2.8853900817779268f, 2.8853900817779268f } } }; // 2.0f / ln(2.0f) + + XMVECTOR E = _mm_mul_ps(V, Scale.v); + E = XMVectorExp(E); + E = XM_FMADD_PS(E, g_XMOneHalf.v, g_XMOneHalf.v); + E = _mm_div_ps(g_XMOne.v, E); + return _mm_sub_ps(g_XMOne.v, E); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorASin(FXMVECTOR V) noexcept +{ + // 7-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + asinf(V.vector4_f32[0]), + asinf(V.vector4_f32[1]), + asinf(V.vector4_f32[2]), + asinf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t nonnegative = vcgeq_f32(V, g_XMZero); + float32x4_t x = vabsq_f32(V); + + // Compute (1-|V|), clamp to zero to avoid sqrt of negative number. + float32x4_t oneMValue = vsubq_f32(g_XMOne, x); + float32x4_t clampOneMValue = vmaxq_f32(g_XMZero, oneMValue); + float32x4_t root = XMVectorSqrt(clampOneMValue); + + // Compute polynomial approximation + const XMVECTOR AC1 = g_XMArcCoefficients1; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(AC1), 0); + XMVECTOR t0 = vmlaq_lane_f32(vConstants, x, vget_high_f32(AC1), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(AC1), 1); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_low_f32(AC1), 0); + t0 = vmlaq_f32(vConstants, t0, x); + + const XMVECTOR AC0 = g_XMArcCoefficients0; + vConstants = vdupq_lane_f32(vget_high_f32(AC0), 1); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_high_f32(AC0), 0); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_low_f32(AC0), 1); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_low_f32(AC0), 0); + t0 = vmlaq_f32(vConstants, t0, x); + t0 = vmulq_f32(t0, root); + + float32x4_t t1 = vsubq_f32(g_XMPi, t0); + t0 = vbslq_f32(nonnegative, t0, t1); + t0 = vsubq_f32(g_XMHalfPi, t0); + return t0; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_asin_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128 nonnegative = _mm_cmpge_ps(V, g_XMZero); + __m128 mvalue = _mm_sub_ps(g_XMZero, V); + __m128 x = _mm_max_ps(V, mvalue); // |V| + + // Compute (1-|V|), clamp to zero to avoid sqrt of negative number. + __m128 oneMValue = _mm_sub_ps(g_XMOne, x); + __m128 clampOneMValue = _mm_max_ps(g_XMZero, oneMValue); + __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|V|) + + // Compute polynomial approximation + const XMVECTOR AC1 = g_XMArcCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(AC1, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vConstants = XM_PERMUTE_PS(AC1, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 t0 = XM_FMADD_PS(vConstantsB, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC1, _MM_SHUFFLE(1, 1, 1, 1)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC1, _MM_SHUFFLE(0, 0, 0, 0)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + const XMVECTOR AC0 = g_XMArcCoefficients0; + vConstants = XM_PERMUTE_PS(AC0, _MM_SHUFFLE(3, 3, 3, 3)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC0, _MM_SHUFFLE(2, 2, 2, 2)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC0, _MM_SHUFFLE(1, 1, 1, 1)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC0, _MM_SHUFFLE(0, 0, 0, 0)); + t0 = XM_FMADD_PS(t0, x, vConstants); + t0 = _mm_mul_ps(t0, root); + + __m128 t1 = _mm_sub_ps(g_XMPi, t0); + t0 = _mm_and_ps(nonnegative, t0); + t1 = _mm_andnot_ps(nonnegative, t1); + t0 = _mm_or_ps(t0, t1); + t0 = _mm_sub_ps(g_XMHalfPi, t0); + return t0; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorACos(FXMVECTOR V) noexcept +{ + // 7-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + acosf(V.vector4_f32[0]), + acosf(V.vector4_f32[1]), + acosf(V.vector4_f32[2]), + acosf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t nonnegative = vcgeq_f32(V, g_XMZero); + float32x4_t x = vabsq_f32(V); + + // Compute (1-|V|), clamp to zero to avoid sqrt of negative number. + float32x4_t oneMValue = vsubq_f32(g_XMOne, x); + float32x4_t clampOneMValue = vmaxq_f32(g_XMZero, oneMValue); + float32x4_t root = XMVectorSqrt(clampOneMValue); + + // Compute polynomial approximation + const XMVECTOR AC1 = g_XMArcCoefficients1; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(AC1), 0); + XMVECTOR t0 = vmlaq_lane_f32(vConstants, x, vget_high_f32(AC1), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(AC1), 1); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_low_f32(AC1), 0); + t0 = vmlaq_f32(vConstants, t0, x); + + const XMVECTOR AC0 = g_XMArcCoefficients0; + vConstants = vdupq_lane_f32(vget_high_f32(AC0), 1); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_high_f32(AC0), 0); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_low_f32(AC0), 1); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_low_f32(AC0), 0); + t0 = vmlaq_f32(vConstants, t0, x); + t0 = vmulq_f32(t0, root); + + float32x4_t t1 = vsubq_f32(g_XMPi, t0); + t0 = vbslq_f32(nonnegative, t0, t1); + return t0; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_acos_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128 nonnegative = _mm_cmpge_ps(V, g_XMZero); + __m128 mvalue = _mm_sub_ps(g_XMZero, V); + __m128 x = _mm_max_ps(V, mvalue); // |V| + + // Compute (1-|V|), clamp to zero to avoid sqrt of negative number. + __m128 oneMValue = _mm_sub_ps(g_XMOne, x); + __m128 clampOneMValue = _mm_max_ps(g_XMZero, oneMValue); + __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|V|) + + // Compute polynomial approximation + const XMVECTOR AC1 = g_XMArcCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(AC1, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vConstants = XM_PERMUTE_PS(AC1, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 t0 = XM_FMADD_PS(vConstantsB, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC1, _MM_SHUFFLE(1, 1, 1, 1)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC1, _MM_SHUFFLE(0, 0, 0, 0)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + const XMVECTOR AC0 = g_XMArcCoefficients0; + vConstants = XM_PERMUTE_PS(AC0, _MM_SHUFFLE(3, 3, 3, 3)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC0, _MM_SHUFFLE(2, 2, 2, 2)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC0, _MM_SHUFFLE(1, 1, 1, 1)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AC0, _MM_SHUFFLE(0, 0, 0, 0)); + t0 = XM_FMADD_PS(t0, x, vConstants); + t0 = _mm_mul_ps(t0, root); + + __m128 t1 = _mm_sub_ps(g_XMPi, t0); + t0 = _mm_and_ps(nonnegative, t0); + t1 = _mm_andnot_ps(nonnegative, t1); + t0 = _mm_or_ps(t0, t1); + return t0; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorATan(FXMVECTOR V) noexcept +{ + // 17-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + atanf(V.vector4_f32[0]), + atanf(V.vector4_f32[1]), + atanf(V.vector4_f32[2]), + atanf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t absV = vabsq_f32(V); + float32x4_t invV = XMVectorReciprocal(V); + uint32x4_t comp = vcgtq_f32(V, g_XMOne); + float32x4_t sign = vbslq_f32(comp, g_XMOne, g_XMNegativeOne); + comp = vcleq_f32(absV, g_XMOne); + sign = vbslq_f32(comp, g_XMZero, sign); + float32x4_t x = vbslq_f32(comp, V, invV); + + float32x4_t x2 = vmulq_f32(x, x); + + // Compute polynomial approximation + const XMVECTOR TC1 = g_XMATanCoefficients1; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(TC1), 0); + XMVECTOR Result = vmlaq_lane_f32(vConstants, x2, vget_high_f32(TC1), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(TC1), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(TC1), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + const XMVECTOR TC0 = g_XMATanCoefficients0; + vConstants = vdupq_lane_f32(vget_high_f32(TC0), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_high_f32(TC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(TC0), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(TC0), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + Result = vmlaq_f32(g_XMOne, Result, x2); + Result = vmulq_f32(Result, x); + + float32x4_t result1 = vmulq_f32(sign, g_XMHalfPi); + result1 = vsubq_f32(result1, Result); + + comp = vceqq_f32(sign, g_XMZero); + Result = vbslq_f32(comp, Result, result1); + return Result; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_atan_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128 absV = XMVectorAbs(V); + __m128 invV = _mm_div_ps(g_XMOne, V); + __m128 comp = _mm_cmpgt_ps(V, g_XMOne); + __m128 select0 = _mm_and_ps(comp, g_XMOne); + __m128 select1 = _mm_andnot_ps(comp, g_XMNegativeOne); + __m128 sign = _mm_or_ps(select0, select1); + comp = _mm_cmple_ps(absV, g_XMOne); + select0 = _mm_and_ps(comp, g_XMZero); + select1 = _mm_andnot_ps(comp, sign); + sign = _mm_or_ps(select0, select1); + select0 = _mm_and_ps(comp, V); + select1 = _mm_andnot_ps(comp, invV); + __m128 x = _mm_or_ps(select0, select1); + + __m128 x2 = _mm_mul_ps(x, x); + + // Compute polynomial approximation + const XMVECTOR TC1 = g_XMATanCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(TC1, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vConstants = XM_PERMUTE_PS(TC1, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(TC1, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(TC1, _MM_SHUFFLE(0, 0, 0, 0)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + const XMVECTOR TC0 = g_XMATanCoefficients0; + vConstants = XM_PERMUTE_PS(TC0, _MM_SHUFFLE(3, 3, 3, 3)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(TC0, _MM_SHUFFLE(2, 2, 2, 2)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(TC0, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(TC0, _MM_SHUFFLE(0, 0, 0, 0)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + Result = XM_FMADD_PS(Result, x2, g_XMOne); + + Result = _mm_mul_ps(Result, x); + __m128 result1 = _mm_mul_ps(sign, g_XMHalfPi); + result1 = _mm_sub_ps(result1, Result); + + comp = _mm_cmpeq_ps(sign, g_XMZero); + select0 = _mm_and_ps(comp, Result); + select1 = _mm_andnot_ps(comp, result1); + Result = _mm_or_ps(select0, select1); + return Result; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorATan2 +( + FXMVECTOR Y, + FXMVECTOR X +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + atan2f(Y.vector4_f32[0], X.vector4_f32[0]), + atan2f(Y.vector4_f32[1], X.vector4_f32[1]), + atan2f(Y.vector4_f32[2], X.vector4_f32[2]), + atan2f(Y.vector4_f32[3], X.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_atan2_ps(Y, X); + return Result; +#else + + // Return the inverse tangent of Y / X in the range of -Pi to Pi with the following exceptions: + + // Y == 0 and X is Negative -> Pi with the sign of Y + // y == 0 and x is positive -> 0 with the sign of y + // Y != 0 and X == 0 -> Pi / 2 with the sign of Y + // Y != 0 and X is Negative -> atan(y/x) + (PI with the sign of Y) + // X == -Infinity and Finite Y -> Pi with the sign of Y + // X == +Infinity and Finite Y -> 0 with the sign of Y + // Y == Infinity and X is Finite -> Pi / 2 with the sign of Y + // Y == Infinity and X == -Infinity -> 3Pi / 4 with the sign of Y + // Y == Infinity and X == +Infinity -> Pi / 4 with the sign of Y + + static const XMVECTORF32 ATan2Constants = { { { XM_PI, XM_PIDIV2, XM_PIDIV4, XM_PI * 3.0f / 4.0f } } }; + + XMVECTOR Zero = XMVectorZero(); + XMVECTOR ATanResultValid = XMVectorTrueInt(); + + XMVECTOR Pi = XMVectorSplatX(ATan2Constants); + XMVECTOR PiOverTwo = XMVectorSplatY(ATan2Constants); + XMVECTOR PiOverFour = XMVectorSplatZ(ATan2Constants); + XMVECTOR ThreePiOverFour = XMVectorSplatW(ATan2Constants); + + XMVECTOR YEqualsZero = XMVectorEqual(Y, Zero); + XMVECTOR XEqualsZero = XMVectorEqual(X, Zero); + XMVECTOR XIsPositive = XMVectorAndInt(X, g_XMNegativeZero.v); + XIsPositive = XMVectorEqualInt(XIsPositive, Zero); + XMVECTOR YEqualsInfinity = XMVectorIsInfinite(Y); + XMVECTOR XEqualsInfinity = XMVectorIsInfinite(X); + + XMVECTOR YSign = XMVectorAndInt(Y, g_XMNegativeZero.v); + Pi = XMVectorOrInt(Pi, YSign); + PiOverTwo = XMVectorOrInt(PiOverTwo, YSign); + PiOverFour = XMVectorOrInt(PiOverFour, YSign); + ThreePiOverFour = XMVectorOrInt(ThreePiOverFour, YSign); + + XMVECTOR R1 = XMVectorSelect(Pi, YSign, XIsPositive); + XMVECTOR R2 = XMVectorSelect(ATanResultValid, PiOverTwo, XEqualsZero); + XMVECTOR R3 = XMVectorSelect(R2, R1, YEqualsZero); + XMVECTOR R4 = XMVectorSelect(ThreePiOverFour, PiOverFour, XIsPositive); + XMVECTOR R5 = XMVectorSelect(PiOverTwo, R4, XEqualsInfinity); + XMVECTOR Result = XMVectorSelect(R3, R5, YEqualsInfinity); + ATanResultValid = XMVectorEqualInt(Result, ATanResultValid); + + XMVECTOR V = XMVectorDivide(Y, X); + + XMVECTOR R0 = XMVectorATan(V); + + R1 = XMVectorSelect(Pi, g_XMNegativeZero, XIsPositive); + R2 = XMVectorAdd(R0, R1); + + return XMVectorSelect(Result, R2, ATanResultValid); + +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorSinEst(FXMVECTOR V) noexcept +{ + // 7-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + sinf(V.vector4_f32[0]), + sinf(V.vector4_f32[1]), + sinf(V.vector4_f32[2]), + sinf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Force the value within the bounds of pi + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with sin(y) = sin(x). + uint32x4_t sign = vandq_u32(vreinterpretq_u32_f32(x), g_XMNegativeZero); + uint32x4_t c = vorrq_u32(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + float32x4_t absx = vabsq_f32(x); + float32x4_t rflx = vsubq_f32(vreinterpretq_f32_u32(c), x); + uint32x4_t comp = vcleq_f32(absx, g_XMHalfPi); + x = vbslq_f32(comp, x, rflx); + + float32x4_t x2 = vmulq_f32(x, x); + + // Compute polynomial approximation + const XMVECTOR SEC = g_XMSinCoefficients1; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(SEC), 0); + XMVECTOR Result = vmlaq_lane_f32(vConstants, x2, vget_high_f32(SEC), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(SEC), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + Result = vmlaq_f32(g_XMOne, Result, x2); + Result = vmulq_f32(Result, x); + return Result; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_sin_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + // Force the value within the bounds of pi + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with sin(y) = sin(x). + __m128 sign = _mm_and_ps(x, g_XMNegativeZero); + __m128 c = _mm_or_ps(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + __m128 absx = _mm_andnot_ps(sign, x); // |x| + __m128 rflx = _mm_sub_ps(c, x); + __m128 comp = _mm_cmple_ps(absx, g_XMHalfPi); + __m128 select0 = _mm_and_ps(comp, x); + __m128 select1 = _mm_andnot_ps(comp, rflx); + x = _mm_or_ps(select0, select1); + + __m128 x2 = _mm_mul_ps(x, x); + + // Compute polynomial approximation + const XMVECTOR SEC = g_XMSinCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(SEC, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vConstants = XM_PERMUTE_PS(SEC, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SEC, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + Result = XM_FMADD_PS(Result, x2, g_XMOne); + Result = _mm_mul_ps(Result, x); + return Result; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorCosEst(FXMVECTOR V) noexcept +{ + // 6-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + cosf(V.vector4_f32[0]), + cosf(V.vector4_f32[1]), + cosf(V.vector4_f32[2]), + cosf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Map V to x in [-pi,pi]. + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with cos(y) = sign*cos(x). + uint32x4_t sign = vandq_u32(vreinterpretq_u32_f32(x), g_XMNegativeZero); + uint32x4_t c = vorrq_u32(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + float32x4_t absx = vabsq_f32(x); + float32x4_t rflx = vsubq_f32(vreinterpretq_f32_u32(c), x); + uint32x4_t comp = vcleq_f32(absx, g_XMHalfPi); + x = vbslq_f32(comp, x, rflx); + float32x4_t fsign = vbslq_f32(comp, g_XMOne, g_XMNegativeOne); + + float32x4_t x2 = vmulq_f32(x, x); + + // Compute polynomial approximation + const XMVECTOR CEC = g_XMCosCoefficients1; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(CEC), 0); + XMVECTOR Result = vmlaq_lane_f32(vConstants, x2, vget_high_f32(CEC), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(CEC), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + Result = vmlaq_f32(g_XMOne, Result, x2); + Result = vmulq_f32(Result, fsign); + return Result; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_cos_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + // Map V to x in [-pi,pi]. + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with cos(y) = sign*cos(x). + XMVECTOR sign = _mm_and_ps(x, g_XMNegativeZero); + __m128 c = _mm_or_ps(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + __m128 absx = _mm_andnot_ps(sign, x); // |x| + __m128 rflx = _mm_sub_ps(c, x); + __m128 comp = _mm_cmple_ps(absx, g_XMHalfPi); + __m128 select0 = _mm_and_ps(comp, x); + __m128 select1 = _mm_andnot_ps(comp, rflx); + x = _mm_or_ps(select0, select1); + select0 = _mm_and_ps(comp, g_XMOne); + select1 = _mm_andnot_ps(comp, g_XMNegativeOne); + sign = _mm_or_ps(select0, select1); + + __m128 x2 = _mm_mul_ps(x, x); + + // Compute polynomial approximation + const XMVECTOR CEC = g_XMCosCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(CEC, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vConstants = XM_PERMUTE_PS(CEC, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(CEC, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + Result = XM_FMADD_PS(Result, x2, g_XMOne); + Result = _mm_mul_ps(Result, sign); + return Result; +#endif +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline void XM_CALLCONV XMVectorSinCosEst +( + XMVECTOR* pSin, + XMVECTOR* pCos, + FXMVECTOR V +) noexcept +{ + assert(pSin != nullptr); + assert(pCos != nullptr); + + // 7/6-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Sin = { { { + sinf(V.vector4_f32[0]), + sinf(V.vector4_f32[1]), + sinf(V.vector4_f32[2]), + sinf(V.vector4_f32[3]) + } } }; + + XMVECTORF32 Cos = { { { + cosf(V.vector4_f32[0]), + cosf(V.vector4_f32[1]), + cosf(V.vector4_f32[2]), + cosf(V.vector4_f32[3]) + } } }; + + *pSin = Sin.v; + *pCos = Cos.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Force the value within the bounds of pi + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with cos(y) = sign*cos(x). + uint32x4_t sign = vandq_u32(vreinterpretq_u32_f32(x), g_XMNegativeZero); + uint32x4_t c = vorrq_u32(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + float32x4_t absx = vabsq_f32(x); + float32x4_t rflx = vsubq_f32(vreinterpretq_f32_u32(c), x); + uint32x4_t comp = vcleq_f32(absx, g_XMHalfPi); + x = vbslq_f32(comp, x, rflx); + float32x4_t fsign = vbslq_f32(comp, g_XMOne, g_XMNegativeOne); + + float32x4_t x2 = vmulq_f32(x, x); + + // Compute polynomial approximation for sine + const XMVECTOR SEC = g_XMSinCoefficients1; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(SEC), 0); + XMVECTOR Result = vmlaq_lane_f32(vConstants, x2, vget_high_f32(SEC), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(SEC), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + Result = vmlaq_f32(g_XMOne, Result, x2); + *pSin = vmulq_f32(Result, x); + + // Compute polynomial approximation + const XMVECTOR CEC = g_XMCosCoefficients1; + vConstants = vdupq_lane_f32(vget_high_f32(CEC), 0); + Result = vmlaq_lane_f32(vConstants, x2, vget_high_f32(CEC), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(CEC), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + Result = vmlaq_f32(g_XMOne, Result, x2); + *pCos = vmulq_f32(Result, fsign); +#elif defined(_XM_SSE_INTRINSICS_) + // Force the value within the bounds of pi + XMVECTOR x = XMVectorModAngles(V); + + // Map in [-pi/2,pi/2] with sin(y) = sin(x), cos(y) = sign*cos(x). + XMVECTOR sign = _mm_and_ps(x, g_XMNegativeZero); + __m128 c = _mm_or_ps(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + __m128 absx = _mm_andnot_ps(sign, x); // |x| + __m128 rflx = _mm_sub_ps(c, x); + __m128 comp = _mm_cmple_ps(absx, g_XMHalfPi); + __m128 select0 = _mm_and_ps(comp, x); + __m128 select1 = _mm_andnot_ps(comp, rflx); + x = _mm_or_ps(select0, select1); + select0 = _mm_and_ps(comp, g_XMOne); + select1 = _mm_andnot_ps(comp, g_XMNegativeOne); + sign = _mm_or_ps(select0, select1); + + __m128 x2 = _mm_mul_ps(x, x); + + // Compute polynomial approximation for sine + const XMVECTOR SEC = g_XMSinCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(SEC, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vConstants = XM_PERMUTE_PS(SEC, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SEC, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + Result = XM_FMADD_PS(Result, x2, g_XMOne); + Result = _mm_mul_ps(Result, x); + *pSin = Result; + + // Compute polynomial approximation for cosine + const XMVECTOR CEC = g_XMCosCoefficients1; + vConstantsB = XM_PERMUTE_PS(CEC, _MM_SHUFFLE(3, 3, 3, 3)); + vConstants = XM_PERMUTE_PS(CEC, _MM_SHUFFLE(2, 2, 2, 2)); + Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(CEC, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + Result = XM_FMADD_PS(Result, x2, g_XMOne); + Result = _mm_mul_ps(Result, sign); + *pCos = Result; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorTanEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + tanf(V.vector4_f32[0]), + tanf(V.vector4_f32[1]), + tanf(V.vector4_f32[2]), + tanf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_tan_ps(V); + return Result; +#else + + XMVECTOR OneOverPi = XMVectorSplatW(g_XMTanEstCoefficients.v); + + XMVECTOR V1 = XMVectorMultiply(V, OneOverPi); + V1 = XMVectorRound(V1); + + V1 = XMVectorNegativeMultiplySubtract(g_XMPi.v, V1, V); + + XMVECTOR T0 = XMVectorSplatX(g_XMTanEstCoefficients.v); + XMVECTOR T1 = XMVectorSplatY(g_XMTanEstCoefficients.v); + XMVECTOR T2 = XMVectorSplatZ(g_XMTanEstCoefficients.v); + + XMVECTOR V2T2 = XMVectorNegativeMultiplySubtract(V1, V1, T2); + XMVECTOR V2 = XMVectorMultiply(V1, V1); + XMVECTOR V1T0 = XMVectorMultiply(V1, T0); + XMVECTOR V1T1 = XMVectorMultiply(V1, T1); + + XMVECTOR D = XMVectorReciprocalEst(V2T2); + XMVECTOR N = XMVectorMultiplyAdd(V2, V1T1, V1T0); + + return XMVectorMultiply(N, D); + +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorASinEst(FXMVECTOR V) noexcept +{ + // 3-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result; + Result.f[0] = asinf(V.vector4_f32[0]); + Result.f[1] = asinf(V.vector4_f32[1]); + Result.f[2] = asinf(V.vector4_f32[2]); + Result.f[3] = asinf(V.vector4_f32[3]); + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t nonnegative = vcgeq_f32(V, g_XMZero); + float32x4_t x = vabsq_f32(V); + + // Compute (1-|V|), clamp to zero to avoid sqrt of negative number. + float32x4_t oneMValue = vsubq_f32(g_XMOne, x); + float32x4_t clampOneMValue = vmaxq_f32(g_XMZero, oneMValue); + float32x4_t root = XMVectorSqrt(clampOneMValue); + + // Compute polynomial approximation + const XMVECTOR AEC = g_XMArcEstCoefficients; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(AEC), 0); + XMVECTOR t0 = vmlaq_lane_f32(vConstants, x, vget_high_f32(AEC), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(AEC), 1); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_low_f32(AEC), 0); + t0 = vmlaq_f32(vConstants, t0, x); + t0 = vmulq_f32(t0, root); + + float32x4_t t1 = vsubq_f32(g_XMPi, t0); + t0 = vbslq_f32(nonnegative, t0, t1); + t0 = vsubq_f32(g_XMHalfPi, t0); + return t0; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_asin_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128 nonnegative = _mm_cmpge_ps(V, g_XMZero); + __m128 mvalue = _mm_sub_ps(g_XMZero, V); + __m128 x = _mm_max_ps(V, mvalue); // |V| + + // Compute (1-|V|), clamp to zero to avoid sqrt of negative number. + __m128 oneMValue = _mm_sub_ps(g_XMOne, x); + __m128 clampOneMValue = _mm_max_ps(g_XMZero, oneMValue); + __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|V|) + + // Compute polynomial approximation + const XMVECTOR AEC = g_XMArcEstCoefficients; + __m128 vConstantsB = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vConstants = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 t0 = XM_FMADD_PS(vConstantsB, x, vConstants); + + vConstants = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(1, 1, 1, 1)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(0, 0, 0, 0)); + t0 = XM_FMADD_PS(t0, x, vConstants); + t0 = _mm_mul_ps(t0, root); + + __m128 t1 = _mm_sub_ps(g_XMPi, t0); + t0 = _mm_and_ps(nonnegative, t0); + t1 = _mm_andnot_ps(nonnegative, t1); + t0 = _mm_or_ps(t0, t1); + t0 = _mm_sub_ps(g_XMHalfPi, t0); + return t0; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorACosEst(FXMVECTOR V) noexcept +{ + // 3-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + acosf(V.vector4_f32[0]), + acosf(V.vector4_f32[1]), + acosf(V.vector4_f32[2]), + acosf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t nonnegative = vcgeq_f32(V, g_XMZero); + float32x4_t x = vabsq_f32(V); + + // Compute (1-|V|), clamp to zero to avoid sqrt of negative number. + float32x4_t oneMValue = vsubq_f32(g_XMOne, x); + float32x4_t clampOneMValue = vmaxq_f32(g_XMZero, oneMValue); + float32x4_t root = XMVectorSqrt(clampOneMValue); + + // Compute polynomial approximation + const XMVECTOR AEC = g_XMArcEstCoefficients; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(AEC), 0); + XMVECTOR t0 = vmlaq_lane_f32(vConstants, x, vget_high_f32(AEC), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(AEC), 1); + t0 = vmlaq_f32(vConstants, t0, x); + + vConstants = vdupq_lane_f32(vget_low_f32(AEC), 0); + t0 = vmlaq_f32(vConstants, t0, x); + t0 = vmulq_f32(t0, root); + + float32x4_t t1 = vsubq_f32(g_XMPi, t0); + t0 = vbslq_f32(nonnegative, t0, t1); + return t0; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_acos_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128 nonnegative = _mm_cmpge_ps(V, g_XMZero); + __m128 mvalue = _mm_sub_ps(g_XMZero, V); + __m128 x = _mm_max_ps(V, mvalue); // |V| + + // Compute (1-|V|), clamp to zero to avoid sqrt of negative number. + __m128 oneMValue = _mm_sub_ps(g_XMOne, x); + __m128 clampOneMValue = _mm_max_ps(g_XMZero, oneMValue); + __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|V|) + + // Compute polynomial approximation + const XMVECTOR AEC = g_XMArcEstCoefficients; + __m128 vConstantsB = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vConstants = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 t0 = XM_FMADD_PS(vConstantsB, x, vConstants); + + vConstants = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(1, 1, 1, 1)); + t0 = XM_FMADD_PS(t0, x, vConstants); + + vConstants = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(0, 0, 0, 0)); + t0 = XM_FMADD_PS(t0, x, vConstants); + t0 = _mm_mul_ps(t0, root); + + __m128 t1 = _mm_sub_ps(g_XMPi, t0); + t0 = _mm_and_ps(nonnegative, t0); + t1 = _mm_andnot_ps(nonnegative, t1); + t0 = _mm_or_ps(t0, t1); + return t0; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorATanEst(FXMVECTOR V) noexcept +{ + // 9-degree minimax approximation + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + atanf(V.vector4_f32[0]), + atanf(V.vector4_f32[1]), + atanf(V.vector4_f32[2]), + atanf(V.vector4_f32[3]) + } } }; + return Result.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t absV = vabsq_f32(V); + float32x4_t invV = XMVectorReciprocalEst(V); + uint32x4_t comp = vcgtq_f32(V, g_XMOne); + float32x4_t sign = vbslq_f32(comp, g_XMOne, g_XMNegativeOne); + comp = vcleq_f32(absV, g_XMOne); + sign = vbslq_f32(comp, g_XMZero, sign); + float32x4_t x = vbslq_f32(comp, V, invV); + + float32x4_t x2 = vmulq_f32(x, x); + + // Compute polynomial approximation + const XMVECTOR AEC = g_XMATanEstCoefficients1; + XMVECTOR vConstants = vdupq_lane_f32(vget_high_f32(AEC), 0); + XMVECTOR Result = vmlaq_lane_f32(vConstants, x2, vget_high_f32(AEC), 1); + + vConstants = vdupq_lane_f32(vget_low_f32(AEC), 1); + Result = vmlaq_f32(vConstants, Result, x2); + + vConstants = vdupq_lane_f32(vget_low_f32(AEC), 0); + Result = vmlaq_f32(vConstants, Result, x2); + + // ATanEstCoefficients0 is already splatted + Result = vmlaq_f32(g_XMATanEstCoefficients0, Result, x2); + Result = vmulq_f32(Result, x); + + float32x4_t result1 = vmulq_f32(sign, g_XMHalfPi); + result1 = vsubq_f32(result1, Result); + + comp = vceqq_f32(sign, g_XMZero); + Result = vbslq_f32(comp, Result, result1); + return Result; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_atan_ps(V); + return Result; +#elif defined(_XM_SSE_INTRINSICS_) + __m128 absV = XMVectorAbs(V); + __m128 invV = _mm_div_ps(g_XMOne, V); + __m128 comp = _mm_cmpgt_ps(V, g_XMOne); + __m128 select0 = _mm_and_ps(comp, g_XMOne); + __m128 select1 = _mm_andnot_ps(comp, g_XMNegativeOne); + __m128 sign = _mm_or_ps(select0, select1); + comp = _mm_cmple_ps(absV, g_XMOne); + select0 = _mm_and_ps(comp, g_XMZero); + select1 = _mm_andnot_ps(comp, sign); + sign = _mm_or_ps(select0, select1); + select0 = _mm_and_ps(comp, V); + select1 = _mm_andnot_ps(comp, invV); + __m128 x = _mm_or_ps(select0, select1); + + __m128 x2 = _mm_mul_ps(x, x); + + // Compute polynomial approximation + const XMVECTOR AEC = g_XMATanEstCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vConstants = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Result = XM_FMADD_PS(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(1, 1, 1, 1)); + Result = XM_FMADD_PS(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(AEC, _MM_SHUFFLE(0, 0, 0, 0)); + Result = XM_FMADD_PS(Result, x2, vConstants); + // ATanEstCoefficients0 is already splatted + Result = XM_FMADD_PS(Result, x2, g_XMATanEstCoefficients0); + Result = _mm_mul_ps(Result, x); + __m128 result1 = _mm_mul_ps(sign, g_XMHalfPi); + result1 = _mm_sub_ps(result1, Result); + + comp = _mm_cmpeq_ps(sign, g_XMZero); + select0 = _mm_and_ps(comp, Result); + select1 = _mm_andnot_ps(comp, result1); + Result = _mm_or_ps(select0, select1); + return Result; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorATan2Est +( + FXMVECTOR Y, + FXMVECTOR X +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 Result = { { { + atan2f(Y.vector4_f32[0], X.vector4_f32[0]), + atan2f(Y.vector4_f32[1], X.vector4_f32[1]), + atan2f(Y.vector4_f32[2], X.vector4_f32[2]), + atan2f(Y.vector4_f32[3], X.vector4_f32[3]), + } } }; + return Result.v; +#elif defined(_XM_SVML_INTRINSICS_) + XMVECTOR Result = _mm_atan2_ps(Y, X); + return Result; +#else + + static const XMVECTORF32 ATan2Constants = { { { XM_PI, XM_PIDIV2, XM_PIDIV4, 2.3561944905f /* Pi*3/4 */ } } }; + + const XMVECTOR Zero = XMVectorZero(); + XMVECTOR ATanResultValid = XMVectorTrueInt(); + + XMVECTOR Pi = XMVectorSplatX(ATan2Constants); + XMVECTOR PiOverTwo = XMVectorSplatY(ATan2Constants); + XMVECTOR PiOverFour = XMVectorSplatZ(ATan2Constants); + XMVECTOR ThreePiOverFour = XMVectorSplatW(ATan2Constants); + + XMVECTOR YEqualsZero = XMVectorEqual(Y, Zero); + XMVECTOR XEqualsZero = XMVectorEqual(X, Zero); + XMVECTOR XIsPositive = XMVectorAndInt(X, g_XMNegativeZero.v); + XIsPositive = XMVectorEqualInt(XIsPositive, Zero); + XMVECTOR YEqualsInfinity = XMVectorIsInfinite(Y); + XMVECTOR XEqualsInfinity = XMVectorIsInfinite(X); + + XMVECTOR YSign = XMVectorAndInt(Y, g_XMNegativeZero.v); + Pi = XMVectorOrInt(Pi, YSign); + PiOverTwo = XMVectorOrInt(PiOverTwo, YSign); + PiOverFour = XMVectorOrInt(PiOverFour, YSign); + ThreePiOverFour = XMVectorOrInt(ThreePiOverFour, YSign); + + XMVECTOR R1 = XMVectorSelect(Pi, YSign, XIsPositive); + XMVECTOR R2 = XMVectorSelect(ATanResultValid, PiOverTwo, XEqualsZero); + XMVECTOR R3 = XMVectorSelect(R2, R1, YEqualsZero); + XMVECTOR R4 = XMVectorSelect(ThreePiOverFour, PiOverFour, XIsPositive); + XMVECTOR R5 = XMVectorSelect(PiOverTwo, R4, XEqualsInfinity); + XMVECTOR Result = XMVectorSelect(R3, R5, YEqualsInfinity); + ATanResultValid = XMVectorEqualInt(Result, ATanResultValid); + + XMVECTOR Reciprocal = XMVectorReciprocalEst(X); + XMVECTOR V = XMVectorMultiply(Y, Reciprocal); + XMVECTOR R0 = XMVectorATanEst(V); + + R1 = XMVectorSelect(Pi, g_XMNegativeZero, XIsPositive); + R2 = XMVectorAdd(R0, R1); + + Result = XMVectorSelect(Result, R2, ATanResultValid); + + return Result; + +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorLerp +( + FXMVECTOR V0, + FXMVECTOR V1, + float t +) noexcept +{ + // V0 + t * (V1 - V0) + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Scale = XMVectorReplicate(t); + XMVECTOR Length = XMVectorSubtract(V1, V0); + return XMVectorMultiplyAdd(Length, Scale, V0); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR L = vsubq_f32(V1, V0); + return vmlaq_n_f32(V0, L, t); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR L = _mm_sub_ps(V1, V0); + XMVECTOR S = _mm_set_ps1(t); + return XM_FMADD_PS(L, S, V0); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorLerpV +( + FXMVECTOR V0, + FXMVECTOR V1, + FXMVECTOR T +) noexcept +{ + // V0 + T * (V1 - V0) + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Length = XMVectorSubtract(V1, V0); + return XMVectorMultiplyAdd(Length, T, V0); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR L = vsubq_f32(V1, V0); + return vmlaq_f32(V0, L, T); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR Length = _mm_sub_ps(V1, V0); + return XM_FMADD_PS(Length, T, V0); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorHermite +( + FXMVECTOR Position0, + FXMVECTOR Tangent0, + FXMVECTOR Position1, + GXMVECTOR Tangent1, + float t +) noexcept +{ + // Result = (2 * t^3 - 3 * t^2 + 1) * Position0 + + // (t^3 - 2 * t^2 + t) * Tangent0 + + // (-2 * t^3 + 3 * t^2) * Position1 + + // (t^3 - t^2) * Tangent1 + +#if defined(_XM_NO_INTRINSICS_) + + float t2 = t * t; + float t3 = t * t2; + + XMVECTOR P0 = XMVectorReplicate(2.0f * t3 - 3.0f * t2 + 1.0f); + XMVECTOR T0 = XMVectorReplicate(t3 - 2.0f * t2 + t); + XMVECTOR P1 = XMVectorReplicate(-2.0f * t3 + 3.0f * t2); + XMVECTOR T1 = XMVectorReplicate(t3 - t2); + + XMVECTOR Result = XMVectorMultiply(P0, Position0); + Result = XMVectorMultiplyAdd(T0, Tangent0, Result); + Result = XMVectorMultiplyAdd(P1, Position1, Result); + Result = XMVectorMultiplyAdd(T1, Tangent1, Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float t2 = t * t; + float t3 = t * t2; + + float p0 = 2.0f * t3 - 3.0f * t2 + 1.0f; + float t0 = t3 - 2.0f * t2 + t; + float p1 = -2.0f * t3 + 3.0f * t2; + float t1 = t3 - t2; + + XMVECTOR vResult = vmulq_n_f32(Position0, p0); + vResult = vmlaq_n_f32(vResult, Tangent0, t0); + vResult = vmlaq_n_f32(vResult, Position1, p1); + vResult = vmlaq_n_f32(vResult, Tangent1, t1); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + float t2 = t * t; + float t3 = t * t2; + + XMVECTOR P0 = _mm_set_ps1(2.0f * t3 - 3.0f * t2 + 1.0f); + XMVECTOR T0 = _mm_set_ps1(t3 - 2.0f * t2 + t); + XMVECTOR P1 = _mm_set_ps1(-2.0f * t3 + 3.0f * t2); + XMVECTOR T1 = _mm_set_ps1(t3 - t2); + + XMVECTOR vResult = _mm_mul_ps(P0, Position0); + vResult = XM_FMADD_PS(Tangent0, T0, vResult); + vResult = XM_FMADD_PS(Position1, P1, vResult); + vResult = XM_FMADD_PS(Tangent1, T1, vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorHermiteV +( + FXMVECTOR Position0, + FXMVECTOR Tangent0, + FXMVECTOR Position1, + GXMVECTOR Tangent1, + HXMVECTOR T +) noexcept +{ + // Result = (2 * t^3 - 3 * t^2 + 1) * Position0 + + // (t^3 - 2 * t^2 + t) * Tangent0 + + // (-2 * t^3 + 3 * t^2) * Position1 + + // (t^3 - t^2) * Tangent1 + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR T2 = XMVectorMultiply(T, T); + XMVECTOR T3 = XMVectorMultiply(T, T2); + + XMVECTOR P0 = XMVectorReplicate(2.0f * T3.vector4_f32[0] - 3.0f * T2.vector4_f32[0] + 1.0f); + XMVECTOR T0 = XMVectorReplicate(T3.vector4_f32[1] - 2.0f * T2.vector4_f32[1] + T.vector4_f32[1]); + XMVECTOR P1 = XMVectorReplicate(-2.0f * T3.vector4_f32[2] + 3.0f * T2.vector4_f32[2]); + XMVECTOR T1 = XMVectorReplicate(T3.vector4_f32[3] - T2.vector4_f32[3]); + + XMVECTOR Result = XMVectorMultiply(P0, Position0); + Result = XMVectorMultiplyAdd(T0, Tangent0, Result); + Result = XMVectorMultiplyAdd(P1, Position1, Result); + Result = XMVectorMultiplyAdd(T1, Tangent1, Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 CatMulT2 = { { { -3.0f, -2.0f, 3.0f, -1.0f } } }; + static const XMVECTORF32 CatMulT3 = { { { 2.0f, 1.0f, -2.0f, 1.0f } } }; + + XMVECTOR T2 = vmulq_f32(T, T); + XMVECTOR T3 = vmulq_f32(T, T2); + // Mul by the constants against t^2 + T2 = vmulq_f32(T2, CatMulT2); + // Mul by the constants against t^3 + T3 = vmlaq_f32(T2, T3, CatMulT3); + // T3 now has the pre-result. + // I need to add t.y only + T2 = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(T), g_XMMaskY)); + T3 = vaddq_f32(T3, T2); + // Add 1.0f to x + T3 = vaddq_f32(T3, g_XMIdentityR0); + // Now, I have the constants created + // Mul the x constant to Position0 + XMVECTOR vResult = vmulq_lane_f32(Position0, vget_low_f32(T3), 0); // T3[0] + // Mul the y constant to Tangent0 + vResult = vmlaq_lane_f32(vResult, Tangent0, vget_low_f32(T3), 1); // T3[1] + // Mul the z constant to Position1 + vResult = vmlaq_lane_f32(vResult, Position1, vget_high_f32(T3), 0); // T3[2] + // Mul the w constant to Tangent1 + vResult = vmlaq_lane_f32(vResult, Tangent1, vget_high_f32(T3), 1); // T3[3] + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 CatMulT2 = { { { -3.0f, -2.0f, 3.0f, -1.0f } } }; + static const XMVECTORF32 CatMulT3 = { { { 2.0f, 1.0f, -2.0f, 1.0f } } }; + + XMVECTOR T2 = _mm_mul_ps(T, T); + XMVECTOR T3 = _mm_mul_ps(T, T2); + // Mul by the constants against t^2 + T2 = _mm_mul_ps(T2, CatMulT2); + // Mul by the constants against t^3 + T3 = XM_FMADD_PS(T3, CatMulT3, T2); + // T3 now has the pre-result. + // I need to add t.y only + T2 = _mm_and_ps(T, g_XMMaskY); + T3 = _mm_add_ps(T3, T2); + // Add 1.0f to x + T3 = _mm_add_ps(T3, g_XMIdentityR0); + // Now, I have the constants created + // Mul the x constant to Position0 + XMVECTOR vResult = XM_PERMUTE_PS(T3, _MM_SHUFFLE(0, 0, 0, 0)); + vResult = _mm_mul_ps(vResult, Position0); + // Mul the y constant to Tangent0 + T2 = XM_PERMUTE_PS(T3, _MM_SHUFFLE(1, 1, 1, 1)); + vResult = XM_FMADD_PS(T2, Tangent0, vResult); + // Mul the z constant to Position1 + T2 = XM_PERMUTE_PS(T3, _MM_SHUFFLE(2, 2, 2, 2)); + vResult = XM_FMADD_PS(T2, Position1, vResult); + // Mul the w constant to Tangent1 + T3 = XM_PERMUTE_PS(T3, _MM_SHUFFLE(3, 3, 3, 3)); + vResult = XM_FMADD_PS(T3, Tangent1, vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorCatmullRom +( + FXMVECTOR Position0, + FXMVECTOR Position1, + FXMVECTOR Position2, + GXMVECTOR Position3, + float t +) noexcept +{ + // Result = ((-t^3 + 2 * t^2 - t) * Position0 + + // (3 * t^3 - 5 * t^2 + 2) * Position1 + + // (-3 * t^3 + 4 * t^2 + t) * Position2 + + // (t^3 - t^2) * Position3) * 0.5 + +#if defined(_XM_NO_INTRINSICS_) + + float t2 = t * t; + float t3 = t * t2; + + XMVECTOR P0 = XMVectorReplicate((-t3 + 2.0f * t2 - t) * 0.5f); + XMVECTOR P1 = XMVectorReplicate((3.0f * t3 - 5.0f * t2 + 2.0f) * 0.5f); + XMVECTOR P2 = XMVectorReplicate((-3.0f * t3 + 4.0f * t2 + t) * 0.5f); + XMVECTOR P3 = XMVectorReplicate((t3 - t2) * 0.5f); + + XMVECTOR Result = XMVectorMultiply(P0, Position0); + Result = XMVectorMultiplyAdd(P1, Position1, Result); + Result = XMVectorMultiplyAdd(P2, Position2, Result); + Result = XMVectorMultiplyAdd(P3, Position3, Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float t2 = t * t; + float t3 = t * t2; + + float p0 = (-t3 + 2.0f * t2 - t) * 0.5f; + float p1 = (3.0f * t3 - 5.0f * t2 + 2.0f) * 0.5f; + float p2 = (-3.0f * t3 + 4.0f * t2 + t) * 0.5f; + float p3 = (t3 - t2) * 0.5f; + + XMVECTOR P1 = vmulq_n_f32(Position1, p1); + XMVECTOR P0 = vmlaq_n_f32(P1, Position0, p0); + XMVECTOR P3 = vmulq_n_f32(Position3, p3); + XMVECTOR P2 = vmlaq_n_f32(P3, Position2, p2); + P0 = vaddq_f32(P0, P2); + return P0; +#elif defined(_XM_SSE_INTRINSICS_) + float t2 = t * t; + float t3 = t * t2; + + XMVECTOR P0 = _mm_set_ps1((-t3 + 2.0f * t2 - t) * 0.5f); + XMVECTOR P1 = _mm_set_ps1((3.0f * t3 - 5.0f * t2 + 2.0f) * 0.5f); + XMVECTOR P2 = _mm_set_ps1((-3.0f * t3 + 4.0f * t2 + t) * 0.5f); + XMVECTOR P3 = _mm_set_ps1((t3 - t2) * 0.5f); + + P1 = _mm_mul_ps(Position1, P1); + P0 = XM_FMADD_PS(Position0, P0, P1); + P3 = _mm_mul_ps(Position3, P3); + P2 = XM_FMADD_PS(Position2, P2, P3); + P0 = _mm_add_ps(P0, P2); + return P0; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorCatmullRomV +( + FXMVECTOR Position0, + FXMVECTOR Position1, + FXMVECTOR Position2, + GXMVECTOR Position3, + HXMVECTOR T +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float fx = T.vector4_f32[0]; + float fy = T.vector4_f32[1]; + float fz = T.vector4_f32[2]; + float fw = T.vector4_f32[3]; + XMVECTORF32 vResult = { { { + 0.5f * ((-fx * fx * fx + 2 * fx * fx - fx) * Position0.vector4_f32[0] + + (3 * fx * fx * fx - 5 * fx * fx + 2) * Position1.vector4_f32[0] + + (-3 * fx * fx * fx + 4 * fx * fx + fx) * Position2.vector4_f32[0] + + (fx * fx * fx - fx * fx) * Position3.vector4_f32[0]), + + 0.5f * ((-fy * fy * fy + 2 * fy * fy - fy) * Position0.vector4_f32[1] + + (3 * fy * fy * fy - 5 * fy * fy + 2) * Position1.vector4_f32[1] + + (-3 * fy * fy * fy + 4 * fy * fy + fy) * Position2.vector4_f32[1] + + (fy * fy * fy - fy * fy) * Position3.vector4_f32[1]), + + 0.5f * ((-fz * fz * fz + 2 * fz * fz - fz) * Position0.vector4_f32[2] + + (3 * fz * fz * fz - 5 * fz * fz + 2) * Position1.vector4_f32[2] + + (-3 * fz * fz * fz + 4 * fz * fz + fz) * Position2.vector4_f32[2] + + (fz * fz * fz - fz * fz) * Position3.vector4_f32[2]), + + 0.5f * ((-fw * fw * fw + 2 * fw * fw - fw) * Position0.vector4_f32[3] + + (3 * fw * fw * fw - 5 * fw * fw + 2) * Position1.vector4_f32[3] + + (-3 * fw * fw * fw + 4 * fw * fw + fw) * Position2.vector4_f32[3] + + (fw * fw * fw - fw * fw) * Position3.vector4_f32[3]) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Catmul2 = { { { 2.0f, 2.0f, 2.0f, 2.0f } } }; + static const XMVECTORF32 Catmul3 = { { { 3.0f, 3.0f, 3.0f, 3.0f } } }; + static const XMVECTORF32 Catmul4 = { { { 4.0f, 4.0f, 4.0f, 4.0f } } }; + static const XMVECTORF32 Catmul5 = { { { 5.0f, 5.0f, 5.0f, 5.0f } } }; + // Cache T^2 and T^3 + XMVECTOR T2 = vmulq_f32(T, T); + XMVECTOR T3 = vmulq_f32(T, T2); + // Perform the Position0 term + XMVECTOR vResult = vaddq_f32(T2, T2); + vResult = vsubq_f32(vResult, T); + vResult = vsubq_f32(vResult, T3); + vResult = vmulq_f32(vResult, Position0); + // Perform the Position1 term and add + XMVECTOR vTemp = vmulq_f32(T3, Catmul3); + vTemp = vmlsq_f32(vTemp, T2, Catmul5); + vTemp = vaddq_f32(vTemp, Catmul2); + vResult = vmlaq_f32(vResult, vTemp, Position1); + // Perform the Position2 term and add + vTemp = vmulq_f32(T2, Catmul4); + vTemp = vmlsq_f32(vTemp, T3, Catmul3); + vTemp = vaddq_f32(vTemp, T); + vResult = vmlaq_f32(vResult, vTemp, Position2); + // Position3 is the last term + T3 = vsubq_f32(T3, T2); + vResult = vmlaq_f32(vResult, T3, Position3); + // Multiply by 0.5f and exit + vResult = vmulq_f32(vResult, g_XMOneHalf); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Catmul2 = { { { 2.0f, 2.0f, 2.0f, 2.0f } } }; + static const XMVECTORF32 Catmul3 = { { { 3.0f, 3.0f, 3.0f, 3.0f } } }; + static const XMVECTORF32 Catmul4 = { { { 4.0f, 4.0f, 4.0f, 4.0f } } }; + static const XMVECTORF32 Catmul5 = { { { 5.0f, 5.0f, 5.0f, 5.0f } } }; + // Cache T^2 and T^3 + XMVECTOR T2 = _mm_mul_ps(T, T); + XMVECTOR T3 = _mm_mul_ps(T, T2); + // Perform the Position0 term + XMVECTOR vResult = _mm_add_ps(T2, T2); + vResult = _mm_sub_ps(vResult, T); + vResult = _mm_sub_ps(vResult, T3); + vResult = _mm_mul_ps(vResult, Position0); + // Perform the Position1 term and add + XMVECTOR vTemp = _mm_mul_ps(T3, Catmul3); + vTemp = XM_FNMADD_PS(T2, Catmul5, vTemp); + vTemp = _mm_add_ps(vTemp, Catmul2); + vResult = XM_FMADD_PS(vTemp, Position1, vResult); + // Perform the Position2 term and add + vTemp = _mm_mul_ps(T2, Catmul4); + vTemp = XM_FNMADD_PS(T3, Catmul3, vTemp); + vTemp = _mm_add_ps(vTemp, T); + vResult = XM_FMADD_PS(vTemp, Position2, vResult); + // Position3 is the last term + T3 = _mm_sub_ps(T3, T2); + vResult = XM_FMADD_PS(T3, Position3, vResult); + // Multiply by 0.5f and exit + vResult = _mm_mul_ps(vResult, g_XMOneHalf); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorBaryCentric +( + FXMVECTOR Position0, + FXMVECTOR Position1, + FXMVECTOR Position2, + float f, + float g +) noexcept +{ + // Result = Position0 + f * (Position1 - Position0) + g * (Position2 - Position0) + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR P10 = XMVectorSubtract(Position1, Position0); + XMVECTOR ScaleF = XMVectorReplicate(f); + + XMVECTOR P20 = XMVectorSubtract(Position2, Position0); + XMVECTOR ScaleG = XMVectorReplicate(g); + + XMVECTOR Result = XMVectorMultiplyAdd(P10, ScaleF, Position0); + Result = XMVectorMultiplyAdd(P20, ScaleG, Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR R1 = vsubq_f32(Position1, Position0); + XMVECTOR R2 = vsubq_f32(Position2, Position0); + R1 = vmlaq_n_f32(Position0, R1, f); + return vmlaq_n_f32(R1, R2, g); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR R1 = _mm_sub_ps(Position1, Position0); + XMVECTOR R2 = _mm_sub_ps(Position2, Position0); + XMVECTOR SF = _mm_set_ps1(f); + R1 = XM_FMADD_PS(R1, SF, Position0); + XMVECTOR SG = _mm_set_ps1(g); + return XM_FMADD_PS(R2, SG, R1); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVectorBaryCentricV +( + FXMVECTOR Position0, + FXMVECTOR Position1, + FXMVECTOR Position2, + GXMVECTOR F, + HXMVECTOR G +) noexcept +{ + // Result = Position0 + f * (Position1 - Position0) + g * (Position2 - Position0) + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR P10 = XMVectorSubtract(Position1, Position0); + XMVECTOR P20 = XMVectorSubtract(Position2, Position0); + + XMVECTOR Result = XMVectorMultiplyAdd(P10, F, Position0); + Result = XMVectorMultiplyAdd(P20, G, Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR R1 = vsubq_f32(Position1, Position0); + XMVECTOR R2 = vsubq_f32(Position2, Position0); + R1 = vmlaq_f32(Position0, R1, F); + return vmlaq_f32(R1, R2, G); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR R1 = _mm_sub_ps(Position1, Position0); + XMVECTOR R2 = _mm_sub_ps(Position2, Position0); + R1 = XM_FMADD_PS(R1, F, Position0); + return XM_FMADD_PS(R2, G, R1); +#endif +} + +/**************************************************************************** + * + * 2D Vector + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparison operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2Equal +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] == V2.vector4_f32[0]) && (V1.vector4_f32[1] == V2.vector4_f32[1])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vceq_f32(vget_low_f32(V1), vget_low_f32(V2)); + return (vget_lane_u64(vreinterpret_u64_u32(vTemp), 0) == 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpeq_ps(V1, V2); + // z and w are don't care + return (((_mm_movemask_ps(vTemp) & 3) == 3) != 0); +#endif +} + + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector2EqualR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + uint32_t CR = 0; + if ((V1.vector4_f32[0] == V2.vector4_f32[0]) && + (V1.vector4_f32[1] == V2.vector4_f32[1])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_f32[0] != V2.vector4_f32[0]) && + (V1.vector4_f32[1] != V2.vector4_f32[1])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vceq_f32(vget_low_f32(V1), vget_low_f32(V2)); + uint64_t r = vget_lane_u64(vreinterpret_u64_u32(vTemp), 0); + uint32_t CR = 0; + if (r == 0xFFFFFFFFFFFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpeq_ps(V1, V2); + // z and w are don't care + int iTest = _mm_movemask_ps(vTemp) & 3; + uint32_t CR = 0; + if (iTest == 3) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2EqualInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_u32[0] == V2.vector4_u32[0]) && (V1.vector4_u32[1] == V2.vector4_u32[1])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vceq_u32(vget_low_u32(vreinterpretq_u32_f32(V1)), vget_low_u32(vreinterpretq_u32_f32(V2))); + return (vget_lane_u64(vreinterpret_u64_u32(vTemp), 0) == 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return (((_mm_movemask_ps(_mm_castsi128_ps(vTemp)) & 3) == 3) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector2EqualIntR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + uint32_t CR = 0; + if ((V1.vector4_u32[0] == V2.vector4_u32[0]) && + (V1.vector4_u32[1] == V2.vector4_u32[1])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_u32[0] != V2.vector4_u32[0]) && + (V1.vector4_u32[1] != V2.vector4_u32[1])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vceq_u32(vget_low_u32(vreinterpretq_u32_f32(V1)), vget_low_u32(vreinterpretq_u32_f32(V2))); + uint64_t r = vget_lane_u64(vreinterpret_u64_u32(vTemp), 0); + uint32_t CR = 0; + if (r == 0xFFFFFFFFFFFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + int iTest = _mm_movemask_ps(_mm_castsi128_ps(vTemp)) & 3; + uint32_t CR = 0; + if (iTest == 3) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2NearEqual +( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR Epsilon +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float dx = fabsf(V1.vector4_f32[0] - V2.vector4_f32[0]); + float dy = fabsf(V1.vector4_f32[1] - V2.vector4_f32[1]); + return ((dx <= Epsilon.vector4_f32[0]) && + (dy <= Epsilon.vector4_f32[1])); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t vDelta = vsub_f32(vget_low_f32(V1), vget_low_f32(V2)); +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + uint32x2_t vTemp = vacle_f32(vDelta, vget_low_u32(Epsilon)); +#else + uint32x2_t vTemp = vcle_f32(vabs_f32(vDelta), vget_low_f32(Epsilon)); +#endif + uint64_t r = vget_lane_u64(vreinterpret_u64_u32(vTemp), 0); + return (r == 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + // Get the difference + XMVECTOR vDelta = _mm_sub_ps(V1, V2); + // Get the absolute value of the difference + XMVECTOR vTemp = _mm_setzero_ps(); + vTemp = _mm_sub_ps(vTemp, vDelta); + vTemp = _mm_max_ps(vTemp, vDelta); + vTemp = _mm_cmple_ps(vTemp, Epsilon); + // z and w are don't care + return (((_mm_movemask_ps(vTemp) & 3) == 0x3) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2NotEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] != V2.vector4_f32[0]) || (V1.vector4_f32[1] != V2.vector4_f32[1])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vceq_f32(vget_low_f32(V1), vget_low_f32(V2)); + return (vget_lane_u64(vreinterpret_u64_u32(vTemp), 0) != 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpeq_ps(V1, V2); + // z and w are don't care + return (((_mm_movemask_ps(vTemp) & 3) != 3) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2NotEqualInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_u32[0] != V2.vector4_u32[0]) || (V1.vector4_u32[1] != V2.vector4_u32[1])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vceq_u32(vget_low_u32(vreinterpretq_u32_f32(V1)), vget_low_u32(vreinterpretq_u32_f32(V2))); + return (vget_lane_u64(vreinterpret_u64_u32(vTemp), 0) != 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return (((_mm_movemask_ps(_mm_castsi128_ps(vTemp)) & 3) != 3) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2Greater +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] > V2.vector4_f32[0]) && (V1.vector4_f32[1] > V2.vector4_f32[1])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vcgt_f32(vget_low_f32(V1), vget_low_f32(V2)); + return (vget_lane_u64(vreinterpret_u64_u32(vTemp), 0) == 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpgt_ps(V1, V2); + // z and w are don't care + return (((_mm_movemask_ps(vTemp) & 3) == 3) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector2GreaterR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + uint32_t CR = 0; + if ((V1.vector4_f32[0] > V2.vector4_f32[0]) && + (V1.vector4_f32[1] > V2.vector4_f32[1])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_f32[0] <= V2.vector4_f32[0]) && + (V1.vector4_f32[1] <= V2.vector4_f32[1])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vcgt_f32(vget_low_f32(V1), vget_low_f32(V2)); + uint64_t r = vget_lane_u64(vreinterpret_u64_u32(vTemp), 0); + uint32_t CR = 0; + if (r == 0xFFFFFFFFFFFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpgt_ps(V1, V2); + int iTest = _mm_movemask_ps(vTemp) & 3; + uint32_t CR = 0; + if (iTest == 3) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2GreaterOrEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] >= V2.vector4_f32[0]) && (V1.vector4_f32[1] >= V2.vector4_f32[1])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vcge_f32(vget_low_f32(V1), vget_low_f32(V2)); + return (vget_lane_u64(vreinterpret_u64_u32(vTemp), 0) == 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpge_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 3) == 3) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector2GreaterOrEqualR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + uint32_t CR = 0; + if ((V1.vector4_f32[0] >= V2.vector4_f32[0]) && + (V1.vector4_f32[1] >= V2.vector4_f32[1])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_f32[0] < V2.vector4_f32[0]) && + (V1.vector4_f32[1] < V2.vector4_f32[1])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vcge_f32(vget_low_f32(V1), vget_low_f32(V2)); + uint64_t r = vget_lane_u64(vreinterpret_u64_u32(vTemp), 0); + uint32_t CR = 0; + if (r == 0xFFFFFFFFFFFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpge_ps(V1, V2); + int iTest = _mm_movemask_ps(vTemp) & 3; + uint32_t CR = 0; + if (iTest == 3) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2Less +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] < V2.vector4_f32[0]) && (V1.vector4_f32[1] < V2.vector4_f32[1])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vclt_f32(vget_low_f32(V1), vget_low_f32(V2)); + return (vget_lane_u64(vreinterpret_u64_u32(vTemp), 0) == 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmplt_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 3) == 3) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2LessOrEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] <= V2.vector4_f32[0]) && (V1.vector4_f32[1] <= V2.vector4_f32[1])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vTemp = vcle_f32(vget_low_f32(V1), vget_low_f32(V2)); + return (vget_lane_u64(vreinterpret_u64_u32(vTemp), 0) == 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmple_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 3) == 3) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2InBounds +( + FXMVECTOR V, + FXMVECTOR Bounds +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V.vector4_f32[0] <= Bounds.vector4_f32[0] && V.vector4_f32[0] >= -Bounds.vector4_f32[0]) && + (V.vector4_f32[1] <= Bounds.vector4_f32[1] && V.vector4_f32[1] >= -Bounds.vector4_f32[1])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + float32x2_t B = vget_low_f32(Bounds); + // Test if less than or equal + uint32x2_t ivTemp1 = vcle_f32(VL, B); + // Negate the bounds + float32x2_t vTemp2 = vneg_f32(B); + // Test if greater or equal (Reversed) + uint32x2_t ivTemp2 = vcle_f32(vTemp2, VL); + // Blend answers + ivTemp1 = vand_u32(ivTemp1, ivTemp2); + // x and y in bounds? + return (vget_lane_u64(vreinterpret_u64_u32(ivTemp1), 0) == 0xFFFFFFFFFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + // Test if less than or equal + XMVECTOR vTemp1 = _mm_cmple_ps(V, Bounds); + // Negate the bounds + XMVECTOR vTemp2 = _mm_mul_ps(Bounds, g_XMNegativeOne); + // Test if greater or equal (Reversed) + vTemp2 = _mm_cmple_ps(vTemp2, V); + // Blend answers + vTemp1 = _mm_and_ps(vTemp1, vTemp2); + // x and y in bounds? (z and w are don't care) + return (((_mm_movemask_ps(vTemp1) & 0x3) == 0x3) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(push) +#pragma float_control(precise, on) +#endif + +inline bool XM_CALLCONV XMVector2IsNaN(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (XMISNAN(V.vector4_f32[0]) || + XMISNAN(V.vector4_f32[1])); +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(__clang__) && defined(__FINITE_MATH_ONLY__) + return isnan(vgetq_lane_f32(V, 0)) || isnan(vgetq_lane_f32(V, 1)); +#else + float32x2_t VL = vget_low_f32(V); + // Test against itself. NaN is always not equal + uint32x2_t vTempNan = vceq_f32(VL, VL); + // If x or y are NaN, the mask is zero + return (vget_lane_u64(vreinterpret_u64_u32(vTempNan), 0) != 0xFFFFFFFFFFFFFFFFU); +#endif +#elif defined(_XM_SSE_INTRINSICS_) +#if defined(__clang__) && defined(__FINITE_MATH_ONLY__) + XM_ALIGNED_DATA(16) float tmp[4]; + _mm_store_ps(tmp, V); + return isnan(tmp[0]) || isnan(tmp[1]); +#else +// Test against itself. NaN is always not equal + XMVECTOR vTempNan = _mm_cmpneq_ps(V, V); + // If x or y are NaN, the mask is non-zero + return ((_mm_movemask_ps(vTempNan) & 3) != 0); +#endif +#endif +} + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(pop) +#endif + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector2IsInfinite(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + return (XMISINF(V.vector4_f32[0]) || + XMISINF(V.vector4_f32[1])); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Mask off the sign bit + uint32x2_t vTemp = vand_u32(vget_low_u32(vreinterpretq_u32_f32(V)), vget_low_u32(g_XMAbsMask)); + // Compare to infinity + vTemp = vceq_f32(vreinterpret_f32_u32(vTemp), vget_low_f32(g_XMInfinity)); + // If any are infinity, the signs are true. + return vget_lane_u64(vreinterpret_u64_u32(vTemp), 0) != 0; +#elif defined(_XM_SSE_INTRINSICS_) + // Mask off the sign bit + __m128 vTemp = _mm_and_ps(V, g_XMAbsMask); + // Compare to infinity + vTemp = _mm_cmpeq_ps(vTemp, g_XMInfinity); + // If x or z are infinity, the signs are true. + return ((_mm_movemask_ps(vTemp) & 3) != 0); +#endif +} + +//------------------------------------------------------------------------------ +// Computation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2Dot +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result; + Result.f[0] = + Result.f[1] = + Result.f[2] = + Result.f[3] = V1.vector4_f32[0] * V2.vector4_f32[0] + V1.vector4_f32[1] * V2.vector4_f32[1]; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Perform the dot product on x and y + float32x2_t vTemp = vmul_f32(vget_low_f32(V1), vget_low_f32(V2)); + vTemp = vpadd_f32(vTemp, vTemp); + return vcombine_f32(vTemp, vTemp); +#elif defined(_XM_SSE4_INTRINSICS_) + return _mm_dp_ps(V1, V2, 0x3f); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vDot = _mm_mul_ps(V1, V2); + vDot = _mm_hadd_ps(vDot, vDot); + vDot = _mm_moveldup_ps(vDot); + return vDot; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x and y + XMVECTOR vLengthSq = _mm_mul_ps(V1, V2); + // vTemp has y splatted + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 1, 1, 1)); + // x+y + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2Cross +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + // [ V1.x*V2.y - V1.y*V2.x, V1.x*V2.y - V1.y*V2.x ] + +#if defined(_XM_NO_INTRINSICS_) + float fCross = (V1.vector4_f32[0] * V2.vector4_f32[1]) - (V1.vector4_f32[1] * V2.vector4_f32[0]); + XMVECTORF32 vResult; + vResult.f[0] = + vResult.f[1] = + vResult.f[2] = + vResult.f[3] = fCross; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Negate = { { { 1.f, -1.f, 0, 0 } } }; + + float32x2_t vTemp = vmul_f32(vget_low_f32(V1), vrev64_f32(vget_low_f32(V2))); + vTemp = vmul_f32(vTemp, vget_low_f32(Negate)); + vTemp = vpadd_f32(vTemp, vTemp); + return vcombine_f32(vTemp, vTemp); +#elif defined(_XM_SSE_INTRINSICS_) + // Swap x and y + XMVECTOR vResult = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 1, 0, 1)); + // Perform the muls + vResult = _mm_mul_ps(vResult, V1); + // Splat y + XMVECTOR vTemp = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(1, 1, 1, 1)); + // Sub the values + vResult = _mm_sub_ss(vResult, vTemp); + // Splat the cross product + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(0, 0, 0, 0)); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2LengthSq(FXMVECTOR V) noexcept +{ + return XMVector2Dot(V, V); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2ReciprocalLengthEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + Result = XMVector2LengthSq(V); + Result = XMVectorReciprocalSqrtEst(Result); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + // Dot2 + float32x2_t vTemp = vmul_f32(VL, VL); + vTemp = vpadd_f32(vTemp, vTemp); + // Reciprocal sqrt (estimate) + vTemp = vrsqrte_f32(vTemp); + return vcombine_f32(vTemp, vTemp); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + return _mm_rsqrt_ps(vTemp); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + XMVECTOR vTemp = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_rsqrt_ss(vTemp); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x and y + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has y splatted + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 1, 1, 1)); + // x+y + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vLengthSq = _mm_rsqrt_ss(vLengthSq); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2ReciprocalLength(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + Result = XMVector2LengthSq(V); + Result = XMVectorReciprocalSqrt(Result); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + // Dot2 + float32x2_t vTemp = vmul_f32(VL, VL); + vTemp = vpadd_f32(vTemp, vTemp); + // Reciprocal sqrt + float32x2_t S0 = vrsqrte_f32(vTemp); + float32x2_t P0 = vmul_f32(vTemp, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(vTemp, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + float32x2_t Result = vmul_f32(S1, R1); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + XMVECTOR vLengthSq = _mm_sqrt_ps(vTemp); + return _mm_div_ps(g_XMOne, vLengthSq); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + XMVECTOR vTemp = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_sqrt_ss(vTemp); + vLengthSq = _mm_div_ss(g_XMOne, vLengthSq); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x and y + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has y splatted + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 1, 1, 1)); + // x+y + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vLengthSq = _mm_sqrt_ss(vLengthSq); + vLengthSq = _mm_div_ss(g_XMOne, vLengthSq); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2LengthEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + Result = XMVector2LengthSq(V); + Result = XMVectorSqrtEst(Result); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + // Dot2 + float32x2_t vTemp = vmul_f32(VL, VL); + vTemp = vpadd_f32(vTemp, vTemp); + const float32x2_t zero = vdup_n_f32(0); + uint32x2_t VEqualsZero = vceq_f32(vTemp, zero); + // Sqrt (estimate) + float32x2_t Result = vrsqrte_f32(vTemp); + Result = vmul_f32(vTemp, Result); + Result = vbsl_f32(VEqualsZero, zero, Result); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + return _mm_sqrt_ps(vTemp); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + XMVECTOR vTemp = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_sqrt_ss(vTemp); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x and y + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has y splatted + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 1, 1, 1)); + // x+y + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vLengthSq = _mm_sqrt_ss(vLengthSq); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2Length(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + Result = XMVector2LengthSq(V); + Result = XMVectorSqrt(Result); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + // Dot2 + float32x2_t vTemp = vmul_f32(VL, VL); + vTemp = vpadd_f32(vTemp, vTemp); + const float32x2_t zero = vdup_n_f32(0); + uint32x2_t VEqualsZero = vceq_f32(vTemp, zero); + // Sqrt + float32x2_t S0 = vrsqrte_f32(vTemp); + float32x2_t P0 = vmul_f32(vTemp, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(vTemp, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + float32x2_t Result = vmul_f32(S1, R1); + Result = vmul_f32(vTemp, Result); + Result = vbsl_f32(VEqualsZero, zero, Result); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + return _mm_sqrt_ps(vTemp); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + XMVECTOR vTemp = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_sqrt_ss(vTemp); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x and y + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has y splatted + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 1, 1, 1)); + // x+y + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + vLengthSq = _mm_sqrt_ps(vLengthSq); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ +// XMVector2NormalizeEst uses a reciprocal estimate and +// returns QNaN on zero and infinite vectors. + +inline XMVECTOR XM_CALLCONV XMVector2NormalizeEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + Result = XMVector2ReciprocalLength(V); + Result = XMVectorMultiply(V, Result); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + // Dot2 + float32x2_t vTemp = vmul_f32(VL, VL); + vTemp = vpadd_f32(vTemp, vTemp); + // Reciprocal sqrt (estimate) + vTemp = vrsqrte_f32(vTemp); + // Normalize + float32x2_t Result = vmul_f32(VL, vTemp); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x3f); + XMVECTOR vResult = _mm_rsqrt_ps(vTemp); + return _mm_mul_ps(vResult, V); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_rsqrt_ss(vLengthSq); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + vLengthSq = _mm_mul_ps(vLengthSq, V); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x and y + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has y splatted + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 1, 1, 1)); + // x+y + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vLengthSq = _mm_rsqrt_ss(vLengthSq); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + vLengthSq = _mm_mul_ps(vLengthSq, V); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2Normalize(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR vResult = XMVector2Length(V); + float fLength = vResult.vector4_f32[0]; + + // Prevent divide by zero + if (fLength > 0) + { + fLength = 1.0f / fLength; + } + + vResult.vector4_f32[0] = V.vector4_f32[0] * fLength; + vResult.vector4_f32[1] = V.vector4_f32[1] * fLength; + vResult.vector4_f32[2] = V.vector4_f32[2] * fLength; + vResult.vector4_f32[3] = V.vector4_f32[3] * fLength; + return vResult; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + // Dot2 + float32x2_t vTemp = vmul_f32(VL, VL); + vTemp = vpadd_f32(vTemp, vTemp); + uint32x2_t VEqualsZero = vceq_f32(vTemp, vdup_n_f32(0)); + uint32x2_t VEqualsInf = vceq_f32(vTemp, vget_low_f32(g_XMInfinity)); + // Reciprocal sqrt (2 iterations of Newton-Raphson) + float32x2_t S0 = vrsqrte_f32(vTemp); + float32x2_t P0 = vmul_f32(vTemp, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(vTemp, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + vTemp = vmul_f32(S1, R1); + // Normalize + float32x2_t Result = vmul_f32(VL, vTemp); + Result = vbsl_f32(VEqualsZero, vdup_n_f32(0), Result); + Result = vbsl_f32(VEqualsInf, vget_low_f32(g_XMQNaN), Result); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vLengthSq = _mm_dp_ps(V, V, 0x3f); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Reciprocal mul to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; +#elif defined(_XM_SSE3_INTRINSICS_) + // Perform the dot product on x and y only + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_moveldup_ps(vLengthSq); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Reciprocal mul to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x and y only + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 1, 1, 1)); + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Reciprocal mul to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2ClampLength +( + FXMVECTOR V, + float LengthMin, + float LengthMax +) noexcept +{ + XMVECTOR ClampMax = XMVectorReplicate(LengthMax); + XMVECTOR ClampMin = XMVectorReplicate(LengthMin); + return XMVector2ClampLengthV(V, ClampMin, ClampMax); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2ClampLengthV +( + FXMVECTOR V, + FXMVECTOR LengthMin, + FXMVECTOR LengthMax +) noexcept +{ + assert((XMVectorGetY(LengthMin) == XMVectorGetX(LengthMin))); + assert((XMVectorGetY(LengthMax) == XMVectorGetX(LengthMax))); + assert(XMVector2GreaterOrEqual(LengthMin, g_XMZero)); + assert(XMVector2GreaterOrEqual(LengthMax, g_XMZero)); + assert(XMVector2GreaterOrEqual(LengthMax, LengthMin)); + + XMVECTOR LengthSq = XMVector2LengthSq(V); + + const XMVECTOR Zero = XMVectorZero(); + + XMVECTOR RcpLength = XMVectorReciprocalSqrt(LengthSq); + + XMVECTOR InfiniteLength = XMVectorEqualInt(LengthSq, g_XMInfinity.v); + XMVECTOR ZeroLength = XMVectorEqual(LengthSq, Zero); + + XMVECTOR Length = XMVectorMultiply(LengthSq, RcpLength); + + XMVECTOR Normal = XMVectorMultiply(V, RcpLength); + + XMVECTOR Select = XMVectorEqualInt(InfiniteLength, ZeroLength); + Length = XMVectorSelect(LengthSq, Length, Select); + Normal = XMVectorSelect(LengthSq, Normal, Select); + + XMVECTOR ControlMax = XMVectorGreater(Length, LengthMax); + XMVECTOR ControlMin = XMVectorLess(Length, LengthMin); + + XMVECTOR ClampLength = XMVectorSelect(Length, LengthMax, ControlMax); + ClampLength = XMVectorSelect(ClampLength, LengthMin, ControlMin); + + XMVECTOR Result = XMVectorMultiply(Normal, ClampLength); + + // Preserve the original vector (with no precision loss) if the length falls within the given range + XMVECTOR Control = XMVectorEqualInt(ControlMax, ControlMin); + Result = XMVectorSelect(Result, V, Control); + + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2Reflect +( + FXMVECTOR Incident, + FXMVECTOR Normal +) noexcept +{ + // Result = Incident - (2 * dot(Incident, Normal)) * Normal + + XMVECTOR Result; + Result = XMVector2Dot(Incident, Normal); + Result = XMVectorAdd(Result, Result); + Result = XMVectorNegativeMultiplySubtract(Result, Normal, Incident); + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2Refract +( + FXMVECTOR Incident, + FXMVECTOR Normal, + float RefractionIndex +) noexcept +{ + XMVECTOR Index = XMVectorReplicate(RefractionIndex); + return XMVector2RefractV(Incident, Normal, Index); +} + +//------------------------------------------------------------------------------ + +// Return the refraction of a 2D vector +inline XMVECTOR XM_CALLCONV XMVector2RefractV +( + FXMVECTOR Incident, + FXMVECTOR Normal, + FXMVECTOR RefractionIndex +) noexcept +{ + // Result = RefractionIndex * Incident - Normal * (RefractionIndex * dot(Incident, Normal) + + // sqrt(1 - RefractionIndex * RefractionIndex * (1 - dot(Incident, Normal) * dot(Incident, Normal)))) + +#if defined(_XM_NO_INTRINSICS_) + + float IDotN = (Incident.vector4_f32[0] * Normal.vector4_f32[0]) + (Incident.vector4_f32[1] * Normal.vector4_f32[1]); + // R = 1.0f - RefractionIndex * RefractionIndex * (1.0f - IDotN * IDotN) + float RY = 1.0f - (IDotN * IDotN); + float RX = 1.0f - (RY * RefractionIndex.vector4_f32[0] * RefractionIndex.vector4_f32[0]); + RY = 1.0f - (RY * RefractionIndex.vector4_f32[1] * RefractionIndex.vector4_f32[1]); + if (RX >= 0.0f) + { + RX = (RefractionIndex.vector4_f32[0] * Incident.vector4_f32[0]) - (Normal.vector4_f32[0] * ((RefractionIndex.vector4_f32[0] * IDotN) + sqrtf(RX))); + } + else + { + RX = 0.0f; + } + if (RY >= 0.0f) + { + RY = (RefractionIndex.vector4_f32[1] * Incident.vector4_f32[1]) - (Normal.vector4_f32[1] * ((RefractionIndex.vector4_f32[1] * IDotN) + sqrtf(RY))); + } + else + { + RY = 0.0f; + } + + XMVECTOR vResult; + vResult.vector4_f32[0] = RX; + vResult.vector4_f32[1] = RY; + vResult.vector4_f32[2] = 0.0f; + vResult.vector4_f32[3] = 0.0f; + return vResult; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t IL = vget_low_f32(Incident); + float32x2_t NL = vget_low_f32(Normal); + float32x2_t RIL = vget_low_f32(RefractionIndex); + // Get the 2D Dot product of Incident-Normal + float32x2_t vTemp = vmul_f32(IL, NL); + float32x2_t IDotN = vpadd_f32(vTemp, vTemp); + // vTemp = 1.0f - RefractionIndex * RefractionIndex * (1.0f - IDotN * IDotN) + vTemp = vmls_f32(vget_low_f32(g_XMOne), IDotN, IDotN); + vTemp = vmul_f32(vTemp, RIL); + vTemp = vmls_f32(vget_low_f32(g_XMOne), vTemp, RIL); + // If any terms are <=0, sqrt() will fail, punt to zero + uint32x2_t vMask = vcgt_f32(vTemp, vget_low_f32(g_XMZero)); + // Sqrt(vTemp) + float32x2_t S0 = vrsqrte_f32(vTemp); + float32x2_t P0 = vmul_f32(vTemp, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(vTemp, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + float32x2_t S2 = vmul_f32(S1, R1); + vTemp = vmul_f32(vTemp, S2); + // R = RefractionIndex * IDotN + sqrt(R) + vTemp = vmla_f32(vTemp, RIL, IDotN); + // Result = RefractionIndex * Incident - Normal * R + float32x2_t vResult = vmul_f32(RIL, IL); + vResult = vmls_f32(vResult, vTemp, NL); + vResult = vreinterpret_f32_u32(vand_u32(vreinterpret_u32_f32(vResult), vMask)); + return vcombine_f32(vResult, vResult); +#elif defined(_XM_SSE_INTRINSICS_) + // Result = RefractionIndex * Incident - Normal * (RefractionIndex * dot(Incident, Normal) + + // sqrt(1 - RefractionIndex * RefractionIndex * (1 - dot(Incident, Normal) * dot(Incident, Normal)))) + // Get the 2D Dot product of Incident-Normal + XMVECTOR IDotN = XMVector2Dot(Incident, Normal); + // vTemp = 1.0f - RefractionIndex * RefractionIndex * (1.0f - IDotN * IDotN) + XMVECTOR vTemp = XM_FNMADD_PS(IDotN, IDotN, g_XMOne); + vTemp = _mm_mul_ps(vTemp, RefractionIndex); + vTemp = XM_FNMADD_PS(vTemp, RefractionIndex, g_XMOne); + // If any terms are <=0, sqrt() will fail, punt to zero + XMVECTOR vMask = _mm_cmpgt_ps(vTemp, g_XMZero); + // R = RefractionIndex * IDotN + sqrt(R) + vTemp = _mm_sqrt_ps(vTemp); + vTemp = XM_FMADD_PS(RefractionIndex, IDotN, vTemp); + // Result = RefractionIndex * Incident - Normal * R + XMVECTOR vResult = _mm_mul_ps(RefractionIndex, Incident); + vResult = XM_FNMADD_PS(vTemp, Normal, vResult); + vResult = _mm_and_ps(vResult, vMask); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2Orthogonal(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + -V.vector4_f32[1], + V.vector4_f32[0], + 0.f, + 0.f + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Negate = { { { -1.f, 1.f, 0, 0 } } }; + const float32x2_t zero = vdup_n_f32(0); + + float32x2_t VL = vget_low_f32(V); + float32x2_t Result = vmul_f32(vrev64_f32(VL), vget_low_f32(Negate)); + return vcombine_f32(Result, zero); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 2, 0, 1)); + vResult = _mm_mul_ps(vResult, g_XMNegateX); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2AngleBetweenNormalsEst +( + FXMVECTOR N1, + FXMVECTOR N2 +) noexcept +{ + XMVECTOR Result = XMVector2Dot(N1, N2); + Result = XMVectorClamp(Result, g_XMNegativeOne.v, g_XMOne.v); + Result = XMVectorACosEst(Result); + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2AngleBetweenNormals +( + FXMVECTOR N1, + FXMVECTOR N2 +) noexcept +{ + XMVECTOR Result = XMVector2Dot(N1, N2); + Result = XMVectorClamp(Result, g_XMNegativeOne, g_XMOne); + Result = XMVectorACos(Result); + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2AngleBetweenVectors +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + XMVECTOR L1 = XMVector2ReciprocalLength(V1); + XMVECTOR L2 = XMVector2ReciprocalLength(V2); + + XMVECTOR Dot = XMVector2Dot(V1, V2); + + L1 = XMVectorMultiply(L1, L2); + + XMVECTOR CosAngle = XMVectorMultiply(Dot, L1); + CosAngle = XMVectorClamp(CosAngle, g_XMNegativeOne.v, g_XMOne.v); + + return XMVectorACos(CosAngle); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2LinePointDistance +( + FXMVECTOR LinePoint1, + FXMVECTOR LinePoint2, + FXMVECTOR Point +) noexcept +{ + // Given a vector PointVector from LinePoint1 to Point and a vector + // LineVector from LinePoint1 to LinePoint2, the scaled distance + // PointProjectionScale from LinePoint1 to the perpendicular projection + // of PointVector onto the line is defined as: + // + // PointProjectionScale = dot(PointVector, LineVector) / LengthSq(LineVector) + + XMVECTOR PointVector = XMVectorSubtract(Point, LinePoint1); + XMVECTOR LineVector = XMVectorSubtract(LinePoint2, LinePoint1); + + XMVECTOR LengthSq = XMVector2LengthSq(LineVector); + + XMVECTOR PointProjectionScale = XMVector2Dot(PointVector, LineVector); + PointProjectionScale = XMVectorDivide(PointProjectionScale, LengthSq); + + XMVECTOR DistanceVector = XMVectorMultiply(LineVector, PointProjectionScale); + DistanceVector = XMVectorSubtract(PointVector, DistanceVector); + + return XMVector2Length(DistanceVector); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2IntersectLine +( + FXMVECTOR Line1Point1, + FXMVECTOR Line1Point2, + FXMVECTOR Line2Point1, + GXMVECTOR Line2Point2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) || defined(_XM_ARM_NEON_INTRINSICS_) + + XMVECTOR V1 = XMVectorSubtract(Line1Point2, Line1Point1); + XMVECTOR V2 = XMVectorSubtract(Line2Point2, Line2Point1); + XMVECTOR V3 = XMVectorSubtract(Line1Point1, Line2Point1); + + XMVECTOR C1 = XMVector2Cross(V1, V2); + XMVECTOR C2 = XMVector2Cross(V2, V3); + + XMVECTOR Result; + const XMVECTOR Zero = XMVectorZero(); + if (XMVector2NearEqual(C1, Zero, g_XMEpsilon.v)) + { + if (XMVector2NearEqual(C2, Zero, g_XMEpsilon.v)) + { + // Coincident + Result = g_XMInfinity.v; + } + else + { + // Parallel + Result = g_XMQNaN.v; + } + } + else + { + // Intersection point = Line1Point1 + V1 * (C2 / C1) + XMVECTOR Scale = XMVectorReciprocal(C1); + Scale = XMVectorMultiply(C2, Scale); + Result = XMVectorMultiplyAdd(V1, Scale, Line1Point1); + } + + return Result; + +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR V1 = _mm_sub_ps(Line1Point2, Line1Point1); + XMVECTOR V2 = _mm_sub_ps(Line2Point2, Line2Point1); + XMVECTOR V3 = _mm_sub_ps(Line1Point1, Line2Point1); + // Generate the cross products + XMVECTOR C1 = XMVector2Cross(V1, V2); + XMVECTOR C2 = XMVector2Cross(V2, V3); + // If C1 is not close to epsilon, use the calculated value + XMVECTOR vResultMask = _mm_setzero_ps(); + vResultMask = _mm_sub_ps(vResultMask, C1); + vResultMask = _mm_max_ps(vResultMask, C1); + // 0xFFFFFFFF if the calculated value is to be used + vResultMask = _mm_cmpgt_ps(vResultMask, g_XMEpsilon); + // If C1 is close to epsilon, which fail type is it? INFINITY or NAN? + XMVECTOR vFailMask = _mm_setzero_ps(); + vFailMask = _mm_sub_ps(vFailMask, C2); + vFailMask = _mm_max_ps(vFailMask, C2); + vFailMask = _mm_cmple_ps(vFailMask, g_XMEpsilon); + XMVECTOR vFail = _mm_and_ps(vFailMask, g_XMInfinity); + vFailMask = _mm_andnot_ps(vFailMask, g_XMQNaN); + // vFail is NAN or INF + vFail = _mm_or_ps(vFail, vFailMask); + // Intersection point = Line1Point1 + V1 * (C2 / C1) + XMVECTOR vResult = _mm_div_ps(C2, C1); + vResult = XM_FMADD_PS(vResult, V1, Line1Point1); + // Use result, or failure value + vResult = _mm_and_ps(vResult, vResultMask); + vResultMask = _mm_andnot_ps(vResultMask, vFail); + vResult = _mm_or_ps(vResult, vResultMask); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2Transform +( + FXMVECTOR V, + FXMMATRIX M +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiplyAdd(Y, M.r[1], M.r[3]); + Result = XMVectorMultiplyAdd(X, M.r[0], Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + float32x4_t Result = vmlaq_lane_f32(M.r[3], M.r[1], VL, 1); // Y + return vmlaq_lane_f32(Result, M.r[0], VL, 0); // X +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = XM_FMADD_PS(vResult, M.r[1], M.r[3]); + XMVECTOR vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = XM_FMADD_PS(vTemp, M.r[0], vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline XMFLOAT4* XM_CALLCONV XMVector2TransformStream +( + XMFLOAT4* pOutputStream, + size_t OutputStride, + const XMFLOAT2* pInputStream, + size_t InputStride, + size_t VectorCount, + FXMMATRIX M +) noexcept +{ + assert(pOutputStream != nullptr); + assert(pInputStream != nullptr); + + assert(InputStride >= sizeof(XMFLOAT2)); + _Analysis_assume_(InputStride >= sizeof(XMFLOAT2)); + + assert(OutputStride >= sizeof(XMFLOAT4)); + _Analysis_assume_(OutputStride >= sizeof(XMFLOAT4)); + +#if defined(_XM_NO_INTRINSICS_) + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row3 = M.r[3]; + + for (size_t i = 0; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat2(reinterpret_cast(pInputVector)); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiplyAdd(Y, row1, row3); + Result = XMVectorMultiplyAdd(X, row0, Result); + + #ifdef _PREFAST_ + #pragma prefast(push) + #pragma prefast(disable : 26015, "PREfast noise: Esp:1307" ) + #endif + + XMStoreFloat4(reinterpret_cast(pOutputVector), Result); + + #ifdef _PREFAST_ + #pragma prefast(pop) + #endif + + pInputVector += InputStride; + pOutputVector += OutputStride; + } + + return pOutputStream; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row3 = M.r[3]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if ((InputStride == sizeof(XMFLOAT2)) && (OutputStride == sizeof(XMFLOAT4))) + { + for (size_t j = 0; j < four; ++j) + { + float32x4x2_t V = vld2q_f32(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + float32x2_t r3 = vget_low_f32(row3); + float32x2_t r = vget_low_f32(row0); + XMVECTOR vResult0 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), V.val[0], r, 0); // Ax+M + XMVECTOR vResult1 = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Bx+N + + XM_PREFETCH(pInputVector); + + r3 = vget_high_f32(row3); + r = vget_high_f32(row0); + XMVECTOR vResult2 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), V.val[0], r, 0); // Cx+O + XMVECTOR vResult3 = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Dx+P + + XM_PREFETCH(pInputVector + XM_CACHE_LINE_SIZE); + + r = vget_low_f32(row1); + vResult0 = vmlaq_lane_f32(vResult0, V.val[1], r, 0); // Ax+Ey+M + vResult1 = vmlaq_lane_f32(vResult1, V.val[1], r, 1); // Bx+Fy+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 2)); + + r = vget_high_f32(row1); + vResult2 = vmlaq_lane_f32(vResult2, V.val[1], r, 0); // Cx+Gy+O + vResult3 = vmlaq_lane_f32(vResult3, V.val[1], r, 1); // Dx+Hy+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 3)); + + float32x4x4_t R; + R.val[0] = vResult0; + R.val[1] = vResult1; + R.val[2] = vResult2; + R.val[3] = vResult3; + + vst4q_f32(reinterpret_cast(pOutputVector), R); + pOutputVector += sizeof(XMFLOAT4) * 4; + + i += 4; + } + } + } + + for (; i < VectorCount; i++) + { + float32x2_t V = vld1_f32(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR vResult = vmlaq_lane_f32(row3, row0, V, 0); // X + vResult = vmlaq_lane_f32(vResult, row1, V, 1); // Y + + vst1q_f32(reinterpret_cast(pOutputVector), vResult); + pOutputVector += OutputStride; + } + + return pOutputStream; +#elif defined(_XM_AVX2_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + __m256 row0 = _mm256_broadcast_ps(&M.r[0]); + __m256 row1 = _mm256_broadcast_ps(&M.r[1]); + __m256 row3 = _mm256_broadcast_ps(&M.r[3]); + + if (InputStride == sizeof(XMFLOAT2)) + { + if (OutputStride == sizeof(XMFLOAT4)) + { + if (!(reinterpret_cast(pOutputStream) & 0x1F)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + __m256 Y2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + __m256 X2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 Y1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 X1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + + __m256 vTempB = _mm256_fmadd_ps(Y1, row1, row3); + __m256 vTempB2 = _mm256_fmadd_ps(Y2, row1, row3); + __m256 vTempA = _mm256_mul_ps(X1, row0); + __m256 vTempA2 = _mm256_mul_ps(X2, row0); + vTempA = _mm256_add_ps(vTempA, vTempB); + vTempA2 = _mm256_add_ps(vTempA2, vTempB2); + + X1 = _mm256_insertf128_ps(vTempA, _mm256_castps256_ps128(vTempA2), 1); + XM256_STREAM_PS(reinterpret_cast(pOutputVector), X1); + pOutputVector += sizeof(XMFLOAT4) * 2; + + X2 = _mm256_insertf128_ps(vTempA2, _mm256_extractf128_ps(vTempA, 1), 0); + XM256_STREAM_PS(reinterpret_cast(pOutputVector), X2); + pOutputVector += sizeof(XMFLOAT4) * 2; + + i += 4; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + __m256 Y2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + __m256 X2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 Y1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 X1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + + __m256 vTempB = _mm256_fmadd_ps(Y1, row1, row3); + __m256 vTempB2 = _mm256_fmadd_ps(Y2, row1, row3); + __m256 vTempA = _mm256_mul_ps(X1, row0); + __m256 vTempA2 = _mm256_mul_ps(X2, row0); + vTempA = _mm256_add_ps(vTempA, vTempB); + vTempA2 = _mm256_add_ps(vTempA2, vTempB2); + + X1 = _mm256_insertf128_ps(vTempA, _mm256_castps256_ps128(vTempA2), 1); + _mm256_storeu_ps(reinterpret_cast(pOutputVector), X1); + pOutputVector += sizeof(XMFLOAT4) * 2; + + X2 = _mm256_insertf128_ps(vTempA2, _mm256_extractf128_ps(vTempA, 1), 0); + _mm256_storeu_ps(reinterpret_cast(pOutputVector), X2); + pOutputVector += sizeof(XMFLOAT4) * 2; + + i += 4; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < four; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + __m256 Y2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + __m256 X2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 Y1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 X1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + + __m256 vTempB = _mm256_fmadd_ps(Y1, row1, row3); + __m256 vTempB2 = _mm256_fmadd_ps(Y2, row1, row3); + __m256 vTempA = _mm256_mul_ps(X1, row0); + __m256 vTempA2 = _mm256_mul_ps(X2, row0); + vTempA = _mm256_add_ps(vTempA, vTempB); + vTempA2 = _mm256_add_ps(vTempA2, vTempB2); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), _mm256_castps256_ps128(vTempA)); + pOutputVector += OutputStride; + + _mm_storeu_ps(reinterpret_cast(pOutputVector), _mm256_castps256_ps128(vTempA2)); + pOutputVector += OutputStride; + + _mm_storeu_ps(reinterpret_cast(pOutputVector), _mm256_extractf128_ps(vTempA, 1)); + pOutputVector += OutputStride; + + _mm_storeu_ps(reinterpret_cast(pOutputVector), _mm256_extractf128_ps(vTempA2, 1)); + pOutputVector += OutputStride; + + i += 4; + } + } + } + } + + if (i < VectorCount) + { + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row3 = M.r[3]; + + for (; i < VectorCount; i++) + { + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(xy, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(xy, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + } + + XM_SFENCE(); + + return pOutputStream; +#elif defined(_XM_SSE_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row3 = M.r[3]; + + size_t i = 0; + size_t two = VectorCount >> 1; + if (two > 0) + { + if (InputStride == sizeof(XMFLOAT2)) + { + if (!(reinterpret_cast(pOutputStream) & 0xF) && !(OutputStride & 0xF)) + { + // Packed input, aligned output + for (size_t j = 0; j < two; ++j) + { + XMVECTOR V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 2; + + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + X = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + + vTemp = XM_FMADD_PS(Y, row1, row3); + vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + i += 2; + } + } + else + { + // Packed input, unaligned output + for (size_t j = 0; j < two; ++j) + { + XMVECTOR V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 2; + + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + X = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + + vTemp = XM_FMADD_PS(Y, row1, row3); + vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + i += 2; + } + } + } + } + + if (!(reinterpret_cast(pInputVector) & 0xF) && !(InputStride & 0xF)) + { + if (!(reinterpret_cast(pOutputStream) & 0xF) && !(OutputStride & 0xF)) + { + // Aligned input, aligned output + for (; i < VectorCount; i++) + { + XMVECTOR V = _mm_castsi128_ps(_mm_loadl_epi64(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + } + else + { + // Aligned input, unaligned output + for (; i < VectorCount; i++) + { + XMVECTOR V = _mm_castsi128_ps(_mm_loadl_epi64(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + } + } + else + { + // Unaligned input + for (; i < VectorCount; i++) + { + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(xy, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(xy, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + } + + XM_SFENCE(); + + return pOutputStream; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2TransformCoord +( + FXMVECTOR V, + FXMMATRIX M +) noexcept +{ + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiplyAdd(Y, M.r[1], M.r[3]); + Result = XMVectorMultiplyAdd(X, M.r[0], Result); + + XMVECTOR W = XMVectorSplatW(Result); + return XMVectorDivide(Result, W); +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline XMFLOAT2* XM_CALLCONV XMVector2TransformCoordStream +( + XMFLOAT2* pOutputStream, + size_t OutputStride, + const XMFLOAT2* pInputStream, + size_t InputStride, + size_t VectorCount, + FXMMATRIX M +) noexcept +{ + assert(pOutputStream != nullptr); + assert(pInputStream != nullptr); + + assert(InputStride >= sizeof(XMFLOAT2)); + _Analysis_assume_(InputStride >= sizeof(XMFLOAT2)); + + assert(OutputStride >= sizeof(XMFLOAT2)); + _Analysis_assume_(OutputStride >= sizeof(XMFLOAT2)); + +#if defined(_XM_NO_INTRINSICS_) + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row3 = M.r[3]; + + for (size_t i = 0; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat2(reinterpret_cast(pInputVector)); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiplyAdd(Y, row1, row3); + Result = XMVectorMultiplyAdd(X, row0, Result); + + XMVECTOR W = XMVectorSplatW(Result); + + Result = XMVectorDivide(Result, W); + + #ifdef _PREFAST_ + #pragma prefast(push) + #pragma prefast(disable : 26015, "PREfast noise: Esp:1307" ) + #endif + + XMStoreFloat2(reinterpret_cast(pOutputVector), Result); + + #ifdef _PREFAST_ + #pragma prefast(pop) + #endif + + pInputVector += InputStride; + pOutputVector += OutputStride; + } + + return pOutputStream; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row3 = M.r[3]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if ((InputStride == sizeof(XMFLOAT2)) && (OutputStride == sizeof(XMFLOAT2))) + { + for (size_t j = 0; j < four; ++j) + { + float32x4x2_t V = vld2q_f32(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + float32x2_t r3 = vget_low_f32(row3); + float32x2_t r = vget_low_f32(row0); + XMVECTOR vResult0 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), V.val[0], r, 0); // Ax+M + XMVECTOR vResult1 = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Bx+N + + XM_PREFETCH(pInputVector); + + r3 = vget_high_f32(row3); + r = vget_high_f32(row0); + XMVECTOR W = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Dx+P + + XM_PREFETCH(pInputVector + XM_CACHE_LINE_SIZE); + + r = vget_low_f32(row1); + vResult0 = vmlaq_lane_f32(vResult0, V.val[1], r, 0); // Ax+Ey+M + vResult1 = vmlaq_lane_f32(vResult1, V.val[1], r, 1); // Bx+Fy+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 2)); + + r = vget_high_f32(row1); + W = vmlaq_lane_f32(W, V.val[1], r, 1); // Dx+Hy+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 3)); + + #if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + V.val[0] = vdivq_f32(vResult0, W); + V.val[1] = vdivq_f32(vResult1, W); + #else + // 2 iterations of Newton-Raphson refinement of reciprocal + float32x4_t Reciprocal = vrecpeq_f32(W); + float32x4_t S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + + V.val[0] = vmulq_f32(vResult0, Reciprocal); + V.val[1] = vmulq_f32(vResult1, Reciprocal); + #endif + + vst2q_f32(reinterpret_cast(pOutputVector), V); + pOutputVector += sizeof(XMFLOAT2) * 4; + + i += 4; + } + } + } + + for (; i < VectorCount; i++) + { + float32x2_t V = vld1_f32(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR vResult = vmlaq_lane_f32(row3, row0, V, 0); // X + vResult = vmlaq_lane_f32(vResult, row1, V, 1); // Y + + V = vget_high_f32(vResult); + float32x2_t W = vdup_lane_f32(V, 1); + + #if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + V = vget_low_f32(vResult); + V = vdiv_f32(V, W); + #else + // 2 iterations of Newton-Raphson refinement of reciprocal for W + float32x2_t Reciprocal = vrecpe_f32(W); + float32x2_t S = vrecps_f32(Reciprocal, W); + Reciprocal = vmul_f32(S, Reciprocal); + S = vrecps_f32(Reciprocal, W); + Reciprocal = vmul_f32(S, Reciprocal); + + V = vget_low_f32(vResult); + V = vmul_f32(V, Reciprocal); + #endif + + vst1_f32(reinterpret_cast(pOutputVector), V); + pOutputVector += OutputStride; + } + + return pOutputStream; +#elif defined(_XM_AVX2_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + __m256 row0 = _mm256_broadcast_ps(&M.r[0]); + __m256 row1 = _mm256_broadcast_ps(&M.r[1]); + __m256 row3 = _mm256_broadcast_ps(&M.r[3]); + + if (InputStride == sizeof(XMFLOAT2)) + { + if (OutputStride == sizeof(XMFLOAT2)) + { + if (!(reinterpret_cast(pOutputStream) & 0x1F)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + __m256 Y2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + __m256 X2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 Y1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 X1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + + __m256 vTempB = _mm256_fmadd_ps(Y1, row1, row3); + __m256 vTempB2 = _mm256_fmadd_ps(Y2, row1, row3); + __m256 vTempA = _mm256_mul_ps(X1, row0); + __m256 vTempA2 = _mm256_mul_ps(X2, row0); + vTempA = _mm256_add_ps(vTempA, vTempB); + vTempA2 = _mm256_add_ps(vTempA2, vTempB2); + + __m256 W = _mm256_shuffle_ps(vTempA, vTempA, _MM_SHUFFLE(3, 3, 3, 3)); + vTempA = _mm256_div_ps(vTempA, W); + + W = _mm256_shuffle_ps(vTempA2, vTempA2, _MM_SHUFFLE(3, 3, 3, 3)); + vTempA2 = _mm256_div_ps(vTempA2, W); + + X1 = _mm256_shuffle_ps(vTempA, vTempA2, 0x44); + XM256_STREAM_PS(reinterpret_cast(pOutputVector), X1); + pOutputVector += sizeof(XMFLOAT2) * 4; + + i += 4; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + __m256 Y2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + __m256 X2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 Y1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 X1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + + __m256 vTempB = _mm256_fmadd_ps(Y1, row1, row3); + __m256 vTempB2 = _mm256_fmadd_ps(Y2, row1, row3); + __m256 vTempA = _mm256_mul_ps(X1, row0); + __m256 vTempA2 = _mm256_mul_ps(X2, row0); + vTempA = _mm256_add_ps(vTempA, vTempB); + vTempA2 = _mm256_add_ps(vTempA2, vTempB2); + + __m256 W = _mm256_shuffle_ps(vTempA, vTempA, _MM_SHUFFLE(3, 3, 3, 3)); + vTempA = _mm256_div_ps(vTempA, W); + + W = _mm256_shuffle_ps(vTempA2, vTempA2, _MM_SHUFFLE(3, 3, 3, 3)); + vTempA2 = _mm256_div_ps(vTempA2, W); + + X1 = _mm256_shuffle_ps(vTempA, vTempA2, 0x44); + _mm256_storeu_ps(reinterpret_cast(pOutputVector), X1); + pOutputVector += sizeof(XMFLOAT2) * 4; + + i += 4; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < four; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + __m256 Y2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + __m256 X2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 Y1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 X1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + + __m256 vTempB = _mm256_fmadd_ps(Y1, row1, row3); + __m256 vTempB2 = _mm256_fmadd_ps(Y2, row1, row3); + __m256 vTempA = _mm256_mul_ps(X1, row0); + __m256 vTempA2 = _mm256_mul_ps(X2, row0); + vTempA = _mm256_add_ps(vTempA, vTempB); + vTempA2 = _mm256_add_ps(vTempA2, vTempB2); + + __m256 W = _mm256_shuffle_ps(vTempA, vTempA, _MM_SHUFFLE(3, 3, 3, 3)); + vTempA = _mm256_div_ps(vTempA, W); + + W = _mm256_shuffle_ps(vTempA2, vTempA2, _MM_SHUFFLE(3, 3, 3, 3)); + vTempA2 = _mm256_div_ps(vTempA2, W); + + _mm_store_sd(reinterpret_cast(pOutputVector), + _mm_castps_pd(_mm256_castps256_ps128(vTempA))); + pOutputVector += OutputStride; + + _mm_store_sd(reinterpret_cast(pOutputVector), + _mm_castps_pd(_mm256_castps256_ps128(vTempA2))); + pOutputVector += OutputStride; + + _mm_store_sd(reinterpret_cast(pOutputVector), + _mm_castps_pd(_mm256_extractf128_ps(vTempA, 1))); + pOutputVector += OutputStride; + + _mm_store_sd(reinterpret_cast(pOutputVector), + _mm_castps_pd(_mm256_extractf128_ps(vTempA2, 1))); + pOutputVector += OutputStride; + + i += 4; + } + } + } + } + + if (i < VectorCount) + { + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row3 = M.r[3]; + + for (; i < VectorCount; i++) + { + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(xy, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(xy, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + } + } + + XM_SFENCE(); + + return pOutputStream; +#elif defined(_XM_SSE_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row3 = M.r[3]; + + size_t i = 0; + size_t two = VectorCount >> 1; + if (two > 0) + { + if (InputStride == sizeof(XMFLOAT2)) + { + if (OutputStride == sizeof(XMFLOAT2)) + { + if (!(reinterpret_cast(pOutputStream) & 0xF)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < two; ++j) + { + XMVECTOR V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 2; + + // Result 1 + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + XMVECTOR V1 = _mm_div_ps(vTemp, W); + + // Result 2 + Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + X = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + + vTemp = XM_FMADD_PS(Y, row1, row3); + vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + XMVECTOR V2 = _mm_div_ps(vTemp, W); + + vTemp = _mm_movelh_ps(V1, V2); + + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += sizeof(XMFLOAT2) * 2; + + i += 2; + } + } + else + { + // Packed input, unaligned & packed output + for (size_t j = 0; j < two; ++j) + { + XMVECTOR V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 2; + + // Result 1 + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + XMVECTOR V1 = _mm_div_ps(vTemp, W); + + // Result 2 + Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + X = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + + vTemp = XM_FMADD_PS(Y, row1, row3); + vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + XMVECTOR V2 = _mm_div_ps(vTemp, W); + + vTemp = _mm_movelh_ps(V1, V2); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += sizeof(XMFLOAT2) * 2; + + i += 2; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < two; ++j) + { + XMVECTOR V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 2; + + // Result 1 + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + vTemp = _mm_div_ps(vTemp, W); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + + // Result 2 + Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + X = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + + vTemp = XM_FMADD_PS(Y, row1, row3); + vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + vTemp = _mm_div_ps(vTemp, W); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + + i += 2; + } + } + } + } + + if (!(reinterpret_cast(pInputVector) & 0xF) && !(InputStride & 0xF)) + { + // Aligned input + for (; i < VectorCount; i++) + { + XMVECTOR V = _mm_castsi128_ps(_mm_loadl_epi64(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + vTemp = _mm_div_ps(vTemp, W); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + } + } + else + { + // Unaligned input + for (; i < VectorCount; i++) + { + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(xy, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(xy, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Y, row1, row3); + XMVECTOR vTemp2 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + vTemp = _mm_div_ps(vTemp, W); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + } + } + + XM_SFENCE(); + + return pOutputStream; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector2TransformNormal +( + FXMVECTOR V, + FXMMATRIX M +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiply(Y, M.r[1]); + Result = XMVectorMultiplyAdd(X, M.r[0], Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + float32x4_t Result = vmulq_lane_f32(M.r[1], VL, 1); // Y + return vmlaq_lane_f32(Result, M.r[0], VL, 0); // X +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = _mm_mul_ps(vResult, M.r[1]); + XMVECTOR vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = XM_FMADD_PS(vTemp, M.r[0], vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline XMFLOAT2* XM_CALLCONV XMVector2TransformNormalStream +( + XMFLOAT2* pOutputStream, + size_t OutputStride, + const XMFLOAT2* pInputStream, + size_t InputStride, + size_t VectorCount, + FXMMATRIX M +) noexcept +{ + assert(pOutputStream != nullptr); + assert(pInputStream != nullptr); + + assert(InputStride >= sizeof(XMFLOAT2)); + _Analysis_assume_(InputStride >= sizeof(XMFLOAT2)); + + assert(OutputStride >= sizeof(XMFLOAT2)); + _Analysis_assume_(OutputStride >= sizeof(XMFLOAT2)); + +#if defined(_XM_NO_INTRINSICS_) + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + + for (size_t i = 0; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat2(reinterpret_cast(pInputVector)); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiply(Y, row1); + Result = XMVectorMultiplyAdd(X, row0, Result); + + #ifdef _PREFAST_ + #pragma prefast(push) + #pragma prefast(disable : 26015, "PREfast noise: Esp:1307" ) + #endif + + XMStoreFloat2(reinterpret_cast(pOutputVector), Result); + + #ifdef _PREFAST_ + #pragma prefast(pop) + #endif + + pInputVector += InputStride; + pOutputVector += OutputStride; + } + + return pOutputStream; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if ((InputStride == sizeof(XMFLOAT2)) && (OutputStride == sizeof(XMFLOAT2))) + { + for (size_t j = 0; j < four; ++j) + { + float32x4x2_t V = vld2q_f32(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + float32x2_t r = vget_low_f32(row0); + XMVECTOR vResult0 = vmulq_lane_f32(V.val[0], r, 0); // Ax + XMVECTOR vResult1 = vmulq_lane_f32(V.val[0], r, 1); // Bx + + XM_PREFETCH(pInputVector); + XM_PREFETCH(pInputVector + XM_CACHE_LINE_SIZE); + + r = vget_low_f32(row1); + vResult0 = vmlaq_lane_f32(vResult0, V.val[1], r, 0); // Ax+Ey + vResult1 = vmlaq_lane_f32(vResult1, V.val[1], r, 1); // Bx+Fy + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 2)); + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 3)); + + V.val[0] = vResult0; + V.val[1] = vResult1; + + vst2q_f32(reinterpret_cast(pOutputVector), V); + pOutputVector += sizeof(XMFLOAT2) * 4; + + i += 4; + } + } + } + + for (; i < VectorCount; i++) + { + float32x2_t V = vld1_f32(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR vResult = vmulq_lane_f32(row0, V, 0); // X + vResult = vmlaq_lane_f32(vResult, row1, V, 1); // Y + + V = vget_low_f32(vResult); + vst1_f32(reinterpret_cast(pOutputVector), V); + pOutputVector += OutputStride; + } + + return pOutputStream; +#elif defined(_XM_AVX2_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + __m256 row0 = _mm256_broadcast_ps(&M.r[0]); + __m256 row1 = _mm256_broadcast_ps(&M.r[1]); + + if (InputStride == sizeof(XMFLOAT2)) + { + if (OutputStride == sizeof(XMFLOAT2)) + { + if (!(reinterpret_cast(pOutputStream) & 0x1F)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + __m256 Y2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + __m256 X2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 Y1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 X1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + + __m256 vTempA = _mm256_mul_ps(Y1, row1); + __m256 vTempB = _mm256_mul_ps(Y2, row1); + vTempA = _mm256_fmadd_ps(X1, row0, vTempA); + vTempB = _mm256_fmadd_ps(X2, row0, vTempB); + + X1 = _mm256_shuffle_ps(vTempA, vTempB, 0x44); + XM256_STREAM_PS(reinterpret_cast(pOutputVector), X1); + pOutputVector += sizeof(XMFLOAT2) * 4; + + i += 4; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + __m256 Y2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + __m256 X2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 Y1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 X1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + + __m256 vTempA = _mm256_mul_ps(Y1, row1); + __m256 vTempB = _mm256_mul_ps(Y2, row1); + vTempA = _mm256_fmadd_ps(X1, row0, vTempA); + vTempB = _mm256_fmadd_ps(X2, row0, vTempB); + + X1 = _mm256_shuffle_ps(vTempA, vTempB, 0x44); + _mm256_storeu_ps(reinterpret_cast(pOutputVector), X1); + pOutputVector += sizeof(XMFLOAT2) * 4; + + i += 4; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < four; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 4; + + __m256 Y2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + __m256 X2 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 Y1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 X1 = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + + __m256 vTempA = _mm256_mul_ps(Y1, row1); + __m256 vTempB = _mm256_mul_ps(Y2, row1); + vTempA = _mm256_fmadd_ps(X1, row0, vTempA); + vTempB = _mm256_fmadd_ps(X2, row0, vTempB); + + _mm_store_sd(reinterpret_cast(pOutputVector), + _mm_castps_pd(_mm256_castps256_ps128(vTempA))); + pOutputVector += OutputStride; + + _mm_store_sd(reinterpret_cast(pOutputVector), + _mm_castps_pd(_mm256_castps256_ps128(vTempB))); + pOutputVector += OutputStride; + + _mm_store_sd(reinterpret_cast(pOutputVector), + _mm_castps_pd(_mm256_extractf128_ps(vTempA, 1))); + pOutputVector += OutputStride; + + _mm_store_sd(reinterpret_cast(pOutputVector), + _mm_castps_pd(_mm256_extractf128_ps(vTempB, 1))); + pOutputVector += OutputStride; + + i += 4; + } + } + } + } + + if (i < VectorCount) + { + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + + for (; i < VectorCount; i++) + { + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(xy, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(xy, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Y, row1); + vTemp = XM_FMADD_PS(X, row0, vTemp); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + } + } + + XM_SFENCE(); + + return pOutputStream; +#elif defined(_XM_SSE_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + + size_t i = 0; + size_t two = VectorCount >> 1; + if (two > 0) + { + if (InputStride == sizeof(XMFLOAT2)) + { + if (OutputStride == sizeof(XMFLOAT2)) + { + if (!(reinterpret_cast(pOutputStream) & 0xF)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < two; ++j) + { + XMVECTOR V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 2; + + // Result 1 + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Y, row1); + XMVECTOR V1 = XM_FMADD_PS(X, row0, vTemp); + + // Result 2 + Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + X = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + + vTemp = _mm_mul_ps(Y, row1); + XMVECTOR V2 = XM_FMADD_PS(X, row0, vTemp); + + vTemp = _mm_movelh_ps(V1, V2); + + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += sizeof(XMFLOAT2) * 2; + + i += 2; + } + } + else + { + // Packed input, unaligned & packed output + for (size_t j = 0; j < two; ++j) + { + XMVECTOR V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 2; + + // Result 1 + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Y, row1); + XMVECTOR V1 = XM_FMADD_PS(X, row0, vTemp); + + // Result 2 + Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + X = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + + vTemp = _mm_mul_ps(Y, row1); + XMVECTOR V2 = XM_FMADD_PS(X, row0, vTemp); + + vTemp = _mm_movelh_ps(V1, V2); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += sizeof(XMFLOAT2) * 2; + + i += 2; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < two; ++j) + { + XMVECTOR V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT2) * 2; + + // Result 1 + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Y, row1); + vTemp = XM_FMADD_PS(X, row0, vTemp); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + + // Result 2 + Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + X = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + + vTemp = _mm_mul_ps(Y, row1); + vTemp = XM_FMADD_PS(X, row0, vTemp); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + + i += 2; + } + } + } + } + + if (!(reinterpret_cast(pInputVector) & 0xF) && !(InputStride & 0xF)) + { + // Aligned input + for (; i < VectorCount; i++) + { + XMVECTOR V = _mm_castsi128_ps(_mm_loadl_epi64(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Y, row1); + vTemp = XM_FMADD_PS(X, row0, vTemp); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + } + } + else + { + // Unaligned input + for (; i < VectorCount; i++) + { + __m128 xy = _mm_castpd_ps(_mm_load_sd(reinterpret_cast(pInputVector))); + pInputVector += InputStride; + + XMVECTOR Y = XM_PERMUTE_PS(xy, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(xy, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Y, row1); + vTemp = XM_FMADD_PS(X, row0, vTemp); + + _mm_store_sd(reinterpret_cast(pOutputVector), _mm_castps_pd(vTemp)); + pOutputVector += OutputStride; + } + } + + XM_SFENCE(); + + return pOutputStream; +#endif +} + +/**************************************************************************** + * + * 3D Vector + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparison operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3Equal +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] == V2.vector4_f32[0]) && (V1.vector4_f32[1] == V2.vector4_f32[1]) && (V1.vector4_f32[2] == V2.vector4_f32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) == 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpeq_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 7) == 7) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector3EqualR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + uint32_t CR = 0; + if ((V1.vector4_f32[0] == V2.vector4_f32[0]) && + (V1.vector4_f32[1] == V2.vector4_f32[1]) && + (V1.vector4_f32[2] == V2.vector4_f32[2])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_f32[0] != V2.vector4_f32[0]) && + (V1.vector4_f32[1] != V2.vector4_f32[1]) && + (V1.vector4_f32[2] != V2.vector4_f32[2])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU; + + uint32_t CR = 0; + if (r == 0xFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpeq_ps(V1, V2); + int iTest = _mm_movemask_ps(vTemp) & 7; + uint32_t CR = 0; + if (iTest == 7) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3EqualInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_u32[0] == V2.vector4_u32[0]) && (V1.vector4_u32[1] == V2.vector4_u32[1]) && (V1.vector4_u32[2] == V2.vector4_u32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2)); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) == 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return (((_mm_movemask_ps(_mm_castsi128_ps(vTemp)) & 7) == 7) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector3EqualIntR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + uint32_t CR = 0; + if ((V1.vector4_u32[0] == V2.vector4_u32[0]) && + (V1.vector4_u32[1] == V2.vector4_u32[1]) && + (V1.vector4_u32[2] == V2.vector4_u32[2])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_u32[0] != V2.vector4_u32[0]) && + (V1.vector4_u32[1] != V2.vector4_u32[1]) && + (V1.vector4_u32[2] != V2.vector4_u32[2])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2)); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU; + + uint32_t CR = 0; + if (r == 0xFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + int iTemp = _mm_movemask_ps(_mm_castsi128_ps(vTemp)) & 7; + uint32_t CR = 0; + if (iTemp == 7) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTemp) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3NearEqual +( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR Epsilon +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float dx, dy, dz; + + dx = fabsf(V1.vector4_f32[0] - V2.vector4_f32[0]); + dy = fabsf(V1.vector4_f32[1] - V2.vector4_f32[1]); + dz = fabsf(V1.vector4_f32[2] - V2.vector4_f32[2]); + return (((dx <= Epsilon.vector4_f32[0]) && + (dy <= Epsilon.vector4_f32[1]) && + (dz <= Epsilon.vector4_f32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vDelta = vsubq_f32(V1, V2); +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + uint32x4_t vResult = vacleq_f32(vDelta, Epsilon); +#else + uint32x4_t vResult = vcleq_f32(vabsq_f32(vDelta), Epsilon); +#endif + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) == 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + // Get the difference + XMVECTOR vDelta = _mm_sub_ps(V1, V2); + // Get the absolute value of the difference + XMVECTOR vTemp = _mm_setzero_ps(); + vTemp = _mm_sub_ps(vTemp, vDelta); + vTemp = _mm_max_ps(vTemp, vDelta); + vTemp = _mm_cmple_ps(vTemp, Epsilon); + // w is don't care + return (((_mm_movemask_ps(vTemp) & 7) == 0x7) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3NotEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] != V2.vector4_f32[0]) || (V1.vector4_f32[1] != V2.vector4_f32[1]) || (V1.vector4_f32[2] != V2.vector4_f32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) != 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpeq_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 7) != 7) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3NotEqualInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_u32[0] != V2.vector4_u32[0]) || (V1.vector4_u32[1] != V2.vector4_u32[1]) || (V1.vector4_u32[2] != V2.vector4_u32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2)); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) != 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return (((_mm_movemask_ps(_mm_castsi128_ps(vTemp)) & 7) != 7) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3Greater +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] > V2.vector4_f32[0]) && (V1.vector4_f32[1] > V2.vector4_f32[1]) && (V1.vector4_f32[2] > V2.vector4_f32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgtq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) == 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpgt_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 7) == 7) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector3GreaterR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + uint32_t CR = 0; + if ((V1.vector4_f32[0] > V2.vector4_f32[0]) && + (V1.vector4_f32[1] > V2.vector4_f32[1]) && + (V1.vector4_f32[2] > V2.vector4_f32[2])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_f32[0] <= V2.vector4_f32[0]) && + (V1.vector4_f32[1] <= V2.vector4_f32[1]) && + (V1.vector4_f32[2] <= V2.vector4_f32[2])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgtq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU; + + uint32_t CR = 0; + if (r == 0xFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpgt_ps(V1, V2); + uint32_t CR = 0; + int iTest = _mm_movemask_ps(vTemp) & 7; + if (iTest == 7) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3GreaterOrEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] >= V2.vector4_f32[0]) && (V1.vector4_f32[1] >= V2.vector4_f32[1]) && (V1.vector4_f32[2] >= V2.vector4_f32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgeq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) == 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpge_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 7) == 7) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector3GreaterOrEqualR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + uint32_t CR = 0; + if ((V1.vector4_f32[0] >= V2.vector4_f32[0]) && + (V1.vector4_f32[1] >= V2.vector4_f32[1]) && + (V1.vector4_f32[2] >= V2.vector4_f32[2])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_f32[0] < V2.vector4_f32[0]) && + (V1.vector4_f32[1] < V2.vector4_f32[1]) && + (V1.vector4_f32[2] < V2.vector4_f32[2])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgeq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU; + + uint32_t CR = 0; + if (r == 0xFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpge_ps(V1, V2); + uint32_t CR = 0; + int iTest = _mm_movemask_ps(vTemp) & 7; + if (iTest == 7) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3Less +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] < V2.vector4_f32[0]) && (V1.vector4_f32[1] < V2.vector4_f32[1]) && (V1.vector4_f32[2] < V2.vector4_f32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcltq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) == 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmplt_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 7) == 7) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3LessOrEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] <= V2.vector4_f32[0]) && (V1.vector4_f32[1] <= V2.vector4_f32[1]) && (V1.vector4_f32[2] <= V2.vector4_f32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcleq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) == 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmple_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 7) == 7) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3InBounds +( + FXMVECTOR V, + FXMVECTOR Bounds +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V.vector4_f32[0] <= Bounds.vector4_f32[0] && V.vector4_f32[0] >= -Bounds.vector4_f32[0]) && + (V.vector4_f32[1] <= Bounds.vector4_f32[1] && V.vector4_f32[1] >= -Bounds.vector4_f32[1]) && + (V.vector4_f32[2] <= Bounds.vector4_f32[2] && V.vector4_f32[2] >= -Bounds.vector4_f32[2])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Test if less than or equal + uint32x4_t ivTemp1 = vcleq_f32(V, Bounds); + // Negate the bounds + float32x4_t vTemp2 = vnegq_f32(Bounds); + // Test if greater or equal (Reversed) + uint32x4_t ivTemp2 = vcleq_f32(vTemp2, V); + // Blend answers + ivTemp1 = vandq_u32(ivTemp1, ivTemp2); + // in bounds? + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(ivTemp1)), vget_high_u8(vreinterpretq_u8_u32(ivTemp1))); + uint16x4x2_t vTemp3 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp3.val[1]), 1) & 0xFFFFFFU) == 0xFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + // Test if less than or equal + XMVECTOR vTemp1 = _mm_cmple_ps(V, Bounds); + // Negate the bounds + XMVECTOR vTemp2 = _mm_mul_ps(Bounds, g_XMNegativeOne); + // Test if greater or equal (Reversed) + vTemp2 = _mm_cmple_ps(vTemp2, V); + // Blend answers + vTemp1 = _mm_and_ps(vTemp1, vTemp2); + // x,y and z in bounds? (w is don't care) + return (((_mm_movemask_ps(vTemp1) & 0x7) == 0x7) != 0); +#else + return XMComparisonAllInBounds(XMVector3InBoundsR(V, Bounds)); +#endif +} + +//------------------------------------------------------------------------------ + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(push) +#pragma float_control(precise, on) +#endif + +inline bool XM_CALLCONV XMVector3IsNaN(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + return (XMISNAN(V.vector4_f32[0]) || + XMISNAN(V.vector4_f32[1]) || + XMISNAN(V.vector4_f32[2])); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(__clang__) && defined(__FINITE_MATH_ONLY__) + return isnan(vgetq_lane_f32(V, 0)) || isnan(vgetq_lane_f32(V, 1)) || isnan(vgetq_lane_f32(V, 2)); +#else +// Test against itself. NaN is always not equal + uint32x4_t vTempNan = vceqq_f32(V, V); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vTempNan)), vget_high_u8(vreinterpretq_u8_u32(vTempNan))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + // If x or y or z are NaN, the mask is zero + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) != 0xFFFFFFU); +#endif +#elif defined(_XM_SSE_INTRINSICS_) +#if defined(__clang__) && defined(__FINITE_MATH_ONLY__) + XM_ALIGNED_DATA(16) float tmp[4]; + _mm_store_ps(tmp, V); + return isnan(tmp[0]) || isnan(tmp[1]) || isnan(tmp[2]); +#else +// Test against itself. NaN is always not equal + XMVECTOR vTempNan = _mm_cmpneq_ps(V, V); + // If x or y or z are NaN, the mask is non-zero + return ((_mm_movemask_ps(vTempNan) & 7) != 0); +#endif +#endif +} + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(pop) +#endif + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector3IsInfinite(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (XMISINF(V.vector4_f32[0]) || + XMISINF(V.vector4_f32[1]) || + XMISINF(V.vector4_f32[2])); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Mask off the sign bit + uint32x4_t vTempInf = vandq_u32(vreinterpretq_u32_f32(V), g_XMAbsMask); + // Compare to infinity + vTempInf = vceqq_f32(vreinterpretq_f32_u32(vTempInf), g_XMInfinity); + // If any are infinity, the signs are true. + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vTempInf)), vget_high_u8(vreinterpretq_u8_u32(vTempInf))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return ((vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) & 0xFFFFFFU) != 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Mask off the sign bit + __m128 vTemp = _mm_and_ps(V, g_XMAbsMask); + // Compare to infinity + vTemp = _mm_cmpeq_ps(vTemp, g_XMInfinity); + // If x,y or z are infinity, the signs are true. + return ((_mm_movemask_ps(vTemp) & 7) != 0); +#endif +} + +//------------------------------------------------------------------------------ +// Computation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Dot +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float fValue = V1.vector4_f32[0] * V2.vector4_f32[0] + V1.vector4_f32[1] * V2.vector4_f32[1] + V1.vector4_f32[2] * V2.vector4_f32[2]; + XMVECTORF32 vResult; + vResult.f[0] = + vResult.f[1] = + vResult.f[2] = + vResult.f[3] = fValue; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vTemp = vmulq_f32(V1, V2); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vpadd_f32(v1, v1); + v2 = vdup_lane_f32(v2, 0); + v1 = vadd_f32(v1, v2); + return vcombine_f32(v1, v1); +#elif defined(_XM_SSE4_INTRINSICS_) + return _mm_dp_ps(V1, V2, 0x7f); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vTemp = _mm_mul_ps(V1, V2); + vTemp = _mm_and_ps(vTemp, g_XMMask3); + vTemp = _mm_hadd_ps(vTemp, vTemp); + return _mm_hadd_ps(vTemp, vTemp); +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product + XMVECTOR vDot = _mm_mul_ps(V1, V2); + // x=Dot.vector4_f32[1], y=Dot.vector4_f32[2] + XMVECTOR vTemp = XM_PERMUTE_PS(vDot, _MM_SHUFFLE(2, 1, 2, 1)); + // Result.vector4_f32[0] = x+y + vDot = _mm_add_ss(vDot, vTemp); + // x=Dot.vector4_f32[2] + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(1, 1, 1, 1)); + // Result.vector4_f32[0] = (x+y)+z + vDot = _mm_add_ss(vDot, vTemp); + // Splat x + return XM_PERMUTE_PS(vDot, _MM_SHUFFLE(0, 0, 0, 0)); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Cross +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + // [ V1.y*V2.z - V1.z*V2.y, V1.z*V2.x - V1.x*V2.z, V1.x*V2.y - V1.y*V2.x ] + +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + (V1.vector4_f32[1] * V2.vector4_f32[2]) - (V1.vector4_f32[2] * V2.vector4_f32[1]), + (V1.vector4_f32[2] * V2.vector4_f32[0]) - (V1.vector4_f32[0] * V2.vector4_f32[2]), + (V1.vector4_f32[0] * V2.vector4_f32[1]) - (V1.vector4_f32[1] * V2.vector4_f32[0]), + 0.0f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t v1xy = vget_low_f32(V1); + float32x2_t v2xy = vget_low_f32(V2); + + float32x2_t v1yx = vrev64_f32(v1xy); + float32x2_t v2yx = vrev64_f32(v2xy); + + float32x2_t v1zz = vdup_lane_f32(vget_high_f32(V1), 0); + float32x2_t v2zz = vdup_lane_f32(vget_high_f32(V2), 0); + + XMVECTOR vResult = vmulq_f32(vcombine_f32(v1yx, v1xy), vcombine_f32(v2zz, v2yx)); + vResult = vmlsq_f32(vResult, vcombine_f32(v1zz, v1yx), vcombine_f32(v2yx, v2xy)); + vResult = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(vResult), g_XMFlipY)); + return vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(vResult), g_XMMask3)); +#elif defined(_XM_SSE_INTRINSICS_) + // y1,z1,x1,w1 + XMVECTOR vTemp1 = XM_PERMUTE_PS(V1, _MM_SHUFFLE(3, 0, 2, 1)); + // z2,x2,y2,w2 + XMVECTOR vTemp2 = XM_PERMUTE_PS(V2, _MM_SHUFFLE(3, 1, 0, 2)); + // Perform the left operation + XMVECTOR vResult = _mm_mul_ps(vTemp1, vTemp2); + // z1,x1,y1,w1 + vTemp1 = XM_PERMUTE_PS(vTemp1, _MM_SHUFFLE(3, 0, 2, 1)); + // y2,z2,x2,w2 + vTemp2 = XM_PERMUTE_PS(vTemp2, _MM_SHUFFLE(3, 1, 0, 2)); + // Perform the right operation + vResult = XM_FNMADD_PS(vTemp1, vTemp2, vResult); + // Set w to zero + return _mm_and_ps(vResult, g_XMMask3); +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3LengthSq(FXMVECTOR V) noexcept +{ + return XMVector3Dot(V, V); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3ReciprocalLengthEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + + Result = XMVector3LengthSq(V); + Result = XMVectorReciprocalSqrtEst(Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot3 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vpadd_f32(v1, v1); + v2 = vdup_lane_f32(v2, 0); + v1 = vadd_f32(v1, v2); + // Reciprocal sqrt (estimate) + v2 = vrsqrte_f32(v1); + return vcombine_f32(v2, v2); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + return _mm_rsqrt_ps(vTemp); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_and_ps(vLengthSq, g_XMMask3); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_rsqrt_ps(vLengthSq); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y and z + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has z and y + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 2, 1, 2)); + // x+z, y + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + // y,y,y,y + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(1, 1, 1, 1)); + // x+z+y,??,??,?? + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + // Splat the length squared + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + // Get the reciprocal + vLengthSq = _mm_rsqrt_ps(vLengthSq); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3ReciprocalLength(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + + Result = XMVector3LengthSq(V); + Result = XMVectorReciprocalSqrt(Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot3 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vpadd_f32(v1, v1); + v2 = vdup_lane_f32(v2, 0); + v1 = vadd_f32(v1, v2); + // Reciprocal sqrt + float32x2_t S0 = vrsqrte_f32(v1); + float32x2_t P0 = vmul_f32(v1, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(v1, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + float32x2_t Result = vmul_f32(S1, R1); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + XMVECTOR vLengthSq = _mm_sqrt_ps(vTemp); + return _mm_div_ps(g_XMOne, vLengthSq); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vDot = _mm_mul_ps(V, V); + vDot = _mm_and_ps(vDot, g_XMMask3); + vDot = _mm_hadd_ps(vDot, vDot); + vDot = _mm_hadd_ps(vDot, vDot); + vDot = _mm_sqrt_ps(vDot); + vDot = _mm_div_ps(g_XMOne, vDot); + return vDot; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product + XMVECTOR vDot = _mm_mul_ps(V, V); + // x=Dot.y, y=Dot.z + XMVECTOR vTemp = XM_PERMUTE_PS(vDot, _MM_SHUFFLE(2, 1, 2, 1)); + // Result.x = x+y + vDot = _mm_add_ss(vDot, vTemp); + // x=Dot.z + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(1, 1, 1, 1)); + // Result.x = (x+y)+z + vDot = _mm_add_ss(vDot, vTemp); + // Splat x + vDot = XM_PERMUTE_PS(vDot, _MM_SHUFFLE(0, 0, 0, 0)); + // Get the reciprocal + vDot = _mm_sqrt_ps(vDot); + // Get the reciprocal + vDot = _mm_div_ps(g_XMOne, vDot); + return vDot; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3LengthEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + + Result = XMVector3LengthSq(V); + Result = XMVectorSqrtEst(Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot3 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vpadd_f32(v1, v1); + v2 = vdup_lane_f32(v2, 0); + v1 = vadd_f32(v1, v2); + const float32x2_t zero = vdup_n_f32(0); + uint32x2_t VEqualsZero = vceq_f32(v1, zero); + // Sqrt (estimate) + float32x2_t Result = vrsqrte_f32(v1); + Result = vmul_f32(v1, Result); + Result = vbsl_f32(VEqualsZero, zero, Result); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + return _mm_sqrt_ps(vTemp); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_and_ps(vLengthSq, g_XMMask3); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_sqrt_ps(vLengthSq); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y and z + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has z and y + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 2, 1, 2)); + // x+z, y + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + // y,y,y,y + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(1, 1, 1, 1)); + // x+z+y,??,??,?? + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + // Splat the length squared + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + // Get the length + vLengthSq = _mm_sqrt_ps(vLengthSq); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Length(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + + Result = XMVector3LengthSq(V); + Result = XMVectorSqrt(Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot3 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vpadd_f32(v1, v1); + v2 = vdup_lane_f32(v2, 0); + v1 = vadd_f32(v1, v2); + const float32x2_t zero = vdup_n_f32(0); + uint32x2_t VEqualsZero = vceq_f32(v1, zero); + // Sqrt + float32x2_t S0 = vrsqrte_f32(v1); + float32x2_t P0 = vmul_f32(v1, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(v1, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + float32x2_t Result = vmul_f32(S1, R1); + Result = vmul_f32(v1, Result); + Result = vbsl_f32(VEqualsZero, zero, Result); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + return _mm_sqrt_ps(vTemp); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_and_ps(vLengthSq, g_XMMask3); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_sqrt_ps(vLengthSq); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y and z + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has z and y + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 2, 1, 2)); + // x+z, y + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + // y,y,y,y + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(1, 1, 1, 1)); + // x+z+y,??,??,?? + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + // Splat the length squared + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + // Get the length + vLengthSq = _mm_sqrt_ps(vLengthSq); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ +// XMVector3NormalizeEst uses a reciprocal estimate and +// returns QNaN on zero and infinite vectors. + +inline XMVECTOR XM_CALLCONV XMVector3NormalizeEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + Result = XMVector3ReciprocalLength(V); + Result = XMVectorMultiply(V, Result); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot3 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vpadd_f32(v1, v1); + v2 = vdup_lane_f32(v2, 0); + v1 = vadd_f32(v1, v2); + // Reciprocal sqrt (estimate) + v2 = vrsqrte_f32(v1); + // Normalize + return vmulq_f32(V, vcombine_f32(v2, v2)); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0x7f); + XMVECTOR vResult = _mm_rsqrt_ps(vTemp); + return _mm_mul_ps(vResult, V); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vDot = _mm_mul_ps(V, V); + vDot = _mm_and_ps(vDot, g_XMMask3); + vDot = _mm_hadd_ps(vDot, vDot); + vDot = _mm_hadd_ps(vDot, vDot); + vDot = _mm_rsqrt_ps(vDot); + vDot = _mm_mul_ps(vDot, V); + return vDot; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product + XMVECTOR vDot = _mm_mul_ps(V, V); + // x=Dot.y, y=Dot.z + XMVECTOR vTemp = XM_PERMUTE_PS(vDot, _MM_SHUFFLE(2, 1, 2, 1)); + // Result.x = x+y + vDot = _mm_add_ss(vDot, vTemp); + // x=Dot.z + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(1, 1, 1, 1)); + // Result.x = (x+y)+z + vDot = _mm_add_ss(vDot, vTemp); + // Splat x + vDot = XM_PERMUTE_PS(vDot, _MM_SHUFFLE(0, 0, 0, 0)); + // Get the reciprocal + vDot = _mm_rsqrt_ps(vDot); + // Perform the normalization + vDot = _mm_mul_ps(vDot, V); + return vDot; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Normalize(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float fLength; + XMVECTOR vResult; + + vResult = XMVector3Length(V); + fLength = vResult.vector4_f32[0]; + + // Prevent divide by zero + if (fLength > 0) + { + fLength = 1.0f / fLength; + } + + vResult.vector4_f32[0] = V.vector4_f32[0] * fLength; + vResult.vector4_f32[1] = V.vector4_f32[1] * fLength; + vResult.vector4_f32[2] = V.vector4_f32[2] * fLength; + vResult.vector4_f32[3] = V.vector4_f32[3] * fLength; + return vResult; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot3 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vpadd_f32(v1, v1); + v2 = vdup_lane_f32(v2, 0); + v1 = vadd_f32(v1, v2); + uint32x2_t VEqualsZero = vceq_f32(v1, vdup_n_f32(0)); + uint32x2_t VEqualsInf = vceq_f32(v1, vget_low_f32(g_XMInfinity)); + // Reciprocal sqrt (2 iterations of Newton-Raphson) + float32x2_t S0 = vrsqrte_f32(v1); + float32x2_t P0 = vmul_f32(v1, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(v1, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + v2 = vmul_f32(S1, R1); + // Normalize + XMVECTOR vResult = vmulq_f32(V, vcombine_f32(v2, v2)); + vResult = vbslq_f32(vcombine_u32(VEqualsZero, VEqualsZero), vdupq_n_f32(0), vResult); + return vbslq_f32(vcombine_u32(VEqualsInf, VEqualsInf), g_XMQNaN, vResult); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vLengthSq = _mm_dp_ps(V, V, 0x7f); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Divide to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; +#elif defined(_XM_SSE3_INTRINSICS_) + // Perform the dot product on x,y and z only + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_and_ps(vLengthSq, g_XMMask3); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Divide to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y and z only + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(2, 1, 2, 1)); + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(1, 1, 1, 1)); + vLengthSq = _mm_add_ss(vLengthSq, vTemp); + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Divide to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3ClampLength +( + FXMVECTOR V, + float LengthMin, + float LengthMax +) noexcept +{ + XMVECTOR ClampMax = XMVectorReplicate(LengthMax); + XMVECTOR ClampMin = XMVectorReplicate(LengthMin); + + return XMVector3ClampLengthV(V, ClampMin, ClampMax); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3ClampLengthV +( + FXMVECTOR V, + FXMVECTOR LengthMin, + FXMVECTOR LengthMax +) noexcept +{ + assert((XMVectorGetY(LengthMin) == XMVectorGetX(LengthMin)) && (XMVectorGetZ(LengthMin) == XMVectorGetX(LengthMin))); + assert((XMVectorGetY(LengthMax) == XMVectorGetX(LengthMax)) && (XMVectorGetZ(LengthMax) == XMVectorGetX(LengthMax))); + assert(XMVector3GreaterOrEqual(LengthMin, XMVectorZero())); + assert(XMVector3GreaterOrEqual(LengthMax, XMVectorZero())); + assert(XMVector3GreaterOrEqual(LengthMax, LengthMin)); + + XMVECTOR LengthSq = XMVector3LengthSq(V); + + const XMVECTOR Zero = XMVectorZero(); + + XMVECTOR RcpLength = XMVectorReciprocalSqrt(LengthSq); + + XMVECTOR InfiniteLength = XMVectorEqualInt(LengthSq, g_XMInfinity.v); + XMVECTOR ZeroLength = XMVectorEqual(LengthSq, Zero); + + XMVECTOR Normal = XMVectorMultiply(V, RcpLength); + + XMVECTOR Length = XMVectorMultiply(LengthSq, RcpLength); + + XMVECTOR Select = XMVectorEqualInt(InfiniteLength, ZeroLength); + Length = XMVectorSelect(LengthSq, Length, Select); + Normal = XMVectorSelect(LengthSq, Normal, Select); + + XMVECTOR ControlMax = XMVectorGreater(Length, LengthMax); + XMVECTOR ControlMin = XMVectorLess(Length, LengthMin); + + XMVECTOR ClampLength = XMVectorSelect(Length, LengthMax, ControlMax); + ClampLength = XMVectorSelect(ClampLength, LengthMin, ControlMin); + + XMVECTOR Result = XMVectorMultiply(Normal, ClampLength); + + // Preserve the original vector (with no precision loss) if the length falls within the given range + XMVECTOR Control = XMVectorEqualInt(ControlMax, ControlMin); + Result = XMVectorSelect(Result, V, Control); + + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Reflect +( + FXMVECTOR Incident, + FXMVECTOR Normal +) noexcept +{ + // Result = Incident - (2 * dot(Incident, Normal)) * Normal + + XMVECTOR Result = XMVector3Dot(Incident, Normal); + Result = XMVectorAdd(Result, Result); + Result = XMVectorNegativeMultiplySubtract(Result, Normal, Incident); + + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Refract +( + FXMVECTOR Incident, + FXMVECTOR Normal, + float RefractionIndex +) noexcept +{ + XMVECTOR Index = XMVectorReplicate(RefractionIndex); + return XMVector3RefractV(Incident, Normal, Index); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3RefractV +( + FXMVECTOR Incident, + FXMVECTOR Normal, + FXMVECTOR RefractionIndex +) noexcept +{ + // Result = RefractionIndex * Incident - Normal * (RefractionIndex * dot(Incident, Normal) + + // sqrt(1 - RefractionIndex * RefractionIndex * (1 - dot(Incident, Normal) * dot(Incident, Normal)))) + +#if defined(_XM_NO_INTRINSICS_) + + const XMVECTOR Zero = XMVectorZero(); + + XMVECTOR IDotN = XMVector3Dot(Incident, Normal); + + // R = 1.0f - RefractionIndex * RefractionIndex * (1.0f - IDotN * IDotN) + XMVECTOR R = XMVectorNegativeMultiplySubtract(IDotN, IDotN, g_XMOne.v); + R = XMVectorMultiply(R, RefractionIndex); + R = XMVectorNegativeMultiplySubtract(R, RefractionIndex, g_XMOne.v); + + if (XMVector4LessOrEqual(R, Zero)) + { + // Total internal reflection + return Zero; + } + else + { + // R = RefractionIndex * IDotN + sqrt(R) + R = XMVectorSqrt(R); + R = XMVectorMultiplyAdd(RefractionIndex, IDotN, R); + + // Result = RefractionIndex * Incident - Normal * R + XMVECTOR Result = XMVectorMultiply(RefractionIndex, Incident); + Result = XMVectorNegativeMultiplySubtract(Normal, R, Result); + + return Result; + } + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR IDotN = XMVector3Dot(Incident, Normal); + + // R = 1.0f - RefractionIndex * RefractionIndex * (1.0f - IDotN * IDotN) + float32x4_t R = vmlsq_f32(g_XMOne, IDotN, IDotN); + R = vmulq_f32(R, RefractionIndex); + R = vmlsq_f32(g_XMOne, R, RefractionIndex); + + uint32x4_t isrzero = vcleq_f32(R, g_XMZero); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(isrzero)), vget_high_u8(vreinterpretq_u8_u32(isrzero))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + + float32x4_t vResult; + if (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) == 0xFFFFFFFFU) + { + // Total internal reflection + vResult = g_XMZero; + } + else + { + // Sqrt(R) + float32x4_t S0 = vrsqrteq_f32(R); + float32x4_t P0 = vmulq_f32(R, S0); + float32x4_t R0 = vrsqrtsq_f32(P0, S0); + float32x4_t S1 = vmulq_f32(S0, R0); + float32x4_t P1 = vmulq_f32(R, S1); + float32x4_t R1 = vrsqrtsq_f32(P1, S1); + float32x4_t S2 = vmulq_f32(S1, R1); + R = vmulq_f32(R, S2); + // R = RefractionIndex * IDotN + sqrt(R) + R = vmlaq_f32(R, RefractionIndex, IDotN); + // Result = RefractionIndex * Incident - Normal * R + vResult = vmulq_f32(RefractionIndex, Incident); + vResult = vmlsq_f32(vResult, R, Normal); + } + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Result = RefractionIndex * Incident - Normal * (RefractionIndex * dot(Incident, Normal) + + // sqrt(1 - RefractionIndex * RefractionIndex * (1 - dot(Incident, Normal) * dot(Incident, Normal)))) + XMVECTOR IDotN = XMVector3Dot(Incident, Normal); + // R = 1.0f - RefractionIndex * RefractionIndex * (1.0f - IDotN * IDotN) + XMVECTOR R = XM_FNMADD_PS(IDotN, IDotN, g_XMOne); + XMVECTOR R2 = _mm_mul_ps(RefractionIndex, RefractionIndex); + R = XM_FNMADD_PS(R, R2, g_XMOne); + + XMVECTOR vResult = _mm_cmple_ps(R, g_XMZero); + if (_mm_movemask_ps(vResult) == 0x0f) + { + // Total internal reflection + vResult = g_XMZero; + } + else + { + // R = RefractionIndex * IDotN + sqrt(R) + R = _mm_sqrt_ps(R); + R = XM_FMADD_PS(RefractionIndex, IDotN, R); + // Result = RefractionIndex * Incident - Normal * R + vResult = _mm_mul_ps(RefractionIndex, Incident); + vResult = XM_FNMADD_PS(R, Normal, vResult); + } + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Orthogonal(FXMVECTOR V) noexcept +{ + XMVECTOR Zero = XMVectorZero(); + XMVECTOR Z = XMVectorSplatZ(V); + XMVECTOR YZYY = XMVectorSwizzle(V); + + XMVECTOR NegativeV = XMVectorSubtract(Zero, V); + + XMVECTOR ZIsNegative = XMVectorLess(Z, Zero); + XMVECTOR YZYYIsNegative = XMVectorLess(YZYY, Zero); + + XMVECTOR S = XMVectorAdd(YZYY, Z); + XMVECTOR D = XMVectorSubtract(YZYY, Z); + + XMVECTOR Select = XMVectorEqualInt(ZIsNegative, YZYYIsNegative); + + XMVECTOR R0 = XMVectorPermute(NegativeV, S); + XMVECTOR R1 = XMVectorPermute(V, D); + + return XMVectorSelect(R1, R0, Select); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3AngleBetweenNormalsEst +( + FXMVECTOR N1, + FXMVECTOR N2 +) noexcept +{ + XMVECTOR Result = XMVector3Dot(N1, N2); + Result = XMVectorClamp(Result, g_XMNegativeOne.v, g_XMOne.v); + Result = XMVectorACosEst(Result); + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3AngleBetweenNormals +( + FXMVECTOR N1, + FXMVECTOR N2 +) noexcept +{ + XMVECTOR Result = XMVector3Dot(N1, N2); + Result = XMVectorClamp(Result, g_XMNegativeOne.v, g_XMOne.v); + Result = XMVectorACos(Result); + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3AngleBetweenVectors +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + XMVECTOR L1 = XMVector3ReciprocalLength(V1); + XMVECTOR L2 = XMVector3ReciprocalLength(V2); + + XMVECTOR Dot = XMVector3Dot(V1, V2); + + L1 = XMVectorMultiply(L1, L2); + + XMVECTOR CosAngle = XMVectorMultiply(Dot, L1); + CosAngle = XMVectorClamp(CosAngle, g_XMNegativeOne.v, g_XMOne.v); + + return XMVectorACos(CosAngle); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3LinePointDistance +( + FXMVECTOR LinePoint1, + FXMVECTOR LinePoint2, + FXMVECTOR Point +) noexcept +{ + // Given a vector PointVector from LinePoint1 to Point and a vector + // LineVector from LinePoint1 to LinePoint2, the scaled distance + // PointProjectionScale from LinePoint1 to the perpendicular projection + // of PointVector onto the line is defined as: + // + // PointProjectionScale = dot(PointVector, LineVector) / LengthSq(LineVector) + + XMVECTOR PointVector = XMVectorSubtract(Point, LinePoint1); + XMVECTOR LineVector = XMVectorSubtract(LinePoint2, LinePoint1); + + XMVECTOR LengthSq = XMVector3LengthSq(LineVector); + + XMVECTOR PointProjectionScale = XMVector3Dot(PointVector, LineVector); + PointProjectionScale = XMVectorDivide(PointProjectionScale, LengthSq); + + XMVECTOR DistanceVector = XMVectorMultiply(LineVector, PointProjectionScale); + DistanceVector = XMVectorSubtract(PointVector, DistanceVector); + + return XMVector3Length(DistanceVector); +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline void XM_CALLCONV XMVector3ComponentsFromNormal +( + XMVECTOR* pParallel, + XMVECTOR* pPerpendicular, + FXMVECTOR V, + FXMVECTOR Normal +) noexcept +{ + assert(pParallel != nullptr); + assert(pPerpendicular != nullptr); + + XMVECTOR Scale = XMVector3Dot(V, Normal); + + XMVECTOR Parallel = XMVectorMultiply(Normal, Scale); + + *pParallel = Parallel; + *pPerpendicular = XMVectorSubtract(V, Parallel); +} + +//------------------------------------------------------------------------------ +// Transform a vector using a rotation expressed as a unit quaternion + +inline XMVECTOR XM_CALLCONV XMVector3Rotate +( + FXMVECTOR V, + FXMVECTOR RotationQuaternion +) noexcept +{ + XMVECTOR A = XMVectorSelect(g_XMSelect1110.v, V, g_XMSelect1110.v); + XMVECTOR Q = XMQuaternionConjugate(RotationQuaternion); + XMVECTOR Result = XMQuaternionMultiply(Q, A); + return XMQuaternionMultiply(Result, RotationQuaternion); +} + +//------------------------------------------------------------------------------ +// Transform a vector using the inverse of a rotation expressed as a unit quaternion + +inline XMVECTOR XM_CALLCONV XMVector3InverseRotate +( + FXMVECTOR V, + FXMVECTOR RotationQuaternion +) noexcept +{ + XMVECTOR A = XMVectorSelect(g_XMSelect1110.v, V, g_XMSelect1110.v); + XMVECTOR Result = XMQuaternionMultiply(RotationQuaternion, A); + XMVECTOR Q = XMQuaternionConjugate(RotationQuaternion); + return XMQuaternionMultiply(Result, Q); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Transform +( + FXMVECTOR V, + FXMMATRIX M +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Z = XMVectorSplatZ(V); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiplyAdd(Z, M.r[2], M.r[3]); + Result = XMVectorMultiplyAdd(Y, M.r[1], Result); + Result = XMVectorMultiplyAdd(X, M.r[0], Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + XMVECTOR vResult = vmlaq_lane_f32(M.r[3], M.r[0], VL, 0); // X + vResult = vmlaq_lane_f32(vResult, M.r[1], VL, 1); // Y + return vmlaq_lane_f32(vResult, M.r[2], vget_high_f32(V), 0); // Z +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = XM_FMADD_PS(vResult, M.r[2], M.r[3]); + XMVECTOR vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = XM_FMADD_PS(vTemp, M.r[1], vResult); + vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = XM_FMADD_PS(vTemp, M.r[0], vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable : 26015 26019, "PREfast noise: Esp:1307" ) +#endif + +_Use_decl_annotations_ +inline XMFLOAT4* XM_CALLCONV XMVector3TransformStream +( + XMFLOAT4* pOutputStream, + size_t OutputStride, + const XMFLOAT3* pInputStream, + size_t InputStride, + size_t VectorCount, + FXMMATRIX M +) noexcept +{ + assert(pOutputStream != nullptr); + assert(pInputStream != nullptr); + + assert(InputStride >= sizeof(XMFLOAT3)); + _Analysis_assume_(InputStride >= sizeof(XMFLOAT3)); + + assert(OutputStride >= sizeof(XMFLOAT4)); + _Analysis_assume_(OutputStride >= sizeof(XMFLOAT4)); + +#if defined(_XM_NO_INTRINSICS_) + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + for (size_t i = 0; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + XMVECTOR Z = XMVectorSplatZ(V); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiplyAdd(Z, row2, row3); + Result = XMVectorMultiplyAdd(Y, row1, Result); + Result = XMVectorMultiplyAdd(X, row0, Result); + + XMStoreFloat4(reinterpret_cast(pOutputVector), Result); + + pInputVector += InputStride; + pOutputVector += OutputStride; + } + + return pOutputStream; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if ((InputStride == sizeof(XMFLOAT3)) && (OutputStride == sizeof(XMFLOAT4))) + { + for (size_t j = 0; j < four; ++j) + { + float32x4x3_t V = vld3q_f32(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT3) * 4; + + float32x2_t r3 = vget_low_f32(row3); + float32x2_t r = vget_low_f32(row0); + XMVECTOR vResult0 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), V.val[0], r, 0); // Ax+M + XMVECTOR vResult1 = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Bx+N + + XM_PREFETCH(pInputVector); + + r3 = vget_high_f32(row3); + r = vget_high_f32(row0); + XMVECTOR vResult2 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), V.val[0], r, 0); // Cx+O + XMVECTOR vResult3 = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Dx+P + + XM_PREFETCH(pInputVector + XM_CACHE_LINE_SIZE); + + r = vget_low_f32(row1); + vResult0 = vmlaq_lane_f32(vResult0, V.val[1], r, 0); // Ax+Ey+M + vResult1 = vmlaq_lane_f32(vResult1, V.val[1], r, 1); // Bx+Fy+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 2)); + + r = vget_high_f32(row1); + vResult2 = vmlaq_lane_f32(vResult2, V.val[1], r, 0); // Cx+Gy+O + vResult3 = vmlaq_lane_f32(vResult3, V.val[1], r, 1); // Dx+Hy+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 3)); + + r = vget_low_f32(row2); + vResult0 = vmlaq_lane_f32(vResult0, V.val[2], r, 0); // Ax+Ey+Iz+M + vResult1 = vmlaq_lane_f32(vResult1, V.val[2], r, 1); // Bx+Fy+Jz+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 4)); + + r = vget_high_f32(row2); + vResult2 = vmlaq_lane_f32(vResult2, V.val[2], r, 0); // Cx+Gy+Kz+O + vResult3 = vmlaq_lane_f32(vResult3, V.val[2], r, 1); // Dx+Hy+Lz+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 5)); + + float32x4x4_t R; + R.val[0] = vResult0; + R.val[1] = vResult1; + R.val[2] = vResult2; + R.val[3] = vResult3; + + vst4q_f32(reinterpret_cast(pOutputVector), R); + pOutputVector += sizeof(XMFLOAT4) * 4; + + i += 4; + } + } + } + + for (; i < VectorCount; i++) + { + float32x2_t VL = vld1_f32(reinterpret_cast(pInputVector)); + float32x2_t zero = vdup_n_f32(0); + float32x2_t VH = vld1_lane_f32(reinterpret_cast(pInputVector) + 2, zero, 0); + pInputVector += InputStride; + + XMVECTOR vResult = vmlaq_lane_f32(row3, row0, VL, 0); // X + vResult = vmlaq_lane_f32(vResult, row1, VL, 1); // Y + vResult = vmlaq_lane_f32(vResult, row2, VH, 0); // Z + + vst1q_f32(reinterpret_cast(pOutputVector), vResult); + pOutputVector += OutputStride; + } + + return pOutputStream; +#elif defined(_XM_SSE_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(XMFLOAT3)) + { + if (!(reinterpret_cast(pOutputStream) & 0xF) && !(OutputStride & 0xF)) + { + // Packed input, aligned output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, row2, row3); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + i += 4; + } + } + else + { + // Packed input, unaligned output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, row2, row3); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + i += 4; + } + } + } + } + + if (!(reinterpret_cast(pOutputStream) & 0xF) && !(OutputStride & 0xF)) + { + // Aligned output + for (; i < VectorCount; ++i) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR Z = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, row2, row3); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + } + else + { + // Unaligned output + for (; i < VectorCount; ++i) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR Z = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, row2, row3); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + } + + XM_SFENCE(); + + return pOutputStream; +#endif +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3TransformCoord +( + FXMVECTOR V, + FXMMATRIX M +) noexcept +{ + XMVECTOR Z = XMVectorSplatZ(V); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiplyAdd(Z, M.r[2], M.r[3]); + Result = XMVectorMultiplyAdd(Y, M.r[1], Result); + Result = XMVectorMultiplyAdd(X, M.r[0], Result); + + XMVECTOR W = XMVectorSplatW(Result); + return XMVectorDivide(Result, W); +} + +//------------------------------------------------------------------------------ + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable : 26015 26019, "PREfast noise: Esp:1307" ) +#endif + +_Use_decl_annotations_ +inline XMFLOAT3* XM_CALLCONV XMVector3TransformCoordStream +( + XMFLOAT3* pOutputStream, + size_t OutputStride, + const XMFLOAT3* pInputStream, + size_t InputStride, + size_t VectorCount, + FXMMATRIX M +) noexcept +{ + assert(pOutputStream != nullptr); + assert(pInputStream != nullptr); + + assert(InputStride >= sizeof(XMFLOAT3)); + _Analysis_assume_(InputStride >= sizeof(XMFLOAT3)); + + assert(OutputStride >= sizeof(XMFLOAT3)); + _Analysis_assume_(OutputStride >= sizeof(XMFLOAT3)); + +#if defined(_XM_NO_INTRINSICS_) + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + for (size_t i = 0; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + XMVECTOR Z = XMVectorSplatZ(V); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiplyAdd(Z, row2, row3); + Result = XMVectorMultiplyAdd(Y, row1, Result); + Result = XMVectorMultiplyAdd(X, row0, Result); + + XMVECTOR W = XMVectorSplatW(Result); + + Result = XMVectorDivide(Result, W); + + XMStoreFloat3(reinterpret_cast(pOutputVector), Result); + + pInputVector += InputStride; + pOutputVector += OutputStride; + } + + return pOutputStream; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if ((InputStride == sizeof(XMFLOAT3)) && (OutputStride == sizeof(XMFLOAT3))) + { + for (size_t j = 0; j < four; ++j) + { + float32x4x3_t V = vld3q_f32(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT3) * 4; + + float32x2_t r3 = vget_low_f32(row3); + float32x2_t r = vget_low_f32(row0); + XMVECTOR vResult0 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), V.val[0], r, 0); // Ax+M + XMVECTOR vResult1 = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Bx+N + + XM_PREFETCH(pInputVector); + + r3 = vget_high_f32(row3); + r = vget_high_f32(row0); + XMVECTOR vResult2 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), V.val[0], r, 0); // Cx+O + XMVECTOR W = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Dx+P + + XM_PREFETCH(pInputVector + XM_CACHE_LINE_SIZE); + + r = vget_low_f32(row1); + vResult0 = vmlaq_lane_f32(vResult0, V.val[1], r, 0); // Ax+Ey+M + vResult1 = vmlaq_lane_f32(vResult1, V.val[1], r, 1); // Bx+Fy+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 2)); + + r = vget_high_f32(row1); + vResult2 = vmlaq_lane_f32(vResult2, V.val[1], r, 0); // Cx+Gy+O + W = vmlaq_lane_f32(W, V.val[1], r, 1); // Dx+Hy+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 3)); + + r = vget_low_f32(row2); + vResult0 = vmlaq_lane_f32(vResult0, V.val[2], r, 0); // Ax+Ey+Iz+M + vResult1 = vmlaq_lane_f32(vResult1, V.val[2], r, 1); // Bx+Fy+Jz+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 4)); + + r = vget_high_f32(row2); + vResult2 = vmlaq_lane_f32(vResult2, V.val[2], r, 0); // Cx+Gy+Kz+O + W = vmlaq_lane_f32(W, V.val[2], r, 1); // Dx+Hy+Lz+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 5)); + + #if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + V.val[0] = vdivq_f32(vResult0, W); + V.val[1] = vdivq_f32(vResult1, W); + V.val[2] = vdivq_f32(vResult2, W); + #else + // 2 iterations of Newton-Raphson refinement of reciprocal + float32x4_t Reciprocal = vrecpeq_f32(W); + float32x4_t S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + + V.val[0] = vmulq_f32(vResult0, Reciprocal); + V.val[1] = vmulq_f32(vResult1, Reciprocal); + V.val[2] = vmulq_f32(vResult2, Reciprocal); + #endif + + vst3q_f32(reinterpret_cast(pOutputVector), V); + pOutputVector += sizeof(XMFLOAT3) * 4; + + i += 4; + } + } + } + + for (; i < VectorCount; i++) + { + float32x2_t VL = vld1_f32(reinterpret_cast(pInputVector)); + float32x2_t zero = vdup_n_f32(0); + float32x2_t VH = vld1_lane_f32(reinterpret_cast(pInputVector) + 2, zero, 0); + pInputVector += InputStride; + + XMVECTOR vResult = vmlaq_lane_f32(row3, row0, VL, 0); // X + vResult = vmlaq_lane_f32(vResult, row1, VL, 1); // Y + vResult = vmlaq_lane_f32(vResult, row2, VH, 0); // Z + + VH = vget_high_f32(vResult); + XMVECTOR W = vdupq_lane_f32(VH, 1); + + #if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + vResult = vdivq_f32(vResult, W); + #else + // 2 iterations of Newton-Raphson refinement of reciprocal for W + float32x4_t Reciprocal = vrecpeq_f32(W); + float32x4_t S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + + vResult = vmulq_f32(vResult, Reciprocal); + #endif + + VL = vget_low_f32(vResult); + vst1_f32(reinterpret_cast(pOutputVector), VL); + vst1q_lane_f32(reinterpret_cast(pOutputVector) + 2, vResult, 2); + pOutputVector += OutputStride; + } + + return pOutputStream; +#elif defined(_XM_SSE_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(XMFLOAT3)) + { + if (OutputStride == sizeof(XMFLOAT3)) + { + if (!(reinterpret_cast(pOutputStream) & 0xF)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, row2, row3); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + V1 = _mm_div_ps(vTemp, W); + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + V2 = _mm_div_ps(vTemp, W); + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + V3 = _mm_div_ps(vTemp, W); + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + V4 = _mm_div_ps(vTemp, W); + + // Pack and store the vectors + XM3PACK4INTO3(vTemp); + XM_STREAM_PS(reinterpret_cast(pOutputVector), V1); + XM_STREAM_PS(reinterpret_cast(pOutputVector + 16), vTemp); + XM_STREAM_PS(reinterpret_cast(pOutputVector + 32), V3); + pOutputVector += sizeof(XMFLOAT3) * 4; + i += 4; + } + } + else + { + // Packed input, unaligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, row2, row3); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + V1 = _mm_div_ps(vTemp, W); + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + V2 = _mm_div_ps(vTemp, W); + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + V3 = _mm_div_ps(vTemp, W); + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + V4 = _mm_div_ps(vTemp, W); + + // Pack and store the vectors + XM3PACK4INTO3(vTemp); + _mm_storeu_ps(reinterpret_cast(pOutputVector), V1); + _mm_storeu_ps(reinterpret_cast(pOutputVector + 16), vTemp); + _mm_storeu_ps(reinterpret_cast(pOutputVector + 32), V3); + pOutputVector += sizeof(XMFLOAT3) * 4; + i += 4; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, row2, row3); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + vTemp = _mm_div_ps(vTemp, W); + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + vTemp = _mm_div_ps(vTemp, W); + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + vTemp = _mm_div_ps(vTemp, W); + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, row2, row3); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + vTemp = _mm_div_ps(vTemp, W); + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + i += 4; + } + } + } + } + + for (; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR Z = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, row2, row3); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + + vTemp = _mm_div_ps(vTemp, W); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + + XM_SFENCE(); + + return pOutputStream; +#endif +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3TransformNormal +( + FXMVECTOR V, + FXMMATRIX M +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Z = XMVectorSplatZ(V); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiply(Z, M.r[2]); + Result = XMVectorMultiplyAdd(Y, M.r[1], Result); + Result = XMVectorMultiplyAdd(X, M.r[0], Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + XMVECTOR vResult = vmulq_lane_f32(M.r[0], VL, 0); // X + vResult = vmlaq_lane_f32(vResult, M.r[1], VL, 1); // Y + return vmlaq_lane_f32(vResult, M.r[2], vget_high_f32(V), 0); // Z +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = _mm_mul_ps(vResult, M.r[2]); + XMVECTOR vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = XM_FMADD_PS(vTemp, M.r[1], vResult); + vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = XM_FMADD_PS(vTemp, M.r[0], vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable : 26015 26019, "PREfast noise: Esp:1307" ) +#endif + +_Use_decl_annotations_ +inline XMFLOAT3* XM_CALLCONV XMVector3TransformNormalStream +( + XMFLOAT3* pOutputStream, + size_t OutputStride, + const XMFLOAT3* pInputStream, + size_t InputStride, + size_t VectorCount, + FXMMATRIX M +) noexcept +{ + assert(pOutputStream != nullptr); + assert(pInputStream != nullptr); + + assert(InputStride >= sizeof(XMFLOAT3)); + _Analysis_assume_(InputStride >= sizeof(XMFLOAT3)); + + assert(OutputStride >= sizeof(XMFLOAT3)); + _Analysis_assume_(OutputStride >= sizeof(XMFLOAT3)); + +#if defined(_XM_NO_INTRINSICS_) + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + + for (size_t i = 0; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + XMVECTOR Z = XMVectorSplatZ(V); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiply(Z, row2); + Result = XMVectorMultiplyAdd(Y, row1, Result); + Result = XMVectorMultiplyAdd(X, row0, Result); + + XMStoreFloat3(reinterpret_cast(pOutputVector), Result); + + pInputVector += InputStride; + pOutputVector += OutputStride; + } + + return pOutputStream; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if ((InputStride == sizeof(XMFLOAT3)) && (OutputStride == sizeof(XMFLOAT3))) + { + for (size_t j = 0; j < four; ++j) + { + float32x4x3_t V = vld3q_f32(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT3) * 4; + + float32x2_t r = vget_low_f32(row0); + XMVECTOR vResult0 = vmulq_lane_f32(V.val[0], r, 0); // Ax + XMVECTOR vResult1 = vmulq_lane_f32(V.val[0], r, 1); // Bx + + XM_PREFETCH(pInputVector); + + r = vget_high_f32(row0); + XMVECTOR vResult2 = vmulq_lane_f32(V.val[0], r, 0); // Cx + + XM_PREFETCH(pInputVector + XM_CACHE_LINE_SIZE); + + r = vget_low_f32(row1); + vResult0 = vmlaq_lane_f32(vResult0, V.val[1], r, 0); // Ax+Ey + vResult1 = vmlaq_lane_f32(vResult1, V.val[1], r, 1); // Bx+Fy + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 2)); + + r = vget_high_f32(row1); + vResult2 = vmlaq_lane_f32(vResult2, V.val[1], r, 0); // Cx+Gy + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 3)); + + r = vget_low_f32(row2); + vResult0 = vmlaq_lane_f32(vResult0, V.val[2], r, 0); // Ax+Ey+Iz + vResult1 = vmlaq_lane_f32(vResult1, V.val[2], r, 1); // Bx+Fy+Jz + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 4)); + + r = vget_high_f32(row2); + vResult2 = vmlaq_lane_f32(vResult2, V.val[2], r, 0); // Cx+Gy+Kz + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 5)); + + V.val[0] = vResult0; + V.val[1] = vResult1; + V.val[2] = vResult2; + + vst3q_f32(reinterpret_cast(pOutputVector), V); + pOutputVector += sizeof(XMFLOAT3) * 4; + + i += 4; + } + } + } + + for (; i < VectorCount; i++) + { + float32x2_t VL = vld1_f32(reinterpret_cast(pInputVector)); + float32x2_t zero = vdup_n_f32(0); + float32x2_t VH = vld1_lane_f32(reinterpret_cast(pInputVector) + 2, zero, 0); + pInputVector += InputStride; + + XMVECTOR vResult = vmulq_lane_f32(row0, VL, 0); // X + vResult = vmlaq_lane_f32(vResult, row1, VL, 1); // Y + vResult = vmlaq_lane_f32(vResult, row2, VH, 0); // Z + + VL = vget_low_f32(vResult); + vst1_f32(reinterpret_cast(pOutputVector), VL); + vst1q_lane_f32(reinterpret_cast(pOutputVector) + 2, vResult, 2); + pOutputVector += OutputStride; + } + + return pOutputStream; +#elif defined(_XM_SSE_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(XMFLOAT3)) + { + if (OutputStride == sizeof(XMFLOAT3)) + { + if (!(reinterpret_cast(pOutputStream) & 0xF)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Z, row2); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + V1 = _mm_add_ps(vTemp, vTemp3); + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = _mm_mul_ps(Z, row2); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + V2 = _mm_add_ps(vTemp, vTemp3); + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = _mm_mul_ps(Z, row2); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + V3 = _mm_add_ps(vTemp, vTemp3); + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = _mm_mul_ps(Z, row2); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + V4 = _mm_add_ps(vTemp, vTemp3); + + // Pack and store the vectors + XM3PACK4INTO3(vTemp); + XM_STREAM_PS(reinterpret_cast(pOutputVector), V1); + XM_STREAM_PS(reinterpret_cast(pOutputVector + 16), vTemp); + XM_STREAM_PS(reinterpret_cast(pOutputVector + 32), V3); + pOutputVector += sizeof(XMFLOAT3) * 4; + i += 4; + } + } + else + { + // Packed input, unaligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Z, row2); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + V1 = _mm_add_ps(vTemp, vTemp3); + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = _mm_mul_ps(Z, row2); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + V2 = _mm_add_ps(vTemp, vTemp3); + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = _mm_mul_ps(Z, row2); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + V3 = _mm_add_ps(vTemp, vTemp3); + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = _mm_mul_ps(Z, row2); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + V4 = _mm_add_ps(vTemp, vTemp3); + + // Pack and store the vectors + XM3PACK4INTO3(vTemp); + _mm_storeu_ps(reinterpret_cast(pOutputVector), V1); + _mm_storeu_ps(reinterpret_cast(pOutputVector + 16), vTemp); + _mm_storeu_ps(reinterpret_cast(pOutputVector + 32), V3); + pOutputVector += sizeof(XMFLOAT3) * 4; + i += 4; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Z, row2); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = _mm_mul_ps(Z, row2); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = _mm_mul_ps(Z, row2); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = _mm_mul_ps(Z, row2); + vTemp2 = _mm_mul_ps(Y, row1); + vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + i += 4; + } + } + } + } + + for (; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR Z = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = _mm_mul_ps(Z, row2); + XMVECTOR vTemp2 = _mm_mul_ps(Y, row1); + XMVECTOR vTemp3 = _mm_mul_ps(X, row0); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + + XM_SFENCE(); + + return pOutputStream; +#endif +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Project +( + FXMVECTOR V, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + FXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World +) noexcept +{ + const float HalfViewportWidth = ViewportWidth * 0.5f; + const float HalfViewportHeight = ViewportHeight * 0.5f; + + XMVECTOR Scale = XMVectorSet(HalfViewportWidth, -HalfViewportHeight, ViewportMaxZ - ViewportMinZ, 0.0f); + XMVECTOR Offset = XMVectorSet(ViewportX + HalfViewportWidth, ViewportY + HalfViewportHeight, ViewportMinZ, 0.0f); + + XMMATRIX Transform = XMMatrixMultiply(World, View); + Transform = XMMatrixMultiply(Transform, Projection); + + XMVECTOR Result = XMVector3TransformCoord(V, Transform); + + Result = XMVectorMultiplyAdd(Result, Scale, Offset); + + return Result; +} + +//------------------------------------------------------------------------------ + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable : 26015 26019, "PREfast noise: Esp:1307" ) +#endif + +_Use_decl_annotations_ +inline XMFLOAT3* XM_CALLCONV XMVector3ProjectStream +( + XMFLOAT3* pOutputStream, + size_t OutputStride, + const XMFLOAT3* pInputStream, + size_t InputStride, + size_t VectorCount, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + FXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World +) noexcept +{ + assert(pOutputStream != nullptr); + assert(pInputStream != nullptr); + + assert(InputStride >= sizeof(XMFLOAT3)); + _Analysis_assume_(InputStride >= sizeof(XMFLOAT3)); + + assert(OutputStride >= sizeof(XMFLOAT3)); + _Analysis_assume_(OutputStride >= sizeof(XMFLOAT3)); + +#if defined(_XM_NO_INTRINSICS_) + + const float HalfViewportWidth = ViewportWidth * 0.5f; + const float HalfViewportHeight = ViewportHeight * 0.5f; + + XMVECTOR Scale = XMVectorSet(HalfViewportWidth, -HalfViewportHeight, ViewportMaxZ - ViewportMinZ, 1.0f); + XMVECTOR Offset = XMVectorSet(ViewportX + HalfViewportWidth, ViewportY + HalfViewportHeight, ViewportMinZ, 0.0f); + + XMMATRIX Transform = XMMatrixMultiply(World, View); + Transform = XMMatrixMultiply(Transform, Projection); + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + for (size_t i = 0; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + + XMVECTOR Result = XMVector3TransformCoord(V, Transform); + Result = XMVectorMultiplyAdd(Result, Scale, Offset); + + XMStoreFloat3(reinterpret_cast(pOutputVector), Result); + + pInputVector += InputStride; + pOutputVector += OutputStride; + } + + return pOutputStream; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + const float HalfViewportWidth = ViewportWidth * 0.5f; + const float HalfViewportHeight = ViewportHeight * 0.5f; + + XMMATRIX Transform = XMMatrixMultiply(World, View); + Transform = XMMatrixMultiply(Transform, Projection); + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if ((InputStride == sizeof(XMFLOAT3)) && (OutputStride == sizeof(XMFLOAT3))) + { + XMVECTOR ScaleX = vdupq_n_f32(HalfViewportWidth); + XMVECTOR ScaleY = vdupq_n_f32(-HalfViewportHeight); + XMVECTOR ScaleZ = vdupq_n_f32(ViewportMaxZ - ViewportMinZ); + + XMVECTOR OffsetX = vdupq_n_f32(ViewportX + HalfViewportWidth); + XMVECTOR OffsetY = vdupq_n_f32(ViewportY + HalfViewportHeight); + XMVECTOR OffsetZ = vdupq_n_f32(ViewportMinZ); + + for (size_t j = 0; j < four; ++j) + { + float32x4x3_t V = vld3q_f32(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT3) * 4; + + float32x2_t r3 = vget_low_f32(Transform.r[3]); + float32x2_t r = vget_low_f32(Transform.r[0]); + XMVECTOR vResult0 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), V.val[0], r, 0); // Ax+M + XMVECTOR vResult1 = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Bx+N + + XM_PREFETCH(pInputVector); + + r3 = vget_high_f32(Transform.r[3]); + r = vget_high_f32(Transform.r[0]); + XMVECTOR vResult2 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), V.val[0], r, 0); // Cx+O + XMVECTOR W = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), V.val[0], r, 1); // Dx+P + + XM_PREFETCH(pInputVector + XM_CACHE_LINE_SIZE); + + r = vget_low_f32(Transform.r[1]); + vResult0 = vmlaq_lane_f32(vResult0, V.val[1], r, 0); // Ax+Ey+M + vResult1 = vmlaq_lane_f32(vResult1, V.val[1], r, 1); // Bx+Fy+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 2)); + + r = vget_high_f32(Transform.r[1]); + vResult2 = vmlaq_lane_f32(vResult2, V.val[1], r, 0); // Cx+Gy+O + W = vmlaq_lane_f32(W, V.val[1], r, 1); // Dx+Hy+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 3)); + + r = vget_low_f32(Transform.r[2]); + vResult0 = vmlaq_lane_f32(vResult0, V.val[2], r, 0); // Ax+Ey+Iz+M + vResult1 = vmlaq_lane_f32(vResult1, V.val[2], r, 1); // Bx+Fy+Jz+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 4)); + + r = vget_high_f32(Transform.r[2]); + vResult2 = vmlaq_lane_f32(vResult2, V.val[2], r, 0); // Cx+Gy+Kz+O + W = vmlaq_lane_f32(W, V.val[2], r, 1); // Dx+Hy+Lz+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 5)); + + #if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + vResult0 = vdivq_f32(vResult0, W); + vResult1 = vdivq_f32(vResult1, W); + vResult2 = vdivq_f32(vResult2, W); + #else + // 2 iterations of Newton-Raphson refinement of reciprocal + float32x4_t Reciprocal = vrecpeq_f32(W); + float32x4_t S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + + vResult0 = vmulq_f32(vResult0, Reciprocal); + vResult1 = vmulq_f32(vResult1, Reciprocal); + vResult2 = vmulq_f32(vResult2, Reciprocal); + #endif + + V.val[0] = vmlaq_f32(OffsetX, vResult0, ScaleX); + V.val[1] = vmlaq_f32(OffsetY, vResult1, ScaleY); + V.val[2] = vmlaq_f32(OffsetZ, vResult2, ScaleZ); + + vst3q_f32(reinterpret_cast(pOutputVector), V); + pOutputVector += sizeof(XMFLOAT3) * 4; + + i += 4; + } + } + } + + if (i < VectorCount) + { + XMVECTOR Scale = XMVectorSet(HalfViewportWidth, -HalfViewportHeight, ViewportMaxZ - ViewportMinZ, 1.0f); + XMVECTOR Offset = XMVectorSet(ViewportX + HalfViewportWidth, ViewportY + HalfViewportHeight, ViewportMinZ, 0.0f); + + for (; i < VectorCount; i++) + { + float32x2_t VL = vld1_f32(reinterpret_cast(pInputVector)); + float32x2_t zero = vdup_n_f32(0); + float32x2_t VH = vld1_lane_f32(reinterpret_cast(pInputVector) + 2, zero, 0); + pInputVector += InputStride; + + XMVECTOR vResult = vmlaq_lane_f32(Transform.r[3], Transform.r[0], VL, 0); // X + vResult = vmlaq_lane_f32(vResult, Transform.r[1], VL, 1); // Y + vResult = vmlaq_lane_f32(vResult, Transform.r[2], VH, 0); // Z + + VH = vget_high_f32(vResult); + XMVECTOR W = vdupq_lane_f32(VH, 1); + + #if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + vResult = vdivq_f32(vResult, W); + #else + // 2 iterations of Newton-Raphson refinement of reciprocal for W + float32x4_t Reciprocal = vrecpeq_f32(W); + float32x4_t S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + + vResult = vmulq_f32(vResult, Reciprocal); + #endif + + vResult = vmlaq_f32(Offset, vResult, Scale); + + VL = vget_low_f32(vResult); + vst1_f32(reinterpret_cast(pOutputVector), VL); + vst1q_lane_f32(reinterpret_cast(pOutputVector) + 2, vResult, 2); + pOutputVector += OutputStride; + } + } + + return pOutputStream; +#elif defined(_XM_SSE_INTRINSICS_) + const float HalfViewportWidth = ViewportWidth * 0.5f; + const float HalfViewportHeight = ViewportHeight * 0.5f; + + XMVECTOR Scale = XMVectorSet(HalfViewportWidth, -HalfViewportHeight, ViewportMaxZ - ViewportMinZ, 1.0f); + XMVECTOR Offset = XMVectorSet(ViewportX + HalfViewportWidth, ViewportY + HalfViewportHeight, ViewportMinZ, 0.0f); + + XMMATRIX Transform = XMMatrixMultiply(World, View); + Transform = XMMatrixMultiply(Transform, Projection); + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(XMFLOAT3)) + { + if (OutputStride == sizeof(XMFLOAT3)) + { + if (!(reinterpret_cast(pOutputStream) & 0xF)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + XMVECTOR vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + XMVECTOR vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + V1 = XM_FMADD_PS(vTemp, Scale, Offset); + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + V2 = XM_FMADD_PS(vTemp, Scale, Offset); + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + V3 = XM_FMADD_PS(vTemp, Scale, Offset); + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + V4 = XM_FMADD_PS(vTemp, Scale, Offset); + + // Pack and store the vectors + XM3PACK4INTO3(vTemp); + XM_STREAM_PS(reinterpret_cast(pOutputVector), V1); + XM_STREAM_PS(reinterpret_cast(pOutputVector + 16), vTemp); + XM_STREAM_PS(reinterpret_cast(pOutputVector + 32), V3); + pOutputVector += sizeof(XMFLOAT3) * 4; + i += 4; + } + } + else + { + // Packed input, unaligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + XMVECTOR vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + XMVECTOR vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + V1 = XM_FMADD_PS(vTemp, Scale, Offset); + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + V2 = XM_FMADD_PS(vTemp, Scale, Offset); + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + V3 = XM_FMADD_PS(vTemp, Scale, Offset); + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + V4 = XM_FMADD_PS(vTemp, Scale, Offset); + + // Pack and store the vectors + XM3PACK4INTO3(vTemp); + _mm_storeu_ps(reinterpret_cast(pOutputVector), V1); + _mm_storeu_ps(reinterpret_cast(pOutputVector + 16), vTemp); + _mm_storeu_ps(reinterpret_cast(pOutputVector + 32), V3); + pOutputVector += sizeof(XMFLOAT3) * 4; + i += 4; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + XMVECTOR vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + XMVECTOR vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + vTemp = XM_FMADD_PS(vTemp, Scale, Offset); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 2 + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + vTemp = XM_FMADD_PS(vTemp, Scale, Offset); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 3 + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + vTemp = XM_FMADD_PS(vTemp, Scale, Offset); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 4 + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + vTemp = XM_FMADD_PS(vTemp, Scale, Offset); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + i += 4; + } + } + } + } + + for (; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR Z = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + XMVECTOR vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + XMVECTOR vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + vTemp = XM_FMADD_PS(vTemp, Scale, Offset); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + + XM_SFENCE(); + + return pOutputStream; +#endif +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector3Unproject +( + FXMVECTOR V, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + FXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World +) noexcept +{ + static const XMVECTORF32 D = { { { -1.0f, 1.0f, 0.0f, 0.0f } } }; + + XMVECTOR Scale = XMVectorSet(ViewportWidth * 0.5f, -ViewportHeight * 0.5f, ViewportMaxZ - ViewportMinZ, 1.0f); + Scale = XMVectorReciprocal(Scale); + + XMVECTOR Offset = XMVectorSet(-ViewportX, -ViewportY, -ViewportMinZ, 0.0f); + Offset = XMVectorMultiplyAdd(Scale, Offset, D.v); + + XMMATRIX Transform = XMMatrixMultiply(World, View); + Transform = XMMatrixMultiply(Transform, Projection); + Transform = XMMatrixInverse(nullptr, Transform); + + XMVECTOR Result = XMVectorMultiplyAdd(V, Scale, Offset); + + return XMVector3TransformCoord(Result, Transform); +} + +//------------------------------------------------------------------------------ + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable : 26015 26019, "PREfast noise: Esp:1307" ) +#endif + +_Use_decl_annotations_ +inline XMFLOAT3* XM_CALLCONV XMVector3UnprojectStream +( + XMFLOAT3* pOutputStream, + size_t OutputStride, + const XMFLOAT3* pInputStream, + size_t InputStride, + size_t VectorCount, + float ViewportX, + float ViewportY, + float ViewportWidth, + float ViewportHeight, + float ViewportMinZ, + float ViewportMaxZ, + FXMMATRIX Projection, + CXMMATRIX View, + CXMMATRIX World +) noexcept +{ + assert(pOutputStream != nullptr); + assert(pInputStream != nullptr); + + assert(InputStride >= sizeof(XMFLOAT3)); + _Analysis_assume_(InputStride >= sizeof(XMFLOAT3)); + + assert(OutputStride >= sizeof(XMFLOAT3)); + _Analysis_assume_(OutputStride >= sizeof(XMFLOAT3)); + +#if defined(_XM_NO_INTRINSICS_) + + static const XMVECTORF32 D = { { { -1.0f, 1.0f, 0.0f, 0.0f } } }; + + XMVECTOR Scale = XMVectorSet(ViewportWidth * 0.5f, -ViewportHeight * 0.5f, ViewportMaxZ - ViewportMinZ, 1.0f); + Scale = XMVectorReciprocal(Scale); + + XMVECTOR Offset = XMVectorSet(-ViewportX, -ViewportY, -ViewportMinZ, 0.0f); + Offset = XMVectorMultiplyAdd(Scale, Offset, D.v); + + XMMATRIX Transform = XMMatrixMultiply(World, View); + Transform = XMMatrixMultiply(Transform, Projection); + Transform = XMMatrixInverse(nullptr, Transform); + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + for (size_t i = 0; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + + XMVECTOR Result = XMVectorMultiplyAdd(V, Scale, Offset); + + Result = XMVector3TransformCoord(Result, Transform); + + XMStoreFloat3(reinterpret_cast(pOutputVector), Result); + + pInputVector += InputStride; + pOutputVector += OutputStride; + } + + return pOutputStream; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMMATRIX Transform = XMMatrixMultiply(World, View); + Transform = XMMatrixMultiply(Transform, Projection); + Transform = XMMatrixInverse(nullptr, Transform); + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + float sx = 1.f / (ViewportWidth * 0.5f); + float sy = 1.f / (-ViewportHeight * 0.5f); + float sz = 1.f / (ViewportMaxZ - ViewportMinZ); + + float ox = (-ViewportX * sx) - 1.f; + float oy = (-ViewportY * sy) + 1.f; + float oz = (-ViewportMinZ * sz); + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if ((InputStride == sizeof(XMFLOAT3)) && (OutputStride == sizeof(XMFLOAT3))) + { + for (size_t j = 0; j < four; ++j) + { + float32x4x3_t V = vld3q_f32(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT3) * 4; + + XMVECTOR ScaleX = vdupq_n_f32(sx); + XMVECTOR OffsetX = vdupq_n_f32(ox); + XMVECTOR VX = vmlaq_f32(OffsetX, ScaleX, V.val[0]); + + float32x2_t r3 = vget_low_f32(Transform.r[3]); + float32x2_t r = vget_low_f32(Transform.r[0]); + XMVECTOR vResult0 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), VX, r, 0); // Ax+M + XMVECTOR vResult1 = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), VX, r, 1); // Bx+N + + XM_PREFETCH(pInputVector); + + r3 = vget_high_f32(Transform.r[3]); + r = vget_high_f32(Transform.r[0]); + XMVECTOR vResult2 = vmlaq_lane_f32(vdupq_lane_f32(r3, 0), VX, r, 0); // Cx+O + XMVECTOR W = vmlaq_lane_f32(vdupq_lane_f32(r3, 1), VX, r, 1); // Dx+P + + XM_PREFETCH(pInputVector + XM_CACHE_LINE_SIZE); + + XMVECTOR ScaleY = vdupq_n_f32(sy); + XMVECTOR OffsetY = vdupq_n_f32(oy); + XMVECTOR VY = vmlaq_f32(OffsetY, ScaleY, V.val[1]); + + r = vget_low_f32(Transform.r[1]); + vResult0 = vmlaq_lane_f32(vResult0, VY, r, 0); // Ax+Ey+M + vResult1 = vmlaq_lane_f32(vResult1, VY, r, 1); // Bx+Fy+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 2)); + + r = vget_high_f32(Transform.r[1]); + vResult2 = vmlaq_lane_f32(vResult2, VY, r, 0); // Cx+Gy+O + W = vmlaq_lane_f32(W, VY, r, 1); // Dx+Hy+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 3)); + + XMVECTOR ScaleZ = vdupq_n_f32(sz); + XMVECTOR OffsetZ = vdupq_n_f32(oz); + XMVECTOR VZ = vmlaq_f32(OffsetZ, ScaleZ, V.val[2]); + + r = vget_low_f32(Transform.r[2]); + vResult0 = vmlaq_lane_f32(vResult0, VZ, r, 0); // Ax+Ey+Iz+M + vResult1 = vmlaq_lane_f32(vResult1, VZ, r, 1); // Bx+Fy+Jz+N + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 4)); + + r = vget_high_f32(Transform.r[2]); + vResult2 = vmlaq_lane_f32(vResult2, VZ, r, 0); // Cx+Gy+Kz+O + W = vmlaq_lane_f32(W, VZ, r, 1); // Dx+Hy+Lz+P + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 5)); + + #if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + V.val[0] = vdivq_f32(vResult0, W); + V.val[1] = vdivq_f32(vResult1, W); + V.val[2] = vdivq_f32(vResult2, W); + #else + // 2 iterations of Newton-Raphson refinement of reciprocal + float32x4_t Reciprocal = vrecpeq_f32(W); + float32x4_t S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + + V.val[0] = vmulq_f32(vResult0, Reciprocal); + V.val[1] = vmulq_f32(vResult1, Reciprocal); + V.val[2] = vmulq_f32(vResult2, Reciprocal); + #endif + + vst3q_f32(reinterpret_cast(pOutputVector), V); + pOutputVector += sizeof(XMFLOAT3) * 4; + + i += 4; + } + } + } + + if (i < VectorCount) + { + float32x2_t ScaleL = vcreate_f32( + static_cast(*reinterpret_cast(&sx)) + | (static_cast(*reinterpret_cast(&sy)) << 32)); + float32x2_t ScaleH = vcreate_f32(static_cast(*reinterpret_cast(&sz))); + + float32x2_t OffsetL = vcreate_f32( + static_cast(*reinterpret_cast(&ox)) + | (static_cast(*reinterpret_cast(&oy)) << 32)); + float32x2_t OffsetH = vcreate_f32(static_cast(*reinterpret_cast(&oz))); + + for (; i < VectorCount; i++) + { + float32x2_t VL = vld1_f32(reinterpret_cast(pInputVector)); + float32x2_t zero = vdup_n_f32(0); + float32x2_t VH = vld1_lane_f32(reinterpret_cast(pInputVector) + 2, zero, 0); + pInputVector += InputStride; + + VL = vmla_f32(OffsetL, VL, ScaleL); + VH = vmla_f32(OffsetH, VH, ScaleH); + + XMVECTOR vResult = vmlaq_lane_f32(Transform.r[3], Transform.r[0], VL, 0); // X + vResult = vmlaq_lane_f32(vResult, Transform.r[1], VL, 1); // Y + vResult = vmlaq_lane_f32(vResult, Transform.r[2], VH, 0); // Z + + VH = vget_high_f32(vResult); + XMVECTOR W = vdupq_lane_f32(VH, 1); + + #if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__ + vResult = vdivq_f32(vResult, W); + #else + // 2 iterations of Newton-Raphson refinement of reciprocal for W + float32x4_t Reciprocal = vrecpeq_f32(W); + float32x4_t S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + S = vrecpsq_f32(Reciprocal, W); + Reciprocal = vmulq_f32(S, Reciprocal); + + vResult = vmulq_f32(vResult, Reciprocal); + #endif + + VL = vget_low_f32(vResult); + vst1_f32(reinterpret_cast(pOutputVector), VL); + vst1q_lane_f32(reinterpret_cast(pOutputVector) + 2, vResult, 2); + pOutputVector += OutputStride; + } + } + + return pOutputStream; +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 D = { { { -1.0f, 1.0f, 0.0f, 0.0f } } }; + + XMVECTOR Scale = XMVectorSet(ViewportWidth * 0.5f, -ViewportHeight * 0.5f, ViewportMaxZ - ViewportMinZ, 1.0f); + Scale = XMVectorReciprocal(Scale); + + XMVECTOR Offset = XMVectorSet(-ViewportX, -ViewportY, -ViewportMinZ, 0.0f); + Offset = _mm_mul_ps(Scale, Offset); + Offset = _mm_add_ps(Offset, D); + + XMMATRIX Transform = XMMatrixMultiply(World, View); + Transform = XMMatrixMultiply(Transform, Projection); + Transform = XMMatrixInverse(nullptr, Transform); + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(XMFLOAT3)) + { + if (OutputStride == sizeof(XMFLOAT3)) + { + if (!(reinterpret_cast(pOutputStream) & 0xF)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + V1 = XM_FMADD_PS(V1, Scale, Offset); + + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + XMVECTOR vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + XMVECTOR vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + V1 = _mm_div_ps(vTemp, W); + + // Result 2 + V2 = XM_FMADD_PS(V2, Scale, Offset); + + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + V2 = _mm_div_ps(vTemp, W); + + // Result 3 + V3 = XM_FMADD_PS(V3, Scale, Offset); + + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + V3 = _mm_div_ps(vTemp, W); + + // Result 4 + V4 = XM_FMADD_PS(V4, Scale, Offset); + + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + V4 = _mm_div_ps(vTemp, W); + + // Pack and store the vectors + XM3PACK4INTO3(vTemp); + XM_STREAM_PS(reinterpret_cast(pOutputVector), V1); + XM_STREAM_PS(reinterpret_cast(pOutputVector + 16), vTemp); + XM_STREAM_PS(reinterpret_cast(pOutputVector + 32), V3); + pOutputVector += sizeof(XMFLOAT3) * 4; + i += 4; + } + } + else + { + // Packed input, unaligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + V1 = XM_FMADD_PS(V1, Scale, Offset); + + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + XMVECTOR vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + XMVECTOR vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + V1 = _mm_div_ps(vTemp, W); + + // Result 2 + V2 = XM_FMADD_PS(V2, Scale, Offset); + + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + V2 = _mm_div_ps(vTemp, W); + + // Result 3 + V3 = XM_FMADD_PS(V3, Scale, Offset); + + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + V3 = _mm_div_ps(vTemp, W); + + // Result 4 + V4 = XM_FMADD_PS(V4, Scale, Offset); + + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + V4 = _mm_div_ps(vTemp, W); + + // Pack and store the vectors + XM3PACK4INTO3(vTemp); + _mm_storeu_ps(reinterpret_cast(pOutputVector), V1); + _mm_storeu_ps(reinterpret_cast(pOutputVector + 16), vTemp); + _mm_storeu_ps(reinterpret_cast(pOutputVector + 32), V3); + pOutputVector += sizeof(XMFLOAT3) * 4; + i += 4; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < four; ++j) + { + __m128 V1 = _mm_loadu_ps(reinterpret_cast(pInputVector)); + __m128 L2 = _mm_loadu_ps(reinterpret_cast(pInputVector + 16)); + __m128 L3 = _mm_loadu_ps(reinterpret_cast(pInputVector + 32)); + pInputVector += sizeof(XMFLOAT3) * 4; + + // Unpack the 4 vectors (.w components are junk) + XM3UNPACK3INTO4(V1, L2, L3); + + // Result 1 + V1 = XM_FMADD_PS(V1, Scale, Offset); + + XMVECTOR Z = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + XMVECTOR vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + XMVECTOR vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 2 + V2 = XM_FMADD_PS(V2, Scale, Offset); + + Z = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V2, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 3 + V3 = XM_FMADD_PS(V3, Scale, Offset); + + Z = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + // Result 4 + V4 = XM_FMADD_PS(V4, Scale, Offset); + + Z = XM_PERMUTE_PS(V4, _MM_SHUFFLE(2, 2, 2, 2)); + Y = XM_PERMUTE_PS(V4, _MM_SHUFFLE(1, 1, 1, 1)); + X = XM_PERMUTE_PS(V4, _MM_SHUFFLE(0, 0, 0, 0)); + + vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + + i += 4; + } + } + } + } + + for (; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + V = _mm_mul_ps(V, Scale); + V = _mm_add_ps(V, Offset); + + XMVECTOR Z = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR Y = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR X = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + + XMVECTOR vTemp = XM_FMADD_PS(Z, Transform.r[2], Transform.r[3]); + XMVECTOR vTemp2 = _mm_mul_ps(Y, Transform.r[1]); + XMVECTOR vTemp3 = _mm_mul_ps(X, Transform.r[0]); + vTemp = _mm_add_ps(vTemp, vTemp2); + vTemp = _mm_add_ps(vTemp, vTemp3); + + XMVECTOR W = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 3, 3, 3)); + vTemp = _mm_div_ps(vTemp, W); + + XMStoreFloat3(reinterpret_cast(pOutputVector), vTemp); + pOutputVector += OutputStride; + } + + XM_SFENCE(); + + return pOutputStream; +#endif +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + +/**************************************************************************** + * + * 4D Vector + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparison operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4Equal +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] == V2.vector4_f32[0]) && (V1.vector4_f32[1] == V2.vector4_f32[1]) && (V1.vector4_f32[2] == V2.vector4_f32[2]) && (V1.vector4_f32[3] == V2.vector4_f32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) == 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpeq_ps(V1, V2); + return ((_mm_movemask_ps(vTemp) == 0x0f) != 0); +#else + return XMComparisonAllTrue(XMVector4EqualR(V1, V2)); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector4EqualR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + uint32_t CR = 0; + + if ((V1.vector4_f32[0] == V2.vector4_f32[0]) && + (V1.vector4_f32[1] == V2.vector4_f32[1]) && + (V1.vector4_f32[2] == V2.vector4_f32[2]) && + (V1.vector4_f32[3] == V2.vector4_f32[3])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_f32[0] != V2.vector4_f32[0]) && + (V1.vector4_f32[1] != V2.vector4_f32[1]) && + (V1.vector4_f32[2] != V2.vector4_f32[2]) && + (V1.vector4_f32[3] != V2.vector4_f32[3])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + + uint32_t CR = 0; + if (r == 0xFFFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpeq_ps(V1, V2); + int iTest = _mm_movemask_ps(vTemp); + uint32_t CR = 0; + if (iTest == 0xf) // All equal? + { + CR = XM_CRMASK_CR6TRUE; + } + else if (iTest == 0) // All not equal? + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4EqualInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_u32[0] == V2.vector4_u32[0]) && (V1.vector4_u32[1] == V2.vector4_u32[1]) && (V1.vector4_u32[2] == V2.vector4_u32[2]) && (V1.vector4_u32[3] == V2.vector4_u32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2)); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) == 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return ((_mm_movemask_ps(_mm_castsi128_ps(vTemp)) == 0xf) != 0); +#else + return XMComparisonAllTrue(XMVector4EqualIntR(V1, V2)); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector4EqualIntR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + uint32_t CR = 0; + if (V1.vector4_u32[0] == V2.vector4_u32[0] && + V1.vector4_u32[1] == V2.vector4_u32[1] && + V1.vector4_u32[2] == V2.vector4_u32[2] && + V1.vector4_u32[3] == V2.vector4_u32[3]) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (V1.vector4_u32[0] != V2.vector4_u32[0] && + V1.vector4_u32[1] != V2.vector4_u32[1] && + V1.vector4_u32[2] != V2.vector4_u32[2] && + V1.vector4_u32[3] != V2.vector4_u32[3]) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2)); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + + uint32_t CR = 0; + if (r == 0xFFFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + int iTest = _mm_movemask_ps(_mm_castsi128_ps(vTemp)); + uint32_t CR = 0; + if (iTest == 0xf) // All equal? + { + CR = XM_CRMASK_CR6TRUE; + } + else if (iTest == 0) // All not equal? + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +inline bool XM_CALLCONV XMVector4NearEqual +( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR Epsilon +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float dx, dy, dz, dw; + + dx = fabsf(V1.vector4_f32[0] - V2.vector4_f32[0]); + dy = fabsf(V1.vector4_f32[1] - V2.vector4_f32[1]); + dz = fabsf(V1.vector4_f32[2] - V2.vector4_f32[2]); + dw = fabsf(V1.vector4_f32[3] - V2.vector4_f32[3]); + return (((dx <= Epsilon.vector4_f32[0]) && + (dy <= Epsilon.vector4_f32[1]) && + (dz <= Epsilon.vector4_f32[2]) && + (dw <= Epsilon.vector4_f32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vDelta = vsubq_f32(V1, V2); +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_ARM64_DISTINCT_NEON_TYPES) + uint32x4_t vResult = vacleq_f32(vDelta, Epsilon); +#else + uint32x4_t vResult = vcleq_f32(vabsq_f32(vDelta), Epsilon); +#endif + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) == 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + // Get the difference + XMVECTOR vDelta = _mm_sub_ps(V1, V2); + // Get the absolute value of the difference + XMVECTOR vTemp = _mm_setzero_ps(); + vTemp = _mm_sub_ps(vTemp, vDelta); + vTemp = _mm_max_ps(vTemp, vDelta); + vTemp = _mm_cmple_ps(vTemp, Epsilon); + return ((_mm_movemask_ps(vTemp) == 0xf) != 0); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4NotEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] != V2.vector4_f32[0]) || (V1.vector4_f32[1] != V2.vector4_f32[1]) || (V1.vector4_f32[2] != V2.vector4_f32[2]) || (V1.vector4_f32[3] != V2.vector4_f32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) != 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpneq_ps(V1, V2); + return ((_mm_movemask_ps(vTemp)) != 0); +#else + return XMComparisonAnyFalse(XMVector4EqualR(V1, V2)); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4NotEqualInt +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_u32[0] != V2.vector4_u32[0]) || (V1.vector4_u32[1] != V2.vector4_u32[1]) || (V1.vector4_u32[2] != V2.vector4_u32[2]) || (V1.vector4_u32[3] != V2.vector4_u32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vceqq_u32(vreinterpretq_u32_f32(V1), vreinterpretq_u32_f32(V2)); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) != 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + __m128i vTemp = _mm_cmpeq_epi32(_mm_castps_si128(V1), _mm_castps_si128(V2)); + return ((_mm_movemask_ps(_mm_castsi128_ps(vTemp)) != 0xF) != 0); +#else + return XMComparisonAnyFalse(XMVector4EqualIntR(V1, V2)); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4Greater +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] > V2.vector4_f32[0]) && (V1.vector4_f32[1] > V2.vector4_f32[1]) && (V1.vector4_f32[2] > V2.vector4_f32[2]) && (V1.vector4_f32[3] > V2.vector4_f32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgtq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) == 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpgt_ps(V1, V2); + return ((_mm_movemask_ps(vTemp) == 0x0f) != 0); +#else + return XMComparisonAllTrue(XMVector4GreaterR(V1, V2)); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector4GreaterR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + uint32_t CR = 0; + if (V1.vector4_f32[0] > V2.vector4_f32[0] && + V1.vector4_f32[1] > V2.vector4_f32[1] && + V1.vector4_f32[2] > V2.vector4_f32[2] && + V1.vector4_f32[3] > V2.vector4_f32[3]) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (V1.vector4_f32[0] <= V2.vector4_f32[0] && + V1.vector4_f32[1] <= V2.vector4_f32[1] && + V1.vector4_f32[2] <= V2.vector4_f32[2] && + V1.vector4_f32[3] <= V2.vector4_f32[3]) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgtq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + + uint32_t CR = 0; + if (r == 0xFFFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + uint32_t CR = 0; + XMVECTOR vTemp = _mm_cmpgt_ps(V1, V2); + int iTest = _mm_movemask_ps(vTemp); + if (iTest == 0xf) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4GreaterOrEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] >= V2.vector4_f32[0]) && (V1.vector4_f32[1] >= V2.vector4_f32[1]) && (V1.vector4_f32[2] >= V2.vector4_f32[2]) && (V1.vector4_f32[3] >= V2.vector4_f32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgeq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) == 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmpge_ps(V1, V2); + return ((_mm_movemask_ps(vTemp) == 0x0f) != 0); +#else + return XMComparisonAllTrue(XMVector4GreaterOrEqualR(V1, V2)); +#endif +} + +//------------------------------------------------------------------------------ + +inline uint32_t XM_CALLCONV XMVector4GreaterOrEqualR +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + uint32_t CR = 0; + if ((V1.vector4_f32[0] >= V2.vector4_f32[0]) && + (V1.vector4_f32[1] >= V2.vector4_f32[1]) && + (V1.vector4_f32[2] >= V2.vector4_f32[2]) && + (V1.vector4_f32[3] >= V2.vector4_f32[3])) + { + CR = XM_CRMASK_CR6TRUE; + } + else if ((V1.vector4_f32[0] < V2.vector4_f32[0]) && + (V1.vector4_f32[1] < V2.vector4_f32[1]) && + (V1.vector4_f32[2] < V2.vector4_f32[2]) && + (V1.vector4_f32[3] < V2.vector4_f32[3])) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcgeq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + uint32_t r = vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1); + + uint32_t CR = 0; + if (r == 0xFFFFFFFFU) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!r) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#elif defined(_XM_SSE_INTRINSICS_) + uint32_t CR = 0; + XMVECTOR vTemp = _mm_cmpge_ps(V1, V2); + int iTest = _mm_movemask_ps(vTemp); + if (iTest == 0x0f) + { + CR = XM_CRMASK_CR6TRUE; + } + else if (!iTest) + { + CR = XM_CRMASK_CR6FALSE; + } + return CR; +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4Less +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] < V2.vector4_f32[0]) && (V1.vector4_f32[1] < V2.vector4_f32[1]) && (V1.vector4_f32[2] < V2.vector4_f32[2]) && (V1.vector4_f32[3] < V2.vector4_f32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcltq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) == 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmplt_ps(V1, V2); + return ((_mm_movemask_ps(vTemp) == 0x0f) != 0); +#else + return XMComparisonAllTrue(XMVector4GreaterR(V2, V1)); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4LessOrEqual +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V1.vector4_f32[0] <= V2.vector4_f32[0]) && (V1.vector4_f32[1] <= V2.vector4_f32[1]) && (V1.vector4_f32[2] <= V2.vector4_f32[2]) && (V1.vector4_f32[3] <= V2.vector4_f32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vResult = vcleq_f32(V1, V2); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vResult)), vget_high_u8(vreinterpretq_u8_u32(vResult))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) == 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp = _mm_cmple_ps(V1, V2); + return ((_mm_movemask_ps(vTemp) == 0x0f) != 0); +#else + return XMComparisonAllTrue(XMVector4GreaterOrEqualR(V2, V1)); +#endif +} + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4InBounds +( + FXMVECTOR V, + FXMVECTOR Bounds +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (((V.vector4_f32[0] <= Bounds.vector4_f32[0] && V.vector4_f32[0] >= -Bounds.vector4_f32[0]) && + (V.vector4_f32[1] <= Bounds.vector4_f32[1] && V.vector4_f32[1] >= -Bounds.vector4_f32[1]) && + (V.vector4_f32[2] <= Bounds.vector4_f32[2] && V.vector4_f32[2] >= -Bounds.vector4_f32[2]) && + (V.vector4_f32[3] <= Bounds.vector4_f32[3] && V.vector4_f32[3] >= -Bounds.vector4_f32[3])) != 0); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Test if less than or equal + uint32x4_t ivTemp1 = vcleq_f32(V, Bounds); + // Negate the bounds + float32x4_t vTemp2 = vnegq_f32(Bounds); + // Test if greater or equal (Reversed) + uint32x4_t ivTemp2 = vcleq_f32(vTemp2, V); + // Blend answers + ivTemp1 = vandq_u32(ivTemp1, ivTemp2); + // in bounds? + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(ivTemp1)), vget_high_u8(vreinterpretq_u8_u32(ivTemp1))); + uint16x4x2_t vTemp3 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp3.val[1]), 1) == 0xFFFFFFFFU); +#elif defined(_XM_SSE_INTRINSICS_) + // Test if less than or equal + XMVECTOR vTemp1 = _mm_cmple_ps(V, Bounds); + // Negate the bounds + XMVECTOR vTemp2 = _mm_mul_ps(Bounds, g_XMNegativeOne); + // Test if greater or equal (Reversed) + vTemp2 = _mm_cmple_ps(vTemp2, V); + // Blend answers + vTemp1 = _mm_and_ps(vTemp1, vTemp2); + // All in bounds? + return ((_mm_movemask_ps(vTemp1) == 0x0f) != 0); +#else + return XMComparisonAllInBounds(XMVector4InBoundsR(V, Bounds)); +#endif +} + +//------------------------------------------------------------------------------ + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(push) +#pragma float_control(precise, on) +#endif + +inline bool XM_CALLCONV XMVector4IsNaN(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + return (XMISNAN(V.vector4_f32[0]) || + XMISNAN(V.vector4_f32[1]) || + XMISNAN(V.vector4_f32[2]) || + XMISNAN(V.vector4_f32[3])); +#elif defined(_XM_ARM_NEON_INTRINSICS_) +#if defined(__clang__) && defined(__FINITE_MATH_ONLY__) + return isnan(vgetq_lane_f32(V, 0)) || isnan(vgetq_lane_f32(V, 1)) || isnan(vgetq_lane_f32(V, 2)) || isnan(vgetq_lane_f32(V, 3)); +#else +// Test against itself. NaN is always not equal + uint32x4_t vTempNan = vceqq_f32(V, V); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vTempNan)), vget_high_u8(vreinterpretq_u8_u32(vTempNan))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + // If any are NaN, the mask is zero + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) != 0xFFFFFFFFU); +#endif +#elif defined(_XM_SSE_INTRINSICS_) +#if defined(__clang__) && defined(__FINITE_MATH_ONLY__) + XM_ALIGNED_DATA(16) float tmp[4]; + _mm_store_ps(tmp, V); + return isnan(tmp[0]) || isnan(tmp[1]) || isnan(tmp[2]) || isnan(tmp[3]); +#else +// Test against itself. NaN is always not equal + XMVECTOR vTempNan = _mm_cmpneq_ps(V, V); + // If any are NaN, the mask is non-zero + return (_mm_movemask_ps(vTempNan) != 0); +#endif +#endif +} + +#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma float_control(pop) +#endif + +//------------------------------------------------------------------------------ + +inline bool XM_CALLCONV XMVector4IsInfinite(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + return (XMISINF(V.vector4_f32[0]) || + XMISINF(V.vector4_f32[1]) || + XMISINF(V.vector4_f32[2]) || + XMISINF(V.vector4_f32[3])); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Mask off the sign bit + uint32x4_t vTempInf = vandq_u32(vreinterpretq_u32_f32(V), g_XMAbsMask); + // Compare to infinity + vTempInf = vceqq_f32(vreinterpretq_f32_u32(vTempInf), g_XMInfinity); + // If any are infinity, the signs are true. + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(vTempInf)), vget_high_u8(vreinterpretq_u8_u32(vTempInf))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + return (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) != 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Mask off the sign bit + XMVECTOR vTemp = _mm_and_ps(V, g_XMAbsMask); + // Compare to infinity + vTemp = _mm_cmpeq_ps(vTemp, g_XMInfinity); + // If any are infinity, the signs are true. + return (_mm_movemask_ps(vTemp) != 0); +#endif +} + +//------------------------------------------------------------------------------ +// Computation operations +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4Dot +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result; + Result.f[0] = + Result.f[1] = + Result.f[2] = + Result.f[3] = V1.vector4_f32[0] * V2.vector4_f32[0] + V1.vector4_f32[1] * V2.vector4_f32[1] + V1.vector4_f32[2] * V2.vector4_f32[2] + V1.vector4_f32[3] * V2.vector4_f32[3]; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vTemp = vmulq_f32(V1, V2); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vadd_f32(v1, v2); + v1 = vpadd_f32(v1, v1); + return vcombine_f32(v1, v1); +#elif defined(_XM_SSE4_INTRINSICS_) + return _mm_dp_ps(V1, V2, 0xff); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vTemp = _mm_mul_ps(V1, V2); + vTemp = _mm_hadd_ps(vTemp, vTemp); + return _mm_hadd_ps(vTemp, vTemp); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vTemp2 = V2; + XMVECTOR vTemp = _mm_mul_ps(V1, vTemp2); + vTemp2 = _mm_shuffle_ps(vTemp2, vTemp, _MM_SHUFFLE(1, 0, 0, 0)); // Copy X to the Z position and Y to the W position + vTemp2 = _mm_add_ps(vTemp2, vTemp); // Add Z = X+Z; W = Y+W; + vTemp = _mm_shuffle_ps(vTemp, vTemp2, _MM_SHUFFLE(0, 3, 0, 0)); // Copy W to the Z position + vTemp = _mm_add_ps(vTemp, vTemp2); // Add Z and W together + return XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(2, 2, 2, 2)); // Splat Z and return +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4Cross +( + FXMVECTOR V1, + FXMVECTOR V2, + FXMVECTOR V3 +) noexcept +{ + // [ ((v2.z*v3.w-v2.w*v3.z)*v1.y)-((v2.y*v3.w-v2.w*v3.y)*v1.z)+((v2.y*v3.z-v2.z*v3.y)*v1.w), + // ((v2.w*v3.z-v2.z*v3.w)*v1.x)-((v2.w*v3.x-v2.x*v3.w)*v1.z)+((v2.z*v3.x-v2.x*v3.z)*v1.w), + // ((v2.y*v3.w-v2.w*v3.y)*v1.x)-((v2.x*v3.w-v2.w*v3.x)*v1.y)+((v2.x*v3.y-v2.y*v3.x)*v1.w), + // ((v2.z*v3.y-v2.y*v3.z)*v1.x)-((v2.z*v3.x-v2.x*v3.z)*v1.y)+((v2.y*v3.x-v2.x*v3.y)*v1.z) ] + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + (((V2.vector4_f32[2] * V3.vector4_f32[3]) - (V2.vector4_f32[3] * V3.vector4_f32[2])) * V1.vector4_f32[1]) - (((V2.vector4_f32[1] * V3.vector4_f32[3]) - (V2.vector4_f32[3] * V3.vector4_f32[1])) * V1.vector4_f32[2]) + (((V2.vector4_f32[1] * V3.vector4_f32[2]) - (V2.vector4_f32[2] * V3.vector4_f32[1])) * V1.vector4_f32[3]), + (((V2.vector4_f32[3] * V3.vector4_f32[2]) - (V2.vector4_f32[2] * V3.vector4_f32[3])) * V1.vector4_f32[0]) - (((V2.vector4_f32[3] * V3.vector4_f32[0]) - (V2.vector4_f32[0] * V3.vector4_f32[3])) * V1.vector4_f32[2]) + (((V2.vector4_f32[2] * V3.vector4_f32[0]) - (V2.vector4_f32[0] * V3.vector4_f32[2])) * V1.vector4_f32[3]), + (((V2.vector4_f32[1] * V3.vector4_f32[3]) - (V2.vector4_f32[3] * V3.vector4_f32[1])) * V1.vector4_f32[0]) - (((V2.vector4_f32[0] * V3.vector4_f32[3]) - (V2.vector4_f32[3] * V3.vector4_f32[0])) * V1.vector4_f32[1]) + (((V2.vector4_f32[0] * V3.vector4_f32[1]) - (V2.vector4_f32[1] * V3.vector4_f32[0])) * V1.vector4_f32[3]), + (((V2.vector4_f32[2] * V3.vector4_f32[1]) - (V2.vector4_f32[1] * V3.vector4_f32[2])) * V1.vector4_f32[0]) - (((V2.vector4_f32[2] * V3.vector4_f32[0]) - (V2.vector4_f32[0] * V3.vector4_f32[2])) * V1.vector4_f32[1]) + (((V2.vector4_f32[1] * V3.vector4_f32[0]) - (V2.vector4_f32[0] * V3.vector4_f32[1])) * V1.vector4_f32[2]), + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + const uint32x2_t select = vget_low_u32(g_XMMaskX); + + // Term1: V2zwyz * V3wzwy + const float32x2_t v2xy = vget_low_f32(V2); + const float32x2_t v2zw = vget_high_f32(V2); + const float32x2_t v2yx = vrev64_f32(v2xy); + const float32x2_t v2wz = vrev64_f32(v2zw); + const float32x2_t v2yz = vbsl_f32(select, v2yx, v2wz); + + const float32x2_t v3zw = vget_high_f32(V3); + const float32x2_t v3wz = vrev64_f32(v3zw); + const float32x2_t v3xy = vget_low_f32(V3); + const float32x2_t v3wy = vbsl_f32(select, v3wz, v3xy); + + float32x4_t vTemp1 = vcombine_f32(v2zw, v2yz); + float32x4_t vTemp2 = vcombine_f32(v3wz, v3wy); + XMVECTOR vResult = vmulq_f32(vTemp1, vTemp2); + + // - V2wzwy * V3zwyz + const float32x2_t v2wy = vbsl_f32(select, v2wz, v2xy); + + const float32x2_t v3yx = vrev64_f32(v3xy); + const float32x2_t v3yz = vbsl_f32(select, v3yx, v3wz); + + vTemp1 = vcombine_f32(v2wz, v2wy); + vTemp2 = vcombine_f32(v3zw, v3yz); + vResult = vmlsq_f32(vResult, vTemp1, vTemp2); + + // term1 * V1yxxx + const float32x2_t v1xy = vget_low_f32(V1); + const float32x2_t v1yx = vrev64_f32(v1xy); + + vTemp1 = vcombine_f32(v1yx, vdup_lane_f32(v1yx, 1)); + vResult = vmulq_f32(vResult, vTemp1); + + // Term2: V2ywxz * V3wxwx + const float32x2_t v2yw = vrev64_f32(v2wy); + const float32x2_t v2xz = vbsl_f32(select, v2xy, v2wz); + + const float32x2_t v3wx = vbsl_f32(select, v3wz, v3yx); + + vTemp1 = vcombine_f32(v2yw, v2xz); + vTemp2 = vcombine_f32(v3wx, v3wx); + float32x4_t vTerm = vmulq_f32(vTemp1, vTemp2); + + // - V2wxwx * V3ywxz + const float32x2_t v2wx = vbsl_f32(select, v2wz, v2yx); + + const float32x2_t v3yw = vrev64_f32(v3wy); + const float32x2_t v3xz = vbsl_f32(select, v3xy, v3wz); + + vTemp1 = vcombine_f32(v2wx, v2wx); + vTemp2 = vcombine_f32(v3yw, v3xz); + vTerm = vmlsq_f32(vTerm, vTemp1, vTemp2); + + // vResult - term2 * V1zzyy + const float32x2_t v1zw = vget_high_f32(V1); + + vTemp1 = vcombine_f32(vdup_lane_f32(v1zw, 0), vdup_lane_f32(v1yx, 0)); + vResult = vmlsq_f32(vResult, vTerm, vTemp1); + + // Term3: V2yzxy * V3zxyx + const float32x2_t v3zx = vrev64_f32(v3xz); + + vTemp1 = vcombine_f32(v2yz, v2xy); + vTemp2 = vcombine_f32(v3zx, v3yx); + vTerm = vmulq_f32(vTemp1, vTemp2); + + // - V2zxyx * V3yzxy + const float32x2_t v2zx = vrev64_f32(v2xz); + + vTemp1 = vcombine_f32(v2zx, v2yx); + vTemp2 = vcombine_f32(v3yz, v3xy); + vTerm = vmlsq_f32(vTerm, vTemp1, vTemp2); + + // vResult + term3 * V1wwwz + const float32x2_t v1wz = vrev64_f32(v1zw); + + vTemp1 = vcombine_f32(vdup_lane_f32(v1wz, 0), v1wz); + return vmlaq_f32(vResult, vTerm, vTemp1); +#elif defined(_XM_SSE_INTRINSICS_) + // V2zwyz * V3wzwy + XMVECTOR vResult = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 1, 3, 2)); + XMVECTOR vTemp3 = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 3, 2, 3)); + vResult = _mm_mul_ps(vResult, vTemp3); + // - V2wzwy * V3zwyz + XMVECTOR vTemp2 = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 3, 2, 3)); + vTemp3 = XM_PERMUTE_PS(vTemp3, _MM_SHUFFLE(1, 3, 0, 1)); + vResult = XM_FNMADD_PS(vTemp2, vTemp3, vResult); + // term1 * V1yxxx + XMVECTOR vTemp1 = XM_PERMUTE_PS(V1, _MM_SHUFFLE(0, 0, 0, 1)); + vResult = _mm_mul_ps(vResult, vTemp1); + + // V2ywxz * V3wxwx + vTemp2 = XM_PERMUTE_PS(V2, _MM_SHUFFLE(2, 0, 3, 1)); + vTemp3 = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 3, 0, 3)); + vTemp3 = _mm_mul_ps(vTemp3, vTemp2); + // - V2wxwx * V3ywxz + vTemp2 = XM_PERMUTE_PS(vTemp2, _MM_SHUFFLE(2, 1, 2, 1)); + vTemp1 = XM_PERMUTE_PS(V3, _MM_SHUFFLE(2, 0, 3, 1)); + vTemp3 = XM_FNMADD_PS(vTemp2, vTemp1, vTemp3); + // vResult - temp * V1zzyy + vTemp1 = XM_PERMUTE_PS(V1, _MM_SHUFFLE(1, 1, 2, 2)); + vResult = XM_FNMADD_PS(vTemp1, vTemp3, vResult); + + // V2yzxy * V3zxyx + vTemp2 = XM_PERMUTE_PS(V2, _MM_SHUFFLE(1, 0, 2, 1)); + vTemp3 = XM_PERMUTE_PS(V3, _MM_SHUFFLE(0, 1, 0, 2)); + vTemp3 = _mm_mul_ps(vTemp3, vTemp2); + // - V2zxyx * V3yzxy + vTemp2 = XM_PERMUTE_PS(vTemp2, _MM_SHUFFLE(2, 0, 2, 1)); + vTemp1 = XM_PERMUTE_PS(V3, _MM_SHUFFLE(1, 0, 2, 1)); + vTemp3 = XM_FNMADD_PS(vTemp1, vTemp2, vTemp3); + // vResult + term * V1wwwz + vTemp1 = XM_PERMUTE_PS(V1, _MM_SHUFFLE(2, 3, 3, 3)); + vResult = XM_FMADD_PS(vTemp3, vTemp1, vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4LengthSq(FXMVECTOR V) noexcept +{ + return XMVector4Dot(V, V); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4ReciprocalLengthEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + + Result = XMVector4LengthSq(V); + Result = XMVectorReciprocalSqrtEst(Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot4 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vadd_f32(v1, v2); + v1 = vpadd_f32(v1, v1); + // Reciprocal sqrt (estimate) + v2 = vrsqrte_f32(v1); + return vcombine_f32(v2, v2); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + return _mm_rsqrt_ps(vTemp); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_rsqrt_ps(vLengthSq); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y,z and w + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has z and w + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(3, 2, 3, 2)); + // x+z, y+w + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // x+z,x+z,x+z,y+w + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 0, 0, 0)); + // ??,??,y+w,y+w + vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0)); + // ??,??,x+z+y+w,?? + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // Splat the length + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(2, 2, 2, 2)); + // Get the reciprocal + vLengthSq = _mm_rsqrt_ps(vLengthSq); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4ReciprocalLength(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + + Result = XMVector4LengthSq(V); + Result = XMVectorReciprocalSqrt(Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot4 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vadd_f32(v1, v2); + v1 = vpadd_f32(v1, v1); + // Reciprocal sqrt + float32x2_t S0 = vrsqrte_f32(v1); + float32x2_t P0 = vmul_f32(v1, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(v1, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + float32x2_t Result = vmul_f32(S1, R1); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + XMVECTOR vLengthSq = _mm_sqrt_ps(vTemp); + return _mm_div_ps(g_XMOne, vLengthSq); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_sqrt_ps(vLengthSq); + vLengthSq = _mm_div_ps(g_XMOne, vLengthSq); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y,z and w + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has z and w + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(3, 2, 3, 2)); + // x+z, y+w + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // x+z,x+z,x+z,y+w + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 0, 0, 0)); + // ??,??,y+w,y+w + vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0)); + // ??,??,x+z+y+w,?? + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // Splat the length + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(2, 2, 2, 2)); + // Get the reciprocal + vLengthSq = _mm_sqrt_ps(vLengthSq); + // Accurate! + vLengthSq = _mm_div_ps(g_XMOne, vLengthSq); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4LengthEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + + Result = XMVector4LengthSq(V); + Result = XMVectorSqrtEst(Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot4 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vadd_f32(v1, v2); + v1 = vpadd_f32(v1, v1); + const float32x2_t zero = vdup_n_f32(0); + uint32x2_t VEqualsZero = vceq_f32(v1, zero); + // Sqrt (estimate) + float32x2_t Result = vrsqrte_f32(v1); + Result = vmul_f32(v1, Result); + Result = vbsl_f32(VEqualsZero, zero, Result); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + return _mm_sqrt_ps(vTemp); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_sqrt_ps(vLengthSq); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y,z and w + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has z and w + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(3, 2, 3, 2)); + // x+z, y+w + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // x+z,x+z,x+z,y+w + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 0, 0, 0)); + // ??,??,y+w,y+w + vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0)); + // ??,??,x+z+y+w,?? + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // Splat the length + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(2, 2, 2, 2)); + // Get the length + vLengthSq = _mm_sqrt_ps(vLengthSq); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4Length(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + + Result = XMVector4LengthSq(V); + Result = XMVectorSqrt(Result); + + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot4 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vadd_f32(v1, v2); + v1 = vpadd_f32(v1, v1); + const float32x2_t zero = vdup_n_f32(0); + uint32x2_t VEqualsZero = vceq_f32(v1, zero); + // Sqrt + float32x2_t S0 = vrsqrte_f32(v1); + float32x2_t P0 = vmul_f32(v1, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(v1, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + float32x2_t Result = vmul_f32(S1, R1); + Result = vmul_f32(v1, Result); + Result = vbsl_f32(VEqualsZero, zero, Result); + return vcombine_f32(Result, Result); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + return _mm_sqrt_ps(vTemp); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_sqrt_ps(vLengthSq); + return vLengthSq; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y,z and w + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has z and w + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(3, 2, 3, 2)); + // x+z, y+w + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // x+z,x+z,x+z,y+w + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 0, 0, 0)); + // ??,??,y+w,y+w + vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0)); + // ??,??,x+z+y+w,?? + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // Splat the length + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(2, 2, 2, 2)); + // Get the length + vLengthSq = _mm_sqrt_ps(vLengthSq); + return vLengthSq; +#endif +} + +//------------------------------------------------------------------------------ +// XMVector4NormalizeEst uses a reciprocal estimate and +// returns QNaN on zero and infinite vectors. + +inline XMVECTOR XM_CALLCONV XMVector4NormalizeEst(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR Result; + Result = XMVector4ReciprocalLength(V); + Result = XMVectorMultiply(V, Result); + return Result; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot4 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vadd_f32(v1, v2); + v1 = vpadd_f32(v1, v1); + // Reciprocal sqrt (estimate) + v2 = vrsqrte_f32(v1); + // Normalize + return vmulq_f32(V, vcombine_f32(v2, v2)); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vTemp = _mm_dp_ps(V, V, 0xff); + XMVECTOR vResult = _mm_rsqrt_ps(vTemp); + return _mm_mul_ps(vResult, V); +#elif defined(_XM_SSE3_INTRINSICS_) + XMVECTOR vDot = _mm_mul_ps(V, V); + vDot = _mm_hadd_ps(vDot, vDot); + vDot = _mm_hadd_ps(vDot, vDot); + vDot = _mm_rsqrt_ps(vDot); + vDot = _mm_mul_ps(vDot, V); + return vDot; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y,z and w + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has z and w + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(3, 2, 3, 2)); + // x+z, y+w + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // x+z,x+z,x+z,y+w + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 0, 0, 0)); + // ??,??,y+w,y+w + vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0)); + // ??,??,x+z+y+w,?? + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // Splat the length + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(2, 2, 2, 2)); + // Get the reciprocal + XMVECTOR vResult = _mm_rsqrt_ps(vLengthSq); + // Reciprocal mul to perform the normalization + vResult = _mm_mul_ps(vResult, V); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4Normalize(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + float fLength; + XMVECTOR vResult; + + vResult = XMVector4Length(V); + fLength = vResult.vector4_f32[0]; + + // Prevent divide by zero + if (fLength > 0) + { + fLength = 1.0f / fLength; + } + + vResult.vector4_f32[0] = V.vector4_f32[0] * fLength; + vResult.vector4_f32[1] = V.vector4_f32[1] * fLength; + vResult.vector4_f32[2] = V.vector4_f32[2] * fLength; + vResult.vector4_f32[3] = V.vector4_f32[3] * fLength; + return vResult; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + // Dot4 + float32x4_t vTemp = vmulq_f32(V, V); + float32x2_t v1 = vget_low_f32(vTemp); + float32x2_t v2 = vget_high_f32(vTemp); + v1 = vadd_f32(v1, v2); + v1 = vpadd_f32(v1, v1); + uint32x2_t VEqualsZero = vceq_f32(v1, vdup_n_f32(0)); + uint32x2_t VEqualsInf = vceq_f32(v1, vget_low_f32(g_XMInfinity)); + // Reciprocal sqrt (2 iterations of Newton-Raphson) + float32x2_t S0 = vrsqrte_f32(v1); + float32x2_t P0 = vmul_f32(v1, S0); + float32x2_t R0 = vrsqrts_f32(P0, S0); + float32x2_t S1 = vmul_f32(S0, R0); + float32x2_t P1 = vmul_f32(v1, S1); + float32x2_t R1 = vrsqrts_f32(P1, S1); + v2 = vmul_f32(S1, R1); + // Normalize + XMVECTOR vResult = vmulq_f32(V, vcombine_f32(v2, v2)); + vResult = vbslq_f32(vcombine_u32(VEqualsZero, VEqualsZero), vdupq_n_f32(0), vResult); + return vbslq_f32(vcombine_u32(VEqualsInf, VEqualsInf), g_XMQNaN, vResult); +#elif defined(_XM_SSE4_INTRINSICS_) + XMVECTOR vLengthSq = _mm_dp_ps(V, V, 0xff); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Divide to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; +#elif defined(_XM_SSE3_INTRINSICS_) + // Perform the dot product on x,y,z and w + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + vLengthSq = _mm_hadd_ps(vLengthSq, vLengthSq); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Divide to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + // Perform the dot product on x,y,z and w + XMVECTOR vLengthSq = _mm_mul_ps(V, V); + // vTemp has z and w + XMVECTOR vTemp = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(3, 2, 3, 2)); + // x+z, y+w + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // x+z,x+z,x+z,y+w + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(1, 0, 0, 0)); + // ??,??,y+w,y+w + vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0)); + // ??,??,x+z+y+w,?? + vLengthSq = _mm_add_ps(vLengthSq, vTemp); + // Splat the length + vLengthSq = XM_PERMUTE_PS(vLengthSq, _MM_SHUFFLE(2, 2, 2, 2)); + // Prepare for the division + XMVECTOR vResult = _mm_sqrt_ps(vLengthSq); + // Create zero with a single instruction + XMVECTOR vZeroMask = _mm_setzero_ps(); + // Test for a divide by zero (Must be FP to detect -0.0) + vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); + // Failsafe on zero (Or epsilon) length planes + // If the length is infinity, set the elements to zero + vLengthSq = _mm_cmpneq_ps(vLengthSq, g_XMInfinity); + // Divide to perform the normalization + vResult = _mm_div_ps(V, vResult); + // Any that are infinity, set to zero + vResult = _mm_and_ps(vResult, vZeroMask); + // Select qnan or result based on infinite length + XMVECTOR vTemp1 = _mm_andnot_ps(vLengthSq, g_XMQNaN); + XMVECTOR vTemp2 = _mm_and_ps(vResult, vLengthSq); + vResult = _mm_or_ps(vTemp1, vTemp2); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4ClampLength +( + FXMVECTOR V, + float LengthMin, + float LengthMax +) noexcept +{ + XMVECTOR ClampMax = XMVectorReplicate(LengthMax); + XMVECTOR ClampMin = XMVectorReplicate(LengthMin); + + return XMVector4ClampLengthV(V, ClampMin, ClampMax); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4ClampLengthV +( + FXMVECTOR V, + FXMVECTOR LengthMin, + FXMVECTOR LengthMax +) noexcept +{ + assert((XMVectorGetY(LengthMin) == XMVectorGetX(LengthMin)) && (XMVectorGetZ(LengthMin) == XMVectorGetX(LengthMin)) && (XMVectorGetW(LengthMin) == XMVectorGetX(LengthMin))); + assert((XMVectorGetY(LengthMax) == XMVectorGetX(LengthMax)) && (XMVectorGetZ(LengthMax) == XMVectorGetX(LengthMax)) && (XMVectorGetW(LengthMax) == XMVectorGetX(LengthMax))); + assert(XMVector4GreaterOrEqual(LengthMin, XMVectorZero())); + assert(XMVector4GreaterOrEqual(LengthMax, XMVectorZero())); + assert(XMVector4GreaterOrEqual(LengthMax, LengthMin)); + + XMVECTOR LengthSq = XMVector4LengthSq(V); + + const XMVECTOR Zero = XMVectorZero(); + + XMVECTOR RcpLength = XMVectorReciprocalSqrt(LengthSq); + + XMVECTOR InfiniteLength = XMVectorEqualInt(LengthSq, g_XMInfinity.v); + XMVECTOR ZeroLength = XMVectorEqual(LengthSq, Zero); + + XMVECTOR Normal = XMVectorMultiply(V, RcpLength); + + XMVECTOR Length = XMVectorMultiply(LengthSq, RcpLength); + + XMVECTOR Select = XMVectorEqualInt(InfiniteLength, ZeroLength); + Length = XMVectorSelect(LengthSq, Length, Select); + Normal = XMVectorSelect(LengthSq, Normal, Select); + + XMVECTOR ControlMax = XMVectorGreater(Length, LengthMax); + XMVECTOR ControlMin = XMVectorLess(Length, LengthMin); + + XMVECTOR ClampLength = XMVectorSelect(Length, LengthMax, ControlMax); + ClampLength = XMVectorSelect(ClampLength, LengthMin, ControlMin); + + XMVECTOR Result = XMVectorMultiply(Normal, ClampLength); + + // Preserve the original vector (with no precision loss) if the length falls within the given range + XMVECTOR Control = XMVectorEqualInt(ControlMax, ControlMin); + Result = XMVectorSelect(Result, V, Control); + + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4Reflect +( + FXMVECTOR Incident, + FXMVECTOR Normal +) noexcept +{ + // Result = Incident - (2 * dot(Incident, Normal)) * Normal + + XMVECTOR Result = XMVector4Dot(Incident, Normal); + Result = XMVectorAdd(Result, Result); + Result = XMVectorNegativeMultiplySubtract(Result, Normal, Incident); + + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4Refract +( + FXMVECTOR Incident, + FXMVECTOR Normal, + float RefractionIndex +) noexcept +{ + XMVECTOR Index = XMVectorReplicate(RefractionIndex); + return XMVector4RefractV(Incident, Normal, Index); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4RefractV +( + FXMVECTOR Incident, + FXMVECTOR Normal, + FXMVECTOR RefractionIndex +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR IDotN; + XMVECTOR R; + const XMVECTOR Zero = XMVectorZero(); + + // Result = RefractionIndex * Incident - Normal * (RefractionIndex * dot(Incident, Normal) + + // sqrt(1 - RefractionIndex * RefractionIndex * (1 - dot(Incident, Normal) * dot(Incident, Normal)))) + + IDotN = XMVector4Dot(Incident, Normal); + + // R = 1.0f - RefractionIndex * RefractionIndex * (1.0f - IDotN * IDotN) + R = XMVectorNegativeMultiplySubtract(IDotN, IDotN, g_XMOne.v); + R = XMVectorMultiply(R, RefractionIndex); + R = XMVectorNegativeMultiplySubtract(R, RefractionIndex, g_XMOne.v); + + if (XMVector4LessOrEqual(R, Zero)) + { + // Total internal reflection + return Zero; + } + else + { + XMVECTOR Result; + + // R = RefractionIndex * IDotN + sqrt(R) + R = XMVectorSqrt(R); + R = XMVectorMultiplyAdd(RefractionIndex, IDotN, R); + + // Result = RefractionIndex * Incident - Normal * R + Result = XMVectorMultiply(RefractionIndex, Incident); + Result = XMVectorNegativeMultiplySubtract(Normal, R, Result); + + return Result; + } + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + XMVECTOR IDotN = XMVector4Dot(Incident, Normal); + + // R = 1.0f - RefractionIndex * RefractionIndex * (1.0f - IDotN * IDotN) + float32x4_t R = vmlsq_f32(g_XMOne, IDotN, IDotN); + R = vmulq_f32(R, RefractionIndex); + R = vmlsq_f32(g_XMOne, R, RefractionIndex); + + uint32x4_t isrzero = vcleq_f32(R, g_XMZero); + uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vreinterpretq_u8_u32(isrzero)), vget_high_u8(vreinterpretq_u8_u32(isrzero))); + uint16x4x2_t vTemp2 = vzip_u16(vreinterpret_u16_u8(vTemp.val[0]), vreinterpret_u16_u8(vTemp.val[1])); + + float32x4_t vResult; + if (vget_lane_u32(vreinterpret_u32_u16(vTemp2.val[1]), 1) == 0xFFFFFFFFU) + { + // Total internal reflection + vResult = g_XMZero; + } + else + { + // Sqrt(R) + float32x4_t S0 = vrsqrteq_f32(R); + float32x4_t P0 = vmulq_f32(R, S0); + float32x4_t R0 = vrsqrtsq_f32(P0, S0); + float32x4_t S1 = vmulq_f32(S0, R0); + float32x4_t P1 = vmulq_f32(R, S1); + float32x4_t R1 = vrsqrtsq_f32(P1, S1); + float32x4_t S2 = vmulq_f32(S1, R1); + R = vmulq_f32(R, S2); + // R = RefractionIndex * IDotN + sqrt(R) + R = vmlaq_f32(R, RefractionIndex, IDotN); + // Result = RefractionIndex * Incident - Normal * R + vResult = vmulq_f32(RefractionIndex, Incident); + vResult = vmlsq_f32(vResult, R, Normal); + } + return vResult; +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR IDotN = XMVector4Dot(Incident, Normal); + + // R = 1.0f - RefractionIndex * RefractionIndex * (1.0f - IDotN * IDotN) + XMVECTOR R = XM_FNMADD_PS(IDotN, IDotN, g_XMOne); + XMVECTOR R2 = _mm_mul_ps(RefractionIndex, RefractionIndex); + R = XM_FNMADD_PS(R, R2, g_XMOne); + + XMVECTOR vResult = _mm_cmple_ps(R, g_XMZero); + if (_mm_movemask_ps(vResult) == 0x0f) + { + // Total internal reflection + vResult = g_XMZero; + } + else + { + // R = RefractionIndex * IDotN + sqrt(R) + R = _mm_sqrt_ps(R); + R = XM_FMADD_PS(RefractionIndex, IDotN, R); + // Result = RefractionIndex * Incident - Normal * R + vResult = _mm_mul_ps(RefractionIndex, Incident); + vResult = XM_FNMADD_PS(R, Normal, vResult); + } + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4Orthogonal(FXMVECTOR V) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + XMVECTORF32 Result = { { { + V.vector4_f32[2], + V.vector4_f32[3], + -V.vector4_f32[0], + -V.vector4_f32[1] + } } }; + return Result.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Negate = { { { 1.f, 1.f, -1.f, -1.f } } }; + + float32x4_t Result = vcombine_f32(vget_high_f32(V), vget_low_f32(V)); + return vmulq_f32(Result, Negate); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 FlipZW = { { { 1.0f, 1.0f, -1.0f, -1.0f } } }; + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 0, 3, 2)); + vResult = _mm_mul_ps(vResult, FlipZW); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4AngleBetweenNormalsEst +( + FXMVECTOR N1, + FXMVECTOR N2 +) noexcept +{ + XMVECTOR Result = XMVector4Dot(N1, N2); + Result = XMVectorClamp(Result, g_XMNegativeOne.v, g_XMOne.v); + Result = XMVectorACosEst(Result); + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4AngleBetweenNormals +( + FXMVECTOR N1, + FXMVECTOR N2 +) noexcept +{ + XMVECTOR Result = XMVector4Dot(N1, N2); + Result = XMVectorClamp(Result, g_XMNegativeOne.v, g_XMOne.v); + Result = XMVectorACos(Result); + return Result; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4AngleBetweenVectors +( + FXMVECTOR V1, + FXMVECTOR V2 +) noexcept +{ + XMVECTOR L1 = XMVector4ReciprocalLength(V1); + XMVECTOR L2 = XMVector4ReciprocalLength(V2); + + XMVECTOR Dot = XMVector4Dot(V1, V2); + + L1 = XMVectorMultiply(L1, L2); + + XMVECTOR CosAngle = XMVectorMultiply(Dot, L1); + CosAngle = XMVectorClamp(CosAngle, g_XMNegativeOne.v, g_XMOne.v); + + return XMVectorACos(CosAngle); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV XMVector4Transform +( + FXMVECTOR V, + FXMMATRIX M +) noexcept +{ +#if defined(_XM_NO_INTRINSICS_) + + float fX = (M.m[0][0] * V.vector4_f32[0]) + (M.m[1][0] * V.vector4_f32[1]) + (M.m[2][0] * V.vector4_f32[2]) + (M.m[3][0] * V.vector4_f32[3]); + float fY = (M.m[0][1] * V.vector4_f32[0]) + (M.m[1][1] * V.vector4_f32[1]) + (M.m[2][1] * V.vector4_f32[2]) + (M.m[3][1] * V.vector4_f32[3]); + float fZ = (M.m[0][2] * V.vector4_f32[0]) + (M.m[1][2] * V.vector4_f32[1]) + (M.m[2][2] * V.vector4_f32[2]) + (M.m[3][2] * V.vector4_f32[3]); + float fW = (M.m[0][3] * V.vector4_f32[0]) + (M.m[1][3] * V.vector4_f32[1]) + (M.m[2][3] * V.vector4_f32[2]) + (M.m[3][3] * V.vector4_f32[3]); + XMVECTORF32 vResult = { { { fX, fY, fZ, fW } } }; + return vResult.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x2_t VL = vget_low_f32(V); + XMVECTOR vResult = vmulq_lane_f32(M.r[0], VL, 0); // X + vResult = vmlaq_lane_f32(vResult, M.r[1], VL, 1); // Y + float32x2_t VH = vget_high_f32(V); + vResult = vmlaq_lane_f32(vResult, M.r[2], VH, 0); // Z + return vmlaq_lane_f32(vResult, M.r[3], VH, 1); // W +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); // W + vResult = _mm_mul_ps(vResult, M.r[3]); + XMVECTOR vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); // Z + vResult = XM_FMADD_PS(vTemp, M.r[2], vResult); + vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); // Y + vResult = XM_FMADD_PS(vTemp, M.r[1], vResult); + vTemp = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); // X + vResult = XM_FMADD_PS(vTemp, M.r[0], vResult); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMFLOAT4* XM_CALLCONV XMVector4TransformStream +( + XMFLOAT4* pOutputStream, + size_t OutputStride, + const XMFLOAT4* pInputStream, + size_t InputStride, + size_t VectorCount, + FXMMATRIX M +) noexcept +{ + assert(pOutputStream != nullptr); + assert(pInputStream != nullptr); + + assert(InputStride >= sizeof(XMFLOAT4)); + _Analysis_assume_(InputStride >= sizeof(XMFLOAT4)); + + assert(OutputStride >= sizeof(XMFLOAT4)); + _Analysis_assume_(OutputStride >= sizeof(XMFLOAT4)); + +#if defined(_XM_NO_INTRINSICS_) + + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + for (size_t i = 0; i < VectorCount; i++) + { + XMVECTOR V = XMLoadFloat4(reinterpret_cast(pInputVector)); + XMVECTOR W = XMVectorSplatW(V); + XMVECTOR Z = XMVectorSplatZ(V); + XMVECTOR Y = XMVectorSplatY(V); + XMVECTOR X = XMVectorSplatX(V); + + XMVECTOR Result = XMVectorMultiply(W, row3); + Result = XMVectorMultiplyAdd(Z, row2, Result); + Result = XMVectorMultiplyAdd(Y, row1, Result); + Result = XMVectorMultiplyAdd(X, row0, Result); + + #ifdef _PREFAST_ + #pragma prefast(push) + #pragma prefast(disable : 26015, "PREfast noise: Esp:1307" ) + #endif + + XMStoreFloat4(reinterpret_cast(pOutputVector), Result); + + #ifdef _PREFAST_ + #pragma prefast(pop) + #endif + + pInputVector += InputStride; + pOutputVector += OutputStride; + } + + return pOutputStream; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + size_t i = 0; + size_t four = VectorCount >> 2; + if (four > 0) + { + if ((InputStride == sizeof(XMFLOAT4)) && (OutputStride == sizeof(XMFLOAT4))) + { + for (size_t j = 0; j < four; ++j) + { + float32x4x4_t V = vld4q_f32(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT4) * 4; + + float32x2_t r = vget_low_f32(row0); + XMVECTOR vResult0 = vmulq_lane_f32(V.val[0], r, 0); // Ax + XMVECTOR vResult1 = vmulq_lane_f32(V.val[0], r, 1); // Bx + + XM_PREFETCH(pInputVector); + + r = vget_high_f32(row0); + XMVECTOR vResult2 = vmulq_lane_f32(V.val[0], r, 0); // Cx + XMVECTOR vResult3 = vmulq_lane_f32(V.val[0], r, 1); // Dx + + XM_PREFETCH(pInputVector + XM_CACHE_LINE_SIZE); + + r = vget_low_f32(row1); + vResult0 = vmlaq_lane_f32(vResult0, V.val[1], r, 0); // Ax+Ey + vResult1 = vmlaq_lane_f32(vResult1, V.val[1], r, 1); // Bx+Fy + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 2)); + + r = vget_high_f32(row1); + vResult2 = vmlaq_lane_f32(vResult2, V.val[1], r, 0); // Cx+Gy + vResult3 = vmlaq_lane_f32(vResult3, V.val[1], r, 1); // Dx+Hy + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 3)); + + r = vget_low_f32(row2); + vResult0 = vmlaq_lane_f32(vResult0, V.val[2], r, 0); // Ax+Ey+Iz + vResult1 = vmlaq_lane_f32(vResult1, V.val[2], r, 1); // Bx+Fy+Jz + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 4)); + + r = vget_high_f32(row2); + vResult2 = vmlaq_lane_f32(vResult2, V.val[2], r, 0); // Cx+Gy+Kz + vResult3 = vmlaq_lane_f32(vResult3, V.val[2], r, 1); // Dx+Hy+Lz + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 5)); + + r = vget_low_f32(row3); + vResult0 = vmlaq_lane_f32(vResult0, V.val[3], r, 0); // Ax+Ey+Iz+Mw + vResult1 = vmlaq_lane_f32(vResult1, V.val[3], r, 1); // Bx+Fy+Jz+Nw + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 6)); + + r = vget_high_f32(row3); + vResult2 = vmlaq_lane_f32(vResult2, V.val[3], r, 0); // Cx+Gy+Kz+Ow + vResult3 = vmlaq_lane_f32(vResult3, V.val[3], r, 1); // Dx+Hy+Lz+Pw + + XM_PREFETCH(pInputVector + (XM_CACHE_LINE_SIZE * 7)); + + V.val[0] = vResult0; + V.val[1] = vResult1; + V.val[2] = vResult2; + V.val[3] = vResult3; + + vst4q_f32(reinterpret_cast(pOutputVector), V); + pOutputVector += sizeof(XMFLOAT4) * 4; + + i += 4; + } + } + } + + for (; i < VectorCount; i++) + { + XMVECTOR V = vld1q_f32(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + float32x2_t VL = vget_low_f32(V); + XMVECTOR vResult = vmulq_lane_f32(row0, VL, 0); // X + vResult = vmlaq_lane_f32(vResult, row1, VL, 1); // Y + float32x2_t VH = vget_high_f32(V); + vResult = vmlaq_lane_f32(vResult, row2, VH, 0); // Z + vResult = vmlaq_lane_f32(vResult, row3, VH, 1); // W + + vst1q_f32(reinterpret_cast(pOutputVector), vResult); + pOutputVector += OutputStride; + } + + return pOutputStream; +#elif defined(_XM_AVX2_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t two = VectorCount >> 1; + if (two > 0) + { + __m256 row0 = _mm256_broadcast_ps(&M.r[0]); + __m256 row1 = _mm256_broadcast_ps(&M.r[1]); + __m256 row2 = _mm256_broadcast_ps(&M.r[2]); + __m256 row3 = _mm256_broadcast_ps(&M.r[3]); + + if (InputStride == sizeof(XMFLOAT4)) + { + if (OutputStride == sizeof(XMFLOAT4)) + { + if (!(reinterpret_cast(pOutputStream) & 0x1F)) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < two; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT4) * 2; + + __m256 vTempX = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + __m256 vTempY = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 vTempZ = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 vTempW = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + + vTempX = _mm256_mul_ps(vTempX, row0); + vTempY = _mm256_mul_ps(vTempY, row1); + vTempZ = _mm256_fmadd_ps(vTempZ, row2, vTempX); + vTempW = _mm256_fmadd_ps(vTempW, row3, vTempY); + vTempX = _mm256_add_ps(vTempZ, vTempW); + + XM256_STREAM_PS(reinterpret_cast(pOutputVector), vTempX); + pOutputVector += sizeof(XMFLOAT4) * 2; + + i += 2; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < two; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT4) * 2; + + __m256 vTempX = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + __m256 vTempY = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 vTempZ = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 vTempW = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + + vTempX = _mm256_mul_ps(vTempX, row0); + vTempY = _mm256_mul_ps(vTempY, row1); + vTempZ = _mm256_fmadd_ps(vTempZ, row2, vTempX); + vTempW = _mm256_fmadd_ps(vTempW, row3, vTempY); + vTempX = _mm256_add_ps(vTempZ, vTempW); + + _mm256_storeu_ps(reinterpret_cast(pOutputVector), vTempX); + pOutputVector += sizeof(XMFLOAT4) * 2; + + i += 2; + } + } + } + else + { + // Packed input, unpacked output + for (size_t j = 0; j < two; ++j) + { + __m256 VV = _mm256_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += sizeof(XMFLOAT4) * 2; + + __m256 vTempX = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(0, 0, 0, 0)); + __m256 vTempY = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(1, 1, 1, 1)); + __m256 vTempZ = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(2, 2, 2, 2)); + __m256 vTempW = _mm256_shuffle_ps(VV, VV, _MM_SHUFFLE(3, 3, 3, 3)); + + vTempX = _mm256_mul_ps(vTempX, row0); + vTempY = _mm256_mul_ps(vTempY, row1); + vTempZ = _mm256_fmadd_ps(vTempZ, row2, vTempX); + vTempW = _mm256_fmadd_ps(vTempW, row3, vTempY); + vTempX = _mm256_add_ps(vTempZ, vTempW); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), _mm256_castps256_ps128(vTempX)); + pOutputVector += OutputStride; + + _mm_storeu_ps(reinterpret_cast(pOutputVector), _mm256_extractf128_ps(vTempX, 1)); + pOutputVector += OutputStride; + i += 2; + } + } + } + } + + if (i < VectorCount) + { + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + for (; i < VectorCount; i++) + { + __m128 V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR vTempX = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vTempY = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vTempZ = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR vTempW = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + + vTempX = _mm_mul_ps(vTempX, row0); + vTempY = _mm_mul_ps(vTempY, row1); + vTempZ = XM_FMADD_PS(vTempZ, row2, vTempX); + vTempW = XM_FMADD_PS(vTempW, row3, vTempY); + vTempX = _mm_add_ps(vTempZ, vTempW); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTempX); + pOutputVector += OutputStride; + } + } + + XM_SFENCE(); + + return pOutputStream; +#elif defined(_XM_SSE_INTRINSICS_) + auto pInputVector = reinterpret_cast(pInputStream); + auto pOutputVector = reinterpret_cast(pOutputStream); + + const XMVECTOR row0 = M.r[0]; + const XMVECTOR row1 = M.r[1]; + const XMVECTOR row2 = M.r[2]; + const XMVECTOR row3 = M.r[3]; + + if (!(reinterpret_cast(pOutputStream) & 0xF) && !(OutputStride & 0xF)) + { + if (!(reinterpret_cast(pInputStream) & 0xF) && !(InputStride & 0xF)) + { + // Aligned input, aligned output + for (size_t i = 0; i < VectorCount; i++) + { + __m128 V = _mm_load_ps(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR vTempX = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vTempY = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vTempZ = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR vTempW = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + + vTempX = _mm_mul_ps(vTempX, row0); + vTempY = _mm_mul_ps(vTempY, row1); + vTempZ = XM_FMADD_PS(vTempZ, row2, vTempX); + vTempW = XM_FMADD_PS(vTempW, row3, vTempY); + vTempX = _mm_add_ps(vTempZ, vTempW); + + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTempX); + pOutputVector += OutputStride; + } + } + else + { + // Unaligned input, aligned output + for (size_t i = 0; i < VectorCount; i++) + { + __m128 V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR vTempX = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vTempY = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vTempZ = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR vTempW = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + + vTempX = _mm_mul_ps(vTempX, row0); + vTempY = _mm_mul_ps(vTempY, row1); + vTempZ = XM_FMADD_PS(vTempZ, row2, vTempX); + vTempW = XM_FMADD_PS(vTempW, row3, vTempY); + vTempX = _mm_add_ps(vTempZ, vTempW); + + XM_STREAM_PS(reinterpret_cast(pOutputVector), vTempX); + pOutputVector += OutputStride; + } + } + } + else + { + if (!(reinterpret_cast(pInputStream) & 0xF) && !(InputStride & 0xF)) + { + // Aligned input, unaligned output + for (size_t i = 0; i < VectorCount; i++) + { + __m128 V = _mm_load_ps(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR vTempX = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vTempY = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vTempZ = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR vTempW = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + + vTempX = _mm_mul_ps(vTempX, row0); + vTempY = _mm_mul_ps(vTempY, row1); + vTempZ = XM_FMADD_PS(vTempZ, row2, vTempX); + vTempW = XM_FMADD_PS(vTempW, row3, vTempY); + vTempX = _mm_add_ps(vTempZ, vTempW); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTempX); + pOutputVector += OutputStride; + } + } + else + { + // Unaligned input, unaligned output + for (size_t i = 0; i < VectorCount; i++) + { + __m128 V = _mm_loadu_ps(reinterpret_cast(pInputVector)); + pInputVector += InputStride; + + XMVECTOR vTempX = XM_PERMUTE_PS(V, _MM_SHUFFLE(0, 0, 0, 0)); + XMVECTOR vTempY = XM_PERMUTE_PS(V, _MM_SHUFFLE(1, 1, 1, 1)); + XMVECTOR vTempZ = XM_PERMUTE_PS(V, _MM_SHUFFLE(2, 2, 2, 2)); + XMVECTOR vTempW = XM_PERMUTE_PS(V, _MM_SHUFFLE(3, 3, 3, 3)); + + vTempX = _mm_mul_ps(vTempX, row0); + vTempY = _mm_mul_ps(vTempY, row1); + vTempZ = XM_FMADD_PS(vTempZ, row2, vTempX); + vTempW = XM_FMADD_PS(vTempW, row3, vTempY); + vTempX = _mm_add_ps(vTempZ, vTempW); + + _mm_storeu_ps(reinterpret_cast(pOutputVector), vTempX); + pOutputVector += OutputStride; + } + } + } + + XM_SFENCE(); + + return pOutputStream; +#endif +} + +/**************************************************************************** + * + * XMVECTOR operators + * + ****************************************************************************/ + +#ifndef _XM_NO_XMVECTOR_OVERLOADS_ + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV operator+ (FXMVECTOR V) noexcept +{ + return V; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV operator- (FXMVECTOR V) noexcept +{ + return XMVectorNegate(V); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR& XM_CALLCONV operator+= +( + XMVECTOR& V1, + FXMVECTOR V2 + ) noexcept +{ + V1 = XMVectorAdd(V1, V2); + return V1; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR& XM_CALLCONV operator-= +( + XMVECTOR& V1, + FXMVECTOR V2 + ) noexcept +{ + V1 = XMVectorSubtract(V1, V2); + return V1; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR& XM_CALLCONV operator*= +( + XMVECTOR& V1, + FXMVECTOR V2 + ) noexcept +{ + V1 = XMVectorMultiply(V1, V2); + return V1; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR& XM_CALLCONV operator/= +( + XMVECTOR& V1, + FXMVECTOR V2 + ) noexcept +{ + V1 = XMVectorDivide(V1, V2); + return V1; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR& operator*= +( + XMVECTOR& V, + const float S + ) noexcept +{ + V = XMVectorScale(V, S); + return V; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR& operator/= +( + XMVECTOR& V, + const float S + ) noexcept +{ + XMVECTOR vS = XMVectorReplicate(S); + V = XMVectorDivide(V, vS); + return V; +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV operator+ +( + FXMVECTOR V1, + FXMVECTOR V2 + ) noexcept +{ + return XMVectorAdd(V1, V2); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV operator- +( + FXMVECTOR V1, + FXMVECTOR V2 + ) noexcept +{ + return XMVectorSubtract(V1, V2); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV operator* +( + FXMVECTOR V1, + FXMVECTOR V2 + ) noexcept +{ + return XMVectorMultiply(V1, V2); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV operator/ +( + FXMVECTOR V1, + FXMVECTOR V2 + ) noexcept +{ + return XMVectorDivide(V1, V2); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV operator* +( + FXMVECTOR V, + const float S + ) noexcept +{ + return XMVectorScale(V, S); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV operator/ +( + FXMVECTOR V, + const float S + ) noexcept +{ + XMVECTOR vS = XMVectorReplicate(S); + return XMVectorDivide(V, vS); +} + +//------------------------------------------------------------------------------ + +inline XMVECTOR XM_CALLCONV operator* +( + float S, + FXMVECTOR V + ) noexcept +{ + return XMVectorScale(V, S); +} + +#endif /* !_XM_NO_XMVECTOR_OVERLOADS_ */ + +#if defined(_XM_NO_INTRINSICS_) +#undef XMISNAN +#undef XMISINF +#endif + +#if defined(_XM_SSE_INTRINSICS_) +#undef XM3UNPACK3INTO4 +#undef XM3PACK4INTO3 +#endif + diff --git a/Extern/dxmath/Inc/DirectXPackedVector.h b/Extern/dxmath/Inc/DirectXPackedVector.h new file mode 100644 index 000000000..92f7d7098 --- /dev/null +++ b/Extern/dxmath/Inc/DirectXPackedVector.h @@ -0,0 +1,1259 @@ +//------------------------------------------------------------------------------------- +// DirectXPackedVector.h -- SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +#include "DirectXMath.h" + +namespace DirectX +{ + + namespace PackedVector + { + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4201 4365 4324 4996) + // C4201: nonstandard extension used + // C4365: Off by default noise + // C4324: alignment padding warnings + // C4996: deprecation warnings + #endif + + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wgnu-anonymous-struct" + #pragma clang diagnostic ignored "-Wnested-anon-types" + #endif + + //------------------------------------------------------------------------------ + // ARGB Color; 8-8-8-8 bit unsigned normalized integer components packed into + // a 32 bit integer. The normalized color is packed into 32 bits using 8 bit + // unsigned, normalized integers for the alpha, red, green, and blue components. + // The alpha component is stored in the most significant bits and the blue + // component in the least significant bits (A8R8G8B8): + // [32] aaaaaaaa rrrrrrrr gggggggg bbbbbbbb [0] + struct XMCOLOR + { + union + { + struct + { + uint8_t b; // Blue: 0/255 to 255/255 + uint8_t g; // Green: 0/255 to 255/255 + uint8_t r; // Red: 0/255 to 255/255 + uint8_t a; // Alpha: 0/255 to 255/255 + }; + uint32_t c; + }; + + XMCOLOR() = default; + + XMCOLOR(const XMCOLOR&) = default; + XMCOLOR& operator=(const XMCOLOR&) = default; + + XMCOLOR(XMCOLOR&&) = default; + XMCOLOR& operator=(XMCOLOR&&) = default; + + constexpr XMCOLOR(uint32_t Color) noexcept : c(Color) {} + XMCOLOR(float _r, float _g, float _b, float _a) noexcept; + explicit XMCOLOR(_In_reads_(4) const float* pArray) noexcept; + + operator uint32_t () const noexcept { return c; } + + XMCOLOR& operator= (const uint32_t Color) noexcept { c = Color; return *this; } + }; + + //------------------------------------------------------------------------------ + // 16 bit floating point number consisting of a sign bit, a 5 bit biased + // exponent, and a 10 bit mantissa + using HALF = uint16_t; + + //------------------------------------------------------------------------------ + // 2D Vector; 16 bit floating point components + struct XMHALF2 + { + union + { + struct + { + HALF x; + HALF y; + }; + uint32_t v; + }; + + XMHALF2() = default; + + XMHALF2(const XMHALF2&) = default; + XMHALF2& operator=(const XMHALF2&) = default; + + XMHALF2(XMHALF2&&) = default; + XMHALF2& operator=(XMHALF2&&) = default; + + explicit constexpr XMHALF2(uint32_t Packed) noexcept : v(Packed) {} + constexpr XMHALF2(HALF _x, HALF _y) noexcept : x(_x), y(_y) {} + explicit XMHALF2(_In_reads_(2) const HALF* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + XMHALF2(float _x, float _y) noexcept; + explicit XMHALF2(_In_reads_(2) const float* pArray) noexcept; + + XMHALF2& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 2D Vector; 16 bit signed normalized integer components + struct XMSHORTN2 + { + union + { + struct + { + int16_t x; + int16_t y; + }; + uint32_t v; + }; + + XMSHORTN2() = default; + + XMSHORTN2(const XMSHORTN2&) = default; + XMSHORTN2& operator=(const XMSHORTN2&) = default; + + XMSHORTN2(XMSHORTN2&&) = default; + XMSHORTN2& operator=(XMSHORTN2&&) = default; + + explicit constexpr XMSHORTN2(uint32_t Packed) noexcept : v(Packed) {} + constexpr XMSHORTN2(int16_t _x, int16_t _y) noexcept : x(_x), y(_y) {} + explicit XMSHORTN2(_In_reads_(2) const int16_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + XMSHORTN2(float _x, float _y) noexcept; + explicit XMSHORTN2(_In_reads_(2) const float* pArray) noexcept; + + XMSHORTN2& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 2D Vector; 16 bit signed integer components + struct XMSHORT2 + { + union + { + struct + { + int16_t x; + int16_t y; + }; + uint32_t v; + }; + + XMSHORT2() = default; + + XMSHORT2(const XMSHORT2&) = default; + XMSHORT2& operator=(const XMSHORT2&) = default; + + XMSHORT2(XMSHORT2&&) = default; + XMSHORT2& operator=(XMSHORT2&&) = default; + + explicit constexpr XMSHORT2(uint32_t Packed) noexcept : v(Packed) {} + constexpr XMSHORT2(int16_t _x, int16_t _y) noexcept : x(_x), y(_y) {} + explicit XMSHORT2(_In_reads_(2) const int16_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + XMSHORT2(float _x, float _y) noexcept; + explicit XMSHORT2(_In_reads_(2) const float* pArray) noexcept; + + XMSHORT2& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 2D Vector; 16 bit unsigned normalized integer components + struct XMUSHORTN2 + { + union + { + struct + { + uint16_t x; + uint16_t y; + }; + uint32_t v; + }; + + XMUSHORTN2() = default; + + XMUSHORTN2(const XMUSHORTN2&) = default; + XMUSHORTN2& operator=(const XMUSHORTN2&) = default; + + XMUSHORTN2(XMUSHORTN2&&) = default; + XMUSHORTN2& operator=(XMUSHORTN2&&) = default; + + explicit constexpr XMUSHORTN2(uint32_t Packed) noexcept : v(Packed) {} + constexpr XMUSHORTN2(uint16_t _x, uint16_t _y) noexcept : x(_x), y(_y) {} + explicit XMUSHORTN2(_In_reads_(2) const uint16_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + XMUSHORTN2(float _x, float _y) noexcept; + explicit XMUSHORTN2(_In_reads_(2) const float* pArray) noexcept; + + XMUSHORTN2& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 2D Vector; 16 bit unsigned integer components + struct XMUSHORT2 + { + union + { + struct + { + uint16_t x; + uint16_t y; + }; + uint32_t v; + }; + + XMUSHORT2() = default; + + XMUSHORT2(const XMUSHORT2&) = default; + XMUSHORT2& operator=(const XMUSHORT2&) = default; + + XMUSHORT2(XMUSHORT2&&) = default; + XMUSHORT2& operator=(XMUSHORT2&&) = default; + + explicit constexpr XMUSHORT2(uint32_t Packed) noexcept : v(Packed) {} + constexpr XMUSHORT2(uint16_t _x, uint16_t _y) noexcept : x(_x), y(_y) {} + explicit XMUSHORT2(_In_reads_(2) const uint16_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + XMUSHORT2(float _x, float _y) noexcept; + explicit XMUSHORT2(_In_reads_(2) const float* pArray) noexcept; + + XMUSHORT2& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 2D Vector; 8 bit signed normalized integer components + struct XMBYTEN2 + { + union + { + struct + { + int8_t x; + int8_t y; + }; + uint16_t v; + }; + + XMBYTEN2() = default; + + XMBYTEN2(const XMBYTEN2&) = default; + XMBYTEN2& operator=(const XMBYTEN2&) = default; + + XMBYTEN2(XMBYTEN2&&) = default; + XMBYTEN2& operator=(XMBYTEN2&&) = default; + + explicit constexpr XMBYTEN2(uint16_t Packed) noexcept : v(Packed) {} + constexpr XMBYTEN2(int8_t _x, int8_t _y) noexcept : x(_x), y(_y) {} + explicit XMBYTEN2(_In_reads_(2) const int8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + XMBYTEN2(float _x, float _y) noexcept; + explicit XMBYTEN2(_In_reads_(2) const float* pArray) noexcept; + + XMBYTEN2& operator= (uint16_t Packed) noexcept { v = Packed; return *this; } + }; + + // 2D Vector; 8 bit signed integer components + struct XMBYTE2 + { + union + { + struct + { + int8_t x; + int8_t y; + }; + uint16_t v; + }; + + XMBYTE2() = default; + + XMBYTE2(const XMBYTE2&) = default; + XMBYTE2& operator=(const XMBYTE2&) = default; + + XMBYTE2(XMBYTE2&&) = default; + XMBYTE2& operator=(XMBYTE2&&) = default; + + explicit constexpr XMBYTE2(uint16_t Packed) noexcept : v(Packed) {} + constexpr XMBYTE2(int8_t _x, int8_t _y) noexcept : x(_x), y(_y) {} + explicit XMBYTE2(_In_reads_(2) const int8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + XMBYTE2(float _x, float _y) noexcept; + explicit XMBYTE2(_In_reads_(2) const float* pArray) noexcept; + + XMBYTE2& operator= (uint16_t Packed) noexcept { v = Packed; return *this; } + }; + + // 2D Vector; 8 bit unsigned normalized integer components + struct XMUBYTEN2 + { + union + { + struct + { + uint8_t x; + uint8_t y; + }; + uint16_t v; + }; + + XMUBYTEN2() = default; + + XMUBYTEN2(const XMUBYTEN2&) = default; + XMUBYTEN2& operator=(const XMUBYTEN2&) = default; + + XMUBYTEN2(XMUBYTEN2&&) = default; + XMUBYTEN2& operator=(XMUBYTEN2&&) = default; + + explicit constexpr XMUBYTEN2(uint16_t Packed) noexcept : v(Packed) {} + constexpr XMUBYTEN2(uint8_t _x, uint8_t _y) noexcept : x(_x), y(_y) {} + explicit XMUBYTEN2(_In_reads_(2) const uint8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + XMUBYTEN2(float _x, float _y) noexcept; + explicit XMUBYTEN2(_In_reads_(2) const float* pArray) noexcept; + + XMUBYTEN2& operator= (uint16_t Packed) noexcept { v = Packed; return *this; } + }; + + // 2D Vector; 8 bit unsigned integer components + struct XMUBYTE2 + { + union + { + struct + { + uint8_t x; + uint8_t y; + }; + uint16_t v; + }; + + XMUBYTE2() = default; + + XMUBYTE2(const XMUBYTE2&) = default; + XMUBYTE2& operator=(const XMUBYTE2&) = default; + + XMUBYTE2(XMUBYTE2&&) = default; + XMUBYTE2& operator=(XMUBYTE2&&) = default; + + explicit constexpr XMUBYTE2(uint16_t Packed) noexcept : v(Packed) {} + constexpr XMUBYTE2(uint8_t _x, uint8_t _y) noexcept : x(_x), y(_y) {} + explicit XMUBYTE2(_In_reads_(2) const uint8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]) {} + XMUBYTE2(float _x, float _y) noexcept; + explicit XMUBYTE2(_In_reads_(2) const float* pArray) noexcept; + + XMUBYTE2& operator= (uint16_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 3D vector: 5/6/5 unsigned integer components + struct XMU565 + { + union + { + struct + { + uint16_t x : 5; // 0 to 31 + uint16_t y : 6; // 0 to 63 + uint16_t z : 5; // 0 to 31 + }; + uint16_t v; + }; + + XMU565() = default; + + XMU565(const XMU565&) = default; + XMU565& operator=(const XMU565&) = default; + + XMU565(XMU565&&) = default; + XMU565& operator=(XMU565&&) = default; + + explicit constexpr XMU565(uint16_t Packed) noexcept : v(Packed) {} + constexpr XMU565(uint8_t _x, uint8_t _y, uint8_t _z) noexcept : x(_x), y(_y), z(_z) {} + explicit XMU565(_In_reads_(3) const uint8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]) {} + XMU565(float _x, float _y, float _z) noexcept; + explicit XMU565(_In_reads_(3) const float* pArray) noexcept; + + operator uint16_t () const noexcept { return v; } + + XMU565& operator= (uint16_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 3D vector: 11/11/10 floating-point components + // The 3D vector is packed into 32 bits as follows: a 5-bit biased exponent + // and 6-bit mantissa for x component, a 5-bit biased exponent and + // 6-bit mantissa for y component, a 5-bit biased exponent and a 5-bit + // mantissa for z. The z component is stored in the most significant bits + // and the x component in the least significant bits. No sign bits so + // all partial-precision numbers are positive. + // (Z10Y11X11): [32] ZZZZZzzz zzzYYYYY yyyyyyXX XXXxxxxx [0] + struct XMFLOAT3PK + { + union + { + struct + { + uint32_t xm : 6; // x-mantissa + uint32_t xe : 5; // x-exponent + uint32_t ym : 6; // y-mantissa + uint32_t ye : 5; // y-exponent + uint32_t zm : 5; // z-mantissa + uint32_t ze : 5; // z-exponent + }; + uint32_t v; + }; + + XMFLOAT3PK() = default; + + XMFLOAT3PK(const XMFLOAT3PK&) = default; + XMFLOAT3PK& operator=(const XMFLOAT3PK&) = default; + + XMFLOAT3PK(XMFLOAT3PK&&) = default; + XMFLOAT3PK& operator=(XMFLOAT3PK&&) = default; + + explicit constexpr XMFLOAT3PK(uint32_t Packed) noexcept : v(Packed) {} + XMFLOAT3PK(float _x, float _y, float _z) noexcept; + explicit XMFLOAT3PK(_In_reads_(3) const float* pArray) noexcept; + + operator uint32_t () const noexcept { return v; } + + XMFLOAT3PK& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 3D vector: 9/9/9 floating-point components with shared 5-bit exponent + // The 3D vector is packed into 32 bits as follows: a 5-bit biased exponent + // with 9-bit mantissa for the x, y, and z component. The shared exponent + // is stored in the most significant bits and the x component mantissa is in + // the least significant bits. No sign bits so all partial-precision numbers + // are positive. + // (E5Z9Y9X9): [32] EEEEEzzz zzzzzzyy yyyyyyyx xxxxxxxx [0] + struct XMFLOAT3SE + { + union + { + struct + { + uint32_t xm : 9; // x-mantissa + uint32_t ym : 9; // y-mantissa + uint32_t zm : 9; // z-mantissa + uint32_t e : 5; // shared exponent + }; + uint32_t v; + }; + + XMFLOAT3SE() = default; + + XMFLOAT3SE(const XMFLOAT3SE&) = default; + XMFLOAT3SE& operator=(const XMFLOAT3SE&) = default; + + XMFLOAT3SE(XMFLOAT3SE&&) = default; + XMFLOAT3SE& operator=(XMFLOAT3SE&&) = default; + + explicit constexpr XMFLOAT3SE(uint32_t Packed) noexcept : v(Packed) {} + XMFLOAT3SE(float _x, float _y, float _z) noexcept; + explicit XMFLOAT3SE(_In_reads_(3) const float* pArray) noexcept; + + operator uint32_t () const noexcept { return v; } + + XMFLOAT3SE& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 4D Vector; 16 bit floating point components + struct XMHALF4 + { + union + { + struct + { + HALF x; + HALF y; + HALF z; + HALF w; + }; + uint64_t v; + }; + + XMHALF4() = default; + + XMHALF4(const XMHALF4&) = default; + XMHALF4& operator=(const XMHALF4&) = default; + + XMHALF4(XMHALF4&&) = default; + XMHALF4& operator=(XMHALF4&&) = default; + + explicit constexpr XMHALF4(uint64_t Packed) noexcept : v(Packed) {} + constexpr XMHALF4(HALF _x, HALF _y, HALF _z, HALF _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit XMHALF4(_In_reads_(4) const HALF* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMHALF4(float _x, float _y, float _z, float _w) noexcept; + explicit XMHALF4(_In_reads_(4) const float* pArray) noexcept; + + XMHALF4& operator= (uint64_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 4D Vector; 16 bit signed normalized integer components + struct XMSHORTN4 + { + union + { + struct + { + int16_t x; + int16_t y; + int16_t z; + int16_t w; + }; + uint64_t v; + }; + + XMSHORTN4() = default; + + XMSHORTN4(const XMSHORTN4&) = default; + XMSHORTN4& operator=(const XMSHORTN4&) = default; + + XMSHORTN4(XMSHORTN4&&) = default; + XMSHORTN4& operator=(XMSHORTN4&&) = default; + + explicit constexpr XMSHORTN4(uint64_t Packed) noexcept : v(Packed) {} + constexpr XMSHORTN4(int16_t _x, int16_t _y, int16_t _z, int16_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit XMSHORTN4(_In_reads_(4) const int16_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMSHORTN4(float _x, float _y, float _z, float _w) noexcept; + explicit XMSHORTN4(_In_reads_(4) const float* pArray) noexcept; + + XMSHORTN4& operator= (uint64_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 16 bit signed integer components + struct XMSHORT4 + { + union + { + struct + { + int16_t x; + int16_t y; + int16_t z; + int16_t w; + }; + uint64_t v; + }; + + XMSHORT4() = default; + + XMSHORT4(const XMSHORT4&) = default; + XMSHORT4& operator=(const XMSHORT4&) = default; + + XMSHORT4(XMSHORT4&&) = default; + XMSHORT4& operator=(XMSHORT4&&) = default; + + explicit constexpr XMSHORT4(uint64_t Packed) noexcept : v(Packed) {} + constexpr XMSHORT4(int16_t _x, int16_t _y, int16_t _z, int16_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit XMSHORT4(_In_reads_(4) const int16_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMSHORT4(float _x, float _y, float _z, float _w) noexcept; + explicit XMSHORT4(_In_reads_(4) const float* pArray) noexcept; + + XMSHORT4& operator= (uint64_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 16 bit unsigned normalized integer components + struct XMUSHORTN4 + { + union + { + struct + { + uint16_t x; + uint16_t y; + uint16_t z; + uint16_t w; + }; + uint64_t v; + }; + + XMUSHORTN4() = default; + + XMUSHORTN4(const XMUSHORTN4&) = default; + XMUSHORTN4& operator=(const XMUSHORTN4&) = default; + + XMUSHORTN4(XMUSHORTN4&&) = default; + XMUSHORTN4& operator=(XMUSHORTN4&&) = default; + + explicit constexpr XMUSHORTN4(uint64_t Packed) noexcept : v(Packed) {} + constexpr XMUSHORTN4(uint16_t _x, uint16_t _y, uint16_t _z, uint16_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit XMUSHORTN4(_In_reads_(4) const uint16_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMUSHORTN4(float _x, float _y, float _z, float _w) noexcept; + explicit XMUSHORTN4(_In_reads_(4) const float* pArray) noexcept; + + XMUSHORTN4& operator= (uint64_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 16 bit unsigned integer components + struct XMUSHORT4 + { + union + { + struct + { + uint16_t x; + uint16_t y; + uint16_t z; + uint16_t w; + }; + uint64_t v; + }; + + XMUSHORT4() = default; + + XMUSHORT4(const XMUSHORT4&) = default; + XMUSHORT4& operator=(const XMUSHORT4&) = default; + + XMUSHORT4(XMUSHORT4&&) = default; + XMUSHORT4& operator=(XMUSHORT4&&) = default; + + explicit constexpr XMUSHORT4(uint64_t Packed) noexcept : v(Packed) {} + constexpr XMUSHORT4(uint16_t _x, uint16_t _y, uint16_t _z, uint16_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit XMUSHORT4(_In_reads_(4) const uint16_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMUSHORT4(float _x, float _y, float _z, float _w) noexcept; + explicit XMUSHORT4(_In_reads_(4) const float* pArray) noexcept; + + XMUSHORT4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 4D Vector; 10-10-10-2 bit normalized components packed into a 32 bit integer + // The normalized 4D Vector is packed into 32 bits as follows: a 2 bit unsigned, + // normalized integer for the w component and 10 bit signed, normalized + // integers for the z, y, and x components. The w component is stored in the + // most significant bits and the x component in the least significant bits + // (W2Z10Y10X10): [32] wwzzzzzz zzzzyyyy yyyyyyxx xxxxxxxx [0] + struct XMXDECN4 + { + union + { + struct + { + int32_t x : 10; // -511/511 to 511/511 + int32_t y : 10; // -511/511 to 511/511 + int32_t z : 10; // -511/511 to 511/511 + uint32_t w : 2; // 0/3 to 3/3 + }; + uint32_t v; + }; + + XMXDECN4() = default; + + XMXDECN4(const XMXDECN4&) = default; + XMXDECN4& operator=(const XMXDECN4&) = default; + + XMXDECN4(XMXDECN4&&) = default; + XMXDECN4& operator=(XMXDECN4&&) = default; + + explicit constexpr XMXDECN4(uint32_t Packed) : v(Packed) {} + XMXDECN4(float _x, float _y, float _z, float _w) noexcept; + explicit XMXDECN4(_In_reads_(4) const float* pArray) noexcept; + + operator uint32_t () const noexcept { return v; } + + XMXDECN4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 10-10-10-2 bit components packed into a 32 bit integer + // The normalized 4D Vector is packed into 32 bits as follows: a 2 bit unsigned + // integer for the w component and 10 bit signed integers for the + // z, y, and x components. The w component is stored in the + // most significant bits and the x component in the least significant bits + // (W2Z10Y10X10): [32] wwzzzzzz zzzzyyyy yyyyyyxx xxxxxxxx [0] + struct XM_DEPRECATED XMXDEC4 + { + union + { + struct + { + int32_t x : 10; // -511 to 511 + int32_t y : 10; // -511 to 511 + int32_t z : 10; // -511 to 511 + uint32_t w : 2; // 0 to 3 + }; + uint32_t v; + }; + + XMXDEC4() = default; + + XMXDEC4(const XMXDEC4&) = default; + XMXDEC4& operator=(const XMXDEC4&) = default; + + XMXDEC4(XMXDEC4&&) = default; + XMXDEC4& operator=(XMXDEC4&&) = default; + + explicit constexpr XMXDEC4(uint32_t Packed) noexcept : v(Packed) {} + XMXDEC4(float _x, float _y, float _z, float _w) noexcept; + explicit XMXDEC4(_In_reads_(4) const float* pArray) noexcept; + + operator uint32_t () const noexcept { return v; } + + XMXDEC4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 10-10-10-2 bit normalized components packed into a 32 bit integer + // The normalized 4D Vector is packed into 32 bits as follows: a 2 bit signed, + // normalized integer for the w component and 10 bit signed, normalized + // integers for the z, y, and x components. The w component is stored in the + // most significant bits and the x component in the least significant bits + // (W2Z10Y10X10): [32] wwzzzzzz zzzzyyyy yyyyyyxx xxxxxxxx [0] + struct XM_DEPRECATED XMDECN4 + { + union + { + struct + { + int32_t x : 10; // -511/511 to 511/511 + int32_t y : 10; // -511/511 to 511/511 + int32_t z : 10; // -511/511 to 511/511 + int32_t w : 2; // -1/1 to 1/1 + }; + uint32_t v; + }; + + XMDECN4() = default; + + XMDECN4(const XMDECN4&) = default; + XMDECN4& operator=(const XMDECN4&) = default; + + XMDECN4(XMDECN4&&) = default; + XMDECN4& operator=(XMDECN4&&) = default; + + explicit constexpr XMDECN4(uint32_t Packed) noexcept : v(Packed) {} + XMDECN4(float _x, float _y, float _z, float _w) noexcept; + explicit XMDECN4(_In_reads_(4) const float* pArray) noexcept; + + operator uint32_t () const noexcept { return v; } + + XMDECN4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 10-10-10-2 bit components packed into a 32 bit integer + // The 4D Vector is packed into 32 bits as follows: a 2 bit signed, + // integer for the w component and 10 bit signed integers for the + // z, y, and x components. The w component is stored in the + // most significant bits and the x component in the least significant bits + // (W2Z10Y10X10): [32] wwzzzzzz zzzzyyyy yyyyyyxx xxxxxxxx [0] + struct XM_DEPRECATED XMDEC4 + { + union + { + struct + { + int32_t x : 10; // -511 to 511 + int32_t y : 10; // -511 to 511 + int32_t z : 10; // -511 to 511 + int32_t w : 2; // -1 to 1 + }; + uint32_t v; + }; + + XMDEC4() = default; + + XMDEC4(const XMDEC4&) = default; + XMDEC4& operator=(const XMDEC4&) = default; + + XMDEC4(XMDEC4&&) = default; + XMDEC4& operator=(XMDEC4&&) = default; + + explicit constexpr XMDEC4(uint32_t Packed) noexcept : v(Packed) {} + XMDEC4(float _x, float _y, float _z, float _w) noexcept; + explicit XMDEC4(_In_reads_(4) const float* pArray) noexcept; + + operator uint32_t () const noexcept { return v; } + + XMDEC4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 10-10-10-2 bit normalized components packed into a 32 bit integer + // The normalized 4D Vector is packed into 32 bits as follows: a 2 bit unsigned, + // normalized integer for the w component and 10 bit unsigned, normalized + // integers for the z, y, and x components. The w component is stored in the + // most significant bits and the x component in the least significant bits + // (W2Z10Y10X10): [32] wwzzzzzz zzzzyyyy yyyyyyxx xxxxxxxx [0] + struct XMUDECN4 + { + union + { + struct + { + uint32_t x : 10; // 0/1023 to 1023/1023 + uint32_t y : 10; // 0/1023 to 1023/1023 + uint32_t z : 10; // 0/1023 to 1023/1023 + uint32_t w : 2; // 0/3 to 3/3 + }; + uint32_t v; + }; + + XMUDECN4() = default; + + XMUDECN4(const XMUDECN4&) = default; + XMUDECN4& operator=(const XMUDECN4&) = default; + + XMUDECN4(XMUDECN4&&) = default; + XMUDECN4& operator=(XMUDECN4&&) = default; + + explicit constexpr XMUDECN4(uint32_t Packed) noexcept : v(Packed) {} + XMUDECN4(float _x, float _y, float _z, float _w) noexcept; + explicit XMUDECN4(_In_reads_(4) const float* pArray) noexcept; + + operator uint32_t () const noexcept { return v; } + + XMUDECN4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 10-10-10-2 bit components packed into a 32 bit integer + // The 4D Vector is packed into 32 bits as follows: a 2 bit unsigned, + // integer for the w component and 10 bit unsigned integers + // for the z, y, and x components. The w component is stored in the + // most significant bits and the x component in the least significant bits + // (W2Z10Y10X10): [32] wwzzzzzz zzzzyyyy yyyyyyxx xxxxxxxx [0] + struct XMUDEC4 + { + union + { + struct + { + uint32_t x : 10; // 0 to 1023 + uint32_t y : 10; // 0 to 1023 + uint32_t z : 10; // 0 to 1023 + uint32_t w : 2; // 0 to 3 + }; + uint32_t v; + }; + + XMUDEC4() = default; + + XMUDEC4(const XMUDEC4&) = default; + XMUDEC4& operator=(const XMUDEC4&) = default; + + XMUDEC4(XMUDEC4&&) = default; + XMUDEC4& operator=(XMUDEC4&&) = default; + + explicit constexpr XMUDEC4(uint32_t Packed) noexcept : v(Packed) {} + XMUDEC4(float _x, float _y, float _z, float _w) noexcept; + explicit XMUDEC4(_In_reads_(4) const float* pArray) noexcept; + + operator uint32_t () const noexcept { return v; } + + XMUDEC4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 4D Vector; 8 bit signed normalized integer components + struct XMBYTEN4 + { + union + { + struct + { + int8_t x; + int8_t y; + int8_t z; + int8_t w; + }; + uint32_t v; + }; + + XMBYTEN4() = default; + + XMBYTEN4(const XMBYTEN4&) = default; + XMBYTEN4& operator=(const XMBYTEN4&) = default; + + XMBYTEN4(XMBYTEN4&&) = default; + XMBYTEN4& operator=(XMBYTEN4&&) = default; + + constexpr XMBYTEN4(int8_t _x, int8_t _y, int8_t _z, int8_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit constexpr XMBYTEN4(uint32_t Packed) noexcept : v(Packed) {} + explicit XMBYTEN4(_In_reads_(4) const int8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMBYTEN4(float _x, float _y, float _z, float _w) noexcept; + explicit XMBYTEN4(_In_reads_(4) const float* pArray) noexcept; + + XMBYTEN4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 8 bit signed integer components + struct XMBYTE4 + { + union + { + struct + { + int8_t x; + int8_t y; + int8_t z; + int8_t w; + }; + uint32_t v; + }; + + XMBYTE4() = default; + + XMBYTE4(const XMBYTE4&) = default; + XMBYTE4& operator=(const XMBYTE4&) = default; + + XMBYTE4(XMBYTE4&&) = default; + XMBYTE4& operator=(XMBYTE4&&) = default; + + constexpr XMBYTE4(int8_t _x, int8_t _y, int8_t _z, int8_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit constexpr XMBYTE4(uint32_t Packed) noexcept : v(Packed) {} + explicit XMBYTE4(_In_reads_(4) const int8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMBYTE4(float _x, float _y, float _z, float _w) noexcept; + explicit XMBYTE4(_In_reads_(4) const float* pArray) noexcept; + + XMBYTE4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 8 bit unsigned normalized integer components + struct XMUBYTEN4 + { + union + { + struct + { + uint8_t x; + uint8_t y; + uint8_t z; + uint8_t w; + }; + uint32_t v; + }; + + XMUBYTEN4() = default; + + XMUBYTEN4(const XMUBYTEN4&) = default; + XMUBYTEN4& operator=(const XMUBYTEN4&) = default; + + XMUBYTEN4(XMUBYTEN4&&) = default; + XMUBYTEN4& operator=(XMUBYTEN4&&) = default; + + constexpr XMUBYTEN4(uint8_t _x, uint8_t _y, uint8_t _z, uint8_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit constexpr XMUBYTEN4(uint32_t Packed) noexcept : v(Packed) {} + explicit XMUBYTEN4(_In_reads_(4) const uint8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMUBYTEN4(float _x, float _y, float _z, float _w) noexcept; + explicit XMUBYTEN4(_In_reads_(4) const float* pArray) noexcept; + + XMUBYTEN4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + // 4D Vector; 8 bit unsigned integer components + struct XMUBYTE4 + { + union + { + struct + { + uint8_t x; + uint8_t y; + uint8_t z; + uint8_t w; + }; + uint32_t v; + }; + + XMUBYTE4() = default; + + XMUBYTE4(const XMUBYTE4&) = default; + XMUBYTE4& operator=(const XMUBYTE4&) = default; + + XMUBYTE4(XMUBYTE4&&) = default; + XMUBYTE4& operator=(XMUBYTE4&&) = default; + + constexpr XMUBYTE4(uint8_t _x, uint8_t _y, uint8_t _z, uint8_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit constexpr XMUBYTE4(uint32_t Packed) noexcept : v(Packed) {} + explicit XMUBYTE4(_In_reads_(4) const uint8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMUBYTE4(float _x, float _y, float _z, float _w) noexcept; + explicit XMUBYTE4(_In_reads_(4) const float* pArray) noexcept; + + XMUBYTE4& operator= (uint32_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 4D vector; 4 bit unsigned integer components + struct XMUNIBBLE4 + { + union + { + struct + { + uint16_t x : 4; // 0 to 15 + uint16_t y : 4; // 0 to 15 + uint16_t z : 4; // 0 to 15 + uint16_t w : 4; // 0 to 15 + }; + uint16_t v; + }; + + XMUNIBBLE4() = default; + + XMUNIBBLE4(const XMUNIBBLE4&) = default; + XMUNIBBLE4& operator=(const XMUNIBBLE4&) = default; + + XMUNIBBLE4(XMUNIBBLE4&&) = default; + XMUNIBBLE4& operator=(XMUNIBBLE4&&) = default; + + explicit constexpr XMUNIBBLE4(uint16_t Packed) noexcept : v(Packed) {} + constexpr XMUNIBBLE4(uint8_t _x, uint8_t _y, uint8_t _z, uint8_t _w) noexcept : x(_x), y(_y), z(_z), w(_w) {} + explicit XMUNIBBLE4(_In_reads_(4) const uint8_t* pArray) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {} + XMUNIBBLE4(float _x, float _y, float _z, float _w) noexcept; + explicit XMUNIBBLE4(_In_reads_(4) const float* pArray) noexcept; + + operator uint16_t () const noexcept { return v; } + + XMUNIBBLE4& operator= (uint16_t Packed) noexcept { v = Packed; return *this; } + }; + + //------------------------------------------------------------------------------ + // 4D vector: 5/5/5/1 unsigned integer components + struct XMU555 + { + union + { + struct + { + uint16_t x : 5; // 0 to 31 + uint16_t y : 5; // 0 to 31 + uint16_t z : 5; // 0 to 31 + uint16_t w : 1; // 0 or 1 + }; + uint16_t v; + }; + + XMU555() = default; + + XMU555(const XMU555&) = default; + XMU555& operator=(const XMU555&) = default; + + XMU555(XMU555&&) = default; + XMU555& operator=(XMU555&&) = default; + + explicit constexpr XMU555(uint16_t Packed) noexcept : v(Packed) {} + constexpr XMU555(uint8_t _x, uint8_t _y, uint8_t _z, bool _w) noexcept : x(_x), y(_y), z(_z), w(_w ? 0x1 : 0) {} + XMU555(_In_reads_(3) const uint8_t* pArray, _In_ bool _w) noexcept : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(_w ? 0x1 : 0) {} + XMU555(float _x, float _y, float _z, bool _w) noexcept; + XMU555(_In_reads_(3) const float* pArray, _In_ bool _w) noexcept; + + operator uint16_t () const noexcept { return v; } + + XMU555& operator= (uint16_t Packed) noexcept { v = Packed; return *this; } + }; + + #ifdef __clang__ + #pragma clang diagnostic pop + #endif + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + /**************************************************************************** + * + * Data conversion operations + * + ****************************************************************************/ + + float XMConvertHalfToFloat(HALF Value) noexcept; + float* XMConvertHalfToFloatStream(_Out_writes_bytes_(sizeof(float) + OutputStride * (HalfCount - 1)) float* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(HALF) + InputStride * (HalfCount - 1)) const HALF* pInputStream, + _In_ size_t InputStride, _In_ size_t HalfCount) noexcept; + HALF XMConvertFloatToHalf(float Value) noexcept; + HALF* XMConvertFloatToHalfStream(_Out_writes_bytes_(sizeof(HALF) + OutputStride * (FloatCount - 1)) HALF* pOutputStream, + _In_ size_t OutputStride, + _In_reads_bytes_(sizeof(float) + InputStride * (FloatCount - 1)) const float* pInputStream, + _In_ size_t InputStride, _In_ size_t FloatCount) noexcept; + + /**************************************************************************** + * + * Load operations + * + ****************************************************************************/ + + XMVECTOR XM_CALLCONV XMLoadColor(_In_ const XMCOLOR* pSource) noexcept; + + XMVECTOR XM_CALLCONV XMLoadHalf2(_In_ const XMHALF2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadShortN2(_In_ const XMSHORTN2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadShort2(_In_ const XMSHORT2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUShortN2(_In_ const XMUSHORTN2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUShort2(_In_ const XMUSHORT2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadByteN2(_In_ const XMBYTEN2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadByte2(_In_ const XMBYTE2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUByteN2(_In_ const XMUBYTEN2* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUByte2(_In_ const XMUBYTE2* pSource) noexcept; + + XMVECTOR XM_CALLCONV XMLoadU565(_In_ const XMU565* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadFloat3PK(_In_ const XMFLOAT3PK* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadFloat3SE(_In_ const XMFLOAT3SE* pSource) noexcept; + + XMVECTOR XM_CALLCONV XMLoadHalf4(_In_ const XMHALF4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadShortN4(_In_ const XMSHORTN4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadShort4(_In_ const XMSHORT4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUShortN4(_In_ const XMUSHORTN4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUShort4(_In_ const XMUSHORT4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadXDecN4(_In_ const XMXDECN4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUDecN4(_In_ const XMUDECN4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUDecN4_XR(_In_ const XMUDECN4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUDec4(_In_ const XMUDEC4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadByteN4(_In_ const XMBYTEN4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadByte4(_In_ const XMBYTE4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUByteN4(_In_ const XMUBYTEN4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUByte4(_In_ const XMUBYTE4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadUNibble4(_In_ const XMUNIBBLE4* pSource) noexcept; + XMVECTOR XM_CALLCONV XMLoadU555(_In_ const XMU555* pSource) noexcept; + + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable : 4996) + // C4996: ignore deprecation warning + #endif + + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + #endif + + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif + + XM_DEPRECATED + XMVECTOR XM_CALLCONV XMLoadDecN4(_In_ const XMDECN4* pSource) noexcept; + + XM_DEPRECATED + XMVECTOR XM_CALLCONV XMLoadDec4(_In_ const XMDEC4* pSource) noexcept; + + XM_DEPRECATED + XMVECTOR XM_CALLCONV XMLoadXDec4(_In_ const XMXDEC4* pSource) noexcept; + + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + #ifdef __clang__ + #pragma clang diagnostic pop + #endif + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + /**************************************************************************** + * + * Store operations + * + ****************************************************************************/ + + void XM_CALLCONV XMStoreColor(_Out_ XMCOLOR* pDestination, _In_ FXMVECTOR V) noexcept; + + void XM_CALLCONV XMStoreHalf2(_Out_ XMHALF2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreShortN2(_Out_ XMSHORTN2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreShort2(_Out_ XMSHORT2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUShortN2(_Out_ XMUSHORTN2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUShort2(_Out_ XMUSHORT2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreByteN2(_Out_ XMBYTEN2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreByte2(_Out_ XMBYTE2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUByteN2(_Out_ XMUBYTEN2* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUByte2(_Out_ XMUBYTE2* pDestination, _In_ FXMVECTOR V) noexcept; + + void XM_CALLCONV XMStoreU565(_Out_ XMU565* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreFloat3PK(_Out_ XMFLOAT3PK* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreFloat3SE(_Out_ XMFLOAT3SE* pDestination, _In_ FXMVECTOR V) noexcept; + + void XM_CALLCONV XMStoreHalf4(_Out_ XMHALF4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreShortN4(_Out_ XMSHORTN4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreShort4(_Out_ XMSHORT4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUShortN4(_Out_ XMUSHORTN4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUShort4(_Out_ XMUSHORT4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreXDecN4(_Out_ XMXDECN4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUDecN4(_Out_ XMUDECN4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUDecN4_XR(_Out_ XMUDECN4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUDec4(_Out_ XMUDEC4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreByteN4(_Out_ XMBYTEN4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreByte4(_Out_ XMBYTE4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUByteN4(_Out_ XMUBYTEN4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUByte4(_Out_ XMUBYTE4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreUNibble4(_Out_ XMUNIBBLE4* pDestination, _In_ FXMVECTOR V) noexcept; + void XM_CALLCONV XMStoreU555(_Out_ XMU555* pDestination, _In_ FXMVECTOR V) noexcept; + + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable : 4996) + // C4996: ignore deprecation warning + #endif + + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + #endif + + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif + + XM_DEPRECATED + void XM_CALLCONV XMStoreDecN4(_Out_ XMDECN4* pDestination, _In_ FXMVECTOR V) noexcept; + + XM_DEPRECATED + void XM_CALLCONV XMStoreDec4(_Out_ XMDEC4* pDestination, _In_ FXMVECTOR V) noexcept; + + XM_DEPRECATED + void XM_CALLCONV XMStoreXDec4(_Out_ XMXDEC4* pDestination, _In_ FXMVECTOR V) noexcept; + + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + #ifdef __clang__ + #pragma clang diagnostic pop + #endif + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + /**************************************************************************** + * + * Implementation + * + ****************************************************************************/ + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4068 4214 4204 4365 4616 6001 6101) + // C4068/4616: ignore unknown pragmas + // C4214/4204: nonstandard extension used + // C4365: Off by default noise + // C6001/6101: False positives + #endif + + #ifdef _PREFAST_ + #pragma prefast(push) + #pragma prefast(disable : 25000, "FXMVECTOR is 16 bytes") + #pragma prefast(disable : 26495, "Union initialization confuses /analyze") + #endif + + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunknown-warning-option" + #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + #endif + + #include "DirectXPackedVector.inl" + + #ifdef __clang__ + #pragma clang diagnostic pop + #endif + #ifdef _PREFAST_ + #pragma prefast(pop) + #endif + #ifdef _MSC_VER + #pragma warning(pop) + #endif + } // namespace PackedVector + +} // namespace DirectX + diff --git a/Extern/dxmath/Inc/DirectXPackedVector.inl b/Extern/dxmath/Inc/DirectXPackedVector.inl new file mode 100644 index 000000000..ccb07dd6c --- /dev/null +++ b/Extern/dxmath/Inc/DirectXPackedVector.inl @@ -0,0 +1,4503 @@ +//------------------------------------------------------------------------------------- +// DirectXPackedVector.inl -- SIMD C++ Math library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615560 +//------------------------------------------------------------------------------------- + +#pragma once + +/**************************************************************************** + * + * Data conversion + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline float XMConvertHalfToFloat(HALF Value) noexcept +{ +#if defined(_XM_F16C_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + __m128i V1 = _mm_cvtsi32_si128(static_cast(Value)); + __m128 V2 = _mm_cvtph_ps(V1); + return _mm_cvtss_f32(V2); +#elif defined(_XM_ARM_NEON_INTRINSICS_) && (defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__) && !defined(_XM_NO_INTRINSICS_) && (!defined(__GNUC__) || (__ARM_FP & 2)) + uint16x4_t vHalf = vdup_n_u16(Value); + float32x4_t vFloat = vcvt_f32_f16(vreinterpret_f16_u16(vHalf)); + return vgetq_lane_f32(vFloat, 0); +#else + auto Mantissa = static_cast(Value & 0x03FF); + + uint32_t Exponent = (Value & 0x7C00); + if (Exponent == 0x7C00) // INF/NAN + { + Exponent = 0x8f; + } + else if (Exponent != 0) // The value is normalized + { + Exponent = static_cast((static_cast(Value) >> 10) & 0x1F); + } + else if (Mantissa != 0) // The value is denormalized + { + // Normalize the value in the resulting float + Exponent = 1; + + do + { + Exponent--; + Mantissa <<= 1; + } + while ((Mantissa & 0x0400) == 0); + + Mantissa &= 0x03FF; + } + else // The value is zero + { + Exponent = static_cast(-112); + } + + uint32_t Result = + ((static_cast(Value) & 0x8000) << 16) // Sign + | ((Exponent + 112) << 23) // Exponent + | (Mantissa << 13); // Mantissa + + return reinterpret_cast(&Result)[0]; +#endif // !_XM_F16C_INTRINSICS_ +} + +//------------------------------------------------------------------------------ +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable : 26015 26019, "PREfast noise: Esp:1307" ) +#endif + +_Use_decl_annotations_ +inline float* XMConvertHalfToFloatStream +( + float* pOutputStream, + size_t OutputStride, + const HALF* pInputStream, + size_t InputStride, + size_t HalfCount +) noexcept +{ + assert(pOutputStream); + assert(pInputStream); + + assert(InputStride >= sizeof(HALF)); + _Analysis_assume_(InputStride >= sizeof(HALF)); + + assert(OutputStride >= sizeof(float)); + _Analysis_assume_(OutputStride >= sizeof(float)); + +#if defined(_XM_F16C_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + auto pHalf = reinterpret_cast(pInputStream); + auto pFloat = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = HalfCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(HALF)) + { + if (OutputStride == sizeof(float)) + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Packed input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + __m128i HV = _mm_loadl_epi64(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + __m128 FV = _mm_cvtph_ps(HV); + + XM_STREAM_PS(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128i HV = _mm_loadl_epi64(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + __m128 FV = _mm_cvtph_ps(HV); + + _mm_storeu_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + } + else + { + // Packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128i HV = _mm_loadl_epi64(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + __m128 FV = _mm_cvtph_ps(HV); + + _mm_store_ss(reinterpret_cast(pFloat), FV); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 1); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 2); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 3); + pFloat += OutputStride; + i += 4; + } + } + } + else if (OutputStride == sizeof(float)) + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Scattered input, aligned & packed output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + __m128i HV = _mm_setzero_si128(); + HV = _mm_insert_epi16(HV, H1, 0); + HV = _mm_insert_epi16(HV, H2, 1); + HV = _mm_insert_epi16(HV, H3, 2); + HV = _mm_insert_epi16(HV, H4, 3); + __m128 FV = _mm_cvtph_ps(HV); + + XM_STREAM_PS(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + else + { + // Scattered input, packed output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + __m128i HV = _mm_setzero_si128(); + HV = _mm_insert_epi16(HV, H1, 0); + HV = _mm_insert_epi16(HV, H2, 1); + HV = _mm_insert_epi16(HV, H3, 2); + HV = _mm_insert_epi16(HV, H4, 3); + __m128 FV = _mm_cvtph_ps(HV); + + _mm_storeu_ps(reinterpret_cast(pFloat), FV); + pFloat += OutputStride * 4; + i += 4; + } + } + } + else + { + // Scattered input, scattered output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + __m128i HV = _mm_setzero_si128(); + HV = _mm_insert_epi16(HV, H1, 0); + HV = _mm_insert_epi16(HV, H2, 1); + HV = _mm_insert_epi16(HV, H3, 2); + HV = _mm_insert_epi16(HV, H4, 3); + __m128 FV = _mm_cvtph_ps(HV); + + _mm_store_ss(reinterpret_cast(pFloat), FV); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 1); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 2); + pFloat += OutputStride; + *reinterpret_cast(pFloat) = _mm_extract_ps(FV, 3); + pFloat += OutputStride; + i += 4; + } + } + } + + for (; i < HalfCount; ++i) + { + *reinterpret_cast(pFloat) = XMConvertHalfToFloat(reinterpret_cast(pHalf)[0]); + pHalf += InputStride; + pFloat += OutputStride; + } + + XM_SFENCE(); + + return pOutputStream; +#elif defined(_XM_ARM_NEON_INTRINSICS_) && (defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) ||__aarch64__) && !defined(_XM_NO_INTRINSICS_) && (!defined(__GNUC__) || (__ARM_FP & 2)) + auto pHalf = reinterpret_cast(pInputStream); + auto pFloat = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = HalfCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(HALF)) + { + if (OutputStride == sizeof(float)) + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + uint16x4_t vHalf = vld1_u16(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + float32x4_t vFloat = vcvt_f32_f16(vreinterpret_f16_u16(vHalf)); + + vst1q_f32(reinterpret_cast(pFloat), vFloat); + pFloat += OutputStride * 4; + i += 4; + } + } + else + { + // Packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + uint16x4_t vHalf = vld1_u16(reinterpret_cast(pHalf)); + pHalf += InputStride * 4; + + float32x4_t vFloat = vcvt_f32_f16(vreinterpret_f16_u16(vHalf)); + + vst1q_lane_f32(reinterpret_cast(pFloat), vFloat, 0); + pFloat += OutputStride; + vst1q_lane_f32(reinterpret_cast(pFloat), vFloat, 1); + pFloat += OutputStride; + vst1q_lane_f32(reinterpret_cast(pFloat), vFloat, 2); + pFloat += OutputStride; + vst1q_lane_f32(reinterpret_cast(pFloat), vFloat, 3); + pFloat += OutputStride; + i += 4; + } + } + } + else if (OutputStride == sizeof(float)) + { + // Scattered input, packed output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + uint64_t iHalf = uint64_t(H1) | (uint64_t(H2) << 16) | (uint64_t(H3) << 32) | (uint64_t(H4) << 48); + uint16x4_t vHalf = vcreate_u16(iHalf); + + float32x4_t vFloat = vcvt_f32_f16(vreinterpret_f16_u16(vHalf)); + + vst1q_f32(reinterpret_cast(pFloat), vFloat); + pFloat += OutputStride * 4; + i += 4; + } + } + else + { + // Scattered input, scattered output + for (size_t j = 0; j < four; ++j) + { + uint16_t H1 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H2 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H3 = *reinterpret_cast(pHalf); + pHalf += InputStride; + uint16_t H4 = *reinterpret_cast(pHalf); + pHalf += InputStride; + + uint64_t iHalf = uint64_t(H1) | (uint64_t(H2) << 16) | (uint64_t(H3) << 32) | (uint64_t(H4) << 48); + uint16x4_t vHalf = vcreate_u16(iHalf); + + float32x4_t vFloat = vcvt_f32_f16(vreinterpret_f16_u16(vHalf)); + + vst1q_lane_f32(reinterpret_cast(pFloat), vFloat, 0); + pFloat += OutputStride; + vst1q_lane_f32(reinterpret_cast(pFloat), vFloat, 1); + pFloat += OutputStride; + vst1q_lane_f32(reinterpret_cast(pFloat), vFloat, 2); + pFloat += OutputStride; + vst1q_lane_f32(reinterpret_cast(pFloat), vFloat, 3); + pFloat += OutputStride; + i += 4; + } + } + } + + for (; i < HalfCount; ++i) + { + *reinterpret_cast(pFloat) = XMConvertHalfToFloat(reinterpret_cast(pHalf)[0]); + pHalf += InputStride; + pFloat += OutputStride; + } + + return pOutputStream; +#else + auto pHalf = reinterpret_cast(pInputStream); + auto pFloat = reinterpret_cast(pOutputStream); + + for (size_t i = 0; i < HalfCount; i++) + { + *reinterpret_cast(pFloat) = XMConvertHalfToFloat(reinterpret_cast(pHalf)[0]); + pHalf += InputStride; + pFloat += OutputStride; + } + + return pOutputStream; +#endif // !_XM_F16C_INTRINSICS_ +} + +//------------------------------------------------------------------------------ + +inline HALF XMConvertFloatToHalf(float Value) noexcept +{ +#if defined(_XM_F16C_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + __m128 V1 = _mm_set_ss(Value); + __m128i V2 = _mm_cvtps_ph(V1, _MM_FROUND_TO_NEAREST_INT); + return static_cast(_mm_extract_epi16(V2, 0)); +#elif defined(_XM_ARM_NEON_INTRINSICS_) && (defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__) && !defined(_XM_NO_INTRINSICS_) && (!defined(__GNUC__) || (__ARM_FP & 2)) + float32x4_t vFloat = vdupq_n_f32(Value); + float16x4_t vHalf = vcvt_f16_f32(vFloat); + return vget_lane_u16(vreinterpret_u16_f16(vHalf), 0); +#else + uint32_t Result; + + auto IValue = reinterpret_cast(&Value)[0]; + uint32_t Sign = (IValue & 0x80000000U) >> 16U; + IValue = IValue & 0x7FFFFFFFU; // Hack off the sign + if (IValue >= 0x47800000 /*e+16*/) + { + // The number is too large to be represented as a half. Return infinity or NaN + Result = 0x7C00U | ((IValue > 0x7F800000) ? (0x200 | ((IValue >> 13U) & 0x3FFU)) : 0U); + } + else if (IValue <= 0x33000000U /*e-25*/) + { + Result = 0; + } + else if (IValue < 0x38800000U /*e-14*/) + { + // The number is too small to be represented as a normalized half. + // Convert it to a denormalized value. + uint32_t Shift = 125U - (IValue >> 23U); + IValue = 0x800000U | (IValue & 0x7FFFFFU); + Result = IValue >> (Shift + 1); + uint32_t s = (IValue & ((1U << Shift) - 1)) != 0; + Result += (Result | s) & ((IValue >> Shift) & 1U); + } + else + { + // Rebias the exponent to represent the value as a normalized half. + IValue += 0xC8000000U; + Result = ((IValue + 0x0FFFU + ((IValue >> 13U) & 1U)) >> 13U) & 0x7FFFU; + } + return static_cast(Result | Sign); +#endif // !_XM_F16C_INTRINSICS_ +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline HALF* XMConvertFloatToHalfStream +( + HALF* pOutputStream, + size_t OutputStride, + const float* pInputStream, + size_t InputStride, + size_t FloatCount +) noexcept +{ + assert(pOutputStream); + assert(pInputStream); + + assert(InputStride >= sizeof(float)); + _Analysis_assume_(InputStride >= sizeof(float)); + + assert(OutputStride >= sizeof(HALF)); + _Analysis_assume_(OutputStride >= sizeof(HALF)); + +#if defined(_XM_F16C_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + auto pFloat = reinterpret_cast(pInputStream); + auto pHalf = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = FloatCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(float)) + { + if (OutputStride == sizeof(HALF)) + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Aligned and packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_load_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, _MM_FROUND_TO_NEAREST_INT); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(pHalf), HV); + pHalf += OutputStride * 4; + i += 4; + } + } + else + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_loadu_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, _MM_FROUND_TO_NEAREST_INT); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(pHalf), HV); + pHalf += OutputStride * 4; + i += 4; + } + } + } + else + { + if ((reinterpret_cast(pFloat) & 0xF) == 0) + { + // Aligned & packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_load_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, _MM_FROUND_TO_NEAREST_INT); + + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 0)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 1)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 2)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 3)); + pHalf += OutputStride; + i += 4; + } + } + else + { + // Packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128 FV = _mm_loadu_ps(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + __m128i HV = _mm_cvtps_ph(FV, _MM_FROUND_TO_NEAREST_INT); + + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 0)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 1)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 2)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 3)); + pHalf += OutputStride; + i += 4; + } + } + } + } + else if (OutputStride == sizeof(HALF)) + { + // Scattered input, packed output + for (size_t j = 0; j < four; ++j) + { + __m128 FV1 = _mm_load_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV2 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV3 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV4 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV = _mm_blend_ps(FV1, FV2, 0x2); + __m128 FT = _mm_blend_ps(FV3, FV4, 0x8); + FV = _mm_blend_ps(FV, FT, 0xC); + + __m128i HV = _mm_cvtps_ph(FV, _MM_FROUND_TO_NEAREST_INT); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(pHalf), HV); + pHalf += OutputStride * 4; + i += 4; + } + } + else + { + // Scattered input, scattered output + for (size_t j = 0; j < four; ++j) + { + __m128 FV1 = _mm_load_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV2 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV3 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV4 = _mm_broadcast_ss(reinterpret_cast(pFloat)); + pFloat += InputStride; + + __m128 FV = _mm_blend_ps(FV1, FV2, 0x2); + __m128 FT = _mm_blend_ps(FV3, FV4, 0x8); + FV = _mm_blend_ps(FV, FT, 0xC); + + __m128i HV = _mm_cvtps_ph(FV, _MM_FROUND_TO_NEAREST_INT); + + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 0)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 1)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 2)); + pHalf += OutputStride; + *reinterpret_cast(pHalf) = static_cast(_mm_extract_epi16(HV, 3)); + pHalf += OutputStride; + i += 4; + } + } + } + + for (; i < FloatCount; ++i) + { + *reinterpret_cast(pHalf) = XMConvertFloatToHalf(reinterpret_cast(pFloat)[0]); + pFloat += InputStride; + pHalf += OutputStride; + } + + return pOutputStream; +#elif defined(_XM_ARM_NEON_INTRINSICS_) && (defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || __aarch64__) && !defined(_XM_NO_INTRINSICS_) && (!defined(__GNUC__) || (__ARM_FP & 2)) + auto pFloat = reinterpret_cast(pInputStream); + auto pHalf = reinterpret_cast(pOutputStream); + + size_t i = 0; + size_t four = FloatCount >> 2; + if (four > 0) + { + if (InputStride == sizeof(float)) + { + if (OutputStride == sizeof(HALF)) + { + // Packed input, packed output + for (size_t j = 0; j < four; ++j) + { + float32x4_t vFloat = vld1q_f32(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + uint16x4_t vHalf = vreinterpret_u16_f16(vcvt_f16_f32(vFloat)); + + vst1_u16(reinterpret_cast(pHalf), vHalf); + pHalf += OutputStride * 4; + i += 4; + } + } + else + { + // Packed input, scattered output + for (size_t j = 0; j < four; ++j) + { + float32x4_t vFloat = vld1q_f32(reinterpret_cast(pFloat)); + pFloat += InputStride * 4; + + uint16x4_t vHalf = vreinterpret_u16_f16(vcvt_f16_f32(vFloat)); + + vst1_lane_u16(reinterpret_cast(pHalf), vHalf, 0); + pHalf += OutputStride; + vst1_lane_u16(reinterpret_cast(pHalf), vHalf, 1); + pHalf += OutputStride; + vst1_lane_u16(reinterpret_cast(pHalf), vHalf, 2); + pHalf += OutputStride; + vst1_lane_u16(reinterpret_cast(pHalf), vHalf, 3); + pHalf += OutputStride; + i += 4; + } + } + } + else if (OutputStride == sizeof(HALF)) + { + // Scattered input, packed output + for (size_t j = 0; j < four; ++j) + { + float32x4_t vFloat = vdupq_n_f32(0); + vFloat = vld1q_lane_f32(reinterpret_cast(pFloat), vFloat, 0); + pFloat += InputStride; + + vFloat = vld1q_lane_f32(reinterpret_cast(pFloat), vFloat, 1); + pFloat += InputStride; + + vFloat = vld1q_lane_f32(reinterpret_cast(pFloat), vFloat, 2); + pFloat += InputStride; + + vFloat = vld1q_lane_f32(reinterpret_cast(pFloat), vFloat, 3); + pFloat += InputStride; + + uint16x4_t vHalf = vreinterpret_u16_f16(vcvt_f16_f32(vFloat)); + + vst1_u16(reinterpret_cast(pHalf), vHalf); + pHalf += OutputStride * 4; + i += 4; + } + } + else + { + // Scattered input, scattered output + for (size_t j = 0; j < four; ++j) + { + float32x4_t vFloat = vdupq_n_f32(0); + vFloat = vld1q_lane_f32(reinterpret_cast(pFloat), vFloat, 0); + pFloat += InputStride; + + vFloat = vld1q_lane_f32(reinterpret_cast(pFloat), vFloat, 1); + pFloat += InputStride; + + vFloat = vld1q_lane_f32(reinterpret_cast(pFloat), vFloat, 2); + pFloat += InputStride; + + vFloat = vld1q_lane_f32(reinterpret_cast(pFloat), vFloat, 3); + pFloat += InputStride; + + uint16x4_t vHalf = vreinterpret_u16_f16(vcvt_f16_f32(vFloat)); + + vst1_lane_u16(reinterpret_cast(pHalf), vHalf, 0); + pHalf += OutputStride; + vst1_lane_u16(reinterpret_cast(pHalf), vHalf, 1); + pHalf += OutputStride; + vst1_lane_u16(reinterpret_cast(pHalf), vHalf, 2); + pHalf += OutputStride; + vst1_lane_u16(reinterpret_cast(pHalf), vHalf, 3); + pHalf += OutputStride; + i += 4; + } + } + } + + for (; i < FloatCount; ++i) + { + *reinterpret_cast(pHalf) = XMConvertFloatToHalf(reinterpret_cast(pFloat)[0]); + pFloat += InputStride; + pHalf += OutputStride; + } + + return pOutputStream; +#else + auto pFloat = reinterpret_cast(pInputStream); + auto pHalf = reinterpret_cast(pOutputStream); + + for (size_t i = 0; i < FloatCount; i++) + { + *reinterpret_cast(pHalf) = XMConvertFloatToHalf(reinterpret_cast(pFloat)[0]); + pFloat += InputStride; + pHalf += OutputStride; + } + return pOutputStream; +#endif // !_XM_F16C_INTRINSICS_ +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + +/**************************************************************************** + * + * Vector and matrix load operations + * + ****************************************************************************/ + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable:28931, "PREfast noise: Esp:1266") +#endif + +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadColor(const XMCOLOR* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + // int32_t -> Float conversions are done in one instruction. + // uint32_t -> Float calls a runtime function. Keep in int32_t + auto iColor = static_cast(pSource->c); + XMVECTORF32 vColor = { { { + static_cast((iColor >> 16) & 0xFF)* (1.0f / 255.0f), + static_cast((iColor >> 8) & 0xFF)* (1.0f / 255.0f), + static_cast(iColor & 0xFF)* (1.0f / 255.0f), + static_cast((iColor >> 24) & 0xFF)* (1.0f / 255.0f) + } } }; + return vColor.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32_t bgra = pSource->c; + uint32_t rgba = (bgra & 0xFF00FF00) | ((bgra >> 16) & 0xFF) | ((bgra << 16) & 0xFF0000); + uint32x2_t vInt8 = vdup_n_u32(rgba); + uint16x8_t vInt16 = vmovl_u8(vreinterpret_u8_u32(vInt8)); + uint32x4_t vInt = vmovl_u16(vget_low_u16(vInt16)); + float32x4_t R = vcvtq_f32_u32(vInt); + return vmulq_n_f32(R, 1.0f / 255.0f); +#elif defined(_XM_SSE_INTRINSICS_) + // Splat the color in all four entries + __m128i vInt = _mm_set1_epi32(static_cast(pSource->c)); + // Shift R&0xFF0000, G&0xFF00, B&0xFF, A&0xFF000000 + vInt = _mm_and_si128(vInt, g_XMMaskA8R8G8B8); + // a is unsigned! Flip the bit to convert the order to signed + vInt = _mm_xor_si128(vInt, g_XMFlipA8R8G8B8); + // Convert to floating point numbers + XMVECTOR vTemp = _mm_cvtepi32_ps(vInt); + // RGB + 0, A + 0x80000000.f to undo the signed order. + vTemp = _mm_add_ps(vTemp, g_XMFixAA8R8G8B8); + // Convert 0-255 to 0.0f-1.0f + return _mm_mul_ps(vTemp, g_XMNormalizeA8R8G8B8); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadHalf2(const XMHALF2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_F16C_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + __m128 V = _mm_load_ss(reinterpret_cast(pSource)); + return _mm_cvtph_ps(_mm_castps_si128(V)); +#else + XMVECTORF32 vResult = { { { + XMConvertHalfToFloat(pSource->x), + XMConvertHalfToFloat(pSource->y), + 0.0f, + 0.0f + } } }; + return vResult.v; +#endif // !_XM_F16C_INTRINSICS_ +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadShortN2(const XMSHORTN2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + (pSource->x == -32768) ? -1.f : (static_cast(pSource->x)* (1.0f / 32767.0f)), + (pSource->y == -32768) ? -1.f : (static_cast(pSource->y)* (1.0f / 32767.0f)), + 0.0f, + 0.0f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vInt16 = vld1_dup_u32(reinterpret_cast(pSource)); + int32x4_t vInt = vmovl_s16(vreinterpret_s16_u32(vInt16)); + vInt = vandq_s32(vInt, g_XMMaskXY); + float32x4_t R = vcvtq_f32_s32(vInt); + R = vmulq_n_f32(R, 1.0f / 32767.0f); + return vmaxq_f32(R, vdupq_n_f32(-1.f)); +#elif defined(_XM_SSE_INTRINSICS_) + // Splat the two shorts in all four entries (WORD alignment okay, + // DWORD alignment preferred) + __m128 vTemp = _mm_load_ps1(reinterpret_cast(&pSource->x)); + // Mask x&0xFFFF, y&0xFFFF0000,z&0,w&0 + vTemp = _mm_and_ps(vTemp, g_XMMaskX16Y16); + // x needs to be sign extended + vTemp = _mm_xor_ps(vTemp, g_XMFlipX16Y16); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // x - 0x8000 to undo the signed order. + vTemp = _mm_add_ps(vTemp, g_XMFixX16Y16); + // Convert -1.0f - 1.0f + vTemp = _mm_mul_ps(vTemp, g_XMNormalizeX16Y16); + // Clamp result (for case of -32768) + return _mm_max_ps(vTemp, g_XMNegativeOne); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadShort2(const XMSHORT2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x), + static_cast(pSource->y), + 0.f, + 0.f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vInt16 = vld1_dup_u32(reinterpret_cast(pSource)); + int32x4_t vInt = vmovl_s16(vreinterpret_s16_u32(vInt16)); + vInt = vandq_s32(vInt, g_XMMaskXY); + return vcvtq_f32_s32(vInt); +#elif defined(_XM_SSE_INTRINSICS_) + // Splat the two shorts in all four entries (WORD alignment okay, + // DWORD alignment preferred) + __m128 vTemp = _mm_load_ps1(reinterpret_cast(&pSource->x)); + // Mask x&0xFFFF, y&0xFFFF0000,z&0,w&0 + vTemp = _mm_and_ps(vTemp, g_XMMaskX16Y16); + // x needs to be sign extended + vTemp = _mm_xor_ps(vTemp, g_XMFlipX16Y16); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // x - 0x8000 to undo the signed order. + vTemp = _mm_add_ps(vTemp, g_XMFixX16Y16); + // Y is 65536 too large + return _mm_mul_ps(vTemp, g_XMFixupY16); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUShortN2(const XMUSHORTN2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x) / 65535.0f, + static_cast(pSource->y) / 65535.0f, + 0.f, + 0.f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vInt16 = vld1_dup_u32(reinterpret_cast(pSource)); + uint32x4_t vInt = vmovl_u16(vreinterpret_u16_u32(vInt16)); + vInt = vandq_u32(vInt, g_XMMaskXY); + float32x4_t R = vcvtq_f32_u32(vInt); + R = vmulq_n_f32(R, 1.0f / 65535.0f); + return vmaxq_f32(R, vdupq_n_f32(-1.f)); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 FixupY16 = { { { 1.0f / 65535.0f, 1.0f / (65535.0f * 65536.0f), 0.0f, 0.0f } } }; + static const XMVECTORF32 FixaddY16 = { { { 0, 32768.0f * 65536.0f, 0, 0 } } }; + // Splat the two shorts in all four entries (WORD alignment okay, + // DWORD alignment preferred) + __m128 vTemp = _mm_load_ps1(reinterpret_cast(&pSource->x)); + // Mask x&0xFFFF, y&0xFFFF0000,z&0,w&0 + vTemp = _mm_and_ps(vTemp, g_XMMaskX16Y16); + // y needs to be sign flipped + vTemp = _mm_xor_ps(vTemp, g_XMFlipY); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // y + 0x8000 to undo the signed order. + vTemp = _mm_add_ps(vTemp, FixaddY16); + // Y is 65536 times too large + vTemp = _mm_mul_ps(vTemp, FixupY16); + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUShort2(const XMUSHORT2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x), + static_cast(pSource->y), + 0.f, + 0.f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vInt16 = vld1_dup_u32(reinterpret_cast(pSource)); + uint32x4_t vInt = vmovl_u16(vreinterpret_u16_u32(vInt16)); + vInt = vandq_u32(vInt, g_XMMaskXY); + return vcvtq_f32_u32(vInt); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 FixaddY16 = { { { 0, 32768.0f, 0, 0 } } }; + // Splat the two shorts in all four entries (WORD alignment okay, + // DWORD alignment preferred) + __m128 vTemp = _mm_load_ps1(reinterpret_cast(&pSource->x)); + // Mask x&0xFFFF, y&0xFFFF0000,z&0,w&0 + vTemp = _mm_and_ps(vTemp, g_XMMaskX16Y16); + // y needs to be sign flipped + vTemp = _mm_xor_ps(vTemp, g_XMFlipY); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // Y is 65536 times too large + vTemp = _mm_mul_ps(vTemp, g_XMFixupY16); + // y + 0x8000 to undo the signed order. + vTemp = _mm_add_ps(vTemp, FixaddY16); + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadByteN2(const XMBYTEN2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + (pSource->x == -128) ? -1.f : (static_cast(pSource->x)* (1.0f / 127.0f)), + (pSource->y == -128) ? -1.f : (static_cast(pSource->y)* (1.0f / 127.0f)), + 0.0f, + 0.0f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint16x4_t vInt8 = vld1_dup_u16(reinterpret_cast(pSource)); + int16x8_t vInt16 = vmovl_s8(vreinterpret_s8_u16(vInt8)); + int32x4_t vInt = vmovl_s16(vget_low_s16(vInt16)); + vInt = vandq_s32(vInt, g_XMMaskXY); + float32x4_t R = vcvtq_f32_s32(vInt); + R = vmulq_n_f32(R, 1.0f / 127.0f); + return vmaxq_f32(R, vdupq_n_f32(-1.f)); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.0f / 127.0f, 1.0f / (127.0f * 256.0f), 0, 0 } } }; + static const XMVECTORU32 Mask = { { { 0xFF, 0xFF00, 0, 0 } } }; + // Splat the color in all four entries (x,z,y,w) + __m128i vInt = XM_LOADU_SI16(&pSource->v); + XMVECTOR vTemp = XM_PERMUTE_PS(_mm_castsi128_ps(vInt), _MM_SHUFFLE(0, 0, 0, 0)); + // Mask + vTemp = _mm_and_ps(vTemp, Mask); + // x,y and z are unsigned! Flip the bits to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMXorByte4); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // x, y and z - 0x80 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMAddByte4); + // Fix y, z and w because they are too large + vTemp = _mm_mul_ps(vTemp, Scale); + // Clamp result (for case of -128) + return _mm_max_ps(vTemp, g_XMNegativeOne); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadByte2(const XMBYTE2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x), + static_cast(pSource->y), + 0.0f, + 0.0f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint16x4_t vInt8 = vld1_dup_u16(reinterpret_cast(pSource)); + int16x8_t vInt16 = vmovl_s8(vreinterpret_s8_u16(vInt8)); + int32x4_t vInt = vmovl_s16(vget_low_s16(vInt16)); + vInt = vandq_s32(vInt, g_XMMaskXY); + return vcvtq_f32_s32(vInt); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.0f, 1.0f / 256.0f, 1.0f / 65536.0f, 1.0f / (65536.0f * 256.0f) } } }; + static const XMVECTORU32 Mask = { { { 0xFF, 0xFF00, 0, 0 } } }; + // Splat the color in all four entries (x,z,y,w) + __m128i vInt = XM_LOADU_SI16(&pSource->v); + XMVECTOR vTemp = XM_PERMUTE_PS(_mm_castsi128_ps(vInt), _MM_SHUFFLE(0, 0, 0, 0)); + // Mask + vTemp = _mm_and_ps(vTemp, Mask); + // x,y and z are unsigned! Flip the bits to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMXorByte4); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // x, y and z - 0x80 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMAddByte4); + // Fix y, z and w because they are too large + return _mm_mul_ps(vTemp, Scale); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUByteN2(const XMUBYTEN2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x)* (1.0f / 255.0f), + static_cast(pSource->y)* (1.0f / 255.0f), + 0.0f, + 0.0f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint16x4_t vInt8 = vld1_dup_u16(reinterpret_cast(pSource)); + uint16x8_t vInt16 = vmovl_u8(vreinterpret_u8_u16(vInt8)); + uint32x4_t vInt = vmovl_u16(vget_low_u16(vInt16)); + vInt = vandq_u32(vInt, g_XMMaskXY); + float32x4_t R = vcvtq_f32_u32(vInt); + return vmulq_n_f32(R, 1.0f / 255.0f); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.0f / 255.0f, 1.0f / (255.0f * 256.0f), 0, 0 } } }; + static const XMVECTORU32 Mask = { { { 0xFF, 0xFF00, 0, 0 } } }; + // Splat the color in all four entries (x,z,y,w) + __m128i vInt = XM_LOADU_SI16(&pSource->v); + XMVECTOR vTemp = XM_PERMUTE_PS(_mm_castsi128_ps(vInt), _MM_SHUFFLE(0, 0, 0, 0)); + // Mask + vTemp = _mm_and_ps(vTemp, Mask); + // w is signed! Flip the bits to convert the order to unsigned + vTemp = _mm_xor_ps(vTemp, g_XMFlipW); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // w + 0x80 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMAddUDec4); + // Fix y, z and w because they are too large + return _mm_mul_ps(vTemp, Scale); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUByte2(const XMUBYTE2* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x), + static_cast(pSource->y), + 0.0f, + 0.0f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint16x4_t vInt8 = vld1_dup_u16(reinterpret_cast(pSource)); + uint16x8_t vInt16 = vmovl_u8(vreinterpret_u8_u16(vInt8)); + uint32x4_t vInt = vmovl_u16(vget_low_u16(vInt16)); + vInt = vandq_u32(vInt, g_XMMaskXY); + return vcvtq_f32_u32(vInt); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.0f, 1.0f / 256.0f, 0, 0 } } }; + static const XMVECTORU32 Mask = { { { 0xFF, 0xFF00, 0, 0 } } }; + // Splat the color in all four entries (x,z,y,w) + __m128i vInt = XM_LOADU_SI16(&pSource->v); + XMVECTOR vTemp = XM_PERMUTE_PS(_mm_castsi128_ps(vInt), _MM_SHUFFLE(0, 0, 0, 0)); + // Mask + vTemp = _mm_and_ps(vTemp, Mask); + // w is signed! Flip the bits to convert the order to unsigned + vTemp = _mm_xor_ps(vTemp, g_XMFlipW); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // w + 0x80 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMAddUDec4); + // Fix y, z and w because they are too large + return _mm_mul_ps(vTemp, Scale); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadU565(const XMU565* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + float(pSource->v & 0x1F), + float((pSource->v >> 5) & 0x3F), + float((pSource->v >> 11) & 0x1F), + 0.f, + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORI32 U565And = { { { 0x1F, 0x3F << 5, 0x1F << 11, 0 } } }; + static const XMVECTORF32 U565Mul = { { { 1.0f, 1.0f / 32.0f, 1.0f / 2048.f, 0 } } }; + uint16x4_t vInt16 = vld1_dup_u16(reinterpret_cast(pSource)); + uint32x4_t vInt = vmovl_u16(vInt16); + vInt = vandq_u32(vInt, U565And); + float32x4_t R = vcvtq_f32_u32(vInt); + return vmulq_f32(R, U565Mul); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORI32 U565And = { { { 0x1F, 0x3F << 5, 0x1F << 11, 0 } } }; + static const XMVECTORF32 U565Mul = { { { 1.0f, 1.0f / 32.0f, 1.0f / 2048.f, 0 } } }; + // Get the 16 bit value and splat it + __m128i vInt = XM_LOADU_SI16(&pSource->v); + XMVECTOR vResult = XM_PERMUTE_PS(_mm_castsi128_ps(vInt), _MM_SHUFFLE(0, 0, 0, 0)); + // Mask off x, y and z + vResult = _mm_and_ps(vResult, U565And); + // Convert to float + vResult = _mm_cvtepi32_ps(_mm_castps_si128(vResult)); + // Normalize x, y, and z + vResult = _mm_mul_ps(vResult, U565Mul); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadFloat3PK(const XMFLOAT3PK* pSource) noexcept +{ + assert(pSource); + + XM_ALIGNED_DATA(16) uint32_t Result[4]; + uint32_t Mantissa; + uint32_t Exponent; + + // X Channel (6-bit mantissa) + Mantissa = pSource->xm; + + if (pSource->xe == 0x1f) // INF or NAN + { + Result[0] = static_cast(0x7f800000 | (static_cast(pSource->xm) << 17)); + } + else + { + if (pSource->xe != 0) // The value is normalized + { + Exponent = pSource->xe; + } + else if (Mantissa != 0) // The value is denormalized + { + // Normalize the value in the resulting float + Exponent = 1; + + do + { + Exponent--; + Mantissa <<= 1; + } + while ((Mantissa & 0x40) == 0); + + Mantissa &= 0x3F; + } + else // The value is zero + { + Exponent = static_cast(-112); + } + + Result[0] = ((Exponent + 112) << 23) | (Mantissa << 17); + } + + // Y Channel (6-bit mantissa) + Mantissa = pSource->ym; + + if (pSource->ye == 0x1f) // INF or NAN + { + Result[1] = static_cast(0x7f800000 | (static_cast(pSource->ym) << 17)); + } + else + { + if (pSource->ye != 0) // The value is normalized + { + Exponent = pSource->ye; + } + else if (Mantissa != 0) // The value is denormalized + { + // Normalize the value in the resulting float + Exponent = 1; + + do + { + Exponent--; + Mantissa <<= 1; + } + while ((Mantissa & 0x40) == 0); + + Mantissa &= 0x3F; + } + else // The value is zero + { + Exponent = static_cast(-112); + } + + Result[1] = ((Exponent + 112) << 23) | (Mantissa << 17); + } + + // Z Channel (5-bit mantissa) + Mantissa = pSource->zm; + + if (pSource->ze == 0x1f) // INF or NAN + { + Result[2] = static_cast(0x7f800000 | (static_cast(pSource->zm) << 17)); + } + else + { + if (pSource->ze != 0) // The value is normalized + { + Exponent = pSource->ze; + } + else if (Mantissa != 0) // The value is denormalized + { + // Normalize the value in the resulting float + Exponent = 1; + + do + { + Exponent--; + Mantissa <<= 1; + } + while ((Mantissa & 0x20) == 0); + + Mantissa &= 0x1F; + } + else // The value is zero + { + Exponent = static_cast(-112); + } + + Result[2] = ((Exponent + 112) << 23) | (Mantissa << 18); + } + + return XMLoadFloat3A(reinterpret_cast(&Result)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadFloat3SE(const XMFLOAT3SE* pSource) noexcept +{ + assert(pSource); + + union { float f; int32_t i; } fi; + fi.i = 0x33800000 + (pSource->e << 23); + float Scale = fi.f; + + XMVECTORF32 v = { { { + Scale * float(pSource->xm), + Scale * float(pSource->ym), + Scale * float(pSource->zm), + 1.0f } } }; + return v; +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadHalf4(const XMHALF4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_F16C_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + __m128i V = _mm_loadl_epi64(reinterpret_cast(pSource)); + return _mm_cvtph_ps(V); +#else + XMVECTORF32 vResult = { { { + XMConvertHalfToFloat(pSource->x), + XMConvertHalfToFloat(pSource->y), + XMConvertHalfToFloat(pSource->z), + XMConvertHalfToFloat(pSource->w) + } } }; + return vResult.v; +#endif // !_XM_F16C_INTRINSICS_ +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadShortN4(const XMSHORTN4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + (pSource->x == -32768) ? -1.f : (static_cast(pSource->x)* (1.0f / 32767.0f)), + (pSource->y == -32768) ? -1.f : (static_cast(pSource->y)* (1.0f / 32767.0f)), + (pSource->z == -32768) ? -1.f : (static_cast(pSource->z)* (1.0f / 32767.0f)), + (pSource->w == -32768) ? -1.f : (static_cast(pSource->w)* (1.0f / 32767.0f)) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int16x4_t vInt = vld1_s16(reinterpret_cast(pSource)); + int32x4_t V = vmovl_s16(vInt); + float32x4_t vResult = vcvtq_f32_s32(V); + vResult = vmulq_n_f32(vResult, 1.0f / 32767.0f); + return vmaxq_f32(vResult, vdupq_n_f32(-1.f)); +#elif defined(_XM_SSE_INTRINSICS_) + // Splat the color in all four entries (x,z,y,w) + __m128d vIntd = _mm_load1_pd(reinterpret_cast(&pSource->x)); + // Shift x&0ffff,z&0xffff,y&0xffff0000,w&0xffff0000 + __m128 vTemp = _mm_and_ps(_mm_castpd_ps(vIntd), g_XMMaskX16Y16Z16W16); + // x and z are unsigned! Flip the bits to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMFlipX16Y16Z16W16); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // x and z - 0x8000 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMFixX16Y16Z16W16); + // Convert to -1.0f - 1.0f + vTemp = _mm_mul_ps(vTemp, g_XMNormalizeX16Y16Z16W16); + // Very important! The entries are x,z,y,w, flip it to x,y,z,w + vTemp = XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 1, 2, 0)); + // Clamp result (for case of -32768) + return _mm_max_ps(vTemp, g_XMNegativeOne); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadShort4(const XMSHORT4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x), + static_cast(pSource->y), + static_cast(pSource->z), + static_cast(pSource->w) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + int16x4_t vInt = vld1_s16(reinterpret_cast(pSource)); + int32x4_t V = vmovl_s16(vInt); + return vcvtq_f32_s32(V); +#elif defined(_XM_SSE_INTRINSICS_) + // Splat the color in all four entries (x,z,y,w) + __m128d vIntd = _mm_load1_pd(reinterpret_cast(&pSource->x)); + // Shift x&0ffff,z&0xffff,y&0xffff0000,w&0xffff0000 + __m128 vTemp = _mm_and_ps(_mm_castpd_ps(vIntd), g_XMMaskX16Y16Z16W16); + // x and z are unsigned! Flip the bits to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMFlipX16Y16Z16W16); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // x and z - 0x8000 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMFixX16Y16Z16W16); + // Fix y and w because they are 65536 too large + vTemp = _mm_mul_ps(vTemp, g_XMFixupY16W16); + // Very important! The entries are x,z,y,w, flip it to x,y,z,w + return XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 1, 2, 0)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUShortN4(const XMUSHORTN4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x) / 65535.0f, + static_cast(pSource->y) / 65535.0f, + static_cast(pSource->z) / 65535.0f, + static_cast(pSource->w) / 65535.0f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint16x4_t vInt = vld1_u16(reinterpret_cast(pSource)); + uint32x4_t V = vmovl_u16(vInt); + float32x4_t vResult = vcvtq_f32_u32(V); + return vmulq_n_f32(vResult, 1.0f / 65535.0f); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 FixupY16W16 = { { { 1.0f / 65535.0f, 1.0f / 65535.0f, 1.0f / (65535.0f * 65536.0f), 1.0f / (65535.0f * 65536.0f) } } }; + static const XMVECTORF32 FixaddY16W16 = { { { 0, 0, 32768.0f * 65536.0f, 32768.0f * 65536.0f } } }; + // Splat the color in all four entries (x,z,y,w) + __m128d vIntd = _mm_load1_pd(reinterpret_cast(&pSource->x)); + // Shift x&0ffff,z&0xffff,y&0xffff0000,w&0xffff0000 + __m128 vTemp = _mm_and_ps(_mm_castpd_ps(vIntd), g_XMMaskX16Y16Z16W16); + // y and w are signed! Flip the bits to convert the order to unsigned + vTemp = _mm_xor_ps(vTemp, g_XMFlipZW); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // y and w + 0x8000 to complete the conversion + vTemp = _mm_add_ps(vTemp, FixaddY16W16); + // Fix y and w because they are 65536 too large + vTemp = _mm_mul_ps(vTemp, FixupY16W16); + // Very important! The entries are x,z,y,w, flip it to x,y,z,w + return XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 1, 2, 0)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUShort4(const XMUSHORT4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x), + static_cast(pSource->y), + static_cast(pSource->z), + static_cast(pSource->w) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint16x4_t vInt = vld1_u16(reinterpret_cast(pSource)); + uint32x4_t V = vmovl_u16(vInt); + return vcvtq_f32_u32(V); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 FixaddY16W16 = { { { 0, 0, 32768.0f, 32768.0f } } }; + // Splat the color in all four entries (x,z,y,w) + __m128d vIntd = _mm_load1_pd(reinterpret_cast(&pSource->x)); + // Shift x&0ffff,z&0xffff,y&0xffff0000,w&0xffff0000 + __m128 vTemp = _mm_and_ps(_mm_castpd_ps(vIntd), g_XMMaskX16Y16Z16W16); + // y and w are signed! Flip the bits to convert the order to unsigned + vTemp = _mm_xor_ps(vTemp, g_XMFlipZW); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // Fix y and w because they are 65536 too large + vTemp = _mm_mul_ps(vTemp, g_XMFixupY16W16); + // y and w + 0x8000 to complete the conversion + vTemp = _mm_add_ps(vTemp, FixaddY16W16); + // Very important! The entries are x,z,y,w, flip it to x,y,z,w + return XM_PERMUTE_PS(vTemp, _MM_SHUFFLE(3, 1, 2, 0)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadXDecN4(const XMXDECN4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + static const uint32_t SignExtend[] = { 0x00000000, 0xFFFFFC00 }; + + uint32_t ElementX = pSource->v & 0x3FF; + uint32_t ElementY = (pSource->v >> 10) & 0x3FF; + uint32_t ElementZ = (pSource->v >> 20) & 0x3FF; + + XMVECTORF32 vResult = { { { + (ElementX == 0x200) ? -1.f : (static_cast(static_cast(ElementX | SignExtend[ElementX >> 9])) / 511.0f), + (ElementY == 0x200) ? -1.f : (static_cast(static_cast(ElementY | SignExtend[ElementY >> 9])) / 511.0f), + (ElementZ == 0x200) ? -1.f : (static_cast(static_cast(ElementZ | SignExtend[ElementZ >> 9])) / 511.0f), + static_cast(pSource->v >> 30) / 3.0f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vInt = vld1q_dup_u32(reinterpret_cast(pSource)); + vInt = vandq_u32(vInt, g_XMMaskA2B10G10R10); + vInt = veorq_u32(vInt, g_XMFlipA2B10G10R10); + float32x4_t R = vcvtq_f32_s32(vreinterpretq_s32_u32(vInt)); + R = vaddq_f32(R, g_XMFixAA2B10G10R10); + R = vmulq_f32(R, g_XMNormalizeA2B10G10R10); + return vmaxq_f32(R, vdupq_n_f32(-1.0f)); +#elif defined(_XM_SSE_INTRINSICS_) + // Splat the color in all four entries + __m128 vTemp = _mm_load_ps1(reinterpret_cast(&pSource->v)); + // Shift R&0xFF0000, G&0xFF00, B&0xFF, A&0xFF000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskA2B10G10R10); + // a is unsigned! Flip the bit to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMFlipA2B10G10R10); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // RGB + 0, A + 0x80000000.f to undo the signed order. + vTemp = _mm_add_ps(vTemp, g_XMFixAA2B10G10R10); + // Convert 0-255 to 0.0f-1.0f + vTemp = _mm_mul_ps(vTemp, g_XMNormalizeA2B10G10R10); + // Clamp result (for case of -512) + return _mm_max_ps(vTemp, g_XMNegativeOne); +#endif +} + +//------------------------------------------------------------------------------ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +// C4996: ignore deprecation warning +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadXDec4(const XMXDEC4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + static const uint32_t SignExtend[] = { 0x00000000, 0xFFFFFC00 }; + + uint32_t ElementX = pSource->v & 0x3FF; + uint32_t ElementY = (pSource->v >> 10) & 0x3FF; + uint32_t ElementZ = (pSource->v >> 20) & 0x3FF; + + XMVECTORF32 vResult = { { { + static_cast(static_cast(ElementX | SignExtend[ElementX >> 9])), + static_cast(static_cast(ElementY | SignExtend[ElementY >> 9])), + static_cast(static_cast(ElementZ | SignExtend[ElementZ >> 9])), + static_cast(pSource->v >> 30) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORU32 XDec4Xor = { { { 0x200, 0x200 << 10, 0x200 << 20, 0x80000000 } } }; + static const XMVECTORF32 XDec4Add = { { { -512.0f, -512.0f * 1024.0f, -512.0f * 1024.0f * 1024.0f, 32768 * 65536.0f } } }; + uint32x4_t vInt = vld1q_dup_u32(reinterpret_cast(pSource)); + vInt = vandq_u32(vInt, g_XMMaskDec4); + vInt = veorq_u32(vInt, XDec4Xor); + float32x4_t R = vcvtq_f32_s32(vreinterpretq_s32_u32(vInt)); + R = vaddq_f32(R, XDec4Add); + return vmulq_f32(R, g_XMMulDec4); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORU32 XDec4Xor = { { { 0x200, 0x200 << 10, 0x200 << 20, 0x80000000 } } }; + static const XMVECTORF32 XDec4Add = { { { -512.0f, -512.0f * 1024.0f, -512.0f * 1024.0f * 1024.0f, 32768 * 65536.0f } } }; + // Splat the color in all four entries + XMVECTOR vTemp = _mm_load_ps1(reinterpret_cast(&pSource->v)); + // Shift R&0xFF0000, G&0xFF00, B&0xFF, A&0xFF000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskDec4); + // a is unsigned! Flip the bit to convert the order to signed + vTemp = _mm_xor_ps(vTemp, XDec4Xor); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // RGB + 0, A + 0x80000000.f to undo the signed order. + vTemp = _mm_add_ps(vTemp, XDec4Add); + // Convert 0-255 to 0.0f-1.0f + vTemp = _mm_mul_ps(vTemp, g_XMMulDec4); + return vTemp; +#endif +} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUDecN4(const XMUDECN4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + + uint32_t ElementX = pSource->v & 0x3FF; + uint32_t ElementY = (pSource->v >> 10) & 0x3FF; + uint32_t ElementZ = (pSource->v >> 20) & 0x3FF; + + XMVECTORF32 vResult = { { { + static_cast(ElementX) / 1023.0f, + static_cast(ElementY) / 1023.0f, + static_cast(ElementZ) / 1023.0f, + static_cast(pSource->v >> 30) / 3.0f + } } }; + return vResult.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 UDecN4Mul = { { { 1.0f / 1023.0f, 1.0f / (1023.0f * 1024.0f), 1.0f / (1023.0f * 1024.0f * 1024.0f), 1.0f / (3.0f * 1024.0f * 1024.0f * 1024.0f) } } }; + uint32x4_t vInt = vld1q_dup_u32(reinterpret_cast(pSource)); + vInt = vandq_u32(vInt, g_XMMaskDec4); + float32x4_t R = vcvtq_f32_u32(vInt); + return vmulq_f32(R, UDecN4Mul); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 UDecN4Mul = { { { 1.0f / 1023.0f, 1.0f / (1023.0f * 1024.0f), 1.0f / (1023.0f * 1024.0f * 1024.0f), 1.0f / (3.0f * 1024.0f * 1024.0f * 1024.0f) } } }; + // Splat the color in all four entries + XMVECTOR vTemp = _mm_load_ps1(reinterpret_cast(&pSource->v)); + // Shift R&0xFF0000, G&0xFF00, B&0xFF, A&0xFF000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskDec4); + // a is unsigned! Flip the bit to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMFlipW); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // RGB + 0, A + 0x80000000.f to undo the signed order. + vTemp = _mm_add_ps(vTemp, g_XMAddUDec4); + // Convert 0-255 to 0.0f-1.0f + vTemp = _mm_mul_ps(vTemp, UDecN4Mul); + return vTemp; +#endif +} + + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUDecN4_XR(const XMUDECN4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + + int32_t ElementX = pSource->v & 0x3FF; + int32_t ElementY = (pSource->v >> 10) & 0x3FF; + int32_t ElementZ = (pSource->v >> 20) & 0x3FF; + + XMVECTORF32 vResult = { { { + static_cast(ElementX - 0x180) / 510.0f, + static_cast(ElementY - 0x180) / 510.0f, + static_cast(ElementZ - 0x180) / 510.0f, + static_cast(pSource->v >> 30) / 3.0f + } } }; + + return vResult.v; + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 XRMul = { { { 1.0f / 510.0f, 1.0f / (510.0f * 1024.0f), 1.0f / (510.0f * 1024.0f * 1024.0f), 1.0f / (3.0f * 1024.0f * 1024.0f * 1024.0f) } } }; + static const XMVECTORI32 XRBias = { { { 0x180, 0x180 * 1024, 0x180 * 1024 * 1024, 0 } } }; + uint32x4_t vInt = vld1q_dup_u32(reinterpret_cast(pSource)); + vInt = vandq_u32(vInt, g_XMMaskDec4); + int32x4_t vTemp = vsubq_s32(vreinterpretq_s32_u32(vInt), XRBias); + vTemp = veorq_s32(vTemp, g_XMFlipW); + float32x4_t R = vcvtq_f32_s32(vTemp); + R = vaddq_f32(R, g_XMAddUDec4); + return vmulq_f32(R, XRMul); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 XRMul = { { { 1.0f / 510.0f, 1.0f / (510.0f * 1024.0f), 1.0f / (510.0f * 1024.0f * 1024.0f), 1.0f / (3.0f * 1024.0f * 1024.0f * 1024.0f) } } }; + static const XMVECTORI32 XRBias = { { { 0x180, 0x180 * 1024, 0x180 * 1024 * 1024, 0 } } }; + // Splat the color in all four entries + XMVECTOR vTemp = _mm_load_ps1(reinterpret_cast(&pSource->v)); + // Mask channels + vTemp = _mm_and_ps(vTemp, g_XMMaskDec4); + // Subtract bias + vTemp = _mm_castsi128_ps(_mm_sub_epi32(_mm_castps_si128(vTemp), XRBias)); + // a is unsigned! Flip the bit to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMFlipW); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // RGB + 0, A + 0x80000000.f to undo the signed order. + vTemp = _mm_add_ps(vTemp, g_XMAddUDec4); + // Convert to 0.0f-1.0f + return _mm_mul_ps(vTemp, XRMul); +#endif +} + + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUDec4(const XMUDEC4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + uint32_t ElementX = pSource->v & 0x3FF; + uint32_t ElementY = (pSource->v >> 10) & 0x3FF; + uint32_t ElementZ = (pSource->v >> 20) & 0x3FF; + + XMVECTORF32 vResult = { { { + static_cast(ElementX), + static_cast(ElementY), + static_cast(ElementZ), + static_cast(pSource->v >> 30) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vInt = vld1q_dup_u32(reinterpret_cast(pSource)); + vInt = vandq_u32(vInt, g_XMMaskDec4); + float32x4_t R = vcvtq_f32_u32(vInt); + return vmulq_f32(R, g_XMMulDec4); +#elif defined(_XM_SSE_INTRINSICS_) + // Splat the color in all four entries + XMVECTOR vTemp = _mm_load_ps1(reinterpret_cast(&pSource->v)); + // Shift R&0xFF0000, G&0xFF00, B&0xFF, A&0xFF000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskDec4); + // a is unsigned! Flip the bit to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMFlipW); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // RGB + 0, A + 0x80000000.f to undo the signed order. + vTemp = _mm_add_ps(vTemp, g_XMAddUDec4); + // Convert 0-255 to 0.0f-1.0f + vTemp = _mm_mul_ps(vTemp, g_XMMulDec4); + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +// C4996: ignore deprecation warning +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadDecN4(const XMDECN4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + static const uint32_t SignExtend[] = { 0x00000000, 0xFFFFFC00 }; + static const uint32_t SignExtendW[] = { 0x00000000, 0xFFFFFFFC }; + + uint32_t ElementX = pSource->v & 0x3FF; + uint32_t ElementY = (pSource->v >> 10) & 0x3FF; + uint32_t ElementZ = (pSource->v >> 20) & 0x3FF; + uint32_t ElementW = pSource->v >> 30; + + XMVECTORF32 vResult = { { { + (ElementX == 0x200) ? -1.f : (static_cast(static_cast(ElementX | SignExtend[ElementX >> 9])) / 511.0f), + (ElementY == 0x200) ? -1.f : (static_cast(static_cast(ElementY | SignExtend[ElementY >> 9])) / 511.0f), + (ElementZ == 0x200) ? -1.f : (static_cast(static_cast(ElementZ | SignExtend[ElementZ >> 9])) / 511.0f), + (ElementW == 0x2) ? -1.f : static_cast(static_cast(ElementW | SignExtendW[(ElementW >> 1) & 1])) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 DecN4Mul = { { { 1.0f / 511.0f, 1.0f / (511.0f * 1024.0f), 1.0f / (511.0f * 1024.0f * 1024.0f), 1.0f / (1024.0f * 1024.0f * 1024.0f) } } }; + uint32x4_t vInt = vld1q_dup_u32(reinterpret_cast(pSource)); + vInt = vandq_u32(vInt, g_XMMaskDec4); + vInt = veorq_u32(vInt, g_XMXorDec4); + float32x4_t R = vcvtq_f32_s32(vreinterpretq_s32_u32(vInt)); + R = vaddq_f32(R, g_XMAddDec4); + R = vmulq_f32(R, DecN4Mul); + return vmaxq_f32(R, vdupq_n_f32(-1.0f)); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 DecN4Mul = { { { 1.0f / 511.0f, 1.0f / (511.0f * 1024.0f), 1.0f / (511.0f * 1024.0f * 1024.0f), 1.0f / (1024.0f * 1024.0f * 1024.0f) } } }; + // Splat the color in all four entries + XMVECTOR vTemp = _mm_load_ps1(reinterpret_cast(&pSource->v)); + // Shift R&0xFF0000, G&0xFF00, B&0xFF, A&0xFF000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskDec4); + // a is unsigned! Flip the bit to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMXorDec4); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // RGB + 0, A + 0x80000000.f to undo the signed order. + vTemp = _mm_add_ps(vTemp, g_XMAddDec4); + // Convert 0-255 to 0.0f-1.0f + vTemp = _mm_mul_ps(vTemp, DecN4Mul); + // Clamp result (for case of -512/-1) + return _mm_max_ps(vTemp, g_XMNegativeOne); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadDec4(const XMDEC4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + static const uint32_t SignExtend[] = { 0x00000000, 0xFFFFFC00 }; + static const uint32_t SignExtendW[] = { 0x00000000, 0xFFFFFFFC }; + + uint32_t ElementX = pSource->v & 0x3FF; + uint32_t ElementY = (pSource->v >> 10) & 0x3FF; + uint32_t ElementZ = (pSource->v >> 20) & 0x3FF; + uint32_t ElementW = pSource->v >> 30; + + XMVECTORF32 vResult = { { { + static_cast(static_cast(ElementX | SignExtend[ElementX >> 9])), + static_cast(static_cast(ElementY | SignExtend[ElementY >> 9])), + static_cast(static_cast(ElementZ | SignExtend[ElementZ >> 9])), + static_cast(static_cast(ElementW | SignExtendW[ElementW >> 1])) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x4_t vInt = vld1q_dup_u32(reinterpret_cast(pSource)); + vInt = vandq_u32(vInt, g_XMMaskDec4); + vInt = veorq_u32(vInt, g_XMXorDec4); + float32x4_t R = vcvtq_f32_s32(vreinterpretq_s32_u32(vInt)); + R = vaddq_f32(R, g_XMAddDec4); + return vmulq_f32(R, g_XMMulDec4); +#elif defined(_XM_SSE_INTRINSICS_) + // Splat the color in all four entries + XMVECTOR vTemp = _mm_load_ps1(reinterpret_cast(&pSource->v)); + // Shift R&0xFF0000, G&0xFF00, B&0xFF, A&0xFF000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskDec4); + // a is unsigned! Flip the bit to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMXorDec4); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // RGB + 0, A + 0x80000000.f to undo the signed order. + vTemp = _mm_add_ps(vTemp, g_XMAddDec4); + // Convert 0-255 to 0.0f-1.0f + vTemp = _mm_mul_ps(vTemp, g_XMMulDec4); + return vTemp; +#endif +} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUByteN4(const XMUBYTEN4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x) / 255.0f, + static_cast(pSource->y) / 255.0f, + static_cast(pSource->z) / 255.0f, + static_cast(pSource->w) / 255.0f + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vInt8 = vld1_dup_u32(reinterpret_cast(pSource)); + uint16x8_t vInt16 = vmovl_u8(vreinterpret_u8_u32(vInt8)); + uint32x4_t vInt = vmovl_u16(vget_low_u16(vInt16)); + float32x4_t R = vcvtq_f32_u32(vInt); + return vmulq_n_f32(R, 1.0f / 255.0f); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 LoadUByteN4Mul = { { { 1.0f / 255.0f, 1.0f / (255.0f * 256.0f), 1.0f / (255.0f * 65536.0f), 1.0f / (255.0f * 65536.0f * 256.0f) } } }; + // Splat the color in all four entries (x,z,y,w) + XMVECTOR vTemp = _mm_load1_ps(reinterpret_cast(&pSource->x)); + // Mask x&0ff,y&0xff00,z&0xff0000,w&0xff000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskByte4); + // w is signed! Flip the bits to convert the order to unsigned + vTemp = _mm_xor_ps(vTemp, g_XMFlipW); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // w + 0x80 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMAddUDec4); + // Fix y, z and w because they are too large + vTemp = _mm_mul_ps(vTemp, LoadUByteN4Mul); + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUByte4(const XMUBYTE4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x), + static_cast(pSource->y), + static_cast(pSource->z), + static_cast(pSource->w) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vInt8 = vld1_dup_u32(reinterpret_cast(pSource)); + uint16x8_t vInt16 = vmovl_u8(vreinterpret_u8_u32(vInt8)); + uint32x4_t vInt = vmovl_u16(vget_low_u16(vInt16)); + return vcvtq_f32_u32(vInt); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 LoadUByte4Mul = { { { 1.0f, 1.0f / 256.0f, 1.0f / 65536.0f, 1.0f / (65536.0f * 256.0f) } } }; + // Splat the color in all four entries (x,z,y,w) + XMVECTOR vTemp = _mm_load1_ps(reinterpret_cast(&pSource->x)); + // Mask x&0ff,y&0xff00,z&0xff0000,w&0xff000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskByte4); + // w is signed! Flip the bits to convert the order to unsigned + vTemp = _mm_xor_ps(vTemp, g_XMFlipW); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // w + 0x80 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMAddUDec4); + // Fix y, z and w because they are too large + vTemp = _mm_mul_ps(vTemp, LoadUByte4Mul); + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadByteN4(const XMBYTEN4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + (pSource->x == -128) ? -1.f : (static_cast(pSource->x) / 127.0f), + (pSource->y == -128) ? -1.f : (static_cast(pSource->y) / 127.0f), + (pSource->z == -128) ? -1.f : (static_cast(pSource->z) / 127.0f), + (pSource->w == -128) ? -1.f : (static_cast(pSource->w) / 127.0f) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vInt8 = vld1_dup_u32(reinterpret_cast(pSource)); + int16x8_t vInt16 = vmovl_s8(vreinterpret_s8_u32(vInt8)); + int32x4_t vInt = vmovl_s16(vget_low_s16(vInt16)); + float32x4_t R = vcvtq_f32_s32(vInt); + R = vmulq_n_f32(R, 1.0f / 127.0f); + return vmaxq_f32(R, vdupq_n_f32(-1.f)); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 LoadByteN4Mul = { { { 1.0f / 127.0f, 1.0f / (127.0f * 256.0f), 1.0f / (127.0f * 65536.0f), 1.0f / (127.0f * 65536.0f * 256.0f) } } }; + // Splat the color in all four entries (x,z,y,w) + XMVECTOR vTemp = _mm_load1_ps(reinterpret_cast(&pSource->x)); + // Mask x&0ff,y&0xff00,z&0xff0000,w&0xff000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskByte4); + // x,y and z are unsigned! Flip the bits to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMXorByte4); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // x, y and z - 0x80 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMAddByte4); + // Fix y, z and w because they are too large + vTemp = _mm_mul_ps(vTemp, LoadByteN4Mul); + // Clamp result (for case of -128) + return _mm_max_ps(vTemp, g_XMNegativeOne); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadByte4(const XMBYTE4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + static_cast(pSource->x), + static_cast(pSource->y), + static_cast(pSource->z), + static_cast(pSource->w) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + uint32x2_t vInt8 = vld1_dup_u32(reinterpret_cast(pSource)); + int16x8_t vInt16 = vmovl_s8(vreinterpret_s8_u32(vInt8)); + int32x4_t vInt = vmovl_s16(vget_low_s16(vInt16)); + return vcvtq_f32_s32(vInt); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 LoadByte4Mul = { { { 1.0f, 1.0f / 256.0f, 1.0f / 65536.0f, 1.0f / (65536.0f * 256.0f) } } }; + // Splat the color in all four entries (x,z,y,w) + XMVECTOR vTemp = _mm_load1_ps(reinterpret_cast(&pSource->x)); + // Mask x&0ff,y&0xff00,z&0xff0000,w&0xff000000 + vTemp = _mm_and_ps(vTemp, g_XMMaskByte4); + // x,y and z are unsigned! Flip the bits to convert the order to signed + vTemp = _mm_xor_ps(vTemp, g_XMXorByte4); + // Convert to floating point numbers + vTemp = _mm_cvtepi32_ps(_mm_castps_si128(vTemp)); + // x, y and z - 0x80 to complete the conversion + vTemp = _mm_add_ps(vTemp, g_XMAddByte4); + // Fix y, z and w because they are too large + vTemp = _mm_mul_ps(vTemp, LoadByte4Mul); + return vTemp; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadUNibble4(const XMUNIBBLE4* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + float(pSource->v & 0xF), + float((pSource->v >> 4) & 0xF), + float((pSource->v >> 8) & 0xF), + float((pSource->v >> 12) & 0xF) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORI32 UNibble4And = { { { 0xF, 0xF0, 0xF00, 0xF000 } } }; + static const XMVECTORF32 UNibble4Mul = { { { 1.0f, 1.0f / 16.f, 1.0f / 256.f, 1.0f / 4096.f } } }; + uint16x4_t vInt16 = vld1_dup_u16(reinterpret_cast(pSource)); + uint32x4_t vInt = vmovl_u16(vInt16); + vInt = vandq_u32(vInt, UNibble4And); + float32x4_t R = vcvtq_f32_u32(vInt); + return vmulq_f32(R, UNibble4Mul); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORI32 UNibble4And = { { { 0xF, 0xF0, 0xF00, 0xF000 } } }; + static const XMVECTORF32 UNibble4Mul = { { { 1.0f, 1.0f / 16.f, 1.0f / 256.f, 1.0f / 4096.f } } }; + // Get the 16 bit value and splat it + __m128i vInt = XM_LOADU_SI16(&pSource->v); + XMVECTOR vResult = XM_PERMUTE_PS(_mm_castsi128_ps(vInt), _MM_SHUFFLE(0, 0, 0, 0)); + // Mask off x, y and z + vResult = _mm_and_ps(vResult, UNibble4And); + // Convert to float + vResult = _mm_cvtepi32_ps(_mm_castps_si128(vResult)); + // Normalize x, y, and z + vResult = _mm_mul_ps(vResult, UNibble4Mul); + return vResult; +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMVECTOR XM_CALLCONV XMLoadU555(const XMU555* pSource) noexcept +{ + assert(pSource); +#if defined(_XM_NO_INTRINSICS_) + XMVECTORF32 vResult = { { { + float(pSource->v & 0x1F), + float((pSource->v >> 5) & 0x1F), + float((pSource->v >> 10) & 0x1F), + float((pSource->v >> 15) & 0x1) + } } }; + return vResult.v; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORI32 U555And = { { { 0x1F, 0x1F << 5, 0x1F << 10, 0x8000 } } }; + static const XMVECTORF32 U555Mul = { { { 1.0f, 1.0f / 32.f, 1.0f / 1024.f, 1.0f / 32768.f } } }; + uint16x4_t vInt16 = vld1_dup_u16(reinterpret_cast(pSource)); + uint32x4_t vInt = vmovl_u16(vInt16); + vInt = vandq_u32(vInt, U555And); + float32x4_t R = vcvtq_f32_u32(vInt); + return vmulq_f32(R, U555Mul); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORI32 U555And = { { { 0x1F, 0x1F << 5, 0x1F << 10, 0x8000 } } }; + static const XMVECTORF32 U555Mul = { { { 1.0f, 1.0f / 32.f, 1.0f / 1024.f, 1.0f / 32768.f } } }; + // Get the 16bit value and splat it + __m128i vInt = XM_LOADU_SI16(&pSource->v); + XMVECTOR vResult = XM_PERMUTE_PS(_mm_castsi128_ps(vInt), _MM_SHUFFLE(0, 0, 0, 0)); + // Mask off x, y and z + vResult = _mm_and_ps(vResult, U555And); + // Convert to float + vResult = _mm_cvtepi32_ps(_mm_castps_si128(vResult)); + // Normalize x, y, and z + vResult = _mm_mul_ps(vResult, U555Mul); + return vResult; +#endif +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + +/**************************************************************************** + * + * Vector and matrix store operations + * + ****************************************************************************/ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreColor +( + XMCOLOR* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorSaturate(V); + N = XMVectorMultiply(N, g_UByteMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->c = (static_cast(tmp.w) << 24) | + (static_cast(tmp.x) << 16) | + (static_cast(tmp.y) << 8) | + static_cast(tmp.z); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(0)); + R = vminq_f32(R, vdupq_n_f32(1.0f)); + R = vmulq_n_f32(R, 255.0f); + R = XMVectorRound(R); + uint32x4_t vInt32 = vcvtq_u32_f32(R); + uint16x4_t vInt16 = vqmovn_u32(vInt32); + uint8x8_t vInt8 = vqmovn_u16(vcombine_u16(vInt16, vInt16)); + uint32_t rgba = vget_lane_u32(vreinterpret_u32_u8(vInt8), 0); + pDestination->c = (rgba & 0xFF00FF00) | ((rgba >> 16) & 0xFF) | ((rgba << 16) & 0xFF0000); +#elif defined(_XM_SSE_INTRINSICS_) + // Set <0 to 0 + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + // Set>1 to 1 + vResult = _mm_min_ps(vResult, g_XMOne); + // Convert to 0-255 + vResult = _mm_mul_ps(vResult, g_UByteMax); + // Shuffle RGBA to ARGB + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(3, 0, 1, 2)); + // Convert to int + __m128i vInt = _mm_cvtps_epi32(vResult); + // Mash to shorts + vInt = _mm_packs_epi32(vInt, vInt); + // Mash to bytes + vInt = _mm_packus_epi16(vInt, vInt); + // Store the color + _mm_store_ss(reinterpret_cast(&pDestination->c), _mm_castsi128_ps(vInt)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreHalf2 +( + XMHALF2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_F16C_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + __m128i V1 = _mm_cvtps_ph(V, _MM_FROUND_TO_NEAREST_INT); + _mm_store_ss(reinterpret_cast(pDestination), _mm_castsi128_ps(V1)); +#else + pDestination->x = XMConvertFloatToHalf(XMVectorGetX(V)); + pDestination->y = XMConvertFloatToHalf(XMVectorGetY(V)); +#endif // !_XM_F16C_INTRINSICS_ +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreShortN2 +( + XMSHORTN2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, g_XMNegativeOne.v, g_XMOne.v); + N = XMVectorMultiply(N, g_ShortMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(-1.f)); + R = vminq_f32(R, vdupq_n_f32(1.0f)); + R = vmulq_n_f32(R, 32767.0f); + int32x4_t vInt32 = vcvtq_s32_f32(R); + int16x4_t vInt16 = vqmovn_s32(vInt32); + vst1_lane_u32(&pDestination->v, vreinterpret_u32_s16(vInt16), 0); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = _mm_max_ps(V, g_XMNegativeOne); + vResult = _mm_min_ps(vResult, g_XMOne); + vResult = _mm_mul_ps(vResult, g_ShortMax); + __m128i vResulti = _mm_cvtps_epi32(vResult); + vResulti = _mm_packs_epi32(vResulti, vResulti); + _mm_store_ss(reinterpret_cast(&pDestination->x), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreShort2 +( + XMSHORT2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, g_ShortMin, g_ShortMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(-32767.f)); + R = vminq_f32(R, vdupq_n_f32(32767.0f)); + int32x4_t vInt32 = vcvtq_s32_f32(R); + int16x4_t vInt16 = vqmovn_s32(vInt32); + vst1_lane_u32(&pDestination->v, vreinterpret_u32_s16(vInt16), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Bounds check + XMVECTOR vResult = _mm_max_ps(V, g_ShortMin); + vResult = _mm_min_ps(vResult, g_ShortMax); + // Convert to int with rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // Pack the ints into shorts + vInt = _mm_packs_epi32(vInt, vInt); + _mm_store_ss(reinterpret_cast(&pDestination->x), _mm_castsi128_ps(vInt)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUShortN2 +( + XMUSHORTN2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorSaturate(V); + N = XMVectorMultiplyAdd(N, g_UShortMax, g_XMOneHalf.v); + N = XMVectorTruncate(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(0.f)); + R = vminq_f32(R, vdupq_n_f32(1.0f)); + R = vmulq_n_f32(R, 65535.0f); + R = vaddq_f32(R, g_XMOneHalf); + uint32x4_t vInt32 = vcvtq_u32_f32(R); + uint16x4_t vInt16 = vqmovn_u32(vInt32); + vst1_lane_u32(&pDestination->v, vreinterpret_u32_u16(vInt16), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Bounds check + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, g_XMOne); + vResult = _mm_mul_ps(vResult, g_UShortMax); + vResult = _mm_add_ps(vResult, g_XMOneHalf); + // Convert to int + __m128i vInt = _mm_cvttps_epi32(vResult); + // Since the SSE pack instruction clamps using signed rules, + // manually extract the values to store them to memory + pDestination->x = static_cast(_mm_extract_epi16(vInt, 0)); + pDestination->y = static_cast(_mm_extract_epi16(vInt, 2)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUShort2 +( + XMUSHORT2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, XMVectorZero(), g_UShortMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(0.f)); + R = vminq_f32(R, vdupq_n_f32(65535.0f)); + uint32x4_t vInt32 = vcvtq_u32_f32(R); + uint16x4_t vInt16 = vqmovn_u32(vInt32); + vst1_lane_u32(&pDestination->v, vreinterpret_u32_u16(vInt16), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Bounds check + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, g_UShortMax); + // Convert to int with rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // Since the SSE pack instruction clamps using signed rules, + // manually extract the values to store them to memory + pDestination->x = static_cast(_mm_extract_epi16(vInt, 0)); + pDestination->y = static_cast(_mm_extract_epi16(vInt, 2)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreByteN2 +( + XMBYTEN2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, g_XMNegativeOne.v, g_XMOne.v); + N = XMVectorMultiply(N, g_ByteMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(-1.f)); + R = vminq_f32(R, vdupq_n_f32(1.0f)); + R = vmulq_n_f32(R, 127.0f); + int32x4_t vInt32 = vcvtq_s32_f32(R); + int16x4_t vInt16 = vqmovn_s32(vInt32); + int8x8_t vInt8 = vqmovn_s16(vcombine_s16(vInt16, vInt16)); + vst1_lane_u16(reinterpret_cast(pDestination), vreinterpret_u16_s8(vInt8), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_XMNegativeOne); + vResult = _mm_min_ps(vResult, g_XMOne); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, g_ByteMax); + // Convert to int by rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // No SSE operations will write to 16-bit values, so we have to extract them manually + auto x = static_cast(_mm_extract_epi16(vInt, 0)); + auto y = static_cast(_mm_extract_epi16(vInt, 2)); + pDestination->v = static_cast(((static_cast(y) & 0xFF) << 8) | (static_cast(x) & 0xFF)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreByte2 +( + XMBYTE2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, g_ByteMin, g_ByteMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(-127.f)); + R = vminq_f32(R, vdupq_n_f32(127.0f)); + int32x4_t vInt32 = vcvtq_s32_f32(R); + int16x4_t vInt16 = vqmovn_s32(vInt32); + int8x8_t vInt8 = vqmovn_s16(vcombine_s16(vInt16, vInt16)); + vst1_lane_u16(reinterpret_cast(pDestination), vreinterpret_u16_s8(vInt8), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_ByteMin); + vResult = _mm_min_ps(vResult, g_ByteMax); + // Convert to int by rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // No SSE operations will write to 16-bit values, so we have to extract them manually + auto x = static_cast(_mm_extract_epi16(vInt, 0)); + auto y = static_cast(_mm_extract_epi16(vInt, 2)); + pDestination->v = static_cast(((static_cast(y) & 0xFF) << 8) | (static_cast(x) & 0xFF)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUByteN2 +( + XMUBYTEN2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorSaturate(V); + N = XMVectorMultiplyAdd(N, g_UByteMax, g_XMOneHalf.v); + N = XMVectorTruncate(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(0.f)); + R = vminq_f32(R, vdupq_n_f32(1.0f)); + R = vmulq_n_f32(R, 255.0f); + R = vaddq_f32(R, g_XMOneHalf); + uint32x4_t vInt32 = vcvtq_u32_f32(R); + uint16x4_t vInt16 = vqmovn_u32(vInt32); + uint8x8_t vInt8 = vqmovn_u16(vcombine_u16(vInt16, vInt16)); + vst1_lane_u16(reinterpret_cast(pDestination), vreinterpret_u16_u8(vInt8), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, g_XMOne); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, g_UByteMax); + vResult = _mm_add_ps(vResult, g_XMOneHalf); + // Convert to int + __m128i vInt = _mm_cvttps_epi32(vResult); + // No SSE operations will write to 16-bit values, so we have to extract them manually + auto x = static_cast(_mm_extract_epi16(vInt, 0)); + auto y = static_cast(_mm_extract_epi16(vInt, 2)); + pDestination->v = static_cast(((static_cast(y) & 0xFF) << 8) | (static_cast(x) & 0xFF)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUByte2 +( + XMUBYTE2* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, XMVectorZero(), g_UByteMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(0.f)); + R = vminq_f32(R, vdupq_n_f32(255.0f)); + uint32x4_t vInt32 = vcvtq_u32_f32(R); + uint16x4_t vInt16 = vqmovn_u32(vInt32); + uint8x8_t vInt8 = vqmovn_u16(vcombine_u16(vInt16, vInt16)); + vst1_lane_u16(reinterpret_cast(pDestination), vreinterpret_u16_u8(vInt8), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, g_UByteMax); + // Convert to int by rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // No SSE operations will write to 16-bit values, so we have to extract them manually + auto x = static_cast(_mm_extract_epi16(vInt, 0)); + auto y = static_cast(_mm_extract_epi16(vInt, 2)); + pDestination->v = static_cast(((static_cast(y) & 0xFF) << 8) | (static_cast(x) & 0xFF)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreU565 +( + XMU565* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + static const XMVECTORF32 Max = { { { 31.0f, 63.0f, 31.0f, 0.0f } } }; + +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR N = XMVectorClamp(V, XMVectorZero(), Max.v); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + ((static_cast(tmp.z) & 0x1F) << 11) + | ((static_cast(tmp.y) & 0x3F) << 5) + | ((static_cast(tmp.x) & 0x1F))); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.0f, 32.f, 32.f * 64.f, 0.f } } }; + static const XMVECTORU32 Mask = { { { 0x1F, 0x3F << 5, 0x1F << 11, 0 } } }; + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(0)); + vResult = vminq_f32(vResult, Max); + vResult = vmulq_f32(vResult, Scale); + uint32x4_t vResulti = vcvtq_u32_f32(vResult); + vResulti = vandq_u32(vResulti, Mask); + // Do a horizontal or of 4 entries + uint32x2_t vTemp = vget_low_u32(vResulti); + uint32x2_t vhi = vget_high_u32(vResulti); + vTemp = vorr_u32(vTemp, vhi); + vTemp = vpadd_u32(vTemp, vTemp); + vst1_lane_u16(&pDestination->v, vreinterpret_u16_u32(vTemp), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Bounds check + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, Max); + // Convert to int with rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // No SSE operations will write to 16-bit values, so we have to extract them manually + auto x = static_cast(_mm_extract_epi16(vInt, 0)); + auto y = static_cast(_mm_extract_epi16(vInt, 2)); + auto z = static_cast(_mm_extract_epi16(vInt, 4)); + pDestination->v = static_cast( + ((static_cast(z) & 0x1F) << 11) + | ((static_cast(y) & 0x3F) << 5) + | ((static_cast(x) & 0x1F))); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat3PK +( + XMFLOAT3PK* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + + XM_ALIGNED_DATA(16) uint32_t IValue[4]; + XMStoreFloat3A(reinterpret_cast(&IValue), V); + + uint32_t Result[3]; + + // X & Y Channels (5-bit exponent, 6-bit mantissa) + for (uint32_t j = 0; j < 2; ++j) + { + uint32_t Sign = IValue[j] & 0x80000000; + uint32_t I = IValue[j] & 0x7FFFFFFF; + + if ((I & 0x7F800000) == 0x7F800000) + { + // INF or NAN + Result[j] = 0x7C0U; + if ((I & 0x7FFFFF) != 0) + { + Result[j] = 0x7FFU; + } + else if (Sign) + { + // -INF is clamped to 0 since 3PK is positive only + Result[j] = 0; + } + } + else if (Sign || I < 0x35800000) + { + // 3PK is positive only, so clamp to zero + Result[j] = 0; + } + else if (I > 0x477E0000U) + { + // The number is too large to be represented as a float11, set to max + Result[j] = 0x7BFU; + } + else + { + if (I < 0x38800000U) + { + // The number is too small to be represented as a normalized float11 + // Convert it to a denormalized value. + uint32_t Shift = 113U - (I >> 23U); + I = (0x800000U | (I & 0x7FFFFFU)) >> Shift; + } + else + { + // Rebias the exponent to represent the value as a normalized float11 + I += 0xC8000000U; + } + + Result[j] = ((I + 0xFFFFU + ((I >> 17U) & 1U)) >> 17U) & 0x7ffU; + } + } + + // Z Channel (5-bit exponent, 5-bit mantissa) + uint32_t Sign = IValue[2] & 0x80000000; + uint32_t I = IValue[2] & 0x7FFFFFFF; + + if ((I & 0x7F800000) == 0x7F800000) + { + // INF or NAN + Result[2] = 0x3E0U; + if (I & 0x7FFFFF) + { + Result[2] = 0x3FFU; + } + else if (Sign || I < 0x36000000) + { + // -INF is clamped to 0 since 3PK is positive only + Result[2] = 0; + } + } + else if (Sign) + { + // 3PK is positive only, so clamp to zero + Result[2] = 0; + } + else if (I > 0x477C0000U) + { + // The number is too large to be represented as a float10, set to max + Result[2] = 0x3DFU; + } + else + { + if (I < 0x38800000U) + { + // The number is too small to be represented as a normalized float10 + // Convert it to a denormalized value. + uint32_t Shift = 113U - (I >> 23U); + I = (0x800000U | (I & 0x7FFFFFU)) >> Shift; + } + else + { + // Rebias the exponent to represent the value as a normalized float10 + I += 0xC8000000U; + } + + Result[2] = ((I + 0x1FFFFU + ((I >> 18U) & 1U)) >> 18U) & 0x3ffU; + } + + // Pack Result into memory + pDestination->v = (Result[0] & 0x7ff) + | ((Result[1] & 0x7ff) << 11) + | ((Result[2] & 0x3ff) << 22); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreFloat3SE +( + XMFLOAT3SE* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + + XMFLOAT3A tmp; + XMStoreFloat3A(&tmp, V); + + static constexpr float maxf9 = float(0x1FF << 7); + static constexpr float minf9 = float(1.f / (1 << 16)); + + float x = (tmp.x >= 0.f) ? ((tmp.x > maxf9) ? maxf9 : tmp.x) : 0.f; + float y = (tmp.y >= 0.f) ? ((tmp.y > maxf9) ? maxf9 : tmp.y) : 0.f; + float z = (tmp.z >= 0.f) ? ((tmp.z > maxf9) ? maxf9 : tmp.z) : 0.f; + + const float max_xy = (x > y) ? x : y; + const float max_xyz = (max_xy > z) ? max_xy : z; + + const float maxColor = (max_xyz > minf9) ? max_xyz : minf9; + + union { float f; int32_t i; } fi; + fi.f = maxColor; + fi.i += 0x00004000; // round up leaving 9 bits in fraction (including assumed 1) + + auto exp = static_cast(fi.i) >> 23; + pDestination->e = exp - 0x6f; + + fi.i = static_cast(0x83000000 - (exp << 23)); + float ScaleR = fi.f; + + pDestination->xm = static_cast(MathInternal::round_to_nearest(x * ScaleR)); + pDestination->ym = static_cast(MathInternal::round_to_nearest(y * ScaleR)); + pDestination->zm = static_cast(MathInternal::round_to_nearest(z * ScaleR)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreHalf4 +( + XMHALF4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_F16C_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + __m128i V1 = _mm_cvtps_ph(V, _MM_FROUND_TO_NEAREST_INT); + _mm_storel_epi64(reinterpret_cast<__m128i*>(pDestination), V1); +#else + XMFLOAT4A t; + XMStoreFloat4A(&t, V); + + pDestination->x = XMConvertFloatToHalf(t.x); + pDestination->y = XMConvertFloatToHalf(t.y); + pDestination->z = XMConvertFloatToHalf(t.z); + pDestination->w = XMConvertFloatToHalf(t.w); +#endif // !_XM_F16C_INTRINSICS_ +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreShortN4 +( + XMSHORTN4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, g_XMNegativeOne.v, g_XMOne.v); + N = XMVectorMultiply(N, g_ShortMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + pDestination->z = static_cast(tmp.z); + pDestination->w = static_cast(tmp.w); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(-1.f)); + vResult = vminq_f32(vResult, vdupq_n_f32(1.0f)); + vResult = vmulq_n_f32(vResult, 32767.0f); + int16x4_t vInt = vmovn_s32(vcvtq_s32_f32(vResult)); + vst1_s16(reinterpret_cast(pDestination), vInt); +#elif defined(_XM_SSE_INTRINSICS_) + XMVECTOR vResult = _mm_max_ps(V, g_XMNegativeOne); + vResult = _mm_min_ps(vResult, g_XMOne); + vResult = _mm_mul_ps(vResult, g_ShortMax); + __m128i vResulti = _mm_cvtps_epi32(vResult); + vResulti = _mm_packs_epi32(vResulti, vResulti); + _mm_store_sd(reinterpret_cast(&pDestination->x), _mm_castsi128_pd(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreShort4 +( + XMSHORT4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, g_ShortMin, g_ShortMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + pDestination->z = static_cast(tmp.z); + pDestination->w = static_cast(tmp.w); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vResult = vmaxq_f32(V, g_ShortMin); + vResult = vminq_f32(vResult, g_ShortMax); + int16x4_t vInt = vmovn_s32(vcvtq_s32_f32(vResult)); + vst1_s16(reinterpret_cast(pDestination), vInt); +#elif defined(_XM_SSE_INTRINSICS_) + // Bounds check + XMVECTOR vResult = _mm_max_ps(V, g_ShortMin); + vResult = _mm_min_ps(vResult, g_ShortMax); + // Convert to int with rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // Pack the ints into shorts + vInt = _mm_packs_epi32(vInt, vInt); + _mm_store_sd(reinterpret_cast(&pDestination->x), _mm_castsi128_pd(vInt)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUShortN4 +( + XMUSHORTN4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorSaturate(V); + N = XMVectorMultiplyAdd(N, g_UShortMax, g_XMOneHalf.v); + N = XMVectorTruncate(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + pDestination->z = static_cast(tmp.z); + pDestination->w = static_cast(tmp.w); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(0)); + vResult = vminq_f32(vResult, vdupq_n_f32(1.0f)); + vResult = vmulq_n_f32(vResult, 65535.0f); + vResult = vaddq_f32(vResult, g_XMOneHalf); + uint16x4_t vInt = vmovn_u32(vcvtq_u32_f32(vResult)); + vst1_u16(reinterpret_cast(pDestination), vInt); +#elif defined(_XM_SSE_INTRINSICS_) + // Bounds check + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, g_XMOne); + vResult = _mm_mul_ps(vResult, g_UShortMax); + vResult = _mm_add_ps(vResult, g_XMOneHalf); + // Convert to int + __m128i vInt = _mm_cvttps_epi32(vResult); + // Since the SSE pack instruction clamps using signed rules, + // manually extract the values to store them to memory + pDestination->x = static_cast(_mm_extract_epi16(vInt, 0)); + pDestination->y = static_cast(_mm_extract_epi16(vInt, 2)); + pDestination->z = static_cast(_mm_extract_epi16(vInt, 4)); + pDestination->w = static_cast(_mm_extract_epi16(vInt, 6)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUShort4 +( + XMUSHORT4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, XMVectorZero(), g_UShortMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + pDestination->z = static_cast(tmp.z); + pDestination->w = static_cast(tmp.w); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(0)); + vResult = vminq_f32(vResult, g_UShortMax); + uint16x4_t vInt = vmovn_u32(vcvtq_u32_f32(vResult)); + vst1_u16(reinterpret_cast(pDestination), vInt); +#elif defined(_XM_SSE_INTRINSICS_) + // Bounds check + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, g_UShortMax); + // Convert to int with rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // Since the SSE pack instruction clamps using signed rules, + // manually extract the values to store them to memory + pDestination->x = static_cast(_mm_extract_epi16(vInt, 0)); + pDestination->y = static_cast(_mm_extract_epi16(vInt, 2)); + pDestination->z = static_cast(_mm_extract_epi16(vInt, 4)); + pDestination->w = static_cast(_mm_extract_epi16(vInt, 6)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreXDecN4 +( + XMXDECN4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + static const XMVECTORF32 Min = { { { -1.0f, -1.0f, -1.0f, 0.0f } } }; + +#if defined(_XM_NO_INTRINSICS_) + + static const XMVECTORF32 Scale = { { { 511.0f, 511.0f, 511.0f, 3.0f } } }; + + XMVECTOR N = XMVectorClamp(V, Min.v, g_XMOne.v); + N = XMVectorMultiply(N, Scale.v); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + (static_cast(tmp.w) << 30) + | ((static_cast(tmp.z) & 0x3FF) << 20) + | ((static_cast(tmp.y) & 0x3FF) << 10) + | (static_cast(tmp.x) & 0x3FF)); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 511.0f, 511.0f * 1024.0f, 511.0f * 1048576.0f, 3.0f * 536870912.0f } } }; + static const XMVECTORI32 ScaleMask = { { { 0x3FF, 0x3FF << 10, 0x3FF << 20, 0x3 << 29 } } }; + float32x4_t vResult = vmaxq_f32(V, Min); + vResult = vminq_f32(vResult, vdupq_n_f32(1.0f)); + vResult = vmulq_f32(vResult, Scale); + int32x4_t vResulti = vcvtq_s32_f32(vResult); + vResulti = vandq_s32(vResulti, ScaleMask); + int32x4_t vResultw = vandq_s32(vResulti, g_XMMaskW); + vResulti = vaddq_s32(vResulti, vResultw); + // Do a horizontal or of all 4 entries + uint32x2_t vTemp = vget_low_u32(vreinterpretq_u32_s32(vResulti)); + uint32x2_t vhi = vget_high_u32(vreinterpretq_u32_s32(vResulti)); + vTemp = vorr_u32(vTemp, vhi); + vTemp = vpadd_u32(vTemp, vTemp); + vst1_lane_u32(&pDestination->v, vTemp, 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 511.0f, 511.0f * 1024.0f, 511.0f * 1048576.0f, 3.0f * 536870912.0f } } }; + static const XMVECTORI32 ScaleMask = { { { 0x3FF, 0x3FF << 10, 0x3FF << 20, 0x3 << 29 } } }; + XMVECTOR vResult = _mm_max_ps(V, Min); + vResult = _mm_min_ps(vResult, g_XMOne); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, Scale); + // Convert to int (W is unsigned) + __m128i vResulti = _mm_cvtps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, ScaleMask); + // To fix W, add itself to shift it up to <<30 instead of <<29 + __m128i vResultw = _mm_and_si128(vResulti, g_XMMaskW); + vResulti = _mm_add_epi32(vResulti, vResultw); + // Do a horizontal or of all 4 entries + vResult = XM_PERMUTE_PS(_mm_castsi128_ps(vResulti), _MM_SHUFFLE(0, 3, 2, 1)); + vResulti = _mm_or_si128(vResulti, _mm_castps_si128(vResult)); + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(0, 3, 2, 1)); + vResulti = _mm_or_si128(vResulti, _mm_castps_si128(vResult)); + vResult = XM_PERMUTE_PS(vResult, _MM_SHUFFLE(0, 3, 2, 1)); + vResulti = _mm_or_si128(vResulti, _mm_castps_si128(vResult)); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +// C4996: ignore deprecation warning +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreXDec4 +( + XMXDEC4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + static const XMVECTORF32 MinXDec4 = { { { -511.0f, -511.0f, -511.0f, 0.0f } } }; + static const XMVECTORF32 MaxXDec4 = { { { 511.0f, 511.0f, 511.0f, 3.0f } } }; + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, MinXDec4, MaxXDec4); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + (static_cast(tmp.w) << 30) + | ((static_cast(tmp.z) & 0x3FF) << 20) + | ((static_cast(tmp.y) & 0x3FF) << 10) + | ((static_cast(tmp.x) & 0x3FF))); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 ScaleXDec4 = { { { 1.0f, 1024.0f / 2.0f, 1024.0f * 1024.0f, 1024.0f * 1024.0f * 1024.0f / 2.0f } } }; + static const XMVECTORI32 MaskXDec4 = { { { 0x3FF, 0x3FF << (10 - 1), 0x3FF << 20, 0x3 << (30 - 1) } } }; + float32x4_t vResult = vmaxq_f32(V, MinXDec4); + vResult = vminq_f32(vResult, MaxXDec4); + vResult = vmulq_f32(vResult, ScaleXDec4); + int32x4_t vResulti = vcvtq_s32_f32(vResult); + vResulti = vandq_s32(vResulti, MaskXDec4); + // Do a horizontal or of 4 entries + uint32x2_t vTemp = vget_low_u32(vreinterpretq_u32_s32(vResulti)); + uint32x2_t vTemp2 = vget_high_u32(vreinterpretq_u32_s32(vResulti)); + vTemp = vorr_u32(vTemp, vTemp2); + // Perform a single bit left shift on y|w + vTemp2 = vdup_lane_u32(vTemp, 1); + vTemp2 = vadd_u32(vTemp2, vTemp2); + vTemp = vorr_u32(vTemp, vTemp2); + vst1_lane_u32(&pDestination->v, vTemp, 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ScaleXDec4 = { { { 1.0f, 1024.0f / 2.0f, 1024.0f * 1024.0f, 1024.0f * 1024.0f * 1024.0f / 2.0f } } }; + static const XMVECTORI32 MaskXDec4 = { { { 0x3FF, 0x3FF << (10 - 1), 0x3FF << 20, 0x3 << (30 - 1) } } }; + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, MinXDec4); + vResult = _mm_min_ps(vResult, MaxXDec4); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, ScaleXDec4); + // Convert to int + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, MaskXDec4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // Perform a single bit left shift on y|w + vResulti2 = _mm_add_epi32(vResulti2, vResulti2); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUDecN4 +( + XMUDECN4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + static const XMVECTORF32 Scale = { { { 1023.0f, 1023.0f, 1023.0f, 3.0f } } }; + + XMVECTOR N = XMVectorSaturate(V); + N = XMVectorMultiply(N, Scale.v); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + (static_cast(tmp.w) << 30) + | ((static_cast(tmp.z) & 0x3FF) << 20) + | ((static_cast(tmp.y) & 0x3FF) << 10) + | ((static_cast(tmp.x) & 0x3FF))); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 ScaleUDecN4 = { { { 1023.0f, 1023.0f * 1024.0f * 0.5f, 1023.0f * 1024.0f * 1024.0f, 3.0f * 1024.0f * 1024.0f * 1024.0f * 0.5f } } }; + static const XMVECTORI32 MaskUDecN4 = { { { 0x3FF, 0x3FF << (10 - 1), 0x3FF << 20, 0x3 << (30 - 1) } } }; + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(0.f)); + vResult = vminq_f32(vResult, vdupq_n_f32(1.f)); + vResult = vmulq_f32(vResult, ScaleUDecN4); + uint32x4_t vResulti = vcvtq_u32_f32(vResult); + vResulti = vandq_u32(vResulti, MaskUDecN4); + // Do a horizontal or of 4 entries + uint32x2_t vTemp = vget_low_u32(vResulti); + uint32x2_t vTemp2 = vget_high_u32(vResulti); + vTemp = vorr_u32(vTemp, vTemp2); + // Perform a single bit left shift on y|w + vTemp2 = vdup_lane_u32(vTemp, 1); + vTemp2 = vadd_u32(vTemp2, vTemp2); + vTemp = vorr_u32(vTemp, vTemp2); + vst1_lane_u32(&pDestination->v, vTemp, 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ScaleUDecN4 = { { { 1023.0f, 1023.0f * 1024.0f * 0.5f, 1023.0f * 1024.0f * 1024.0f, 3.0f * 1024.0f * 1024.0f * 1024.0f * 0.5f } } }; + static const XMVECTORI32 MaskUDecN4 = { { { 0x3FF, 0x3FF << (10 - 1), 0x3FF << 20, 0x3 << (30 - 1) } } }; + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, g_XMOne); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, ScaleUDecN4); + // Convert to int + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, MaskUDecN4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // Perform a left shift by one bit on y|w + vResulti2 = _mm_add_epi32(vResulti2, vResulti2); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUDecN4_XR +( + XMUDECN4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + static const XMVECTORF32 Scale = { { { 510.0f, 510.0f, 510.0f, 3.0f } } }; + static const XMVECTORF32 Bias = { { { 384.0f, 384.0f, 384.0f, 0.0f } } }; + static const XMVECTORF32 C = { { { 1023.f, 1023.f, 1023.f, 3.f } } }; + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorMultiplyAdd(V, Scale, Bias); + N = XMVectorClamp(N, g_XMZero, C); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + (static_cast(tmp.w) << 30) + | ((static_cast(tmp.z) & 0x3FF) << 20) + | ((static_cast(tmp.y) & 0x3FF) << 10) + | ((static_cast(tmp.x) & 0x3FF))); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Shift = { { { 1.0f, 1024.0f * 0.5f, 1024.0f * 1024.0f, 1024.0f * 1024.0f * 1024.0f * 0.5f } } }; + static const XMVECTORU32 MaskUDecN4 = { { { 0x3FF, 0x3FF << (10 - 1), 0x3FF << 20, 0x3 << (30 - 1) } } }; + float32x4_t vResult = vmlaq_f32(Bias, V, Scale); + vResult = vmaxq_f32(vResult, vdupq_n_f32(0.f)); + vResult = vminq_f32(vResult, C); + vResult = vmulq_f32(vResult, Shift); + uint32x4_t vResulti = vcvtq_u32_f32(vResult); + vResulti = vandq_u32(vResulti, MaskUDecN4); + // Do a horizontal or of 4 entries + uint32x2_t vTemp = vget_low_u32(vResulti); + uint32x2_t vTemp2 = vget_high_u32(vResulti); + vTemp = vorr_u32(vTemp, vTemp2); + // Perform a single bit left shift on y|w + vTemp2 = vdup_lane_u32(vTemp, 1); + vTemp2 = vadd_u32(vTemp2, vTemp2); + vTemp = vorr_u32(vTemp, vTemp2); + vst1_lane_u32(&pDestination->v, vTemp, 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 Shift = { { { 1.0f, 1024.0f * 0.5f, 1024.0f * 1024.0f, 1024.0f * 1024.0f * 1024.0f * 0.5f } } }; + static const XMVECTORU32 MaskUDecN4 = { { { 0x3FF, 0x3FF << (10 - 1), 0x3FF << 20, 0x3 << (30 - 1) } } }; + // Scale & bias + XMVECTOR vResult = XM_FMADD_PS(V, Scale, Bias); + // Clamp to bounds + vResult = _mm_max_ps(vResult, g_XMZero); + vResult = _mm_min_ps(vResult, C); + // Scale by shift values + vResult = _mm_mul_ps(vResult, Shift); + // Convert to int + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, MaskUDecN4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // Perform a left shift by one bit on y|w + vResulti2 = _mm_add_epi32(vResulti2, vResulti2); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUDec4 +( + XMUDEC4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + static const XMVECTORF32 MaxUDec4 = { { { 1023.0f, 1023.0f, 1023.0f, 3.0f } } }; + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, XMVectorZero(), MaxUDec4); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + (static_cast(tmp.w) << 30) + | ((static_cast(tmp.z) & 0x3FF) << 20) + | ((static_cast(tmp.y) & 0x3FF) << 10) + | ((static_cast(tmp.x) & 0x3FF))); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 ScaleUDec4 = { { { 1.0f, 1024.0f / 2.0f, 1024.0f * 1024.0f, 1024.0f * 1024.0f * 1024.0f / 2.0f } } }; + static const XMVECTORI32 MaskUDec4 = { { { 0x3FF, 0x3FF << (10 - 1), 0x3FF << 20, 0x3 << (30 - 1) } } }; + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(0.f)); + vResult = vminq_f32(vResult, MaxUDec4); + vResult = vmulq_f32(vResult, ScaleUDec4); + uint32x4_t vResulti = vcvtq_u32_f32(vResult); + vResulti = vandq_u32(vResulti, MaskUDec4); + // Do a horizontal or of 4 entries + uint32x2_t vTemp = vget_low_u32(vResulti); + uint32x2_t vTemp2 = vget_high_u32(vResulti); + vTemp = vorr_u32(vTemp, vTemp2); + // Perform a single bit left shift on y|w + vTemp2 = vdup_lane_u32(vTemp, 1); + vTemp2 = vadd_u32(vTemp2, vTemp2); + vTemp = vorr_u32(vTemp, vTemp2); + vst1_lane_u32(&pDestination->v, vTemp, 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ScaleUDec4 = { { { 1.0f, 1024.0f / 2.0f, 1024.0f * 1024.0f, 1024.0f * 1024.0f * 1024.0f / 2.0f } } }; + static const XMVECTORI32 MaskUDec4 = { { { 0x3FF, 0x3FF << (10 - 1), 0x3FF << 20, 0x3 << (30 - 1) } } }; + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, MaxUDec4); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, ScaleUDec4); + // Convert to int + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, MaskUDec4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // Perform a left shift by one bit on y|w + vResulti2 = _mm_add_epi32(vResulti2, vResulti2); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +// C4996: ignore deprecation warning +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreDecN4 +( + XMDECN4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + static const XMVECTORF32 Scale = { { { 511.0f, 511.0f, 511.0f, 1.0f } } }; + + XMVECTOR N = XMVectorClamp(V, g_XMNegativeOne.v, g_XMOne.v); + N = XMVectorMultiply(N, Scale.v); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + (static_cast(tmp.w) << 30) + | ((static_cast(tmp.z) & 0x3FF) << 20) + | ((static_cast(tmp.y) & 0x3FF) << 10) + | ((static_cast(tmp.x) & 0x3FF))); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 ScaleDecN4 = { { { 511.0f, 511.0f * 1024.0f, 511.0f * 1024.0f * 1024.0f, 1.0f * 1024.0f * 1024.0f * 1024.0f } } }; + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(-1.f)); + vResult = vminq_f32(vResult, vdupq_n_f32(1.f)); + vResult = vmulq_f32(vResult, ScaleDecN4); + int32x4_t vResulti = vcvtq_s32_f32(vResult); + vResulti = vandq_s32(vResulti, g_XMMaskDec4); + // Do a horizontal or of 4 entries + uint32x2_t vTemp = vget_low_u32(vreinterpretq_u32_s32(vResulti)); + uint32x2_t vhi = vget_high_u32(vreinterpretq_u32_s32(vResulti)); + vTemp = vorr_u32(vTemp, vhi); + vTemp = vpadd_u32(vTemp, vTemp); + vst1_lane_u32(&pDestination->v, vTemp, 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ScaleDecN4 = { { { 511.0f, 511.0f * 1024.0f, 511.0f * 1024.0f * 1024.0f, 1.0f * 1024.0f * 1024.0f * 1024.0f } } }; + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_XMNegativeOne); + vResult = _mm_min_ps(vResult, g_XMOne); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, ScaleDecN4); + // Convert to int + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, g_XMMaskDec4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreDec4 +( + XMDEC4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + static const XMVECTORF32 MinDec4 = { { { -511.0f, -511.0f, -511.0f, -1.0f } } }; + static const XMVECTORF32 MaxDec4 = { { { 511.0f, 511.0f, 511.0f, 1.0f } } }; + +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, MinDec4, MaxDec4); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + (static_cast(tmp.w) << 30) + | ((static_cast(tmp.z) & 0x3FF) << 20) + | ((static_cast(tmp.y) & 0x3FF) << 10) + | ((static_cast(tmp.x) & 0x3FF))); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 ScaleDec4 = { { { 1.0f, 1024.0f, 1024.0f * 1024.0f, 1024.0f * 1024.0f * 1024.0f } } }; + float32x4_t vResult = vmaxq_f32(V, MinDec4); + vResult = vminq_f32(vResult, MaxDec4); + vResult = vmulq_f32(vResult, ScaleDec4); + int32x4_t vResulti = vcvtq_s32_f32(vResult); + vResulti = vandq_s32(vResulti, g_XMMaskDec4); + // Do a horizontal or of all 4 entries + uint32x2_t vTemp = vget_low_u32(vreinterpretq_u32_s32(vResulti)); + uint32x2_t vhi = vget_high_u32(vreinterpretq_u32_s32(vResulti)); + vTemp = vorr_u32(vTemp, vhi); + vTemp = vpadd_u32(vTemp, vTemp); + vst1_lane_u32(&pDestination->v, vTemp, 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ScaleDec4 = { { { 1.0f, 1024.0f, 1024.0f * 1024.0f, 1024.0f * 1024.0f * 1024.0f } } }; + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, MinDec4); + vResult = _mm_min_ps(vResult, MaxDec4); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, ScaleDec4); + // Convert to int + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, g_XMMaskDec4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUByteN4 +( + XMUBYTEN4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorSaturate(V); + N = XMVectorMultiply(N, g_UByteMax); + N = XMVectorTruncate(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + pDestination->z = static_cast(tmp.z); + pDestination->w = static_cast(tmp.w); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(0)); + R = vminq_f32(R, vdupq_n_f32(1.0f)); + R = vmulq_n_f32(R, 255.0f); + uint32x4_t vInt32 = vcvtq_u32_f32(R); + uint16x4_t vInt16 = vqmovn_u32(vInt32); + uint8x8_t vInt8 = vqmovn_u16(vcombine_u16(vInt16, vInt16)); + vst1_lane_u32(&pDestination->v, vreinterpret_u32_u8(vInt8), 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ScaleUByteN4 = { { { 255.0f, 255.0f * 256.0f * 0.5f, 255.0f * 256.0f * 256.0f, 255.0f * 256.0f * 256.0f * 256.0f * 0.5f } } }; + static const XMVECTORI32 MaskUByteN4 = { { { 0xFF, 0xFF << (8 - 1), 0xFF << 16, 0xFF << (24 - 1) } } }; + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, g_XMOne); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, ScaleUByteN4); + // Convert to int + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, MaskUByteN4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // Perform a single bit left shift to fix y|w + vResulti2 = _mm_add_epi32(vResulti2, vResulti2); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUByte4 +( + XMUBYTE4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, XMVectorZero(), g_UByteMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + pDestination->z = static_cast(tmp.z); + pDestination->w = static_cast(tmp.w); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(0)); + R = vminq_f32(R, vdupq_n_f32(255.0f)); + uint32x4_t vInt32 = vcvtq_u32_f32(R); + uint16x4_t vInt16 = vqmovn_u32(vInt32); + uint8x8_t vInt8 = vqmovn_u16(vcombine_u16(vInt16, vInt16)); + vst1_lane_u32(&pDestination->v, vreinterpret_u32_u8(vInt8), 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ScaleUByte4 = { { { 1.0f, 256.0f * 0.5f, 256.0f * 256.0f, 256.0f * 256.0f * 256.0f * 0.5f } } }; + static const XMVECTORI32 MaskUByte4 = { { { 0xFF, 0xFF << (8 - 1), 0xFF << 16, 0xFF << (24 - 1) } } }; + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, g_UByteMax); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, ScaleUByte4); + // Convert to int by rounding + __m128i vResulti = _mm_cvtps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, MaskUByte4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // Perform a single bit left shift to fix y|w + vResulti2 = _mm_add_epi32(vResulti2, vResulti2); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreByteN4 +( + XMBYTEN4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, g_XMNegativeOne.v, g_XMOne.v); + N = XMVectorMultiply(N, g_ByteMax); + N = XMVectorTruncate(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + pDestination->z = static_cast(tmp.z); + pDestination->w = static_cast(tmp.w); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(-1.f)); + R = vminq_f32(R, vdupq_n_f32(1.0f)); + R = vmulq_n_f32(R, 127.0f); + int32x4_t vInt32 = vcvtq_s32_f32(R); + int16x4_t vInt16 = vqmovn_s32(vInt32); + int8x8_t vInt8 = vqmovn_s16(vcombine_s16(vInt16, vInt16)); + vst1_lane_u32(&pDestination->v, vreinterpret_u32_s8(vInt8), 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ScaleByteN4 = { { { 127.0f, 127.0f * 256.0f, 127.0f * 256.0f * 256.0f, 127.0f * 256.0f * 256.0f * 256.0f } } }; + static const XMVECTORI32 MaskByteN4 = { { { 0xFF, 0xFF << 8, 0xFF << 16, static_cast(0xFF000000) } } }; + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_XMNegativeOne); + vResult = _mm_min_ps(vResult, g_XMOne); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, ScaleByteN4); + // Convert to int + __m128i vResulti = _mm_cvttps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, MaskByteN4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreByte4 +( + XMBYTE4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, g_ByteMin, g_ByteMax); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->x = static_cast(tmp.x); + pDestination->y = static_cast(tmp.y); + pDestination->z = static_cast(tmp.z); + pDestination->w = static_cast(tmp.w); + +#elif defined(_XM_ARM_NEON_INTRINSICS_) + float32x4_t R = vmaxq_f32(V, vdupq_n_f32(-127.f)); + R = vminq_f32(R, vdupq_n_f32(127.f)); + int32x4_t vInt32 = vcvtq_s32_f32(R); + int16x4_t vInt16 = vqmovn_s32(vInt32); + int8x8_t vInt8 = vqmovn_s16(vcombine_s16(vInt16, vInt16)); + vst1_lane_u32(&pDestination->v, vreinterpret_u32_s8(vInt8), 0); +#elif defined(_XM_SSE_INTRINSICS_) + static const XMVECTORF32 ScaleByte4 = { { { 1.0f, 256.0f, 256.0f * 256.0f, 256.0f * 256.0f * 256.0f } } }; + static const XMVECTORI32 MaskByte4 = { { { 0xFF, 0xFF << 8, 0xFF << 16, static_cast(0xFF000000) } } }; + // Clamp to bounds + XMVECTOR vResult = _mm_max_ps(V, g_ByteMin); + vResult = _mm_min_ps(vResult, g_ByteMax); + // Scale by multiplication + vResult = _mm_mul_ps(vResult, ScaleByte4); + // Convert to int by rounding + __m128i vResulti = _mm_cvtps_epi32(vResult); + // Mask off any fraction + vResulti = _mm_and_si128(vResulti, MaskByte4); + // Do a horizontal or of 4 entries + __m128i vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(3, 2, 3, 2)); + // x = x|z, y = y|w + vResulti = _mm_or_si128(vResulti, vResulti2); + // Move Z to the x position + vResulti2 = _mm_shuffle_epi32(vResulti, _MM_SHUFFLE(1, 1, 1, 1)); + // i = x|y|z|w + vResulti = _mm_or_si128(vResulti, vResulti2); + _mm_store_ss(reinterpret_cast(&pDestination->v), _mm_castsi128_ps(vResulti)); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreUNibble4 +( + XMUNIBBLE4* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + static const XMVECTORF32 Max = { { { 15.0f, 15.0f, 15.0f, 15.0f } } }; +#if defined(_XM_NO_INTRINSICS_) + + XMVECTOR N = XMVectorClamp(V, XMVectorZero(), Max.v); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + ((static_cast(tmp.w) & 0xF) << 12) + | ((static_cast(tmp.z) & 0xF) << 8) + | ((static_cast(tmp.y) & 0xF) << 4) + | (static_cast(tmp.x) & 0xF)); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.0f, 16.f, 16.f * 16.f, 16.f * 16.f * 16.f } } }; + static const XMVECTORU32 Mask = { { { 0xF, 0xF << 4, 0xF << 8, 0xF << 12 } } }; + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(0)); + vResult = vminq_f32(vResult, Max); + vResult = vmulq_f32(vResult, Scale); + uint32x4_t vResulti = vcvtq_u32_f32(vResult); + vResulti = vandq_u32(vResulti, Mask); + // Do a horizontal or of 4 entries + uint32x2_t vTemp = vget_low_u32(vResulti); + uint32x2_t vhi = vget_high_u32(vResulti); + vTemp = vorr_u32(vTemp, vhi); + vTemp = vpadd_u32(vTemp, vTemp); + vst1_lane_u16(&pDestination->v, vreinterpret_u16_u32(vTemp), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Bounds check + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, Max); + // Convert to int with rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // No SSE operations will write to 16-bit values, so we have to extract them manually + auto x = static_cast(_mm_extract_epi16(vInt, 0)); + auto y = static_cast(_mm_extract_epi16(vInt, 2)); + auto z = static_cast(_mm_extract_epi16(vInt, 4)); + auto w = static_cast(_mm_extract_epi16(vInt, 6)); + pDestination->v = static_cast( + ((static_cast(w) & 0xF) << 12) + | ((static_cast(z) & 0xF) << 8) + | ((static_cast(y) & 0xF) << 4) + | ((static_cast(x) & 0xF))); +#endif +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline void XM_CALLCONV XMStoreU555 +( + XMU555* pDestination, + FXMVECTOR V +) noexcept +{ + assert(pDestination); + static const XMVECTORF32 Max = { { { 31.0f, 31.0f, 31.0f, 1.0f } } }; + +#if defined(_XM_NO_INTRINSICS_) + XMVECTOR N = XMVectorClamp(V, XMVectorZero(), Max.v); + N = XMVectorRound(N); + + XMFLOAT4A tmp; + XMStoreFloat4A(&tmp, N); + + pDestination->v = static_cast( + ((tmp.w > 0.f) ? 0x8000 : 0) + | ((static_cast(tmp.z) & 0x1F) << 10) + | ((static_cast(tmp.y) & 0x1F) << 5) + | (static_cast(tmp.x) & 0x1F)); +#elif defined(_XM_ARM_NEON_INTRINSICS_) + static const XMVECTORF32 Scale = { { { 1.0f, 32.f / 2.f, 32.f * 32.f, 32.f * 32.f * 32.f / 2.f } } }; + static const XMVECTORU32 Mask = { { { 0x1F, 0x1F << (5 - 1), 0x1F << 10, 0x1 << (15 - 1) } } }; + float32x4_t vResult = vmaxq_f32(V, vdupq_n_f32(0)); + vResult = vminq_f32(vResult, Max); + vResult = vmulq_f32(vResult, Scale); + uint32x4_t vResulti = vcvtq_u32_f32(vResult); + vResulti = vandq_u32(vResulti, Mask); + // Do a horizontal or of 4 entries + uint32x2_t vTemp = vget_low_u32(vResulti); + uint32x2_t vTemp2 = vget_high_u32(vResulti); + vTemp = vorr_u32(vTemp, vTemp2); + // Perform a single bit left shift on y|w + vTemp2 = vdup_lane_u32(vTemp, 1); + vTemp2 = vadd_u32(vTemp2, vTemp2); + vTemp = vorr_u32(vTemp, vTemp2); + vst1_lane_u16(&pDestination->v, vreinterpret_u16_u32(vTemp), 0); +#elif defined(_XM_SSE_INTRINSICS_) + // Bounds check + XMVECTOR vResult = _mm_max_ps(V, g_XMZero); + vResult = _mm_min_ps(vResult, Max); + // Convert to int with rounding + __m128i vInt = _mm_cvtps_epi32(vResult); + // No SSE operations will write to 16-bit values, so we have to extract them manually + auto x = static_cast(_mm_extract_epi16(vInt, 0)); + auto y = static_cast(_mm_extract_epi16(vInt, 2)); + auto z = static_cast(_mm_extract_epi16(vInt, 4)); + auto w = static_cast(_mm_extract_epi16(vInt, 6)); + pDestination->v = static_cast( + (static_cast(w) ? 0x8000 : 0) + | ((static_cast(z) & 0x1F) << 10) + | ((static_cast(y) & 0x1F) << 5) + | ((static_cast(x) & 0x1F))); +#endif +} + + +/**************************************************************************** + * + * XMCOLOR operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMCOLOR::XMCOLOR +( + float _r, + float _g, + float _b, + float _a +) noexcept +{ + XMStoreColor(this, XMVectorSet(_r, _g, _b, _a)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMCOLOR::XMCOLOR(const float* pArray) noexcept +{ + XMStoreColor(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMHALF2 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMHALF2::XMHALF2 +( + float _x, + float _y +) noexcept +{ + x = XMConvertFloatToHalf(_x); + y = XMConvertFloatToHalf(_y); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMHALF2::XMHALF2(const float* pArray) noexcept +{ + assert(pArray != nullptr); + x = XMConvertFloatToHalf(pArray[0]); + y = XMConvertFloatToHalf(pArray[1]); +} + +/**************************************************************************** + * + * XMSHORTN2 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMSHORTN2::XMSHORTN2 +( + float _x, + float _y +) noexcept +{ + XMStoreShortN2(this, XMVectorSet(_x, _y, 0.0f, 0.0f)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMSHORTN2::XMSHORTN2(const float* pArray) noexcept +{ + XMStoreShortN2(this, XMLoadFloat2(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMSHORT2 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMSHORT2::XMSHORT2 +( + float _x, + float _y +) noexcept +{ + XMStoreShort2(this, XMVectorSet(_x, _y, 0.0f, 0.0f)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMSHORT2::XMSHORT2(const float* pArray) noexcept +{ + XMStoreShort2(this, XMLoadFloat2(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUSHORTN2 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUSHORTN2::XMUSHORTN2 +( + float _x, + float _y +) noexcept +{ + XMStoreUShortN2(this, XMVectorSet(_x, _y, 0.0f, 0.0f)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUSHORTN2::XMUSHORTN2(const float* pArray) noexcept +{ + XMStoreUShortN2(this, XMLoadFloat2(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUSHORT2 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUSHORT2::XMUSHORT2 +( + float _x, + float _y +) noexcept +{ + XMStoreUShort2(this, XMVectorSet(_x, _y, 0.0f, 0.0f)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUSHORT2::XMUSHORT2(const float* pArray) noexcept +{ + XMStoreUShort2(this, XMLoadFloat2(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMBYTEN2 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMBYTEN2::XMBYTEN2 +( + float _x, + float _y +) noexcept +{ + XMStoreByteN2(this, XMVectorSet(_x, _y, 0.0f, 0.0f)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMBYTEN2::XMBYTEN2(const float* pArray) noexcept +{ + XMStoreByteN2(this, XMLoadFloat2(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMBYTE2 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMBYTE2::XMBYTE2 +( + float _x, + float _y +) noexcept +{ + XMStoreByte2(this, XMVectorSet(_x, _y, 0.0f, 0.0f)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMBYTE2::XMBYTE2(const float* pArray) noexcept +{ + XMStoreByte2(this, XMLoadFloat2(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUBYTEN2 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUBYTEN2::XMUBYTEN2 +( + float _x, + float _y +) noexcept +{ + XMStoreUByteN2(this, XMVectorSet(_x, _y, 0.0f, 0.0f)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUBYTEN2::XMUBYTEN2(const float* pArray) noexcept +{ + XMStoreUByteN2(this, XMLoadFloat2(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUBYTE2 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUBYTE2::XMUBYTE2 +( + float _x, + float _y +) noexcept +{ + XMStoreUByte2(this, XMVectorSet(_x, _y, 0.0f, 0.0f)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUBYTE2::XMUBYTE2(const float* pArray) noexcept +{ + XMStoreUByte2(this, XMLoadFloat2(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMU565 operators + * + ****************************************************************************/ + +inline XMU565::XMU565 +( + float _x, + float _y, + float _z +) noexcept +{ + XMStoreU565(this, XMVectorSet(_x, _y, _z, 0.0f)); +} + +_Use_decl_annotations_ +inline XMU565::XMU565(const float* pArray) noexcept +{ + XMStoreU565(this, XMLoadFloat3(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMFLOAT3PK operators + * + ****************************************************************************/ + +inline XMFLOAT3PK::XMFLOAT3PK +( + float _x, + float _y, + float _z +) noexcept +{ + XMStoreFloat3PK(this, XMVectorSet(_x, _y, _z, 0.0f)); +} + +_Use_decl_annotations_ +inline XMFLOAT3PK::XMFLOAT3PK(const float* pArray) noexcept +{ + XMStoreFloat3PK(this, XMLoadFloat3(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMFLOAT3SE operators + * + ****************************************************************************/ + +inline XMFLOAT3SE::XMFLOAT3SE +( + float _x, + float _y, + float _z +) noexcept +{ + XMStoreFloat3SE(this, XMVectorSet(_x, _y, _z, 0.0f)); +} + +_Use_decl_annotations_ +inline XMFLOAT3SE::XMFLOAT3SE(const float* pArray) noexcept +{ + XMStoreFloat3SE(this, XMLoadFloat3(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMHALF4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMHALF4::XMHALF4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + x = XMConvertFloatToHalf(_x); + y = XMConvertFloatToHalf(_y); + z = XMConvertFloatToHalf(_z); + w = XMConvertFloatToHalf(_w); +} + +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline XMHALF4::XMHALF4(const float* pArray) noexcept +{ + XMConvertFloatToHalfStream(&x, sizeof(HALF), pArray, sizeof(float), 4); +} + +/**************************************************************************** + * + * XMSHORTN4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMSHORTN4::XMSHORTN4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreShortN4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMSHORTN4::XMSHORTN4(const float* pArray) noexcept +{ + XMStoreShortN4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMSHORT4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMSHORT4::XMSHORT4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreShort4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMSHORT4::XMSHORT4(const float* pArray) noexcept +{ + XMStoreShort4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUSHORTN4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUSHORTN4::XMUSHORTN4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreUShortN4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUSHORTN4::XMUSHORTN4(const float* pArray) noexcept +{ + XMStoreUShortN4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUSHORT4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUSHORT4::XMUSHORT4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreUShort4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUSHORT4::XMUSHORT4(const float* pArray) noexcept +{ + XMStoreUShort4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMXDECN4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMXDECN4::XMXDECN4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreXDecN4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMXDECN4::XMXDECN4(const float* pArray) noexcept +{ + XMStoreXDecN4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMXDEC4 operators + * + ****************************************************************************/ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +// C4996: ignore deprecation warning +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +//------------------------------------------------------------------------------ + +inline XMXDEC4::XMXDEC4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreXDec4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMXDEC4::XMXDEC4(const float* pArray) noexcept +{ + XMStoreXDec4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMDECN4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMDECN4::XMDECN4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreDecN4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMDECN4::XMDECN4(const float* pArray) noexcept +{ + XMStoreDecN4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMDEC4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMDEC4::XMDEC4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreDec4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMDEC4::XMDEC4(const float* pArray) noexcept +{ + XMStoreDec4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +/**************************************************************************** + * + * XMUDECN4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUDECN4::XMUDECN4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreUDecN4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUDECN4::XMUDECN4(const float* pArray) noexcept +{ + XMStoreUDecN4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUDEC4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUDEC4::XMUDEC4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreUDec4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUDEC4::XMUDEC4(const float* pArray) noexcept +{ + XMStoreUDec4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMBYTEN4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMBYTEN4::XMBYTEN4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreByteN4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMBYTEN4::XMBYTEN4(const float* pArray) noexcept +{ + XMStoreByteN4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMBYTE4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMBYTE4::XMBYTE4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreByte4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMBYTE4::XMBYTE4(const float* pArray) noexcept +{ + XMStoreByte4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUBYTEN4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUBYTEN4::XMUBYTEN4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreUByteN4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUBYTEN4::XMUBYTEN4(const float* pArray) noexcept +{ + XMStoreUByteN4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUBYTE4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUBYTE4::XMUBYTE4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreUByte4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUBYTE4::XMUBYTE4(const float* pArray) noexcept +{ + XMStoreUByte4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMUNIBBLE4 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMUNIBBLE4::XMUNIBBLE4 +( + float _x, + float _y, + float _z, + float _w +) noexcept +{ + XMStoreUNibble4(this, XMVectorSet(_x, _y, _z, _w)); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMUNIBBLE4::XMUNIBBLE4(const float* pArray) noexcept +{ + XMStoreUNibble4(this, XMLoadFloat4(reinterpret_cast(pArray))); +} + +/**************************************************************************** + * + * XMU555 operators + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ + +inline XMU555::XMU555 +( + float _x, + float _y, + float _z, + bool _w +) noexcept +{ + XMStoreU555(this, XMVectorSet(_x, _y, _z, ((_w) ? 1.0f : 0.0f))); +} + +//------------------------------------------------------------------------------ +_Use_decl_annotations_ +inline XMU555::XMU555 +( + const float* pArray, + bool _w +) noexcept +{ + XMVECTOR V = XMLoadFloat3(reinterpret_cast(pArray)); + XMStoreU555(this, XMVectorSetW(V, ((_w) ? 1.0f : 0.0f))); +} + diff --git a/Extern/dxmath/LICENSE b/Extern/dxmath/LICENSE new file mode 100644 index 000000000..9e841e7a2 --- /dev/null +++ b/Extern/dxmath/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + 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 diff --git a/Extern/dxmath/README.md b/Extern/dxmath/README.md new file mode 100644 index 000000000..0c1cfcfe8 --- /dev/null +++ b/Extern/dxmath/README.md @@ -0,0 +1,130 @@ +![DirectX Logo](https://raw.githubusercontent.com/wiki/Microsoft/DirectXMath/X_jpg.jpg) + +# DirectXMath + +https://github.com/Microsoft/DirectXMath + +Copyright (c) Microsoft Corporation. + +## April 2025 + +This package contains the DirectXMath library, an all inline SIMD C++ linear algebra library for use in games and graphics apps. + +This code is designed to build with Visual Studio 2019 (16.11), Visual Studio 2022, or clang/LLVM for Windows. It is recommended that you make use of the latest updates. + +These components are designed to work without requiring any content from the legacy DirectX SDK. For details, see [Where is the DirectX SDK?](https://aka.ms/dxsdk). + +## Directory Layout + +* ``Inc\`` + + * DirectXMath Files (in the DirectX C++ namespace) + + * DirectXMath.h - Core library + * DirectXPackedVector.h - Load/Store functions and types for working with various compressed GPU formats + * DirectXColors.h - .NET-style Color defines in sRGB and linear color space + * DirectXCollision.h - Bounding volume collision library + +* ``Extentions\`` + + * Advanced instruction set variants for guarded codepaths + + * DirectXMathSSE3.h - SSE3 + * DirectXMathBE.h - Supplemental SSE3 (SSSE3) + * DirectXMathSSE4.h - SSE4.1 + * DirectXMathAVX.h - Advanced Vector Extensions (AVX) + * DirectXMathAVX2.h - Advanced Vector Extensions 2 (AVX2) + * DirectXMathF16C.h - Half-precision conversions (F16C) + * DirectXMathFMA3.h - Fused multiply-accumulate (FMA3) + * DirectXMathFMA4.h - Fused multiply-accumulate (FMA4) + +* ``SHMath\`` + + * Spherical Harmonics math functions + + * DirectXSH.h - Header for SHMath functions + * DirectXSH.cpp, DirectXSHD3D11.cpp, DirectXSHD3D12.cpp - Implementation + +* ``XDSP\`` + + * XDSP.h - Digital Signal Processing helper functions + +* ``build\`` + + * Contains miscellaneous build files and scripts. + +## Documentation + +Documentation is available on the [Microsoft Docs](https://docs.microsoft.com/en-us/windows/desktop/dxmath/directxmath-portal). Additional information can be found on the [project wiki](https://github.com/microsoft/DirectXMath/wiki). + +## Compiler support + +Officially the library is supported with Microsoft Visual C++ 2019 (16.11) or later, clang/LLVM v12 or later, and GCC 10 or later. It should also compile with the Intel C++ and MinGW compilers. + +When building with clang/LLVM or other GNU C compilers, the ``_XM_NO_XMVECTOR_OVERLOADS_`` control define is set because these compilers do not support creating operator overloads for the ``XMVECTOR`` type. You can choose to enable this preprocessor define explicitly to do the same thing with Visual C++ for improved portability. + +To build for non-Windows platforms, you need to provide a ``sal.h`` header in your include path. You can obtain an open source version from [GitHub](https://raw.githubusercontent.com/dotnet/runtime/main/src/coreclr/pal/inc/rt/sal.h). + +With GCC, the SAL annotation preprocessor symbols can conflict with the GNU implementation of the Standard C++ Library. The workaround is to include the system headers before including DirectXMath: + +```cpp +#include +#include +#include + +#include // C++20 header + +#include +``` + +## Notices + +All content and source code for this package are subject to the terms of the [MIT License](https://github.com/microsoft/DirectXMath/blob/main/LICENSE). + +For the latest version of DirectXMath, bug reports, etc. please visit the project site on [GitHub](https://github.com/microsoft/DirectXMath). + +## Release Notes + +FOR SECURITY ADVISORIES, see [GitHub](https://github.com/microsoft/DirectXMath/security/advisories). + +For a full change history, see [CHANGELOG.md](https://github.com/microsoft/DirectXMath/blob/main/CHANGELOG.md). + +* The clang/LLVM toolset currently does not respect the ``float_control`` pragma for SSE instrinsics. Therefore, the use of ``/fp:fast`` is not recommended on clang/LLVM until this issue is fixed. See [55713](https://github.com/llvm/llvm-project/issues/55713). + +## Support + +For questions, consider using [Stack Overflow](https://stackoverflow.com/questions/tagged/directxmath) with the *directxmath* tag, or the [DirectX Discord Server](https://discord.gg/directx) in the *dx12-developers* or *dx9-dx11-developers* channel. + +For bug reports and feature requests, please use GitHub [issues](https://github.com/microsoft/DirectXMath/issues) for this project. + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. + +Tests for new features should also be submitted as a PR to the [Test Suite](https://github.com/walbourn/directxmathtest/wiki) repository. + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Trademarks + +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. + +## Credits + +The xboxmath library was originated by Matt Bronder with contributions from Sakphong Chanbai and David Hefner for the Xbox 360. + +The xnamath library for the DirectX SDK and Xbox XDK was the work of Chuck Walbourn and Becky Heineman based on xboxmath, with contributions from Jeremy Gup, Dan Haffner, Matt Lee, Casey Meekhof, Rich Sauer, Jason Strayer, and Xiaoyue Zheng. + +The DirectXMath library for the Windows SDK and Xbox One XDK is the work of Chuck Walbourn based on xnamath, with contributions from Darren Anderson, Matt Lee, Aaron Rodriguez Hernandez, Yuichi Ito, Reza Nourai, Rich Sauer, and Jason Strayer. + +Thanks to Dave Eberly for his contributions particularly in improving the transcendental functions. + +Thanks to Bruce Dawson for his help with the rounding functions. + +Thanks to Andrew Farrier for the fixes to ``XMVerifyCPUSupport`` to properly support clang. + +Thanks to Scott Matloff for his help in getting the library updated to use Intel SVML for VS 2019. diff --git a/Extern/dxmath/SECURITY.md b/Extern/dxmath/SECURITY.md new file mode 100644 index 000000000..167c4b408 --- /dev/null +++ b/Extern/dxmath/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + +* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) +* Full paths of source file(s) related to the manifestation of the issue +* The location of the affected source code (tag/branch/commit or direct URL) +* Any special configuration required to reproduce the issue +* Step-by-step instructions to reproduce the issue +* Proof-of-concept or exploit code (if possible) +* Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). + + diff --git a/Extern/dxmath/SHMath/CMakeLists.txt b/Extern/dxmath/SHMath/CMakeLists.txt new file mode 100644 index 000000000..5d9ea30ed --- /dev/null +++ b/Extern/dxmath/SHMath/CMakeLists.txt @@ -0,0 +1,229 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cmake_minimum_required (VERSION 3.20) + +if(DEFINED DIRECTXMATH_VERSION) + set(SHMATH_VERSION ${DIRECTXMATH_VERSION}) +else() + set(SHMATH_VERSION 1.0.6) +endif() + +project(DirectXSH + VERSION ${SHMATH_VERSION} + DESCRIPTION "C++ Spherical Harmonics Math Library" + HOMEPAGE_URL "https://go.microsoft.com/fwlink/?LinkID=615560" + LANGUAGES CXX) + +if(CMAKE_VERSION VERSION_LESS 3.21) + get_property(not_top DIRECTORY PROPERTY PARENT_DIRECTORY) + if(not_top) + set(PROJECT_IS_TOP_LEVEL false) + else() + set(PROJECT_IS_TOP_LEVEL true) + endif() +endif() + +if(PROJECT_IS_TOP_LEVEL) + message(FATAL_ERROR "SHMath should be built by the main CMakeLists using BUILD_SHMATH") +endif() + +option(BUILD_DX11 "Build with DirectX11 support" OFF) + +option(BUILD_DX12 "Build with DirectX12 support" OFF) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +if(DEFINED VCPKG_TARGET_ARCHITECTURE) + set(DXMATH_ARCHITECTURE ${VCPKG_TARGET_ARCHITECTURE}) +elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Ww][Ii][Nn]32$") + set(DXMATH_ARCHITECTURE x86) +elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Xx]64$") + set(DXMATH_ARCHITECTURE x64) +elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Aa][Rr][Mm]$") + set(DXMATH_ARCHITECTURE arm) +elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Aa][Rr][Mm]64$") + set(DXMATH_ARCHITECTURE arm64) +elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Aa][Rr][Mm]64EC$") + set(DXMATH_ARCHITECTURE arm64ec) +elseif(CMAKE_VS_PLATFORM_NAME_DEFAULT MATCHES "^[Ww][Ii][Nn]32$") + set(DXMATH_ARCHITECTURE x86) +elseif(CMAKE_VS_PLATFORM_NAME_DEFAULT MATCHES "^[Xx]64$") + set(DXMATH_ARCHITECTURE x64) +elseif(CMAKE_VS_PLATFORM_NAME_DEFAULT MATCHES "^[Aa][Rr][Mm]$") + set(DXMATH_ARCHITECTURE arm) +elseif(CMAKE_VS_PLATFORM_NAME_DEFAULT MATCHES "^[Aa][Rr][Mm]64$") + set(DXMATH_ARCHITECTURE arm64) +elseif(CMAKE_VS_PLATFORM_NAME_DEFAULT MATCHES "^[Aa][Rr][Mm]64EC$") + set(DXMATH_ARCHITECTURE arm64ec) +elseif(NOT (DEFINED DXMATH_ARCHITECTURE)) + if(CMAKE_SYSTEM_PROCESSOR MATCHES "[Aa][Rr][Mm]64|aarch64|arm64") + set(DXMATH_ARCHITECTURE arm64) + else() + set(DXMATH_ARCHITECTURE x64) + endif() +endif() + +#--- Library +set(LIBRARY_HEADERS DirectXSH.h) +set(LIBRARY_SOURCES DirectXSH.cpp) + +if(WIN32 AND BUILD_DX11) + set(LIBRARY_SOURCES ${LIBRARY_SOURCES} DirectXSHD3D11.cpp) +endif() + +if(WIN32 AND BUILD_DX12) + set(LIBRARY_SOURCES ${LIBRARY_SOURCES} DirectXSHD3D12.cpp) +endif() + +add_library(${PROJECT_NAME} STATIC ${LIBRARY_SOURCES} ${LIBRARY_HEADERS}) + +target_include_directories(${PROJECT_NAME} PUBLIC + $ + $) + +target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11) + +target_link_libraries(${PROJECT_NAME} PRIVATE DirectXMath) + +if(MINGW AND BUILD_DX12) + find_package(directx-headers CONFIG REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC Microsoft::DirectX-Headers) + target_compile_definitions(${PROJECT_NAME} PUBLIC USING_DIRECTX_HEADERS) +else() + find_package(directx-headers CONFIG QUIET) +endif() + +if(directx-headers_FOUND) + message(STATUS "Using DirectX-Headers package") + target_link_libraries(${PROJECT_NAME} PRIVATE Microsoft::DirectX-Headers) + target_compile_definitions(${PROJECT_NAME} PRIVATE USING_DIRECTX_HEADERS) +endif() + +#--- Package +include(CMakePackageConfigHelpers) + +string(TOLOWER ${PROJECT_NAME} PACKAGE_NAME) +cmake_path(GET CMAKE_CURRENT_LIST_DIR PARENT_PATH DIRECTXMATH_PATH) + +write_basic_package_version_file( + ${PACKAGE_NAME}-config-version.cmake + VERSION ${SHMATH_VERSION} + COMPATIBILITY AnyNewerVersion + ARCH_INDEPENDENT) + +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +configure_package_config_file(${DIRECTXMATH_PATH}/build/DirectXMath-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE_NAME}) + +install(EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}-targets.cmake + NAMESPACE Microsoft:: + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE_NAME}) + +install(FILES ${LIBRARY_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE_NAME}) + +#--- Compiler switches +if(MSVC) + target_compile_options(${PROJECT_NAME} PRIVATE /Wall /EHsc /GR "$<$>:/guard:cf>") + + if((MSVC_VERSION GREATER_EQUAL 1928) + AND (CMAKE_SIZEOF_VOID_P EQUAL 8) + AND ((NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang|IntelLLVM")) OR (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0))) + target_compile_options(${PROJECT_NAME} PRIVATE "$<$>:/guard:ehcont>") + endif() +else() + target_compile_definitions(${PROJECT_NAME} PRIVATE $,_DEBUG,NDEBUG>) +endif() + +if(NOT (${DXMATH_ARCHITECTURE} MATCHES "^arm")) + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(ARCH_SSE2 $<$:/arch:SSE2> $<$>:-msse2>) + else() + set(ARCH_SSE2 $<$>:-msse2>) + endif() + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + list(APPEND ARCH_SSE2 -mfpmath=sse) + endif() + + target_compile_options(${PROJECT_NAME} PRIVATE ${ARCH_SSE2}) +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|IntelLLVM") + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) + target_compile_options(${PROJECT_NAME} PRIVATE /ZH:SHA_256 "-Wno-unsafe-buffer-usage") + endif() +elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + target_compile_options(${PROJECT_NAME} PRIVATE -Wno-ignored-attributes) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") + target_compile_options(${PROJECT_NAME} PRIVATE /Zc:__cplusplus /Zc:inline /fp:fast /Qdiag-disable:161) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + target_compile_options(${PROJECT_NAME} PRIVATE + /sdl /Zc:inline /fp:fast + "/wd4061" "/wd4365" "/wd4514" "/wd4571" "/wd4668" "/wd4710" "/wd4820" "/wd5039" "/wd5045") + + if(CMAKE_INTERPROCEDURAL_OPTIMIZATION) + target_compile_options(${PROJECT_NAME} PRIVATE $<$>:/Gy /Gw>) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.10) + target_compile_options(${PROJECT_NAME} PRIVATE /permissive-) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.14) + target_compile_options(${PROJECT_NAME} PRIVATE /Zc:__cplusplus) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.24) + target_compile_options(${PROJECT_NAME} PRIVATE /ZH:SHA_256) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.26) + target_compile_options(${PROJECT_NAME} PRIVATE /Zc:preprocessor /wd5105) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.28) + target_compile_options(${PROJECT_NAME} PRIVATE /Zc:lambda) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29) + target_compile_options(${PROJECT_NAME} PRIVATE /external:W4) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.34) + target_compile_options(${PROJECT_NAME} PRIVATE /wd5262 /wd5264) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.35) + if(CMAKE_INTERPROCEDURAL_OPTIMIZATION) + target_compile_options(${PROJECT_NAME} PRIVATE $<$>:/Zc:checkGwOdr>) + endif() + + target_compile_options(${PROJECT_NAME} PRIVATE $<$:/Zc:templateScope>) + endif() +endif() + +if(NOT WIN32) + file(DOWNLOAD + https://raw.githubusercontent.com/dotnet/runtime/v9.0.2/src/coreclr/pal/inc/rt/sal.h + "${CMAKE_CURRENT_BINARY_DIR}/sal/sal.h" + EXPECTED_HASH SHA512=8085f67bfa4ce01ae89461cadf72454a9552fde3f08b2dcc3de36b9830e29ce7a6192800f8a5cb2a66af9637be0017e85719826a4cfdade508ae97f319e0ee8e + ) + + target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/sal") +endif() diff --git a/Extern/dxmath/SHMath/DirectXSH.cpp b/Extern/dxmath/SHMath/DirectXSH.cpp new file mode 100644 index 000000000..3cbff7fc0 --- /dev/null +++ b/Extern/dxmath/SHMath/DirectXSH.cpp @@ -0,0 +1,4911 @@ +//----------------------------------------------------------------------------------- +// DirectXSH.cpp -- C++ Spherical Harmonics Math Library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/p/?LinkId=262885 +//------------------------------------------------------------------------------------- + +#ifdef _MSC_VER +#pragma warning( disable : 4619 4456 5264) +// C4619 #pragma warning warnings +// C4456 declaration hides previous local declaration +// C5264 'const' variable is not used +#endif + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wunused-const-variable" +#pragma clang diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + +#include "DirectXSH.h" +#include + +using namespace DirectX; + +namespace +{ +#ifdef _PREFAST_ +#pragma prefast(disable:246, "generated code by maple (nested const variable definitions)") +#endif + + const float fExtraNormFac[XM_SH_MAXORDER] = { 2.0f*sqrtf(XM_PI), 2.0f / 3.0f*sqrtf(3.0f*XM_PI), 2.0f / 5.0f*sqrtf(5.0f*XM_PI), 2.0f / 7.0f*sqrtf(7.0f*XM_PI), 2.0f / 3.0f*sqrtf(XM_PI), 2.0f / 11.0f*sqrtf(11.0f*XM_PI) }; + + // computes the integral of a constant function over a solid angular + // extent. No error checking - only used internaly. This function + // only returns the Yl0 coefficients, since the rest are zero for + // circularly symmetric functions. + const float ComputeCapInt_t1 = sqrtf(0.3141593E1f); + const float ComputeCapInt_t5 = sqrtf(3.0f); + const float ComputeCapInt_t11 = sqrtf(5.0f); + const float ComputeCapInt_t18 = sqrtf(7.0f); + const float ComputeCapInt_t32 = sqrtf(11.0f); + + inline void ComputeCapInt(const size_t order, float angle, float *pR) + { + const float t2 = cosf(angle); + const float t3 = ComputeCapInt_t1*t2; + const float t7 = sinf(angle); + const float t8 = t7*t7; + + + pR[0] = -t3 + ComputeCapInt_t1; + pR[1] = ComputeCapInt_t5*ComputeCapInt_t1*t8 / 2.0f; + + if (order > 2) + { + const float t13 = t2*t2; + + pR[2] = -ComputeCapInt_t11*ComputeCapInt_t1*t2*(t13 - 1.0f) / 2.0f; + if (order > 3) + { + const float t19 = ComputeCapInt_t18*ComputeCapInt_t1; + const float t20 = t13*t13; + + pR[3] = -5.0f / 8.0f*t19*t20 + 3.0f / 4.0f*t19*t13 - t19 / 8.0f; + if (order > 4) + { + + + pR[4] = -3.0f / 8.0f*t3*(7.0f*t20 - 10.0f*t13 + 3.0f); + if (order > 5) + { + const float t33 = ComputeCapInt_t32*ComputeCapInt_t1; + pR[5] = -21.0f / 16.0f*t33*t20*t13 + 35.0f / 16.0f*t33*t20 - 15.0f / 16.0f*t33*t13 + t33 / 16.0f; + } + } + } + } + } + + // input pF only consists of Yl0 values, normalizes coefficients for directional + // lights. + inline float CosWtInt(const size_t order) + { + const float fCW0 = 0.25f; + const float fCW1 = 0.5f; + const float fCW2 = 5.0f / 16.0f; + //const float fCW3 = 0.0f; + const float fCW4 = -3.0f / 32.0f; + //const float fCW5 = 0.0f; + + // order has to be at least linear... + + float fRet = fCW0 + fCW1; + + if (order > 2) fRet += fCW2; + if (order > 4) fRet += fCW4; + + // odd degrees >= 3 evaluate to zero integrated against cosine... + + return fRet; + } + + const float SHEvalHemisphereLight_fSqrtPi = sqrtf(XM_PI); + const float SHEvalHemisphereLight_fSqrtPi3 = sqrtf(XM_PI / 3.0f); + + using REAL = float; +#define CONSTANT(x) (x ## f) + + // routine generated programmatically for evaluating SH basis for degree 1 + // inputs (x,y,z) are a point on the sphere (i.e., must be unit length) + // output is vector b with SH basis evaluated at (x,y,z). + // + inline void sh_eval_basis_1(REAL x, REAL y, REAL z, REAL b[4]) + { + /* m=0 */ + + // l=0 + const REAL p_0_0 = CONSTANT(0.282094791773878140); + b[0] = p_0_0; // l=0,m=0 + // l=1 + const REAL p_1_0 = CONSTANT(0.488602511902919920)*z; + b[2] = p_1_0; // l=1,m=0 + + + /* m=1 */ + + const REAL s1 = y; + const REAL c1 = x; + + // l=1 + const REAL p_1_1 = CONSTANT(-0.488602511902919920); + b[1] = p_1_1*s1; // l=1,m=-1 + b[3] = p_1_1*c1; // l=1,m=+1 + } + + // routine generated programmatically for evaluating SH basis for degree 2 + // inputs (x,y,z) are a point on the sphere (i.e., must be unit length) + // output is vector b with SH basis evaluated at (x,y,z). + // + inline void sh_eval_basis_2(REAL x, REAL y, REAL z, REAL b[9]) + { + const REAL z2 = z*z; + + + /* m=0 */ + + // l=0 + const REAL p_0_0 = CONSTANT(0.282094791773878140); + b[0] = p_0_0; // l=0,m=0 + // l=1 + const REAL p_1_0 = CONSTANT(0.488602511902919920)*z; + b[2] = p_1_0; // l=1,m=0 + // l=2 + const REAL p_2_0 = CONSTANT(0.946174695757560080)*z2 + CONSTANT(-0.315391565252520050); + b[6] = p_2_0; // l=2,m=0 + + + /* m=1 */ + + const REAL s1 = y; + const REAL c1 = x; + + // l=1 + const REAL p_1_1 = CONSTANT(-0.488602511902919920); + b[1] = p_1_1*s1; // l=1,m=-1 + b[3] = p_1_1*c1; // l=1,m=+1 + // l=2 + const REAL p_2_1 = CONSTANT(-1.092548430592079200)*z; + b[5] = p_2_1*s1; // l=2,m=-1 + b[7] = p_2_1*c1; // l=2,m=+1 + + + /* m=2 */ + + const REAL s2 = x*s1 + y*c1; + const REAL c2 = x*c1 - y*s1; + + // l=2 + const REAL p_2_2 = CONSTANT(0.546274215296039590); + b[4] = p_2_2*s2; // l=2,m=-2 + b[8] = p_2_2*c2; // l=2,m=+2 + } + + // routine generated programmatically for evaluating SH basis for degree 3 + // inputs (x,y,z) are a point on the sphere (i.e., must be unit length) + // output is vector b with SH basis evaluated at (x,y,z). + // + void sh_eval_basis_3(REAL x, REAL y, REAL z, REAL b[16]) + { + const REAL z2 = z*z; + + + /* m=0 */ + + // l=0 + const REAL p_0_0 = CONSTANT(0.282094791773878140); + b[0] = p_0_0; // l=0,m=0 + // l=1 + const REAL p_1_0 = CONSTANT(0.488602511902919920)*z; + b[2] = p_1_0; // l=1,m=0 + // l=2 + const REAL p_2_0 = CONSTANT(0.946174695757560080)*z2 + CONSTANT(-0.315391565252520050); + b[6] = p_2_0; // l=2,m=0 + // l=3 + const REAL p_3_0 = z*(CONSTANT(1.865881662950577000)*z2 + CONSTANT(-1.119528997770346200)); + b[12] = p_3_0; // l=3,m=0 + + + /* m=1 */ + + const REAL s1 = y; + const REAL c1 = x; + + // l=1 + const REAL p_1_1 = CONSTANT(-0.488602511902919920); + b[1] = p_1_1*s1; // l=1,m=-1 + b[3] = p_1_1*c1; // l=1,m=+1 + // l=2 + const REAL p_2_1 = CONSTANT(-1.092548430592079200)*z; + b[5] = p_2_1*s1; // l=2,m=-1 + b[7] = p_2_1*c1; // l=2,m=+1 + // l=3 + const REAL p_3_1 = CONSTANT(-2.285228997322328800)*z2 + CONSTANT(0.457045799464465770); + b[11] = p_3_1*s1; // l=3,m=-1 + b[13] = p_3_1*c1; // l=3,m=+1 + + + /* m=2 */ + + const REAL s2 = x*s1 + y*c1; + const REAL c2 = x*c1 - y*s1; + + // l=2 + const REAL p_2_2 = CONSTANT(0.546274215296039590); + b[4] = p_2_2*s2; // l=2,m=-2 + b[8] = p_2_2*c2; // l=2,m=+2 + // l=3 + const REAL p_3_2 = CONSTANT(1.445305721320277100)*z; + b[10] = p_3_2*s2; // l=3,m=-2 + b[14] = p_3_2*c2; // l=3,m=+2 + + + /* m=3 */ + + const REAL s3 = x*s2 + y*c2; + const REAL c3 = x*c2 - y*s2; + + // l=3 + const REAL p_3_3 = CONSTANT(-0.590043589926643520); + b[9] = p_3_3*s3; // l=3,m=-3 + b[15] = p_3_3*c3; // l=3,m=+3 + } + + // routine generated programmatically for evaluating SH basis for degree 4 + // inputs (x,y,z) are a point on the sphere (i.e., must be unit length) + // output is vector b with SH basis evaluated at (x,y,z). + // + void sh_eval_basis_4(REAL x, REAL y, REAL z, REAL b[25]) + { + const REAL z2 = z*z; + + + /* m=0 */ + + // l=0 + const REAL p_0_0 = CONSTANT(0.282094791773878140); + b[0] = p_0_0; // l=0,m=0 + // l=1 + const REAL p_1_0 = CONSTANT(0.488602511902919920)*z; + b[2] = p_1_0; // l=1,m=0 + // l=2 + const REAL p_2_0 = CONSTANT(0.946174695757560080)*z2 + CONSTANT(-0.315391565252520050); + b[6] = p_2_0; // l=2,m=0 + // l=3 + const REAL p_3_0 = z*(CONSTANT(1.865881662950577000)*z2 + CONSTANT(-1.119528997770346200)); + b[12] = p_3_0; // l=3,m=0 + // l=4 + const REAL p_4_0 = CONSTANT(1.984313483298443000)*z*p_3_0 + CONSTANT(-1.006230589874905300)*p_2_0; + b[20] = p_4_0; // l=4,m=0 + + + /* m=1 */ + + const REAL s1 = y; + const REAL c1 = x; + + // l=1 + const REAL p_1_1 = CONSTANT(-0.488602511902919920); + b[1] = p_1_1*s1; // l=1,m=-1 + b[3] = p_1_1*c1; // l=1,m=+1 + // l=2 + const REAL p_2_1 = CONSTANT(-1.092548430592079200)*z; + b[5] = p_2_1*s1; // l=2,m=-1 + b[7] = p_2_1*c1; // l=2,m=+1 + // l=3 + const REAL p_3_1 = CONSTANT(-2.285228997322328800)*z2 + CONSTANT(0.457045799464465770); + b[11] = p_3_1*s1; // l=3,m=-1 + b[13] = p_3_1*c1; // l=3,m=+1 + // l=4 + const REAL p_4_1 = z*(CONSTANT(-4.683325804901024000)*z2 + CONSTANT(2.007139630671867200)); + b[19] = p_4_1*s1; // l=4,m=-1 + b[21] = p_4_1*c1; // l=4,m=+1 + + + /* m=2 */ + + const REAL s2 = x*s1 + y*c1; + const REAL c2 = x*c1 - y*s1; + + // l=2 + const REAL p_2_2 = CONSTANT(0.546274215296039590); + b[4] = p_2_2*s2; // l=2,m=-2 + b[8] = p_2_2*c2; // l=2,m=+2 + // l=3 + const REAL p_3_2 = CONSTANT(1.445305721320277100)*z; + b[10] = p_3_2*s2; // l=3,m=-2 + b[14] = p_3_2*c2; // l=3,m=+2 + // l=4 + const REAL p_4_2 = CONSTANT(3.311611435151459800)*z2 + CONSTANT(-0.473087347878779980); + b[18] = p_4_2*s2; // l=4,m=-2 + b[22] = p_4_2*c2; // l=4,m=+2 + + + /* m=3 */ + + const REAL s3 = x*s2 + y*c2; + const REAL c3 = x*c2 - y*s2; + + // l=3 + const REAL p_3_3 = CONSTANT(-0.590043589926643520); + b[9] = p_3_3*s3; // l=3,m=-3 + b[15] = p_3_3*c3; // l=3,m=+3 + // l=4 + const REAL p_4_3 = CONSTANT(-1.770130769779930200)*z; + b[17] = p_4_3*s3; // l=4,m=-3 + b[23] = p_4_3*c3; // l=4,m=+3 + + + /* m=4 */ + + const REAL s4 = x*s3 + y*c3; + const REAL c4 = x*c3 - y*s3; + + // l=4 + const REAL p_4_4 = CONSTANT(0.625835735449176030); + b[16] = p_4_4*s4; // l=4,m=-4 + b[24] = p_4_4*c4; // l=4,m=+4 + } + + // routine generated programmatically for evaluating SH basis for degree 5 + // inputs (x,y,z) are a point on the sphere (i.e., must be unit length) + // output is vector b with SH basis evaluated at (x,y,z). + // + void sh_eval_basis_5(REAL x, REAL y, REAL z, REAL b[36]) + { + const REAL z2 = z*z; + + + /* m=0 */ + + // l=0 + const REAL p_0_0 = CONSTANT(0.282094791773878140); + b[0] = p_0_0; // l=0,m=0 + // l=1 + const REAL p_1_0 = CONSTANT(0.488602511902919920)*z; + b[2] = p_1_0; // l=1,m=0 + // l=2 + const REAL p_2_0 = CONSTANT(0.946174695757560080)*z2 + CONSTANT(-0.315391565252520050); + b[6] = p_2_0; // l=2,m=0 + // l=3 + const REAL p_3_0 = z*(CONSTANT(1.865881662950577000)*z2 + CONSTANT(-1.119528997770346200)); + b[12] = p_3_0; // l=3,m=0 + // l=4 + const REAL p_4_0 = CONSTANT(1.984313483298443000)*z*p_3_0 + CONSTANT(-1.006230589874905300)*p_2_0; + b[20] = p_4_0; // l=4,m=0 + // l=5 + const REAL p_5_0 = CONSTANT(1.989974874213239700)*z*p_4_0 + CONSTANT(-1.002853072844814000)*p_3_0; + b[30] = p_5_0; // l=5,m=0 + + + /* m=1 */ + + const REAL s1 = y; + const REAL c1 = x; + + // l=1 + const REAL p_1_1 = CONSTANT(-0.488602511902919920); + b[1] = p_1_1*s1; // l=1,m=-1 + b[3] = p_1_1*c1; // l=1,m=+1 + // l=2 + const REAL p_2_1 = CONSTANT(-1.092548430592079200)*z; + b[5] = p_2_1*s1; // l=2,m=-1 + b[7] = p_2_1*c1; // l=2,m=+1 + // l=3 + const REAL p_3_1 = CONSTANT(-2.285228997322328800)*z2 + CONSTANT(0.457045799464465770); + b[11] = p_3_1*s1; // l=3,m=-1 + b[13] = p_3_1*c1; // l=3,m=+1 + // l=4 + const REAL p_4_1 = z*(CONSTANT(-4.683325804901024000)*z2 + CONSTANT(2.007139630671867200)); + b[19] = p_4_1*s1; // l=4,m=-1 + b[21] = p_4_1*c1; // l=4,m=+1 + // l=5 + const REAL p_5_1 = CONSTANT(2.031009601158990200)*z*p_4_1 + CONSTANT(-0.991031208965114650)*p_3_1; + b[29] = p_5_1*s1; // l=5,m=-1 + b[31] = p_5_1*c1; // l=5,m=+1 + + + /* m=2 */ + + const REAL s2 = x*s1 + y*c1; + const REAL c2 = x*c1 - y*s1; + + // l=2 + const REAL p_2_2 = CONSTANT(0.546274215296039590); + b[4] = p_2_2*s2; // l=2,m=-2 + b[8] = p_2_2*c2; // l=2,m=+2 + // l=3 + const REAL p_3_2 = CONSTANT(1.445305721320277100)*z; + b[10] = p_3_2*s2; // l=3,m=-2 + b[14] = p_3_2*c2; // l=3,m=+2 + // l=4 + const REAL p_4_2 = CONSTANT(3.311611435151459800)*z2 + CONSTANT(-0.473087347878779980); + b[18] = p_4_2*s2; // l=4,m=-2 + b[22] = p_4_2*c2; // l=4,m=+2 + // l=5 + const REAL p_5_2 = z*(CONSTANT(7.190305177459987500)*z2 + CONSTANT(-2.396768392486662100)); + b[28] = p_5_2*s2; // l=5,m=-2 + b[32] = p_5_2*c2; // l=5,m=+2 + + + /* m=3 */ + + const REAL s3 = x*s2 + y*c2; + const REAL c3 = x*c2 - y*s2; + + // l=3 + const REAL p_3_3 = CONSTANT(-0.590043589926643520); + b[9] = p_3_3*s3; // l=3,m=-3 + b[15] = p_3_3*c3; // l=3,m=+3 + // l=4 + const REAL p_4_3 = CONSTANT(-1.770130769779930200)*z; + b[17] = p_4_3*s3; // l=4,m=-3 + b[23] = p_4_3*c3; // l=4,m=+3 + // l=5 + const REAL p_5_3 = CONSTANT(-4.403144694917253700)*z2 + CONSTANT(0.489238299435250430); + b[27] = p_5_3*s3; // l=5,m=-3 + b[33] = p_5_3*c3; // l=5,m=+3 + + + /* m=4 */ + + const REAL s4 = x*s3 + y*c3; + const REAL c4 = x*c3 - y*s3; + + // l=4 + const REAL p_4_4 = CONSTANT(0.625835735449176030); + b[16] = p_4_4*s4; // l=4,m=-4 + b[24] = p_4_4*c4; // l=4,m=+4 + // l=5 + const REAL p_5_4 = CONSTANT(2.075662314881041100)*z; + b[26] = p_5_4*s4; // l=5,m=-4 + b[34] = p_5_4*c4; // l=5,m=+4 + + + /* m=5 */ + + const REAL s5 = x*s4 + y*c4; + const REAL c5 = x*c4 - y*s4; + + // l=5 + const REAL p_5_5 = CONSTANT(-0.656382056840170150); + b[25] = p_5_5*s5; // l=5,m=-5 + b[35] = p_5_5*c5; // l=5,m=+5 + } + + const REAL M_PIjs = (REAL)(4.0*atan(1.0)); + const REAL maxang = (REAL)(M_PIjs / 2); + const int NSH0 = 1; + const int NSH1 = 4; + const int NSH2 = 9; + const int NSH3 = 16; + const int NSH4 = 25; + const int NSH5 = 36; + const int NSH6 = 49; + const int NSH7 = 64; + const int NSH8 = 81; + const int NSH9 = 100; + const int NL0 = 1; + const int NL1 = 3; + const int NL2 = 5; + const int NL3 = 7; + const int NL4 = 9; + const int NL5 = 11; + const int NL6 = 13; + const int NL7 = 15; + const int NL8 = 17; + const int NL9 = 19; + + inline void rot(REAL ct, REAL st, REAL x, REAL y, REAL &xout, REAL &yout) + { + xout = x*ct - y*st; + yout = y*ct + x*st; + } + + inline void rot_inv(REAL ct, REAL st, REAL x, REAL y, REAL &xout, REAL &yout) + { + xout = x*ct + y*st; + yout = y*ct - x*st; + } + + inline void rot_1(REAL ct, REAL st, REAL ctm[1], REAL stm[1]) + { + ctm[0] = ct; + stm[0] = st; + } + + inline void rot_2(REAL ct, REAL st, REAL ctm[2], REAL stm[2]) + { + REAL ct2 = CONSTANT(2.0)*ct; + ctm[0] = ct; + stm[0] = st; + ctm[1] = ct2*ct - CONSTANT(1.0); + stm[1] = ct2*st; + } + + inline void rot_3(REAL ct, REAL st, REAL ctm[3], REAL stm[3]) + { + REAL ct2 = CONSTANT(2.0)*ct; + ctm[0] = ct; + stm[0] = st; + ctm[1] = ct2*ct - CONSTANT(1.0); + stm[1] = ct2*st; + ctm[2] = ct2*ctm[1] - ct; + stm[2] = ct2*stm[1] - st; + } + + inline void rot_4(REAL ct, REAL st, REAL ctm[4], REAL stm[4]) + { + REAL ct2 = CONSTANT(2.0)*ct; + ctm[0] = ct; + stm[0] = st; + ctm[1] = ct2*ct - CONSTANT(1.0); + stm[1] = ct2*st; + ctm[2] = ct2*ctm[1] - ct; + stm[2] = ct2*stm[1] - st; + ctm[3] = ct2*ctm[2] - ctm[1]; + stm[3] = ct2*stm[2] - stm[1]; + } + + inline void rot_5(REAL ct, REAL st, REAL ctm[5], REAL stm[5]) + { + REAL ct2 = CONSTANT(2.0)*ct; + ctm[0] = ct; + stm[0] = st; + ctm[1] = ct2*ct - CONSTANT(1.0); + stm[1] = ct2*st; + ctm[2] = ct2*ctm[1] - ct; + stm[2] = ct2*stm[1] - st; + ctm[3] = ct2*ctm[2] - ctm[1]; + stm[3] = ct2*stm[2] - stm[1]; + ctm[4] = ct2*ctm[3] - ctm[2]; + stm[4] = ct2*stm[3] - stm[2]; + } + + inline void sh_rotz_1(REAL ctm[1], REAL stm[1], REAL y[NL1], REAL yr[NL1]) + { + yr[1] = y[1]; + rot_inv(ctm[0], stm[0], y[0], y[2], yr[0], yr[2]); + } + + inline void sh_rotz_2(REAL ctm[2], REAL stm[2], REAL y[NL2], REAL yr[NL2]) + { + yr[2] = y[2]; + rot_inv(ctm[0], stm[0], y[1], y[3], yr[1], yr[3]); + rot_inv(ctm[1], stm[1], y[0], y[4], yr[0], yr[4]); + } + + inline void sh_rotz_3(REAL ctm[3], REAL stm[3], REAL y[NL3], REAL yr[NL3]) + { + yr[3] = y[3]; + rot_inv(ctm[0], stm[0], y[2], y[4], yr[2], yr[4]); + rot_inv(ctm[1], stm[1], y[1], y[5], yr[1], yr[5]); + rot_inv(ctm[2], stm[2], y[0], y[6], yr[0], yr[6]); + } + + inline void sh_rotz_4(REAL ctm[4], REAL stm[4], REAL y[NL4], REAL yr[NL4]) + { + yr[4] = y[4]; + rot_inv(ctm[0], stm[0], y[3], y[5], yr[3], yr[5]); + rot_inv(ctm[1], stm[1], y[2], y[6], yr[2], yr[6]); + rot_inv(ctm[2], stm[2], y[1], y[7], yr[1], yr[7]); + rot_inv(ctm[3], stm[3], y[0], y[8], yr[0], yr[8]); + } + + inline void sh_rotz_5(REAL ctm[5], REAL stm[5], REAL y[NL5], REAL yr[NL5]) + { + yr[5] = y[5]; + rot_inv(ctm[0], stm[0], y[4], y[6], yr[4], yr[6]); + rot_inv(ctm[1], stm[1], y[3], y[7], yr[3], yr[7]); + rot_inv(ctm[2], stm[2], y[2], y[8], yr[2], yr[8]); + rot_inv(ctm[3], stm[3], y[1], y[9], yr[1], yr[9]); + rot_inv(ctm[4], stm[4], y[0], y[10], yr[0], yr[10]); + } + + // rotation code generated programmatically by rotatex (2000x4000 samples, eps=1e-008) + + const REAL fx_1_001 = (REAL)(sqrt(1.0) / 1.0); // 1 + const REAL fx_1_002 = (REAL)(-sqrt(1.0) / 1.0); // -1.00000030843 + + inline void sh_rotx90_1(REAL y[], REAL yr[]) + { + yr[0] = fx_1_001*y[1]; + yr[1] = fx_1_002*y[0]; + yr[2] = fx_1_001*y[2]; + }; + + inline void sh_rotx90_inv_1(REAL y[], REAL yr[]) + { + yr[0] = fx_1_002*y[1]; + yr[1] = fx_1_001*y[0]; + yr[2] = fx_1_001*y[2]; + } + + const REAL fx_2_001 = (REAL)(sqrt(4.0) / 2.0); // 1 + const REAL fx_2_002 = (REAL)(-sqrt(4.0) / 2.0); // -1 + const REAL fx_2_003 = (REAL)(-sqrt(1.0) / 2.0); // -0.500000257021 + const REAL fx_2_004 = (REAL)(-sqrt(3.0) / 2.0); // -0.866025848959 + const REAL fx_2_005 = (REAL)(sqrt(1.0) / 2.0); // 0.5 + + inline void sh_rotx90_2(REAL y[], REAL yr[]) + { + yr[0] = fx_2_001*y[3]; + yr[1] = fx_2_002*y[1]; + yr[2] = fx_2_003*y[2] + fx_2_004*y[4]; + yr[3] = fx_2_002*y[0]; + yr[4] = fx_2_004*y[2] + fx_2_005*y[4]; + }; + + inline void sh_rotx90_inv_2(REAL y[], REAL yr[]) + { + yr[0] = fx_2_002*y[3]; + yr[1] = fx_2_002*y[1]; + yr[2] = fx_2_003*y[2] + fx_2_004*y[4]; + yr[3] = fx_2_001*y[0]; + yr[4] = fx_2_004*y[2] + fx_2_005*y[4]; + } + + const REAL fx_3_001 = (REAL)(-sqrt(10.0) / 4.0); // -0.790569415042 + const REAL fx_3_002 = (REAL)(sqrt(6.0) / 4.0); // 0.612372435696 + const REAL fx_3_003 = (REAL)(-sqrt(16.0) / 4.0); // -1 + const REAL fx_3_004 = (REAL)(-sqrt(6.0) / 4.0); // -0.612372435695 + const REAL fx_3_005 = (REAL)(-sqrt(1.0) / 4.0); // -0.25 + const REAL fx_3_006 = (REAL)(-sqrt(15.0) / 4.0); // -0.968245836551 + const REAL fx_3_007 = (REAL)(sqrt(1.0) / 4.0); // 0.25 + const REAL fx_3_008 = (REAL)(sqrt(10.0) / 4.0); // 0.790569983984 + + inline void sh_rotx90_3(REAL y[], REAL yr[]) + { + yr[0] = fx_3_001*y[3] + fx_3_002*y[5]; + yr[1] = fx_3_003*y[1]; + yr[2] = fx_3_004*y[3] + fx_3_001*y[5]; + yr[3] = fx_3_008*y[0] + fx_3_002*y[2]; + yr[4] = fx_3_005*y[4] + fx_3_006*y[6]; + yr[5] = fx_3_004*y[0] - fx_3_001*y[2]; + yr[6] = fx_3_006*y[4] + fx_3_007*y[6]; + }; + + inline void sh_rotx90_inv_3(REAL y[], REAL yr[]) + { + yr[0] = fx_3_008*y[3] + fx_3_004*y[5]; + yr[1] = fx_3_003*y[1]; + yr[2] = fx_3_002*y[3] - fx_3_001*y[5]; + yr[3] = fx_3_001*y[0] + fx_3_004*y[2]; + yr[4] = fx_3_005*y[4] + fx_3_006*y[6]; + yr[5] = fx_3_002*y[0] + fx_3_001*y[2]; + yr[6] = fx_3_006*y[4] + fx_3_007*y[6]; + } + + const REAL fx_4_001 = (REAL)(-sqrt(56.0) / 8.0); // -0.935414346694 + const REAL fx_4_002 = (REAL)(sqrt(8.0) / 8.0); // 0.353553390593 + const REAL fx_4_003 = (REAL)(-sqrt(36.0) / 8.0); // -0.75 + const REAL fx_4_004 = (REAL)(sqrt(28.0) / 8.0); // 0.661437827766 + const REAL fx_4_005 = (REAL)(-sqrt(8.0) / 8.0); // -0.353553390593 + const REAL fx_4_006 = (REAL)(sqrt(36.0) / 8.0); // 0.749999999999 + const REAL fx_4_007 = (REAL)(sqrt(9.0) / 8.0); // 0.37500034698 + const REAL fx_4_008 = (REAL)(sqrt(20.0) / 8.0); // 0.559017511622 + const REAL fx_4_009 = (REAL)(sqrt(35.0) / 8.0); // 0.739510657141 + const REAL fx_4_010 = (REAL)(sqrt(16.0) / 8.0); // 0.5 + const REAL fx_4_011 = (REAL)(-sqrt(28.0) / 8.0); // -0.661437827766 + const REAL fx_4_012 = (REAL)(sqrt(1.0) / 8.0); // 0.125 + const REAL fx_4_013 = (REAL)(sqrt(56.0) / 8.0); // 0.935414346692 + + inline void sh_rotx90_4(REAL y[], REAL yr[]) + { + yr[0] = fx_4_001*y[5] + fx_4_002*y[7]; + yr[1] = fx_4_003*y[1] + fx_4_004*y[3]; + yr[2] = fx_4_005*y[5] + fx_4_001*y[7]; + yr[3] = fx_4_004*y[1] + fx_4_006*y[3]; + yr[4] = fx_4_007*y[4] + fx_4_008*y[6] + fx_4_009*y[8]; + yr[5] = fx_4_013*y[0] + fx_4_002*y[2]; + yr[6] = fx_4_008*y[4] + fx_4_010*y[6] + fx_4_011*y[8]; + yr[7] = fx_4_005*y[0] - fx_4_001*y[2]; + yr[8] = fx_4_009*y[4] + fx_4_011*y[6] + fx_4_012*y[8]; + }; + + inline void sh_rotx90_inv_4(REAL y[], REAL yr[]) + { + yr[0] = fx_4_013*y[5] + fx_4_005*y[7]; + yr[1] = fx_4_003*y[1] + fx_4_004*y[3]; + yr[2] = fx_4_002*y[5] - fx_4_001*y[7]; + yr[3] = fx_4_004*y[1] + fx_4_006*y[3]; + yr[4] = fx_4_007*y[4] + fx_4_008*y[6] + fx_4_009*y[8]; + yr[5] = fx_4_001*y[0] + fx_4_005*y[2]; + yr[6] = fx_4_008*y[4] + fx_4_010*y[6] + fx_4_011*y[8]; + yr[7] = fx_4_002*y[0] + fx_4_001*y[2]; + yr[8] = fx_4_009*y[4] + fx_4_011*y[6] + fx_4_012*y[8]; + } + + const REAL fx_5_001 = (REAL)(sqrt(126.0) / 16.0); // 0.70156076002 + const REAL fx_5_002 = (REAL)(-sqrt(120.0) / 16.0); // -0.684653196882 + const REAL fx_5_003 = (REAL)(sqrt(10.0) / 16.0); // 0.197642353761 + const REAL fx_5_004 = (REAL)(-sqrt(64.0) / 16.0); // -0.5 + const REAL fx_5_005 = (REAL)(sqrt(192.0) / 16.0); // 0.866025403784 + const REAL fx_5_006 = (REAL)(sqrt(70.0) / 16.0); // 0.522912516584 + const REAL fx_5_007 = (REAL)(sqrt(24.0) / 16.0); // 0.306186217848 + const REAL fx_5_008 = (REAL)(-sqrt(162.0) / 16.0); // -0.795495128835 + const REAL fx_5_009 = (REAL)(sqrt(64.0) / 16.0); // 0.5 + const REAL fx_5_010 = (REAL)(sqrt(60.0) / 16.0); // 0.484122918274 + const REAL fx_5_011 = (REAL)(sqrt(112.0) / 16.0); // 0.661437827763 + const REAL fx_5_012 = (REAL)(sqrt(84.0) / 16.0); // 0.572821961867 + const REAL fx_5_013 = (REAL)(sqrt(4.0) / 16.0); // 0.125 + const REAL fx_5_014 = (REAL)(sqrt(42.0) / 16.0); // 0.405046293649 + const REAL fx_5_015 = (REAL)(sqrt(210.0) / 16.0); // 0.905711046633 + const REAL fx_5_016 = (REAL)(sqrt(169.0) / 16.0); // 0.8125 + const REAL fx_5_017 = (REAL)(-sqrt(45.0) / 16.0); // -0.419262745781 + const REAL fx_5_018 = (REAL)(sqrt(1.0) / 16.0); // 0.0625 + const REAL fx_5_019 = (REAL)(-sqrt(126.0) / 16.0); // -0.701561553415 + const REAL fx_5_020 = (REAL)(sqrt(120.0) / 16.0); // 0.684653196881 + const REAL fx_5_021 = (REAL)(-sqrt(10.0) / 16.0); // -0.197642353761 + const REAL fx_5_022 = (REAL)(-sqrt(70.0) / 16.0); // -0.522913107945 + const REAL fx_5_023 = (REAL)(-sqrt(60.0) / 16.0); // -0.48412346577 + + inline void sh_rotx90_5(REAL y[], REAL yr[]) + { + yr[0] = fx_5_001*y[5] + fx_5_002*y[7] + fx_5_003*y[9]; + yr[1] = fx_5_004*y[1] + fx_5_005*y[3]; + yr[2] = fx_5_006*y[5] + fx_5_007*y[7] + fx_5_008*y[9]; + yr[3] = fx_5_005*y[1] + fx_5_009*y[3]; + yr[4] = fx_5_010*y[5] + fx_5_011*y[7] + fx_5_012*y[9]; + yr[5] = fx_5_019*y[0] + fx_5_022*y[2] + fx_5_023*y[4]; + yr[6] = fx_5_013*y[6] + fx_5_014*y[8] + fx_5_015*y[10]; + yr[7] = fx_5_020*y[0] - fx_5_007*y[2] - fx_5_011*y[4]; + yr[8] = fx_5_014*y[6] + fx_5_016*y[8] + fx_5_017*y[10]; + yr[9] = fx_5_021*y[0] - fx_5_008*y[2] - fx_5_012*y[4]; + yr[10] = fx_5_015*y[6] + fx_5_017*y[8] + fx_5_018*y[10]; + }; + + inline void sh_rotx90_inv_5(REAL y[], REAL yr[]) + { + yr[0] = fx_5_019*y[5] + fx_5_020*y[7] + fx_5_021*y[9]; + yr[1] = fx_5_004*y[1] + fx_5_005*y[3]; + yr[2] = fx_5_022*y[5] - fx_5_007*y[7] - fx_5_008*y[9]; + yr[3] = fx_5_005*y[1] + fx_5_009*y[3]; + yr[4] = fx_5_023*y[5] - fx_5_011*y[7] - fx_5_012*y[9]; + yr[5] = fx_5_001*y[0] + fx_5_006*y[2] + fx_5_010*y[4]; + yr[6] = fx_5_013*y[6] + fx_5_014*y[8] + fx_5_015*y[10]; + yr[7] = fx_5_002*y[0] + fx_5_007*y[2] + fx_5_011*y[4]; + yr[8] = fx_5_014*y[6] + fx_5_016*y[8] + fx_5_017*y[10]; + yr[9] = fx_5_003*y[0] + fx_5_008*y[2] + fx_5_012*y[4]; + yr[10] = fx_5_015*y[6] + fx_5_017*y[8] + fx_5_018*y[10]; + } + + inline void sh_rot_1(REAL m[3 * 3], REAL y[NL1], REAL yr[NL1]) + { + REAL yr0 = m[4] * y[0] - m[5] * y[1] + m[3] * y[2]; + REAL yr1 = m[8] * y[1] - m[7] * y[0] - m[6] * y[2]; + REAL yr2 = m[1] * y[0] - m[2] * y[1] + m[0] * y[2]; + + yr[0] = yr0; + yr[1] = yr1; + yr[2] = yr2; + } + + inline void sh_roty_1(REAL ctm[1], REAL stm[1], REAL y[NL1], REAL yr[NL1]) + { + yr[0] = y[0]; + rot_inv(ctm[0], stm[0], y[1], y[2], yr[1], yr[2]); + } + + inline void sh_roty_2(REAL ctm[2], REAL stm[2], REAL y[NL2], REAL yr[NL2]) + { + REAL ytmp[NL2]; + sh_rotx90_2(y, yr); + sh_rotz_2(ctm, stm, yr, ytmp); + sh_rotx90_inv_2(ytmp, yr); + } + + inline void sh_roty_3(REAL ctm[3], REAL stm[3], REAL y[NL3], REAL yr[NL3]) + { + REAL ytmp[NL3]; + sh_rotx90_3(y, yr); + sh_rotz_3(ctm, stm, yr, ytmp); + sh_rotx90_inv_3(ytmp, yr); + } + + inline void sh_roty_4(REAL ctm[4], REAL stm[4], REAL y[NL4], REAL yr[NL4]) + { + REAL ytmp[NL4]; + sh_rotx90_4(y, yr); + sh_rotz_4(ctm, stm, yr, ytmp); + sh_rotx90_inv_4(ytmp, yr); + } + + inline void sh_roty_5(REAL ctm[5], REAL stm[5], REAL y[NL5], REAL yr[NL5]) + { + REAL ytmp[NL5]; + sh_rotx90_5(y, yr); + sh_rotz_5(ctm, stm, yr, ytmp); + sh_rotx90_inv_5(ytmp, yr); + } + +#define ROT_TOL CONSTANT(1e-4) + + /* + Finds cosine,sine pairs for zyz rotation (i.e. rotation R_z2 R_y R_z1 v). + The rotation is one which maps mx to (1,0,0) and mz to (0,0,1). + */ + inline void zyz(REAL m[3 * 3], REAL &zc1, REAL &zs1, REAL &yc, REAL &ys, REAL &zc2, REAL &zs2) + { + REAL cz = m[8]; + + // rotate so that (cx,cy,0) aligns to (1,0,0) + REAL cxylen = (REAL)sqrtf(1.0f - cz*cz); + if (cxylen >= ROT_TOL) + { + // if above is a NaN, will do the correct thing + yc = cz; + ys = cxylen; + REAL len67inv = 1.0f / sqrtf(m[6] * m[6] + m[7] * m[7]); + zc1 = -m[6] * len67inv; + zs1 = m[7] * len67inv; + REAL len25inv = 1.0f / sqrtf(m[2] * m[2] + m[5] * m[5]); + zc2 = m[2] * len25inv; + zs2 = m[5] * len25inv; + } + else + { // m[6],m[7],m[8] already aligned to (0,0,1) + zc1 = 1.0; zs1 = 0.0; // identity + yc = cz; ys = 0.0; // identity + zc2 = m[0] * cz; zs2 = -m[1]; // align x axis (mx[0],mx[1],0) to (1,0,0) + } + } + + inline void sh_rotzyz_2(REAL zc1m[2], REAL zs1m[2], REAL ycm[2], REAL ysm[2], REAL zc2m[2], REAL zs2m[2], REAL y[NL2], REAL yr[NL2]) + { + REAL ytmp[NL2]; + sh_rotz_2(zc1m, zs1m, y, yr); + sh_roty_2(ycm, ysm, yr, ytmp); + sh_rotz_2(zc2m, zs2m, ytmp, yr); + } + + inline void sh_rotzyz_3(REAL zc1m[3], REAL zs1m[3], REAL ycm[3], REAL ysm[3], REAL zc2m[3], REAL zs2m[3], REAL y[NL3], REAL yr[NL3]) + { + REAL ytmp[NL3]; + sh_rotz_3(zc1m, zs1m, y, yr); + sh_roty_3(ycm, ysm, yr, ytmp); + sh_rotz_3(zc2m, zs2m, ytmp, yr); + } + + inline void sh_rotzyz_4(REAL zc1m[4], REAL zs1m[4], REAL ycm[4], REAL ysm[4], REAL zc2m[4], REAL zs2m[4], REAL y[NL4], REAL yr[NL4]) + { + REAL ytmp[NL4]; + sh_rotz_4(zc1m, zs1m, y, yr); + sh_roty_4(ycm, ysm, yr, ytmp); + sh_rotz_4(zc2m, zs2m, ytmp, yr); + } + + inline void sh_rotzyz_5(REAL zc1m[5], REAL zs1m[5], REAL ycm[5], REAL ysm[5], REAL zc2m[5], REAL zs2m[5], REAL y[NL5], REAL yr[NL5]) + { + REAL ytmp[NL5]; + sh_rotz_5(zc1m, zs1m, y, yr); + sh_roty_5(ycm, ysm, yr, ytmp); + sh_rotz_5(zc2m, zs2m, ytmp, yr); + } + + inline void sh3_rot(REAL m[3 * 3], REAL zc1, REAL zs1, REAL yc, REAL ys, REAL zc2, REAL zs2, REAL y[NSH3], REAL yr[NSH3]) + { + REAL zc1m[3], zs1m[3]; + rot_3(zc1, zs1, zc1m, zs1m); + REAL ycm[3], ysm[3]; + rot_3(yc, ys, ycm, ysm); + REAL zc2m[3], zs2m[3]; + rot_3(zc2, zs2, zc2m, zs2m); + + yr[0] = y[0]; + sh_rot_1(m, y + NSH0, yr + NSH0); + sh_rotzyz_2(zc1m, zs1m, ycm, ysm, zc2m, zs2m, y + NSH1, yr + NSH1); + sh_rotzyz_3(zc1m, zs1m, ycm, ysm, zc2m, zs2m, y + NSH2, yr + NSH2); + } + + inline void sh4_rot(REAL m[3 * 3], REAL zc1, REAL zs1, REAL yc, REAL ys, REAL zc2, REAL zs2, REAL y[NSH4], REAL yr[NSH4]) + { + REAL zc1m[4], zs1m[4]; + rot_4(zc1, zs1, zc1m, zs1m); + REAL ycm[4], ysm[4]; + rot_4(yc, ys, ycm, ysm); + REAL zc2m[4], zs2m[4]; + rot_4(zc2, zs2, zc2m, zs2m); + + yr[0] = y[0]; + sh_rot_1(m, y + NSH0, yr + NSH0); + sh_rotzyz_2(zc1m, zs1m, ycm, ysm, zc2m, zs2m, y + NSH1, yr + NSH1); + sh_rotzyz_3(zc1m, zs1m, ycm, ysm, zc2m, zs2m, y + NSH2, yr + NSH2); + sh_rotzyz_4(zc1m, zs1m, ycm, ysm, zc2m, zs2m, y + NSH3, yr + NSH3); + } + + inline void sh5_rot(REAL m[3 * 3], REAL zc1, REAL zs1, REAL yc, REAL ys, REAL zc2, REAL zs2, REAL y[NSH5], REAL yr[NSH5]) + { + REAL zc1m[5], zs1m[5]; + rot_5(zc1, zs1, zc1m, zs1m); + REAL ycm[5], ysm[5]; + rot_5(yc, ys, ycm, ysm); + REAL zc2m[5], zs2m[5]; + rot_5(zc2, zs2, zc2m, zs2m); + + yr[0] = y[0]; + sh_rot_1(m, y + NSH0, yr + NSH0); + sh_rotzyz_2(zc1m, zs1m, ycm, ysm, zc2m, zs2m, y + NSH1, yr + NSH1); + sh_rotzyz_3(zc1m, zs1m, ycm, ysm, zc2m, zs2m, y + NSH2, yr + NSH2); + sh_rotzyz_4(zc1m, zs1m, ycm, ysm, zc2m, zs2m, y + NSH3, yr + NSH3); + sh_rotzyz_5(zc1m, zs1m, ycm, ysm, zc2m, zs2m, y + NSH4, yr + NSH4); + } + + inline void sh1_rot(REAL m[3 * 3], REAL y[NSH1], REAL yr[NSH1]) + { + yr[0] = y[0]; + sh_rot_1(m, y + NSH0, yr + NSH0); + } + + inline void sh3_rot(REAL m[3 * 3], REAL y[NSH3], REAL yr[NSH3]) + { + REAL zc1, zs1, yc, ys, zc2, zs2; + zyz(m, zc1, zs1, yc, ys, zc2, zs2); + sh3_rot(m, zc1, zs1, yc, ys, zc2, zs2, y, yr); + } + + inline void sh4_rot(REAL m[3 * 3], REAL y[NSH4], REAL yr[NSH4]) + { + REAL zc1, zs1, yc, ys, zc2, zs2; + zyz(m, zc1, zs1, yc, ys, zc2, zs2); + sh4_rot(m, zc1, zs1, yc, ys, zc2, zs2, y, yr); + } + + inline void sh5_rot(REAL m[3 * 3], REAL y[NSH5], REAL yr[NSH5]) + { + REAL zc1, zs1, yc, ys, zc2, zs2; + zyz(m, zc1, zs1, yc, ys, zc2, zs2); + sh5_rot(m, zc1, zs1, yc, ys, zc2, zs2, y, yr); + } + + // simple matrix vector multiply for a square matrix (only used by ZRotation) + inline void SimpMatMul(size_t dim, const float *matrix, const float *input, float *result) + { + for (size_t iR = 0; iR < dim; ++iR) + { + result[iR + 0] = matrix[iR*dim + 0] * input[0]; + for (size_t iC = 1; iC < dim; ++iC) + { + result[iR] += matrix[iR*dim + iC] * input[iC]; + } + } + } + +}; // anonymous namespace + + +//------------------------------------------------------------------------------------- +// Evaluates the Spherical Harmonic basis functions +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb205448.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* XM_CALLCONV DirectX::XMSHEvalDirection( + float *result, + size_t order, + FXMVECTOR dir) noexcept +{ + if (!result) + return nullptr; + + XMFLOAT4A dv; + XMStoreFloat4A(&dv, dir); + + const float fX = dv.x; + const float fY = dv.y; + const float fZ = dv.z; + + switch (order) + { + case 2: + sh_eval_basis_1(fX, fY, fZ, result); + break; + + case 3: + sh_eval_basis_2(fX, fY, fZ, result); + break; + + case 4: + sh_eval_basis_3(fX, fY, fZ, result); + break; + + case 5: + sh_eval_basis_4(fX, fY, fZ, result); + break; + + case 6: + sh_eval_basis_5(fX, fY, fZ, result); + break; + + default: + assert(order < XM_SH_MINORDER || order > XM_SH_MAXORDER); + return nullptr; + } + + return result; +} + + +//------------------------------------------------------------------------------------- +// Rotates SH vector by a rotation matrix +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb204992.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* XM_CALLCONV DirectX::XMSHRotate( + float *result, + size_t order, + FXMMATRIX rotMatrix, + const float *input) noexcept +{ + if (!result || !input) + return nullptr; + + if (result == input) + return nullptr; + + XMFLOAT3X3 mat; + XMStoreFloat3x3(&mat, rotMatrix); + + float mRot[3 * 3]; + const float r00 = mRot[0 * 3 + 0] = mat._11; + const float r10 = mRot[1 * 3 + 0] = mat._12; + const float r20 = mRot[2 * 3 + 0] = mat._13; + + const float r01 = mRot[0 * 3 + 1] = mat._21; + const float r11 = mRot[1 * 3 + 1] = mat._22; + const float r21 = mRot[2 * 3 + 1] = mat._23; + + const float r02 = mRot[0 * 3 + 2] = mat._31; + const float r12 = mRot[1 * 3 + 2] = mat._32; + const float r22 = mRot[2 * 3 + 2] = mat._33; + + result[0] = input[0]; // rotate the constant term + + switch (order) + { + case 2: + { + // do linear by hand... + + result[1] = r11*input[1] - r12*input[2] + r10*input[3]; + result[2] = -r21*input[1] + r22*input[2] - r20*input[3]; + result[3] = r01*input[1] - r02*input[2] + r00*input[3]; + } + break; + + case 3: + { + float R[25]; + // do linear by hand... + + result[1] = r11*input[1] - r12*input[2] + r10*input[3]; + result[2] = -r21*input[1] + r22*input[2] - r20*input[3]; + result[3] = r01*input[1] - r02*input[2] + r00*input[3]; + + // direct code for quadratics is faster than ZYZ reccurence relations + + const float t41 = r01 * r00; + const float t43 = r11 * r10; + const float t48 = r11 * r12; + const float t50 = r01 * r02; + const float t55 = r02 * r02; + const float t57 = r22 * r22; + const float t58 = r12 * r12; + const float t61 = r00 * r02; + const float t63 = r10 * r12; + const float t68 = r10 * r10; + const float t70 = r01 * r01; + const float t72 = r11 * r11; + const float t74 = r00 * r00; + const float t76 = r21 * r21; + const float t78 = r20 * r20; + + const float v173 = 0.1732050808e1f; + const float v577 = 0.5773502693e0f; + const float v115 = 0.1154700539e1f; + const float v288 = 0.2886751347e0f; + const float v866 = 0.8660254040e0f; + + R[0] = r11 * r00 + r01 * r10; + R[1] = -r01 * r12 - r11 * r02; + R[2] = v173 * r02 * r12; + R[3] = -r10 * r02 - r00 * r12; + R[4] = r00 * r10 - r01 * r11; + R[5] = -r11 * r20 - r21 * r10; + R[6] = r11 * r22 + r21 * r12; + R[7] = -v173 * r22 * r12; + R[8] = r20 * r12 + r10 * r22; + R[9] = -r10 * r20 + r11 * r21; + R[10] = -v577* (t41 + t43) + v115 * r21 * r20; + R[11] = v577* (t48 + t50) - v115 * r21 * r22; + R[12] = -0.5000000000e0f * (t55 + t58) + t57; + R[13] = v577 * (t61 + t63) - v115 * r20 * r22; + R[14] = v288 * (t70 - t68 + t72 - t74) - v577 * (t76 - t78); + R[15] = -r01 * r20 - r21 * r00; + R[16] = r01 * r22 + r21 * r02; + R[17] = -v173 * r22 * r02; + R[18] = r00 * r22 + r20 * r02; + R[19] = -r00 * r20 + r01 * r21; + R[20] = t41 - t43; + R[21] = -t50 + t48; + R[22] = v866 * (t55 - t58); + R[23] = t63 - t61; + R[24] = 0.5000000000e0f *(t74 - t68 - t70 + t72); + + // blow the matrix multiply out by hand, looping is ineficient on a P4... + for (unsigned int iR = 0; iR < 5; iR++) + { + const unsigned int uBase = iR * 5; + result[4 + iR] = R[uBase + 0] * input[4] + R[uBase + 1] * input[5] + R[uBase + 2] * input[6] + R[uBase + 3] * input[7] + R[uBase + 4] * input[8]; + } + } + break; + + case 4: + sh3_rot(mRot, const_cast(input), result); + break; + + case 5: + sh4_rot(mRot, const_cast(input), result); + break; + + case 6: + sh5_rot(mRot, const_cast(input), result); + break; + + default: + assert(order < XM_SH_MINORDER || order > XM_SH_MAXORDER); + return nullptr; + } + + return result; +} + + +//------------------------------------------------------------------------------------- +// Rotates the SH vector in the Z axis by an angle +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb205461.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* DirectX::XMSHRotateZ( + float *result, + size_t order, + float angle, + const float *input) noexcept +{ + if (!result || !input) + return nullptr; + + if (result == input) + return nullptr; + + if (order < XM_SH_MINORDER || order > XM_SH_MAXORDER) + return nullptr; + + float R[(2 * (XM_SH_MAXORDER - 1) + 1)*(2 * (XM_SH_MAXORDER - 1) + 1)]; // used to store rotation matrices... + + // these are actually very sparse matrices, most of the entries are zero's... + + const float ca = cosf(angle); + const float sa = sinf(angle); + + const float t1 = ca; + const float t2 = sa; + R[0] = t1; + R[1] = 0.0f; + R[2] = t2; + R[3] = 0.0f; + R[4] = 1.0f; + R[5] = 0.0f; + R[6] = -t2; + R[7] = 0.0f; + R[8] = t1; + + result[0] = input[0]; + SimpMatMul(3, R, input + 1, result + 1); + + if (order > 2) + { + for (int j = 0; j < 5 * 5; j++) R[j] = 0.0f; + const float t1 = sa; + const float t2 = t1*t1; + const float t3 = ca; + const float t4 = t3*t3; + const float t5 = -t2 + t4; + const float t7 = 2.0f*t3*t1; + R[0] = t5; + R[4] = t7; + R[6] = t3; + R[8] = t1; + R[12] = 1.0f; + R[16] = -t1; + R[18] = t3; + R[20] = -t7; + R[24] = t5; + + SimpMatMul(5, R, input + 4, result + 4); // un-roll matrix/vector multiply + if (order > 3) + { + for (int j = 0; j < 7 * 7; j++) R[j] = 0.0f; + const float t1 = ca; + const float t2 = t1*t1; + const float t4 = sa; + const float t5 = t4*t4; + const float t8 = t2*t1 - 3.0f*t1*t5; + const float t12 = 3.0f*t4*t2 - t5*t4; + const float t13 = -t5 + t2; + const float t15 = 2.0f*t1*t4; + R[0] = t8; + R[6] = t12; + R[8] = t13; + R[12] = t15; + R[16] = t1; + R[18] = t4; + R[24] = 1.0f; + R[30] = -t4; + R[32] = t1; + R[36] = -t15; + R[40] = t13; + R[42] = -t12; + R[48] = t8; + SimpMatMul(7, R, input + 9, result + 9); + if (order > 4) + { + for (int j = 0; j <= 9 * 9; j++) R[j] = 0.0f; + const float t1 = ca; + const float t2 = t1*t1; + const float t3 = t2*t2; + const float t4 = sa; + const float t5 = t4*t4; + const float t6 = t5*t5; + const float t9 = t3 + t6 - 6.0f*t5*t2; + const float t10 = t5*t4; + const float t12 = t2*t1; + const float t14 = -t10*t1 + t4*t12; + const float t17 = t12 - 3.0f*t1*t5; + const float t20 = 3.0f*t4*t2 - t10; + const float t21 = -t5 + t2; + const float t23 = 2.0f*t1*t4; + R[0] = t9; + R[8] = 4.0f*t14; + R[10] = t17; + R[16] = t20; + R[20] = t21; + R[24] = t23; + R[30] = t1; + R[32] = t4; + R[40] = 1.0f; + R[48] = -t4; + R[50] = t1; + R[56] = -t23; + R[60] = t21; + R[64] = -t20; + R[70] = t17; + R[72] = -4.0f*t14; + R[80] = t9; + + SimpMatMul(9, R, input + 16, result + 16); + if (order > 5) + { + for (int j = 0; j < 11 * 11; j++) R[j] = 0.0f; + const float t1 = ca; + const float t2 = sa; + const float t3 = t2*t2; + const float t4 = t3*t3; + const float t7 = t1*t1; + const float t8 = t7*t1; + const float t11 = t7*t7; + const float t13 = 5.0f*t1*t4 - 10.0f*t3*t8 + t11*t1; + const float t14 = t3*t2; + const float t20 = -10.0f*t14*t7 + 5.0f*t2*t11 + t4*t2; + const float t23 = t11 + t4 - 6.0f*t3*t7; + const float t26 = -t14*t1 + t2*t8; + const float t29 = t8 - 3.0f*t1*t3; + const float t32 = 3.0f*t2*t7 - t14; + const float t33 = -t3 + t7; + const float t35 = 2.0f*t1*t2; + R[0] = t13; + R[10] = t20; + R[12] = t23; + R[20] = 4.0f*t26; + R[24] = t29; + R[30] = t32; + R[36] = t33; + R[40] = t35; + R[48] = t1; + R[50] = t2; + R[60] = 1.0f; + R[70] = -t2; + R[72] = t1; + R[80] = -t35; + R[84] = t33; + R[90] = -t32; + R[96] = t29; + R[100] = -4.0f*t26; + R[108] = t23; + R[110] = -t20; + R[120] = t13; + SimpMatMul(11, R, input + 25, result + 25); + } + } + } + } + + return result; +} + + +//------------------------------------------------------------------------------------- +// Adds two SH vectors, result[i] = inputA[i] + inputB[i]; +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb205438.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* DirectX::XMSHAdd( + float *result, + size_t order, + const float *inputA, + const float *inputB) noexcept +{ + if (!result || !inputA || !inputB) + return nullptr; + + const size_t numcoeff = order*order; + + for (size_t i = 0; i < numcoeff; ++i) + { + result[i] = inputA[i] + inputB[i]; + } + + return result; +} + + +//------------------------------------------------------------------------------------- +// Scales a SH vector, result[i] = input[i] * scale; +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb204994.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* DirectX::XMSHScale( + float *result, + size_t order, + const float *input, + float scale) noexcept +{ + if (!result || !input) + return nullptr; + + const size_t numcoeff = order*order; + + for (size_t i = 0; i < numcoeff; ++i) + { + result[i] = scale * input[i]; + } + + return result; +} + + +//------------------------------------------------------------------------------------- +// Computes the dot product of two SH vectors +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb205446.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float DirectX::XMSHDot( + size_t order, + const float *inputA, + const float *inputB) noexcept +{ + if (!inputA || !inputB) + return 0.f; + + float result = inputA[0] * inputB[0]; + + const size_t numcoeff = order*order; + + for (size_t i = 1; i < numcoeff; ++i) + { + result += inputA[i] * inputB[i]; + } + + return result; +} + + +//------------------------------------------------------------------------------------- +// Computes the product of two functions represented using SH (f and g), where: +// result[i] = int(y_i(s) * f(s) * g(s)), where y_i(s) is the ith SH basis +// function, f(s) and g(s) are SH functions (sum_i(y_i(s)*c_i)). The order O +// determines the lengths of the arrays, where there should always be O^2 +// coefficients. In general the product of two SH functions of order O generates +// and SH function of order 2*O - 1, but we truncate the result. This means +// that the product commutes (f*g == g*f) but doesn't associate +// (f*(g*h) != (f*g)*h. +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* DirectX::XMSHMultiply( + float *result, + size_t order, + const float *inputF, + const float *inputG) noexcept +{ + switch (order) + { + case 2: + return XMSHMultiply2(result, inputF, inputG); + + case 3: + return XMSHMultiply3(result, inputF, inputG); + + case 4: + return XMSHMultiply4(result, inputF, inputG); + + case 5: + return XMSHMultiply5(result, inputF, inputG); + + case 6: + return XMSHMultiply6(result, inputF, inputG); + + default: + assert(order < XM_SH_MINORDER || order > XM_SH_MAXORDER); + return nullptr; + } +} + + +//------------------------------------------------------------------------------------- +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb205454.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* DirectX::XMSHMultiply2( + float *y, + const float *f, + const float *g) noexcept +{ + if (!y || !f || !g) + return nullptr; + + REAL tf, tg, t; + // [0,0]: 0, + y[0] = CONSTANT(0.282094792935999980)*f[0] * g[0]; + + // [1,1]: 0, + tf = CONSTANT(0.282094791773000010)*f[0]; + tg = CONSTANT(0.282094791773000010)*g[0]; + y[1] = tf*g[1] + tg*f[1]; + t = f[1] * g[1]; + y[0] += CONSTANT(0.282094791773000010)*t; + + // [2,2]: 0, + tf = CONSTANT(0.282094795249000000)*f[0]; + tg = CONSTANT(0.282094795249000000)*g[0]; + y[2] = tf*g[2] + tg*f[2]; + t = f[2] * g[2]; + y[0] += CONSTANT(0.282094795249000000)*t; + + // [3,3]: 0, + tf = CONSTANT(0.282094791773000010)*f[0]; + tg = CONSTANT(0.282094791773000010)*g[0]; + y[3] = tf*g[3] + tg*f[3]; + t = f[3] * g[3]; + y[0] += CONSTANT(0.282094791773000010)*t; + + // multiply count=20 + + return y; +} + + +//------------------------------------------------------------------------------------- +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb232906.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* DirectX::XMSHMultiply3( + float *y, + const float *f, + const float *g) noexcept +{ + if (!y || !f || !g) + return nullptr; + + REAL tf, tg, t; + // [0,0]: 0, + y[0] = CONSTANT(0.282094792935999980)*f[0] * g[0]; + + // [1,1]: 0,6,8, + tf = CONSTANT(0.282094791773000010)*f[0] + CONSTANT(-0.126156626101000010)*f[6] + CONSTANT(-0.218509686119999990)*f[8]; + tg = CONSTANT(0.282094791773000010)*g[0] + CONSTANT(-0.126156626101000010)*g[6] + CONSTANT(-0.218509686119999990)*g[8]; + y[1] = tf*g[1] + tg*f[1]; + t = f[1] * g[1]; + y[0] += CONSTANT(0.282094791773000010)*t; + y[6] = CONSTANT(-0.126156626101000010)*t; + y[8] = CONSTANT(-0.218509686119999990)*t; + + // [1,2]: 5, + tf = CONSTANT(0.218509686118000010)*f[5]; + tg = CONSTANT(0.218509686118000010)*g[5]; + y[1] += tf*g[2] + tg*f[2]; + y[2] = tf*g[1] + tg*f[1]; + t = f[1] * g[2] + f[2] * g[1]; + y[5] = CONSTANT(0.218509686118000010)*t; + + // [1,3]: 4, + tf = CONSTANT(0.218509686114999990)*f[4]; + tg = CONSTANT(0.218509686114999990)*g[4]; + y[1] += tf*g[3] + tg*f[3]; + y[3] = tf*g[1] + tg*f[1]; + t = f[1] * g[3] + f[3] * g[1]; + y[4] = CONSTANT(0.218509686114999990)*t; + + // [2,2]: 0,6, + tf = CONSTANT(0.282094795249000000)*f[0] + CONSTANT(0.252313259986999990)*f[6]; + tg = CONSTANT(0.282094795249000000)*g[0] + CONSTANT(0.252313259986999990)*g[6]; + y[2] += tf*g[2] + tg*f[2]; + t = f[2] * g[2]; + y[0] += CONSTANT(0.282094795249000000)*t; + y[6] += CONSTANT(0.252313259986999990)*t; + + // [2,3]: 7, + tf = CONSTANT(0.218509686118000010)*f[7]; + tg = CONSTANT(0.218509686118000010)*g[7]; + y[2] += tf*g[3] + tg*f[3]; + y[3] += tf*g[2] + tg*f[2]; + t = f[2] * g[3] + f[3] * g[2]; + y[7] = CONSTANT(0.218509686118000010)*t; + + // [3,3]: 0,6,8, + tf = CONSTANT(0.282094791773000010)*f[0] + CONSTANT(-0.126156626101000010)*f[6] + CONSTANT(0.218509686119999990)*f[8]; + tg = CONSTANT(0.282094791773000010)*g[0] + CONSTANT(-0.126156626101000010)*g[6] + CONSTANT(0.218509686119999990)*g[8]; + y[3] += tf*g[3] + tg*f[3]; + t = f[3] * g[3]; + y[0] += CONSTANT(0.282094791773000010)*t; + y[6] += CONSTANT(-0.126156626101000010)*t; + y[8] += CONSTANT(0.218509686119999990)*t; + + // [4,4]: 0,6, + tf = CONSTANT(0.282094791770000020)*f[0] + CONSTANT(-0.180223751576000010)*f[6]; + tg = CONSTANT(0.282094791770000020)*g[0] + CONSTANT(-0.180223751576000010)*g[6]; + y[4] += tf*g[4] + tg*f[4]; + t = f[4] * g[4]; + y[0] += CONSTANT(0.282094791770000020)*t; + y[6] += CONSTANT(-0.180223751576000010)*t; + + // [4,5]: 7, + tf = CONSTANT(0.156078347226000000)*f[7]; + tg = CONSTANT(0.156078347226000000)*g[7]; + y[4] += tf*g[5] + tg*f[5]; + y[5] += tf*g[4] + tg*f[4]; + t = f[4] * g[5] + f[5] * g[4]; + y[7] += CONSTANT(0.156078347226000000)*t; + + // [5,5]: 0,6,8, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.090111875786499998)*f[6] + CONSTANT(-0.156078347227999990)*f[8]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.090111875786499998)*g[6] + CONSTANT(-0.156078347227999990)*g[8]; + y[5] += tf*g[5] + tg*f[5]; + t = f[5] * g[5]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[6] += CONSTANT(0.090111875786499998)*t; + y[8] += CONSTANT(-0.156078347227999990)*t; + + // [6,6]: 0,6, + tf = CONSTANT(0.282094797560000000)*f[0]; + tg = CONSTANT(0.282094797560000000)*g[0]; + y[6] += tf*g[6] + tg*f[6]; + t = f[6] * g[6]; + y[0] += CONSTANT(0.282094797560000000)*t; + y[6] += CONSTANT(0.180223764527000010)*t; + + // [7,7]: 0,6,8, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.090111875786499998)*f[6] + CONSTANT(0.156078347227999990)*f[8]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.090111875786499998)*g[6] + CONSTANT(0.156078347227999990)*g[8]; + y[7] += tf*g[7] + tg*f[7]; + t = f[7] * g[7]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[6] += CONSTANT(0.090111875786499998)*t; + y[8] += CONSTANT(0.156078347227999990)*t; + + // [8,8]: 0,6, + tf = CONSTANT(0.282094791770000020)*f[0] + CONSTANT(-0.180223751576000010)*f[6]; + tg = CONSTANT(0.282094791770000020)*g[0] + CONSTANT(-0.180223751576000010)*g[6]; + y[8] += tf*g[8] + tg*f[8]; + t = f[8] * g[8]; + y[0] += CONSTANT(0.282094791770000020)*t; + y[6] += CONSTANT(-0.180223751576000010)*t; + + // multiply count=120 + + return y; +} + + +//------------------------------------------------------------------------------------- +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb232907.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* DirectX::XMSHMultiply4( + float *y, + const float *f, + const float *g) noexcept +{ + if (!y || !f || !g) + return nullptr; + + REAL tf, tg, t; + // [0,0]: 0, + y[0] = CONSTANT(0.282094792935999980)*f[0] * g[0]; + + // [1,1]: 0,6,8, + tf = CONSTANT(0.282094791773000010)*f[0] + CONSTANT(-0.126156626101000010)*f[6] + CONSTANT(-0.218509686119999990)*f[8]; + tg = CONSTANT(0.282094791773000010)*g[0] + CONSTANT(-0.126156626101000010)*g[6] + CONSTANT(-0.218509686119999990)*g[8]; + y[1] = tf*g[1] + tg*f[1]; + t = f[1] * g[1]; + y[0] += CONSTANT(0.282094791773000010)*t; + y[6] = CONSTANT(-0.126156626101000010)*t; + y[8] = CONSTANT(-0.218509686119999990)*t; + + // [1,4]: 3,13,15, + tf = CONSTANT(0.218509686114999990)*f[3] + CONSTANT(-0.058399170082300000)*f[13] + CONSTANT(-0.226179013157999990)*f[15]; + tg = CONSTANT(0.218509686114999990)*g[3] + CONSTANT(-0.058399170082300000)*g[13] + CONSTANT(-0.226179013157999990)*g[15]; + y[1] += tf*g[4] + tg*f[4]; + y[4] = tf*g[1] + tg*f[1]; + t = f[1] * g[4] + f[4] * g[1]; + y[3] = CONSTANT(0.218509686114999990)*t; + y[13] = CONSTANT(-0.058399170082300000)*t; + y[15] = CONSTANT(-0.226179013157999990)*t; + + // [1,5]: 2,12,14, + tf = CONSTANT(0.218509686118000010)*f[2] + CONSTANT(-0.143048168103000000)*f[12] + CONSTANT(-0.184674390923000000)*f[14]; + tg = CONSTANT(0.218509686118000010)*g[2] + CONSTANT(-0.143048168103000000)*g[12] + CONSTANT(-0.184674390923000000)*g[14]; + y[1] += tf*g[5] + tg*f[5]; + y[5] = tf*g[1] + tg*f[1]; + t = f[1] * g[5] + f[5] * g[1]; + y[2] = CONSTANT(0.218509686118000010)*t; + y[12] = CONSTANT(-0.143048168103000000)*t; + y[14] = CONSTANT(-0.184674390923000000)*t; + + // [1,6]: 11, + tf = CONSTANT(0.202300659402999990)*f[11]; + tg = CONSTANT(0.202300659402999990)*g[11]; + y[1] += tf*g[6] + tg*f[6]; + y[6] += tf*g[1] + tg*f[1]; + t = f[1] * g[6] + f[6] * g[1]; + y[11] = CONSTANT(0.202300659402999990)*t; + + // [1,8]: 9,11, + tf = CONSTANT(0.226179013155000000)*f[9] + CONSTANT(0.058399170081799998)*f[11]; + tg = CONSTANT(0.226179013155000000)*g[9] + CONSTANT(0.058399170081799998)*g[11]; + y[1] += tf*g[8] + tg*f[8]; + y[8] += tf*g[1] + tg*f[1]; + t = f[1] * g[8] + f[8] * g[1]; + y[9] = CONSTANT(0.226179013155000000)*t; + y[11] += CONSTANT(0.058399170081799998)*t; + + // [2,2]: 0,6, + tf = CONSTANT(0.282094795249000000)*f[0] + CONSTANT(0.252313259986999990)*f[6]; + tg = CONSTANT(0.282094795249000000)*g[0] + CONSTANT(0.252313259986999990)*g[6]; + y[2] += tf*g[2] + tg*f[2]; + t = f[2] * g[2]; + y[0] += CONSTANT(0.282094795249000000)*t; + y[6] += CONSTANT(0.252313259986999990)*t; + + // [2,6]: 12, + tf = CONSTANT(0.247766706973999990)*f[12]; + tg = CONSTANT(0.247766706973999990)*g[12]; + y[2] += tf*g[6] + tg*f[6]; + y[6] += tf*g[2] + tg*f[2]; + t = f[2] * g[6] + f[6] * g[2]; + y[12] += CONSTANT(0.247766706973999990)*t; + + // [3,3]: 0,6,8, + tf = CONSTANT(0.282094791773000010)*f[0] + CONSTANT(-0.126156626101000010)*f[6] + CONSTANT(0.218509686119999990)*f[8]; + tg = CONSTANT(0.282094791773000010)*g[0] + CONSTANT(-0.126156626101000010)*g[6] + CONSTANT(0.218509686119999990)*g[8]; + y[3] += tf*g[3] + tg*f[3]; + t = f[3] * g[3]; + y[0] += CONSTANT(0.282094791773000010)*t; + y[6] += CONSTANT(-0.126156626101000010)*t; + y[8] += CONSTANT(0.218509686119999990)*t; + + // [3,6]: 13, + tf = CONSTANT(0.202300659402999990)*f[13]; + tg = CONSTANT(0.202300659402999990)*g[13]; + y[3] += tf*g[6] + tg*f[6]; + y[6] += tf*g[3] + tg*f[3]; + t = f[3] * g[6] + f[6] * g[3]; + y[13] += CONSTANT(0.202300659402999990)*t; + + // [3,7]: 2,12,14, + tf = CONSTANT(0.218509686118000010)*f[2] + CONSTANT(-0.143048168103000000)*f[12] + CONSTANT(0.184674390923000000)*f[14]; + tg = CONSTANT(0.218509686118000010)*g[2] + CONSTANT(-0.143048168103000000)*g[12] + CONSTANT(0.184674390923000000)*g[14]; + y[3] += tf*g[7] + tg*f[7]; + y[7] = tf*g[3] + tg*f[3]; + t = f[3] * g[7] + f[7] * g[3]; + y[2] += CONSTANT(0.218509686118000010)*t; + y[12] += CONSTANT(-0.143048168103000000)*t; + y[14] += CONSTANT(0.184674390923000000)*t; + + // [3,8]: 13,15, + tf = CONSTANT(-0.058399170081799998)*f[13] + CONSTANT(0.226179013155000000)*f[15]; + tg = CONSTANT(-0.058399170081799998)*g[13] + CONSTANT(0.226179013155000000)*g[15]; + y[3] += tf*g[8] + tg*f[8]; + y[8] += tf*g[3] + tg*f[3]; + t = f[3] * g[8] + f[8] * g[3]; + y[13] += CONSTANT(-0.058399170081799998)*t; + y[15] += CONSTANT(0.226179013155000000)*t; + + // [4,4]: 0,6, + tf = CONSTANT(0.282094791770000020)*f[0] + CONSTANT(-0.180223751576000010)*f[6]; + tg = CONSTANT(0.282094791770000020)*g[0] + CONSTANT(-0.180223751576000010)*g[6]; + y[4] += tf*g[4] + tg*f[4]; + t = f[4] * g[4]; + y[0] += CONSTANT(0.282094791770000020)*t; + y[6] += CONSTANT(-0.180223751576000010)*t; + + // [4,5]: 7, + tf = CONSTANT(0.156078347226000000)*f[7]; + tg = CONSTANT(0.156078347226000000)*g[7]; + y[4] += tf*g[5] + tg*f[5]; + y[5] += tf*g[4] + tg*f[4]; + t = f[4] * g[5] + f[5] * g[4]; + y[7] += CONSTANT(0.156078347226000000)*t; + + // [4,9]: 3,13, + tf = CONSTANT(0.226179013157999990)*f[3] + CONSTANT(-0.094031597258400004)*f[13]; + tg = CONSTANT(0.226179013157999990)*g[3] + CONSTANT(-0.094031597258400004)*g[13]; + y[4] += tf*g[9] + tg*f[9]; + y[9] += tf*g[4] + tg*f[4]; + t = f[4] * g[9] + f[9] * g[4]; + y[3] += CONSTANT(0.226179013157999990)*t; + y[13] += CONSTANT(-0.094031597258400004)*t; + + // [4,10]: 2,12, + tf = CONSTANT(0.184674390919999990)*f[2] + CONSTANT(-0.188063194517999990)*f[12]; + tg = CONSTANT(0.184674390919999990)*g[2] + CONSTANT(-0.188063194517999990)*g[12]; + y[4] += tf*g[10] + tg*f[10]; + y[10] = tf*g[4] + tg*f[4]; + t = f[4] * g[10] + f[10] * g[4]; + y[2] += CONSTANT(0.184674390919999990)*t; + y[12] += CONSTANT(-0.188063194517999990)*t; + + // [4,11]: 3,13,15, + tf = CONSTANT(-0.058399170082300000)*f[3] + CONSTANT(0.145673124078000010)*f[13] + CONSTANT(0.094031597258400004)*f[15]; + tg = CONSTANT(-0.058399170082300000)*g[3] + CONSTANT(0.145673124078000010)*g[13] + CONSTANT(0.094031597258400004)*g[15]; + y[4] += tf*g[11] + tg*f[11]; + y[11] += tf*g[4] + tg*f[4]; + t = f[4] * g[11] + f[11] * g[4]; + y[3] += CONSTANT(-0.058399170082300000)*t; + y[13] += CONSTANT(0.145673124078000010)*t; + y[15] += CONSTANT(0.094031597258400004)*t; + + // [5,5]: 0,6,8, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.090111875786499998)*f[6] + CONSTANT(-0.156078347227999990)*f[8]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.090111875786499998)*g[6] + CONSTANT(-0.156078347227999990)*g[8]; + y[5] += tf*g[5] + tg*f[5]; + t = f[5] * g[5]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[6] += CONSTANT(0.090111875786499998)*t; + y[8] += CONSTANT(-0.156078347227999990)*t; + + // [5,9]: 14, + tf = CONSTANT(0.148677009677999990)*f[14]; + tg = CONSTANT(0.148677009677999990)*g[14]; + y[5] += tf*g[9] + tg*f[9]; + y[9] += tf*g[5] + tg*f[5]; + t = f[5] * g[9] + f[9] * g[5]; + y[14] += CONSTANT(0.148677009677999990)*t; + + // [5,10]: 3,13,15, + tf = CONSTANT(0.184674390919999990)*f[3] + CONSTANT(0.115164716490000000)*f[13] + CONSTANT(-0.148677009678999990)*f[15]; + tg = CONSTANT(0.184674390919999990)*g[3] + CONSTANT(0.115164716490000000)*g[13] + CONSTANT(-0.148677009678999990)*g[15]; + y[5] += tf*g[10] + tg*f[10]; + y[10] += tf*g[5] + tg*f[5]; + t = f[5] * g[10] + f[10] * g[5]; + y[3] += CONSTANT(0.184674390919999990)*t; + y[13] += CONSTANT(0.115164716490000000)*t; + y[15] += CONSTANT(-0.148677009678999990)*t; + + // [5,11]: 2,12,14, + tf = CONSTANT(0.233596680327000010)*f[2] + CONSTANT(0.059470803871800003)*f[12] + CONSTANT(-0.115164716491000000)*f[14]; + tg = CONSTANT(0.233596680327000010)*g[2] + CONSTANT(0.059470803871800003)*g[12] + CONSTANT(-0.115164716491000000)*g[14]; + y[5] += tf*g[11] + tg*f[11]; + y[11] += tf*g[5] + tg*f[5]; + t = f[5] * g[11] + f[11] * g[5]; + y[2] += CONSTANT(0.233596680327000010)*t; + y[12] += CONSTANT(0.059470803871800003)*t; + y[14] += CONSTANT(-0.115164716491000000)*t; + + // [6,6]: 0,6, + tf = CONSTANT(0.282094797560000000)*f[0]; + tg = CONSTANT(0.282094797560000000)*g[0]; + y[6] += tf*g[6] + tg*f[6]; + t = f[6] * g[6]; + y[0] += CONSTANT(0.282094797560000000)*t; + y[6] += CONSTANT(0.180223764527000010)*t; + + // [7,7]: 6,0,8, + tf = CONSTANT(0.090111875786499998)*f[6] + CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.156078347227999990)*f[8]; + tg = CONSTANT(0.090111875786499998)*g[6] + CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.156078347227999990)*g[8]; + y[7] += tf*g[7] + tg*f[7]; + t = f[7] * g[7]; + y[6] += CONSTANT(0.090111875786499998)*t; + y[0] += CONSTANT(0.282094791773999990)*t; + y[8] += CONSTANT(0.156078347227999990)*t; + + // [7,10]: 9,1,11, + tf = CONSTANT(0.148677009678999990)*f[9] + CONSTANT(0.184674390919999990)*f[1] + CONSTANT(0.115164716490000000)*f[11]; + tg = CONSTANT(0.148677009678999990)*g[9] + CONSTANT(0.184674390919999990)*g[1] + CONSTANT(0.115164716490000000)*g[11]; + y[7] += tf*g[10] + tg*f[10]; + y[10] += tf*g[7] + tg*f[7]; + t = f[7] * g[10] + f[10] * g[7]; + y[9] += CONSTANT(0.148677009678999990)*t; + y[1] += CONSTANT(0.184674390919999990)*t; + y[11] += CONSTANT(0.115164716490000000)*t; + + // [7,13]: 12,2,14, + tf = CONSTANT(0.059470803871800003)*f[12] + CONSTANT(0.233596680327000010)*f[2] + CONSTANT(0.115164716491000000)*f[14]; + tg = CONSTANT(0.059470803871800003)*g[12] + CONSTANT(0.233596680327000010)*g[2] + CONSTANT(0.115164716491000000)*g[14]; + y[7] += tf*g[13] + tg*f[13]; + y[13] += tf*g[7] + tg*f[7]; + t = f[7] * g[13] + f[13] * g[7]; + y[12] += CONSTANT(0.059470803871800003)*t; + y[2] += CONSTANT(0.233596680327000010)*t; + y[14] += CONSTANT(0.115164716491000000)*t; + + // [7,14]: 15, + tf = CONSTANT(0.148677009677999990)*f[15]; + tg = CONSTANT(0.148677009677999990)*g[15]; + y[7] += tf*g[14] + tg*f[14]; + y[14] += tf*g[7] + tg*f[7]; + t = f[7] * g[14] + f[14] * g[7]; + y[15] += CONSTANT(0.148677009677999990)*t; + + // [8,8]: 0,6, + tf = CONSTANT(0.282094791770000020)*f[0] + CONSTANT(-0.180223751576000010)*f[6]; + tg = CONSTANT(0.282094791770000020)*g[0] + CONSTANT(-0.180223751576000010)*g[6]; + y[8] += tf*g[8] + tg*f[8]; + t = f[8] * g[8]; + y[0] += CONSTANT(0.282094791770000020)*t; + y[6] += CONSTANT(-0.180223751576000010)*t; + + // [8,9]: 11, + tf = CONSTANT(-0.094031597259499999)*f[11]; + tg = CONSTANT(-0.094031597259499999)*g[11]; + y[8] += tf*g[9] + tg*f[9]; + y[9] += tf*g[8] + tg*f[8]; + t = f[8] * g[9] + f[9] * g[8]; + y[11] += CONSTANT(-0.094031597259499999)*t; + + // [8,13]: 15, + tf = CONSTANT(-0.094031597259499999)*f[15]; + tg = CONSTANT(-0.094031597259499999)*g[15]; + y[8] += tf*g[13] + tg*f[13]; + y[13] += tf*g[8] + tg*f[8]; + t = f[8] * g[13] + f[13] * g[8]; + y[15] += CONSTANT(-0.094031597259499999)*t; + + // [8,14]: 2,12, + tf = CONSTANT(0.184674390919999990)*f[2] + CONSTANT(-0.188063194517999990)*f[12]; + tg = CONSTANT(0.184674390919999990)*g[2] + CONSTANT(-0.188063194517999990)*g[12]; + y[8] += tf*g[14] + tg*f[14]; + y[14] += tf*g[8] + tg*f[8]; + t = f[8] * g[14] + f[14] * g[8]; + y[2] += CONSTANT(0.184674390919999990)*t; + y[12] += CONSTANT(-0.188063194517999990)*t; + + // [9,9]: 6,0, + tf = CONSTANT(-0.210261043508000010)*f[6] + CONSTANT(0.282094791766999970)*f[0]; + tg = CONSTANT(-0.210261043508000010)*g[6] + CONSTANT(0.282094791766999970)*g[0]; + y[9] += tf*g[9] + tg*f[9]; + t = f[9] * g[9]; + y[6] += CONSTANT(-0.210261043508000010)*t; + y[0] += CONSTANT(0.282094791766999970)*t; + + // [10,10]: 0, + tf = CONSTANT(0.282094791771999980)*f[0]; + tg = CONSTANT(0.282094791771999980)*g[0]; + y[10] += tf*g[10] + tg*f[10]; + t = f[10] * g[10]; + y[0] += CONSTANT(0.282094791771999980)*t; + + // [11,11]: 0,6,8, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.126156626101000010)*f[6] + CONSTANT(-0.145673124078999990)*f[8]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.126156626101000010)*g[6] + CONSTANT(-0.145673124078999990)*g[8]; + y[11] += tf*g[11] + tg*f[11]; + t = f[11] * g[11]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[6] += CONSTANT(0.126156626101000010)*t; + y[8] += CONSTANT(-0.145673124078999990)*t; + + // [12,12]: 0,6, + tf = CONSTANT(0.282094799871999980)*f[0] + CONSTANT(0.168208852954000010)*f[6]; + tg = CONSTANT(0.282094799871999980)*g[0] + CONSTANT(0.168208852954000010)*g[6]; + y[12] += tf*g[12] + tg*f[12]; + t = f[12] * g[12]; + y[0] += CONSTANT(0.282094799871999980)*t; + y[6] += CONSTANT(0.168208852954000010)*t; + + // [13,13]: 0,8,6, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.145673124078999990)*f[8] + CONSTANT(0.126156626101000010)*f[6]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.145673124078999990)*g[8] + CONSTANT(0.126156626101000010)*g[6]; + y[13] += tf*g[13] + tg*f[13]; + t = f[13] * g[13]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[8] += CONSTANT(0.145673124078999990)*t; + y[6] += CONSTANT(0.126156626101000010)*t; + + // [14,14]: 0, + tf = CONSTANT(0.282094791771999980)*f[0]; + tg = CONSTANT(0.282094791771999980)*g[0]; + y[14] += tf*g[14] + tg*f[14]; + t = f[14] * g[14]; + y[0] += CONSTANT(0.282094791771999980)*t; + + // [15,15]: 0,6, + tf = CONSTANT(0.282094791766999970)*f[0] + CONSTANT(-0.210261043508000010)*f[6]; + tg = CONSTANT(0.282094791766999970)*g[0] + CONSTANT(-0.210261043508000010)*g[6]; + y[15] += tf*g[15] + tg*f[15]; + t = f[15] * g[15]; + y[0] += CONSTANT(0.282094791766999970)*t; + y[6] += CONSTANT(-0.210261043508000010)*t; + + // multiply count=399 + + return y; +} + + +//------------------------------------------------------------------------------------- +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb232908.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* DirectX::XMSHMultiply5( + float *y, + const float *f, + const float *g) noexcept +{ + if (!y || !f || !g) + return nullptr; + + REAL tf, tg, t; + // [0,0]: 0, + y[0] = CONSTANT(0.282094792935999980)*f[0] * g[0]; + + // [1,1]: 0,6,8, + tf = CONSTANT(0.282094791773000010)*f[0] + CONSTANT(-0.126156626101000010)*f[6] + CONSTANT(-0.218509686119999990)*f[8]; + tg = CONSTANT(0.282094791773000010)*g[0] + CONSTANT(-0.126156626101000010)*g[6] + CONSTANT(-0.218509686119999990)*g[8]; + y[1] = tf*g[1] + tg*f[1]; + t = f[1] * g[1]; + y[0] += CONSTANT(0.282094791773000010)*t; + y[6] = CONSTANT(-0.126156626101000010)*t; + y[8] = CONSTANT(-0.218509686119999990)*t; + + // [1,4]: 3,13,15, + tf = CONSTANT(0.218509686114999990)*f[3] + CONSTANT(-0.058399170082300000)*f[13] + CONSTANT(-0.226179013157999990)*f[15]; + tg = CONSTANT(0.218509686114999990)*g[3] + CONSTANT(-0.058399170082300000)*g[13] + CONSTANT(-0.226179013157999990)*g[15]; + y[1] += tf*g[4] + tg*f[4]; + y[4] = tf*g[1] + tg*f[1]; + t = f[1] * g[4] + f[4] * g[1]; + y[3] = CONSTANT(0.218509686114999990)*t; + y[13] = CONSTANT(-0.058399170082300000)*t; + y[15] = CONSTANT(-0.226179013157999990)*t; + + // [1,5]: 2,12,14, + tf = CONSTANT(0.218509686118000010)*f[2] + CONSTANT(-0.143048168103000000)*f[12] + CONSTANT(-0.184674390923000000)*f[14]; + tg = CONSTANT(0.218509686118000010)*g[2] + CONSTANT(-0.143048168103000000)*g[12] + CONSTANT(-0.184674390923000000)*g[14]; + y[1] += tf*g[5] + tg*f[5]; + y[5] = tf*g[1] + tg*f[1]; + t = f[1] * g[5] + f[5] * g[1]; + y[2] = CONSTANT(0.218509686118000010)*t; + y[12] = CONSTANT(-0.143048168103000000)*t; + y[14] = CONSTANT(-0.184674390923000000)*t; + + // [1,9]: 8,22,24, + tf = CONSTANT(0.226179013155000000)*f[8] + CONSTANT(-0.043528171378199997)*f[22] + CONSTANT(-0.230329432978999990)*f[24]; + tg = CONSTANT(0.226179013155000000)*g[8] + CONSTANT(-0.043528171378199997)*g[22] + CONSTANT(-0.230329432978999990)*g[24]; + y[1] += tf*g[9] + tg*f[9]; + y[9] = tf*g[1] + tg*f[1]; + t = f[1] * g[9] + f[9] * g[1]; + y[8] += CONSTANT(0.226179013155000000)*t; + y[22] = CONSTANT(-0.043528171378199997)*t; + y[24] = CONSTANT(-0.230329432978999990)*t; + + // [1,10]: 7,21,23, + tf = CONSTANT(0.184674390919999990)*f[7] + CONSTANT(-0.075393004386799994)*f[21] + CONSTANT(-0.199471140200000010)*f[23]; + tg = CONSTANT(0.184674390919999990)*g[7] + CONSTANT(-0.075393004386799994)*g[21] + CONSTANT(-0.199471140200000010)*g[23]; + y[1] += tf*g[10] + tg*f[10]; + y[10] = tf*g[1] + tg*f[1]; + t = f[1] * g[10] + f[10] * g[1]; + y[7] = CONSTANT(0.184674390919999990)*t; + y[21] = CONSTANT(-0.075393004386799994)*t; + y[23] = CONSTANT(-0.199471140200000010)*t; + + // [1,11]: 6,8,20,22, + tf = CONSTANT(0.202300659402999990)*f[6] + CONSTANT(0.058399170081799998)*f[8] + CONSTANT(-0.150786008773000000)*f[20] + CONSTANT(-0.168583882836999990)*f[22]; + tg = CONSTANT(0.202300659402999990)*g[6] + CONSTANT(0.058399170081799998)*g[8] + CONSTANT(-0.150786008773000000)*g[20] + CONSTANT(-0.168583882836999990)*g[22]; + y[1] += tf*g[11] + tg*f[11]; + y[11] = tf*g[1] + tg*f[1]; + t = f[1] * g[11] + f[11] * g[1]; + y[6] += CONSTANT(0.202300659402999990)*t; + y[8] += CONSTANT(0.058399170081799998)*t; + y[20] = CONSTANT(-0.150786008773000000)*t; + y[22] += CONSTANT(-0.168583882836999990)*t; + + // [1,12]: 19, + tf = CONSTANT(0.194663900273000010)*f[19]; + tg = CONSTANT(0.194663900273000010)*g[19]; + y[1] += tf*g[12] + tg*f[12]; + y[12] += tf*g[1] + tg*f[1]; + t = f[1] * g[12] + f[12] * g[1]; + y[19] = CONSTANT(0.194663900273000010)*t; + + // [1,13]: 18, + tf = CONSTANT(0.168583882834000000)*f[18]; + tg = CONSTANT(0.168583882834000000)*g[18]; + y[1] += tf*g[13] + tg*f[13]; + y[13] += tf*g[1] + tg*f[1]; + t = f[1] * g[13] + f[13] * g[1]; + y[18] = CONSTANT(0.168583882834000000)*t; + + // [1,14]: 17,19, + tf = CONSTANT(0.199471140196999990)*f[17] + CONSTANT(0.075393004386399995)*f[19]; + tg = CONSTANT(0.199471140196999990)*g[17] + CONSTANT(0.075393004386399995)*g[19]; + y[1] += tf*g[14] + tg*f[14]; + y[14] += tf*g[1] + tg*f[1]; + t = f[1] * g[14] + f[14] * g[1]; + y[17] = CONSTANT(0.199471140196999990)*t; + y[19] += CONSTANT(0.075393004386399995)*t; + + // [1,15]: 16,18, + tf = CONSTANT(0.230329432973999990)*f[16] + CONSTANT(0.043528171377799997)*f[18]; + tg = CONSTANT(0.230329432973999990)*g[16] + CONSTANT(0.043528171377799997)*g[18]; + y[1] += tf*g[15] + tg*f[15]; + y[15] += tf*g[1] + tg*f[1]; + t = f[1] * g[15] + f[15] * g[1]; + y[16] = CONSTANT(0.230329432973999990)*t; + y[18] += CONSTANT(0.043528171377799997)*t; + + // [2,2]: 0,6, + tf = CONSTANT(0.282094795249000000)*f[0] + CONSTANT(0.252313259986999990)*f[6]; + tg = CONSTANT(0.282094795249000000)*g[0] + CONSTANT(0.252313259986999990)*g[6]; + y[2] += tf*g[2] + tg*f[2]; + t = f[2] * g[2]; + y[0] += CONSTANT(0.282094795249000000)*t; + y[6] += CONSTANT(0.252313259986999990)*t; + + // [2,10]: 4,18, + tf = CONSTANT(0.184674390919999990)*f[4] + CONSTANT(0.213243618621000000)*f[18]; + tg = CONSTANT(0.184674390919999990)*g[4] + CONSTANT(0.213243618621000000)*g[18]; + y[2] += tf*g[10] + tg*f[10]; + y[10] += tf*g[2] + tg*f[2]; + t = f[2] * g[10] + f[10] * g[2]; + y[4] += CONSTANT(0.184674390919999990)*t; + y[18] += CONSTANT(0.213243618621000000)*t; + + // [2,12]: 6,20, + tf = CONSTANT(0.247766706973999990)*f[6] + CONSTANT(0.246232537174000010)*f[20]; + tg = CONSTANT(0.247766706973999990)*g[6] + CONSTANT(0.246232537174000010)*g[20]; + y[2] += tf*g[12] + tg*f[12]; + y[12] += tf*g[2] + tg*f[2]; + t = f[2] * g[12] + f[12] * g[2]; + y[6] += CONSTANT(0.247766706973999990)*t; + y[20] += CONSTANT(0.246232537174000010)*t; + + // [2,14]: 8,22, + tf = CONSTANT(0.184674390919999990)*f[8] + CONSTANT(0.213243618621000000)*f[22]; + tg = CONSTANT(0.184674390919999990)*g[8] + CONSTANT(0.213243618621000000)*g[22]; + y[2] += tf*g[14] + tg*f[14]; + y[14] += tf*g[2] + tg*f[2]; + t = f[2] * g[14] + f[14] * g[2]; + y[8] += CONSTANT(0.184674390919999990)*t; + y[22] += CONSTANT(0.213243618621000000)*t; + + // [3,3]: 0,6,8, + tf = CONSTANT(0.282094791773000010)*f[0] + CONSTANT(-0.126156626101000010)*f[6] + CONSTANT(0.218509686119999990)*f[8]; + tg = CONSTANT(0.282094791773000010)*g[0] + CONSTANT(-0.126156626101000010)*g[6] + CONSTANT(0.218509686119999990)*g[8]; + y[3] += tf*g[3] + tg*f[3]; + t = f[3] * g[3]; + y[0] += CONSTANT(0.282094791773000010)*t; + y[6] += CONSTANT(-0.126156626101000010)*t; + y[8] += CONSTANT(0.218509686119999990)*t; + + // [3,7]: 2,12,14, + tf = CONSTANT(0.218509686118000010)*f[2] + CONSTANT(-0.143048168103000000)*f[12] + CONSTANT(0.184674390923000000)*f[14]; + tg = CONSTANT(0.218509686118000010)*g[2] + CONSTANT(-0.143048168103000000)*g[12] + CONSTANT(0.184674390923000000)*g[14]; + y[3] += tf*g[7] + tg*f[7]; + y[7] += tf*g[3] + tg*f[3]; + t = f[3] * g[7] + f[7] * g[3]; + y[2] += CONSTANT(0.218509686118000010)*t; + y[12] += CONSTANT(-0.143048168103000000)*t; + y[14] += CONSTANT(0.184674390923000000)*t; + + // [3,9]: 4,16,18, + tf = CONSTANT(0.226179013157999990)*f[4] + CONSTANT(0.230329432973999990)*f[16] + CONSTANT(-0.043528171377799997)*f[18]; + tg = CONSTANT(0.226179013157999990)*g[4] + CONSTANT(0.230329432973999990)*g[16] + CONSTANT(-0.043528171377799997)*g[18]; + y[3] += tf*g[9] + tg*f[9]; + y[9] += tf*g[3] + tg*f[3]; + t = f[3] * g[9] + f[9] * g[3]; + y[4] += CONSTANT(0.226179013157999990)*t; + y[16] += CONSTANT(0.230329432973999990)*t; + y[18] += CONSTANT(-0.043528171377799997)*t; + + // [3,10]: 5,17,19, + tf = CONSTANT(0.184674390919999990)*f[5] + CONSTANT(0.199471140200000010)*f[17] + CONSTANT(-0.075393004386799994)*f[19]; + tg = CONSTANT(0.184674390919999990)*g[5] + CONSTANT(0.199471140200000010)*g[17] + CONSTANT(-0.075393004386799994)*g[19]; + y[3] += tf*g[10] + tg*f[10]; + y[10] += tf*g[3] + tg*f[3]; + t = f[3] * g[10] + f[10] * g[3]; + y[5] += CONSTANT(0.184674390919999990)*t; + y[17] += CONSTANT(0.199471140200000010)*t; + y[19] += CONSTANT(-0.075393004386799994)*t; + + // [3,12]: 21, + tf = CONSTANT(0.194663900273000010)*f[21]; + tg = CONSTANT(0.194663900273000010)*g[21]; + y[3] += tf*g[12] + tg*f[12]; + y[12] += tf*g[3] + tg*f[3]; + t = f[3] * g[12] + f[12] * g[3]; + y[21] += CONSTANT(0.194663900273000010)*t; + + // [3,13]: 8,6,20,22, + tf = CONSTANT(-0.058399170081799998)*f[8] + CONSTANT(0.202300659402999990)*f[6] + CONSTANT(-0.150786008773000000)*f[20] + CONSTANT(0.168583882836999990)*f[22]; + tg = CONSTANT(-0.058399170081799998)*g[8] + CONSTANT(0.202300659402999990)*g[6] + CONSTANT(-0.150786008773000000)*g[20] + CONSTANT(0.168583882836999990)*g[22]; + y[3] += tf*g[13] + tg*f[13]; + y[13] += tf*g[3] + tg*f[3]; + t = f[3] * g[13] + f[13] * g[3]; + y[8] += CONSTANT(-0.058399170081799998)*t; + y[6] += CONSTANT(0.202300659402999990)*t; + y[20] += CONSTANT(-0.150786008773000000)*t; + y[22] += CONSTANT(0.168583882836999990)*t; + + // [3,14]: 21,23, + tf = CONSTANT(-0.075393004386399995)*f[21] + CONSTANT(0.199471140196999990)*f[23]; + tg = CONSTANT(-0.075393004386399995)*g[21] + CONSTANT(0.199471140196999990)*g[23]; + y[3] += tf*g[14] + tg*f[14]; + y[14] += tf*g[3] + tg*f[3]; + t = f[3] * g[14] + f[14] * g[3]; + y[21] += CONSTANT(-0.075393004386399995)*t; + y[23] += CONSTANT(0.199471140196999990)*t; + + // [3,15]: 8,22,24, + tf = CONSTANT(0.226179013155000000)*f[8] + CONSTANT(-0.043528171378199997)*f[22] + CONSTANT(0.230329432978999990)*f[24]; + tg = CONSTANT(0.226179013155000000)*g[8] + CONSTANT(-0.043528171378199997)*g[22] + CONSTANT(0.230329432978999990)*g[24]; + y[3] += tf*g[15] + tg*f[15]; + y[15] += tf*g[3] + tg*f[3]; + t = f[3] * g[15] + f[15] * g[3]; + y[8] += CONSTANT(0.226179013155000000)*t; + y[22] += CONSTANT(-0.043528171378199997)*t; + y[24] += CONSTANT(0.230329432978999990)*t; + + // [4,4]: 0,6,20,24, + tf = CONSTANT(0.282094791770000020)*f[0] + CONSTANT(-0.180223751576000010)*f[6] + CONSTANT(0.040299255967500003)*f[20] + CONSTANT(-0.238413613505999990)*f[24]; + tg = CONSTANT(0.282094791770000020)*g[0] + CONSTANT(-0.180223751576000010)*g[6] + CONSTANT(0.040299255967500003)*g[20] + CONSTANT(-0.238413613505999990)*g[24]; + y[4] += tf*g[4] + tg*f[4]; + t = f[4] * g[4]; + y[0] += CONSTANT(0.282094791770000020)*t; + y[6] += CONSTANT(-0.180223751576000010)*t; + y[20] += CONSTANT(0.040299255967500003)*t; + y[24] += CONSTANT(-0.238413613505999990)*t; + + // [4,5]: 7,21,23, + tf = CONSTANT(0.156078347226000000)*f[7] + CONSTANT(-0.063718718434399996)*f[21] + CONSTANT(-0.168583882835000000)*f[23]; + tg = CONSTANT(0.156078347226000000)*g[7] + CONSTANT(-0.063718718434399996)*g[21] + CONSTANT(-0.168583882835000000)*g[23]; + y[4] += tf*g[5] + tg*f[5]; + y[5] += tf*g[4] + tg*f[4]; + t = f[4] * g[5] + f[5] * g[4]; + y[7] += CONSTANT(0.156078347226000000)*t; + y[21] += CONSTANT(-0.063718718434399996)*t; + y[23] += CONSTANT(-0.168583882835000000)*t; + + // [4,11]: 3,13,15, + tf = CONSTANT(-0.058399170082300000)*f[3] + CONSTANT(0.145673124078000010)*f[13] + CONSTANT(0.094031597258400004)*f[15]; + tg = CONSTANT(-0.058399170082300000)*g[3] + CONSTANT(0.145673124078000010)*g[13] + CONSTANT(0.094031597258400004)*g[15]; + y[4] += tf*g[11] + tg*f[11]; + y[11] += tf*g[4] + tg*f[4]; + t = f[4] * g[11] + f[11] * g[4]; + y[3] += CONSTANT(-0.058399170082300000)*t; + y[13] += CONSTANT(0.145673124078000010)*t; + y[15] += CONSTANT(0.094031597258400004)*t; + + // [4,16]: 8,22, + tf = CONSTANT(0.238413613494000000)*f[8] + CONSTANT(-0.075080816693699995)*f[22]; + tg = CONSTANT(0.238413613494000000)*g[8] + CONSTANT(-0.075080816693699995)*g[22]; + y[4] += tf*g[16] + tg*f[16]; + y[16] += tf*g[4] + tg*f[4]; + t = f[4] * g[16] + f[16] * g[4]; + y[8] += CONSTANT(0.238413613494000000)*t; + y[22] += CONSTANT(-0.075080816693699995)*t; + + // [4,18]: 6,20,24, + tf = CONSTANT(0.156078347226000000)*f[6] + CONSTANT(-0.190364615029000010)*f[20] + CONSTANT(0.075080816691500005)*f[24]; + tg = CONSTANT(0.156078347226000000)*g[6] + CONSTANT(-0.190364615029000010)*g[20] + CONSTANT(0.075080816691500005)*g[24]; + y[4] += tf*g[18] + tg*f[18]; + y[18] += tf*g[4] + tg*f[4]; + t = f[4] * g[18] + f[18] * g[4]; + y[6] += CONSTANT(0.156078347226000000)*t; + y[20] += CONSTANT(-0.190364615029000010)*t; + y[24] += CONSTANT(0.075080816691500005)*t; + + // [4,19]: 7,21,23, + tf = CONSTANT(-0.063718718434399996)*f[7] + CONSTANT(0.141889406569999990)*f[21] + CONSTANT(0.112621225039000000)*f[23]; + tg = CONSTANT(-0.063718718434399996)*g[7] + CONSTANT(0.141889406569999990)*g[21] + CONSTANT(0.112621225039000000)*g[23]; + y[4] += tf*g[19] + tg*f[19]; + y[19] += tf*g[4] + tg*f[4]; + t = f[4] * g[19] + f[19] * g[4]; + y[7] += CONSTANT(-0.063718718434399996)*t; + y[21] += CONSTANT(0.141889406569999990)*t; + y[23] += CONSTANT(0.112621225039000000)*t; + + // [5,5]: 0,6,8,20,22, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.090111875786499998)*f[6] + CONSTANT(-0.156078347227999990)*f[8] + CONSTANT(-0.161197023870999990)*f[20] + CONSTANT(-0.180223751574000000)*f[22]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.090111875786499998)*g[6] + CONSTANT(-0.156078347227999990)*g[8] + CONSTANT(-0.161197023870999990)*g[20] + CONSTANT(-0.180223751574000000)*g[22]; + y[5] += tf*g[5] + tg*f[5]; + t = f[5] * g[5]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[6] += CONSTANT(0.090111875786499998)*t; + y[8] += CONSTANT(-0.156078347227999990)*t; + y[20] += CONSTANT(-0.161197023870999990)*t; + y[22] += CONSTANT(-0.180223751574000000)*t; + + // [5,11]: 2,12,14, + tf = CONSTANT(0.233596680327000010)*f[2] + CONSTANT(0.059470803871800003)*f[12] + CONSTANT(-0.115164716491000000)*f[14]; + tg = CONSTANT(0.233596680327000010)*g[2] + CONSTANT(0.059470803871800003)*g[12] + CONSTANT(-0.115164716491000000)*g[14]; + y[5] += tf*g[11] + tg*f[11]; + y[11] += tf*g[5] + tg*f[5]; + t = f[5] * g[11] + f[11] * g[5]; + y[2] += CONSTANT(0.233596680327000010)*t; + y[12] += CONSTANT(0.059470803871800003)*t; + y[14] += CONSTANT(-0.115164716491000000)*t; + + // [5,17]: 8,22,24, + tf = CONSTANT(0.168583882832999990)*f[8] + CONSTANT(0.132725386548000010)*f[22] + CONSTANT(-0.140463346189000000)*f[24]; + tg = CONSTANT(0.168583882832999990)*g[8] + CONSTANT(0.132725386548000010)*g[22] + CONSTANT(-0.140463346189000000)*g[24]; + y[5] += tf*g[17] + tg*f[17]; + y[17] += tf*g[5] + tg*f[5]; + t = f[5] * g[17] + f[17] * g[5]; + y[8] += CONSTANT(0.168583882832999990)*t; + y[22] += CONSTANT(0.132725386548000010)*t; + y[24] += CONSTANT(-0.140463346189000000)*t; + + // [5,18]: 7,21,23, + tf = CONSTANT(0.180223751571000010)*f[7] + CONSTANT(0.090297865407399994)*f[21] + CONSTANT(-0.132725386549000010)*f[23]; + tg = CONSTANT(0.180223751571000010)*g[7] + CONSTANT(0.090297865407399994)*g[21] + CONSTANT(-0.132725386549000010)*g[23]; + y[5] += tf*g[18] + tg*f[18]; + y[18] += tf*g[5] + tg*f[5]; + t = f[5] * g[18] + f[18] * g[5]; + y[7] += CONSTANT(0.180223751571000010)*t; + y[21] += CONSTANT(0.090297865407399994)*t; + y[23] += CONSTANT(-0.132725386549000010)*t; + + // [5,19]: 6,8,20,22, + tf = CONSTANT(0.220728115440999990)*f[6] + CONSTANT(0.063718718433900007)*f[8] + CONSTANT(0.044869370061299998)*f[20] + CONSTANT(-0.090297865408399999)*f[22]; + tg = CONSTANT(0.220728115440999990)*g[6] + CONSTANT(0.063718718433900007)*g[8] + CONSTANT(0.044869370061299998)*g[20] + CONSTANT(-0.090297865408399999)*g[22]; + y[5] += tf*g[19] + tg*f[19]; + y[19] += tf*g[5] + tg*f[5]; + t = f[5] * g[19] + f[19] * g[5]; + y[6] += CONSTANT(0.220728115440999990)*t; + y[8] += CONSTANT(0.063718718433900007)*t; + y[20] += CONSTANT(0.044869370061299998)*t; + y[22] += CONSTANT(-0.090297865408399999)*t; + + // [6,6]: 0,6,20, + tf = CONSTANT(0.282094797560000000)*f[0] + CONSTANT(0.241795553185999990)*f[20]; + tg = CONSTANT(0.282094797560000000)*g[0] + CONSTANT(0.241795553185999990)*g[20]; + y[6] += tf*g[6] + tg*f[6]; + t = f[6] * g[6]; + y[0] += CONSTANT(0.282094797560000000)*t; + y[6] += CONSTANT(0.180223764527000010)*t; + y[20] += CONSTANT(0.241795553185999990)*t; + + // [7,7]: 6,0,8,20,22, + tf = CONSTANT(0.090111875786499998)*f[6] + CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.156078347227999990)*f[8] + CONSTANT(-0.161197023870999990)*f[20] + CONSTANT(0.180223751574000000)*f[22]; + tg = CONSTANT(0.090111875786499998)*g[6] + CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.156078347227999990)*g[8] + CONSTANT(-0.161197023870999990)*g[20] + CONSTANT(0.180223751574000000)*g[22]; + y[7] += tf*g[7] + tg*f[7]; + t = f[7] * g[7]; + y[6] += CONSTANT(0.090111875786499998)*t; + y[0] += CONSTANT(0.282094791773999990)*t; + y[8] += CONSTANT(0.156078347227999990)*t; + y[20] += CONSTANT(-0.161197023870999990)*t; + y[22] += CONSTANT(0.180223751574000000)*t; + + // [7,13]: 12,2,14, + tf = CONSTANT(0.059470803871800003)*f[12] + CONSTANT(0.233596680327000010)*f[2] + CONSTANT(0.115164716491000000)*f[14]; + tg = CONSTANT(0.059470803871800003)*g[12] + CONSTANT(0.233596680327000010)*g[2] + CONSTANT(0.115164716491000000)*g[14]; + y[7] += tf*g[13] + tg*f[13]; + y[13] += tf*g[7] + tg*f[7]; + t = f[7] * g[13] + f[13] * g[7]; + y[12] += CONSTANT(0.059470803871800003)*t; + y[2] += CONSTANT(0.233596680327000010)*t; + y[14] += CONSTANT(0.115164716491000000)*t; + + // [7,17]: 16,4,18, + tf = CONSTANT(0.140463346187999990)*f[16] + CONSTANT(0.168583882835000000)*f[4] + CONSTANT(0.132725386549000010)*f[18]; + tg = CONSTANT(0.140463346187999990)*g[16] + CONSTANT(0.168583882835000000)*g[4] + CONSTANT(0.132725386549000010)*g[18]; + y[7] += tf*g[17] + tg*f[17]; + y[17] += tf*g[7] + tg*f[7]; + t = f[7] * g[17] + f[17] * g[7]; + y[16] += CONSTANT(0.140463346187999990)*t; + y[4] += CONSTANT(0.168583882835000000)*t; + y[18] += CONSTANT(0.132725386549000010)*t; + + // [7,21]: 8,20,6,22, + tf = CONSTANT(-0.063718718433900007)*f[8] + CONSTANT(0.044869370061299998)*f[20] + CONSTANT(0.220728115440999990)*f[6] + CONSTANT(0.090297865408399999)*f[22]; + tg = CONSTANT(-0.063718718433900007)*g[8] + CONSTANT(0.044869370061299998)*g[20] + CONSTANT(0.220728115440999990)*g[6] + CONSTANT(0.090297865408399999)*g[22]; + y[7] += tf*g[21] + tg*f[21]; + y[21] += tf*g[7] + tg*f[7]; + t = f[7] * g[21] + f[21] * g[7]; + y[8] += CONSTANT(-0.063718718433900007)*t; + y[20] += CONSTANT(0.044869370061299998)*t; + y[6] += CONSTANT(0.220728115440999990)*t; + y[22] += CONSTANT(0.090297865408399999)*t; + + // [7,23]: 8,22,24, + tf = CONSTANT(0.168583882832999990)*f[8] + CONSTANT(0.132725386548000010)*f[22] + CONSTANT(0.140463346189000000)*f[24]; + tg = CONSTANT(0.168583882832999990)*g[8] + CONSTANT(0.132725386548000010)*g[22] + CONSTANT(0.140463346189000000)*g[24]; + y[7] += tf*g[23] + tg*f[23]; + y[23] += tf*g[7] + tg*f[7]; + t = f[7] * g[23] + f[23] * g[7]; + y[8] += CONSTANT(0.168583882832999990)*t; + y[22] += CONSTANT(0.132725386548000010)*t; + y[24] += CONSTANT(0.140463346189000000)*t; + + // [8,8]: 0,6,20,24, + tf = CONSTANT(0.282094791770000020)*f[0] + CONSTANT(-0.180223751576000010)*f[6] + CONSTANT(0.040299255967500003)*f[20] + CONSTANT(0.238413613505999990)*f[24]; + tg = CONSTANT(0.282094791770000020)*g[0] + CONSTANT(-0.180223751576000010)*g[6] + CONSTANT(0.040299255967500003)*g[20] + CONSTANT(0.238413613505999990)*g[24]; + y[8] += tf*g[8] + tg*f[8]; + t = f[8] * g[8]; + y[0] += CONSTANT(0.282094791770000020)*t; + y[6] += CONSTANT(-0.180223751576000010)*t; + y[20] += CONSTANT(0.040299255967500003)*t; + y[24] += CONSTANT(0.238413613505999990)*t; + + // [8,22]: 6,20,24, + tf = CONSTANT(0.156078347226000000)*f[6] + CONSTANT(-0.190364615029000010)*f[20] + CONSTANT(-0.075080816691500005)*f[24]; + tg = CONSTANT(0.156078347226000000)*g[6] + CONSTANT(-0.190364615029000010)*g[20] + CONSTANT(-0.075080816691500005)*g[24]; + y[8] += tf*g[22] + tg*f[22]; + y[22] += tf*g[8] + tg*f[8]; + t = f[8] * g[22] + f[22] * g[8]; + y[6] += CONSTANT(0.156078347226000000)*t; + y[20] += CONSTANT(-0.190364615029000010)*t; + y[24] += CONSTANT(-0.075080816691500005)*t; + + // [9,9]: 6,0,20, + tf = CONSTANT(-0.210261043508000010)*f[6] + CONSTANT(0.282094791766999970)*f[0] + CONSTANT(0.076934943209800002)*f[20]; + tg = CONSTANT(-0.210261043508000010)*g[6] + CONSTANT(0.282094791766999970)*g[0] + CONSTANT(0.076934943209800002)*g[20]; + y[9] += tf*g[9] + tg*f[9]; + t = f[9] * g[9]; + y[6] += CONSTANT(-0.210261043508000010)*t; + y[0] += CONSTANT(0.282094791766999970)*t; + y[20] += CONSTANT(0.076934943209800002)*t; + + // [9,10]: 7,21, + tf = CONSTANT(0.148677009678999990)*f[7] + CONSTANT(-0.099322584599600000)*f[21]; + tg = CONSTANT(0.148677009678999990)*g[7] + CONSTANT(-0.099322584599600000)*g[21]; + y[9] += tf*g[10] + tg*f[10]; + y[10] += tf*g[9] + tg*f[9]; + t = f[9] * g[10] + f[10] * g[9]; + y[7] += CONSTANT(0.148677009678999990)*t; + y[21] += CONSTANT(-0.099322584599600000)*t; + + // [9,11]: 8,22,24, + tf = CONSTANT(-0.094031597259499999)*f[8] + CONSTANT(0.133255230518000010)*f[22] + CONSTANT(0.117520066950999990)*f[24]; + tg = CONSTANT(-0.094031597259499999)*g[8] + CONSTANT(0.133255230518000010)*g[22] + CONSTANT(0.117520066950999990)*g[24]; + y[9] += tf*g[11] + tg*f[11]; + y[11] += tf*g[9] + tg*f[9]; + t = f[9] * g[11] + f[11] * g[9]; + y[8] += CONSTANT(-0.094031597259499999)*t; + y[22] += CONSTANT(0.133255230518000010)*t; + y[24] += CONSTANT(0.117520066950999990)*t; + + // [9,13]: 4,16,18, + tf = CONSTANT(-0.094031597258400004)*f[4] + CONSTANT(-0.117520066953000000)*f[16] + CONSTANT(0.133255230519000010)*f[18]; + tg = CONSTANT(-0.094031597258400004)*g[4] + CONSTANT(-0.117520066953000000)*g[16] + CONSTANT(0.133255230519000010)*g[18]; + y[9] += tf*g[13] + tg*f[13]; + y[13] += tf*g[9] + tg*f[9]; + t = f[9] * g[13] + f[13] * g[9]; + y[4] += CONSTANT(-0.094031597258400004)*t; + y[16] += CONSTANT(-0.117520066953000000)*t; + y[18] += CONSTANT(0.133255230519000010)*t; + + // [9,14]: 5,19, + tf = CONSTANT(0.148677009677999990)*f[5] + CONSTANT(-0.099322584600699995)*f[19]; + tg = CONSTANT(0.148677009677999990)*g[5] + CONSTANT(-0.099322584600699995)*g[19]; + y[9] += tf*g[14] + tg*f[14]; + y[14] += tf*g[9] + tg*f[9]; + t = f[9] * g[14] + f[14] * g[9]; + y[5] += CONSTANT(0.148677009677999990)*t; + y[19] += CONSTANT(-0.099322584600699995)*t; + + // [9,17]: 2,12, + tf = CONSTANT(0.162867503964999990)*f[2] + CONSTANT(-0.203550726872999990)*f[12]; + tg = CONSTANT(0.162867503964999990)*g[2] + CONSTANT(-0.203550726872999990)*g[12]; + y[9] += tf*g[17] + tg*f[17]; + y[17] += tf*g[9] + tg*f[9]; + t = f[9] * g[17] + f[17] * g[9]; + y[2] += CONSTANT(0.162867503964999990)*t; + y[12] += CONSTANT(-0.203550726872999990)*t; + + // [10,10]: 0,20,24, + tf = CONSTANT(0.282094791771999980)*f[0] + CONSTANT(-0.179514867494000000)*f[20] + CONSTANT(-0.151717754049000010)*f[24]; + tg = CONSTANT(0.282094791771999980)*g[0] + CONSTANT(-0.179514867494000000)*g[20] + CONSTANT(-0.151717754049000010)*g[24]; + y[10] += tf*g[10] + tg*f[10]; + t = f[10] * g[10]; + y[0] += CONSTANT(0.282094791771999980)*t; + y[20] += CONSTANT(-0.179514867494000000)*t; + y[24] += CONSTANT(-0.151717754049000010)*t; + + // [10,11]: 7,21,23, + tf = CONSTANT(0.115164716490000000)*f[7] + CONSTANT(0.102579924281000000)*f[21] + CONSTANT(-0.067850242288900006)*f[23]; + tg = CONSTANT(0.115164716490000000)*g[7] + CONSTANT(0.102579924281000000)*g[21] + CONSTANT(-0.067850242288900006)*g[23]; + y[10] += tf*g[11] + tg*f[11]; + y[11] += tf*g[10] + tg*f[10]; + t = f[10] * g[11] + f[11] * g[10]; + y[7] += CONSTANT(0.115164716490000000)*t; + y[21] += CONSTANT(0.102579924281000000)*t; + y[23] += CONSTANT(-0.067850242288900006)*t; + + // [10,12]: 4,18, + tf = CONSTANT(-0.188063194517999990)*f[4] + CONSTANT(-0.044418410173299998)*f[18]; + tg = CONSTANT(-0.188063194517999990)*g[4] + CONSTANT(-0.044418410173299998)*g[18]; + y[10] += tf*g[12] + tg*f[12]; + y[12] += tf*g[10] + tg*f[10]; + t = f[10] * g[12] + f[12] * g[10]; + y[4] += CONSTANT(-0.188063194517999990)*t; + y[18] += CONSTANT(-0.044418410173299998)*t; + + // [10,13]: 5,17,19, + tf = CONSTANT(0.115164716490000000)*f[5] + CONSTANT(0.067850242288900006)*f[17] + CONSTANT(0.102579924281000000)*f[19]; + tg = CONSTANT(0.115164716490000000)*g[5] + CONSTANT(0.067850242288900006)*g[17] + CONSTANT(0.102579924281000000)*g[19]; + y[10] += tf*g[13] + tg*f[13]; + y[13] += tf*g[10] + tg*f[10]; + t = f[10] * g[13] + f[13] * g[10]; + y[5] += CONSTANT(0.115164716490000000)*t; + y[17] += CONSTANT(0.067850242288900006)*t; + y[19] += CONSTANT(0.102579924281000000)*t; + + // [10,14]: 16, + tf = CONSTANT(0.151717754044999990)*f[16]; + tg = CONSTANT(0.151717754044999990)*g[16]; + y[10] += tf*g[14] + tg*f[14]; + y[14] += tf*g[10] + tg*f[10]; + t = f[10] * g[14] + f[14] * g[10]; + y[16] += CONSTANT(0.151717754044999990)*t; + + // [10,15]: 5,19, + tf = CONSTANT(-0.148677009678999990)*f[5] + CONSTANT(0.099322584599600000)*f[19]; + tg = CONSTANT(-0.148677009678999990)*g[5] + CONSTANT(0.099322584599600000)*g[19]; + y[10] += tf*g[15] + tg*f[15]; + y[15] += tf*g[10] + tg*f[10]; + t = f[10] * g[15] + f[15] * g[10]; + y[5] += CONSTANT(-0.148677009678999990)*t; + y[19] += CONSTANT(0.099322584599600000)*t; + + // [11,11]: 0,6,8,20,22, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.126156626101000010)*f[6] + CONSTANT(-0.145673124078999990)*f[8] + CONSTANT(0.025644981070299999)*f[20] + CONSTANT(-0.114687841910000000)*f[22]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.126156626101000010)*g[6] + CONSTANT(-0.145673124078999990)*g[8] + CONSTANT(0.025644981070299999)*g[20] + CONSTANT(-0.114687841910000000)*g[22]; + y[11] += tf*g[11] + tg*f[11]; + t = f[11] * g[11]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[6] += CONSTANT(0.126156626101000010)*t; + y[8] += CONSTANT(-0.145673124078999990)*t; + y[20] += CONSTANT(0.025644981070299999)*t; + y[22] += CONSTANT(-0.114687841910000000)*t; + + // [11,14]: 17, + tf = CONSTANT(0.067850242288500007)*f[17]; + tg = CONSTANT(0.067850242288500007)*g[17]; + y[11] += tf*g[14] + tg*f[14]; + y[14] += tf*g[11] + tg*f[11]; + t = f[11] * g[14] + f[14] * g[11]; + y[17] += CONSTANT(0.067850242288500007)*t; + + // [11,15]: 16, + tf = CONSTANT(-0.117520066953000000)*f[16]; + tg = CONSTANT(-0.117520066953000000)*g[16]; + y[11] += tf*g[15] + tg*f[15]; + y[15] += tf*g[11] + tg*f[11]; + t = f[11] * g[15] + f[15] * g[11]; + y[16] += CONSTANT(-0.117520066953000000)*t; + + // [11,18]: 3,13,15, + tf = CONSTANT(0.168583882834000000)*f[3] + CONSTANT(0.114687841909000000)*f[13] + CONSTANT(-0.133255230519000010)*f[15]; + tg = CONSTANT(0.168583882834000000)*g[3] + CONSTANT(0.114687841909000000)*g[13] + CONSTANT(-0.133255230519000010)*g[15]; + y[11] += tf*g[18] + tg*f[18]; + y[18] += tf*g[11] + tg*f[11]; + t = f[11] * g[18] + f[18] * g[11]; + y[3] += CONSTANT(0.168583882834000000)*t; + y[13] += CONSTANT(0.114687841909000000)*t; + y[15] += CONSTANT(-0.133255230519000010)*t; + + // [11,19]: 2,14,12, + tf = CONSTANT(0.238413613504000000)*f[2] + CONSTANT(-0.102579924282000000)*f[14] + CONSTANT(0.099322584599300004)*f[12]; + tg = CONSTANT(0.238413613504000000)*g[2] + CONSTANT(-0.102579924282000000)*g[14] + CONSTANT(0.099322584599300004)*g[12]; + y[11] += tf*g[19] + tg*f[19]; + y[19] += tf*g[11] + tg*f[11]; + t = f[11] * g[19] + f[19] * g[11]; + y[2] += CONSTANT(0.238413613504000000)*t; + y[14] += CONSTANT(-0.102579924282000000)*t; + y[12] += CONSTANT(0.099322584599300004)*t; + + // [12,12]: 0,6,20, + tf = CONSTANT(0.282094799871999980)*f[0] + CONSTANT(0.168208852954000010)*f[6] + CONSTANT(0.153869910786000010)*f[20]; + tg = CONSTANT(0.282094799871999980)*g[0] + CONSTANT(0.168208852954000010)*g[6] + CONSTANT(0.153869910786000010)*g[20]; + y[12] += tf*g[12] + tg*f[12]; + t = f[12] * g[12]; + y[0] += CONSTANT(0.282094799871999980)*t; + y[6] += CONSTANT(0.168208852954000010)*t; + y[20] += CONSTANT(0.153869910786000010)*t; + + // [12,14]: 8,22, + tf = CONSTANT(-0.188063194517999990)*f[8] + CONSTANT(-0.044418410173299998)*f[22]; + tg = CONSTANT(-0.188063194517999990)*g[8] + CONSTANT(-0.044418410173299998)*g[22]; + y[12] += tf*g[14] + tg*f[14]; + y[14] += tf*g[12] + tg*f[12]; + t = f[12] * g[14] + f[14] * g[12]; + y[8] += CONSTANT(-0.188063194517999990)*t; + y[22] += CONSTANT(-0.044418410173299998)*t; + + // [13,13]: 0,8,6,20,22, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.145673124078999990)*f[8] + CONSTANT(0.126156626101000010)*f[6] + CONSTANT(0.025644981070299999)*f[20] + CONSTANT(0.114687841910000000)*f[22]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.145673124078999990)*g[8] + CONSTANT(0.126156626101000010)*g[6] + CONSTANT(0.025644981070299999)*g[20] + CONSTANT(0.114687841910000000)*g[22]; + y[13] += tf*g[13] + tg*f[13]; + t = f[13] * g[13]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[8] += CONSTANT(0.145673124078999990)*t; + y[6] += CONSTANT(0.126156626101000010)*t; + y[20] += CONSTANT(0.025644981070299999)*t; + y[22] += CONSTANT(0.114687841910000000)*t; + + // [13,14]: 23, + tf = CONSTANT(0.067850242288500007)*f[23]; + tg = CONSTANT(0.067850242288500007)*g[23]; + y[13] += tf*g[14] + tg*f[14]; + y[14] += tf*g[13] + tg*f[13]; + t = f[13] * g[14] + f[14] * g[13]; + y[23] += CONSTANT(0.067850242288500007)*t; + + // [13,15]: 8,22,24, + tf = CONSTANT(-0.094031597259499999)*f[8] + CONSTANT(0.133255230518000010)*f[22] + CONSTANT(-0.117520066950999990)*f[24]; + tg = CONSTANT(-0.094031597259499999)*g[8] + CONSTANT(0.133255230518000010)*g[22] + CONSTANT(-0.117520066950999990)*g[24]; + y[13] += tf*g[15] + tg*f[15]; + y[15] += tf*g[13] + tg*f[13]; + t = f[13] * g[15] + f[15] * g[13]; + y[8] += CONSTANT(-0.094031597259499999)*t; + y[22] += CONSTANT(0.133255230518000010)*t; + y[24] += CONSTANT(-0.117520066950999990)*t; + + // [13,21]: 2,12,14, + tf = CONSTANT(0.238413613504000000)*f[2] + CONSTANT(0.099322584599300004)*f[12] + CONSTANT(0.102579924282000000)*f[14]; + tg = CONSTANT(0.238413613504000000)*g[2] + CONSTANT(0.099322584599300004)*g[12] + CONSTANT(0.102579924282000000)*g[14]; + y[13] += tf*g[21] + tg*f[21]; + y[21] += tf*g[13] + tg*f[13]; + t = f[13] * g[21] + f[21] * g[13]; + y[2] += CONSTANT(0.238413613504000000)*t; + y[12] += CONSTANT(0.099322584599300004)*t; + y[14] += CONSTANT(0.102579924282000000)*t; + + // [14,14]: 0,20,24, + tf = CONSTANT(0.282094791771999980)*f[0] + CONSTANT(-0.179514867494000000)*f[20] + CONSTANT(0.151717754049000010)*f[24]; + tg = CONSTANT(0.282094791771999980)*g[0] + CONSTANT(-0.179514867494000000)*g[20] + CONSTANT(0.151717754049000010)*g[24]; + y[14] += tf*g[14] + tg*f[14]; + t = f[14] * g[14]; + y[0] += CONSTANT(0.282094791771999980)*t; + y[20] += CONSTANT(-0.179514867494000000)*t; + y[24] += CONSTANT(0.151717754049000010)*t; + + // [14,15]: 7,21, + tf = CONSTANT(0.148677009677999990)*f[7] + CONSTANT(-0.099322584600699995)*f[21]; + tg = CONSTANT(0.148677009677999990)*g[7] + CONSTANT(-0.099322584600699995)*g[21]; + y[14] += tf*g[15] + tg*f[15]; + y[15] += tf*g[14] + tg*f[14]; + t = f[14] * g[15] + f[15] * g[14]; + y[7] += CONSTANT(0.148677009677999990)*t; + y[21] += CONSTANT(-0.099322584600699995)*t; + + // [15,15]: 0,6,20, + tf = CONSTANT(0.282094791766999970)*f[0] + CONSTANT(-0.210261043508000010)*f[6] + CONSTANT(0.076934943209800002)*f[20]; + tg = CONSTANT(0.282094791766999970)*g[0] + CONSTANT(-0.210261043508000010)*g[6] + CONSTANT(0.076934943209800002)*g[20]; + y[15] += tf*g[15] + tg*f[15]; + t = f[15] * g[15]; + y[0] += CONSTANT(0.282094791766999970)*t; + y[6] += CONSTANT(-0.210261043508000010)*t; + y[20] += CONSTANT(0.076934943209800002)*t; + + // [15,23]: 12,2, + tf = CONSTANT(-0.203550726872999990)*f[12] + CONSTANT(0.162867503964999990)*f[2]; + tg = CONSTANT(-0.203550726872999990)*g[12] + CONSTANT(0.162867503964999990)*g[2]; + y[15] += tf*g[23] + tg*f[23]; + y[23] += tf*g[15] + tg*f[15]; + t = f[15] * g[23] + f[23] * g[15]; + y[12] += CONSTANT(-0.203550726872999990)*t; + y[2] += CONSTANT(0.162867503964999990)*t; + + // [16,16]: 0,6,20, + tf = CONSTANT(0.282094791763999990)*f[0] + CONSTANT(-0.229375683829000000)*f[6] + CONSTANT(0.106525305981000000)*f[20]; + tg = CONSTANT(0.282094791763999990)*g[0] + CONSTANT(-0.229375683829000000)*g[6] + CONSTANT(0.106525305981000000)*g[20]; + y[16] += tf*g[16] + tg*f[16]; + t = f[16] * g[16]; + y[0] += CONSTANT(0.282094791763999990)*t; + y[6] += CONSTANT(-0.229375683829000000)*t; + y[20] += CONSTANT(0.106525305981000000)*t; + + // [16,18]: 8,22, + tf = CONSTANT(-0.075080816693699995)*f[8] + CONSTANT(0.135045473380000000)*f[22]; + tg = CONSTANT(-0.075080816693699995)*g[8] + CONSTANT(0.135045473380000000)*g[22]; + y[16] += tf*g[18] + tg*f[18]; + y[18] += tf*g[16] + tg*f[16]; + t = f[16] * g[18] + f[18] * g[16]; + y[8] += CONSTANT(-0.075080816693699995)*t; + y[22] += CONSTANT(0.135045473380000000)*t; + + // [16,23]: 19,5, + tf = CONSTANT(-0.119098912754999990)*f[19] + CONSTANT(0.140463346187999990)*f[5]; + tg = CONSTANT(-0.119098912754999990)*g[19] + CONSTANT(0.140463346187999990)*g[5]; + y[16] += tf*g[23] + tg*f[23]; + y[23] += tf*g[16] + tg*f[16]; + t = f[16] * g[23] + f[23] * g[16]; + y[19] += CONSTANT(-0.119098912754999990)*t; + y[5] += CONSTANT(0.140463346187999990)*t; + + // [17,17]: 0,6,20, + tf = CONSTANT(0.282094791768999990)*f[0] + CONSTANT(-0.057343920955899998)*f[6] + CONSTANT(-0.159787958979000000)*f[20]; + tg = CONSTANT(0.282094791768999990)*g[0] + CONSTANT(-0.057343920955899998)*g[6] + CONSTANT(-0.159787958979000000)*g[20]; + y[17] += tf*g[17] + tg*f[17]; + t = f[17] * g[17]; + y[0] += CONSTANT(0.282094791768999990)*t; + y[6] += CONSTANT(-0.057343920955899998)*t; + y[20] += CONSTANT(-0.159787958979000000)*t; + + // [17,19]: 8,22,24, + tf = CONSTANT(-0.112621225039000000)*f[8] + CONSTANT(0.045015157794100001)*f[22] + CONSTANT(0.119098912753000000)*f[24]; + tg = CONSTANT(-0.112621225039000000)*g[8] + CONSTANT(0.045015157794100001)*g[22] + CONSTANT(0.119098912753000000)*g[24]; + y[17] += tf*g[19] + tg*f[19]; + y[19] += tf*g[17] + tg*f[17]; + t = f[17] * g[19] + f[19] * g[17]; + y[8] += CONSTANT(-0.112621225039000000)*t; + y[22] += CONSTANT(0.045015157794100001)*t; + y[24] += CONSTANT(0.119098912753000000)*t; + + // [17,21]: 16,4,18, + tf = CONSTANT(-0.119098912754999990)*f[16] + CONSTANT(-0.112621225039000000)*f[4] + CONSTANT(0.045015157794399997)*f[18]; + tg = CONSTANT(-0.119098912754999990)*g[16] + CONSTANT(-0.112621225039000000)*g[4] + CONSTANT(0.045015157794399997)*g[18]; + y[17] += tf*g[21] + tg*f[21]; + y[21] += tf*g[17] + tg*f[17]; + t = f[17] * g[21] + f[21] * g[17]; + y[16] += CONSTANT(-0.119098912754999990)*t; + y[4] += CONSTANT(-0.112621225039000000)*t; + y[18] += CONSTANT(0.045015157794399997)*t; + + // [18,18]: 6,0,20,24, + tf = CONSTANT(0.065535909662600006)*f[6] + CONSTANT(0.282094791771999980)*f[0] + CONSTANT(-0.083698454702400005)*f[20] + CONSTANT(-0.135045473384000000)*f[24]; + tg = CONSTANT(0.065535909662600006)*g[6] + CONSTANT(0.282094791771999980)*g[0] + CONSTANT(-0.083698454702400005)*g[20] + CONSTANT(-0.135045473384000000)*g[24]; + y[18] += tf*g[18] + tg*f[18]; + t = f[18] * g[18]; + y[6] += CONSTANT(0.065535909662600006)*t; + y[0] += CONSTANT(0.282094791771999980)*t; + y[20] += CONSTANT(-0.083698454702400005)*t; + y[24] += CONSTANT(-0.135045473384000000)*t; + + // [18,19]: 7,21,23, + tf = CONSTANT(0.090297865407399994)*f[7] + CONSTANT(0.102084782359000000)*f[21] + CONSTANT(-0.045015157794399997)*f[23]; + tg = CONSTANT(0.090297865407399994)*g[7] + CONSTANT(0.102084782359000000)*g[21] + CONSTANT(-0.045015157794399997)*g[23]; + y[18] += tf*g[19] + tg*f[19]; + y[19] += tf*g[18] + tg*f[18]; + t = f[18] * g[19] + f[19] * g[18]; + y[7] += CONSTANT(0.090297865407399994)*t; + y[21] += CONSTANT(0.102084782359000000)*t; + y[23] += CONSTANT(-0.045015157794399997)*t; + + // [19,19]: 6,8,0,20,22, + tf = CONSTANT(0.139263808033999990)*f[6] + CONSTANT(-0.141889406570999990)*f[8] + CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.068480553847200004)*f[20] + CONSTANT(-0.102084782360000000)*f[22]; + tg = CONSTANT(0.139263808033999990)*g[6] + CONSTANT(-0.141889406570999990)*g[8] + CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.068480553847200004)*g[20] + CONSTANT(-0.102084782360000000)*g[22]; + y[19] += tf*g[19] + tg*f[19]; + t = f[19] * g[19]; + y[6] += CONSTANT(0.139263808033999990)*t; + y[8] += CONSTANT(-0.141889406570999990)*t; + y[0] += CONSTANT(0.282094791773999990)*t; + y[20] += CONSTANT(0.068480553847200004)*t; + y[22] += CONSTANT(-0.102084782360000000)*t; + + // [20,20]: 6,0,20, + tf = CONSTANT(0.163839797503000010)*f[6] + CONSTANT(0.282094802232000010)*f[0]; + tg = CONSTANT(0.163839797503000010)*g[6] + CONSTANT(0.282094802232000010)*g[0]; + y[20] += tf*g[20] + tg*f[20]; + t = f[20] * g[20]; + y[6] += CONSTANT(0.163839797503000010)*t; + y[0] += CONSTANT(0.282094802232000010)*t; + y[20] += CONSTANT(0.136961139005999990)*t; + + // [21,21]: 6,20,0,8,22, + tf = CONSTANT(0.139263808033999990)*f[6] + CONSTANT(0.068480553847200004)*f[20] + CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.141889406570999990)*f[8] + CONSTANT(0.102084782360000000)*f[22]; + tg = CONSTANT(0.139263808033999990)*g[6] + CONSTANT(0.068480553847200004)*g[20] + CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.141889406570999990)*g[8] + CONSTANT(0.102084782360000000)*g[22]; + y[21] += tf*g[21] + tg*f[21]; + t = f[21] * g[21]; + y[6] += CONSTANT(0.139263808033999990)*t; + y[20] += CONSTANT(0.068480553847200004)*t; + y[0] += CONSTANT(0.282094791773999990)*t; + y[8] += CONSTANT(0.141889406570999990)*t; + y[22] += CONSTANT(0.102084782360000000)*t; + + // [21,23]: 8,22,24, + tf = CONSTANT(-0.112621225039000000)*f[8] + CONSTANT(0.045015157794100001)*f[22] + CONSTANT(-0.119098912753000000)*f[24]; + tg = CONSTANT(-0.112621225039000000)*g[8] + CONSTANT(0.045015157794100001)*g[22] + CONSTANT(-0.119098912753000000)*g[24]; + y[21] += tf*g[23] + tg*f[23]; + y[23] += tf*g[21] + tg*f[21]; + t = f[21] * g[23] + f[23] * g[21]; + y[8] += CONSTANT(-0.112621225039000000)*t; + y[22] += CONSTANT(0.045015157794100001)*t; + y[24] += CONSTANT(-0.119098912753000000)*t; + + // [22,22]: 6,20,0,24, + tf = CONSTANT(0.065535909662600006)*f[6] + CONSTANT(-0.083698454702400005)*f[20] + CONSTANT(0.282094791771999980)*f[0] + CONSTANT(0.135045473384000000)*f[24]; + tg = CONSTANT(0.065535909662600006)*g[6] + CONSTANT(-0.083698454702400005)*g[20] + CONSTANT(0.282094791771999980)*g[0] + CONSTANT(0.135045473384000000)*g[24]; + y[22] += tf*g[22] + tg*f[22]; + t = f[22] * g[22]; + y[6] += CONSTANT(0.065535909662600006)*t; + y[20] += CONSTANT(-0.083698454702400005)*t; + y[0] += CONSTANT(0.282094791771999980)*t; + y[24] += CONSTANT(0.135045473384000000)*t; + + // [23,23]: 6,20,0, + tf = CONSTANT(-0.057343920955899998)*f[6] + CONSTANT(-0.159787958979000000)*f[20] + CONSTANT(0.282094791768999990)*f[0]; + tg = CONSTANT(-0.057343920955899998)*g[6] + CONSTANT(-0.159787958979000000)*g[20] + CONSTANT(0.282094791768999990)*g[0]; + y[23] += tf*g[23] + tg*f[23]; + t = f[23] * g[23]; + y[6] += CONSTANT(-0.057343920955899998)*t; + y[20] += CONSTANT(-0.159787958979000000)*t; + y[0] += CONSTANT(0.282094791768999990)*t; + + // [24,24]: 6,0,20, + tf = CONSTANT(-0.229375683829000000)*f[6] + CONSTANT(0.282094791763999990)*f[0] + CONSTANT(0.106525305981000000)*f[20]; + tg = CONSTANT(-0.229375683829000000)*g[6] + CONSTANT(0.282094791763999990)*g[0] + CONSTANT(0.106525305981000000)*g[20]; + y[24] += tf*g[24] + tg*f[24]; + t = f[24] * g[24]; + y[6] += CONSTANT(-0.229375683829000000)*t; + y[0] += CONSTANT(0.282094791763999990)*t; + y[20] += CONSTANT(0.106525305981000000)*t; + + // multiply count=1135 + + return y; +} + + +//------------------------------------------------------------------------------------- +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb232909.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +float* DirectX::XMSHMultiply6( + float *y, + const float *f, + const float *g) noexcept +{ + if (!y || !f || !g) + return nullptr; + + REAL tf, tg, t; + // [0,0]: 0, + y[0] = CONSTANT(0.282094792935999980)*f[0] * g[0]; + + // [1,1]: 0,6,8, + tf = CONSTANT(0.282094791773000010)*f[0] + CONSTANT(-0.126156626101000010)*f[6] + CONSTANT(-0.218509686119999990)*f[8]; + tg = CONSTANT(0.282094791773000010)*g[0] + CONSTANT(-0.126156626101000010)*g[6] + CONSTANT(-0.218509686119999990)*g[8]; + y[1] = tf*g[1] + tg*f[1]; + t = f[1] * g[1]; + y[0] += CONSTANT(0.282094791773000010)*t; + y[6] = CONSTANT(-0.126156626101000010)*t; + y[8] = CONSTANT(-0.218509686119999990)*t; + + // [1,4]: 3,13,15, + tf = CONSTANT(0.218509686114999990)*f[3] + CONSTANT(-0.058399170082300000)*f[13] + CONSTANT(-0.226179013157999990)*f[15]; + tg = CONSTANT(0.218509686114999990)*g[3] + CONSTANT(-0.058399170082300000)*g[13] + CONSTANT(-0.226179013157999990)*g[15]; + y[1] += tf*g[4] + tg*f[4]; + y[4] = tf*g[1] + tg*f[1]; + t = f[1] * g[4] + f[4] * g[1]; + y[3] = CONSTANT(0.218509686114999990)*t; + y[13] = CONSTANT(-0.058399170082300000)*t; + y[15] = CONSTANT(-0.226179013157999990)*t; + + // [1,5]: 2,12, + tf = CONSTANT(0.218509686118000010)*f[2] + CONSTANT(-0.143048168103000000)*f[12]; + tg = CONSTANT(0.218509686118000010)*g[2] + CONSTANT(-0.143048168103000000)*g[12]; + y[1] += tf*g[5] + tg*f[5]; + y[5] = tf*g[1] + tg*f[1]; + t = f[1] * g[5] + f[5] * g[1]; + y[2] = CONSTANT(0.218509686118000010)*t; + y[12] = CONSTANT(-0.143048168103000000)*t; + + // [1,11]: 6,8,20,22, + tf = CONSTANT(0.202300659402999990)*f[6] + CONSTANT(0.058399170081799998)*f[8] + CONSTANT(-0.150786008773000000)*f[20] + CONSTANT(-0.168583882836999990)*f[22]; + tg = CONSTANT(0.202300659402999990)*g[6] + CONSTANT(0.058399170081799998)*g[8] + CONSTANT(-0.150786008773000000)*g[20] + CONSTANT(-0.168583882836999990)*g[22]; + y[1] += tf*g[11] + tg*f[11]; + y[11] = tf*g[1] + tg*f[1]; + t = f[1] * g[11] + f[11] * g[1]; + y[6] += CONSTANT(0.202300659402999990)*t; + y[8] += CONSTANT(0.058399170081799998)*t; + y[20] = CONSTANT(-0.150786008773000000)*t; + y[22] = CONSTANT(-0.168583882836999990)*t; + + // [1,16]: 15,33,35, + tf = CONSTANT(0.230329432973999990)*f[15] + CONSTANT(-0.034723468517399998)*f[33] + CONSTANT(-0.232932108051999990)*f[35]; + tg = CONSTANT(0.230329432973999990)*g[15] + CONSTANT(-0.034723468517399998)*g[33] + CONSTANT(-0.232932108051999990)*g[35]; + y[1] += tf*g[16] + tg*f[16]; + y[16] = tf*g[1] + tg*f[1]; + t = f[1] * g[16] + f[16] * g[1]; + y[15] += CONSTANT(0.230329432973999990)*t; + y[33] = CONSTANT(-0.034723468517399998)*t; + y[35] = CONSTANT(-0.232932108051999990)*t; + + // [1,18]: 15,13,31,33, + tf = CONSTANT(0.043528171377799997)*f[15] + CONSTANT(0.168583882834000000)*f[13] + CONSTANT(-0.085054779966799998)*f[31] + CONSTANT(-0.183739324705999990)*f[33]; + tg = CONSTANT(0.043528171377799997)*g[15] + CONSTANT(0.168583882834000000)*g[13] + CONSTANT(-0.085054779966799998)*g[31] + CONSTANT(-0.183739324705999990)*g[33]; + y[1] += tf*g[18] + tg*f[18]; + y[18] = tf*g[1] + tg*f[1]; + t = f[1] * g[18] + f[18] * g[1]; + y[15] += CONSTANT(0.043528171377799997)*t; + y[13] += CONSTANT(0.168583882834000000)*t; + y[31] = CONSTANT(-0.085054779966799998)*t; + y[33] += CONSTANT(-0.183739324705999990)*t; + + // [1,19]: 14,12,30,32, + tf = CONSTANT(0.075393004386399995)*f[14] + CONSTANT(0.194663900273000010)*f[12] + CONSTANT(-0.155288072037000010)*f[30] + CONSTANT(-0.159122922869999990)*f[32]; + tg = CONSTANT(0.075393004386399995)*g[14] + CONSTANT(0.194663900273000010)*g[12] + CONSTANT(-0.155288072037000010)*g[30] + CONSTANT(-0.159122922869999990)*g[32]; + y[1] += tf*g[19] + tg*f[19]; + y[19] = tf*g[1] + tg*f[1]; + t = f[1] * g[19] + f[19] * g[1]; + y[14] = CONSTANT(0.075393004386399995)*t; + y[12] += CONSTANT(0.194663900273000010)*t; + y[30] = CONSTANT(-0.155288072037000010)*t; + y[32] = CONSTANT(-0.159122922869999990)*t; + + // [1,24]: 9,25,27, + tf = CONSTANT(-0.230329432978999990)*f[9] + CONSTANT(0.232932108049000000)*f[25] + CONSTANT(0.034723468517100002)*f[27]; + tg = CONSTANT(-0.230329432978999990)*g[9] + CONSTANT(0.232932108049000000)*g[25] + CONSTANT(0.034723468517100002)*g[27]; + y[1] += tf*g[24] + tg*f[24]; + y[24] = tf*g[1] + tg*f[1]; + t = f[1] * g[24] + f[24] * g[1]; + y[9] = CONSTANT(-0.230329432978999990)*t; + y[25] = CONSTANT(0.232932108049000000)*t; + y[27] = CONSTANT(0.034723468517100002)*t; + + // [1,29]: 22,20, + tf = CONSTANT(0.085054779965999999)*f[22] + CONSTANT(0.190188269815000010)*f[20]; + tg = CONSTANT(0.085054779965999999)*g[22] + CONSTANT(0.190188269815000010)*g[20]; + y[1] += tf*g[29] + tg*f[29]; + y[29] = tf*g[1] + tg*f[1]; + t = f[1] * g[29] + f[29] * g[1]; + y[22] += CONSTANT(0.085054779965999999)*t; + y[20] += CONSTANT(0.190188269815000010)*t; + + // [2,2]: 0,6, + tf = CONSTANT(0.282094795249000000)*f[0] + CONSTANT(0.252313259986999990)*f[6]; + tg = CONSTANT(0.282094795249000000)*g[0] + CONSTANT(0.252313259986999990)*g[6]; + y[2] += tf*g[2] + tg*f[2]; + t = f[2] * g[2]; + y[0] += CONSTANT(0.282094795249000000)*t; + y[6] += CONSTANT(0.252313259986999990)*t; + + // [2,12]: 6,20, + tf = CONSTANT(0.247766706973999990)*f[6] + CONSTANT(0.246232537174000010)*f[20]; + tg = CONSTANT(0.247766706973999990)*g[6] + CONSTANT(0.246232537174000010)*g[20]; + y[2] += tf*g[12] + tg*f[12]; + y[12] += tf*g[2] + tg*f[2]; + t = f[2] * g[12] + f[12] * g[2]; + y[6] += CONSTANT(0.247766706973999990)*t; + y[20] += CONSTANT(0.246232537174000010)*t; + + // [2,20]: 30, + tf = CONSTANT(0.245532020560000010)*f[30]; + tg = CONSTANT(0.245532020560000010)*g[30]; + y[2] += tf*g[20] + tg*f[20]; + y[20] += tf*g[2] + tg*f[2]; + t = f[2] * g[20] + f[20] * g[2]; + y[30] += CONSTANT(0.245532020560000010)*t; + + // [3,3]: 0,6,8, + tf = CONSTANT(0.282094791773000010)*f[0] + CONSTANT(-0.126156626101000010)*f[6] + CONSTANT(0.218509686119999990)*f[8]; + tg = CONSTANT(0.282094791773000010)*g[0] + CONSTANT(-0.126156626101000010)*g[6] + CONSTANT(0.218509686119999990)*g[8]; + y[3] += tf*g[3] + tg*f[3]; + t = f[3] * g[3]; + y[0] += CONSTANT(0.282094791773000010)*t; + y[6] += CONSTANT(-0.126156626101000010)*t; + y[8] += CONSTANT(0.218509686119999990)*t; + + // [3,7]: 2,12, + tf = CONSTANT(0.218509686118000010)*f[2] + CONSTANT(-0.143048168103000000)*f[12]; + tg = CONSTANT(0.218509686118000010)*g[2] + CONSTANT(-0.143048168103000000)*g[12]; + y[3] += tf*g[7] + tg*f[7]; + y[7] = tf*g[3] + tg*f[3]; + t = f[3] * g[7] + f[7] * g[3]; + y[2] += CONSTANT(0.218509686118000010)*t; + y[12] += CONSTANT(-0.143048168103000000)*t; + + // [3,13]: 8,6,20,22, + tf = CONSTANT(-0.058399170081799998)*f[8] + CONSTANT(0.202300659402999990)*f[6] + CONSTANT(-0.150786008773000000)*f[20] + CONSTANT(0.168583882836999990)*f[22]; + tg = CONSTANT(-0.058399170081799998)*g[8] + CONSTANT(0.202300659402999990)*g[6] + CONSTANT(-0.150786008773000000)*g[20] + CONSTANT(0.168583882836999990)*g[22]; + y[3] += tf*g[13] + tg*f[13]; + y[13] += tf*g[3] + tg*f[3]; + t = f[3] * g[13] + f[13] * g[3]; + y[8] += CONSTANT(-0.058399170081799998)*t; + y[6] += CONSTANT(0.202300659402999990)*t; + y[20] += CONSTANT(-0.150786008773000000)*t; + y[22] += CONSTANT(0.168583882836999990)*t; + + // [3,16]: 9,25,27, + tf = CONSTANT(0.230329432973999990)*f[9] + CONSTANT(0.232932108051999990)*f[25] + CONSTANT(-0.034723468517399998)*f[27]; + tg = CONSTANT(0.230329432973999990)*g[9] + CONSTANT(0.232932108051999990)*g[25] + CONSTANT(-0.034723468517399998)*g[27]; + y[3] += tf*g[16] + tg*f[16]; + y[16] += tf*g[3] + tg*f[3]; + t = f[3] * g[16] + f[16] * g[3]; + y[9] += CONSTANT(0.230329432973999990)*t; + y[25] += CONSTANT(0.232932108051999990)*t; + y[27] += CONSTANT(-0.034723468517399998)*t; + + // [3,21]: 12,14,30,32, + tf = CONSTANT(0.194663900273000010)*f[12] + CONSTANT(-0.075393004386399995)*f[14] + CONSTANT(-0.155288072037000010)*f[30] + CONSTANT(0.159122922869999990)*f[32]; + tg = CONSTANT(0.194663900273000010)*g[12] + CONSTANT(-0.075393004386399995)*g[14] + CONSTANT(-0.155288072037000010)*g[30] + CONSTANT(0.159122922869999990)*g[32]; + y[3] += tf*g[21] + tg*f[21]; + y[21] = tf*g[3] + tg*f[3]; + t = f[3] * g[21] + f[21] * g[3]; + y[12] += CONSTANT(0.194663900273000010)*t; + y[14] += CONSTANT(-0.075393004386399995)*t; + y[30] += CONSTANT(-0.155288072037000010)*t; + y[32] += CONSTANT(0.159122922869999990)*t; + + // [3,24]: 15,33,35, + tf = CONSTANT(0.230329432978999990)*f[15] + CONSTANT(-0.034723468517100002)*f[33] + CONSTANT(0.232932108049000000)*f[35]; + tg = CONSTANT(0.230329432978999990)*g[15] + CONSTANT(-0.034723468517100002)*g[33] + CONSTANT(0.232932108049000000)*g[35]; + y[3] += tf*g[24] + tg*f[24]; + y[24] += tf*g[3] + tg*f[3]; + t = f[3] * g[24] + f[24] * g[3]; + y[15] += CONSTANT(0.230329432978999990)*t; + y[33] += CONSTANT(-0.034723468517100002)*t; + y[35] += CONSTANT(0.232932108049000000)*t; + + // [3,31]: 20,22, + tf = CONSTANT(0.190188269815000010)*f[20] + CONSTANT(-0.085054779965999999)*f[22]; + tg = CONSTANT(0.190188269815000010)*g[20] + CONSTANT(-0.085054779965999999)*g[22]; + y[3] += tf*g[31] + tg*f[31]; + y[31] += tf*g[3] + tg*f[3]; + t = f[3] * g[31] + f[31] * g[3]; + y[20] += CONSTANT(0.190188269815000010)*t; + y[22] += CONSTANT(-0.085054779965999999)*t; + + // [4,4]: 0,6,20,24, + tf = CONSTANT(0.282094791770000020)*f[0] + CONSTANT(-0.180223751576000010)*f[6] + CONSTANT(0.040299255967500003)*f[20] + CONSTANT(-0.238413613505999990)*f[24]; + tg = CONSTANT(0.282094791770000020)*g[0] + CONSTANT(-0.180223751576000010)*g[6] + CONSTANT(0.040299255967500003)*g[20] + CONSTANT(-0.238413613505999990)*g[24]; + y[4] += tf*g[4] + tg*f[4]; + t = f[4] * g[4]; + y[0] += CONSTANT(0.282094791770000020)*t; + y[6] += CONSTANT(-0.180223751576000010)*t; + y[20] += CONSTANT(0.040299255967500003)*t; + y[24] += CONSTANT(-0.238413613505999990)*t; + + // [4,5]: 7,21,23, + tf = CONSTANT(0.156078347226000000)*f[7] + CONSTANT(-0.063718718434399996)*f[21] + CONSTANT(-0.168583882835000000)*f[23]; + tg = CONSTANT(0.156078347226000000)*g[7] + CONSTANT(-0.063718718434399996)*g[21] + CONSTANT(-0.168583882835000000)*g[23]; + y[4] += tf*g[5] + tg*f[5]; + y[5] += tf*g[4] + tg*f[4]; + t = f[4] * g[5] + f[5] * g[4]; + y[7] += CONSTANT(0.156078347226000000)*t; + y[21] += CONSTANT(-0.063718718434399996)*t; + y[23] = CONSTANT(-0.168583882835000000)*t; + + // [4,9]: 3,13,31,35, + tf = CONSTANT(0.226179013157999990)*f[3] + CONSTANT(-0.094031597258400004)*f[13] + CONSTANT(0.016943317729299998)*f[31] + CONSTANT(-0.245532000542000000)*f[35]; + tg = CONSTANT(0.226179013157999990)*g[3] + CONSTANT(-0.094031597258400004)*g[13] + CONSTANT(0.016943317729299998)*g[31] + CONSTANT(-0.245532000542000000)*g[35]; + y[4] += tf*g[9] + tg*f[9]; + y[9] += tf*g[4] + tg*f[4]; + t = f[4] * g[9] + f[9] * g[4]; + y[3] += CONSTANT(0.226179013157999990)*t; + y[13] += CONSTANT(-0.094031597258400004)*t; + y[31] += CONSTANT(0.016943317729299998)*t; + y[35] += CONSTANT(-0.245532000542000000)*t; + + // [4,10]: 2,12,30,34, + tf = CONSTANT(0.184674390919999990)*f[2] + CONSTANT(-0.188063194517999990)*f[12] + CONSTANT(0.053579475144400000)*f[30] + CONSTANT(-0.190188269816000010)*f[34]; + tg = CONSTANT(0.184674390919999990)*g[2] + CONSTANT(-0.188063194517999990)*g[12] + CONSTANT(0.053579475144400000)*g[30] + CONSTANT(-0.190188269816000010)*g[34]; + y[4] += tf*g[10] + tg*f[10]; + y[10] = tf*g[4] + tg*f[4]; + t = f[4] * g[10] + f[10] * g[4]; + y[2] += CONSTANT(0.184674390919999990)*t; + y[12] += CONSTANT(-0.188063194517999990)*t; + y[30] += CONSTANT(0.053579475144400000)*t; + y[34] = CONSTANT(-0.190188269816000010)*t; + + // [4,11]: 3,13,15,31,33, + tf = CONSTANT(-0.058399170082300000)*f[3] + CONSTANT(0.145673124078000010)*f[13] + CONSTANT(0.094031597258400004)*f[15] + CONSTANT(-0.065621187395699998)*f[31] + CONSTANT(-0.141757966610000010)*f[33]; + tg = CONSTANT(-0.058399170082300000)*g[3] + CONSTANT(0.145673124078000010)*g[13] + CONSTANT(0.094031597258400004)*g[15] + CONSTANT(-0.065621187395699998)*g[31] + CONSTANT(-0.141757966610000010)*g[33]; + y[4] += tf*g[11] + tg*f[11]; + y[11] += tf*g[4] + tg*f[4]; + t = f[4] * g[11] + f[11] * g[4]; + y[3] += CONSTANT(-0.058399170082300000)*t; + y[13] += CONSTANT(0.145673124078000010)*t; + y[15] += CONSTANT(0.094031597258400004)*t; + y[31] += CONSTANT(-0.065621187395699998)*t; + y[33] += CONSTANT(-0.141757966610000010)*t; + + // [4,16]: 8,22, + tf = CONSTANT(0.238413613494000000)*f[8] + CONSTANT(-0.075080816693699995)*f[22]; + tg = CONSTANT(0.238413613494000000)*g[8] + CONSTANT(-0.075080816693699995)*g[22]; + y[4] += tf*g[16] + tg*f[16]; + y[16] += tf*g[4] + tg*f[4]; + t = f[4] * g[16] + f[16] * g[4]; + y[8] += CONSTANT(0.238413613494000000)*t; + y[22] += CONSTANT(-0.075080816693699995)*t; + + // [4,18]: 6,20,24, + tf = CONSTANT(0.156078347226000000)*f[6] + CONSTANT(-0.190364615029000010)*f[20] + CONSTANT(0.075080816691500005)*f[24]; + tg = CONSTANT(0.156078347226000000)*g[6] + CONSTANT(-0.190364615029000010)*g[20] + CONSTANT(0.075080816691500005)*g[24]; + y[4] += tf*g[18] + tg*f[18]; + y[18] += tf*g[4] + tg*f[4]; + t = f[4] * g[18] + f[18] * g[4]; + y[6] += CONSTANT(0.156078347226000000)*t; + y[20] += CONSTANT(-0.190364615029000010)*t; + y[24] += CONSTANT(0.075080816691500005)*t; + + // [4,19]: 7,21,23, + tf = CONSTANT(-0.063718718434399996)*f[7] + CONSTANT(0.141889406569999990)*f[21] + CONSTANT(0.112621225039000000)*f[23]; + tg = CONSTANT(-0.063718718434399996)*g[7] + CONSTANT(0.141889406569999990)*g[21] + CONSTANT(0.112621225039000000)*g[23]; + y[4] += tf*g[19] + tg*f[19]; + y[19] += tf*g[4] + tg*f[4]; + t = f[4] * g[19] + f[19] * g[4]; + y[7] += CONSTANT(-0.063718718434399996)*t; + y[21] += CONSTANT(0.141889406569999990)*t; + y[23] += CONSTANT(0.112621225039000000)*t; + + // [4,25]: 15,33, + tf = CONSTANT(0.245532000542000000)*f[15] + CONSTANT(-0.062641347680800000)*f[33]; + tg = CONSTANT(0.245532000542000000)*g[15] + CONSTANT(-0.062641347680800000)*g[33]; + y[4] += tf*g[25] + tg*f[25]; + y[25] += tf*g[4] + tg*f[4]; + t = f[4] * g[25] + f[25] * g[4]; + y[15] += CONSTANT(0.245532000542000000)*t; + y[33] += CONSTANT(-0.062641347680800000)*t; + + // [4,26]: 14,32, + tf = CONSTANT(0.190188269806999990)*f[14] + CONSTANT(-0.097043558542400002)*f[32]; + tg = CONSTANT(0.190188269806999990)*g[14] + CONSTANT(-0.097043558542400002)*g[32]; + y[4] += tf*g[26] + tg*f[26]; + y[26] = tf*g[4] + tg*f[4]; + t = f[4] * g[26] + f[26] * g[4]; + y[14] += CONSTANT(0.190188269806999990)*t; + y[32] += CONSTANT(-0.097043558542400002)*t; + + // [4,27]: 13,31,35, + tf = CONSTANT(0.141757966610000010)*f[13] + CONSTANT(-0.121034582549000000)*f[31] + CONSTANT(0.062641347680800000)*f[35]; + tg = CONSTANT(0.141757966610000010)*g[13] + CONSTANT(-0.121034582549000000)*g[31] + CONSTANT(0.062641347680800000)*g[35]; + y[4] += tf*g[27] + tg*f[27]; + y[27] += tf*g[4] + tg*f[4]; + t = f[4] * g[27] + f[27] * g[4]; + y[13] += CONSTANT(0.141757966610000010)*t; + y[31] += CONSTANT(-0.121034582549000000)*t; + y[35] += CONSTANT(0.062641347680800000)*t; + + // [4,28]: 12,30,34, + tf = CONSTANT(0.141757966609000000)*f[12] + CONSTANT(-0.191372478254000000)*f[30] + CONSTANT(0.097043558538899996)*f[34]; + tg = CONSTANT(0.141757966609000000)*g[12] + CONSTANT(-0.191372478254000000)*g[30] + CONSTANT(0.097043558538899996)*g[34]; + y[4] += tf*g[28] + tg*f[28]; + y[28] = tf*g[4] + tg*f[4]; + t = f[4] * g[28] + f[28] * g[4]; + y[12] += CONSTANT(0.141757966609000000)*t; + y[30] += CONSTANT(-0.191372478254000000)*t; + y[34] += CONSTANT(0.097043558538899996)*t; + + // [4,29]: 13,15,31,33, + tf = CONSTANT(-0.065621187395699998)*f[13] + CONSTANT(-0.016943317729299998)*f[15] + CONSTANT(0.140070311613999990)*f[31] + CONSTANT(0.121034582549000000)*f[33]; + tg = CONSTANT(-0.065621187395699998)*g[13] + CONSTANT(-0.016943317729299998)*g[15] + CONSTANT(0.140070311613999990)*g[31] + CONSTANT(0.121034582549000000)*g[33]; + y[4] += tf*g[29] + tg*f[29]; + y[29] += tf*g[4] + tg*f[4]; + t = f[4] * g[29] + f[29] * g[4]; + y[13] += CONSTANT(-0.065621187395699998)*t; + y[15] += CONSTANT(-0.016943317729299998)*t; + y[31] += CONSTANT(0.140070311613999990)*t; + y[33] += CONSTANT(0.121034582549000000)*t; + + // [5,5]: 0,6,8,20,22, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.090111875786499998)*f[6] + CONSTANT(-0.156078347227999990)*f[8] + CONSTANT(-0.161197023870999990)*f[20] + CONSTANT(-0.180223751574000000)*f[22]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.090111875786499998)*g[6] + CONSTANT(-0.156078347227999990)*g[8] + CONSTANT(-0.161197023870999990)*g[20] + CONSTANT(-0.180223751574000000)*g[22]; + y[5] += tf*g[5] + tg*f[5]; + t = f[5] * g[5]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[6] += CONSTANT(0.090111875786499998)*t; + y[8] += CONSTANT(-0.156078347227999990)*t; + y[20] += CONSTANT(-0.161197023870999990)*t; + y[22] += CONSTANT(-0.180223751574000000)*t; + + // [5,10]: 3,13,15,31,33, + tf = CONSTANT(0.184674390919999990)*f[3] + CONSTANT(0.115164716490000000)*f[13] + CONSTANT(-0.148677009678999990)*f[15] + CONSTANT(-0.083004965974099995)*f[31] + CONSTANT(-0.179311220383999990)*f[33]; + tg = CONSTANT(0.184674390919999990)*g[3] + CONSTANT(0.115164716490000000)*g[13] + CONSTANT(-0.148677009678999990)*g[15] + CONSTANT(-0.083004965974099995)*g[31] + CONSTANT(-0.179311220383999990)*g[33]; + y[5] += tf*g[10] + tg*f[10]; + y[10] += tf*g[5] + tg*f[5]; + t = f[5] * g[10] + f[10] * g[5]; + y[3] += CONSTANT(0.184674390919999990)*t; + y[13] += CONSTANT(0.115164716490000000)*t; + y[15] += CONSTANT(-0.148677009678999990)*t; + y[31] += CONSTANT(-0.083004965974099995)*t; + y[33] += CONSTANT(-0.179311220383999990)*t; + + // [5,11]: 2,12,14,30,32, + tf = CONSTANT(0.233596680327000010)*f[2] + CONSTANT(0.059470803871800003)*f[12] + CONSTANT(-0.115164716491000000)*f[14] + CONSTANT(-0.169433177294000010)*f[30] + CONSTANT(-0.173617342585000000)*f[32]; + tg = CONSTANT(0.233596680327000010)*g[2] + CONSTANT(0.059470803871800003)*g[12] + CONSTANT(-0.115164716491000000)*g[14] + CONSTANT(-0.169433177294000010)*g[30] + CONSTANT(-0.173617342585000000)*g[32]; + y[5] += tf*g[11] + tg*f[11]; + y[11] += tf*g[5] + tg*f[5]; + t = f[5] * g[11] + f[11] * g[5]; + y[2] += CONSTANT(0.233596680327000010)*t; + y[12] += CONSTANT(0.059470803871800003)*t; + y[14] += CONSTANT(-0.115164716491000000)*t; + y[30] += CONSTANT(-0.169433177294000010)*t; + y[32] += CONSTANT(-0.173617342585000000)*t; + + // [5,14]: 9,1,27,29, + tf = CONSTANT(0.148677009677999990)*f[9] + CONSTANT(-0.184674390923000000)*f[1] + CONSTANT(0.179311220382000010)*f[27] + CONSTANT(0.083004965973399999)*f[29]; + tg = CONSTANT(0.148677009677999990)*g[9] + CONSTANT(-0.184674390923000000)*g[1] + CONSTANT(0.179311220382000010)*g[27] + CONSTANT(0.083004965973399999)*g[29]; + y[5] += tf*g[14] + tg*f[14]; + y[14] += tf*g[5] + tg*f[5]; + t = f[5] * g[14] + f[14] * g[5]; + y[9] += CONSTANT(0.148677009677999990)*t; + y[1] += CONSTANT(-0.184674390923000000)*t; + y[27] += CONSTANT(0.179311220382000010)*t; + y[29] += CONSTANT(0.083004965973399999)*t; + + // [5,17]: 8,22,24, + tf = CONSTANT(0.168583882832999990)*f[8] + CONSTANT(0.132725386548000010)*f[22] + CONSTANT(-0.140463346189000000)*f[24]; + tg = CONSTANT(0.168583882832999990)*g[8] + CONSTANT(0.132725386548000010)*g[22] + CONSTANT(-0.140463346189000000)*g[24]; + y[5] += tf*g[17] + tg*f[17]; + y[17] = tf*g[5] + tg*f[5]; + t = f[5] * g[17] + f[17] * g[5]; + y[8] += CONSTANT(0.168583882832999990)*t; + y[22] += CONSTANT(0.132725386548000010)*t; + y[24] += CONSTANT(-0.140463346189000000)*t; + + // [5,18]: 7,21,23, + tf = CONSTANT(0.180223751571000010)*f[7] + CONSTANT(0.090297865407399994)*f[21] + CONSTANT(-0.132725386549000010)*f[23]; + tg = CONSTANT(0.180223751571000010)*g[7] + CONSTANT(0.090297865407399994)*g[21] + CONSTANT(-0.132725386549000010)*g[23]; + y[5] += tf*g[18] + tg*f[18]; + y[18] += tf*g[5] + tg*f[5]; + t = f[5] * g[18] + f[18] * g[5]; + y[7] += CONSTANT(0.180223751571000010)*t; + y[21] += CONSTANT(0.090297865407399994)*t; + y[23] += CONSTANT(-0.132725386549000010)*t; + + // [5,19]: 6,8,20,22, + tf = CONSTANT(0.220728115440999990)*f[6] + CONSTANT(0.063718718433900007)*f[8] + CONSTANT(0.044869370061299998)*f[20] + CONSTANT(-0.090297865408399999)*f[22]; + tg = CONSTANT(0.220728115440999990)*g[6] + CONSTANT(0.063718718433900007)*g[8] + CONSTANT(0.044869370061299998)*g[20] + CONSTANT(-0.090297865408399999)*g[22]; + y[5] += tf*g[19] + tg*f[19]; + y[19] += tf*g[5] + tg*f[5]; + t = f[5] * g[19] + f[19] * g[5]; + y[6] += CONSTANT(0.220728115440999990)*t; + y[8] += CONSTANT(0.063718718433900007)*t; + y[20] += CONSTANT(0.044869370061299998)*t; + y[22] += CONSTANT(-0.090297865408399999)*t; + + // [5,26]: 15,33,35, + tf = CONSTANT(0.155288072035000000)*f[15] + CONSTANT(0.138662534056999990)*f[33] + CONSTANT(-0.132882365179999990)*f[35]; + tg = CONSTANT(0.155288072035000000)*g[15] + CONSTANT(0.138662534056999990)*g[33] + CONSTANT(-0.132882365179999990)*g[35]; + y[5] += tf*g[26] + tg*f[26]; + y[26] += tf*g[5] + tg*f[5]; + t = f[5] * g[26] + f[26] * g[5]; + y[15] += CONSTANT(0.155288072035000000)*t; + y[33] += CONSTANT(0.138662534056999990)*t; + y[35] += CONSTANT(-0.132882365179999990)*t; + + // [5,28]: 15,13,31,33, + tf = CONSTANT(0.044827805096399997)*f[15] + CONSTANT(0.173617342584000000)*f[13] + CONSTANT(0.074118242118699995)*f[31] + CONSTANT(-0.114366930522000000)*f[33]; + tg = CONSTANT(0.044827805096399997)*g[15] + CONSTANT(0.173617342584000000)*g[13] + CONSTANT(0.074118242118699995)*g[31] + CONSTANT(-0.114366930522000000)*g[33]; + y[5] += tf*g[28] + tg*f[28]; + y[28] += tf*g[5] + tg*f[5]; + t = f[5] * g[28] + f[28] * g[5]; + y[15] += CONSTANT(0.044827805096399997)*t; + y[13] += CONSTANT(0.173617342584000000)*t; + y[31] += CONSTANT(0.074118242118699995)*t; + y[33] += CONSTANT(-0.114366930522000000)*t; + + // [5,29]: 12,30,32, + tf = CONSTANT(0.214317900578999990)*f[12] + CONSTANT(0.036165998945399999)*f[30] + CONSTANT(-0.074118242119099995)*f[32]; + tg = CONSTANT(0.214317900578999990)*g[12] + CONSTANT(0.036165998945399999)*g[30] + CONSTANT(-0.074118242119099995)*g[32]; + y[5] += tf*g[29] + tg*f[29]; + y[29] += tf*g[5] + tg*f[5]; + t = f[5] * g[29] + f[29] * g[5]; + y[12] += CONSTANT(0.214317900578999990)*t; + y[30] += CONSTANT(0.036165998945399999)*t; + y[32] += CONSTANT(-0.074118242119099995)*t; + + // [5,32]: 9,27, + tf = CONSTANT(-0.044827805096799997)*f[9] + CONSTANT(0.114366930522000000)*f[27]; + tg = CONSTANT(-0.044827805096799997)*g[9] + CONSTANT(0.114366930522000000)*g[27]; + y[5] += tf*g[32] + tg*f[32]; + y[32] += tf*g[5] + tg*f[5]; + t = f[5] * g[32] + f[32] * g[5]; + y[9] += CONSTANT(-0.044827805096799997)*t; + y[27] += CONSTANT(0.114366930522000000)*t; + + // [5,34]: 9,27,25, + tf = CONSTANT(-0.155288072036000010)*f[9] + CONSTANT(-0.138662534059000000)*f[27] + CONSTANT(0.132882365179000010)*f[25]; + tg = CONSTANT(-0.155288072036000010)*g[9] + CONSTANT(-0.138662534059000000)*g[27] + CONSTANT(0.132882365179000010)*g[25]; + y[5] += tf*g[34] + tg*f[34]; + y[34] += tf*g[5] + tg*f[5]; + t = f[5] * g[34] + f[34] * g[5]; + y[9] += CONSTANT(-0.155288072036000010)*t; + y[27] += CONSTANT(-0.138662534059000000)*t; + y[25] += CONSTANT(0.132882365179000010)*t; + + // [6,6]: 0,6,20, + tf = CONSTANT(0.282094797560000000)*f[0] + CONSTANT(0.241795553185999990)*f[20]; + tg = CONSTANT(0.282094797560000000)*g[0] + CONSTANT(0.241795553185999990)*g[20]; + y[6] += tf*g[6] + tg*f[6]; + t = f[6] * g[6]; + y[0] += CONSTANT(0.282094797560000000)*t; + y[6] += CONSTANT(0.180223764527000010)*t; + y[20] += CONSTANT(0.241795553185999990)*t; + + // [7,7]: 6,0,8,20,22, + tf = CONSTANT(0.090111875786499998)*f[6] + CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.156078347227999990)*f[8] + CONSTANT(-0.161197023870999990)*f[20] + CONSTANT(0.180223751574000000)*f[22]; + tg = CONSTANT(0.090111875786499998)*g[6] + CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.156078347227999990)*g[8] + CONSTANT(-0.161197023870999990)*g[20] + CONSTANT(0.180223751574000000)*g[22]; + y[7] += tf*g[7] + tg*f[7]; + t = f[7] * g[7]; + y[6] += CONSTANT(0.090111875786499998)*t; + y[0] += CONSTANT(0.282094791773999990)*t; + y[8] += CONSTANT(0.156078347227999990)*t; + y[20] += CONSTANT(-0.161197023870999990)*t; + y[22] += CONSTANT(0.180223751574000000)*t; + + // [7,10]: 9,1,11,27,29, + tf = CONSTANT(0.148677009678999990)*f[9] + CONSTANT(0.184674390919999990)*f[1] + CONSTANT(0.115164716490000000)*f[11] + CONSTANT(0.179311220383999990)*f[27] + CONSTANT(-0.083004965974099995)*f[29]; + tg = CONSTANT(0.148677009678999990)*g[9] + CONSTANT(0.184674390919999990)*g[1] + CONSTANT(0.115164716490000000)*g[11] + CONSTANT(0.179311220383999990)*g[27] + CONSTANT(-0.083004965974099995)*g[29]; + y[7] += tf*g[10] + tg*f[10]; + y[10] += tf*g[7] + tg*f[7]; + t = f[7] * g[10] + f[10] * g[7]; + y[9] += CONSTANT(0.148677009678999990)*t; + y[1] += CONSTANT(0.184674390919999990)*t; + y[11] += CONSTANT(0.115164716490000000)*t; + y[27] += CONSTANT(0.179311220383999990)*t; + y[29] += CONSTANT(-0.083004965974099995)*t; + + // [7,13]: 12,2,14,30,32, + tf = CONSTANT(0.059470803871800003)*f[12] + CONSTANT(0.233596680327000010)*f[2] + CONSTANT(0.115164716491000000)*f[14] + CONSTANT(-0.169433177294000010)*f[30] + CONSTANT(0.173617342585000000)*f[32]; + tg = CONSTANT(0.059470803871800003)*g[12] + CONSTANT(0.233596680327000010)*g[2] + CONSTANT(0.115164716491000000)*g[14] + CONSTANT(-0.169433177294000010)*g[30] + CONSTANT(0.173617342585000000)*g[32]; + y[7] += tf*g[13] + tg*f[13]; + y[13] += tf*g[7] + tg*f[7]; + t = f[7] * g[13] + f[13] * g[7]; + y[12] += CONSTANT(0.059470803871800003)*t; + y[2] += CONSTANT(0.233596680327000010)*t; + y[14] += CONSTANT(0.115164716491000000)*t; + y[30] += CONSTANT(-0.169433177294000010)*t; + y[32] += CONSTANT(0.173617342585000000)*t; + + // [7,14]: 3,15,31,33, + tf = CONSTANT(0.184674390923000000)*f[3] + CONSTANT(0.148677009677999990)*f[15] + CONSTANT(-0.083004965973399999)*f[31] + CONSTANT(0.179311220382000010)*f[33]; + tg = CONSTANT(0.184674390923000000)*g[3] + CONSTANT(0.148677009677999990)*g[15] + CONSTANT(-0.083004965973399999)*g[31] + CONSTANT(0.179311220382000010)*g[33]; + y[7] += tf*g[14] + tg*f[14]; + y[14] += tf*g[7] + tg*f[7]; + t = f[7] * g[14] + f[14] * g[7]; + y[3] += CONSTANT(0.184674390923000000)*t; + y[15] += CONSTANT(0.148677009677999990)*t; + y[31] += CONSTANT(-0.083004965973399999)*t; + y[33] += CONSTANT(0.179311220382000010)*t; + + // [7,17]: 16,4,18, + tf = CONSTANT(0.140463346187999990)*f[16] + CONSTANT(0.168583882835000000)*f[4] + CONSTANT(0.132725386549000010)*f[18]; + tg = CONSTANT(0.140463346187999990)*g[16] + CONSTANT(0.168583882835000000)*g[4] + CONSTANT(0.132725386549000010)*g[18]; + y[7] += tf*g[17] + tg*f[17]; + y[17] += tf*g[7] + tg*f[7]; + t = f[7] * g[17] + f[17] * g[7]; + y[16] += CONSTANT(0.140463346187999990)*t; + y[4] += CONSTANT(0.168583882835000000)*t; + y[18] += CONSTANT(0.132725386549000010)*t; + + // [7,21]: 8,20,6,22, + tf = CONSTANT(-0.063718718433900007)*f[8] + CONSTANT(0.044869370061299998)*f[20] + CONSTANT(0.220728115440999990)*f[6] + CONSTANT(0.090297865408399999)*f[22]; + tg = CONSTANT(-0.063718718433900007)*g[8] + CONSTANT(0.044869370061299998)*g[20] + CONSTANT(0.220728115440999990)*g[6] + CONSTANT(0.090297865408399999)*g[22]; + y[7] += tf*g[21] + tg*f[21]; + y[21] += tf*g[7] + tg*f[7]; + t = f[7] * g[21] + f[21] * g[7]; + y[8] += CONSTANT(-0.063718718433900007)*t; + y[20] += CONSTANT(0.044869370061299998)*t; + y[6] += CONSTANT(0.220728115440999990)*t; + y[22] += CONSTANT(0.090297865408399999)*t; + + // [7,23]: 8,22,24, + tf = CONSTANT(0.168583882832999990)*f[8] + CONSTANT(0.132725386548000010)*f[22] + CONSTANT(0.140463346189000000)*f[24]; + tg = CONSTANT(0.168583882832999990)*g[8] + CONSTANT(0.132725386548000010)*g[22] + CONSTANT(0.140463346189000000)*g[24]; + y[7] += tf*g[23] + tg*f[23]; + y[23] += tf*g[7] + tg*f[7]; + t = f[7] * g[23] + f[23] * g[7]; + y[8] += CONSTANT(0.168583882832999990)*t; + y[22] += CONSTANT(0.132725386548000010)*t; + y[24] += CONSTANT(0.140463346189000000)*t; + + // [7,26]: 9,25,27, + tf = CONSTANT(0.155288072035000000)*f[9] + CONSTANT(0.132882365179999990)*f[25] + CONSTANT(0.138662534056999990)*f[27]; + tg = CONSTANT(0.155288072035000000)*g[9] + CONSTANT(0.132882365179999990)*g[25] + CONSTANT(0.138662534056999990)*g[27]; + y[7] += tf*g[26] + tg*f[26]; + y[26] += tf*g[7] + tg*f[7]; + t = f[7] * g[26] + f[26] * g[7]; + y[9] += CONSTANT(0.155288072035000000)*t; + y[25] += CONSTANT(0.132882365179999990)*t; + y[27] += CONSTANT(0.138662534056999990)*t; + + // [7,28]: 27,11,9,29, + tf = CONSTANT(0.114366930522000000)*f[27] + CONSTANT(0.173617342584000000)*f[11] + CONSTANT(-0.044827805096399997)*f[9] + CONSTANT(0.074118242118699995)*f[29]; + tg = CONSTANT(0.114366930522000000)*g[27] + CONSTANT(0.173617342584000000)*g[11] + CONSTANT(-0.044827805096399997)*g[9] + CONSTANT(0.074118242118699995)*g[29]; + y[7] += tf*g[28] + tg*f[28]; + y[28] += tf*g[7] + tg*f[7]; + t = f[7] * g[28] + f[28] * g[7]; + y[27] += CONSTANT(0.114366930522000000)*t; + y[11] += CONSTANT(0.173617342584000000)*t; + y[9] += CONSTANT(-0.044827805096399997)*t; + y[29] += CONSTANT(0.074118242118699995)*t; + + // [7,31]: 30,12,32, + tf = CONSTANT(0.036165998945399999)*f[30] + CONSTANT(0.214317900578999990)*f[12] + CONSTANT(0.074118242119099995)*f[32]; + tg = CONSTANT(0.036165998945399999)*g[30] + CONSTANT(0.214317900578999990)*g[12] + CONSTANT(0.074118242119099995)*g[32]; + y[7] += tf*g[31] + tg*f[31]; + y[31] += tf*g[7] + tg*f[7]; + t = f[7] * g[31] + f[31] * g[7]; + y[30] += CONSTANT(0.036165998945399999)*t; + y[12] += CONSTANT(0.214317900578999990)*t; + y[32] += CONSTANT(0.074118242119099995)*t; + + // [7,32]: 15,33, + tf = CONSTANT(-0.044827805096799997)*f[15] + CONSTANT(0.114366930522000000)*f[33]; + tg = CONSTANT(-0.044827805096799997)*g[15] + CONSTANT(0.114366930522000000)*g[33]; + y[7] += tf*g[32] + tg*f[32]; + y[32] += tf*g[7] + tg*f[7]; + t = f[7] * g[32] + f[32] * g[7]; + y[15] += CONSTANT(-0.044827805096799997)*t; + y[33] += CONSTANT(0.114366930522000000)*t; + + // [7,34]: 15,33,35, + tf = CONSTANT(0.155288072036000010)*f[15] + CONSTANT(0.138662534059000000)*f[33] + CONSTANT(0.132882365179000010)*f[35]; + tg = CONSTANT(0.155288072036000010)*g[15] + CONSTANT(0.138662534059000000)*g[33] + CONSTANT(0.132882365179000010)*g[35]; + y[7] += tf*g[34] + tg*f[34]; + y[34] += tf*g[7] + tg*f[7]; + t = f[7] * g[34] + f[34] * g[7]; + y[15] += CONSTANT(0.155288072036000010)*t; + y[33] += CONSTANT(0.138662534059000000)*t; + y[35] += CONSTANT(0.132882365179000010)*t; + + // [8,8]: 0,6,20,24, + tf = CONSTANT(0.282094791770000020)*f[0] + CONSTANT(-0.180223751576000010)*f[6] + CONSTANT(0.040299255967500003)*f[20] + CONSTANT(0.238413613505999990)*f[24]; + tg = CONSTANT(0.282094791770000020)*g[0] + CONSTANT(-0.180223751576000010)*g[6] + CONSTANT(0.040299255967500003)*g[20] + CONSTANT(0.238413613505999990)*g[24]; + y[8] += tf*g[8] + tg*f[8]; + t = f[8] * g[8]; + y[0] += CONSTANT(0.282094791770000020)*t; + y[6] += CONSTANT(-0.180223751576000010)*t; + y[20] += CONSTANT(0.040299255967500003)*t; + y[24] += CONSTANT(0.238413613505999990)*t; + + // [8,9]: 1,11,25,29, + tf = CONSTANT(0.226179013155000000)*f[1] + CONSTANT(-0.094031597259499999)*f[11] + CONSTANT(0.245532000541000000)*f[25] + CONSTANT(0.016943317729199998)*f[29]; + tg = CONSTANT(0.226179013155000000)*g[1] + CONSTANT(-0.094031597259499999)*g[11] + CONSTANT(0.245532000541000000)*g[25] + CONSTANT(0.016943317729199998)*g[29]; + y[8] += tf*g[9] + tg*f[9]; + y[9] += tf*g[8] + tg*f[8]; + t = f[8] * g[9] + f[9] * g[8]; + y[1] += CONSTANT(0.226179013155000000)*t; + y[11] += CONSTANT(-0.094031597259499999)*t; + y[25] += CONSTANT(0.245532000541000000)*t; + y[29] += CONSTANT(0.016943317729199998)*t; + + // [8,14]: 2,12,30,34, + tf = CONSTANT(0.184674390919999990)*f[2] + CONSTANT(-0.188063194517999990)*f[12] + CONSTANT(0.053579475144400000)*f[30] + CONSTANT(0.190188269816000010)*f[34]; + tg = CONSTANT(0.184674390919999990)*g[2] + CONSTANT(-0.188063194517999990)*g[12] + CONSTANT(0.053579475144400000)*g[30] + CONSTANT(0.190188269816000010)*g[34]; + y[8] += tf*g[14] + tg*f[14]; + y[14] += tf*g[8] + tg*f[8]; + t = f[8] * g[14] + f[14] * g[8]; + y[2] += CONSTANT(0.184674390919999990)*t; + y[12] += CONSTANT(-0.188063194517999990)*t; + y[30] += CONSTANT(0.053579475144400000)*t; + y[34] += CONSTANT(0.190188269816000010)*t; + + // [8,15]: 13,3,31,35, + tf = CONSTANT(-0.094031597259499999)*f[13] + CONSTANT(0.226179013155000000)*f[3] + CONSTANT(0.016943317729199998)*f[31] + CONSTANT(0.245532000541000000)*f[35]; + tg = CONSTANT(-0.094031597259499999)*g[13] + CONSTANT(0.226179013155000000)*g[3] + CONSTANT(0.016943317729199998)*g[31] + CONSTANT(0.245532000541000000)*g[35]; + y[8] += tf*g[15] + tg*f[15]; + y[15] += tf*g[8] + tg*f[8]; + t = f[8] * g[15] + f[15] * g[8]; + y[13] += CONSTANT(-0.094031597259499999)*t; + y[3] += CONSTANT(0.226179013155000000)*t; + y[31] += CONSTANT(0.016943317729199998)*t; + y[35] += CONSTANT(0.245532000541000000)*t; + + // [8,22]: 6,20,24, + tf = CONSTANT(0.156078347226000000)*f[6] + CONSTANT(-0.190364615029000010)*f[20] + CONSTANT(-0.075080816691500005)*f[24]; + tg = CONSTANT(0.156078347226000000)*g[6] + CONSTANT(-0.190364615029000010)*g[20] + CONSTANT(-0.075080816691500005)*g[24]; + y[8] += tf*g[22] + tg*f[22]; + y[22] += tf*g[8] + tg*f[8]; + t = f[8] * g[22] + f[22] * g[8]; + y[6] += CONSTANT(0.156078347226000000)*t; + y[20] += CONSTANT(-0.190364615029000010)*t; + y[24] += CONSTANT(-0.075080816691500005)*t; + + // [8,26]: 10,28, + tf = CONSTANT(0.190188269806999990)*f[10] + CONSTANT(-0.097043558542400002)*f[28]; + tg = CONSTANT(0.190188269806999990)*g[10] + CONSTANT(-0.097043558542400002)*g[28]; + y[8] += tf*g[26] + tg*f[26]; + y[26] += tf*g[8] + tg*f[8]; + t = f[8] * g[26] + f[26] * g[8]; + y[10] += CONSTANT(0.190188269806999990)*t; + y[28] += CONSTANT(-0.097043558542400002)*t; + + // [8,27]: 25,11,29, + tf = CONSTANT(-0.062641347680800000)*f[25] + CONSTANT(0.141757966609000000)*f[11] + CONSTANT(-0.121034582550000010)*f[29]; + tg = CONSTANT(-0.062641347680800000)*g[25] + CONSTANT(0.141757966609000000)*g[11] + CONSTANT(-0.121034582550000010)*g[29]; + y[8] += tf*g[27] + tg*f[27]; + y[27] += tf*g[8] + tg*f[8]; + t = f[8] * g[27] + f[27] * g[8]; + y[25] += CONSTANT(-0.062641347680800000)*t; + y[11] += CONSTANT(0.141757966609000000)*t; + y[29] += CONSTANT(-0.121034582550000010)*t; + + // [8,32]: 30,12,34, + tf = CONSTANT(-0.191372478254000000)*f[30] + CONSTANT(0.141757966609000000)*f[12] + CONSTANT(-0.097043558538899996)*f[34]; + tg = CONSTANT(-0.191372478254000000)*g[30] + CONSTANT(0.141757966609000000)*g[12] + CONSTANT(-0.097043558538899996)*g[34]; + y[8] += tf*g[32] + tg*f[32]; + y[32] += tf*g[8] + tg*f[8]; + t = f[8] * g[32] + f[32] * g[8]; + y[30] += CONSTANT(-0.191372478254000000)*t; + y[12] += CONSTANT(0.141757966609000000)*t; + y[34] += CONSTANT(-0.097043558538899996)*t; + + // [8,33]: 13,31,35, + tf = CONSTANT(0.141757966609000000)*f[13] + CONSTANT(-0.121034582550000010)*f[31] + CONSTANT(-0.062641347680800000)*f[35]; + tg = CONSTANT(0.141757966609000000)*g[13] + CONSTANT(-0.121034582550000010)*g[31] + CONSTANT(-0.062641347680800000)*g[35]; + y[8] += tf*g[33] + tg*f[33]; + y[33] += tf*g[8] + tg*f[8]; + t = f[8] * g[33] + f[33] * g[8]; + y[13] += CONSTANT(0.141757966609000000)*t; + y[31] += CONSTANT(-0.121034582550000010)*t; + y[35] += CONSTANT(-0.062641347680800000)*t; + + // [9,9]: 6,0,20, + tf = CONSTANT(-0.210261043508000010)*f[6] + CONSTANT(0.282094791766999970)*f[0] + CONSTANT(0.076934943209800002)*f[20]; + tg = CONSTANT(-0.210261043508000010)*g[6] + CONSTANT(0.282094791766999970)*g[0] + CONSTANT(0.076934943209800002)*g[20]; + y[9] += tf*g[9] + tg*f[9]; + t = f[9] * g[9]; + y[6] += CONSTANT(-0.210261043508000010)*t; + y[0] += CONSTANT(0.282094791766999970)*t; + y[20] += CONSTANT(0.076934943209800002)*t; + + // [9,17]: 2,12,30, + tf = CONSTANT(0.162867503964999990)*f[2] + CONSTANT(-0.203550726872999990)*f[12] + CONSTANT(0.098140130728100003)*f[30]; + tg = CONSTANT(0.162867503964999990)*g[2] + CONSTANT(-0.203550726872999990)*g[12] + CONSTANT(0.098140130728100003)*g[30]; + y[9] += tf*g[17] + tg*f[17]; + y[17] += tf*g[9] + tg*f[9]; + t = f[9] * g[17] + f[17] * g[9]; + y[2] += CONSTANT(0.162867503964999990)*t; + y[12] += CONSTANT(-0.203550726872999990)*t; + y[30] += CONSTANT(0.098140130728100003)*t; + + // [9,18]: 3,13,31,35, + tf = CONSTANT(-0.043528171377799997)*f[3] + CONSTANT(0.133255230519000010)*f[13] + CONSTANT(-0.101584686310000010)*f[31] + CONSTANT(0.098140130731999994)*f[35]; + tg = CONSTANT(-0.043528171377799997)*g[3] + CONSTANT(0.133255230519000010)*g[13] + CONSTANT(-0.101584686310000010)*g[31] + CONSTANT(0.098140130731999994)*g[35]; + y[9] += tf*g[18] + tg*f[18]; + y[18] += tf*g[9] + tg*f[9]; + t = f[9] * g[18] + f[18] * g[9]; + y[3] += CONSTANT(-0.043528171377799997)*t; + y[13] += CONSTANT(0.133255230519000010)*t; + y[31] += CONSTANT(-0.101584686310000010)*t; + y[35] += CONSTANT(0.098140130731999994)*t; + + // [9,19]: 14,32,34, + tf = CONSTANT(-0.099322584600699995)*f[14] + CONSTANT(0.126698363970000010)*f[32] + CONSTANT(0.131668802180999990)*f[34]; + tg = CONSTANT(-0.099322584600699995)*g[14] + CONSTANT(0.126698363970000010)*g[32] + CONSTANT(0.131668802180999990)*g[34]; + y[9] += tf*g[19] + tg*f[19]; + y[19] += tf*g[9] + tg*f[9]; + t = f[9] * g[19] + f[19] * g[9]; + y[14] += CONSTANT(-0.099322584600699995)*t; + y[32] += CONSTANT(0.126698363970000010)*t; + y[34] += CONSTANT(0.131668802180999990)*t; + + // [9,22]: 1,11,25,29, + tf = CONSTANT(-0.043528171378199997)*f[1] + CONSTANT(0.133255230518000010)*f[11] + CONSTANT(-0.098140130732499997)*f[25] + CONSTANT(-0.101584686311000000)*f[29]; + tg = CONSTANT(-0.043528171378199997)*g[1] + CONSTANT(0.133255230518000010)*g[11] + CONSTANT(-0.098140130732499997)*g[25] + CONSTANT(-0.101584686311000000)*g[29]; + y[9] += tf*g[22] + tg*f[22]; + y[22] += tf*g[9] + tg*f[9]; + t = f[9] * g[22] + f[22] * g[9]; + y[1] += CONSTANT(-0.043528171378199997)*t; + y[11] += CONSTANT(0.133255230518000010)*t; + y[25] += CONSTANT(-0.098140130732499997)*t; + y[29] += CONSTANT(-0.101584686311000000)*t; + + // [9,27]: 6,20, + tf = CONSTANT(0.126792179874999990)*f[6] + CONSTANT(-0.196280261464999990)*f[20]; + tg = CONSTANT(0.126792179874999990)*g[6] + CONSTANT(-0.196280261464999990)*g[20]; + y[9] += tf*g[27] + tg*f[27]; + y[27] += tf*g[9] + tg*f[9]; + t = f[9] * g[27] + f[27] * g[9]; + y[6] += CONSTANT(0.126792179874999990)*t; + y[20] += CONSTANT(-0.196280261464999990)*t; + + // [10,10]: 0,20,24, + tf = CONSTANT(0.282094791771999980)*f[0] + CONSTANT(-0.179514867494000000)*f[20] + CONSTANT(-0.151717754049000010)*f[24]; + tg = CONSTANT(0.282094791771999980)*g[0] + CONSTANT(-0.179514867494000000)*g[20] + CONSTANT(-0.151717754049000010)*g[24]; + y[10] += tf*g[10] + tg*f[10]; + t = f[10] * g[10]; + y[0] += CONSTANT(0.282094791771999980)*t; + y[20] += CONSTANT(-0.179514867494000000)*t; + y[24] += CONSTANT(-0.151717754049000010)*t; + + // [10,16]: 14,32, + tf = CONSTANT(0.151717754044999990)*f[14] + CONSTANT(-0.077413979111300005)*f[32]; + tg = CONSTANT(0.151717754044999990)*g[14] + CONSTANT(-0.077413979111300005)*g[32]; + y[10] += tf*g[16] + tg*f[16]; + y[16] += tf*g[10] + tg*f[10]; + t = f[10] * g[16] + f[16] * g[10]; + y[14] += CONSTANT(0.151717754044999990)*t; + y[32] += CONSTANT(-0.077413979111300005)*t; + + // [10,17]: 13,3,31,35, + tf = CONSTANT(0.067850242288900006)*f[13] + CONSTANT(0.199471140200000010)*f[3] + CONSTANT(-0.113793659091000000)*f[31] + CONSTANT(-0.149911525925999990)*f[35]; + tg = CONSTANT(0.067850242288900006)*g[13] + CONSTANT(0.199471140200000010)*g[3] + CONSTANT(-0.113793659091000000)*g[31] + CONSTANT(-0.149911525925999990)*g[35]; + y[10] += tf*g[17] + tg*f[17]; + y[17] += tf*g[10] + tg*f[10]; + t = f[10] * g[17] + f[17] * g[10]; + y[13] += CONSTANT(0.067850242288900006)*t; + y[3] += CONSTANT(0.199471140200000010)*t; + y[31] += CONSTANT(-0.113793659091000000)*t; + y[35] += CONSTANT(-0.149911525925999990)*t; + + // [10,18]: 12,2,30,34, + tf = CONSTANT(-0.044418410173299998)*f[12] + CONSTANT(0.213243618621000000)*f[2] + CONSTANT(-0.171327458205000000)*f[30] + CONSTANT(-0.101358691177000000)*f[34]; + tg = CONSTANT(-0.044418410173299998)*g[12] + CONSTANT(0.213243618621000000)*g[2] + CONSTANT(-0.171327458205000000)*g[30] + CONSTANT(-0.101358691177000000)*g[34]; + y[10] += tf*g[18] + tg*f[18]; + y[18] += tf*g[10] + tg*f[10]; + t = f[10] * g[18] + f[18] * g[10]; + y[12] += CONSTANT(-0.044418410173299998)*t; + y[2] += CONSTANT(0.213243618621000000)*t; + y[30] += CONSTANT(-0.171327458205000000)*t; + y[34] += CONSTANT(-0.101358691177000000)*t; + + // [10,19]: 3,15,13,31,33, + tf = CONSTANT(-0.075393004386799994)*f[3] + CONSTANT(0.099322584599600000)*f[15] + CONSTANT(0.102579924281000000)*f[13] + CONSTANT(0.097749909976500002)*f[31] + CONSTANT(-0.025339672794100002)*f[33]; + tg = CONSTANT(-0.075393004386799994)*g[3] + CONSTANT(0.099322584599600000)*g[15] + CONSTANT(0.102579924281000000)*g[13] + CONSTANT(0.097749909976500002)*g[31] + CONSTANT(-0.025339672794100002)*g[33]; + y[10] += tf*g[19] + tg*f[19]; + y[19] += tf*g[10] + tg*f[10]; + t = f[10] * g[19] + f[19] * g[10]; + y[3] += CONSTANT(-0.075393004386799994)*t; + y[15] += CONSTANT(0.099322584599600000)*t; + y[13] += CONSTANT(0.102579924281000000)*t; + y[31] += CONSTANT(0.097749909976500002)*t; + y[33] += CONSTANT(-0.025339672794100002)*t; + + // [10,21]: 11,1,9,27,29, + tf = CONSTANT(0.102579924281000000)*f[11] + CONSTANT(-0.075393004386799994)*f[1] + CONSTANT(-0.099322584599600000)*f[9] + CONSTANT(0.025339672794100002)*f[27] + CONSTANT(0.097749909976500002)*f[29]; + tg = CONSTANT(0.102579924281000000)*g[11] + CONSTANT(-0.075393004386799994)*g[1] + CONSTANT(-0.099322584599600000)*g[9] + CONSTANT(0.025339672794100002)*g[27] + CONSTANT(0.097749909976500002)*g[29]; + y[10] += tf*g[21] + tg*f[21]; + y[21] += tf*g[10] + tg*f[10]; + t = f[10] * g[21] + f[21] * g[10]; + y[11] += CONSTANT(0.102579924281000000)*t; + y[1] += CONSTANT(-0.075393004386799994)*t; + y[9] += CONSTANT(-0.099322584599600000)*t; + y[27] += CONSTANT(0.025339672794100002)*t; + y[29] += CONSTANT(0.097749909976500002)*t; + + // [10,23]: 11,1,25,29, + tf = CONSTANT(-0.067850242288900006)*f[11] + CONSTANT(-0.199471140200000010)*f[1] + CONSTANT(0.149911525925999990)*f[25] + CONSTANT(0.113793659091000000)*f[29]; + tg = CONSTANT(-0.067850242288900006)*g[11] + CONSTANT(-0.199471140200000010)*g[1] + CONSTANT(0.149911525925999990)*g[25] + CONSTANT(0.113793659091000000)*g[29]; + y[10] += tf*g[23] + tg*f[23]; + y[23] += tf*g[10] + tg*f[10]; + t = f[10] * g[23] + f[23] * g[10]; + y[11] += CONSTANT(-0.067850242288900006)*t; + y[1] += CONSTANT(-0.199471140200000010)*t; + y[25] += CONSTANT(0.149911525925999990)*t; + y[29] += CONSTANT(0.113793659091000000)*t; + + // [10,28]: 6,20,24, + tf = CONSTANT(0.190188269814000000)*f[6] + CONSTANT(-0.065426753820500005)*f[20] + CONSTANT(0.077413979109600004)*f[24]; + tg = CONSTANT(0.190188269814000000)*g[6] + CONSTANT(-0.065426753820500005)*g[20] + CONSTANT(0.077413979109600004)*g[24]; + y[10] += tf*g[28] + tg*f[28]; + y[28] += tf*g[10] + tg*f[10]; + t = f[10] * g[28] + f[28] * g[10]; + y[6] += CONSTANT(0.190188269814000000)*t; + y[20] += CONSTANT(-0.065426753820500005)*t; + y[24] += CONSTANT(0.077413979109600004)*t; + + // [11,11]: 0,6,8,20,22, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.126156626101000010)*f[6] + CONSTANT(-0.145673124078999990)*f[8] + CONSTANT(0.025644981070299999)*f[20] + CONSTANT(-0.114687841910000000)*f[22]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.126156626101000010)*g[6] + CONSTANT(-0.145673124078999990)*g[8] + CONSTANT(0.025644981070299999)*g[20] + CONSTANT(-0.114687841910000000)*g[22]; + y[11] += tf*g[11] + tg*f[11]; + t = f[11] * g[11]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[6] += CONSTANT(0.126156626101000010)*t; + y[8] += CONSTANT(-0.145673124078999990)*t; + y[20] += CONSTANT(0.025644981070299999)*t; + y[22] += CONSTANT(-0.114687841910000000)*t; + + // [11,16]: 15,33,35, + tf = CONSTANT(-0.117520066953000000)*f[15] + CONSTANT(0.119929220739999990)*f[33] + CONSTANT(0.134084945035999990)*f[35]; + tg = CONSTANT(-0.117520066953000000)*g[15] + CONSTANT(0.119929220739999990)*g[33] + CONSTANT(0.134084945035999990)*g[35]; + y[11] += tf*g[16] + tg*f[16]; + y[16] += tf*g[11] + tg*f[11]; + t = f[11] * g[16] + f[16] * g[11]; + y[15] += CONSTANT(-0.117520066953000000)*t; + y[33] += CONSTANT(0.119929220739999990)*t; + y[35] += CONSTANT(0.134084945035999990)*t; + + // [11,18]: 3,13,15,31,33, + tf = CONSTANT(0.168583882834000000)*f[3] + CONSTANT(0.114687841909000000)*f[13] + CONSTANT(-0.133255230519000010)*f[15] + CONSTANT(0.075189952564900006)*f[31] + CONSTANT(-0.101990215611000000)*f[33]; + tg = CONSTANT(0.168583882834000000)*g[3] + CONSTANT(0.114687841909000000)*g[13] + CONSTANT(-0.133255230519000010)*g[15] + CONSTANT(0.075189952564900006)*g[31] + CONSTANT(-0.101990215611000000)*g[33]; + y[11] += tf*g[18] + tg*f[18]; + y[18] += tf*g[11] + tg*f[11]; + t = f[11] * g[18] + f[18] * g[11]; + y[3] += CONSTANT(0.168583882834000000)*t; + y[13] += CONSTANT(0.114687841909000000)*t; + y[15] += CONSTANT(-0.133255230519000010)*t; + y[31] += CONSTANT(0.075189952564900006)*t; + y[33] += CONSTANT(-0.101990215611000000)*t; + + // [11,19]: 2,14,12,30,32, + tf = CONSTANT(0.238413613504000000)*f[2] + CONSTANT(-0.102579924282000000)*f[14] + CONSTANT(0.099322584599300004)*f[12] + CONSTANT(0.009577496073830001)*f[30] + CONSTANT(-0.104682806112000000)*f[32]; + tg = CONSTANT(0.238413613504000000)*g[2] + CONSTANT(-0.102579924282000000)*g[14] + CONSTANT(0.099322584599300004)*g[12] + CONSTANT(0.009577496073830001)*g[30] + CONSTANT(-0.104682806112000000)*g[32]; + y[11] += tf*g[19] + tg*f[19]; + y[19] += tf*g[11] + tg*f[11]; + t = f[11] * g[19] + f[19] * g[11]; + y[2] += CONSTANT(0.238413613504000000)*t; + y[14] += CONSTANT(-0.102579924282000000)*t; + y[12] += CONSTANT(0.099322584599300004)*t; + y[30] += CONSTANT(0.009577496073830001)*t; + y[32] += CONSTANT(-0.104682806112000000)*t; + + // [11,24]: 9,25,27, + tf = CONSTANT(0.117520066950999990)*f[9] + CONSTANT(-0.134084945037000000)*f[25] + CONSTANT(-0.119929220742000010)*f[27]; + tg = CONSTANT(0.117520066950999990)*g[9] + CONSTANT(-0.134084945037000000)*g[25] + CONSTANT(-0.119929220742000010)*g[27]; + y[11] += tf*g[24] + tg*f[24]; + y[24] += tf*g[11] + tg*f[11]; + t = f[11] * g[24] + f[24] * g[11]; + y[9] += CONSTANT(0.117520066950999990)*t; + y[25] += CONSTANT(-0.134084945037000000)*t; + y[27] += CONSTANT(-0.119929220742000010)*t; + + // [11,29]: 6,20,22,8, + tf = CONSTANT(0.227318461243000010)*f[6] + CONSTANT(0.086019920779800002)*f[20] + CONSTANT(-0.075189952565200002)*f[22] + CONSTANT(0.065621187395299999)*f[8]; + tg = CONSTANT(0.227318461243000010)*g[6] + CONSTANT(0.086019920779800002)*g[20] + CONSTANT(-0.075189952565200002)*g[22] + CONSTANT(0.065621187395299999)*g[8]; + y[11] += tf*g[29] + tg*f[29]; + y[29] += tf*g[11] + tg*f[11]; + t = f[11] * g[29] + f[29] * g[11]; + y[6] += CONSTANT(0.227318461243000010)*t; + y[20] += CONSTANT(0.086019920779800002)*t; + y[22] += CONSTANT(-0.075189952565200002)*t; + y[8] += CONSTANT(0.065621187395299999)*t; + + // [12,12]: 0,6,20, + tf = CONSTANT(0.282094799871999980)*f[0] + CONSTANT(0.168208852954000010)*f[6] + CONSTANT(0.153869910786000010)*f[20]; + tg = CONSTANT(0.282094799871999980)*g[0] + CONSTANT(0.168208852954000010)*g[6] + CONSTANT(0.153869910786000010)*g[20]; + y[12] += tf*g[12] + tg*f[12]; + t = f[12] * g[12]; + y[0] += CONSTANT(0.282094799871999980)*t; + y[6] += CONSTANT(0.168208852954000010)*t; + y[20] += CONSTANT(0.153869910786000010)*t; + + // [12,30]: 20,6, + tf = CONSTANT(0.148373961712999990)*f[20] + CONSTANT(0.239614719999000000)*f[6]; + tg = CONSTANT(0.148373961712999990)*g[20] + CONSTANT(0.239614719999000000)*g[6]; + y[12] += tf*g[30] + tg*f[30]; + y[30] += tf*g[12] + tg*f[12]; + t = f[12] * g[30] + f[30] * g[12]; + y[20] += CONSTANT(0.148373961712999990)*t; + y[6] += CONSTANT(0.239614719999000000)*t; + + // [13,13]: 0,8,6,20,22, + tf = CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.145673124078999990)*f[8] + CONSTANT(0.126156626101000010)*f[6] + CONSTANT(0.025644981070299999)*f[20] + CONSTANT(0.114687841910000000)*f[22]; + tg = CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.145673124078999990)*g[8] + CONSTANT(0.126156626101000010)*g[6] + CONSTANT(0.025644981070299999)*g[20] + CONSTANT(0.114687841910000000)*g[22]; + y[13] += tf*g[13] + tg*f[13]; + t = f[13] * g[13]; + y[0] += CONSTANT(0.282094791773999990)*t; + y[8] += CONSTANT(0.145673124078999990)*t; + y[6] += CONSTANT(0.126156626101000010)*t; + y[20] += CONSTANT(0.025644981070299999)*t; + y[22] += CONSTANT(0.114687841910000000)*t; + + // [13,16]: 9,25,27, + tf = CONSTANT(-0.117520066953000000)*f[9] + CONSTANT(-0.134084945035999990)*f[25] + CONSTANT(0.119929220739999990)*f[27]; + tg = CONSTANT(-0.117520066953000000)*g[9] + CONSTANT(-0.134084945035999990)*g[25] + CONSTANT(0.119929220739999990)*g[27]; + y[13] += tf*g[16] + tg*f[16]; + y[16] += tf*g[13] + tg*f[13]; + t = f[13] * g[16] + f[16] * g[13]; + y[9] += CONSTANT(-0.117520066953000000)*t; + y[25] += CONSTANT(-0.134084945035999990)*t; + y[27] += CONSTANT(0.119929220739999990)*t; + + // [13,21]: 2,12,14,30,32, + tf = CONSTANT(0.238413613504000000)*f[2] + CONSTANT(0.099322584599300004)*f[12] + CONSTANT(0.102579924282000000)*f[14] + CONSTANT(0.009577496073830001)*f[30] + CONSTANT(0.104682806112000000)*f[32]; + tg = CONSTANT(0.238413613504000000)*g[2] + CONSTANT(0.099322584599300004)*g[12] + CONSTANT(0.102579924282000000)*g[14] + CONSTANT(0.009577496073830001)*g[30] + CONSTANT(0.104682806112000000)*g[32]; + y[13] += tf*g[21] + tg*f[21]; + y[21] += tf*g[13] + tg*f[13]; + t = f[13] * g[21] + f[21] * g[13]; + y[2] += CONSTANT(0.238413613504000000)*t; + y[12] += CONSTANT(0.099322584599300004)*t; + y[14] += CONSTANT(0.102579924282000000)*t; + y[30] += CONSTANT(0.009577496073830001)*t; + y[32] += CONSTANT(0.104682806112000000)*t; + + // [13,24]: 15,33,35, + tf = CONSTANT(-0.117520066950999990)*f[15] + CONSTANT(0.119929220742000010)*f[33] + CONSTANT(-0.134084945037000000)*f[35]; + tg = CONSTANT(-0.117520066950999990)*g[15] + CONSTANT(0.119929220742000010)*g[33] + CONSTANT(-0.134084945037000000)*g[35]; + y[13] += tf*g[24] + tg*f[24]; + y[24] += tf*g[13] + tg*f[13]; + t = f[13] * g[24] + f[24] * g[13]; + y[15] += CONSTANT(-0.117520066950999990)*t; + y[33] += CONSTANT(0.119929220742000010)*t; + y[35] += CONSTANT(-0.134084945037000000)*t; + + // [13,31]: 6,22,20,8, + tf = CONSTANT(0.227318461243000010)*f[6] + CONSTANT(0.075189952565200002)*f[22] + CONSTANT(0.086019920779800002)*f[20] + CONSTANT(-0.065621187395299999)*f[8]; + tg = CONSTANT(0.227318461243000010)*g[6] + CONSTANT(0.075189952565200002)*g[22] + CONSTANT(0.086019920779800002)*g[20] + CONSTANT(-0.065621187395299999)*g[8]; + y[13] += tf*g[31] + tg*f[31]; + y[31] += tf*g[13] + tg*f[13]; + t = f[13] * g[31] + f[31] * g[13]; + y[6] += CONSTANT(0.227318461243000010)*t; + y[22] += CONSTANT(0.075189952565200002)*t; + y[20] += CONSTANT(0.086019920779800002)*t; + y[8] += CONSTANT(-0.065621187395299999)*t; + + // [14,14]: 0,20,24, + tf = CONSTANT(0.282094791771999980)*f[0] + CONSTANT(-0.179514867494000000)*f[20] + CONSTANT(0.151717754049000010)*f[24]; + tg = CONSTANT(0.282094791771999980)*g[0] + CONSTANT(-0.179514867494000000)*g[20] + CONSTANT(0.151717754049000010)*g[24]; + y[14] += tf*g[14] + tg*f[14]; + t = f[14] * g[14]; + y[0] += CONSTANT(0.282094791771999980)*t; + y[20] += CONSTANT(-0.179514867494000000)*t; + y[24] += CONSTANT(0.151717754049000010)*t; + + // [14,17]: 11,1,25,29, + tf = CONSTANT(0.067850242288500007)*f[11] + CONSTANT(0.199471140196999990)*f[1] + CONSTANT(0.149911525925999990)*f[25] + CONSTANT(-0.113793659092000000)*f[29]; + tg = CONSTANT(0.067850242288500007)*g[11] + CONSTANT(0.199471140196999990)*g[1] + CONSTANT(0.149911525925999990)*g[25] + CONSTANT(-0.113793659092000000)*g[29]; + y[14] += tf*g[17] + tg*f[17]; + y[17] += tf*g[14] + tg*f[14]; + t = f[14] * g[17] + f[17] * g[14]; + y[11] += CONSTANT(0.067850242288500007)*t; + y[1] += CONSTANT(0.199471140196999990)*t; + y[25] += CONSTANT(0.149911525925999990)*t; + y[29] += CONSTANT(-0.113793659092000000)*t; + + // [14,22]: 12,2,30,34, + tf = CONSTANT(-0.044418410173299998)*f[12] + CONSTANT(0.213243618621000000)*f[2] + CONSTANT(-0.171327458205000000)*f[30] + CONSTANT(0.101358691177000000)*f[34]; + tg = CONSTANT(-0.044418410173299998)*g[12] + CONSTANT(0.213243618621000000)*g[2] + CONSTANT(-0.171327458205000000)*g[30] + CONSTANT(0.101358691177000000)*g[34]; + y[14] += tf*g[22] + tg*f[22]; + y[22] += tf*g[14] + tg*f[14]; + t = f[14] * g[22] + f[22] * g[14]; + y[12] += CONSTANT(-0.044418410173299998)*t; + y[2] += CONSTANT(0.213243618621000000)*t; + y[30] += CONSTANT(-0.171327458205000000)*t; + y[34] += CONSTANT(0.101358691177000000)*t; + + // [14,23]: 13,3,31,35, + tf = CONSTANT(0.067850242288500007)*f[13] + CONSTANT(0.199471140196999990)*f[3] + CONSTANT(-0.113793659092000000)*f[31] + CONSTANT(0.149911525925999990)*f[35]; + tg = CONSTANT(0.067850242288500007)*g[13] + CONSTANT(0.199471140196999990)*g[3] + CONSTANT(-0.113793659092000000)*g[31] + CONSTANT(0.149911525925999990)*g[35]; + y[14] += tf*g[23] + tg*f[23]; + y[23] += tf*g[14] + tg*f[14]; + t = f[14] * g[23] + f[23] * g[14]; + y[13] += CONSTANT(0.067850242288500007)*t; + y[3] += CONSTANT(0.199471140196999990)*t; + y[31] += CONSTANT(-0.113793659092000000)*t; + y[35] += CONSTANT(0.149911525925999990)*t; + + // [14,32]: 20,6,24, + tf = CONSTANT(-0.065426753820500005)*f[20] + CONSTANT(0.190188269814000000)*f[6] + CONSTANT(-0.077413979109600004)*f[24]; + tg = CONSTANT(-0.065426753820500005)*g[20] + CONSTANT(0.190188269814000000)*g[6] + CONSTANT(-0.077413979109600004)*g[24]; + y[14] += tf*g[32] + tg*f[32]; + y[32] += tf*g[14] + tg*f[14]; + t = f[14] * g[32] + f[32] * g[14]; + y[20] += CONSTANT(-0.065426753820500005)*t; + y[6] += CONSTANT(0.190188269814000000)*t; + y[24] += CONSTANT(-0.077413979109600004)*t; + + // [15,15]: 0,6,20, + tf = CONSTANT(0.282094791766999970)*f[0] + CONSTANT(-0.210261043508000010)*f[6] + CONSTANT(0.076934943209800002)*f[20]; + tg = CONSTANT(0.282094791766999970)*g[0] + CONSTANT(-0.210261043508000010)*g[6] + CONSTANT(0.076934943209800002)*g[20]; + y[15] += tf*g[15] + tg*f[15]; + t = f[15] * g[15]; + y[0] += CONSTANT(0.282094791766999970)*t; + y[6] += CONSTANT(-0.210261043508000010)*t; + y[20] += CONSTANT(0.076934943209800002)*t; + + // [15,21]: 14,32,34, + tf = CONSTANT(-0.099322584600699995)*f[14] + CONSTANT(0.126698363970000010)*f[32] + CONSTANT(-0.131668802180999990)*f[34]; + tg = CONSTANT(-0.099322584600699995)*g[14] + CONSTANT(0.126698363970000010)*g[32] + CONSTANT(-0.131668802180999990)*g[34]; + y[15] += tf*g[21] + tg*f[21]; + y[21] += tf*g[15] + tg*f[15]; + t = f[15] * g[21] + f[21] * g[15]; + y[14] += CONSTANT(-0.099322584600699995)*t; + y[32] += CONSTANT(0.126698363970000010)*t; + y[34] += CONSTANT(-0.131668802180999990)*t; + + // [15,22]: 13,3,31,35, + tf = CONSTANT(0.133255230518000010)*f[13] + CONSTANT(-0.043528171378199997)*f[3] + CONSTANT(-0.101584686311000000)*f[31] + CONSTANT(-0.098140130732499997)*f[35]; + tg = CONSTANT(0.133255230518000010)*g[13] + CONSTANT(-0.043528171378199997)*g[3] + CONSTANT(-0.101584686311000000)*g[31] + CONSTANT(-0.098140130732499997)*g[35]; + y[15] += tf*g[22] + tg*f[22]; + y[22] += tf*g[15] + tg*f[15]; + t = f[15] * g[22] + f[22] * g[15]; + y[13] += CONSTANT(0.133255230518000010)*t; + y[3] += CONSTANT(-0.043528171378199997)*t; + y[31] += CONSTANT(-0.101584686311000000)*t; + y[35] += CONSTANT(-0.098140130732499997)*t; + + // [15,23]: 12,2,30, + tf = CONSTANT(-0.203550726872999990)*f[12] + CONSTANT(0.162867503964999990)*f[2] + CONSTANT(0.098140130728100003)*f[30]; + tg = CONSTANT(-0.203550726872999990)*g[12] + CONSTANT(0.162867503964999990)*g[2] + CONSTANT(0.098140130728100003)*g[30]; + y[15] += tf*g[23] + tg*f[23]; + y[23] += tf*g[15] + tg*f[15]; + t = f[15] * g[23] + f[23] * g[15]; + y[12] += CONSTANT(-0.203550726872999990)*t; + y[2] += CONSTANT(0.162867503964999990)*t; + y[30] += CONSTANT(0.098140130728100003)*t; + + // [15,33]: 6,20, + tf = CONSTANT(0.126792179874999990)*f[6] + CONSTANT(-0.196280261464999990)*f[20]; + tg = CONSTANT(0.126792179874999990)*g[6] + CONSTANT(-0.196280261464999990)*g[20]; + y[15] += tf*g[33] + tg*f[33]; + y[33] += tf*g[15] + tg*f[15]; + t = f[15] * g[33] + f[33] * g[15]; + y[6] += CONSTANT(0.126792179874999990)*t; + y[20] += CONSTANT(-0.196280261464999990)*t; + + // [16,16]: 0,6,20, + tf = CONSTANT(0.282094791763999990)*f[0] + CONSTANT(-0.229375683829000000)*f[6] + CONSTANT(0.106525305981000000)*f[20]; + tg = CONSTANT(0.282094791763999990)*g[0] + CONSTANT(-0.229375683829000000)*g[6] + CONSTANT(0.106525305981000000)*g[20]; + y[16] += tf*g[16] + tg*f[16]; + t = f[16] * g[16]; + y[0] += CONSTANT(0.282094791763999990)*t; + y[6] += CONSTANT(-0.229375683829000000)*t; + y[20] += CONSTANT(0.106525305981000000)*t; + + // [16,18]: 8,22, + tf = CONSTANT(-0.075080816693699995)*f[8] + CONSTANT(0.135045473380000000)*f[22]; + tg = CONSTANT(-0.075080816693699995)*g[8] + CONSTANT(0.135045473380000000)*g[22]; + y[16] += tf*g[18] + tg*f[18]; + y[18] += tf*g[16] + tg*f[16]; + t = f[16] * g[18] + f[18] * g[16]; + y[8] += CONSTANT(-0.075080816693699995)*t; + y[22] += CONSTANT(0.135045473380000000)*t; + + // [16,23]: 19,5, + tf = CONSTANT(-0.119098912754999990)*f[19] + CONSTANT(0.140463346187999990)*f[5]; + tg = CONSTANT(-0.119098912754999990)*g[19] + CONSTANT(0.140463346187999990)*g[5]; + y[16] += tf*g[23] + tg*f[23]; + y[23] += tf*g[16] + tg*f[16]; + t = f[16] * g[23] + f[23] * g[16]; + y[19] += CONSTANT(-0.119098912754999990)*t; + y[5] += CONSTANT(0.140463346187999990)*t; + + // [16,26]: 12,2,30, + tf = CONSTANT(-0.207723503645000000)*f[12] + CONSTANT(0.147319200325000010)*f[2] + CONSTANT(0.130197596199999990)*f[30]; + tg = CONSTANT(-0.207723503645000000)*g[12] + CONSTANT(0.147319200325000010)*g[2] + CONSTANT(0.130197596199999990)*g[30]; + y[16] += tf*g[26] + tg*f[26]; + y[26] += tf*g[16] + tg*f[16]; + t = f[16] * g[26] + f[26] * g[16]; + y[12] += CONSTANT(-0.207723503645000000)*t; + y[2] += CONSTANT(0.147319200325000010)*t; + y[30] += CONSTANT(0.130197596199999990)*t; + + // [16,28]: 14,32, + tf = CONSTANT(-0.077413979111300005)*f[14] + CONSTANT(0.128376561115000010)*f[32]; + tg = CONSTANT(-0.077413979111300005)*g[14] + CONSTANT(0.128376561115000010)*g[32]; + y[16] += tf*g[28] + tg*f[28]; + y[28] += tf*g[16] + tg*f[16]; + t = f[16] * g[28] + f[28] * g[16]; + y[14] += CONSTANT(-0.077413979111300005)*t; + y[32] += CONSTANT(0.128376561115000010)*t; + + // [16,29]: 15,33,35, + tf = CONSTANT(0.035835708931099997)*f[15] + CONSTANT(-0.118853600623999990)*f[33] + CONSTANT(-0.053152946071899999)*f[35]; + tg = CONSTANT(0.035835708931099997)*g[15] + CONSTANT(-0.118853600623999990)*g[33] + CONSTANT(-0.053152946071899999)*g[35]; + y[16] += tf*g[29] + tg*f[29]; + y[29] += tf*g[16] + tg*f[16]; + t = f[16] * g[29] + f[29] * g[16]; + y[15] += CONSTANT(0.035835708931099997)*t; + y[33] += CONSTANT(-0.118853600623999990)*t; + y[35] += CONSTANT(-0.053152946071899999)*t; + + // [16,31]: 27,9,25, + tf = CONSTANT(-0.118853600623999990)*f[27] + CONSTANT(0.035835708931099997)*f[9] + CONSTANT(0.053152946071899999)*f[25]; + tg = CONSTANT(-0.118853600623999990)*g[27] + CONSTANT(0.035835708931099997)*g[9] + CONSTANT(0.053152946071899999)*g[25]; + y[16] += tf*g[31] + tg*f[31]; + y[31] += tf*g[16] + tg*f[16]; + t = f[16] * g[31] + f[31] * g[16]; + y[27] += CONSTANT(-0.118853600623999990)*t; + y[9] += CONSTANT(0.035835708931099997)*t; + y[25] += CONSTANT(0.053152946071899999)*t; + + // [17,17]: 0,6,20, + tf = CONSTANT(0.282094791768999990)*f[0] + CONSTANT(-0.057343920955899998)*f[6] + CONSTANT(-0.159787958979000000)*f[20]; + tg = CONSTANT(0.282094791768999990)*g[0] + CONSTANT(-0.057343920955899998)*g[6] + CONSTANT(-0.159787958979000000)*g[20]; + y[17] += tf*g[17] + tg*f[17]; + t = f[17] * g[17]; + y[0] += CONSTANT(0.282094791768999990)*t; + y[6] += CONSTANT(-0.057343920955899998)*t; + y[20] += CONSTANT(-0.159787958979000000)*t; + + // [17,19]: 8,22,24, + tf = CONSTANT(-0.112621225039000000)*f[8] + CONSTANT(0.045015157794100001)*f[22] + CONSTANT(0.119098912753000000)*f[24]; + tg = CONSTANT(-0.112621225039000000)*g[8] + CONSTANT(0.045015157794100001)*g[22] + CONSTANT(0.119098912753000000)*g[24]; + y[17] += tf*g[19] + tg*f[19]; + y[19] += tf*g[17] + tg*f[17]; + t = f[17] * g[19] + f[19] * g[17]; + y[8] += CONSTANT(-0.112621225039000000)*t; + y[22] += CONSTANT(0.045015157794100001)*t; + y[24] += CONSTANT(0.119098912753000000)*t; + + // [17,21]: 16,4,18, + tf = CONSTANT(-0.119098912754999990)*f[16] + CONSTANT(-0.112621225039000000)*f[4] + CONSTANT(0.045015157794399997)*f[18]; + tg = CONSTANT(-0.119098912754999990)*g[16] + CONSTANT(-0.112621225039000000)*g[4] + CONSTANT(0.045015157794399997)*g[18]; + y[17] += tf*g[21] + tg*f[21]; + y[21] += tf*g[17] + tg*f[17]; + t = f[17] * g[21] + f[21] * g[17]; + y[16] += CONSTANT(-0.119098912754999990)*t; + y[4] += CONSTANT(-0.112621225039000000)*t; + y[18] += CONSTANT(0.045015157794399997)*t; + + // [17,26]: 3,13,31, + tf = CONSTANT(0.208340811096000000)*f[3] + CONSTANT(0.029982305185199998)*f[13] + CONSTANT(-0.118853600623999990)*f[31]; + tg = CONSTANT(0.208340811096000000)*g[3] + CONSTANT(0.029982305185199998)*g[13] + CONSTANT(-0.118853600623999990)*g[31]; + y[17] += tf*g[26] + tg*f[26]; + y[26] += tf*g[17] + tg*f[17]; + t = f[17] * g[26] + f[26] * g[17]; + y[3] += CONSTANT(0.208340811096000000)*t; + y[13] += CONSTANT(0.029982305185199998)*t; + y[31] += CONSTANT(-0.118853600623999990)*t; + + // [17,27]: 12,2,30, + tf = CONSTANT(-0.103861751821000010)*f[12] + CONSTANT(0.196425600433000000)*f[2] + CONSTANT(-0.130197596204999990)*f[30]; + tg = CONSTANT(-0.103861751821000010)*g[12] + CONSTANT(0.196425600433000000)*g[2] + CONSTANT(-0.130197596204999990)*g[30]; + y[17] += tf*g[27] + tg*f[27]; + y[27] += tf*g[17] + tg*f[17]; + t = f[17] * g[27] + f[27] * g[17]; + y[12] += CONSTANT(-0.103861751821000010)*t; + y[2] += CONSTANT(0.196425600433000000)*t; + y[30] += CONSTANT(-0.130197596204999990)*t; + + // [17,28]: 13,3,31,35, + tf = CONSTANT(0.121172043789000000)*f[13] + CONSTANT(-0.060142811686500000)*f[3] + CONSTANT(0.034310079156700000)*f[31] + CONSTANT(0.099440056652200001)*f[35]; + tg = CONSTANT(0.121172043789000000)*g[13] + CONSTANT(-0.060142811686500000)*g[3] + CONSTANT(0.034310079156700000)*g[31] + CONSTANT(0.099440056652200001)*g[35]; + y[17] += tf*g[28] + tg*f[28]; + y[28] += tf*g[17] + tg*f[17]; + t = f[17] * g[28] + f[28] * g[17]; + y[13] += CONSTANT(0.121172043789000000)*t; + y[3] += CONSTANT(-0.060142811686500000)*t; + y[31] += CONSTANT(0.034310079156700000)*t; + y[35] += CONSTANT(0.099440056652200001)*t; + + // [17,32]: 11,1,25,29, + tf = CONSTANT(0.121172043788000010)*f[11] + CONSTANT(-0.060142811686900000)*f[1] + CONSTANT(-0.099440056652700004)*f[25] + CONSTANT(0.034310079156599997)*f[29]; + tg = CONSTANT(0.121172043788000010)*g[11] + CONSTANT(-0.060142811686900000)*g[1] + CONSTANT(-0.099440056652700004)*g[25] + CONSTANT(0.034310079156599997)*g[29]; + y[17] += tf*g[32] + tg*f[32]; + y[32] += tf*g[17] + tg*f[17]; + t = f[17] * g[32] + f[32] * g[17]; + y[11] += CONSTANT(0.121172043788000010)*t; + y[1] += CONSTANT(-0.060142811686900000)*t; + y[25] += CONSTANT(-0.099440056652700004)*t; + y[29] += CONSTANT(0.034310079156599997)*t; + + // [17,34]: 29,11,1, + tf = CONSTANT(0.118853600623000000)*f[29] + CONSTANT(-0.029982305185400002)*f[11] + CONSTANT(-0.208340811100000000)*f[1]; + tg = CONSTANT(0.118853600623000000)*g[29] + CONSTANT(-0.029982305185400002)*g[11] + CONSTANT(-0.208340811100000000)*g[1]; + y[17] += tf*g[34] + tg*f[34]; + y[34] += tf*g[17] + tg*f[17]; + t = f[17] * g[34] + f[34] * g[17]; + y[29] += CONSTANT(0.118853600623000000)*t; + y[11] += CONSTANT(-0.029982305185400002)*t; + y[1] += CONSTANT(-0.208340811100000000)*t; + + // [18,18]: 6,0,20,24, + tf = CONSTANT(0.065535909662600006)*f[6] + CONSTANT(0.282094791771999980)*f[0] + CONSTANT(-0.083698454702400005)*f[20] + CONSTANT(-0.135045473384000000)*f[24]; + tg = CONSTANT(0.065535909662600006)*g[6] + CONSTANT(0.282094791771999980)*g[0] + CONSTANT(-0.083698454702400005)*g[20] + CONSTANT(-0.135045473384000000)*g[24]; + y[18] += tf*g[18] + tg*f[18]; + t = f[18] * g[18]; + y[6] += CONSTANT(0.065535909662600006)*t; + y[0] += CONSTANT(0.282094791771999980)*t; + y[20] += CONSTANT(-0.083698454702400005)*t; + y[24] += CONSTANT(-0.135045473384000000)*t; + + // [18,19]: 7,21,23, + tf = CONSTANT(0.090297865407399994)*f[7] + CONSTANT(0.102084782359000000)*f[21] + CONSTANT(-0.045015157794399997)*f[23]; + tg = CONSTANT(0.090297865407399994)*g[7] + CONSTANT(0.102084782359000000)*g[21] + CONSTANT(-0.045015157794399997)*g[23]; + y[18] += tf*g[19] + tg*f[19]; + y[19] += tf*g[18] + tg*f[18]; + t = f[18] * g[19] + f[19] * g[18]; + y[7] += CONSTANT(0.090297865407399994)*t; + y[21] += CONSTANT(0.102084782359000000)*t; + y[23] += CONSTANT(-0.045015157794399997)*t; + + // [18,25]: 15,33, + tf = CONSTANT(-0.098140130731999994)*f[15] + CONSTANT(0.130197596202000000)*f[33]; + tg = CONSTANT(-0.098140130731999994)*g[15] + CONSTANT(0.130197596202000000)*g[33]; + y[18] += tf*g[25] + tg*f[25]; + y[25] += tf*g[18] + tg*f[18]; + t = f[18] * g[25] + f[25] * g[18]; + y[15] += CONSTANT(-0.098140130731999994)*t; + y[33] += CONSTANT(0.130197596202000000)*t; + + // [18,26]: 14,32, + tf = CONSTANT(0.101358691174000000)*f[14] + CONSTANT(0.084042186965900004)*f[32]; + tg = CONSTANT(0.101358691174000000)*g[14] + CONSTANT(0.084042186965900004)*g[32]; + y[18] += tf*g[26] + tg*f[26]; + y[26] += tf*g[18] + tg*f[18]; + t = f[18] * g[26] + f[26] * g[18]; + y[14] += CONSTANT(0.101358691174000000)*t; + y[32] += CONSTANT(0.084042186965900004)*t; + + // [18,27]: 13,3,35, + tf = CONSTANT(0.101990215611000000)*f[13] + CONSTANT(0.183739324705999990)*f[3] + CONSTANT(-0.130197596202000000)*f[35]; + tg = CONSTANT(0.101990215611000000)*g[13] + CONSTANT(0.183739324705999990)*g[3] + CONSTANT(-0.130197596202000000)*g[35]; + y[18] += tf*g[27] + tg*f[27]; + y[27] += tf*g[18] + tg*f[18]; + t = f[18] * g[27] + f[27] * g[18]; + y[13] += CONSTANT(0.101990215611000000)*t; + y[3] += CONSTANT(0.183739324705999990)*t; + y[35] += CONSTANT(-0.130197596202000000)*t; + + // [18,28]: 2,12,30,34, + tf = CONSTANT(0.225033795606000010)*f[2] + CONSTANT(0.022664492358099999)*f[12] + CONSTANT(-0.099440056651100006)*f[30] + CONSTANT(-0.084042186968800003)*f[34]; + tg = CONSTANT(0.225033795606000010)*g[2] + CONSTANT(0.022664492358099999)*g[12] + CONSTANT(-0.099440056651100006)*g[30] + CONSTANT(-0.084042186968800003)*g[34]; + y[18] += tf*g[28] + tg*f[28]; + y[28] += tf*g[18] + tg*f[18]; + t = f[18] * g[28] + f[28] * g[18]; + y[2] += CONSTANT(0.225033795606000010)*t; + y[12] += CONSTANT(0.022664492358099999)*t; + y[30] += CONSTANT(-0.099440056651100006)*t; + y[34] += CONSTANT(-0.084042186968800003)*t; + + // [18,29]: 3,13,15,31, + tf = CONSTANT(-0.085054779966799998)*f[3] + CONSTANT(0.075189952564900006)*f[13] + CONSTANT(0.101584686310000010)*f[15] + CONSTANT(0.097043558538999999)*f[31]; + tg = CONSTANT(-0.085054779966799998)*g[3] + CONSTANT(0.075189952564900006)*g[13] + CONSTANT(0.101584686310000010)*g[15] + CONSTANT(0.097043558538999999)*g[31]; + y[18] += tf*g[29] + tg*f[29]; + y[29] += tf*g[18] + tg*f[18]; + t = f[18] * g[29] + f[29] * g[18]; + y[3] += CONSTANT(-0.085054779966799998)*t; + y[13] += CONSTANT(0.075189952564900006)*t; + y[15] += CONSTANT(0.101584686310000010)*t; + y[31] += CONSTANT(0.097043558538999999)*t; + + // [19,19]: 6,8,0,20,22, + tf = CONSTANT(0.139263808033999990)*f[6] + CONSTANT(-0.141889406570999990)*f[8] + CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.068480553847200004)*f[20] + CONSTANT(-0.102084782360000000)*f[22]; + tg = CONSTANT(0.139263808033999990)*g[6] + CONSTANT(-0.141889406570999990)*g[8] + CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.068480553847200004)*g[20] + CONSTANT(-0.102084782360000000)*g[22]; + y[19] += tf*g[19] + tg*f[19]; + t = f[19] * g[19]; + y[6] += CONSTANT(0.139263808033999990)*t; + y[8] += CONSTANT(-0.141889406570999990)*t; + y[0] += CONSTANT(0.282094791773999990)*t; + y[20] += CONSTANT(0.068480553847200004)*t; + y[22] += CONSTANT(-0.102084782360000000)*t; + + // [19,25]: 34, + tf = CONSTANT(-0.130197596205999990)*f[34]; + tg = CONSTANT(-0.130197596205999990)*g[34]; + y[19] += tf*g[25] + tg*f[25]; + y[25] += tf*g[19] + tg*f[19]; + t = f[19] * g[25] + f[25] * g[19]; + y[34] += CONSTANT(-0.130197596205999990)*t; + + // [19,26]: 15,35, + tf = CONSTANT(-0.131668802182000000)*f[15] + CONSTANT(0.130197596204999990)*f[35]; + tg = CONSTANT(-0.131668802182000000)*g[15] + CONSTANT(0.130197596204999990)*g[35]; + y[19] += tf*g[26] + tg*f[26]; + y[26] += tf*g[19] + tg*f[19]; + t = f[19] * g[26] + f[26] * g[19]; + y[15] += CONSTANT(-0.131668802182000000)*t; + y[35] += CONSTANT(0.130197596204999990)*t; + + // [19,27]: 14,32, + tf = CONSTANT(0.025339672793899998)*f[14] + CONSTANT(0.084042186967699994)*f[32]; + tg = CONSTANT(0.025339672793899998)*g[14] + CONSTANT(0.084042186967699994)*g[32]; + y[19] += tf*g[27] + tg*f[27]; + y[27] += tf*g[19] + tg*f[19]; + t = f[19] * g[27] + f[27] * g[19]; + y[14] += CONSTANT(0.025339672793899998)*t; + y[32] += CONSTANT(0.084042186967699994)*t; + + // [19,28]: 13,3,15,31,33, + tf = CONSTANT(0.104682806111000000)*f[13] + CONSTANT(0.159122922869999990)*f[3] + CONSTANT(-0.126698363970000010)*f[15] + CONSTANT(0.090775936911399999)*f[31] + CONSTANT(-0.084042186968400004)*f[33]; + tg = CONSTANT(0.104682806111000000)*g[13] + CONSTANT(0.159122922869999990)*g[3] + CONSTANT(-0.126698363970000010)*g[15] + CONSTANT(0.090775936911399999)*g[31] + CONSTANT(-0.084042186968400004)*g[33]; + y[19] += tf*g[28] + tg*f[28]; + y[28] += tf*g[19] + tg*f[19]; + t = f[19] * g[28] + f[28] * g[19]; + y[13] += CONSTANT(0.104682806111000000)*t; + y[3] += CONSTANT(0.159122922869999990)*t; + y[15] += CONSTANT(-0.126698363970000010)*t; + y[31] += CONSTANT(0.090775936911399999)*t; + y[33] += CONSTANT(-0.084042186968400004)*t; + + // [19,29]: 12,14,2,30,32, + tf = CONSTANT(0.115089467124000010)*f[12] + CONSTANT(-0.097749909977199997)*f[14] + CONSTANT(0.240571246744999990)*f[2] + CONSTANT(0.053152946072499999)*f[30] + CONSTANT(-0.090775936912099994)*f[32]; + tg = CONSTANT(0.115089467124000010)*g[12] + CONSTANT(-0.097749909977199997)*g[14] + CONSTANT(0.240571246744999990)*g[2] + CONSTANT(0.053152946072499999)*g[30] + CONSTANT(-0.090775936912099994)*g[32]; + y[19] += tf*g[29] + tg*f[29]; + y[29] += tf*g[19] + tg*f[19]; + t = f[19] * g[29] + f[29] * g[19]; + y[12] += CONSTANT(0.115089467124000010)*t; + y[14] += CONSTANT(-0.097749909977199997)*t; + y[2] += CONSTANT(0.240571246744999990)*t; + y[30] += CONSTANT(0.053152946072499999)*t; + y[32] += CONSTANT(-0.090775936912099994)*t; + + // [20,20]: 6,0,20, + tf = CONSTANT(0.163839797503000010)*f[6] + CONSTANT(0.282094802232000010)*f[0]; + tg = CONSTANT(0.163839797503000010)*g[6] + CONSTANT(0.282094802232000010)*g[0]; + y[20] += tf*g[20] + tg*f[20]; + t = f[20] * g[20]; + y[6] += CONSTANT(0.163839797503000010)*t; + y[0] += CONSTANT(0.282094802232000010)*t; + y[20] += CONSTANT(0.136961139005999990)*t; + + // [21,21]: 6,20,0,8,22, + tf = CONSTANT(0.139263808033999990)*f[6] + CONSTANT(0.068480553847200004)*f[20] + CONSTANT(0.282094791773999990)*f[0] + CONSTANT(0.141889406570999990)*f[8] + CONSTANT(0.102084782360000000)*f[22]; + tg = CONSTANT(0.139263808033999990)*g[6] + CONSTANT(0.068480553847200004)*g[20] + CONSTANT(0.282094791773999990)*g[0] + CONSTANT(0.141889406570999990)*g[8] + CONSTANT(0.102084782360000000)*g[22]; + y[21] += tf*g[21] + tg*f[21]; + t = f[21] * g[21]; + y[6] += CONSTANT(0.139263808033999990)*t; + y[20] += CONSTANT(0.068480553847200004)*t; + y[0] += CONSTANT(0.282094791773999990)*t; + y[8] += CONSTANT(0.141889406570999990)*t; + y[22] += CONSTANT(0.102084782360000000)*t; + + // [21,23]: 8,22,24, + tf = CONSTANT(-0.112621225039000000)*f[8] + CONSTANT(0.045015157794100001)*f[22] + CONSTANT(-0.119098912753000000)*f[24]; + tg = CONSTANT(-0.112621225039000000)*g[8] + CONSTANT(0.045015157794100001)*g[22] + CONSTANT(-0.119098912753000000)*g[24]; + y[21] += tf*g[23] + tg*f[23]; + y[23] += tf*g[21] + tg*f[21]; + t = f[21] * g[23] + f[23] * g[21]; + y[8] += CONSTANT(-0.112621225039000000)*t; + y[22] += CONSTANT(0.045015157794100001)*t; + y[24] += CONSTANT(-0.119098912753000000)*t; + + // [21,26]: 9,25, + tf = CONSTANT(-0.131668802182000000)*f[9] + CONSTANT(-0.130197596204999990)*f[25]; + tg = CONSTANT(-0.131668802182000000)*g[9] + CONSTANT(-0.130197596204999990)*g[25]; + y[21] += tf*g[26] + tg*f[26]; + y[26] += tf*g[21] + tg*f[21]; + t = f[21] * g[26] + f[26] * g[21]; + y[9] += CONSTANT(-0.131668802182000000)*t; + y[25] += CONSTANT(-0.130197596204999990)*t; + + // [21,28]: 27,1,11,9,29, + tf = CONSTANT(0.084042186968400004)*f[27] + CONSTANT(0.159122922869999990)*f[1] + CONSTANT(0.104682806111000000)*f[11] + CONSTANT(0.126698363970000010)*f[9] + CONSTANT(0.090775936911399999)*f[29]; + tg = CONSTANT(0.084042186968400004)*g[27] + CONSTANT(0.159122922869999990)*g[1] + CONSTANT(0.104682806111000000)*g[11] + CONSTANT(0.126698363970000010)*g[9] + CONSTANT(0.090775936911399999)*g[29]; + y[21] += tf*g[28] + tg*f[28]; + y[28] += tf*g[21] + tg*f[21]; + t = f[21] * g[28] + f[28] * g[21]; + y[27] += CONSTANT(0.084042186968400004)*t; + y[1] += CONSTANT(0.159122922869999990)*t; + y[11] += CONSTANT(0.104682806111000000)*t; + y[9] += CONSTANT(0.126698363970000010)*t; + y[29] += CONSTANT(0.090775936911399999)*t; + + // [21,31]: 14,2,30,12,32, + tf = CONSTANT(0.097749909977199997)*f[14] + CONSTANT(0.240571246744999990)*f[2] + CONSTANT(0.053152946072499999)*f[30] + CONSTANT(0.115089467124000010)*f[12] + CONSTANT(0.090775936912099994)*f[32]; + tg = CONSTANT(0.097749909977199997)*g[14] + CONSTANT(0.240571246744999990)*g[2] + CONSTANT(0.053152946072499999)*g[30] + CONSTANT(0.115089467124000010)*g[12] + CONSTANT(0.090775936912099994)*g[32]; + y[21] += tf*g[31] + tg*f[31]; + y[31] += tf*g[21] + tg*f[21]; + t = f[21] * g[31] + f[31] * g[21]; + y[14] += CONSTANT(0.097749909977199997)*t; + y[2] += CONSTANT(0.240571246744999990)*t; + y[30] += CONSTANT(0.053152946072499999)*t; + y[12] += CONSTANT(0.115089467124000010)*t; + y[32] += CONSTANT(0.090775936912099994)*t; + + // [21,33]: 32,14, + tf = CONSTANT(0.084042186967699994)*f[32] + CONSTANT(0.025339672793899998)*f[14]; + tg = CONSTANT(0.084042186967699994)*g[32] + CONSTANT(0.025339672793899998)*g[14]; + y[21] += tf*g[33] + tg*f[33]; + y[33] += tf*g[21] + tg*f[21]; + t = f[21] * g[33] + f[33] * g[21]; + y[32] += CONSTANT(0.084042186967699994)*t; + y[14] += CONSTANT(0.025339672793899998)*t; + + // [21,34]: 35, + tf = CONSTANT(-0.130197596205999990)*f[35]; + tg = CONSTANT(-0.130197596205999990)*g[35]; + y[21] += tf*g[34] + tg*f[34]; + y[34] += tf*g[21] + tg*f[21]; + t = f[21] * g[34] + f[34] * g[21]; + y[35] += CONSTANT(-0.130197596205999990)*t; + + // [22,22]: 6,20,0,24, + tf = CONSTANT(0.065535909662600006)*f[6] + CONSTANT(-0.083698454702400005)*f[20] + CONSTANT(0.282094791771999980)*f[0] + CONSTANT(0.135045473384000000)*f[24]; + tg = CONSTANT(0.065535909662600006)*g[6] + CONSTANT(-0.083698454702400005)*g[20] + CONSTANT(0.282094791771999980)*g[0] + CONSTANT(0.135045473384000000)*g[24]; + y[22] += tf*g[22] + tg*f[22]; + t = f[22] * g[22]; + y[6] += CONSTANT(0.065535909662600006)*t; + y[20] += CONSTANT(-0.083698454702400005)*t; + y[0] += CONSTANT(0.282094791771999980)*t; + y[24] += CONSTANT(0.135045473384000000)*t; + + // [22,26]: 10,28, + tf = CONSTANT(0.101358691174000000)*f[10] + CONSTANT(0.084042186965900004)*f[28]; + tg = CONSTANT(0.101358691174000000)*g[10] + CONSTANT(0.084042186965900004)*g[28]; + y[22] += tf*g[26] + tg*f[26]; + y[26] += tf*g[22] + tg*f[22]; + t = f[22] * g[26] + f[26] * g[22]; + y[10] += CONSTANT(0.101358691174000000)*t; + y[28] += CONSTANT(0.084042186965900004)*t; + + // [22,27]: 1,11,25, + tf = CONSTANT(0.183739324704000010)*f[1] + CONSTANT(0.101990215611000000)*f[11] + CONSTANT(0.130197596200999990)*f[25]; + tg = CONSTANT(0.183739324704000010)*g[1] + CONSTANT(0.101990215611000000)*g[11] + CONSTANT(0.130197596200999990)*g[25]; + y[22] += tf*g[27] + tg*f[27]; + y[27] += tf*g[22] + tg*f[22]; + t = f[22] * g[27] + f[27] * g[22]; + y[1] += CONSTANT(0.183739324704000010)*t; + y[11] += CONSTANT(0.101990215611000000)*t; + y[25] += CONSTANT(0.130197596200999990)*t; + + // [22,32]: 2,30,12,34, + tf = CONSTANT(0.225033795606000010)*f[2] + CONSTANT(-0.099440056651100006)*f[30] + CONSTANT(0.022664492358099999)*f[12] + CONSTANT(0.084042186968800003)*f[34]; + tg = CONSTANT(0.225033795606000010)*g[2] + CONSTANT(-0.099440056651100006)*g[30] + CONSTANT(0.022664492358099999)*g[12] + CONSTANT(0.084042186968800003)*g[34]; + y[22] += tf*g[32] + tg*f[32]; + y[32] += tf*g[22] + tg*f[22]; + t = f[22] * g[32] + f[32] * g[22]; + y[2] += CONSTANT(0.225033795606000010)*t; + y[30] += CONSTANT(-0.099440056651100006)*t; + y[12] += CONSTANT(0.022664492358099999)*t; + y[34] += CONSTANT(0.084042186968800003)*t; + + // [22,33]: 3,13,35, + tf = CONSTANT(0.183739324704000010)*f[3] + CONSTANT(0.101990215611000000)*f[13] + CONSTANT(0.130197596200999990)*f[35]; + tg = CONSTANT(0.183739324704000010)*g[3] + CONSTANT(0.101990215611000000)*g[13] + CONSTANT(0.130197596200999990)*g[35]; + y[22] += tf*g[33] + tg*f[33]; + y[33] += tf*g[22] + tg*f[22]; + t = f[22] * g[33] + f[33] * g[22]; + y[3] += CONSTANT(0.183739324704000010)*t; + y[13] += CONSTANT(0.101990215611000000)*t; + y[35] += CONSTANT(0.130197596200999990)*t; + + // [23,23]: 6,20,0, + tf = CONSTANT(-0.057343920955899998)*f[6] + CONSTANT(-0.159787958979000000)*f[20] + CONSTANT(0.282094791768999990)*f[0]; + tg = CONSTANT(-0.057343920955899998)*g[6] + CONSTANT(-0.159787958979000000)*g[20] + CONSTANT(0.282094791768999990)*g[0]; + y[23] += tf*g[23] + tg*f[23]; + t = f[23] * g[23]; + y[6] += CONSTANT(-0.057343920955899998)*t; + y[20] += CONSTANT(-0.159787958979000000)*t; + y[0] += CONSTANT(0.282094791768999990)*t; + + // [23,26]: 1,11,29, + tf = CONSTANT(0.208340811096000000)*f[1] + CONSTANT(0.029982305185199998)*f[11] + CONSTANT(-0.118853600623999990)*f[29]; + tg = CONSTANT(0.208340811096000000)*g[1] + CONSTANT(0.029982305185199998)*g[11] + CONSTANT(-0.118853600623999990)*g[29]; + y[23] += tf*g[26] + tg*f[26]; + y[26] += tf*g[23] + tg*f[23]; + t = f[23] * g[26] + f[26] * g[23]; + y[1] += CONSTANT(0.208340811096000000)*t; + y[11] += CONSTANT(0.029982305185199998)*t; + y[29] += CONSTANT(-0.118853600623999990)*t; + + // [23,28]: 25,11,1,29, + tf = CONSTANT(-0.099440056652200001)*f[25] + CONSTANT(-0.121172043789000000)*f[11] + CONSTANT(0.060142811686500000)*f[1] + CONSTANT(-0.034310079156700000)*f[29]; + tg = CONSTANT(-0.099440056652200001)*g[25] + CONSTANT(-0.121172043789000000)*g[11] + CONSTANT(0.060142811686500000)*g[1] + CONSTANT(-0.034310079156700000)*g[29]; + y[23] += tf*g[28] + tg*f[28]; + y[28] += tf*g[23] + tg*f[23]; + t = f[23] * g[28] + f[28] * g[23]; + y[25] += CONSTANT(-0.099440056652200001)*t; + y[11] += CONSTANT(-0.121172043789000000)*t; + y[1] += CONSTANT(0.060142811686500000)*t; + y[29] += CONSTANT(-0.034310079156700000)*t; + + // [23,32]: 31,13,3,35, + tf = CONSTANT(0.034310079156599997)*f[31] + CONSTANT(0.121172043788000010)*f[13] + CONSTANT(-0.060142811686900000)*f[3] + CONSTANT(-0.099440056652700004)*f[35]; + tg = CONSTANT(0.034310079156599997)*g[31] + CONSTANT(0.121172043788000010)*g[13] + CONSTANT(-0.060142811686900000)*g[3] + CONSTANT(-0.099440056652700004)*g[35]; + y[23] += tf*g[32] + tg*f[32]; + y[32] += tf*g[23] + tg*f[23]; + t = f[23] * g[32] + f[32] * g[23]; + y[31] += CONSTANT(0.034310079156599997)*t; + y[13] += CONSTANT(0.121172043788000010)*t; + y[3] += CONSTANT(-0.060142811686900000)*t; + y[35] += CONSTANT(-0.099440056652700004)*t; + + // [23,33]: 2,30,12, + tf = CONSTANT(0.196425600433000000)*f[2] + CONSTANT(-0.130197596204999990)*f[30] + CONSTANT(-0.103861751821000010)*f[12]; + tg = CONSTANT(0.196425600433000000)*g[2] + CONSTANT(-0.130197596204999990)*g[30] + CONSTANT(-0.103861751821000010)*g[12]; + y[23] += tf*g[33] + tg*f[33]; + y[33] += tf*g[23] + tg*f[23]; + t = f[23] * g[33] + f[33] * g[23]; + y[2] += CONSTANT(0.196425600433000000)*t; + y[30] += CONSTANT(-0.130197596204999990)*t; + y[12] += CONSTANT(-0.103861751821000010)*t; + + // [23,34]: 3,13,31, + tf = CONSTANT(0.208340811100000000)*f[3] + CONSTANT(0.029982305185400002)*f[13] + CONSTANT(-0.118853600623000000)*f[31]; + tg = CONSTANT(0.208340811100000000)*g[3] + CONSTANT(0.029982305185400002)*g[13] + CONSTANT(-0.118853600623000000)*g[31]; + y[23] += tf*g[34] + tg*f[34]; + y[34] += tf*g[23] + tg*f[23]; + t = f[23] * g[34] + f[34] * g[23]; + y[3] += CONSTANT(0.208340811100000000)*t; + y[13] += CONSTANT(0.029982305185400002)*t; + y[31] += CONSTANT(-0.118853600623000000)*t; + + // [24,24]: 6,0,20, + tf = CONSTANT(-0.229375683829000000)*f[6] + CONSTANT(0.282094791763999990)*f[0] + CONSTANT(0.106525305981000000)*f[20]; + tg = CONSTANT(-0.229375683829000000)*g[6] + CONSTANT(0.282094791763999990)*g[0] + CONSTANT(0.106525305981000000)*g[20]; + y[24] += tf*g[24] + tg*f[24]; + t = f[24] * g[24]; + y[6] += CONSTANT(-0.229375683829000000)*t; + y[0] += CONSTANT(0.282094791763999990)*t; + y[20] += CONSTANT(0.106525305981000000)*t; + + // [24,29]: 9,27,25, + tf = CONSTANT(-0.035835708931400000)*f[9] + CONSTANT(0.118853600623000000)*f[27] + CONSTANT(0.053152946071199997)*f[25]; + tg = CONSTANT(-0.035835708931400000)*g[9] + CONSTANT(0.118853600623000000)*g[27] + CONSTANT(0.053152946071199997)*g[25]; + y[24] += tf*g[29] + tg*f[29]; + y[29] += tf*g[24] + tg*f[24]; + t = f[24] * g[29] + f[29] * g[24]; + y[9] += CONSTANT(-0.035835708931400000)*t; + y[27] += CONSTANT(0.118853600623000000)*t; + y[25] += CONSTANT(0.053152946071199997)*t; + + // [24,31]: 15,33,35, + tf = CONSTANT(0.035835708931400000)*f[15] + CONSTANT(-0.118853600623000000)*f[33] + CONSTANT(0.053152946071199997)*f[35]; + tg = CONSTANT(0.035835708931400000)*g[15] + CONSTANT(-0.118853600623000000)*g[33] + CONSTANT(0.053152946071199997)*g[35]; + y[24] += tf*g[31] + tg*f[31]; + y[31] += tf*g[24] + tg*f[24]; + t = f[24] * g[31] + f[31] * g[24]; + y[15] += CONSTANT(0.035835708931400000)*t; + y[33] += CONSTANT(-0.118853600623000000)*t; + y[35] += CONSTANT(0.053152946071199997)*t; + + // [24,34]: 12,30,2, + tf = CONSTANT(-0.207723503645000000)*f[12] + CONSTANT(0.130197596199999990)*f[30] + CONSTANT(0.147319200325000010)*f[2]; + tg = CONSTANT(-0.207723503645000000)*g[12] + CONSTANT(0.130197596199999990)*g[30] + CONSTANT(0.147319200325000010)*g[2]; + y[24] += tf*g[34] + tg*f[34]; + y[34] += tf*g[24] + tg*f[24]; + t = f[24] * g[34] + f[34] * g[24]; + y[12] += CONSTANT(-0.207723503645000000)*t; + y[30] += CONSTANT(0.130197596199999990)*t; + y[2] += CONSTANT(0.147319200325000010)*t; + + // [25,25]: 0,6,20, + tf = CONSTANT(0.282094791761999970)*f[0] + CONSTANT(-0.242608896358999990)*f[6] + CONSTANT(0.130197596198000000)*f[20]; + tg = CONSTANT(0.282094791761999970)*g[0] + CONSTANT(-0.242608896358999990)*g[6] + CONSTANT(0.130197596198000000)*g[20]; + y[25] += tf*g[25] + tg*f[25]; + t = f[25] * g[25]; + y[0] += CONSTANT(0.282094791761999970)*t; + y[6] += CONSTANT(-0.242608896358999990)*t; + y[20] += CONSTANT(0.130197596198000000)*t; + + // [26,26]: 6,20,0, + tf = CONSTANT(-0.097043558542400002)*f[6] + CONSTANT(-0.130197596207000000)*f[20] + CONSTANT(0.282094791766000000)*f[0]; + tg = CONSTANT(-0.097043558542400002)*g[6] + CONSTANT(-0.130197596207000000)*g[20] + CONSTANT(0.282094791766000000)*g[0]; + y[26] += tf*g[26] + tg*f[26]; + t = f[26] * g[26]; + y[6] += CONSTANT(-0.097043558542400002)*t; + y[20] += CONSTANT(-0.130197596207000000)*t; + y[0] += CONSTANT(0.282094791766000000)*t; + + // [27,27]: 0,20,6, + tf = CONSTANT(0.282094791770000020)*f[0] + CONSTANT(-0.130197596204999990)*f[20] + CONSTANT(0.016173926423100001)*f[6]; + tg = CONSTANT(0.282094791770000020)*g[0] + CONSTANT(-0.130197596204999990)*g[20] + CONSTANT(0.016173926423100001)*g[6]; + y[27] += tf*g[27] + tg*f[27]; + t = f[27] * g[27]; + y[0] += CONSTANT(0.282094791770000020)*t; + y[20] += CONSTANT(-0.130197596204999990)*t; + y[6] += CONSTANT(0.016173926423100001)*t; + + // [28,28]: 6,0,20,24, + tf = CONSTANT(0.097043558538800007)*f[6] + CONSTANT(0.282094791771999980)*f[0] + CONSTANT(-0.021699599367299999)*f[20] + CONSTANT(-0.128376561118000000)*f[24]; + tg = CONSTANT(0.097043558538800007)*g[6] + CONSTANT(0.282094791771999980)*g[0] + CONSTANT(-0.021699599367299999)*g[20] + CONSTANT(-0.128376561118000000)*g[24]; + y[28] += tf*g[28] + tg*f[28]; + t = f[28] * g[28]; + y[6] += CONSTANT(0.097043558538800007)*t; + y[0] += CONSTANT(0.282094791771999980)*t; + y[20] += CONSTANT(-0.021699599367299999)*t; + y[24] += CONSTANT(-0.128376561118000000)*t; + + // [29,29]: 20,6,0,22,8, + tf = CONSTANT(0.086798397468799998)*f[20] + CONSTANT(0.145565337808999990)*f[6] + CONSTANT(0.282094791773999990)*f[0] + CONSTANT(-0.097043558539500002)*f[22] + CONSTANT(-0.140070311615000000)*f[8]; + tg = CONSTANT(0.086798397468799998)*g[20] + CONSTANT(0.145565337808999990)*g[6] + CONSTANT(0.282094791773999990)*g[0] + CONSTANT(-0.097043558539500002)*g[22] + CONSTANT(-0.140070311615000000)*g[8]; + y[29] += tf*g[29] + tg*f[29]; + t = f[29] * g[29]; + y[20] += CONSTANT(0.086798397468799998)*t; + y[6] += CONSTANT(0.145565337808999990)*t; + y[0] += CONSTANT(0.282094791773999990)*t; + y[22] += CONSTANT(-0.097043558539500002)*t; + y[8] += CONSTANT(-0.140070311615000000)*t; + + // [30,30]: 0,20,6, + tf = CONSTANT(0.282094804531000000)*f[0] + CONSTANT(0.130197634486000000)*f[20] + CONSTANT(0.161739292769000010)*f[6]; + tg = CONSTANT(0.282094804531000000)*g[0] + CONSTANT(0.130197634486000000)*g[20] + CONSTANT(0.161739292769000010)*g[6]; + y[30] += tf*g[30] + tg*f[30]; + t = f[30] * g[30]; + y[0] += CONSTANT(0.282094804531000000)*t; + y[20] += CONSTANT(0.130197634486000000)*t; + y[6] += CONSTANT(0.161739292769000010)*t; + + // [31,31]: 6,8,20,22,0, + tf = CONSTANT(0.145565337808999990)*f[6] + CONSTANT(0.140070311615000000)*f[8] + CONSTANT(0.086798397468799998)*f[20] + CONSTANT(0.097043558539500002)*f[22] + CONSTANT(0.282094791773999990)*f[0]; + tg = CONSTANT(0.145565337808999990)*g[6] + CONSTANT(0.140070311615000000)*g[8] + CONSTANT(0.086798397468799998)*g[20] + CONSTANT(0.097043558539500002)*g[22] + CONSTANT(0.282094791773999990)*g[0]; + y[31] += tf*g[31] + tg*f[31]; + t = f[31] * g[31]; + y[6] += CONSTANT(0.145565337808999990)*t; + y[8] += CONSTANT(0.140070311615000000)*t; + y[20] += CONSTANT(0.086798397468799998)*t; + y[22] += CONSTANT(0.097043558539500002)*t; + y[0] += CONSTANT(0.282094791773999990)*t; + + // [32,32]: 0,24,20,6, + tf = CONSTANT(0.282094791771999980)*f[0] + CONSTANT(0.128376561118000000)*f[24] + CONSTANT(-0.021699599367299999)*f[20] + CONSTANT(0.097043558538800007)*f[6]; + tg = CONSTANT(0.282094791771999980)*g[0] + CONSTANT(0.128376561118000000)*g[24] + CONSTANT(-0.021699599367299999)*g[20] + CONSTANT(0.097043558538800007)*g[6]; + y[32] += tf*g[32] + tg*f[32]; + t = f[32] * g[32]; + y[0] += CONSTANT(0.282094791771999980)*t; + y[24] += CONSTANT(0.128376561118000000)*t; + y[20] += CONSTANT(-0.021699599367299999)*t; + y[6] += CONSTANT(0.097043558538800007)*t; + + // [33,33]: 6,20,0, + tf = CONSTANT(0.016173926423100001)*f[6] + CONSTANT(-0.130197596204999990)*f[20] + CONSTANT(0.282094791770000020)*f[0]; + tg = CONSTANT(0.016173926423100001)*g[6] + CONSTANT(-0.130197596204999990)*g[20] + CONSTANT(0.282094791770000020)*g[0]; + y[33] += tf*g[33] + tg*f[33]; + t = f[33] * g[33]; + y[6] += CONSTANT(0.016173926423100001)*t; + y[20] += CONSTANT(-0.130197596204999990)*t; + y[0] += CONSTANT(0.282094791770000020)*t; + + // [34,34]: 20,6,0, + tf = CONSTANT(-0.130197596207000000)*f[20] + CONSTANT(-0.097043558542400002)*f[6] + CONSTANT(0.282094791766000000)*f[0]; + tg = CONSTANT(-0.130197596207000000)*g[20] + CONSTANT(-0.097043558542400002)*g[6] + CONSTANT(0.282094791766000000)*g[0]; + y[34] += tf*g[34] + tg*f[34]; + t = f[34] * g[34]; + y[20] += CONSTANT(-0.130197596207000000)*t; + y[6] += CONSTANT(-0.097043558542400002)*t; + y[0] += CONSTANT(0.282094791766000000)*t; + + // [35,35]: 6,0,20, + tf = CONSTANT(-0.242608896358999990)*f[6] + CONSTANT(0.282094791761999970)*f[0] + CONSTANT(0.130197596198000000)*f[20]; + tg = CONSTANT(-0.242608896358999990)*g[6] + CONSTANT(0.282094791761999970)*g[0] + CONSTANT(0.130197596198000000)*g[20]; + y[35] += tf*g[35] + tg*f[35]; + t = f[35] * g[35]; + y[6] += CONSTANT(-0.242608896358999990)*t; + y[0] += CONSTANT(0.282094791761999970)*t; + y[20] += CONSTANT(0.130197596198000000)*t; + + // multiply count=2527 + + return y; +} + + +//------------------------------------------------------------------------------------- +// Evaluates a directional light and returns spectral SH data. The output +// vector is computed so that if the intensity of R/G/B is unit the resulting +// exit radiance of a point directly under the light on a diffuse object with +// an albedo of 1 would be 1.0. This will compute 3 spectral samples, resultR +// has to be specified, while resultG and resultB are optional. +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb204988.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +bool XM_CALLCONV DirectX::XMSHEvalDirectionalLight( + size_t order, + FXMVECTOR dir, + FXMVECTOR color, + float *resultR, + float *resultG, + float *resultB) noexcept +{ + if (!resultR) + return false; + + if (order < XM_SH_MINORDER || order > XM_SH_MAXORDER) + return false; + + XMFLOAT3A clr; + XMStoreFloat3A(&clr, color); + + float fTmp[XM_SH_MAXORDER * XM_SH_MAXORDER]; + + XMSHEvalDirection(fTmp, order, dir); // evaluate the BF in this direction... + + // now compute "normalization" and scale vector for each valid spectral band + const float fNorm = XM_PI / CosWtInt(order); + + const size_t numcoeff = order*order; + + const float fRScale = fNorm * clr.x; + + for (size_t i = 0; i < numcoeff; ++i) + { + resultR[i] = fTmp[i] * fRScale; + } + + if (resultG) + { + const float fGScale = fNorm * clr.y; + + for (size_t i = 0; i < numcoeff; ++i) + { + resultG[i] = fTmp[i] * fGScale; + } + } + + if (resultB) + { + const float fBScale = fNorm * clr.z; + + for (size_t i = 0; i < numcoeff; ++i) + { + resultB[i] = fTmp[i] * fBScale; + } + } + + return true; +} + + +//------------------------------------------------------------------------------------ +// Evaluates a spherical light and returns spectral SH data. There is no +// normalization of the intensity of the light like there is for directional +// lights, care has to be taken when specifiying the intensities. This will +// compute 3 spectral samples, resultR has to be specified, while resultG and +// resultB are optional. +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb205451.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +bool XM_CALLCONV DirectX::XMSHEvalSphericalLight( + size_t order, + FXMVECTOR pos, + float radius, + FXMVECTOR color, + float *resultR, + float *resultG, + float *resultB) noexcept +{ + if (!resultR) + return false; + + if (radius < 0.f) + return false; + + const float fDist = XMVectorGetX(XMVector3Length(pos)); + + // WARNING: fDist should not be < radius - otherwise light contains origin + + //const float fSinConeAngle = (fDist <= radius) ? 0.99999f : radius/fDist; + const float fConeAngle = (fDist <= radius) ? (XM_PIDIV2) : asinf(radius / fDist); + + XMVECTOR dir = XMVector3Normalize(pos); + + float fTmpDir[XM_SH_MAXORDER* XM_SH_MAXORDER]; // rotation "vector" + float fTmpL0[XM_SH_MAXORDER]; + + // + // Sphere at distance fDist, the cone angle is determined by looking at the + // right triangle with one side (the hypotenuse) beind the vector from the + // origin to the center of the sphere, another side is from the origin to + // a point on the sphere whose normal is perpendicular to the given side (this + // is one of the points on the cone that is defined by the projection of the sphere + // through the origin - we want to find the angle of this cone) and the final + // side being from the center of the sphere to the point of tagency (the two + // sides conected to this are at a right angle by construction.) + // From trig we know that sin(theta) = ||opposite||/||hypotenuse||, where + // ||opposite|| = Radius, ||hypotenuse|| = fDist + // theta is the angle of the cone that subtends the sphere from the origin + // + + // no default normalization is done for this case, have to be careful how + // you represent the coefficients... + + const float fNewNorm = 1.0f;///(fSinConeAngle*fSinConeAngle); + + ComputeCapInt(order, fConeAngle, fTmpL0); + + XMFLOAT3A vd; + XMStoreFloat3(&vd, dir); + + const float fX = vd.x; + const float fY = vd.y; + const float fZ = vd.z; + + switch (order) + { + case 2: + sh_eval_basis_1(fX, fY, fZ, fTmpDir); + break; + + case 3: + sh_eval_basis_2(fX, fY, fZ, fTmpDir); + break; + + case 4: + sh_eval_basis_3(fX, fY, fZ, fTmpDir); + break; + + case 5: + sh_eval_basis_4(fX, fY, fZ, fTmpDir); + break; + + case 6: + sh_eval_basis_5(fX, fY, fZ, fTmpDir); + break; + + default: + assert(order < XM_SH_MINORDER || order > XM_SH_MAXORDER); + return false; + } + + XMFLOAT3A clr; + XMStoreFloat3A(&clr, color); + + for (size_t i = 0; i < order; ++i) + { + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + const float fValUse = fTmpL0[i] * clr.x*fNewNorm*fExtraNormFac[i]; + for (size_t j = 0; j < cNumCoefs; ++j) resultR[cStart + j] = fTmpDir[cStart + j] * fValUse; + } + + if (resultG) + { + for (size_t i = 0; i < order; ++i) + { + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + const float fValUse = fTmpL0[i] * clr.y*fNewNorm*fExtraNormFac[i]; + for (size_t j = 0; j < cNumCoefs; ++j) resultG[cStart + j] = fTmpDir[cStart + j] * fValUse; + } + } + + if (resultB) + { + for (size_t i = 0; i < order; ++i) + { + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + const float fValUse = fTmpL0[i] * clr.z*fNewNorm*fExtraNormFac[i]; + for (size_t j = 0; j < cNumCoefs; ++j) resultB[cStart + j] = fTmpDir[cStart + j] * fValUse; + } + } + + return true; +} + + +//------------------------------------------------------------------------------------- +// Evaluates a light that is a cone of constant intensity and returns spectral +// SH data. The output vector is computed so that if the intensity of R/G/B is +// unit the resulting exit radiance of a point directly under the light oriented +// in the cone direction on a diffuse object with an albedo of 1 would be 1.0. +// This will compute 3 spectral samples, resultR has to be specified, while resultG +// and resultB are optional. +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb204986.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +bool XM_CALLCONV DirectX::XMSHEvalConeLight( + size_t order, + FXMVECTOR dir, + float radius, + FXMVECTOR color, + float *resultR, + float *resultG, + float *resultB) noexcept +{ + if (!resultR) + return false; + + if (radius < 0.f || radius >(XM_PI*1.00001f)) + return false; + + if (radius < 0.0001f) + { + // turn it into a pure directional light... + return XMSHEvalDirectionalLight(order, dir, color, resultR, resultG, resultB); + } + else + { + float fTmpL0[XM_SH_MAXORDER]; + float fTmpDir[XM_SH_MAXORDER * XM_SH_MAXORDER]; + + const float fConeAngle = radius; + const float fAngCheck = (fConeAngle > XM_PIDIV2) ? (XM_PIDIV2) : fConeAngle; + + const float fNewNorm = 1.0f / (sinf(fAngCheck)*sinf(fAngCheck)); + + ComputeCapInt(order, fConeAngle, fTmpL0); + + XMFLOAT3A vd; + XMStoreFloat3(&vd, dir); + + const float fX = vd.x; + const float fY = vd.y; + const float fZ = vd.z; + + switch (order) + { + case 2: + sh_eval_basis_1(fX, fY, fZ, fTmpDir); + break; + + case 3: + sh_eval_basis_2(fX, fY, fZ, fTmpDir); + break; + + case 4: + sh_eval_basis_3(fX, fY, fZ, fTmpDir); + break; + + case 5: + sh_eval_basis_4(fX, fY, fZ, fTmpDir); + break; + + case 6: + sh_eval_basis_5(fX, fY, fZ, fTmpDir); + break; + + default: + assert(order < XM_SH_MINORDER || order > XM_SH_MAXORDER); + return false; + } + + XMFLOAT3A clr; + XMStoreFloat3A(&clr, color); + + for (size_t i = 0; i < order; ++i) + { + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + const float fValUse = fTmpL0[i] * clr.x*fNewNorm*fExtraNormFac[i]; + for (size_t j = 0; j < cNumCoefs; ++j) + resultR[cStart + j] = fTmpDir[cStart + j] * fValUse; + } + + if (resultG) + { + for (size_t i = 0; i < order; ++i) + { + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + const float fValUse = fTmpL0[i] * clr.y*fNewNorm*fExtraNormFac[i]; + for (size_t j = 0; j < cNumCoefs; ++j) + resultG[cStart + j] = fTmpDir[cStart + j] * fValUse; + } + } + + if (resultB) + { + for (size_t i = 0; i < order; ++i) + { + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + const float fValUse = fTmpL0[i] * clr.z*fNewNorm*fExtraNormFac[i]; + for (size_t j = 0; j < cNumCoefs; ++j) + resultB[cStart + j] = fTmpDir[cStart + j] * fValUse; + } + } + } + + return true; +} + + +//------------------------------------------------------------------------------------ +// Evaluates a light that is a linear interpolant between two colors over the +// sphere. The interpolant is linear along the axis of the two points, not +// over the surface of the sphere (ie: if the axis was (0,0,1) it is linear in +// Z, not in the azimuthal angle.) The resulting spherical lighting function +// is normalized so that a point on a perfectly diffuse surface with no +// shadowing and a normal pointed in the direction pDir would result in exit +// radiance with a value of 1 if the top color was white and the bottom color +// was black. This is a very simple model where topColor represents the intensity +// of the "sky" and bottomColor represents the intensity of the "ground". +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb204989.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +bool XM_CALLCONV DirectX::XMSHEvalHemisphereLight( + size_t order, + FXMVECTOR dir, + FXMVECTOR topColor, + FXMVECTOR bottomColor, + float *resultR, + float *resultG, + float *resultB) noexcept +{ + if (!resultR) + return false; + + if (order < XM_SH_MINORDER || order > XM_SH_MAXORDER) + return false; + + // seperate "R/G/B colors... + + float fTmpDir[XM_SH_MAXORDER * XM_SH_MAXORDER]; // rotation "vector" + float fTmpL0[XM_SH_MAXORDER]; + + const float fNewNorm = 3.0f / 2.0f; // normalizes things for 1 sky color, 0 ground color... + + XMFLOAT3A vd; + XMStoreFloat3(&vd, dir); + + const float fX = vd.x; + const float fY = vd.y; + const float fZ = vd.z; + + sh_eval_basis_1(fX, fY, fZ, fTmpDir); + + XMFLOAT3A clrTop; + XMStoreFloat3A(&clrTop, topColor); + + XMFLOAT3A clrBottom; + XMStoreFloat3A(&clrBottom, bottomColor); + + float fA = clrTop.x; + float fAvrg = (clrTop.x + clrBottom.x)*0.5f; + + fTmpL0[0] = fAvrg*2.0f*SHEvalHemisphereLight_fSqrtPi; + fTmpL0[1] = (fA - fAvrg)*2.0f*SHEvalHemisphereLight_fSqrtPi3; + + size_t i = 0; + for (; i < 2; ++i) + { + _Analysis_assume_(i < order); + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + const float fValUse = fTmpL0[i] * fNewNorm*fExtraNormFac[i]; + for (size_t j = 0; j < cNumCoefs; ++j) resultR[cStart + j] = fTmpDir[cStart + j] * fValUse; + } + + for (; i < order; ++i) + { + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + for (size_t j = 0; j < cNumCoefs; ++j) resultR[cStart + j] = 0.0f; + } + + if (resultG) + { + fA = clrTop.y; + fAvrg = (clrTop.y + clrBottom.y)*0.5f; + + fTmpL0[0] = fAvrg*2.0f*SHEvalHemisphereLight_fSqrtPi; + fTmpL0[1] = (fA - fAvrg)*2.0f*SHEvalHemisphereLight_fSqrtPi3; + + for (i = 0; i < 2; ++i) + { + _Analysis_assume_(i < order); + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + const float fValUse = fTmpL0[i] * fNewNorm*fExtraNormFac[i]; + for (size_t j = 0; j < cNumCoefs; ++j) resultG[cStart + j] = fTmpDir[cStart + j] * fValUse; + } + + for (; i < order; ++i) + { + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + for (size_t j = 0; j < cNumCoefs; ++j) resultG[cStart + j] = 0.0f; + } + } + + if (resultB) + { + fA = clrTop.z; + fAvrg = (clrTop.z + clrBottom.z)*0.5f; + + fTmpL0[0] = fAvrg*2.0f*SHEvalHemisphereLight_fSqrtPi; + fTmpL0[1] = (fA - fAvrg)*2.0f*SHEvalHemisphereLight_fSqrtPi3; + + for (i = 0; i < 2; ++i) + { + _Analysis_assume_(i < order); + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + const float fValUse = fTmpL0[i] * fNewNorm*fExtraNormFac[i]; + for (size_t j = 0; j < cNumCoefs; ++j) resultB[cStart + j] = fTmpDir[cStart + j] * fValUse; + } + + for (; i < order; ++i) + { + const size_t cNumCoefs = 2 * i + 1; + const size_t cStart = i*i; + for (size_t j = 0; j < cNumCoefs; ++j) resultB[cStart + j] = 0.0f; + } + } + + return true; +} diff --git a/Extern/dxmath/SHMath/DirectXSH.h b/Extern/dxmath/SHMath/DirectXSH.h new file mode 100644 index 000000000..89dfb91b0 --- /dev/null +++ b/Extern/dxmath/SHMath/DirectXSH.h @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------------- +// DirectXSH.h -- C++ Spherical Harmonics Math Library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/p/?LinkId=262885 +//------------------------------------------------------------------------------------- + +#pragma once + +#define DIRECTX_SHMATH_VERSION 106 + +#include "DirectXMath.h" + +namespace DirectX +{ + constexpr size_t XM_SH_MINORDER = 2; + constexpr size_t XM_SH_MAXORDER = 6; + + float* XM_CALLCONV XMSHEvalDirection(_Out_writes_(order*order) float *result, _In_ size_t order, _In_ FXMVECTOR dir) noexcept; + + float* XM_CALLCONV XMSHRotate(_Out_writes_(order*order) float *result, _In_ size_t order, _In_ FXMMATRIX rotMatrix, _In_reads_(order*order) const float *input) noexcept; + + float* XMSHRotateZ(_Out_writes_(order*order) float *result, _In_ size_t order, _In_ float angle, _In_reads_(order*order) const float *input) noexcept; + + float* XMSHAdd(_Out_writes_(order*order) float *result, _In_ size_t order, _In_reads_(order*order) const float *inputA, _In_reads_(order*order) const float *inputB) noexcept; + + float* XMSHScale(_Out_writes_(order*order) float *result, _In_ size_t order, _In_reads_(order*order) const float *input, _In_ float scale) noexcept; + + float XMSHDot(_In_ size_t order, _In_reads_(order*order) const float *inputA, _In_reads_(order*order) const float *inputB) noexcept; + + float* XMSHMultiply(_Out_writes_(order*order) float *result, _In_ size_t order, _In_reads_(order*order) const float *inputF, _In_reads_(order*order) const float *inputG) noexcept; + + float* XMSHMultiply2(_Out_writes_(4) float *result, _In_reads_(4) const float *inputF, _In_reads_(4) const float *inputG) noexcept; + + float* XMSHMultiply3(_Out_writes_(9) float *result, _In_reads_(9) const float *inputF, _In_reads_(9) const float *inputG) noexcept; + + float* XMSHMultiply4(_Out_writes_(16) float *result, _In_reads_(16) const float *inputF, _In_reads_(16) const float *inputG) noexcept; + + float* XMSHMultiply5(_Out_writes_(25) float *result, _In_reads_(25) const float *inputF, _In_reads_(25) const float *inputG) noexcept; + + float* XMSHMultiply6(_Out_writes_(36) float *result, _In_reads_(36) const float *inputF, _In_reads_(36) const float *inputG) noexcept; + + bool XM_CALLCONV XMSHEvalDirectionalLight( + _In_ size_t order, _In_ FXMVECTOR dir, _In_ FXMVECTOR color, + _Out_writes_(order*order) float *resultR, _Out_writes_opt_(order*order) float *resultG, _Out_writes_opt_(order*order) float *resultB) noexcept; + + bool XM_CALLCONV XMSHEvalSphericalLight( + _In_ size_t order, _In_ FXMVECTOR pos, _In_ float radius, _In_ FXMVECTOR color, + _Out_writes_(order*order) float *resultR, _Out_writes_opt_(order*order) float *resultG, _Out_writes_opt_(order*order) float *resultB) noexcept; + + bool XM_CALLCONV XMSHEvalConeLight( + _In_ size_t order, _In_ FXMVECTOR dir, _In_ float radius, _In_ FXMVECTOR color, + _Out_writes_(order*order) float *resultR, _Out_writes_opt_(order*order) float *resultG, _Out_writes_opt_(order*order) float *resultB) noexcept; + + bool XM_CALLCONV XMSHEvalHemisphereLight( + _In_ size_t order, _In_ FXMVECTOR dir, _In_ FXMVECTOR topColor, _In_ FXMVECTOR bottomColor, + _Out_writes_(order*order) float *resultR, _Out_writes_opt_(order*order) float *resultG, _Out_writes_opt_(order*order) float *resultB) noexcept; + +#if defined(__d3d11_h__) || defined(__d3d11_x_h__) + HRESULT SHProjectCubeMap( + _In_ ID3D11DeviceContext *context, _In_ size_t order, _In_ ID3D11Texture2D *cubeMap, + _Out_writes_opt_(order*order) float *resultR, _Out_writes_opt_(order*order) float *resultG, _Out_writes_opt_(order*order) float *resultB) noexcept; +#endif + +#if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) + HRESULT SHProjectCubeMap( + _In_ size_t order, _In_ const D3D12_RESOURCE_DESC& desc, _In_ const D3D12_SUBRESOURCE_DATA cubeMap[6], + _Out_writes_opt_(order*order) float *resultR, _Out_writes_opt_(order*order) float *resultG, _Out_writes_opt_(order*order) float *resultB) noexcept; +#endif +} // namespace DirectX diff --git a/Extern/dxmath/SHMath/DirectXSHD3D11.cpp b/Extern/dxmath/SHMath/DirectXSHD3D11.cpp new file mode 100644 index 000000000..6f61f41de --- /dev/null +++ b/Extern/dxmath/SHMath/DirectXSHD3D11.cpp @@ -0,0 +1,385 @@ +//------------------------------------------------------------------------------------- +// DirectXSHD3D11.cpp -- C++ Spherical Harmonics Math Library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/p/?LinkId=262885 +//------------------------------------------------------------------------------------- + +#ifdef _MSC_VER +#pragma warning( disable : 4616 4619 4061 4265 4626 5039 ) +// C4616/C4619 #pragma warning warnings +// C4061 numerator 'identifier' in switch of enum 'enumeration' is not explicitly handled by a case label +// C4265 class has virtual functions, but destructor is not virtual +// C4626 assignment operator was implicitly defined as deleted +// C5039 pointer or reference to potentially throwing function passed to extern C function under - EHc + +#pragma warning(push) +#pragma warning(disable: 4365) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "DirectXSH.h" + +#include + +#include +#include +#include + +#include + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + +using namespace DirectX; + +using Microsoft::WRL::ComPtr; + +namespace +{ + struct aligned_deleter { void operator()(void* p) { _aligned_free(p); } }; + + using ScopedAlignedArrayXMVECTOR = std::unique_ptr; + + //------------------------------------------------------------------------------------- + // This code is lifted from DirectXTex http://go.microsoft.com/fwlink/?LinkId=248926 + // If you need additional DXGI format support, see DirectXTexConvert.cpp + //------------------------------------------------------------------------------------- +#define LOAD_SCANLINE( type, func )\ + if ( size >= sizeof(type) )\ + {\ + const type * __restrict sPtr = reinterpret_cast(pSource);\ + for( size_t icount = 0; icount < ( size - sizeof(type) + 1 ); icount += sizeof(type) )\ + {\ + if ( dPtr >= ePtr ) break;\ + *(dPtr++) = func( sPtr++ );\ + }\ + return true;\ + }\ + return false; + +#define LOAD_SCANLINE3( type, func, defvec )\ + if ( size >= sizeof(type) )\ + {\ + const type * __restrict sPtr = reinterpret_cast(pSource);\ + for( size_t icount = 0; icount < ( size - sizeof(type) + 1 ); icount += sizeof(type) )\ + {\ + XMVECTOR v = func( sPtr++ );\ + if ( dPtr >= ePtr ) break;\ + *(dPtr++) = XMVectorSelect( defvec, v, g_XMSelect1110 );\ + }\ + return true;\ + }\ + return false; + +#define LOAD_SCANLINE2( type, func, defvec )\ + if ( size >= sizeof(type) )\ + {\ + const type * __restrict sPtr = reinterpret_cast(pSource);\ + for( size_t icount = 0; icount < ( size - sizeof(type) + 1 ); icount += sizeof(type) )\ + {\ + XMVECTOR v = func( sPtr++ );\ + if ( dPtr >= ePtr ) break;\ + *(dPtr++) = XMVectorSelect( defvec, v, g_XMSelect1100 );\ + }\ + return true;\ + }\ + return false; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 6101) +#endif + _Success_(return) + bool LoadScanline( + _Out_writes_(count) DirectX::XMVECTOR* pDestination, + size_t count, + _In_reads_bytes_(size) LPCVOID pSource, + size_t size, + DXGI_FORMAT format) + { + assert(pDestination && count > 0 && ((reinterpret_cast(pDestination) & 0xF) == 0)); + assert(pSource && size > 0); + + using namespace DirectX::PackedVector; + + XMVECTOR* __restrict dPtr = pDestination; + if (!dPtr) + return false; + + const XMVECTOR* ePtr = pDestination + count; + + switch (format) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + { + size_t msize = (size > (sizeof(XMVECTOR)*count)) ? (sizeof(XMVECTOR)*count) : size; + memcpy_s(dPtr, sizeof(XMVECTOR)*count, pSource, msize); + } + return true; + + case DXGI_FORMAT_R32G32B32_FLOAT: + LOAD_SCANLINE3(XMFLOAT3, XMLoadFloat3, g_XMIdentityR3) + + case DXGI_FORMAT_R16G16B16A16_FLOAT: + LOAD_SCANLINE(XMHALF4, XMLoadHalf4) + + case DXGI_FORMAT_R32G32_FLOAT: + LOAD_SCANLINE2(XMFLOAT2, XMLoadFloat2, g_XMIdentityR3) + + case DXGI_FORMAT_R11G11B10_FLOAT: + LOAD_SCANLINE3(XMFLOAT3PK, XMLoadFloat3PK, g_XMIdentityR3) + + case DXGI_FORMAT_R16G16_FLOAT: + LOAD_SCANLINE2(XMHALF2, XMLoadHalf2, g_XMIdentityR3) + + case DXGI_FORMAT_R32_FLOAT: + if (size >= sizeof(float)) + { + const float* __restrict sPtr = reinterpret_cast(pSource); + for (size_t icount = 0; icount < size; icount += sizeof(float)) + { + XMVECTOR v = XMLoadFloat(sPtr++); + if (dPtr >= ePtr) break; + *(dPtr++) = XMVectorSelect(g_XMIdentityR3, v, g_XMSelect1000); + } + return true; + } + return false; + + case DXGI_FORMAT_R16_FLOAT: + if (size >= sizeof(HALF)) + { + const HALF * __restrict sPtr = reinterpret_cast(pSource); + for (size_t icount = 0; icount < size; icount += sizeof(HALF)) + { + if (dPtr >= ePtr) break; + *(dPtr++) = XMVectorSet(XMConvertHalfToFloat(*sPtr++), 0.f, 0.f, 1.f); + } + return true; + } + return false; + + default: + return false; + } + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif +} // namespace anonymous + +//------------------------------------------------------------------------------------- +// Projects a function represented in a cube map into spherical harmonics. +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/ff476300.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::SHProjectCubeMap( + ID3D11DeviceContext *context, + size_t order, + ID3D11Texture2D *cubeMap, + float *resultR, + float *resultG, + float* resultB) noexcept +{ + if (!context || !cubeMap) + return E_INVALIDARG; + + if (order < XM_SH_MINORDER || order > XM_SH_MAXORDER) + return E_INVALIDARG; + + D3D11_TEXTURE2D_DESC desc; + cubeMap->GetDesc(&desc); + + if ((desc.ArraySize != 6) + || (desc.Width != desc.Height) + || (desc.SampleDesc.Count > 1)) + return E_FAIL; + + switch (desc.Format) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R16_FLOAT: + // See LoadScanline to support more pixel formats + break; + + default: + return E_FAIL; + } + + //--- Create a staging resource copy (if needed) to be able to read data + ID3D11Texture2D* texture = nullptr; + + ComPtr staging; + if (!(desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) + { + D3D11_TEXTURE2D_DESC sdesc = desc; + sdesc.BindFlags = 0; + sdesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + sdesc.Usage = D3D11_USAGE_STAGING; + + ComPtr device; + context->GetDevice(&device); + + HRESULT hr = device->CreateTexture2D(&sdesc, nullptr, &staging); + if (FAILED(hr)) + return hr; + + context->CopyResource(staging.Get(), cubeMap); + + texture = staging.Get(); + } + else + texture = cubeMap; + + assert(texture != nullptr); + + //--- Setup for SH projection + ScopedAlignedArrayXMVECTOR scanline(reinterpret_cast(_aligned_malloc(sizeof(XMVECTOR)*desc.Width, 16))); + if (!scanline) + return E_OUTOFMEMORY; + + assert(desc.Width > 0); + float fSize = static_cast(desc.Width); + float fPicSize = 1.0f / fSize; + + // index from [0,W-1], f(0) maps to -1 + 1/W, f(W-1) maps to 1 - 1/w + // linear function x*S +B, 1st constraint means B is (-1+1/W), plug into + // second and solve for S: S = 2*(1-1/W)/(W-1). The old code that did + // this was incorrect - but only for computing the differential solid + // angle, where the final value was 1.0 instead of 1-1/w... + + float fB = -1.0f + 1.0f / fSize; + float fS = (desc.Width > 1) ? (2.0f*(1.0f - 1.0f / fSize) / (fSize - 1.0f)) : 0.f; + + // clear out accumulation variables + float fWt = 0.0f; + + if (resultR) + memset(resultR, 0, sizeof(float)*order*order); + if (resultG) + memset(resultG, 0, sizeof(float)*order*order); + if (resultB) + memset(resultB, 0, sizeof(float)*order*order); + + float shBuff[XM_SH_MAXORDER*XM_SH_MAXORDER] = {}; + float shBuffB[XM_SH_MAXORDER*XM_SH_MAXORDER] = {}; + + //--- Process each face of the cubemap + for (UINT face = 0; face < 6; ++face) + { + UINT dindex = D3D11CalcSubresource(0, face, desc.MipLevels); + + D3D11_MAPPED_SUBRESOURCE mapped; + HRESULT hr = context->Map(texture, dindex, D3D11_MAP_READ, 0, &mapped); + if (FAILED(hr)) + return hr; + + const uint8_t *pSrc = reinterpret_cast(mapped.pData); + for (UINT y = 0; y < desc.Height; ++y) + { + XMVECTOR* ptr = scanline.get(); + if (!LoadScanline(ptr, desc.Width, pSrc, mapped.RowPitch, desc.Format)) + { + context->Unmap(texture, dindex); + return E_FAIL; + } + + const float v = float(y) * fS + fB; + + XMVECTOR* pixel = ptr; + for (UINT x = 0; x < desc.Width; ++x, ++pixel) + { + const float u = float(x) * fS + fB; + + float ix, iy, iz; + switch (face) + { + case 0: // Positive X + iz = 1.0f - (2.0f * float(x) + 1.0f) * fPicSize; + iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + ix = 1.0f; + break; + + case 1: // Negative X + iz = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize; + iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + ix = -1; + break; + + case 2: // Positive Y + iz = -1.0f + (2.0f * float(y) + 1.0f) * fPicSize; + iy = 1.0f; + ix = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize; + break; + + case 3: // Negative Y + iz = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + iy = -1.0f; + ix = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize; + break; + + case 4: // Positive Z + iz = 1.0f; + iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + ix = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize; + break; + + case 5: // Negative Z + iz = -1.0f; + iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + ix = 1.0f - (2.0f * float(x) + 1.0f) * fPicSize; + break; + + default: + ix = iy = iz = 0.f; + assert(false); + break; + } + + XMVECTOR dir = XMVectorSet(ix, iy, iz, 0); + dir = XMVector3Normalize(dir); + + const float fDiffSolid = 4.0f / ((1.0f + u * u + v * v)*sqrtf(1.0f + u * u + v * v)); + fWt += fDiffSolid; + + XMSHEvalDirection(shBuff, order, dir); + + XMFLOAT3A clr; + XMStoreFloat3A(&clr, *pixel); + + if (resultR) XMSHAdd(resultR, order, resultR, XMSHScale(shBuffB, order, shBuff, clr.x*fDiffSolid)); + if (resultG) XMSHAdd(resultG, order, resultG, XMSHScale(shBuffB, order, shBuff, clr.y*fDiffSolid)); + if (resultB) XMSHAdd(resultB, order, resultB, XMSHScale(shBuffB, order, shBuff, clr.z*fDiffSolid)); + } + + pSrc += mapped.RowPitch; + } + + context->Unmap(texture, dindex); + } + + const float fNormProj = (4.0f*XM_PI) / fWt; + + if (resultR) XMSHScale(resultR, order, resultR, fNormProj); + if (resultG) XMSHScale(resultG, order, resultG, fNormProj); + if (resultB) XMSHScale(resultB, order, resultB, fNormProj); + + return S_OK; +} diff --git a/Extern/dxmath/SHMath/DirectXSHD3D12.cpp b/Extern/dxmath/SHMath/DirectXSHD3D12.cpp new file mode 100644 index 000000000..ccabe91bd --- /dev/null +++ b/Extern/dxmath/SHMath/DirectXSHD3D12.cpp @@ -0,0 +1,349 @@ +//------------------------------------------------------------------------------------- +// DirectXSHD3D12.cpp -- C++ Spherical Harmonics Math Library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/p/?LinkId=262885 +//------------------------------------------------------------------------------------- + +#ifdef _MSC_VER +#pragma warning( disable : 4616 4619 4061 4265 4626 5039 ) +// C4616/C4619 #pragma warning warnings +// C4061 numerator 'identifier' in switch of enum 'enumeration' is not explicitly handled by a case label +// C4265 class has virtual functions, but destructor is not virtual +// C4626 assignment operator was implicitly defined as deleted +// C5039 pointer or reference to potentially throwing function passed to extern C function under - EHc +#endif + +#ifdef __MINGW32__ +#include +#endif + +#ifdef USING_DIRECTX_HEADERS +#include +#else +#include +#endif + +#include "DirectXSH.h" + +#include + +#include +#include +#include + +#include + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + +using namespace DirectX; + +using Microsoft::WRL::ComPtr; + +namespace +{ + struct aligned_deleter { void operator()(void* p) { _aligned_free(p); } }; + + using ScopedAlignedArrayXMVECTOR = std::unique_ptr; + + //------------------------------------------------------------------------------------- + // This code is lifted from DirectXTex http://go.microsoft.com/fwlink/?LinkId=248926 + // If you need additional DXGI format support, see DirectXTexConvert.cpp + //------------------------------------------------------------------------------------- +#define LOAD_SCANLINE( type, func )\ + if ( size >= sizeof(type) )\ + {\ + const type * __restrict sPtr = reinterpret_cast(pSource);\ + for( size_t icount = 0; icount < ( size - sizeof(type) + 1 ); icount += sizeof(type) )\ + {\ + if ( dPtr >= ePtr ) break;\ + *(dPtr++) = func( sPtr++ );\ + }\ + return true;\ + }\ + return false; + +#define LOAD_SCANLINE3( type, func, defvec )\ + if ( size >= sizeof(type) )\ + {\ + const type * __restrict sPtr = reinterpret_cast(pSource);\ + for( size_t icount = 0; icount < ( size - sizeof(type) + 1 ); icount += sizeof(type) )\ + {\ + XMVECTOR v = func( sPtr++ );\ + if ( dPtr >= ePtr ) break;\ + *(dPtr++) = XMVectorSelect( defvec, v, g_XMSelect1110 );\ + }\ + return true;\ + }\ + return false; + +#define LOAD_SCANLINE2( type, func, defvec )\ + if ( size >= sizeof(type) )\ + {\ + const type * __restrict sPtr = reinterpret_cast(pSource);\ + for( size_t icount = 0; icount < ( size - sizeof(type) + 1 ); icount += sizeof(type) )\ + {\ + XMVECTOR v = func( sPtr++ );\ + if ( dPtr >= ePtr ) break;\ + *(dPtr++) = XMVectorSelect( defvec, v, g_XMSelect1100 );\ + }\ + return true;\ + }\ + return false; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 6101) +#endif + _Success_(return) + bool LoadScanline( + _Out_writes_(count) DirectX::XMVECTOR* pDestination, + size_t count, + _In_reads_bytes_(size) LPCVOID pSource, + size_t size, + DXGI_FORMAT format) + { + assert(pDestination && count > 0 && ((reinterpret_cast(pDestination) & 0xF) == 0)); + assert(pSource && size > 0); + + using namespace DirectX::PackedVector; + + XMVECTOR* __restrict dPtr = pDestination; + if (!dPtr) + return false; + + const XMVECTOR* ePtr = pDestination + count; + + switch (format) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + { + size_t msize = (size > (sizeof(XMVECTOR)*count)) ? (sizeof(XMVECTOR)*count) : size; + memcpy_s(dPtr, sizeof(XMVECTOR)*count, pSource, msize); + } + return true; + + case DXGI_FORMAT_R32G32B32_FLOAT: + LOAD_SCANLINE3(XMFLOAT3, XMLoadFloat3, g_XMIdentityR3) + + case DXGI_FORMAT_R16G16B16A16_FLOAT: + LOAD_SCANLINE(XMHALF4, XMLoadHalf4) + + case DXGI_FORMAT_R32G32_FLOAT: + LOAD_SCANLINE2(XMFLOAT2, XMLoadFloat2, g_XMIdentityR3) + + case DXGI_FORMAT_R11G11B10_FLOAT: + LOAD_SCANLINE3(XMFLOAT3PK, XMLoadFloat3PK, g_XMIdentityR3) + + case DXGI_FORMAT_R16G16_FLOAT: + LOAD_SCANLINE2(XMHALF2, XMLoadHalf2, g_XMIdentityR3) + + case DXGI_FORMAT_R32_FLOAT: + if (size >= sizeof(float)) + { + const float* __restrict sPtr = reinterpret_cast(pSource); + for (size_t icount = 0; icount < size; icount += sizeof(float)) + { + XMVECTOR v = XMLoadFloat(sPtr++); + if (dPtr >= ePtr) break; + *(dPtr++) = XMVectorSelect(g_XMIdentityR3, v, g_XMSelect1000); + } + return true; + } + return false; + + case DXGI_FORMAT_R16_FLOAT: + if (size >= sizeof(HALF)) + { + const HALF * __restrict sPtr = reinterpret_cast(pSource); + for (size_t icount = 0; icount < size; icount += sizeof(HALF)) + { + if (dPtr >= ePtr) break; + *(dPtr++) = XMVectorSet(XMConvertHalfToFloat(*sPtr++), 0.f, 0.f, 1.f); + } + return true; + } + return false; + + default: + return false; + } + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif +} // namespace anonymous + +//------------------------------------------------------------------------------------- +// Projects a function represented in a cube map into spherical harmonics. +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/ff476300.aspx +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::SHProjectCubeMap( + size_t order, + const D3D12_RESOURCE_DESC& desc, + const D3D12_SUBRESOURCE_DATA cubeMap[6], + float *resultR, + float *resultG, + float *resultB) noexcept +{ + if (order < XM_SH_MINORDER || order > XM_SH_MAXORDER) + return E_INVALIDARG; + + if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D + || (desc.DepthOrArraySize != 6) + || (desc.Width != desc.Height) + || (desc.SampleDesc.Count > 1)) + return E_FAIL; + + switch (desc.Format) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R16_FLOAT: + // See LoadScanline to support more pixel formats + break; + + default: + return E_FAIL; + } + + //--- Setup for SH projection + ScopedAlignedArrayXMVECTOR scanline(reinterpret_cast(_aligned_malloc(static_cast(sizeof(XMVECTOR)*desc.Width), 16))); + if (!scanline) + return E_OUTOFMEMORY; + + assert(desc.Width > 0); + float fSize = static_cast(desc.Width); + float fPicSize = 1.0f / fSize; + + // index from [0,W-1], f(0) maps to -1 + 1/W, f(W-1) maps to 1 - 1/w + // linear function x*S +B, 1st constraint means B is (-1+1/W), plug into + // second and solve for S: S = 2*(1-1/W)/(W-1). The old code that did + // this was incorrect - but only for computing the differential solid + // angle, where the final value was 1.0 instead of 1-1/w... + + float fB = -1.0f + 1.0f / fSize; + float fS = (desc.Width > 1) ? (2.0f*(1.0f - 1.0f / fSize) / (fSize - 1.0f)) : 0.f; + + // clear out accumulation variables + float fWt = 0.0f; + + if (resultR) + memset(resultR, 0, sizeof(float)*order*order); + if (resultG) + memset(resultG, 0, sizeof(float)*order*order); + if (resultB) + memset(resultB, 0, sizeof(float)*order*order); + + float shBuff[XM_SH_MAXORDER*XM_SH_MAXORDER] = {}; + float shBuffB[XM_SH_MAXORDER*XM_SH_MAXORDER] = {}; + + //--- Process each face of the cubemap + for (UINT face = 0; face < 6; ++face) + { + if (!cubeMap[face].pData) + return E_POINTER; + + const uint8_t *pSrc = reinterpret_cast(cubeMap[face].pData); + for (UINT y = 0; y < desc.Height; ++y) + { + XMVECTOR* ptr = scanline.get(); + if (!LoadScanline(ptr, static_cast(desc.Width), pSrc, static_cast(cubeMap[face].RowPitch), desc.Format)) + { + return E_FAIL; + } + + const float v = float(y) * fS + fB; + + XMVECTOR* pixel = ptr; + for (UINT x = 0; x < desc.Width; ++x, ++pixel) + { + const float u = float(x) * fS + fB; + + float ix, iy, iz; + switch (face) + { + case 0: // Positive X + iz = 1.0f - (2.0f * float(x) + 1.0f) * fPicSize; + iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + ix = 1.0f; + break; + + case 1: // Negative X + iz = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize; + iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + ix = -1; + break; + + case 2: // Positive Y + iz = -1.0f + (2.0f * float(y) + 1.0f) * fPicSize; + iy = 1.0f; + ix = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize; + break; + + case 3: // Negative Y + iz = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + iy = -1.0f; + ix = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize; + break; + + case 4: // Positive Z + iz = 1.0f; + iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + ix = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize; + break; + + case 5: // Negative Z + iz = -1.0f; + iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize; + ix = 1.0f - (2.0f * float(x) + 1.0f) * fPicSize; + break; + + default: + ix = iy = iz = 0.f; + assert(false); + break; + } + + XMVECTOR dir = XMVectorSet(ix, iy, iz, 0); + dir = XMVector3Normalize(dir); + + const float fDiffSolid = 4.0f / ((1.0f + u * u + v * v)*sqrtf(1.0f + u * u + v * v)); + fWt += fDiffSolid; + + XMSHEvalDirection(shBuff, order, dir); + + XMFLOAT3A clr; + XMStoreFloat3A(&clr, *pixel); + + if (resultR) XMSHAdd(resultR, order, resultR, XMSHScale(shBuffB, order, shBuff, clr.x*fDiffSolid)); + if (resultG) XMSHAdd(resultG, order, resultG, XMSHScale(shBuffB, order, shBuff, clr.y*fDiffSolid)); + if (resultB) XMSHAdd(resultB, order, resultB, XMSHScale(shBuffB, order, shBuff, clr.z*fDiffSolid)); + } + + pSrc += cubeMap[face].RowPitch; + } + } + + const float fNormProj = (4.0f*XM_PI) / fWt; + + if (resultR) XMSHScale(resultR, order, resultR, fNormProj); + if (resultG) XMSHScale(resultG, order, resultG, fNormProj); + if (resultB) XMSHScale(resultB, order, resultB, fNormProj); + + return S_OK; +} diff --git a/Extern/dxmath/Stereo3D/Stereo3DMatrixHelper.cpp b/Extern/dxmath/Stereo3D/Stereo3DMatrixHelper.cpp new file mode 100644 index 000000000..a03bc8239 --- /dev/null +++ b/Extern/dxmath/Stereo3D/Stereo3DMatrixHelper.cpp @@ -0,0 +1,257 @@ +//------------------------------------------------------------------------------------- +// Stereo3DMatrixHelper.cpp -- SIMD C++ Math helper for Stereo 3D matricies +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//------------------------------------------------------------------------------------- + +#include "Stereo3DMatrixHelper.h" + +using namespace DirectX; + +namespace +{ + inline bool StereoProjectionHelper + ( + const STEREO_PARAMETERS& stereoParameters, + _Out_ float* fVirtualProjection, + _Out_ float* zNearWidth, + _Out_ float* zNearHeight, + float FovAngleY, + float AspectRatio, + float NearZ + ) + { + // note that most people have difficulty fusing images into 3D + // if the separation equals even just the human average. by + // reducing the separation (interocular distance) by 1/2, we + // guarantee a larger subset of people will see full 3D + + // the conservative setting should always be used. the only problem + // with the conservative setting is that the 3D effect will be less + // impressive on smaller screens (which makes sense, since your eye + // cannot be tricked as easily based on the smaller fov). to simulate + // the effect of a larger screen, use the liberal settings (debug only) + + // Conservative Settings: * max acuity angle: 0.8f degrees * interoc distance: 1.25 inches + + // Liberal Settings: * max acuity angle: 1.6f degrees * interoc distance: 2.5f inches + + // maximum visual accuity angle allowed is 3.2 degrees for + // a physical scene, and 1.6 degrees for a virtual one. + // thus we cannot allow an object to appear any closer to + // the viewer than 1.6 degrees (divided by two for most + // half-angle calculations) + + static const float fMaxStereoDistance = 780; // inches (should be between 10 and 20m) + static const float fMaxVisualAcuityAngle = 1.6f * (XM_PI / 180.0f); // radians + static const float fInterocularDistance = 1.25f; // inches + + float fDisplayHeight = stereoParameters.fDisplaySizeInches / sqrtf(AspectRatio * AspectRatio + 1.0f); + float fDisplayWidth = fDisplayHeight * AspectRatio; + float fHalfInterocular = 0.5f * fInterocularDistance * stereoParameters.fStereoExaggerationFactor; + float fHalfPixelWidth = fDisplayWidth / stereoParameters.fPixelResolutionWidth * 0.5f; + float fHalfMaximumAcuityAngle = fMaxVisualAcuityAngle * 0.5f * stereoParameters.fStereoExaggerationFactor; + // float fHalfWidth = fDisplayWidth * 0.5f; + + float fMaxSeparationAcuityAngle = atanf(fHalfInterocular / fMaxStereoDistance); + float fMaxSeparationDistance = fHalfPixelWidth / tanf(fMaxSeparationAcuityAngle); + float fRefinedMaxStereoDistance = fMaxStereoDistance - fMaxSeparationDistance; + float fFovHalfAngle = FovAngleY / 2.0f; + + bool ComfortableResult = true; + if (fRefinedMaxStereoDistance < 0.0f || fMaxSeparationDistance > 0.1f * fMaxStereoDistance) + { + // Pixel resolution is too low to offer a comfortable stereo experience + ComfortableResult = false; + } + + float fRefinedMaxSeparationAcuityAngle = atanf(fHalfInterocular / (fRefinedMaxStereoDistance)); + float fPhysicalZNearDistance = fHalfInterocular / tanf(fHalfMaximumAcuityAngle); + // float fScalingFactor = fHalfMaximumAcuityAngle / atanf(fHalfInterocular / stereoParameters.fViewerDistanceInches); + + float fNearZSeparation = tanf(fRefinedMaxSeparationAcuityAngle) * (fRefinedMaxStereoDistance - fPhysicalZNearDistance); + // float fNearZSeparation2 = fHalfInterocular * (fRefinedMaxStereoDistance - fPhysicalZNearDistance) / fRefinedMaxStereoDistance; + + (*zNearHeight) = cosf(fFovHalfAngle) / sinf(fFovHalfAngle); + (*zNearWidth) = (*zNearHeight) / AspectRatio; + (*fVirtualProjection) = (fNearZSeparation * NearZ * (*zNearWidth * 4.0f)) / (2.0f * NearZ); + + return ComfortableResult; + } +} + +//------------------------------------------------------------------------------ + +void DirectX::StereoCreateDefaultParameters +( + STEREO_PARAMETERS& stereoParameters +) +{ + // Default assumption is 1920x1200 resolution, a 22" LCD monitor, and a 2' viewing distance + stereoParameters.fViewerDistanceInches = 24.0f; + stereoParameters.fPixelResolutionWidth = 1920.0f; + stereoParameters.fPixelResolutionHeight = 1200.0f; + stereoParameters.fDisplaySizeInches = 22.0f; + + stereoParameters.fStereoSeparationFactor = 1.0f; + stereoParameters.fStereoExaggerationFactor = 1.0f; +} + +//------------------------------------------------------------------------------ + +XMMATRIX DirectX::StereoProjectionFovLH +( + _In_opt_ const STEREO_PARAMETERS* pStereoParameters, + STEREO_CHANNEL Channel, + float FovAngleY, + float AspectRatio, + float NearZ, + float FarZ, + STEREO_MODE StereoMode +) +{ + assert(Channel == STEREO_CHANNEL_LEFT || Channel == STEREO_CHANNEL_RIGHT); + assert(StereoMode == STEREO_MODE_NORMAL || StereoMode == STEREO_MODE_INVERTED); + assert(!XMScalarNearEqual(FovAngleY, 0.0f, 0.00001f * 2.0f)); + assert(!XMScalarNearEqual(AspectRatio, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + + STEREO_PARAMETERS DefaultParameters = {}; + if (pStereoParameters == nullptr) + { + StereoCreateDefaultParameters(DefaultParameters); + pStereoParameters = &DefaultParameters; + } + + assert(pStereoParameters->fStereoSeparationFactor >= 0.0f && pStereoParameters->fStereoSeparationFactor <= 1.0f); + assert(pStereoParameters->fStereoExaggerationFactor >= 1.0f && pStereoParameters->fStereoExaggerationFactor <= 2.0f); + + float fVirtualProjection = 0.0f; + float zNearWidth = 0.0f; + float zNearHeight = 0.0f; + StereoProjectionHelper(*pStereoParameters, &fVirtualProjection, &zNearWidth, &zNearHeight, FovAngleY, AspectRatio, NearZ); + + fVirtualProjection *= pStereoParameters->fStereoSeparationFactor; // incorporate developer defined bias + + // + // By applying a translation, we are forcing our cameras to be parallel + // + + float fInvertedAngle = atanf(fVirtualProjection / (2.0f * NearZ)); + + XMMATRIX proj = XMMatrixPerspectiveFovLH(FovAngleY, AspectRatio, NearZ, FarZ); + + XMMATRIX patchedProjection; + if (Channel == STEREO_CHANNEL_LEFT) + { + if (StereoMode > STEREO_MODE_NORMAL) + { + XMMATRIX rots = XMMatrixRotationY(fInvertedAngle); + XMMATRIX trans = XMMatrixTranslation(-fVirtualProjection, 0, 0); + patchedProjection = XMMatrixMultiply(XMMatrixMultiply(rots, trans), proj); + } + else + { + XMMATRIX trans = XMMatrixTranslation(-fVirtualProjection, 0, 0); + patchedProjection = XMMatrixMultiply(trans, proj); + } + } + else + { + if (StereoMode > STEREO_MODE_NORMAL) + { + XMMATRIX rots = XMMatrixRotationY(-fInvertedAngle); + XMMATRIX trans = XMMatrixTranslation(fVirtualProjection, 0, 0); + patchedProjection = XMMatrixMultiply(XMMatrixMultiply(rots, trans), proj); + } + else + { + XMMATRIX trans = XMMatrixTranslation(fVirtualProjection, 0, 0); + patchedProjection = XMMatrixMultiply(trans, proj); + } + } + + return patchedProjection; +} + +//------------------------------------------------------------------------------ + +XMMATRIX DirectX::StereoProjectionFovRH +( + _In_opt_ const STEREO_PARAMETERS* pStereoParameters, + STEREO_CHANNEL Channel, + float FovAngleY, + float AspectRatio, + float NearZ, + float FarZ, + STEREO_MODE StereoMode +) +{ + assert(Channel == STEREO_CHANNEL_LEFT || Channel == STEREO_CHANNEL_RIGHT); + assert(StereoMode == STEREO_MODE_NORMAL || StereoMode == STEREO_MODE_INVERTED); + assert(!XMScalarNearEqual(FovAngleY, 0.0f, 0.00001f * 2.0f)); + assert(!XMScalarNearEqual(AspectRatio, 0.0f, 0.00001f)); + assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); + + STEREO_PARAMETERS DefaultParameters = {}; + if (pStereoParameters == nullptr) + { + StereoCreateDefaultParameters(DefaultParameters); + pStereoParameters = &DefaultParameters; + } + + assert(pStereoParameters->fStereoSeparationFactor >= 0.0f && pStereoParameters->fStereoSeparationFactor <= 1.0f); + assert(pStereoParameters->fStereoExaggerationFactor >= 1.0f && pStereoParameters->fStereoExaggerationFactor <= 2.0f); + + float fVirtualProjection = 0.0f; + float zNearWidth = 0.0f; + float zNearHeight = 0.0f; + StereoProjectionHelper(*pStereoParameters, &fVirtualProjection, &zNearWidth, &zNearHeight, FovAngleY, AspectRatio, NearZ); + + fVirtualProjection *= pStereoParameters->fStereoSeparationFactor; // incorporate developer defined bias + + // + // By applying a translation, we are forcing our cameras to be parallel + // + + float fInvertedAngle = atanf(fVirtualProjection / (2.0f * NearZ)); + + XMMATRIX proj = XMMatrixPerspectiveFovRH(FovAngleY, AspectRatio, NearZ, FarZ); + + // + // By applying a translation, we are forcing our cameras to be parallel + // + + XMMATRIX patchedProjection; + if (Channel == STEREO_CHANNEL_LEFT) + { + if (StereoMode > STEREO_MODE_NORMAL) + { + XMMATRIX rots = XMMatrixRotationY(fInvertedAngle); + XMMATRIX trans = XMMatrixTranslation(-fVirtualProjection, 0, 0); + patchedProjection = XMMatrixMultiply(XMMatrixMultiply(rots, trans), proj); + } + else + { + XMMATRIX trans = XMMatrixTranslation(-fVirtualProjection, 0, 0); + patchedProjection = XMMatrixMultiply(trans, proj); + } + } + else + { + if (StereoMode > STEREO_MODE_NORMAL) + { + XMMATRIX rots = XMMatrixRotationY(-fInvertedAngle); + XMMATRIX trans = XMMatrixTranslation(fVirtualProjection, 0, 0); + patchedProjection = XMMatrixMultiply(XMMatrixMultiply(rots, trans), proj); + } + else + { + XMMATRIX trans = XMMatrixTranslation(fVirtualProjection, 0, 0); + patchedProjection = XMMatrixMultiply(trans, proj); + } + } + + return patchedProjection; +} diff --git a/Extern/dxmath/Stereo3D/Stereo3DMatrixHelper.h b/Extern/dxmath/Stereo3D/Stereo3DMatrixHelper.h new file mode 100644 index 000000000..86a749701 --- /dev/null +++ b/Extern/dxmath/Stereo3D/Stereo3DMatrixHelper.h @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------------- +// Stereo3DMatrixHelper.h -- SIMD C++ Math helper for Stereo 3D matrices +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//------------------------------------------------------------------------------------- + +#pragma once + +#include "DirectXMath.h" + +namespace DirectX +{ + // Enumeration for stereo channels (left and right). + enum STEREO_CHANNEL + { + STEREO_CHANNEL_LEFT = 0, + STEREO_CHANNEL_RIGHT + }; + + // Enumeration for stereo mode (normal or inverted). + enum STEREO_MODE + { + STEREO_MODE_NORMAL = 0, + STEREO_MODE_INVERTED, + }; + + //------------------------------------------------------------------------------ + // + // Stereo calibration settings + // + // * Viewer distance to the display + // * Physical display size + // * Render resolution + // + // The stereo separation factor indicates how much separation is between the left and right + // eyes. 0 is no separation, 1 is full separation. It defaults to 1.0. + // + // The debug stereo exaggeration factor indicates how much to increase the interocular spacing and + // maximum acuity angle from comfortable defaults. For retail builds, this value should always + // be 1.0, but during development, on small screens, this value can be raised to up to 2.0 in + // order to exaggerate the 3D effect. Values over 1.0 may cause discomfort on normal sized + // displays. It defaults to 1.0. + // + struct STEREO_PARAMETERS + { + float fViewerDistanceInches; + float fDisplaySizeInches; + float fPixelResolutionWidth; + float fPixelResolutionHeight; + float fStereoSeparationFactor; + float fStereoExaggerationFactor; + }; + + void StereoCreateDefaultParameters(STEREO_PARAMETERS& stereoParameters); + + XMMATRIX StereoProjectionFovLH(_In_opt_ const STEREO_PARAMETERS* pStereoParameters, + STEREO_CHANNEL Channel, float FovAngleY, float AspectRatio, float NearZ, float FarZ, + STEREO_MODE StereoMode = STEREO_MODE_NORMAL); + + XMMATRIX StereoProjectionFovRH(_In_opt_ const STEREO_PARAMETERS* pStereoParameters, + STEREO_CHANNEL Channel, float FovAngleY, float AspectRatio, float NearZ, float FarZ, + STEREO_MODE StereoMode = STEREO_MODE_NORMAL); +} diff --git a/Meshes/D3D9Box.msh b/Meshes/D3D9Box.msh new file mode 100644 index 000000000..1d57a4b98 --- /dev/null +++ b/Meshes/D3D9Box.msh @@ -0,0 +1,49 @@ +MSHX1 +GROUPS 1 +LABEL Box +MATERIAL 1 // Material.001 +GEOM 24 12 +-1.000000 1.000000 1.000000 0.000000 0.000000 1.000000 +1.000000 -1.000000 1.000000 0.000000 0.000000 1.000000 +1.000000 1.000000 1.000000 0.000000 0.000000 1.000000 +1.000000 -1.000000 1.000000 0.000000 -1.000000 0.000000 +-1.000000 -1.000000 -1.000000 0.000000 -1.000000 0.000000 +1.000000 -1.000000 -1.000000 0.000000 -1.000000 0.000000 +-1.000000 -1.000000 1.000000 -1.000000 0.000000 0.000000 +-1.000000 1.000000 -1.000000 -1.000000 0.000000 0.000000 +-1.000000 -1.000000 -1.000000 -1.000000 0.000000 0.000000 +1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 +-1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 +-1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 +1.000000 1.000000 1.000000 1.000000 0.000000 -0.000000 +1.000000 -1.000000 -1.000000 1.000000 0.000000 -0.000000 +1.000000 1.000000 -1.000000 1.000000 0.000000 -0.000000 +-1.000000 1.000000 1.000000 0.000000 1.000000 -0.000000 +1.000000 1.000000 -1.000000 0.000000 1.000000 -0.000000 +-1.000000 1.000000 -1.000000 0.000000 1.000000 -0.000000 +-1.000000 -1.000000 1.000000 0.000000 -0.000000 1.000000 +-1.000000 -1.000000 1.000000 0.000000 -1.000000 0.000000 +-1.000000 1.000000 1.000000 -1.000000 0.000000 0.000000 +1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 +1.000000 -1.000000 1.000000 1.000000 0.000000 0.000000 +1.000000 1.000000 1.000000 0.000000 1.000000 -0.000000 +0 1 2 +3 4 5 +6 7 8 +9 10 11 +12 13 14 +15 16 17 +0 18 1 +3 19 4 +6 20 7 +9 21 10 +12 22 13 +15 23 16 +MATERIALS 1 +Material.001 +MATERIAL Material.001 +1 1 1 1 +0 0 0 1 +0 0 0 0 1 +0 0 0 1 +TEXTURES 0 diff --git a/Meshes/D3D9Cube.msh b/Meshes/D3D9Cube.msh new file mode 100644 index 000000000..7298a2268 --- /dev/null +++ b/Meshes/D3D9Cube.msh @@ -0,0 +1,27 @@ +MSHX1 +GROUPS 1 +LABEL Cube +MATERIAL 0 +GEOM 8 12 +1.0 -1.0 1.0 -0.5773491859436035 0.5773491859436035 -0.5773491859436035 +1.0 1.0 1.0 -0.5773491859436035 -0.5773491859436035 -0.5773491859436035 +1.0 -1.0 -1.0 -0.5773491859436035 0.5773491859436035 0.5773491859436035 +1.0 1.0 -1.0 -0.5773491859436035 -0.5773491859436035 0.5773491859436035 +-1.0 -1.0 1.0 0.5773491859436035 0.5773491859436035 -0.5773491859436035 +-1.0 1.0 1.0 0.5773491859436035 -0.5773491859436035 -0.5773491859436035 +-1.0 -1.0 -1.0 0.5773491859436035 0.5773491859436035 0.5773491859436035 +-1.0 1.0 -1.0 0.5773491859436035 -0.5773491859436035 0.5773491859436035 +0 3 2 +3 0 1 +2 7 6 +7 2 3 +6 5 4 +5 6 7 +4 1 0 +1 4 5 +2 4 0 +4 2 6 +7 1 5 +1 7 3 +MATERIALS 0 +TEXTURES 0 \ No newline at end of file diff --git a/OVP/CMakeLists.txt b/OVP/CMakeLists.txt index 01d439877..2f76d45bc 100644 --- a/OVP/CMakeLists.txt +++ b/OVP/CMakeLists.txt @@ -6,3 +6,7 @@ add_subdirectory(GDIClient) if(ORBITER_BUILD_D3D9CLIENT) add_subdirectory(D3D9Client) endif() + +if(ORBITER_BUILD_VULKANCLIENT) + add_subdirectory(VulkanClient) +endif() diff --git a/OVP/D3D9Client/AABBUtil.cpp b/OVP/D3D9Client/AABBUtil.cpp index e7531ad71..8528e89ef 100644 --- a/OVP/D3D9Client/AABBUtil.cpp +++ b/OVP/D3D9Client/AABBUtil.cpp @@ -20,10 +20,12 @@ #include "OrbiterAPI.h" #include "VectorHelpers.h" #include "Log.h" +#include "D3D9Client.h" +#include "MathAPI.h" #pragma warning(push) #pragma warning(disable : 4838) -#include +#include #pragma warning(pop) using std::min; @@ -53,16 +55,14 @@ bool SolveLUSystem(int n, double *A, double *b, double *x, double *det) } -D3DXVECTOR3 WorldPickRay(float x, float y, const LPD3DXMATRIX mProj, const LPD3DXMATRIX mView) +FVECTOR3 WorldPickRay(float x, float y, const FMATRIX4* mProj, const FMATRIX4* mView) { - x = float((x*2.0-1.0)/mProj->_11); - y = float((y*2.0-1.0)/mProj->_22); - D3DXVECTOR3 pick(x, 1.0f, y); - D3DXMATRIX mViewI; - D3DXMatrixInverse(&mViewI, NULL, (const D3DXMATRIX *)&mView); - D3DXVec3TransformNormal(&pick, &pick, &mViewI); - D3DXVec3Normalize(&pick, &pick); - return pick; + x = float((x*2.0-1.0)/mProj->m11); + y = float((y*2.0-1.0)/mProj->m22); + XMVECTOR pick = FVECTOR3(x, 1.0f, y).XM(); + XMMATRIX mViewI = XMMatrixInverse(NULL, mView->XM()); + XMVECTOR R = XMVector3Normalize(XMVector3TransformNormal(pick, mViewI)); + return FVECTOR3(R); } @@ -73,35 +73,35 @@ void D9ZeroAABB(D9BBox *box) void D9InitAABB(D9BBox *box) { - box->min = D3DXVECTOR4( 1e12f, 1e12f, 1e12f, 0); - box->max = D3DXVECTOR4(-1e12f, -1e12f, -1e12f, 0); - box->bs = D3DXVECTOR4(0,0,0,0); + box->mn = FVECTOR4( 1e12f, 1e12f, 1e12f, 0.0f); + box->mx = FVECTOR4(-1e12f, -1e12f, -1e12f, 0.0f); + box->bs = FVECTOR4(); } -void D9AddPointAABB(D9BBox *box, LPD3DXVECTOR3 point) +void D9AddPointAABB(D9BBox *box, FVECTOR3* point) { - XMVECTOR q = XMLoadFloat4((const XMFLOAT4*)&box->min); - XMVECTOR w = XMLoadFloat4((const XMFLOAT4*)&box->max); + XMVECTOR q = XMLoadFloat4((const XMFLOAT4*)&box->mn); + XMVECTOR w = XMLoadFloat4((const XMFLOAT4*)&box->mx); XMVECTOR p = XMLoadFloat3((const XMFLOAT3*)point); - XMStoreFloat4((XMFLOAT4*)&box->min, XMVectorMin(q,p)); - XMStoreFloat4((XMFLOAT4*)&box->max, XMVectorMax(w,p)); + XMStoreFloat4((XMFLOAT4*)&box->mn, XMVectorMin(q,p)); + XMStoreFloat4((XMFLOAT4*)&box->mx, XMVectorMax(w,p)); } -D3DXVECTOR4 D9LinearFieldOfView(const D3DXMATRIX *pProj) +FVECTOR4 D9LinearFieldOfView(const FMATRIX4 *pProj) { - float a = 1.0f/pProj->_22; - float s = (pProj->_11/pProj->_22); + float a = 1.0f/pProj->m22; + float s = (pProj->m11/pProj->m22); float l = 1.0f/cos(atan(a)); - return D3DXVECTOR4(l, l/s, a, a/s); + return FVECTOR4(l, l/s, a, a/s); } -float D9NearPlane(LPDIRECT3DDEVICE9 pDev, float znear, float zfar, float dmin, const D3DXMATRIX *pProj, bool bReduced) +float D9NearPlane(LPDIRECT3DDEVICE9 pDev, float znear, float zfar, float dmin, const FMATRIX4 *pProj, bool bReduced) { - float b = 1.0f/pProj->_11; - float a = 1.0f/pProj->_22; + float b = 1.0f/pProj->m11; + float a = 1.0f/pProj->m22; float q = atan(sqrt(a*a+b*b)); D3DVIEWPORT9 vp; pDev->GetViewport(&vp); @@ -124,7 +124,7 @@ float D9NearPlane(LPDIRECT3DDEVICE9 pDev, float znear, float zfar, float dmin, c } -D3DXVECTOR4 D9OffsetRange(double R, double r) +FVECTOR4 D9OffsetRange(double R, double r) { double t = r*0.5; double r2 = r*r; @@ -132,16 +132,14 @@ D3DXVECTOR4 D9OffsetRange(double R, double r) double h2 = sqrt(R*R+t*t)-R; double a = (h2 - h1*0.0625)/(0.1875*r2); double b = -(h2 - h1*0.25)/(0.1875*r2*r2); - return D3DXVECTOR4(float(a), float(b), 1.0f/float(r2), 0.0f); + return FVECTOR4(float(a), float(b), 1.0f/float(r2), 0.0f); } -bool D9IsAABBVisible(const D9BBox *in, const D3DXMATRIX *pWV, const D3DXVECTOR4 *F) +bool D9IsAABBVisible(const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F) { - D3DXVECTOR3 bv; - - D3DXVec3TransformCoord(&bv, (LPD3DXVECTOR3)&in->bs, pWV); + FVECTOR3 bv = oapiTransformCoord(&in->bs.xyz, pWV); float w = in->bs.w; if (bv.z<-w) return false; // Not visible @@ -152,15 +150,14 @@ bool D9IsAABBVisible(const D9BBox *in, const D3DXMATRIX *pWV, const D3DXVECTOR4 if (fabs(bv.y)-(F->x*w) > zz*F->z) return false; // Not visible if (fabs(bv.x)-(F->y*w) > zz*F->w) return false; // Not visible - D3DXVECTOR4 size = (in->max - in->min)*0.5f; - D3DXVECTOR3 xv,yv,zv; - D3DXVec3TransformNormal(&xv, (LPD3DXVECTOR3)&in->a, pWV); - D3DXVec3TransformNormal(&yv, (LPD3DXVECTOR3)&in->b, pWV); - D3DXVec3TransformNormal(&zv, (LPD3DXVECTOR3)&in->c, pWV); - - float dx = D3DXVec3Dot(&xv, &bv); - float dy = D3DXVec3Dot(&yv, &bv); - float dz = D3DXVec3Dot(&zv, &bv); + FVECTOR4 size = (in->mx - in->mn)*0.5f; + FVECTOR3 xv = oapiTransformNormal(&in->a.xyz, pWV); + FVECTOR3 yv = oapiTransformNormal(&in->b.xyz, pWV); + FVECTOR3 zv = oapiTransformNormal(&in->c.xyz, pWV); + + float dx = dotp(xv, bv); + float dy = dotp(yv, bv); + float dz = dotp(zv, bv); float adx = fabs(dx) - size.x; float ady = fabs(dy) - size.y; @@ -185,10 +182,10 @@ bool D9IsAABBVisible(const D9BBox *in, const D3DXMATRIX *pWV, const D3DXVECTOR4 } -bool D9IsBSVisible(const D9BBox *in, const D3DXMATRIX *pWV, const D3DXVECTOR4 *F) +bool D9IsBSVisible(const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F) { - D3DXVECTOR3 bv = D3DXVECTOR3(in->bs.x, in->bs.y, in->bs.z); - D3DXVec3TransformCoord(&bv, &bv, pWV); + FVECTOR3 bv = FVECTOR3(in->bs.x, in->bs.y, in->bs.z); + bv = oapiTransformCoord(&bv, pWV); float r = in->bs.w; if (bv.z<-r) return false; // Not visible @@ -203,11 +200,11 @@ bool D9IsBSVisible(const D9BBox *in, const D3DXMATRIX *pWV, const D3DXVECTOR4 *F return true; } -int D9ComputeMinMaxDistance(LPDIRECT3DDEVICE9 pDev, const D9BBox *in, const D3DXMATRIX *pWV, const D3DXVECTOR4 *F, float *zmin, float *zmax, float *dst) +int D9ComputeMinMaxDistance(LPDIRECT3DDEVICE9 pDev, const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F, float *zmin, float *zmax, float *dst) { - D3DXVECTOR3 bv = D3DXVECTOR3(in->bs.x, in->bs.y, in->bs.z); - D3DXVec3TransformCoord(&bv, &bv, pWV); + FVECTOR3 bv = FVECTOR3(in->bs.x, in->bs.y, in->bs.z); + bv = oapiTransformCoord(&bv, pWV); float r = in->bs.w; if (bv.z<-r) return -1; // Not visible @@ -218,16 +215,14 @@ int D9ComputeMinMaxDistance(LPDIRECT3DDEVICE9 pDev, const D9BBox *in, const D3DX if (fabs(bv.y)-(F->x*r) > zz*F->z) return -3; // Not visible if (fabs(bv.x)-(F->y*r) > zz*F->w) return -4; // Not visible - - D3DXVECTOR4 size = (in->max - in->min)*0.5; - D3DXVECTOR3 xv,yv,zv; - D3DXVec3TransformNormal(&xv, (LPD3DXVECTOR3)&in->a, pWV); - D3DXVec3TransformNormal(&yv, (LPD3DXVECTOR3)&in->b, pWV); - D3DXVec3TransformNormal(&zv, (LPD3DXVECTOR3)&in->c, pWV); - - float dx = D3DXVec3Dot(&xv, &bv); - float dy = D3DXVec3Dot(&yv, &bv); - float dz = D3DXVec3Dot(&zv, &bv); + FVECTOR4 size = (in->mx - in->mn)*0.5; + FVECTOR3 xv = oapiTransformNormal(&in->a.xyz, pWV); + FVECTOR3 yv = oapiTransformNormal(&in->b.xyz, pWV); + FVECTOR3 zv = oapiTransformNormal(&in->c.xyz, pWV); + + float dx = dotp(xv, bv); + float dy = dotp(yv, bv); + float dz = dotp(zv, bv); float adx = fabs(dx) - size.x; float ady = fabs(dy) - size.y; @@ -293,14 +288,14 @@ int D9ComputeMinMaxDistance(LPDIRECT3DDEVICE9 pDev, const D9BBox *in, const D3DX } -void D9UpdateAABB(D9BBox *box, const D3DXMATRIX *pFirst, const D3DXMATRIX *pSecond) +void D9UpdateAABB(D9BBox *box, const FMATRIX4 *pFirst, const FMATRIX4 *pSecond) { XMVECTOR x = XMVectorSet(1, 0, 0, 0); XMVECTOR y = XMVectorSet(0, 1, 0, 0); XMVECTOR z = XMVectorSet(0, 0, 1, 0); - XMVECTOR q = XMLoadFloat4((const XMFLOAT4*)&box->min); - XMVECTOR w = XMLoadFloat4((const XMFLOAT4*)&box->max); + XMVECTOR q = XMLoadFloat4((const XMFLOAT4*)&box->mn); + XMVECTOR w = XMLoadFloat4((const XMFLOAT4*)&box->mx); if (pFirst) { XMMATRIX MF = XMLoadFloat4x4((const XMFLOAT4X4*)pFirst); @@ -334,7 +329,7 @@ void D9UpdateAABB(D9BBox *box, const D3DXMATRIX *pFirst, const D3DXMATRIX *pSeco -void D9AddAABB(const D9BBox *in, const D3DXMATRIX *pM, D9BBox *out, bool bReset) +void D9AddAABB(const D9BBox *in, const FMATRIX4 *pM, D9BBox *out, bool bReset) { XMVECTOR x,mi,mx; @@ -344,12 +339,12 @@ void D9AddAABB(const D9BBox *in, const D3DXMATRIX *pM, D9BBox *out, bool bReset) mx = XMVectorSet(-1e12f, -1e12f, -1e12f, 0); } else { - mi = XMLoadFloat4((const XMFLOAT4*)&out->min); - mx = XMLoadFloat4((const XMFLOAT4*)&out->max); + mi = XMLoadFloat4((const XMFLOAT4*)&out->mn); + mx = XMLoadFloat4((const XMFLOAT4*)&out->mx); } - XMVECTOR q = XMLoadFloat4((const XMFLOAT4*)&in->min); - XMVECTOR w = XMLoadFloat4((const XMFLOAT4*)&in->max); + XMVECTOR q = XMLoadFloat4((const XMFLOAT4*)&in->mn); + XMVECTOR w = XMLoadFloat4((const XMFLOAT4*)&in->mx); q = XMVectorSetW(q, 0); w = XMVectorSetW(w, 0); @@ -381,41 +376,41 @@ void D9AddAABB(const D9BBox *in, const D3DXMATRIX *pM, D9BBox *out, bool bReset) mx = XMVectorMax(mx, XMVectorMax(q,w)); } - XMStoreFloat4((XMFLOAT4*)&out->min, mi); - XMStoreFloat4((XMFLOAT4*)&out->max, mx); + XMStoreFloat4((XMFLOAT4*)&out->mn, mi); + XMStoreFloat4((XMFLOAT4*)&out->mx, mx); } -void EnvMapDirection(int dir, D3DXVECTOR3 *Dir, D3DXVECTOR3 *Up) +void EnvMapDirection(int dir, FVECTOR3 *Dir, FVECTOR3 *Up) { switch (dir) { case 0: - *Dir = D3DXVECTOR3(1.0f, 0.0f, 0.0f); - *Up = D3DXVECTOR3(0.0f, 1.0f, 0.0f); + *Dir = FVECTOR3(1.0f, 0.0f, 0.0f); + *Up = FVECTOR3(0.0f, 1.0f, 0.0f); break; case 1: - *Dir = D3DXVECTOR3(-1.0f, 0.0f, 0.0f); - *Up = D3DXVECTOR3( 0.0f, 1.0f, 0.0f); + *Dir = FVECTOR3(-1.0f, 0.0f, 0.0f); + *Up = FVECTOR3( 0.0f, 1.0f, 0.0f); break; case 2: - *Dir = D3DXVECTOR3(0.0f, 1.0f, 0.0f); - *Up = D3DXVECTOR3(0.0f, 0.0f, -1.0f); + *Dir = FVECTOR3(0.0f, 1.0f, 0.0f); + *Up = FVECTOR3(0.0f, 0.0f, -1.0f); break; case 3: - *Dir = D3DXVECTOR3(0.0f, -1.0f, 0.0f); - *Up = D3DXVECTOR3(0.0f, 0.0f, 1.0f); + *Dir = FVECTOR3(0.0f, -1.0f, 0.0f); + *Up = FVECTOR3(0.0f, 0.0f, 1.0f); break; case 4: - *Dir = D3DXVECTOR3(0.0f, 0.0f, 1.0f); - *Up = D3DXVECTOR3(0.0f, 1.0f, 0.0f); + *Dir = FVECTOR3(0.0f, 0.0f, 1.0f); + *Up = FVECTOR3(0.0f, 1.0f, 0.0f); break; case 5: - *Dir = D3DXVECTOR3(0.0f, 0.0f, -1.0f); - *Up = D3DXVECTOR3(0.0f, 1.0f, 0.0f); + *Dir = FVECTOR3(0.0f, 0.0f, -1.0f); + *Up = FVECTOR3(0.0f, 1.0f, 0.0f); break; default: - *Dir = D3DXVECTOR3(0.0f, 0.0f, 0.0f); - *Up = D3DXVECTOR3(0.0f, 0.0f, 0.0f); + *Dir = FVECTOR3(0.0f, 0.0f, 0.0f); + *Up = FVECTOR3(0.0f, 0.0f, 0.0f); break; } } diff --git a/OVP/D3D9Client/AABBUtil.h b/OVP/D3D9Client/AABBUtil.h index 9d1a792d1..74796c631 100644 --- a/OVP/D3D9Client/AABBUtil.h +++ b/OVP/D3D9Client/AABBUtil.h @@ -18,7 +18,7 @@ #include -#include +#include "MathAPI.h" #ifndef __D3D9TK_H #define __D3D9TK_H @@ -26,23 +26,24 @@ #define SctPwr 1.0 #define SctPwr2 2.0 +using namespace oapi; -typedef struct { - D3DXVECTOR4 min, max, bs, a, b, c; -} D9BBox; +struct D9BBox { + FVECTOR4 mn, mx, bs, a, b, c; +}; -float D9NearPlane(LPDIRECT3DDEVICE9 pDev, float zmin, float zmax, float dmax, const D3DXMATRIX *pProj, bool bReduced); -int D9ComputeMinMaxDistance(LPDIRECT3DDEVICE9 pDev, const D9BBox *in, const D3DXMATRIX *pWV, const D3DXVECTOR4 *F, float *zmin, float *zmax, float *dmin); -void D9AddAABB(const D9BBox *in, const D3DXMATRIX *pM, D9BBox *out, bool bReset=false); -void D9UpdateAABB(D9BBox *box, const D3DXMATRIX *pFisrt=NULL, const D3DXMATRIX *pSecond=NULL); +float D9NearPlane(LPDIRECT3DDEVICE9 pDev, float zmin, float zmax, float dmax, const FMATRIX4 *pProj, bool bReduced); +int D9ComputeMinMaxDistance(LPDIRECT3DDEVICE9 pDev, const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F, float *zmin, float *zmax, float *dmin); +void D9AddAABB(const D9BBox *in, const FMATRIX4 *pM, D9BBox *out, bool bReset=false); +void D9UpdateAABB(D9BBox *box, const FMATRIX4 *pFisrt=NULL, const FMATRIX4 *pSecond=NULL); void D9ZeroAABB(D9BBox *box); void D9InitAABB(D9BBox *box); -void D9AddPointAABB(D9BBox *box, LPD3DXVECTOR3 point); -bool D9IsAABBVisible(const D9BBox *in, const D3DXMATRIX *pWV, const D3DXVECTOR4 *F); -bool D9IsBSVisible(const D9BBox *in, const D3DXMATRIX *pWV, const D3DXVECTOR4 *F); -D3DXVECTOR4 D9LinearFieldOfView(const D3DXMATRIX *pProj); -D3DXVECTOR4 D9OffsetRange(double R, double r); -void EnvMapDirection(int dir, D3DXVECTOR3 *Dir, D3DXVECTOR3 *Up); -D3DXVECTOR3 WorldPickRay(float x, float y, const LPD3DXMATRIX mProj, const LPD3DXMATRIX mView); +void D9AddPointAABB(D9BBox *box, FVECTOR3* point); +bool D9IsAABBVisible(const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F); +bool D9IsBSVisible(const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F); +FVECTOR4 D9LinearFieldOfView(const FMATRIX4 *pProj); +FVECTOR4 D9OffsetRange(double R, double r); +void EnvMapDirection(int dir, FVECTOR3 *Dir, FVECTOR3 *Up); +FVECTOR3 WorldPickRay(float x, float y, const FMATRIX4* mProj, const FMATRIX4* mView); bool SolveLUSystem(int n, double *A, double *b, double *x, double *det=NULL); #endif diff --git a/OVP/D3D9Client/AtmoControls.cpp b/OVP/D3D9Client/AtmoControls.cpp index 6763911d6..5e1cd0b56 100644 --- a/OVP/D3D9Client/AtmoControls.cpp +++ b/OVP/D3D9Client/AtmoControls.cpp @@ -448,7 +448,7 @@ void SetVisual(vObject *vo) if (!hDlg || !dwCmd) return; - OBJHANDLE hObj = vo->GetObjectA(); + OBJHANDLE hObj = vo->GetObjHandle(); if (oapiGetObjectType(hObj)!=OBJTP_PLANET) { LogErr("Invalid Object Type in AtmoControls"); diff --git a/OVP/D3D9Client/BeaconArray.cpp b/OVP/D3D9Client/BeaconArray.cpp index 3c8092f1d..af13d8380 100644 --- a/OVP/D3D9Client/BeaconArray.cpp +++ b/OVP/D3D9Client/BeaconArray.cpp @@ -27,7 +27,7 @@ BeaconArray::BeaconArray(BeaconArrayEntry *pEnt, DWORD nEntry, vBase *_vB) { _TRACE; - if (vB) hBase = vB->GetObject(); + if (vB) hBase = vB->GetObjHandle(); pBeaconPos = new BeaconPos[nEntry]; @@ -41,8 +41,8 @@ BeaconArray::BeaconArray(BeaconArrayEntry *pEnt, DWORD nEntry, vBase *_vB) if (vB) pBeaconPos[i].vLoc = unit(vB->ToLocal(pEnt[i].pos, &pBeaconPos[i].lng, &pBeaconPos[i].lat)); - pVrt[i].pos = D3DXVEC(pEnt[i].pos); - pVrt[i].dir = D3DXVEC(pEnt[i].dir); + pVrt[i].pos = _F(pEnt[i].pos); + pVrt[i].dir = _F(pEnt[i].dir); pVrt[i].color = pEnt[i].color; pVrt[i].size = pEnt[i].size; @@ -118,13 +118,13 @@ void BeaconArray::UnLockVertexBuffer() // =========================================================================================== // -void BeaconArray::Render(LPDIRECT3DDEVICE9 dev, const LPD3DXMATRIX pW, float time) +void BeaconArray::Render(LPDIRECT3DDEVICE9 dev, const FMATRIX4* pW, float time) { if (!pVB) return; UINT numPasses = 0; HR(FX->SetTechnique(eBeaconArrayTech)); - HR(FX->SetMatrix(eW, pW)); + HR(FX->SetMatrix(eW, _DX(pW))); HR(FX->SetTexture(eTex0, SURFACE(pBright)->GetTexture())); HR(FX->SetFloat(eTime, time)); HR(FX->SetFloat(eMix, float(Config->RwyBrightness))); diff --git a/OVP/D3D9Client/BeaconArray.h b/OVP/D3D9Client/BeaconArray.h index 4dd534f94..6d5c698a7 100644 --- a/OVP/D3D9Client/BeaconArray.h +++ b/OVP/D3D9Client/BeaconArray.h @@ -11,7 +11,7 @@ #include "D3D9Client.h" #include "D3D9Effect.h" #include -#include +#include "MathAPI.h" /** @@ -63,7 +63,7 @@ class BeaconArray : private D3D9Effect * \param pW 3DX matrix to operate on * \param time Seconds-only part of the simulation elapsed time (0...1.0) */ - void Render(LPDIRECT3DDEVICE9 dev, const LPD3DXMATRIX pW, float time=0.5f); + void Render(LPDIRECT3DDEVICE9 dev, const FMATRIX4* pW, float time=0.5f); void Update(DWORD nCount, class vPlanet *vP); diff --git a/OVP/D3D9Client/CMakeLists.txt b/OVP/D3D9Client/CMakeLists.txt index ddbaf2e1c..464897381 100644 --- a/OVP/D3D9Client/CMakeLists.txt +++ b/OVP/D3D9Client/CMakeLists.txt @@ -124,6 +124,12 @@ set(IncludeFiles set(APIHeaders ${INCLUDE_TARGET_DIR}/gcGUI.h ${INCLUDE_TARGET_DIR}/gcCoreAPI.h + ${CMAKE_SOURCE_DIR}/Orbitersdk/include/MathAPI.h + ${CMAKE_SOURCE_DIR}/Orbitersdk/include/DrawAPI.h + ${CMAKE_SOURCE_DIR}/Orbitersdk/include/OrbiterAPI.h + ${CMAKE_SOURCE_DIR}/Orbitersdk/include/GraphicsAPI.h + ${EXTERN_DIR}/dxmath/Inc/DirectXMath.h + ${EXTERN_DIR}/dxmath/Inc/DirectXCollision.h ) set(ShaderFiles @@ -142,6 +148,8 @@ set(ShaderFiles ${ShaderDir}/NewPlanet.hlsl ${ShaderDir}/Scatter.hlsl ${ShaderDir}/Glare.hlsl + ${ShaderDir}/PreBakeLights.hlsl + ${ShaderDir}/Custom.hlsl ${ShaderDir}/Mesh.fx ${ShaderDir}/Metalness.fx ${ShaderDir}/Particle.fx @@ -150,6 +158,7 @@ set(ShaderFiles ${ShaderDir}/SceneTech.fx ${ShaderDir}/Sketchpad.fx ${ShaderDir}/Vessel.fx + ${ShaderDir}/BakedVC.fx ) source_group(APIHeaders FILES ${APIHeaders}) @@ -171,6 +180,7 @@ add_library(D3D9Client MODULE target_include_directories(D3D9Client PUBLIC ${ORBITER_SOURCE_SDK_INCLUDE_DIR} ${DXSDK_DIR}/Include + ${EXTERN_DIR}/dxmath/Inc ) target_link_directories(D3D9Client PUBLIC diff --git a/OVP/D3D9Client/CSphereMgr.cpp b/OVP/D3D9Client/CSphereMgr.cpp index ee77b9fa7..56500ae99 100644 --- a/OVP/D3D9Client/CSphereMgr.cpp +++ b/OVP/D3D9Client/CSphereMgr.cpp @@ -118,16 +118,16 @@ CSphereManager::CSphereManager(D3D9Client *gc, const Scene *scene) : gc(gc), tex ecl2gal = mul (_M(1,0,0, 0,cost,sint, 0,-sint,cost), ecl2gal); ecl2gal = mul (_M(cosl,0,sinl, 0,1,0, -sinl,0,cosl), ecl2gal); - D3DMAT_Identity (&trans); - trans._11 = float(ecl2gal.m11); - trans._12 = float(ecl2gal.m12); - trans._13 = float(ecl2gal.m13); - trans._21 = float(ecl2gal.m21); - trans._22 = float(ecl2gal.m22); - trans._23 = float(ecl2gal.m23); - trans._31 = float(ecl2gal.m31); - trans._32 = float(ecl2gal.m32); - trans._33 = float(ecl2gal.m33); + oapiMatrixIdentity (&trans); + trans.m11 = float(ecl2gal.m11); + trans.m12 = float(ecl2gal.m12); + trans.m13 = float(ecl2gal.m13); + trans.m21 = float(ecl2gal.m21); + trans.m22 = float(ecl2gal.m22); + trans.m23 = float(ecl2gal.m23); + trans.m31 = float(ecl2gal.m31); + trans.m32 = float(ecl2gal.m32); + trans.m33 = float(ecl2gal.m33); LogAlw("CSphere Manager constructed"); } @@ -443,7 +443,7 @@ void CSphereManager::Render (LPDIRECT3DDEVICE9 dev, int level, double bglvl) for (hemisp = idx = 0; hemisp < 2; hemisp++) { if (hemisp) { // flip world transformation to southern hemisphere - D3DXMatrixMultiply(&RenderParam.wmat, &TileManager::Rsouth, &RenderParam.wmat); + oapiMatrixMultiply(&RenderParam.wmat, &TileManager::Rsouth, &RenderParam.wmat); } for (ilat = nlat-1; ilat >= 0; ilat--) { for (ilng = 0; ilng < nlng[ilat]; ilng++) { @@ -487,11 +487,11 @@ void CSphereManager::ProcessTile (int lvl, int hemisp, int ilat, int nlat, int i void CSphereManager::SetWorldMatrix (int ilng, int nlng, int ilat, int nlat) { // set up world transformation matrix - D3DXMATRIX rtile; + FMATRIX4 rtile; double lng = PI2 * (double)ilng/(double)nlng + PI; // add pi so texture wraps at +-180° D3DMAT_RotY (&rtile, lng); - D3DXMatrixMultiply(&mWorld, &rtile, &RenderParam.wmat); + oapiMatrixMultiply(&mWorld, &rtile, &RenderParam.wmat); } // ======================================================================= @@ -547,8 +547,7 @@ void CSphereManager::TileExtents (int hemisp, int ilat, int nlat, int ilng, int bool CSphereManager::TileInView (int lvl, int ilat) { VBMESH &mesh = PATCH_TPL[lvl][ilat]; - D3DXVECTOR3 vP; - D3DXVec3TransformCoord(&vP, &mesh.bsCnt, &mWorld); + FVECTOR3 vP = oapiTransformCoord(&mesh.bsCnt, &mWorld); return gc->GetScene()->IsVisibleInCamera(&vP, mesh.bsRad); } diff --git a/OVP/D3D9Client/CSphereMgr.h b/OVP/D3D9Client/CSphereMgr.h index 0f8be2e62..0d2ea0187 100644 --- a/OVP/D3D9Client/CSphereMgr.h +++ b/OVP/D3D9Client/CSphereMgr.h @@ -136,14 +136,14 @@ class CSphereManager std::vector m_starbuf; // texture buffer for starfield textures (level <= 8) bool bPreloadTile; // preload high-resolution tile textures MATRIX3 ecl2gal; // rotates from ecliptic to galactic frame - D3DXMATRIX trans; // transformation from ecliptic to galactic frame - D3DXMATRIX mWorld; + FMATRIX4 trans; // transformation from ecliptic to galactic frame + FMATRIX4 mWorld; TileBuffer *tilebuf; struct RENDERPARAM { LPDIRECT3DDEVICE9 dev; // render device int tgtlvl; // target resolution level - D3DXMATRIX wmat; // world matrix + FMATRIX4 wmat; // world matrix VECTOR3 camdir; // camera direction in galactic frame double viewap; // viewport aperture (semi-diagonal) } RenderParam; diff --git a/OVP/D3D9Client/CelSphere.cpp b/OVP/D3D9Client/CelSphere.cpp index 805feb1c0..f7dbcabc7 100644 --- a/OVP/D3D9Client/CelSphere.cpp +++ b/OVP/D3D9Client/CelSphere.cpp @@ -87,17 +87,17 @@ void D3D9CelestialSphere::InitCelestialTransform() { m_rotCelestial = Ecliptic_CelestialAtEpoch(); - m_transformCelestial._11 = (float)m_rotCelestial.m11; m_transformCelestial._12 = (float)m_rotCelestial.m12; m_transformCelestial._13 = (float)m_rotCelestial.m13; m_transformCelestial._14 = 0.0f; - m_transformCelestial._21 = (float)m_rotCelestial.m21; m_transformCelestial._22 = (float)m_rotCelestial.m22; m_transformCelestial._23 = (float)m_rotCelestial.m23; m_transformCelestial._24 = 0.0f; - m_transformCelestial._31 = (float)m_rotCelestial.m31; m_transformCelestial._32 = (float)m_rotCelestial.m32; m_transformCelestial._33 = (float)m_rotCelestial.m33; m_transformCelestial._34 = 0.0f; - m_transformCelestial._41 = 0.0f; m_transformCelestial._42 = 0.0f; m_transformCelestial._43 = 0.0f; m_transformCelestial._44 = 1.0f; + m_transformCelestial.m11 = (float)m_rotCelestial.m11; m_transformCelestial.m12 = (float)m_rotCelestial.m12; m_transformCelestial.m13 = (float)m_rotCelestial.m13; m_transformCelestial.m14 = 0.0f; + m_transformCelestial.m21 = (float)m_rotCelestial.m21; m_transformCelestial.m22 = (float)m_rotCelestial.m22; m_transformCelestial.m23 = (float)m_rotCelestial.m23; m_transformCelestial.m24 = 0.0f; + m_transformCelestial.m31 = (float)m_rotCelestial.m31; m_transformCelestial.m32 = (float)m_rotCelestial.m32; m_transformCelestial.m33 = (float)m_rotCelestial.m33; m_transformCelestial.m34 = 0.0f; + m_transformCelestial.m41 = 0.0f; m_transformCelestial.m42 = 0.0f; m_transformCelestial.m43 = 0.0f; m_transformCelestial.m44 = 1.0f; m_mjdPrecessionChecked = oapiGetSimMJD(); } // ============================================================== -bool D3D9CelestialSphere::LocalHorizonTransform(MATRIX3& R, D3DXMATRIX& T) +bool D3D9CelestialSphere::LocalHorizonTransform(MATRIX3& R, FMATRIX4& T) { MATRIX3 rot; if (LocalHorizon_Ecliptic(rot)) { @@ -141,7 +141,7 @@ void D3D9CelestialSphere::InitStars () v.x = (float)rec.pos.x; v.y = (float)rec.pos.y; v.z = (float)rec.pos.z; - v.col = D3DXCOLOR(rec.col.x, rec.col.y, rec.col.z, 1); + v.col = FVECTOR4(rec.col.x, rec.col.y, rec.col.z, 1.0).dword_abgr(); idx++; } (*it)->Unlock(); @@ -327,17 +327,17 @@ void D3D9CelestialSphere::Render(LPDIRECT3DDEVICE9 pDevice, const VECTOR3& skyCo if (renderFlag & PLN_ENABLE) { HR(s_FX->SetTechnique(s_eLine)); - HR(s_FX->SetMatrix(s_eWVP, m_scene->GetProjectionViewMatrix())); + HR(s_FX->SetMatrix(s_eWVP, _DX(m_scene->GetProjectionViewMatrix()))); // render ecliptic grid if (renderFlag & PLN_EGRID) { FVECTOR4 baseCol1(0.0f, 0.2f, 0.3f, 1.0f); - D3DXVECTOR4 vColor1 = ColorAdjusted(baseCol1); - HR(s_FX->SetVector(s_eColor, &vColor1)); + FVECTOR4 vColor1 = ColorAdjusted(baseCol1); + HR(s_FX->SetVector(s_eColor, _DX(vColor1))); RenderGrid(s_FX, false); FVECTOR4 baseCol2(0.0f, 0.4f, 0.6f, 1.0f); - D3DXVECTOR4 vColor2 = ColorAdjusted(baseCol2); - HR(s_FX->SetVector(s_eColor, &vColor2)); + FVECTOR4 vColor2 = ColorAdjusted(baseCol2); + HR(s_FX->SetVector(s_eColor, _DX(vColor2))); RenderGreatCircle(s_FX); MATRIX3 ident = _M(1, 0, 0, 0, 1, 0, 0, 0, 1); double dphi = ElevationScaleRotation(ident); @@ -347,20 +347,20 @@ void D3D9CelestialSphere::Render(LPDIRECT3DDEVICE9 pDevice, const VECTOR3& skyCo // render galactic grid if (renderFlag & PLN_GGRID) { static const MATRIX3& R = Ecliptic_Galactic(); - static D3DXMATRIX T = { (float)R.m11, (float)R.m12, (float)R.m13, 0.0f, + static FMATRIX4 T = { (float)R.m11, (float)R.m12, (float)R.m13, 0.0f, (float)R.m21, (float)R.m22, (float)R.m23, 0.0f, (float)R.m31, (float)R.m32, (float)R.m33, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; - D3DXMATRIX rot; - D3DXMatrixMultiply(&rot, &T, m_scene->GetProjectionViewMatrix()); - HR(s_FX->SetMatrix(s_eWVP, &rot)); + FMATRIX4 rot; + oapiMatrixMultiply(&rot, &T, m_scene->GetProjectionViewMatrix()); + HR(s_FX->SetMatrix(s_eWVP, _DX(rot))); FVECTOR4 baseCol1(0.3f, 0.0f, 0.0f, 1.0f); - D3DXVECTOR4 vColor1 = ColorAdjusted(baseCol1); - HR(s_FX->SetVector(s_eColor, &vColor1)); + FVECTOR4 vColor1 = ColorAdjusted(baseCol1); + HR(s_FX->SetVector(s_eColor, _DX(vColor1))); RenderGrid(s_FX, false); FVECTOR4 baseCol2(0.7f, 0.0f, 0.0f, 1.0f); - D3DXVECTOR4 vColor2 = ColorAdjusted(baseCol2); - HR(s_FX->SetVector(s_eColor, &vColor2)); + FVECTOR4 vColor2 = ColorAdjusted(baseCol2); + HR(s_FX->SetVector(s_eColor, _DX(vColor2))); RenderGreatCircle(s_FX); double dphi = ElevationScaleRotation(R); RenderGridLabels(s_FX, 2, baseCol2, R, dphi); @@ -370,16 +370,16 @@ void D3D9CelestialSphere::Render(LPDIRECT3DDEVICE9 pDevice, const VECTOR3& skyCo if (renderFlag & PLN_CGRID) { if (fabs(m_mjdPrecessionChecked - oapiGetSimMJD()) > 1e3) InitCelestialTransform(); - D3DXMATRIX rot; - D3DXMatrixMultiply(&rot, &m_transformCelestial, m_scene->GetProjectionViewMatrix()); - HR(s_FX->SetMatrix(s_eWVP, &rot)); + FMATRIX4 rot; + oapiMatrixMultiply(&rot, &m_transformCelestial, m_scene->GetProjectionViewMatrix()); + HR(s_FX->SetMatrix(s_eWVP, _DX(rot))); FVECTOR4 baseCol1(0.3f, 0.0f, 0.3f, 1.0f); - D3DXVECTOR4 vColor1 = ColorAdjusted(baseCol1); - HR(s_FX->SetVector(s_eColor, &vColor1)); + FVECTOR4 vColor1 = ColorAdjusted(baseCol1); + HR(s_FX->SetVector(s_eColor, _DX(vColor1))); RenderGrid(s_FX, false); FVECTOR4 baseCol2(0.7f, 0.0f, 0.7f, 1.0f); - D3DXVECTOR4 vColor2 = ColorAdjusted(baseCol2); - HR(s_FX->SetVector(s_eColor, &vColor2)); + FVECTOR4 vColor2 = ColorAdjusted(baseCol2); + HR(s_FX->SetVector(s_eColor, _DX(vColor2))); RenderGreatCircle(s_FX); double dphi = ElevationScaleRotation(m_rotCelestial); RenderGridLabels(s_FX, 1, baseCol2, m_rotCelestial, dphi); @@ -388,17 +388,17 @@ void D3D9CelestialSphere::Render(LPDIRECT3DDEVICE9 pDevice, const VECTOR3& skyCo // render local horizon grid if (renderFlag & PLN_HGRID) { MATRIX3 R; - D3DXMATRIX T, rot; + FMATRIX4 T, rot; if (LocalHorizonTransform(R, T)) { - D3DXMatrixMultiply(&rot, &T, m_scene->GetProjectionViewMatrix()); - HR(s_FX->SetMatrix(s_eWVP, &rot)); + oapiMatrixMultiply(&rot, &T, m_scene->GetProjectionViewMatrix()); + HR(s_FX->SetMatrix(s_eWVP, _DX(rot))); oapi::FVECTOR4 baseCol1(0.2f, 0.2f, 0.0f, 1.0f); - D3DXVECTOR4 vColor1 = ColorAdjusted(baseCol1); - HR(s_FX->SetVector(s_eColor, &vColor1)); + FVECTOR4 vColor1 = ColorAdjusted(baseCol1); + HR(s_FX->SetVector(s_eColor, _DX(vColor1))); RenderGrid(s_FX, false); oapi::FVECTOR4 baseCol2(0.5f, 0.5f, 0.0f, 1.0f); - D3DXVECTOR4 vColor2 = ColorAdjusted(baseCol2); - HR(s_FX->SetVector(s_eColor, &vColor2)); + FVECTOR4 vColor2 = ColorAdjusted(baseCol2); + HR(s_FX->SetVector(s_eColor, _DX(vColor2))); RenderGreatCircle(s_FX); double dphi = ElevationScaleRotation(R); RenderGridLabels(s_FX, 0, baseCol2, R, dphi); @@ -411,38 +411,38 @@ void D3D9CelestialSphere::Render(LPDIRECT3DDEVICE9 pDevice, const VECTOR3& skyCo if (hRef) { MATRIX3 R; oapiGetRotationMatrix(hRef, &R); - D3DXMATRIX iR = { + FMATRIX4 iR = { (float)R.m11, (float)R.m21, (float)R.m31, 0.0f, (float)R.m12, (float)R.m22, (float)R.m32, 0.0f, (float)R.m13, (float)R.m23, (float)R.m33, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; - D3DXMATRIX rot; - D3DXMatrixMultiply(&rot, &iR, m_scene->GetProjectionViewMatrix()); - HR(s_FX->SetMatrix(s_eWVP, &rot)); + FMATRIX4 rot; + oapiMatrixMultiply(&rot, &iR, m_scene->GetProjectionViewMatrix()); + HR(s_FX->SetMatrix(s_eWVP, _DX(rot))); FVECTOR4 baseCol(0.0f, 0.6f, 0.0f, 1.0f); - D3DXVECTOR4 vColor = ColorAdjusted(baseCol); - HR(s_FX->SetVector(s_eColor, &vColor)); + FVECTOR4 vColor = ColorAdjusted(baseCol); + HR(s_FX->SetVector(s_eColor, _DX(vColor))); RenderGreatCircle(s_FX); } } // render constellation boundaries ---------------------------------------- if (renderFlag & PLN_CNSTBND) { // for now, hijack the constellation line flag - HR(s_FX->SetMatrix(s_eWVP, m_scene->GetProjectionViewMatrix())); + HR(s_FX->SetMatrix(s_eWVP, _DX(m_scene->GetProjectionViewMatrix()))); RenderConstellationBoundaries(s_FX); } // render constellation lines --------------------------------------------- if (renderFlag & PLN_CONST) { - HR(s_FX->SetMatrix(s_eWVP, m_scene->GetProjectionViewMatrix())); + HR(s_FX->SetMatrix(s_eWVP, _DX(m_scene->GetProjectionViewMatrix()))); RenderConstellationLines(s_FX); } } // render stars HR(s_FX->SetTechnique(s_eStar)); - HR(s_FX->SetMatrix(s_eWVP, m_scene->GetProjectionViewMatrix())); + HR(s_FX->SetMatrix(s_eWVP, _DX(m_scene->GetProjectionViewMatrix()))); RenderStars(s_FX); // render markers and labels @@ -497,8 +497,8 @@ void D3D9CelestialSphere::RenderStars(ID3DXEffect *FX) void D3D9CelestialSphere::RenderConstellationLines(ID3DXEffect *FX) { const FVECTOR4 baseCol(0.5f, 0.3f, 0.2f, 1.0f); - D3DXVECTOR4 vColor = ColorAdjusted(baseCol); - HR(s_FX->SetVector(s_eColor, &vColor)); + FVECTOR4 vColor = ColorAdjusted(baseCol); + HR(s_FX->SetVector(s_eColor, _DX(vColor))); _TRACE; UINT numPasses = 0; @@ -516,8 +516,8 @@ void D3D9CelestialSphere::RenderConstellationLines(ID3DXEffect *FX) void D3D9CelestialSphere::RenderConstellationBoundaries(ID3DXEffect* FX) { const FVECTOR4 baseCol(0.25f, 0.2f, 0.15f, 1.0f); - D3DXVECTOR4 vColor = ColorAdjusted(baseCol); - HR(s_FX->SetVector(s_eColor, &vColor)); + FVECTOR4 vColor = ColorAdjusted(baseCol); + HR(s_FX->SetVector(s_eColor, _DX(vColor))); _TRACE; UINT numPasses = 0; @@ -588,18 +588,18 @@ void D3D9CelestialSphere::RenderGridLabels(ID3DXEffect* FX, int az_idx, const oa HR(FX->EndPass()); HR(FX->End()); - D3DXMATRIX T0, T1; + FMATRIX4 T0, T1; if (dphi) { - FX->GetMatrix(s_eWVP, &T0); + FX->GetMatrix(s_eWVP, (D3DXMATRIX*)&T0); double cosp = cos(dphi), sinp = sin(dphi); - D3DXMATRIX R2 = { + FMATRIX4 R2 = { (float)(cosp * R.m11 + sinp * R.m31), (float)(cosp * R.m12 + sinp * R.m32), (float)(cosp * R.m13 + sinp * R.m33), 0.0f, (float)R.m21, (float)R.m22, (float)R.m23, 0.0f, (float)(-sinp * R.m11 + cosp * R.m31), (float)(-sinp * R.m12 + cosp * R.m32), (float)(-sinp * R.m13 + cosp * R.m33), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; - D3DXMatrixMultiply(&T1, &R2, m_scene->GetProjectionViewMatrix()); - FX->SetMatrix(s_eWVP, &T1); + oapiMatrixMultiply(&T1, &R2, m_scene->GetProjectionViewMatrix()); + FX->SetMatrix(s_eWVP, _DX(T1)); } HR(m_pDevice->SetStreamSource(0, m_elGridLabelVtx, 0, sizeof(VERTEX_XYZ_TEX))); @@ -613,7 +613,7 @@ void D3D9CelestialSphere::RenderGridLabels(ID3DXEffect* FX, int az_idx, const oa HR(FX->SetTechnique(s_eLine)); // Restore default tech if (dphi) - FX->SetMatrix(s_eWVP, &T0); + FX->SetMatrix(s_eWVP, _DX(T0)); } // ============================================================== @@ -628,10 +628,8 @@ void D3D9CelestialSphere::RenderBkgImage(LPDIRECT3DDEVICE9 dev) bool D3D9CelestialSphere::EclDir2WindowPos(const VECTOR3& dir, int& x, int& y) const { - D3DXVECTOR3 homog; - D3DXVECTOR3 fdir((float)dir.x, (float)dir.y, (float)dir.z); - - D3DXVec3TransformCoord(&homog, &fdir, m_scene->GetProjectionViewMatrix()); + FVECTOR3 fdir((float)dir.x, (float)dir.y, (float)dir.z); + FVECTOR3 homog = oapiTransformCoord(&fdir, m_scene->GetProjectionViewMatrix()); if (homog.x >= -1.0f && homog.x <= 1.0f && homog.y >= -1.0f && homog.y <= 1.0f && diff --git a/OVP/D3D9Client/CelSphere.h b/OVP/D3D9Client/CelSphere.h index 798c7cedf..e98b236e9 100644 --- a/OVP/D3D9Client/CelSphere.h +++ b/OVP/D3D9Client/CelSphere.h @@ -144,7 +144,7 @@ class D3D9CelestialSphere : public oapi::CelestialSphere { void InitCelestialTransform(); - bool LocalHorizonTransform(MATRIX3& R, D3DXMATRIX& T); + bool LocalHorizonTransform(MATRIX3& R, FMATRIX4& T); /** * \brief Convert a direction into viewport coordinates @@ -177,7 +177,7 @@ class D3D9CelestialSphere : public oapi::CelestialSphere { LPDIRECT3DVERTEXBUFFER9 m_elGridLabelVtx; ///< vertex buffer for elevation grid labels LPDIRECT3DINDEXBUFFER9 m_GridLabelIdx; ///< index list for azimuth/elevation grid labels MATRIX3 m_rotCelestial; ///< rotation matrix for celestial grid rendering - D3DXMATRIX m_transformCelestial; ///< rotation for celestial grid rendering + FMATRIX4 m_transformCelestial; ///< rotation for celestial grid rendering double m_mjdPrecessionChecked; static ID3DXEffect* s_FX; diff --git a/OVP/D3D9Client/CloudMgr.cpp b/OVP/D3D9Client/CloudMgr.cpp index b949d39ad..22ae283cf 100644 --- a/OVP/D3D9Client/CloudMgr.cpp +++ b/OVP/D3D9Client/CloudMgr.cpp @@ -57,7 +57,7 @@ void CloudManager::LoadData() // ======================================================================= -void CloudManager::Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, int level, double viewap) +void CloudManager::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap) { LoadData(); if (bNoTextures) return; @@ -86,7 +86,7 @@ void CloudManager::Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, } -void CloudManager::RenderShadow(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, int level, double viewap, float shadowalpha) +void CloudManager::RenderShadow(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap, float shadowalpha) { LoadData(); if (bNoTextures) return; @@ -117,7 +117,7 @@ void CloudManager::RenderShadow(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double // ============================================================== -void CloudManager::RenderSimple(int level, int npatch, TILEDESC *tile, LPD3DXMATRIX mWrld) +void CloudManager::RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWrld) { LoadData(); if (bNoTextures) return; @@ -126,7 +126,7 @@ void CloudManager::RenderSimple(int level, int npatch, TILEDESC *tile, LPD3DXMAT LPDIRECT3DDEVICE9 pDev = gc->GetDevice(); pDev->SetVertexDeclaration(pPatchVertexDecl); - HR(FX->SetMatrix(eW, mWrld)); + HR(FX->SetMatrix(eW, _DX(mWrld))); UINT numPasses = 0; HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); HR(FX->BeginPass(0)); @@ -172,14 +172,14 @@ void CloudManager::RenderTile (int lvl, int hemisp, int ilat, int nlat, int ilng VBMESH &mesh = PATCH_TPL[lvl][ilat]; // patch template if (range.tumin == 0 && range.tumax == 1) { - HR(FX->SetVector(eTexOff, ptr(D3DXVECTOR4(1.0f, 0.0f, 1.0f, 0.0f)))); + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(1.0f, 0.0f, 1.0f, 0.0f)))); } else { float tuscale = range.tumax-range.tumin, tuofs = range.tumin; float tvscale = range.tvmax-range.tvmin, tvofs = range.tvmin; - HR(FX->SetVector(eTexOff, ptr(D3DXVECTOR4(tuscale,tuofs,tvscale,tvofs)))); + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(tuscale,tuofs,tvscale,tvofs)))); } - HR(FX->SetMatrix(eW, &mWorld)); + HR(FX->SetMatrix(eW, _DX(mWorld))); HR(FX->SetTexture(eTex0, tex)); // Diffuse Texture FX->CommitChanges(); diff --git a/OVP/D3D9Client/CloudMgr.h b/OVP/D3D9Client/CloudMgr.h index 8ea5e7274..a9bcb5344 100644 --- a/OVP/D3D9Client/CloudMgr.h +++ b/OVP/D3D9Client/CloudMgr.h @@ -20,15 +20,15 @@ class CloudManager: public TileManager { public: CloudManager (oapi::D3D9Client *gclient, const vPlanet *vplanet); - void Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, int level, double viewap = 0.0); - void RenderShadow(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, int level, double viewap, float shadowalpha); + void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap = 0.0); + void RenderShadow(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap, float shadowalpha); void LoadData(); protected: void InitRenderTile(); void EndRenderTile(); - void RenderSimple(int level, int npatch, TILEDESC *tile, LPD3DXMATRIX mWorld); + void RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWorld); void RenderTile(int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, double sdist, TILEDESC *tile, const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag); @@ -36,4 +36,4 @@ class CloudManager: public TileManager { // int cloudtexidx; }; -#endif // !__CLOUDMGR_H \ No newline at end of file +#endif // !__CLOUDMGR_H diff --git a/OVP/D3D9Client/D3D9Catalog.h b/OVP/D3D9Client/D3D9Catalog.h index 9f161b9c7..93e85b54b 100644 --- a/OVP/D3D9Client/D3D9Catalog.h +++ b/OVP/D3D9Client/D3D9Catalog.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include "MathAPI.h" #include "OrbiterAPI.h" template diff --git a/OVP/D3D9Client/D3D9Client.cpp b/OVP/D3D9Client/D3D9Client.cpp index 8307faaac..3e3de9f7c 100644 --- a/OVP/D3D9Client/D3D9Client.cpp +++ b/OVP/D3D9Client/D3D9Client.cpp @@ -41,6 +41,9 @@ #include #include +#include +#include +#include #if defined(_MSC_VER) && (_MSC_VER <= 1700 ) // Microsoft Visual Studio Version 2012 and lower #define round(v) floor(v+0.5) @@ -67,26 +70,34 @@ HINSTANCE g_hInst = 0; D3D9Client *g_client = 0; class gcConst* g_pConst = 0; IDirect3D9* g_pD3DObject = 0; // Made valid when VideoTab is created + +typedef IDirect3D9* (__stdcall* __Direct3DCreate9On12)(UINT SDKVersion, D3D9ON12_ARGS* pOverrideList, UINT NumOverrideEntries); + + +// Resource tracking +// Memgr* g_pMemgr_f = nullptr; Memgr* g_pMemgr_i = nullptr; Memgr* g_pMemgr_u = nullptr; -Memgr* g_pMemgr_w = nullptr; +Memgr* g_pMemgr_w = nullptr; Memgr* g_pMemgr_vtx = nullptr; Texmgr* g_pTexmgr_tt = nullptr; Vtxmgr* g_pVtxmgr_vb = nullptr; Idxmgr* g_pIdxmgr_ib = nullptr; - -typedef IDirect3D9* (__stdcall* __Direct3DCreate9On12)(UINT SDKVersion, D3D9ON12_ARGS* pOverrideList, UINT NumOverrideEntries); - set MeshCatalog; set SurfaceCatalog; unordered_map SharedTextures; unordered_map ClonedTextures; unordered_map MeshMap; unordered_map MicroTextures; +std::set g_fonts; +std::set g_pens; +std::set g_brushes; + -DWORD uCurrentMesh = 0; -vObject *pCurrentVisual = 0; + +DWORD g_uCurrentMesh = 0; +vObject *g_pCurrentVisual = nullptr; _D3D9Stats D3D9Stats; #ifdef _NVAPI_H @@ -97,10 +108,7 @@ bool bFreeze = false; bool bFreezeEnable = false; bool bFreezeRenderAll = false; -// Debuging Brush-, Pen- and Font-accounting -std::set g_fonts; -std::set g_pens; -std::set g_brushes; + extern list g_gcGUIAppList; @@ -157,7 +165,6 @@ DLLCLBK void InitModule(HINSTANCE hDLL) g_pMemgr_u = new Memgr("UINT8"); g_pMemgr_w = new Memgr("WORD"); g_pMemgr_vtx = new Memgr("VERTEX_2TEX"); - if (!D3DXCheckVersion(D3D_SDK_VERSION, D3DX_SDK_VERSION)) { MissingRuntimeError(); return; @@ -208,7 +215,7 @@ DLLCLBK void InitModule(HINSTANCE hDLL) DLLCLBK void ExitModule(HINSTANCE hDLL) { - LogAlw("--------------ExitModule------------"); + LogVerbose("[D3D9] === ExitModule ==="); delete Config; delete g_pConst; @@ -303,7 +310,7 @@ D3D9Client::D3D9Client (HINSTANCE hInstance) : D3D9Client::~D3D9Client() { - LogAlw("D3D9Client destructor called"); + LogVerbose("[D3D9] === destructor called ==="); SAFE_DELETE(vtab); SAFE_RELEASE(g_pD3DObject); } @@ -339,7 +346,7 @@ const void *D3D9Client::GetConfigParam (DWORD paramtype) const bool D3D9Client::clbkInitialise() { _TRACE; - LogAlw("================ clbkInitialise ==============="); + LogVerbose("[D3D9] === clbkInitialise ==="); LogAlw("Orbiter Version = %d",oapiGetOrbiterVersion()); D3D9ON12_ARGS args = {}; @@ -384,7 +391,7 @@ HWND D3D9Client::clbkCreateRenderWindow() { _TRACE; - LogAlw("================ clbkCreateRenderWindow ==============="); + LogVerbose("[D3D9] === clbkCreateRenderWindow ==="); if (!g_pD3DObject) return NULL; @@ -415,7 +422,7 @@ HWND D3D9Client::clbkCreateRenderWindow() surfBltTgt = NULL; // This variable is not used, set it to NULL anyway hMainThread = GetCurrentThread(); - D3DXMatrixIdentity(&ident); + oapiMatrixIdentity(&ident); oapiDebugString()[0] = '\0'; @@ -476,7 +483,7 @@ HWND D3D9Client::clbkCreateRenderWindow() LogAlw("Render Target = %s", _PTR(pBackBuffer)); LogAlw("DepthStencil = %s", _PTR(pDepthStencil)); - meshmgr = new MeshManager(this); + meshmgr = new MeshManager(this); // Bring Sketchpad Online D3D9PadFont::D3D9TechInit(pDevice); @@ -586,9 +593,10 @@ HWND D3D9Client::clbkCreateRenderWindow() void D3D9Client::clbkPostCreation() { _TRACE; - LogAlw("================ clbkPostCreation ==============="); - if (scene) scene->Initialise(); + LogVerbose("[D3D9] === clbkPostCreation ==="); + + if (scene) scene->clbkInitialise(); // Create Window Manager ----------------------------------------- // @@ -599,8 +607,6 @@ void D3D9Client::clbkPostCreation() bRunning = true; - LogAlw("=============== Loading Completed and Visuals Created ================"); - #ifdef _DEBUG SketchPadTest(); #endif @@ -763,26 +769,26 @@ void D3D9Client::SketchPadTest() pSkp->QuickPen(0xA0000000, 3.0f); pSkp->PushWorldTransform(); - pSkp->SetWorldScaleTransform2D(ptr(FVECTOR2(100.0f, 100.0f)), &pos0); + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos0); pSkp->DrawPoly(hColors); pSkp->DrawPoly(hOutline); - pSkp->SetWorldScaleTransform2D(ptr(FVECTOR2(100.0f, 100.0f)), &pos1); + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos1); pSkp->DrawPoly(hOutline2); - pSkp->SetWorldScaleTransform2D(ptr(FVECTOR2(100.0f, 100.0f)), &pos2); + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos2); pSkp->DrawPoly(hStrip); - pSkp->SetWorldScaleTransform2D(ptr(FVECTOR2(100.0f, 100.0f)), &pos3); + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos3); pSkp->DrawPoly(hStrip2); - pSkp->SetWorldScaleTransform2D(ptr(FVECTOR2(100.0f, 100.0f)), &pos4); + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos4); pSkp->QuickPen(0xFF000000, 25.0f); pSkp->DrawPoly(hOutline); hSrc = clbkLoadSurface("generic/noisep.dds", OAPISURFACE_TEXTURE); - pSkp->SetWorldScaleTransform2D(ptr(FVECTOR2(1.0f, 1.0f)), &pos5); + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(1.0f, 1.0f)), &pos5); FVECTOR2 pt[4]; pt[0] = FVECTOR2(-100.0f, -100.0f); @@ -817,9 +823,8 @@ void D3D9Client::SketchPadTest() // void D3D9Client::clbkCloseSession(bool fastclose) { - - LogAlw("================ clbkCloseSession ==============="); - + LogVerbose("[D3D9] === clbkCloseSession ==="); + // Post shutdown signals for gcGUI applications // for (auto pApp : g_gcGUIAppList) pApp->clbkShutdown(); @@ -872,7 +877,7 @@ void D3D9Client::clbkCloseSession(bool fastclose) LogAlw("============== Deleting Mesh Manager ============"); SAFE_DELETE(meshmgr); WriteLog("[Session Closed. Scene deleted.]"); - + } // ============================================================== @@ -881,7 +886,8 @@ void D3D9Client::clbkDestroyRenderWindow (bool fastclose) { _TRACE; oapiWriteLog((char*)"D3D9: [Destroy Render Window Called]"); - LogAlw("============= clbkDestroyRenderWindow ==========="); + + LogVerbose("[D3D9] === clbkDestroyRenderWindow ==="); #ifdef _NVAPI_H if (bNVAPI) { @@ -1166,7 +1172,7 @@ void D3D9Client::clbkUpdate(bool running) { _TRACE; double tot_update = D3D9GetTime(); - if (bFailed==false && bRunning) scene->Update(); + if (bFailed==false && bRunning) scene->clbkUpdate(); D3D9SetTime(D3D9Stats.Timer.Update, tot_update); } @@ -1198,7 +1204,7 @@ void D3D9Client::clbkRenderScene() UINT mem = pDevice->GetAvailableTextureMem()>>20; if (mem<32) TileBuffer::HoldThread(true); - scene->RenderMainScene(); // Render the main scene + scene->clbkRenderMainScene(); // Render the main scene VESSEL *hVes = oapiGetFocusInterface(); @@ -1541,6 +1547,14 @@ bool D3D9Client::clbkSetMeshProperty(DEVMESHHANDLE hMesh, DWORD prop, DWORD valu return false; } +// ============================================================== + +void D3D9Client::clbkSetVisualProperty(VISHANDLE vis, VisualProp prp, int idx, const type_info& t, const void* val) +{ + vVessel* vV = (vVessel*)vis; + if (vV && vV->Type() == OBJTP_VESSEL) vV->SetVisualProperty(prp, idx, t, val); +} + // ============================================================== // Returns a dev-mesh for a visual @@ -1551,7 +1565,7 @@ MESHHANDLE D3D9Client::clbkGetMesh(VISHANDLE vis, UINT idx) LogErr("NULL visual in clbkGetMesh(NULL,%u)",idx); return NULL; } - MESHHANDLE hMesh = ((vObject*)vis)->GetMesh(idx); + MESHHANDLE hMesh = (MESHHANDLE)((vObject*)vis)->GetMesh(idx); if (hMesh==NULL) LogWrn("clbkGetMesh() returns NULL"); return hMesh; } @@ -1580,14 +1594,14 @@ int D3D9Client::clbkGetMeshGroup (DEVMESHHANDLE hMesh, DWORD grpidx, GROUPREQUES void D3D9Client::clbkNewVessel(OBJHANDLE hVessel) { _TRACE; - if (scene) scene->NewVessel(hVessel); + if (scene) scene->clbkNewVessel(hVessel); } // ============================================================== void D3D9Client::clbkDeleteVessel(OBJHANDLE hVessel) { - if (scene) scene->DeleteVessel(hVessel); + if (scene) scene->clbkDeleteVessel(hVessel); } @@ -1606,13 +1620,20 @@ void D3D9Client::clbkOptionChanged(DWORD cat, DWORD item) { switch (cat) { case OPTCAT_CELSPHERE: - if (scene) scene->OnOptionChanged(cat, item); + if (scene) scene->clbkOnOptionChanged(cat, item); return; } } // ============================================================== +void D3D9Client::clbkScenarioChanged(OBJHANDLE hVesselA, ScnChgEvent type) +{ + if (scene) scene->clbkScenarioChanged(hVesselA, type); +} + +// ============================================================== + bool D3D9Client::clbkUseLaunchpadVideoTab() const { _TRACE; @@ -1709,8 +1730,6 @@ LRESULT D3D9Client::RenderWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l static bool bTrackMouse = false; static short xpos=0, ypos=0; - D3D9Pick pick; - if (hRenderWnd!=hWnd && uMsg!= WM_NCDESTROY) { LogErr("Invalid Window !! RenderWndProc() called after calling clbkDestroyRenderWindow() uMsg=0x%X", uMsg); return 0; @@ -1759,66 +1778,75 @@ LRESULT D3D9Client::RenderWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l TRACKMOUSEEVENT te; te.cbSize = sizeof(TRACKMOUSEEVENT); te.dwFlags = TME_LEAVE; te.hwndTrack = hRenderWnd; TrackMouseEvent(&te); + PickProp prp = { NULL, 0.1f, false }; bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0; bool bCtrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; bool bPckVsl = IsGenericProcEnabled(GENERICPROC_PICK_VESSEL); - if (DebugControls::IsActive() || bPckVsl || (bShift && bCtrl)) { - pick = GetScene()->PickScene(xpos, ypos); + if (DebugControls::IsActive() || bPckVsl || (bShift && bCtrl)) + { + if (DebugControls::IsActive()) { + if (DebugControls::debugFlags & DBG_FLAGS_PICKCURRENT) prp.pMesh = DebugControls::GetMesh(); + if (DebugControls::debugFlags & DBG_FLAGS_DUALSIDED) prp.bDualSided = true; + } + + D3D9Pick pick = GetScene()->PickScene(xpos, ypos, &prp); + if (bPckVsl) { gcCore::PickData out; - out.hVessel = pick.vObj->GetObjectA(); + out.hVessel = pick.vObj->GetObjHandle(); out.mesh = MESHHANDLE(pick.pMesh); out.group = pick.group; - out.pos = _FV(pick.pos); - out.normal = _FV(pick.normal); + out.pos = pick.pos; + out.normal = pick.normal; out.dist = pick.dist; MakeGenericProcCall(GENERICPROC_PICK_VESSEL, sizeof(gcCore::PickData), &out); } - } - PickTerrain(uMsg, xpos, ypos); - // No Debug Controls - if (bShift && bCtrl && !DebugControls::IsActive() && !oapiCameraInternal()) { + // No Debug Controls + if (bShift && bCtrl && !DebugControls::IsActive() && !oapiCameraInternal()) { - if (!pick.pMesh) break; + if (!pick.pMesh) break; - OBJHANDLE hObj = pick.vObj->Object(); - if (oapiGetObjectType(hObj) == OBJTP_VESSEL) { - oapiSetFocusObject(hObj); - } + OBJHANDLE hObj = pick.vObj->Object(); + if (oapiGetObjectType(hObj) == OBJTP_VESSEL) { + oapiSetFocusObject(hObj); + } - break; - } + break; + } - // With Debug Controls - if (DebugControls::IsActive()) { + // With Debug Controls + if (DebugControls::IsActive()) { - DWORD flags = *(DWORD*)GetConfigParam(CFGPRM_GETDEBUGFLAGS); + DWORD flags = *(DWORD*)GetConfigParam(CFGPRM_GETDEBUGFLAGS); - if (flags&DBG_FLAGS_PICK) { + if (flags & DBG_FLAGS_PICK) { - if (!pick.pMesh) break; + if (!pick.pMesh) break; - if (bShift && bCtrl) { - OBJHANDLE hObj = pick.vObj->Object(); - if (oapiGetObjectType(hObj)==OBJTP_VESSEL) { - oapiSetFocusObject(hObj); - break; + if (bShift && bCtrl) { + OBJHANDLE hObj = pick.vObj->Object(); + if (oapiGetObjectType(hObj) == OBJTP_VESSEL) { + oapiSetFocusObject(hObj); + break; + } + } + else if (pick.group >= 0) { + DebugControls::SetVisual(pick.vObj); + DebugControls::SelectMesh(pick.pMesh); + DebugControls::SelectGroup(pick.group); + DebugControls::SetGroupHighlight(true); + DebugControls::SetPickPos(pick.pos); } - } - else if (pick.group>=0) { - DebugControls::SetVisual(pick.vObj); - DebugControls::SelectMesh(pick.pMesh); - DebugControls::SelectGroup(pick.group); - DebugControls::SetGroupHighlight(true); - DebugControls::SetPickPos(pick.pos); } } } + PickTerrain(uMsg, xpos, ypos); + break; } @@ -1845,12 +1873,7 @@ LRESULT D3D9Client::RenderWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l bool bCtrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000)!=0; if (wParam == 'C' && bShift && bCtrl) bControlPanel = !bControlPanel; if (wParam == 'N' && bShift && bCtrl) Config->bCloudNormals = !Config->bCloudNormals; - if (wParam == 'F' && bShift && bCtrl) { - if (bFreeze) bFreezeEnable = bFreeze = false; - else bFreezeEnable = true; - } - if (wParam == 'A' && bFreeze) bFreezeRenderAll = !bFreezeRenderAll; - + if (wParam == 'V' && bShift && bCtrl) GetScene()->bStageSet = !GetScene()->bStageSet; break; } @@ -1948,8 +1971,8 @@ void D3D9Client::clbkRender2DPanel (SURFHANDLE *hSurf, MESHHANDLE hMesh, MATRIX3 float vw = (float)viewW; float vh = (float)viewH; - D3DXMATRIX mVP; - D3DXMatrixOrthoOffCenterRH(&mVP, (0.0f-dx)*sx, (vw-dx)*sx, (vh-dy)*sy, (0.0f-dy)*sy, -100.0f, 100.0f); + FMATRIX4 mVP; + D3DMAT_OrthoOffCenterRH(&mVP, (0.0f-dx)*sx, (vw-dx)*sx, (vh-dy)*sy, (0.0f-dy)*sy, -100.0f, 100.0f); D3D9Effect::SetViewProjMatrix(&mVP); for (DWORD i=0;iTexturePath(maps, mpath)) return NULL; + } + if (diff) { + SURFHANDLE hSrf = NatLoadSurface(diff, OAPISURFACE_TEXTURE | OAPISURFACE_SHARED | OAPISURFACE_DIFFUSE_ONLY, bPath); + if (hSrf && maps) { + if (bAll) NatLoadMaps(SURFACE(hSrf), mpath); + else NatLoadMap(SURFACE(hSrf), mpath); + } + return hSrf; + } + else if (maps) { + if (bAll) NatLoadMaps(SURFACE(hOld), mpath); + else NatLoadMap(SURFACE(hOld), mpath); + } + return hOld; +} + +// ============================================================== + SURFHANDLE D3D9Client::clbkLoadSurface (const char *fname, DWORD attrib, bool bPath) { _TRACE; @@ -2757,7 +2809,7 @@ SURFHANDLE D3D9Client::GetBackBufferHandle() const // ======================================================================= -void D3D9Client::MakeRenderProcCall(Sketchpad *pSkp, DWORD id, LPD3DXMATRIX pV, LPD3DXMATRIX pP) +void D3D9Client::MakeRenderProcCall(Sketchpad *pSkp, DWORD id, const FMATRIX4* pV, const FMATRIX4* pP) { for (auto it = RenderProcs.cbegin(); it != RenderProcs.cend(); ++it) { if (it->id == id) { @@ -2956,27 +3008,27 @@ void D3D9Client::SplashScreen() HR(pDevice->ColorFill(pSplashScreen, NULL, D3DCOLOR_XRGB(0, 0, 0))); HR(D3DXLoadSurfaceFromFile(pSplashScreen, NULL, &imgRect, pCustomSplashScreen, NULL, D3DX_FILTER_LINEAR, 0, NULL)); } else { - D3DXIMAGE_INFO Info; - HMODULE hOrbiter = GetModuleHandleA("orbiter.exe"); - HRSRC hRes = FindResourceA(hOrbiter, MAKEINTRESOURCEA(292), "IMAGE"); - HGLOBAL hImage = LoadResource(hOrbiter, hRes); - LPVOID pData = LockResource(hImage); - DWORD size = SizeofResource(hOrbiter, hRes); - - // Splash screen image is 1920 x 1200 pixel - double scale = min(viewW / 1920.0, viewH / 1200.0); - double _w = (1920.0 * scale); - double _h = (1200.0 * scale); - double _l = abs(viewW - _w)/2.0; - double _t = abs(viewH - _h)/2.0; - RECT imgRect = { - static_cast( round(_l) ), - static_cast( round(_t) ), - static_cast( round(_w + _l) ), - static_cast( round(_h + _t) ) - }; - HR(pDevice->ColorFill(pSplashScreen, NULL, D3DCOLOR_XRGB(0, 0, 0))); - HR(D3DXLoadSurfaceFromFileInMemory(pSplashScreen, NULL, &imgRect, pData, size, NULL, D3DX_FILTER_LINEAR, 0, &Info)); + D3DXIMAGE_INFO Info; + HMODULE hOrbiter = GetModuleHandleA("orbiter.exe"); + HRSRC hRes = FindResourceA(hOrbiter, MAKEINTRESOURCEA(292), "IMAGE"); + HGLOBAL hImage = LoadResource(hOrbiter, hRes); + LPVOID pData = LockResource(hImage); + DWORD size = SizeofResource(hOrbiter, hRes); + + // Splash screen image is 1920 x 1200 pixel + double scale = min(viewW / 1920.0, viewH / 1200.0); + double _w = (1920.0 * scale); + double _h = (1200.0 * scale); + double _l = abs(viewW - _w)/2.0; + double _t = abs(viewH - _h)/2.0; + RECT imgRect = { + static_cast( round(_l) ), + static_cast( round(_t) ), + static_cast( round(_w + _l) ), + static_cast( round(_h + _t) ) + }; + HR(pDevice->ColorFill(pSplashScreen, NULL, D3DCOLOR_XRGB(0, 0, 0))); + HR(D3DXLoadSurfaceFromFileInMemory(pSplashScreen, NULL, &imgRect, pData, size, NULL, D3DX_FILTER_LINEAR, 0, &Info)); } HDC hDC; @@ -3256,3 +3308,43 @@ VisObject::VisObject(OBJHANDLE hObj) : hObj(hObj) VisObject::~VisObject () { } + +// ======================================================================= + +SHADOWMAP::SHADOWMAP(LPDIRECT3DDEVICE9 pDevice, sMapType t, DWORD sz) : SMapInput(), tp(t) +{ + if (Config->ShadowMapMode == 0) return; + + // Single fixed size shadow map + if (tp == sMapType::SingleLod) { + HR(pDevice->CreateTexture(sz, sz, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptShmRT[0], NULL)); + HR(ptShmRT[0]->GetSurfaceLevel(0, &psShmRT[0])); + } + + // Exterior shadows + if (tp == sMapType::MultiLod) { + UINT size = Config->ShadowMapSize; + for (int i = 0; i < SHM_LOD_COUNT; i++) { + HR(pDevice->CreateTexture(size, size, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptShmRT[i], NULL)); + HR(ptShmRT[i]->GetSurfaceLevel(0, &psShmRT[i])); + size >>= 1; + } + } + + // VC Shadows + if (tp == sMapType::Cascaded) { + UINT size = Config->ShadowMapSize; + for (int i = 0; i < SHM_CASCADE_COUNT; i++) { + HR(pDevice->CreateTexture(size, size, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptShmRT[i], NULL)); + HR(ptShmRT[i]->GetSurfaceLevel(0, &psShmRT[i])); + } + } +} + +// ======================================================================= + +SHADOWMAP::~SHADOWMAP() +{ + for (auto& x : psShmRT) SAFE_RELEASE(x); + for (auto& x : ptShmRT) SAFE_RELEASE(x); +} diff --git a/OVP/D3D9Client/D3D9Client.h b/OVP/D3D9Client/D3D9Client.h index c1015c2eb..b3786d3d4 100644 --- a/OVP/D3D9Client/D3D9Client.h +++ b/OVP/D3D9Client/D3D9Client.h @@ -19,7 +19,7 @@ #endif #include -#include +#include "MathAPI.h" #include "D3D9Catalog.h" #include "GraphicsAPI.h" #include "D3D9Util.h" @@ -33,6 +33,10 @@ #include #include "WindowMgr.h" +#define SHM_CASCADE_COUNT 3 +#define SHM_LOD_COUNT 6 + + #define PP_DEFAULT 0x1 #define PP_LENSFLARE 0x2 @@ -58,23 +62,61 @@ class FileParser; class OapiExtension; class D3D9Pad; -typedef char* LPCHAR; -typedef void* CAMERAHANDLE; -typedef class D3D9Mesh* HMESH; +typedef char * LPCHAR; +typedef void * CAMERAHANDLE; +typedef class D3D9Mesh * HMESH; typedef class SurfNative* lpSurfNative; + +enum class EnvCamType { Undefined, Exterior, Interior, Mesh }; +enum class ShdPackage { None, Main, VC, Stage }; + + /** - * \brief Statistical data storage + * \brief Storage structure to keep reflection camera information. */ -struct _D3D9Stats +struct ENVCAMREC { + ~ENVCAMREC() + { + SAFE_RELEASE(pCube); + SAFE_RELEASE(pIrrad); + SAFE_RELEASE(pPlane); + oapiWriteLog("ENVCAMREC::Destruct"); + } + + std::vector omitAttc = {}; + std::vector omitDock = {}; + DWORD flags = 0; ///< Camera flags + EnvCamType type = EnvCamType::Undefined; + FVECTOR3 lPos = { 0,0,0 }; ///< Camera local position + FVECTOR3 lDir = { 1,0,0 }; ///< Camera local direction (in 'PLANE' mode only) + float near_clip = 0.1f; ///< Near clip-plane distance + float da_curve = 0.4f; + float da_bounch = 0.35f; + float da_force = 0.2f; + int mesh_idx = -1; ///< Camera is attached to a mesh + int group_idx = -1; ///< Camera is attached to a group + int id = -1; ///< User Id, for binding + BYTE iSide = 0; ///< [Private] Current side being rendered + bool bRendered = false; ///< [Private] Rendering of camera view is completed + LPDIRECT3DCUBETEXTURE9 pCube = nullptr; ///< Reflection cube map + LPDIRECT3DTEXTURE9 pIrrad = nullptr; ///< Irradiance map (baraboloidal) + LPDIRECT3DTEXTURE9 pPlane = nullptr; ///< Reflection 2D map if (flags & ENVCAM_PLANE) +}; + +/** + * \brief Statistical data storage + */ +struct _D3D9Stats { + _D3D9Stats() { memset(&Mesh, 0, sizeof(Mesh)); memset(&Timer, 0, sizeof(Timer)); TilesAllocated = 0; } - + struct { DWORD Vertices; ///< Number of vertices rendered DWORD MeshGrps; ///< Number of mesh groups rendered @@ -82,7 +124,7 @@ struct _D3D9Stats DWORD TexChanges; ///< Number of texture changes DWORD MtrlChanges; ///< Number of material changes } Mesh; ///< Mesh related statistics - + struct { D3D9Time Update; ///< clbkUpdate D3D9Time Scene; ///< clbkRenderScene @@ -112,13 +154,76 @@ struct RenderTgtData { int code; }; +struct PickProp { + D3D9Mesh* pMesh; // Mesh to pick, or NULL for full scene + float fnear; // Near clip distance, ignore entities closer than this + bool bDualSided; // Pick also back-facing triangles +}; + +struct SMapInput { + FVECTOR3 pos = { }; ///< Shadow map center in camera centric coords (ecl frame) + FVECTOR3 ld = { }; ///< Direction of sunlight (ecl frame) + float rad = { }; ///< Radius of shadow mapped area +}; + +class SHADOWMAP : public SMapInput +{ +public: + + enum class sMapType { MultiLod, Cascaded, SingleLod }; + + SHADOWMAP(LPDIRECT3DDEVICE9 pDevice, sMapType tp, DWORD sz = 1024); + ~SHADOWMAP(); + + LPDIRECT3DTEXTURE9 Map(int idx) const + { + if (tp == sMapType::MultiLod) { + if (idx == 0) return ptShmRT[lod]; + else return nullptr; + } + return ptShmRT[idx]; + } + + void Clear() { bValid = false; } + bool IsValid() const { return bValid && (Map(0) != nullptr); } + + + + FMATRIX4 mVP[SHM_CASCADE_COUNT] = {}; + FVECTOR4 Subrect[SHM_CASCADE_COUNT] = {}; + FVECTOR4 SubrectTF[SHM_CASCADE_COUNT] = {}; + FVECTOR2 Center[SHM_CASCADE_COUNT] = {}; + float SubPx[SHM_CASCADE_COUNT] = {}; + FMATRIX4 mLVP = {}; + float dist = 0.0f; // Shadow camera distance from shadow origin + float depth = 0.0f; // near to far plane distance. i.r. depth of the field + int lod = 0; // level of detail, 0 = highest + int size = 0; // Map size in pixels + int cascades = 0; // Number of active cascades + bool bValid = false; + sMapType tp; + + + // Cascades are located in entries 0, 1, 2 + // Active entry in MultiLod map is located in index pointed by 'lod' + // Active entry in SingleLod map is in index 0, 'lod' entry is 0 + LPDIRECT3DSURFACE9 psShmRT[max(SHM_LOD_COUNT, SHM_CASCADE_COUNT)] = { nullptr }; + LPDIRECT3DTEXTURE9 ptShmRT[max(SHM_LOD_COUNT, SHM_CASCADE_COUNT)] = { nullptr }; +}; + +struct LVLH { + FVECTOR3 Up; + FVECTOR3 North; + FVECTOR3 East; +}; + extern _D3D9Stats D3D9Stats; extern bool bFreeze; extern bool bFreezeEnable; extern bool bFreezeRenderAll; -extern DWORD uCurrentMesh; -extern class vObject* pCurrentVisual; +extern DWORD g_uCurrentMesh; +extern class vObject* g_pCurrentVisual; extern set MeshCatalog; extern set SurfaceCatalog; extern IDirect3D9* g_pD3DObject; @@ -267,6 +372,8 @@ class D3D9Client : public GraphicsClient * \sa oapiCreateSurface(DWORD,DWORD,DWORD) */ SURFHANDLE clbkLoadSurface (const char *fname, DWORD attrib, bool bPath = false); + SURFHANDLE clbkLoadMaps(const char* diff, const char* maps, bool bPath, SURFHANDLE hOld = NULL, bool bAll = true); + /** @@ -362,6 +469,15 @@ class D3D9Client : public GraphicsClient * \default None, returns \e false. */ bool clbkSetMeshProperty (DEVMESHHANDLE hMesh, DWORD property, DWORD value); + void clbkSetVisualProperty(VISHANDLE vis, VisualProp prp, int idx, const type_info& t, const void* val); + + /** + * \brief React to vessel docking, attaching events + * \param hVesselA object handle of first vessel + * \param hVesselB object handle of second vessel + * \param type Event type + */ + void clbkScenarioChanged(OBJHANDLE hVessel, ScnChgEvent type); /** * \brief React to vessel creation @@ -1051,7 +1167,6 @@ class D3D9Client : public GraphicsClient inline bool IsControlPanelOpen() const { return bControlPanel; } inline bool IsRunning() const { return bRunning; } inline bool IsLimited() const { return ((pCaps->TextureCaps&D3DPTEXTURECAPS_POW2) && (pCaps->TextureCaps&D3DPTEXTURECAPS_NONPOW2CONDITIONAL)); } - const LPD3DXMATRIX GetIdentity() const { return (const LPD3DXMATRIX)&ident; } HWND GetWindow(); bool HasVertexTextureSupport() const { return bVertexTex; } const D3DCAPS9 * GetHardwareCaps() const { return pCaps; } @@ -1061,7 +1176,7 @@ class D3D9Client : public GraphicsClient const void * GetConfigParam (DWORD paramtype) const; bool RegisterRenderProc(__gcRenderProc proc, DWORD id, void *pParam = NULL); bool RegisterGenericProc(__gcGenericProc proc, DWORD id, void *pParam = NULL); - void MakeRenderProcCall(Sketchpad *pSkp, DWORD id, LPD3DXMATRIX pV, LPD3DXMATRIX pP); + void MakeRenderProcCall(Sketchpad *pSkp, DWORD id, const FMATRIX4* pV, const FMATRIX4* pP); void MakeGenericProcCall(DWORD id, int iUser, void *pUser) const; bool IsGenericProcEnabled(DWORD id) const; void SetScenarioName(const std::string &path) { scenarioName = path; }; @@ -1334,7 +1449,7 @@ class D3D9Client : public GraphicsClient Scene *scene; // Scene description MeshManager *meshmgr; // mesh manager - D3DXMATRIX ident; + FMATRIX4 ident; struct RenderProcData; struct GenericProcData; @@ -1402,7 +1517,7 @@ class VisObject { * \brief Returns the object handle associated with the visual. * \return Object handle */ - OBJHANDLE GetObject () const { return hObj; } + OBJHANDLE GetObjHandle () const { return hObj; } /** * \brief Message callback. diff --git a/OVP/D3D9Client/D3D9Client.rc b/OVP/D3D9Client/D3D9Client.rc index 29c30beae..1b9d5e260 100644 --- a/OVP/D3D9Client/D3D9Client.rc +++ b/OVP/D3D9Client/D3D9Client.rc @@ -34,98 +34,131 @@ FONT 8, "Ms Shell Dlg" LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK -IDD_D3D9MESHDEBUG DIALOG 0, 0, 374, 428 +IDD_D3D9MESHDEBUG DIALOG 0, 0, 373, 465 STYLE DS_3DLOOK | DS_CENTER | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_THICKFRAME | WS_SYSMENU EXSTYLE WS_EX_WINDOWEDGE CAPTION "D3D9 Debug Controls" FONT 8, "Ms Shell Dlg" { - GROUPBOX "Mesh Options", IDC_DBG_MESHGRP, 5, 166, 176, 60, 0, WS_EX_LEFT - PUSHBUTTON "Close", IDCANCEL, 127, 393, 51, 14, 0, WS_EX_LEFT - PUSHBUTTON "Open", IDC_DBG_OPEN, 202, 169, 30, 14, 0, WS_EX_LEFT - PUSHBUTTON "Execute", IDC_DBG_EXECUTE, 305, 282, 56, 14, 0, WS_EX_LEFT - LTEXT "Selected Visual: Cape Canaveral", IDC_DBG_VISUAL, 7, 5, 104, 8, SS_LEFT, WS_EX_LEFT - COMBOBOX IDC_DBG_DISPLAY, 48, 33, 120, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT - LTEXT "Display", IDC_STATIC, 18, 36, 24, 8, SS_LEFT, WS_EX_LEFT - LTEXT "Camera", IDC_STATIC, 18, 51, 25, 11, SS_LEFT, WS_EX_LEFT - COMBOBOX IDC_DBG_CAMERA, 48, 51, 120, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT - EDITTEXT IDC_DBG_MESH, 54, 87, 44, 12, ES_AUTOHSCROLL, WS_EX_LEFT - LTEXT "Group Idx", IDC_STATIC, 18, 105, 32, 12, SS_LEFT, WS_EX_LEFT - LTEXT "Mesh Idx", IDC_STATIC, 18, 90, 30, 8, SS_LEFT, WS_EX_LEFT - EDITTEXT IDC_DBG_GROUP, 54, 105, 44, 12, WS_GROUP | ES_AUTOHSCROLL, WS_EX_LEFT - EDITTEXT IDC_DBG_FILE, 236, 170, 125, 12, WS_GROUP | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT - AUTOCHECKBOX "Highlight selected group", IDC_DBG_HSG, 18, 123, 92, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Highlight selected mesh", IDC_DBG_HSM, 18, 135, 91, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Boxes", IDC_DBG_BOXES, 208, 317, 35, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Normalize", IDC_DBG_NORM, 215, 214, 47, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Mipmap fade", IDC_DBG_FADE, 215, 227, 57, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Reduce seams", IDC_DBG_SEAMS, 275, 214, 63, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Spheres", IDC_DBG_SPHERES, 208, 328, 42, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Surface Tiles", IDC_DBG_TILEBB, 208, 340, 57, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Selected group only", IDC_DBG_GRPO, 274, 317, 79, 8, 0, WS_EX_LEFT - GROUPBOX "Bounding geometry", IDC_STATIC, 195, 305, 175, 50, 0, WS_EX_LEFT - GROUPBOX "Visualize local light cones", IDC_STATIC, 195, 360, 175, 35, 0, WS_EX_LEFT - GROUPBOX "Mesh debugger", IDC_STATIC, 5, 20, 175, 142, 0, WS_EX_LEFT + GROUPBOX "Mesh Options and Flags", IDC_STATIC, 5, 165, 176, 60, 0, WS_EX_LEFT + PUSHBUTTON "Close", IDCANCEL, 124, 429, 51, 14, 0, WS_EX_LEFT + PUSHBUTTON "Open", IDC_DBG_OPEN, 387, 34, 30, 14, 0, WS_EX_LEFT + PUSHBUTTON "Execute", IDC_DBG_EXECUTE, 490, 147, 56, 14, 0, WS_EX_LEFT + LTEXT "Selected Visual: Cape Canaveral", IDC_DBG_VISUAL, 7, 2, 104, 8, SS_LEFT, WS_EX_LEFT + COMBOBOX IDC_DBG_DISPLAY, 48, 27, 120, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Display", IDC_STATIC, 18, 30, 24, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Camera", IDC_STATIC, 18, 43, 25, 11, SS_LEFT, WS_EX_LEFT + COMBOBOX IDC_DBG_CAMERA, 48, 42, 120, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + EDITTEXT IDC_DBG_MESH, 54, 73, 44, 12, ES_AUTOHSCROLL, WS_EX_LEFT + LTEXT "Group Idx", IDC_STATIC, 18, 92, 32, 12, SS_LEFT, WS_EX_LEFT + LTEXT "Mesh Idx", IDC_STATIC, 18, 76, 30, 8, SS_LEFT, WS_EX_LEFT + EDITTEXT IDC_DBG_GROUP, 54, 89, 44, 12, WS_GROUP | ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_FILE, 421, 35, 125, 12, WS_GROUP | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + AUTOCHECKBOX "Highlight selected group", IDC_DBG_HSG, 18, 107, 92, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Highlight selected mesh", IDC_DBG_HSM, 18, 117, 91, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Boxes", IDC_DBG_BOXES, 208, 217, 35, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Normalize", IDC_DBG_NORM, 400, 79, 47, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Mipmap fade", IDC_DBG_FADE, 400, 92, 57, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Reduce seams", IDC_DBG_SEAMS, 460, 79, 63, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Spheres", IDC_DBG_SPHERES, 208, 226, 42, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Surface Tiles", IDC_DBG_TILEBB, 208, 235, 57, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Selected group only", IDC_DBG_GRPO, 274, 217, 79, 8, 0, WS_EX_LEFT + GROUPBOX "Bounding geometry", IDC_STATIC, 195, 205, 175, 45, 0, WS_EX_LEFT + GROUPBOX "Visualize local light cones", IDC_STATIC, 195, 255, 175, 35, 0, WS_EX_LEFT + GROUPBOX "Selection and Display Options", IDC_STATIC, 5, 15, 175, 145, 0, WS_EX_LEFT GROUPBOX "Misc.", IDC_STATIC, 195, 10, 173, 70, 0, WS_EX_LEFT - GROUPBOX "Scene Debugger", IDC_STATIC, 195, 85, 173, 60, 0, WS_EX_LEFT - GROUPBOX "Texture Tools", IDC_STATIC, 195, 150, 173, 151, 0, WS_EX_LEFT - AUTOCHECKBOX "Selected mesh only", IDC_DBG_MSHO, 274, 329, 77, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Dual sided", IDC_DBG_DUAL, 120, 123, 49, 8, 0, WS_EX_LEFT - PUSHBUTTON "<", IDC_DBG_GRPUP, 102, 105, 9, 12, BS_CENTER, WS_EX_LEFT - PUSHBUTTON ">", IDC_DBG_GRPDN, 114, 105, 9, 12, BS_CENTER, WS_EX_LEFT - PUSHBUTTON "<", IDC_DBG_MSHUP, 102, 87, 9, 12, BS_CENTER, WS_EX_LEFT - PUSHBUTTON ">", IDC_DBG_MSHDN, 114, 87, 9, 12, BS_CENTER, WS_EX_LEFT - AUTOCHECKBOX "Selected visual only", IDC_DBG_VISO, 274, 341, 79, 8, 0, WS_EX_LEFT - CONTROL "", IDC_DBG_SPEED, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 42, 69, 108, 13, WS_EX_LEFT + GROUPBOX "Scene Debugger", IDC_STATIC, 195, 85, 173, 115, 0, WS_EX_LEFT + GROUPBOX "Texture Tools", IDC_STATIC, 380, 15, 173, 151, 0, WS_EX_LEFT + AUTOCHECKBOX "Selected mesh only", IDC_DBG_MSHO, 274, 226, 77, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Dual sided", IDC_DBG_DUAL, 115, 107, 49, 8, 0, WS_EX_LEFT + PUSHBUTTON "<", IDC_DBG_GRPUP, 102, 90, 9, 12, BS_CENTER, WS_EX_LEFT + PUSHBUTTON ">", IDC_DBG_GRPDN, 114, 90, 9, 12, BS_CENTER, WS_EX_LEFT + PUSHBUTTON "<", IDC_DBG_MSHUP, 102, 73, 9, 12, BS_CENTER, WS_EX_LEFT + PUSHBUTTON ">", IDC_DBG_MSHDN, 114, 73, 9, 12, BS_CENTER, WS_EX_LEFT + AUTOCHECKBOX "Selected visual only", IDC_DBG_VISO, 274, 235, 79, 8, 0, WS_EX_LEFT + CONTROL "", IDC_DBG_SPEED, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 42, 58, 108, 13, WS_EX_LEFT CONTROL "", IDC_DBG_RESBIAS, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_BOTH, 205, 55, 155, 20, WS_EX_LEFT - LTEXT "Speed", IDC_STATIC, 18, 69, 22, 8, SS_LEFT, WS_EX_LEFT - LTEXT "Static", IDC_DBG_SPEEDDSP, 150, 69, 21, 10, SS_LEFT, WS_EX_LEFT - AUTOCHECKBOX "Add ambient light", IDC_DBG_AMBIENT, 18, 147, 70, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Wireframe", IDC_DBG_WIRE, 120, 135, 48, 8, 0, WS_EX_LEFT - GROUPBOX "Material", IDC_DBG_MATGRP, 6, 230, 175, 160, 0, WS_EX_LEFT - COMBOBOX IDC_DBG_MATPRP, 78, 245, 96, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT - COMBOBOX IDC_DBG_DEFSHADER, 77, 181, 96, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT - COMBOBOX IDC_DBG_CONES, 200, 375, 165, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT - LTEXT "Material property", IDC_STATIC, 19, 247, 53, 8, SS_LEFT, WS_EX_LEFT - LTEXT "Default Shader", IDC_STATIC, 15, 183, 48, 8, SS_LEFT, WS_EX_LEFT - EDITTEXT IDC_DBG_RED, 32, 265, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT - EDITTEXT IDC_DBG_GREEN, 68, 265, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT - EDITTEXT IDC_DBG_BLUE, 104, 265, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT - EDITTEXT IDC_DBG_ALPHA, 140, 265, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT - EDITTEXT IDC_DBG_VARA, 280, 262, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT - EDITTEXT IDC_DBG_VARC, 230, 262, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT - EDITTEXT IDC_DBG_VARB, 330, 262, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + LTEXT "Speed", IDC_STATIC, 18, 58, 22, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Static", IDC_DBG_SPEEDDSP, 150, 58, 21, 10, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "Add ambient light", IDC_DBG_AMBIENT, 18, 127, 70, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Wireframe", IDC_DBG_WIRE, 115, 117, 48, 8, 0, WS_EX_LEFT + GROUPBOX "Material", IDC_DBG_MATGRP, 5, 230, 175, 165, 0, WS_EX_LEFT + COMBOBOX IDC_DBG_MATPRP, 77, 240, 96, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_DEFSHADER, 75, 177, 96, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_CONES, 200, 270, 165, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Material property", IDC_STATIC, 18, 242, 53, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Mesh Shader", IDC_STATIC, 15, 179, 43, 9, SS_LEFT, WS_EX_LEFT + EDITTEXT IDC_DBG_RED, 31, 260, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_GREEN, 67, 260, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_BLUE, 103, 260, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_ALPHA, 139, 260, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_VARA, 465, 127, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_VARC, 415, 127, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_VARB, 515, 127, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT COMBOBOX IDC_DBG_ENVMAP, 275, 115, 86, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT COMBOBOX IDC_DBG_SCENEDBG, 255, 100, 106, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT - COMBOBOX IDC_DBG_ACTION, 265, 193, 95, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT - COMBOBOX IDC_DBG_TARGET, 235, 242, 126, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_ACTION, 450, 58, 95, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_TARGET, 420, 107, 126, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT LTEXT "Debug Mode", IDC_STATIC, 205, 102, 42, 8, SS_LEFT, WS_EX_LEFT LTEXT "Display special buffer", IDC_STATIC, 205, 117, 68, 9, SS_LEFT, WS_EX_LEFT LTEXT "Resolution Bias", IDC_STATIC, 205, 40, 50, 8, SS_LEFT, WS_EX_LEFT - LTEXT "Select Action", IDC_STATIC, 216, 195, 43, 8, SS_LEFT, WS_EX_LEFT - LTEXT "Target", IDC_STATIC, 207, 244, 25, 8, SS_LEFT, WS_EX_LEFT - LTEXT "Fb:", IDC_STATIC, 317, 264, 11, 8, SS_LEFT, WS_EX_LEFT - LTEXT "Fa:", IDC_STATIC, 267, 264, 11, 8, SS_LEFT, WS_EX_LEFT - LTEXT "Noise:", IDC_STATIC, 205, 264, 21, 8, SS_LEFT, WS_EX_LEFT - CONTROL "", IDC_DBG_MATADJ, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 20, 282, 156, 14, WS_EX_LEFT - PUSHBUTTON "Save materials", IDC_DBG_MATSAVE, 8, 393, 51, 14, 0, WS_EX_LEFT - PUSHBUTTON "Create kernel", IDC_DBG_KERNEL, 310, 25, 47, 14, 0, WS_EX_LEFT - PUSHBUTTON "Data window", IDC_DBG_DATAWND, 8, 410, 50, 14, 0, WS_EX_LEFT - PUSHBUTTON "Save envmap", IDC_DBG_ENVSAVE, 205, 128, 60, 12, 0, WS_EX_LEFT - PUSHBUTTON ">>>", IDC_DBG_MORE, 127, 410, 50, 14, 0, WS_EX_LEFT - PUSHBUTTON "Reload Shader", IDC_DBG_RELOADSHD, 63, 410, 60, 14, 0, WS_EX_LEFT - PUSHBUTTON "Reload Textures", IDC_DBG_RELOADTEX, 63, 394, 60, 14, 0, WS_EX_LEFT - LTEXT "Texture: None", IDC_DBG_TEXTURE, 10, 352, 168, 10, SS_LEFT, WS_EX_LEFT - AUTOCHECKBOX "Pick", IDC_DBG_PICK, 120, 147, 30, 8, 0, WS_EX_LEFT - LTEXT "Mesh: None", IDC_DBG_MESHNAME, 10, 363, 168, 10, SS_LEFT, WS_EX_LEFT - LTEXT "Group Status:", IDC_DBG_GROUPSTAT, 10, 374, 170, 10, SS_LEFT, WS_EX_LEFT - PUSHBUTTON "Paste", IDC_DBG_PASTE, 134, 300, 37, 14, 0, WS_EX_LEFT - PUSHBUTTON "Copy", IDC_DBG_COPY, 91, 300, 39, 14, 0, WS_EX_LEFT - AUTOCHECKBOX "Link channels", IDC_DBG_LINK, 20, 307, 60, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Use and Save the property", IDC_DBG_DEFINED, 20, 335, 101, 8, 0, WS_EX_LEFT - AUTOCHECKBOX "Extend material range", IDC_DBG_EXTEND, 20, 321, 84, 8, 0, WS_EX_LEFT + LTEXT "Select Action", IDC_STATIC, 401, 60, 43, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Target", IDC_STATIC, 392, 109, 25, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Fb:", IDC_STATIC, 502, 129, 11, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Fa:", IDC_STATIC, 452, 129, 11, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Noise:", IDC_STATIC, 390, 129, 21, 8, SS_LEFT, WS_EX_LEFT + CONTROL "", IDC_DBG_MATADJ, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 19, 277, 156, 14, WS_EX_LEFT + PUSHBUTTON "Save materials", IDC_DBG_MATSAVE, 5, 429, 51, 14, 0, WS_EX_LEFT + PUSHBUTTON "Create kernel", IDC_DBG_KERNEL, 315, 430, 47, 14, 0, WS_EX_LEFT + PUSHBUTTON "Data window", IDC_DBG_DATAWND, 5, 446, 50, 14, 0, WS_EX_LEFT + PUSHBUTTON "Save envmap", IDC_DBG_ENVSAVE, 300, 167, 60, 12, 0, WS_EX_LEFT + PUSHBUTTON ">>>", IDC_DBG_MORE, 124, 446, 50, 14, 0, WS_EX_LEFT + PUSHBUTTON "Reload Shader", IDC_DBG_RELOADSHD, 60, 446, 60, 14, 0, WS_EX_LEFT + PUSHBUTTON "Reload Textures", IDC_DBG_RELOADTEX, 60, 430, 60, 14, 0, WS_EX_LEFT + LTEXT "Texture: None", IDC_DBG_TEXTURE, 9, 347, 168, 10, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "Pick", IDC_DBG_PICK, 115, 127, 30, 8, 0, WS_EX_LEFT + LTEXT "Mesh: None", IDC_DBG_MESHNAME, 9, 358, 168, 10, SS_LEFT, WS_EX_LEFT + LTEXT "Group Status:", IDC_DBG_GROUPSTAT, 10, 369, 170, 10, SS_LEFT, WS_EX_LEFT + PUSHBUTTON "Paste", IDC_DBG_PASTE, 133, 295, 37, 14, 0, WS_EX_LEFT + PUSHBUTTON "Copy", IDC_DBG_COPY, 90, 295, 39, 14, 0, WS_EX_LEFT + AUTOCHECKBOX "Link channels", IDC_DBG_LINK, 19, 302, 60, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Use and Save the property", IDC_DBG_DEFINED, 19, 324, 101, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Extend material range", IDC_DBG_EXTEND, 19, 313, 84, 8, 0, WS_EX_LEFT AUTOCHECKBOX "FPS Limiter", IDC_DBG_FPSLIM, 205, 25, 51, 8, 0, WS_EX_LEFT - PUSHBUTTON "Export textures", IDC_DBG_EXPTEX, 315, 410, 52, 14, 0, WS_EX_LEFT + PUSHBUTTON "Export textures", IDC_DBG_EXPTEX, 315, 445, 52, 14, 0, WS_EX_LEFT + COMBOBOX IDC_DBG_BKLID, 11, 407, 50, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + GROUPBOX "Baked Lights", IDC_DBG_BKLGROUP, 6, 395, 175, 30, 0, WS_EX_LEFT + CONTROL "", IDC_DBG_BKLADJ, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 65, 405, 115, 15, WS_EX_LEFT + GROUPBOX "Mesh Group Information", IDC_STATIC, 195, 295, 175, 130, 0, WS_EX_LEFT + AUTOCHECKBOX "No Shadow", IDC_DBG_NOSHADOW, 200, 310, 53, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Do not render", IDC_DBG_NORENDER, 200, 320, 59, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Always Lit", IDC_DBG_NOLIGHT, 200, 330, 47, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Additive Blend", IDC_DBG_ADDITIVE, 200, 340, 61, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Texture Alpha Only (No Color)", IDC_DBG_NOCOLOR, 200, 350, 109, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Order Independent Transparency", IDC_DBG_OIT, 200, 360, 121, 8, 0, WS_EX_LEFT + EDITTEXT IDC_DBG_MATIDX, 225, 375, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_TEXIDX, 225, 390, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + LTEXT "MatIdx", IDC_STATIC, 200, 376, 22, 9, SS_LEFT, WS_EX_LEFT + LTEXT "TexIdx", IDC_STATIC, 200, 392, 22, 9, SS_LEFT, WS_EX_LEFT + PUSHBUTTON "Save Mesh", IDC_DBG_MESHSAVE, 200, 430, 55, 14, 0, WS_EX_LEFT + PUSHBUTTON "Unrendered", IDC_DBG_NEXT, 127, 90, 48, 12, 0, WS_EX_LEFT + LTEXT "Label", IDC_STATIC, 201, 407, 18, 9, SS_LEFT, WS_EX_LEFT + EDITTEXT IDC_DBG_GRPLABEL, 225, 405, 140, 12, ES_AUTOHSCROLL, WS_EX_LEFT + AUTOCHECKBOX "Exterior in VC", IDC_DBG_EXTVC, 115, 137, 58, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "2cm near clip distance", IDC_DBG_CLIPDIST, 18, 136, 87, 8, 0, WS_EX_LEFT + LTEXT "MeshVisMode: ", IDC_DBG_VISMODE, 10, 380, 162, 9, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "Pick only current mesh", IDC_DBG_PICKCURRENT, 18, 146, 87, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Dynamic", IDC_DBG_DYNAMIC, 285, 310, 43, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "This is VC mesh", IDC_DBG_ISVCMESH, 15, 193, 66, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Cast VC shadow", IDC_DBG_VCSHADOW, 15, 203, 68, 8, 0, WS_EX_LEFT + COMBOBOX IDC_DBG_AMBDIR, 275, 130, 86, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "VC Ambient View Dir", IDC_STATIC, 205, 132, 66, 9, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "No ambient sunlight", IDC_DBG_NOSUNAMB, 205, 161, 78, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "No planet glow", IDC_DBG_NOPLNAMB, 205, 172, 63, 8, 0, WS_EX_LEFT + COMBOBOX IDC_DBG_DATASRC, 275, 145, 86, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Probe Data Source", IDC_STATIC, 206, 147, 62, 9, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "No dynamic sunlight", IDC_DBG_NODYNSUN, 205, 184, 79, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "VC Click Zones", IDC_DBG_VCZONES, 115, 147, 60, 8, 0, WS_EX_LEFT } @@ -257,7 +290,7 @@ FONT 8, "MS Sans Serif", 0, 0, 0 COMBOBOX IDC_LIGHTCONFIG, 201, 255, 150, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT COMBOBOX IDC_SELFSHADOWS, 196, 310, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT COMBOBOX IDC_SHADOWFILTER, 291, 310, 65, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT - COMBOBOX IDC_TERRAIN, 196, 335, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_TERRAIN, 290, 335, 65, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT AUTOCHECKBOX "Enable Development Tools", IDC_MESH_DEBUGGER, 200, 85, 101, 8, 0, WS_EX_LEFT CONTROL "", IDC_CONVERGENCE, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_TOP, 425, 20, 85, 20, WS_EX_LEFT CONTROL "", IDC_SEPARATION, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_TOP, 425, 40, 85, 20, WS_EX_LEFT @@ -282,7 +315,7 @@ FONT 8, "MS Sans Serif", 0, 0, 0 LTEXT "Anisotropic filtering", IDC_STATIC, 15, 127, 60, 8, SS_LEFT, WS_EX_LEFT LTEXT "Planet glow", IDC_STATIC, 16, 144, 38, 8, SS_LEFT, WS_EX_LEFT LTEXT "Vessel mapping", IDC_STATIC, 196, 300, 50, 8, SS_LEFT, WS_EX_LEFT - LTEXT "Terrain mapping", IDC_STATIC, 196, 325, 52, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Terrain mapping", IDC_STATIC, 290, 325, 52, 8, SS_LEFT, WS_EX_LEFT LTEXT "Filter options", IDC_STATIC, 291, 300, 40, 8, SS_LEFT, WS_EX_LEFT LTEXT "Texture Mipmaps", IDC_STATIC, 16, 162, 55, 8, SS_LEFT, WS_EX_LEFT LTEXT "Post Processing", IDC_STATIC, 17, 179, 52, 8, SS_LEFT, WS_EX_LEFT @@ -311,9 +344,11 @@ FONT 8, "MS Sans Serif", 0, 0, 0 LTEXT "Earth visual config", IDC_STATIC, 17, 215, 59, 9, SS_LEFT, WS_EX_LEFT AUTOCHECKBOX "Enable shader cache for faster startup", IDC_ESCACHE, 370, 250, 136, 8, 0, WS_EX_LEFT AUTOCHECKBOX "Enable increased atmosphere quality", IDC_EAQUALITY, 20, 344, 131, 8, 0, WS_EX_LEFT - COMBOBOX IDC_MESHRES, 263, 170, 50, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_MESHRES, 263, 170, 50, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT COMBOBOX IDC_TILECOUNT, 263, 185, 50, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT LTEXT "Max tiles allocated", IDC_STATIC, 200, 187, 59, 9, SS_LEFT, WS_EX_LEFT + LTEXT "Virtual cockpit shadows", IDC_STATIC, 195, 326, 76, 9, SS_LEFT, WS_EX_LEFT + COMBOBOX IDC_CASCOUNT, 195, 335, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT } diff --git a/OVP/D3D9Client/D3D9Config.cpp b/OVP/D3D9Client/D3D9Config.cpp index d867b73c9..52b798827 100644 --- a/OVP/D3D9Client/D3D9Config.cpp +++ b/OVP/D3D9Client/D3D9Config.cpp @@ -98,6 +98,8 @@ void D3D9Config::Reset () bIrradiance = 1; bAtmoQuality = 1; NoPlanetAA = 0; + VCCascadeCount = 2; + ExpVCLight = 0; GFXIntensity = 0.5; GFXDistance = 0.8; @@ -196,6 +198,8 @@ bool D3D9Config::ReadParams () if (oapiReadItem_int (hFile, (char*)"DebugBreak", i)) DebugBreak = max(0, min(1, i)); if (oapiReadItem_int (hFile, (char*)"ShaderCacheUse", i)) ShaderCacheUse = max(0, min(1, i)); if (oapiReadItem_int (hFile, (char*)"NoPlanetAA", i)) NoPlanetAA = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"VCCascadeCount", i)) VCCascadeCount = max(1, min(3, i)); + if (oapiReadItem_int (hFile, (char*)"ExperimentalVCLight", i)) ExpVCLight = max(0, min(1, i)); if (oapiReadItem_float (hFile, (char*)"OrbitalShadowMult", d)) OrbitalShadowMult = max(0.5, min(10.0, d)); if (oapiReadItem_float (hFile, (char*)"GFXIntensity", d)) GFXIntensity = max(0.0, min(1.0, d)); @@ -287,6 +291,8 @@ void D3D9Config::WriteParams () oapiWriteItem_int (hFile, (char*)"DebugBreak", DebugBreak); oapiWriteItem_int (hFile, (char*)"ShaderCacheUse", ShaderCacheUse); oapiWriteItem_int (hFile, (char*)"NoPlanetAA", NoPlanetAA); + oapiWriteItem_int (hFile, (char*)"VCCascadeCount", VCCascadeCount); + oapiWriteItem_int (hFile, (char*)"ExperimentalVCLight", ExpVCLight); oapiWriteItem_float (hFile, (char*)"OrbitalShadowMult", OrbitalShadowMult); oapiWriteItem_float (hFile, (char*)"GFXIntensity", GFXIntensity); diff --git a/OVP/D3D9Client/D3D9Config.h b/OVP/D3D9Client/D3D9Config.h index c23be7f34..2c9a250ba 100644 --- a/OVP/D3D9Client/D3D9Config.h +++ b/OVP/D3D9Client/D3D9Config.h @@ -97,7 +97,9 @@ class D3D9Config { int bLocalGlares; int bIrradiance; int bAtmoQuality; - int NoPlanetAA; ///< Disable planet surface anti-aliasing to prevent white pixels at horizon + int NoPlanetAA; ///< Disable planet surface anti-aliasing to prevent white pixels at horizon + int VCCascadeCount; + int ExpVCLight; char *DebugFont; ///< Font face for debug lines (default="Fixed") char *SolCfg; ///< Solar system to use (default="Sol") double GFXIntensity; ///< Post Processing | Light glow intensity (0.0...1.0, default=0.5) diff --git a/OVP/D3D9Client/D3D9ControlPanel.cpp b/OVP/D3D9Client/D3D9ControlPanel.cpp index 320e2fc35..bd6d6edbc 100644 --- a/OVP/D3D9Client/D3D9ControlPanel.cpp +++ b/OVP/D3D9Client/D3D9ControlPanel.cpp @@ -230,7 +230,7 @@ void D3D9Client::RenderControlPanel() if (vObj) { DWORD nMesh = vObj->GetMeshCount(); for (DWORD i = 0; i < nMesh; i++) { - D3D9Mesh *hMesh = static_cast(vObj->GetMesh(i)); + D3D9Mesh *hMesh = vObj->GetMesh(i); hMesh->ResetRenderStatus(); } } diff --git a/OVP/D3D9Client/D3D9Effect.cpp b/OVP/D3D9Client/D3D9Effect.cpp index 8c40c0f18..cf0137d09 100644 --- a/OVP/D3D9Client/D3D9Effect.cpp +++ b/OVP/D3D9Client/D3D9Effect.cpp @@ -16,7 +16,7 @@ D3D9Client * D3D9Effect::gc = 0; ID3DXEffect * D3D9Effect::FX = 0; LPDIRECT3DVERTEXBUFFER9 D3D9Effect::VB = 0; -D3DXVECTOR4 D3D9Effect::atm_color; // Earth glow color +FVECTOR4 D3D9Effect::atm_color; // Earth glow color D3D9MatExt D3D9Effect::mfdmat; D3D9MatExt D3D9Effect::defmat; @@ -64,7 +64,6 @@ D3DXHANDLE D3D9Effect::eGT = 0; // Mesh group transformation matrix D3DXHANDLE D3D9Effect::eMat = 0; // Material D3DXHANDLE D3D9Effect::eWater = 0; // Water D3DXHANDLE D3D9Effect::eMtrl = 0; -D3DXHANDLE D3D9Effect::eTune = 0; D3DXHANDLE D3D9Effect::eSun = 0; D3DXHANDLE D3D9Effect::eNight = 0; D3DXHANDLE D3D9Effect::eLights = 0; // Additional light sources @@ -75,15 +74,16 @@ D3DXHANDLE D3D9Effect::eTex3 = 0; // Tertiary texture D3DXHANDLE D3D9Effect::eSpecMap = 0; D3DXHANDLE D3D9Effect::eEmisMap = 0; D3DXHANDLE D3D9Effect::eEnvMapA = 0; -D3DXHANDLE D3D9Effect::eEnvMapB = 0; D3DXHANDLE D3D9Effect::eReflMap = 0; D3DXHANDLE D3D9Effect::eRghnMap = 0; D3DXHANDLE D3D9Effect::eMetlMap = 0; D3DXHANDLE D3D9Effect::eHeatMap = 0; -D3DXHANDLE D3D9Effect::eShadowMap = 0; D3DXHANDLE D3D9Effect::eTranslMap = 0; D3DXHANDLE D3D9Effect::eTransmMap = 0; D3DXHANDLE D3D9Effect::eIrradMap = 0; +D3DXHANDLE D3D9Effect::eAmbientMap = 0; +D3DXHANDLE D3D9Effect::eCombinedMap = 0; +D3DXHANDLE D3D9Effect::eCombSunMap = 0; D3DXHANDLE D3D9Effect::eSpecularMode = 0; D3DXHANDLE D3D9Effect::eHazeMode = 0; @@ -95,6 +95,8 @@ D3DXHANDLE D3D9Effect::eMix = 0; // FLOAT Auxiliary factor/multiplier D3DXHANDLE D3D9Effect::eFogDensity = 0; // D3DXHANDLE D3D9Effect::ePointScale = 0; D3DXHANDLE D3D9Effect::eSHD = 0; +D3DXHANDLE D3D9Effect::eSHDPx = 0; +D3DXHANDLE D3D9Effect::eSHDSubRect = 0; D3DXHANDLE D3D9Effect::eAtmColor = 0; D3DXHANDLE D3D9Effect::eProxySize = 0; @@ -113,22 +115,24 @@ D3DXHANDLE D3D9Effect::eRghnSw = 0; // BOOL D3DXHANDLE D3D9Effect::eShadowToggle = 0; // BOOL D3DXHANDLE D3D9Effect::eEnvMapEnable = 0; // BOOL D3DXHANDLE D3D9Effect::eInSpace = 0; // BOOL -D3DXHANDLE D3D9Effect::eNoColor = 0; // BOOL D3DXHANDLE D3D9Effect::eLightsEnabled = 0; // BOOL -D3DXHANDLE D3D9Effect::eTuneEnabled = 0; // BOOL D3DXHANDLE D3D9Effect::eBaseBuilding = 0; // BOOL +D3DXHANDLE D3D9Effect::eCockpit = 0; // BOOL D3DXHANDLE D3D9Effect::eOITEnable = 0; // BOOL // -------------------------------------------------------------- D3DXHANDLE D3D9Effect::eExposure = 0; D3DXHANDLE D3D9Effect::eCameraPos = 0; D3DXHANDLE D3D9Effect::eNorth = 0; D3DXHANDLE D3D9Effect::eEast = 0; +D3DXHANDLE D3D9Effect::eVCAmbient = 0; D3DXHANDLE D3D9Effect::eDistScale = 0; D3DXHANDLE D3D9Effect::eRadius = 0; D3DXHANDLE D3D9Effect::eAttennuate = 0; D3DXHANDLE D3D9Effect::eInScatter = 0; D3DXHANDLE D3D9Effect::eInvProxySize = 0; D3DXHANDLE D3D9Effect::eGlowConst = 0; +D3DXHANDLE D3D9Effect::eNoColor = 0; +D3DXHANDLE D3D9Effect::eVCIrrad = 0; // -------------------------------------------------------------- D3DXHANDLE D3D9Effect::eGlobalAmb = 0; D3DXHANDLE D3D9Effect::eSunAppRad = 0; @@ -192,7 +196,7 @@ NTVERTEX exhaust_vtx[8] = { // TotalWeight = 21.337518, Count = 27, x - balance = -0.145075, y - balance = -0.012734 /* -static D3DXVECTOR3 shadow_kernel[27] = { +static FVECTOR3 shadow_kernel[27] = { { -0.2607f, -0.9643f, 0.9995f }, { -0.7879f, -0.5824f, 0.9898f }, { -0.3796f, -0.6389f, 0.8620f }, @@ -223,7 +227,7 @@ static D3DXVECTOR3 shadow_kernel[27] = { }; */ -static D3DXVECTOR3 shadow_kernel[27] = { +static FVECTOR3 shadow_kernel[27] = { { -0.0000f, 0.0000f, 1.0000f }, { 0.1915f, 0.0188f, 1.0000f }, { 0.0558f, -0.2664f, 1.0000f }, @@ -338,8 +342,12 @@ void D3D9Effect::D3D9TechInit(D3D9Client *_gc, LPDIRECT3DDEVICE9 _pDev, const ch if (Config->ShadowFilter >= 3) sprintf_s((char*)macro[5].Definition, 32, "%f", 0.0285f); else sprintf_s((char*)macro[5].Definition, 32, "%f", 1.0f / 27.0f); // 0.04634f); // ------------------------------------------------------------------------------ + macro[6].Name = "CASCOUNT"; + macro[6].Definition = new char[32]; + sprintf_s((char*)macro[6].Definition, 32, "%d", Config->VCCascadeCount); + // ------------------------------------------------------------------------------ - int m = 6; + int m = 7; if (Config->EnableGlass) macro[m++].Name = "_GLASS"; if (Config->EnableMeshDbg) macro[m++].Name = "_DEBUG"; if (Config->EnvMapMode) macro[m++].Name = "_ENVMAP"; @@ -426,13 +434,14 @@ void D3D9Effect::D3D9TechInit(D3D9Client *_gc, LPDIRECT3DDEVICE9 _pDev, const ch eSwitch = FX->GetParameterByName(0,"gPBRSw"); eRghnSw = FX->GetParameterByName(0,"gRghnSw"); eInSpace = FX->GetParameterByName(0,"gInSpace"); - eNoColor = FX->GetParameterByName(0,"gNoColor"); eLightsEnabled = FX->GetParameterByName(0,"gLightsEnabled"); - eTuneEnabled = FX->GetParameterByName(0,"gTuneEnabled"); eBaseBuilding = FX->GetParameterByName(0,"gBaseBuilding"); + eCockpit = FX->GetParameterByName(0,"gCockpit"); eOITEnable = FX->GetParameterByName(0,"gOITEnable"); - // General parameters -------------------------------------------------- + // General parameters -------------------------------------------------- + eNoColor = FX->GetParameterByName(0, "gNoColor"); + eVCIrrad = FX->GetParameterByName(0, "gVCIrrad"); eSpecularMode = FX->GetParameterByName(0,"gSpecMode"); eLights = FX->GetParameterByName(0,"gLights"); eColor = FX->GetParameterByName(0,"gColor"); @@ -443,12 +452,15 @@ void D3D9Effect::D3D9TechInit(D3D9Client *_gc, LPDIRECT3DDEVICE9 _pDev, const ch eCameraPos = FX->GetParameterByName(0,"gCameraPos"); eNorth = FX->GetParameterByName(0,"gNorth"); eEast = FX->GetParameterByName(0,"gEast"); + eVCAmbient = FX->GetParameterByName(0,"gVCAmbient"); ePointScale = FX->GetParameterByName(0,"gPointScale"); eMix = FX->GetParameterByName(0,"gMix"); eTime = FX->GetParameterByName(0,"gTime"); eMtrlAlpha = FX->GetParameterByName(0,"gMtrlAlpha"); eGlowConst = FX->GetParameterByName(0,"gGlowConst"); eSHD = FX->GetParameterByName(0,"gSHD"); + eSHDPx = FX->GetParameterByName(0,"gSHDPx"); + eSHDSubRect = FX->GetParameterByName(0,"gSHDSubRect"); eKernel = FX->GetParameterByName(0,"kernel"); eAtmoParams = FX->GetParameterByName(0,"gAtmo"); // ---------------------------------------------------------------------- @@ -461,7 +473,6 @@ void D3D9Effect::D3D9TechInit(D3D9Client *_gc, LPDIRECT3DDEVICE9 _pDev, const ch eMat = FX->GetParameterByName(0,"gMat"); eWater = FX->GetParameterByName(0,"gWater"); eMtrl = FX->GetParameterByName(0,"gMtrl"); - eTune = FX->GetParameterByName(0,"gTune"); // ---------------------------------------------------------------------- eTex0 = FX->GetParameterByName(0,"gTex0"); eTex1 = FX->GetParameterByName(0,"gTex1"); @@ -469,15 +480,16 @@ void D3D9Effect::D3D9TechInit(D3D9Client *_gc, LPDIRECT3DDEVICE9 _pDev, const ch eSpecMap = FX->GetParameterByName(0,"gSpecMap"); eEmisMap = FX->GetParameterByName(0,"gEmisMap"); eEnvMapA = FX->GetParameterByName(0,"gEnvMapA"); - eEnvMapB = FX->GetParameterByName(0,"gEnvMapB"); eReflMap = FX->GetParameterByName(0,"gReflMap"); eRghnMap = FX->GetParameterByName(0,"gRghnMap"); eMetlMap = FX->GetParameterByName(0,"gMetlMap"); eHeatMap = FX->GetParameterByName(0,"gHeatMap"); - eShadowMap = FX->GetParameterByName(0,"gShadowMap"); eTranslMap = FX->GetParameterByName(0, "gTranslMap"); eTransmMap = FX->GetParameterByName(0, "gTransmMap"); eIrradMap = FX->GetParameterByName(0,"gIrradianceMap"); + eAmbientMap = FX->GetParameterByName(0, "gAmbientMap"); + eCombinedMap = FX->GetParameterByName(0, "gCombinedMap"); + eCombSunMap = FX->GetParameterByName(0, "gCombinedSunMap"); // Atmosphere ----------------------------------------------------------- eGlobalAmb = FX->GetParameterByName(0,"gGlobalAmb"); @@ -497,9 +509,9 @@ void D3D9Effect::D3D9TechInit(D3D9Client *_gc, LPDIRECT3DDEVICE9 _pDev, const ch // FX->SetInt(eHazeMode, 0); FX->SetBool(eInSpace, false); - FX->SetVector(eAttennuate, ptr(D3DXVECTOR4(1,1,1,1))); - FX->SetVector(eInScatter, ptr(D3DXVECTOR4(0,0,0,0))); - FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); + FX->SetVector(eAttennuate, _DX(F4_One)); + FX->SetVector(eInScatter, _DX(F4_Zero)); + FX->SetVector(eColor, _DX(F4_Zero)); //if (Config->ShadowFilter>=3) FX->SetValue(eKernel, &shadow_kernel2, sizeof(shadow_kernel2)); FX->SetValue(eKernel, &shadow_kernel, sizeof(shadow_kernel)); @@ -512,13 +524,13 @@ void D3D9Effect::D3D9TechInit(D3D9Client *_gc, LPDIRECT3DDEVICE9 _pDev, const ch // Create a Circle Mesh -------------------------------------------- // if (!VB) { - HR(pDev->CreateVertexBuffer(256 * sizeof(D3DXVECTOR3), 0, 0, D3DPOOL_DEFAULT, &VB, NULL)); + HR(pDev->CreateVertexBuffer(256 * sizeof(FVECTOR3), 0, 0, D3DPOOL_DEFAULT, &VB, NULL)); - D3DXVECTOR3 *pVert; + FVECTOR3 *pVert; if (VB->Lock(0, 0, (void **)&pVert, 0) == S_OK) { float angle = 0.0f, step = float(PI2) / 255.0f; - pVert[0] = D3DXVECTOR3(0, 0, 0); + pVert[0] = FVECTOR3(0, 0, 0); for (int i = 1; i < 256; i++) { pVert[i].x = 0; pVert[i].y = cos(angle); @@ -532,9 +544,9 @@ void D3D9Effect::D3D9TechInit(D3D9Client *_gc, LPDIRECT3DDEVICE9 _pDev, const ch } -void D3D9Effect::SetViewProjMatrix(LPD3DXMATRIX pVP) +void D3D9Effect::SetViewProjMatrix(FMATRIX4* pVP) { - FX->SetMatrix(eVP, pVP); + FX->SetMatrix(eVP, _DX(pVP)); } @@ -560,7 +572,7 @@ void D3D9Effect::UpdateEffectCamera(OBJHANDLE hPlanet) float radlimit = float(rad) + 1.0f; float rho0 = 1.0f; - atm_color = D3DXVECTOR4(0.5f, 0.5f, 0.5f, 1.0f); + atm_color = FVECTOR4(0.5f, 0.5f, 0.5f, 1.0f); const ATMCONST *atm = oapiGetPlanetAtmConstants(hPlanet); VESSEL *hVessel = oapiGetFocusInterface(); @@ -581,14 +593,14 @@ void D3D9Effect::UpdateEffectCamera(OBJHANDLE hPlanet) if (atm) { radlimit = float(atm->radlimit); - atm_color = D3DXVEC4(atm->color0, 1.0f); + atm_color = FVECTOR4(FVECTOR3(atm->color0), 1.0f); rho0 = float(atm->rho0); } float av = (atm_color.x + atm_color.y + atm_color.z) * 0.3333333f; float fc = 1.5f; float alt = 1.0f - pow(float(hVessel->GetAtmDensity()/rho0), 0.2f); - atm_color += D3DXVECTOR4(av,av,av,1.0)*fc; + atm_color += FVECTOR4(av,av,av,1.0f)*fc; atm_color *= 1.0f/(fc+1.0f); atm_color *= float(Config->PlanetGlow) * alt; @@ -597,12 +609,12 @@ void D3D9Effect::UpdateEffectCamera(OBJHANDLE hPlanet) float proxy_size = asin(min(1.0f, rl)) + float(40.0*PI/180.0); if (rl>1e-3) atm_color *= pow(rl, 1.5f); - else atm_color = D3DXVECTOR4(0,0,0,1); + else atm_color = FVECTOR4(0.0f, 0.0f, 0.0f, 1.0f); - FX->SetValue(eEast, ptr(D3DXVEC(east)), sizeof(D3DXVECTOR3)); - FX->SetValue(eNorth, ptr(D3DXVEC(north)), sizeof(D3DXVECTOR3)); - FX->SetValue(eCameraPos, ptr(D3DXVEC(cam)), sizeof(D3DXVECTOR3)); - FX->SetVector(eRadius, ptr(D3DXVECTOR4((float)rad, radlimit, (float)len, (float)(len-rad)))); + FX->SetValue(eEast, _DX(FVECTOR3(east)), sizeof(FVECTOR3)); + FX->SetValue(eNorth, _DX(FVECTOR3(north)), sizeof(FVECTOR3)); + FX->SetValue(eCameraPos, _DX(FVECTOR3(cam)), sizeof(FVECTOR3)); + FX->SetVector(eRadius, _DX(FVECTOR4((float)rad, radlimit, (float)len, (float)(len-rad)))); FX->SetFloat(ePointScale, 0.5f*float(height)/tan(ap)); FX->SetFloat(eProxySize, cos(proxy_size)); FX->SetFloat(eInvProxySize, 1.0f/(1.0f-cos(proxy_size))); @@ -614,8 +626,8 @@ void D3D9Effect::UpdateEffectCamera(OBJHANDLE hPlanet) // void D3D9Effect::EnablePlanetGlow(bool bEnabled) { - if (bEnabled) FX->SetVector(eAtmColor, &atm_color); - else FX->SetVector(eAtmColor, ptr(D3DXVECTOR4(0,0,0,0))); + if (bEnabled) FX->SetVector(eAtmColor, _DX(atm_color)); + else FX->SetVector(eAtmColor, _DX(F4_Zero)); } @@ -650,7 +662,7 @@ void D3D9Effect::InitLegacyAtmosphere(OBJHANDLE hPlanet, float GlobalAmbient) // =========================================================================================== // -void D3D9Effect::Render2DPanel(const MESHGROUP *mg, const SURFHANDLE pTex, const LPD3DXMATRIX pW, float alpha, float scale, bool additive) +void D3D9Effect::Render2DPanel(const MESHGROUP *mg, const SURFHANDLE pTex, const FMATRIX4* pW, float alpha, float scale, bool additive) { UINT numPasses = 0; if (!pTex || !mg || !pW) return; @@ -658,7 +670,7 @@ void D3D9Effect::Render2DPanel(const MESHGROUP *mg, const SURFHANDLE pTex, const if (SURFACE(pTex)->IsPowerOfTwo() || (!gc->IsLimited())) FX->SetTechnique(ePanelTech); // ANISOTROPIC filter else FX->SetTechnique(ePanelTechB); // POINT filter (for non-pow2 conditional) - HR(FX->SetMatrix(eW, pW)); + HR(FX->SetMatrix(eW, _DX(pW))); if (pTex) FX->SetTexture(eTex0, SURFACE(pTex)->GetTexture()); else FX->SetTexture(eTex0, NULL); @@ -682,7 +694,7 @@ void D3D9Effect::Render2DPanel(const MESHGROUP *mg, const SURFHANDLE pTex, const // =========================================================================================== // -void D3D9Effect::RenderReEntry(const SURFHANDLE pTex, const LPD3DXVECTOR3 vPosA, const LPD3DXVECTOR3 vPosB, const LPD3DXVECTOR3 vDir, float alpha_a, float alpha_b, float size) +void D3D9Effect::RenderReEntry(const SURFHANDLE pTex, const FVECTOR3* vPosA, const FVECTOR3* vPosB, const FVECTOR3* vDir, float alpha_a, float alpha_b, float size) { static WORD ReentryIdx[6] = {0,1,2, 3,2,1}; @@ -703,9 +715,8 @@ void D3D9Effect::RenderReEntry(const SURFHANDLE pTex, const LPD3DXVECTOR3 vPosA, }; UINT numPasses = 0; - D3DXMATRIX WA, WB; - D3DXVECTOR3 vCam; - D3DXVec3Normalize(&vCam, vPosA); + FMATRIX4 WA, WB; + FVECTOR3 vCam = unit(*vPosA); D3DMAT_CreateX_Billboard(&vCam, vPosB, size*(0.8f+x*0.02f), &WB); D3DMAT_CreateX_Billboard(&vCam, vPosA, vDir, size, size, &WA); @@ -714,12 +725,12 @@ void D3D9Effect::RenderReEntry(const SURFHANDLE pTex, const LPD3DXVECTOR3 vPosA, FX->SetTechnique(eExhaust); FX->SetTexture(eTex0, SURFACE(pTex)->GetTexture()); FX->SetFloat(eMix, alpha_b); - FX->SetMatrix(eW, &WB); + FX->SetMatrix(eW, _DX(WB)); FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); FX->BeginPass(0); pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &ReentryIdx, D3DFMT_INDEX16, &ReentryVtxB, sizeof(NTVERTEX)); FX->SetFloat(eMix, alpha_a); - FX->SetMatrix(eW, &WA); + FX->SetMatrix(eW, _DX(WA)); FX->CommitChanges(); pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &ReentryIdx, D3DFMT_INDEX16, &ReentryVtxA, sizeof(NTVERTEX)); @@ -731,14 +742,14 @@ void D3D9Effect::RenderReEntry(const SURFHANDLE pTex, const LPD3DXVECTOR3 vPosA, // =========================================================================================== // This is a special rendering routine used to render beacons // -void D3D9Effect::RenderSpot(float alpha, const LPD3DXCOLOR pColor, const LPD3DXMATRIX pW, SURFHANDLE pTex) +void D3D9Effect::RenderSpot(float alpha, const FVECTOR4* pColor, const FMATRIX4* pW, SURFHANDLE pTex) { UINT numPasses = 0; HR(pDev->SetVertexDeclaration(pNTVertexDecl)); HR(FX->SetTechnique(eSpotTech)); HR(FX->SetFloat(eMix, alpha)); - HR(FX->SetValue(eColor, pColor, sizeof(D3DXCOLOR))); - HR(FX->SetMatrix(eW, pW)); + HR(FX->SetValue(eColor, pColor, sizeof(FVECTOR4))); + HR(FX->SetMatrix(eW, _DX(pW))); HR(FX->SetTexture(eTex0, SURFACE(pTex)->GetTexture())); HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); HR(FX->BeginPass(0)); @@ -751,13 +762,13 @@ void D3D9Effect::RenderSpot(float alpha, const LPD3DXCOLOR pColor, const LPD3DXM // =========================================================================================== // Used by Render Star only // -void D3D9Effect::RenderBillboard(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex, float alpha) +void D3D9Effect::RenderBillboard(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex, float alpha) { UINT numPasses = 0; HR(pDev->SetVertexDeclaration(pNTVertexDecl)); HR(FX->SetTechnique(eSimple)); - HR(FX->SetMatrix(eW, pW)); + HR(FX->SetMatrix(eW, _DX(pW))); HR(FX->SetFloat(eMix, alpha)); HR(FX->SetTexture(eTex0, pTex)); HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); @@ -771,7 +782,7 @@ void D3D9Effect::RenderBillboard(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex, // =========================================================================================== // This is a special rendering routine used to render engine exhaust // -void D3D9Effect::RenderExhaust(const LPD3DXMATRIX pW, VECTOR3 &cdir, EXHAUSTSPEC *es, SURFHANDLE def) +void D3D9Effect::RenderExhaust(const FMATRIX4* pW, VECTOR3 &cdir, EXHAUSTSPEC *es, SURFHANDLE def) { SURFHANDLE pTex = SURFACE(es->tex); @@ -820,7 +831,7 @@ void D3D9Effect::RenderExhaust(const LPD3DXMATRIX pW, VECTOR3 &cdir, EXHAUSTSPEC HR(pDev->SetVertexDeclaration(pNTVertexDecl)); HR(FX->SetTechnique(eExhaust)); HR(FX->SetFloat(eMix, float(alpha))); - HR(FX->SetMatrix(eW, pW)); + HR(FX->SetMatrix(eW, _DX(pW))); HR(FX->SetTexture(eTex0, SURFACE(pTex)->GetTexture())); HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); HR(FX->BeginPass(0)); @@ -830,12 +841,12 @@ void D3D9Effect::RenderExhaust(const LPD3DXMATRIX pW, VECTOR3 &cdir, EXHAUSTSPEC } -void D3D9Effect::RenderBoundingBox(const LPD3DXMATRIX pW, const LPD3DXMATRIX pGT, const D3DXVECTOR4 *bmin, const D3DXVECTOR4 *bmax, const D3DXVECTOR4 *color) +void D3D9Effect::RenderBoundingBox(const FMATRIX4* pW, const FMATRIX4* pGT, const FVECTOR4 *bmin, const FVECTOR4 *bmax, const FVECTOR4 *color) { - D3DXMATRIX ident; - D3DXMatrixIdentity(&ident); + FMATRIX4 ident; + oapiMatrixIdentity(&ident); - static D3DVECTOR poly[10] = { + static FVECTOR3 poly[10] = { {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, @@ -848,7 +859,7 @@ void D3D9Effect::RenderBoundingBox(const LPD3DXMATRIX pW, const LPD3DXMATRIX pGT {0, 0, 1} }; - static D3DVECTOR list[6] = { + static FVECTOR3 list[6] = { {1, 0, 0}, {1, 0, 1}, {1, 1, 0}, @@ -859,87 +870,83 @@ void D3D9Effect::RenderBoundingBox(const LPD3DXMATRIX pW, const LPD3DXMATRIX pGT pDev->SetVertexDeclaration(pPositionDecl); - FX->SetMatrix(eW, pW); - FX->SetMatrix(eGT, pGT); - FX->SetVector(eAttennuate, bmin); - FX->SetVector(eInScatter, bmax); - FX->SetVector(eColor, color); + FX->SetMatrix(eW, _DX(pW)); + FX->SetMatrix(eGT, _DX(pGT)); + FX->SetVector(eAttennuate, _DX(bmin)); + FX->SetVector(eInScatter, _DX(bmax)); + FX->SetVector(eColor, _DX(color)); FX->SetTechnique(eBBTech); UINT numPasses = 0; FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); FX->BeginPass(0); - pDev->DrawPrimitiveUP(D3DPT_LINESTRIP, 9, &poly, sizeof(D3DVECTOR)); - pDev->DrawPrimitiveUP(D3DPT_LINELIST, 3, &list, sizeof(D3DVECTOR)); + pDev->DrawPrimitiveUP(D3DPT_LINESTRIP, 9, &poly, sizeof(FVECTOR3)); + pDev->DrawPrimitiveUP(D3DPT_LINELIST, 3, &list, sizeof(FVECTOR3)); FX->EndPass(); FX->End(); } -void D3D9Effect::RenderTileBoundingBox(const LPD3DXMATRIX pW, VECTOR4 *pVtx, const LPD3DXVECTOR4 color) +void D3D9Effect::RenderTileBoundingBox(const FMATRIX4* pW, VECTOR4 *pVtx, const FVECTOR4* color) { - D3DXVECTOR3 poly[8]; + FVECTOR3 poly[8]; - for (int i=0;i<8;i++) poly[i] = D3DXVEC(pVtx[i]); + for (int i=0;i<8;i++) poly[i] = FVECTOR4(pVtx[i]).xyz; WORD idc1[10] = { 0, 1, 3, 2, 0, 4, 5, 7, 6, 4 }; WORD idc2[6] = { 1, 5, 3, 7, 2, 6}; pDev->SetVertexDeclaration(pPositionDecl); - FX->SetMatrix(eW, pW); - FX->SetVector(eColor, color); + FX->SetMatrix(eW, _DX(pW)); + FX->SetVector(eColor, _DX(color)); FX->SetTechnique(eTBBTech); UINT numPasses = 0; FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); FX->BeginPass(0); - pDev->DrawIndexedPrimitiveUP(D3DPT_LINESTRIP, 0, 8, 9, &idc1, D3DFMT_INDEX16, &poly, sizeof(D3DXVECTOR3)); - pDev->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, 8, 3, &idc2, D3DFMT_INDEX16, &poly, sizeof(D3DXVECTOR3)); + pDev->DrawIndexedPrimitiveUP(D3DPT_LINESTRIP, 0, 8, 9, &idc1, D3DFMT_INDEX16, &poly, sizeof(FVECTOR3)); + pDev->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, 8, 3, &idc2, D3DFMT_INDEX16, &poly, sizeof(FVECTOR3)); FX->EndPass(); FX->End(); } -void D3D9Effect::RenderLines(const D3DXVECTOR3 *pVtx, const WORD *pIdx, int nVtx, int nIdx, const D3DXMATRIX *pW, DWORD color) +void D3D9Effect::RenderLines(const FVECTOR3 *pVtx, const WORD *pIdx, int nVtx, int nIdx, const FMATRIX4 *pW, DWORD color) { UINT numPasses = 0; pDev->SetVertexDeclaration(pPositionDecl); - FX->SetMatrix(eW, pW); - FX->SetVector(eColor, (const D3DXVECTOR4 *)ptr(D3DXCOLOR(color))); + FX->SetMatrix(eW, _DX(pW)); + FX->SetVector(eColor, _DX(FVECTOR4(color))); FX->SetTechnique(eTBBTech); FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); FX->BeginPass(0); - pDev->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, nVtx, nIdx/2, pIdx, D3DFMT_INDEX16, pVtx, sizeof(D3DXVECTOR3)); + pDev->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, nVtx, nIdx/2, pIdx, D3DFMT_INDEX16, pVtx, sizeof(FVECTOR3)); FX->EndPass(); FX->End(); } -void D3D9Effect::RenderBoundingSphere(const LPD3DXMATRIX pW, const LPD3DXMATRIX pGT, const D3DXVECTOR4 *bs, const D3DXVECTOR4 *color) +void D3D9Effect::RenderBoundingSphere(const FMATRIX4* pW, const FMATRIX4* pGT, const FVECTOR4 *bs, const FVECTOR4 *color) { - D3DXMATRIX mW; - - D3DXVECTOR3 vCam; - D3DXVECTOR3 vPos; - - D3DXVec3TransformCoord(&vPos, ptr(D3DXVECTOR3(bs->x, bs->y, bs->z)), pW); + FMATRIX4 mW; + FVECTOR3 vPos = oapiTransformCoord(&(FVECTOR3(bs->x, bs->y, bs->z)), pW); + FVECTOR3 vCam = unit(vPos); - D3DXVec3Normalize(&vCam, &vPos); D3DMAT_CreateX_Billboard(&vCam, &vPos, bs->w, &mW); pDev->SetVertexDeclaration(pPositionDecl); - FX->SetMatrix(eW, &mW); - FX->SetVector(eColor, color); + FX->SetMatrix(eW, _DX(mW)); + FX->SetVector(eColor, _DX(color)); FX->SetTechnique(eBSTech); UINT numPasses = 0; FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); FX->BeginPass(0); - pDev->SetStreamSource(0, VB, 0, sizeof(D3DXVECTOR3)); + pDev->SetStreamSource(0, VB, 0, sizeof(FVECTOR3)); pDev->DrawPrimitive(D3DPT_LINESTRIP, 0, 255); FX->EndPass(); @@ -949,9 +956,9 @@ void D3D9Effect::RenderBoundingSphere(const LPD3DXMATRIX pW, const LPD3DXMATRIX // =========================================================================================== // This is a special rendering routine used to render (grapple point) arrows // -void D3D9Effect::RenderArrow(OBJHANDLE hObj, const VECTOR3 *ofs, const VECTOR3 *dir, const VECTOR3 *rot, float size, const D3DXCOLOR *pColor) +void D3D9Effect::RenderArrow(OBJHANDLE hObj, const VECTOR3 *ofs, const VECTOR3 *dir, const VECTOR3 *rot, float size, const FVECTOR4 *pColor) { - static D3DVECTOR arrow[18] = { + static FVECTOR3 arrow[18] = { // Head (front- & back-face) {0.0, 0.0, 0.0}, {0.0,-1.0, 1.0}, @@ -976,7 +983,7 @@ void D3D9Effect::RenderArrow(OBJHANDLE hObj, const VECTOR3 *ofs, const VECTOR3 * }; MATRIX3 grot; - D3DXMATRIX W; + FMATRIX4 W; VECTOR3 camp, gpos; oapiGetRotationMatrix(hObj, &grot); @@ -986,36 +993,36 @@ void D3D9Effect::RenderArrow(OBJHANDLE hObj, const VECTOR3 *ofs, const VECTOR3 * VECTOR3 pos = gpos - camp; if (ofs) pos += mul (grot, *ofs); - VECTOR3 z = mul (grot, unit(*dir)) * size; - VECTOR3 y = mul (grot, unit(*rot)) * size; - VECTOR3 x = mul (grot, unit(crossp(*dir, *rot))) * size; + VECTOR3 z = mul (grot, unit(*dir)) * double(size); + VECTOR3 y = mul (grot, unit(*rot)) * double(size); + VECTOR3 x = mul (grot, unit(crossp(*dir, *rot))) * double(size); - D3DXMatrixIdentity(&W); + oapiMatrixIdentity(&W); - W._11 = float(x.x); - W._12 = float(x.y); - W._13 = float(x.z); + W.m11 = float(x.x); + W.m12 = float(x.y); + W.m13 = float(x.z); - W._21 = float(y.x); - W._22 = float(y.y); - W._23 = float(y.z); + W.m21 = float(y.x); + W.m22 = float(y.y); + W.m23 = float(y.z); - W._31 = float(z.x); - W._32 = float(z.y); - W._33 = float(z.z); + W.m31 = float(z.x); + W.m32 = float(z.y); + W.m33 = float(z.z); - W._41 = float(pos.x); - W._42 = float(pos.y); - W._43 = float(pos.z); + W.m41 = float(pos.x); + W.m42 = float(pos.y); + W.m43 = float(pos.z); UINT numPasses = 0; HR(pDev->SetVertexDeclaration(pPositionDecl)); // Position only vertex decleration HR(FX->SetTechnique(eArrowTech)); // Use arrow shader - HR(FX->SetValue(eColor, pColor, sizeof(D3DXCOLOR))); // Setup arrow color - HR(FX->SetMatrix(eW, &W)); + HR(FX->SetValue(eColor, pColor, sizeof(FVECTOR4))); // Setup arrow color + HR(FX->SetMatrix(eW, _DX(W))); HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); HR(FX->BeginPass(0)); - HR(pDev->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 6, &arrow, sizeof(D3DVECTOR))); // Draw 6 triangles un-indexed + HR(pDev->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 6, &arrow, sizeof(FVECTOR3))); // Draw 6 triangles un-indexed HR(FX->EndPass()); HR(FX->End()); } diff --git a/OVP/D3D9Client/D3D9Effect.h b/OVP/D3D9Client/D3D9Effect.h index c9213f372..69f2174e4 100644 --- a/OVP/D3D9Client/D3D9Effect.h +++ b/OVP/D3D9Client/D3D9Effect.h @@ -9,10 +9,11 @@ #define D3D9SM_SPHERE 0x01 #define D3D9SM_ARROW 0x02 +#define D3D9SM_BOX 0x03 #include "D3D9Client.h" #include -#include +#include "MathAPI.h" // NOTE: a "bool" in HLSL is 32bits (i.e. int) // Must match with counterpart in D3D9Client.fx @@ -27,6 +28,9 @@ struct TexFlow { BOOL Norm; // Enable normal map BOOL Metl; // Enable metalness map BOOL Heat; // Enable heat map + BOOL Baked; // Enable pre-baked maps + BOOL BakedAO; // Enable pre-baked AO map + BOOL BakedAmb; // Enable pre-baked Ambient light map }; @@ -53,23 +57,23 @@ class D3D9Effect { static void EnablePlanetGlow(bool bEnabled); static void UpdateEffectCamera(OBJHANDLE hPlanet); static void InitLegacyAtmosphere(OBJHANDLE hPlanet, float GlobalAmbient); - static void SetViewProjMatrix(LPD3DXMATRIX pVP); - - static void RenderLines(const D3DXVECTOR3 *pVtx, const WORD *pIdx, int nVtx, int nIdx, const D3DXMATRIX *pW, DWORD color); - static void RenderTileBoundingBox(const LPD3DXMATRIX pW, VECTOR4 *pVtx, const LPD3DXVECTOR4 color); - static void RenderBoundingBox(const LPD3DXMATRIX pW, const LPD3DXMATRIX pGT, const D3DXVECTOR4 *bmin, const D3DXVECTOR4 *bmax, const D3DXVECTOR4 *color); - static void RenderBoundingSphere(const LPD3DXMATRIX pW, const LPD3DXMATRIX pGT, const D3DXVECTOR4 *bs, const D3DXVECTOR4 *color); - static void RenderBillboard(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex, float alpha = 1.0f); - static void RenderExhaust(const LPD3DXMATRIX pW, VECTOR3 &cdir, EXHAUSTSPEC *es, SURFHANDLE def); - static void RenderSpot(float intens, const LPD3DXCOLOR color, const LPD3DXMATRIX pW, SURFHANDLE pTex); - static void Render2DPanel(const MESHGROUP *mg, const SURFHANDLE pTex, const LPD3DXMATRIX pW, float alpha, float scale, bool additive); - static void RenderReEntry(const SURFHANDLE pTex, const LPD3DXVECTOR3 vPosA, const LPD3DXVECTOR3 vPosB, const LPD3DXVECTOR3 vDir, float alpha_a, float alpha_b, float size); - static void RenderArrow(OBJHANDLE hObj, const VECTOR3 *ofs, const VECTOR3 *dir, const VECTOR3 *rot, float size, const D3DXCOLOR *pColor); + static void SetViewProjMatrix(FMATRIX4* pVP); + + static void RenderLines(const FVECTOR3 *pVtx, const WORD *pIdx, int nVtx, int nIdx, const FMATRIX4 *pW, DWORD color); + static void RenderTileBoundingBox(const FMATRIX4* pW, VECTOR4 *pVtx, const FVECTOR4* color); + static void RenderBoundingBox(const FMATRIX4* pW, const FMATRIX4* pGT, const FVECTOR4 *bmin, const FVECTOR4 *bmax, const FVECTOR4 *color); + static void RenderBoundingSphere(const FMATRIX4* pW, const FMATRIX4* pGT, const FVECTOR4 *bs, const FVECTOR4 *color); + static void RenderBillboard(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex, float alpha = 1.0f); + static void RenderExhaust(const FMATRIX4* pW, VECTOR3 &cdir, EXHAUSTSPEC *es, SURFHANDLE def); + static void RenderSpot(float intens, const FVECTOR4* color, const FMATRIX4* pW, SURFHANDLE pTex); + static void Render2DPanel(const MESHGROUP *mg, const SURFHANDLE pTex, const FMATRIX4* pW, float alpha, float scale, bool additive); + static void RenderReEntry(const SURFHANDLE pTex, const FVECTOR3* vPosA, const FVECTOR3* vPosB, const FVECTOR3* vDir, float alpha_a, float alpha_b, float size); + static void RenderArrow(OBJHANDLE hObj, const VECTOR3 *ofs, const VECTOR3 *dir, const VECTOR3 *rot, float size, const FVECTOR4 *pColor); static LPDIRECT3DDEVICE9 pDev; ///< Static (global) render device static LPDIRECT3DVERTEXBUFFER9 VB; ///< Static (global) Vertex buffer pointer - static D3DXVECTOR4 atm_color; ///< Earth glow color + static FVECTOR4 atm_color; ///< Earth glow color // Rendering Technique related parameters static ID3DXEffect *FX; @@ -116,7 +120,6 @@ class D3D9Effect { // Lighting related parameters ------------------------------------ static D3DXHANDLE eMtrl; - static D3DXHANDLE eTune; static D3DXHANDLE eMat; ///< Material static D3DXHANDLE eWater; ///< Water static D3DXHANDLE eSun; ///< Sun @@ -131,16 +134,17 @@ class D3D9Effect { static D3DXHANDLE eShadowToggle; ///< BOOL static D3DXHANDLE eEnvMapEnable; ///< BOOL static D3DXHANDLE eInSpace; ///< BOOL - static D3DXHANDLE eNoColor; ///< BOOL static D3DXHANDLE eLightsEnabled;///< BOOL static D3DXHANDLE eBaseBuilding; ///< BOOL - static D3DXHANDLE eTuneEnabled; ///< BOOL + static D3DXHANDLE eCockpit; ///< BOOL static D3DXHANDLE eFresnel; ///< BOOL static D3DXHANDLE eSwitch; ///< BOOL static D3DXHANDLE eRghnSw; ///< BOOL static D3DXHANDLE eTextured; ///< BOOL static D3DXHANDLE eOITEnable; ///< BOOL static D3DXHANDLE eInvProxySize; + static D3DXHANDLE eNoColor; + static D3DXHANDLE eVCIrrad; static D3DXHANDLE eMix; ///< FLOAT Auxiliary factor/multiplier static D3DXHANDLE eColor; ///< Auxiliary color input static D3DXHANDLE eFogColor; ///< Fog color input @@ -152,6 +156,7 @@ class D3D9Effect { static D3DXHANDLE eCameraPos; static D3DXHANDLE eNorth; static D3DXHANDLE eEast; + static D3DXHANDLE eVCAmbient; static D3DXHANDLE eDistScale; static D3DXHANDLE eGlowConst; static D3DXHANDLE eRadius; @@ -163,6 +168,8 @@ class D3D9Effect { static D3DXHANDLE eAttennuate; static D3DXHANDLE eInScatter; static D3DXHANDLE eSHD; + static D3DXHANDLE eSHDPx; + static D3DXHANDLE eSHDSubRect; static D3DXHANDLE eNight; // Textures -------------------------------------------------------- @@ -172,15 +179,16 @@ class D3D9Effect { static D3DXHANDLE eSpecMap; static D3DXHANDLE eEmisMap; static D3DXHANDLE eEnvMapA; - static D3DXHANDLE eEnvMapB; static D3DXHANDLE eReflMap; static D3DXHANDLE eMetlMap; static D3DXHANDLE eHeatMap; static D3DXHANDLE eRghnMap; static D3DXHANDLE eTranslMap; static D3DXHANDLE eTransmMap; - static D3DXHANDLE eShadowMap; static D3DXHANDLE eIrradMap; + static D3DXHANDLE eAmbientMap; + static D3DXHANDLE eCombinedMap; + static D3DXHANDLE eCombSunMap; // Legacy Atmosphere ----------------------------------------------- static D3DXHANDLE eGlobalAmb; diff --git a/OVP/D3D9Client/D3D9Frame.h b/OVP/D3D9Client/D3D9Frame.h index f1620b32c..532e8e26d 100644 --- a/OVP/D3D9Client/D3D9Frame.h +++ b/OVP/D3D9Client/D3D9Frame.h @@ -26,7 +26,7 @@ #define D3D9FRAME_H #include -#include +#include "MathAPI.h" #include "Orbitersdk.h" #include "D3D9Client.h" diff --git a/OVP/D3D9Client/D3D9Pad.cpp b/OVP/D3D9Client/D3D9Pad.cpp index e16640939..5377a5b22 100644 --- a/OVP/D3D9Client/D3D9Pad.cpp +++ b/OVP/D3D9Client/D3D9Pad.cpp @@ -51,7 +51,7 @@ oapi::Pen * defpen = 0; // void D3D9Pad::SinCos(int n, int k) { - pSinCos[k] = new D3DXVECTOR2[n]; + pSinCos[k] = new FVECTOR2[n]; float s = float(PI2) / float(n); float q = -s / 2.0f; for (int i = 0; iGetDesc(&tgt_desc); zfar = float(max(tgt_desc.Width, tgt_desc.Height)); - D3DXMatrixOrthoOffCenterLH(&mO, 0.0f, (float)tgt_desc.Width, (float)tgt_desc.Height, 0.0f, 0.0f, zfar); - vTarget = D3DXVECTOR4(2.0f / (float)tgt_desc.Width, 2.0f / (float)tgt_desc.Height, (float)tgt_desc.Width, (float)tgt_desc.Height); + D3DMAT_OrthoOffCenterLH(&mO, 0.0f, (float)tgt_desc.Width, (float)tgt_desc.Height, 0.0f, 0.0f, zfar); + vTarget = FVECTOR4(2.0f / (float)tgt_desc.Width, 2.0f / (float)tgt_desc.Height, (float)tgt_desc.Width, (float)tgt_desc.Height); + + pTgt = pRenderTgt; pDep = pDepthStensil; tgt = { 0, 0, (long)tgt_desc.Width, (long)tgt_desc.Height }; @@ -474,7 +476,7 @@ bool D3D9Pad::Flush(HPOLY hPoly) HR(pDev->SetVertexDeclaration(pSketchpadDecl)); HR(FX->SetFloat(eRandom, float(oapiRand()))); - HR(FX->SetVector(eTarget, &vTarget)); + HR(FX->SetVector(eTarget, _DX(vTarget))); HR(FX->SetTechnique(eSketch)); HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); @@ -633,19 +635,19 @@ void D3D9Pad::SetupDevice(Topo tNew) if (Change & (SKPCHG_TRANSFORM | SKPCHG_CLIPCONE)) { if (vmode == ORTHO) { - D3DXMATRIX mWVP; - D3DXMatrixMultiply(&mWVP, &mW, &mO); - HR(FX->SetMatrix(eWVP, &mWVP)); - HR(FX->SetMatrix(eVP, &mO)); - HR(FX->SetMatrix(eW, &mW)); + FMATRIX4 mWVP; + oapiMatrixMultiply(&mWVP, &mW, &mO); + HR(FX->SetMatrix(eWVP, _DX(mWVP))); + HR(FX->SetMatrix(eVP, _DX(mO))); + HR(FX->SetMatrix(eW, _DX(mW))); HR(FX->SetBool(eCovEn, false)); } else { - float d = float(tgt_desc.Height) * mP._22; + float d = float(tgt_desc.Height) * mP.m22; float f = atan(1.0f / d) * 1.7f; - D3DXMatrixMultiply(&mVP, &mV, &mP); - HR(FX->SetMatrix(eVP, &mVP)); - HR(FX->SetMatrix(eW, &mW)); + oapiMatrixMultiply(&mVP, &mV, &mP); + HR(FX->SetMatrix(eVP, _DX(mVP))); + HR(FX->SetMatrix(eW, _DX(mW))); HR(FX->SetFloat(eFov, f)); HR(FX->SetBool(eCovEn, ClipData[0].bEnable || ClipData[1].bEnable)); } @@ -658,9 +660,9 @@ void D3D9Pad::SetupDevice(Topo tNew) float offset = 0.0f; int w = int(ceil(GetPenWidth())); if ((w & 1) == 0) offset = 0.5f; - HR(FX->SetValue(ePen, &pencolor.fclr, sizeof(D3DXCOLOR))); + HR(FX->SetValue(ePen, &pencolor.fclr, sizeof(FVECTOR4))); HR(FX->SetBool(eDashEn, IsDashed())); - HR(FX->SetValue(eWidth, ptr(D3DXVECTOR3(GetPenWidth(), pattern*0.13f, offset)), sizeof(D3DXVECTOR3))); + HR(FX->SetValue(eWidth, &(FVECTOR3(GetPenWidth(), pattern*0.13f, offset)), sizeof(FVECTOR3))); } @@ -692,9 +694,9 @@ void D3D9Pad::SetupDevice(Topo tNew) // if (Change & SKPCHG_CLIPCONE) { if (ClipData[0].bEnable || ClipData[1].bEnable) { - HR(FX->SetValue(ePos, &ClipData[0].uDir, sizeof(D3DXVECTOR3))); - HR(FX->SetValue(ePos2, &ClipData[1].uDir, sizeof(D3DXVECTOR3))); - HR(FX->SetValue(eCov, ptr(D3DXVECTOR4(ClipData[0].ca, ClipData[0].dst, ClipData[1].ca, ClipData[1].dst)), sizeof(D3DXVECTOR4))); + HR(FX->SetValue(ePos, &ClipData[0].uDir, sizeof(FVECTOR3))); + HR(FX->SetValue(ePos2, &ClipData[1].uDir, sizeof(FVECTOR3))); + HR(FX->SetValue(eCov, &(FVECTOR4(ClipData[0].ca, ClipData[0].dst, ClipData[1].ca, ClipData[1].dst)), sizeof(FVECTOR4))); } } @@ -717,7 +719,7 @@ void D3D9Pad::SetupDevice(Topo tNew) if (Change & SKPCHG_TEXTURE) { HR(FX->SetTexture(eTex0, hTexture)); HR(FX->SetBool(eKeyEn, bColorKey)); - HR(FX->SetValue(eKey, &cColorKey, sizeof(D3DXCOLOR))); + HR(FX->SetValue(eKey, &cColorKey, sizeof(FVECTOR4))); } } @@ -727,7 +729,7 @@ void D3D9Pad::SetupDevice(Topo tNew) } } - HR(FX->SetVector(eSize, ptr(D3DXVECTOR4(tw, th, 1.0f, 1.0f)))); + HR(FX->SetVector(eSize, _DX(FVECTOR4(tw, th, 1.0f, 1.0f)))); HR(FX->SetBool(eTexEn, (hTexture != NULL))); HR(FX->SetBool(eFntEn, (hFontTex != NULL))); } @@ -796,7 +798,7 @@ Font *D3D9Pad::SetFont(Font *font) #ifdef SKPDBG LOGFONTA lf; - GetObjectA(font->GetGDIFont(), sizeof(LOGFONT), &lf); + GetObjHandle(font->GetGDIFont(), sizeof(LOGFONT), &lf); Log("SetFont(%s) Face=[%s] Height=%d Weight=%d", _PTR(font), lf.lfFaceName, lf.lfHeight, lf.lfWeight); #endif // No "Change" falgs required here, covered in SetFontTextureNative() @@ -945,8 +947,8 @@ void D3D9Pad::SetOrigin (int x, int y) #endif Change |= SKPCHG_TRANSFORM; - mW._41 = float(x); - mW._42 = float(y); + mW.m41 = float(x); + mW.m42 = float(y); } @@ -954,8 +956,8 @@ void D3D9Pad::SetOrigin (int x, int y) // void D3D9Pad::GetOrigin(int *x, int *y) const { - if (x) *x = int(mW._41); - if (y) *y = int(mW._42); + if (x) *x = int(mW.m41); + if (y) *y = int(mW.m42); } @@ -1255,7 +1257,7 @@ void D3D9Pad::Ellipse (int x0, int y0, int x1, int y1) //fl += w; //ft += h; - IVECTOR2 pts[65] = { 0 }; + IVECTOR2 pts[65] = {}; WORD n = 8; WORD q = 0; @@ -1534,14 +1536,14 @@ int CreatePolyIndexList(const Type *pt, short npt, WORD *Out) // =============================================================================================== // -inline D3DXVECTOR2 _DXV2(const IVECTOR2 &pt) +inline FVECTOR2 _DXV2(const IVECTOR2 &pt) { - return D3DXVECTOR2(float(pt.x), float(pt.y)); + return FVECTOR2(float(pt.x), float(pt.y)); } -inline D3DXVECTOR2 _DXV2(const FVECTOR2 &pt) +inline FVECTOR2 _DXV2(const FVECTOR2 &pt) { - return D3DXVECTOR2(pt.x, pt.y); + return FVECTOR2(pt.x, pt.y); } @@ -1613,8 +1615,8 @@ void D3D9Pad::AppendLineVertexList(const Type *pt, int _npt, bool bLoop) WORD aV, bV, cV, dV; float length = 0.0f; - D3DXVECTOR2 pp; // Prev point - D3DXVECTOR2 np; // Next point + FVECTOR2 pp; // Prev point + FVECTOR2 np; // Next point // Line Init ------------------------------------------------------------ // @@ -1661,7 +1663,7 @@ void D3D9Pad::AppendLineVertexList(const Type *pt, int _npt, bool bLoop) pp = _DXV2(pt[i]); - if (IsDashed()) length += D3DXVec2Length(ptr(np - pp)); + if (IsDashed()) length += ::length(np - pp); } // Last segment --------------------------------------------------------- @@ -1714,8 +1716,8 @@ void D3D9Pad::AppendLineVertexList(const Type *pt) if (Topology(TRIANGLE)) { - D3DXVECTOR2 pp = _DXV2(pt[0]) * 2.0 - _DXV2(pt[1]); - D3DXVECTOR2 np; + FVECTOR2 pp = _DXV2(pt[0]) * 2.0 - _DXV2(pt[1]); + FVECTOR2 np; WORD vF = vI; @@ -1796,7 +1798,7 @@ ID3DXEffect* D3D9Pad::FX = 0; D3D9Client * D3D9Pad::gc = 0; WORD * D3D9Pad::Idx = 0; SkpVtx * D3D9Pad::Vtx = 0; -LPD3DXVECTOR2 D3D9Pad::pSinCos[]; +FVECTOR2* D3D9Pad::pSinCos[]; LPDIRECT3DDEVICE9 D3D9PadFont::pDev = 0; LPDIRECT3DDEVICE9 D3D9Pad::pDev = 0; LPDIRECT3DTEXTURE9 D3D9Pad::pNoise = 0; diff --git a/OVP/D3D9Client/D3D9Pad.h b/OVP/D3D9Client/D3D9Pad.h index 73822d4ad..d9b426de6 100644 --- a/OVP/D3D9Client/D3D9Pad.h +++ b/OVP/D3D9Client/D3D9Pad.h @@ -9,7 +9,7 @@ #include "OrbiterAPI.h" #include "D3D9Client.h" #include -#include +#include "MathAPI.h" #include #include #include "DrawAPI.h" @@ -77,12 +77,12 @@ struct SkpColor { SkpColor() { dclr = 0; - fclr = D3DXCOLOR(0, 0, 0, 0); + fclr = FVECTOR4(); } explicit SkpColor (DWORD c) { dclr = c; - fclr = D3DXCOLOR(c); + fclr = FVECTOR4(c); D3DXCOLORSWAP(&fclr); } @@ -93,7 +93,7 @@ struct SkpColor { } DWORD dclr; - D3DXCOLOR fclr; + FVECTOR4 fclr; }; @@ -203,7 +203,7 @@ class D3D9Pad : public Sketchpad } QBrush; struct { - D3DXVECTOR3 uDir; + FVECTOR3 uDir; float ca, dst; bool bEnable; } ClipData[2]; @@ -553,9 +553,9 @@ class D3D9Pad : public Sketchpad inline void FlushAll() { Flush(NULL); } - void SetViewProj(const D3DXMATRIX* pV, const D3DXMATRIX* pP); + void SetViewProj(const FMATRIX4* pV, const FMATRIX4* pP); - LPD3DXMATRIX WorldMatrix(); + FMATRIX4* WorldMatrix(); DWORD GetLineHeight(); ///< Return height of a character in the currently selected font with "internal leading" const char *GetName() const { return name; } LPDIRECT3DSURFACE9 GetRenderTarget() const { return pTgt; } @@ -615,17 +615,17 @@ class D3D9Pad : public Sketchpad bool bMustEndScene; bool bBeginDraw; RECT ScissorRect; - D3DXCOLOR cColorKey; + FVECTOR4 cColorKey; SkpView vmode; Topo tCurrent; RenderState* pRState; WORD vI = 0, iI = 0; - mutable D3DXMATRIX mVP; - D3DXMATRIX mV, mP, mW, mO; - D3DXMATRIX mVOrig, mPOrig; - D3DXVECTOR4 vTarget; + mutable FMATRIX4 mVP; + FMATRIX4 mV, mP, mW, mO; + FMATRIX4 mVOrig, mPOrig; + FVECTOR4 vTarget; DWORD bkmode; BlendState dwBlendState; TAlign_horizontal tah; @@ -649,7 +649,7 @@ class D3D9Pad : public Sketchpad FMATRIX4 ColorMatrix; FVECTOR4 Gamma, Noise; - std::stack mWStack; + std::stack mWStack; @@ -672,7 +672,7 @@ class D3D9Pad : public Sketchpad static SkpVtx *Vtx; // List of vertices static D3D9Client *gc; static LPDIRECT3DDEVICE9 pDev; - static LPD3DXVECTOR2 pSinCos[5]; + static FVECTOR2* pSinCos[5]; static LPDIRECT3DTEXTURE9 pNoise; // ------------------------------------------- diff --git a/OVP/D3D9Client/D3D9Pad2.cpp b/OVP/D3D9Client/D3D9Pad2.cpp index 7568bcd9c..d4cbce506 100644 --- a/OVP/D3D9Client/D3D9Pad2.cpp +++ b/OVP/D3D9Client/D3D9Pad2.cpp @@ -9,7 +9,7 @@ #include "D3D9Surface.h" #include "Scene.h" #include "Mesh.h" -#include +#include "MathAPI.h" #include @@ -327,7 +327,7 @@ void D3D9Pad::StretchRectNative(const LPDIRECT3DTEXTURE9 pSrc, const RECT *_s, c if (Topology(TRIANGLE)) { auto s = _s ? *_s : GetFullRectNative(pSrc); - auto t = *_t; + auto t = _t ? *_t : tgt; AddRectIdx(vI); @@ -520,13 +520,13 @@ void D3D9Pad::Clipper(int idx, const VECTOR3 *uDir, double cos_angle, double dis if (idx > 1) idx = 1; if (uDir) { - ClipData[idx].uDir = D3DXVEC(*uDir); + ClipData[idx].uDir = _F(uDir); ClipData[idx].ca = float(cos_angle); ClipData[idx].dst = float(dist); ClipData[idx].bEnable = true; } else { - ClipData[idx].uDir = D3DXVECTOR3(0,0,1); + ClipData[idx].uDir = FVECTOR3(0,0,1); ClipData[idx].ca = 2.0f; ClipData[idx].dst = 0.0f; ClipData[idx].bEnable = false; @@ -572,7 +572,7 @@ const FMATRIX4 *D3D9Pad::ProjectionMatrix() const // const FMATRIX4 *D3D9Pad::GetViewProjectionMatrix() const { - D3DXMatrixMultiply(&mVP, &mV, &mP); + oapiMatrixMultiply(&mVP, &mV, &mP); return (const FMATRIX4 *)&mVP; } @@ -619,13 +619,13 @@ void D3D9Pad::SetViewMode(SkpView mode) // =============================================================================================== // !! For a private use in D3D9Client !! // -LPD3DXMATRIX D3D9Pad::WorldMatrix() +FMATRIX4* D3D9Pad::WorldMatrix() { #ifdef SKPDBG Log("WorldMatrix() ! - ! - !"); #endif Change |= SKPCHG_TRANSFORM; - return (LPD3DXMATRIX)&mW; + return (FMATRIX4*)&mW; } @@ -638,13 +638,13 @@ void D3D9Pad::SetWorldTransform2D(float scale, float rot, const IVECTOR2 *c, con #endif Change |= SKPCHG_TRANSFORM; - D3DXVECTOR2 ctr = D3DXVECTOR2(0, 0); - D3DXVECTOR2 trl = D3DXVECTOR2(0, 0); + FVECTOR2 ctr = FVECTOR2(0, 0); + FVECTOR2 trl = FVECTOR2(0, 0); - if (c) ctr = D3DXVECTOR2(float(c->x), float(c->y)); - if (t) trl = D3DXVECTOR2(float(t->x), float(t->y)); + if (c) ctr = FVECTOR2(float(c->x), float(c->y)); + if (t) trl = FVECTOR2(float(t->x), float(t->y)); - D3DXMatrixAffineTransformation2D(&mW, scale, &ctr, rot, &trl); + D3DMAT_AffineTransformation2D(&mW, scale, &ctr, rot, &trl); } @@ -658,7 +658,7 @@ void D3D9Pad::SetWorldTransform(const FMATRIX4 *pWT) Change |= SKPCHG_TRANSFORM; if (pWT) memcpy(&mW, pWT, sizeof(FMATRIX4)); - else D3DXMatrixIdentity(&mW); + else oapiMatrixIdentity(&mW); } @@ -669,12 +669,12 @@ void D3D9Pad::SetWorldBillboard(const FVECTOR3& wpos, float scale, bool bFixed, #ifdef SKPDBG Log("SetWorldBillboard()"); #endif - scale *= (mP._11 + mP._22) * 0.5f; + scale *= (mP.m11 + mP.m22) * 0.5f; Change |= SKPCHG_TRANSFORM; FVECTOR3 up = unit(wpos); - FVECTOR3 y = unit(cross((index ? *index : FVECTOR3(mV._11, mV._21, mV._31)), up)); - FVECTOR3 x = cross(up, y); - float d = (bFixed ? dot(up, wpos) / float(tgt_desc.Width) : 1.0f) * scale; + FVECTOR3 y = unit(crossp((index ? *index : FVECTOR3(mV.m11, mV.m21, mV.m31)), up)); + FVECTOR3 x = crossp(up, y); + float d = (bFixed ? dotp(up, wpos) / float(tgt_desc.Width) : 1.0f) * scale; FMATRIX4 mWorld; mWorld._x.xyz = x * d; mWorld._y.xyz = y * d; @@ -733,11 +733,11 @@ void D3D9Pad::TexChange(SURFHANDLE hNew) if (SURFACE(hNew)->IsColorKeyEnabled()) { bColorKey = true; - cColorKey = D3DXCOLOR(SURFACE(hNew)->ColorKey); + cColorKey = FVECTOR4(SURFACE(hNew)->ColorKey); } else { bColorKey = false; - cColorKey = D3DXCOLOR(DWORD(0)); + cColorKey = FVECTOR4(DWORD(0)); } } @@ -782,7 +782,7 @@ int D3D9Pad::DrawMeshGroup(const MESHHANDLE hMesh, DWORD grp, Sketchpad::MeshFla while (grp < nGrp) { SURFHANDLE pTex = hTex ? hTex : pMesh->GetTexture(grp); - D3DXCOLOR Mat = pMesh->GetMaterial(grp); + FVECTOR4 Mat = pMesh->GetMaterial(grp); if (pTex) { @@ -794,7 +794,7 @@ int D3D9Pad::DrawMeshGroup(const MESHHANDLE hMesh, DWORD grp, Sketchpad::MeshFla HR(FX->SetBool(eTexEn, false)); } - HR(FX->SetValue(eMtrl, &Mat, sizeof(D3DXCOLOR))); + HR(FX->SetValue(eMtrl, &Mat, sizeof(FVECTOR4))); HR(FX->CommitChanges()); pMesh->RenderGroup(grp); @@ -892,7 +892,7 @@ void D3D9PolyLine::Draw(D3D9Pad *pSkp, LPDIRECT3DDEVICE9 pDev) void D3D9PolyLine::Update(const FVECTOR2 *_pt, int _npt, bool bConnect) { SkpVtx *Vtx = NULL; - D3DXVECTOR2 *pt = (D3DXVECTOR2 *)_pt; + FVECTOR2 *pt = (FVECTOR2 *)_pt; HR(pVB->Lock(0, 0, (LPVOID*)&Vtx, D3DLOCK_DISCARD)); @@ -902,8 +902,8 @@ void D3D9PolyLine::Update(const FVECTOR2 *_pt, int _npt, bool bConnect) vI = 0; float length = 0.0f; - D3DXVECTOR2 pp; // Prev point - D3DXVECTOR2 np; // Next point + FVECTOR2 pp; // Prev point + FVECTOR2 np; // Next point bLoop = bConnect; @@ -938,7 +938,7 @@ void D3D9PolyLine::Update(const FVECTOR2 *_pt, int _npt, bool bConnect) vI+=2; // -------------------------------------- pp = pt[i]; - length += D3DXVec2Length(ptr(np - pp)); + length += ::length(np - pp); } if (bLoop) { diff --git a/OVP/D3D9Client/D3D9Pad3.cpp b/OVP/D3D9Client/D3D9Pad3.cpp index ec653e005..ed0267ac6 100644 --- a/OVP/D3D9Client/D3D9Pad3.cpp +++ b/OVP/D3D9Client/D3D9Pad3.cpp @@ -6,7 +6,7 @@ #include "D3D9Pad.h" #include "D3D9Surface.h" -#include +#include "MathAPI.h" #include @@ -80,7 +80,7 @@ FVECTOR4 D3D9Pad::GetRenderParam(RenderParam param) case Sketchpad::RenderParam::PRM_GAMMA: return Gamma; case Sketchpad::RenderParam::PRM_NOISE: return Noise; } - return FVECTOR4(0, 0, 0, 0); + return F4_Zero; } @@ -93,8 +93,8 @@ void D3D9Pad::SetRenderParam(RenderParam param, const FVECTOR4 *d) #endif if (d == NULL) { switch (param) { - case Sketchpad::RenderParam::PRM_GAMMA: Gamma = FVECTOR4(1, 1, 1, 1); ClearEnable(SKP3E_GAMMA); break; - case Sketchpad::RenderParam::PRM_NOISE: Noise = FVECTOR4(0, 0, 0, 0); ClearEnable(SKP3E_NOISE); break; + case Sketchpad::RenderParam::PRM_GAMMA: Gamma = F4_One; ClearEnable(SKP3E_GAMMA); break; + case Sketchpad::RenderParam::PRM_NOISE: Noise = F4_Zero; ClearEnable(SKP3E_NOISE); break; } return; } @@ -141,7 +141,7 @@ void D3D9Pad::SetBlendState(BlendState dwState) FMATRIX4 D3D9Pad::GetWorldTransform() const { FMATRIX4 fm; - memcpy_s(&fm, sizeof(FMATRIX4), &mW, sizeof(D3DXMATRIX)); + memcpy_s(&fm, sizeof(FMATRIX4), &mW, sizeof(FMATRIX4)); return fm; } @@ -185,7 +185,7 @@ void D3D9Pad::SetWorldScaleTransform2D(const FVECTOR2 *scl, const IVECTOR2 *trl) if (scl) sx = scl->x, sy = scl->y; - D3DXVECTOR3 t; + FVECTOR3 t; t.x = 0; t.y = 0; @@ -193,7 +193,7 @@ void D3D9Pad::SetWorldScaleTransform2D(const FVECTOR2 *scl, const IVECTOR2 *trl) if (trl) t.x = float(trl->x), t.y = float(trl->y); - D3DXMatrixScaling(&mW, sx, sy, 1.0f); + D3DMAT_Scale(&mW, sx, sy, 1.0f); D3DMAT_SetTranslation(&mW, &t); } @@ -269,19 +269,19 @@ void D3D9Pad::StretchRegion(const skpRegion *rgn, const SURFHANDLE hSrc, const L int ty2 = ty3 - (y3 - y2); // Corners - if (x0 != x1 && y0 != y1) CopyRect(hSrc, ptr(_R(x0, y0, x1, y1)), tx0, ty0); // TOP-LEFT - if (x2 != x3 && y0 != y1) CopyRect(hSrc, ptr(_R(x2, y0, x3, y1)), tx2, ty0); // TOP-RIGHT - if (x0 != x1 && y2 != y3) CopyRect(hSrc, ptr(_R(x0, y2, x1, y3)), tx0, ty2); // BTM-LEFT - if (x2 != x3 && y2 != y3) CopyRect(hSrc, ptr(_R(x2, y2, x3, y3)), tx2, ty2); // BTM-RIGHT + if (x0 != x1 && y0 != y1) CopyRect(hSrc, &(_R(x0, y0, x1, y1)), tx0, ty0); // TOP-LEFT + if (x2 != x3 && y0 != y1) CopyRect(hSrc, &(_R(x2, y0, x3, y1)), tx2, ty0); // TOP-RIGHT + if (x0 != x1 && y2 != y3) CopyRect(hSrc, &(_R(x0, y2, x1, y3)), tx0, ty2); // BTM-LEFT + if (x2 != x3 && y2 != y3) CopyRect(hSrc, &(_R(x2, y2, x3, y3)), tx2, ty2); // BTM-RIGHT // Sides - if (x1 != x2 && y0 != y1) StretchRect(hSrc, ptr(_R(x1, y0, x2, y1)), ptr(_R(tx1, ty0, tx2, ty1))); // TOP - if (x1 != x2 && y2 != y3) StretchRect(hSrc, ptr(_R(x1, y2, x2, y3)), ptr(_R(tx1, ty2, tx2, ty3))); // BOTTOM - if (x0 != x1 && y1 != y2) StretchRect(hSrc, ptr(_R(x0, y1, x1, y2)), ptr(_R(tx0, ty1, tx1, ty2))); // LEFT - if (x2 != x3 && y1 != y2) StretchRect(hSrc, ptr(_R(x2, y1, x3, y2)), ptr(_R(tx2, ty1, tx3, ty2))); // RIGHT + if (x1 != x2 && y0 != y1) StretchRect(hSrc, &(_R(x1, y0, x2, y1)), &(_R(tx1, ty0, tx2, ty1))); // TOP + if (x1 != x2 && y2 != y3) StretchRect(hSrc, &(_R(x1, y2, x2, y3)), &(_R(tx1, ty2, tx2, ty3))); // BOTTOM + if (x0 != x1 && y1 != y2) StretchRect(hSrc, &(_R(x0, y1, x1, y2)), &(_R(tx0, ty1, tx1, ty2))); // LEFT + if (x2 != x3 && y1 != y2) StretchRect(hSrc, &(_R(x2, y1, x3, y2)), &(_R(tx2, ty1, tx3, ty2))); // RIGHT // Center - StretchRect(hSrc, ptr(_R(x1, y1, x2, y2)), ptr(_R(tx1, ty1, tx2, ty2))); + StretchRect(hSrc, &(_R(x1, y1, x2, y2)), &(_R(tx1, ty1, tx2, ty2))); } // =============================================================================================== @@ -298,7 +298,7 @@ void D3D9Pad::Clear(DWORD color, bool bColor, bool bDepth) // void D3D9Pad::SetClipDistance(float nr, float fr) { - D3DXMatrixOrthoOffCenterLH(&mO, 0.0f, (float)tgt_desc.Width, (float)tgt_desc.Height, 0.0f, nr, fr); - mP._33 = fr / (fr - nr); - mP._43 = -nr * mP._33; + D3DMAT_OrthoOffCenterLH(&mO, 0.0f, (float)tgt_desc.Width, (float)tgt_desc.Height, 0.0f, nr, fr); + mP.m33 = fr / (fr - nr); + mP.m43 = -nr * mP.m33; } diff --git a/OVP/D3D9Client/D3D9Surface.cpp b/OVP/D3D9Client/D3D9Surface.cpp index 41d2e119f..806582826 100644 --- a/OVP/D3D9Client/D3D9Surface.cpp +++ b/OVP/D3D9Client/D3D9Surface.cpp @@ -1,7 +1,7 @@ // =========================================================================================== // D3D9Surface.cpp // Part of the ORBITER VISUALISATION PROJECT (OVP) -// Dual licensed under GPL v3 and LGPL v3 +// Licensed under LGPL v2 // Copyright (C) 2011 - 2016 Jarmo Nikkanen // =========================================================================================== @@ -20,6 +20,8 @@ using namespace oapi; extern D3D9Client* g_client; +// =============================================================================================== +// void NatCheckFlags(DWORD &flags) { // Append dependend flags @@ -53,34 +55,80 @@ void NatCheckFlags(DWORD &flags) } } -LPDIRECT3DTEXTURE9 NatLoadSpecialTexture(const char* fname, const char* ext) -{ - char path[MAX_PATH]; - char name[MAX_PATH]; - - NatCreateName(name, ARRAYSIZE(name), fname, ext); +// =============================================================================================== +// Load a simple plain texture +// +LPDIRECT3DTEXTURE9 NatLoadTexture(const char* path, bool bNoMips) +{ LPDIRECT3DTEXTURE9 pTex = NULL; - - if (g_client->TexturePath(name, path)) { - D3DXIMAGE_INFO info; - if (D3DXGetImageInfoFromFileA(path, &info) == S_OK) { + D3DXIMAGE_INFO info; - DWORD Mips = D3DFMT_FROM_FILE; + if (D3DXGetImageInfoFromFileA(path, &info) == S_OK) { - if (Config->TextureMips == 2) Mips = 0; // Autogen all - if (Config->TextureMips == 1 && info.MipLevels == 1) Mips = 0; // Autogen missing + DWORD Mips = D3DFMT_FROM_FILE; - if (S_OK == D3DXCreateTextureFromFileExA(g_client->GetDevice(), path, info.Width, info.Height, Mips, 0, D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pTex)) - { - return pTex; - } + if (Config->TextureMips == 2) Mips = 0; // Autogen all + if (Config->TextureMips == 1 && info.MipLevels == 1) Mips = 0; // Autogen missing + if (bNoMips) Mips = 1; + + if (S_OK == D3DXCreateTextureFromFileExA(g_client->GetDevice(), path, info.Width, info.Height, Mips, 0, D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pTex)) + { + LogBlu("TextureLoaded [%s] Mips=%u Format=%u (%u,%u)", RemovePath(path), pTex->GetLevelCount(), info.Format, info.Width, info.Height); + return pTex; } } + return NULL; } +// =============================================================================================== +// Load a simple plain texture with map type extension +// +LPDIRECT3DTEXTURE9 NatLoadSpecialTexture(const char* path, const char* ext, bool bNoMips) +{ + char name[MAX_PATH]; + NatCreateName(name, ARRAYSIZE(name), path, ext); + return NatLoadTexture(name, bNoMips); +} + + +// ====================================================================================== +// Main loading routine for all maps +// +void NatLoadMaps(SurfNative *pNat, const char* path) +{ + pNat->AddMap(MAP_HEAT, NatLoadSpecialTexture(path, "_heat")); + pNat->AddMap(MAP_NORMAL, NatLoadSpecialTexture(path, "_norm")); + pNat->AddMap(MAP_SPECULAR, NatLoadSpecialTexture(path, "_spec")); + pNat->AddMap(MAP_EMISSION, NatLoadSpecialTexture(path, "_emis")); + pNat->AddMap(MAP_ROUGHNESS, NatLoadSpecialTexture(path, "_rghn")); + pNat->AddMap(MAP_METALNESS, NatLoadSpecialTexture(path, "_metal")); + pNat->AddMap(MAP_REFLECTION, NatLoadSpecialTexture(path, "_refl")); + pNat->AddMap(MAP_TRANSLUCENCE, NatLoadSpecialTexture(path, "_transl")); + pNat->AddMap(MAP_TRANSMITTANCE, NatLoadSpecialTexture(path, "_transm")); + pNat->AddMap(MAP_AMBIENT, NatLoadSpecialTexture(path, "_bkao")); +} + + +// ====================================================================================== +// Main loading routine for a single map +// +void NatLoadMap(SurfNative* pNat, const char* path) +{ + if (NatIsTypeOf(path, "heat")) { pNat->AddMap(MAP_HEAT, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "norm")) { pNat->AddMap(MAP_NORMAL, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "spec")) { pNat->AddMap(MAP_SPECULAR, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "emis")) { pNat->AddMap(MAP_EMISSION, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "rghn")) { pNat->AddMap(MAP_ROUGHNESS, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "metal")) { pNat->AddMap(MAP_METALNESS, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "refl")) { pNat->AddMap(MAP_REFLECTION, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "transl")) { pNat->AddMap(MAP_TRANSLUCENCE, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "transm")) { pNat->AddMap(MAP_TRANSMITTANCE, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "bkao")) { pNat->AddMap(MAP_AMBIENT, NatLoadTexture(path)); return; } +} + // ====================================================================================== // Main loading routine @@ -95,12 +143,8 @@ SURFHANDLE NatLoadSurface(const char* file, DWORD flags, bool bPath) char path[MAX_PATH]; if (bPath) strcpy_s(path, MAX_PATH, file); - else { - if (!g_client->TexturePath(file, path)) { - return NULL; - } - } - + else if (!g_client->TexturePath(file, path)) return NULL; + DWORD pass = OAPISURFACE_TEXTURE | OAPISURFACE_SHARED; // Load regular texture with additional maps if exists @@ -112,9 +156,9 @@ SURFHANDLE NatLoadSurface(const char* file, DWORD flags, bool bPath) if (D3DXGetImageInfoFromFileA(path, &info) == S_OK) { - if (info.ImageFileFormat == D3DXIFF_JPG) info.Format = D3DFMT_X8R8G8B8; - if (info.ImageFileFormat == D3DXIFF_PNG) info.Format = D3DFMT_X8R8G8B8; - if (info.ImageFileFormat == D3DXIFF_BMP) info.Format = D3DFMT_X8R8G8B8; + if (info.ImageFileFormat == D3DXIFF_JPG) info.Format = D3DFMT_X8R8G8B8, flags |= OAPISURFACE_DIFFUSE_ONLY; + if (info.ImageFileFormat == D3DXIFF_PNG) info.Format = D3DFMT_X8R8G8B8, flags |= OAPISURFACE_DIFFUSE_ONLY; + if (info.ImageFileFormat == D3DXIFF_BMP) info.Format = D3DFMT_X8R8G8B8, flags |= OAPISURFACE_DIFFUSE_ONLY; DWORD Mips = D3DFMT_FROM_FILE; if (Config->TextureMips == 2) Mips = 0; // Autogen all @@ -124,18 +168,13 @@ SURFHANDLE NatLoadSurface(const char* file, DWORD flags, bool bPath) { pNat = new SurfNative(pTex, flags); pNat->SetName(file); - + pNat->SetPath(path); LogBlu("TextureLoaded [%s] PLAIN Mips=%u Format=%u (%u,%u) Flags=0x%X, %s", file, pTex->GetLevelCount(), info.Format, info.Width, info.Height, flags, _PTR(pNat)); - pNat->AddMap(MAP_HEAT, NatLoadSpecialTexture(file, "heat")); - pNat->AddMap(MAP_NORMAL, NatLoadSpecialTexture(file, "norm")); - pNat->AddMap(MAP_SPECULAR, NatLoadSpecialTexture(file, "spec")); - pNat->AddMap(MAP_EMISSION, NatLoadSpecialTexture(file, "emis")); - pNat->AddMap(MAP_ROUGHNESS, NatLoadSpecialTexture(file, "rghn")); - pNat->AddMap(MAP_METALNESS, NatLoadSpecialTexture(file, "metal")); - pNat->AddMap(MAP_REFLECTION, NatLoadSpecialTexture(file, "refl")); - pNat->AddMap(MAP_TRANSLUCENCE, NatLoadSpecialTexture(file, "transl")); - pNat->AddMap(MAP_TRANSMITTANCE, NatLoadSpecialTexture(file, "transm")); + if ((flags & OAPISURFACE_DIFFUSE_ONLY) == 0) + { + NatLoadMaps(pNat, path); + } } else oapiWriteLogV("FAILED: NatLoadSurface(%d)", path); } @@ -197,6 +236,7 @@ SURFHANDLE NatLoadSurface(const char* file, DWORD flags, bool bPath) { SurfNative* pSrf = new SurfNative(pTex, flags); pSrf->SetName(file); + pSrf->SetPath(path); LogBlu("TextureLoaded [%s] Mips=%u, Usage=%u, Format=%u (%u,%u) Flags=0x%X, %s", file, pTex->GetLevelCount(), Usage, Format, info.Width, info.Height, flags, _PTR(pSrf)); return SURFHANDLE(pSrf); } @@ -212,6 +252,7 @@ SURFHANDLE NatLoadSurface(const char* file, DWORD flags, bool bPath) { SurfNative* pSrf = new SurfNative(pSurf, flags); pSrf->SetName(file); + pSrf->SetPath(path); LogBlu("SurfaceLoaded [%s] RENDERTARGET Format=%u (%u,%u) Flags=0x%X %s", file, Format, info.Width, info.Height, flags, _PTR(pSrf)); return SURFHANDLE(pSrf); } @@ -226,22 +267,22 @@ SURFHANDLE NatLoadSurface(const char* file, DWORD flags, bool bPath) // =============================================================================================== // -bool NatSaveSurface(const char* file, LPDIRECT3DRESOURCE9 pResource) +bool NatSaveSurface(const char* path, LPDIRECT3DRESOURCE9 pResource) { LPDIRECT3DDEVICE9 pDev = g_client->GetDevice(); D3DXIMAGE_FILEFORMAT fmt = D3DXIMAGE_FILEFORMAT(0); - if (contains(file, ".dds")) fmt = D3DXIFF_DDS; - if (contains(file, ".bmp")) fmt = D3DXIFF_BMP; - if (contains(file, ".jpg")) fmt = D3DXIFF_JPG; - if (contains(file, ".png")) fmt = D3DXIFF_PNG; + if (contains(path, ".dds")) fmt = D3DXIFF_DDS; + if (contains(path, ".bmp")) fmt = D3DXIFF_BMP; + if (contains(path, ".jpg")) fmt = D3DXIFF_JPG; + if (contains(path, ".png")) fmt = D3DXIFF_PNG; if (pResource->GetType() == D3DRTYPE_SURFACE) { LPDIRECT3DSURFACE9 pSurf = static_cast(pResource); - if (D3DXSaveSurfaceToFileA(file, fmt, pSurf, NULL, NULL) == S_OK) return true; + if (D3DXSaveSurfaceToFileA(path, fmt, pSurf, NULL, NULL) == S_OK) return true; oapiWriteLog((char*)"NatSaveSurface(SURF):"); NatDumpResource(pResource); return false; @@ -255,7 +296,7 @@ bool NatSaveSurface(const char* file, LPDIRECT3DRESOURCE9 pResource) pTex->GetLevelDesc(0, &desc); if (desc.Pool == D3DPOOL_SYSTEMMEM || desc.Usage & D3DUSAGE_DYNAMIC) { - if (D3DXSaveTextureToFileA(file, fmt, pTex, NULL) == S_OK) return true; + if (D3DXSaveTextureToFileA(path, fmt, pTex, NULL) == S_OK) return true; oapiWriteLog((char*)"NatSaveSurface(TEX):"); NatDumpResource(pResource); return false; @@ -273,14 +314,14 @@ bool NatSaveSurface(const char* file, LPDIRECT3DRESOURCE9 pResource) pSrc->Release(); pTgt->Release(); } - if (D3DXSaveTextureToFile(file, fmt, pSys, NULL) == S_OK) { + if (D3DXSaveTextureToFile(path, fmt, pSys, NULL) == S_OK) { pSys->Release(); return true; } pSys->Release(); } - if (D3DXSaveTextureToFileA(file, fmt, pTex, NULL) == S_OK) return true; + if (D3DXSaveTextureToFileA(path, fmt, pTex, NULL) == S_OK) return true; } oapiWriteLog((char*)"NatSaveSurface():"); @@ -333,7 +374,7 @@ SURFHANDLE NatCreateSurface(int width, int height, DWORD flags) { if (flags & OAPISURFACE_RENDER3D) { - HR(pDev->CreateDepthStencilSurface(width, height, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, true, &pDepth, NULL)); + HR(pDev->CreateDepthStencilSurface(width, height, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, true, &pDepth, NULL)); } return SURFHANDLE(new SurfNative(pTex, flags, pDepth)); } @@ -349,7 +390,7 @@ SURFHANDLE NatCreateSurface(int width, int height, DWORD flags) { if (flags & OAPISURFACE_RENDER3D) { - HR(pDev->CreateDepthStencilSurface(width, height, D3DFMT_D24S8, Multi, 0, true, &pDepth, NULL)); + HR(pDev->CreateDepthStencilSurface(width, height, D3DFMT_D24X8, Multi, 0, true, &pDepth, NULL)); } return SURFHANDLE(new SurfNative(pSurf, flags, pDepth)); } @@ -500,6 +541,7 @@ SurfNative::SurfNative(LPDIRECT3DRESOURCE9 pRes, DWORD flags, LPDIRECT3DSURFACE9 memset(&DC, 0, sizeof(DC)); strcpy_s(name, sizeof(name), "null"); + strcpy_s(path, sizeof(path), "null"); type = pResource->GetType(); @@ -538,7 +580,8 @@ SurfNative::SurfNative(SurfNative* pOrigin) for (int i = 0; i < MAP_MAX_COUNT; i++) pMap[i] = pOrigin->pMap[i]; - strcpy_s(name, 128, pOrigin->name); + strcpy_s(name, sizeof(name), pOrigin->name); + strcpy_s(path, sizeof(path), pOrigin->path); } @@ -689,12 +732,22 @@ bool SurfNative::IsPowerOfTwo() const // void SurfNative::SetName(const char* n) { - strcpy_s(name, 128, n); + strcpy_s(name, sizeof(name), n); int i = -1; while (name[++i] != 0) if (name[i] == '/') name[i] = '\\'; } +// ----------------------------------------------------------------------------------------------- +// +void SurfNative::SetPath(const char* n) +{ + strcpy_s(path, sizeof(path), n); + int i = -1; + while (path[++i] != 0) if (path[i] == '/') path[i] = '\\'; +} + + // ----------------------------------------------------------------------------------------------- // LPDIRECT3DTEXTURE9 SurfNative::GetGDICache(DWORD Flags) @@ -924,13 +977,6 @@ bool SurfNative::Decompress() if (desc.Format == D3DFMT_DXT5) Format = D3DFMT_A8R8G8B8; if (desc.Format == D3DFMT_DXT3) Format = D3DFMT_A8R8G8B8; - char path[MAX_PATH]; - - if (!g_client->TexturePath(name, path)) { - oapiWriteLogV("SurfNative::Reload() File Not Found [%s]", path); - return false; - } - if (S_OK == D3DXCreateTextureFromFileExA(pDevice, path, desc.Width, desc.Height, Mipmaps, D3DUSAGE_RENDERTARGET, Format, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pDecomp)) { @@ -962,7 +1008,6 @@ bool SurfNative::Decompress() // bool SurfNative::DeClone() { - char path[MAX_PATH]; if (!IsClone()) return false; else @@ -982,11 +1027,6 @@ bool SurfNative::DeClone() if (desc.Format == D3DFMT_DXT5) Format = D3DFMT_A8R8G8B8; if (desc.Format == D3DFMT_DXT3) Format = D3DFMT_A8R8G8B8; - if (!g_client->TexturePath(name, path)) { - oapiWriteLogV("SurfNative::DeClone() File Not Found [%s]", path); - return false; - } - if (S_OK == D3DXCreateTextureFromFileExA(pDevice, path, desc.Width, desc.Height, Mipmaps, D3DUSAGE_RENDERTARGET, Format, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pTex)) { @@ -1015,13 +1055,6 @@ void SurfNative::Reload() SAFE_RELEASE(pResource); for (int i = 0; i < ARRAYSIZE(pMap); i++) SAFE_RELEASE(pMap[i]); - char path[MAX_PATH]; - - if (!g_client->TexturePath(name, path)) { - oapiWriteLogV("SurfNative::Reload() File Not Found [%s]", path); - return; - } - if (Flags == OAPISURFACE_TEXTURE) { D3DXIMAGE_INFO info; @@ -1035,15 +1068,15 @@ void SurfNative::Reload() if (S_OK == D3DXCreateTextureFromFileExA(g_client->GetDevice(), path, info.Width, info.Height, Mips, 0, D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, (LPDIRECT3DTEXTURE9 *)&pResource)) { - AddMap(MAP_HEAT, NatLoadSpecialTexture(name, "heat")); - AddMap(MAP_NORMAL, NatLoadSpecialTexture(name, "norm")); - AddMap(MAP_SPECULAR, NatLoadSpecialTexture(name, "spec")); - AddMap(MAP_EMISSION, NatLoadSpecialTexture(name, "emis")); - AddMap(MAP_ROUGHNESS, NatLoadSpecialTexture(name, "rghn")); - AddMap(MAP_METALNESS, NatLoadSpecialTexture(name, "metal")); - AddMap(MAP_REFLECTION, NatLoadSpecialTexture(name, "refl")); - AddMap(MAP_TRANSLUCENCE, NatLoadSpecialTexture(name, "transl")); - AddMap(MAP_TRANSMITTANCE, NatLoadSpecialTexture(name, "transm")); + AddMap(MAP_HEAT, NatLoadSpecialTexture(name, "_heat")); + AddMap(MAP_NORMAL, NatLoadSpecialTexture(name, "_norm")); + AddMap(MAP_SPECULAR, NatLoadSpecialTexture(name, "_spec")); + AddMap(MAP_EMISSION, NatLoadSpecialTexture(name, "_emis")); + AddMap(MAP_ROUGHNESS, NatLoadSpecialTexture(name, "_rghn")); + AddMap(MAP_METALNESS, NatLoadSpecialTexture(name, "_metal")); + AddMap(MAP_REFLECTION, NatLoadSpecialTexture(name, "_refl")); + AddMap(MAP_TRANSLUCENCE, NatLoadSpecialTexture(name, "_transl")); + AddMap(MAP_TRANSMITTANCE, NatLoadSpecialTexture(name, "_transm")); } } } @@ -1150,11 +1183,20 @@ bool NatCreateName(char* out, int mlen, const char* fname, const char* id) char* p = strrchr(buffe, '.'); if (p != NULL) { *p = '\0'; - sprintf_s(out, mlen, "%s_%s.%s", buffe, id, ++p); + sprintf_s(out, mlen, "%s%s.%s", buffe, id, ++p); } return (p != NULL); } +// ----------------------------------------------------------------------------------------------- +// +bool NatIsTypeOf(const char* fname, const char* id) +{ + char buf[32]; + sprintf_s(buf, 32, "_%s.dds", id); + const char* p = strstr(fname, id); + return (p != NULL); +} // ----------------------------------------------------------------------------------------------- // diff --git a/OVP/D3D9Client/D3D9Surface.h b/OVP/D3D9Client/D3D9Surface.h index 580f21a46..1cc7e8865 100644 --- a/OVP/D3D9Client/D3D9Surface.h +++ b/OVP/D3D9Client/D3D9Surface.h @@ -1,6 +1,6 @@ // ============================================================== // Part of the ORBITER VISUALISATION PROJECT (OVP) -// Dual licensed under GPL v3 and LGPL v3 +// Licensed under LGPL v2 // Copyright (C) 2012 - 2016 Jarmo Nikkanen // ============================================================== @@ -13,7 +13,7 @@ #include "GDIPad.h" //#include "gcCore.h" #include -#include +#include "MathAPI.h" #define MAP_NORMAL 0 #define MAP_SPECULAR 1 @@ -24,7 +24,8 @@ #define MAP_ROUGHNESS 6 #define MAP_METALNESS 7 #define MAP_HEAT 8 -#define MAP_MAX_COUNT 9 +#define MAP_AMBIENT 9 +#define MAP_MAX_COUNT 10 #define OAPISURFACE_MAPS 0x80000000 // Additional Texture Maps #define OAPISURFACE_BACKBUFFER 0x40000000 // It's a backbuffer @@ -33,7 +34,8 @@ #define OAPISURF_SKP_GDI_WARN 0x00000001 -LPDIRECT3DTEXTURE9 NatLoadSpecialTexture(const char* fname, const char* ext); +LPDIRECT3DTEXTURE9 NatLoadTexture(const char* path, bool bNoMips = false); +LPDIRECT3DTEXTURE9 NatLoadSpecialTexture(const char* fname, const char* ext, bool bNoMips = false); SURFHANDLE NatLoadSurface(const char* file, DWORD flags, bool bPath = false); bool NatSaveSurface(const char* file, LPDIRECT3DRESOURCE9 pResource); SURFHANDLE NatCreateSurface(int width, int height, DWORD flags); @@ -48,6 +50,9 @@ const char* NatPool(D3DPOOL Pool); const char* NatOAPIFlags(DWORD AF); const char* NatOAPIFormat(DWORD PF); void NatDumpResource(LPDIRECT3DRESOURCE9 pResource); +void NatLoadMaps(SurfNative* pNat, const char* file); +void NatLoadMap(SurfNative* pNat, const char* file); +bool NatIsTypeOf(const char*, const char*); #define ERR_DC_NOT_AVAILABLE 0x1 @@ -94,7 +99,9 @@ class SurfNative DWORD* GetClientFlags(); const char* GetName() const { return name; } + const char* GetPath() const { return path; } void SetName(const char*); + void SetPath(const char*); HDC GetDC(); void ReleaseDC(HDC); @@ -134,6 +141,7 @@ class SurfNative // ------------------------------------------------------------------------------- char name[128]; // Surface name + char path[MAX_PATH]; // Surface name with path SURFHANDLE hOrigin; D3DSURFACE_DESC desc; // Surface size and format description D3DRESOURCETYPE type; // Resource type diff --git a/OVP/D3D9Client/D3D9TextMgr.cpp b/OVP/D3D9Client/D3D9TextMgr.cpp index f2819d921..c6ae4d54d 100644 --- a/OVP/D3D9Client/D3D9TextMgr.cpp +++ b/OVP/D3D9Client/D3D9TextMgr.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include "MathAPI.h" #include "D3D9TextMgr.h" #include "Log.h" #include "D3D9Client.h" @@ -445,18 +445,19 @@ float D3D9Text::PrintSkp(D3D9Pad *pSkp, float xpos, float ypos, const char *_str c = str[idx++]; } - D3DXMATRIX rot, out, mBak; + FMATRIX4 rot, out, mBak; bool bRestore = false; if (fabs(rotation)>1e-3 || fabs(scaling - 1.0f)>0.001f) { - D3DXVECTOR2 center = D3DXVECTOR2((bbox_l + bbox_r)*0.5f, bbox_t); - D3DXVECTOR2 scale = D3DXVECTOR2(scaling, scaling); + FVECTOR2 center = FVECTOR2((bbox_l + bbox_r)*0.5f, bbox_t); + FVECTOR2 scale = FVECTOR2(scaling, scaling); center.x = ceil(center.x); center.y = ceil(center.y); - D3DXMatrixTransformation2D(&rot, ¢er, 0.0f, &scale, ¢er, -rotation*0.01745329f, NULL); - memcpy(&mBak, pSkp->WorldMatrix(), sizeof(D3DXMATRIX)); - D3DXMatrixMultiply(pSkp->WorldMatrix(), &rot, &mBak); + D3DMAT_Transformation2D(&rot, ¢er, 0.0f, &scale, ¢er, -rotation*0.01745329f, NULL); + + memcpy(&mBak, pSkp->WorldMatrix(), sizeof(FMATRIX4)); + oapiMatrixMultiply(pSkp->WorldMatrix(), &rot, &mBak); bRestore = true; } @@ -517,7 +518,7 @@ float D3D9Text::PrintSkp(D3D9Pad *pSkp, float xpos, float ypos, const char *_str if (bRestore) { - memcpy(pSkp->WorldMatrix(), &mBak, sizeof(D3DXMATRIX)); + memcpy(pSkp->WorldMatrix(), &mBak, sizeof(FMATRIX4)); } float l = xpos - x_orig; diff --git a/OVP/D3D9Client/D3D9TextMgr.h b/OVP/D3D9Client/D3D9TextMgr.h index f6a370daa..babc61908 100644 --- a/OVP/D3D9Client/D3D9TextMgr.h +++ b/OVP/D3D9Client/D3D9TextMgr.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include "MathAPI.h" #include "D3D9Client.h" #include "AABBUtil.h" diff --git a/OVP/D3D9Client/D3D9Util.cpp b/OVP/D3D9Client/D3D9Util.cpp index 0f9904d1e..807b6abb3 100644 --- a/OVP/D3D9Client/D3D9Util.cpp +++ b/OVP/D3D9Client/D3D9Util.cpp @@ -20,6 +20,7 @@ #include #include #include +#include "DirectXCollision.h" extern D3D9Client* g_client; extern unordered_map MeshMap; @@ -68,7 +69,7 @@ float SunOcclusionByPlanet(OBJHANDLE hObj, VECTOR3 gpos) VECTOR3 usd = spos / sd; VECTOR3 up = unit(rpos); double r = length(rpos); - double ca = -dot(up, usd); + double ca = -dotp(up, usd); double qr = sqrt(saturate(1.0 - ca * ca)) * r; double dp = r * r - sz * sz; double hd = dp > 1e4 ? sqrt(dp) : 1000.0; // Distance to horizon @@ -84,7 +85,7 @@ bool IsCastingShadows(vObject* body, vObject* ref, double* sunsize_out) { double sz = oapiGetSize(oapiGetGbodyByIndex(0)); VECTOR3 bc = body->GlobalPos() - ref->GlobalPos(); - double x = dot(bc, ref->SunDirection()); // Distance to projection plane + double x = dotp(bc, ref->SunDirection()); // Distance to projection plane double s = abs(x) * sz / ref->SunDistance(); // Size of the sun at projection plane double refrad = body->GetSize() + ref->GetSize() + s; if (sunsize_out) *sunsize_out = s; @@ -233,37 +234,28 @@ bool CopyBuffer(LPDIRECT3DRESOURCE9 _pDst, LPDIRECT3DRESOURCE9 _pSrc) return false; } -void LogMatrix(D3DXMATRIX *pM, const char *name) +inline FVECTOR4 CV2VEC4(const D3DCOLORVALUE &in) { - LogAlw("%s", name); - LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->_11, pM->_12, pM->_13, pM->_14); - LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->_21, pM->_22, pM->_23, pM->_24); - LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->_31, pM->_32, pM->_33, pM->_34); - LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->_41, pM->_42, pM->_43, pM->_44); + return FVECTOR4(in.r, in.g, in.b, in.a); } -inline D3DXVECTOR4 CV2VEC4(const D3DCOLORVALUE &in) +inline FVECTOR4 CV2VEC4(const D3DCOLORVALUE &in, float w) { - return D3DXVECTOR4(in.r, in.g, in.b, in.a); + return FVECTOR4(in.r, in.g, in.b, w); } -inline D3DXVECTOR4 CV2VEC4(const D3DCOLORVALUE &in, float w) +inline FVECTOR3 CV2VEC3(const D3DCOLORVALUE &in) { - return D3DXVECTOR4(in.r, in.g, in.b, w); + return FVECTOR3(in.r, in.g, in.b); } -inline D3DXVECTOR3 CV2VEC3(const D3DCOLORVALUE &in) -{ - return D3DXVECTOR3(in.r, in.g, in.b); -} - -inline D3DCOLORVALUE VECtoCV(const D3DXVECTOR3 &in, float w) +inline D3DCOLORVALUE VECtoCV(const FVECTOR3 &in, float w) { D3DCOLORVALUE c = { in.x, in.y, in.z, w }; return c; } -inline D3DCOLORVALUE VECtoCV(const D3DXVECTOR4 &in) +inline D3DCOLORVALUE VECtoCV(const FVECTOR4 &in) { D3DCOLORVALUE c = { in.x, in.y, in.z, in.w }; return c; @@ -293,48 +285,37 @@ void CreateMatExt(const D3DMATERIAL9 *pIn, D3D9MatExt *pOut) pOut->Diffuse = CV2VEC4(pIn->Diffuse); pOut->Emissive = CV2VEC3(pIn->Emissive); pOut->Specular = CV2VEC4(pIn->Specular, pIn->Power); - pOut->Reflect = D3DXVECTOR3(0, 0, 0); - pOut->Fresnel = D3DXVECTOR3(1, 0, 1024.0f); - pOut->Emission2 = D3DXVECTOR3(1, 1, 1); - pOut->Roughness = D3DXVECTOR2(1.0f, 1.0f); - pOut->SpecialFX = D3DXVECTOR4(0, 0, 0, 0); + pOut->Reflect = FVECTOR3(0.0f, 0.0f, 0.0f); + pOut->Fresnel = FVECTOR3(1.0f, 0.0f, 1024.0f); + pOut->Emission2 = FVECTOR3(1.0f, 1.0f, 1.0f); + pOut->Roughness = FVECTOR2(1.0f, 1.0f); + pOut->SpecialFX = FVECTOR4(0.0f, 0.0f, 0.0f, 0.0f); pOut->Metalness = 0.0f; pOut->ModFlags = 0; } void CreateDefaultMat(D3D9MatExt *pOut) { - pOut->Ambient = D3DXVECTOR3(0, 0, 0); - pOut->Diffuse = D3DXVECTOR4(1, 1, 1, 1); - pOut->Emissive = D3DXVECTOR3(0, 0, 0); - pOut->Specular = D3DXVECTOR4(0.2f, 0.2f, 0.2f, 50.0f); - pOut->Reflect = D3DXVECTOR3(0, 0, 0); - pOut->Fresnel = D3DXVECTOR3(1, 0, 1024.0f); - pOut->Emission2 = D3DXVECTOR3(1, 1, 1); - pOut->Roughness = D3DXVECTOR2(1.0f, 1.0f); - pOut->SpecialFX = D3DXVECTOR4(0, 0, 0, 0); + pOut->Ambient = FVECTOR3(0.0f, 0.0f, 0.0f); + pOut->Diffuse = FVECTOR4(1.0f, 1.0f, 1.0f, 1.0f); + pOut->Emissive = FVECTOR3(0.0f, 0.0f, 0.0f); + pOut->Specular = FVECTOR4(0.2f, 0.2f, 0.2f, 50.0f); + pOut->Reflect = FVECTOR3(0.0f, 0.0f, 0.0f); + pOut->Fresnel = FVECTOR3(1.0f, 0.0f, 1024.0f); + pOut->Emission2 = FVECTOR3(1.0f, 1.0f, 1.0f); + pOut->Roughness = FVECTOR2(1.0f, 1.0f); + pOut->SpecialFX = FVECTOR4(0.0f, 0.0f, 0.0f, 0.0f); pOut->Metalness = 0.0f; pOut->ModFlags = 0; } -void D3D9TuneInit(D3D9Tune *pTune) -{ - pTune->Albedo = D3DXCOLOR(1, 1, 1, 1); - pTune->Emis = D3DXCOLOR(1, 1, 1, 1); - pTune->Spec = D3DXCOLOR(1, 1, 1, 1); - pTune->Refl = D3DXCOLOR(1, 1, 1, 1); - pTune->Transl = D3DXCOLOR(1, 1, 1, 1); - pTune->Transm = D3DXCOLOR(1, 1, 1, 1); - pTune->Norm = D3DXCOLOR(1, 1, 1, 1); - pTune->Rghn = D3DXCOLOR(1, 1, 1, 1); -} void SurfaceLighting(D3D9Sun *light, OBJHANDLE hP, OBJHANDLE hO, float ao) { // hP=hPlanet, hS=hSun VECTOR3 GO, GS, GP; - D3DXVECTOR3 _one(1,1,1); + FVECTOR3 _one(1,1,1); OBJHANDLE hS = oapiGetGbodyByIndex(0); // the central star oapiGetGlobalPos (hO, &GO); // object position @@ -362,8 +343,8 @@ void SurfaceLighting(D3D9Sun *light, OBJHANDLE hP, OBJHANDLE hO, float ao) disp = float(max (0.02, min(0.9, log1p(atm->rho0)))); } - D3DXVECTOR3 lcol; - D3DXVECTOR3 r0 = _one - D3DXVECTOR3(0.65f, 0.75f, 1.0f) * disp; + FVECTOR3 lcol; + FVECTOR3 r0 = _one - FVECTOR3(0.65f, 0.75f, 1.0f) * disp; if (atm) { // case 1: planet has atmosphere lcol = (r0 + (_one-r0) * saturate(h/d)) * saturate((h+rs)/(2.0f*rs)); @@ -377,9 +358,9 @@ void SurfaceLighting(D3D9Sun *light, OBJHANDLE hP, OBJHANDLE hO, float ao) lcol *= 1.0f-amb*0.5f; // reduce direct light component to avoid overexposure } - light->Color = D3DXCOLOR(lcol.x, lcol.y, lcol.z, 1.0f); - light->Ambient = D3DXCOLOR(amb, amb, amb, 1.0f); - light->Dir = D3DXVEC(S) * (-1.0f/s); + light->Color = FVECTOR3(lcol.x, lcol.y, lcol.z); + light->Ambient = FVECTOR3(amb, amb, amb); + light->Dir = S * (-1.0/s); } // =========================================== // Remove unecessary spaces and tablations @@ -683,11 +664,11 @@ std::string::size_type replace_all (std::string &subj, const std::string &s, con // Some utility methods for D3D vectors and matrices // ============================================================================ -float D3DXVec3Angle(D3DXVECTOR3 a, D3DXVECTOR3 b) +float D3DXVec3Angle(FVECTOR3 a, FVECTOR3 b) { - D3DXVec3Normalize(&a,&a); - D3DXVec3Normalize(&b,&b); - float x = D3DXVec3Dot(&a,&b); + normalise(a); + normalise(b); + float x = dotp(a, b); if (x<-1.0f) x=-1.0f; if (x> 1.0f) x= 1.0f; return acos(x); @@ -695,180 +676,187 @@ float D3DXVec3Angle(D3DXVECTOR3 a, D3DXVECTOR3 b) // ============================================================================ // -D3DXVECTOR3 Perpendicular(D3DXVECTOR3 *a) +FVECTOR3 Perpendicular(FVECTOR3 *a) { float x = fabs(a->x); float y = fabs(a->y); float z = fabs(a->z); float m = min(min(x, y), z); - if (m==x) return D3DXVECTOR3(0, a->z, a->y); - if (m==y) return D3DXVECTOR3(a->z, 0, -a->x); - else return D3DXVECTOR3(a->y, -a->x, 0); + if (m==x) return FVECTOR3(0, a->z, a->y); + if (m==y) return FVECTOR3(a->z, 0, -a->x); + else return FVECTOR3(a->y, -a->x, 0); } // Cleate a billboarding matrix. X-axis of the vertex data will be pointing to the camera // -void D3DMAT_CreateX_Billboard(const D3DXVECTOR3 *toCam, const D3DXVECTOR3 *pos, float size, D3DXMATRIX *pOut) +void D3DMAT_CreateX_Billboard(const FVECTOR3 *toCam, const FVECTOR3 *pos, float size, FMATRIX4 *pOut) { float hz = 1.0f/sqrt(toCam->x*toCam->x + toCam->z*toCam->z); - pOut->_11 = toCam->x; - pOut->_12 = toCam->y; - pOut->_13 = toCam->z; - pOut->_31 = -toCam->z*hz; - pOut->_32 = 0.0f; - pOut->_33 = toCam->x*hz; - pOut->_21 = -pOut->_12*pOut->_33; - pOut->_22 = pOut->_33*pOut->_11 - pOut->_13*pOut->_31; - pOut->_23 = pOut->_31*pOut->_12; - pOut->_41 = pos->x; - pOut->_42 = pos->y; - pOut->_43 = pos->z; - pOut->_14 = pOut->_24 = pOut->_34 = pOut->_44 = 0.0f; - pOut->_11 *= size; pOut->_12 *= size; pOut->_13 *= size; - pOut->_21 *= size; pOut->_22 *= size; pOut->_23 *= size; - pOut->_31 *= size; pOut->_33 *= size; + pOut->m11 = toCam->x; + pOut->m12 = toCam->y; + pOut->m13 = toCam->z; + pOut->m31 = -toCam->z*hz; + pOut->m32 = 0.0f; + pOut->m33 = toCam->x*hz; + pOut->m21 = -pOut->m12*pOut->m33; + pOut->m22 = pOut->m33*pOut->m11 - pOut->m13*pOut->m31; + pOut->m23 = pOut->m31*pOut->m12; + pOut->m41 = pos->x; + pOut->m42 = pos->y; + pOut->m43 = pos->z; + pOut->m14 = pOut->m24 = pOut->m34 = pOut->m44 = 0.0f; + pOut->m11 *= size; pOut->m12 *= size; pOut->m13 *= size; + pOut->m21 *= size; pOut->m22 *= size; pOut->m23 *= size; + pOut->m31 *= size; pOut->m33 *= size; } // Cleate a billboarding matrix. X-axis of the vertex data will be pointing to the camera // -void D3DMAT_CreateX_Billboard(const D3DXVECTOR3 *toCam, const D3DXVECTOR3 *pos, const D3DXVECTOR3 *dir, float size, float stretch, D3DXMATRIX *pOut) +void D3DMAT_CreateX_Billboard(const FVECTOR3 *toCam, const FVECTOR3 *pos, const FVECTOR3 *dir, float size, float stretch, FMATRIX4 *pOut) { - D3DXVECTOR3 q,w; - D3DXVec3Normalize(&q, D3DXVec3Cross(&q, dir, toCam)); - D3DXVec3Normalize(&w, D3DXVec3Cross(&w, &q, dir)); + FVECTOR3 q = unit(crossp(*dir, *toCam)); + FVECTOR3 w = unit(crossp(q, *dir)); - pOut->_11 = w.x * size; - pOut->_12 = w.y * size; - pOut->_13 = w.z * size; + pOut->m11 = w.x * size; + pOut->m12 = w.y * size; + pOut->m13 = w.z * size; - pOut->_21 = q.x * size; - pOut->_22 = q.y * size; - pOut->_23 = q.z * size; + pOut->m21 = q.x * size; + pOut->m22 = q.y * size; + pOut->m23 = q.z * size; - pOut->_31 = dir->x * stretch; - pOut->_32 = dir->y * stretch; - pOut->_33 = dir->z * stretch; + pOut->m31 = dir->x * stretch; + pOut->m32 = dir->y * stretch; + pOut->m33 = dir->z * stretch; - pOut->_41 = pos->x; - pOut->_42 = pos->y; - pOut->_43 = pos->z; + pOut->m41 = pos->x; + pOut->m42 = pos->y; + pOut->m43 = pos->z; - pOut->_14 = pOut->_24 = pOut->_34 = pOut->_44 = 0.0f; + pOut->m14 = pOut->m24 = pOut->m34 = pOut->m44 = 0.0f; } // ============================================================================ // -void D3DMAT_ZeroMatrix(D3DXMATRIX *mat) +void D3DMAT_ZeroMatrix(FMATRIX4 *mat) { - ZeroMemory(mat, sizeof (D3DXMATRIX)); + ZeroMemory(mat, sizeof (FMATRIX4)); } // ============================================================================ -// Matrix identity +// Copy a FMATRIX4 -void D3DMAT_Identity (D3DXMATRIX *mat) +void D3DMAT_Copy (FMATRIX4 *tgt, const FMATRIX4 *src) { - ZeroMemory(mat, sizeof (D3DXMATRIX)); - mat->_11 = mat->_22 = mat->_33 = mat->_44 = 1.0f; + memcpy(tgt, src, sizeof (FMATRIX4)); } // ============================================================================ -// Copy a D3DXMATRIX - -void D3DMAT_Copy (D3DXMATRIX *tgt, const D3DXMATRIX *src) +// +void D3DMAT_FromAxis(FMATRIX4 *mat, const FVECTOR3 *x, const FVECTOR3 *y, const FVECTOR3 *z) { - memcpy(tgt, src, sizeof (D3DXMATRIX)); + mat->m11 = x->x; + mat->m21 = x->y; + mat->m31 = x->z; + + mat->m12 = y->x; + mat->m22 = y->y; + mat->m32 = y->z; + + mat->m13 = z->x; + mat->m23 = z->y; + mat->m33 = z->z; } // ============================================================================ // -void D3DMAT_FromAxis(D3DXMATRIX *mat, const D3DVECTOR *x, const D3DVECTOR *y, const D3DVECTOR *z) +void D3DMAT_FromAxis(FMATRIX4 *mat, const VECTOR3 *x, const VECTOR3 *y, const VECTOR3 *z) { - mat->_11 = x->x; - mat->_21 = x->y; - mat->_31 = x->z; + mat->m11 = float(x->x); + mat->m21 = float(x->y); + mat->m31 = float(x->z); - mat->_12 = y->x; - mat->_22 = y->y; - mat->_32 = y->z; + mat->m12 = float(y->x); + mat->m22 = float(y->y); + mat->m32 = float(y->z); - mat->_13 = z->x; - mat->_23 = z->y; - mat->_33 = z->z; + mat->m13 = float(z->x); + mat->m23 = float(z->y); + mat->m33 = float(z->z); } // ============================================================================ // -void D3DMAT_FromAxis(D3DXMATRIX *mat, const VECTOR3 *x, const VECTOR3 *y, const VECTOR3 *z) +void D3DMAT_FromAxisT(FMATRIX4 *mat, const FVECTOR3 *x, const FVECTOR3 *y, const FVECTOR3 *z) { - mat->_11 = float(x->x); - mat->_21 = float(x->y); - mat->_31 = float(x->z); + mat->m11 = x->x; + mat->m12 = x->y; + mat->m13 = x->z; - mat->_12 = float(y->x); - mat->_22 = float(y->y); - mat->_32 = float(y->z); + mat->m21 = y->x; + mat->m22 = y->y; + mat->m23 = y->z; - mat->_13 = float(z->x); - mat->_23 = float(z->y); - mat->_33 = float(z->z); + mat->m31 = z->x; + mat->m32 = z->y; + mat->m33 = z->z; } // ============================================================================ // -void D3DMAT_FromAxisT(D3DXMATRIX *mat, const D3DVECTOR *x, const D3DVECTOR *y, const D3DVECTOR *z) +void D3DMAT_Scale(FMATRIX4* mat, float x, float y, float z) { - mat->_11 = x->x; - mat->_12 = x->y; - mat->_13 = x->z; + mat->m11 *= x; + mat->m12 *= x; + mat->m13 *= x; - mat->_21 = y->x; - mat->_22 = y->y; - mat->_23 = y->z; + mat->m21 *= y; + mat->m22 *= y; + mat->m23 *= y; - mat->_31 = z->x; - mat->_32 = z->y; - mat->_33 = z->z; + mat->m31 *= z; + mat->m32 *= z; + mat->m33 *= z; } // ============================================================================ -// Copy a rotation matrix into a D3DXMATRIX +// Copy a rotation matrix into a FMATRIX4 -void D3DMAT_SetRotation (D3DXMATRIX *mat, const MATRIX3 *rot) +void D3DMAT_SetRotation (FMATRIX4 *mat, const MATRIX3 *rot) { - mat->_11 = (FLOAT)rot->m11; - mat->_12 = (FLOAT)rot->m12; - mat->_13 = (FLOAT)rot->m13; - mat->_21 = (FLOAT)rot->m21; - mat->_22 = (FLOAT)rot->m22; - mat->_23 = (FLOAT)rot->m23; - mat->_31 = (FLOAT)rot->m31; - mat->_32 = (FLOAT)rot->m32; - mat->_33 = (FLOAT)rot->m33; + mat->m11 = (FLOAT)rot->m11; + mat->m12 = (FLOAT)rot->m12; + mat->m13 = (FLOAT)rot->m13; + mat->m21 = (FLOAT)rot->m21; + mat->m22 = (FLOAT)rot->m22; + mat->m23 = (FLOAT)rot->m23; + mat->m31 = (FLOAT)rot->m31; + mat->m32 = (FLOAT)rot->m32; + mat->m33 = (FLOAT)rot->m33; } // ============================================================================ // Copy the transpose of a matrix as rotation of a D3D transformation matrix -void D3DMAT_SetInvRotation (D3DXMATRIX *mat, const MATRIX3 *rot) +void D3DMAT_SetInvRotation (FMATRIX4 *mat, const MATRIX3 *rot) { - mat->_11 = (FLOAT)rot->m11; - mat->_12 = (FLOAT)rot->m21; - mat->_13 = (FLOAT)rot->m31; - mat->_21 = (FLOAT)rot->m12; - mat->_22 = (FLOAT)rot->m22; - mat->_23 = (FLOAT)rot->m32; - mat->_31 = (FLOAT)rot->m13; - mat->_32 = (FLOAT)rot->m23; - mat->_33 = (FLOAT)rot->m33; + mat->m11 = (FLOAT)rot->m11; + mat->m12 = (FLOAT)rot->m21; + mat->m13 = (FLOAT)rot->m31; + mat->m21 = (FLOAT)rot->m12; + mat->m22 = (FLOAT)rot->m22; + mat->m23 = (FLOAT)rot->m32; + mat->m31 = (FLOAT)rot->m13; + mat->m32 = (FLOAT)rot->m23; + mat->m33 = (FLOAT)rot->m33; } // ============================================================================ // Define a rotation matrix from a rotation axis & rotation angle -void D3DMAT_RotationFromAxis (const D3DXVECTOR3 &axis, float angle, D3DXMATRIX *rot) +void D3DMAT_RotationFromAxis (const FVECTOR3 &axis, float angle, FMATRIX4 *rot) { // Calculate quaternion angle *= 0.5f; @@ -882,127 +870,135 @@ void D3DMAT_RotationFromAxis (const D3DXVECTOR3 &axis, float angle, D3DXMATRIX * float xy = x*y, xz = x*z, yz = y*z; float wx = w*x, wy = w*y, wz = w*z; - rot->_11 = 1 - 2 * (yy+zz); - rot->_12 = 2 * (xy+wz); - rot->_13 = 2 * (xz-wy); - rot->_21 = 2 * (xy-wz); - rot->_22 = 1 - 2 * (xx+zz); - rot->_23 = 2 * (yz+wx); - rot->_31 = 2 * (xz+wy); - rot->_32 = 2 * (yz-wx); - rot->_33 = 1 - 2 * (xx+yy); + rot->m11 = 1 - 2 * (yy+zz); + rot->m12 = 2 * (xy+wz); + rot->m13 = 2 * (xz-wy); + rot->m21 = 2 * (xy-wz); + rot->m22 = 1 - 2 * (xx+zz); + rot->m23 = 2 * (yz+wx); + rot->m31 = 2 * (xz+wy); + rot->m32 = 2 * (yz-wx); + rot->m33 = 1 - 2 * (xx+yy); - rot->_14 = rot->_24 = rot->_34 = rot->_41 = rot->_42 = rot->_43 = 0.0f; - rot->_44 = 1.0f; + rot->m14 = rot->m24 = rot->m34 = rot->m41 = rot->m42 = rot->m43 = 0.0f; + rot->m44 = 1.0f; } // ============================================================================ // Set up a as matrix for ANTICLOCKWISE rotation r around x/y/z-axis -void D3DMAT_RotX (D3DXMATRIX *mat, double r) +void D3DMAT_RotX (FMATRIX4 *mat, double r) { double sinr = sin(r), cosr = cos(r); - ZeroMemory (mat, sizeof (D3DXMATRIX)); - mat->_22 = mat->_33 = (FLOAT)cosr; - mat->_23 = -(mat->_32 = (FLOAT)sinr); - mat->_11 = mat->_44 = 1.0f; + ZeroMemory (mat, sizeof (FMATRIX4)); + mat->m22 = mat->m33 = (FLOAT)cosr; + mat->m23 = -(mat->m32 = (FLOAT)sinr); + mat->m11 = mat->m44 = 1.0f; } // ============================================================================ // -void D3DMAT_RotY (D3DXMATRIX *mat, double r) +void D3DMAT_RotY (FMATRIX4 *mat, double r) { double sinr = sin(r), cosr = cos(r); - ZeroMemory (mat, sizeof (D3DXMATRIX)); - mat->_11 = mat->_33 = (FLOAT)cosr; - mat->_31 = -(mat->_13 = (FLOAT)sinr); - mat->_22 = mat->_44 = 1.0f; + ZeroMemory (mat, sizeof (FMATRIX4)); + mat->m11 = mat->m33 = (FLOAT)cosr; + mat->m31 = -(mat->m13 = (FLOAT)sinr); + mat->m22 = mat->m44 = 1.0f; } // ============================================================================ // -float D3DMAT_BSScaleFactor(const D3DXMATRIX *mat) +float D3DMAT_BSScaleFactor(const FMATRIX4 *mat) { - float lx = mat->_11*mat->_11 + mat->_12*mat->_12 + mat->_13*mat->_13; - float ly = mat->_21*mat->_21 + mat->_22*mat->_22 + mat->_23*mat->_23; - float lz = mat->_31*mat->_31 + mat->_32*mat->_32 + mat->_33*mat->_33; + float lx = mat->m11*mat->m11 + mat->m12*mat->m12 + mat->m13*mat->m13; + float ly = mat->m21*mat->m21 + mat->m22*mat->m22 + mat->m23*mat->m23; + float lz = mat->m31*mat->m31 + mat->m32*mat->m32 + mat->m33*mat->m33; return sqrt(max(max(lx,ly),lz)); } // ============================================================================ // Apply a translation vector toa D3D transformation matrix -void D3DMAT_SetTranslation (D3DXMATRIX *mat, const VECTOR3 *trans) +void D3DMAT_SetTranslation (FMATRIX4 *mat, const VECTOR3 *trans) { - mat->_41 = (FLOAT)trans->x; - mat->_42 = (FLOAT)trans->y; - mat->_43 = (FLOAT)trans->z; + mat->m41 = (FLOAT)trans->x; + mat->m42 = (FLOAT)trans->y; + mat->m43 = (FLOAT)trans->z; } -void D3DMAT_SetTranslation(D3DXMATRIX *mat, const D3DXVECTOR3 *trans) +// ============================================================================ +// +void D3DMAT_SetTranslation(FMATRIX4 *mat, const FVECTOR3 *trans) { - mat->_41 = (FLOAT)trans->x; - mat->_42 = (FLOAT)trans->y; - mat->_43 = (FLOAT)trans->z; + mat->m41 = (FLOAT)trans->x; + mat->m42 = (FLOAT)trans->y; + mat->m43 = (FLOAT)trans->z; } // ============================================================================ // -bool D3DMAT_VectorMatrixMultiply (D3DXVECTOR3 *res, const D3DXVECTOR3 *v, const D3DXMATRIX *mat) +void D3DMAT_Transform(FVECTOR4* o, const FVECTOR4* i, const FMATRIX4* m) { - float x = v->x*mat->_11 + v->y*mat->_21 + v->z* mat->_31 + mat->_41; - float y = v->x*mat->_12 + v->y*mat->_22 + v->z* mat->_32 + mat->_42; - float z = v->x*mat->_13 + v->y*mat->_23 + v->z* mat->_33 + mat->_43; - float w = v->x*mat->_14 + v->y*mat->_24 + v->z* mat->_34 + mat->_44; + o->Load(XMVector4Transform(i->XM(), m->XM())); +} - if (fabs (w) < 1e-5f) return false; +// ============================================================================ +// +void D3DMAT_AffineTransformation2D(FMATRIX4* pOut, float Scl, const FVECTOR2* pRotCtr, float Rot, const FVECTOR2* pTransl) +{ + XMVECTOR Tr = FVECTOR2(0, 0).XM(); + XMVECTOR Rc = Tr; + XMVECTOR Sl = FVECTOR2(Scl).XM(); - res->x = x/w; - res->y = y/w; - res->z = z/w; - return true; -} + if (pRotCtr) Rc = pRotCtr->XM(); + if (pTransl) Tr = pTransl->XM(); -// ======================================================================= -// Name: D3DMath_MatrixInvert() -// Desc: Does the matrix operation: [Q] = inv[A]. Note: this function only -// works for matrices with [0 0 0 1] for the 4th column. -// ======================================================================= + XMMATRIX M = XMMatrixAffineTransformation2D(Sl, Rc, Rot, Tr); + pOut->Load(M); +} -HRESULT D3DMAT_MatrixInvert (D3DXMATRIX *res, D3DXMATRIX *a) +// ============================================================================ +// +void D3DMAT_Transformation2D(FMATRIX4* pOut, const FVECTOR2* pSclCtr, float SclRot, const FVECTOR2* pScl, + const FVECTOR2* pRotCtr, float Rot, const FVECTOR2* pTransl) { - if( fabs(a->_44 - 1.0f) > .001f) - return E_INVALIDARG; - if( fabs(a->_14) > .001f || fabs(a->_24) > .001f || fabs(a->_34) > .001f ) - return E_INVALIDARG; + XMVECTOR Tr = FVECTOR2(0, 0).XM(); + XMVECTOR Sc = Tr; + XMVECTOR Rc = Tr; + XMVECTOR Sl = FVECTOR2(1, 1).XM(); - FLOAT fDetInv = 1.0f / ( a->_11 * ( a->_22 * a->_33 - a->_23 * a->_32 ) - - a->_12 * ( a->_21 * a->_33 - a->_23 * a->_31 ) + - a->_13 * ( a->_21 * a->_32 - a->_22 * a->_31 ) ); + if (pSclCtr) Sc = pSclCtr->XM(); + if (pScl) Sl = pScl->XM(); + if (pRotCtr) Rc = pRotCtr->XM(); + if (pTransl) Tr = pTransl->XM(); - res->_11 = fDetInv * ( a->_22 * a->_33 - a->_23 * a->_32 ); - res->_12 = -fDetInv * ( a->_12 * a->_33 - a->_13 * a->_32 ); - res->_13 = fDetInv * ( a->_12 * a->_23 - a->_13 * a->_22 ); - res->_14 = 0.0f; - - res->_21 = -fDetInv * ( a->_21 * a->_33 - a->_23 * a->_31 ); - res->_22 = fDetInv * ( a->_11 * a->_33 - a->_13 * a->_31 ); - res->_23 = -fDetInv * ( a->_11 * a->_23 - a->_13 * a->_21 ); - res->_24 = 0.0f; + XMMATRIX M = XMMatrixTransformation2D(Sc, SclRot, Sl, Rc, Rot, Tr); + pOut->Load(M); +} - res->_31 = fDetInv * ( a->_21 * a->_32 - a->_22 * a->_31 ); - res->_32 = -fDetInv * ( a->_11 * a->_32 - a->_12 * a->_31 ); - res->_33 = fDetInv * ( a->_11 * a->_22 - a->_12 * a->_21 ); - res->_34 = 0.0f; +// ============================================================================ +// +void D3DMAT_OrthoOffCenterLH(FMATRIX4* o, float l, float r, float b, float t, float zn, float zf) +{ + o->Load(XMMatrixOrthographicOffCenterLH(l, r, b, t, zn, zf)); +} - res->_41 = -( a->_41 * res->_11 + a->_42 * res->_21 + a->_43 * res->_31 ); - res->_42 = -( a->_41 * res->_12 + a->_42 * res->_22 + a->_43 * res->_32 ); - res->_43 = -( a->_41 * res->_13 + a->_42 * res->_23 + a->_43 * res->_33 ); - res->_44 = 1.0f; +// ============================================================================ +// +void D3DMAT_OrthoOffCenterRH(FMATRIX4* o, float l, float r, float b, float t, float zn, float zf) +{ + o->Load(XMMatrixOrthographicOffCenterRH(l, r, b, t, zn, zf)); +} - return S_OK; +// ============================================================================ +// +void D3DMAT_LookAtRH(FMATRIX4* o, const FVECTOR3* pEye, const FVECTOR3* pAt, const FVECTOR3* pUp) +{ + o->Load(XMMatrixLookAtRH(pEye->XM(), pAt->XM(), pUp->XM())); } + // ============================================================================ // LPDIRECT3DPIXELSHADER9 CompilePixelShader(LPDIRECT3DDEVICE9 pDev, const char *file, const char *function, const char *name, const char* options, LPD3DXCONSTANTTABLE *pConst) @@ -1379,20 +1375,20 @@ void D3D9Light::Reset() // ============================================================================ // -float D3D9Light::GetIlluminance(D3DXVECTOR3 &_pos, float r) const +float D3D9Light::GetIlluminance(FVECTOR3 &_pos, float r) const { if (intensity < 0) return -1.0f; - D3DXVECTOR3 pos = _pos - Position; + FVECTOR3 pos = _pos - Position; - float d = D3DXVec3Length(&pos); + float d = length(pos); float d2 = d*d; if (d < r) return 1e6; // Light is inside the sphere if (d > (r + range)) return -1.0f; // Light can't reach the sphere if ((Type == 1) && (cosp>0.1)) { - float x = D3DXVec3Dot(&pos, &Direction); + float x = dotp(pos, Direction); if (x < -r) return -1.0f; // The sphere is a way behind the spotlight if ((sqrt(d2 - x*x) - x*tanp) * cosp > r) return -1.0f; // Light cone doesn't intersect the sphere } @@ -1416,13 +1412,13 @@ void D3D9Light::UpdateLight(const LightEmitter *_le, const class vObject *vo) // ----------------------------------------------------------------------------- - D3DXVec3TransformCoord(&Position, ptr(D3DXVEC(le->GetPosition())), vo->MWorld()); - Dst2 = D3DXVec3Dot(&Position, &Position); + Position = oapiTransformCoord(&(_F(le->GetPosition())), vo->MWorld()); + Dst2 = dotp(Position, Position); // ----------------------------------------------------------------------------- const double *att = ((PointLight*)le)->GetAttenuation(); - Attenuation = D3DXVECTOR3((float)att[0], (float)att[1], (float)att[2]); + Attenuation = FVECTOR3((float)att[0], (float)att[1], (float)att[2]); // ----------------------------------------------------------------------------- @@ -1464,7 +1460,7 @@ void D3D9Light::UpdateLight(const LightEmitter *_le, const class vObject *vo) // ----------------------------------------------------------------------------- intensity = float(le->GetIntensity()); - const COLOUR4 &col_d = le->GetDiffuseColour(); + const FVECTOR4 &col_d = le->GetDiffuseColour(); Diffuse.r = (col_d.r*intensity); Diffuse.g = (col_d.g*intensity); Diffuse.b = (col_d.b*intensity); @@ -1489,8 +1485,8 @@ void D3D9Light::UpdateLight(const LightEmitter *_le, const class vObject *vo) // ----------------------------------------------------------------------------- if (Type != 0) { - D3DXVec3TransformNormal(&Direction, ptr(D3DXVEC(le->GetDirection())), vo->MWorld()); - float angle = acos(dot(unit(Position), Direction)); + Direction = oapiTransformNormal(&_F(le->GetDirection()), vo->MWorld()); + float angle = acos(dotp(unit(Position), Direction)); cone = ilerp(U * 0.5f, P * 0.5f, angle); } } @@ -1693,7 +1689,7 @@ bool SketchMesh::LoadMeshFromHandle(MESHHANDLE hMesh) // ----------------------------------------------------------------------- nMtrl = oapiMeshMaterialCount(hMesh); - if (nMtrl) Mtrl = new D3DXCOLOR[nMtrl]; + if (nMtrl) Mtrl = new FVECTOR4[nMtrl]; for (DWORD i = 0; i < nMtrl; i++) { MATERIAL* pMat = oapiMeshMaterial(hMesh, i); if (pMat) { @@ -1787,11 +1783,11 @@ SURFHANDLE SketchMesh::GetTexture(DWORD idx) // =============================================================================================== // -D3DXCOLOR SketchMesh::GetMaterial(DWORD idx) +FVECTOR4 SketchMesh::GetMaterial(DWORD idx) { assert(idx < nGrp); if (Grp[idx].MtrlIdx != SPEC_DEFAULT && Mtrl) return Mtrl[Grp[idx].MtrlIdx]; - return D3DXCOLOR(1, 1, 1, 1); + return F4_One; } @@ -1974,14 +1970,14 @@ HANDLE ShaderClass::GetVSHandle(const char* name) -void ShaderClass::SetTexture(const char* name, LPDIRECT3DTEXTURE9 pTex, UINT flags, UINT aniso) +void ShaderClass::SetTexture(const char* name, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso) { D3DXHANDLE hVar = pPSCB->GetConstantByName(NULL, name); SetTexture((HANDLE)hVar, pTex, flags, aniso); } -void ShaderClass::SetTextureVS(const char* name, LPDIRECT3DTEXTURE9 pTex, UINT flags, UINT aniso) +void ShaderClass::SetTextureVS(const char* name, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso) { D3DXHANDLE hVar = pVSCB->GetConstantByName(NULL, name); SetTextureVS((HANDLE)hVar, pTex, flags, aniso); @@ -2019,7 +2015,7 @@ void ShaderClass::SetVSConstants(const char* name, void* data, UINT bytes) -void ShaderClass::SetTexture(HANDLE hVar, LPDIRECT3DTEXTURE9 pTex, UINT flags, UINT aniso) +void ShaderClass::SetTexture(HANDLE hVar, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso) { #ifdef SHDCLSDBG if (!hVar) { @@ -2044,7 +2040,7 @@ void ShaderClass::SetTexture(HANDLE hVar, LPDIRECT3DTEXTURE9 pTex, UINT flags, U } -void ShaderClass::SetTextureVS(HANDLE hVar, LPDIRECT3DTEXTURE9 pTex, UINT flags, UINT aniso) +void ShaderClass::SetTextureVS(HANDLE hVar, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso) { #ifdef SHDCLSDBG if (!hVar) { diff --git a/OVP/D3D9Client/D3D9Util.h b/OVP/D3D9Client/D3D9Util.h index 8dba8307e..05c564192 100644 --- a/OVP/D3D9Client/D3D9Util.h +++ b/OVP/D3D9Client/D3D9Util.h @@ -13,6 +13,7 @@ #include "DrawAPI.h" #include #include +#include "MathAPI.h" #include #include "gcCore.h" @@ -72,12 +73,9 @@ const char *_PTR(const void *p); // helper function to get address of a temporary // The regular "easy" way no longer works on some compilers so lets use a hack to get a simple thing done. // NB: use with caution - template T* ptr(T&& x) { return &x; } - - // ------------------------------------------------------------------------------------ // Vertex Declaration equal to NTVERTEX // ------------------------------------------------------------------------------------ @@ -191,26 +189,26 @@ typedef struct { typedef struct { - D3DXVECTOR3 pos; ///< beacon position - D3DXVECTOR3 dir; ///< light direction + FVECTOR3 pos; ///< beacon position + FVECTOR3 dir; ///< light direction float size, angle, on, off; ///< beacon size and light cone angle float bright, falloff; DWORD color; ///< beacon color } BAVERTEX; typedef struct _LightStruct { - int Type; ///< Type of light source - float Dst2; ///< Square distance between camera and the light emitter - D3DXCOLOR Diffuse; ///< Color of light - D3DXVECTOR3 Position; ///< position in world space - D3DXVECTOR3 Direction; ///< direction in world space - D3DXVECTOR3 Attenuation; ///< Attenuation - D3DXVECTOR4 Param; ///< range, falloff, theta, phi + int Type; ///< Type of light source + float Dst2; ///< Square distance between camera and the light emitter + FVECTOR4 Diffuse; ///< Color of light + FVECTOR3 Position; ///< position in world space + FVECTOR3 Direction; ///< direction in world space + FVECTOR3 Attenuation; ///< Attenuation + FVECTOR4 Param; ///< range, falloff, theta, phi public : _LightStruct () : Type(0), Dst2(0.0), - Diffuse(D3DXCOLOR(0ul)), - Position(0,0,0), Direction(1.0f, 0.0f, 0.0f), Attenuation(1.0f, 1.0f, 1.0f), - Param(0,0,0,0) + Diffuse(0ul), + Position(), Direction(1.0f, 0.0f, 0.0f), Attenuation(1.0f, 1.0f, 1.0f), + Param() {} } LightStruct; @@ -222,7 +220,7 @@ class D3D9Light : public LightStruct D3D9Light(const LightEmitter *le, const class vObject *vo); ~D3D9Light(); - float GetIlluminance(D3DXVECTOR3 &pos, float r) const; + float GetIlluminance(FVECTOR3 &pos, float r) const; void UpdateLight(const LightEmitter *le, const class vObject *vo); void Reset(); const LightEmitter *GetEmitter() const; @@ -259,7 +257,7 @@ class SketchMesh bool LoadMeshFromHandle(MESHHANDLE hMesh); void RenderGroup(DWORD idx); SURFHANDLE GetTexture(DWORD idx); - D3DXCOLOR GetMaterial(DWORD idx); + FVECTOR4 GetMaterial(DWORD idx); DWORD GroupCount() const { return nGrp; } private: @@ -277,7 +275,7 @@ class SketchMesh LPDIRECT3DDEVICE9 pDev; SURFHANDLE* Tex; // list of mesh textures SKETCHGRP* Grp; // list of mesh groups - D3DXCOLOR* Mtrl; + FVECTOR4* Mtrl; }; #pragma pack(push, 4) @@ -307,32 +305,20 @@ typedef struct { * \brief Material structure used in D3D9Mesh. ModFlags is not loaded to shaders */ typedef struct { - D3DXVECTOR4 Diffuse; - D3DXVECTOR4 Specular; ///< Specular color, power in alpha - D3DXVECTOR3 Ambient; - D3DXVECTOR3 Emissive; - D3DXVECTOR3 Reflect; ///< Color multiplier and intensity (alpha) - D3DXVECTOR3 Emission2; ///< - D3DXVECTOR3 Fresnel; ///< Fresnel reflection - D3DXVECTOR2 Roughness; ///< - float Metalness; - D3DXVECTOR4 SpecialFX; + FVECTOR4 Diffuse; + FVECTOR4 Specular; ///< Specular color, power in alpha + FVECTOR3 Ambient; + FVECTOR3 Emissive; + FVECTOR3 Reflect; ///< Color multiplier and intensity (alpha) + FVECTOR3 Emission2; ///< + FVECTOR3 Fresnel; ///< Fresnel reflection + FVECTOR2 Roughness; ///< + float Metalness; + FVECTOR4 SpecialFX; // ----------------------- DWORD ModFlags; ///< Modification flags } D3D9MatExt; - -typedef struct { - D3DCOLORVALUE Albedo; // Tune Albedo - D3DCOLORVALUE Emis; // Tune Emission Maps - D3DCOLORVALUE Spec; // Tune Specular Maps - D3DCOLORVALUE Refl; // Tune Reflection Maps - D3DCOLORVALUE Transl; // Tune translucent effect - D3DCOLORVALUE Transm; // Tune transmissive effect - D3DCOLORVALUE Norm; // Tune normal map - D3DCOLORVALUE Rghn; // Tune roughness map -} D3D9Tune; - #pragma pack(pop) typedef struct { @@ -340,15 +326,15 @@ typedef struct { class vObject *vObj; ///< Visual handle float dist; ///< Distance to a pick point int group; ///< Mesh group that was picked - D3DXVECTOR3 normal; ///< Normal vector in local vessel coordinates - D3DXVECTOR3 pos; ///< Position in local vessel coordinates + FVECTOR3 normal; ///< Normal vector in local vessel coordinates + FVECTOR3 pos; ///< Position in local vessel coordinates int idx; ///< Index that was picked float u, v; ///< Barycentric coordinates } D3D9Pick; typedef struct { - D3DXVECTOR3 _p; // Position from camera - D3DXVECTOR3 _n; // Normal + FVECTOR3 _p; // Position from camera + FVECTOR3 _n; // Normal int i; // Face Index float d; // Distance from camera float u, v; @@ -389,13 +375,13 @@ class ShaderClass HANDLE GetPSHandle(const char* name); HANDLE GetVSHandle(const char* name); - void SetTexture(const char* name, LPDIRECT3DTEXTURE9 pTex, UINT Flags = IPF_CLAMP | IPF_ANISOTROPIC, UINT AnisoLvl = 4); - void SetTextureVS(const char* name, LPDIRECT3DTEXTURE9 pTex, UINT flags = IPF_CLAMP | IPF_POINT, UINT AnisoLvl = 0); + void SetTexture(const char* name, LPDIRECT3DBASETEXTURE9 pTex, UINT Flags = IPF_CLAMP | IPF_ANISOTROPIC, UINT AnisoLvl = 4); + void SetTextureVS(const char* name, LPDIRECT3DBASETEXTURE9 pTex, UINT flags = IPF_CLAMP | IPF_POINT, UINT AnisoLvl = 0); void SetPSConstants(const char* name, void* data, UINT bytes); void SetVSConstants(const char* name, void* data, UINT bytes); - void SetTexture(HANDLE hVar, LPDIRECT3DTEXTURE9 pTex, UINT flags = IPF_CLAMP | IPF_POINT, UINT AnisoLvl = 0); - void SetTextureVS(HANDLE hVar, LPDIRECT3DTEXTURE9 pTex, UINT flags, UINT aniso); + void SetTexture(HANDLE hVar, LPDIRECT3DBASETEXTURE9 pTex, UINT flags = IPF_CLAMP | IPF_POINT, UINT AnisoLvl = 0); + void SetTextureVS(HANDLE hVar, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso); void SetPSConstants(HANDLE hVar, void* data, UINT bytes); void SetVSConstants(HANDLE hVar, void* data, UINT bytes); LPDIRECT3DDEVICE9 GetDevice() { return pDev; } @@ -404,8 +390,8 @@ class ShaderClass struct TexParams { - LPDIRECT3DTEXTURE9 pTex; - LPDIRECT3DTEXTURE9 pAssigned; + LPDIRECT3DBASETEXTURE9 pTex; + LPDIRECT3DBASETEXTURE9 pAssigned; UINT Flags; UINT AnisoLvl; bool bSamplerSet; @@ -433,7 +419,6 @@ inline double wrap(double a) return a; } -void LogMatrix(D3DXMATRIX *pM, const char *name); inline void LogSunLight(D3D9Sun& s) { LogAlw("Sunlight.Dir = [%f, %f, %f]", s.Dir.x, s.Dir.y, s.Dir.z); @@ -443,44 +428,16 @@ inline void LogSunLight(D3D9Sun& s) LogAlw("Sunlight.Incat = [%f, %f, %f]", s.Incatter.x, s.Incatter.y, s.Incatter.z); } -// ----------------------------------------------------------------------------------- -// Conversion functions -// ------------------------------------------------------------------------------------ - -inline RECT _RECT(DWORD l, DWORD t, DWORD r, DWORD b) -{ - RECT rect = { long(l), long(t), long(r), long(b) }; - return rect; -} - -inline VECTOR3 _V(D3DXVECTOR3 &i) -{ - return _V(double(i.x), double(i.y), double(i.z)); -} - -inline oapi::FVECTOR3 _FV(D3DXVECTOR3 &i) -{ - return oapi::FVECTOR3(i.x, i.y, i.z); -} -inline VECTOR3 _V(D3DXVECTOR4 &i) -{ - return _V(double(i.x), double(i.y), double(i.z)); -} -inline void D3DXCOLORSWAP(D3DXCOLOR *x) +inline void D3DXCOLORSWAP(FVECTOR4 *x) { float a = x->r; x->r = x->b; x->b = a; } -inline D3DXVECTOR3 D3DXVECTOR3f4(D3DXVECTOR4 v) +inline FVECTOR4 D3DCOLORMULT(const FVECTOR4*a, const FVECTOR4*b) { - return D3DXVECTOR3(v.x, v.y, v.z); -} - -inline D3DCOLORVALUE D3DCOLORMULT(const D3DCOLORVALUE *a, const D3DCOLORVALUE *b) -{ - D3DCOLORVALUE c; + FVECTOR4 c; c.a = a->a * b->a; c.r = a->r * b->r; c.g = a->g * b->g; @@ -488,88 +445,40 @@ inline D3DCOLORVALUE D3DCOLORMULT(const D3DCOLORVALUE *a, const D3DCOLORVALUE *b return c; } -inline void MATRIX4toD3DMATRIX (const MATRIX4 &M, D3DXMATRIX &D) +inline FMATRIX4 _FMATRIX (const MATRIX4 &M) { - D._11 = (float)M.m11; D._12 = (float)M.m12; D._13 = (float)M.m13; D._14 = (float)M.m14; - D._21 = (float)M.m21; D._22 = (float)M.m22; D._23 = (float)M.m23; D._24 = (float)M.m24; - D._31 = (float)M.m31; D._32 = (float)M.m32; D._33 = (float)M.m33; D._34 = (float)M.m34; - D._41 = (float)M.m41; D._42 = (float)M.m42; D._43 = (float)M.m43; D._44 = (float)M.m44; + FMATRIX4 D; + D.m11 = (float)M.m11; D.m12 = (float)M.m12; D.m13 = (float)M.m13; D.m14 = (float)M.m14; + D.m21 = (float)M.m21; D.m22 = (float)M.m22; D.m23 = (float)M.m23; D.m24 = (float)M.m24; + D.m31 = (float)M.m31; D.m32 = (float)M.m32; D.m33 = (float)M.m33; D.m34 = (float)M.m34; + D.m41 = (float)M.m41; D.m42 = (float)M.m42; D.m43 = (float)M.m43; D.m44 = (float)M.m44; + return D; } -inline MATRIX4 _MATRIX4(const LPD3DXMATRIX M) +inline MATRIX4 _MATRIX4(const FMATRIX4* M) { MATRIX4 D; - D.m11 = (double)M->_11; D.m12 = (double)M->_12; D.m13 = (double)M->_13; D.m14 = (double)M->_14; - D.m21 = (double)M->_21; D.m22 = (double)M->_22; D.m23 = (double)M->_23; D.m24 = (double)M->_24; - D.m31 = (double)M->_31; D.m32 = (double)M->_32; D.m33 = (double)M->_33; D.m34 = (double)M->_34; - D.m41 = (double)M->_41; D.m42 = (double)M->_42; D.m43 = (double)M->_43; D.m44 = (double)M->_44; + D.m11 = (double)M->m11; D.m12 = (double)M->m12; D.m13 = (double)M->m13; D.m14 = (double)M->m14; + D.m21 = (double)M->m21; D.m22 = (double)M->m22; D.m23 = (double)M->m23; D.m24 = (double)M->m24; + D.m31 = (double)M->m31; D.m32 = (double)M->m32; D.m33 = (double)M->m33; D.m34 = (double)M->m34; + D.m41 = (double)M->m41; D.m42 = (double)M->m42; D.m43 = (double)M->m43; D.m44 = (double)M->m44; return D; } -inline void TransformVertex(NMVERTEX *pVrt, LPD3DXMATRIX pW) +inline void TransformVertex(NMVERTEX *pVrt, const FMATRIX4* pW) { - D3DXVECTOR3 p,n,t; - D3DXVec3TransformCoord(&p, ptr(D3DXVECTOR3(pVrt->x, pVrt->y, pVrt->z)), pW); - D3DXVec3TransformNormal(&n, ptr(D3DXVECTOR3(pVrt->nx, pVrt->ny, pVrt->nz)), pW); - D3DXVec3TransformNormal(&t, ptr(D3DXVECTOR3(pVrt->tx, pVrt->ty, pVrt->tz)), pW); + FVECTOR3 p = oapiTransformCoord(ptr(FVECTOR3(pVrt->x, pVrt->y, pVrt->z)), pW); + FVECTOR3 n = oapiTransformNormal(ptr(FVECTOR3(pVrt->nx, pVrt->ny, pVrt->nz)), pW); + FVECTOR3 t = oapiTransformNormal(ptr(FVECTOR3(pVrt->tx, pVrt->ty, pVrt->tz)), pW); pVrt->x = p.x; pVrt->y = p.y; pVrt->z = p.z; pVrt->nx = n.x; pVrt->ny = n.y; pVrt->nz = n.z; pVrt->tx = t.x; pVrt->ty = t.y; pVrt->tz = t.z; } -inline void D3DVEC (const VECTOR3 &v, D3DVECTOR &d3dv) -{ - d3dv.x = (float)v.x; - d3dv.y = (float)v.y; - d3dv.z = (float)v.z; -} - -inline D3DXVECTOR4 D3DXC2V(const D3DXCOLOR &v) -{ - return D3DXVECTOR4(v.r, v.g, v.b, v.a); -} - -inline D3DXVECTOR3 D3DXVEC(const VECTOR3 &v) -{ - return D3DXVECTOR3(float(v.x), float(v.y), float(v.z)); -} - -inline D3DXVECTOR3 D3DXVEC(const VECTOR4 &v) -{ - return D3DXVECTOR3(float(v.x), float(v.y), float(v.z)); -} - -inline D3DXVECTOR4 D3DXVEC4(const VECTOR3 &v, float w) -{ - return D3DXVECTOR4(float(v.x), float(v.y), float(v.z), w); -} - -inline D3DXCOLOR _D3DXCOLOR(const VECTOR3 &v, float a = 1.0f) -{ - return D3DXCOLOR(float(v.x), float(v.y), float(v.z), a); -} - -inline VECTOR3 _VD3DX(const D3DXVECTOR3 &v) -{ - return _V(double(v.x), double(v.y), double(v.z)); -} - -inline VECTOR4 _VD4DX(const D3DXVECTOR4 &v) -{ - return _V(double(v.x), double(v.y), double(v.z), double(v.w)); -} - -inline float D3DVAL (double x) -{ - return (float)x; -} - -//char* _fgets(char* cbuf, int num, FILE* stream, bool keepOneSpace = false); - int fgets2(char *buf, int cmax, FILE *file, DWORD param=0); -float D3DXVec3Angle(D3DXVECTOR3 a, D3DXVECTOR3 b); -D3DXVECTOR3 Perpendicular(D3DXVECTOR3 *a); +float D3DXVec3Angle(FVECTOR3 a, FVECTOR3 b); +FVECTOR3 Perpendicular(FVECTOR3 *a); const char *RemovePath(const char *in); SketchMesh * GetSketchMesh(const MESHHANDLE hMesh); @@ -581,7 +490,6 @@ void UpdateMatExt(const D3DMATERIAL9 *pIn, D3D9MatExt *pOut); void CreateDefaultMat(D3D9MatExt *pOut); void GetMatExt(const D3D9MatExt *pIn, D3DMATERIAL9 *pOut); bool CopyBuffer(LPDIRECT3DRESOURCE9 _pDst, LPDIRECT3DRESOURCE9 _pSrc); -void D3D9TuneInit(D3D9Tune *); int LoadPlanetTextures(const char* fname, LPDIRECT3DTEXTURE9* ppdds, DWORD flags, int amount); float SunOcclusionByPlanet(OBJHANDLE hObj, VECTOR3 gpos); float OcclusionFactor(float x, float sunrad, float plnrad); @@ -598,33 +506,39 @@ DWORD BuildDate(); // D3D vector and matrix operations // ------------------------------------------------------------------------------------ -float D3DMAT_BSScaleFactor(const D3DXMATRIX *mat); -void D3DMAT_Identity (D3DXMATRIX *mat); -void D3DMAT_ZeroMatrix(D3DXMATRIX *mat); -void D3DMAT_Copy (D3DXMATRIX *tgt, const D3DXMATRIX *src); -void D3DMAT_SetRotation (D3DXMATRIX *mat, const MATRIX3 *rot); -void D3DMAT_SetInvRotation (D3DXMATRIX *mat, const MATRIX3 *rot); -void D3DMAT_RotationFromAxis (const D3DXVECTOR3 &axis, float angle, D3DXMATRIX *rot); -void D3DMAT_FromAxis(D3DXMATRIX *out, const D3DVECTOR *x, const D3DVECTOR *y, const D3DVECTOR *z); -void D3DMAT_FromAxis(D3DXMATRIX *out, const VECTOR3 *x, const VECTOR3 *y, const VECTOR3 *z); -void D3DMAT_FromAxisT(D3DXMATRIX *out, const D3DVECTOR *x, const D3DVECTOR *y, const D3DVECTOR *z); -void D3DMAT_CreateX_Billboard(const D3DXVECTOR3 *toCam, const D3DXVECTOR3 *pos, float scale, D3DXMATRIX *pOut); -void D3DMAT_CreateX_Billboard(const D3DXVECTOR3 *toCam, const D3DXVECTOR3 *pos, const D3DXVECTOR3 *dir, float size, float stretch, D3DXMATRIX *pOut); +float D3DMAT_BSScaleFactor(const FMATRIX4 *mat); +void D3DMAT_ZeroMatrix(FMATRIX4 *mat); +void D3DMAT_Copy (FMATRIX4 *tgt, const FMATRIX4 *src); +void D3DMAT_Scale(FMATRIX4* mat, float x, float y, float z); +void D3DMAT_SetRotation (FMATRIX4 *mat, const MATRIX3 *rot); +void D3DMAT_SetInvRotation (FMATRIX4 *mat, const MATRIX3 *rot); +void D3DMAT_RotationFromAxis (const FVECTOR3 &axis, float angle, FMATRIX4 *rot); +void D3DMAT_FromAxis(FMATRIX4 *out, const FVECTOR3 *x, const FVECTOR3 *y, const FVECTOR3 *z); +void D3DMAT_FromAxis(FMATRIX4 *out, const VECTOR3 *x, const VECTOR3 *y, const VECTOR3 *z); +void D3DMAT_FromAxisT(FMATRIX4 *out, const FVECTOR3 *x, const FVECTOR3 *y, const FVECTOR3 *z); +void D3DMAT_CreateX_Billboard(const FVECTOR3 *toCam, const FVECTOR3 *pos, float scale, FMATRIX4 *pOut); +void D3DMAT_CreateX_Billboard(const FVECTOR3 *toCam, const FVECTOR3 *pos, const FVECTOR3 *dir, float size, float stretch, FMATRIX4 *pOut); +void D3DMAT_Transformation2D(FMATRIX4* pOut, const FVECTOR2* pSclCtr, float SclRot, const FVECTOR2* pScl, const FVECTOR2* pRotCtr, float Rot, const FVECTOR2* pTransl); +void D3DMAT_AffineTransformation2D(FMATRIX4* pOut, float Scl, const FVECTOR2* pRotCtr, float Rot, const FVECTOR2* pTransl); + +void D3DMAT_OrthoOffCenterLH(FMATRIX4* o, float l, float r, float b, float t, float zn, float zf); +void D3DMAT_OrthoOffCenterRH(FMATRIX4* o, float l, float r, float b, float t, float zn, float zf); +void D3DMAT_LookAtRH(FMATRIX4* o, const FVECTOR3* pEye, const FVECTOR3* pAt, const FVECTOR3* pUp); // Set up a as matrix for ANTICLOCKWISE rotation r around x/y/z-axis -void D3DMAT_RotX (D3DXMATRIX *mat, double r); -void D3DMAT_RotY (D3DXMATRIX *mat, double r); +void D3DMAT_RotX (FMATRIX4 *mat, double r); +void D3DMAT_RotY (FMATRIX4 *mat, double r); + +void D3DMAT_SetTranslation(FMATRIX4 *mat, const VECTOR3 *trans); +void D3DMAT_SetTranslation(FMATRIX4 *mat, const FVECTOR3 *trans); -void D3DMAT_SetTranslation (D3DXMATRIX *mat, const VECTOR3 *trans); -void D3DMAT_SetTranslation(D3DXMATRIX *mat, const D3DXVECTOR3 *trans); -bool D3DMAT_VectorMatrixMultiply (D3DXVECTOR3 *res, const D3DXVECTOR3 *v, const D3DXMATRIX *mat); -HRESULT D3DMAT_MatrixInvert (D3DXMATRIX *res, D3DXMATRIX *a); +void D3DMAT_Transform(FVECTOR4* o, const FVECTOR4* i, const FMATRIX4* m); // ------------------------------------------------------------------------------------ // Vertex formats // ------------------------------------------------------------------------------------ struct VERTEX_XYZ { float x, y, z; }; // transformed vertex -struct VERTEX_XYZC { float x, y, z; D3DCOLOR col; }; // untransformed vertex with single colour component +struct VERTEX_XYZC { float x, y, z; DWORD col; }; // untransformed vertex with single colour component // untransformed lit vertex with texture coordinates struct VERTEX_XYZ_TEX { @@ -638,7 +552,7 @@ struct VERTEX_2TEX { float tu0, tv0, e; inline VERTEX_2TEX() : x(0.0f), y(0.0f), z(0.0f), nx(0.0f), ny(0.0f), nz(0.0f), tu0(0.0f), tv0(0.0f), e(0.0f) {} - inline VERTEX_2TEX(const D3DVECTOR& p, const D3DVECTOR& n, float u0, float v0, float u1, float v1) + inline VERTEX_2TEX(const FVECTOR3& p, const FVECTOR3& n, float u0, float v0, float u1, float v1) : x(p.x), y(p.y), z(p.z), nx(n.x), ny(n.y), nz(n.z), tu0(u0), tv0(v0), e(0.0f) {} }; @@ -762,6 +676,29 @@ struct AutoFile }; +// ----------------------------------------------------------------------------------- +// Conversion functions +// ------------------------------------------------------------------------------------ + +inline RECT _RECT(DWORD l, DWORD t, DWORD r, DWORD b) +{ + RECT rect = { long(l), long(t), long(r), long(b) }; + return rect; +} + + + + +inline FVECTOR3 _F(const XMFLOAT3& i) { return FVECTOR3(float(i.x), float(i.y), float(i.z)); } + + +inline const D3DXVECTOR3* _DX(const FVECTOR3& a) { return (D3DXVECTOR3*)&a; } +inline const D3DXVECTOR4* _DX(const FVECTOR4& a) { return (D3DXVECTOR4*)&a; } +inline const D3DXVECTOR3* _DX(const FVECTOR3* a) { return (D3DXVECTOR3*)a; } +inline const D3DXVECTOR4* _DX(const FVECTOR4* a) { return (D3DXVECTOR4*)a; } +inline const D3DXMATRIX* _DX(const FMATRIX4& a) { return (D3DXMATRIX*)&a; } +inline const D3DXMATRIX* _DX(const FMATRIX4* a) { return (D3DXMATRIX*)a; } + // ------------------------------------------------------------------------------------ // Miscellaneous helper functions // ------------------------------------------------------------------------------------ diff --git a/OVP/D3D9Client/DebugControls.cpp b/OVP/D3D9Client/DebugControls.cpp index dad0ce16e..613f73b86 100644 --- a/OVP/D3D9Client/DebugControls.cpp +++ b/OVP/D3D9Client/DebugControls.cpp @@ -17,7 +17,9 @@ #include "Mesh.h" #include "MaterialMgr.h" #include "VectorHelpers.h" +#include "OapiExtension.h" #include +#include enum scale { LIN, SQRT, SQR }; @@ -34,6 +36,10 @@ extern D3D9Client *g_client; namespace DebugControls { +DbgDisplay dbgdsp = {}; +int ambdir = -1; +int bkl_id = 0; +int probe_id = -1; DWORD dwGFX, dwCmd, nMesh, nGroup, sMesh, sGroup, debugFlags, dspMode, camMode, SelColor, sEmitter; double camSpeed; float cpr, cpg, cpb, cpa; @@ -44,20 +50,24 @@ HWND hGfxDlg = NULL; HWND hDlg = NULL; HWND hDataWnd = NULL; vObject *vObj = NULL; +D3D9Mesh* hSelMesh = NULL; std::string buffer(""); std::string buffer2(""); -D3DXVECTOR3 PickLocation; +FVECTOR3 PickLocation; std::map Emitters; HWND hTipRed, hTipGrn, hTipBlu, hTipAlp; -OPENFILENAMEA OpenTex, SaveTex; +OPENFILENAMEA OpenTex, SaveTex, SaveMesh; char OpenFileName[255]; char SaveFileName[255]; +char SaveMeshName[255]; +char SaveMeshTitle[255]; void UpdateMaterialDisplay(bool bSetup=false); void SetGFXSliders(); +void UpdateLightsSlider(); INT_PTR CALLBACK WndProcGFX(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void OpenGFXDlgClbk(void *context); @@ -84,8 +94,186 @@ struct _Params { }; -_Params Params[20] = { 0 }; +_Params Params[10] = { 0 }; +struct DbgMeshGrp { + vector Lines; + vector Vtx; + vector Idx; +}; + + +class DbgMesh +{ +public: + + DbgMesh(const char* file) + { + enum sec { header, group, geom, materials, textures }; + sec sec = header; + char buf[256]; + DWORD ng = 0, nm = 0, nt = 0, nv = 0, ni = 0, gi = 0; + std::ifstream is(file); + + if (is.is_open()) + { + while (is.getline(buf, 256)) + { + if (!_strnicmp(buf, "MATERIALS", 9)) sec = materials; + if (!_strnicmp(buf, "TEXTURES", 8)) sec = textures; + + if (sec == header) Header.push_back(buf); + if (sec == materials) Materials.push_back(buf); + if (sec == textures) Textures.push_back(buf); + + if (!_strnicmp(buf, "GROUPS", 6)) + { + if (sscanf(buf + 6, "%d", &ng) != 1) throw std::invalid_argument("End of file"); + Groups.resize(ng); + + for (gi = 0;gi> key >> x; + return x; + } + } + return string(""); + } + + void SetMeshFlags(DWORD f) + { + std::stringstream s; + s << std::hex << f; + for (auto &a : Header) { + if (a.find("MESHFLAGS") != string::npos) { + a = string("MESHFLAGS ") + s.str(); + return; + } + } + Header.insert(Header.begin() + 1, string("MESHFLAGS ") + s.str()); + } + + void SetGroupFlags(DWORD grp, DWORD f) + { + std::stringstream s; + s << std::hex << f; + for (auto &a : Groups[grp].Lines) { + if (a.find("FLAG") != string::npos) { + a = string("FLAG ") + s.str(); + return; + } + } + auto b = Groups[grp].Lines.begin(); + Groups[grp].Lines.insert(b, string("FLAG ") + s.str()); + } + + void SetGroupMaterial(DWORD grp, DWORD x) + { + for (auto& a : Groups[grp].Lines) { + if (a.find("MATERIAL") != string::npos) { + a = string("MATERIAL ") + std::to_string(x + 1); + return; + } + } + auto b = Groups[grp].Lines.begin(); + Groups[grp].Lines.insert(b, string("MATERIAL ") + std::to_string(x)); + } + + void SetGroupTexture(DWORD grp, DWORD x) + { + for (auto& a : Groups[grp].Lines) { + if (a.find("TEXTURE") != string::npos) { + a = string("TEXTURE ") + std::to_string(x); + return; + } + } + auto b = Groups[grp].Lines.begin(); + Groups[grp].Lines.insert(b, string("TEXTURE ") + std::to_string(x)); + } + + void SetGroupLabel(DWORD grp, string x) + { + for (auto& a : Groups[grp].Lines) { + if (a.find("LABEL") != string::npos) { + a = string("LABEL ") + x; + return; + } + } + auto b = Groups[grp].Lines.begin(); + Groups[grp].Lines.insert(b, string("LABEL ") + x); + } + + vector Header; + vector Materials; + vector Textures; + vector Groups; + string key; +}; + + +map dbgMsh; +DbgMesh* hDbgMsh = NULL; // =========================================================================== @@ -168,7 +356,7 @@ void Create() camMode = 0; dspMode = 0; SelColor = 0; - PickLocation = D3DXVECTOR3(0,0,0); + PickLocation = FVECTOR3(0,0,0); cpr = cpg = cpb = cpa = 0.0f; @@ -209,6 +397,20 @@ void Create() SaveTex.nMaxFileTitle = 0; SaveTex.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; + memset(&SaveMesh, 0, sizeof(OPENFILENAME)); + memset(SaveMeshName, 0, sizeof(SaveMeshName)); + memset(SaveMeshTitle, 0, sizeof(SaveMeshTitle)); + + SaveMesh.lStructSize = sizeof(OPENFILENAME); + SaveMesh.lpstrFile = SaveMeshName; + SaveMesh.lpstrInitialDir = "Meshes\0"; + SaveMesh.nMaxFile = sizeof(SaveMeshName); + SaveMesh.lpstrFilter = "*.msh\0"; + SaveMesh.nFilterIndex = 0; + SaveMesh.lpstrFileTitle = SaveMeshTitle; + SaveMesh.nMaxFileTitle = 0; + SaveMesh.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; + PrmList.push_back(MatParams("Diffuse", 0)); PrmList.push_back(MatParams("Ambient", 1)); PrmList.push_back(MatParams("Specular", 2)); @@ -219,14 +421,6 @@ void Create() PrmList.push_back(MatParams("Emission2", 7)); PrmList.push_back(MatParams("Metalness", 8)); PrmList.push_back(MatParams("SpecialFX", 9)); - PrmList.push_back(MatParams("- - - - - -", 10)); - PrmList.push_back(MatParams("Tune Albedo", 11)); - PrmList.push_back(MatParams("Tune _Emis", 12)); - PrmList.push_back(MatParams("Tune _Refl", 13)); - PrmList.push_back(MatParams("Tune _Rghn", 14)); - PrmList.push_back(MatParams("Tune _Transl", 15)); - PrmList.push_back(MatParams("Tune _Transm", 16)); - PrmList.push_back(MatParams("Tune _Spec", 17)); } // ============================================================================================= @@ -257,10 +451,10 @@ int GetSceneDebug() // ============================================================================================= // -int GetSelectedEnvMap() +DbgDisplay GetSelectedEnvMap() { - if (!hDlg) return 0; - return (int)SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_GETCURSEL, 0, 0); + if (!hDlg) return DbgDisplay(0); + return (DbgDisplay)SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_GETCURSEL, 0, 0); } // ============================================================================================= @@ -274,6 +468,7 @@ void Release() if (dwGFX) oapiUnregisterCustomCmd(dwGFX); dwCmd = NULL; dwGFX = NULL; + for (auto x : dbgMsh) SAFE_DELETE(x.second); } // ============================================================================================= @@ -293,6 +488,13 @@ void UpdateFlags() SETFLAG(debugFlags, DBG_FLAGS_DUALSIDED, (SendDlgItemMessageA(hDlg, IDC_DBG_DUAL, BM_GETCHECK, 0, 0)==BST_CHECKED)); SETFLAG(debugFlags, DBG_FLAGS_PICK, (SendDlgItemMessageA(hDlg, IDC_DBG_PICK, BM_GETCHECK, 0, 0)==BST_CHECKED)); SETFLAG(debugFlags, DBG_FLAGS_FPSLIM, (SendDlgItemMessageA(hDlg, IDC_DBG_FPSLIM, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_NEARCLIP, (SendDlgItemMessageA(hDlg, IDC_DBG_CLIPDIST, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_RENDEREXT, (SendDlgItemMessageA(hDlg, IDC_DBG_EXTVC, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_PICKCURRENT, (SendDlgItemMessageA(hDlg, IDC_DBG_PICKCURRENT, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_NOSUNAMB, (SendDlgItemMessageA(hDlg, IDC_DBG_NOSUNAMB, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_NOPLNAMB, (SendDlgItemMessageA(hDlg, IDC_DBG_NOPLNAMB, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_NODYNSUN, (SendDlgItemMessageA(hDlg, IDC_DBG_NODYNSUN, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_VCZONES, (SendDlgItemMessageA(hDlg, IDC_DBG_VCZONES, BM_GETCHECK, 0, 0) == BST_CHECKED)); Config->EnableLimiter = (int)((debugFlags&DBG_FLAGS_FPSLIM)>0); } @@ -346,7 +548,7 @@ void InitMatList(WORD shader) Dropdown.clear(); if (shader == SHADER_NULL) { - std::list list = { 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17 }; + std::list list = { 0, 1, 2, 3, 4, 5, 6, 7, 9 }; for (auto x : list) Dropdown.push_back(PrmList[x]); for (auto x : Dropdown) SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_ADDSTRING, 0, (LPARAM)x.name.c_str()); } @@ -357,6 +559,12 @@ void InitMatList(WORD shader) for (auto x : Dropdown) SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_ADDSTRING, 0, (LPARAM)x.name.c_str()); } + if (shader == SHADER_BAKED_VC) { + std::list list = { 0, 3, 5, 7, 8, 9 }; + for (auto x : list) Dropdown.push_back(PrmList[x]); + for (auto x : Dropdown) SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_ADDSTRING, 0, (LPARAM)x.name.c_str()); + } + SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_SETCURSEL, idx, 0); switch (shader) { @@ -365,6 +573,7 @@ void InitMatList(WORD shader) Params[6].var[2] = DefVar(10.0f, 4096.0f, SQRT, "Specular lobe size"); break; case SHADER_METALNESS: + case SHADER_BAKED_VC: Params[6].var[1] = DefVar(0, 1, LIN, "Fresnel effect attennuation 1.0 = disabled, 0.0 = max intensity"); Params[6].var[2].bUsed = false; break; @@ -377,6 +586,7 @@ void InitMatList(WORD shader) // void OpenDlgClbk(void *context) { + char buf[64]; DWORD idx = 0; HWND l_hDlg = oapiOpenDialog(g_hInst, IDD_D3D9MESHDEBUG, WndProc); @@ -405,6 +615,7 @@ void OpenDlgClbk(void *context) SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_RESETCONTENT, 0, 0); SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_ADDSTRING, 0, (LPARAM)"PBR (Old)"); SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_ADDSTRING, 0, (LPARAM)"Metalness PBR"); + SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_ADDSTRING, 0, (LPARAM)"Baked VC"); SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_SETCURSEL, 0, 0); SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_RESETCONTENT, 0, 0); @@ -436,23 +647,59 @@ void OpenDlgClbk(void *context) SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Blur 2"); SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Blur 3"); SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Blur 4"); - SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Irrad.Probe"); - SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"IrdPreItg"); - SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"ShadowMap"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"SS_ShadowMap"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"VC_ShadowMap"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"EX_ShadowMap"); SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Irradiance"); SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"GlowMask"); SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"ScreenDepth"); SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Normals"); SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"LightVisbil."); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"BakedLightMap"); SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"EclipseTbl"); SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"Omnidir"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Up (+y)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Down (-y)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Left (-x)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Right (+x)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Fwd (+z)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Aft (-z)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Exterior"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Interior 0"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Interior 1"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Interior 2"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Interior 3"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_RESETCONTENT, 0, 0); + for (int i = 0; i < 16; i++) { + sprintf_s(buf, 64, "Baked_%d", i); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)buf); + } + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)"Ambient"); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)"DA.Curve"); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)"DA.Bounch"); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)"DA.Force"); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_SETCURSEL, 0, 0); SetWindowText(GetDlgItem(hDlg, IDC_DBG_VARA), "1.3"); SetWindowText(GetDlgItem(hDlg, IDC_DBG_VARB), "0.01"); SetWindowText(GetDlgItem(hDlg, IDC_DBG_VARC), "0.00"); - // Speed slider + // BakedLights slider + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETPOS, 1, 0); + + // Resolution bias slider SendDlgItemMessage(hDlg, IDC_DBG_RESBIAS, TBM_SETRANGEMAX, 1, 10); SendDlgItemMessage(hDlg, IDC_DBG_RESBIAS, TBM_SETRANGEMIN, 1, -10); SendDlgItemMessage(hDlg, IDC_DBG_RESBIAS, TBM_SETTICFREQ, 1, 0); @@ -546,95 +793,30 @@ void OpenDlgClbk(void *context) // SpecialFX Params[9].var[0] = DefVar(0, 1, LIN, "Part Temperature"); - - - // Unused index 10 - - // Tuning ------------------------------------------------------------------------------------- - // Albedo - int i = 11; - Params[i].var[0] = DefVar(0.2f, 5.0f, SQRT, "Red"); - Params[i].var[1] = DefVar(0.2f, 5.0f, SQRT, "Green"); - Params[i].var[2] = DefVar(0.2f, 5.0f, SQRT, "Blue"); - Params[i].var[3] = DefVar(0.2f, 5.0f, SQRT, "Gamma", true); - - Params[1 + i] = Params[i]; // Emis - Params[1 + i].var[3] = DefVar(0.2f, 5.0f, SQRT, "Gamma", true); - - Params[2 + i] = Params[i]; // Refl - Params[2 + i].var[3] = DefVar(0.2f, 5.0f, SQRT, "Gamma", true); - - Params[3 + i] = Params[i]; // Regn - Params[3 + i].var[3] = DefVar(0.2f, 5.0f, SQRT, "Gamma", true); - - Params[4 + i] = Params[i]; // Transl - Params[4 + i].var[3] = DefVar(0.2f, 5.0f, SQRT, "???"); - - Params[5 + i] = Params[i]; // Transm - Params[5 + i].var[3] = DefVar(0.2f, 5.0f, SQRT, "???"); - - Params[6 + i] = Params[i]; // Spec - Params[6 + i].var[3] = DefVar(0.1f, 9.9f, SQRT, "Power", false); } // ============================================================================================= // -void SetTuningValue(int idx, D3DCOLORVALUE *pClr, DWORD clr, float value) +float _Clamp(float value, DWORD p, DWORD v) { bool bExtend = (SendDlgItemMessageA(hDlg, IDC_DBG_EXTEND, BM_GETCHECK, 0, 0) == BST_CHECKED); + return CLAMP(value, Params[p].var[v].min, (bExtend ? Params[p].var[v].extmax : Params[p].var[v].max)); +} - float mi = Params[idx].var[clr].min; - float mx = (bExtend ? Params[idx].var[clr].extmax : Params[idx].var[clr].max); - switch (clr) { - case 0: pClr->r = CLAMP(value, mi, mx); break; - case 1: pClr->g = CLAMP(value, mi, mx); break; - case 2: pClr->b = CLAMP(value, mi, mx); break; - case 3: - { - if (Params[idx].var[clr].bGamma) pClr->a = 1.0f / CLAMP(value, mi, mx); - else pClr->a = CLAMP(value, mi, mx); - } break; - } -} -// ============================================================================================= -// -float GetTuningValue(int idx, D3DCOLORVALUE *pClr, DWORD clr) -{ - switch (clr) { - case 0: return pClr->r; - case 1: return pClr->g; - case 2: return pClr->b; - case 3: - { - if (Params[idx].var[clr].bGamma) return 1.0f / pClr->a; - else return pClr->a; - } - } - return 1.0f; -} -// ============================================================================================= -// -float _Clamp(float value, DWORD p, DWORD v) -{ - bool bExtend = (SendDlgItemMessageA(hDlg, IDC_DBG_EXTEND, BM_GETCHECK, 0, 0) == BST_CHECKED); - return CLAMP(value, Params[p].var[v].min, (bExtend ? Params[p].var[v].extmax : Params[p].var[v].max)); -} // ============================================================================================= // void UpdateShader() { - OBJHANDLE hObj = vObj->GetObjectA(); + OBJHANDLE hObj = vObj->GetObjHandle(); if (!oapiIsVessel(hObj)) return; - D3D9Mesh *hMesh = (D3D9Mesh *)vObj->GetMesh(sMesh); - - if (!hMesh) return; + if (!hSelMesh) return; DWORD Shader = DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_GETCURSEL, 0, 0)); @@ -643,39 +825,130 @@ void UpdateShader() switch (Shader) { case 0: - hMesh->SetDefaultShader(SHADER_NULL); - pMgr->RegisterShaderChange(hMesh, SHADER_NULL); + hSelMesh->SetDefaultShader(SHADER_NULL); + pMgr->RegisterShaderChange(hSelMesh, SHADER_NULL); break; case 1: - hMesh->SetDefaultShader(SHADER_METALNESS); - pMgr->RegisterShaderChange(hMesh, SHADER_METALNESS); + hSelMesh->SetDefaultShader(SHADER_METALNESS); + pMgr->RegisterShaderChange(hSelMesh, SHADER_METALNESS); + break; + case 2: + hSelMesh->SetDefaultShader(SHADER_BAKED_VC); + pMgr->RegisterShaderChange(hSelMesh, SHADER_BAKED_VC); break; } - InitMatList(hMesh->GetDefaultShader()); + InitMatList(hSelMesh->GetDefaultShader()); +} + + +// ============================================================================================= +// +void UpdateGroup(DWORD grp) +{ + if (!hSelMesh) return; + if (grp >= hSelMesh->GetGroupCount()) return; + auto g = hSelMesh->GetGroup(grp); + + if (g) { + SendDlgItemMessage(hDlg, IDC_DBG_NOSHADOW, BM_SETCHECK, (g->UsrFlag & 0x1) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_NORENDER, BM_SETCHECK, (g->UsrFlag & 0x2) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_NOLIGHT, BM_SETCHECK, (g->UsrFlag & 0x4) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_ADDITIVE, BM_SETCHECK, (g->UsrFlag & 0x8) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_NOCOLOR, BM_SETCHECK, (g->UsrFlag & 0x10) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_OIT, BM_SETCHECK, (g->UsrFlag & 0x20) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_DYNAMIC, BM_SETCHECK, 0, 0); + + char buf[64]; + sprintf_s(buf, 64, "%d", g->MtrlIdx); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_MATIDX), buf); + sprintf_s(buf, 64, "%d", g->TexIdx); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_TEXIDX), buf); + string s = hDbgMsh->GetGroupLabel(grp); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_GRPLABEL), s.c_str()); + } } + // ============================================================================================= // -void UpdateMeshMaterial(float value, DWORD MatPrp, DWORD clr) +void ValidateGroup(DWORD grp) { - OBJHANDLE hObj = vObj->GetObjectA(); + if (!hSelMesh) return; + if (!hDbgMsh) return; - if (!oapiIsVessel(hObj)) return; + if (grp >= hSelMesh->GetGroupCount()) return; + auto g = hSelMesh->GetGroup(grp); + DWORD f = 0; - D3D9Mesh *hMesh = (D3D9Mesh *)vObj->GetMesh(sMesh); + if (g) + { + if (SendDlgItemMessage(hDlg, IDC_DBG_NOSHADOW, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x1; + if (SendDlgItemMessage(hDlg, IDC_DBG_NORENDER, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x2; + if (SendDlgItemMessage(hDlg, IDC_DBG_NOLIGHT, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x4; + if (SendDlgItemMessage(hDlg, IDC_DBG_ADDITIVE, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x8; + if (SendDlgItemMessage(hDlg, IDC_DBG_NOCOLOR, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x10; + if (SendDlgItemMessage(hDlg, IDC_DBG_OIT, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x20; + if (SendDlgItemMessage(hDlg, IDC_DBG_DYNAMIC, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x0; // TODO: To be implemented + + g->UsrFlag = f; + hDbgMsh->SetGroupFlags(grp, f); + + char buf[64]; + GetWindowText(GetDlgItem(hDlg, IDC_DBG_MATIDX), buf, 64); + g->MtrlIdx = atoi(buf); + if (g->MtrlIdx >= hSelMesh->GetMaterialCount()) g->MtrlIdx = hSelMesh->GetMaterialCount() - 1; + hDbgMsh->SetGroupMaterial(grp, g->MtrlIdx); + + GetWindowText(GetDlgItem(hDlg, IDC_DBG_TEXIDX), buf, 64); + g->TexIdx = atoi(buf); + if (g->TexIdx >= hSelMesh->GetTextureCount()) g->TexIdx = hSelMesh->GetTextureCount() - 1; + hDbgMsh->SetGroupTexture(grp, g->TexIdx); + + GetWindowText(GetDlgItem(hDlg, IDC_DBG_GRPLABEL), buf, 64); + hDbgMsh->SetGroupLabel(grp, string(buf)); + } +} - if (!hMesh) return; - - DWORD matidx = hMesh->GetMeshGroupMaterialIdx(sGroup); - DWORD texidx = hMesh->GetMeshGroupTextureIdx(sGroup); - D3D9MatExt Mat; - D3D9Tune Tune; +// ============================================================================================= +// +void NextDoNotRender() +{ + if (!hSelMesh) return; + DWORD bak = sGroup; + sGroup++; + for (; sGroup < hSelMesh->GetGroupCount(); sGroup++) { + auto g = hSelMesh->GetGroup(sGroup); + if (g->UsrFlag & 0x2) { + SetupMeshGroups(); + return; + } + } + for (sGroup = 0; sGroup < hSelMesh->GetGroupCount(); sGroup++) { + auto g = hSelMesh->GetGroup(sGroup); + if (g->UsrFlag & 0x2) { + SetupMeshGroups(); + return; + } + } + sGroup = bak; +} - if (!hMesh->GetMaterial(&Mat, matidx)) return; - bool bTune = hMesh->GetTexTune(&Tune, texidx); +// ============================================================================================= +// +void UpdateMeshMaterial(float value, DWORD MatPrp, DWORD clr) +{ + OBJHANDLE hObj = vObj->GetObjHandle(); + if (!oapiIsVessel(hObj)) return; + if (!hSelMesh) return; + + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); + DWORD texidx = hSelMesh->GetMeshGroupTextureIdx(sGroup); + + D3D9MatExt Mat; + if (!hSelMesh->GetMaterial(&Mat, matidx)) return; switch(MatPrp) { @@ -748,55 +1021,11 @@ void UpdateMeshMaterial(float value, DWORD MatPrp, DWORD clr) Mat.SpecialFX[clr] = _Clamp(value, MatPrp, clr); break; } - - case 11: // Tune Albedo - { - SetTuningValue(MatPrp, &Tune.Albedo, clr, value); - break; - } - - case 12: // Tune Emis - { - SetTuningValue(MatPrp, &Tune.Emis, clr, value); - break; - } - - case 13: // Tune Refl - { - SetTuningValue(MatPrp, &Tune.Refl, clr, value); - break; - } - - case 14: // Tune _Rghn - { - SetTuningValue(MatPrp, &Tune.Rghn, clr, value); - break; - } - - case 15: // Tune _Transl - { - SetTuningValue(MatPrp, &Tune.Transl, clr, value); - break; - } - - case 16: // Tune _Transm - { - SetTuningValue(MatPrp, &Tune.Transm, clr, value); - break; - } - - case 17: // Tune _Spec - { - SetTuningValue(MatPrp, &Tune.Spec, clr, value); - break; - } } - if (bTune) hMesh->SetTexTune(&Tune, texidx); - hMesh->SetMaterial(&Mat, matidx); + hSelMesh->SetMaterial(&Mat, matidx); vVessel *vVes = (vVessel *)vObj; - - vVes->GetMaterialManager()->RegisterMaterialChange(hMesh, matidx, &Mat); + vVes->GetMaterialManager()->RegisterMaterialChange(hSelMesh, matidx, &Mat); } @@ -824,19 +1053,14 @@ DWORD GetModFlags(DWORD MatPrp) // bool IsMaterialModified(DWORD MatPrp) { - OBJHANDLE hObj = vObj->GetObjectA(); - + OBJHANDLE hObj = vObj->GetObjHandle(); if (!oapiIsVessel(hObj)) return false; + if (!hSelMesh) return false; - D3D9Mesh *hMesh = (D3D9Mesh *)vObj->GetMesh(sMesh); - - if (!hMesh) return false; - - DWORD matidx = hMesh->GetMeshGroupMaterialIdx(sGroup); - + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); D3D9MatExt Mat; - if (!hMesh->GetMaterial(&Mat, matidx)) return false; + if (!hSelMesh->GetMaterial(&Mat, matidx)) return false; return (Mat.ModFlags & GetModFlags(MatPrp)) != 0; } @@ -846,24 +1070,20 @@ bool IsMaterialModified(DWORD MatPrp) // void SetMaterialModified(DWORD MatPrp, bool bState) { - OBJHANDLE hObj = vObj->GetObjectA(); + OBJHANDLE hObj = vObj->GetObjHandle(); if (!oapiIsVessel(hObj)) return; + if (!hSelMesh) return; - D3D9Mesh *hMesh = (D3D9Mesh *)vObj->GetMesh(sMesh); - - if (!hMesh) return; - - DWORD matidx = hMesh->GetMeshGroupMaterialIdx(sGroup); - + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); D3D9MatExt Mat; - if (!hMesh->GetMaterial(&Mat, matidx)) return; + if (!hSelMesh->GetMaterial(&Mat, matidx)) return; if (bState) Mat.ModFlags |= GetModFlags(MatPrp); else Mat.ModFlags &= (~GetModFlags(MatPrp)); - hMesh->SetMaterial(&Mat, matidx); + hSelMesh->SetMaterial(&Mat, matidx); } @@ -871,24 +1091,17 @@ void SetMaterialModified(DWORD MatPrp, bool bState) // float GetMaterialValue(DWORD MatPrp, DWORD clr) { - OBJHANDLE hObj = vObj->GetObjectA(); + OBJHANDLE hObj = vObj->GetObjHandle(); if (!oapiIsVessel(hObj)) return 0.0f; + if (!hSelMesh) return 0.0f; - D3D9Mesh *hMesh = (D3D9Mesh *)vObj->GetMesh(sMesh); + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); + DWORD texidx = hSelMesh->GetMeshGroupTextureIdx(sGroup); - if (!hMesh) return 0.0f; - - DWORD matidx = hMesh->GetMeshGroupMaterialIdx(sGroup); - DWORD texidx = hMesh->GetMeshGroupTextureIdx(sGroup); - - const D3D9MatExt *pMat = hMesh->GetMaterial(matidx); - + const D3D9MatExt *pMat = hSelMesh->GetMaterial(matidx); if (!pMat) return 0.0f; - D3D9Tune Tune; - bool bTune = hMesh->GetTexTune(&Tune, texidx); - switch(MatPrp) { case 0: // Diffuse @@ -990,41 +1203,6 @@ float GetMaterialValue(DWORD MatPrp, DWORD clr) } break; } - - case 11: // Tune Albedo - { - return GetTuningValue(MatPrp, &Tune.Albedo, clr); - } - - case 12: // Tune Emis - { - return GetTuningValue(MatPrp, &Tune.Emis, clr); - } - - case 13: // Tune Refl - { - return GetTuningValue(MatPrp, &Tune.Refl, clr); - } - - case 14: // Tune _Rghn - { - return GetTuningValue(MatPrp, &Tune.Rghn, clr); - } - - case 15: // Tune _Transl - { - return GetTuningValue(MatPrp, &Tune.Transl, clr); - } - - case 16: // Tune _Transm - { - return GetTuningValue(MatPrp, &Tune.Transm, clr); - } - - case 17: // Tune _Spec - { - return GetTuningValue(MatPrp, &Tune.Spec, clr); - } } return 0.0f; @@ -1100,17 +1278,16 @@ void UpdateMaterialDisplay(bool bSetup) char lbl[256]; char lbl2[64]; - OBJHANDLE hObj = vObj->GetObjectA(); + OBJHANDLE hObj = vObj->GetObjHandle(); if (!oapiIsVessel(hObj)) return; - - D3D9Mesh *hMesh = (D3D9Mesh *)vObj->GetMesh(sMesh); - if (!hMesh) return; + if (!hSelMesh) return; - WORD Shader = hMesh->GetDefaultShader(); + WORD Shader = hSelMesh->GetDefaultShader(); if (Shader == SHADER_NULL) SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_SETCURSEL, 0, 0); if (Shader == SHADER_METALNESS) SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_SETCURSEL, 1, 0); + if (Shader == SHADER_BAKED_VC) SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_SETCURSEL, 2, 0); - DWORD matidx = hMesh->GetMeshGroupMaterialIdx(sGroup); + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); // Set material info const char *skin = NULL; @@ -1131,22 +1308,33 @@ void UpdateMaterialDisplay(bool bSetup) SetToolTip(IDC_DBG_BLUE, hTipBlu, Params[MatPrp].var[2].tip); SetToolTip(IDC_DBG_ALPHA, hTipAlp, Params[MatPrp].var[3].tip); - DWORD texidx = hMesh->GetMeshGroupTextureIdx(sGroup); + DWORD texidx = hSelMesh->GetMeshGroupTextureIdx(sGroup); if (texidx==0) SetWindowText(GetDlgItem(hDlg, IDC_DBG_TEXTURE), "Texture: None"); else { - SURFHANDLE hSrf = hMesh->GetTexture(texidx); + SURFHANDLE hSrf = hSelMesh->GetTexture(texidx); if (hSrf) { sprintf_s(lbl, 256, "Texture: %s [%u]", RemovePath(SURFACE(hSrf)->GetName()), texidx); SetWindowText(GetDlgItem(hDlg, IDC_DBG_TEXTURE), lbl); } } - sprintf_s(lbl, 256, "Mesh: %s", RemovePath(hMesh->GetName())); + sprintf_s(lbl, 256, "Mesh: %s", RemovePath(hSelMesh->GetName())); SetWindowText(GetDlgItem(hDlg, IDC_DBG_MESHNAME), lbl); - - GetWindowText(GetDlgItem(hDlg, IDC_DBG_MESHGRP), lbl2, 64); - if (strcmp(lbl, lbl2)) SetWindowText(GetDlgItem(hDlg, IDC_DBG_MESHGRP), lbl); // Avoid causing flashing + + string str = ""; + DWORD vis = vObj->GetMeshVisMode(sMesh); + + if (vis == 0) str = "NEVER"; + if (vis == MESHVIS_ALWAYS) str = "ALWAYS"; + + if (vis & MESHVIS_COCKPIT) str.append("COCKPIT "); + if (vis & MESHVIS_VC) str.append("VC "); + if (vis & MESHVIS_EXTERNAL) str.append("EXTERNAL "); + if (vis & MESHVIS_EXTPASS) str.append("EXTPASS "); + + sprintf_s(lbl, 256, "MeshVisMode: %s", str.c_str()); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_VISMODE), lbl); } // ============================================================================================= @@ -1154,8 +1342,7 @@ void UpdateMaterialDisplay(bool bSetup) bool IsSelectedGroupRendered() { if (!vObj) return false; - D3D9Mesh *hMesh = (D3D9Mesh *)vObj->GetMesh(sMesh); - if (hMesh) return hMesh->IsGroupRendered(sGroup); + if (hSelMesh) return hSelMesh->IsGroupRendered(sGroup); return false; } @@ -1207,7 +1394,7 @@ DWORD GetSelectedMesh() return sMesh; } -void SetPickPos(D3DXVECTOR3 pos) +void SetPickPos(FVECTOR3 pos) { PickLocation = pos; } @@ -1222,17 +1409,48 @@ void SelectGroup(DWORD idx) } } +// ============================================================================================= +// +void ValidateMesh(D3D9Mesh* pMesh) +{ + if (pMesh != hSelMesh) + { + if (dbgMsh.find(pMesh) == dbgMsh.end()) { + char MeshFile[MAX_PATH]; + sprintf_s(MeshFile, MAX_PATH, "%s%s.msh", OapiExtension::GetMeshDir(), pMesh->GetName()); + dbgMsh[pMesh] = new DbgMesh(MeshFile); + } + hSelMesh = pMesh; + hDbgMsh = dbgMsh[pMesh]; + } +} + // ============================================================================================= // void SelectMesh(D3D9Mesh *pMesh) { - for (DWORD i=0;iGetMesh(i)==pMesh) { + sMesh = 0; + for (DWORD i = 0; i < nMesh; i++) { + if (vObj->GetMesh(i) == pMesh) { sMesh = i; - break; + ValidateMesh(pMesh); + SetupMeshGroups(); + return; } } - SetupMeshGroups(); +} + +// ============================================================================================= +// +void UpdateMeshFlags() +{ + if (!hSelMesh) return; + DWORD f = 0; + + if (SendDlgItemMessage(hDlg, IDC_DBG_ISVCMESH, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= MESHFLAG_VC; + if (SendDlgItemMessage(hDlg, IDC_DBG_VCSHADOW, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= MESHFLAG_SHADOW_VC; + hSelMesh->MeshFlags = f | 0x1; + hDbgMsh->SetMeshFlags(hSelMesh->MeshFlags); } // ============================================================================================= @@ -1259,14 +1477,15 @@ void SetupMeshGroups() sprintf_s(lbl,256,"%u/%u",sMesh,nMesh-1); SetWindowText(GetDlgItem(hDlg, IDC_DBG_MESH), lbl); - D3D9Mesh *mesh = (class D3D9Mesh *)vObj->GetMesh(sMesh); + ValidateMesh(vObj->GetMesh(sMesh)); - if (mesh) nGroup = mesh->GetGroupCount(); - else nGroup = 0; + if (hSelMesh) nGroup = hSelMesh->GetGroupCount(); + else nGroup = 0; if (nGroup!=0) { if (sGroup>0xFFFF) sGroup = nGroup-1; if (sGroup>=nGroup) sGroup = 0; + UpdateGroup(sGroup); } else { sGroup=0; @@ -1277,9 +1496,67 @@ void SetupMeshGroups() sprintf_s(lbl,256,"%u/%u",sGroup,nGroup-1); SetWindowText(GetDlgItem(hDlg, IDC_DBG_GROUP), lbl); + if (hSelMesh) { + SendDlgItemMessage(hDlg, IDC_DBG_ISVCMESH, BM_SETCHECK, (hSelMesh->MeshFlags & MESHFLAG_VC) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_VCSHADOW, BM_SETCHECK, (hSelMesh->MeshFlags & MESHFLAG_SHADOW_VC) != 0, 0); + } + UpdateMaterialDisplay(); SetColorSlider(); - InitMatList(mesh->GetDefaultShader()); + UpdateLightsSlider(); + InitMatList(hSelMesh->GetDefaultShader()); +} + + +// ============================================================================================= +// +void UpdateBakedLights(float lvl) +{ + vVessel* vV = (vVessel*)vObj; + if (vObj->Type() == OBJTP_VESSEL) + { + if (bkl_id < 16 && bkl_id >= 0) + vV->SetVisualProperty(VisualProp::BAKED_LIGHT, bkl_id, typeid(FVECTOR3), &FVECTOR3(lvl, lvl, lvl)); + if (bkl_id == 16) vV->SetVisualProperty(VisualProp::AMBIENT, 0, typeid(FVECTOR3), &FVECTOR3(lvl, lvl, lvl)); + if (bkl_id == 17) vV->SetVisualProperty(VisualProp::DA_CURVE, 0, typeid(float), &lvl); + if (bkl_id == 18) vV->SetVisualProperty(VisualProp::DA_BOUNCH, 0, typeid(float), &lvl); + if (bkl_id == 19) vV->SetVisualProperty(VisualProp::DA_FORCE, 0, typeid(float), &lvl); + + char lbl[128]; sprintf_s(lbl, 128, "Light Controls (%1.3f)", lvl); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_BKLGROUP), lbl); + } +} + +// ============================================================================================= +// +void UpdateLightsSlider() +{ + FVECTOR3 val = 0.0f; + float fVal = 0.0f; + vVessel* vV = (vVessel*)vObj; + if (vObj->Type() == OBJTP_VESSEL) + { + if (bkl_id < 16 && bkl_id >= 0) vV->GetVisualProperty(VisualProp::BAKED_LIGHT, bkl_id, typeid(val), &val); + if (bkl_id == 16) vV->GetVisualProperty(VisualProp::AMBIENT, 0, typeid(val), &val); + fVal = val.x; + if (bkl_id == 17) vV->GetVisualProperty(VisualProp::DA_CURVE, 0, typeid(float), &fVal); + if (bkl_id == 18) vV->GetVisualProperty(VisualProp::DA_BOUNCH, 0, typeid(float), &fVal); + if (bkl_id == 19) vV->GetVisualProperty(VisualProp::DA_FORCE, 0, typeid(float), &fVal); + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETPOS, 1, WORD(255.0f * fVal)); + char lbl[128]; sprintf_s(lbl, 128, "Light Controls (%1.3f)", fVal); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_BKLGROUP), lbl); + } +} + +// ============================================================================================= +// +LPDIRECT3DTEXTURE9 GetCombinedMap() +{ + if (hSelMesh) { + DWORD texidx = hSelMesh->GetMeshGroupTextureIdx(sGroup); + return hSelMesh->GetCombinedMap(texidx); + } + return NULL; } // ============================================================================================= @@ -1287,7 +1564,7 @@ void SetupMeshGroups() double GetVisualSize() { if (hDlg && vObj) { - OBJHANDLE hObj = vObj->GetObjectA(); + OBJHANDLE hObj = vObj->GetObjHandle(); if (hObj) return oapiGetSize(hObj); } return 1.0; @@ -1300,6 +1577,13 @@ vObject * GetVisual() return vObj; } +// ============================================================================================= +// +D3D9Mesh* GetMesh() +{ + return hSelMesh; +} + // ============================================================================================= // void SetVisual(vObject *vo) @@ -1329,7 +1613,7 @@ void UpdateVisual() SendDlgItemMessageA(hDlg, IDC_DBG_CONES, CB_ADDSTRING, 0, (LPARAM)"NONE"); Emitters[0] = NULL; - char line[64]; + char line[64]; strcpy(line, ""); vVessel *vV = static_cast(vObj); VESSEL *vessel = vV->GetInterface(); @@ -1403,7 +1687,7 @@ struct PCParam { // ============================================================================================= // -D3DXCOLOR ProcessColor(D3DXVECTOR4 C, PCParam *prm, int x, int y) +FVECTOR4 ProcessColor(FVECTOR4 C, PCParam *prm, int x, int y) { float fMip = float(prm->Mip); float a = prm->a; @@ -1411,28 +1695,28 @@ D3DXCOLOR ProcessColor(D3DXVECTOR4 C, PCParam *prm, int x, int y) float c = prm->c; // Swap color channels - C = D3DXVECTOR4(C.z, C.y, C.z, C.x); + C = FVECTOR4(C.z, C.y, C.z, C.x); if (c>0.001) { - D3DXVECTOR4 rnd((float)oapiRand(),(float)oapiRand(), (float)oapiRand(), (float)oapiRand()); + FVECTOR4 rnd((float)oapiRand(),(float)oapiRand(), (float)oapiRand(), (float)oapiRand()); C += (rnd*2.0f-1.0f) * (c/(2.0f+fMip)); } // Reduce contrast if (prm->Func & 0x2) { - if (prm->Mip==0) return D3DXCOLOR(C.x, C.y, C.z, C.w); // Do nothing for the main level + if (prm->Mip==0) return FVECTOR4(C.x, C.y, C.z, C.w); // Do nothing for the main level C = C*2.0f - 1.0f; // Expand to [-1, 1] C *= pow(a, -abs(C)*fMip); float k = b * fMip; - C = D3DXVECTOR4(reduce(C.x, k), reduce(C.y, k), reduce(C.z, k), reduce(C.w, k)); + C = FVECTOR4(reduce(C.x, k), reduce(C.y, k), reduce(C.z, k), reduce(C.w, k)); C = C*0.5f + 0.5f; // Back to [0, 1] } - return D3DXCOLOR(C.x, C.y, C.z, C.w); + return FVECTOR4(C.x, C.y, C.z, C.w); } // ============================================================================================= @@ -1486,7 +1770,7 @@ bool Execute(HWND hWnd, LPOPENFILENAME pOF) // for (DWORD n=0;n>n; DWORD h = info.Height>>n; @@ -1505,33 +1789,33 @@ bool Execute(HWND hWnd, LPOPENFILENAME pOF) for (DWORD y=0;yGetEnvMap(ENVMAP_MAIN); - if (D3DXSaveTextureToFileA("EnvMap.dds", D3DXIFF_DDS, pTex, NULL) != S_OK) { - LogErr("Failed to save envmap"); + auto pTex = vVes->GetExteriorEnvMap() ? vVes->GetExteriorEnvMap()->pCube : nullptr; + if (pTex->GetType() == D3DRTYPE_CUBETEXTURE) { + if (D3DXSaveTextureToFileA("EnvMap.dds", D3DXIFF_DDS, (LPDIRECT3DCUBETEXTURE9)pTex, NULL) != S_OK) { + LogErr("Failed to save envmap"); + } } } } @@ -1805,6 +2091,12 @@ INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) UpdateColorSlider(pos); UpdateMaterialDisplay(); } + + if (HWND(lParam) == GetDlgItem(hWnd, IDC_DBG_BKLADJ)) { + if (pos == 0) pos = WORD(SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_GETPOS, 0, 0)); + float val = float(pos) / 255.0f; + UpdateBakedLights(val); + } } return false; } @@ -1823,9 +2115,15 @@ INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; } + case IDC_DBG_NEXT: + { + NextDoNotRender(); + break; + } + case IDC_DBG_MATSAVE: { - OBJHANDLE hObj = vObj->GetObjectA(); + OBJHANDLE hObj = vObj->GetObjHandle(); if (oapiIsVessel(hObj)) { vVessel *vVes = (vVessel *)vObj; vVes->GetMaterialManager()->SaveConfiguration(); @@ -1932,6 +2230,31 @@ INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } break; + case IDC_DBG_BKLID: + if (HIWORD(wParam) == CBN_SELCHANGE) { + bkl_id = int(SendDlgItemMessage(hDlg, IDC_DBG_BKLID, CB_GETCURSEL, 0, 0)); + UpdateLightsSlider(); + } + break; + + case IDC_DBG_DATASRC: + if (HIWORD(wParam) == CBN_SELCHANGE) { + probe_id = int(SendDlgItemMessage(hDlg, IDC_DBG_DATASRC, CB_GETCURSEL, 0, 0)) - 1; + } + break; + + case IDC_DBG_AMBDIR: + if (HIWORD(wParam) == CBN_SELCHANGE) { + ambdir = int(SendDlgItemMessage(hDlg, IDC_DBG_AMBDIR, CB_GETCURSEL, 0, 0)) - 1; + } + break; + + case IDC_DBG_ENVMAP: + if (HIWORD(wParam) == CBN_SELCHANGE) { + dbgdsp = DbgDisplay(SendDlgItemMessage(hDlg, IDC_DBG_ENVMAP, CB_GETCURSEL, 0, 0)); + } + break; + case IDC_DBG_DISPLAY: if (HIWORD(wParam)==CBN_SELCHANGE) dspMode = DWORD(SendDlgItemMessage(hWnd, IDC_DBG_DISPLAY, CB_GETCURSEL, 0, 0)); break; @@ -1997,6 +2320,20 @@ INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) oapiSetPause(bPaused); break; + case IDC_DBG_MESHSAVE: + if (dbgMsh[hSelMesh]) { + bPaused = oapiGetPause(); + oapiSetPause(true); + strcpy(SaveMesh.lpstrFileTitle, hSelMesh->GetName()); + SaveMesh.hwndOwner = hWnd; + if (GetSaveFileName(&SaveMesh)) { + dbgMsh[hSelMesh]->Save(SaveMesh.lpstrFile); + return true; + } + oapiSetPause(bPaused); + } + break; + case IDC_DBG_ACTION: break; @@ -2027,6 +2364,11 @@ INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) SetColorSlider(); break; + case IDC_DBG_ISVCMESH: + case IDC_DBG_VCSHADOW: + UpdateMeshFlags(); + break; + case IDC_DBG_GRPO: case IDC_DBG_VISO: case IDC_DBG_MSHO: @@ -2040,9 +2382,36 @@ INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case IDC_DBG_PICK: case IDC_DBG_FPSLIM: case IDC_DBG_TILEBB: + case IDC_DBG_CLIPDIST: + case IDC_DBG_EXTVC: + case IDC_DBG_PICKCURRENT: + case IDC_DBG_NOSUNAMB: + case IDC_DBG_NOPLNAMB: + case IDC_DBG_NODYNSUN: + case IDC_DBG_VCZONES: UpdateFlags(); break; + case IDC_DBG_NOSHADOW: + case IDC_DBG_NORENDER: + case IDC_DBG_NOLIGHT: + case IDC_DBG_ADDITIVE: + case IDC_DBG_NOCOLOR: + case IDC_DBG_OIT: + case IDC_DBG_DYNAMIC: + if (HIWORD(wParam) == BN_CLICKED) { + ValidateGroup(sGroup); + } + break; + + case IDC_DBG_MATIDX: + case IDC_DBG_TEXIDX: + case IDC_DBG_GRPLABEL: + if (HIWORD(wParam) == EN_KILLFOCUS) { + ValidateGroup(sGroup); + } + break; + case IDC_DBG_VARA: case IDC_DBG_VARB: case IDC_DBG_VARC: @@ -2050,7 +2419,7 @@ INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; default: - LogErr("LOWORD(%hu), HIWORD(0x%hX)",LOWORD(wParam),HIWORD(wParam)); + //LogErr("LOWORD(%hu), HIWORD(0x%hX)",LOWORD(wParam),HIWORD(wParam)); break; } break; @@ -2326,7 +2695,7 @@ INT_PTR CALLBACK WndProcGFX(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; default: - LogErr("WndProcGFX() LOWORD(%hu), HIWORD(0x%hX)", LOWORD(wParam), HIWORD(wParam)); + //LogErr("WndProcGFX() LOWORD(%hu), HIWORD(0x%hX)", LOWORD(wParam), HIWORD(wParam)); break; } break; diff --git a/OVP/D3D9Client/DebugControls.h b/OVP/D3D9Client/DebugControls.h index b05e4a676..c07b20462 100644 --- a/OVP/D3D9Client/DebugControls.h +++ b/OVP/D3D9Client/DebugControls.h @@ -61,15 +61,31 @@ #define DBG_FLAGS_PICK 0x1000 ///< Enable mesh picking with mouse #define DBG_FLAGS_FPSLIM 0x2000 ///< FPS Limiter enabled #define DBG_FLAGS_TILEBOXES 0x4000 ///< Tile Boxes +#define DBG_FLAGS_NEARCLIP 0x8000 ///< Set Clip distance to 2cm +#define DBG_FLAGS_RENDEREXT 0x10000 ///< Render exterior meshes for VC view +#define DBG_FLAGS_PICKCURRENT 0x20000 ///< Only use Pick for current mesh +#define DBG_FLAGS_NOSUNAMB 0x40000 ///< Disable sun ambient effect in VC +#define DBG_FLAGS_NOPLNAMB 0x80000 ///< Disable planet shine ambient effect in VC +#define DBG_FLAGS_NODYNSUN 0x100000 ///< Disable dynamic sunlight +#define DBG_FLAGS_VCZONES 0x200000 ///< Display VC Click Zones /// @} class vObject; +enum class DbgDisplay { + None, Mirror, Blur1, Blur2, Blur3, Blur4, + smSS, smVC, smEX, Irradiance, GlowMask, + ScreenDepth, Normals, LightVisbil, BakedLightMap, Eclipse +}; + // ============================================================== namespace DebugControls { + extern DbgDisplay dbgdsp; + extern int ambdir; + extern int probe_id; extern DWORD sMesh; extern DWORD sGroup; extern DWORD debugFlags; @@ -106,7 +122,8 @@ namespace DebugControls { void SetVisual(vObject *vo); void RemoveVisual(vObject *vo); vObject * GetVisual(); - void SetPickPos(D3DXVECTOR3 pos); + void SetPickPos(FVECTOR3 pos); + D3D9Mesh* GetMesh(); void SetupMeshGroups(); void UpdateVisual(); @@ -117,7 +134,7 @@ namespace DebugControls { void SelectMesh(D3D9Mesh *pMesh); void SetGroupHighlight(bool bStat); int GetSceneDebug(); - int GetSelectedEnvMap(); + DbgDisplay GetSelectedEnvMap(); bool IsActive(); bool IsSelectedGroupRendered(); @@ -125,6 +142,10 @@ namespace DebugControls { void Append(const char *format, ...); void Refresh(); + inline int GetProbeId() { return probe_id; } + + LPDIRECT3DTEXTURE9 GetCombinedMap(); + INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK ViewProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); }; diff --git a/OVP/D3D9Client/GDIPad.h b/OVP/D3D9Client/GDIPad.h index 1d0a73074..e9744d85a 100644 --- a/OVP/D3D9Client/GDIPad.h +++ b/OVP/D3D9Client/GDIPad.h @@ -10,7 +10,7 @@ #include "OrbiterAPI.h" #include "D3D9Client.h" #include -#include +#include "MathAPI.h" // ====================================================================== diff --git a/OVP/D3D9Client/HazeMgr.cpp b/OVP/D3D9Client/HazeMgr.cpp index 76667bf30..7468dc947 100644 --- a/OVP/D3D9Client/HazeMgr.cpp +++ b/OVP/D3D9Client/HazeMgr.cpp @@ -73,17 +73,18 @@ void HazeManager::GlobalExit() // ----------------------------------------------------------------------- -void HazeManager::Render(LPDIRECT3DDEVICE9 pDev, D3DXMATRIX &wmat, bool dual) +void HazeManager::Render(LPDIRECT3DDEVICE9 pDev, FMATRIX4 &wmat, bool dual) { - D3DXMATRIX imat, transm; + FMATRIX4 imat, transm; VECTOR3 psun; int i, j; double phi, csun, alpha, colofs; float cosp, sinp, cost, sint, h1, h2, r1, r2, intr, intg, intb; - D3DMAT_MatrixInvert (&imat, &wmat); - VECTOR3 rpos = {imat._41, imat._42, imat._43}; // camera in local coords (planet radius = 1) + oapiMatrixInverse(&imat, NULL, &wmat); + + VECTOR3 rpos = {imat.m41, imat.m42, imat.m43}; // camera in local coords (planet radius = 1) double cdist = length (rpos); alpha = dens0 * min (1.0, (cdist-1.0)*200.0); @@ -123,12 +124,12 @@ void HazeManager::Render(LPDIRECT3DDEVICE9 pDev, D3DXMATRIX &wmat, bool dual) cost = (float)rpos.y, sint = (float)sqrt (1.0-cost*cost); phi = atan2 (rpos.z, rpos.x), cosp = (float)cos(phi), sinp = (float)sin(phi); - D3DXMATRIX rmat = D3DXMATRIX(cost*cosp, -sint, cost*sinp, 0, + FMATRIX4 rmat = FMATRIX4(cost*cosp, -sint, cost*sinp, 0, sint*cosp, cost, sint*sinp, 0, -sinp, 0, cosp, 0, 0, 0, 0, 1); - D3DXMatrixMultiply(&transm, &rmat, &wmat); + oapiMatrixMultiply(&transm, &rmat, &wmat); MATRIX3 rrmat = {cost*cosp, -sint, cost*sinp, sint*cosp, cost, sint*sinp, @@ -165,20 +166,20 @@ void HazeManager::Render(LPDIRECT3DDEVICE9 pDev, D3DXMATRIX &wmat, bool dual) else if (csun < minblue) intb = 0.0f; else intb = (float)((csun-minblue)*2.5); - D3DXCOLOR col = D3DXCOLOR(intr*min(1.0f,dens*(float)basecol.x), intg*min(1.0f,dens*(float)basecol.y), intb*min(1.0f,dens*(float)basecol.z), (float)alpha); - //D3DXCOLOR col = D3DXCOLOR(intr*min(1.0f,dens*(float)basecol.x), intg*min(1.0f,dens*(float)basecol.y), intb*min(1.0f,dens*(float)basecol.z), 1.0f); + FVECTOR4 col = FVECTOR4(intr*min(1.0f,dens*(float)basecol.x), intg*min(1.0f,dens*(float)basecol.y), intb*min(1.0f,dens*(float)basecol.z), (float)alpha); + //FVECTOR4 col = FVECTOR4(intr*min(1.0f,dens*(float)basecol.x), intg*min(1.0f,dens*(float)basecol.y), intb*min(1.0f,dens*(float)basecol.z), 1.0f); - Vtx[j].dcol = col; + Vtx[j].dcol = col.dword_abgr(); j++; Vtx[j].x = r2*CosP[i]; Vtx[j].y = h2; Vtx[j].z = r2*SinP[i]; - Vtx[j].dcol = col; + Vtx[j].dcol = col.dword_abgr(); j++; } HR(FX->SetTechnique(eHazeTech)); - HR(FX->SetMatrix(eW, &transm)); + HR(FX->SetMatrix(eW, _DX(transm))); HR(FX->SetTexture(eTex0, SURFACE(horizon)->GetTexture())); HR(pDev->SetVertexDeclaration(pHazeVertexDecl)); @@ -283,7 +284,7 @@ void HazeManager2::GlobalExit() // ----------------------------------------------------------------------- -void HazeManager2::Render(D3DXMATRIX &wmat, float horizontal_aperture_deg) +void HazeManager2::Render(FMATRIX4 &wmat, float horizontal_aperture_deg) { Scene* scn = vp->GetScene(); VECTOR3 cdir = scn->GetCameraGDir(); @@ -311,18 +312,19 @@ void HazeManager2::RenderSky(VECTOR3 cpos, VECTOR3 cdir, double rad, double apr) VECTOR3 ux = unit(crossp(cdir, ur)); VECTOR3 uy = unit(crossp(ur, ux)); - D3DXMATRIX mWL, mL; - D3DMAT_Identity(&mWL); - D3DMAT_FromAxisT(&mWL, ptr(_D3DXVECTOR3(ux)), ptr(_D3DXVECTOR3(ur)), ptr(_D3DXVECTOR3(uy))); + FMATRIX4 mWL, mL; + oapiMatrixIdentity(&mWL); + D3DMAT_FromAxisT(&mWL, ptr(_F(ux)), ptr(_F(ur)), ptr(_F(uy))); double a = 15.0*RAD; double b = (PI-asin(rad/cr))/6.0; - D3DXVECTOR3 vTileCenter = D3DXVECTOR3(float(sin(15.0*RAD)), 1.0f, float(1.0+cos(15.0*RAD))) * 0.5; - D3DXMatrixRotationAxis(&mL, ptr(_D3DXVECTOR3(ur)), float(-a*0.5)); - D3DXMatrixMultiply(&mWL, &mWL, &mL); - D3DXMatrixRotationAxis(&mL, ptr(_D3DXVECTOR3(ur)), float(-a)); + FVECTOR3 vTileCenter = FVECTOR3(float(sin(15.0*RAD)), 1.0f, float(1.0+cos(15.0*RAD))) * 0.5; + oapiMatrixRotationAxis(&mL, ptr(_F(ur)), float(-a*0.5)); + oapiMatrixMultiply(&mWL, &mWL, &mL); + oapiMatrixRotationAxis(&mL, ptr(_F(ur)), float(-a)); + //vp->GetScatterConst()->mVP = vp->GetScene()->PushCameraFrustumLimits(hd * 0.1, hd * 5.0); pDome->Setup(pPositionDecl, false, 2); @@ -340,12 +342,12 @@ void HazeManager2::RenderSky(VECTOR3 cpos, VECTOR3 cdir, double rad, double apr) for (int i=0;i<24;i++) { double x = al; - D3DXMatrixMultiply(&mWL, &mWL, &mL); + oapiMatrixMultiply(&mWL, &mWL, &mL); for (int j=0;j<6;j++) { float r1 = float(sin(x)); float h1 = -float(cos(x)); float r2 = float(sin(x+b)); float h2 = -float(cos(x+b)); - D3DXVECTOR3 vCnt = vTileCenter * D3DXVECTOR3((r1+r2)*0.5f, 1.0f, (r1+r2)*0.5f); vCnt.y = (h1+h2)*0.5f; - D3DXVec3TransformCoord(&vCnt, &vCnt, &mWL); + FVECTOR3 vCnt = vTileCenter * FVECTOR3((r1+r2)*0.5f, 1.0f, (r1+r2)*0.5f); vCnt.y = (h1+h2)*0.5f; + vCnt = oapiTransformCoord(&vCnt, &mWL); if (vp->GetScene()->IsVisibleInCamera(&vCnt, float(sin(a*0.5)*1.5))) RenderSkySegment(mWL, hd, x, x+b, j); x+=b; } @@ -358,7 +360,7 @@ void HazeManager2::RenderSky(VECTOR3 cpos, VECTOR3 cdir, double rad, double apr) // ----------------------------------------------------------------------- -void HazeManager2::RenderSkySegment(D3DXMATRIX &wmat, double rad, double dmin, double dmax, int index) +void HazeManager2::RenderSkySegment(FMATRIX4 &wmat, double rad, double dmin, double dmax, int index) { float r1 = float(rad * sin(dmin)); float h1 = -float(rad * cos(dmin)); @@ -375,7 +377,7 @@ void HazeManager2::RenderSkySegment(D3DXMATRIX &wmat, double rad, double dmin, d UINT prims = xres * yres * 2 - 2; pDome->SetVSConstants("Prm", &sprm, sizeof(ShaderParams)); - pDev->SetStreamSource(0, pSkyVB[index], 0, sizeof(D3DXVECTOR3)); + pDev->SetStreamSource(0, pSkyVB[index], 0, sizeof(FVECTOR3)); pDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, prims); } @@ -404,9 +406,9 @@ void HazeManager2::RenderRing(VECTOR3 cpos, VECTOR3 cdir, double rad, double hra VECTOR3 ux = unit(crossp(cdir, ur)); VECTOR3 uy = unit(crossp(ur, ux)); - D3DXMATRIX mW; - D3DMAT_Identity(&mW); - D3DMAT_FromAxisT(&mW, ptr(_D3DXVECTOR3(ux)), ptr(_D3DXVECTOR3(ur)), ptr(_D3DXVECTOR3(uy))); + FMATRIX4 mW; + oapiMatrixIdentity(&mW); + D3DMAT_FromAxisT(&mW, ptr(_F(ux)), ptr(_F(ur)), ptr(_F(uy))); ShaderParams sprm; pRing->Setup(pPositionDecl, false, 2); @@ -431,7 +433,7 @@ void HazeManager2::RenderRing(VECTOR3 cpos, VECTOR3 cdir, double rad, double hra UINT nPrims = HORIZON2_NSEG * HORIZON2_NRING * 2 - 2; pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - pDev->SetStreamSource(0, pRingVB, 0, sizeof(D3DXVECTOR3)); + pDev->SetStreamSource(0, pRingVB, 0, sizeof(FVECTOR3)); pDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, nPrims); pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); @@ -447,8 +449,8 @@ void HazeManager2::CreateRingBuffers() int v = 0; int nvrt = HORIZON2_NSEG * 2 * HORIZON2_NRING + 2; - D3DXVECTOR3 *pVrt = new D3DXVECTOR3[nvrt]; - D3DXVECTOR3 *pBuf = NULL; + FVECTOR3 *pVrt = new FVECTOR3[nvrt]; + FVECTOR3 *pBuf = NULL; float d = 1.0f/float(HORIZON2_NRING); double phi = 0.0; @@ -459,19 +461,19 @@ void HazeManager2::CreateRingBuffers() for (int k=0;kCreateVertexBuffer(v*sizeof(D3DXVECTOR3), 0, 0, D3DPOOL_DEFAULT, &pRingVB, NULL)); + HR(pDev->CreateVertexBuffer(v*sizeof(FVECTOR3), 0, 0, D3DPOOL_DEFAULT, &pRingVB, NULL)); if (pRingVB->Lock(0, 0, (void **)&pBuf,0)==S_OK) { - memcpy(pBuf, pVrt, v*sizeof(D3DXVECTOR3)); + memcpy(pBuf, pVrt, v*sizeof(FVECTOR3)); pRingVB->Unlock(); } @@ -487,8 +489,8 @@ void HazeManager2::CreateSkydomeBuffers(int index) int xseg = xreslvl[index]; int yseg = yreslvl[index]; - D3DXVECTOR3 *pVrt = new D3DXVECTOR3[xseg*yseg*2+2]; - D3DXVECTOR3 *pBuf = NULL; + FVECTOR3 *pVrt = new FVECTOR3[xseg*yseg*2+2]; + FVECTOR3 *pBuf = NULL; double sa = 0.0, ca = 1.0; double db = 1.0/double(yseg); @@ -498,17 +500,17 @@ void HazeManager2::CreateSkydomeBuffers(int index) for (int s=0;sCreateVertexBuffer(k*sizeof(D3DXVECTOR3), 0, 0, D3DPOOL_DEFAULT, &pSkyVB[index], NULL)); + HR(pDev->CreateVertexBuffer(k*sizeof(FVECTOR3), 0, 0, D3DPOOL_DEFAULT, &pSkyVB[index], NULL)); if (pSkyVB[index]->Lock(0, 0, (void **)&pBuf,0)==S_OK) { - memcpy(pBuf, pVrt, k*sizeof(D3DXVECTOR3)); + memcpy(pBuf, pVrt, k*sizeof(FVECTOR3)); pSkyVB[index]->Unlock(); } diff --git a/OVP/D3D9Client/HazeMgr.h b/OVP/D3D9Client/HazeMgr.h index fc3279702..b6a674725 100644 --- a/OVP/D3D9Client/HazeMgr.h +++ b/OVP/D3D9Client/HazeMgr.h @@ -46,7 +46,7 @@ class HazeManager : private D3D9Effect */ static void GlobalExit(); - void Render (LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, bool dual = false); + void Render (LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, bool dual = false); private: OBJHANDLE obj; @@ -89,13 +89,13 @@ class HazeManager2 static void CreateSkydomeBuffers(int index); static void CreateRingBuffers(); - void Render(D3DXMATRIX &wmat, float hz_aperture_deg); + void Render(FMATRIX4 &wmat, float hz_aperture_deg); private: void RenderRing(VECTOR3 cpos, VECTOR3 cdir, double rad, double hralt); void RenderSky(VECTOR3 cpos, VECTOR3 cdir, double rad, double apr); - void RenderSkySegment(D3DXMATRIX &wmat, double rad, double dmin, double dmax, int index); + void RenderSkySegment(FMATRIX4 &wmat, double rad, double dmin, double dmax, int index); OBJHANDLE obj; vPlanet *vp; diff --git a/OVP/D3D9Client/IProcess.cpp b/OVP/D3D9Client/IProcess.cpp index 8e104f505..5d1f7101c 100644 --- a/OVP/D3D9Client/IProcess.cpp +++ b/OVP/D3D9Client/IProcess.cpp @@ -176,7 +176,7 @@ bool ImageProcessing::SetupViewPort() // Setup view-projection matrix and viewport // - D3DXMatrixOrthoOffCenterLH(&mVP, 0.0f, (float)desc.Width, (float)desc.Height, 0.0f, 0.0f, 1.0f); + D3DMAT_OrthoOffCenterLH(&mVP, 0.0f, (float)desc.Width, (float)desc.Height, 0.0f, 0.0f, 1.0f); iVP.X = 0; iVP.Y = 0; @@ -186,9 +186,9 @@ bool ImageProcessing::SetupViewPort() iVP.MaxZ = 1.0f; HR(pDevice->SetViewport(&iVP)); - HR(pVSConst->SetMatrix(pDevice, hVP, &mVP)); - HR(pVSConst->SetVector(pDevice, hSiz, ptr(D3DXVECTOR4(float(desc.Width), float(desc.Height), 1.0f/float(desc.Width), 1.0f/float(desc.Height))))); - HR(pVSConst->SetVector(pDevice, hPos, &vTemplate)); + HR(pVSConst->SetMatrix(pDevice, hVP, _DX(mVP))); + HR(pVSConst->SetVector(pDevice, hSiz, _DX(FVECTOR4(float(desc.Width), float(desc.Height), 1.0f/float(desc.Width), 1.0f/float(desc.Height))))); + HR(pVSConst->SetVector(pDevice, hPos, _DX(vTemplate))); return true; } @@ -197,7 +197,7 @@ bool ImageProcessing::SetupViewPort() // void ImageProcessing::SetTemplate(float w, float h, float x, float y) { - vTemplate = D3DXVECTOR4(w, h, x, y); + vTemplate = FVECTOR4(w, h, x, y); } @@ -260,6 +260,7 @@ bool ImageProcessing::Execute(DWORD blendop, bool bInScene, gcIPInterface::ipite HR(pDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false)); HR(pDevice->SetRenderState(D3DRS_STENCILENABLE, false)); HR(pDevice->SetRenderState(D3DRS_COLORWRITEENABLE, 0xF)); + HR(pDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, false)); if (blendop == 1) { HR(pDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD)); @@ -269,14 +270,14 @@ bool ImageProcessing::Execute(DWORD blendop, bool bInScene, gcIPInterface::ipite // Define vertices -------------------------------------------------------- // - SMVERTEX Vertex[4] = { + static const SMVERTEX Vertex[4] = { {0, 0, 0, 0, 0}, {0, 1, 0, 0, 1}, {1, 1, 0, 1, 1}, {1, 0, 0, 1, 0} }; - static WORD cIndex[6] = {0, 2, 1, 0, 3, 2}; + static const WORD cIndex[6] = {0, 2, 1, 0, 3, 2}; // Set render targets ----------------------------------------------------- // @@ -292,16 +293,18 @@ bool ImageProcessing::Execute(DWORD blendop, bool bInScene, gcIPInterface::ipite // Set Depth-Stencil surface ---------------------------------------------- // + + pDevice->GetDepthStencilSurface(&pDepthBak); + if (pDepth) { HR(pDevice->SetRenderState(D3DRS_ZENABLE, true)); HR(pDevice->SetRenderState(D3DRS_ZWRITEENABLE, true)); - - pDevice->GetDepthStencilSurface(&pDepthBak); - pDevice->SetDepthStencilSurface(pDepth); + HR(pDevice->SetDepthStencilSurface(pDepth)); } else { HR(pDevice->SetRenderState(D3DRS_ZENABLE, false)); HR(pDevice->SetRenderState(D3DRS_ZWRITEENABLE, false)); + HR(pDevice->SetDepthStencilSurface(NULL)); } // Set textures and samplers ----------------------------------------------- @@ -382,9 +385,9 @@ bool ImageProcessing::Execute(DWORD blendop, bool bInScene, gcIPInterface::ipite // Disconnect render targets ---------------------------------------------- // - if (pDepth) { - pDevice->SetDepthStencilSurface(pDepthBak); - } + pDevice->SetDepthStencilSurface(pDepthBak); + SAFE_RELEASE(pDepthBak); + // Disconnect render targets ---------------------------------------------- // @@ -629,6 +632,23 @@ void ImageProcessing::SetTextureNative(const char *var, LPDIRECT3DBASETEXTURE9 h } +// ================================================================================================ +// +void ImageProcessing::SetTextureNative(int idx, LPDIRECT3DBASETEXTURE9 hTex, DWORD flags) +{ + if (idx < 0 || idx>15) return; + + if (!hTex) { + pTextures[idx].hTex = NULL; + pTextures[idx].flags = 0; + return; + } + + pTextures[idx].hTex = hTex; + pTextures[idx].flags = flags; +} + + // ================================================================================================ // void ImageProcessing::SetOutput(int id, SURFHANDLE hTex) diff --git a/OVP/D3D9Client/IProcess.h b/OVP/D3D9Client/IProcess.h index 195968849..34e379f18 100644 --- a/OVP/D3D9Client/IProcess.h +++ b/OVP/D3D9Client/IProcess.h @@ -7,7 +7,7 @@ #define __IPROCESS_H #include -#include +#include "MathAPI.h" #include #include #include "OrbiterAPI.h" @@ -115,6 +115,7 @@ class ImageProcessing { void SetDepthStencil(LPDIRECT3DSURFACE9 hSrf = NULL); void SetOutputNative(int id, LPDIRECT3DSURFACE9 hSrf); void SetTextureNative(const char *var, LPDIRECT3DBASETEXTURE9 hTex, DWORD flags); + void SetTextureNative(int idx, LPDIRECT3DBASETEXTURE9 hTex, DWORD flags); private: @@ -128,7 +129,7 @@ class ImageProcessing { struct { LPDIRECT3DBASETEXTURE9 hTex; DWORD flags; - } pTextures[8]; + } pTextures[16]; gcIPInterface::ipicull mesh_cull; @@ -142,8 +143,8 @@ class ImageProcessing { LPDIRECT3DVERTEXSHADER9 pVertex; LPDIRECT3DPIXELSHADER9 pPixel; D3DSURFACE_DESC desc; - D3DXMATRIX mVP; - D3DXVECTOR4 vTemplate; + FMATRIX4 mVP; + FVECTOR4 vTemplate; D3DVIEWPORT9 iVP; D3DXHANDLE hVP; D3DXHANDLE hPos; diff --git a/OVP/D3D9Client/Log.cpp b/OVP/D3D9Client/Log.cpp index 2a8a9f86d..ad3e85915 100644 --- a/OVP/D3D9Client/Log.cpp +++ b/OVP/D3D9Client/Log.cpp @@ -152,6 +152,21 @@ void D3D9DebugLogVec(const char* lbl, oapi::FVECTOR4 &v) D3D9DebugQueue.push(std::string(ErrBuf)); } +//------------------------------------------------------------------------------------------- +// +void D3D9DebugLogMatrix(const char* name, FMATRIX4* pM) +{ + D3D9DebugQueue.push(std::string(name)); + sprintf_s(ErrBuf, ERRBUF, "[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m11, pM->m12, pM->m13, pM->m14); + D3D9DebugQueue.push(std::string(ErrBuf)); + sprintf_s(ErrBuf, ERRBUF, "[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m21, pM->m22, pM->m23, pM->m24); + D3D9DebugQueue.push(std::string(ErrBuf)); + sprintf_s(ErrBuf, ERRBUF, "[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m31, pM->m32, pM->m33, pM->m34); + D3D9DebugQueue.push(std::string(ErrBuf)); + sprintf_s(ErrBuf, ERRBUF, "[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m41, pM->m42, pM->m43, pM->m44); + D3D9DebugQueue.push(std::string(ErrBuf)); +} + //------------------------------------------------------------------------------------------- // void D3D9InitLog(const char *file) @@ -361,6 +376,33 @@ void LogOapi(const char *format, ...) } } +//------------------------------------------------------------------------------------------- +// +void LogVerbose(const char* format, ...) +{ + + if (d3d9client_log == NULL) return; + if (iLine > LOG_MAX_LINES) return; + if (uEnableLog > 0) { + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log, "(%s)(0x%lX) ", my_ctime(), th); + + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + oapiWriteLogVerbose(ErrBuf); + + escape_ErrBuf(); + fputs(ErrBuf, d3d9client_log); + fputs("
\n", d3d9client_log); + fflush(d3d9client_log); + LeaveCriticalSection(&LogCrit); + } +} + // --------------------------------------------------- // void LogErr(const char *format, ...) @@ -487,3 +529,24 @@ void LogOk(const char *format, ...) LeaveCriticalSection(&LogCrit); }*/ } + +void LogMatrix(FMATRIX4* pM, const char* name) +{ + LogAlw("%s", name); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m11, pM->m12, pM->m13, pM->m14); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m21, pM->m22, pM->m23, pM->m24); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m31, pM->m32, pM->m33, pM->m34); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m41, pM->m42, pM->m43, pM->m44); +} + +void LogVec(FVECTOR4* pM, const char* name) +{ + LogAlw("%s", name); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->x, pM->y, pM->z, pM->w); +} + +void LogVec(VECTOR3* pM, const char* name) +{ + LogAlw("%s", name); + LogAlw("[%9.9g, %9.9g, %9.9g]", pM->x, pM->y, pM->z); +} diff --git a/OVP/D3D9Client/Log.h b/OVP/D3D9Client/Log.h index 9b8b325fd..59dd9dbe1 100644 --- a/OVP/D3D9Client/Log.h +++ b/OVP/D3D9Client/Log.h @@ -47,6 +47,7 @@ extern std::queue D3D9DebugQueue; void RuntimeError(const char* File, const char* Fnc, UINT Line); void D3D9DebugLog(const char *format, ...); void D3D9DebugLogVec(const char* lbl, oapi::FVECTOR4 &v); +void D3D9DebugLogMatrix(const char* name, oapi::FMATRIX4* pM); void D3D9InitLog(const char *file); void D3D9CloseLog(); void LogTrace(const char *format, ...); @@ -56,16 +57,21 @@ void LogOk (const char *format, ...); void LogBreak(const char* format, ...); void LogBlu(const char *format, ...); void LogOapi(const char *format, ...); +void LogVerbose(const char* format, ...); void LogAlw(const char *format, ...); void LogDbg(const char *color, const char *format, ...); void LogClr(const char *color, const char *format, ...); + double D3D9GetTime(); void D3D9SetTime(D3D9Time &inout, double ref); void MissingRuntimeError(); void FailedDeviceError(); void LogAttribs(DWORD attrib, DWORD w, DWORD h, LPCSTR origin); +void LogVec(oapi::FVECTOR4* pM, const char* name); +void LogVec(VECTOR3* pM, const char* name); +void LogMatrix(oapi::FMATRIX4* pM, const char* name); #define HALT() { RuntimeError(__FILE__,__FUNCTION__,__LINE__); } diff --git a/OVP/D3D9Client/MaterialMgr.cpp b/OVP/D3D9Client/MaterialMgr.cpp index d04e93f50..b37e0b9ee 100644 --- a/OVP/D3D9Client/MaterialMgr.cpp +++ b/OVP/D3D9Client/MaterialMgr.cpp @@ -18,60 +18,19 @@ MatMgr::MatMgr(class vObject *v, class D3D9Client *_gc) { gc = _gc; vObj = v; - - pCamera = new ENVCAMREC[1]; - - ResetCamera(0); - - Shaders.push_back(SHADER("PBR-Old",SHADER_NULL)); + + Shaders.push_back(SHADER("PBR-Old", SHADER_NULL)); Shaders.push_back(SHADER("Metalness", SHADER_METALNESS)); + Shaders.push_back(SHADER("BakedVC", SHADER_BAKED_VC)); } - // =========================================================================================== // MatMgr::~MatMgr() { MeshConfig.clear(); - - if (pCamera) { - if (pCamera[0].pOmitAttc) delete[] pCamera[0].pOmitAttc; - if (pCamera[0].pOmitDock) delete[] pCamera[0].pOmitDock; - delete[] pCamera; - } } - -// =========================================================================================== -// -ENVCAMREC * MatMgr::GetCamera(DWORD idx) -{ - return &pCamera[0]; -} - - -// =========================================================================================== -// -DWORD MatMgr::CameraCount() -{ - return 1; -} - - -// =========================================================================================== -// -void MatMgr::ResetCamera(DWORD idx) -{ - pCamera[idx].near_clip = 0.25f; - pCamera[idx].lPos = D3DXVECTOR3(0,0,0); - pCamera[idx].nAttc = 0; - pCamera[idx].nDock = 0; - pCamera[idx].flags = ENVCAM_OMIT_ATTC; - pCamera[idx].pOmitAttc = NULL; - pCamera[idx].pOmitDock = NULL; -} - - // =========================================================================================== // void MatMgr::RegisterMaterialChange(D3D9Mesh *pMesh, DWORD midx, const D3D9MatExt *pM) @@ -168,7 +127,7 @@ bool MatMgr::LoadConfiguration(bool bAppend) char meshname[64]; char shadername[64]; - OBJHANDLE hObj = vObj->GetObjectA(); + OBJHANDLE hObj = vObj->GetObjHandle(); if (oapiGetObjectType(hObj)!=OBJTP_VESSEL) return false; @@ -234,7 +193,7 @@ bool MatMgr::LoadConfiguration(bool bAppend) // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "SPECULAR", 8)) { if (sscanf_s(cbuf, "SPECULAR %f %f %f %f", &a, &b, &c, &d)!=4) LogErr("Invalid Line in (%s): %s", path, cbuf); - Mat.Specular = D3DXVECTOR4(a, b, c, d); + Mat.Specular = FVECTOR4(a, b, c, d); Mat.ModFlags |= D3D9MATEX_SPECULAR; continue; } @@ -242,7 +201,7 @@ bool MatMgr::LoadConfiguration(bool bAppend) // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "DIFFUSE", 7)) { if (sscanf_s(cbuf, "DIFFUSE %f %f %f %f", &a, &b, &c, &d)!=4) LogErr("Invalid Line in (%s): %s", path, cbuf); - Mat.Diffuse = D3DXVECTOR4(a, b, c, d); + Mat.Diffuse = FVECTOR4(a, b, c, d); Mat.ModFlags |= D3D9MATEX_DIFFUSE; continue; } @@ -250,7 +209,7 @@ bool MatMgr::LoadConfiguration(bool bAppend) // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "EMISSIVE", 8)) { if (sscanf_s(cbuf, "EMISSIVE %f %f %f", &a, &b, &c)!=3) LogErr("Invalid Line in (%s): %s", path, cbuf); - Mat.Emissive = D3DXVECTOR3(a, b, c); + Mat.Emissive = FVECTOR3(a, b, c); Mat.ModFlags |= D3D9MATEX_EMISSIVE; continue; } @@ -258,7 +217,7 @@ bool MatMgr::LoadConfiguration(bool bAppend) // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "EMISSION2", 9)) { if (sscanf_s(cbuf, "EMISSION2 %f %f %f", &a, &b, &c) != 3) LogErr("Invalid Line in (%s): %s", path, cbuf); - Mat.Emission2 = D3DXVECTOR3(a, b, c); + Mat.Emission2 = FVECTOR3(a, b, c); Mat.ModFlags |= D3D9MATEX_EMISSION2; continue; } @@ -266,7 +225,7 @@ bool MatMgr::LoadConfiguration(bool bAppend) // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "AMBIENT", 7)) { if (sscanf_s(cbuf, "AMBIENT %f %f %f", &a, &b, &c)!=3) LogErr("Invalid Line in (%s): %s", path, cbuf); - Mat.Ambient = D3DXVECTOR3(a, b, c); + Mat.Ambient = FVECTOR3(a, b, c); Mat.ModFlags |= D3D9MATEX_AMBIENT; continue; } @@ -274,7 +233,7 @@ bool MatMgr::LoadConfiguration(bool bAppend) // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "REFLECT", 7)) { if (sscanf_s(cbuf, "REFLECT %f %f %f", &a, &b, &c) != 3) LogErr("Invalid Line in (%s): %s", path, cbuf); - Mat.Reflect = D3DXVECTOR3(a, b, c); + Mat.Reflect = FVECTOR3(a, b, c); Mat.ModFlags |= D3D9MATEX_REFLECT; continue; } @@ -283,7 +242,7 @@ bool MatMgr::LoadConfiguration(bool bAppend) if (!strncmp(cbuf, "FRESNEL", 7)) { if (sscanf_s(cbuf, "FRESNEL %f %f %f", &a, &b, &c) != 3) LogErr("Invalid Line in (%s): %s", path, cbuf); if (b < 10.0f) b = 1024.0f; - Mat.Fresnel = D3DXVECTOR3(a, c, b); + Mat.Fresnel = FVECTOR3(a, c, b); Mat.ModFlags |= D3D9MATEX_FRESNEL; continue; } @@ -291,8 +250,8 @@ bool MatMgr::LoadConfiguration(bool bAppend) // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "ROUGHNESS", 9)) { int cnt = sscanf_s(cbuf, "ROUGHNESS %f %f", &a, &b); - if (cnt == 1) Mat.Roughness = D3DXVECTOR2(a, 1.0f); - else if (cnt == 2) Mat.Roughness = D3DXVECTOR2(a, b); + if (cnt == 1) Mat.Roughness = FVECTOR2(a, 1.0f); + else if (cnt == 2) Mat.Roughness = FVECTOR2(a, b); else LogErr("Invalid Line in (%s): %s", path, cbuf); Mat.ModFlags |= D3D9MATEX_ROUGHNESS; continue; @@ -301,8 +260,8 @@ bool MatMgr::LoadConfiguration(bool bAppend) // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "SMOOTHNESS", 10)) { int cnt = sscanf_s(cbuf, "SMOOTHNESS %f %f", &a, &b); - if (cnt == 1) Mat.Roughness = D3DXVECTOR2(a, 1.0f); - else if (cnt == 2) Mat.Roughness = D3DXVECTOR2(a, b); + if (cnt == 1) Mat.Roughness = FVECTOR2(a, 1.0f); + else if (cnt == 2) Mat.Roughness = FVECTOR2(a, b); else LogErr("Invalid Line in (%s): %s", path, cbuf); Mat.ModFlags |= D3D9MATEX_ROUGHNESS; continue; @@ -332,7 +291,7 @@ bool MatMgr::SaveConfiguration() char classname[256]; - OBJHANDLE hObj = vObj->GetObjectA(); + OBJHANDLE hObj = vObj->GetObjHandle(); if (oapiGetObjectType(hObj)!=OBJTP_VESSEL) return false; @@ -402,7 +361,7 @@ bool MatMgr::LoadCameraConfig() char path[256]; char classname[256]; - OBJHANDLE hObj = vObj->GetObjectA(); + OBJHANDLE hObj = vObj->GetObjHandle(); if (oapiGetObjectType(hObj)!=OBJTP_VESSEL) return false; @@ -420,85 +379,65 @@ bool MatMgr::LoadCameraConfig() if (file.IsInvalid()) return true; LogAlw("Reading a camera configuration file for a vessel %s (%s)", vessel->GetName(), vessel->GetClassNameA()); - - DWORD iattc = 0; - DWORD idock = 0; - DWORD camera = 0; - BYTE attclist[256]; - BYTE docklist[256]; + ENVCAMREC* pCamera = NULL; while(fgets2(cbuf, 256, file.pFile, 0x08)>=0) { float a, b, c; - DWORD id; // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "END_CAMERA", 10)) { - - if (iattc) pCamera[camera].pOmitAttc = new BYTE[iattc]; - if (idock) pCamera[camera].pOmitDock = new BYTE[idock]; - - if (iattc) memcpy(pCamera[camera].pOmitAttc, attclist, iattc); - if (idock) memcpy(pCamera[camera].pOmitDock, docklist, idock); - - pCamera[camera].nAttc = WORD(iattc); - pCamera[camera].nDock = WORD(idock); - + pCamera = NULL; continue; } // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "BEGIN_CAMERA", 12)) { - if (sscanf_s(cbuf, "BEGIN_CAMERA %u", &camera)!=1) LogErr("Invalid Line in (%s): %s", path, cbuf); - camera = 0; // For now just one camera - pCamera[camera].flags = 0; // Clear default flags + int idx = -1; + if (sscanf_s(cbuf, "BEGIN_CAMERA %d", &idx)!=1) LogErr("Invalid Line in (%s): %s", path, cbuf); + if (idx == 0) { + pCamera = ((vVessel*)vObj)->CreateEnvCam(EnvCamType::Exterior); + pCamera->id = -1; + pCamera->flags = 0; // Clear default flags + } + if (idx == 1) { + pCamera = ((vVessel*)vObj)->CreateEnvCam(EnvCamType::Interior); + pCamera->id = -1; + pCamera->flags = 0; // Clear default flags + } continue; } // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "LPOS", 4)) { if (sscanf_s(cbuf, "LPOS %g %g %g", &a, &b, &c)!=3) LogErr("Invalid Line in (%s): %s", path, cbuf); - pCamera[camera].lPos = D3DXVECTOR3(a,b,c); - continue; - } - - // -------------------------------------------------------------------------------------------- - if (!strncmp(cbuf, "OMITATTC", 8)) { - if (sscanf_s(cbuf, "OMITATTC %u", &id)!=1) LogErr("Invalid Line in (%s): %s", path, cbuf); - attclist[iattc++] = BYTE(id); - continue; - } - - // -------------------------------------------------------------------------------------------- - if (!strncmp(cbuf, "OMITDOCK", 8)) { - if (sscanf_s(cbuf, "OMITDOCK %u", &id)!=1) LogErr("Invalid Line in (%s): %s", path, cbuf); - docklist[idock++] = BYTE(id); + pCamera->lPos = FVECTOR3(a,b,c); continue; } // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "CLIPDIST", 8)) { if (sscanf_s(cbuf, "CLIPDIST %g", &a)!=1) LogErr("Invalid Line in (%s): %s", path, cbuf); - pCamera[camera].near_clip = a; + pCamera->near_clip = a; continue; } // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "OMIT_ALL_ATTC", 13)) { - pCamera[camera].flags |= ENVCAM_OMIT_ATTC; + pCamera->flags |= ENVCAM_OMIT_ATTC; continue; } // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "DO_NOT_OMIT_FOCUS", 17)) { - pCamera[camera].flags |= ENVCAM_FOCUS; + pCamera->flags |= ENVCAM_FOCUS; continue; } // -------------------------------------------------------------------------------------------- if (!strncmp(cbuf, "OMIT_ALL_DOCKS", 14)) { - pCamera[camera].flags |= ENVCAM_OMIT_DOCKS; + pCamera->flags |= ENVCAM_OMIT_DOCKS; continue; } diff --git a/OVP/D3D9Client/MaterialMgr.h b/OVP/D3D9Client/MaterialMgr.h index 0c24536f6..c3e643d26 100644 --- a/OVP/D3D9Client/MaterialMgr.h +++ b/OVP/D3D9Client/MaterialMgr.h @@ -9,31 +9,13 @@ #define __MATERIALMGR_H #include -#include +#include "MathAPI.h" #include "Mesh.h" #include "D3D9Client.h" #include "D3D9Util.h" #include "vObject.h" -#define ENVCAM_OMIT_ATTC 0x0001 -#define ENVCAM_OMIT_DOCKS 0x0002 -#define ENVCAM_FOCUS 0x0004 - - -/** - * \brief Storage structure to keep environmental camera information. - */ -struct ENVCAMREC { - D3DXVECTOR3 lPos; ///< Camera local position - float near_clip; ///< Near clip-plane distance - DWORD flags; ///< Camera flags - WORD nAttc; ///< Number of attachments points in a list - WORD nDock; ///< Number of docking ports in a list - BYTE * pOmitAttc; ///< Omit attachments - BYTE * pOmitDock; ///< Omit vessels in docking ports -}; - /** * \brief Management of custom configurations for vessel materials */ @@ -47,8 +29,6 @@ class MatMgr { MatMgr(class vObject *vObj, class D3D9Client *_gc); ~MatMgr(); - //DWORD NewRecord(const char *name, DWORD midx); - //void ClearRecord(DWORD iRec); void RegisterMaterialChange(D3D9Mesh *pMesh, DWORD midx, const D3D9MatExt *pM); void RegisterShaderChange(D3D9Mesh *pMesh, WORD id); void ApplyConfiguration(D3D9Mesh *pMesh); @@ -56,18 +36,11 @@ class MatMgr { bool LoadConfiguration(bool bAppend=false); bool LoadCameraConfig(); bool HasMesh(const char *name); - void ResetCamera(DWORD idx); - - ENVCAMREC * GetCamera(DWORD idx); - DWORD CameraCount(); private: vObject *vObj; D3D9Client *gc; - //DWORD nRec; ///< Number of records - //DWORD mRec; ///< Allocated records - struct SHADER { SHADER(string x, WORD i) { name = x; id = i; } @@ -82,8 +55,6 @@ class MatMgr { std::map MeshConfig; std::list Shaders; - - ENVCAMREC *pCamera; }; #endif diff --git a/OVP/D3D9Client/Mesh.cpp b/OVP/D3D9Client/Mesh.cpp index decef7603..32b8148af 100644 --- a/OVP/D3D9Client/Mesh.cpp +++ b/OVP/D3D9Client/Mesh.cpp @@ -19,13 +19,16 @@ #pragma warning(push) #pragma warning(disable : 4838) -#include +#include "DirectXMath.h" +#include "DirectXCollision.h" #pragma warning(pop) using namespace oapi; - +LPDIRECT3DTEXTURE9 D3D9Mesh::pShadowMap[SHM_CASCADE_COUNT] = {}; +FVECTOR4 D3D9Mesh::ShdSubRect[SHM_CASCADE_COUNT] = {}; MeshShader* D3D9Mesh::s_pShader[16] = {}; + MeshShader::VSConst MeshShader::vs_const = {}; MeshShader::PSConst MeshShader::ps_const = {}; MeshShader::PSBools MeshShader::ps_bools = {}; @@ -51,6 +54,7 @@ MeshBuffer::MeshBuffer(DWORD _nVtx, DWORD _nFace, const class D3D9Mesh *_pRoot) { nVtx = _nVtx; nIdx = _nFace * 3; + nRef = 1; pVB = NULL; pIB = NULL; @@ -59,7 +63,7 @@ MeshBuffer::MeshBuffer(DWORD _nVtx, DWORD _nFace, const class D3D9Mesh *_pRoot) pVBSys = new NMVERTEX[nVtx]; pIBSys = new WORD[nIdx]; - pGBSys = new D3DXVECTOR4[nVtx]; + pGBSys = new XMVECTOR[nVtx]; pSBSys = new SMVERTEX[nVtx]; pRoot = _pRoot; @@ -72,6 +76,7 @@ MeshBuffer::MeshBuffer(MeshBuffer *pSrc, const class D3D9Mesh *_pRoot) { nVtx = pSrc->nVtx; nIdx = pSrc->nIdx; + nRef = 1; pVB = NULL; pIB = NULL; @@ -80,12 +85,12 @@ MeshBuffer::MeshBuffer(MeshBuffer *pSrc, const class D3D9Mesh *_pRoot) pVBSys = new NMVERTEX[nVtx]; pIBSys = new WORD[nIdx]; - pGBSys = new D3DXVECTOR4[nVtx]; + pGBSys = new XMVECTOR[nVtx]; pSBSys = new SMVERTEX[nVtx]; memcpy(pSBSys, pSrc->pSBSys, sizeof(SMVERTEX) * nVtx); memcpy(pVBSys, pSrc->pVBSys, sizeof(NMVERTEX) * nVtx); - memcpy(pGBSys, pSrc->pGBSys, sizeof(D3DXVECTOR4) * nVtx); + memcpy(pGBSys, pSrc->pGBSys, sizeof(XMVECTOR) * nVtx); memcpy(pIBSys, pSrc->pIBSys, sizeof(WORD) * nIdx); pRoot = _pRoot; @@ -104,6 +109,19 @@ MeshBuffer::~MeshBuffer() SAFE_RELEASE(pVB); SAFE_RELEASE(pGB); SAFE_RELEASE(pSB); + oapiWriteLog("MeshBuffer::Destruct"); +} + +bool MeshBuffer::Release() +{ + nRef--; + return nRef <= 0; +} + +MeshBuffer* MeshBuffer::Reference() +{ + nRef++; + return this; } void MeshBuffer::MustRemap(DWORD mode) @@ -140,7 +158,7 @@ void MeshBuffer::Map(LPDIRECT3DDEVICE9 pDev) HR(pDev->CreateVertexBuffer(nVtx * sizeof(NMVERTEX), Usage, 0, D3DPOOL_DEFAULT, &pVB, NULL)); } if (!pGB) { - HR(pDev->CreateVertexBuffer(nVtx * sizeof(D3DXVECTOR4), Usage, 0, D3DPOOL_DEFAULT, &pGB, NULL)); + HR(pDev->CreateVertexBuffer(nVtx * sizeof(FVECTOR4), Usage, 0, D3DPOOL_DEFAULT, &pGB, NULL)); } if (!pIB) { HR(pDev->CreateIndexBuffer(nIdx * sizeof(WORD), Usage, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &pIB, NULL)); @@ -157,7 +175,7 @@ void MeshBuffer::Map(LPDIRECT3DDEVICE9 pDev) HR(pVB->Unlock()); HR(pGB->Lock(0, 0, (LPVOID*)&pTgt, Lock)); - memcpy(pTgt, pGBSys, nVtx * sizeof(D3DXVECTOR4)); + memcpy(pTgt, pGBSys, nVtx * sizeof(FVECTOR4)); HR(pGB->Unlock()); HR(pIB->Lock(0, 0, (LPVOID*)&pTgt, Lock)); @@ -182,16 +200,18 @@ void D3D9Mesh::Null(const char *meshName /* = NULL */) Grp = NULL; nTex = 0; Tex = NULL; - pTune = NULL; nMtrl = 0; pBuf = NULL; Mtrl = NULL; pGrpTF = NULL; - cAmbient = 0; + cAmbient = { 0.0f, 0.0f, 0.0f }; MaxFace = 0; MaxVert = 0; vClass = 0; - DefShader = SHADER_NULL; + DefShader = SHADER_LEGACY; + hOapiMesh = NULL; + MeshFlags = 0; + Flags = 0; bIsTemplate = false; bGlobalTF = false; @@ -202,8 +222,15 @@ void D3D9Mesh::Null(const char *meshName /* = NULL */) bCanRenderFast = false; bMtrlModidied = false; + bli = BakedLights.begin(); + Locals = new LightStruct[Config->MaxLights()]; + for (int i = 0; i < SHM_CASCADE_COUNT; i++) { + pShadowMap[i] = NULL; + ShdSubRect[i] = _V4(0,0,1,1); + } + memset(Locals, 0, sizeof(LightStruct) * Config->MaxLights()); memset(LightList, 0, sizeof(LightList)); strcpy_s(this->name, ARRAYSIZE(this->name), meshName ? meshName : "???"); @@ -220,6 +247,9 @@ D3D9Mesh::D3D9Mesh(const char *fname) : D3D9Effect() LoadMeshFromHandle(hMesh); oapiDeleteMesh(hMesh); } + else { + LogErr("Failed to load [%s]", fname); + } MeshCatalog.insert(this); if (pBuf) pBuf->Map(pDev); @@ -227,7 +257,7 @@ D3D9Mesh::D3D9Mesh(const char *fname) : D3D9Effect() // =========================================================================================== // -D3D9Mesh::D3D9Mesh(MESHHANDLE hMesh, bool asTemplate, D3DXVECTOR3 *reorig, float *scale) : D3D9Effect() +D3D9Mesh::D3D9Mesh(MESHHANDLE hMesh, bool asTemplate, FVECTOR3 *reorig, float *scale) : D3D9Effect() { Null(); LoadMeshFromHandle(hMesh, reorig, scale); @@ -265,10 +295,10 @@ D3D9Mesh::D3D9Mesh(DWORD groups, const MESHGROUPEX **hGroup, const SURFHANDLE *h for (DWORD i=0;iReference(); BBox = hTemp.BBox; MaxVert = hTemp.MaxVert; MaxFace = hTemp.MaxFace; + MeshFlags = hTemp.MeshFlags; + Flags = hTemp.Flags; + // Clone group records from a tremplate Grp = new GROUPREC[nGrp]; memcpy(Grp, hTemp.Grp, sizeof(GROUPREC)*nGrp); @@ -352,10 +386,10 @@ D3D9Mesh::D3D9Mesh(MESHHANDLE hMesh, const D3D9Mesh &hTemp) if (nMtrl) Mtrl = new D3D9MatExt[nMtrl]; for (DWORD i = 0; iIsLocalTo(this)) delete pBuf; + if (pBuf) if (pBuf->Release()) delete pBuf; + pBuf = nullptr; + + for (auto x : BakedLights) { + for (auto y : x.second.pMap) SAFE_RELEASE(y); + for (auto y : x.second.pSunAO) SAFE_RELEASE(y); + SAFE_RELEASE(x.second.pCombined); + SAFE_RELEASE(x.second.pSunAOComb); + } } @@ -402,6 +443,7 @@ void D3D9Mesh::Release() void D3D9Mesh::ReLoadMeshFromHandle(MESHHANDLE hMesh) { + hOapiMesh = hMesh; const char* meshn = oapiGetMeshFilename(hMesh); strcpy_s(name, 128, meshn ? meshn : "???"); @@ -425,7 +467,7 @@ void D3D9Mesh::ReLoadMeshFromHandle(MESHHANDLE hMesh) if (MaxVert == 0 || MaxFace == 0) { if (pBuf) if (pBuf->IsLocalTo(this)) { - delete pBuf; pBuf = NULL; + if (pBuf->Release()) SAFE_DELETE(pBuf); } return; } @@ -448,10 +490,11 @@ void D3D9Mesh::ReLoadMeshFromHandle(MESHHANDLE hMesh) for (DWORD i = 0; iMap(pDev); } +// =========================================================================================== +// +const char* D3D9Mesh::GetDirName(int i, int v) +{ + static const char* names[] = { "up", "down", "left", "right", "fwd", "aft" }; + static const char* named[] = { "+y", "-y", "-x", "+x", "+z", "-z" }; + if (v == 0) return names[i]; + return named[i]; +} + +// =========================================================================================== +// +FVECTOR3 D3D9Mesh::GetDir(int i) +{ + switch (i) { + case 0: return FVECTOR3(0, 1, 0); // Up + case 1: return FVECTOR3(0, -1, 0); // Down + case 2: return FVECTOR3(-1, 0, 0); // Left + case 3: return FVECTOR3( 1, 0, 0); // Right + case 4: return FVECTOR3(0, 0, 1); // Fwd + case 5: return FVECTOR3(0, 0, -1); // Aft + } + return FVECTOR3(0, 0, 1); +} + +// =========================================================================================== +// +void D3D9Mesh::ClearBake(int i) +{ + for (int k = 0; k < 16; k++) BakedLights[i].pMap[k] = NULL; + for (int k = 0; k < 6; k++) BakedLights[i].pSunAO[k] = NULL; + BakedLights[i].pCombined = NULL; + BakedLights[i].pSunAOComb = NULL; +} + +// =========================================================================================== +// +void D3D9Mesh::LoadBakedLights() +{ + if (BakedLights.size()) return; // Already Loaded, skip the rest + char id[32]; + + for (int i = 0; i < nTex; i++) + { + if (!Tex[i]) continue; // No base texture, pick next + + for (int j = 0; j < 16; j++) + { + sprintf_s(id, "_bkl%d", j); + LPDIRECT3DTEXTURE9 pTex = NatLoadSpecialTexture(Tex[i]->GetPath(), id, true); + if (pTex) { + if (BakedLights.find(i) == BakedLights.end()) ClearBake(i); + BakedLights[i].pMap[j] = pTex; + } + } + + if (Config->ExpVCLight == 0) + { + for (int j = 0; j < 6; j++) + { + sprintf_s(id, " baked sunlight %s", GetDirName(j, 0)); + LPDIRECT3DTEXTURE9 pTex = NatLoadSpecialTexture(Tex[i]->GetPath(), id, true); + if (pTex) { + if (BakedLights.find(i) == BakedLights.end()) ClearBake(i); + BakedLights[i].pSunAO[j] = pTex; + } + else { + sprintf_s(id, "_bs%s", GetDirName(j, 1)); + LPDIRECT3DTEXTURE9 pTex = NatLoadSpecialTexture(Tex[i]->GetPath(), id, true); + if (pTex) { + if (BakedLights.find(i) == BakedLights.end()) ClearBake(i); + BakedLights[i].pSunAO[j] = pTex; + } + } + } + } + } + + // Construct render surface for all combined maps + for (auto& a : BakedLights) { + int i = a.first; + if (Tex[i]) { + HR(D3DXCreateTexture(pDev, Tex[i]->GetWidth(), Tex[i]->GetHeight(), 0, D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &a.second.pCombined)); + HR(D3DXCreateTexture(pDev, Tex[i]->GetWidth(), Tex[i]->GetHeight(), 0, D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &a.second.pSunAOComb)); + } + } + + bli = BakedLights.begin(); +} + // =========================================================================================== // -void D3D9Mesh::LoadMeshFromHandle(MESHHANDLE hMesh, D3DXVECTOR3 *reorig, float *scale) +void D3D9Mesh::BakeLights(ImageProcessing* pBaker, const FVECTOR3* BakedLightsControl) { + if (!pBaker->IsOK()) return; // Baker not initialized + if (DefShader != SHADER_BAKED_VC) return; // Not supported by shader + if (BakedLights.size() == 0) return; // Nothing to bake + + DWORD flags = IPF_POINT | IPF_CLAMP; + FVECTOR3 control[16]; + + pBaker->Activate("PSMain"); + + for (auto x : BakedLights) + { + int slot = 0; + for (int i = 0; i < 16; i++) + { + auto y = x.second.pMap[i]; + if (y) + { + pBaker->SetTextureNative(slot, y, flags); + control[slot] = BakedLightsControl[i]; + slot++; + } + } + + pBaker->SetFloat("fControl", control, slot * sizeof(FVECTOR3)); + pBaker->SetInt("iCount", slot); + + LPDIRECT3DSURFACE9 pSrf = NULL; + + if (x.second.pCombined) + { + if (x.second.pCombined->GetSurfaceLevel(0, &pSrf) == S_OK) + { + pBaker->SetOutputNative(0, pSrf); + pBaker->Execute(true); + SAFE_RELEASE(pSrf); + } + } + } +} + + +// =========================================================================================== +// +void D3D9Mesh::BakeAO(ImageProcessing* pBaker, const FVECTOR3 &vSun, const LVLH &lvlh, const LPDIRECT3DTEXTURE9 pIrrad) +{ + if (!pBaker->IsOK()) return; // Baker not initialized + if (DefShader != SHADER_BAKED_VC) return; // Not supported by shader + if (BakedLights.size() == 0) return; // Nothing to bake + + DWORD flags = IPF_POINT | IPF_CLAMP; + FVECTOR3 control[6]; + FVECTOR3 ParTexCoord[6]; + bool bSE[6]; + float fShine = 1.0f; + bool bShine = true; + + // Compute texcoords for Irradiance lookup for prime directions + // + for (int i = 0; i < 6; i++) { + FVECTOR3 DirL = GetDir(i); + float z = dotp(lvlh.Up, DirL); // lvlh is in a local vessel frame + FVECTOR2 p = FVECTOR2(dotp(lvlh.East, DirL), dotp(lvlh.North, DirL)) / (1.0f + abs(z)); + p *= FVECTOR2(0.2273f, 0.4545f); + ParTexCoord[i] = FVECTOR3(p.x, p.y, z); + } + + pBaker->Activate("PSSunAO"); + + // Compute sunligh intensity factors for prime directions + // + for (int i = 0; i < 6; i++) + { + bSE[i] = false; + control[i] = 0.0f; + + if (DebugControls::IsActive()) + if ((DebugControls::ambdir != i) && (DebugControls::ambdir != -1)) continue; + + auto tex = bli->second.pSunAO[i]; + bSE[i] = (tex != NULL); + if (tex) + { + pBaker->SetTextureNative(i, tex, flags); + control[i] = saturate(dotp(GetDir(i), vSun)); + control[i] *= control[i]; + } + } + + if (DebugControls::IsActive()) { + if (DebugControls::debugFlags & DBG_FLAGS_NOSUNAMB) + for (int i = 0; i < 6; i++) control[i] = 0; + if (DebugControls::debugFlags & DBG_FLAGS_NOPLNAMB) + fShine = 0.0f, bShine = false; + } + + if (pIrrad == nullptr) fShine = 0.0f, bShine = false; + + pBaker->SetTextureNative("tIrrad", pIrrad, IPF_LINEAR | IPF_CLAMP); + pBaker->SetFloat("fControl", control, sizeof(control)); + pBaker->SetFloat("vParTexPos", ParTexCoord, sizeof(ParTexCoord)); + pBaker->SetFloat("fShine", &fShine, sizeof(fShine)); + pBaker->SetBool("bEnabled", bSE, sizeof(bSE)); + pBaker->SetBool("bShine", &bShine, sizeof(bShine)); + + LPDIRECT3DSURFACE9 pSrf = NULL; + + if (bli->second.pSunAOComb) + { + if (bli->second.pSunAOComb->GetSurfaceLevel(0, &pSrf) == S_OK) + { + pBaker->SetOutputNative(0, pSrf); + pBaker->Execute(true); + SAFE_RELEASE(pSrf); + } + } + + bli++; + if (bli == BakedLights.end()) bli = BakedLights.begin(); +} + + +// =========================================================================================== +// +LPDIRECT3DTEXTURE9 D3D9Mesh::GetCombinedMap(int tex_idx) +{ + if (tex_idx < 0 ) for (auto a : BakedLights) if (a.second.pCombined) return a.second.pCombined; + auto it = BakedLights.find(tex_idx); + if (it != BakedLights.end()) return it->second.pCombined; + return NULL; +} + + +// =========================================================================================== +// +void D3D9Mesh::LoadMeshFromHandle(MESHHANDLE hMesh, FVECTOR3 *reorig, float *scale) +{ + hOapiMesh = hMesh; const char* meshn = oapiGetMeshFilename(hMesh); strcpy_s(name, 128, meshn ? meshn : "???"); @@ -494,10 +764,11 @@ void D3D9Mesh::LoadMeshFromHandle(MESHHANDLE hMesh, D3DXVECTOR3 *reorig, float * for (DWORD i = 0; i=nGrp) return D3DXVECTOR3(0,0,0); - if (Grp[idx].nVert<2) return D3DXVECTOR3(0,0,0); - return D3DXVECTOR3f4(Grp[idx].BBox.max - Grp[idx].BBox.min); + if (!IsOK()) return FVECTOR3(0,0,0); + if (idx>=nGrp) return FVECTOR3(0,0,0); + if (Grp[idx].nVert<2) return FVECTOR3(0,0,0); + return _F(Grp[idx].BBox.mx - Grp[idx].BBox.mn); } @@ -608,14 +879,14 @@ void D3D9Mesh::ResetTransformations() { _TRACE; if (!IsOK()) return; - D3DXMatrixIdentity(&mTransform); - D3DXMatrixIdentity(&mTransformInv); + oapiMatrixIdentity(&mTransform); + oapiMatrixIdentity(&mTransformInv); bGlobalTF = false; bBSRecompute = true; bBSRecomputeAll = true; for (DWORD i=0;iFlags; Grp[i].zBias = mg->zBias; - D3DXMatrixIdentity(&Grp[i].Transform); + oapiMatrixIdentity(&Grp[i].Transform); MaxFace += Grp[i].nFace; MaxVert += Grp[i].nVert; @@ -711,13 +982,13 @@ void D3D9Mesh::SetGroupRec(DWORD i, const MESHGROUPEX *mg) // =========================================================================================== // -bool D3D9Mesh::CopyVertices(GROUPREC *grp, const MESHGROUPEX *mg, D3DXVECTOR3 *reorig, float *scale) +bool D3D9Mesh::CopyVertices(GROUPREC *grp, const MESHGROUPEX *mg, FVECTOR3 *reorig, float *scale) { if (!pBuf) return false; NTVERTEX *pNT = mg->Vtx; SMVERTEX *pShad = pBuf->pSBSys + grp->VertOff; NMVERTEX *pVert = pBuf->pVBSys + grp->VertOff; - D3DXVECTOR4 *pGeo = pBuf->pGBSys + grp->VertOff; + XMVECTOR *pGeo = pBuf->pGBSys + grp->VertOff; WORD *pIndex = pBuf->pIBSys + grp->IdexOff; for (DWORD i=0;inIdx;i++) pIndex[i] = mg->Idx[i]; @@ -753,7 +1024,7 @@ bool D3D9Mesh::CopyVertices(GROUPREC *grp, const MESHGROUPEX *mg, D3DXVECTOR3 *r pVert[i].z += reorig->z; } - pGeo[i] = D3DXVECTOR4(pVert[i].x, pVert[i].y, pVert[i].z, 0); + pGeo[i] = FVECTOR4(pVert[i].x, pVert[i].y, pVert[i].z, 0.0f).XM(); pShad[i].x = pVert[i].x; pShad[i].y = pVert[i].y; pShad[i].z = pVert[i].z; pShad[i].tu = pVert[i].u; pShad[i].tv = pVert[i].v; @@ -811,7 +1082,7 @@ int D3D9Mesh::EditGroup(DWORD grp, GROUPEDITSPEC *ges) pBuf->MustRemap(MAPMODE_CURRENT); SMVERTEX *pShad = pBuf->pSBSys + g->VertOff; - D3DXVECTOR4 *pGeo = pBuf->pGBSys + g->VertOff; + XMVECTOR *pGeo = pBuf->pGBSys + g->VertOff; NMVERTEX *vtx = pBuf->pVBSys + g->VertOff; WORD *idx = pBuf->pIBSys + g->IdexOff; @@ -839,7 +1110,7 @@ int D3D9Mesh::EditGroup(DWORD grp, GROUPEDITSPEC *ges) else if (flag & GRPEDIT_VTXTEXADDV) vtx[vi].v += ges->Vtx[i].tv; if ((flag & GRPEDIT_VTXCRD)!=0 || (flag & GRPEDIT_VTXCRDADD)!=0) { - pGeo[vi] = D3DXVECTOR4(vtx[vi].x, vtx[vi].y, vtx[vi].z, 0); + pGeo[vi] = FVECTOR4(vtx[vi].x, vtx[vi].y, vtx[vi].z, 0.0f).XM(); pShad[vi].x = vtx[vi].x; pShad[vi].y = vtx[vi].y; pShad[vi].z = vtx[vi].z; pShad[vi].tu = vtx[vi].u; pShad[vi].tv = vtx[vi].v; @@ -1039,6 +1310,15 @@ const D3D9Mesh::GROUPREC *D3D9Mesh::GetGroup(DWORD idx) const return NULL; } +// =========================================================================================== +// +D3D9Mesh::GROUPREC* D3D9Mesh::GetGroup(DWORD idx) +{ + if (!IsOK()) return NULL; + if (idx < nGrp) return &Grp[idx]; + return NULL; +} + // =========================================================================================== // const D3D9MatExt * D3D9Mesh::GetMaterial(DWORD idx) const @@ -1101,42 +1381,42 @@ int D3D9Mesh::SetMaterialEx(DWORD idx, MatProp mid, const FVECTOR4* value) if (value) { switch (mid) { case MatProp::Diffuse: - Mtrl[idx].Diffuse = *((D3DXVECTOR4*)value); + Mtrl[idx].Diffuse = *((FVECTOR4*)value); Mtrl[idx].ModFlags |= D3D9MATEX_DIFFUSE; bMtrlModidied = true; return 0; case MatProp::Ambient: - Mtrl[idx].Ambient = *((D3DXVECTOR3*)value); + Mtrl[idx].Ambient = *((FVECTOR3*)value); Mtrl[idx].ModFlags |= D3D9MATEX_AMBIENT; bMtrlModidied = true; return 0; case MatProp::Specular: - Mtrl[idx].Specular = *((D3DXVECTOR4*)value); + Mtrl[idx].Specular = *((FVECTOR4*)value); Mtrl[idx].ModFlags |= D3D9MATEX_SPECULAR; bMtrlModidied = true; return 0; case MatProp::Light: - Mtrl[idx].Emissive = *((D3DXVECTOR3*)value); + Mtrl[idx].Emissive = *((FVECTOR3*)value); Mtrl[idx].ModFlags |= D3D9MATEX_EMISSIVE; bMtrlModidied = true; return 0; case MatProp::Emission: - Mtrl[idx].Emission2 = *((D3DXVECTOR3*)value); + Mtrl[idx].Emission2 = *((FVECTOR3*)value); Mtrl[idx].ModFlags |= D3D9MATEX_EMISSION2; bMtrlModidied = true; return 0; case MatProp::Reflect: - Mtrl[idx].Reflect = *((D3DXVECTOR3*)value); + Mtrl[idx].Reflect = *((FVECTOR3*)value); Mtrl[idx].ModFlags |= D3D9MATEX_REFLECT; bMtrlModidied = true; return 0; case MatProp::Smooth: - Mtrl[idx].Roughness = D3DXVECTOR2(value->g, value->r); + Mtrl[idx].Roughness = FVECTOR2(value->g, value->r); Mtrl[idx].ModFlags |= D3D9MATEX_ROUGHNESS; bMtrlModidied = true; return 0; case MatProp::Fresnel: - Mtrl[idx].Fresnel = *((D3DXVECTOR3*)value); + Mtrl[idx].Fresnel = *((FVECTOR3*)value); Mtrl[idx].ModFlags |= D3D9MATEX_FRESNEL; bMtrlModidied = true; return 0; @@ -1146,7 +1426,7 @@ int D3D9Mesh::SetMaterialEx(DWORD idx, MatProp mid, const FVECTOR4* value) bMtrlModidied = true; return 0; case MatProp::SpecialFX: - Mtrl[idx].SpecialFX = *((D3DXVECTOR4*)value); + Mtrl[idx].SpecialFX = *((FVECTOR4*)value); Mtrl[idx].ModFlags |= D3D9MATEX_SPECIALFX; bMtrlModidied = true; return 0; @@ -1160,42 +1440,42 @@ int D3D9Mesh::SetMaterialEx(DWORD idx, MatProp mid, const FVECTOR4* value) { switch (mid) { case MatProp::Diffuse: - Mtrl[idx].Diffuse = D3DXVECTOR4(1, 1, 1, 1); + Mtrl[idx].Diffuse = FVECTOR4(1, 1, 1, 1); Mtrl[idx].ModFlags &= (~D3D9MATEX_DIFFUSE); bMtrlModidied = true; return 0; case MatProp::Ambient: - Mtrl[idx].Ambient = D3DXVECTOR3(1, 1, 1); + Mtrl[idx].Ambient = FVECTOR3(1, 1, 1); Mtrl[idx].ModFlags &= (~D3D9MATEX_AMBIENT); bMtrlModidied = true; return 0; case MatProp::Specular: - Mtrl[idx].Specular = D3DXVECTOR4(1, 1, 1, 20.0); + Mtrl[idx].Specular = FVECTOR4(1, 1, 1, 20); Mtrl[idx].ModFlags &= (~D3D9MATEX_SPECULAR); bMtrlModidied = true; return 0; case MatProp::Light: - Mtrl[idx].Emissive = D3DXVECTOR3(0, 0, 0); + Mtrl[idx].Emissive = FVECTOR3(0, 0, 0); Mtrl[idx].ModFlags &= (~D3D9MATEX_EMISSIVE); bMtrlModidied = true; return 0; case MatProp::Emission: - Mtrl[idx].Emission2 = D3DXVECTOR3(1, 1, 1); + Mtrl[idx].Emission2 = FVECTOR3(1, 1, 1); Mtrl[idx].ModFlags &= (~D3D9MATEX_EMISSION2); bMtrlModidied = true; return 0; case MatProp::Reflect: - Mtrl[idx].Reflect = D3DXVECTOR3(0, 0, 0); + Mtrl[idx].Reflect = FVECTOR3(0.0f, 0, 0); Mtrl[idx].ModFlags &= (~D3D9MATEX_REFLECT); bMtrlModidied = true; return 0; case MatProp::Smooth: - Mtrl[idx].Roughness = D3DXVECTOR2(0.0f, 1.0f); + Mtrl[idx].Roughness = FVECTOR2(0.0f, 1.0f); Mtrl[idx].ModFlags &= (~D3D9MATEX_ROUGHNESS); bMtrlModidied = true; return 0; case MatProp::Fresnel: - Mtrl[idx].Fresnel = D3DXVECTOR3(1, 0, 1024.0f); + Mtrl[idx].Fresnel = FVECTOR3(1, 0, 1024.0f); Mtrl[idx].ModFlags &= (~D3D9MATEX_FRESNEL); bMtrlModidied = true; return 0; @@ -1205,7 +1485,7 @@ int D3D9Mesh::SetMaterialEx(DWORD idx, MatProp mid, const FVECTOR4* value) bMtrlModidied = true; return 0; case MatProp::SpecialFX: - Mtrl[idx].SpecialFX = D3DXVECTOR4(0, 0, 0, 0); + Mtrl[idx].SpecialFX = FVECTOR4(0.0f, 0, 0, 0); Mtrl[idx].ModFlags &= (~D3D9MATEX_SPECIALFX); bMtrlModidied = true; return 0; @@ -1230,24 +1510,24 @@ int D3D9Mesh::GetMaterialEx(DWORD idx, MatProp mid, FVECTOR4* value) switch (mid) { case MatProp::Diffuse: - *((D3DXVECTOR4*)value) = Mtrl[idx].Diffuse; + *((FVECTOR4*)value) = Mtrl[idx].Diffuse; return 0; case MatProp::Ambient: - *((D3DXVECTOR3*)value) = Mtrl[idx].Ambient; + *((FVECTOR3*)value) = Mtrl[idx].Ambient; return 0; case MatProp::Specular: - *((D3DXVECTOR4*)value) = Mtrl[idx].Specular; + *((FVECTOR4*)value) = Mtrl[idx].Specular; return 0; case MatProp::Light: - *((D3DXVECTOR3*)value) = Mtrl[idx].Emissive; + *((FVECTOR3*)value) = Mtrl[idx].Emissive; return 0; case MatProp::Emission: if ((Mtrl[idx].ModFlags&D3D9MATEX_EMISSION2) == 0) return -2; - *((D3DXVECTOR3*)value) = Mtrl[idx].Emission2; + *((FVECTOR3*)value) = Mtrl[idx].Emission2; return 0; case MatProp::Reflect: if ((Mtrl[idx].ModFlags&D3D9MATEX_REFLECT) == 0) return -2; - *((D3DXVECTOR3*)value) = Mtrl[idx].Reflect; + *((FVECTOR3*)value) = Mtrl[idx].Reflect; return 0; case MatProp::Smooth: if ((Mtrl[idx].ModFlags&D3D9MATEX_ROUGHNESS) == 0) return -2; @@ -1256,7 +1536,7 @@ int D3D9Mesh::GetMaterialEx(DWORD idx, MatProp mid, FVECTOR4* value) return 0; case MatProp::Fresnel: if ((Mtrl[idx].ModFlags&D3D9MATEX_FRESNEL) == 0) return -2; - *((D3DXVECTOR3*)value) = Mtrl[idx].Fresnel; + *((FVECTOR3*)value) = Mtrl[idx].Fresnel; return 0; case MatProp::Metal: if ((Mtrl[idx].ModFlags& D3D9MATEX_METALNESS) == 0) return -2; @@ -1264,7 +1544,7 @@ int D3D9Mesh::GetMaterialEx(DWORD idx, MatProp mid, FVECTOR4* value) return 0; case MatProp::SpecialFX: if ((Mtrl[idx].ModFlags& D3D9MATEX_SPECIALFX) == 0) return -2; - *((D3DXVECTOR4*)value) = Mtrl[idx].SpecialFX; + *((FVECTOR4*)value) = Mtrl[idx].SpecialFX; return 0; } return 5; @@ -1274,46 +1554,29 @@ int D3D9Mesh::GetMaterialEx(DWORD idx, MatProp mid, FVECTOR4* value) // =========================================================================================== // -bool D3D9Mesh::GetTexTune(D3D9Tune *pT, DWORD idx) const +void D3D9Mesh::SetAmbientColor(const FVECTOR3& c) { - if (idxSetVector(eAttennuate, ptr(D3DXVECTOR4(1,1,1,1))); - FX->SetVector(eInScatter, ptr(D3DXVECTOR4(0,0,0,0))); + FX->SetVector(eAttennuate, _DX(FVECTOR4(1.0f, 1, 1, 1))); + FX->SetVector(eInScatter, _DX(FVECTOR4(0.0f, 0, 0, 0))); } // =========================================================================================== @@ -1416,6 +1679,25 @@ void D3D9Mesh::CheckMeshStatus() bIsReflective = true; for (DWORD g = 0; g < nGrp; g++) Grp[g].Shader = SHADER_METALNESS; } + + if (DefShader == SHADER_BAKED_VC) { + bIsReflective = true; + for (DWORD g = 0; g < nGrp; g++) Grp[g].Shader = SHADER_BAKED_VC; + } +} + + +// =========================================================================================== +// +void D3D9Mesh::SetDefaultShader(WORD shader) +{ + DefShader = shader; + bMtrlModidied = true; + CheckMeshStatus(); + + if (shader == SHADER_BAKED_VC) { + LoadBakedLights(); + } } @@ -1426,16 +1708,56 @@ void D3D9Mesh::ConfigureAtmo() //LogSunLight(sunLight); float x = 1.0f - saturate(max(sunLight.Color.r, sunLight.Color.b) * 2.0f); FX->SetFloat(eNight, x); + if (DebugControls::IsActive() && DebugControls::debugFlags & DBG_FLAGS_NODYNSUN) + sunLight.Color = sunLight.Ambient = 0; FX->SetValue(eSun, &sunLight, sizeof(D3D9Sun)); } +// =========================================================================================== +// Setup shadow map samplers and textures +// +void D3D9Mesh::ConfigureShadows() +{ + for (int i = 0; i < SHM_CASCADE_COUNT; i++) + { + int idx = 13 + i; // Sampler index + HR(pDev->SetSamplerState(idx, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP)); + HR(pDev->SetSamplerState(idx, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP)); + HR(pDev->SetSamplerState(idx, D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP)); + HR(pDev->SetSamplerState(idx, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR)); + HR(pDev->SetSamplerState(idx, D3DSAMP_MINFILTER, D3DTEXF_LINEAR)); + HR(pDev->SetSamplerState(idx, D3DSAMP_MIPFILTER, D3DTEXF_NONE)); + HR(pDev->SetTexture(idx, pShadowMap[i])); + } + HR(FX->SetValue(D3D9Effect::eSHDSubRect, ShdSubRect, sizeof(ShdSubRect))); +} + + +// =========================================================================================== +// static: +void D3D9Mesh::SetShadows(const SHADOWMAP *sprm) +{ + if (sprm) { + for (int i = 0; i < SHM_CASCADE_COUNT; i++) { + pShadowMap[i] = sprm->Map(i); + ShdSubRect[i] = sprm->SubrectTF[i]; + } + } + else { + for (int i = 0; i < SHM_CASCADE_COUNT; i++) { + pShadowMap[i] = NULL; + ShdSubRect[i] = _F4(0, 0, 0, 0); + } + } +} + + // ================================================================================================ // This is a rendering routine for a Exterior Mesh, non-spherical moons/asteroids // -void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 *pEnv, int nEnv) +void D3D9Mesh::Render(const FMATRIX4* pW, const ENVCAMREC* em, int iTech) { - _TRACE; if (!IsOK()) return; @@ -1466,9 +1788,9 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * selmsh = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDMESH); selgrp = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDGROUP); displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); - bActiveVisual = (pCurrentVisual==DebugControls::GetVisual()); + bActiveVisual = (g_pCurrentVisual == DebugControls::GetVisual()); if (displ>0 && !bActiveVisual) return; - if ((displ==2 || displ==3) && uCurrentMesh!=selmsh) return; + if ((displ==2 || displ==3) && g_uCurrentMesh!=selmsh) return; } Scene *scn = gc->GetScene(); @@ -1479,12 +1801,14 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * bool bGroupCull = true; bool bUpdateFlow = true; bool bShadowProjection = false; + bool bVirtualCockpit = false; switch (iTech) { case RENDER_VC: EnablePlanetGlow(false); + bVirtualCockpit = true; break; case RENDER_BASE: EnablePlanetGlow(false); @@ -1508,11 +1832,12 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * } HR(D3D9Effect::FX->SetBool(D3D9Effect::eBaseBuilding, bShadowProjection)); + HR(D3D9Effect::FX->SetBool(D3D9Effect::eCockpit, bVirtualCockpit)); - D3DXVECTOR4 Field; - D3DXMATRIX mWorldView, q; + FVECTOR4 Field; + FMATRIX4 mWorldView, q; - D3DXMatrixMultiply(&mWorldView, pW, scn->GetViewMatrix()); + oapiMatrixMultiply(&mWorldView, pW, scn->GetViewMatrix()); if (bMeshCull || bGroupCull) Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); @@ -1521,9 +1846,9 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * return; } - D3DXMATRIX mWorldMesh; + FMATRIX4 mWorldMesh; - if (bGlobalTF) D3DXMatrixMultiply(&mWorldMesh, &mTransform, pW); + if (bGlobalTF) oapiMatrixMultiply(&mWorldMesh, &mTransform, pW); else mWorldMesh = *pW; D3D9Stats.Mesh.Meshes++; @@ -1535,23 +1860,25 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * pDev->SetStreamSource(0, pBuf->pVB, 0, sizeof(NMVERTEX)); pDev->SetIndices(pBuf->pIB); + ConfigureShadows(); + if (flags&DBG_FLAGS_DUALSIDED) pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); FX->SetTechnique(eVesselTech); FX->SetBool(eFresnel, false); - FX->SetBool(eEnvMapEnable, false); - FX->SetBool(eTuneEnabled, false); FX->SetBool(eLightsEnabled, false); FX->SetBool(eOITEnable, false); - FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0, 0, 0))); + FX->SetValue(eVCAmbient, &cAmbient, sizeof(FVECTOR3)); ConfigureAtmo(); - if (DebugControls::IsActive()) if (pTune) FX->SetBool(eTuneEnabled, true); - TexFlow FC; reset(FC); + + // Setup Local lights ------------------------------------------- + // const D3D9Light *pLights = gc->GetScene()->GetLights(); int nSceneLights = gc->GetScene()->GetLightCount(); @@ -1561,8 +1888,7 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * if (pLights && nSceneLights>0) { - D3DXVECTOR3 pos; - D3DXVec3TransformCoord(&pos, ptr(D3DXVECTOR3f4(BBox.bs)), pW); + FVECTOR3 pos = oapiTransformCoord(&_F(BBox.bs), pW); // Find all local lights effecting this mesh ------------------------------------------ // @@ -1597,7 +1923,23 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * FX->SetValue(eLights, Locals, sizeof(LightStruct) * Config->MaxLights()); - if (nEnv >= 1 && pEnv[0]) FX->SetTexture(eEnvMapA, pEnv[0]); + // Setup Env Maps ------------------------------------------- + // + if (em && em->pCube && em->pIrrad) { + FX->SetBool(eEnvMapEnable, true); + FX->SetTexture(eEnvMapA, em->pCube); + FX->SetTexture(eIrradMap, em->pIrrad); + } + else { + FX->SetBool(eEnvMapEnable, false); + } + + bool bNoAmbient = (Config->ExpVCLight == 1); + + /*if (DebugControls::IsActive()) { + bNoAmbient = (flags & DBG_FLAGS_NOSUNAMB) != 0 & (flags & DBG_FLAGS_NOPLNAMB) != 0; + }*/ + UINT numPasses = 0; @@ -1606,6 +1948,7 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * WORD CurrentShader = SHADER_NULL; bool bRefl = true; + int iEnvCam = -1; for (DWORD g=0; gSetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); + FX->SetVector(eColor, _DX(_F4(0, 0, 0, 0))); if (flags&DBG_FLAGS_HLMESH) { - if (uCurrentMesh==selmsh) { - FX->SetVector(eColor, ptr(D3DXVECTOR4(0.0f, 0.0f, 0.5f, 0.5f))); + if (g_uCurrentMesh==selmsh) { + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0.0f, 0.5f, 0.5f))); } } - if (flags&DBG_FLAGS_HLGROUP) { - if (g==selgrp && uCurrentMesh==selmsh) { - FX->SetVector(eColor, ptr(D3DXVECTOR4(0.0f, 0.5f, 0.0f, 0.5f))); + if (flags&DBG_FLAGS_HLGROUP) { + if (g == selgrp && g_uCurrentMesh == selmsh) { + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0.5f, 0.0f, 0.5f))); } } } @@ -1673,17 +2016,15 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * // --------------------------------------------------------------------------------------------------------- // if (Tex[ti] != old_tex) { - if (Tex[ti] == NULL) { - reset(FC); - bUpdateFlow = true; - } + reset(FC); + bUpdateFlow = true; } if (Tex[ti]==NULL) bTextured = false, old_tex = NULL; else bTextured = true; - + // Setup Textures and Normal Maps ========================================================================== // if (bTextured) { @@ -1696,18 +2037,11 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * FX->SetTexture(eTex0, Tex[ti]->GetTexture()); - bUpdateFlow = true; // Fix this later - if (CurrentShader == SHADER_LEGACY) { if (tni && Grp[g].TexMixEx[0] < 0.5f) tni = 0; if (tni && Tex[tni]) FX->SetTexture(eEmisMap, Tex[tni]->GetTexture()); } else { - - if (DebugControls::IsActive()) if (pTune) { - FX->SetValue(eTune, &pTune[ti], sizeof(D3D9Tune)); - } - LPDIRECT3DTEXTURE9 pTransl = NULL; LPDIRECT3DTEXTURE9 pTransm = NULL; LPDIRECT3DTEXTURE9 pMetl = NULL; @@ -1729,7 +2063,9 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * if (pSpec) FX->SetTexture(eSpecMap, pSpec); if (pRefl) FX->SetTexture(eReflMap, pRefl); - if (CurrentShader == SHADER_ADV || CurrentShader == SHADER_METALNESS) + if (CurrentShader == SHADER_ADV + || CurrentShader == SHADER_METALNESS + || CurrentShader == SHADER_BAKED_VC) { pTransl = Tex[ti]->GetMap(MAP_TRANSLUCENCE); pTransm = Tex[ti]->GetMap(MAP_TRANSMITTANCE); @@ -1749,6 +2085,23 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * FC.Metl = false; } + if (CurrentShader == SHADER_BAKED_VC) + { + auto bm = BakedLights.find(ti); + + LPDIRECT3DTEXTURE9 pAmbi = Tex[ti]->GetMap(MAP_AMBIENT); + LPDIRECT3DTEXTURE9 pComb = (bm == BakedLights.end() ? NULL : bm->second.pCombined); + LPDIRECT3DTEXTURE9 pSun = (bm == BakedLights.end() ? NULL : bm->second.pSunAOComb); + + if (pAmbi) FX->SetTexture(eAmbientMap, pAmbi); + if (pComb) FX->SetTexture(eCombinedMap, pComb); + if (pSun) FX->SetTexture(eCombSunMap, pSun); + + FC.Baked = (pComb != NULL); + FC.BakedAO = (pAmbi != NULL); + FC.BakedAmb = (pSun != NULL) & !bNoAmbient; + } + FC.Emis = (pEmis != NULL); FC.Norm = (pNorm != NULL); FC.Rghn = (pRghn != NULL); @@ -1811,10 +2164,10 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * // if (Grp[g].bTransform) { bWorldMesh = false; - FX->SetMatrix(eW, D3DXMatrixMultiply(&q, &pGrpTF[g], pW)); + FX->SetMatrix(eW, _DX(oapiMatrixMultiply(&q, &pGrpTF[g], pW))); } else if (!bWorldMesh) { - FX->SetMatrix(eW, &mWorldMesh); + FX->SetMatrix(eW, _DX(mWorldMesh)); bWorldMesh = true; } @@ -1836,16 +2189,18 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * FX->SetBool(eOITEnable, bOIT); FX->SetBool(eTextured, bTextured); FX->SetBool(eFullyLit, bNoL); - FX->SetBool(eNoColor, bNoC); FX->SetBool(eSwitch, bPBR); FX->SetBool(eRghnSw, bRGH); + if (bNoC) FX->SetValue(eNoColor, &FVECTOR3(1, 1, 1), sizeof(FVECTOR3)); + else FX->SetValue(eNoColor, &FVECTOR3(0, 0, 0), sizeof(FVECTOR3)); + // Update envmap and fresnel status as required if (bRefl) { bFRS = (Grp[g].PBRStatus & 0x1E) >= 0x10; FX->SetBool(eFresnel, bFRS); if (IsReflective()) { - bENV = ((Grp[g].PBRStatus & 0x1E) >= 0xA) | (Grp[g].Shader == SHADER_METALNESS); + bENV = ((Grp[g].PBRStatus & 0x1E) >= 0xA) | (Grp[g].Shader == SHADER_METALNESS) | (Grp[g].Shader == SHADER_BAKED_VC); FX->SetBool(eEnvMapEnable, bENV); } } @@ -1860,7 +2215,7 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * // if (DebugControls::IsActive()) { - if ((bActiveVisual) && (g == selgrp) && (uCurrentMesh == selmsh)) { + if ((bActiveVisual) && (g == selgrp) && (g_uCurrentMesh == selmsh)) { bool bAdd = (Grp[g].UsrFlag & 0x08) != 0; bool bNoS = (Grp[g].UsrFlag & 0x01) != 0; @@ -1868,9 +2223,9 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * static const char *YesNo[2] = { "No", "Yes" }; static const char *LPW[2] = { "Legacy", "PBR" }; static const char *RGH[2] = { "Disabled", "Enabled" }; - static const char *Shaders[7] = { "PBR", "PBR-ADV", "FAST", "XR2", "METALNESS", "SPECULAR", "???"}; + static const char *Shaders[7] = { "PBR", "PBR-ADV", "FAST", "XR2", "METALNESS", "BAKED_VC", "???"}; - DebugControls::Append("MeshIdx = %d, GrpIdx = %d\n", uCurrentMesh, g); + DebugControls::Append("MeshIdx = %d, GrpIdx = %d\n", g_uCurrentMesh, g); DebugControls::Append("MtrlIdx = %d, TexIdx = %d\n", Grp[g].MtrlIdx, Grp[g].TexIdx); DebugControls::Append("FaceCnt = %d, VtxCnt = %d\n", Grp[g].nFace, Grp[g].nVert); @@ -1906,6 +2261,12 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * DebugControls::Append("]\n"); } + if (CurrentShader == SHADER_BAKED_VC) { + DebugControls::Append("BakedLights = [ "); + for (auto& a : BakedLights) DebugControls::Append(" %d", a.first); + DebugControls::Append("]\n"); + } + DebugControls::Append("Local Lights = %d\n", nMeshLights); DebugControls::Refresh(); @@ -1963,7 +2324,7 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * HR(FX->End()); if (flags&(DBG_FLAGS_BOXES|DBG_FLAGS_SPHERES)) RenderBoundingBox(pW); - FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); + FX->SetVector(eColor, _DX(_F4(0, 0, 0, 0))); if (flags&DBG_FLAGS_DUALSIDED) pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); } @@ -1972,7 +2333,7 @@ void D3D9Mesh::Render(const LPD3DXMATRIX pW, int iTech, LPDIRECT3DCUBETEXTURE9 * // ================================================================================================ // Render without animations // -void D3D9Mesh::RenderSimplified(const LPD3DXMATRIX pW, LPDIRECT3DCUBETEXTURE9 *pEnv, int nEnv, bool bSP) +void D3D9Mesh::RenderSimplified(const FMATRIX4* pW, LPDIRECT3DCUBETEXTURE9 *pEnv, int nEnv, bool bSP) { if (!IsOK()) return; @@ -2007,10 +2368,9 @@ void D3D9Mesh::RenderSimplified(const LPD3DXMATRIX pW, LPDIRECT3DCUBETEXTURE9 *p FX->SetTechnique(eVesselTech); FX->SetBool(eFresnel, false); FX->SetBool(eEnvMapEnable, false); - FX->SetBool(eTuneEnabled, false); FX->SetBool(eLightsEnabled, false); - FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); - FX->SetMatrix(eW, pW); + FX->SetVector(eColor, _DX(_F4(0, 0, 0, 0))); + FX->SetMatrix(eW, _DX(pW)); ConfigureAtmo(); @@ -2026,8 +2386,7 @@ void D3D9Mesh::RenderSimplified(const LPD3DXMATRIX pW, LPDIRECT3DCUBETEXTURE9 *p if (pLights && nSceneLights>0) { int nMeshLights = 0; - D3DXVECTOR3 pos; - D3DXVec3TransformCoord(&pos, ptr(D3DXVECTOR3f4(BBox.bs)), pW); + FVECTOR3 pos = oapiTransformCoord(&_F(BBox.bs), pW); // Find all local lights effecting this mesh ------------------------------------------ // @@ -2181,10 +2540,12 @@ void D3D9Mesh::RenderSimplified(const LPD3DXMATRIX pW, LPDIRECT3DCUBETEXTURE9 *p FX->SetBool(eOITEnable, bOIT); FX->SetBool(eTextured, bTextured); FX->SetBool(eFullyLit, bNoL); - FX->SetBool(eNoColor, bNoC); FX->SetBool(eSwitch, bPBR); FX->SetBool(eRghnSw, bRGH); + if (bNoC) FX->SetValue(eNoColor, &FVECTOR3(1, 1, 1), sizeof(FVECTOR3)); + else FX->SetValue(eNoColor, &FVECTOR3(0, 0, 0), sizeof(FVECTOR3)); + // Update envmap and fresnel status as required // @@ -2224,7 +2585,7 @@ void D3D9Mesh::RenderSimplified(const LPD3DXMATRIX pW, LPDIRECT3DCUBETEXTURE9 *p // ================================================================================================ // Render a legacy orbiter mesh without any additional textures // -void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) +void D3D9Mesh::RenderFast(const FMATRIX4* pW, int iTech) { _TRACE; @@ -2242,9 +2603,9 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) selmsh = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDMESH); selgrp = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDGROUP); displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); - bActiveVisual = (pCurrentVisual == DebugControls::GetVisual()); + bActiveVisual = (g_pCurrentVisual == DebugControls::GetVisual()); if (displ>0 && !bActiveVisual) return; - if ((displ == 2 || displ == 3) && uCurrentMesh != selmsh) return; + if ((displ == 2 || displ == 3) && g_uCurrentMesh != selmsh) return; } Scene *scn = gc->GetScene(); @@ -2255,9 +2616,11 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) bool bGroupCull = true; bool bUpdateFlow = true; bool bShadowProjection = false; + bool bVirtualCockpit = false; switch (iTech) { case RENDER_VC: + bVirtualCockpit = true; EnablePlanetGlow(false); break; case RENDER_BASE: @@ -2282,11 +2645,12 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) } HR(D3D9Effect::FX->SetBool(D3D9Effect::eBaseBuilding, bShadowProjection)); + HR(D3D9Effect::FX->SetBool(D3D9Effect::eCockpit, bVirtualCockpit)); - D3DXVECTOR4 Field; - D3DXMATRIX mWorldView, q; + FVECTOR4 Field; + FMATRIX4 mWorldView, q; - D3DXMatrixMultiply(&mWorldView, pW, scn->GetViewMatrix()); + oapiMatrixMultiply(&mWorldView, pW, scn->GetViewMatrix()); if (bMeshCull || bGroupCull) Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); @@ -2295,9 +2659,9 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) return; } - D3DXMATRIX mWorldMesh; + FMATRIX4 mWorldMesh; - if (bGlobalTF) D3DXMatrixMultiply(&mWorldMesh, &mTransform, pW); + if (bGlobalTF) oapiMatrixMultiply(&mWorldMesh, &mTransform, pW); else mWorldMesh = *pW; D3D9Stats.Mesh.Meshes++; @@ -2314,14 +2678,10 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) FX->SetTechnique(eVesselTech); - FX->SetBool(eTuneEnabled, false); FX->SetBool(eLightsEnabled, false); - FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); + FX->SetVector(eColor, _DX(_F4(0, 0, 0, 0))); ConfigureAtmo(); - - if (DebugControls::IsActive()) if (pTune) FX->SetBool(eTuneEnabled, true); - TexFlow FC; reset(FC); const D3D9Light *pLights = gc->GetScene()->GetLights(); @@ -2334,8 +2694,7 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) if (pLights && nSceneLights>0) { int nMeshLights = 0; - D3DXVECTOR3 pos; - D3DXVec3TransformCoord(&pos, ptr(D3DXVECTOR3f4(BBox.bs)), pW); + FVECTOR3 pos = oapiTransformCoord(ptr(_F(BBox.bs)), pW); // Find all local lights effecting this mesh ------------------------------------------ // @@ -2388,16 +2747,16 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) if (displ == 3 && g != selgrp) continue; - FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); + FX->SetVector(eColor, _DX(F4_Zero)); if (flags&DBG_FLAGS_HLMESH) { - if (uCurrentMesh == selmsh) { - FX->SetVector(eColor, ptr(D3DXVECTOR4(0.0f, 0.0f, 0.5f, 0.5f))); + if (g_uCurrentMesh == selmsh) { + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0.0f, 0.5f, 0.5f))); } } if (flags&DBG_FLAGS_HLGROUP) { - if (g == selgrp && uCurrentMesh == selmsh) { - FX->SetVector(eColor, ptr(D3DXVECTOR4(0.0f, 0.5f, 0.0f, 0.5f))); + if (g == selgrp && g_uCurrentMesh == selmsh) { + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0.5f, 0.0f, 0.5f))); } } } @@ -2426,10 +2785,6 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) if (Tex[ti] != old_tex) { - D3D9Stats.Mesh.TexChanges++; - - if (DebugControls::IsActive()) if (pTune) FX->SetValue(eTune, &pTune[ti], sizeof(D3D9Tune)); - old_tex = Tex[ti]; FX->SetTexture(eTex0, Tex[ti]->GetTexture()); @@ -2499,10 +2854,10 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) // if (Grp[g].bTransform) { bWorldMesh = false; - FX->SetMatrix(eW, D3DXMatrixMultiply(&q, &pGrpTF[g], pW)); + FX->SetMatrix(eW, _DX(oapiMatrixMultiply(&q, &pGrpTF[g], pW))); } else if (!bWorldMesh) { - FX->SetMatrix(eW, &mWorldMesh); + FX->SetMatrix(eW, _DX(mWorldMesh)); bWorldMesh = true; } @@ -2515,9 +2870,11 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) // FX->SetBool(eTextured, bTextured); FX->SetBool(eFullyLit, (Grp[g].UsrFlag & 0x4) != 0); - FX->SetBool(eNoColor, (Grp[g].UsrFlag & 0x10) != 0); FX->SetBool(eOITEnable, (Grp[g].UsrFlag & 0x20) != 0); + if ((Grp[g].UsrFlag & 0x10) != 0) FX->SetValue(eNoColor, &FVECTOR3(1, 1, 1), sizeof(FVECTOR3)); + else FX->SetValue(eNoColor, &FVECTOR3(0, 0, 0), sizeof(FVECTOR3)); + FX->CommitChanges(); if (bHUD) { @@ -2553,14 +2910,14 @@ void D3D9Mesh::RenderFast(const LPD3DXMATRIX pW, int iTech) HR(FX->End()); if (flags&(DBG_FLAGS_BOXES | DBG_FLAGS_SPHERES)) RenderBoundingBox(pW); - FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); + FX->SetVector(eColor, _DX(F4_Zero)); if (flags&DBG_FLAGS_DUALSIDED) pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); } // =========================================================================================== // -D3DXMATRIX D3D9Mesh::GetTransform(int g, bool bCombined) +FMATRIX4 D3D9Mesh::GetTransform(int g, bool bCombined) { if (g < 0) return mTransform; @@ -2569,14 +2926,14 @@ D3DXMATRIX D3D9Mesh::GetTransform(int g, bool bCombined) else return Grp[g].Transform; } - D3DXMATRIX Ident; D3DXMatrixIdentity(&Ident); + FMATRIX4 Ident; oapiMatrixIdentity(&Ident); return Ident; } // =========================================================================================== // -bool D3D9Mesh::SetTransform(int g, const LPD3DXMATRIX pMat) +bool D3D9Mesh::SetTransform(int g, const FMATRIX4* pMat) { if (g >= int(nGrp)) return false; @@ -2586,9 +2943,9 @@ bool D3D9Mesh::SetTransform(int g, const LPD3DXMATRIX pMat) bGlobalTF = true; bBSRecompute = true; bBSRecomputeAll = true; - D3DXMatrixInverse(&mTransformInv, NULL, &mTransform); + oapiMatrixInverse(&mTransformInv, NULL, &mTransform); for (DWORD i = 0; iUseNormalMap==1); - D3DXMATRIX mWorldView, q; - D3DXMatrixMultiply(&mWorldView, pW, scn->GetViewMatrix()); + FMATRIX4 mWorldView, q; + oapiMatrixMultiply(&mWorldView, pW, scn->GetViewMatrix()); - D3DXVECTOR4 Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); + FVECTOR4 Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); D3D9MatExt *mat, *old_mat = NULL; SURFHANDLE old_tex = NULL; @@ -2632,9 +2989,9 @@ void D3D9Mesh::RenderBaseTile(const LPD3DXMATRIX pW) FX->SetTechnique(eBaseTile); - FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); - FX->SetMatrix(eGT, gc->GetIdentity()); - FX->SetMatrix(eW, pW); + FX->SetVector(eColor, _DX(F4_Zero)); + FX->SetMatrix(eGT, _DX(FMATRIX_Identity)); + FX->SetMatrix(eW, _DX(pW)); ConfigureAtmo(); @@ -2730,21 +3087,21 @@ void D3D9Mesh::RenderBaseTile(const LPD3DXMATRIX pW) // ================================================================================================ // -void D3D9Mesh::RenderShadowMap(const LPD3DXMATRIX pW, const LPD3DXMATRIX pVP, int opt) +void D3D9Mesh::RenderShadowMap(const FMATRIX4* pW, const FMATRIX4* pVP, int opt, bool bNoCull) { if (!IsOK()) return; pBuf->Map(pDev); - D3DXMATRIX GroupMatrix, mWorldMesh; + FMATRIX4 GroupMatrix, mWorldMesh; MeshShader* pShader = nullptr; - MeshShader::vs_const.mVP = *pVP; + MeshShader::vs_const.mVP = pVP ? *pVP : FMATRIX4(); - D3DXMatrixIdentity(MeshShader::vs_const.mW); + oapiMatrixIdentity(&MeshShader::vs_const.mW); - if (bGlobalTF) D3DXMatrixMultiply(&mWorldMesh, &mTransform, pW); + if (bGlobalTF) oapiMatrixMultiply(&mWorldMesh, &mTransform, pW); else mWorldMesh = *pW; if (opt == 1) { @@ -2762,7 +3119,7 @@ void D3D9Mesh::RenderShadowMap(const LPD3DXMATRIX pW, const LPD3DXMATRIX pVP, in } else { // Regular shadowmap for self shadowing - pDev->SetStreamSource(0, pBuf->pGB, 0, sizeof(D3DXVECTOR4)); + pDev->SetStreamSource(0, pBuf->pGB, 0, sizeof(FVECTOR4)); pShader = s_pShader[SHADER_SHADOWMAP]; pShader->Setup(pVector4Decl, true, 0); } @@ -2791,7 +3148,7 @@ void D3D9Mesh::RenderShadowMap(const LPD3DXMATRIX pW, const LPD3DXMATRIX pVP, in } if (Grp[g].bTransform) { - D3DXMatrixMultiply(MeshShader::vs_const.mW, &pGrpTF[g], pW); // Apply Animations to instance matrices + oapiMatrixMultiply(&MeshShader::vs_const.mW, &pGrpTF[g], pW); // Apply Animations to instance matrices bInit = true; } else { @@ -2804,21 +3161,27 @@ void D3D9Mesh::RenderShadowMap(const LPD3DXMATRIX pW, const LPD3DXMATRIX pVP, in if (pShader->hPSB) pShader->SetPSConstants(pShader->hPSB, &MeshShader::ps_bools, sizeof(MeshShader::ps_bools)); pShader->UpdateTextures(); + DWORD oc; + if (bNoCull) { + pDev->GetRenderState(D3DRS_CULLMODE, &oc); + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + } pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + if (bNoCull) pDev->SetRenderState(D3DRS_CULLMODE, oc); } } // ================================================================================================ // -void D3D9Mesh::RenderStencilShadows(float alpha, const LPD3DXMATRIX pP, const LPD3DXMATRIX pW, bool bShadowMap, const D3DXVECTOR4 *elev) +void D3D9Mesh::RenderStencilShadows(float alpha, const FMATRIX4* pP, const FMATRIX4* pW, bool bShadowMap, const FVECTOR4 *elev) { if (!IsOK()) return; DWORD Pass = 0; - D3DXMATRIX GroupMatrix, mWorldMesh; UINT numPasses = 0; + FMATRIX4 GroupMatrix, mWorldMesh; UINT numPasses = 0; - if (bGlobalTF) D3DXMatrixMultiply(&mWorldMesh, &mTransform, pW); + if (bGlobalTF) oapiMatrixMultiply(&mWorldMesh, &mTransform, pW); else mWorldMesh = *pW; pDev->SetIndices(pBuf->pIB); @@ -2826,15 +3189,15 @@ void D3D9Mesh::RenderStencilShadows(float alpha, const LPD3DXMATRIX pP, const LP pDev->SetStreamSource(0, pBuf->pSB, 0, sizeof(SMVERTEX)); FX->SetTechnique(eShadowTech); - if (elev) FX->SetVector(eInScatter, elev); - else FX->SetVector(eInScatter, ptr(D3DXVECTOR4(0,1,0,0))); + if (elev) FX->SetVector(eInScatter, _DX(elev)); + else FX->SetVector(eInScatter, _DX(FVECTOR4(0.0f, 1, 0, 0))); FX->SetFloat(eMix, alpha); FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); FX->BeginPass(Pass); - if (pP) FX->SetValue(eGT, pP, sizeof(D3DXMATRIX)); // Shadow Projection + if (pP) FX->SetValue(eGT, pP, sizeof(FMATRIX4)); // Shadow Projection bool bInit = true; bool bCurrentState = false; @@ -2859,13 +3222,13 @@ void D3D9Mesh::RenderStencilShadows(float alpha, const LPD3DXMATRIX pP, const LP FX->SetBool(eOITEnable, bOIT); if (Grp[g].bTransform) { - D3DXMatrixMultiply(&GroupMatrix, &pGrpTF[g], pW); // Apply Animations to instance matrices - FX->SetValue(eW, &GroupMatrix, sizeof(D3DXMATRIX)); + oapiMatrixMultiply(&GroupMatrix, &pGrpTF[g], pW); // Apply Animations to instance matrices + FX->SetValue(eW, &GroupMatrix, sizeof(FMATRIX4)); bInit = true; } else { if (bInit) { - FX->SetValue(eW, &mWorldMesh, sizeof(D3DXMATRIX)); + FX->SetValue(eW, &mWorldMesh, sizeof(FMATRIX4)); } bInit = false; } @@ -2881,7 +3244,7 @@ void D3D9Mesh::RenderStencilShadows(float alpha, const LPD3DXMATRIX pP, const LP // ================================================================================================ // -void D3D9Mesh::RenderShadowsEx(float alpha, const LPD3DXMATRIX pP, const LPD3DXMATRIX pW, const D3DXVECTOR4 *light, const D3DXVECTOR4 *param) +void D3D9Mesh::RenderShadowsEx(float alpha, const FMATRIX4* pP, const FMATRIX4* pW, const FVECTOR4 *light, const FVECTOR4 *param) { if (!IsOK()) return; @@ -2892,12 +3255,12 @@ void D3D9Mesh::RenderShadowsEx(float alpha, const LPD3DXMATRIX pP, const LPD3DXM pDev->SetIndices(pBuf->pIB); FX->SetTechnique(eShadowTech); - FX->SetMatrix(eW, pW); - FX->SetMatrix(eGT, pP); + FX->SetMatrix(eW, _DX(pW)); + FX->SetMatrix(eGT, _DX(pP)); FX->SetFloat(eMix, alpha); - if (light) FX->SetVector(eColor, light); - else FX->SetVector(eColor, ptr(D3DXVECTOR4(0,1,0,0))); - FX->SetVector(eTexOff, param); + if (light) FX->SetVector(eColor, _DX(light)); + else FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 1, 0, 0))); + FX->SetVector(eTexOff, _DX(param)); UINT numPasses = 0; @@ -2940,16 +3303,16 @@ void D3D9Mesh::RenderShadowsEx(float alpha, const LPD3DXMATRIX pP, const LPD3DXM // ================================================================================================ // This is a rendering routine for a Exterior Mesh, non-spherical moons/asteroids // -void D3D9Mesh::RenderBoundingBox(const LPD3DXMATRIX pW) +void D3D9Mesh::RenderBoundingBox(const FMATRIX4* pW) { _TRACE; if (!IsOK()) return; if (DebugControls::IsActive()==false) return; - D3DXMATRIX q, qq; + FMATRIX4 q, qq; - static D3DVECTOR poly[10] = { + static FVECTOR3 poly[10] = { {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, @@ -2962,7 +3325,7 @@ void D3D9Mesh::RenderBoundingBox(const LPD3DXMATRIX pW) {0, 0, 1} }; - static D3DVECTOR list[6] = { + static FVECTOR3 list[6] = { {1, 0, 0}, {1, 0, 1}, {1, 1, 0}, @@ -2976,10 +3339,10 @@ void D3D9Mesh::RenderBoundingBox(const LPD3DXMATRIX pW) DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); DWORD selmsh = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDMESH); DWORD selgrp = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDGROUP); - bool bSel = (uCurrentMesh==selmsh); + bool bSel = (g_uCurrentMesh==selmsh); - if (flags&(DBG_FLAGS_SELVISONLY|DBG_FLAGS_SELMSHONLY|DBG_FLAGS_SELGRPONLY) && DebugControls::GetVisual()!=pCurrentVisual) return; + if (flags&(DBG_FLAGS_SELVISONLY|DBG_FLAGS_SELMSHONLY|DBG_FLAGS_SELGRPONLY) && DebugControls::GetVisual()!=g_pCurrentVisual) return; if (flags&DBG_FLAGS_SELMSHONLY && !bSel) return; if (flags&DBG_FLAGS_SELGRPONLY && !bSel) return; @@ -2988,8 +3351,8 @@ void D3D9Mesh::RenderBoundingBox(const LPD3DXMATRIX pW) pDev->SetVertexDeclaration(pPositionDecl); // ---------------------------------------------------------------- - FX->SetMatrix(eW, pW); - FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 1, 0, 0.5f))); + FX->SetMatrix(eW, _DX(pW)); + FX->SetVector(eColor, _DX(FVECTOR4(0, 1, 0, 0.5f))); FX->SetTechnique(eBBTech); // ---------------------------------------------------------------- @@ -3002,24 +3365,24 @@ void D3D9Mesh::RenderBoundingBox(const LPD3DXMATRIX pW) if (flags&DBG_FLAGS_SELGRPONLY && g!=selgrp) continue; if (Grp[g].UsrFlag & 0x2) continue; - FX->SetVector(eAttennuate, &Grp[g].BBox.min); - FX->SetVector(eInScatter, &Grp[g].BBox.max); + FX->SetVector(eAttennuate, _DX(Grp[g].BBox.mn)); + FX->SetVector(eInScatter, _DX(Grp[g].BBox.mx)); // Apply Animations ========================================================================================= // if (Grp[g].bTransform) { - if (bGlobalTF) FX->SetMatrix(eGT, D3DXMatrixMultiply(&q, &mTransform, &Grp[g].Transform)); - else FX->SetMatrix(eGT, &Grp[g].Transform); + if (bGlobalTF) FX->SetMatrix(eGT, _DX(oapiMatrixMultiply(&q, &mTransform, &Grp[g].Transform))); + else FX->SetMatrix(eGT, _DX(Grp[g].Transform)); } - else FX->SetMatrix(eGT, &mTransform); + else FX->SetMatrix(eGT, _DX(mTransform)); // Setup Mesh drawing options ================================================================================= // FX->CommitChanges(); - pDev->DrawPrimitiveUP(D3DPT_LINESTRIP, 9, &poly, sizeof(D3DVECTOR)); - pDev->DrawPrimitiveUP(D3DPT_LINELIST, 3, &list, sizeof(D3DVECTOR)); + pDev->DrawPrimitiveUP(D3DPT_LINESTRIP, 9, &poly, sizeof(FVECTOR3)); + pDev->DrawPrimitiveUP(D3DPT_LINELIST, 3, &list, sizeof(FVECTOR3)); } FX->EndPass(); @@ -3030,11 +3393,11 @@ void D3D9Mesh::RenderBoundingBox(const LPD3DXMATRIX pW) for (DWORD g=0; gmin.x, XMVectorSetW(mi, 0)); - XMStoreFloat4((XMFLOAT4 *)&box->max.x, XMVectorSetW(mx, 0)); + XMStoreFloat4((XMFLOAT4 *)&box->mn.x, XMVectorSetW(mi, 0)); + XMStoreFloat4((XMFLOAT4 *)&box->mx.x, XMVectorSetW(mx, 0)); } // =========================================================================================== // -void D3D9Mesh::TransformGroup(DWORD n, const D3DXMATRIX *m) +void D3D9Mesh::TransformGroup(DWORD n, const FMATRIX4 *m) { if (!IsOK()) return; bBSRecompute = true; + oapiMatrixMultiply(&Grp[n].Transform, &Grp[n].Transform, m); - Grp[n].Transform = Grp[n].Transform * (*m); Grp[n].bTransform = true; Grp[n].bUpdate = true; - D3DXMatrixMultiply(&pGrpTF[n], &mTransform, &Grp[n].Transform); + oapiMatrixMultiply(&pGrpTF[n], &mTransform, &Grp[n].Transform); } // =========================================================================================== // -void D3D9Mesh::Transform(const D3DXMATRIX *m) +void D3D9Mesh::Transform(const FMATRIX4 *m) { if (!IsOK()) return; bBSRecompute = true; bBSRecomputeAll = true; bGlobalTF = true; - mTransform = mTransform * (*m); - D3DXMatrixInverse(&mTransformInv, NULL, &mTransform); + oapiMatrixMultiply(&mTransform, &mTransform, m); + oapiMatrixInverse(&mTransformInv, NULL, &mTransform); for (DWORD i=0;ipMesh) if (p->pMesh != this) return result; + if (!pBuf->pGBSys || !pBuf->pIBSys) { LogErr("D3D9Mesh::Pick() Failed: No Geometry Available"); return result; @@ -3217,44 +3582,46 @@ D3D9Pick D3D9Mesh::Pick(const LPD3DXMATRIX pW, const LPD3DXMATRIX pT, const D3DX UpdateBoundingBox(); - D3DXMATRIX mW, mWT, mWorldMesh; + XMVECTOR Zero = FVECTOR3(0).XM(); + XMVECTOR Dir = vDir->XM(); + + XMMATRIX mW, mWT, mWorldMesh; - if (pT) D3DXMatrixMultiply(&mWT, pT, pW); - else mWT = *pW; + if (pT) mWT = XMMatrixMultiply(pT->XM(), pW->XM()); + else mWT = pW->XM(); - if (bGlobalTF) D3DXMatrixMultiply(&mWorldMesh, &mTransform, &mWT); + if (bGlobalTF) mWorldMesh = XMMatrixMultiply(mTransform.XM(), mWT); else mWorldMesh = mWT; + FMATRIX4 fmWT(mWT); + for (DWORD g=0;g rad) continue; - if (Grp[g].bTransform) D3DXMatrixMultiply(&mW, &pGrpTF[g], &mWT); + if (Grp[g].bTransform) mW = XMMatrixMultiply(pGrpTF[g].XM(), mWT); else mW = mWorldMesh; - D3DXVECTOR3 _a, _b, _c, cp; + XMVECTOR _a, _b, _c, cp; WORD *pIdc = pBuf->pIBSys + Grp[g].IdexOff; - D3DXVECTOR4 *pVrt = pBuf->pGBSys + Grp[g].VertOff; + XMVECTOR *pVrt = pBuf->pGBSys + Grp[g].VertOff; - D3DXMATRIX mWI; float det; - D3DXMatrixInverse(&mWI, &det, &mW); + XMMATRIX mWI = XMMatrixInverse(nullptr, mW); - D3DXVECTOR3 pos, dir; - - D3DXVec3TransformCoord(&pos, ptr(D3DXVECTOR3(0, 0, 0)), &mWI); - D3DXVec3TransformNormal(&dir, vDir, &mWI); + XMVECTOR pos = XMVector3TransformCoord(Zero, mWI); + XMVECTOR dir = XMVector3TransformNormal(Dir, mWI); for (DWORD i=0;i 0.1f) { + if ((dp < 0) || p->bDualSided) { + if (DirectX::TriangleTests::Intersects(pos, dir, _a, _b, _c, dst)) { + if (dst > p->fnear) { if (dst < result.dist) { result.dist = dst; result.group = int(g); result.pMesh = this; - result.idx = int(i); - result.u = u; - result.v = v; + result.idx = int(i); } } } @@ -3289,41 +3655,7 @@ D3D9Pick D3D9Mesh::Pick(const LPD3DXMATRIX pW, const LPD3DXMATRIX pT, const D3DX if (result.idx >= 0 && result.group >= 0) { - - int i = result.idx; - int g = result.group; - - if (Grp[g].bTransform) mW = pGrpTF[g]; - else { - if (bGlobalTF) mW = mTransform; - else D3DXMatrixIdentity(&mW); - } - - if (pT) D3DXMatrixMultiply(&mW, &mW, pT); - - D3DXVECTOR3 cp; - - WORD *pIdc = &pBuf->pIBSys[Grp[g].IdexOff]; - D3DXVECTOR4 *pVrt = &pBuf->pGBSys[Grp[g].VertOff]; - - WORD a = pIdc[i * 3 + 0]; - WORD b = pIdc[i * 3 + 1]; - WORD c = pIdc[i * 3 + 2]; - - D3DXVECTOR3 _a = D3DXVECTOR3f4(pVrt[a]); - D3DXVECTOR3 _b = D3DXVECTOR3f4(pVrt[b]); - D3DXVECTOR3 _c = D3DXVECTOR3f4(pVrt[c]); - - float u = result.u; - float v = result.v; - - D3DXVec3Cross(&cp, ptr(_c - _b), ptr(_a - _b)); - - D3DXVec3TransformNormal(&cp, &cp, &mW); - D3DXVec3Normalize(&result.normal, &cp); - - D3DXVECTOR3 p = (_b * u) + (_a * v) + (_c * (1.0f - u - v)); - D3DXVec3TransformCoord(&result.pos, &p, &mW); + result.pos = (*vDir) * result.dist; } return result; @@ -3338,13 +3670,13 @@ D3D9Pick D3D9Mesh::Pick(const LPD3DXMATRIX pW, const LPD3DXMATRIX pT, const D3DX // This is a special rendering routine used to render 3D arrow -------------------------------- // -void D3D9Mesh::RenderAxisVector(LPD3DXMATRIX pW, const D3DXCOLOR *pColor, float len) +void D3D9Mesh::RenderAxisVector(FMATRIX4* pW, const FVECTOR4 *pColor, float len) { UINT numPasses = 0; HR(FX->SetTechnique(eAxisTech)); HR(FX->SetFloat(eMix, len)); - HR(FX->SetValue(eColor, pColor, sizeof(D3DXCOLOR))); - HR(FX->SetMatrix(eW, pW)); + HR(FX->SetValue(eColor, pColor, sizeof(FVECTOR4))); + HR(FX->SetMatrix(eW, _DX(pW))); HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); HR(FX->BeginPass(0)); pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); @@ -3356,7 +3688,7 @@ void D3D9Mesh::RenderAxisVector(LPD3DXMATRIX pW, const D3DXCOLOR *pColor, float // Used only by ring manager -------------------------------------------------------------------- // -void D3D9Mesh::RenderRings(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex) +void D3D9Mesh::RenderRings(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex) { _TRACE; if (!IsOK()) return; @@ -3367,7 +3699,7 @@ void D3D9Mesh::RenderRings(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex) UINT numPasses = 0; HR(FX->SetTechnique(eRingTech)); - HR(FX->SetMatrix(eW, pW)); + HR(FX->SetMatrix(eW, _DX(pW))); HR(FX->SetTexture(eTex0, pTex)); FX->SetValue(eSun, &sunLight, sizeof(D3D9Sun)); HR(FX->SetValue(eMtrl, &defmat, sizeof(D3D9MatExt)-4)); @@ -3380,7 +3712,7 @@ void D3D9Mesh::RenderRings(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex) // Used only by ring manager -------------------------------------------------------------------- // -void D3D9Mesh::RenderRings2(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex, float irad, float orad) +void D3D9Mesh::RenderRings2(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex, float irad, float orad) { _TRACE; if (!IsOK()) return; @@ -3391,11 +3723,11 @@ void D3D9Mesh::RenderRings2(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex, floa UINT numPasses = 0; HR(FX->SetTechnique(eRingTech2)); - HR(FX->SetMatrix(eW, pW)); + HR(FX->SetMatrix(eW, _DX(pW))); HR(FX->SetTexture(eTex0, pTex)); FX->SetValue(eSun, &sunLight, sizeof(D3D9Sun)); HR(FX->SetValue(eMtrl, &defmat, sizeof(D3D9MatExt)-4)); - HR(FX->SetVector(eTexOff, ptr(D3DXVECTOR4(irad, orad, 0, 0)))); + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(irad, orad, 0, 0)))); HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); HR(FX->BeginPass(0)); RenderGroup(0); diff --git a/OVP/D3D9Client/Mesh.h b/OVP/D3D9Client/Mesh.h index c199f9e54..77807eaae 100644 --- a/OVP/D3D9Client/Mesh.h +++ b/OVP/D3D9Client/Mesh.h @@ -3,7 +3,7 @@ // Part of the ORBITER VISUALISATION PROJECT (OVP) // Dual licensed under GPL v3 and LGPL v3 // Copyright (C) 2006-2016 Martin Schweiger -// 2012-2019 Jarmo Nikkanen +// 2012-2023 Jarmo Nikkanen // ============================================================== // ============================================================== @@ -20,9 +20,11 @@ #include "D3D9Client.h" #include "D3D9Effect.h" #include "AABBUtil.h" +#include "IProcess.h" #include -#include +#include "MathAPI.h" #include +#include const DWORD SPEC_DEFAULT = (DWORD)(-1); // "default" material/texture flag const DWORD SPEC_INHERIT = (DWORD)(-2); // "inherit" material/texture flag @@ -45,6 +47,7 @@ const DWORD SPEC_INHERIT = (DWORD)(-2); // "inherit" material/texture flag #define SHADER_LEGACY 2 // Shader most compatible with DX7 Inline #define SHADER_XR2HUD 3 // XR2 HUD shader #define SHADER_METALNESS 4 +#define SHADER_BAKED_VC 5 #define SHADER_SHADOWMAP 10 #define SHADER_SHADOWMAP_OIT 11 #define SHADER_NORMAL_DEPTH 12 @@ -54,6 +57,7 @@ const DWORD SPEC_INHERIT = (DWORD)(-2); // "inherit" material/texture flag #define VCLASS_ULTRA 3 #define VCLASS_SSU_CENTAUR 4 + // Mesh memory mapping mode #define MAPMODE_UNKNOWN 0 #define MAPMODE_CURRENT 1 @@ -61,6 +65,12 @@ const DWORD SPEC_INHERIT = (DWORD)(-2); // "inherit" material/texture flag #define MAPMODE_DYNAMIC 3 +#define ENVCAM_OMIT_ATTC 0x0001 ///< Do not render attachments, rendered by default +#define ENVCAM_OMIT_DOCKS 0x0002 ///< Do not render docked vessels, rendered by default +#define ENVCAM_FOCUS 0x0004 ///< Force rendering of focus object, omitted by default +#define ENVCAM_PLANE 0x0008 ///< Camera view is 160deg square plane, 360deg cube-map by default +#define ENVCAM_USER 0x0010 ///< User supplied setup + @@ -69,6 +79,12 @@ struct _LightList { float illuminace; }; +struct _BakedLights { + LPDIRECT3DTEXTURE9 pMap[16]; + LPDIRECT3DTEXTURE9 pSunAO[6]; + LPDIRECT3DTEXTURE9 pCombined; + LPDIRECT3DTEXTURE9 pSunAOComb; +}; class MeshShader : public ShaderClass { @@ -121,6 +137,8 @@ class MeshBuffer void Map(LPDIRECT3DDEVICE9 pDev); bool IsLocalTo(const class D3D9Mesh *_pRoot) const { return (_pRoot == pRoot); } void MustRemap(DWORD mode); + bool Release(); + MeshBuffer* Reference(); LPDIRECT3DVERTEXBUFFER9 pVB; LPDIRECT3DVERTEXBUFFER9 pGB; @@ -128,10 +146,11 @@ class MeshBuffer LPDIRECT3DVERTEXBUFFER9 pSB; NMVERTEX *pVBSys; - D3DXVECTOR4 *pGBSys; + XMVECTOR *pGBSys; WORD *pIBSys; SMVERTEX *pSBSys; + DWORD nRef; DWORD nVtx; DWORD nIdx; DWORD mapMode; @@ -161,9 +180,11 @@ class D3D9Mesh : private D3D9Effect bool bIsReflective; // Mesh has a reflective material in one or more groups bool bMtrlModidied; bool bIsTemplate; - + + DWORD MeshFlags; D9BBox BBox; MeshBuffer *pBuf; + MESHHANDLE hOapiMesh; struct GROUPREC { // mesh group definition DWORD VertOff; // Main mesh Vertex Offset @@ -185,7 +206,7 @@ class D3D9Mesh : private D3D9Effect bool bDualSided; bool bDeleted; // This entry is deleted by DelGroup() bool bRendered; - D3DXMATRIX Transform; // Group specific transformation matrix + FMATRIX4 Transform; // Group specific transformation matrix D9BBox BBox; DWORD TexIdxEx[MAXTEX]; float TexMixEx[MAXTEX]; @@ -195,23 +216,28 @@ class D3D9Mesh : private D3D9Effect D3D9Mesh(const char *fname); D3D9Mesh(DWORD nGrp, const MESHGROUPEX **hGroup, const SURFHANDLE *hSurf); D3D9Mesh(const MESHGROUPEX *pGroup, const MATERIAL *pMat, SurfNative *pTex); - D3D9Mesh(MESHHANDLE hMesh, bool asTemplate = false, D3DXVECTOR3 *reorig = NULL, float *scale = NULL); + D3D9Mesh(MESHHANDLE hMesh, bool asTemplate = false, FVECTOR3 *reorig = NULL, float *scale = NULL); D3D9Mesh(MESHHANDLE hMesh, const D3D9Mesh &hTemp); ~D3D9Mesh(); bool IsOK() const { return pBuf != NULL; } - + MESHHANDLE GetOapiHandle() { return hOapiMesh; } void Release(); - - void LoadMeshFromHandle(MESHHANDLE hMesh, D3DXVECTOR3 *reorig = NULL, float *scale = NULL); + void ClearBake(int i); + void LoadBakedLights(); + void BakeLights(ImageProcessing *pBaker, const FVECTOR3* BakedLightsControl); + void BakeAO(ImageProcessing* pBaker, const FVECTOR3 &vSun, const LVLH& lvlh, const LPDIRECT3DTEXTURE9 pIrrad); + void LoadMeshFromHandle(MESHHANDLE hMesh, FVECTOR3 *reorig = NULL, float *scale = NULL); void ReLoadMeshFromHandle(MESHHANDLE hMesh); void ReloadTextures(); void SetName(const char *name); void SetName(UINT idx); const char * GetName() const { return name; } + const char * GetDirName(int i, int v); + FVECTOR3 GetDir(int i); - void SetDefaultShader(WORD shader) { DefShader = shader; bMtrlModidied = true; } + void SetDefaultShader(WORD shader); WORD GetDefaultShader() const { return DefShader; } void SetClass(DWORD cl) { vClass = cl; } @@ -229,6 +255,7 @@ class D3D9Mesh : private D3D9Effect * \return Pointer to group structure. */ const GROUPREC * GetGroup(DWORD idx) const; + GROUPREC* GetGroup(DWORD idx); void SetMFDScreenId(DWORD idx, WORD id); void SetDualSided(DWORD idx, bool bState) { Grp[idx].bDualSided = bState; } @@ -251,8 +278,6 @@ class D3D9Mesh : private D3D9Effect void SetMaterial(const D3DMATERIAL9 *pMat, DWORD idx, bool bUpdateStatus = true); int SetMaterialEx(DWORD idx, MatProp mid, const FVECTOR4* in); int GetMaterialEx(DWORD idx, MatProp mid, FVECTOR4* out); - bool GetTexTune(D3D9Tune *pT, DWORD idx) const; - void SetTexTune(const D3D9Tune *pT, DWORD idx); DWORD GetGroupCount() const { return nGrp; } DWORD GetMaterialCount() const { return nMtrl; } @@ -264,17 +289,17 @@ class D3D9Mesh : private D3D9Effect DWORD GetMeshGroupMaterialIdx(DWORD grp) const; DWORD GetMeshGroupTextureIdx(DWORD grp) const; DWORD GetGroupTransformCount() const; - D3DXVECTOR3 GetBoundingSpherePos(); + FVECTOR3 GetBoundingSpherePos(); float GetBoundingSphereRadius(); D9BBox * GetAABB(); - D3DXVECTOR3 GetGroupSize(DWORD idx) const; - LPD3DXMATRIX GetTransform() { if (bGlobalTF) return &mTransform; else return NULL; } + FVECTOR3 GetGroupSize(DWORD idx) const; + FMATRIX4* GetTransform() { if (bGlobalTF) return &mTransform; else return NULL; } - D3DXMATRIX GetTransform(int grp, bool bCombined); - bool SetTransform(int grp, const LPD3DXMATRIX pMat); + FMATRIX4 GetTransform(int grp, bool bCombined); + bool SetTransform(int grp, const FMATRIX4* pMat); void SetPosition(VECTOR3 &pos); - void SetRotation(D3DXMATRIX &rot); + void SetRotation(FMATRIX4 &rot); /** * \brief Replace a mesh texture. @@ -287,35 +312,37 @@ class D3D9Mesh : private D3D9Effect void RenderGroup(const GROUPREC *grp); void RenderGroup(int idx); - void RenderBaseTile(const LPD3DXMATRIX pW); - void RenderBoundingBox(const LPD3DXMATRIX pW); - void Render(const LPD3DXMATRIX pW, int iTech=RENDER_VESSEL, LPDIRECT3DCUBETEXTURE9 *pEnv=NULL, int nEnv=0); - void RenderFast(const LPD3DXMATRIX pW, int iTech); - void RenderShadowMap(const LPD3DXMATRIX pW, const LPD3DXMATRIX pVP, int flags); - void RenderStencilShadows(float alpha, const LPD3DXMATRIX pP, const LPD3DXMATRIX pW, bool bShadowMap = false, const D3DXVECTOR4 *elev = NULL); - void RenderShadowsEx(float alpha, const LPD3DXMATRIX pP, const LPD3DXMATRIX pW, const D3DXVECTOR4 *light, const D3DXVECTOR4 *param); - void RenderRings(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex); - void RenderRings2(const LPD3DXMATRIX pW, LPDIRECT3DTEXTURE9 pTex, float irad, float orad); - void RenderAxisVector(LPD3DXMATRIX pW, const D3DXCOLOR *pColor, float len); - void RenderSimplified(const LPD3DXMATRIX pW, LPDIRECT3DCUBETEXTURE9 *pEnv = NULL, int nEnv = 0, bool bSP = false); + void RenderBaseTile(const FMATRIX4* pW); + void RenderBoundingBox(const FMATRIX4* pW); + void Render(const FMATRIX4* pW, const ENVCAMREC* em = NULL, int iTech = RENDER_VESSEL); + void RenderFast(const FMATRIX4* pW, int iTech); + void RenderShadowMap(const FMATRIX4* pW, const FMATRIX4* pVP, int flags, bool bNoCull = false); + void RenderStencilShadows(float alpha, const FMATRIX4* pP, const FMATRIX4* pW, bool bShadowMap = false, const FVECTOR4 *elev = NULL); + void RenderShadowsEx(float alpha, const FMATRIX4* pP, const FMATRIX4* pW, const FVECTOR4 *light, const FVECTOR4 *param); + void RenderRings(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex); + void RenderRings2(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex, float irad, float orad); + void RenderAxisVector(FMATRIX4* pW, const FVECTOR4 *pColor, float len); + void RenderSimplified(const FMATRIX4* pW, LPDIRECT3DCUBETEXTURE9 *pEnv = NULL, int nEnv = 0, bool bSP = false); void CheckMeshStatus(); void ResetTransformations(); - void TransformGroup(DWORD n, const D3DXMATRIX *m); - void Transform(const D3DXMATRIX *m); + void TransformGroup(DWORD n, const FMATRIX4 *m); + void Transform(const FMATRIX4 *m); int GetGroup (DWORD grp, GROUPREQUESTSPEC *grs); int EditGroup (DWORD grp, GROUPEDITSPEC *ges); void SetSunLight(const D3D9Sun *pLight); - D3D9Pick Pick(const LPD3DXMATRIX pW, const LPD3DXMATRIX pT, const D3DXVECTOR3 *vDir); + D3D9Pick Pick(const FMATRIX4* pW, const FMATRIX4* pT, const FVECTOR3 *vDir, const PickProp* p); void UpdateBoundingBox(); void BoundingBox(const NMVERTEX *vtx, DWORD n, D9BBox *box); - void SetAmbientColor(D3DCOLOR c); - void SetupFog(const LPD3DXMATRIX pW); + void SetAmbientColor(const FVECTOR3& c); + const FVECTOR3& GetAmbientColor(); + void SetupFog(const FMATRIX4* pW); void ResetRenderStatus(); + LPDIRECT3DTEXTURE9 GetCombinedMap(int tex_idx = -1); /** * \brief Enable/disable material alpha value for transparency calculation. @@ -329,17 +356,19 @@ class D3D9Mesh : private D3D9Effect static void GlobalInit(LPDIRECT3DDEVICE9 pDev); static void GlobalExit(); + static void SetShadows(const SHADOWMAP* sprm); private: void UpdateTangentSpace(NMVERTEX *pVrt, WORD *pIdx, DWORD nVtx, DWORD nFace, bool bTextured); void ProcessInherit(); - bool CopyVertices(GROUPREC *grp, const MESHGROUPEX *mg, D3DXVECTOR3 *reorig = NULL, float *scale = NULL); + bool CopyVertices(GROUPREC *grp, const MESHGROUPEX *mg, FVECTOR3 *reorig = NULL, float *scale = NULL); void SetGroupRec(DWORD i, const MESHGROUPEX *mg); void Null(const char *meshName = NULL); void UpdateFlags(); void ConfigureAtmo(); + void ConfigureShadows(); WORD DefShader; DWORD MaxVert; @@ -352,12 +381,15 @@ class D3D9Mesh : private D3D9Effect DWORD Flags; D3D9MatExt *Mtrl; // list of mesh materials SurfNative **Tex; // list of mesh textures - D3D9Tune *pTune; - D3DXMATRIX mTransform; - D3DXMATRIX mTransformInv; - D3DXMATRIX *pGrpTF; + std::map BakedLights; + std::map::const_iterator bli; + std::vector env_cams; + + FMATRIX4 mTransform; + FMATRIX4 mTransformInv; + FMATRIX4 *pGrpTF; D3D9Sun sunLight; - D3DCOLOR cAmbient; + FVECTOR3 cAmbient; LightStruct null_light; _LightList LightList[MAX_SCENE_LIGHTS]; @@ -369,6 +401,8 @@ class D3D9Mesh : private D3D9Effect char name[128]; + static LPDIRECT3DTEXTURE9 pShadowMap[SHM_CASCADE_COUNT]; + static FVECTOR4 ShdSubRect[SHM_CASCADE_COUNT]; static MeshShader* s_pShader[16]; }; diff --git a/OVP/D3D9Client/MeshMgr.cpp b/OVP/D3D9Client/MeshMgr.cpp index b60a32e44..aa8a0419f 100644 --- a/OVP/D3D9Client/MeshMgr.cpp +++ b/OVP/D3D9Client/MeshMgr.cpp @@ -63,7 +63,7 @@ int MeshManager::StoreMesh(MESHHANDLE hMesh, const char *name) DWORD count = mlist[nmlist-1].mesh->GetGroupCount(); for (DWORD i=0;iGetGroupSize(i); + FVECTOR3 s = mlist[nmlist-1].mesh->GetGroupSize(i); if (fabs(s.x)>lim || fabs(s.y)>lim || fabs(s.z)>lim) return i; } diff --git a/OVP/D3D9Client/Particle.cpp b/OVP/D3D9Client/Particle.cpp index 6bc93d18a..15450d39a 100644 --- a/OVP/D3D9Client/Particle.cpp +++ b/OVP/D3D9Client/Particle.cpp @@ -63,7 +63,7 @@ D3D9ParticleStream::D3D9ParticleStream(GraphicsClient *_gc, PARTICLESTREAMSPEC * pfirst = NULL; plast = NULL; np = 0; - D3DMAT_Identity(&mWorld); + oapiMatrixIdentity(&mWorld); if (needsetup) { int i, j, k, r, ofs; @@ -437,7 +437,7 @@ void D3D9ParticleStream::RenderDiffuse(LPDIRECT3DDEVICE9 dev) HR(dev->SetVertexDeclaration(pNTVertexDecl)); HR(FX->SetTechnique(eDiffuseTech)); - HR(FX->SetMatrix(eW, &mWorld)); + HR(FX->SetMatrix(eW, _DX(mWorld))); if (tex) HR(FX->SetTexture(eTex0, SURFACE(tex)->GetTexture())); @@ -493,7 +493,7 @@ void D3D9ParticleStream::RenderEmissive(LPDIRECT3DDEVICE9 dev) HR(dev->SetVertexDeclaration(pPosTexDecl)); HR(FX->SetTechnique(eEmissiveTech)); - HR(FX->SetMatrix(eW, &mWorld)); + HR(FX->SetMatrix(eW, _DX(mWorld))); if (tex) HR(FX->SetTexture(eTex0, SURFACE(tex)->GetTexture())); @@ -714,7 +714,7 @@ void ExhaustStream::RenderGroundShadow (LPDIRECT3DDEVICE9 dev, LPDIRECT3DTEXTURE HR(dev->SetVertexDeclaration(pPosTexDecl)); HR(FX->SetTechnique(eEmissiveTech)); - HR(FX->SetMatrix(eW, &mWorld)); + HR(FX->SetMatrix(eW, _DX(mWorld))); if (tex) FX->SetTexture(eTex0, SURFACE(tex)->GetTexture()); diff --git a/OVP/D3D9Client/Particle.h b/OVP/D3D9Client/Particle.h index 959af8d3b..472d85c30 100644 --- a/OVP/D3D9Client/Particle.h +++ b/OVP/D3D9Client/Particle.h @@ -116,7 +116,7 @@ class D3D9ParticleStream : public oapi::ParticleStream, public D3D9Effect ParticleSpec *pfirst, *plast; int np; // number of current particles int stride; // number of particles rendered simultaneously - D3DXMATRIX mWorld; // ground shadow related matrix + FMATRIX4 mWorld; // ground shadow related matrix SURFHANDLE tex; // particle texture double ipht2; diff --git a/OVP/D3D9Client/RingMgr.cpp b/OVP/D3D9Client/RingMgr.cpp index 6aeec6cd6..79d0ac67f 100644 --- a/OVP/D3D9Client/RingMgr.cpp +++ b/OVP/D3D9Client/RingMgr.cpp @@ -88,12 +88,12 @@ DWORD RingManager::LoadTextures () return LoadPlanetTextures(fname, tex, 0, MAXRINGRES); } -bool RingManager::Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &mWorld, bool front) +bool RingManager::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &mWorld, bool front) { MATRIX3 grot; - static D3DXMATRIX imat; - D3DXVECTOR3 q(mWorld._11, mWorld._21, mWorld._31); - float scale = D3DXVec3Length(&q); + static FMATRIX4 imat; + FVECTOR3 q(mWorld.m11, mWorld.m21, mWorld.m31); + float scale = length(q); oapiGetRotationMatrix(vp->Object(), &grot); @@ -108,11 +108,11 @@ bool RingManager::Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &mWorld, bool front) zaxis = -zaxis; } - D3DXVECTOR3 x(float(xaxis.x), float(xaxis.y), float(xaxis.z)); - D3DXVECTOR3 y(float(yaxis.x), float(yaxis.y), float(yaxis.z)); - D3DXVECTOR3 z(float(zaxis.x), float(zaxis.y), float(zaxis.z)); + FVECTOR3 x(float(xaxis.x), float(xaxis.y), float(xaxis.z)); + FVECTOR3 y(float(yaxis.x), float(yaxis.y), float(yaxis.z)); + FVECTOR3 z(float(zaxis.x), float(zaxis.y), float(zaxis.z)); - D3DXMATRIX World = mWorld; + FMATRIX4 World = mWorld; x*=scale; y*=scale; z*=scale; diff --git a/OVP/D3D9Client/RingMgr.h b/OVP/D3D9Client/RingMgr.h index de2baa2e3..c59f17f77 100644 --- a/OVP/D3D9Client/RingMgr.h +++ b/OVP/D3D9Client/RingMgr.h @@ -46,7 +46,7 @@ class RingManager { inline double InnerRad() const { return irad; } inline double OuterRad() const { return orad; } - bool Render (LPDIRECT3DDEVICE9 dev, D3DXMATRIX &mWorld, bool zenable); + bool Render (LPDIRECT3DDEVICE9 dev, FMATRIX4 &mWorld, bool zenable); protected: D3D9Mesh *CreateRing (double irad, double orad, int nsect); diff --git a/OVP/D3D9Client/RunwayLights.cpp b/OVP/D3D9Client/RunwayLights.cpp index 7689b2f1f..9b954617b 100644 --- a/OVP/D3D9Client/RunwayLights.cpp +++ b/OVP/D3D9Client/RunwayLights.cpp @@ -2,7 +2,7 @@ // RunwayLights.cpp // Part of the ORBITER VISUALISATION PROJECT (OVP) D3D9 Client // Dual licensed under GPL v3 and LGPL v3 -// Copyright (C) 2012 - 2016 Émile "Bibi Uncle" Grégoire +// Copyright (C) 2012 - 2016 Émile "Bibi Uncle" Grégoire // 2012 - 2016 Jarmo Nikkanen // ============================================================== @@ -33,7 +33,7 @@ RunwayLights::RunwayLights(class vBase *_vB, const class Scene *scn) apr_length = 257.0; iCategory = 0; nPAPI = 0; - hObj = vB->GetObjectA(); + hObj = vB->GetObjHandle(); nVASI = 0; bSingleEnded = false; bDisp2 = false; @@ -323,14 +323,14 @@ BeaconArray *RunwayLights::BuildLights(VECTOR3 _start, VECTOR3 _end, double disp // start lights -------------------------------------- - _space = _widthDir * width/float(numLightsEnd-1); + _space = _widthDir * width / double(numLightsEnd-1); if (iCategory==2) { - _current = _start - _space*(float(numLightsEnd-1)/2.0) - _space * 3; + _current = _start - _space*(double(numLightsEnd-1)/2.0) - _space * 3; count = numLightsEnd+6; } else { - _current = _start - _space*(float(numLightsEnd-1)/2.0); + _current = _start - _space*(double(numLightsEnd-1)/2.0); count = numLightsEnd; } @@ -551,7 +551,7 @@ BeaconArray * RunwayLights::BuildPAPI(VECTOR3 start, VECTOR3 end, DWORD i) { entryPAPI[j] = papiLight; entryPAPI[j].dir = _V(-entryPAPI[j].dir.x, entryPAPI[j].dir.y, -entryPAPI[j].dir.z); - entryPAPI[j].pos = start + dir*PAPI_pos[i].z + widthDir*disp + widthDir*j*papi_separation - widthDir*papi_separation*1.5; + entryPAPI[j].pos = start + dir*PAPI_pos[i].z + widthDir * double(disp) + widthDir * double(j*papi_separation) - widthDir * double(papi_separation*1.5); } BeaconArray *beacons = new BeaconArray(entryPAPI, 4); @@ -612,7 +612,7 @@ BeaconArray *RunwayLights::BuildVASI(VECTOR3 _start, VECTOR3 _end, DWORD idx) beaconsEntry1[i] = vasiLight; beaconsEntry1[i].color = red; beaconsEntry1[i].size = 1.0f * lightSize; - beaconsEntry1[i].pos = _current + _widthDir * 2.0 * float(k) + _V(0,1,0); + beaconsEntry1[i].pos = _current + _widthDir * 2.0 * double(k) + _V(0,1,0); } _current -= _dir * VASI[e].y; @@ -620,7 +620,7 @@ BeaconArray *RunwayLights::BuildVASI(VECTOR3 _start, VECTOR3 _end, DWORD idx) for (k=0;k<5;k++, i++) { beaconsEntry1[i] = vasiLight; beaconsEntry1[i].color = white; - beaconsEntry1[i].pos = _current + _widthDir * 2.0 * float(k) + _V(0,1,0) + _V(0,1,0)*(sin(VASI[e].x*RAD)*VASI[e].y); + beaconsEntry1[i].pos = _current + _widthDir * 2.0 * double(k) + _V(0,1,0) + _V(0,1,0)*(sin(VASI[e].x*RAD)*VASI[e].y); } // Post process lights ------------------------------------------ @@ -641,7 +641,7 @@ BeaconArray *RunwayLights::BuildVASI(VECTOR3 _start, VECTOR3 _end, DWORD idx) } -void RunwayLights::SetPAPIColors(BeaconArray *pPAPI, LPD3DXMATRIX world, int i) +void RunwayLights::SetPAPIColors(BeaconArray *pPAPI, FMATRIX4* world, int i) { BAVERTEX *pVrt = pPAPI->LockVertexBuffer(); @@ -651,12 +651,12 @@ void RunwayLights::SetPAPIColors(BeaconArray *pPAPI, LPD3DXMATRIX world, int i) DWORD red = 0xFFFF4444; DWORD white = 0xFFFFEECC; - D3DXVECTOR3 vPos, vUp, vFront; - D3DXVec3TransformNormal(&vUp, ptr(D3DXVECTOR3(0,1,0)), world); - D3DXVECTOR3 vRef1 = pVrt[0].pos; - D3DXVec3Normalize(&vFront, D3DXVec3TransformCoord(&vPos, &vRef1, world)); + FVECTOR3 vRef1 = pVrt[0].pos; + FVECTOR3 vUp = oapiTransformNormal(ptr(FVECTOR3(0,1,0)), world); + FVECTOR3 vPos = oapiTransformCoord(&vRef1, world); + FVECTOR3 vFront = unit(vPos); - float slope = float(-asin(D3DXVec3Dot(&vFront,&vUp))*DEG); + float slope = float(-asin(dotp(vFront, vUp))*DEG); VECTOR3 P = PAPI_pos[i]; @@ -693,7 +693,7 @@ void RunwayLights::Update(class vPlanet *vP) } -void RunwayLights::Render(LPDIRECT3DDEVICE9 dev, LPD3DXMATRIX world, bool night) +void RunwayLights::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4* world, bool night) { _TRACE; currentTime = float(fmod(1.7*oapiGetSimTime(), 1.0)); @@ -703,11 +703,9 @@ void RunwayLights::Render(LPDIRECT3DDEVICE9 dev, LPD3DXMATRIX world, bool night) VECTOR3 dir = unit(end2 - end1); // Vector of the runway VECTOR3 camDir = scene->GetCameraGDir(); - D3DXVECTOR3 dirGlo; + FVECTOR3 dirGlo = oapiTransformNormal(&_F(dir), world); - D3DXVec3TransformNormal(&dirGlo, ptr(D3DXVEC(dir)), world); - - if (D3DXVec3Dot(ptr(D3DXVEC(camDir)), &dirGlo) > 0) + if (dotp(_F(camDir), dirGlo) > 0) { if (night && beacons1) beacons1->Render(dev, world, currentTime); @@ -973,7 +971,7 @@ void TaxiLights::Init() taxiLight.dir = _V(0, 1, 0); taxiLight.pos = _V(0, 0, 0); //taxiLight.lat = taxiLight.lng = 0.0; - taxiLight.color = D3DXCOLOR(float(color.x), float(color.y), float(color.z), 1.0f); + taxiLight.color = FVECTOR4(float(color.x), float(color.y), float(color.z), 1.0f).dword_abgr(); VECTOR3 space = dir * len / (count-1); VECTOR3 current = end1; @@ -990,7 +988,7 @@ void TaxiLights::Init() } -void TaxiLights::Render(LPDIRECT3DDEVICE9 dev, LPD3DXMATRIX world, bool night) +void TaxiLights::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4* world, bool night) { if (night) beacons1->Render(dev, world, 0.5f); } @@ -1075,4 +1073,4 @@ int TaxiLights::CreateTaxiLights(OBJHANDLE base, const class Scene *scn, const c } return numTaxiLights; -} \ No newline at end of file +} diff --git a/OVP/D3D9Client/RunwayLights.h b/OVP/D3D9Client/RunwayLights.h index 6ac1438fe..bf769e261 100644 --- a/OVP/D3D9Client/RunwayLights.h +++ b/OVP/D3D9Client/RunwayLights.h @@ -2,7 +2,7 @@ // RunwayLights.h // Part of the ORBITER VISUALISATION PROJECT (OVP) D3D9 Client // Dual licensed under GPL v3 and LGPL v3 -// Copyright (C) 2012-2016 Émile "Bibi Uncle" Grégoire +// Copyright (C) 2012-2016 Émile "Bibi Uncle" Grégoire // 2012-2016 Jarmo Nikkanen // ============================================================== @@ -17,7 +17,7 @@ #include "OrbiterAPI.h" #include -#include +#include "MathAPI.h" class BeaconArray; @@ -41,7 +41,7 @@ class RunwayLights void SetCategory(int cat); void Init(); - void Render(LPDIRECT3DDEVICE9 dev, LPD3DXMATRIX world, bool night); + void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4* world, bool night); void Update(class vPlanet *vP); float GetWidth() const { return float(width); } @@ -50,7 +50,7 @@ class RunwayLights protected: - void SetPAPIColors(BeaconArray *pPAPI, LPD3DXMATRIX world, int idx); + void SetPAPIColors(BeaconArray *pPAPI, FMATRIX4* world, int idx); class BeaconArray *BuildLights(VECTOR3 start, VECTOR3 end, double disp); class BeaconArray *BuildVASI(VECTOR3 start, VECTOR3 end, DWORD idx); @@ -105,7 +105,7 @@ class TaxiLights void SetColor(VECTOR3 color); void Init(); - void Render(LPDIRECT3DDEVICE9 dev, LPD3DXMATRIX world, bool night); + void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4* world, bool night); static int CreateTaxiLights(OBJHANDLE base, const class Scene *scn, const char *file, TaxiLights**& out); diff --git a/OVP/D3D9Client/Scene.cpp b/OVP/D3D9Client/Scene.cpp index ebed0697f..ef63d6408 100644 --- a/OVP/D3D9Client/Scene.cpp +++ b/OVP/D3D9Client/Scene.cpp @@ -25,22 +25,14 @@ #include #include -#define IKernelSize 150 +#define IKernelSize 120 using namespace oapi; -static D3DXMATRIX ident; +static FMATRIX4 ident; const double LABEL_DISTLIMIT = 0.6; -struct PList { // auxiliary structure for object distance sorting - vObject *vo; - double dist; -}; - -const int MAXPLANET = 512; // hard limit; should be fixed -static PList plist[MAXPLANET]; - ID3DXEffect * Scene::FX = 0; D3DXHANDLE Scene::eLine = 0; D3DXHANDLE Scene::eStar = 0; @@ -49,18 +41,33 @@ D3DXHANDLE Scene::eColor = 0; D3DXHANDLE Scene::eTex0 = 0; -D3DXVECTOR4 IKernel[IKernelSize]; +FVECTOR4 IKernel[IKernelSize]; -bool sort_vessels(const vVessel *a, const vVessel *b) +bool sort_tgt_dist(const vObject *a, const vObject *b) { return a->CameraTgtDist() < b->CameraTgtDist(); } +bool sort_cdist(const vObject* a, const vObject* b) +{ + return a->CamDist() > b->CamDist(); +} + float Rand() { return float(rand()) / 32768.0f; } +void DebugMatrix(FMATRIX4* pM, const char* name) +{ + D3D9DebugLog("[%3.3f, %3.3f, %3.3f, %3.3f]", pM->m41, pM->m42, pM->m43, pM->m44); + D3D9DebugLog("[%3.3f, %3.3f, %3.3f, %3.3f]", pM->m31, pM->m32, pM->m33, pM->m34); + D3D9DebugLog("[%3.3f, %3.3f, %3.3f, %3.3f]", pM->m21, pM->m22, pM->m23, pM->m24); + D3D9DebugLog("[%3.3f, %3.3f, %3.3f, %3.3f]", pM->m11, pM->m12, pM->m13, pM->m14); + D3D9DebugLog("%s", name); +} + + // =========================================================================================== // Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) @@ -68,8 +75,6 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) _TRACE; gc = _gc; - vobjEnv = NULL; - vobjIrd = NULL; m_celSphere = NULL; Lights = NULL; hSun = NULL; @@ -77,6 +82,7 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) pLabelFont = NULL; pDebugFont = NULL; pBlur = NULL; + pBlur2D = NULL; pOffscreenTarget = NULL; pLocalCompute = NULL; pRenderGlares = NULL; @@ -93,30 +99,30 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) pSunGlare = NULL; pSunGlareAtm = NULL; pEnvDS = NULL; - pIrradDS = NULL; pIrradiance = NULL; pIrradTemp = NULL; - pIrradTemp2 = NULL; - pIrradTemp3 = NULL; pDepthNormalDS = NULL; pVisDepth = NULL; pLocalResults = NULL; pLocalResultsSL = NULL; + pBakeLights = NULL; + ptRandom = NULL; + dmCubeMesh = NULL; + pRenderStage = NULL; + + vobjEnv = eCamRenderList.cend(); + itIC = InteriorCams.cend(); fDisplayScale = float(viewH) / 1080.0f; for (auto& a : DepthSampleKernel) a = FVECTOR2(0, 0); memset(&psShmDS, 0, sizeof(psShmDS)); - memset(&ptShmRT, 0, sizeof(ptShmRT)); - memset(&psShmRT, 0, sizeof(psShmRT)); - + memset(&Camera, 0, sizeof(Camera)); pDevice = _gc->GetDevice(); - memset(&Camera, 0, sizeof(Camera)); - - D3DXMatrixIdentity(&ident); + oapiMatrixIdentity(&ident); SetCameraAperture(float(RAD*50.0), float(viewH)/float(viewW)); SetCameraFrustumLimits(2.5f, 5e6f); // initial limits @@ -127,14 +133,14 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) bLocalLight = *(bool*)gc->GetConfigParam(CFGPRM_LOCALLIGHT); memset(&sunLight, 0, sizeof(D3D9Sun)); - memset(&smap, 0, sizeof(smap)); + CLEARARRAY(pBlrTemp); + CLEARARRAY(pBlrTemp2D); CLEARARRAY(pTextures); CLEARARRAY(ptgBuffer); CLEARARRAY(psgBuffer); - vobjFirst = vobjLast = NULL; nstream = 0; iVCheck = 0; @@ -144,7 +150,7 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) float dx = 0; float dy = 0; for (int i = 0; i < IKernelSize; i++) { - double r = oapiRand(); + double r = sqrt(oapiRand()); double a = oapiRand() * PI2; IKernel[i].x = float(cos(a) * r); IKernel[i].y = float(sin(a) * r); @@ -157,7 +163,7 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) for (int i = 0; i < IKernelSize; i++) { float d = IKernel[i].x*IKernel[i].x + IKernel[i].y*IKernel[i].y; IKernel[i].z = sqrt(1.0f - saturate(d)); - IKernel[i].w = sqrt(IKernel[i].z); + IKernel[i].w = IKernel[i].z; } @@ -183,8 +189,15 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) // ------------------------------------------------------------------------------ - // Initialize a shaders for local lights visibility checks and rendering + // Load a mesh to create a stage + // + dmCubeMesh = (DEVMESHHANDLE) new D3D9Mesh("D3D9Cube"); + pRenderStage = new ShaderClass(pDevice, "Modules/D3D9Client/Custom.hlsl", "StageVS", "StagePS", "RenderStage", ""); + + // ------------------------------------------------------------------------------ + // Initialize a shaders for local lights visibility checks and rendering + // if (Config->bGlares || Config->bLocalGlares) { pRenderGlares = new ShaderClass(pDevice, "Modules/D3D9Client/Glare.hlsl", "GlareVS", "GlarePS", "RenderGlares", ""); @@ -208,26 +221,26 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) DWORD ShmMapSize = Config->ShadowMapSize; if (Config->EnvMapMode) { - HR(pDevice->CreateDepthStencilSurface(EnvMapSize, EnvMapSize, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, true, &pEnvDS, NULL)); - } - - if (Config->bIrradiance) { - HR(pDevice->CreateDepthStencilSurface(128, 128, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, true, &pIrradDS, NULL)); + HR(pDevice->CreateDepthStencilSurface(EnvMapSize, EnvMapSize, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, true, &pEnvDS, NULL)); } + // Create Depth Stencil buffers for shadow maps + // if (Config->ShadowMapMode) { UINT size = ShmMapSize; for (int i = 0; i < SHM_LOD_COUNT; i++) { HR(pDevice->CreateDepthStencilSurface(size, size, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, true, &psShmDS[i], NULL)); - HR(pDevice->CreateTexture(size, size, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptShmRT[i], NULL)); - HR(ptShmRT[i]->GetSurfaceLevel(0, &psShmRT[i])); size >>= 1; } - - smap.pShadowMap = ptShmRT[0]; } + // Create shadow map for vessel exterior shadowing + smEX = new SHADOWMAP(pDevice, SHADOWMAP::sMapType::MultiLod); + // Create shadow map for virtual cockpit shadowing + smVC = new SHADOWMAP(pDevice, SHADOWMAP::sMapType::Cascaded); + // Create shadow map for shadowing in a stage-set + smSS = new SHADOWMAP(pDevice, SHADOWMAP::sMapType::SingleLod); // Create auxiliary color buffer for on screen GDI @@ -242,11 +255,20 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) // Create an auxiliary screen space normal and depth buffer (i.e. Shader readable depth buffer) // if (Config->bGlares || Config->bLocalGlares) { - HR(pDevice->CreateDepthStencilSurface(viewW, viewH, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, true, &pDepthNormalDS, NULL)); + HR(pDevice->CreateDepthStencilSurface(viewW, viewH, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, true, &pDepthNormalDS, NULL)); HR(D3DXCreateTexture(pDevice, viewW, viewH, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &ptgBuffer[GBUF_DEPTH])); } + // Create a random number table -------------------------------------------------------------------------------------------------- + // + HR(D3DXCreateTexture(pDevice, 128, 128, 1, D3DUSAGE_DYNAMIC, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptRandom)); + D3DLOCKED_RECT rect; + if (ptRandom->LockRect(0, &rect, 0, 0) == S_OK) { + for (int i = 0; i < (128 * 128); i++) ((float*)rect.pBits)[i] = oapiRand(); + ptRandom->UnlockRect(0); + } else LogErr("Failed to create random table"); + // Initialize post processing effects -------------------------------------------------------------------------------------------------- // pLightBlur = NULL; @@ -314,6 +336,9 @@ Scene::Scene(D3D9Client *_gc, DWORD w, DWORD h) } } + pBakeLights = new ImageProcessing(pDevice, "Modules/D3D9Client/PreBakeLights.hlsl", "PSMain"); + pBakeLights->CompileShader("PSSunAO"); + LogAlw("================ Scene Created ==============="); } @@ -341,13 +366,12 @@ Scene::~Scene () SAFE_DELETE(pLocalCompute); SAFE_DELETE(pRenderGlares); SAFE_DELETE(pCreateGlare); + SAFE_DELETE(pBakeLights); + SAFE_DELETE(pRenderStage); SAFE_RELEASE(pOffscreenTarget); SAFE_RELEASE(pEnvDS); - SAFE_RELEASE(pIrradDS); SAFE_RELEASE(pIrradTemp); - SAFE_RELEASE(pIrradTemp2); - SAFE_RELEASE(pIrradTemp3); SAFE_RELEASE(pDepthNormalDS); SAFE_RELEASE(pLocalResults); SAFE_RELEASE(pLocalResultsSL); @@ -355,11 +379,15 @@ Scene::~Scene () SAFE_RELEASE(pLightGlare); SAFE_RELEASE(pSunGlare); SAFE_RELEASE(pSunGlareAtm); + SAFE_RELEASE(ptRandom); + + SAFE_DELETE(smEX); + SAFE_DELETE(smVC); + SAFE_DELETE(smSS); for (int i = 0; i < ARRAYSIZE(psShmDS); i++) SAFE_RELEASE(psShmDS[i]); - for (int i = 0; i < ARRAYSIZE(ptShmRT); i++) SAFE_RELEASE(ptShmRT[i]); - for (int i = 0; i < ARRAYSIZE(psShmRT); i++) SAFE_RELEASE(psShmRT[i]); for (int i = 0; i < ARRAYSIZE(pBlrTemp); i++) SAFE_RELEASE(pBlrTemp[i]); + for (int i = 0; i < ARRAYSIZE(pBlrTemp2D); i++) SAFE_RELEASE(pBlrTemp2D[i]); if (Lights) { delete []Lights; @@ -434,7 +462,7 @@ void Scene::CreateSunGlare() // =========================================================================================== // -void Scene::Initialise() +void Scene::clbkInitialise() { _TRACE; @@ -473,7 +501,7 @@ static D3D9Pad *_pad = NULL; // =========================================================================================== -void Scene::OnOptionChanged(int cat, int item) +void Scene::clbkOnOptionChanged(int cat, int item) { if (cat == OPTCAT_CELSPHERE) m_celSphere->OnOptionChanged(cat, item); @@ -588,8 +616,7 @@ const D3D9Light *Scene::GetLight(int index) const Scene::VOBJREC *Scene::FindVisual(OBJHANDLE hObj) const { if (hObj==NULL) return NULL; - VOBJREC *pv; - for (pv=vobjFirst; pv; pv=pv->next) if (pv->vobj->Object()==hObj) return pv; + for (auto v : Visuals) if (v->vobj->Object() == hObj) return v; return NULL; } @@ -597,8 +624,8 @@ Scene::VOBJREC *Scene::FindVisual(OBJHANDLE hObj) const // class vObject *Scene::GetVisObject(OBJHANDLE hObj) const { - Scene::VOBJREC *v = FindVisual(hObj); - if (v) return v->vobj; + if (hObj == NULL) return NULL; + for (auto v : Visuals) if (v->vobj->Object() == hObj) return v->vobj; return NULL; } @@ -607,11 +634,10 @@ class vObject *Scene::GetVisObject(OBJHANDLE hObj) const std::set Scene::GetVessels(double max_dst, bool bAct) { std::set List; - VOBJREC *pv; - for (pv = vobjFirst; pv; pv = pv->next) { - if (pv->type != OBJTP_VESSEL) continue; - if (bAct && pv->vobj->IsActive() == false) continue; - if (pv->vobj->CamDist() < max_dst) List.insert((vVessel *)pv->vobj); + for (auto v : Visuals) { + if (v->type != OBJTP_VESSEL) continue; + if (bAct && v->vobj->IsActive() == false) continue; + if (v->vobj->CamDist() < max_dst) List.insert((vVessel *)v->vobj); } return List; } @@ -620,47 +646,26 @@ std::set Scene::GetVessels(double max_dst, bool bAct) // void Scene::DelVisualRec (VOBJREC *pv) { - _TRACE; - // unlink the entry - if (pv->prev) pv->prev->next = pv->next; - else vobjFirst = pv->next; - - if (pv->next) pv->next->prev = pv->prev; - else vobjLast = pv->prev; - - DebugControls::RemoveVisual(pv->vobj); - - vobjEnv = NULL; - vobjIrd = NULL; - // delete the visual, its children and the entry itself - gc->UnregisterVisObject(pv->vobj->GetObject()); - + DebugControls::RemoveVisual(pv->vobj); + gc->UnregisterVisObject(pv->vobj->GetObjHandle()); + if (pv->type == OBJTP_VESSEL) gc->clbkScenarioChanged(pv->vobj, ScnChgEvent::VisualDeleted); delete pv->vobj; - delete pv; + Visuals.remove(pv); } // =========================================================================================== // void Scene::DeleteAllVisuals() { - _TRACE; - VOBJREC *pv = vobjFirst; - while (pv) { - VOBJREC *pvn = pv->next; - - DebugControls::RemoveVisual(pv->vobj); - - gc->UnregisterVisObject(pv->vobj->GetObject()); - - LogAlw("Deleting Visual %s", _PTR(pv->vobj)); - delete pv->vobj; - delete pv; - pv = pvn; + for (auto v : Visuals) + { + DebugControls::RemoveVisual(v->vobj); + gc->UnregisterVisObject(v->vobj->GetObjHandle()); + LogAlw("Deleting Visual %s", _PTR(v->vobj)); + delete v->vobj; } - vobjFirst = vobjLast = NULL; - vobjEnv = NULL; - vobjIrd = NULL; + Visuals.clear(); } // =========================================================================================== @@ -676,6 +681,8 @@ Scene::VOBJREC *Scene::AddVisualRec(OBJHANDLE hObj) memset(pv, 0, sizeof(VOBJREC)); + Visuals.push_back(pv); + pv->vobj = vObject::Create(hObj, this); pv->type = oapiGetObjectType(hObj); @@ -684,13 +691,6 @@ Scene::VOBJREC *Scene::AddVisualRec(OBJHANDLE hObj) VESSEL *hVes=NULL; if (pv->type==OBJTP_VESSEL) hVes = oapiGetVesselInterface(hObj); - // link entry to end of list - pv->prev = vobjLast; - pv->next = NULL; - if (vobjLast) vobjLast->next = pv; - else vobjFirst = pv; - vobjLast = pv; - LogAlw("RegisteringVisual (%s) hVessel=%s, hObj=%s, Vis=%s, Rec=%s, Type=%d", buf, _PTR(hVes), _PTR(hObj), _PTR(pv->vobj), _PTR(pv), pv->type); gc->RegisterVisObject(hObj, (VISHANDLE)pv->vobj); @@ -698,6 +698,8 @@ Scene::VOBJREC *Scene::AddVisualRec(OBJHANDLE hObj) // Initialize Meshes pv->vobj->PreInitObject(); + if (pv->type == OBJTP_VESSEL) gc->clbkScenarioChanged(pv->vobj, ScnChgEvent::VisualCreated); + return pv; } @@ -742,7 +744,7 @@ VECTOR3 Scene::SkyColour () // =========================================================================================== // -void Scene::Update () +void Scene::clbkUpdate () { _TRACE; @@ -869,7 +871,7 @@ double Scene::GetTargetGroundAltitude() const // ============================================================================================ // Up, North, Forward in Ecliptic frame // -void Scene::GetLVLH(vVessel *vV, D3DXVECTOR3 *up, D3DXVECTOR3 *nr, D3DXVECTOR3 *fw) +void Scene::GetLVLH(vVessel *vV, FVECTOR3 *up, FVECTOR3 *nr, FVECTOR3 *fw) { if (!vV || !up || !nr || !fw) return; @@ -880,10 +882,10 @@ void Scene::GetLVLH(vVessel *vV, D3DXVECTOR3 *up, D3DXVECTOR3 *nr, D3DXVECTOR3 * hV->GetRelativePos(hRef, rpos); VECTOR3 axis = mul(grot, _V(0, 1, 0)); normalise(rpos); - *up = D3DXVEC(rpos); - *fw = D3DXVEC(unit(crossp(axis, rpos))); - D3DXVec3Cross(nr, up, fw); - D3DXVec3Normalize(nr, nr); + *up = _F(rpos); + *fw = _F(unit(crossp(axis, rpos))); + *nr = crossp(*up, *fw); + normalize(*nr); } @@ -929,7 +931,7 @@ float Scene::ComputeNearClipPlane() float nearpoint = 10e3f; float neardist = 10e3f; - for (pv = vobjFirst; pv; pv = pv->next) { + for (auto pv : Visuals) { float nr = 10e3f; float fr = 0.0f; @@ -1046,7 +1048,7 @@ bool Scene::UpdateCamVis() // OBJHANDLE hFocus = oapiGetFocusObject(); vFocus = NULL; - for (VOBJREC *pv=vobjFirst; pv; pv=pv->next) { + for (auto pv : Visuals) { if (pv->type==OBJTP_VESSEL) if (pv->vobj->Object()==hFocus) { vFocus = (vVessel *)pv->vobj; break; @@ -1060,50 +1062,19 @@ bool Scene::UpdateCamVis() bg_rgba = D3DCOLOR_RGBA ((int)(sky_color.x*255), (int)(sky_color.y*255), (int)(sky_color.z*255), 255); - // Process Local Light Sources ------------------------------------- - // - if (bLocalLight) { - - ClearLocalLights(); - - VOBJREC *pv = NULL; - for (pv = vobjFirst; pv; pv = pv->next) { - if (!pv->vobj->IsActive()) continue; - OBJHANDLE hObj = pv->vobj->Object(); - if (oapiGetObjectType (hObj) == OBJTP_VESSEL) { - VESSEL *vessel = oapiGetVesselInterface (hObj); - DWORD nemitter = vessel->LightEmitterCount(); - for (DWORD j = 0; j < nemitter; j++) { - const LightEmitter *em = vessel->GetLightEmitter(j); - if ((em->GetVisibility() == LightEmitter::VIS_EXTERNAL) || (em->GetVisibility() == LightEmitter::VIS_ALWAYS)) - AddLocalLight(em, pv->vobj); - } - } - } - } - - // ---------------------------------------------------------------- // render solar system celestial objects (planets and moons) // we render without z-buffer, so need to distance-sort the objects // ---------------------------------------------------------------- - VOBJREC *pv = NULL; - nplanets = 0; + Planets.clear(); - for (pv = vobjFirst; pv && nplanets < MAXPLANET; pv = pv->next) { + for (auto pv : Visuals) { if (pv->apprad < 0.01 && pv->type != OBJTP_STAR) continue; - if (pv->type == OBJTP_PLANET || pv->type == OBJTP_STAR) { - plist[nplanets].vo = pv->vobj; - plist[nplanets].dist = pv->vobj->CamDist(); - nplanets++; - } + if (pv->type == OBJTP_PLANET || pv->type == OBJTP_STAR) Planets.push_back(pv->vobj); } - int distcomp(const void *arg1, const void *arg2); - - qsort((void*)plist, nplanets, sizeof(PList), distcomp); - + Planets.sort(sort_cdist); return bRet && (vFocus != nullptr); } @@ -1146,6 +1117,38 @@ void Scene::AddLocalLight(const LightEmitter *le, const vObject *vo) } } +// =========================================================================================== +// +void Scene::ActivateAllLocalLights(bool bInterior) +{ + if (bLocalLight) { + ClearLocalLights(); + for (auto pv : Visuals) if (pv) ActivateLocalLights(pv->vobj, bInterior); + } +} + + +// =========================================================================================== +// +void Scene::ActivateLocalLights(vObject *vO, bool bInterior) +{ + if (!vO) return; + if (!vO->IsActive()) return; + if (vO->Type() == OBJTP_VESSEL) { + VESSEL* vessel = ((vVessel*)vO)->GetInterface(); + DWORD nemitter = vessel->LightEmitterCount(); + for (DWORD j = 0; j < nemitter; j++) { + const LightEmitter* em = vessel->GetLightEmitter(j); + if (em->GetVisibility() == LightEmitter::VIS_ALWAYS) AddLocalLight(em, vO); + else { + if ((em->GetVisibility() == LightEmitter::VIS_COCKPIT) && bInterior) AddLocalLight(em, vO); + if ((em->GetVisibility() == LightEmitter::VIS_EXTERNAL) && !bInterior) AddLocalLight(em, vO); + } + } + } +} + + // =========================================================================================== // void Scene::ComputeLocalLightsVisibility() @@ -1179,8 +1182,8 @@ void Scene::ComputeLocalLightsVisibility() } struct { - D3DXMATRIX mVP; - D3DXMATRIX mSVP; + FMATRIX4 mVP; + FMATRIX4 mSVP; FVECTOR4 vSrc; FVECTOR3 vDir; } ComputeData; @@ -1188,7 +1191,7 @@ void Scene::ComputeLocalLightsVisibility() D3DSURFACE_DESC desc; pLocalResultsSL->GetDesc(&desc); - D3DXMatrixOrthoOffCenterLH(&ComputeData.mVP, 0.0f, (float)desc.Width, (float)desc.Height, 0.0f, 0.0f, 1.0f); + D3DMAT_OrthoOffCenterLH(&ComputeData.mVP, 0.0f, (float)desc.Width, (float)desc.Height, 0.0f, 0.0f, 1.0f); psgBuffer[GBUF_DEPTH]->GetDesc(&desc); @@ -1235,7 +1238,71 @@ void Scene::RecallDefaultState() // =========================================================================================== // -void Scene::RenderMainScene() +void Scene::clbkNewVessel(OBJHANDLE hVessel) +{ + CheckVisual(hVessel); +} + +// =========================================================================================== +// +void Scene::clbkDeleteVessel(OBJHANDLE hVessel) +{ + VOBJREC* pv = FindVisual(hVessel); + if (pv) DelVisualRec(pv); +} + +// =========================================================================================== +// +void Scene::clbkScenarioChanged(OBJHANDLE hVessel, ScnChgEvent e) +{ + LogVerbose("=== clbkScenarioChanged(%d) Event ===", e); + + // Acquire list of vessel visuals + // + Vessels.clear(); + for (auto v : Visuals) + if (v->type == OBJTP_VESSEL) Vessels.insert((vVessel*)v->vobj); + + + // Update Attachment hierarchy + // + RootList.clear(); + for (auto v : Vessels) { + OBJHANDLE hRoot = v->GetInterface()->GetAttachmentRoot(); + v->vRoot = (vVessel*)GetVisObject(hRoot); + RootList.insert(v->vRoot); + } + + // Update eCam render list + // + eCamRenderList.clear(); + + // Render env-map for all root vessels in range + for (auto v : RootList) eCamRenderList.insert(v); + + // Render env-map for all vessels with user defined eCam setup, if enabled + if (Config->EnvMapFaces > 1) + for (auto v : Vessels) + if (v->HasOwnEnvCam(EnvCamType::Exterior)) eCamRenderList.insert(v); + + + // --------------------------------------------------------------------------------------- + // Return critical iterators to a start of the list. List context may have changed + // --------------------------------------------------------------------------------------- + + vobjEnv = eCamRenderList.begin(); + vobjIP = eCamRenderList.begin(); + + if (e == ScnChgEvent::Deleted) { + InteriorCams.clear(); + itIC = InteriorCams.begin(); + } +} + + +// =========================================================================================== +// +void Scene::clbkRenderMainScene() { _TRACE; @@ -1251,21 +1318,22 @@ void Scene::RenderMainScene() } return; // Scene not yet properly inilialized, return } + + ActivateAllLocalLights(false); + + set Active; + for (auto v : Vessels) if (v->IsActive()) Active.insert(v); // Update Vessel Animations // - for (VOBJREC *pv = vobjFirst; pv; pv = pv->next) { - if (pv->type == OBJTP_VESSEL) { - vVessel *vv = (vVessel *)pv->vobj; - vv->UpdateAnimations(); - } - } + for (auto v : Active) v->UpdateAnimations(); - if (vFocus == NULL) return; + if (vFocus == nullptr) return; - LPDIRECT3DSURFACE9 pBackBuffer; + LPDIRECT3DSURFACE9 pBackBuffer = nullptr; + LPDIRECT3DTEXTURE9 pExtShdMap = nullptr; if (pOffscreenTarget) pBackBuffer = pOffscreenTarget; else pBackBuffer = gc->GetBackBuffer(); @@ -1284,9 +1352,7 @@ void Scene::RenderMainScene() if (Config->CustomCamMode == 0 && dwTurn == RENDERTURN_CUSTOMCAM) dwTurn++; if (Config->EnvMapMode == 0 && dwTurn == RENDERTURN_ENVCAM) dwTurn++; - if (!bIrrad && dwTurn == RENDERTURN_IRRADIANCE) dwTurn++; - - if (dwTurn>RENDERTURN_LAST) dwTurn = 0; + if (dwTurn > RENDERTURN_LAST) dwTurn = 0; int RenderCount = max(1, Config->EnvMapFaces); @@ -1301,14 +1367,14 @@ void Scene::RenderMainScene() { if (camCurrent == CustomCams.cend()) camCurrent = CustomCams.cbegin(); - OBJHANDLE hVessel = vFocus->GetObjectA(); + OBJHANDLE hVessel = vFocus->GetObjHandle(); vObject *vO = GetVisObject((*camCurrent)->hVessel); double maxd = min(500e3, GetCameraAltitude() + 15e3); if (vO->CamDist() < maxd && (*camCurrent)->bActive) { - RenderCustomCameraView((*camCurrent)); + RenderCustomCameraView((*camCurrent)); // Note: World origin is changed here if ((*camCurrent)->pRenderProc) { D3D9Pad *pSkp = (D3D9Pad * )gc->clbkGetSketchpad((*camCurrent)->hSurface); @@ -1323,54 +1389,46 @@ void Scene::RenderMainScene() // ------------------------------------------------------------------------------------------------------- - // Render Environmental Map For the Vessels + // Render reflection cube maps for vessels // ------------------------------------------------------------------------------------------------------- - if (dwTurn == RENDERTURN_ENVCAM) { + if (dwTurn == RENDERTURN_ENVCAM && Config->EnvMapMode) + { + DWORD flags = 0; + if (Config->EnvMapMode == 1) flags |= SCN_PLANETS; + if (Config->EnvMapMode == 2) flags |= SCN_PLANETS | SCN_VESSELS | SCN_BASESTRUCT; - if (Config->EnvMapMode) { - DWORD flags = 0; - if (Config->EnvMapMode == 1) flags |= 0x01; - if (Config->EnvMapMode == 2) flags |= (0x03 | 0x20); + if (vobjEnv == eCamRenderList.end()) vobjEnv = eCamRenderList.begin(); - if (vobjEnv == NULL) vobjEnv = vobjFirst; - - while (vobjEnv) { - if (vobjEnv->type == OBJTP_VESSEL && vobjEnv->apprad>8.0f) { - if (vobjEnv->vobj) { - vVessel *vVes = (vVessel *)vobjEnv->vobj; - if (vVes->RenderENVMap(pDevice, RenderCount, flags) == false) break; // Not yet done with this vessel - } + while (vobjEnv != eCamRenderList.end()) { + auto vV = (*vobjEnv); + if (vV->IsVisible()) { + if (vV->CamDist() < 10e3) { + // Note: World origin is changed here + if (vV->ProcessEnvMaps(pDevice, RenderCount, flags) == false) break; } - vobjEnv = vobjEnv->next; // Move to the next one } + vobjEnv++; } } - // ------------------------------------------------------------------------------------------------------- - // Render Irradiance Map For Vessels + // Render reflection cube maps and irradiance for interior parts // ------------------------------------------------------------------------------------------------------- - if (dwTurn == RENDERTURN_IRRADIANCE) { - - if (Config->EnvMapMode && Config->bIrradiance) { - DWORD flags = 0; - if (Config->EnvMapMode == 1) flags |= 0x01; - if (Config->EnvMapMode == 2) flags |= (0x03 | 0x20); - - if (vobjIrd == NULL) vobjIrd = vobjFirst; + if (Config->EnvMapMode) + { + if (vobjIP == eCamRenderList.end()) vobjIP = eCamRenderList.begin(); - while (vobjIrd) { - if (vobjIrd->type == OBJTP_VESSEL && vobjIrd->apprad>8.0f) { - if (vobjIrd->vobj) { - vVessel *vVes = (vVessel *)vobjIrd->vobj; - if (vVes->ProbeIrradiance(pDevice, RenderCount, flags) == false) break; // Not yet done with this vessel - } + while (vobjIP != eCamRenderList.end()) { + auto vV = (*vobjIP); + if (vV->IsVisible()) { + if (vV->CamDist() < 500.0) { + if (RenderVCProbes(vV) == false) break; // Note: World origin is changed here } - vobjIrd = vobjIrd->next; // Move to the next one } + vobjIP++; } } @@ -1379,22 +1437,15 @@ void Scene::RenderMainScene() // Init. camera setup and create a render list // --------------------------------------------------------------------------------------------- - VOBJREC* pv = NULL; - LPDIRECT3DTEXTURE9 pShdMap = NULL; - - UpdateCameraFromOrbiter(RENDERPASS_MAINSCENE); - UpdateCamVis(); + ResetOrigin(Camera.pos); // Restore world origin to main camera position RenderList.clear(); - for (pv = vobjFirst; pv; pv = pv->next) { - if (!pv->vobj->IsActive()) continue; - if (!pv->vobj->IsVisible()) continue; - if (pv->type == OBJTP_VESSEL) { - vVessel* vV = (vVessel*)pv->vobj; - RenderList.push_back(vV); - vV->bStencilShadow = true; - } + for (auto vV : Active) { + if (!vV->IsVisible()) continue; + vV->bStencilShadow = true; + vV->BakeLights(pBakeLights); + RenderList.push_back(vV); } float znear_for_vessels = ComputeNearClipPlane(); @@ -1416,13 +1467,13 @@ void Scene::RenderMainScene() RecallDefaultState(); // Clear buffers - HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 1.0f, 0L)); + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0L)); // Render vessels - for (auto* vVes : RenderList) vVes->Render(pDevice, false); + for (auto vVes : RenderList) vVes->Render(pDevice, false, nullptr); // Render Cockpit - if (oapiCameraInternal() && vFocus) vFocus->Render(pDevice, true); + if (oapiCameraInternal() && vFocus) vFocus->Render(pDevice, true, nullptr); gc->PopRenderTargets(); PopPass(); @@ -1433,6 +1484,7 @@ void Scene::RenderMainScene() // --------------------------------------------------------------------------------------------- ComputeLocalLightsVisibility(); + ActivateAllLocalLights(false); // ------------------------------------------------------------------------------------------------------- @@ -1499,14 +1551,10 @@ void Scene::RenderMainScene() // Create a caster list for shadow mapping // --------------------------------------------------------------------------------------------- - Casters.clear(); - - for (pv = vobjFirst; pv; pv = pv->next) { - if (!pv->vobj->IsActive()) continue; - if (pv->type == OBJTP_VESSEL) Casters.push_back((vVessel *)pv->vobj); - } - - Casters.sort(sort_vessels); + ObjectsToShadowMap.clear(); + for (auto v : Active) ObjectsToShadowMap.push_back(v); + + ObjectsToShadowMap.sort(sort_tgt_dist); @@ -1517,13 +1565,15 @@ void Scene::RenderMainScene() int shadow_lod = -1; float bouble_rad = 10.0f; // Terrain shadow mapping coverage - if (Config->ShadowMapMode >= 1 && Config->TerrainShadowing == 2) { - SmapRenderList.clear(); - SmapRenderList.push_back(vFocus); + if (Config->ShadowMapMode >= 1 && Config->TerrainShadowing == 2) + { + // Create a list of objects casting shadows on vFocus + Casters.clear(); + Casters.push_back(vFocus); - D3DXVECTOR3 ld = sunLight.Dir; - D3DXVECTOR3 pos = vFocus->GetBoundingSpherePosDX(); + FVECTOR3 ld = sunLight.Dir; + FVECTOR3 pos = vFocus->GetBoundingSpherePosDX(); float rad = vFocus->GetBoundingSphereRadius(); float frad = rad; @@ -1532,21 +1582,21 @@ void Scene::RenderMainScene() // What else should be included besides vFocus ? - for (auto v : Casters) + for (auto v : ObjectsToShadowMap) { if (v == vFocus) continue; if (v->HasShadow() == false) continue; - D3DXVECTOR3 bs_pos = v->GetBoundingSpherePosDX(); + FVECTOR3 bs_pos = v->GetBoundingSpherePosDX(); float bs_rad = v->GetBoundingSphereRadius(); if (bs_rad > 80.0) continue; - D3DXVECTOR3 bc = bs_pos - pos; - float z = D3DXVec3Dot(&ld, &bc); + FVECTOR3 bc = bs_pos - pos; + float z = dotp(ld, bc); if (fabs(z) > 1e3) continue; - D3DXVECTOR3 fbc = bc - ld * z; - float dst = D3DXVec3Length(&fbc); + FVECTOR3 fbc = bc - ld * z; + float dst = length(fbc); if (dst > 1e3) continue; float nrd = (rad + dst + bs_rad) * 0.5f; @@ -1559,7 +1609,7 @@ void Scene::RenderMainScene() if (bInclude) { v->bStencilShadow = false; - SmapRenderList.push_back(v); + Casters.push_back(v); if (nrd < rad) continue; @@ -1574,9 +1624,8 @@ void Scene::RenderMainScene() } } - shadow_lod = RenderShadowMap(pos, ld, rad, false, true); - - pShdMap = smap.pShadowMap; + SMapInput smi = { pos, ld, rad }; + RenderShadowMap(&smi, smEX, Casters, true); } @@ -1588,26 +1637,26 @@ void Scene::RenderMainScene() DWORD plnmode = *(DWORD*)gc->GetConfigParam(CFGPRM_PLANETARIUMFLAG); DWORD mkrmode = *(DWORD*)gc->GetConfigParam(CFGPRM_SURFMARKERFLAG); - for (DWORD i=0;iRenderZRange (&nplane, &fplane); // cam->SetFrustumLimits (nplane, fplane); // since we are not using z-buffers here, we can adjust the projection // matrix at will to make sure the object is within the viewing frustum - OBJHANDLE hObj = plist[i].vo->Object(); - bool isActive = plist[i].vo->IsActive(); + OBJHANDLE hObj = pl->Object(); + bool isActive = pl->IsActive(); - if (isActive) plist[i].vo->Render(pDevice); - else plist[i].vo->RenderDot(pDevice); + if (isActive) pl->Render(pDevice); + else pl->RenderDot(pDevice); D3D9Pad *pSketch = GetPooledSketchpad(SKETCHPAD_LABELS); if (pSketch) { - if (isActive) plist[i].vo->RenderVectors(pDevice, pSketch); + if (isActive) pl->RenderVectors(pDevice, pSketch); if (mkrmode & MKR_ENABLE) { @@ -1627,7 +1676,7 @@ void Scene::RenderMainScene() if (label_format < 2 && (mkrmode & MKR_LMARK)) // user-defined planetary surface labels { double rad = oapiGetSize(hObj); - double apprad = rad / (plist[i].dist * tan(GetCameraAperture())); + double apprad = rad / (pl->CamDist() * tan(GetCameraAperture())); const GraphicsClient::LABELLIST *list; DWORD n, nlist; MATRIX3 prot; @@ -1741,19 +1790,19 @@ void Scene::RenderMainScene() m_celSphere->EnsureMarkerDrawingContext((oapi::Sketchpad**)&pSketch, 0, 0, m_celSphere->MarkerPen(6)); int fontidx = -1; - for (DWORD i = 0; i < nplanets; ++i) + for (auto pl : Planets) { - OBJHANDLE hObj = plist[i].vo->Object(); + OBJHANDLE hObj = pl->Object(); if (oapiGetObjectType(hObj) != OBJTP_PLANET) { continue; } if (!surfLabelsActive) { - static_cast( plist[i].vo )->ActivateLabels(true); + static_cast(pl)->ActivateLabels(true); } int label_format = *(int*)oapiGetObjectParam(hObj, OBJPRM_PLANET_LABELENGINE); if (label_format == 2) { - static_cast(plist[i].vo)->RenderLabels(pDevice, pSketch, label_font, &fontidx); + static_cast(pl)->RenderLabels(pDevice, pSketch, label_font, &fontidx); } } @@ -1779,79 +1828,48 @@ void Scene::RenderMainScene() D3D9Effect::UpdateEffectCamera(Camera.hObj_proxy); - auto RenderMarkers = RenderList; + D3D9Pad* pSketch = GetPooledSketchpad(SKETCHPAD_LABELS); + m_celSphere->EnsureMarkerDrawingContext((oapi::Sketchpad**)&pSketch, 0, m_celSphere->MarkerColor(0), m_celSphere->MarkerPen(0)); - // Render the vessels inside the shadows - // - if (Config->ShadowMapMode >= 1) { - - D3DXVECTOR3 ld = sunLight.Dir; - D3DXVECTOR3 pos = vFocus->GetBoundingSpherePosDX(); - float rad = vFocus->GetBoundingSphereRadius(); - - int shadow_lod = RenderShadowMap(pos, ld, rad); - - if (shadow_lod >= 0) { - - pShdMap = ptShmRT[shadow_lod]; - - auto it = RenderList.begin(); - while (it != RenderList.end()) { - if ((*it)->IsInsideShadows()) { - (*it)->Render(pDevice); - it = RenderList.erase(it); - } - else ++it; - } + // Render the vessels inside the shadows ( Phase 1 ) ================================ + // + if (Config->ShadowMapMode >= 1) + { + // Get shadowing params for vFocus + SMapInput smi; + if (vFocus->GetSMapRenderData(vVessel::SMI::Visual, 0, &smi)) { + pExtShdMap = RenderObjectsInShadow(&smi, RenderList, pSketch); } + else pExtShdMap = nullptr; } + // Render additional shadows ( Phase 2 ) ============================================= + // + if ((Config->ShadowMapMode >= 2) && (DebugControls::IsActive()==false)) + { + // Don't render more shadows if debug controls are open to keep pExtShdMap valid - if ((Config->ShadowMapMode >= 2) && (DebugControls::IsActive()==false)) { - // Don't render more shadows if debug controls are open + SMapInput smf; + vFocus->GetSMapRenderData(vVessel::SMI::Visual, 0, &smf); - std::list Intersect; + list RenderThese; - // Select the objects to shadow map + // Select objects to render with shadow map // - if (Config->ShadowMapMode >= 3) { - for (auto it = RenderList.begin(); it != RenderList.end(); ++it) { - if ((*it)->CamDist() < 1e3) Intersect.push_back((*it)); - } - } - else { - for (auto it = RenderList.begin(); it != RenderList.end(); ++it) { - if ((*it)->IntersectShadowTarget()) Intersect.push_back((*it)); - } - } + if (Config->ShadowMapMode >= 3) for (auto v : RenderList) if (v->CamDist() < 1e3) RenderThese.push_back(v); + else for (auto v : RenderList) if (v->IntersectShadowTarget(&smf)) RenderThese.push_back(v); + // Erase objects from render list + for (auto v : RenderThese) RenderList.remove(v); - while (!Intersect.empty()) { - - D3DXVECTOR3 ld = sunLight.Dir; - D3DXVECTOR3 pos = Intersect.front()->GetBoundingSpherePosDX(); - float rad = Intersect.front()->GetBoundingSphereRadius(); - - Intersect.pop_front(); - - int lod = RenderShadowMap(pos, ld, rad); - - if (lod >= 0) { - - // Render objects in shadow - auto it = RenderList.begin(); - - while (it != RenderList.end()) { - if ((*it)->IsInsideShadows()) { - (*it)->Render(pDevice); - Intersect.remove((*it)); - it = RenderList.erase(it); - } - else ++it; - } + while (RenderThese.size()) + { + SMapInput smi; + if (RenderThese.front()->GetSMapRenderData(vVessel::SMI::Visual, 0, &smi)) { + RenderObjectsInShadow(&smi, RenderThese, pSketch); } } } @@ -1859,14 +1877,13 @@ void Scene::RenderMainScene() // Render the remaining vessels those are not yet renderred // - smap.pShadowMap = NULL; + smEX->Clear(); while (RenderList.empty()==false) { RenderList.front()->Render(pDevice); RenderList.pop_front(); } - D3D9Pad* pSketch = GetPooledSketchpad(SKETCHPAD_LABELS); if (pSketch) { m_celSphere->EnsureMarkerDrawingContext((oapi::Sketchpad**)&pSketch, 0, m_celSphere->MarkerColor(0), m_celSphere->MarkerPen(0)); for (auto x : RenderMarkers) RenderVesselMarker(x, pSketch); @@ -1892,27 +1909,16 @@ void Scene::RenderMainScene() // render exhausts // - for (pv=vobjFirst; pv; pv=pv->next) { - if (!pv->vobj->IsActive() || !pv->vobj->IsVisible() || pv->vobj->GetMeshCount() < 1) continue; - OBJHANDLE hObj = pv->vobj->Object(); - if (oapiGetObjectType(hObj) == OBJTP_VESSEL) { - ((vVessel*)pv->vobj)->RenderExhaust(); - } + for (auto v : Active) { + if (!v->IsVisible() || v->GetMeshCount() < 1) continue; + v->RenderExhaust(); } - // render beacons + // render beacons and grapple points // - for (pv=vobjFirst; pv; pv=pv->next) { - if (!pv->vobj->IsActive()) continue; - pv->vobj->RenderBeacons(pDevice); - } - - // render grapple points - // - for (pv=vobjFirst; pv; pv=pv->next) { - if (!pv->vobj->IsActive()) continue; - pv->vobj->RenderGrapplePoints(pDevice); - } + for (auto v : Active) v->RenderBeacons(pDevice); + for (auto v : Active) v->RenderGrapplePoints(pDevice); + // render exhaust particle system // @@ -1935,7 +1941,7 @@ void Scene::RenderMainScene() pSketch->SetFont(pAxisFont); pSketch->SetTextAlign(Sketchpad::LEFT, Sketchpad::TOP); - for (pv=vobjFirst; pv; pv=pv->next) { + for (auto pv : Visuals) { if (!pv->vobj->IsActive()) continue; if (!pv->vobj->IsVisible()) continue; if (oapiCameraInternal() && vFocus==pv->vobj) continue; @@ -1947,35 +1953,67 @@ void Scene::RenderMainScene() } - - - // ------------------------------------------------------------------------------------------------------- // render the internal parts of the focus object in a separate render pass // ------------------------------------------------------------------------------------------------------- - if (oapiCameraInternal() && vFocus) { - - // switch cockpit lights on, external-only lights off - // - if (bLocalLight) { - ClearLocalLights(); - VESSEL *vessel = oapiGetFocusInterface(); - DWORD nemitter = vessel->LightEmitterCount(); - for (DWORD j = 0; j < nemitter; j++) { - const LightEmitter *em = vessel->GetLightEmitter(j); - if ((em->GetVisibility() == LightEmitter::VIS_COCKPIT) || (em->GetVisibility() == LightEmitter::VIS_ALWAYS)) - AddLocalLight(em, vFocus); - } - } + if (oapiCameraInternal() && vFocus && (oapiCockpitMode() == COCKPIT_VIRTUAL)) + { + // Activate local lights for interior + ClearLocalLights(); + ActivateLocalLights(vFocus, true); pDevice->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, 1.0f, 0L); // clear z-buffer + double znear = Config->VCNearPlane; + if (DebugControls::IsActive()) if (DebugControls::debugFlags & DBG_FLAGS_NEARCLIP) znear = 0.02; + if (znear<0.01) znear=0.01; if (znear>1.0) znear=1.0; + OBJHANDLE hFocus = oapiGetFocusObject(); SetCameraFrustumLimits(znear, oapiGetSize(hFocus)*2.0); - vFocus->Render(pDevice, true); + + if (DebugControls::IsActive()) { + if (DebugControls::debugFlags & DBG_FLAGS_RENDEREXT) { + // vFocus->Render(pDevice, false, nullptr); + } + } + + if (Config->ShadowMapMode >= 1) + { + cascfg[0].dist = 2.5f; + cascfg[0].size = 2.5f; + float sa = sin(GetCameraApertureCorner()); + + if (Config->VCCascadeCount == 2) { + cascfg[0].size = 4.0f; + cascfg[0].dist = 4.0f; + cascfg[1].size = 1.0f; + cascfg[1].dist = 1.0f; + } + + if (Config->VCCascadeCount == 3) { + cascfg[0].size = 12.0f; + cascfg[0].dist = 12.0f; + cascfg[1].size = 3.0f; + cascfg[1].dist = 3.0f; + cascfg[2].size = 1.0f; + cascfg[2].dist = 1.0f; //cascfg[2].size * (1.0f + sa) / (2.0f * sa); + } + + cascfg[1].dist = min(cascfg[0].dist, cascfg[1].dist); + cascfg[2].dist = min(cascfg[1].dist, cascfg[2].dist); + + //D3D9DebugLog("Aperture = %f, dist=%f", GetCameraApertureCorner() * 2.0 * 180.0 / PI, cascfg[2].dist- cascfg[2].size); + + FVECTOR3 ld = sunLight.Dir; + Casters.clear(); + RenderVCShadowMap(Camera.z, ld, Casters); + } + + // Render VC + vFocus->Render(pDevice, true, smVC); } pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); @@ -2006,8 +2044,8 @@ void Scene::RenderMainScene() psgBuffer[GBUF_BLUR]->GetDesc(&blur); psgBuffer[GBUF_COLOR]->GetDesc(&colr); - D3DXVECTOR2 scr = D3DXVECTOR2(1.0f / float(colr.Width), 1.0f / float(colr.Height)); - D3DXVECTOR2 sbf = D3DXVECTOR2(1.0f / float(blur.Width), 1.0f / float(blur.Height)); + FVECTOR2 scr = FVECTOR2(1.0f / float(colr.Width), 1.0f / float(colr.Height)); + FVECTOR2 sbf = FVECTOR2(1.0f / float(blur.Width), 1.0f / float(blur.Height)); if (pLightBlur->IsOK()) @@ -2020,7 +2058,7 @@ void Scene::RenderMainScene() // Grap a copy of a backbuffer pDevice->StretchRect(pOffscreenTarget, NULL, psgBuffer[GBUF_COLOR], NULL, D3DTEXF_POINT); - pLightBlur->SetFloat("vSB", &sbf, sizeof(D3DXVECTOR2)); + pLightBlur->SetFloat("vSB", &sbf, sizeof(FVECTOR2)); pLightBlur->SetBool("bBlendIn", false); pLightBlur->SetBool("bBlur", false); @@ -2087,7 +2125,7 @@ void Scene::RenderMainScene() if (pGDIOverlay->IsOK()) { gc->bGDIClear = true; // Must clear background before continuing drawing into overlay - D3DXCOLOR clr(0x4080F0); // RGB ColorKey + FVECTOR4 clr(0xFF4080F0ul); // ARGB ColorKey pGDIOverlay->SetTextureNative("tSrc", ptgBuffer[GBUF_GDI], IPF_POINT | IPF_CLAMP); pGDIOverlay->SetFloat("vColorKey", &clr, sizeof(clr)); pGDIOverlay->SetOutputNative(0, gc->GetBackBuffer()); @@ -2130,48 +2168,84 @@ void Scene::RenderMainScene() // if (bFreezeEnable) bFreeze = true; - + + bool bVC = oapiCameraInternal() && (oapiCockpitMode() == COCKPIT_VIRTUAL); + // ------------------------------------------------------------------------------------------------------- - // EnvMap Debugger TODO: Should be allowed to visualize other maps as well, not just index 0 + // EnvMap Debugger // ------------------------------------------------------------------------------------------------------- - if (DebugControls::IsActive()) { - - int sel = DebugControls::GetSelectedEnvMap(); + if (DebugControls::IsActive()) + { + auto sel = DebugControls::GetSelectedEnvMap(); + int probeid = DebugControls::GetProbeId(); + + EnvCamType tp = probeid < 0 ? EnvCamType::Exterior : EnvCamType::Interior; + if (probeid < 0) probeid = 0; switch (sel) { - case 1: case 2: case 3: case 4: - case 5: - VisualizeCubeMap(vFocus->GetEnvMap(ENVMAP_MAIN), sel - 1); + case DbgDisplay::Mirror: + case DbgDisplay::Blur1: + case DbgDisplay::Blur2: + case DbgDisplay::Blur3: + case DbgDisplay::Blur4: + { + int idx = int(sel) - int(DbgDisplay::Mirror); + auto ec = vFocus->GetEnvCam(tp, probeid); + auto pData = ec ? ec->pCube : nullptr; + if (pData) VisualizeCubeMap((LPDIRECT3DCUBETEXTURE9)pData, idx); + + /*else { + auto ec = vFocus->GetExteriorEnvMap(); + auto pData = ec ? ec->pCube : nullptr; + if (pData) if (pData->GetType() == D3DRTYPE_CUBETEXTURE) + VisualizeCubeMap((LPDIRECT3DCUBETEXTURE9)pData, idx); + }*/ break; - case 6: - VisualizeCubeMap(vFocus->GetIrradEnv(), 0); + } + + case DbgDisplay::smSS: + VisualizeShadowMap(smSS); break; - case 7: - VisualizeCubeMap(pIrradTemp, 0); + + case DbgDisplay::smVC: + VisualizeShadowMap(smVC); break; - case 8: - if (pShdMap) { - pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); - pSketch->CopyRectNative(pShdMap, NULL, 0, 0); - pSketch->EndDrawing(); - } + + case DbgDisplay::smEX: + VisualizeShadowMap(smEX); break; - case 9: - if (vFocus->GetIrradianceMap()) { + + case DbgDisplay::Irradiance: + { + auto ec = vFocus->GetEnvCam(tp, probeid); + auto pData = ec ? ec->pIrrad : nullptr; + if (pData) { pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); - pSketch->CopyRectNative(vFocus->GetIrradianceMap(), NULL, 0, 0); + pSketch->CopyRectNative(pData, NULL, 0, 0); pSketch->EndDrawing(); } + /*else { + auto ec = vFocus->GetExteriorEnvMap(); + auto pData = ec ? ec->pIrrad : nullptr; + if (pData) { + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + pSketch->CopyRectNative(pData, NULL, 0, 0); + pSketch->EndDrawing(); + } + }*/ break; - case 10: + } + + case DbgDisplay::GlowMask: if (ptgBuffer[GBUF_BLUR]) { pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); pSketch->CopyRectNative(ptgBuffer[GBUF_BLUR], NULL, 0, 0); pSketch->EndDrawing(); } break; - case 11: + + case DbgDisplay::ScreenDepth: if (ptgBuffer[GBUF_DEPTH]) { if (pVisDepth) { if (pVisDepth->IsOK()) { @@ -2183,7 +2257,8 @@ void Scene::RenderMainScene() } } break; - case 12: + + case DbgDisplay::Normals: if (ptgBuffer[GBUF_DEPTH]) { if (pVisDepth) { if (pVisDepth->IsOK()) { @@ -2195,22 +2270,35 @@ void Scene::RenderMainScene() } } break; - case 13: + + case DbgDisplay::LightVisbil: if (pLocalResults) { pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); pSketch->SetBlendState(Sketchpad::BlendState::FILTER_POINT); - pSketch->StretchRectNative(pLocalResults, NULL, ptr(_RECT(0, 0, viewW, 10))); + pSketch->StretchRectNative(pLocalResults, NULL, &(_RECT(0, 0, viewW, 10))); pSketch->SetBlendState(Sketchpad::BlendState::FILTER_LINEAR); pSketch->EndDrawing(); } break; - case 14: + + case DbgDisplay::BakedLightMap: + { + LPDIRECT3DTEXTURE9 pTex = DebugControls::GetCombinedMap(); + if (pTex) { + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + pSketch->StretchRectNative(pTex, NULL, &(_RECT(0, 0, viewH, viewH))); + pSketch->EndDrawing(); + } + break; + } + + case DbgDisplay::Eclipse: if (Camera.vNear) { auto ptE = Camera.vNear->GetEclipse(); if (ptE) { pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); pSketch->SetBlendState(Sketchpad::BlendState::FILTER_POINT); - pSketch->StretchRectNative(ptE, NULL, ptr(_RECT(0, 0, viewW, 10))); + pSketch->StretchRectNative(ptE, NULL, &(_RECT(0, 0, viewW, 10))); pSketch->SetBlendState(Sketchpad::BlendState::FILTER_LINEAR); pSketch->EndDrawing(); } @@ -2233,19 +2321,19 @@ void Scene::RenderMainScene() D3DSURFACE_DESC desc; if (pTab) { pTab->GetLevelDesc(0, &desc); - pSketch->StretchRectNative(pTab, NULL, ptr(_R(0, y - desc.Height, desc.Width, y))); + pSketch->StretchRectNative(pTab, NULL, &(_R(0, y - desc.Height, desc.Width, y))); y -= (desc.Height + 5); } pTab = vP->GetScatterTable(MIE_LAND); if (pTab) { pTab->GetLevelDesc(0, &desc); - pSketch->StretchRectNative(pTab, NULL, ptr(_R(0, y - desc.Height, desc.Width, y))); + pSketch->StretchRectNative(pTab, NULL, &(_R(0, y - desc.Height, desc.Width, y))); y -= (desc.Height + 5); } pTab = vP->GetScatterTable(ATN_LAND); if (pTab) { pTab->GetLevelDesc(0, &desc); - pSketch->StretchRectNative(pTab, NULL, ptr(_R(0, y - desc.Height, desc.Width, y))); + pSketch->StretchRectNative(pTab, NULL, &(_R(0, y - desc.Height, desc.Width, y))); y -= (desc.Height + 5); } for (int i=0;i<9;i++) @@ -2310,41 +2398,103 @@ void Scene::RenderMainScene() } +// =========================================================================================== +// +float Scene::GetLODLevel(SMapInput* smi) +{ + float dist = length(smi->pos); + float tanap = float(GetTanAp()); + float viewh = float(ViewH()); + float rsmax = viewh * smi->rad / (tanap * dist); + return log2f(float(Config->ShadowMapSize) / (rsmax * 1.5f)); +} + // =========================================================================================== // -void Scene::RenderVesselMarker(vVessel *vV, D3D9Pad *pSketch) +void Scene::CombineSMaps(SMapInput* a, SMapInput* b, SMapInput* out) { - DWORD mkrmode = *(DWORD*)gc->GetConfigParam(CFGPRM_SURFMARKERFLAG); - if ((mkrmode & (MKR_ENABLE | MKR_VMARK)) == (MKR_ENABLE | MKR_VMARK)) { - RenderObjectMarker(pSketch, vV->GlobalPos(), std::string(vV->GetName()), std::string(), 0, viewH / 80); - } + FVECTOR3 ab = b->pos - a->pos; + FVECTOR3 ap = a->pos; + FVECTOR3 bp = a->pos - b->ld * dotp(ab, b->ld); // Project 'b' to a plane of 'a' + + ab = bp - ap; + float le = length(ab); // a-b distance + FVECTOR3 uab = ab / le; // Unit vector from a to b + float arad = a->rad; // Store 'a'-rad just in case if out == a + + out->rad = (le + a->rad + b->rad) * 0.5f; + out->pos = ap - uab * arad + uab * out->rad; + out->ld = a->ld; // ld should be identical in a,b } // =========================================================================================== -// Lens flare code (SolarLiner) // -Scene::SUNVISPARAMS Scene::GetSunScreenVisualState() +LPDIRECT3DTEXTURE9 Scene::RenderObjectsInShadow(SMapInput* smi, list& rList, D3D9Pad* pSkp) { - SUNVISPARAMS result = SUNVISPARAMS(); + Casters.clear(); // Clear shadow casters to auto-generate the list + Shadowed.clear(); // Clear list of objects receiving shadow - VECTOR3 cam = GetCameraGPos(); - VECTOR3 sunGPos; - oapiGetGlobalPos(oapiGetGbodyByIndex(0), &sunGPos); - sunGPos -= cam; + int shadow_lod = RenderShadowMap(smi, smEX, Casters); - DWORD w, h; - oapiGetViewportSize(&w, &h); + if (shadow_lod >= 0) { - const LPD3DXMATRIX pVP = GetProjectionViewMatrix(); - D3DXVECTOR4 pos; - D3DXVECTOR4 sun = D3DXVECTOR4(float(sunGPos.x), float(sunGPos.y), float(sunGPos.z), 1.0f); - D3DXVec4Transform(&pos, &sun, pVP); - result.brightness = saturate(pos.z); + // Create a list of objects that can be shadowed in a defined shadow volume + for (auto x : rList) + if (x->Type() == OBJTP_VESSEL) + if (((vVessel*)x)->IsInsideShadows(smi)) Shadowed.push_back(x); - D3DXVECTOR2 scrPos = D3DXVECTOR2(pos.x, pos.y); - scrPos /= pos.w; + // Render objects in shadow + for (auto it : Shadowed) { + it->Render(pDevice, false, smEX); + if (pSkp) RenderVesselMarker(it, pSkp); + rList.remove(it); // Remove from a list of "objects needing rendering" + } + + // Get the map for debugging + return smEX->ptShmRT[shadow_lod]; + } + return nullptr; +} + + +// =========================================================================================== +// +void Scene::RenderVesselMarker(vVessel *vV, D3D9Pad *pSketch) +{ + DWORD mkrmode = *(DWORD*)gc->GetConfigParam(CFGPRM_SURFMARKERFLAG); + if ((mkrmode & (MKR_ENABLE | MKR_VMARK)) == (MKR_ENABLE | MKR_VMARK)) { + RenderObjectMarker(pSketch, vV->GlobalPos(), std::string(vV->GetName()), std::string(), 0, viewH / 80); + } +} + + +// =========================================================================================== +// Lens flare code (SolarLiner) +// +Scene::SUNVISPARAMS Scene::GetSunScreenVisualState() +{ + SUNVISPARAMS result = SUNVISPARAMS(); + + VECTOR3 cam = GetCameraGPos(); + VECTOR3 sunGPos; + oapiGetGlobalPos(oapiGetGbodyByIndex(0), &sunGPos); + sunGPos -= cam; + + DWORD w, h; + oapiGetViewportSize(&w, &h); + + FVECTOR4 pos; + const FMATRIX4* pVP = GetProjectionViewMatrix(); + FVECTOR4 sun = FVECTOR4(float(sunGPos.x), float(sunGPos.y), float(sunGPos.z), 1.0f); + + D3DMAT_Transform(&pos, &sun, pVP); + + result.brightness = saturate(pos.z); + + FVECTOR2 scrPos = FVECTOR2(pos.x, pos.y); + scrPos /= pos.w; scrPos *= 0.5f; scrPos.x *= w / h; @@ -2354,19 +2504,20 @@ Scene::SUNVISPARAMS Scene::GetSunScreenVisualState() short xpos = short((scrPos.x + 0.5f) * w); short ypos = short((1.0f - (scrPos.y + 0.5f)) * h); - D3D9Pick pick = PickScene(xpos, ypos); + PickProp prp = { NULL, 0.1f, false }; + D3D9Pick pick = PickScene(xpos, ypos, &prp); if (pick.pMesh != NULL) { DWORD matIndex = pick.pMesh->GetMeshGroupMaterialIdx(pick.group); D3D9MatExt material; pick.pMesh->GetMaterial(&material, matIndex); - D3DXCOLOR surfCol(material.Diffuse.x, material.Diffuse.y, material.Diffuse.z, material.Diffuse.w); + FVECTOR4 surfCol(material.Diffuse.x, material.Diffuse.y, material.Diffuse.z, material.Diffuse.w); result.visible = (surfCol.a != 1.0f); if (result.visible) { - D3DXCOLOR color = D3DXCOLOR(surfCol.r*surfCol.a, surfCol.g*surfCol.a, surfCol.b*surfCol.a, 1.0f); + FVECTOR4 color = FVECTOR4(surfCol.r*surfCol.a, surfCol.g*surfCol.a, surfCol.b*surfCol.a, 1.0f); color += GetSunDiffColor() * (1 - surfCol.a); result.color = color; @@ -2383,11 +2534,11 @@ Scene::SUNVISPARAMS Scene::GetSunScreenVisualState() // =========================================================================================== // Lens flare code (SolarLiner) // -D3DXCOLOR Scene::GetSunDiffColor() +FVECTOR4 Scene::GetSunDiffColor() { vPlanet *vP = Camera.vProxy; - D3DXVECTOR3 _one(1, 1, 1); + FVECTOR3 _one(1, 1, 1); VECTOR3 GS, GP, GO; oapiCameraGlobalPos(&GO); @@ -2403,7 +2554,7 @@ D3DXCOLOR Scene::GetSunDiffColor() float pwr = 1.0f; - if (hP == hS) return GetSun()->Color; + if (hP == hS) return _F4(GetSun()->Color, 1.0f); double r = length(P); double pres = 1000.0; @@ -2445,9 +2596,9 @@ D3DXCOLOR Scene::GetSunDiffColor() if (alt>10e3f) al = aalt / k; else al = 0.173f; - D3DXVECTOR3 lcol(1, 1, 1); - //D3DXVECTOR3 r0 = _one - D3DXVECTOR3(0.65f, 0.75f, 1.0f) * disp; - D3DXVECTOR3 r0 = _one - D3DXVECTOR3(1.15f, 1.65f, 2.35f) * disp; + FVECTOR3 lcol(1, 1, 1); + //FVECTOR3 r0 = _one - FVECTOR3(0.65f, 0.75f, 1.0f) * disp; + FVECTOR3 r0 = _one - FVECTOR3(1.15f, 1.65f, 2.35f) * disp; if (atm) { float x = sqrt(saturate(h / al)); @@ -2458,111 +2609,327 @@ D3DXCOLOR Scene::GetSunDiffColor() lcol = r0 * saturate((h + rs) / (2.0f*rs)); } - return D3DXCOLOR(lcol.x, lcol.y, lcol.z, 1); + return FVECTOR4(lcol.x, lcol.y, lcol.z, 1); } // =========================================================================================== // -int Scene::RenderShadowMap(D3DXVECTOR3 &pos, D3DXVECTOR3 &ld, float rad, bool bInternal, bool bListExists) +int Scene::RenderShadowMap(SMapInput* smi, SHADOWMAP *sm, std::list& Casters, bool bInterior) { - rad *= 1.02f; + assert(sm->tp != SHADOWMAP::sMapType::Cascaded); - smap.pos = pos; - smap.ld = ld; - smap.rad = rad; + float rad = smi->rad * 1.1f; + sm->pos = smi->pos; + sm->ld = smi->ld; + sm->rad = rad; + sm->cascades = 1; + sm->Center[0] = { 0, 0 }; + sm->Subrect[0] = _F4( 0, 0, 1, 1 ); + sm->SubrectTF[0] = _F4( 0, 0, 1, 1 ); + + float dst = length(smi->pos); float mnd = 1e16f; float mxd = -1e16f; float rsmax = 0.0f; float tanap = float(GetTanAp()); float viewh = float(ViewH()); + bool bExists = Casters.size() != 0; + D3DSURFACE_DESC desc; - if (!bListExists) { - - // If the list doesn't exists then create it... - SmapRenderList.clear(); - + if (!bExists) + { // browse through vessels to find shadowers -------------------------- // - for (VOBJREC *pv = vobjFirst; pv; pv = pv->next) { + for (auto pv : Visuals) { if (pv->type != OBJTP_VESSEL) continue; vVessel *vV = (vVessel *)pv->vobj; if (!vV->IsActive()) continue; - if (vV->IntersectShadowVolume()) { - SmapRenderList.push_back(vV); - vV->GetMinMaxLightDist(&mnd, &mxd); + if (vV->IntersectShadowVolume(smi)) { + Casters.push_back(vV); + vV->GetMinMaxLightDist(smi, &mnd, &mxd); } } + } + else for (auto vV : Casters) vV->GetMinMaxLightDist(smi, &mnd, &mxd); + + + if (Casters.size() == 0) return -1; // The list is empty, Nothing to render + + // Compute shadow lod + rsmax = viewh * rad / (tanap * dst); + + sm->depth = (mxd + rad); + + FMATRIX4 mProj, mView; + D3DMAT_OrthoOffCenterRH(&mProj, -rad, rad, rad, -rad, -rad, sm->depth); + + sm->dist = mxd; + + FVECTOR3 lp = smi->pos - smi->ld * sm->dist; + FVECTOR3 pos = smi->pos; + + D3DMAT_LookAtRH(&mView, &lp, &pos, &FVECTOR3(0, 1, 0)); + oapiMatrixMultiply(&sm->mLVP, &mView, &mProj); + + sm->mVP[0] = sm->mLVP; + + WORD Pass = RENDERPASS_SHADOWMAP; + + if (sm->tp == SHADOWMAP::sMapType::SingleLod) // Manual LOD selection + { + Pass = RENDERPASS_VC_SHADOWMAP; + sm->psShmRT[0]->GetDesc(&desc); + sm->lod = 0; + sm->size = desc.Width; + auto psDS = GetDepthStencilMatch(sm->psShmRT[0]); + gc->PushRenderTarget(sm->psShmRT[0], psDS, Pass); + } + else // Automatic LOD selection + { + int lod = round(log2f(float(Config->ShadowMapSize) / (rsmax * 1.5f))); + lod = min(lod, SHM_LOD_COUNT - 1); + lod = max(lod, 0); + sm->psShmRT[lod]->GetDesc(&desc); + sm->lod = lod; + sm->size = desc.Width; + gc->PushRenderTarget(sm->psShmRT[lod], psShmDS[lod], Pass); + } + + sm->bValid = true; + + // Clear the viewport + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0L)); + - // Compute shadow lod - rsmax = viewh * rad / (tanap * D3DXVec3Length(&pos)); + // render the vessel objects -------------------------------- + // + BeginPass(Pass); + + for (auto vV : Casters) { + if (vV == vFocus) { + if (bInterior) vV->Render(pDevice, sm, vVessel::Render::VC); + else vV->Render(pDevice, sm, 0); + } + else vV->Render(pDevice, sm, 0); } + PopPass(); + gc->PopRenderTargets(); + + return sm->lod; +} - if (SmapRenderList.size() == 0) return -1; // The list is empty, Nothing to render - if (bListExists) { +// =========================================================================================== +// +int Scene::RenderVCShadowMap(FVECTOR3& cdir, FVECTOR3& ld, std::list& Casters) +{ + SHADOWMAP* sm = smVC; - for (auto vV : SmapRenderList) + FVECTOR3 pos = cdir * cascfg[0].dist; + float rad = cascfg[0].size; + sm->pos = pos; + sm->ld = ld; + sm->rad = rad; + + float mnd = 1e16f; + float mxd = -1e16f; + float rsmax = 0.0f; + float tanap = float(GetTanAp()); + float viewh = float(ViewH()); + + bool bExists = Casters.size() != 0; + + if (!bExists) { + + // browse through vessels to find shadowers -------------------------- + // + for (auto pv : Visuals) { + if (pv->type != OBJTP_VESSEL) continue; + vVessel* vV = (vVessel*)pv->vobj; + if (!vV->IsActive()) continue; + if (vV->IntersectShadowVolume(sm)) { + Casters.push_back(vV); + vV->GetMinMaxLightDist(sm, &mnd, &mxd); + } + } + } + else { + for (auto vV : Casters) { // Get shadow min-max distances - vV->GetMinMaxLightDist(&mnd, &mxd); + vV->GetMinMaxLightDist(sm, &mnd, &mxd); + } + } + + if (Casters.size() == 0) return -1; // The list is empty, Nothing to render + + // Compute shadow lod + rsmax = viewh * rad / (tanap * length(pos)); - // Compute shadow lod - D3DXVECTOR3 bspos = vV->GetBoundingSpherePosDX(); - float rs = viewh * rad / (tanap * D3DXVec3Length(&bspos)); - if (rs > rsmax) rsmax = rs; + sm->dist = mxd; + sm->lod = 0; + sm->size = Config->ShadowMapSize; + sm->cascades = Config->VCCascadeCount; + sm->depth = (mxd + vFocus->GetSize()); + sm->bValid = true; + + FMATRIX4 mProj, mView; + + for (int i = 0; i < sm->cascades; i++) + { + // Compute mLVP needed to render this cascade. + + pos = cdir * cascfg[i].dist; + rad = cascfg[i].size; + + // Project pos to a shadow plane + FVECTOR3 sp = pos - ld * dotp(pos, ld); + FVECTOR3 ep = sp - ld * sm->dist; + D3DMAT_OrthoOffCenterRH(&mProj, -rad, rad, rad, -rad, 0, sm->depth); + D3DMAT_LookAtRH(&mView, &ep, &sp, &(FVECTOR3(0, 1, 0))); + oapiMatrixMultiply(&sm->mVP[i], &mView, &mProj); + + FVECTOR3 xy = oapiTransformCoord(&pos, &sm->mVP[0]); + float s = 0.5f * rad / sm->rad; + + xy.y = -xy.y; + xy = (xy + 1.0f) * 0.5f; + + // Cascade's center, subrect and subrect-transform within the main level (i.e '0') + // All cascades share the same near and far plane and exists inside the previous cascade. + sm->Center[i] = { xy.x, xy.y }; + float l = xy.x - s; float t = xy.y - s; float r = xy.x + s; float b = xy.y + s; + sm->Subrect[i] = FVECTOR4(l, t, r, b); + sm->SubrectTF[i] = FVECTOR4(-l, -t, 1.0f / (r - l), 1.0f / (b - t)); + sm->SubPx[i] = rad / float(sm->size); + +#ifdef CASCADE_DEBUG + D3D9DebugLog("Cascade %i pos=[%f, %f]", i, xy.x, xy.y); + D3D9DebugLog("Cascade %i rect=[%f, %f, %f, %f]", i, xy.x-r, xy.y-r, xy.x+r, xy.y+r); +#endif + gc->PushRenderTarget(sm->psShmRT[i], psShmDS[0], RENDERPASS_SHADOWMAP); + + // Clear the viewport + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0L)); + + // render the vessel objects -------------------------------- + // + BeginPass(RENDERPASS_VC_SHADOWMAP); + + // NOTE: sm.mLVP must containg the projection needed for rendering of the map + sm->mLVP = sm->mVP[i]; + + for (auto &a : Casters) + { + a->Render(pDevice, sm, vVessel::Render::VC | vVessel::Render::IP); } + + PopPass(); + + gc->PopRenderTargets(); } - smap.depth = (mxd - mnd) + 10.0f; + // sm.mLVP must point to cascade '0' by default. That's where subrects lie + sm->mLVP = sm->mVP[0]; - D3DXMatrixOrthoOffCenterRH(&smap.mProj, -rad, rad, rad, -rad, 50.0f, 50.0f + smap.depth); + return 0; +} - smap.dist = mnd - 55.0f; - D3DXVECTOR3 lp = pos + ld * smap.dist; +// =========================================================================================== +// Return 'true' if rendering for the vessel is done +// +bool Scene::RenderVCProbes(vVessel* vV) +{ + // Render only focus if in debug mode + if (DebugControls::IsActive() && vV != vFocus) return true; - D3DXMatrixLookAtRH(&smap.mView, &lp, &pos, ptr(D3DXVECTOR3(0, 1, 0))); - D3DXMatrixMultiply(&smap.mViewProj, &smap.mView, &smap.mProj); + if (InteriorCams.empty()) // Get Interior cams and render shadow map + { + if (vV->GetInteriorCams(&InteriorCams)) + { + } + else return true; - float lod = log2f(float(Config->ShadowMapSize) / (rsmax*1.5f)); + itIC = InteriorCams.cbegin(); + } - smap.lod = min(int(round(lod)), SHM_LOD_COUNT - 1); - smap.lod = max(smap.lod, 0); - smap.size = Config->ShadowMapSize >> smap.lod; - gc->PushRenderTarget(psShmRT[smap.lod], psShmDS[smap.lod], RENDERPASS_SHADOWMAP); + if (itIC != InteriorCams.cend()) + { + auto ec = *itIC; + VECTOR3 gp; + vV->GetInterface()->Local2Global(ec->lPos._V(), gp); + ResetOrigin(gp); - // Clear the viewport - HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0L)); + SMapInput smi; + if (vV->GetSMapRenderData(vVessel::SMI::VC, 0, &smi)) + { + if (ec->bRendered) { + vV->RenderInteriorENVMap(pDevice, ec, smSS); + } + else { + Casters.clear(); + if (RenderShadowMap(&smi, smSS, Casters, true) < 0) { + LogErr("Failed to render shadow map for stage-set"); + } + else { + vV->RenderInteriorENVMap(pDevice, ec, smSS); + return false; + } + } + } - // render the vessel objects -------------------------------- - // - BeginPass(RENDERPASS_SHADOWMAP); + itIC++; // Pick next camera + } - while(SmapRenderList.size()>0) { - SmapRenderList.front()->Render(pDevice, bInternal); - SmapRenderList.pop_front(); + if (itIC == InteriorCams.cend()) // All done for this vessel + { + InteriorCams.clear(); + return true; } - PopPass(); + return false; // Not done yet, Continue this vessel next frame +} - gc->PopRenderTargets(); - smap.pShadowMap = ptShmRT[smap.lod]; +// =========================================================================================== +// +LPDIRECT3DSURFACE9 Scene::GetDepthStencilMatch(LPDIRECT3DSURFACE9 pRef) +{ + D3DSURFACE_DESC desc; + pRef->GetDesc(&desc); + for (int i = 0; i < (SHM_LOD_COUNT - 1); i++) { + D3DSURFACE_DESC d; psShmDS[i]->GetDesc(&d); + if ((d.Width == desc.Width) && (d.Height == desc.Height)) return psShmDS[i]; + } + return nullptr; +} + - return smap.lod; +// =========================================================================================== +// +LPDIRECT3DSURFACE9 Scene::GetDepthStencil(DWORD size) +{ + for (int i = 0; i < (SHM_LOD_COUNT - 1); i++) { + D3DSURFACE_DESC d; psShmDS[i]->GetDesc(&d); + if (d.Width == size) return psShmDS[i]; + } + return nullptr; } // =========================================================================================== // -void Scene::RenderSecondaryScene(std::set &RndList, std::set &LightsList, DWORD flags) +void Scene::RenderSecondaryScene(std::set &RndList, + std::set &LightsList, DWORD flags, + const LPDIRECT3DCUBETEXTURE9 pCT, SHADOWMAP *sm) { _TRACE; RenderFlags = flags; @@ -2570,61 +2937,59 @@ void Scene::RenderSecondaryScene(std::set &RndList, std::set // Process Local Light Sources ------------------------------------- // And toggle external lights on // - if (bLocalLight) { - - ClearLocalLights(); - + if (bLocalLight) + { + ClearLocalLights(); for (auto vVes : RndList) { - if (!vVes->IsActive()) continue; - VESSEL *vessel = vVes->GetInterface(); - DWORD nemitter = vessel->LightEmitterCount(); - for (DWORD j = 0; j < nemitter; j++) { - const LightEmitter *em = vessel->GetLightEmitter(j); - if ((em->GetVisibility() == LightEmitter::VIS_EXTERNAL) || (em->GetVisibility() == LightEmitter::VIS_ALWAYS)) AddLocalLight(em, vVes); - } + ActivateLocalLights(vVes, (flags && SCN_VC) != 0); } - for (auto vVes : LightsList) { if (!vVes->IsActive()) continue; if (RndList.count(vVes)) continue; // Already included skip it - VESSEL *vessel = vVes->GetInterface(); - DWORD nemitter = vessel->LightEmitterCount(); - for (DWORD j = 0; j < nemitter; j++) { - const LightEmitter *em = vessel->GetLightEmitter(j); - if ((em->GetVisibility() == LightEmitter::VIS_EXTERNAL) || (em->GetVisibility() == LightEmitter::VIS_ALWAYS)) AddLocalLight(em, vVes); - } + ActivateLocalLights(vVes, (flags && SCN_VC) != 0); } } D3D9Effect::UpdateEffectCamera(GetCameraProxyBody()); // Clear the viewport - HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0xFF000000, 1.0f, 0L)); + if ((flags & SCN_NOCLEAR) == 0) { + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFF000000, 1.0f, 0L)); + } + + // render stage around the scene ---------------------------- + // + if (flags & SCN_STAGE) { + RenderStage(pCT); + } + + // Don't combine these + if ((flags & SCN_STAGE) && (flags & SCN_PLANETS)) assert(false); + if ((flags & SCN_VC) && (flags & SCN_ALLEXT)) assert(false); - // render planets ------------------------------------------- // - if (flags & 0x01) { - for (DWORD i = 0; iIsActive(); - if (isActive) plist[i].vo->Render(pDevice); - else plist[i].vo->RenderDot(pDevice); + if (flags & SCN_PLANETS) { + for (auto pl : Planets) { + bool isActive = pl->IsActive(); + if (isActive) pl->Render(pDevice); + else pl->RenderDot(pDevice); } } // render the vessel objects -------------------------------- // - if (flags & 0x02) { + if (flags & SCN_VESSELS) { for (auto vVes : RndList) { if (!vVes->IsActive()) continue; if (!vVes->IsVisible()) continue; - vVes->Render(pDevice); + vVes->Render(pDevice, nullptr, 0); } } // render exhausts ------------------------------------------- // - if (flags & 0x04) { + if (flags & SCN_EXHAUST) { for (auto vVes : RndList) { if (!vVes->IsActive()) continue; if (!vVes->IsVisible()) continue; @@ -2634,7 +2999,7 @@ void Scene::RenderSecondaryScene(std::set &RndList, std::set // render beacons ------------------------------------------- // - if (flags & 0x08) { + if (flags & SCN_BEACONS) { for (auto vVes : RndList) { if (!vVes->IsActive()) continue; vVes->RenderBeacons(pDevice); @@ -2642,14 +3007,66 @@ void Scene::RenderSecondaryScene(std::set &RndList, std::set } // render exhaust particle system ---------------------------- - if (flags & 0x10) { + if (flags & SCN_PARTICLES) { for (DWORD n = 0; n < nstream; n++) pstream[n]->Render(pDevice); } + if (flags & SCN_VC) + { + vFocus->Render(pDevice, smSS, vVessel::Render::VC | vVessel::Render::IP); + } + // Flags 0x20 = BaseStructures } +// =========================================================================================== +// +void Scene::RenderStageSet(const LPDIRECT3DCUBETEXTURE9 pCT) +{ + ClearLocalLights(); + ActivateLocalLights(vFocus, true); + + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFF000000, 1.0f, 0L)); + + RenderStage(pCT); + + vFocus->Render(pDevice, smSS, vVessel::Render::VC | vVessel::Render::IP); +} + + +// =========================================================================================== +// Rernder a stage/set to contain some meshes. +// +void Scene::RenderStage(LPDIRECT3DCUBETEXTURE9 pCT) +{ + if (!pRenderStage) return; + + struct { + FMATRIX4 mVP; + FMATRIX4 mW; + } ShaderData; + + ShaderData.mVP = Camera.mProjView; + oapiMatrixIdentity(&ShaderData.mW); + ShaderData.mW.m11 = 1e3f; + ShaderData.mW.m22 = 1e3f; + ShaderData.mW.m33 = 1e3f; + + HR(pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE)); + + pRenderStage->ClearTextures(); + pRenderStage->SetPSConstants("cbPS", &ShaderData, sizeof(ShaderData)); + pRenderStage->SetVSConstants("cbPS", &ShaderData, sizeof(ShaderData)); + pRenderStage->SetTexture("tTex", pCT, IPF_CLAMP | IPF_LINEAR); + pRenderStage->Setup(nullptr, false, 0); + pRenderStage->UpdateTextures(); + + ((D3D9Mesh*)dmCubeMesh)->RenderGroup(0); + + pRenderStage->DetachTextures(); +} + // =========================================================================================== // bool Scene::RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DCUBETEXTURE9 pSrc) @@ -2686,11 +3103,11 @@ bool Scene::RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DCUBETEXTURE9 pSrc } - D3DXVECTOR3 dir, up, cp; + FVECTOR3 dir, up, cp; LPDIRECT3DSURFACE9 pSrf = NULL; LPDIRECT3DSURFACE9 pTmp = NULL; - // Create clurred mip sub-levels + // Copy Src to Temp // for (DWORD i = 0; i < 6; i++) { pSrc->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &pSrf); @@ -2701,7 +3118,7 @@ bool Scene::RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DCUBETEXTURE9 pSrc } - // Create clurred mip sub-levels + // Create blurred mip sub-levels // for (int mip = 1; mip < 5; mip++) { @@ -2712,15 +3129,14 @@ bool Scene::RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DCUBETEXTURE9 pSrc for (DWORD i = 0; i < 6; i++) { EnvMapDirection(i, &dir, &up); - D3DXVec3Cross(&cp, &up, &dir); - D3DXVec3Normalize(&cp, &cp); - + cp = unit(crossp(up, dir)); + pSrc->GetCubeMapSurface(D3DCUBEMAP_FACES(i), mip, &pSrf); pBlur->SetOutputNative(0, pSrf); - pBlur->SetFloat("vDir", &dir, sizeof(D3DXVECTOR3)); - pBlur->SetFloat("vUp", &up, sizeof(D3DXVECTOR3)); - pBlur->SetFloat("vCp", &cp, sizeof(D3DXVECTOR3)); + pBlur->SetFloat("vDir", &dir, sizeof(FVECTOR3)); + pBlur->SetFloat("vUp", &up, sizeof(FVECTOR3)); + pBlur->SetFloat("vCp", &cp, sizeof(FVECTOR3)); if (!pBlur->Execute(true)) { LogErr("pBlur Execute Failed"); @@ -2738,15 +3154,14 @@ bool Scene::RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DCUBETEXTURE9 pSrc for (DWORD i = 0; i < 6; i++) { EnvMapDirection(i, &dir, &up); - D3DXVec3Cross(&cp, &up, &dir); - D3DXVec3Normalize(&cp, &cp); - + cp = unit(crossp(up, dir)); + pSrc->GetCubeMapSurface(D3DCUBEMAP_FACES(i), mip, &pSrf); pBlur->SetOutputNative(0, pSrf); - pBlur->SetFloat("vDir", &dir, sizeof(D3DXVECTOR3)); - pBlur->SetFloat("vUp", &up, sizeof(D3DXVECTOR3)); - pBlur->SetFloat("vCp", &cp, sizeof(D3DXVECTOR3)); + pBlur->SetFloat("vDir", &dir, sizeof(FVECTOR3)); + pBlur->SetFloat("vUp", &up, sizeof(FVECTOR3)); + pBlur->SetFloat("vCp", &cp, sizeof(FVECTOR3)); if (!pBlur->Execute(true)) { LogErr("pBlur Execute Failed"); @@ -2765,86 +3180,133 @@ bool Scene::RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DCUBETEXTURE9 pSrc // =========================================================================================== // -bool Scene::IntegrateIrradiance(vVessel *vV, LPDIRECT3DCUBETEXTURE9 pSrc, LPDIRECT3DTEXTURE9 pOut) +bool Scene::RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DTEXTURE9 pSrc) { + bool bQuality = true; + if (!pSrc) return false; - if (!pIrradiance) { - pIrradiance = new ImageProcessing(pDevice, "Modules/D3D9Client/IrradianceInteg.hlsl", "PSPreInteg"); - pIrradiance->CompileShader("PSInteg"); - pIrradiance->CompileShader("PSPostBlur"); + if (!pBlur2D) { + pBlur2D = new ImageProcessing(pDev, "Modules/D3D9Client/EnvMapBlur.hlsl", "PS2DBlur"); } - if (!pIrradiance->IsOK()) { - LogErr("pIrradiance is not OK"); + if (!pBlur2D->IsOK()) { + LogErr("pBlur is not OK"); return false; } - if (!pIrradDS) { - LogErr("pIrradDS doesn't exists"); + if (!pEnvDS) { + LogErr("EnvDepthStencil doesn't exists"); return false; } - LPDIRECT3DSURFACE9 pOuts = NULL; - HR(pOut->GetSurfaceLevel(0, &pOuts)); - - D3DSURFACE_DESC desc, desc_out; - pIrradDS->GetDesc(&desc); - pOuts->GetDesc(&desc_out); - - if (!pIrradTemp) { - if (D3DXCreateCubeTexture(pDevice, 16, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pIrradTemp) != S_OK) { - LogErr("Failed to create irradiance temp"); - return false; - } - if (D3DXCreateTexture(pDevice, 128, 128, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pIrradTemp2) != S_OK) { - LogErr("Failed to create irradiance temp"); - return false; - } - if (D3DXCreateTexture(pDevice, desc_out.Width, desc_out.Height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pIrradTemp3) != S_OK) { - LogErr("Failed to create irradiance temp"); - return false; - } + D3DSURFACE_DESC desc; + pEnvDS->GetDesc(&desc); + DWORD width = min((UINT)512, desc.Width); + DWORD height = min((UINT)512, desc.Height); + + if (!pBlrTemp2D[0]) { + if (D3DXCreateTexture(pDev, width >> 0, height >> 0, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[0]) != S_OK) return false; + if (D3DXCreateTexture(pDev, width >> 1, height >> 1, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[1]) != S_OK) return false; + if (D3DXCreateTexture(pDev, width >> 2, height >> 2, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[2]) != S_OK) return false; + if (D3DXCreateTexture(pDev, width >> 3, height >> 3, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[3]) != S_OK) return false; + if (D3DXCreateTexture(pDev, width >> 4, height >> 4, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[4]) != S_OK) return false; } - D3DXVECTOR3 nr, up, cp; LPDIRECT3DSURFACE9 pSrf = NULL; - LPDIRECT3DSURFACE9 pTgt = NULL; - LPDIRECT3DSURFACE9 pTmp2 = NULL; - LPDIRECT3DSURFACE9 pTmp3 = NULL; - - HR(pIrradTemp2->GetSurfaceLevel(0, &pTmp2)); - HR(pIrradTemp3->GetSurfaceLevel(0, &pTmp3)); + LPDIRECT3DSURFACE9 pTmp = NULL; + // Copy Src to Temp + // + pSrc->GetSurfaceLevel(0, &pSrf); + pBlrTemp2D[0]->GetSurfaceLevel(0, &pTmp); + pDevice->StretchRect(pSrf, NULL, pTmp, NULL, D3DTEXF_POINT); + SAFE_RELEASE(pSrf); + SAFE_RELEASE(pTmp); - // --------------------------------------------------------------------- - // Pre-Integrate Irradiance Cube + // Create blurred mip sub-levels // - pIrradiance->Activate("PSPreInteg"); - pIrradiance->SetFloat("fD", ptr(D3DXVECTOR2(1.0f / float(desc.Width), 1.0f / float(desc.Height))), sizeof(D3DXVECTOR2)); + for (int mip = 1; mip < 5; mip++) { - for (DWORD i = 0; i < 6; i++) - { - pSrc->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &pSrf); - pIrradTemp->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &pTgt); - - pDevice->StretchRect(pSrf, NULL, pTmp2, NULL, D3DTEXF_POINT); + pBlur2D->SetFloat("fD", (4.0f / float(256 >> (mip - 1)))); + pBlur2D->SetBool("bDir", false); + pBlur2D->SetTextureNative("tTex", pBlrTemp2D[mip - 1], IPF_LINEAR); - pIrradiance->SetOutputNative(0, pTgt); - pIrradiance->SetTextureNative("tSrc", pIrradTemp2, IPF_POINT | IPF_CLAMP); + pSrc->GetSurfaceLevel(mip, &pSrf); + pBlur2D->SetOutputNative(0, pSrf); + + if (!pBlur2D->Execute(true)) { + LogErr("pBlur2D Execute Failed"); + return false; + } - if (!pIrradiance->Execute(true)) { - LogErr("pIrradiance Execute Failed"); + pBlrTemp2D[mip - 1]->GetSurfaceLevel(0, &pTmp); + pDevice->StretchRect(pSrf, NULL, pTmp, NULL, D3DTEXF_POINT); + SAFE_RELEASE(pTmp); + + pBlur2D->SetBool("bDir", true); + pSrc->GetSurfaceLevel(mip, &pSrf); + pBlur2D->SetOutputNative(0, pSrf); + + if (!pBlur2D->Execute(true)) { + LogErr("pBlur Execute Failed"); return false; } - SAFE_RELEASE(pTgt); + pBlrTemp2D[mip]->GetSurfaceLevel(0, &pTmp); + pDevice->StretchRect(pSrf, NULL, pTmp, NULL, D3DTEXF_POINT); + SAFE_RELEASE(pTmp); SAFE_RELEASE(pSrf); } - + return true; +} + +// =========================================================================================== +// +bool Scene::IntegrateIrradiance(vVessel *vV, ENVCAMREC *ec, bool bInterior) +{ + if (!ec) return false; + + LPDIRECT3DCUBETEXTURE9 pSrc = ec->pCube; + LPDIRECT3DTEXTURE9 pOut = ec->pIrrad; + + if (!pIrradiance) { + pIrradiance = new ImageProcessing(pDevice, "Modules/D3D9Client/IrradianceInteg.hlsl", "PSInteg"); + pIrradiance->CompileShader("PSPostBlur"); + pIrradiance->CompileShader("PSPostBlurIntr"); + } + + if (!pIrradiance->IsOK()) { + LogErr("pIrradiance is not OK"); + return false; + } + + D3DSURFACE_DESC desc; + LPDIRECT3DSURFACE9 pOuts = NULL; + + HR(pOut->GetSurfaceLevel(0, &pOuts)); + HR(pOuts->GetDesc(&desc)); + + + FVECTOR3 nr, up, cp; + LPDIRECT3DSURFACE9 pTmp = NULL; + UINT size = bInterior ? 3 : 1; + UINT W = desc.Width * size; + UINT H = desc.Height * size; + + + if (!pIrradTemp) { + if (D3DXCreateTexture(pDevice, W, H, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pIrradTemp) != S_OK) { + LogErr("Failed to create irradiance temp"); + return false; + } + } + + HR(pIrradTemp->GetSurfaceLevel(0, &pTmp)); + // --------------------------------------------------------------------- // Main Integration // @@ -2852,13 +3314,16 @@ bool Scene::IntegrateIrradiance(vVessel *vV, LPDIRECT3DCUBETEXTURE9 pSrc, LPDIRE float Glow = float(Config->PlanetGlow); + if (bInterior) Glow = 4.0f; + pIrradiance->Activate("PSInteg"); - pIrradiance->SetOutputNative(0, pTmp3); - pIrradiance->SetTextureNative("tCube", pIrradTemp, IPF_LINEAR); + pIrradiance->SetOutputNative(0, pTmp); + pIrradiance->SetTextureNative("tCube", pSrc, IPF_LINEAR); + pIrradiance->SetTextureNative("tRandom", ptRandom, IPF_POINT); pIrradiance->SetFloat("Kernel", IKernel, sizeof(IKernel)); - pIrradiance->SetFloat("vNr", &nr, sizeof(D3DXVECTOR3)); - pIrradiance->SetFloat("vUp", &up, sizeof(D3DXVECTOR3)); - pIrradiance->SetFloat("vCp", &cp, sizeof(D3DXVECTOR3)); + pIrradiance->SetFloat("vNr", &nr, sizeof(FVECTOR3)); + pIrradiance->SetFloat("vUp", &up, sizeof(FVECTOR3)); + pIrradiance->SetFloat("vCp", &cp, sizeof(FVECTOR3)); pIrradiance->SetFloat("fIntensity", &Glow, sizeof(float)); pIrradiance->SetBool("bUp", false); pIrradiance->SetTemplate(0.5f, 1.0f, 0.0f, 0.0f); @@ -2876,21 +3341,23 @@ bool Scene::IntegrateIrradiance(vVessel *vV, LPDIRECT3DCUBETEXTURE9 pSrc, LPDIRE return false; } + SAFE_RELEASE(pTmp); + // --------------------------------------------------------------------- // Post Blur // - pIrradiance->Activate("PSPostBlur"); - pIrradiance->SetFloat("fD", ptr(D3DXVECTOR2(1.0f / float(desc_out.Width), 1.0f / float(desc_out.Height))), sizeof(D3DXVECTOR2)); + if (bInterior) pIrradiance->Activate("PSPostBlurIntr"); + else pIrradiance->Activate("PSPostBlur"); + + pIrradiance->SetFloat("fD", &(FVECTOR2(1.0f / float(W), 1.0f / float(H))), sizeof(FVECTOR2)); pIrradiance->SetOutputNative(0, pOuts); - pIrradiance->SetTextureNative("tSrc", pIrradTemp3, IPF_POINT | IPF_WRAP); + pIrradiance->SetTextureNative("tSrc", pIrradTemp, IPF_POINT | IPF_WRAP); if (!pIrradiance->Execute(true)) { LogErr("pIrradiance Execute Failed"); return false; } - SAFE_RELEASE(pTmp2); - SAFE_RELEASE(pTmp3); SAFE_RELEASE(pOuts); return true; @@ -2902,8 +3369,7 @@ bool Scene::IntegrateIrradiance(vVessel *vV, LPDIRECT3DCUBETEXTURE9 pSrc, LPDIRE // void Scene::ClearOmitFlags() { - VOBJREC *pv = NULL; - for (pv=vobjFirst; pv; pv=pv->next) pv->vobj->bOmit = false; + for (auto pv : Visuals) pv->vobj->bOmit = false; } @@ -2949,6 +3415,64 @@ void Scene::VisualizeCubeMap(LPDIRECT3DCUBETEXTURE9 pCube, int mip) } } +// =========================================================================================== +// +const SHADOWMAP* Scene::GetSMapData(ShdPackage tp) const +{ + if (tp == ShdPackage::Main) return smEX; + if (tp == ShdPackage::VC) return smVC; + if (tp == ShdPackage::Stage) return smSS; + return nullptr; +} + + +// =========================================================================================== +// Optional parameters 'sm' or 'pTex' not both +// +void Scene::VisualizeShadowMap(SHADOWMAP *sm) +{ + LPDIRECT3DTEXTURE9 pTex = sm ? sm->Map(0) : nullptr; + if (!pTex) return; + + D3DSURFACE_DESC desc; + pTex->GetLevelDesc(0, &desc); + + DWORD s = 0; + float w, h; + if (desc.Height > viewH) w = h = float(s = viewH); + else w = h = float(s = desc.Height); + + D3D9Pad *pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + + if (sm) { + for (int i = 0; i < sm->cascades; i++) + { + int l = int(w * sm->Subrect[i].x); + int t = int(h * sm->Subrect[i].y); + int r = int(w * sm->Subrect[i].z); + int b = int(h * sm->Subrect[i].w); + + RECT tgt = { l, t, r, b }; + auto map = sm->Map(i); + if (map) { + pSketch->SetRenderParam(Sketchpad::RenderParam::PRM_GAMMA, &(FVECTOR4(0.25f, 0.25f, 0.25f, 1.0f))); + pSketch->StretchRectNative(map, nullptr, &tgt); + pSketch->SetRenderParam(Sketchpad::RenderParam::PRM_GAMMA, nullptr); + } + pSketch->QuickBrush(0); + pSketch->QuickPen(0xFFFF00FF); + pSketch->Rectangle(l, t, r, b); + } + } + else if (pTex) { + RECT tgt = { 0, 0, long(s), long(s) }; + pSketch->SetRenderParam(Sketchpad::RenderParam::PRM_GAMMA, &(FVECTOR4(0.25f, 0.25f, 0.25f, 1.0f))); + pSketch->StretchRectNative(pTex, nullptr, &tgt); + pSketch->SetRenderParam(Sketchpad::RenderParam::PRM_GAMMA, nullptr); + } + pSketch->EndDrawing(); +} + // =========================================================================================== @@ -2959,8 +3483,7 @@ void Scene::RenderVesselShadows (OBJHANDLE hPlanet, float depth) const if (hPlanet != oapiCameraProxyGbody()) return; // render vessel shadows - VOBJREC *pv; - for (pv = vobjFirst; pv; pv = pv->next) { + for (auto pv : Visuals) { if (!pv->vobj->IsActive()) continue; if (oapiGetObjectType(pv->vobj->Object()) == OBJTP_VESSEL) ((vVessel*)(pv->vobj))->RenderGroundShadow(pDevice, hPlanet, depth); @@ -2981,36 +3504,35 @@ void Scene::RenderMesh(DEVMESHHANDLE hMesh, const oapi::FMATRIX4 *pWorld) { D3D9Mesh *pMesh = (D3D9Mesh *)hMesh; - const Scene::SHADOWMAPPARAM *shd = GetSMapData(); + float s = float(smEX->size); + float sr = 2.0f * smEX->rad / s; - float s = float(shd->size); - float sr = 2.0f * shd->rad / s; + HR(D3D9Effect::FX->SetMatrix(D3D9Effect::eLVP, _DX(smEX->mLVP))); - HR(D3D9Effect::FX->SetMatrix(D3D9Effect::eLVP, &shd->mViewProj)); - - if (shd->pShadowMap) { - HR(D3D9Effect::FX->SetTexture(D3D9Effect::eShadowMap, shd->pShadowMap)); - HR(D3D9Effect::FX->SetVector(D3D9Effect::eSHD, ptr(D3DXVECTOR4(sr, 1.0f / s, float(oapiRand()), 1.0f / shd->depth)))); + if (smEX->IsValid()) { + pMesh->SetShadows(smEX); + HR(D3D9Effect::FX->SetVector(D3D9Effect::eSHD, _DX(FVECTOR4(sr, 1.0f / s, float(oapiRand()), 1.0f / smEX->depth)))); HR(D3D9Effect::FX->SetBool(D3D9Effect::eShadowToggle, true)); } else { + pMesh->SetShadows(NULL); HR(D3D9Effect::FX->SetBool(D3D9Effect::eShadowToggle, false)); } pMesh->SetSunLight(&sunLight); - pMesh->RenderSimplified(LPD3DXMATRIX(pWorld)); + pMesh->RenderSimplified(pWorld); } // =========================================================================================== // -bool Scene::WorldToScreenSpace(const VECTOR3 &wpos, oapi::IVECTOR2 *pt, D3DXMATRIX *pVP, float clip) +bool Scene::WorldToScreenSpace(const VECTOR3 &wpos, oapi::IVECTOR2 *pt, FMATRIX4 *pVP, float clip) { - D3DXVECTOR4 homog; - D3DXVECTOR3 pos(float(wpos.x), float(wpos.y), float(wpos.z)); + FVECTOR4 homog; + FVECTOR3 pos(float(wpos.x), float(wpos.y), float(wpos.z)); - if (pVP) D3DXVec3Transform(&homog, &pos, pVP); - else D3DXVec3Transform(&homog, &pos, GetProjectionViewMatrix()); + if (pVP) D3DMAT_Transform(&homog, &_F4(pos, 1.0f), pVP); + else D3DMAT_Transform(&homog, &_F4(pos, 1.0f), GetProjectionViewMatrix()); if (homog.w < 0.0f) return false; @@ -3035,13 +3557,13 @@ bool Scene::WorldToScreenSpace(const VECTOR3 &wpos, oapi::IVECTOR2 *pt, D3DXMATR // =========================================================================================== // -bool Scene::WorldToScreenSpace2(const VECTOR3& wpos, oapi::FVECTOR2* pt, D3DXMATRIX* pVP, float clip) +bool Scene::WorldToScreenSpace2(const VECTOR3& wpos, oapi::FVECTOR2* pt, FMATRIX4* pVP, float clip) { - D3DXVECTOR4 homog; - D3DXVECTOR3 pos(float(wpos.x), float(wpos.y), float(wpos.z)); + FVECTOR4 homog; + FVECTOR3 pos(float(wpos.x), float(wpos.y), float(wpos.z)); - if (pVP) D3DXVec3Transform(&homog, &pos, pVP); - else D3DXVec3Transform(&homog, &pos, GetProjectionViewMatrix()); + if (pVP) D3DMAT_Transform(&homog, &_F4(pos, 1.0f), pVP); + else D3DMAT_Transform(&homog, &_F4(pos, 1.0f), GetProjectionViewMatrix()); homog.x /= homog.w; homog.y /= homog.w; @@ -3071,21 +3593,6 @@ void Scene::RenderObjectMarker(oapi::Sketchpad *pSkp, const VECTOR3 &gpos, const m_celSphere->RenderMarker(pSkp, dp, label1, label2, mode, scale); } -// =========================================================================================== -// -void Scene::NewVessel(OBJHANDLE hVessel) -{ - CheckVisual(hVessel); -} - -// =========================================================================================== -// -void Scene::DeleteVessel(OBJHANDLE hVessel) -{ - VOBJREC *pv = FindVisual(hVessel); - if (pv) DelVisualRec(pv); -} - // =========================================================================================== // void Scene::AddParticleStream (class D3D9ParticleStream *_pstream) @@ -3235,12 +3742,12 @@ DWORD Scene::GetRenderPass() const // =========================================================================================== // -D3DXVECTOR3 Scene::GetPickingRay(short xpos, short ypos) +FVECTOR3 Scene::GetPickingRay(short xpos, short ypos) { float x = 2.0f*float(xpos) / float(ViewW()) - 1.0f; float y = 2.0f*float(ypos) / float(ViewH()) - 1.0f; - D3DXVECTOR3 vPick = Camera.x * (x / Camera.mProj._11) + Camera.y * (-y / Camera.mProj._22) + Camera.z; - D3DXVec3Normalize(&vPick, &vPick); + FVECTOR3 vPick = Camera.x * (x / Camera.mProj.m11) + Camera.y * (-y / Camera.mProj.m22) + Camera.z; + normalize(vPick); return vPick; } @@ -3251,16 +3758,16 @@ TILEPICK Scene::PickSurface(short xpos, short ypos) TILEPICK tp; memset(&tp, 0, sizeof(TILEPICK)); vPlanet *vp = GetCameraProxyVisual(); if (!vp) return tp; - D3DXVECTOR3 vRay = GetPickingRay(xpos, ypos); + FVECTOR3 vRay = GetPickingRay(xpos, ypos); vp->PickSurface(vRay, &tp); return tp; } // =========================================================================================== // -D3D9Pick Scene::PickScene(short xpos, short ypos) +D3D9Pick Scene::PickScene(short xpos, short ypos, const PickProp *p) { - D3DXVECTOR3 vPick = GetPickingRay(xpos, ypos); + FVECTOR3 vPick = GetPickingRay(xpos, ypos); D3D9Pick result; result.dist = 1e30f; @@ -3269,7 +3776,7 @@ D3D9Pick Scene::PickScene(short xpos, short ypos) result.group = -1; result.idx = -1; - for (VOBJREC *pv=vobjFirst; pv; pv=pv->next) { + for (auto pv : Visuals) { if (pv->type!=OBJTP_VESSEL) continue; if (!pv->vobj->IsActive()) continue; @@ -3279,7 +3786,7 @@ D3D9Pick Scene::PickScene(short xpos, short ypos) double cd = vVes->CamDist(); if (cd<5e3 && cd>1e-3) { - D3D9Pick pick = vVes->Pick(&vPick); + D3D9Pick pick = vVes->Pick(&vPick, p); if (pick.pMesh) if (pick.distPick(pW, NULL, ptr(GetPickingRay(xpos, ypos))); + PickProp prp = { NULL, 0.1f, false }; + return pMesh->Pick(pW, NULL, &(GetPickingRay(xpos, ypos)), &prp); } // =========================================================================================== // -void Scene::GetAdjProjViewMatrix(LPD3DXMATRIX pMP, float znear, float zfar) +void Scene::GetAdjProjViewMatrix(FMATRIX4* pMP, float znear, float zfar) { float tanap = tan(Camera.aperture); - ZeroMemory(pMP, sizeof(D3DXMATRIX)); - pMP->_11 = (Camera.aspect / tanap); - pMP->_22 = (1.0f / tanap); - pMP->_43 = (pMP->_33 = zfar / (zfar - znear)) * (-znear); - pMP->_34 = 1.0f; + ZeroMemory(pMP, sizeof(FMATRIX4)); + pMP->m11 = (Camera.aspect / tanap); + pMP->m22 = (1.0f / tanap); + pMP->m43 = (pMP->m33 = zfar / (zfar - znear)) * (-znear); + pMP->m34 = 1.0f; } // =========================================================================================== @@ -3314,13 +3822,15 @@ void Scene::SetCameraAperture(float ap, float as) Camera.aspect = as; float tanap = tan(ap); + float cor = sqrt(tanap * tanap + tanap * tanap * as * as); + Camera.corner = atan(cor); - ZeroMemory(&Camera.mProj, sizeof(D3DXMATRIX)); + ZeroMemory(&Camera.mProj, sizeof(FMATRIX4)); - Camera.mProj._11 = (as / tanap); - Camera.mProj._22 = (1.0f / tanap); - Camera.mProj._43 = (Camera.mProj._33 = Camera.farplane / (Camera.farplane-Camera.nearplane)) * (-Camera.nearplane); - Camera.mProj._34 = 1.0f; + Camera.mProj.m11 = (as / tanap); + Camera.mProj.m22 = (1.0f / tanap); + Camera.mProj.m43 = (Camera.mProj.m33 = Camera.farplane / (Camera.farplane-Camera.nearplane)) * (-Camera.nearplane); + Camera.mProj.m34 = 1.0f; float x = tanap / as; float y = tanap; @@ -3333,7 +3843,7 @@ void Scene::SetCameraAperture(float ap, float as) Camera.vhf = 1.0f / cos(ap); Camera.vwf = Camera.vhf/as; - D3DXMatrixMultiply(&Camera.mProjView, &Camera.mView, &Camera.mProj); + oapiMatrixMultiply(&Camera.mProjView, &Camera.mView, &Camera.mProj); D3D9Effect::SetViewProjMatrix(&Camera.mProjView); } @@ -3365,7 +3875,7 @@ bool Scene::CameraPan(VECTOR3 pan, double speed) VECTOR3 pos; oapiGetGlobalPos(hTgt, &pos); Camera.pos = pos + Camera.relpos; - Camera.pos += Camera.dir * (pan.z*speed) + _VD3DX(Camera.x) * (pan.x*speed) + _VD3DX(Camera.y) * (pan.y*speed); + Camera.pos += Camera.dir * (pan.z*speed) + _V(Camera.x) * (pan.x*speed) + _V(Camera.y) * (pan.y*speed); Camera.relpos = Camera.pos - pos; return true; } @@ -3406,7 +3916,7 @@ bool Scene::UpdateCameraFromOrbiter(DWORD dwPass) oapiCameraGlobalDir(&Camera.dir); oapiCameraRotationMatrix(&grot); - D3DXMatrixIdentity(&Camera.mView); + oapiMatrixIdentity(&Camera.mView); D3DMAT_SetRotation(&Camera.mView, &grot); // note: in render space, the camera is always placed at the origin, @@ -3417,30 +3927,50 @@ bool Scene::UpdateCameraFromOrbiter(DWORD dwPass) // translational transformation between orbiter global coordinates // and render coordinates. - for (VOBJREC *pv = vobjFirst; pv; pv = pv->next) pv->vobj->Update(true); + for (auto pv : Visuals) pv->vobj->Update(true); return SetupInternalCamera(&Camera.mView, NULL, oapiCameraAperture(), double(viewH)/double(viewW)); } +// =========================================================================================== +// +void Scene::CameraOffOrigin90(FMATRIX4 *mView, FVECTOR3 pos) +{ + Camera.mView = *mView; + Camera.x = FVECTOR3(Camera.mView.m11, Camera.mView.m21, Camera.mView.m31); + Camera.y = FVECTOR3(Camera.mView.m12, Camera.mView.m22, Camera.mView.m32); + Camera.z = FVECTOR3(Camera.mView.m13, Camera.mView.m23, Camera.mView.m33); + + auto x = FVECTOR3(Camera.mView.m11, Camera.mView.m12, Camera.mView.m13); + auto y = FVECTOR3(Camera.mView.m21, Camera.mView.m22, Camera.mView.m23); + auto z = FVECTOR3(Camera.mView.m31, Camera.mView.m32, Camera.mView.m33); + + Camera.mView.m14 = -dotp(x, pos); + Camera.mView.m24 = -dotp(y, pos); + Camera.mView.m34 = -dotp(z, pos); + + SetCameraAperture(0.7853981634, 1.0f); +} + // =========================================================================================== // -bool Scene::SetupInternalCamera(D3DXMATRIX *mNew, VECTOR3 *gpos, double apr, double asp) +bool Scene::SetupInternalCamera(FMATRIX4 *mNew, VECTOR3 *gpos, double apr, double asp) { // Update camera orientation if a new matrix is provided if (mNew) { Camera.mView = *mNew; - Camera.x = D3DXVECTOR3(Camera.mView._11, Camera.mView._21, Camera.mView._31); - Camera.y = D3DXVECTOR3(Camera.mView._12, Camera.mView._22, Camera.mView._32); - Camera.z = D3DXVECTOR3(Camera.mView._13, Camera.mView._23, Camera.mView._33); - Camera.dir = _VD3DX(Camera.z); + Camera.x = FVECTOR3(Camera.mView.m11, Camera.mView.m21, Camera.mView.m31); + Camera.y = FVECTOR3(Camera.mView.m12, Camera.mView.m22, Camera.mView.m32); + Camera.z = FVECTOR3(Camera.mView.m13, Camera.mView.m23, Camera.mView.m33); + Camera.dir = _V(Camera.z); } if (gpos) Camera.pos = *gpos; - Camera.upos = D3DXVEC(unit(Camera.pos)); + Camera.upos = _F(unit(Camera.pos)); // find a logical reference body Camera.hObj_proxy = oapiCameraProxyGbody(); @@ -3472,7 +4002,7 @@ bool Scene::SetupInternalCamera(D3DXMATRIX *mNew, VECTOR3 *gpos, double apr, dou if (l > closest) { closest = l; Camera.hGravRef = hB; - } + } } /*if (Camera.hNear) { @@ -3483,17 +4013,16 @@ bool Scene::SetupInternalCamera(D3DXMATRIX *mNew, VECTOR3 *gpos, double apr, dou // find the visual if (oapiGetObjectType(Camera.hObj_proxy) == OBJTP_PLANET) - Camera.vProxy = (vPlanet*)GetVisObject(Camera.hObj_proxy); + Camera.vProxy = (vPlanet *)GetVisObject(Camera.hObj_proxy); else Camera.vProxy = nullptr; if (oapiGetObjectType(Camera.hNear) == OBJTP_PLANET) - Camera.vNear = (vPlanet*)GetVisObject(Camera.hNear); + Camera.vNear = (vPlanet *)GetVisObject(Camera.hNear); else Camera.vNear = nullptr; Camera.vGravRef = GetVisObject(Camera.hGravRef); - - // Something is very wrong... abort... + if (Camera.hGravRef == NULL || Camera.hObj_proxy == NULL || Camera.hNear == NULL) { assert(false); return false; } @@ -3510,9 +4039,11 @@ bool Scene::SetupInternalCamera(D3DXMATRIX *mNew, VECTOR3 *gpos, double apr, dou Camera.alt_proxy = dist(Camera.pos, pos) - oapiGetSize(Camera.hObj_proxy); - if (Camera.vProxy->Type() == OBJTP_PLANET) - rad = oapiSurfaceElevation(Camera.hObj_proxy, Camera.lng, Camera.lat); - + if (Camera.vProxy) { + if (Camera.vProxy->Type() == OBJTP_PLANET) + rad = oapiSurfaceElevation(Camera.hObj_proxy, Camera.lng, Camera.lat); + } + Camera.elev = Camera.alt_proxy - rad; // Camera altitude over the proxy @@ -3523,13 +4054,21 @@ bool Scene::SetupInternalCamera(D3DXMATRIX *mNew, VECTOR3 *gpos, double apr, dou SetCameraAperture(float(apr), float(asp)); // Finally update world matrices from all visuals - // - if (gpos) for (VOBJREC *pv = vobjFirst; pv; pv = pv->next) pv->vobj->ReOrigin(Camera.pos); + ResetOrigin(Camera.pos); return true; } +// =========================================================================================== +// +void Scene::ResetOrigin(VECTOR3 pos) +{ + // Update world matrices from all visuals + if (origin.x != pos.x || origin.y != pos.y || origin.z != pos.z) + for (auto pv : Visuals) pv->vobj->ReOrigin(pos); + origin = pos; +} // =========================================================================================== @@ -3618,31 +4157,29 @@ void Scene::RenderCustomCameraView(CAMREC *cCur) pVes->GetRotationMatrix(grot); pVes->Local2Global(cCur->vPosition, gpos); - D3DXMATRIX mEnv, mGlo; + FMATRIX4 mEnv, mGlo; - D3DXMatrixIdentity(&mGlo); + oapiMatrixIdentity(&mGlo); D3DMAT_SetRotation(&mGlo, &grot); - D3DXMatrixIdentity(&mEnv); + oapiMatrixIdentity(&mEnv); D3DMAT_SetRotation(&mEnv, &cCur->mRotation); - D3DXMatrixMultiply(&mEnv, &mGlo, &mEnv); + oapiMatrixMultiply(&mEnv, &mGlo, &mEnv); PushCamera(); SetCameraFrustumLimits(0.1, 2e7); SetupInternalCamera(&mEnv, &gpos, cCur->dAperture, double(h)/double(w)); - - VOBJREC *pv = NULL; std::set List; std::set Lights; - for (pv = vobjFirst; pv; pv = pv->next) if (pv->type == OBJTP_VESSEL) List.insert((vVessel *)pv->vobj); + for (auto pv : Visuals) if (pv->type == OBJTP_VESSEL) List.insert((vVessel *)pv->vobj); BeginPass(RENDERPASS_CUSTOMCAM); gc->PushRenderTarget(pSrf, pDSs, RENDERPASS_CUSTOMCAM); - RenderSecondaryScene(List, Lights, 0xFF); + RenderSecondaryScene(List, Lights, SCN_ALLEXT); gc->PopRenderTargets(); @@ -3664,10 +4201,10 @@ void Scene::RenderGlares() static SMVERTEX Vertex[4] = { {-1, -1, 0, 0, 0}, {-1, 1, 0, 0, 1}, {1, 1, 0, 1, 1}, {1, -1, 0, 1, 0} }; static WORD cIndex[6] = { 0, 2, 1, 0, 3, 2 }; D3DSURFACE_DESC desc; FVECTOR2 pt; - struct { D3DXMATRIX mVP; float4 Pos, Color; float GPUId, Alpha, Blend; } Const; + struct { FMATRIX4 mVP; float4 Pos, Color; float GPUId, Alpha, Blend; } Const; - Const.Color = FVECTOR4(1, 1, 1, 1); - D3DXMatrixOrthoOffCenterLH(&Const.mVP, 0.0f, (float)viewW, (float)viewH, 0.0f, 0.0f, 1.0f); + Const.Color = _F4(1, 1, 1, 1); + D3DMAT_OrthoOffCenterLH(&Const.mVP, 0.0f, (float)viewW, (float)viewH, 0.0f, 0.0f, 1.0f); pLocalResultsSL->GetDesc(&desc); pRenderGlares->ClearTextures(); @@ -3688,7 +4225,7 @@ void Scene::RenderGlares() if (WorldToScreenSpace2(pos, &pt)) { float cis = 1.0f, glare = float(Config->GFXGlare) * saturate(8.0 * AU / sdst); - FVECTOR4 clr = FVECTOR4(1, 1, 1, 1); + FVECTOR4 clr = _F4(1, 1, 1, 1); vPlanet* vp = GetCameraNearVisual(); @@ -3697,7 +4234,7 @@ void Scene::RenderGlares() VECTOR3 crp = vp->CameraPos(); clr = vp->SunLightColor(crp, 2.0); cis = CameraInSpace(); - glare *= pow(clr.MaxRGB(), 0.33f) * cis; + glare *= pow(MaxRGB(clr), 0.33f) * cis; } float cd = length(pt - FVECTOR2(viewW, viewH) * 0.5f) / float(viewW); // Glare distance from a screen center @@ -3706,7 +4243,7 @@ void Scene::RenderGlares() Const.GPUId = 0.5f / float(desc.Width); Const.Pos = FVECTOR4(pt.x, pt.y, size, size); - Const.Color.rgb = clr.rgb / (clr.MaxRGB() + 0.0001f); + Const.Color.rgb = clr.rgb / (MaxRGB(clr) + 0.0001f); Const.Alpha = alpha * 2.0f; Const.Blend = sqrt(cis); @@ -3746,7 +4283,7 @@ void Scene::RenderGlares() // =========================================================================================== // -bool Scene::IsVisibleInCamera(const D3DXVECTOR3 *pCnt, float radius) +bool Scene::IsVisibleInCamera(const FVECTOR3 *pCnt, float radius) { float z = Camera.z.x*pCnt->x + Camera.z.y*pCnt->y + Camera.z.z*pCnt->z; if (z<(-radius)) return false; @@ -3764,9 +4301,10 @@ bool Scene::IsVisibleInCamera(const D3DXVECTOR3 *pCnt, float radius) // bool Scene::CameraDirection2Viewport(const VECTOR3 &dir, int &x, int &y) { - D3DXVECTOR3 homog; - D3DXVECTOR3 idir = D3DXVECTOR3( -float(dir.x), -float(dir.y), -float(dir.z) ); - D3DMAT_VectorMatrixMultiply(&homog, &idir, &Camera.mProjView); + + FVECTOR3 idir = FVECTOR3( -float(dir.x), -float(dir.y), -float(dir.z) ); + FVECTOR3 homog = oapiTransformCoord(&idir, &Camera.mProjView); + if (homog.x >= -1.0f && homog.y <= 1.0f && homog.z >= 0.0) { if (std::hypot(homog.x, homog.y) < 1e-6) { x = viewW / 2, y = viewH / 2; @@ -3829,12 +4367,3 @@ void Scene::D3D9TechInit(LPDIRECT3DDEVICE9 pDev, const char *folder) D3D9CelestialSphere::D3D9TechInit(FX); } - -// =========================================================================================== -// -int distcomp (const void *arg1, const void *arg2) -{ - double d1 = ((PList*)arg1)->dist; - double d2 = ((PList*)arg2)->dist; - return (d1 > d2 ? -1 : d1 < d2 ? 1 : 0); -} diff --git a/OVP/D3D9Client/Scene.h b/OVP/D3D9Client/Scene.h index 917f38b9a..e5d79a831 100644 --- a/OVP/D3D9Client/Scene.h +++ b/OVP/D3D9Client/Scene.h @@ -28,6 +28,7 @@ class vObject; class vPlanet; +class vVessel; class D3D9ParticleStream; class D3D9Text; class D3D9Pad; @@ -39,8 +40,6 @@ class D3D9Pad; #define GBUF_GDI 4 #define GBUF_COUNT 5 // Buffer count -#define SHM_LOD_COUNT 5 - #define TEX_NOISE 0 #define TEX_CLUT 1 #define TEX_COUNT 2 @@ -54,22 +53,38 @@ class D3D9Pad; #define RENDERPASS_SKETCHPAD 0x0006 #define RENDERPASS_MAINOVERLAY 0x0007 #define RENDERPASS_NORMAL_DEPTH 0x0008 +#define RENDERPASS_VC_SHADOWMAP 0x0009 +#define RENDERPASS_STAGESET 0x000A #define RESTORE ((LPDIRECT3DSURFACE9)(-1)) #define CURRENT ((LPDIRECT3DSURFACE9)(-2)) #define RENDERTURN_ENVCAM 0 #define RENDERTURN_CUSTOMCAM 1 -#define RENDERTURN_IRRADIANCE 2 -#define RENDERTURN_LAST 2 +#define RENDERTURN_LAST 1 #define SMAP_MODE_FOCUS 1 #define SMAP_MODE_SCENE 2 #define OBJTP_BUILDING 1000 +// Secundary scene render flags +#define SCN_PLANETS 0x1 +#define SCN_VESSELS 0x2 +#define SCN_EXHAUST 0x4 +#define SCN_BEACONS 0x8 +#define SCN_PARTICLES 0x10 +#define SCN_BASESTRUCT 0x20 +#define SCN_ALLEXT 0x3F ///< All exterior features +#define SCN_VC 0x40 ///< Virtual cockpit +#define SCN_STAGE 0x1000 ///< Render a stage around the world. Cude texture needed. +#define SCN_NOCLEAR 0x2000 ///< Do not clear render target + #define CAMERA(x) ((Scene::CAMREC*)x) + + + class Scene { friend class D3D9CelestialSphere; @@ -80,13 +95,13 @@ class Scene { vObject *vobj; // visual instance int type; float apprad; - VOBJREC *prev, *next; // previous and next list entry - } *vobjFirst, *vobjLast; // first and last list entry + }; public: FVECTOR3 vPickRay; + bool bStageSet = false; struct FRUSTUM { float znear; @@ -108,12 +123,22 @@ class Scene { void* pUser; }; + std::list InteriorCams; + std::list Planets; + std::list Visuals; + std::set RootList; + std::set Vessels; + std::set eCamRenderList; std::set CustomCams; - std::set::const_iterator camCurrent{}; + std::set::const_iterator camCurrent; + std::set::const_iterator vobjEnv, vobjIP; + std::list::const_iterator itIC; + // Camera frustum parameters ======================================================== // struct CAMERA { + float corner; // corner to center aperture [rad] float aperture; // aperture [rad] float aspect; // aspect ratio float nearplane; // frustum nearplane distance @@ -125,15 +150,15 @@ class Scene { VECTOR3 relpos; // Relative camera position (Used by Mesh Debugger) VECTOR3 dir; // Camera direction - D3DXVECTOR3 x; // Camera axis vector - D3DXVECTOR3 y; // Camera axis vector - D3DXVECTOR3 z; // Camera axis vector - D3DXVECTOR3 upos; // Camera position unit vector + FVECTOR3 x; // Camera axis vector + FVECTOR3 y; // Camera axis vector + FVECTOR3 z; // Camera axis vector + FVECTOR3 upos; // Camera position unit vector - D3DXMATRIX mView; // D3DX view matrix for current camera state - D3DXMATRIX mProj; // D3DX projection matrix for current camera state - D3DXMATRIX mProjView; // D3DX combined projection view matrix - D3DXMATRIX mProjViewInf; // D3DX combined projection view matrix, far plane at infinity + FMATRIX4 mView; // D3DX view matrix for current camera state + FMATRIX4 mProj; // D3DX projection matrix for current camera state + FMATRIX4 mProjView; // D3DX combined projection view matrix + FMATRIX4 mProjViewInf; // D3DX combined projection view matrix, far plane at infinity OBJHANDLE hTarget; // Current camera target, Mesh Debugger Related OBJHANDLE hObj_proxy; // closest celestial body @@ -155,21 +180,13 @@ class Scene { struct SUNVISPARAMS { float brightness; bool visible; - D3DXVECTOR2 position; - D3DXCOLOR color; + FVECTOR2 position; + FVECTOR4 color; }; - struct SHADOWMAPPARAM { - LPDIRECT3DTEXTURE9 pShadowMap; - D3DXMATRIX mProj, mView, mViewProj; - D3DXVECTOR3 pos; - D3DXVECTOR3 ld; - float rad; - float dist; - float depth; - int lod; - int size; - } smap; + SHADOWMAP* smEX = nullptr; // Exterior shadow map + SHADOWMAP* smVC = nullptr; // Virtual Cockpit shadow map + SHADOWMAP* smSS = nullptr; // Shadow map for rendering in a stage-set static void D3D9TechInit(LPDIRECT3DDEVICE9 pDev, const char *folder); @@ -187,7 +204,34 @@ class Scene { //inline const oapi::D3D9Client *GetClient() const { return gc; } inline oapi::D3D9Client *GetClient() const { return gc; } - void OnOptionChanged(int cat, int item); + + void clbkOnOptionChanged(int cat, int item); + void clbkScenarioChanged(OBJHANDLE hV, ScnChgEvent e); + void clbkInitialise(); + + /** + * \brief Update camera position, visuals, etc. + */ + void clbkUpdate(); + + /** + * \brief Render the whole main scene + */ + void clbkRenderMainScene(); + + /** + * \brief Create a visual for a new vessel if within visual range. + * \param hVessel vessel object handle + */ + void clbkNewVessel(OBJHANDLE hVessel); + + /** + * \brief Delete a vessel visual prior to destruction of the logical vessel. + * \param hVessel vessel object handle + */ + void clbkDeleteVessel(OBJHANDLE hVessel); + + const D3D9Sun *GetSun() const { return &sunLight; } const D3D9Light *GetLight(int index) const; @@ -204,12 +248,14 @@ class Scene { void BeginPass(DWORD dwPass); void PopPass(); + const SHADOWMAP* GetSMapData(ShdPackage tp) const; + inline DWORD GetStencilDepth() const { return stencilDepth; } - inline const SHADOWMAPPARAM * GetSMapData() const { return &smap; } + /** * \brief Get the ambient background colour */ - inline D3DCOLOR GetBgColour() const { return bg_rgba; } + inline DWORD GetBgColour() const { return bg_rgba; } /** * \brief Get the viewport dimension (width) @@ -222,18 +268,9 @@ class Scene { inline const DWORD ViewH() const { return viewH; } bool UpdateCamVis(); - void Initialise (); - - /** - * \brief Update camera position, visuals, etc. - */ - void Update(); - - /** - * \brief Render the whole main scene - */ - void RenderMainScene(); + + /** * \brief Returns screen space sun visual parameters for Lens Flare rendering. */ @@ -242,24 +279,34 @@ class Scene { /** * \brief Gets sun diffuse colour (accounting for atmospheric shift) */ - D3DXCOLOR GetSunDiffColor(); + FVECTOR4 GetSunDiffColor(); /** * \brief Render a secondary scene. (Env Maps, Shadow Maps, MFD Camera Views) */ - void RenderSecondaryScene(std::set &RndList, std::set &AdditionalLightsList, DWORD flags = 0xFF); - int RenderShadowMap(D3DXVECTOR3 &pos, D3DXVECTOR3 &ld, float rad, bool bInternal = false, bool bListExists = false); + void RenderStageSet(const LPDIRECT3DCUBETEXTURE9 pCT); + void RenderSecondaryScene(std::set &RndList, + std::set &AdditionalLightsList, DWORD flags = SCN_ALLEXT, + const LPDIRECT3DCUBETEXTURE9 pCT = nullptr, SHADOWMAP* sm = nullptr); + + int RenderShadowMap(SMapInput* smp, SHADOWMAP* out, std::list& Casters, bool bInternal = false); + int RenderVCShadowMap(FVECTOR3& cdir, FVECTOR3& ld, std::list& Casters); + bool RenderVCProbes(vVessel *vFocus); - bool IntegrateIrradiance(vVessel *vV, LPDIRECT3DCUBETEXTURE9 pSrc, LPDIRECT3DTEXTURE9 pOut); + bool IntegrateIrradiance(vVessel *vV, ENVCAMREC* ec, bool bInterior); bool RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DCUBETEXTURE9 pSrc); + bool RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DTEXTURE9 pSrc); void RenderMesh(DEVMESHHANDLE hMesh, const oapi::FMATRIX4 *pWorld); + void RenderStage(LPDIRECT3DCUBETEXTURE9 pCT); - LPDIRECT3DSURFACE9 GetIrradianceDepthStencil() const { return pIrradDS; } LPDIRECT3DSURFACE9 GetEnvDepthStencil() const { return pEnvDS; } LPDIRECT3DSURFACE9 GetBuffer(int id) const { return psgBuffer[id]; } LPDIRECT3DTEXTURE9 GetSunTexture() const { return pSunTex; } LPDIRECT3DTEXTURE9 GetSunGlareAtm() const { return pSunGlareAtm; } + LPDIRECT3DSURFACE9 GetDepthStencilMatch(LPDIRECT3DSURFACE9 pRef); + LPDIRECT3DSURFACE9 GetDepthStencil(DWORD size); + /** * \brief Render any shadows cast by vessels on planet surfaces * \param hPlanet handle of planet to cast shadows on @@ -271,17 +318,7 @@ class Scene { */ void RenderVesselShadows(OBJHANDLE hPlanet, float depth) const; - /** - * \brief Create a visual for a new vessel if within visual range. - * \param hVessel vessel object handle - */ - void NewVessel (OBJHANDLE hVessel); - - /** - * \brief Delete a vessel visual prior to destruction of the logical vessel. - * \param hVessel vessel object handle - */ - void DeleteVessel (OBJHANDLE hVessel); + void AddParticleStream (class D3D9ParticleStream *_pstream); void DelParticleStream (DWORD idx); @@ -303,10 +340,10 @@ class Scene { // Picking Functions ============================================================================================================ // - D3DXVECTOR3 GetPickingRay(short x, short y); - D3D9Pick PickScene(short xpos, short ypos); + FVECTOR3 GetPickingRay(short x, short y); + D3D9Pick PickScene(short xpos, short ypos, const PickProp* p); TILEPICK PickSurface(short xpos, short ypos); - D3D9Pick PickMesh(DEVMESHHANDLE hMesh, const LPD3DXMATRIX pW, short xpos, short ypos); + D3D9Pick PickMesh(DEVMESHHANDLE hMesh, const FMATRIX4* pW, short xpos, short ypos); void ClearOmitFlags(); bool IsRendering() const { return bRendering; } @@ -323,10 +360,10 @@ class Scene { // Camera Matrix Access ========================================================================================================= // - void GetAdjProjViewMatrix(LPD3DXMATRIX mP, float znear, float zfar); - const LPD3DXMATRIX GetProjectionViewMatrix() const { return (LPD3DXMATRIX)&Camera.mProjView; } - const LPD3DXMATRIX GetProjectionMatrix() const { return (LPD3DXMATRIX)&Camera.mProj; } - const LPD3DXMATRIX GetViewMatrix() const { return (LPD3DXMATRIX)&Camera.mView; } + void GetAdjProjViewMatrix(FMATRIX4* mP, float znear, float zfar); + const FMATRIX4* GetProjectionViewMatrix() const { return (FMATRIX4*)&Camera.mProjView; } + const FMATRIX4* GetProjectionMatrix() const { return (FMATRIX4*)&Camera.mProj; } + const FMATRIX4* GetViewMatrix() const { return (FMATRIX4*)&Camera.mView; } // Main Camera Interface ========================================================================================================= @@ -335,18 +372,20 @@ class Scene { void SetCameraFrustumLimits(double nearlimit, double farlimit); float GetDepthResolution(float dist) const; float CameraInSpace() const; + void ResetOrigin(VECTOR3 pos); // Acquire camera information from the Orbiter and initialize internal camera setup bool UpdateCameraFromOrbiter(DWORD dwPass); // Manually initialize client's internal camera setup - bool SetupInternalCamera(D3DXMATRIX *mView, VECTOR3 *pos, double apr, double asp); + bool SetupInternalCamera(FMATRIX4 *mView, VECTOR3 *pos, double apr, double asp); + void CameraOffOrigin90(FMATRIX4* mView, FVECTOR3 pos); // Pan Camera in a mesh debugger bool CameraPan(VECTOR3 pan, double speed); // Check if a sphere located in pCnt (relative to cam) with a specified radius is visible in a camera - bool IsVisibleInCamera(const D3DXVECTOR3 *pCnt, float radius); + bool IsVisibleInCamera(const FVECTOR3 *pCnt, float radius); bool IsProxyMesh(); bool CameraDirection2Viewport(const VECTOR3 &dir, int &x, int &y); double GetTanAp() const { return tan(Camera.aperture); } @@ -354,6 +393,7 @@ class Scene { float GetCameraFarPlane() const { return Camera.farplane; } float GetCameraNearPlane() const { return Camera.nearplane; } float GetCameraAperture() const { return (float)Camera.aperture; } + float GetCameraApertureCorner() const { return (float)Camera.corner; } VECTOR3 GetCameraGPos() const { return Camera.pos; } VECTOR3 GetCameraGDir() const { return Camera.dir; } OBJHANDLE GetCameraProxyBody() const { return Camera.hObj_proxy; } @@ -364,14 +404,14 @@ class Scene { double GetCameraNearAltitude() const { return Camera.alt_near; } double GetCameraElevation() const { return Camera.elev; } void GetCameraLngLat(double *lng, double *lat) const; - bool WorldToScreenSpace(const VECTOR3& rdir, oapi::IVECTOR2* pt, D3DXMATRIX* pVP = NULL, float clip = 1.0f); - bool WorldToScreenSpace2(const VECTOR3& rdir, oapi::FVECTOR2* pt, D3DXMATRIX* pVP = NULL, float clip = 1.0f); + bool WorldToScreenSpace(const VECTOR3& rdir, oapi::IVECTOR2* pt, FMATRIX4* pVP = NULL, float clip = 1.0f); + bool WorldToScreenSpace2(const VECTOR3& rdir, oapi::FVECTOR2* pt, FMATRIX4* pVP = NULL, float clip = 1.0f); DWORD GetFrameId() const { return dwFrameId; } - const D3DXVECTOR3 *GetCameraX() const { return &Camera.x; } - const D3DXVECTOR3 *GetCameraY() const { return &Camera.y; } - const D3DXVECTOR3 *GetCameraZ() const { return &Camera.z; } + const FVECTOR3 *GetCameraX() const { return &Camera.x; } + const FVECTOR3 *GetCameraY() const { return &Camera.y; } + const FVECTOR3 *GetCameraZ() const { return &Camera.z; } const CAMERA * GetCamera() const { return &Camera; } @@ -384,7 +424,7 @@ class Scene { // Visual Management ========================================================================================================= // - void GetLVLH(vVessel *vV, D3DXVECTOR3 *up, D3DXVECTOR3 *nr, D3DXVECTOR3 *cp); + void GetLVLH(vVessel *vV, FVECTOR3 *up, FVECTOR3 *nr, FVECTOR3 *cp); class vObject * GetVisObject(OBJHANDLE hObj) const; class vVessel * GetFocusVisual() const { return vFocus; } void CheckVisual(OBJHANDLE hObj); @@ -412,12 +452,19 @@ class Scene { void RenderGlares(); private: + void ActivateLocalLights(vObject* vO, bool bInterior); + void ActivateAllLocalLights(bool bInterior); void ComputeLocalLightsVisibility(); DWORD GetActiveParticleEffectCount(); float ComputeNearClipPlane(); void VisualizeCubeMap(LPDIRECT3DCUBETEXTURE9 pCube, int mip); + void VisualizeShadowMap(SHADOWMAP *sm); VOBJREC * FindVisual (OBJHANDLE hObj) const; void RenderVesselMarker(vVessel *vV, D3D9Pad *pSketch); + float GetLODLevel(SMapInput* smi); + void CombineSMaps(SMapInput* a, SMapInput* b, SMapInput* out); + + LPDIRECT3DTEXTURE9 RenderObjectsInShadow(SMapInput* smi, list& rList, D3D9Pad *pSkp = nullptr); // Locate the visual for hObj in the list if present, or return // NULL if not found @@ -441,89 +488,109 @@ class Scene { // Scene variables ================================================================ // - oapi::D3D9Client* gc; - LPDIRECT3DDEVICE9 pDevice; // render device - DWORD viewW, viewH; // render viewport size - DWORD stencilDepth; // stencil buffer bit depth - D3D9CelestialSphere* m_celSphere; // celestial sphere background - DWORD iVCheck; // index of last object checked for visibility - bool bLocalLight; // enable local light sources - bool surfLabelsActive; // v.2 surface labels activated? - OBJHANDLE hSun; + struct _cascfg { + float size; + float dist; + } cascfg[SHM_CASCADE_COUNT] = {}; - D3D9ParticleStream **pstream; // list of particle streams - DWORD nstream; // number of streams + oapi::D3D9Client* gc = {}; + LPDIRECT3DDEVICE9 pDevice = {}; // render device + DWORD viewW = {}; + DWORD viewH = {}; // render viewport size + DWORD stencilDepth = {}; // stencil buffer bit depth + D3D9CelestialSphere* m_celSphere = {}; // celestial sphere background + DWORD iVCheck = {}; // index of last object checked for visibility + bool bLocalLight = {}; // enable local light sources + bool surfLabelsActive = {}; // v.2 surface labels activated? + OBJHANDLE hSun = {}; - D3DCOLOR bg_rgba; // ambient background colour + D3D9ParticleStream **pstream = {}; // list of particle streams + DWORD nstream = {}; // number of streams + + DEVMESHHANDLE dmCubeMesh = {}; + DWORD bg_rgba = {}; // ambient background colour // GDI resources ==================================================================== // - oapi::Font *label_font[4]; + oapi::Font *label_font[4] = {}; - std::list RenderList; - std::list SmapRenderList; - std::list Casters; + std::list Shadowed; + std::list RenderList; + std::list ObjectsToShadowMap; + std::list Casters; std::stack CameraStack; std::stack PassStack; std::stack FrustumStack; - CAMERA Camera; - D3D9Light* Lights; - D3D9Sun sunLight; - - VECTOR3 sky_color; - double bglvl; - - float fDisplayScale; - float lmaxdst2; - DWORD nLights; - DWORD nplanets; // Number of distance sorted planets to render - DWORD dwTurn; - DWORD dwFrameId; - DWORD camIndex; - DWORD RenderFlags; - bool bRendering; - - oapi::Font *pAxisFont; - oapi::Font *pLabelFont; - oapi::Font *pDebugFont; - - SurfNative *pLblSrf; - - class ImageProcessing *pLightBlur, *pBlur, *pGDIOverlay, *pIrradiance, *pVisDepth, *pCreateGlare; - class ShaderClass *pLocalCompute, *pRenderGlares; - - class vVessel *vFocus; - VOBJREC *vobjEnv, *vobjIrd; - double dVisualAppRad; - - FVECTOR2 DepthSampleKernel[57]; - - LPDIRECT3DTEXTURE9 pSunTex, pLightGlare, pSunGlare, pSunGlareAtm; - LPDIRECT3DTEXTURE9 pLocalResults; - LPDIRECT3DSURFACE9 pLocalResultsSL; + CAMERA Camera = {}; + D3D9Light* Lights = {}; + D3D9Sun sunLight = {}; + + VECTOR3 origin = {}; + VECTOR3 sky_color = {}; + double bglvl = {}; + + float fCascadeRatio = {}; + float fDisplayScale = {}; + float lmaxdst2 = {}; + DWORD nLights = {}; + DWORD dwTurn = {}; + DWORD dwFrameId = {}; + DWORD camIndex = {}; + DWORD RenderFlags = {}; + bool bRendering = {}; + + oapi::Font *pAxisFont = {}; + oapi::Font *pLabelFont = {}; + oapi::Font *pDebugFont = {}; + + SurfNative *pLblSrf = {}; + + class ImageProcessing* pLightBlur = {}; + class ImageProcessing* pBlur = {}; + class ImageProcessing* pBlur2D = {}; + class ImageProcessing* pGDIOverlay = {}; + class ImageProcessing* pIrradiance = {}; + class ImageProcessing* pVisDepth = {}; + class ImageProcessing* pCreateGlare = {}; + class ImageProcessing* pBakeLights = {}; + class ShaderClass* pLocalCompute = {}; + class ShaderClass* pRenderGlares = {}; + class ShaderClass* pRenderStage = {}; + + class vVessel *vFocus = {}; + double dVisualAppRad = {}; + + FVECTOR2 DepthSampleKernel[57] = {}; + + LPDIRECT3DTEXTURE9 pSunTex = {}; + LPDIRECT3DTEXTURE9 pLightGlare = {}; + LPDIRECT3DTEXTURE9 pSunGlare = {}; + LPDIRECT3DTEXTURE9 pSunGlareAtm = {}; + LPDIRECT3DTEXTURE9 pLocalResults = {}; + LPDIRECT3DSURFACE9 pLocalResultsSL = {}; // Blur Sampling Kernel ============================================================== - LPDIRECT3DCUBETEXTURE9 pBlrTemp[5]; - LPDIRECT3DCUBETEXTURE9 pIrradTemp; - LPDIRECT3DTEXTURE9 pIrradTemp2, pIrradTemp3; + LPDIRECT3DCUBETEXTURE9 pBlrTemp[5] = {}; + LPDIRECT3DTEXTURE9 pBlrTemp2D[5] = {}; + LPDIRECT3DTEXTURE9 pIrradTemp = {}; + LPDIRECT3DTEXTURE9 ptRandom = {}; // Deferred Experiment =============================================================== // - LPDIRECT3DSURFACE9 psgBuffer[GBUF_COUNT]; - LPDIRECT3DTEXTURE9 ptgBuffer[GBUF_COUNT]; - LPDIRECT3DSURFACE9 pOffscreenTarget; - LPDIRECT3DTEXTURE9 pTextures[TEX_COUNT]; + LPDIRECT3DSURFACE9 psgBuffer[GBUF_COUNT] = {}; + LPDIRECT3DTEXTURE9 ptgBuffer[GBUF_COUNT] = {}; + LPDIRECT3DSURFACE9 pOffscreenTarget = {}; + LPDIRECT3DTEXTURE9 pTextures[TEX_COUNT] = {}; - LPDIRECT3DSURFACE9 pEnvDS, pIrradDS, pDepthNormalDS; - LPDIRECT3DSURFACE9 psShmDS[SHM_LOD_COUNT]; - LPDIRECT3DSURFACE9 psShmRT[SHM_LOD_COUNT]; - LPDIRECT3DTEXTURE9 ptShmRT[SHM_LOD_COUNT]; + LPDIRECT3DSURFACE9 pEnvDS = {}; + LPDIRECT3DSURFACE9 pDepthNormalDS = {}; + LPDIRECT3DSURFACE9 psShmDS[SHM_LOD_COUNT] = {}; - LocalLightsCompute LLCBuf[MAX_SCENE_LIGHTS + 1]; + LocalLightsCompute LLCBuf[MAX_SCENE_LIGHTS + 1] = {}; // Rendering Technique related parameters ============================================ // diff --git a/OVP/D3D9Client/Spherepatch.cpp b/OVP/D3D9Client/Spherepatch.cpp index 46f8eda65..3ea47c8ec 100644 --- a/OVP/D3D9Client/Spherepatch.cpp +++ b/OVP/D3D9Client/Spherepatch.cpp @@ -12,6 +12,7 @@ #include "Spherepatch.h" #include "AABBUtil.h" #include "TileMgr2.h" +#include "DirectXCollision.h" static float TEX2_MULTIPLIER = 4.0f; // microtexture multiplier @@ -56,13 +57,6 @@ VBMESH::~VBMESH () nf_cur = 0; } -void VBMESH::ComputeSphere() -{ - bsCnt = D3DXVECTOR3(float(Box[0].x + Box[7].x), float(Box[0].y + Box[7].y), float(Box[0].z + Box[7].z)) * 0.5f; - bsRad = D3DXVec3Length(ptr(D3DXVECTOR3(float(Box[0].x - Box[7].x), float(Box[0].y - Box[7].y), float(Box[0].z - Box[7].z)) * 0.5f)); -} - - void VBMESH::MapVertices(LPDIRECT3DDEVICE9 pDev, DWORD MemFlag) { if (nv!=nv_cur && vtx) { @@ -78,7 +72,11 @@ void VBMESH::MapVertices(LPDIRECT3DDEVICE9 pDev, DWORD MemFlag) if (vtx) { - HR(D3DXComputeBoundingSphere((const D3DXVECTOR3 *)&vtx->x, nv, sizeof(VERTEX_2TEX), &bsCnt, &bsRad)); + DirectX::BoundingSphere spr; + DirectX::BoundingSphere::CreateFromPoints(spr, nv, (const XMFLOAT3*)&vtx->x, sizeof(VERTEX_2TEX)); + + bsCnt = _F(spr.Center); + bsRad = spr.Radius; if (pVB) { if (HROK(pVB->Lock(0, 0, (LPVOID*)&pVBuffer, D3DLOCK_DISCARD))) { @@ -144,7 +142,7 @@ void CreateSphere (LPDIRECT3DDEVICE9 pDev, VBMESH &mesh, DWORD nrings, bool hemi FLOAT fDAngX0 = x*fDAng - (FLOAT)PI; // subtract Pi to wrap at +-180° if (hemisphere && which_half) fDAngX0 += (FLOAT)PI; - D3DVECTOR v = {r0*(FLOAT)cos(fDAngX0), y0, r0*(FLOAT)sin(fDAngX0)}; + FVECTOR3 v = {r0*(FLOAT)cos(fDAngX0), y0, r0*(FLOAT)sin(fDAngX0)}; FLOAT tu = a*(FLOAT)x + du; //FLOAT tu = x/(FLOAT)x1; @@ -166,7 +164,7 @@ void CreateSphere (LPDIRECT3DDEVICE9 pDev, VBMESH &mesh, DWORD nrings, bool hemi } } // Make top and bottom - D3DVECTOR pvy = {0, 1, 0}, nvy = {0,-1,0}; + FVECTOR3 pvy = {0, 1, 0}, nvy = {0,-1,0}; WORD wNorthVtx = nvtx; *vtx++ = VERTEX_2TEX (pvy, pvy, 0.5f, 0.0f, 0.5f, 0.0f); nvtx++; @@ -278,14 +276,14 @@ void CreateSpherePatch (LPDIRECT3DDEVICE9 pDev, VBMESH &mesh, int nlng, int nlat else if (tpos.z > tpmax.z) tpmax.z = tpos.z; } - Vtx[n].x = Vtx[n].nx = D3DVAL(pos.x); - Vtx[n].y = Vtx[n].ny = D3DVAL(pos.y); - Vtx[n].z = Vtx[n].nz = D3DVAL(pos.z); + Vtx[n].x = Vtx[n].nx = float(pos.x); + Vtx[n].y = Vtx[n].ny = float(pos.y); + Vtx[n].z = Vtx[n].nz = float(pos.z); if (shift_origin) Vtx[n].x -= dx, Vtx[n].y -= dy; - Vtx[n].tu0 = D3DVAL(nseg ? (c1*j)/nseg+c2 : 0.5f); // overlap to avoid seams - Vtx[n].tv0 = D3DVAL((c1*(res-i))/res+c2); + Vtx[n].tu0 = float(nseg ? (c1*j)/nseg+c2 : 0.5f); // overlap to avoid seams + Vtx[n].tv0 = float((c1*(res-i))/res+c2); //Vtx[n].tu1 = (nseg ? Vtx[n].tu0 * TEX2_MULTIPLIER : 0.5f); //Vtx[n].tv1 = Vtx[n].tv0 * TEX2_MULTIPLIER; if (!outside) { diff --git a/OVP/D3D9Client/Spherepatch.h b/OVP/D3D9Client/Spherepatch.h index fa521e18a..4d0ded9e0 100644 --- a/OVP/D3D9Client/Spherepatch.h +++ b/OVP/D3D9Client/Spherepatch.h @@ -22,8 +22,7 @@ struct VBMESH { ~VBMESH(); void MapVertices (LPDIRECT3DDEVICE9 dev, DWORD MemFlag=0); // copy vertices from vtx to vb - void ComputeSphere(); - + LPDIRECT3DVERTEXBUFFER9 pVB; // mesh vertex buffer LPDIRECT3DINDEXBUFFER9 pIB; // mesh index buffer @@ -34,7 +33,7 @@ struct VBMESH { DWORD nv_cur; DWORD nf_cur; VECTOR4 Box[8]; // bounding box vertices - D3DXVECTOR3 bsCnt; // bounding sphere position + FVECTOR3 bsCnt; // bounding sphere position float bsRad; // bounding sphere radius bool bBox; // true if bounding box data is valid }; diff --git a/OVP/D3D9Client/SurfMgr.cpp b/OVP/D3D9Client/SurfMgr.cpp index 066a22dc7..d23ba6746 100644 --- a/OVP/D3D9Client/SurfMgr.cpp +++ b/OVP/D3D9Client/SurfMgr.cpp @@ -68,7 +68,7 @@ void SurfaceManager::SetMicrotexture (const char *fname) // ======================================================================= -void SurfaceManager::Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, int level, double viewap, bool bfog) +void SurfaceManager::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap, bool bfog) { if (ntex==0) LoadData(); @@ -84,17 +84,17 @@ void SurfaceManager::Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scal // ============================================================== -void SurfaceManager::RenderSimple(int level, int npatch, TILEDESC *tile, LPD3DXMATRIX mWrld) +void SurfaceManager::RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWrld) { // render complete sphere (used at low LOD levels) HR(FX->SetTechnique(ePlanetTile)); HR(FX->SetValue(eSun, gc->GetScene()->GetSun(), sizeof(D3D9Sun))); - HR(FX->SetMatrix(eW, mWrld)); + HR(FX->SetMatrix(eW, _DX(mWrld))); HR(FX->SetValue(eWater, &watermat, sizeof(D3DMATERIAL9))); HR(FX->SetValue(eMat, &def_mat, sizeof(D3DMATERIAL9))); - HR(FX->SetValue(eColor, ptr(D3DXCOLOR(cAmbient)), sizeof(D3DXCOLOR))); + HR(FX->SetValue(eColor, &(FVECTOR4(cAmbient)), sizeof(FVECTOR4))); HR(FX->SetFloat(eTime, float(fmod(oapiGetSimTime(),60.0)))); - HR(FX->SetVector(eTexOff, ptr(D3DXVECTOR4(1.0f, 0.0f, 1.0f, 0.0f)))); + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(1.0f, 0.0f, 1.0f, 0.0f)))); HR(FX->SetFloat(eMix, 0.0f)); LPDIRECT3DDEVICE9 pDev = gc->GetDevice(); @@ -145,7 +145,7 @@ void SurfaceManager::InitRenderTile() HR(FX->SetValue(eSun, gc->GetScene()->GetSun(), sizeof(D3D9Sun))); HR(FX->SetValue(eMat, &def_mat, sizeof(D3DMATERIAL9))); HR(FX->SetValue(eWater, &watermat, sizeof(D3DMATERIAL9))); - HR(FX->SetValue(eColor, ptr(D3DXCOLOR(cAmbient)), sizeof(D3DXCOLOR))); + HR(FX->SetValue(eColor, &(FVECTOR4(cAmbient)), sizeof(FVECTOR4))); HR(FX->SetFloat(eTime, float(fmod(oapiGetSimTime(),60.0)))); LPDIRECT3DDEVICE9 pDev = gc->GetDevice(); @@ -173,12 +173,12 @@ void SurfaceManager::RenderTile (int lvl, int hemisp, int ilat, int nlat, int il VBMESH &mesh = PATCH_TPL[lvl][ilat]; // patch template if (range.tumin == 0 && range.tumax == 1) { - HR(FX->SetVector(eTexOff, ptr(D3DXVECTOR4(1.0f, 0.0f, 1.0f, 0.0f)))); + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(1.0f, 0.0f, 1.0f, 0.0f)))); } else { float tuscale = range.tumax-range.tumin, tuofs = range.tumin; float tvscale = range.tvmax-range.tvmin, tvofs = range.tvmin; - HR(FX->SetVector(eTexOff, ptr(D3DXVECTOR4(tuscale,tuofs,tvscale,tvofs)))); + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(tuscale,tuofs,tvscale,tvofs)))); } DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); @@ -187,13 +187,13 @@ void SurfaceManager::RenderTile (int lvl, int hemisp, int ilat, int nlat, int il if (flags&DBG_FLAGS_TILES) { float x = 0.6f; switch(lvl) { - case 14: FX->SetVector(eColor, ptr(D3DXVECTOR4(x, 0, 0, 0))); break; - case 13: FX->SetVector(eColor, ptr(D3DXVECTOR4(0, x, 0, 0))); break; - case 12: FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, x, 0))); break; - case 11: FX->SetVector(eColor, ptr(D3DXVECTOR4(x, x, 0, 0))); break; - case 10: FX->SetVector(eColor, ptr(D3DXVECTOR4(x, 0, x, 0))); break; - case 9: FX->SetVector(eColor, ptr(D3DXVECTOR4(0, x, x, 0))); break; - default: FX->SetVector(eColor, ptr(D3DXVECTOR4(0, 0, 0, 0))); break; + case 14: FX->SetVector(eColor, _DX(FVECTOR4(x, 0, 0, 0))); break; + case 13: FX->SetVector(eColor, _DX(FVECTOR4(0, x, 0, 0))); break; + case 12: FX->SetVector(eColor, _DX(FVECTOR4(0, 0, x, 0))); break; + case 11: FX->SetVector(eColor, _DX(FVECTOR4(x, x, 0, 0))); break; + case 10: FX->SetVector(eColor, _DX(FVECTOR4(x, 0, x, 0))); break; + case 9: FX->SetVector(eColor, _DX(FVECTOR4(0, x, x, 0))); break; + default: FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0, 0, 0))); break; } } } @@ -203,7 +203,7 @@ void SurfaceManager::RenderTile (int lvl, int hemisp, int ilat, int nlat, int il if (ltex==NULL) ltex = gc->GetDefaultTexture()->GetTexture(); - HR(FX->SetMatrix(eW, &mWorld)); + HR(FX->SetMatrix(eW, _DX(mWorld))); HR(FX->SetTexture(eTex0, tex)); // Base Texture HR(FX->SetTexture(eTex1, ltex)); // Specular Mask and Night Lights diff --git a/OVP/D3D9Client/SurfMgr.h b/OVP/D3D9Client/SurfMgr.h index b4ade104a..dac2e50b5 100644 --- a/OVP/D3D9Client/SurfMgr.h +++ b/OVP/D3D9Client/SurfMgr.h @@ -20,18 +20,18 @@ class SurfaceManager: public TileManager { public: SurfaceManager(oapi::D3D9Client *gclient, const vPlanet *vplanet); void SetMicrotexture(const char *fname); - void Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, int level, double viewap = 0.0, bool bfog = false); + void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap = 0.0, bool bfog = false); void LoadData(); protected: void InitRenderTile(); void EndRenderTile(); - void RenderSimple(int level, int npatch, TILEDESC *tile, LPD3DXMATRIX mWorld); + void RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWorld); void RenderTile(int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, double sdist, TILEDESC *tile, const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag); }; -#endif // !__SURFMGR_H \ No newline at end of file +#endif // !__SURFMGR_H diff --git a/OVP/D3D9Client/Surfmgr2.cpp b/OVP/D3D9Client/Surfmgr2.cpp index 85278c8b9..8d5487cb3 100644 --- a/OVP/D3D9Client/Surfmgr2.cpp +++ b/OVP/D3D9Client/Surfmgr2.cpp @@ -754,7 +754,7 @@ void SurfTile::StepIn () double s = vPlanet->GetSize(); for (int i = 0; i < ARRAYSIZE(vPlanet->MicroCfg.Level); ++i) { double f = s / vPlanet->MicroCfg.Level[i].size; - MicroRep[i] = D3DXVECTOR2(fixinput(width*f, i), fixinput(height*f, i)); + MicroRep[i] = FVECTOR2(fixinput(width*f, i), fixinput(height*f, i)); } } } @@ -808,10 +808,9 @@ void SurfTile::Render () has_lights &= pShader->bNightlights; has_atmosphere &= pShader->bAtmosphere; - sp->vCloudOff = FVECTOR4(0, 0, 1, 1); + sp->vCloudOff = FVECTOR4(0.0f, 0.0f, 1.0f, 1.0f); - D3DXVECTOR3 bs_pos; - D3DXVec3TransformCoord(&bs_pos, &mesh->bsCnt, &mWorld); + FVECTOR3 bs_pos = oapiTransformCoord(&mesh->bsCnt, &mWorld); // ---------------------------------------------------------------------- // Assign micro texture range information to shaders @@ -996,29 +995,29 @@ void SurfTile::Render () if (pShader->bShdMap) { - const Scene::SHADOWMAPPARAM* shd = scene->GetSMapData(); + const SHADOWMAP* shd = scene->GetSMapData(ShdPackage::Main); - D3DXVECTOR3 bc = bs_pos - shd->pos; + FVECTOR3 bc = FVECTOR3(bs_pos) - shd->pos; double alt = scene->GetCameraAltitude() - scene->GetTargetElevation(); if ((alt < 10e3) && (scene->GetCameraProxyVisual() == mgr->GetPlanet())) { - if (shd->pShadowMap && (Config->ShadowMapMode != 0) && (Config->TerrainShadowing == 2)) { + if (shd->IsValid() && (Config->ShadowMapMode != 0) && (Config->TerrainShadowing == 2)) { - float x = D3DXVec3Dot(&bc, &(shd->ld)); + float x = dotp(bc, shd->ld); - if (sqrt(D3DXVec3Dot(&bc, &bc) - x * x) < (shd->rad + mesh->bsRad)) { + if (sqrt(dotp(bc, bc) - x * x) < (shd->rad + mesh->bsRad)) { float s = float(shd->size); float sr = 2.0f * shd->rad / s; - sp->mLVP = shd->mViewProj; + sp->mLVP = shd->mLVP; sp->vSHD = FVECTOR4(sr, 1.0f / s, 0.0f, 1.0f / shd->depth); fc->bShadows = true; } } } - pShader->SetTexture(pShader->tShadowMap, shd->pShadowMap); + pShader->SetTexture(pShader->tShadowMap, shd->ptShmRT[0]); } // --------------------------------------------------------------------- @@ -1119,7 +1118,7 @@ void SurfTile::Render () if (DebugControls::IsActive()) { DWORD flags = *(DWORD*)mgr->GetClient()->GetConfigParam(CFGPRM_GETDEBUGFLAGS); if (flags&DBG_FLAGS_TILEBOXES) { - D3D9Effect::RenderTileBoundingBox(&mWorld, mesh->Box, &D3DXVECTOR4(1,0,0,1)); + D3D9Effect::RenderTileBoundingBox(&mWorld, mesh->Box, &FVECTOR4(1,0,0,1)); } }*/ } @@ -1614,7 +1613,7 @@ void TileManager2::Unload(int lvl) // ----------------------------------------------------------------------- template<> -void TileManager2::Pick(D3DXVECTOR3 &vRay, TILEPICK *pPick) +void TileManager2::Pick(FVECTOR3 &vRay, TILEPICK *pPick) { std::list tiles; diff --git a/OVP/D3D9Client/Surfmgr2.h b/OVP/D3D9Client/Surfmgr2.h index 919b099f3..dc2e33418 100644 --- a/OVP/D3D9Client/Surfmgr2.h +++ b/OVP/D3D9Client/Surfmgr2.h @@ -85,7 +85,7 @@ class SurfTile: public Tile { PlanetShader* pShader; mutable ELEVFILEHEADER ehdr;///< Let's store the complete header for later use - D3DXVECTOR2 MicroRep[3]; + FVECTOR2 MicroRep[3]; DWORD MaxRep; LPDIRECT3DTEXTURE9 ltex; ///< landmask/nightlight texture, if applicable INT16 *elev_file; ///< elevation data [m] diff --git a/OVP/D3D9Client/TileMgr.cpp b/OVP/D3D9Client/TileMgr.cpp index 70c2ed53f..bc57b11bf 100644 --- a/OVP/D3D9Client/TileMgr.cpp +++ b/OVP/D3D9Client/TileMgr.cpp @@ -461,17 +461,17 @@ void TileManager::LoadSpecularMasks () // ============================================================== -void TileManager::SetAmbientColor(D3DCOLOR c) +void TileManager::SetAmbientColor(DWORD c) { cAmbient = c; } // ============================================================== -void TileManager::Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, int level, double viewap, bool bfog) +void TileManager::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap, bool bfog) { VECTOR3 gpos; - D3DXMATRIX imat; + FMATRIX4 imat; FX->SetFloat(eDistScale, 1.0f/float(scale)); @@ -480,8 +480,8 @@ void TileManager::Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, RenderParam.dev = dev; D3DMAT_Copy (&RenderParam.wmat, &wmat); D3DMAT_Copy (&RenderParam.wmat_tmp, &wmat); - D3DMAT_MatrixInvert (&imat, &wmat); - RenderParam.cdir = _V(imat._41, imat._42, imat._43); // camera position in local coordinates (units of planet radii) + oapiMatrixInverse(&imat, NULL, &wmat); + RenderParam.cdir = _V(imat.m41, imat.m42, imat.m43); // camera position in local coordinates (units of planet radii) RenderParam.cpos = vp->PosFromCamera() * scale; normalise (RenderParam.cdir); // camera direction RenderParam.bfog = bfog; @@ -526,7 +526,7 @@ void TileManager::Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, WaitForSingleObject (tilebuf->hQueueMutex, INFINITE); // make sure we can write to texture request queue for (hemisp = idx = 0; hemisp < 2; hemisp++) { if (hemisp) { // flip world transformation to southern hemisphere - D3DXMatrixMultiply(&RenderParam.wmat, &Rsouth, &RenderParam.wmat); + oapiMatrixMultiply(&RenderParam.wmat, &Rsouth, &RenderParam.wmat); D3DMAT_Copy (&RenderParam.wmat_tmp, &RenderParam.wmat); RenderParam.grot.m12 = -RenderParam.grot.m12; RenderParam.grot.m13 = -RenderParam.grot.m13; @@ -658,9 +658,8 @@ void TileManager::ProcessTile (int lvl, int hemisp, int ilat, int nlat, int ilng VBMESH *mesh = &PATCH_TPL[lvl][ilat]; float bsrad = mesh->bsRad * bsScale; - D3DXVECTOR3 vBS; - D3DXVec3TransformCoord(&vBS, &mesh->bsCnt, &mWorld); - float dist = D3DXVec3Length(&vBS); + FVECTOR3 vBS = oapiTransformCoord(&mesh->bsCnt, &mWorld); + float dist = length(vBS); if ((dist-bsrad)>RenderParam.horzdist) return; //Tile is behind the horizon if (gc->GetScene()->IsVisibleInCamera(&vBS, bsrad)==false) return; @@ -711,8 +710,7 @@ int TileManager::IsTileInView(int lvl, int ilat, float scale) { VBMESH &mesh = PATCH_TPL[lvl][ilat]; float rad = mesh.bsRad * scale; - D3DXVECTOR3 vP; - D3DXVec3TransformCoord(&vP, &mesh.bsCnt, &mWorld); + FVECTOR3 vP = oapiTransformCoord(&mesh.bsCnt, &mWorld); //float dist = D3DXVec3Length(&vP); //if ((dist-rad)>RenderParam.horzdist) return -1; //Tile is behind the horizon @@ -725,7 +723,7 @@ int TileManager::IsTileInView(int lvl, int ilat, float scale) void TileManager::SetWorldMatrix (int ilng, int nlng, int ilat, int nlat) { // set up world transformation matrix - D3DXMATRIX rtile, wtrans; + FMATRIX4 rtile, wtrans; double lng = PI*2.0 * (double)ilng/(double)nlng + PI; // add pi so texture wraps at +-180° D3DMAT_RotY (&rtile, lng); @@ -739,12 +737,12 @@ void TileManager::SetWorldMatrix (int ilng, int nlng, int ilat, int nlat) double dx = s*cos(lng)*cos(lat); // the offsets between sphere centre and tile corner double dy = s*sin(lat); double dz = s*sin(lng)*cos(lat); - RenderParam.wmat_tmp._41 = (float)(dx*RenderParam.grot.m11 + dy*RenderParam.grot.m12 + dz*RenderParam.grot.m13 + RenderParam.cpos.x); - RenderParam.wmat_tmp._42 = (float)(dx*RenderParam.grot.m21 + dy*RenderParam.grot.m22 + dz*RenderParam.grot.m23 + RenderParam.cpos.y); - RenderParam.wmat_tmp._43 = (float)(dx*RenderParam.grot.m31 + dy*RenderParam.grot.m32 + dz*RenderParam.grot.m33 + RenderParam.cpos.z); - D3DXMatrixMultiply(&mWorld, &rtile, &RenderParam.wmat_tmp); + RenderParam.wmat_tmp.m41 = (float)(dx*RenderParam.grot.m11 + dy*RenderParam.grot.m12 + dz*RenderParam.grot.m13 + RenderParam.cpos.x); + RenderParam.wmat_tmp.m42 = (float)(dx*RenderParam.grot.m21 + dy*RenderParam.grot.m22 + dz*RenderParam.grot.m23 + RenderParam.cpos.y); + RenderParam.wmat_tmp.m43 = (float)(dx*RenderParam.grot.m31 + dy*RenderParam.grot.m32 + dz*RenderParam.grot.m33 + RenderParam.cpos.z); + oapiMatrixMultiply(&mWorld, &rtile, &RenderParam.wmat_tmp); } else { - D3DXMatrixMultiply(&mWorld, &rtile, &RenderParam.wmat); + oapiMatrixMultiply(&mWorld, &rtile, &RenderParam.wmat); } } @@ -898,7 +896,7 @@ bool TileManager::bGlobalRipple = false; bool TileManager::bGlobalLights = false; TileBuffer *TileManager::tilebuf = NULL; -D3DXMATRIX TileManager::Rsouth; +FMATRIX4 TileManager::Rsouth; VBMESH TileManager::PATCH_TPL_1; VBMESH TileManager::PATCH_TPL_2; diff --git a/OVP/D3D9Client/TileMgr.h b/OVP/D3D9Client/TileMgr.h index 21ab2671b..632fbad10 100644 --- a/OVP/D3D9Client/TileMgr.h +++ b/OVP/D3D9Client/TileMgr.h @@ -94,9 +94,9 @@ class TileManager : public D3D9Effect { virtual void SetMicrotexture (const char *fname); virtual void SetMicrolevel (double lvl); - virtual void Render(LPDIRECT3DDEVICE9 dev, D3DXMATRIX &wmat, double scale, int level, double viewap = 0.0, bool bfog = false); + virtual void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap = 0.0, bool bfog = false); - void SetAmbientColor(D3DCOLOR cAmbient); + void SetAmbientColor(DWORD cAmbient); protected: @@ -106,7 +106,7 @@ class TileManager : public D3D9Effect { virtual void InitRenderTile() = 0; virtual void EndRenderTile() = 0; - virtual void RenderSimple(int level, int npatch, TILEDESC *tile, LPD3DXMATRIX mWorld) = 0; + virtual void RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWorld) = 0; virtual void RenderTile(int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, double sdist, TILEDESC *tile, const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag) = 0; @@ -147,8 +147,8 @@ class TileManager : public D3D9Effect { // adjust specular reflection through atmosphere - D3DXMATRIX mWorld; - D3DCOLOR cAmbient; + FMATRIX4 mWorld; + DWORD cAmbient; const vPlanet *vp; // the planet visual OBJHANDLE obj; // the planet object char *objname; // the name of the planet (for identifying texture files) @@ -163,7 +163,7 @@ class TileManager : public D3D9Effect { double microlvl; // intensity of microtexture DWORD nmask; // number of specular reflection masks/light maps (level <= 8) VECTOR3 pcdir; // previous camera direction - static D3DXMATRIX Rsouth; // rotation matrix for mapping tiles to southern hemisphere + static FMATRIX4 Rsouth; // rotation matrix for mapping tiles to southern hemisphere float spec_base; // base intensity for specular reflections const ATMCONST *atmc; // atmospheric parameters (used for specular colour modification) bool bPreloadTile; // pre-load surface tile textures @@ -205,8 +205,8 @@ class TileManager : public D3D9Effect { struct RENDERPARAM { LPDIRECT3DDEVICE9 dev; // render device - D3DXMATRIX wmat; // world matrix - D3DXMATRIX wmat_tmp; // copy of world matrix used as work buffer + FMATRIX4 wmat; // world matrix + FMATRIX4 wmat_tmp; // copy of world matrix used as work buffer int tgtlvl; // target resolution level MATRIX3 grot; // planet rotation matrix VECTOR3 cpos; // planet offset vector (in global frame) diff --git a/OVP/D3D9Client/Tilemgr2.cpp b/OVP/D3D9Client/Tilemgr2.cpp index ae9e9e4d8..7c3dcda97 100644 --- a/OVP/D3D9Client/Tilemgr2.cpp +++ b/OVP/D3D9Client/Tilemgr2.cpp @@ -15,6 +15,7 @@ #include "D3D9Catalog.h" #include "Scene.h" #include "OapiExtension.h" +#include "DirectXCollision.h" #include #include @@ -277,15 +278,15 @@ float Tile::GetBoundingSphereRad() const // ----------------------------------------------------------------------- -D3DXVECTOR3 Tile::GetBoundingSpherePos() const +FVECTOR3 Tile::GetBoundingSpherePos() const { if (mesh) return mesh->bsCnt; - return D3DXVECTOR3(0,0,0); + return FVECTOR3(0,0,0); } // ----------------------------------------------------------------------- -bool Tile::Pick(const LPD3DXMATRIX pW, const D3DXVECTOR3 *vDir, TILEPICK &result) +bool Tile::Pick(const FMATRIX4* pW, const FVECTOR3 *vDir, TILEPICK &result) { if (!mesh) { LogErr("Tile::Pick() Failed: No Mesh Available"); @@ -296,27 +297,25 @@ bool Tile::Pick(const LPD3DXMATRIX pW, const D3DXVECTOR3 *vDir, TILEPICK &result return false; } - D3DXVECTOR3 bs; - D3DXVec3TransformCoord(&bs, &mesh->bsCnt, pW); + FVECTOR3 bs = oapiTransformCoord(&mesh->bsCnt, pW); - float dst = D3DXVec3Dot(&bs, vDir); - float len2 = D3DXVec3Dot(&bs, &bs); + float dst = dotp(bs, *vDir); + float len2 = dotp(bs, bs); if (dst < -mesh->bsRad) return false; if (sqrt(len2 - dst*dst) > mesh->bsRad) return false; - - D3DXVECTOR3 pos, dir; - D3DXVECTOR3 _a, _b, _c, cp; - D3DXMATRIX mWI; + FVECTOR3 _a, _b, _c, cp; + FMATRIX4 mWI; float det; WORD *pIdc = mesh->idx; VERTEX_2TEX *vtx = mesh->vtx; - D3DXMatrixInverse(&mWI, &det, pW); - D3DXVec3TransformCoord(&pos, ptr(D3DXVECTOR3(0, 0, 0)), &mWI); - D3DXVec3TransformNormal(&dir, vDir, &mWI); + oapiMatrixInverse(&mWI, &det, pW); + + XMVECTOR pos = oapiTransformCoord(&(FVECTOR3(0, 0, 0)), &mWI).XM(); + XMVECTOR dir = oapiTransformNormal(vDir, &mWI).XM(); int idx = -1; @@ -326,22 +325,20 @@ bool Tile::Pick(const LPD3DXMATRIX pW, const D3DXVECTOR3 *vDir, TILEPICK &result WORD b = pIdc[i * 3 + 1]; WORD c = pIdc[i * 3 + 2]; - _a = D3DXVECTOR3(vtx[a].x, vtx[a].y, vtx[a].z); - _b = D3DXVECTOR3(vtx[b].x, vtx[b].y, vtx[b].z); - _c = D3DXVECTOR3(vtx[c].x, vtx[c].y, vtx[c].z); + _a = FVECTOR3(vtx[a].x, vtx[a].y, vtx[a].z); + _b = FVECTOR3(vtx[b].x, vtx[b].y, vtx[b].z); + _c = FVECTOR3(vtx[c].x, vtx[c].y, vtx[c].z); - float u, v, dst; + float dst; - D3DXVec3Cross(&cp, ptr(_c - _b), ptr(_a - _b)); + cp = crossp((_c - _b), (_a - _b)); - if (D3DXVec3Dot(&cp, &dir)<0) { - if (D3DXIntersectTri(&_c, &_b, &_a, &pos, &dir, &u, &v, &dst)) { + if (dotp(cp, dir)<0) { + if (DirectX::TriangleTests::Intersects(pos, dir, _c.XM(), _b.XM(), _a.XM(), dst)) { if (dst > 0.1f) { if (dst < result.d) { idx = i; - result.d = dst; - result.u = u; - result.v = v; + result.d = dst; result.i = i; result.pTile = this; } @@ -353,23 +350,20 @@ bool Tile::Pick(const LPD3DXMATRIX pW, const D3DXVECTOR3 *vDir, TILEPICK &result if (idx >= 0) { int i = result.i; - float u = result.u; - float v = result.v; WORD a = pIdc[i * 3 + 0]; WORD b = pIdc[i * 3 + 1]; WORD c = pIdc[i * 3 + 2]; - _a = D3DXVECTOR3(vtx[a].x, vtx[a].y, vtx[a].z); - _b = D3DXVECTOR3(vtx[b].x, vtx[b].y, vtx[b].z); - _c = D3DXVECTOR3(vtx[c].x, vtx[c].y, vtx[c].z); + _a = FVECTOR3(vtx[a].x, vtx[a].y, vtx[a].z); + _b = FVECTOR3(vtx[b].x, vtx[b].y, vtx[b].z); + _c = FVECTOR3(vtx[c].x, vtx[c].y, vtx[c].z); - D3DXVec3Cross(&cp, ptr(_c - _b), ptr(_a - _b)); - D3DXVec3TransformNormal(&cp, &cp, pW); - D3DXVec3Normalize(&result._n, &cp); + cp = crossp((_c - _b), (_a - _b)); + cp = oapiTransformNormal(&cp, pW); - D3DXVECTOR3 p = (_b * u) + (_a * v) + (_c * (1.0f - u - v)); - D3DXVec3TransformCoord(&result._p, &p, pW); + result._n = unit(cp); + result._p = *vDir * result.d; return true; } @@ -499,13 +493,13 @@ VBMESH *Tile::CreateMesh_quadpatch (int grdlat, int grdlng, float *elev, double if (tpos.z < tpmin.z) tpmin.z = tpos.z; else if (tpos.z > tpmax.z) tpmax.z = tpos.z; } - vtx[n].x = D3DVAL(pos.x - dx); vtx[n].nx = D3DVAL(nml.x); - vtx[n].y = D3DVAL(pos.y - dy); vtx[n].ny = D3DVAL(nml.y); - vtx[n].z = D3DVAL(pos.z); vtx[n].nz = D3DVAL(nml.z); - vtx[n].e = D3DVAL(e); + vtx[n].x = float(pos.x - dx); vtx[n].nx = float(nml.x); + vtx[n].y = float(pos.y - dy); vtx[n].ny = float(nml.y); + vtx[n].z = float(pos.z); vtx[n].nz = float(nml.z); + vtx[n].e = float(e); - vtx[n].tu0 = D3DVAL((c1*j)/grdlng+c2); // overlap to avoid seams - vtx[n].tv0 = D3DVAL(grdlat-i)/D3DVAL(grdlat); + vtx[n].tu0 = float((c1*j)/grdlng+c2); // overlap to avoid seams + vtx[n].tv0 = float(grdlat-i)/float(grdlat); //vtx[n].tu1 = vtx[n].tu0 * TEX2_MULTIPLIER; //vtx[n].tv1 = vtx[n].tv0 * TEX2_MULTIPLIER; // map texture coordinates to subrange @@ -639,7 +633,6 @@ VBMESH *Tile::CreateMesh_quadpatch (int grdlat, int grdlng, float *elev, double mesh->Box[6] = _V(tmul (R, _V(tpmin.x, tpmax.y, tpmax.z)) + pref); mesh->Box[7] = _V(tmul (R, _V(tpmax.x, tpmax.y, tpmax.z)) + pref); - mesh->ComputeSphere(); mesh->MapVertices(TileManager2Base::pDev); return mesh; @@ -686,9 +679,9 @@ VBMESH *Tile::CreateMesh_hemisphere (int grd, float *elev, double globelev) if (elev) eradius += (double)elev[(grd+1-y)*TILE_ELEVSTRIDE + x+1]; nml = _V(slat*clng, clat, slat*slng); pos = nml*eradius; - vtx->x = D3DVAL(pos.x); vtx->nx = D3DVAL(nml.x); - vtx->y = D3DVAL(pos.y); vtx->ny = D3DVAL(nml.y); - vtx->z = D3DVAL(pos.z); vtx->nz = D3DVAL(nml.z); + vtx->x = float(pos.x); vtx->nx = float(nml.x); + vtx->y = float(pos.y); vtx->ny = float(nml.y); + vtx->z = float(pos.z); vtx->nz = float(nml.z); FLOAT tu = a*(FLOAT)x + du; vtx->tu0 = tu; // vtx->tu1 = tu; vtx->tv0 = tv; // vtx->tv1 = tv; @@ -719,9 +712,9 @@ VBMESH *Tile::CreateMesh_hemisphere (int grd, float *elev, double globelev) } nml = _V(0,1,0); pos = nml*eradius; - vtx->x = D3DVAL(pos.x); vtx->nx = D3DVAL(nml.x); - vtx->y = D3DVAL(pos.y); vtx->ny = D3DVAL(nml.y); - vtx->z = D3DVAL(pos.z); vtx->nz = D3DVAL(nml.z); + vtx->x = float(pos.x); vtx->nx = float(nml.x); + vtx->y = float(pos.y); vtx->ny = float(nml.y); + vtx->z = float(pos.z); vtx->nz = float(nml.z); vtx->tu0 = 0.5f; // vtx->tu1 = 0.5f; vtx->tv0 = 0.0f; // vtx->tv1 = 0.0f; vtx++; @@ -737,9 +730,9 @@ VBMESH *Tile::CreateMesh_hemisphere (int grd, float *elev, double globelev) } nml = _V(0,-1,0); pos = nml*eradius; - vtx->x = D3DVAL(pos.x); vtx->nx = D3DVAL(nml.x); - vtx->y = D3DVAL(pos.y); vtx->ny = D3DVAL(nml.y); - vtx->z = D3DVAL(pos.z); vtx->nz = D3DVAL(nml.z); + vtx->x = float(pos.x); vtx->nx = float(nml.x); + vtx->y = float(pos.y); vtx->ny = float(nml.y); + vtx->z = float(pos.z); vtx->nz = float(nml.z); vtx->tu0 = 0.5f; // vtx->tu1 = 0.5f; vtx->tv0 = 1.0f; // vtx->tv1 = 1.0f; vtx++; diff --git a/OVP/D3D9Client/Tilemgr2.h b/OVP/D3D9Client/Tilemgr2.h index f5fc3a271..a2f797a4d 100644 --- a/OVP/D3D9Client/Tilemgr2.h +++ b/OVP/D3D9Client/Tilemgr2.h @@ -106,7 +106,7 @@ class Tile inline void GetIndex(int *lng, int *lat) const { *lng = ilng, *lat = ilat; } inline bool HasOwnTex() const { return owntex; } inline bool HasOwnElev() const { return has_elevfile; } - inline void GetWorldMatrix(void *pOut) const { memcpy(pOut, &mWorld, sizeof(D3DXMATRIX)); } + inline void GetWorldMatrix(void *pOut) const { memcpy(pOut, &mWorld, sizeof(FMATRIX4)); } bool PreDelete(); // Prepare tile for deletion. Return false if tile is locked @@ -126,8 +126,8 @@ class Tile // Match edges with neighbour tiles float GetBoundingSphereRad() const; - D3DXVECTOR3 GetBoundingSpherePos() const; - bool Pick(const LPD3DXMATRIX pW, const D3DXVECTOR3 *vDir, TILEPICK &result); + FVECTOR3 GetBoundingSpherePos() const; + bool Pick(const FMATRIX4* pW, const FVECTOR3 *vDir, TILEPICK &result); FVECTOR4 GetTexRangeDX (const TEXCRDRANGE2 *subrange) const; inline const TEXCRDRANGE2 *GetTexRange () const { return &texrange; } // Returns the tile's texture coordinate range @@ -212,7 +212,7 @@ class Tile float tgtscale; public: - D3DXMATRIX mWorld; + FMATRIX4 mWorld; MATRIX4 dmWorld; TILEBOUNDS bnd; }; @@ -441,7 +441,7 @@ class TileManager2: public TileManager2Base { int GetElevation(double lng, double lat, double *elev, FVECTOR3 *nrm, SurfTile **cache); void Unload(int lvl); - void Pick(D3DXVECTOR3 &vRay, TILEPICK *pPick); + void Pick(FVECTOR3 &vRay, TILEPICK *pPick); // v2 Labels interface ----------------------------------------------- void CreateLabels(); diff --git a/OVP/D3D9Client/Tilemgr2_imp.hpp b/OVP/D3D9Client/Tilemgr2_imp.hpp index 1322b51a9..d5b065758 100644 --- a/OVP/D3D9Client/Tilemgr2_imp.hpp +++ b/OVP/D3D9Client/Tilemgr2_imp.hpp @@ -131,7 +131,7 @@ void TileManager2Base::ProcessNode (QuadTreeNode *node) if (ElevMode == eElevMode::ForcedElevated) bNoRelease = true; tile->dmWorld = WorldMatrix(ilng, nlng, ilat, nlat); - MATRIX4toD3DMATRIX(tile->dmWorld, tile->mWorld); + tile->mWorld = FMATRIX4(tile->dmWorld); if (bFreeze) { for (int i = 0; i < 4; i++) { diff --git a/OVP/D3D9Client/VBase.cpp b/OVP/D3D9Client/VBase.cpp index a748e7a12..9ab542e0d 100644 --- a/OVP/D3D9Client/VBase.cpp +++ b/OVP/D3D9Client/VBase.cpp @@ -29,13 +29,13 @@ #pragma warning(push) #pragma warning(disable : 4838) -#include +#include #pragma warning(pop) typedef struct { float rad; float width, length, height; - D3DXVECTOR3 pos, min, max; + FVECTOR3 pos, min, max; } MeshStats; @@ -44,7 +44,7 @@ void CheckMeshStats(MESHHANDLE hMesh, MeshStats *stats) int nGrp = oapiMeshGroupCount(hMesh); if (nGrp == 0) return; - XMVECTOR mi = XMLoadFloat3(ptr(XMFLOAT3(1e12f, 1e12f, 1e12f))); + XMVECTOR mi = XMLoadFloat3(&(XMFLOAT3(1e12f, 1e12f, 1e12f))); XMVECTOR mx = -mi; for (int i = 0; i < nGrp; i++) { @@ -65,7 +65,7 @@ void CheckMeshStats(MESHHANDLE hMesh, MeshStats *stats) stats->height = stats->max.y - stats->min.y; stats->length = stats->max.z - stats->min.z; stats->pos = (stats->max + stats->min) * 0.5f; - stats->rad = D3DXVec3Length(ptr(stats->max + stats->min)) * 0.5f; + stats->rad = length(stats->max + stats->min) * 0.5f; } @@ -107,7 +107,7 @@ vBase::vBase (OBJHANDLE _hObj, const Scene *scene, vPlanet *_vP): vObject (_hObj mGlobalRot = mul(plrot, mGlobalRot); - D3DXMatrixIdentity(&mGlobalRotDX); + oapiMatrixIdentity(&mGlobalRotDX); D3DMAT_SetRotation(&mGlobalRotDX, &mGlobalRot); //------------------------------------------------------------------------ @@ -197,10 +197,10 @@ VECTOR3 vBase::FromLocal(VECTOR3 pos) const // =========================================================================================== // -void vBase::FromLocal(VECTOR3 pos, D3DXVECTOR3 *pTgt) const +void vBase::FromLocal(VECTOR3 pos, FVECTOR3 *pTgt) const { - D3DXVECTOR3 pv(float(pos.x-vLocalPos.x), float(pos.y-vLocalPos.y), float(pos.z-vLocalPos.z)); - D3DXVec3TransformNormal(pTgt, &pv, &mGlobalRotDX); + FVECTOR3 pv(float(pos.x-vLocalPos.x), float(pos.y-vLocalPos.y), float(pos.z-vLocalPos.z)); + *pTgt = oapiTransformNormal(&pv, &mGlobalRotDX); } // =========================================================================================== @@ -286,13 +286,13 @@ bool vBase::GetMinMaxDistance(float *zmin, float *zmax, float *dmin) { if (bBSRecompute) UpdateBoundingBox(); - D3DXMATRIX mWorldView; + FMATRIX4 mWorldView; Scene *scn = gc->GetScene(); - D3DXVECTOR4 Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); + FVECTOR4 Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); - D3DXMatrixMultiply(&mWorldView, &mWorld, scn->GetViewMatrix()); + oapiMatrixMultiply(&mWorldView, &mWorld, scn->GetViewMatrix()); if (tilemesh) { D9ComputeMinMaxDistance(gc->GetDevice(), tilemesh->GetAABB(), &mWorldView, &Field, zmin, zmax, dmin); @@ -386,22 +386,22 @@ bool vBase::RenderSurface(LPDIRECT3DDEVICE9 dev) if (!active) return false; if (!IsVisible()) return false; - pCurrentVisual = this; + g_pCurrentVisual = this; // render tiles if (tilemesh) { - uCurrentMesh = 0; // Used for debugging + g_uCurrentMesh = 0; // Used for debugging tilemesh->SetSunLight(&sunLight); tilemesh->RenderBaseTile(&mWorld); - ++uCurrentMesh; + ++g_uCurrentMesh; } // render generic objects under shadows if (nstructure_bs) { for (DWORD i = 0; i < nstructure_bs; ++i) { structure_bs[i]->SetSunLight(&sunLight); - structure_bs[i]->Render(&mWorld, RENDER_BASEBS); - ++uCurrentMesh; + structure_bs[i]->Render(&mWorld, nullptr, RENDER_BASEBS); + ++g_uCurrentMesh; } } @@ -416,20 +416,20 @@ bool vBase::RenderStructures(LPDIRECT3DDEVICE9 dev) if (!active) return false; if (!IsVisible()) return false; - pCurrentVisual = this; - uCurrentMesh = 0; // Used for debugging + g_pCurrentVisual = this; + g_uCurrentMesh = 0; // Used for debugging - if (tilemesh) uCurrentMesh++; - uCurrentMesh += nstructure_bs; + if (tilemesh) g_uCurrentMesh++; + g_uCurrentMesh += nstructure_bs; // render generic objects above shadows for (DWORD i=0; iGetBoundingSpherePos(); - FVECTOR3 qw = TransformCoord(bs, mWorld); + FVECTOR3 qw = oapiTransformCoord(&bs, &mWorld); D3D9Sun sp = vP->GetObjectAtmoParams(qw._V() + vP->CameraPos()); structure_as[i]->SetSunLight(&sp); - structure_as[i]->Render(&mWorld, RENDER_BASE); - ++uCurrentMesh; + structure_as[i]->Render(&mWorld, nullptr, RENDER_BASE); + ++g_uCurrentMesh; } return true; } @@ -442,7 +442,7 @@ void vBase::RenderRunwayLights(LPDIRECT3DDEVICE9 dev) if (!active) return; if (!IsVisible()) return; - pCurrentVisual = this; + g_pCurrentVisual = this; for(int i=0; iGetConfigParam(CFGPRM_GETDEBUGFLAGS); if (flags&DBG_FLAGS_SELVISONLY && this!=DebugControls::GetVisual()) return; // Used for debugging if (flags&DBG_FLAGS_BOXES) { - D3DXMATRIX id; - D3D9Effect::RenderBoundingBox(&mWorld, D3DXMatrixIdentity(&id), &BBox.min, &BBox.max, ptr(D3DXVECTOR4(1,0,1,0.75f))); + FMATRIX4 id; + D3D9Effect::RenderBoundingBox(&mWorld, &FMATRIX_Identity, &BBox.mn, &BBox.mx, &(FVECTOR4(1,0,1,0.75f))); } } } @@ -475,14 +475,14 @@ void vBase::RenderGroundShadow(LPDIRECT3DDEVICE9 dev, float alpha) if (!IsVisible()) return; if (Config->TerrainShadowing == 0) return; - pCurrentVisual = this; + g_pCurrentVisual = this; VECTOR3 sd; oapiGetGlobalPos(hObj, &sd); normalise(sd); MATRIX3 mRot; oapiGetRotationMatrix(hObj, &mRot); - D3DXVECTOR3 lsun = D3DXVEC(tmul(mRot, sd)); + FVECTOR3 lsun = _F(tmul(mRot, sd)); if (lsun.y > -0.07f) return; @@ -491,11 +491,11 @@ void vBase::RenderGroundShadow(LPDIRECT3DDEVICE9 dev, float alpha) // build shadow projection matrix - D3DXMATRIX mProj; + FMATRIX4 mProj; OBJHANDLE hPlanet = oapiGetBasePlanet(hObj); double prad = oapiGetSize(hPlanet); - D3DXVECTOR4 param = D9OffsetRange(prad, 30e3); + FVECTOR4 param = D9OffsetRange(prad, 30e3); for (DWORD i=0; iRenderShadowsEx(scale, &mProj, &mWorld, &nrml, ¶m); } diff --git a/OVP/D3D9Client/VBase.h b/OVP/D3D9Client/VBase.h index 5544ad006..bd3791ac6 100644 --- a/OVP/D3D9Client/VBase.h +++ b/OVP/D3D9Client/VBase.h @@ -51,7 +51,7 @@ class vBase: public vObject { // Convert from time invariant geocentric frame to base centric system (Inverse of the ToLocal) // VECTOR3 FromLocal(VECTOR3 pos) const; - void FromLocal(VECTOR3 pos, D3DXVECTOR3 *pTgt) const; + void FromLocal(VECTOR3 pos, FVECTOR3 *pTgt) const; void RenderRunwayLights (LPDIRECT3DDEVICE9 dev); bool RenderSurface (LPDIRECT3DDEVICE9 dev); @@ -89,7 +89,7 @@ class vBase: public vObject { class vPlanet *vP; VECTOR3 vLocalPos; MATRIX3 mGlobalRot; - D3DXMATRIX mGlobalRotDX; + FMATRIX4 mGlobalRotDX; int numRunwayLights; RunwayLights** runwayLights; diff --git a/OVP/D3D9Client/VObject.cpp b/OVP/D3D9Client/VObject.cpp index d18642ba2..a0ebeb2be 100644 --- a/OVP/D3D9Client/VObject.cpp +++ b/OVP/D3D9Client/VObject.cpp @@ -57,7 +57,7 @@ vObject::vObject(OBJHANDLE _hObj, const Scene *scene) , lng(0), lat(0) { _TRACE; - D3DXMatrixIdentity(&mWorld); + oapiMatrixIdentity(&mWorld); if (_hObj) size = oapiGetSize(_hObj); else size = 0; dmWorld = identity4(); @@ -102,8 +102,10 @@ void vObject::GlobalInit(D3D9Client *gclient) // hStockMesh[D3D9SM_ARROW] = new D3D9Mesh("D3D9Arrow"); hStockMesh[D3D9SM_SPHERE] = new D3D9Mesh("D3D9Sphere"); + hStockMesh[D3D9SM_BOX] = new D3D9Mesh("D3D9Box"); hStockMesh[D3D9SM_SPHERE]->SetDualSided(0, true); + hStockMesh[D3D9SM_BOX]->SetDualSided(0, true); } @@ -216,11 +218,10 @@ void vObject::UpdateBoundingBox() // =========================================================================================== // -D3DXVECTOR3 vObject::GetBoundingSpherePosDX() +FVECTOR3 vObject::GetBoundingSpherePosDX() { if (bBSRecompute) UpdateBoundingBox(); - D3DXVECTOR3 pos; - D3DXVec3TransformCoord(&pos, (LPD3DXVECTOR3)&BBox.bs, &mWorld); + FVECTOR3 pos = oapiTransformCoord(&_F(BBox.bs), &mWorld); return pos; } @@ -228,7 +229,7 @@ D3DXVECTOR3 vObject::GetBoundingSpherePosDX() // VECTOR3 vObject::GetBoundingSpherePos() { - D3DXVECTOR3 pos = GetBoundingSpherePosDX(); + FVECTOR3 pos = GetBoundingSpherePosDX(); return _V((double)pos.x, (double)pos.y, (double)pos.z); } @@ -262,7 +263,7 @@ bool vObject::IsVisible() if ((objtp == OBJTP_VESSEL) && apprad < 0.005*apr) return false; if ((objtp == OBJTP_SURFBASE) && apprad < 0.02*apr) return false; - return gc->GetScene()->IsVisibleInCamera(ptr(D3DXVEC(pos)), rad); + return gc->GetScene()->IsVisibleInCamera(ptr(_F(pos)), rad); /* if (bVis) { @@ -292,15 +293,14 @@ void vObject::RenderSpot(LPDIRECT3DDEVICE9 dev, const VECTOR3 *ofs, float size, double cosa = dotp (unit(gpos), unit(gpos - camp)); double intens = (lighting ? 0.5 * ((1.0-ambient)*cosa + 1.0+ambient) : 1.0); - D3DXMATRIX W; - D3DXVECTOR3 vPos(float(pos.x), float(pos.y), float(pos.z)); - D3DXVECTOR3 vCam; - D3DXVec3Normalize(&vCam, &vPos); + FMATRIX4 W; + FVECTOR3 vPos(float(pos.x), float(pos.y), float(pos.z)); + FVECTOR3 vCam = unit(vPos); D3DMAT_CreateX_Billboard(&vCam, &vPos, size, &W); - D3DXCOLOR color((float)col.x, (float)col.y, (float)col.z, 1.0f); + FVECTOR4 color((float)col.x, (float)col.y, (float)col.z, 1.0f); - D3D9Effect::RenderSpot((float)intens, &color, (const LPD3DXMATRIX)&W, blobtex[shape]); + D3D9Effect::RenderSpot((float)intens, &color, (const FMATRIX4*)&W, blobtex[shape]); } @@ -325,21 +325,20 @@ void vObject::RenderDot(LPDIRECT3DDEVICE9 dev) if (apr<0.3) s = 1.0; float scale = float(size * ds * s/apr); - D3DXMATRIX W; - D3DXVECTOR3 vCam; - D3DXVECTOR3 vPos(float(cpos.x), float(cpos.y), float(cpos.z)); + FMATRIX4 W; + FVECTOR3 vPos(float(cpos.x), float(cpos.y), float(cpos.z)); vPos*=float(ds); - D3DXVec3Normalize(&vCam, &vPos); + FVECTOR3 vCam = unit(vPos); D3DMAT_CreateX_Billboard(&vCam, &vPos, scale, &W); float ints = float(sqrt(1.0+dotp(unit(gpos-spos), unit(cpos)))) * 1.0f; if (ints>1.0f) ints=1.0f; - D3DXCOLOR color(float(albedo.x)*ints, float(albedo.y)*ints, float(albedo.z)*ints, 1.0f); + FVECTOR4 color(float(albedo.x)*ints, float(albedo.y)*ints, float(albedo.z)*ints, 1.0f); - D3D9Effect::RenderSpot(1.0f, &color, (const LPD3DXMATRIX)&W, blobtex[0]); + D3D9Effect::RenderSpot(1.0f, &color, (const FMATRIX4*)&W, blobtex[0]); } @@ -364,24 +363,24 @@ void vObject::RenderVectors (LPDIRECT3DDEVICE9 dev, D3D9Pad* pSkp) //scale *= 0.99f; // 1% "slimmer" to avoid z-fighting with force vector(s) float ascale = float(size) * sclset * 0.5f; - RenderAxisVector(pSkp, ptr(D3DXCOLOR(1, 0, 0, alpha)), _V(1, 0, 0), ascale, scale); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(1, 0, 0, alpha)), _V(1, 0, 0), ascale, scale, "+X"); + RenderAxisVector(pSkp, ptr(FVECTOR4(1, 0, 0, alpha)), _V(1, 0, 0), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1, 0, 0, alpha)), _V(1, 0, 0), ascale, scale, "+X"); - RenderAxisVector(pSkp, ptr(D3DXCOLOR(0, 1, 0, alpha)), _V(0, 1, 0), ascale, scale); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(0, 1, 0, alpha)), _V(0, 1, 0), ascale, scale, "+Y"); + RenderAxisVector(pSkp, ptr(FVECTOR4(0, 1, 0, alpha)), _V(0, 1, 0), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0, 1, 0, alpha)), _V(0, 1, 0), ascale, scale, "+Y"); - RenderAxisVector(pSkp, ptr(D3DXCOLOR(0, 0, 1, alpha)), _V(0, 0, 1), ascale, scale); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(0, 0, 1, alpha)), _V(0, 0, 1), ascale, scale, "+Z"); + RenderAxisVector(pSkp, ptr(FVECTOR4(0, 0, 1, alpha)), _V(0, 0, 1), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0, 0, 1, alpha)), _V(0, 0, 1), ascale, scale, "+Z"); if (favmode & FAV_NEGATIVE) { - RenderAxisVector(pSkp, ptr(D3DXCOLOR(1, 0, 0, alpha * 0.5f)), _V(-1, 0, 0), ascale, scale); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(1, 0, 0, alpha)), _V(-1, 0, 0), ascale, scale, "-X"); + RenderAxisVector(pSkp, ptr(FVECTOR4(1, 0, 0, alpha * 0.5f)), _V(-1, 0, 0), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1, 0, 0, alpha)), _V(-1, 0, 0), ascale, scale, "-X"); - RenderAxisVector(pSkp, ptr(D3DXCOLOR(0, 1, 0, alpha * 0.5f)), _V(0, -1, 0), ascale, scale); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(0, 1, 0, alpha)), _V(0, -1, 0), ascale, scale, "-Y"); + RenderAxisVector(pSkp, ptr(FVECTOR4(0, 1, 0, alpha * 0.5f)), _V(0, -1, 0), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0, 1, 0, alpha)), _V(0, -1, 0), ascale, scale, "-Y"); - RenderAxisVector(pSkp, ptr(D3DXCOLOR(0, 0, 1, alpha * 0.5f)), _V(0, 0, -1), ascale, scale); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(0, 0, 1, alpha)), _V(0, 0, -1), ascale, scale, "-Z"); + RenderAxisVector(pSkp, ptr(FVECTOR4(0, 0, 1, alpha * 0.5f)), _V(0, 0, -1), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0, 0, 1, alpha)), _V(0, 0, -1), ascale, scale, "-Z"); } } } @@ -391,9 +390,9 @@ void vObject::RenderVectors (LPDIRECT3DDEVICE9 dev, D3D9Pad* pSkp) // =========================================================================================== // -void vObject::RenderAxisVector(D3D9Pad *pSkp, const D3DXCOLOR *pColor, VECTOR3 vector, float lscale, float size, bool bLog) +void vObject::RenderAxisVector(D3D9Pad *pSkp, const FVECTOR4 *pColor, VECTOR3 vector, float lscale, float size, bool bLog) { - D3DXMATRIX W; + FMATRIX4 W; VECTOR3 dir = mul(grot, vector); VECTOR3 camp = gc->GetScene()->GetCameraGPos(); @@ -401,19 +400,19 @@ void vObject::RenderAxisVector(D3D9Pad *pSkp, const D3DXCOLOR *pColor, VECTOR3 v VECTOR3 pos = gpos - camp; VECTOR3 rot = crossp(pos, vector); - VECTOR3 y = mul (grot, unit(vector)) * size; - VECTOR3 x = mul (grot, unit(rot)) * size; - VECTOR3 z = mul (grot, unit(crossp(vector, rot))) * size; + VECTOR3 y = mul (grot, unit(vector)) * double(size); + VECTOR3 x = mul (grot, unit(rot)) * double(size); + VECTOR3 z = mul (grot, unit(crossp(vector, rot))) * double(size); - D3DXMatrixIdentity(&W); + oapiMatrixIdentity(&W); - W._11 = float(x.x); W._12 = float(x.y); W._13 = float(x.z); - W._21 = float(y.x); W._22 = float(y.y); W._23 = float(y.z); - W._31 = float(z.x); W._32 = float(z.y); W._33 = float(z.z); + W.m11 = float(x.x); W.m12 = float(x.y); W.m13 = float(x.z); + W.m21 = float(y.x); W.m22 = float(y.y); W.m23 = float(y.z); + W.m31 = float(z.x); W.m32 = float(z.y); W.m33 = float(z.z); - W._41 = float(pos.x); - W._42 = float(pos.y); - W._43 = float(pos.z); + W.m41 = float(pos.x); + W.m42 = float(pos.y); + W.m43 = float(pos.z); float len = float(length(vector)); @@ -426,10 +425,10 @@ void vObject::RenderAxisVector(D3D9Pad *pSkp, const D3DXCOLOR *pColor, VECTOR3 v // =========================================================================================== // -void vObject::RenderAxisLabel(D3D9Pad *pSkp, const D3DXCOLOR *clr, VECTOR3 vector, float lscale, float size, const char *label, bool bLog) +void vObject::RenderAxisLabel(D3D9Pad *pSkp, const FVECTOR4 *clr, VECTOR3 vector, float lscale, float size, const char *label, bool bLog) { - D3DXVECTOR3 homog, ws; - D3DXMATRIX W; + FVECTOR3 homog, ws; + FMATRIX4 W; VECTOR3 dir = mul(grot, vector); VECTOR3 camp = gc->GetScene()->GetCameraGPos(); @@ -437,34 +436,34 @@ void vObject::RenderAxisLabel(D3D9Pad *pSkp, const D3DXCOLOR *clr, VECTOR3 vecto VECTOR3 pos = gpos - camp; VECTOR3 rot = crossp(pos, vector); - VECTOR3 y = mul(grot, unit(vector)) * size; - VECTOR3 x = mul(grot, unit(rot)) * size; - VECTOR3 z = mul(grot, unit(crossp(vector, rot))) * size; + VECTOR3 y = mul(grot, unit(vector)) * double(size); + VECTOR3 x = mul(grot, unit(rot)) * double(size); + VECTOR3 z = mul(grot, unit(crossp(vector, rot))) * double(size); - D3DXMatrixIdentity(&W); + oapiMatrixIdentity(&W); - W._11 = float(x.x); W._12 = float(x.y); W._13 = float(x.z); - W._21 = float(y.x); W._22 = float(y.y); W._23 = float(y.z); - W._31 = float(z.x); W._32 = float(z.y); W._33 = float(z.z); + W.m11 = float(x.x); W.m12 = float(x.y); W.m13 = float(x.z); + W.m21 = float(y.x); W.m22 = float(y.y); W.m23 = float(y.z); + W.m31 = float(z.x); W.m32 = float(z.y); W.m33 = float(z.z); - W._41 = float(pos.x); - W._42 = float(pos.y); - W._43 = float(pos.z); + W.m41 = float(pos.x); + W.m42 = float(pos.y); + W.m43 = float(pos.z); float len = float(length(vector)); if (bLog) len = max(0.0f, 13.0f + log(len)) * lscale / size; else len = len * lscale / size; - D3DXVec3TransformCoord(&ws, ptr(D3DXVECTOR3(0, len, 0)), &W); - D3DXVec3TransformCoord(&homog, &ws, scn->GetProjectionViewMatrix()); + ws = oapiTransformCoord(ptr(FVECTOR3(0, len, 0)), &W); + homog = oapiTransformCoord(&ws, scn->GetProjectionViewMatrix()); - if (D3DXVec3Dot(&ws, scn->GetCameraZ()) < 0) return; + if (dotp(ws, *scn->GetCameraZ()) < 0) return; if (homog.x >= -1.0f && homog.x <= 1.0f && homog.y >= -1.0f && homog.y <= 1.0f) { int xc = (int)(scn->ViewW()*0.5*(1.0f + homog.x)); int yc = (int)(scn->ViewH()*0.5*(1.0f - homog.y)); - pSkp->SetTextColor(D3DXCOLOR(clr->b, clr->g, clr->r, clr->a)); + pSkp->SetTextColor(FVECTOR4(clr->b, clr->g, clr->r, clr->a).dword_abgr()); pSkp->Text(xc + 10, yc, label, lstrlen(label)); } } diff --git a/OVP/D3D9Client/VObject.h b/OVP/D3D9Client/VObject.h index 08704b9c9..7662aea23 100644 --- a/OVP/D3D9Client/VObject.h +++ b/OVP/D3D9Client/VObject.h @@ -14,7 +14,7 @@ #include "Scene.h" #include "AABBUtil.h" #include -#include +#include "MathAPI.h" #include extern class D3D9Config *Config; @@ -101,7 +101,7 @@ class vObject: public oapi::VisObject { */ inline bool IsActive () const { return active; } - inline const D3DXMATRIX * MWorld() const { return &mWorld; } + inline const FMATRIX4 * MWorld() const { return &mWorld; } inline Scene * GetScene() const { return scn; } inline oapi::D3D9Client * GetClient() const { return gc; } @@ -120,7 +120,8 @@ class vObject: public oapi::VisObject { * \return Mesh handle * \note Currently only vessel visuals return anything here. */ - virtual MESHHANDLE GetMesh (UINT idx) { return NULL; } + virtual D3D9Mesh * GetMesh (UINT idx) { return NULL; } + virtual DWORD GetMeshVisMode (UINT idx) { return MESHVIS_ALWAYS; } virtual void PreInitObject() { } @@ -130,7 +131,7 @@ class vObject: public oapi::VisObject { virtual bool IsVisible(); virtual DWORD GetMeshCount(); - D3DXVECTOR3 GetBoundingSpherePosDX(); + FVECTOR3 GetBoundingSpherePosDX(); VECTOR3 GetBoundingSpherePos(); float GetBoundingSphereRadius(); const char *GetName() const; @@ -241,8 +242,8 @@ class vObject: public oapi::VisObject { protected: void RenderSpot(LPDIRECT3DDEVICE9 dev, const VECTOR3 *ofs, float size, const VECTOR3 &col, bool lighting, int shape); - void RenderAxisVector(D3D9Pad *pSkp, const D3DXCOLOR *pColor, VECTOR3 vector, float lscale, float size, bool bLog=false); - void RenderAxisLabel(D3D9Pad *pSkp, const D3DXCOLOR *clr, VECTOR3 vector, float lscale, float size, const char *label, bool bLog=false); + void RenderAxisVector(D3D9Pad *pSkp, const FVECTOR4 *pColor, VECTOR3 vector, float lscale, float size, bool bLog=false); + void RenderAxisLabel(D3D9Pad *pSkp, const FVECTOR4 *clr, VECTOR3 vector, float lscale, float size, const char *label, bool bLog=false); static oapi::D3D9Client *gc; // graphics client instance pointer @@ -262,7 +263,7 @@ class vObject: public oapi::VisObject { VECTOR3 gpos; // Global position MATRIX3 grot; // Global rotation MATRIX4 dmWorld; // world matrix in double precision - D3DXMATRIX mWorld; // world matrix in single precision + FMATRIX4 mWorld; // world matrix in single precision double size; // object radius [m] double cdist; // current camera distance double sunapprad; // Apparent size of the sun diff --git a/OVP/D3D9Client/VPlanet.cpp b/OVP/D3D9Client/VPlanet.cpp index 5d15aadbe..1bc0bb14c 100644 --- a/OVP/D3D9Client/VPlanet.cpp +++ b/OVP/D3D9Client/VPlanet.cpp @@ -687,9 +687,8 @@ bool vPlanet::GetMinMaxDistance(float *zmin, float *zmax, float *dmin) if (mesh==NULL) return false; if (bBSRecompute) UpdateBoundingBox(); - D3DXVECTOR3 pos = D3DXVECTOR3(mWorld._41, mWorld._42, mWorld._43); - - float dst = D3DXVec3Length(&pos); + FVECTOR3 pos = FVECTOR3(mWorld.m41, mWorld.m42, mWorld.m43); + float dst = length(pos); *dmin = dst - float(size); *zmin = *dmin; @@ -718,7 +717,7 @@ SurfTile * vPlanet::FindTile(double lng, double lat, int maxlvl) // ============================================================== -void vPlanet::PickSurface(D3DXVECTOR3 &vRay, TILEPICK *result) +void vPlanet::PickSurface(FVECTOR3 &vRay, TILEPICK *result) { if (surfmgr2) { surfmgr2->Pick(vRay, result); @@ -772,15 +771,15 @@ bool vPlanet::Update (bool bMainScene) } if (rescale) { rad_scale *= dist_scale; - mWorld._41 *= dist_scale; - mWorld._42 *= dist_scale; - mWorld._43 *= dist_scale; + mWorld.m41 *= dist_scale; + mWorld.m42 *= dist_scale; + mWorld.m43 *= dist_scale; } // scale up from template sphere radius 1 - mWorld._11 *= rad_scale; mWorld._12 *= rad_scale; mWorld._13 *= rad_scale; - mWorld._21 *= rad_scale; mWorld._22 *= rad_scale; mWorld._23 *= rad_scale; - mWorld._31 *= rad_scale; mWorld._32 *= rad_scale; mWorld._33 *= rad_scale; + mWorld.m11 *= rad_scale; mWorld.m12 *= rad_scale; mWorld.m13 *= rad_scale; + mWorld.m21 *= rad_scale; mWorld.m22 *= rad_scale; mWorld.m23 *= rad_scale; + mWorld.m31 *= rad_scale; mWorld.m32 *= rad_scale; mWorld.m33 *= rad_scale; // cloud layer world matrix if (prm.bCloud) { @@ -803,17 +802,17 @@ bool vPlanet::Update (bool bMainScene) // world matrix for cloud shadows on the surface memcpy (&clouddata->mWorldC0, &mWorld, sizeof (D3DMATRIX)); if (prm.cloudrot) { - static D3DXMATRIX crot (1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); - crot._11 = crot._33 = (float)cos(prm.cloudrot); - crot._13 = -(crot._31 = (float)sin(prm.cloudrot)); - D3DXMatrixMultiply (&clouddata->mWorldC0, &crot, &clouddata->mWorldC0); + static FMATRIX4 crot (1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); + crot.m11 = crot.m33 = (float)cos(prm.cloudrot); + crot.m13 = -(crot.m31 = (float)sin(prm.cloudrot)); + oapiMatrixMultiply (&clouddata->mWorldC0, &crot, &clouddata->mWorldC0); } // world matrix for cloud layer memcpy (&clouddata->mWorldC, &clouddata->mWorldC0, sizeof (D3DMATRIX)); for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { - clouddata->mWorldC.m[i][j] *= cloudscale; + clouddata->mWorldC.data[i + j<<2] *= cloudscale; } // set microtexture intensity @@ -921,15 +920,15 @@ bool vPlanet::Render(LPDIRECT3DDEVICE9 dev) _TRACE; if (!active) return false; - const Scene::SHADOWMAPPARAM *shd = scn->GetSMapData(); + const SHADOWMAP *shd = scn->GetSMapData(ShdPackage::Main); if (DebugControls::IsActive()) { // DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); DWORD displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); vObject *vSel = DebugControls::GetVisual(); if (vSel && displ>0) { - if (vSel->GetObjectA()) { - if (oapiGetObjectType(vSel->GetObjectA())==OBJTP_VESSEL) return false; + if (vSel->GetObjHandle()) { + if (oapiGetObjectType(vSel->GetObjHandle())==OBJTP_VESSEL) return false; } } } @@ -942,7 +941,7 @@ bool vPlanet::Render(LPDIRECT3DDEVICE9 dev) memset(fcv, 0, sizeof(FlowControlVS)); - pCurrentVisual = this; + g_pCurrentVisual = this; if (renderpix) { // render as 2x2 pixel block RenderDot (dev); @@ -961,31 +960,32 @@ bool vPlanet::Render(LPDIRECT3DDEVICE9 dev) HR(D3D9Effect::FX->SetBool(D3D9Effect::eEnvMapEnable, false)); HR(D3D9Effect::FX->SetBool(D3D9Effect::eShadowToggle, false)); - if (shd->pShadowMap && (scn->GetRenderPass() == RENDERPASS_MAINSCENE) && (Config->TerrainShadowing == 2)) { + if (shd->IsValid() && (scn->GetRenderPass() == RENDERPASS_MAINSCENE) && (Config->TerrainShadowing == 2)) { if (scn->GetCameraAltitude() < 10e3 || IsMesh()) { float s = float(shd->size); float is = 1.0f / s; float qw = 1.0f / float(Config->ShadowMapSize); - HR(D3D9Effect::FX->SetMatrix(D3D9Effect::eLVP, &shd->mViewProj)); - HR(D3D9Effect::FX->SetTexture(D3D9Effect::eShadowMap, shd->pShadowMap)); - HR(D3D9Effect::FX->SetVector(D3D9Effect::eSHD, ptr(D3DXVECTOR4(s, is, qw, 0)))); + HR(D3D9Effect::FX->SetMatrix(D3D9Effect::eLVP, _DX(shd->mLVP))); + HR(D3D9Effect::FX->SetVector(D3D9Effect::eSHD, _DX(FVECTOR4(s, is, qw, 0)))); HR(D3D9Effect::FX->SetBool(D3D9Effect::eShadowToggle, true)); + + D3D9Mesh::SetShadows(shd); } } DWORD amb = prm.amb0col; float fogfactor; - D3DCOLOR bg = scn->GetBgColour(); + DWORD bg = scn->GetBgColour(); prm.bFog = prm.bFogEnabled; prm.bTint = prm.bFogEnabled; prm.bAddBkg = ((bg & 0xFFFFFF) && (hObj != scn->GetCameraProxyBody())); prm.FogDensity = 0.0f; - prm.SkyColor = D3DXCOLOR(bg); - prm.AmbColor = D3DXCOLOR(0,0,0,0); - prm.FogColor = D3DXCOLOR(0,0,0,0); - prm.TintColor = D3DXCOLOR(0,0,0,0); - prm.SunDir = _D3DXVECTOR3(SunDirection()); + prm.SkyColor = FVECTOR4(bg); + prm.AmbColor = _F4(0,0,0,0); + prm.FogColor = _F4(0,0,0,0); + prm.TintColor = _F4(0,0,0,0); + prm.SunDir = _F(SunDirection()); SetupEclipse(); @@ -1009,7 +1009,7 @@ bool vPlanet::Render(LPDIRECT3DDEVICE9 dev) if (prm.bAtm) { if (ModLighting (amb)) - prm.AmbColor = D3DXCOLOR(amb); + prm.AmbColor = FVECTOR4(amb); } if (prm.bFog) { // set up distance fog @@ -1047,14 +1047,14 @@ bool vPlanet::Render(LPDIRECT3DDEVICE9 dev) float gfog = (float)(bright*(min(1.0,fogcol.y)+0.0)); float bfog = (float)(bright*(min(1.0,fogcol.z)+0.0)); prm.FogDensity = fogfactor; - prm.FogColor = D3DXCOLOR(rfog, gfog, bfog, 1.0f); + prm.FogColor = FVECTOR4(rfog, gfog, bfog, 1.0f); } } if (mesh) { mesh->SetSunLight(scn->GetSun()); - mesh->Render(&mWorld, RENDER_ASTEROID); + mesh->RenderFast(&mWorld, RENDER_ASTEROID); } else { RenderSphere (dev); } @@ -1176,7 +1176,7 @@ void vPlanet::RenderSphere (LPDIRECT3DDEVICE9 dev) D3D9Effect::FX->GetFloat(D3D9Effect::eFogDensity, &fogfactor); dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); if (prm.bFog) D3D9Effect::FX->SetFloat(D3D9Effect::eFogDensity, fogfactor/dist_scale); - surfmgr->SetAmbientColor(prm.AmbColor); + surfmgr->SetAmbientColor(prm.AmbColor.dword_abgr()); surfmgr->Render (dev, mWorld, dist_scale, patchres, 0.0, prm.bFog); // surface if (prm.bFog) D3D9Effect::FX->SetFloat(D3D9Effect::eFogDensity, fogfactor); } @@ -1520,8 +1520,8 @@ vPlanet::sOverlay * vPlanet::AddOverlaySurface(VECTOR4 lnglat, gcCore::OlayType if (pOld) { pOld->pSurf[int(type)] = pSrf; pOld->lnglat = lnglat; - if (pB) pOld->Blend[int(type)] = D3DXVECTOR4(pB->r, pB->g, pB->b, pB->a); - else pOld->Blend[int(type)] = D3DXVECTOR4(1, 1, 1, 1); + if (pB) pOld->Blend[int(type)] = FVECTOR4(pB->r, pB->g, pB->b, pB->a); + else pOld->Blend[int(type)] = FVECTOR4(1.0f, 1.0f, 1.0f, 1.0f); return pOld; } @@ -1529,8 +1529,8 @@ vPlanet::sOverlay * vPlanet::AddOverlaySurface(VECTOR4 lnglat, gcCore::OlayType memset(&oLay->pSurf, 0, sizeof(oLay->pSurf)); oLay->pSurf[int(type)] = pSrf; oLay->lnglat = lnglat; - if (pB) oLay->Blend[int(type)] = D3DXVECTOR4(pB->r, pB->g, pB->b, pB->a); - else oLay->Blend[int(type)] = D3DXVECTOR4(1, 1, 1, 1); + if (pB) oLay->Blend[int(type)] = FVECTOR4(pB->r, pB->g, pB->b, pB->a); + else oLay->Blend[int(type)] = FVECTOR4(1.0f, 1.0f, 1.0f, 1.0f); overlays.push_back(oLay); return oLay; } diff --git a/OVP/D3D9Client/VPlanet.h b/OVP/D3D9Client/VPlanet.h index cf8eeed64..137230a85 100644 --- a/OVP/D3D9Client/VPlanet.h +++ b/OVP/D3D9Client/VPlanet.h @@ -217,7 +217,7 @@ class vPlanet: public vObject { struct sOverlay { LPDIRECT3DTEXTURE9 pSurf[4]; - D3DXVECTOR4 Blend[4]; + FVECTOR4 Blend[4]; VECTOR4 lnglat; }; @@ -259,7 +259,7 @@ class vPlanet: public vObject { void SetMicroTexture(LPDIRECT3DTEXTURE9 pSrc, int slot); int GetElevation(double lng, double lat, double *elv, FVECTOR3 *nrm = NULL) const; SurfTile * FindTile(double lng, double lat, int maxres); - void PickSurface(D3DXVECTOR3 &vRay, TILEPICK *pPick); + void PickSurface(FVECTOR3 &vRay, TILEPICK *pPick); DWORD GetPhysicsPatchRes() const { return physics_patchres; } sOverlay * AddOverlaySurface(VECTOR4 lnglat, gcCore::OlayType type, LPDIRECT3DTEXTURE9 pSrf = NULL, sOverlay *pOld = NULL, const FVECTOR4* pB = NULL); sOverlay * IntersectOverlay(VECTOR4 bounds, FVECTOR4* texcoord) const; @@ -343,11 +343,11 @@ class vPlanet: public vObject { //bool bCloudFlatShadows; ///< render cloud shadows onto a sphere? // Shader Params - D3DXCOLOR TintColor; - D3DXCOLOR AmbColor; - D3DXCOLOR FogColor; - D3DXCOLOR SkyColor; - D3DXVECTOR3 SunDir; + FVECTOR4 TintColor; + FVECTOR4 AmbColor; + FVECTOR4 FogColor; + FVECTOR4 SkyColor; + FVECTOR3 SunDir; float FogDensity; float DistScale; } prm; @@ -436,8 +436,8 @@ class vPlanet: public vObject { CloudManager *cloudmgr; // cloud tile manager double cloudrad; // cloud layer radius [m] double viewap; // visible radius - D3DXMATRIX mWorldC; // cloud world matrix - D3DXMATRIX mWorldC0; // cloud shadow world matrix + FMATRIX4 mWorldC; // cloud world matrix + FMATRIX4 mWorldC0; // cloud shadow world matrix DWORD rendermode; // bit 0: render from below, bit 1: render from above bool cloudshadow; // render cloud shadows on the surface float shadowalpha; // alpha value for cloud shadows diff --git a/OVP/D3D9Client/VPlanetAtmo.cpp b/OVP/D3D9Client/VPlanetAtmo.cpp index e8ed2bd5d..9ac2e39de 100644 --- a/OVP/D3D9Client/VPlanetAtmo.cpp +++ b/OVP/D3D9Client/VPlanetAtmo.cpp @@ -234,7 +234,7 @@ FVECTOR2 vPlanet::Gauss4(float cos_dir, float r0, float dist, FVECTOR2 ih0) // float vPlanet::SunAltitude() { - float d = dot(cp.toCam, cp.toSun); + float d = dotp(cp.toCam, cp.toSun); float q = cp.CamRad * d; // Ray's closest approach to planet @@ -303,9 +303,9 @@ FVECTOR4 vPlanet::ComputeCameraView(float angle, float rad, float distance) FVECTOR4 vPlanet::ComputeCameraView(FVECTOR3 vPos, FVECTOR3 vNrm, FVECTOR3 vRay, float r, float t_factor) { float d = 0.0f; - float a = dot(vNrm, vRay); + float a = dotp(vNrm, vRay); if (!CameraInAtmosphere()) d = RayLength(a, r); - else d = abs(dot(vPos - cp.CamPos, vRay)); + else d = abs(dotp(vPos - cp.CamPos, vRay)); return ComputeCameraView(a, r, d); } @@ -318,7 +318,7 @@ FVECTOR4 vPlanet::ComputeCameraView(FVECTOR3 vPos) float d = length(vRP); float r = length(vPos); FVECTOR3 vRay = vRP / d; - float a = dot(vPos / r, vRay); + float a = dotp(vPos / r, vRay); if (!CameraInAtmosphere()) d = RayLength(a, r); // Recompute distance to atm exit return ComputeCameraView(a, r, d); } @@ -339,12 +339,12 @@ void vPlanet::IntegrateSegment(FVECTOR3 vOrig, FVECTOR3 vRay, float len, FVECTOR { float dst = len * (iNSEG * (float(i) + 0.5f)); FVECTOR3 pos = vOrig + vRay * dst; - FVECTOR3 n = normalize(pos); - float rad = dot(n, pos); + FVECTOR3 n = unit(pos); + float rad = dotp(n, pos); float alt = rad - cp.PlanetRad; - float ang = dot(n, vRay); + float ang = dotp(n, vRay); - FVECTOR3 x = SunLightColor(-dot(n, cp.toSun), alt) * ComputeCameraView(-ang, rad, len - dst).rgb; + FVECTOR3 x = SunLightColor(-dotp(n, cp.toSun), alt) * ComputeCameraView(-ang, rad, len - dst).rgb; if (ral) { float f = exp(-alt * cp.iH.x) * iNSEG; @@ -362,7 +362,7 @@ void vPlanet::IntegrateSegment(FVECTOR3 vOrig, FVECTOR3 vRay, float len, FVECTOR if (ral) ral->a *= len; if (mie) mie->a *= len; if (tot && ral && mie) { - float dRS = dot(vRay, cp.toSun); + float dRS = dotp(vRay, cp.toSun); tot->rgb = HDR(ral->rgb * RayPhase(dRS) + mie->rgb * MiePhase(dRS)); } } @@ -399,8 +399,8 @@ float vPlanet::SunOcclusionByPlanet() { OBJHANDLE hSun = oapiGetObjectByIndex(0); VECTOR3 up = unit(-cpos); - double r = dot(up, -cpos); - double ca = dot(up, SunDirection()); + double r = dotp(up, -cpos); + double ca = dotp(up, SunDirection()); double om = saturate(1.0 - ca * ca); double qr = sqrt(om) * r; double sd = SunDistance(); @@ -477,7 +477,7 @@ FVECTOR4 vPlanet::SunLightColor(VECTOR3 relpos, double rf) // FVECTOR4 vPlanet::AmbientApprox(FVECTOR3 vNrm, bool bR) { - float dNS = -dot(vNrm, cp.toSun); + float dNS = -dotp(vNrm, cp.toSun); float fA = 1.0f - hermite(ilerp(0.0f, cp.TW_Dst, dNS)); float3 clr = (bR ? cp.RayWave : cp.cAmbient); return float4(clr, fA); @@ -517,7 +517,7 @@ D3D9Sun vPlanet::GetObjectAtmoParams(VECTOR3 vRelPos) if (((r - size) > a) || !HasAtmosphere() || !surfmgr2) // In space { op.Dir = -cp.toSun; - op.Color = (cSun.MaxRGB() > 1.0f ? cSun / cSun.MaxRGB() : cSun) * Config->GFXSunIntensity; + op.Color = (MaxRGB(cSun) > 1.0f ? cSun / MaxRGB(cSun) : cSun) * Config->GFXSunIntensity; op.Ambient = float(ambient) * 0.0039f; return op; } @@ -526,9 +526,9 @@ D3D9Sun vPlanet::GetObjectAtmoParams(VECTOR3 vRelPos) VECTOR3 vRP = (vRelPos + cpos); // Camera relative position double d = length(vRP); // Distance to camera FVECTOR3 vRay = (vRP / d); // Unit viewing ray from cameta to obj_gpos - float dNR = dot(vNrm, vRay); // cosine of (Normal/vRay) angle - float dRS = dot(vRay, cp.toSun); - float dNS = dot(vNrm, cp.toSun); + float dNR = dotp(vNrm, vRay); // cosine of (Normal/vRay) angle + float dRS = dotp(vRay, cp.toSun); + float dNS = dotp(vNrm, cp.toSun); float alt = r - size; FVECTOR4 mc = AmbientApprox(vNrm, false); @@ -536,10 +536,10 @@ D3D9Sun vPlanet::GetObjectAtmoParams(VECTOR3 vRelPos) FVECTOR3 cSunAmb = SunLightColor(-dNS, alt) * cp.cSun; - cSunAmb = cSunAmb.MaxRGB() > 1.0f ? cSunAmb / cSunAmb.MaxRGB() : cSunAmb; + cSunAmb = MaxRGB(cSunAmb) > 1.0f ? cSunAmb / MaxRGB(cSunAmb) : cSunAmb; op.Dir = -cp.toSun; - op.Color = (cSun.MaxRGB() > 1.0f ? cSun / cSun.MaxRGB() : cSun) * Config->GFXSunIntensity; + op.Color = (MaxRGB(cSun) > 1.0f ? cSun / MaxRGB(cSun) : cSun) * Config->GFXSunIntensity; op.Ambient = unit(mc.rgb + cSunAmb * 2.0f) * mc.a * mc.g; op.Ambient *= exp(-alt * cp.iH.x) * cp.rmI.x * 6e5; op.Ambient += float(ambient) * 0.0039f; @@ -644,10 +644,10 @@ void vPlanet::UpdateScatter() cp.CamPos = cam; cp.toCam = unit(cam); cp.toSun = sundir; // Sun-aligned Atmo Scatter RefFrame - cp.dCS = dot(cp.toCam, cp.toSun); - cp.ZeroAz = unit(cross(cp.toCam, cp.toSun)); // Sun-aligned Atmo Scatter RefFrame - cp.SunAz = unit(cross(cp.toCam, cp.ZeroAz)); // Sun-aligned Atmo Scatter RefFrame - cp.Up = unit(cross(cp.ZeroAz, cp.toSun)); + cp.dCS = dotp(cp.toCam, cp.toSun); + cp.ZeroAz = unit(crossp(cp.toCam, cp.toSun)); // Sun-aligned Atmo Scatter RefFrame + cp.SunAz = unit(crossp(cp.toCam, cp.ZeroAz)); // Sun-aligned Atmo Scatter RefFrame + cp.Up = unit(crossp(cp.ZeroAz, cp.toSun)); cp.HrzDst = sqrt(max(mdh, cp.CamRad2 - cp.PlanetRad2)); cp.Time = fmod(oapiGetSimTime(), 3600.0f); cp.TrGamma = 1.0f / float(atmo->tgamma); @@ -656,7 +656,7 @@ void vPlanet::UpdateScatter() cp.CosAlpha = min(1.0f, cp.PlanetRad / cp.CamRad); cp.SinAlpha = sqrt(1.0f - cp.CosAlpha * cp.CosAlpha); cp.cAmbient = atmo->acolor; - float A = dot(cp.toCam, cp.Up) * cp.CamRad; + float A = dotp(cp.toCam, cp.Up) * cp.CamRad; cp.Cr2 = A * A; float g2 = cp.CamRad2 - cp.PlanetRad2; cp.ShdDst = g2 > 0 ? sqrt(g2) : 0.0f; @@ -716,7 +716,7 @@ void vPlanet::UpdateScatter() sFlow Flow; - Flow.bCamLit = !((cp.Cr2 < cp.PlanetRad2) && (dot(cp.toCam, cp.toSun) < 0)); + Flow.bCamLit = !((cp.Cr2 < cp.PlanetRad2) && (dotp(cp.toCam, cp.toSun) < 0)); Flow.bCamInSpace = !CameraInAtmosphere(); @@ -1161,13 +1161,13 @@ vPlanet::SHDPrm vPlanet::ComputeShadow(FVECTOR3 vRay) vPlanet::SHDPrm sp; // Camera radius in "shadow" frame. - double A = dot(TestPrm.Up, TestPrm.toCam * TestPrm.CamRad); + double A = dotp(TestPrm.Up, TestPrm.toCam * TestPrm.CamRad); sp.cr = abs(A); // Projection of viewing ray on 'shadow' axes - double u = dot(vRay, TestPrm.Up); - double t = dot(vRay, TestPrm.ZeroAz); - double z = dot(vRay, TestPrm.toSun); + double u = dotp(vRay, TestPrm.Up); + double t = dotp(vRay, TestPrm.ZeroAz); + double z = dotp(vRay, TestPrm.toSun); // Cosine 'a' double a = u / sqrt(u * u + t * t); @@ -1191,7 +1191,7 @@ vPlanet::SHDPrm vPlanet::ComputeShadow(FVECTOR3 vRay) // Compute atmosphere entry and exit points // - a = -dot(TestPrm.toCam, vRay); + a = -dotp(TestPrm.toCam, vRay); k2 = TestPrm.CamRad2 * a * a; h2 = TestPrm.CamRad2 - k2; v2 = cp.AtmoRad2 - h2; @@ -1215,8 +1215,8 @@ vPlanet::SHDPrm vPlanet::ComputeShadow(FVECTOR3 vRay) else { FVECTOR3 vEn = TestPrm.CamPos + vRay * sp.se; FVECTOR3 vEx = TestPrm.CamPos + vRay * sp.sx; - if (dot(vEn, TestPrm.toSun) > 0) sp.se = invalid_val; - if (dot(vEx, TestPrm.toSun) > 0) sp.sx = invalid_val; + if (dotp(vEn, TestPrm.toSun) > 0) sp.se = invalid_val; + if (dotp(vEx, TestPrm.toSun) > 0) sp.sx = invalid_val; } return sp; @@ -1248,15 +1248,15 @@ void vPlanet::TestComputations(Sketchpad* pSkp) if (length(GetScene()->vPickRay) > 0.8f) { vRef = campos; - beta = dot(unit(vRef), GetScene()->vPickRay); + beta = dotp(unit(vRef), GetScene()->vPickRay); TestPrm.CamPos = cp.CamPos; TestPrm.toSun = cp.toSun; TestPrm.toCam = unit(vRef); TestPrm.CamRad = length(vRef); TestPrm.CamRad2 = TestPrm.CamRad * TestPrm.CamRad; - TestPrm.ZeroAz = unit(cross(TestPrm.toCam, TestPrm.toSun)); - TestPrm.SunAz = unit(cross(TestPrm.toCam, TestPrm.ZeroAz)); - TestPrm.Up = unit(cross(TestPrm.ZeroAz, TestPrm.toSun)); + TestPrm.ZeroAz = unit(crossp(TestPrm.toCam, TestPrm.toSun)); + TestPrm.SunAz = unit(crossp(TestPrm.toCam, TestPrm.ZeroAz)); + TestPrm.Up = unit(crossp(TestPrm.ZeroAz, TestPrm.toSun)); TestPrm.CosAlpha = min(1.0f, cp.PlanetRad / TestPrm.CamRad); TestPrm.SinAlpha = sqrt(1.0f - TestPrm.CosAlpha * TestPrm.CosAlpha); } @@ -1264,7 +1264,7 @@ void vPlanet::TestComputations(Sketchpad* pSkp) // Trace picking ray -------------------------------------------- // - float Ref2 = dot(vRef, vRef); + float Ref2 = dotp(vRef, vRef); float Ref = sqrt(Ref2); float ds = Ref * beta; float he2 = Ref2 - ds * ds; @@ -1293,7 +1293,7 @@ void vPlanet::TestComputations(Sketchpad* pSkp) } } GetScene()->vPickRay = 0; - vRay = normalize(vPos - vRef); // From vRef to vPos + vRay = unit(vPos - vRef); // From vRef to vPos } float cd = length(vRef - vPos); @@ -1304,11 +1304,11 @@ void vPlanet::TestComputations(Sketchpad* pSkp) D3D9DebugLog("Optical Depth=%f RGB(%f, %f, %f)", rd.a, rd.r, rd.g, rd.b); - float sl = dot(cp.toSun, TestPrm.toCam); - float sa = dot(vRay, TestPrm.toCam); + float sl = dotp(cp.toSun, TestPrm.toCam); + float sa = dotp(vRay, TestPrm.toCam); float rl = RayLength(-sa, TestPrm.CamRad); float rf = sqrt(cp.AtmoRad2 - cp.PlanetRad2); - float ph = dot(vRay, cp.toSun); + float ph = dotp(vRay, cp.toSun); FVECTOR3 cl = SunLightColor(sl, cp.CamAlt); FVECTOR4 ral, mie; @@ -1321,10 +1321,10 @@ void vPlanet::TestComputations(Sketchpad* pSkp) D3D9DebugLog("RayLength = %f vs %f, sa = %f, sl = %f", rl, rf, sa, sl); D3D9DebugLog("AtmoAlt = %f(km)", cp.AtmoAlt/1000.0f); - double u = dot(vPos, TestPrm.Up); - double t = dot(vPos, TestPrm.ZeroAz); - bool bTgt = ((u * u + t * t) < cp.PlanetRad2 && dot(vPos, TestPrm.toSun) < 0); - bool bSrc = (sp.cr < cp.PlanetRad&& dot(vRef, TestPrm.toSun) < 0); + double u = dotp(vPos, TestPrm.Up); + double t = dotp(vPos, TestPrm.ZeroAz); + bool bTgt = ((u * u + t * t) < cp.PlanetRad2 && dotp(vPos, TestPrm.toSun) < 0); + bool bSrc = (sp.cr < cp.PlanetRad&& dotp(vRef, TestPrm.toSun) < 0); float s0 = 0, s1 = 0, e0 = 0, e1 = 0, mp = 0, lf = 0; @@ -1424,7 +1424,7 @@ void vPlanet::TestComputations(Sketchpad* pSkp) D3D9DebugLog("Hd=%f, Ca=%f", sp.hd, sp.ca); D3D9DebugLog("CameraInSpace=%f", cp.CamSpace); - D3DXMATRIX mI; D3DXMatrixIdentity(&mI); + FMATRIX4 mI; oapiMatrixIdentity(&mI); VECTOR3 V0, V1; IVECTOR2 pt0, pt1; @@ -1438,7 +1438,7 @@ void vPlanet::TestComputations(Sketchpad* pSkp) if (e0 > s0) { D3D9DebugLog("Primary Integral"); V0 = (vR + vRay * s0)._V(); - V1 = V0 + vRay._V() * (e0 - s0); + V1 = V0 + _V(vRay) * double(e0 - s0); scn->WorldToScreenSpace(V0, &pt0); scn->WorldToScreenSpace(V1, &pt1); pSkp->QuickPen(0xFF90FF90); @@ -1447,8 +1447,8 @@ void vPlanet::TestComputations(Sketchpad* pSkp) if (e1 > s1) { D3D9DebugLog("Secondary Integral"); - V0 = (vR + vRay * s1)._V(); - V1 = V0 + vRay._V() * (e1 - s1); + V0 = _V(vR + vRay * s1); + V1 = V0 + _V(vRay) * double(e1 - s1); scn->WorldToScreenSpace(V0, &pt0); scn->WorldToScreenSpace(V1, &pt1); pSkp->QuickPen(0xFF9090FF); diff --git a/OVP/D3D9Client/VStar.cpp b/OVP/D3D9Client/VStar.cpp index 1bf08dc69..4d8e2c702 100644 --- a/OVP/D3D9Client/VStar.cpp +++ b/OVP/D3D9Client/VStar.cpp @@ -61,15 +61,15 @@ bool vStar::Render(LPDIRECT3DDEVICE9 dev) // double phi = atan2 (bdir.z, bdir.x); // FLOAT sphi = (FLOAT)sin(phi), cphi = (FLOAT)cos(phi); // FLOAT tx = (FLOAT)cpos.x, ty = (FLOAT)cpos.y, tz = (FLOAT)cpos.z; - mWorld._11 = (FLOAT)bdir.x; //cphi; - mWorld._12 = (FLOAT)bdir.y; //0; - mWorld._13 = (FLOAT)bdir.z; //sphi; - mWorld._31 = -(FLOAT)(bdir.z/hz); //-sphi; - mWorld._32 = 0; - mWorld._33 = (FLOAT)(bdir.x/hz); //cphi; - mWorld._21 = -(mWorld._12*mWorld._33 - mWorld._32*mWorld._13); //0; - mWorld._22 = -(mWorld._13*mWorld._31 - mWorld._33*mWorld._11); //1; - mWorld._23 = -(mWorld._11*mWorld._32 - mWorld._31*mWorld._12); // 0; + mWorld.m11 = (FLOAT)bdir.x; //cphi; + mWorld.m12 = (FLOAT)bdir.y; //0; + mWorld.m13 = (FLOAT)bdir.z; //sphi; + mWorld.m31 = -(FLOAT)(bdir.z/hz); //-sphi; + mWorld.m32 = 0; + mWorld.m33 = (FLOAT)(bdir.x/hz); //cphi; + mWorld.m21 = -(mWorld.m12*mWorld.m33 - mWorld.m32*mWorld.m13); //0; + mWorld.m22 = -(mWorld.m13*mWorld.m31 - mWorld.m33*mWorld.m11); //1; + mWorld.m23 = -(mWorld.m11*mWorld.m32 - mWorld.m31*mWorld.m12); // 0; // artificially reduce size reduction with distance // to make star appear larger @@ -82,15 +82,15 @@ bool vStar::Render(LPDIRECT3DDEVICE9 dev) if (cdist > maxdist) { dist_scale = maxdist/cdist; rad_scale *= float(dist_scale); - mWorld._41 = float(cpos.x*dist_scale); - mWorld._42 = float(cpos.y*dist_scale); - mWorld._43 = float(cpos.z*dist_scale); + mWorld.m41 = float(cpos.x*dist_scale); + mWorld.m42 = float(cpos.y*dist_scale); + mWorld.m43 = float(cpos.z*dist_scale); } // scale up sphere radius from 1 to planet radius - mWorld._11 *= rad_scale; mWorld._12 *= rad_scale; mWorld._13 *= rad_scale; - mWorld._21 *= rad_scale; mWorld._22 *= rad_scale; mWorld._23 *= rad_scale; - mWorld._31 *= rad_scale; mWorld._32 *= rad_scale; mWorld._33 *= rad_scale; + mWorld.m11 *= rad_scale; mWorld.m12 *= rad_scale; mWorld.m13 *= rad_scale; + mWorld.m21 *= rad_scale; mWorld.m22 *= rad_scale; mWorld.m23 *= rad_scale; + mWorld.m31 *= rad_scale; mWorld.m32 *= rad_scale; mWorld.m33 *= rad_scale; //D3D9Effect::RenderBillboard(&mWorld, scn->GetSunTexture(), 1.0f); D3D9Effect::RenderBillboard(&mWorld, SURFACE(deftex)->GetTexture(), 1.0f); diff --git a/OVP/D3D9Client/VVessel.cpp b/OVP/D3D9Client/VVessel.cpp index bb0ab52e9..1a2e185dc 100644 --- a/OVP/D3D9Client/VVessel.cpp +++ b/OVP/D3D9Client/VVessel.cpp @@ -7,6 +7,7 @@ // ============================================================== #include +#include #include "VVessel.h" #include "VPlanet.h" #include "MeshMgr.h" @@ -18,14 +19,15 @@ #include "DebugControls.h" #include "D3D9Util.h" #include "MaterialMgr.h" +#include "IProcess.h" using namespace oapi; // ============================================================== // Local prototypes -void TransformPoint (VECTOR3 &p, const D3DXMATRIX &T); -void TransformDirection (VECTOR3 &a, const D3DXMATRIX &T, bool normalise); +void TransformPoint (VECTOR3 &p, const FMATRIX4 &T); +void TransformDirection (VECTOR3 &a, const FMATRIX4 &T, bool normalise); const char *value_string (double val); // ============================================================== @@ -53,17 +55,10 @@ vVessel::vVessel(OBJHANDLE _hObj, const Scene *scene): vObject (_hObj, scene) vessel = oapiGetVesselInterface(_hObj); nmesh = 0; - nEnv = 0; - iFace = 0; - eFace = 0; sunLight = *scene->GetSun(); tCheckLight = oapiGetSimTime()-1.0; vClass = 0; - pIrrad = NULL; - pIrdEnv = NULL; - pMatMgr = new MatMgr(this, scene->GetClient()); - for (int i = 0; i < ARRAYSIZE(pEnv); i++) pEnv[i] = NULL; if (strncmp(vessel->GetClassNameA(), "XR2Ravenstar", 12) == 0) vClass = VCLASS_XR2; if (strncmp(vessel->GetClassNameA(), "SpaceShuttleUltra", 17) == 0) vClass = VCLASS_ULTRA; @@ -81,7 +76,12 @@ vVessel::vVessel(OBJHANDLE _hObj, const Scene *scene): vObject (_hObj, scene) currentstate[i] = anim[i].defstate; if (Config->bAbsAnims) for (UINT k = 0; k < anim[i].ncomp; ++k) StoreDefaultState(anim[i].comp[k]); } - + + // Initialize default eCams; + // + ecDefExt.flags = ENVCAM_OMIT_ATTC; + ecDefExt.type = EnvCamType::Exterior; + /* oapiWriteLogV("%s", vessel->GetClassNameA()); oapiWriteLogV("nanim = %u", na); @@ -90,8 +90,11 @@ vVessel::vVessel(OBJHANDLE _hObj, const Scene *scene): vObject (_hObj, scene) for (UINT k = 0; k < anim[i].ncomp; ++k) LogComp(anim[i].comp[k], 2); }*/ - UpdateAnimations(); + + for (int i = 0; i < 16; i++) BakedLightsControl[i] = FVECTOR3(0.0f, 0.0f, 0.0f); + VCAmbient = FVECTOR3(0.5f, 0.5f, 0.5f); + bMustRebake = true; } @@ -100,14 +103,12 @@ vVessel::vVessel(OBJHANDLE _hObj, const Scene *scene): vObject (_hObj, scene) vVessel::~vVessel () { SAFE_DELETE(pMatMgr); - SAFE_RELEASE(pIrrad); - SAFE_RELEASE(pIrdEnv); - - for (int i = 0; i < ARRAYSIZE(pEnv); i++) SAFE_RELEASE(pEnv[i]); - LogAlw("Deleting Vessel Visual %s ...", _PTR(this)); DisposeAnimations(); DisposeMeshes(); + + for (auto& x: InteriorCams) SAFE_DELETE(x); + LogAlw("Vessel visual deleted succesfully"); } @@ -117,8 +118,10 @@ vVessel::~vVessel () void vVessel::GlobalInit(D3D9Client *gc) { _TRACE; + auto pDevice = gc->GetDevice(); defreentrytex = SURFACE(gc->clbkLoadTexture("Reentry.dds", 0)); defexhausttex = SURFACE(gc->clbkLoadTexture("Exhaust.dds", 0)); + pRenderZone = new ShaderClass(pDevice, "Modules/D3D9Client/Custom.hlsl", "QuadVS", "QuadPS", "RenderZones", ""); } @@ -128,6 +131,7 @@ void vVessel::GlobalExit () { DELETE_SURFACE(defexhausttex); DELETE_SURFACE(defreentrytex); + SAFE_DELETE(pRenderZone); } @@ -165,8 +169,8 @@ void vVessel::clbkEvent(DWORD evnt, DWORD_PTR _context) VECTOR3 ofs; vessel->GetMeshOffset (idx, ofs); if (length(ofs)) { - if (meshlist[idx].trans==NULL) meshlist[idx].trans = new D3DXMATRIX; - D3DMAT_Identity(meshlist[idx].trans); + if (meshlist[idx].trans==NULL) meshlist[idx].trans = new FMATRIX4; + oapiMatrixIdentity(meshlist[idx].trans); D3DMAT_SetTranslation(meshlist[idx].trans, &ofs); } else { @@ -208,12 +212,20 @@ DWORD vVessel::GetMeshCount() // ============================================================================================ // -MESHHANDLE vVessel::GetMesh (UINT idx) +D3D9Mesh* vVessel::GetMesh (UINT idx) { return (idx < nmesh ? meshlist[idx].mesh : NULL); } +// ============================================================================================ +// +DWORD vVessel::GetMeshVisMode(UINT idx) +{ + return (idx < nmesh ? meshlist[idx].vismode : MESHVIS_ALWAYS); +} + + // ============================================================================================ // bool vVessel::HasExtPass() @@ -310,8 +322,8 @@ void vVessel::LoadMeshes() vessel->GetMeshOffset(idx, ofs); LogAlw("Mesh(%s) Offset = (%g, %g, %g)", _PTR(hMesh), ofs.x, ofs.y, ofs.z); if (length(ofs)) { - meshlist[idx].trans = new D3DXMATRIX; - D3DMAT_Identity(meshlist[idx].trans); + meshlist[idx].trans = new FMATRIX4; + oapiMatrixIdentity(meshlist[idx].trans); D3DMAT_SetTranslation(meshlist[idx].trans, &ofs); // currently only mesh translations are supported } @@ -336,7 +348,7 @@ void vVessel::InsertMesh(UINT idx) VECTOR3 ofs=_V(0,0,0); UINT i; - LPD3DXMATRIX pT = NULL; + FMATRIX4* pT = NULL; if (idx >= nmesh) { // append a new entry to the list MESHREC *tmp = new MESHREC[idx+1]; @@ -381,8 +393,8 @@ void vVessel::InsertMesh(UINT idx) meshlist[idx].vismode = vessel->GetMeshVisibilityMode (idx); vessel->GetMeshOffset (idx, ofs); if (length(ofs)) { - meshlist[idx].trans = new D3DXMATRIX; - D3DMAT_Identity (meshlist[idx].trans); + meshlist[idx].trans = new FMATRIX4; + oapiMatrixIdentity (meshlist[idx].trans); D3DMAT_SetTranslation (meshlist[idx].trans, &ofs); // currently only mesh translations are supported } else { @@ -424,8 +436,8 @@ void vVessel::ResetMesh(UINT idx) vessel->GetMeshOffset(idx, ofs); if (length(ofs)) { - if (!meshlist[idx].trans) meshlist[idx].trans = new D3DXMATRIX; - D3DMAT_Identity(meshlist[idx].trans); + if (!meshlist[idx].trans) meshlist[idx].trans = new FMATRIX4; + oapiMatrixIdentity(meshlist[idx].trans); D3DMAT_SetTranslation(meshlist[idx].trans, &ofs); } else { @@ -583,137 +595,249 @@ void vVessel::UpdateAnimations (int mshidx) } } - // ============================================================================================ // -bool vVessel::IsInsideShadows() +bool vVessel::GetSMapRenderData(SMI type, int idx, SMapInput *sm) { - D3DXVECTOR3 bc; - const Scene::SHADOWMAPPARAM *shd = scn->GetSMapData(); - D3DXVec3TransformCoord(&bc, ptr(D3DXVECTOR3f4(BBox.bs)), &mWorld); - bc = bc - shd->pos; - float x = D3DXVec3Dot(&bc, &(shd->ld)); + FVECTOR3 cpos; float rad; - if (sqrt(D3DXVec3Dot(&bc, &bc) - x*x) < (shd->rad - BBox.bs.w)) return true; + if (type == SMI::Visual) { + *sm = { GetBoundingSpherePosDX(), FVECTOR3(-sundir), GetBoundingSphereRadius() }; + return true; + } + if (type == SMI::VC) { + bool bRet = GetVCPos(&cpos, NULL, &rad); + *sm = { cpos, FVECTOR3(-sundir), rad }; + return bRet; + } + if (type == SMI::Mesh) { + bool bRet = GetMeshPosition(idx, &cpos, nullptr, &rad); + *sm = { cpos, FVECTOR3(-sundir), rad }; + return bRet; + } + return false; +} +// ============================================================================================ +// +bool vVessel::IsInsideShadows(const SMapInput* shd) +{ + FVECTOR3 bc = oapiTransformCoord(&_F(BBox.bs), &mWorld); + FVECTOR3 fbc = FVECTOR3(bc) - shd->pos; + float x = dotp(fbc, shd->ld); + if (sqrt(dotp(fbc, fbc) - x*x) < ((shd->rad * 1.01f) - BBox.bs.w)) return true; return false; } // ============================================================================================ // -bool vVessel::IntersectShadowVolume() +bool vVessel::IntersectShadowVolume(const SMapInput* shd) { - D3DXVECTOR3 bc; - const Scene::SHADOWMAPPARAM *shd = scn->GetSMapData(); - D3DXVec3TransformCoord(&bc, ptr(D3DXVECTOR3f4(BBox.bs)), &mWorld); - bc = bc - shd->pos; - float x = D3DXVec3Dot(&bc, &(shd->ld)); - if (sqrt(D3DXVec3Dot(&bc, &bc) - x*x) > (shd->rad + BBox.bs.w)) return false; + FVECTOR3 bc = oapiTransformCoord(&_F(BBox.bs), &mWorld); + FVECTOR3 fbc = FVECTOR3(bc) - shd->pos; + float x = dotp(fbc, shd->ld); + if (sqrt(dotp(fbc, fbc) - x*x) > (shd->rad + BBox.bs.w)) return false; return true; } // ============================================================================================ // -bool vVessel::IntersectShadowTarget() +bool vVessel::IntersectShadowTarget(const SMapInput* shd) { - D3DXVECTOR3 bc; - const Scene::SHADOWMAPPARAM *shd = scn->GetSMapData(); - D3DXVec3TransformCoord(&bc, ptr(D3DXVECTOR3f4(BBox.bs)), &mWorld); - bc = bc - shd->pos; - if (D3DXVec3Length(&bc) < (shd->rad + BBox.bs.w)) return true; + FVECTOR3 bc = oapiTransformCoord(&_F(BBox.bs), &mWorld); + FVECTOR3 fbc = FVECTOR3(bc) - shd->pos; + if (length(fbc) < (shd->rad + BBox.bs.w)) return true; return false; } // ============================================================================================ // -void vVessel::GetMinMaxLightDist(float *mind, float *maxd) +void vVessel::GetMinMaxLightDist(const SMapInput* shd, float *mind, float *maxd) { - D3DXVECTOR3 bc; - const Scene::SHADOWMAPPARAM *shd = scn->GetSMapData(); - D3DXVec3TransformCoord(&bc, ptr(D3DXVECTOR3f4(BBox.bs)), &mWorld); - bc -= shd->pos; - float x = D3DXVec3Dot(&bc, &(shd->ld)); - *mind = min(*mind, x - shd->rad); - *maxd = max(*maxd, x + shd->rad); + FVECTOR3 bc = oapiTransformCoord(&_F(BBox.bs), &mWorld); + FVECTOR3 fbc = FVECTOR3(bc) - shd->pos; + float x = dotp(fbc, shd->ld); + *mind = min(*mind, x - BBox.bs.w); + *maxd = max(*maxd, x + BBox.bs.w); } +// ============================================================================================ +// +bool vVessel::GetVCPos(FVECTOR3* cpos, FVECTOR3* lpos, float* rad) +{ + for (int i = 0; i < nmesh; i++) + { + if (!meshlist[i].mesh) continue; + if (meshlist[i].mesh->MeshFlags & MESHFLAG_VC) + { + return GetMeshPosition(i, cpos, lpos, rad); + } + } + return false; +} + + +// ============================================================================================ +// +bool vVessel::GetMeshPosition(int idx, FVECTOR3* cpos, FVECTOR3* lpos, float* rad) +{ + if (!meshlist[idx].mesh) return false; + + FVECTOR3 pos = meshlist[idx].mesh->GetBoundingSpherePos(); + if (rad) *rad = meshlist[idx].mesh->GetBoundingSphereRadius(); + + if (meshlist[idx].trans) + { + pos = oapiTransformCoord(&pos, meshlist[idx].trans); + if (lpos) *lpos = pos; + if (cpos) pos = oapiTransformCoord(&pos, &mWorld); + } + else { + if (lpos) *lpos = pos; + if (cpos) pos = oapiTransformCoord(&pos, &mWorld); + } + if (cpos) *cpos = pos; + return true; +} + + +// ============================================================================================ +// +void vVessel::BakeLights(ImageProcessing *pBaker) +{ + OBJHANDLE hGRef = vessel->GetGravityRef(); + + MATRIX3 grot; VECTOR3 rpos; LVLH lvlh; + oapiGetRotationMatrix(hGRef, &grot); + vessel->GetRelativePos(hGRef, rpos); + + FMATRIX4 mW(mWorld); + FVECTOR3 polaraxis = mul(grot, _V(0, 1, 0)); + lvlh.Up = unit(rpos); + lvlh.East = unit(crossp(polaraxis, lvlh.Up)); + lvlh.North = unit(crossp(lvlh.Up, lvlh.East)); + lvlh.Up = mul(mW, FVECTOR4(lvlh.Up, 0)).xyz; + lvlh.East = mul(mW, FVECTOR4(lvlh.East, 0)).xyz; + lvlh.North = mul(mW, FVECTOR4(lvlh.North, 0)).xyz; + + auto maps = GetExteriorEnvMap(); + + if (maps) { + for (int i = 0; i < nmesh; i++) + { + if (!meshlist[i].mesh) continue; + if (meshlist[i].vismode & MESHVIS_VC) // TODO: Should check Shader type + { + auto vSun = mul(mW, FVECTOR4(sundir, 0)); + if (bMustRebake) meshlist[i].mesh->BakeLights(pBaker, BakedLightsControl); + if (Config->ExpVCLight == 0) meshlist[i].mesh->BakeAO(pBaker, vSun.xyz, lvlh, maps->pIrrad); + } + } + } + + bMustRebake = false; +} + + +// ============================================================================================ +// +void vVessel::ErrorOnce(Errors e) +{ + static bool bDisp[Errors::ITEMS] = { true }; + static DWORD dwDisp[Errors::ITEMS] = { 0 }; + + if (bDisp[e]) { + switch (e) { + case Errors::NoVC: + oapiWriteLogV("[ERROR:D3D9] Cannot identify Virtual Cockpit mesh [%s]", vessel->GetClassNameA()); + break; + default: + oapiWriteLogV("[ERROR:D3D9] Unknows error code [%s]", vessel->GetName()); + } + dwDisp[e]++; // Count the errors; + bDisp[e] = false; // Disable error from printing again + } +} + // ============================================================================================ // bool vVessel::Render(LPDIRECT3DDEVICE9 dev) { _TRACE; if (!active) return false; - pCurrentVisual = this; // Set current visual for mesh debugger UpdateBoundingBox(); - bool bRet = Render(dev, false); + bool bRet = Render(dev, false, nullptr); if (oapiCameraInternal()==false) RenderReentry(dev); return bRet; } +// ============================================================================================ +// +bool vVessel::Render(LPDIRECT3DDEVICE9 dev, bool internalpass, const SHADOWMAP* shd) +{ + bool bCockpit = (oapiCameraInternal() && (hObj == oapiGetFocusObject())); + bool bVC = (bCockpit && (oapiCockpitMode() == COCKPIT_VIRTUAL)); + + DWORD flags = 0; + flags |= bCockpit ? Render::D2 : 0; + flags |= bVC ? Render::VC : 0; + flags |= internalpass ? Render::IP : 0; + + return Render(dev, shd, flags); +} // ============================================================================================ // -bool vVessel::Render(LPDIRECT3DDEVICE9 dev, bool internalpass) +bool vVessel::Render(LPDIRECT3DDEVICE9 dev, const SHADOWMAP *shd, DWORD flg) { _TRACE; if (!active) return false; UINT i, mfd; + g_pCurrentVisual = this; // Set current visual for mesh debugger DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); DWORD displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); - bool bCockpit = (oapiCameraInternal() && (hObj == oapiGetFocusObject())); - // render cockpit view - - bool bVC = (bCockpit && (oapiCockpitMode() == COCKPIT_VIRTUAL)); - // render virtual cockpit + bool bCockpit = (flg & Render::D2) | (flg & Render::VC); + bool bVC = (flg & Render::VC); + bool bInternal = (flg & Render::IP); if (scn->GetRenderPass() == RENDERPASS_CUSTOMCAM) bCockpit = bVC = false; // Always render exterior view for custom cams - if (scn->GetRenderPass() == RENDERPASS_ENVCAM) bCockpit = bVC = false; - // Always render exterior view for envmaps - - if (scn->GetRenderPass() == RENDERPASS_SHADOWMAP) bCockpit = bVC = false; - // Always render exterior view for envmaps - - // if (scn->GetRenderPass() == RENDERPASS_NORMAL_DEPTH) bCockpit = bVC = false; - // Always render exterior view for envmaps - static VCHUDSPEC hudspec_; - const VCHUDSPEC *hudspec = &hudspec_; + const VCHUDSPEC* hudspec = &hudspec_; static bool gotHUDSpec(false); - const VCMFDSPEC *mfdspec[MAXMFD] = { NULL }; - - - const Scene::SHADOWMAPPARAM *shd = scn->GetSMapData(); - - float s = float(shd->size); - float sr = 2.0f * shd->rad / s; + const VCMFDSPEC* mfdspec[MAXMFD] = { NULL }; HR(D3D9Effect::FX->SetBool(D3D9Effect::eEnvMapEnable, false)); - HR(D3D9Effect::FX->SetMatrix(D3D9Effect::eLVP, &shd->mViewProj)); - if (shd->pShadowMap && (scn->GetRenderPass() == RENDERPASS_MAINSCENE)) { - HR(D3D9Effect::FX->SetTexture(D3D9Effect::eShadowMap, shd->pShadowMap)); - HR(D3D9Effect::FX->SetVector(D3D9Effect::eSHD, ptr(D3DXVECTOR4(sr, 1.0f / s, float(oapiRand()), 1.0f / shd->depth)))); + if (shd && shd->IsValid()) + { + float s = float(shd->size); + float sr = 2.0f * shd->rad / s; + FVECTOR4 sh = FVECTOR4(sr, 1.0f / s, float(oapiRand()), 1.0f / shd->depth); + FVECTOR4 px = FVECTOR4(shd->SubPx[0], shd->SubPx[1], shd->SubPx[2], 0.0f); + HR(D3D9Effect::FX->SetMatrix(D3D9Effect::eLVP, _DX(shd->mLVP))); // Cascade 0 for all + HR(D3D9Effect::FX->SetVector(D3D9Effect::eSHD, _DX(sh))); + HR(D3D9Effect::FX->SetVector(D3D9Effect::eSHDPx, _DX(px))); HR(D3D9Effect::FX->SetBool(D3D9Effect::eShadowToggle, true)); + D3D9Mesh::SetShadows(shd); } else { + D3D9Mesh::SetShadows(NULL); HR(D3D9Effect::FX->SetBool(D3D9Effect::eShadowToggle, false)); } - HR(D3D9Effect::FX->SetTexture(D3D9Effect::eIrradMap, pIrrad)); - // Check VC MFD screen resolutions ------------------------------------------------ // - if (bVC && internalpass) { + if (bVC && bInternal) { for (mfd = 0; mfd < MAXMFD; mfd++) gc->GetVCMFDSurface(mfd, &mfdspec[mfd]); gotHUDSpec = !!gc->GetVCHUDSurface(&hudspec); } @@ -726,83 +850,113 @@ bool vVessel::Render(LPDIRECT3DDEVICE9 dev, bool internalpass) MeshShader::ps_const.Cam_Z = *scn->GetCameraZ(); - // Render Exterior and Interior (VC) meshes -------------------------------------------- // - for (i=0;i1) vismode = MESHVIS_ALWAYS; - if (vismode==0) continue; - - if (internalpass==false) { - if (vismode==MESHVIS_VC) continue; // Added 3-jan-2011 to prevent VC interior double rendering during exterior and interior passes - if ((vismode&MESHVIS_EXTPASS)==0 && bCockpit) continue; - } - - if (bCockpit) { - if (internalpass && (vismode & MESHVIS_EXTPASS)) continue; - if (!(vismode & MESHVIS_COCKPIT)) { - if ((!bVC) || (!(vismode & MESHVIS_VC))) continue; + if (scn->GetRenderPass() != RENDERPASS_VC_SHADOWMAP) + { + if (vismode == 0) continue; + if (bInternal == false) { + if (vismode == MESHVIS_VC) continue; // Added 3-jan-2011 to prevent VC interior double rendering during exterior and interior passes + if ((vismode & MESHVIS_EXTPASS) == 0 && bCockpit) continue; + } + if (bCockpit) { + if (bInternal && (vismode & MESHVIS_EXTPASS)) continue; + if (!(vismode & MESHVIS_COCKPIT)) { + if ((!bVC) || (!(vismode & MESHVIS_VC))) continue; + } + } + else { + if (!(vismode & MESHVIS_EXTERNAL)) continue; } - } else { - if (!(vismode & MESHVIS_EXTERNAL)) continue; } - D3DXMATRIX mWT; - LPD3DXMATRIX pWT; + WORD Shader = pMesh->GetDefaultShader(); + DWORD mFlags = pMesh->MeshFlags; - // transform mesh - if (meshlist[i].trans) pWT = D3DXMatrixMultiply(&mWT, (const D3DXMATRIX *)meshlist[i].trans, &mWorld); + FMATRIX4 mWT; + FMATRIX4* pWT; + + if (meshlist[i].trans) pWT = oapiMatrixMultiply(&mWT, (const FMATRIX4*)meshlist[i].trans, &mWorld); else pWT = &mWorld; - - - if (bVC && internalpass) { + + if (bVC && bInternal && (pMesh->GetDefaultShader() == SHADER_LEGACY)) { D3D9Sun local = sunLight; local.Color *= 0.5f; - meshlist[i].mesh->SetSunLight(&local); + pMesh->SetSunLight(&local); } - else meshlist[i].mesh->SetSunLight(&sunLight); + else pMesh->SetSunLight(&sunLight); - if (bVC && internalpass) { + if (bVC && bInternal) + { for (mfd=0;mfdnmesh == i) { - meshlist[i].mesh->SetMFDScreenId(mfdspec[mfd]->ngroup, 1 + mfd); + pMesh->SetMFDScreenId(mfdspec[mfd]->ngroup, 1 + mfd); + } + } + if (gotHUDSpec) { + if (hudspec->nmesh == i) { + pMesh->SetMFDScreenId(hudspec->ngroup, 0x100); } } } - const LPD3DXMATRIX pVP = scn->GetProjectionViewMatrix(); - const LPD3DXMATRIX pLVP = (const LPD3DXMATRIX)&shd->mViewProj; + const FMATRIX4* pVP = scn->GetProjectionViewMatrix(); + const FMATRIX4* pLVP = shd ? (const FMATRIX4*)&shd->mLVP : nullptr; // Render vessel meshes -------------------------------------------------------------------------- // - if (scn->GetRenderPass() == RENDERPASS_SHADOWMAP) meshlist[i].mesh->RenderShadowMap(pWT, pLVP, 0); - else if (scn->GetRenderPass() == RENDERPASS_NORMAL_DEPTH) + if (scn->GetRenderPass() == RENDERPASS_VC_SHADOWMAP) { - meshlist[i].mesh->RenderShadowMap(pWT, pVP, 1); + if (mFlags & MESHFLAG_SHADOW_VC) + pMesh->RenderShadowMap(pWT, pLVP, 0, bVC); } - else { - if (internalpass) meshlist[i].mesh->Render(pWT, RENDER_VC, NULL, 0); - else meshlist[i].mesh->Render(pWT, RENDER_VESSEL, pEnv, nEnv); + else if (scn->GetRenderPass() == RENDERPASS_SHADOWMAP) + { + pMesh->RenderShadowMap(pWT, pLVP, 0, bVC); } + else if (scn->GetRenderPass() == RENDERPASS_NORMAL_DEPTH) + { + pMesh->RenderShadowMap(pWT, pVP, 1); + } + else + { + auto ec = GetEnvCam(EnvCamType::Interior, 0); + bool bSL = false; + if (shd) bSL = (shd->tp == SHADOWMAP::sMapType::SingleLod); - - // render VC HUD and MFDs ------------------------------------------------------------------------ - // - if (scn->GetRenderPass() == RENDERPASS_MAINSCENE) { - if (bVC && internalpass && gotHUDSpec) { - if (hudspec->nmesh == i) { - meshlist[i].mesh->SetMFDScreenId(hudspec->ngroup, 0x100); + if (Shader != SHADER_LEGACY) + { + if (mFlags & MESHFLAG_VC) { + if (ec) { + float f = ec->da_force * 60.0f; + float b = ec->da_bounch * 2.0f; + float c = ec->da_curve * 3.0f; + if (scn->GetRenderPass() != RENDERPASS_MAINSCENE) + D3D9Effect::FX->SetValue(D3D9Effect::eVCIrrad, &FVECTOR4(b, b, b, 1.0f), sizeof(FVECTOR4)); + else + D3D9Effect::FX->SetValue(D3D9Effect::eVCIrrad, &FVECTOR4(f, f, f, c), sizeof(FVECTOR4)); + } + if (bSL) pMesh->Render(pWT, ec, RENDER_VESSEL); + else pMesh->Render(pWT, ec, RENDER_VC); } + else pMesh->Render(pWT, GetExteriorEnvMap(), RENDER_VESSEL); + } + else { + if (bInternal) pMesh->Render(pWT, ec, RENDER_VC); + else pMesh->Render(pWT, GetExteriorEnvMap(), RENDER_VESSEL); } } } @@ -813,12 +967,15 @@ bool vVessel::Render(LPDIRECT3DDEVICE9 dev, bool internalpass) if (scn->GetRenderPass() == RENDERPASS_MAINSCENE) { if (DebugControls::IsActive()) { if (flags&DBG_FLAGS_SELVISONLY && this != DebugControls::GetVisual()) return true; - if (flags&DBG_FLAGS_BOXES && !internalpass) { - D3DXMATRIX id; - D3D9Effect::RenderBoundingBox(&mWorld, D3DXMatrixIdentity(&id), &BBox.min, &BBox.max, ptr(D3DXVECTOR4(1, 0, 0, 0.75f))); + if (flags&DBG_FLAGS_BOXES && !bInternal) { + FMATRIX4 id; + D3D9Effect::RenderBoundingBox(&mWorld, &FMATRIX_Identity, &BBox.mn, &BBox.mx, ptr(FVECTOR4(1, 0, 0, 0.75f))); } RenderLightCone(&mWorld); + + dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + if (flags & DBG_FLAGS_VCZONES) RenderClickZones(); } } @@ -828,6 +985,56 @@ bool vVessel::Render(LPDIRECT3DDEVICE9 dev, bool internalpass) } +// ============================================================================================ +// +void vVessel::RenderClickZones() +{ + std::list Zones; + oapiVCGetAreaClickZones(&Zones); + + auto hPS = pRenderZone->GetPSHandle("cb"); + auto hVS = pRenderZone->GetVSHandle("cb"); + + pRenderZone->Setup(nullptr, false, 1); + + struct { + FVECTOR3 pt[4]; + FVECTOR4 color; + FMATRIX4 mW; + FMATRIX4 mVP; + BOOL bSphere; + } cb; + + cb.mW = mWorld; + cb.mVP = scn->GetProjectionViewMatrix(); + + cb.bSphere = false; + for (auto& z : Zones) + { + if (z.mode == 2) { // Quadrilateral + for (int i = 0; i < 4; i++) cb.pt[i] = z.pt[i]; + cb.color = FVECTOR4(1.0f, 1.0f, 0.0f, 1.0f); + pRenderZone->SetPSConstants(hPS, &cb, sizeof(cb)); + pRenderZone->SetVSConstants(hVS, &cb, sizeof(cb)); + hStockMesh[D3D9SM_BOX]->RenderGroup(0); + } + } + + cb.bSphere = true; + for (auto& z : Zones) + { + if (z.mode == 1) { // Spherical + cb.pt[0] = z.cnt; + cb.pt[1] = z.rad; + cb.color = FVECTOR4(0.3f, 1.0f, 1.0f, 1.0f); + pRenderZone->SetPSConstants(hPS, &cb, sizeof(cb)); + pRenderZone->SetVSConstants(hVS, &cb, sizeof(cb)); + hStockMesh[D3D9SM_SPHERE]->RenderGroup(0); + } + } +} + + // ============================================================================================ // void vVessel::RenderVectors (LPDIRECT3DDEVICE9 dev, D3D9Pad *pSkp) @@ -875,63 +1082,63 @@ void vVessel::RenderVectors (LPDIRECT3DDEVICE9 dev, D3D9Pad *pSkp) if (bfvmode & BFV_DRAG) { vessel->GetDragVector(vector); if (length(vector) > threshold) { - RenderAxisVector(pSkp, ptr(D3DXCOLOR(1,0,0,alpha)), vector, lscale, scale, bLog); + RenderAxisVector(pSkp, ptr(FVECTOR4(1,0,0,alpha)), vector, lscale, scale, bLog); sprintf_s(label, 64, "D = %sN", value_string(length(vector))); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(1,0,0,alpha)), vector, lscale, scale, label, bLog); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1,0,0,alpha)), vector, lscale, scale, label, bLog); } } if (bfvmode & BFV_WEIGHT) { vessel->GetWeightVector(vector); if (length(vector) > threshold) { - RenderAxisVector(pSkp, ptr(D3DXCOLOR(1,1,0,alpha)), vector, lscale, scale, bLog); + RenderAxisVector(pSkp, ptr(FVECTOR4(1,1,0,alpha)), vector, lscale, scale, bLog); sprintf_s(label, 64, "G = %sN", value_string(length(vector))); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(1,1,0,alpha)), vector, lscale, scale, label, bLog); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1,1,0,alpha)), vector, lscale, scale, label, bLog); } } if (bfvmode & BFV_THRUST) { vessel->GetThrustVector(vector); if (length(vector) > threshold) { - RenderAxisVector(pSkp, ptr(D3DXCOLOR(0,0,1,alpha)), vector, lscale, scale, bLog); + RenderAxisVector(pSkp, ptr(FVECTOR4(0,0,1,alpha)), vector, lscale, scale, bLog); sprintf_s(label, 64, "T = %sN", value_string(length(vector))); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(0,0,1,alpha)), vector, lscale, scale, label, bLog); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0,0,1,alpha)), vector, lscale, scale, label, bLog); } } if (bfvmode & BFV_LIFT) { vessel->GetLiftVector(vector); if (length(vector) > threshold) { - RenderAxisVector(pSkp, ptr(D3DXCOLOR(0,1,0,alpha)), vector, lscale, scale, bLog); + RenderAxisVector(pSkp, ptr(FVECTOR4(0,1,0,alpha)), vector, lscale, scale, bLog); sprintf_s(label, 64, "L = %sN", value_string(length(vector))); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(0,1,0,alpha)), vector, lscale, scale, label, bLog); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0,1,0,alpha)), vector, lscale, scale, label, bLog); } } if (bfvmode & BFV_TOTAL) { vessel->GetForceVector(vector); if (length(vector) > threshold) { - RenderAxisVector(pSkp, ptr(D3DXCOLOR(1,1,1,alpha)), vector, lscale, scale, bLog); + RenderAxisVector(pSkp, ptr(FVECTOR4(1,1,1,alpha)), vector, lscale, scale, bLog); sprintf_s(label, 64, "F = %sN", value_string(length(vector))); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(1,1,1,alpha)), vector, lscale, scale, label, bLog); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1,1,1,alpha)), vector, lscale, scale, label, bLog); } } if (bfvmode & BFV_TORQUE) { vessel->GetTorqueVector(vector); if (length(vector) > threshold) { - RenderAxisVector(pSkp, ptr(D3DXCOLOR(1,0,1,alpha)), vector, lscale, scale, bLog); + RenderAxisVector(pSkp, ptr(FVECTOR4(1,0,1,alpha)), vector, lscale, scale, bLog); sprintf_s(label, 64, "M = %sNm", value_string(length(vector))); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(1,0,1,alpha)), vector, lscale, scale, label, bLog); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1,0,1,alpha)), vector, lscale, scale, label, bLog); } } if (bfvmode & BFV_SIDEFORCE) { vessel->GetSideForceVector(vector); if (length(vector) > threshold) { - RenderAxisVector(pSkp, ptr(D3DXCOLOR(0.0392, 0.6235, 0.4941, alpha)), vector, lscale, scale, bLog); + RenderAxisVector(pSkp, ptr(FVECTOR4(0.0392f, 0.6235f, 0.4941f, alpha)), vector, lscale, scale, bLog); sprintf_s(label, 64, "SF = %sN", value_string(length(vector))); - RenderAxisLabel(pSkp, ptr(D3DXCOLOR(0.0392, 0.6235, 0.4941, alpha)), vector, lscale, scale, label, bLog); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0.0392f, 0.6235f, 0.4941f, alpha)), vector, lscale, scale, label, bLog); } } } @@ -1020,7 +1227,7 @@ void vVessel::RenderGrapplePoints (LPDIRECT3DDEVICE9 dev) { hAtt = vessel->GetAttachmentHandle(true, i); vessel->GetAttachmentParams(hAtt, pos, dir, rot); - D3D9Effect::RenderArrow(hVessel, &pos, &dir, &rot, size, ptr(D3DXCOLOR(1,0,0,alpha))); + D3D9Effect::RenderArrow(hVessel, &pos, &dir, &rot, size, ptr(FVECTOR4(1.0f, 0.0f, 0.0f, alpha))); } // attachment points to children @@ -1028,7 +1235,7 @@ void vVessel::RenderGrapplePoints (LPDIRECT3DDEVICE9 dev) { hAtt = vessel->GetAttachmentHandle(false, i); vessel->GetAttachmentParams(hAtt, pos, dir, rot); - D3D9Effect::RenderArrow(hVessel, &pos, &dir, &rot, size, ptr(D3DXCOLOR(0,0.5,1,alpha))); + D3D9Effect::RenderArrow(hVessel, &pos, &dir, &rot, size, ptr(FVECTOR4(0.0f, 0.5f, 1.0f, alpha))); } } @@ -1077,29 +1284,29 @@ void vVessel::RenderGroundShadow(LPDIRECT3DDEVICE9 dev, OBJHANDLE hPlanet, float double nd = dotp(hn, sdv); VECTOR3 sdvs = sdv / nd; - D3DXVECTOR4 nrml = D3DXVECTOR4(float(hn.x), float(hn.y), float(hn.z), float(alt)); + FVECTOR4 nrml = FVECTOR4(float(hn.x), float(hn.y), float(hn.z), float(alt)); // build shadow projection matrix - D3DXMATRIX mProj, mProjWorld, mProjWorldShift; - - mProj._11 = 1.0f - (float)(sdvs.x*hn.x); - mProj._12 = -(float)(sdvs.y*hn.x); - mProj._13 = -(float)(sdvs.z*hn.x); - mProj._14 = 0; - mProj._21 = -(float)(sdvs.x*hn.y); - mProj._22 = 1.0f - (float)(sdvs.y*hn.y); - mProj._23 = -(float)(sdvs.z*hn.y); - mProj._24 = 0; - mProj._31 = -(float)(sdvs.x*hn.z); - mProj._32 = -(float)(sdvs.y*hn.z); - mProj._33 = 1.0f - (float)(sdvs.z*hn.z); - mProj._34 = 0; - mProj._41 = (float)(sdvs.x*nr0); - mProj._42 = (float)(sdvs.y*nr0); - mProj._43 = (float)(sdvs.z*nr0); - mProj._44 = 1; - - D3DXMatrixMultiply(&mProjWorld, &mProj, &mWorld); + FMATRIX4 mProj, mProjWorld, mProjWorldShift; + + mProj.m11 = 1.0f - (float)(sdvs.x*hn.x); + mProj.m12 = -(float)(sdvs.y*hn.x); + mProj.m13 = -(float)(sdvs.z*hn.x); + mProj.m14 = 0; + mProj.m21 = -(float)(sdvs.x*hn.y); + mProj.m22 = 1.0f - (float)(sdvs.y*hn.y); + mProj.m23 = -(float)(sdvs.z*hn.y); + mProj.m24 = 0; + mProj.m31 = -(float)(sdvs.x*hn.z); + mProj.m32 = -(float)(sdvs.y*hn.z); + mProj.m33 = 1.0f - (float)(sdvs.z*hn.z); + mProj.m34 = 0; + mProj.m41 = (float)(sdvs.x*nr0); + mProj.m42 = (float)(sdvs.y*nr0); + mProj.m43 = (float)(sdvs.z*nr0); + mProj.m44 = 1; + + oapiMatrixMultiply(&mProjWorld, &mProj, &mWorld); float scale = (csun - shadow_elev_limit) * 25.0f; alpha = (1.0f - alpha) * saturate(scale); @@ -1118,24 +1325,24 @@ void vVessel::RenderGroundShadow(LPDIRECT3DDEVICE9 dev, OBJHANDLE hPlanet, float VECTOR3 of; vessel->GetMeshOffset(i, of); nrml.w += float(dotp(of, hn)); // Sift a local groung level - D3DXMatrixMultiply(&mProjWorldShift, meshlist[i].trans, &mProjWorld); + oapiMatrixMultiply(&mProjWorldShift, meshlist[i].trans, &mProjWorld); mesh->RenderStencilShadows(alpha, &mWorld, &mProjWorldShift, false, &nrml); } else mesh->RenderStencilShadows(alpha, &mWorld, &mProjWorld, false, &nrml); } } + // ============================================================================================ // Return true if it's time to move to a next vessel // false, if more rendereing is required here. // -bool vVessel::RenderENVMap(LPDIRECT3DDEVICE9 pDev, DWORD cnt, DWORD flags) +bool vVessel::ProcessEnvMaps(LPDIRECT3DDEVICE9 pDev, DWORD cnt, DWORD flags) { - bool bReflective = false; if (meshlist) { - for (DWORD i=0;iIsReflective()) { bReflective = true; @@ -1145,8 +1352,22 @@ bool vVessel::RenderENVMap(LPDIRECT3DDEVICE9 pDev, DWORD cnt, DWORD flags) } } - if (!bReflective) return true; + if (!bReflective) return true; // If none of the meshes are reflective then we are done + + // Render vessel specific map + bool bRet = RenderENVMap(pDev, &ecDefExt, cnt, flags); + + if (bRet) ecDefExt.bRendered = false; + + return bRet; +} + +// ============================================================================================ +// Render Env Map, return 'true' if the map is completed +// +bool vVessel::RenderENVMap(LPDIRECT3DDEVICE9 pDev, ENVCAMREC* ec, DWORD cnt, DWORD flags) +{ LPDIRECT3DSURFACE9 pEnvDS = GetScene()->GetEnvDepthStencil(); if (!pEnvDS) { @@ -1154,70 +1375,88 @@ bool vVessel::RenderENVMap(LPDIRECT3DDEVICE9 pDev, DWORD cnt, DWORD flags) return true; } + // Create a EnvMap if doesn't already exists -------------------------------------------------------------------- + // + if (ec->flags & ENVCAM_PLANE) + { + if (!ec->pPlane) + { + D3DSURFACE_DESC desc; + pEnvDS->GetDesc(&desc); + if (D3DXCreateTexture(pDev, desc.Width, desc.Height, 5, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &ec->pPlane) != S_OK) { + LogErr("Failed to create env-plane for visual %s", _PTR(this)); + return true; + } + } + } + else { + if (!ec->pCube) // If the maps exists check the type and assign + { + D3DSURFACE_DESC desc; + pEnvDS->GetDesc(&desc); + if (D3DXCreateCubeTexture(pDev, desc.Width, 5, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &ec->pCube) != S_OK) { + LogErr("Failed to create env-cubemap for visual %s", _PTR(this)); + return true; + } + } + } + - // Create a main EnvMap with mipmap chain for blurred maps -------------------------------------------------------------------- + // Create a Irradiance map if doesn't already exists -------------------------------------------------------------------- // - if (pEnv[ENVMAP_MAIN] == NULL) { - D3DSURFACE_DESC desc; - pEnvDS->GetDesc(&desc); - if (D3DXCreateCubeTexture(pDev, desc.Width, 5, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pEnv[ENVMAP_MAIN]) != S_OK) { - LogErr("Failed to create env cubemap for visual %s", _PTR(this)); + if (!ec->pIrrad) + { + if (D3DXCreateTexture(pDev, 128, 64, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &ec->pIrrad) != S_OK) { + LogErr("Failed to create irradiance map for visual %s", _PTR(this)); return true; } - nEnv++; } - // Create blurred maps ------------------------------------------------------------------------------- + + // Create blurred maps and irradiance --------------------------------------------------------------------- // - if (eFace >= 6) { - eFace = 0; - scn->RenderBlurredMap(pDev, pEnv[ENVMAP_MAIN]); + if (ec->bRendered) { + if (ec->pCube) { + scn->RenderBlurredMap(pDev, ec->pCube); + scn->IntegrateIrradiance(this, ec, false); + } + // TODO: Blur 2D plane map return true; } - double tot_env = D3D9GetTime(); - - // Render EnvMaps --------------------------------------------------------------------------------------- // - - std::set RndList = scn->GetVessels(10e3, true); - std::set AddLightSrc; + std::set RndList = scn->GetVessels(10e3, true); + std::set AddLightSrc; AddLightSrc.insert(this); - ENVCAMREC *eCam = pMatMgr->GetCamera(0); - - if ((eCam->flags&ENVCAM_FOCUS) == 0) RndList.erase(this); + if ((ec->flags & ENVCAM_FOCUS) == 0) RndList.erase(this); DWORD nAtc = vessel->AttachmentCount(false); DWORD nDoc = vessel->DockCount(); - if (eCam->flags & ENVCAM_OMIT_ATTC) { - for (DWORD i=0;iflags & ENVCAM_OMIT_ATTC) { + for (DWORD i = 0; i < nAtc; i++) { ATTACHMENTHANDLE hAtc = vessel->GetAttachmentHandle(false, i); if (hAtc) { OBJHANDLE hAtcObj = vessel->GetAttachmentStatus(hAtc); if (hAtcObj) { - vObject *vObj = gc->GetScene()->GetVisObject(hAtcObj); - if (vObj) RndList.erase((vVessel *)vObj); + vObject* vObj = gc->GetScene()->GetVisObject(hAtcObj); + if (vObj) RndList.erase((vVessel*)vObj); } } } } else { - - DWORD nAttc = eCam->nAttc; - - for (DWORD i=0;ipOmitAttc[i]); + for (auto id : ec->omitAttc) { ATTACHMENTHANDLE hAtc = vessel->GetAttachmentHandle(false, id); if (hAtc) { OBJHANDLE hAtcObj = vessel->GetAttachmentStatus(hAtc); if (hAtcObj) { - vObject *vObj = gc->GetScene()->GetVisObject(hAtcObj); - if (vObj) RndList.erase((vVessel *)vObj); + vObject* vObj = gc->GetScene()->GetVisObject(hAtcObj); + if (vObj) RndList.erase((vVessel*)vObj); } } } @@ -1227,171 +1466,263 @@ bool vVessel::RenderENVMap(LPDIRECT3DDEVICE9 pDev, DWORD cnt, DWORD flags) // ----------------------------------------------------------------------------------------------- // VECTOR3 gpos; - vessel->Local2Global(_V(eCam->lPos.x, eCam->lPos.y, eCam->lPos.z), gpos); + vessel->Local2Global(_V(ec->lPos.x, ec->lPos.y, ec->lPos.z), gpos); - // Prepare camera and scene for env map rendering - scn->PushCamera(); - scn->SetupInternalCamera(NULL, &gpos, 0.7853981634, 1.0); - scn->BeginPass(RENDERPASS_ENVCAM); + if (ec->pCube) + { + // Prepare camera and scene for env map rendering + scn->PushCamera(); + scn->SetupInternalCamera(NULL, &gpos, 0.7853981634, 1.0); + scn->BeginPass(RENDERPASS_ENVCAM); + gc->PushRenderTarget(NULL, pEnvDS, RENDERPASS_ENVCAM); - gc->PushRenderTarget(NULL, pEnvDS, RENDERPASS_ENVCAM); + FMATRIX4 mEnv; + FVECTOR3 dir, up; + LPDIRECT3DSURFACE9 pSrf = NULL; - D3DXMATRIX mEnv; - D3DXVECTOR3 dir, up; - LPDIRECT3DSURFACE9 pSrf = NULL; + for (DWORD i = 0; i < cnt; i++) { + HR(ec->pCube->GetCubeMapSurface(D3DCUBEMAP_FACES(ec->iSide), 0, &pSrf)); - for (DWORD i=0;iAlterRenderTarget(pSrf, pEnvDS); - HR(pEnv[ENVMAP_MAIN]->GetCubeMapSurface(D3DCUBEMAP_FACES(eFace), 0, &pSrf)); - - gc->AlterRenderTarget(pSrf, pEnvDS); + EnvMapDirection(ec->iSide, &dir, &up); - EnvMapDirection(eFace, &dir, &up); + FVECTOR3 cp = crossp(up, dir); + normalize(cp); + oapiMatrixIdentity(&mEnv); + D3DMAT_FromAxis(&mEnv, &cp, &up, &dir); - D3DXVECTOR3 cp; - D3DXVec3Cross(&cp, &up, &dir); - D3DXVec3Normalize(&cp, &cp); - D3DXMatrixIdentity(&mEnv); - D3DMAT_FromAxis(&mEnv, &cp, &up, &dir); + scn->SetCameraFrustumLimits(0.25, 1e8); + scn->SetupInternalCamera(&mEnv, NULL, 0.7853981634, 1.0); + scn->RenderSecondaryScene(RndList, AddLightSrc, flags); - scn->SetCameraFrustumLimits(0.25, 1e8); - scn->SetupInternalCamera(&mEnv, NULL, 0.7853981634, 1.0); - scn->RenderSecondaryScene(RndList, AddLightSrc, flags); + SAFE_RELEASE(pSrf); - SAFE_RELEASE(pSrf); + ec->iSide++; - eFace++; - if (eFace >= 6) break; - } + if (ec->iSide >= 6) { + ec->bRendered = true; + ec->iSide = 0; + break; + } + } - gc->PopRenderTargets(); + gc->PopRenderTargets(); + scn->PopPass(); + scn->PopCamera(); - scn->PopPass(); - scn->PopCamera(); + return false; + } - return false; + if (ec->pPlane) + { + // TODO: + return true; + } + + return true; } // ============================================================================================ -// Return true if it's time to move to a next vessel -// false, if more rendereing is required here. -// -bool vVessel::ProbeIrradiance(LPDIRECT3DDEVICE9 pDev, DWORD cnt, DWORD flags) +// Render interior Env Map, return 'true' if the map is completed +// +bool vVessel::RenderInteriorENVMap(LPDIRECT3DDEVICE9 pDev, ENVCAMREC* ec, SHADOWMAP *sm) { + if (!ec) return true; - LPDIRECT3DSURFACE9 pIrDS = GetScene()->GetIrradianceDepthStencil(); + LPDIRECT3DSURFACE9 pEnvDS = GetScene()->GetDepthStencil(128); - if (!pIrDS) return true; // Feature disabled + if (!pEnvDS) { + LogErr("RenderInteriorENVMap() DS doesn't exists"); + return true; + } + D3DSURFACE_DESC desc; + pEnvDS->GetDesc(&desc); - // Create a main EnvMap with mipmap chain for blurred maps -------------------------------------------------------------------- + // Create a EnvMap if doesn't already exists -------------------------------------------------------------------- // - if (pIrdEnv == NULL) - { - D3DSURFACE_DESC desc; - pIrDS->GetDesc(&desc); - if (D3DXCreateCubeTexture(pDev, desc.Width, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pIrdEnv) != S_OK) { - LogErr("Failed to create env cubemap for visual %s", _PTR(this)); + if (!ec->pCube) { + if (D3DXCreateCubeTexture(pDev, desc.Width, 5, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &ec->pCube) != S_OK) { + LogErr("Failed to create env-cubemap for visual %s", _PTR(this)); return true; } - if (D3DXCreateTexture(pDev, 128, 64, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pIrrad) != S_OK) { + } + + + // Create a Irradiance map if doesn't already exists -------------------------------------------------------------------- + // + if (!ec->pIrrad) { + if (D3DXCreateTexture(pDev, 128, 64, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &ec->pIrrad) != S_OK) { LogErr("Failed to create irradiance map for visual %s", _PTR(this)); return true; } } - - // Create blurred maps ------------------------------------------------------------------------------- + + // Compute blur and irradiance -------------------------------------------------------------------------- // - if (iFace >= 6) { - iFace = 0; - scn->IntegrateIrradiance(this, pIrdEnv, pIrrad); + if (ec->bRendered) { + scn->RenderBlurredMap(pDev, ec->pCube); + if (Config->ExpVCLight) scn->IntegrateIrradiance(this, ec, true); + ec->bRendered = false; return true; } + // Render EnvMaps --------------------------------------------------------------------------------------- // + std::set RndList; + std::set AddLightSrc; // empty - std::set RndList = scn->GetVessels(1e3, true); - std::set AddLightSrc; + RndList.insert(this); - RndList.erase(this); - AddLightSrc.insert(this); + if (ec->pCube) + { + auto eStage = GetExteriorEnvMap(); - ENVCAMREC *eCam = pMatMgr->GetCamera(0); + VECTOR3 gp; + vessel->Local2Global(ec->lPos._V(), gp); - DWORD nAtc = vessel->AttachmentCount(false); - DWORD nDoc = vessel->DockCount(); + //FVECTOR3 rpos = TransformCoord(ec->lPos, FMATRIX4(mWorld)); + //FMATRIX4 mW, mWI; + //oapiMatrixIdentity(&mW); + //D3DMAT_SetTranslation(&mW, &rpos._V()); + //oapiMatrixInverse(&mWI, NULL, &mW); - for (DWORD i = 0; iGetAttachmentHandle(false, i); - if (hAtc) { - OBJHANDLE hAtcObj = vessel->GetAttachmentStatus(hAtc); - if (hAtcObj) { - vObject *vObj = gc->GetScene()->GetVisObject(hAtcObj); - if (vObj) RndList.erase((vVessel *)vObj); - } - } - } - + // Prepare camera and scene for env map rendering + scn->PushCamera(); + scn->BeginPass(RENDERPASS_ENVCAM); + scn->SetCameraFrustumLimits(0.1, 3e4); + gc->PushRenderTarget(NULL, pEnvDS, RENDERPASS_ENVCAM); - // ----------------------------------------------------------------------------------------------- - // - VECTOR3 gpos; - vessel->Local2Global(_V(0,0,0), gpos); + FMATRIX4 mEnv; + FVECTOR3 dir, up; + LPDIRECT3DSURFACE9 pSrf = NULL; - // Prepare camera and scene for env map rendering - scn->PushCamera(); - scn->SetupInternalCamera(NULL, &gpos, 0.7853981634, 1.0); - scn->BeginPass(RENDERPASS_ENVCAM); + for (DWORD i = 0; i < 6; i++) + { + HR(ec->pCube->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &pSrf)); + gc->AlterRenderTarget(pSrf, pEnvDS); + EnvMapDirection(i, &dir, &up); + FVECTOR3 cp = crossp(up, dir); + normalize(cp); + oapiMatrixIdentity(&mEnv); + D3DMAT_FromAxis(&mEnv, &cp, &up, &dir); + //oapiMatrixMultiply(&mEnv, &mWI, &mEnv); + + //scn->CameraOffOrigin90(&mEnv, rpos); + scn->SetupInternalCamera(&mEnv, &gp, 0.7853981634, 1.0); + scn->RenderStageSet(eStage->pCube); + + SAFE_RELEASE(pSrf); + } - gc->PushRenderTarget(NULL, pIrDS, RENDERPASS_ENVCAM); + gc->PopRenderTargets(); + scn->PopPass(); + scn->PopCamera(); - D3DXMATRIX mEnv; - D3DXVECTOR3 dir, up; - LPDIRECT3DSURFACE9 pSrf = NULL; - - for (DWORD i = 0; ibRendered = true; + } - HR(pIrdEnv->GetCubeMapSurface(D3DCUBEMAP_FACES(iFace), 0, &pSrf)); + return false; +} - gc->AlterRenderTarget(pSrf, pIrDS); - EnvMapDirection(iFace, &dir, &up); +// ============================================================================================ +// Return exterior env-map for a vessel, seek from a root if doesn't exists. +// Root must always have a map +// +ENVCAMREC*vVessel::GetExteriorEnvMap() +{ + if (ecDefExt.pCube) return &ecDefExt; + else if (vRoot != this && vRoot) return vRoot->GetExteriorEnvMap(); - D3DXVECTOR3 cp; - D3DXVec3Cross(&cp, &up, &dir); - D3DXVec3Normalize(&cp, &cp); - D3DXMatrixIdentity(&mEnv); - D3DMAT_FromAxis(&mEnv, &cp, &up, &dir); + assert(Config->EnvMapMode != 0); // Should always return valid map if feature is enabled + return nullptr; +} - scn->SetCameraFrustumLimits(0.25, 1e8); - scn->SetupInternalCamera(&mEnv, NULL, 0.7853981634, 1.0); - scn->RenderSecondaryScene(RndList, AddLightSrc, flags); - SAFE_RELEASE(pSrf); +// ============================================================================================ +// +ENVCAMREC* vVessel::CreateEnvCam(EnvCamType ec, int idx) +{ + if (ec == EnvCamType::Interior) + { + if (idx >= MAX_INTCAM) return nullptr; + auto x = new ENVCAMREC; + x->type = ec; - iFace++; - if (iFace >= 6) break; + if (idx < 0) { + for (auto& c : InteriorCams) if (!c) { c = x; return x; } // Use empty slot + idx = 0; // No empty slot + } + SAFE_DELETE(InteriorCams[idx]); + InteriorCams[idx] = x; + return x; } + if (ec == EnvCamType::Exterior) { + return &ecDefExt; + } + return nullptr; +} - gc->PopRenderTargets(); - - scn->PopPass(); - scn->PopCamera(); +// ============================================================================================ +// +bool vVessel::HasOwnEnvCam(EnvCamType ec) +{ + if (ec == EnvCamType::Exterior && (ecDefExt.flags & ENVCAM_USER)) return true; + if (ec == EnvCamType::Interior) for (auto x : InteriorCams) if (x) return true; + if (ec == EnvCamType::Mesh) { + // TODO: + } return false; } +// ============================================================================================ +// +ENVCAMREC* vVessel::GetEnvCam(EnvCamType ec, int idx) +{ + if (ec == EnvCamType::Exterior) return &ecDefExt; + if (ec == EnvCamType::Interior) { + if (idx >= MAX_INTCAM) return nullptr; + if (idx < 0) { + for (auto c : InteriorCams) if (c) return c; // Get any cam + } + else return InteriorCams[std::clamp(idx, 0, MAX_INTCAM - 1)]; + } + if (ec == EnvCamType::Mesh) { + // TODO: + } + return nullptr; +} + +// ============================================================================================ +// +bool vVessel::GetInteriorCams(std::list* pCams) +{ + pCams->clear(); + for (auto x : InteriorCams) if (x) pCams->push_back(x); + return !pCams->empty(); +} + // ============================================================================================ // -void vVessel::RenderLightCone(LPD3DXMATRIX pWT) +bool vVessel::IsRoot() const +{ + return (vRoot == nullptr) | (vRoot == this); +} + +// ============================================================================================ +// +void vVessel::RenderLightCone(FMATRIX4* pWT) { if (DebugControls::sEmitter == 0) return; if (DebugControls::Emitters.count(DebugControls::sEmitter) == 0) return; @@ -1420,21 +1751,21 @@ void vVessel::RenderLightCone(LPD3DXMATRIX pWT) VECTOR3 _D = em->GetDirection(); if (em->GetType() == LightEmitter::LT_SPOT) { - D3DXVECTOR3 Main[2]; - Main[0] = D3DXVEC(_P); - Main[1] = D3DXVEC(_P + _D * R); + FVECTOR3 Main[2]; + Main[0] = _F(_P); + Main[1] = _F(_P + _D * double(R)); WORD Idx[2] = { 0, 1 }; D3D9Effect::RenderLines(Main, Idx, 2, 2, pWT, 0xFF00FF00); - D3DXVECTOR3 Circle[65]; + FVECTOR3 Circle[65]; WORD CIdx[130]; VECTOR3 _X = crossp(_D, _V(0.4, 0.2, -0.6)); VECTOR3 _Y = crossp(_D, _X); - D3DXVECTOR3 _x = D3DXVEC(_X); - D3DXVECTOR3 _y = D3DXVEC(_Y); - D3DXVECTOR3 _d = D3DXVEC(_D); + FVECTOR3 _x = _F(_X); + FVECTOR3 _y = _F(_Y); + FVECTOR3 _d = _F(_D); float q = tan(P*0.5f) * R; float a = 0.0f; @@ -1449,15 +1780,6 @@ void vVessel::RenderLightCone(LPD3DXMATRIX pWT) } -// ============================================================================================ -// -LPDIRECT3DCUBETEXTURE9 vVessel::GetEnvMap(int idx) -{ - if (idx>=0 && idx<4) return pEnv[idx]; - return NULL; -} - - // ============================================================================================ // bool vVessel::ModLighting() @@ -1583,7 +1905,7 @@ void vVessel::Animate(UINT an, UINT mshidx) { double s0, s1, ds; UINT i, ii; - D3DXMATRIX T; + FMATRIX4 T; ANIMATION *A = anim+an; for (ii = 0; ii < A->ncomp; ii++) { @@ -1607,29 +1929,29 @@ void vVessel::Animate(UINT an, UINT mshidx) { case MGROUP_TRANSFORM::NULLTRANSFORM: { - D3DMAT_Identity (&T); + oapiMatrixIdentity (&T); AnimateComponent (AC, T); } break; case MGROUP_TRANSFORM::ROTATE: { MGROUP_ROTATE *rot = (MGROUP_ROTATE*)AC->trans; - D3DXVECTOR3 ax(float(rot->axis.x), float(rot->axis.y), float(rot->axis.z)); + FVECTOR3 ax(float(rot->axis.x), float(rot->axis.y), float(rot->axis.z)); D3DMAT_RotationFromAxis (ax, (float)ds*rot->angle, &T); - float dx = D3DVAL(rot->ref.x), dy = D3DVAL(rot->ref.y), dz = D3DVAL(rot->ref.z); - T._41 = dx - T._11*dx - T._21*dy - T._31*dz; - T._42 = dy - T._12*dx - T._22*dy - T._32*dz; - T._43 = dz - T._13*dx - T._23*dy - T._33*dz; + float dx = float(rot->ref.x), dy = float(rot->ref.y), dz = float(rot->ref.z); + T.m41 = dx - T.m11*dx - T.m21*dy - T.m31*dz; + T.m42 = dy - T.m12*dx - T.m22*dy - T.m32*dz; + T.m43 = dz - T.m13*dx - T.m23*dy - T.m33*dz; AnimateComponent (AC, T); } break; case MGROUP_TRANSFORM::TRANSLATE: { MGROUP_TRANSLATE *lin = (MGROUP_TRANSLATE*)AC->trans; - D3DMAT_Identity (&T); - T._41 = (float)(ds*lin->shift.x); - T._42 = (float)(ds*lin->shift.y); - T._43 = (float)(ds*lin->shift.z); + oapiMatrixIdentity (&T); + T.m41 = (float)(ds*lin->shift.x); + T.m42 = (float)(ds*lin->shift.y); + T.m43 = (float)(ds*lin->shift.z); AnimateComponent (AC, T); } break; @@ -1638,13 +1960,13 @@ void vVessel::Animate(UINT an, UINT mshidx) MGROUP_SCALE *scl = (MGROUP_SCALE*)AC->trans; s0 = (s0-AC->state0)/(AC->state1-AC->state0); s1 = (s1-AC->state0)/(AC->state1-AC->state0); - D3DMAT_Identity (&T); - T._11 = (float)((s1*(scl->scale.x-1)+1)/(s0*(scl->scale.x-1)+1)); - T._22 = (float)((s1*(scl->scale.y-1)+1)/(s0*(scl->scale.y-1)+1)); - T._33 = (float)((s1*(scl->scale.z-1)+1)/(s0*(scl->scale.z-1)+1)); - T._41 = (float)scl->ref.x * (1.0f-T._11); - T._42 = (float)scl->ref.y * (1.0f-T._22); - T._43 = (float)scl->ref.z * (1.0f-T._33); + oapiMatrixIdentity (&T); + T.m11 = (float)((s1*(scl->scale.x-1)+1)/(s0*(scl->scale.x-1)+1)); + T.m22 = (float)((s1*(scl->scale.y-1)+1)/(s0*(scl->scale.y-1)+1)); + T.m33 = (float)((s1*(scl->scale.z-1)+1)/(s0*(scl->scale.z-1)+1)); + T.m41 = (float)scl->ref.x * (1.0f-T.m11); + T.m42 = (float)scl->ref.y * (1.0f-T.m22); + T.m43 = (float)scl->ref.z * (1.0f-T.m33); AnimateComponent (AC, T); } break; } @@ -1654,7 +1976,7 @@ void vVessel::Animate(UINT an, UINT mshidx) // ============================================================================================ // -void vVessel::AnimateComponent (ANIMATIONCOMP *comp, const D3DXMATRIX &T) +void vVessel::AnimateComponent (ANIMATIONCOMP *comp, const FMATRIX4 &T) { UINT i; @@ -1714,7 +2036,91 @@ void vVessel::AnimateComponent (ANIMATIONCOMP *comp, const D3DXMATRIX &T) // ============================================================================================ // +void vVessel::SetVisualProperty(VisualProp prp, int idx, const type_info& t, const void* val) +{ + if (t == typeid(FVECTOR3)) + { + FVECTOR3* v = (FVECTOR3*)val; + + if (prp == VisualProp::BAKED_LIGHT) { + if (idx >= 0 && idx <= 15) { + if (BakedLightsControl[idx] != *v) { + BakedLightsControl[idx] = *v; + bMustRebake = true; + } + } + return; + } + + if (prp == VisualProp::AMBIENT) { + VCAmbient = *v; + return; + } + if (prp == VisualProp::EXT_PROBE_POS) { + ecDefExt.lPos = *v; + ecDefExt.flags |= ENVCAM_USER; + return; + } + + if (prp == VisualProp::CREATE_VC_PROBE) { + auto ec = GetEnvCam(EnvCamType::Interior, idx); + if (!ec) ec = CreateEnvCam(EnvCamType::Interior, idx); + ec->lPos = *v; + oapiWriteLogV("-- [ CREATE_VC_PROBE ] --"); + return; + } + } + + if (t == typeid(float)) + { + float* v = (float*)val; + auto ec = GetEnvCam(EnvCamType::Interior, idx); + if (ec) { + if (prp == VisualProp::DA_CURVE) ec->da_curve = *v; + if (prp == VisualProp::DA_BOUNCH) ec->da_bounch = *v; + if (prp == VisualProp::DA_FORCE) ec->da_force = *v; + } + } + + oapiWriteLogV("Failed to set visual property prp=%d, type=[%s]", int(prp), t.name()); +} + + +// ============================================================================================ +// +bool vVessel::GetVisualProperty(VisualProp prp, int idx, const type_info& t, void* val) +{ + if (t == typeid(FVECTOR3)) + { + if (prp == VisualProp::BAKED_LIGHT) if (idx >= 0 && idx <= 15) { + *((FVECTOR3*)val) = BakedLightsControl[idx]; + return true; + } + + if (prp == VisualProp::AMBIENT) { + *((FVECTOR3*)val) = VCAmbient; + return true; + } + } + + if (t == typeid(float)) + { + auto ec = GetEnvCam(EnvCamType::Interior, idx); + if (ec) { + if (prp == VisualProp::DA_CURVE) *((float*)val) = ec->da_curve; + if (prp == VisualProp::DA_BOUNCH) *((float*)val) = ec->da_bounch; + if (prp == VisualProp::DA_FORCE) *((float*)val) = ec->da_force; + } + } + + oapiWriteLogV("Failed to get visual property prp=%d, type=[%s]", int(prp), t.name()); + return false; +} + + +// ============================================================================================ +// void vVessel::RenderReentry(LPDIRECT3DDEVICE9 dev) { @@ -1743,9 +2149,9 @@ void vVessel::RenderReentry(LPDIRECT3DDEVICE9 dev) float size = float(vessel->GetSize()) * 1.7f; - D3DXVECTOR3 vPosA(float(cpos.x), float(cpos.y), float(cpos.z)); - D3DXVECTOR3 vDir(float(d.x), float(d.y), float(d.z)); - D3DXVECTOR3 vPosB = vPosA + vDir * (size*0.25f); + FVECTOR3 vPosA(float(cpos.x), float(cpos.y), float(cpos.z)); + FVECTOR3 vDir(float(d.x), float(d.y), float(d.z)); + FVECTOR3 vPosB = vPosA + vDir * (size*0.25f); D3D9Effect::RenderReEntry(defreentrytex, &vPosA, &vPosB, &vDir, alpha_A, alpha_B, size); } @@ -1757,20 +2163,20 @@ bool vVessel::GetMinMaxDistance(float *zmin, float *zmax, float *dmin) { if (bBSRecompute) UpdateBoundingBox(); - D3DXMATRIX mWorldView, mWorldViewTrans, mTF; + FMATRIX4 mWorldView, mWorldViewTrans, mTF; WORD vismode = MESHVIS_EXTERNAL | MESHVIS_EXTPASS; Scene *scn = gc->GetScene(); - D3DXVECTOR4 Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); + FVECTOR4 Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); - D3DXMatrixMultiply(&mWorldView, &mWorld, scn->GetViewMatrix()); + oapiMatrixMultiply(&mWorldView, &mWorld, scn->GetViewMatrix()); for (DWORD i=0;iGetDevice(), meshlist[i].mesh->GetAABB(), &mWorldViewTrans, &Field, zmin, zmax, dmin); } else { @@ -1794,20 +2200,20 @@ void vVessel::UpdateBoundingBox() WORD vismode = MESHVIS_EXTERNAL | MESHVIS_EXTPASS; - D3DXMATRIX mTF; + FMATRIX4 mTF; for (DWORD i=0;iGetTransform(); - LPD3DXMATRIX pTF = &mTF; + FMATRIX4* pMeshTF = meshlist[i].mesh->GetTransform(); + FMATRIX4* pTF = &mTF; if (meshlist[i].trans) { - if (pMeshTF) D3DXMatrixMultiply(pTF, meshlist[i].trans, pMeshTF); + if (pMeshTF) oapiMatrixMultiply(pTF, meshlist[i].trans, pMeshTF); else pTF = meshlist[i].trans; } else { if (pMeshTF) pTF = pMeshTF; @@ -1831,14 +2237,14 @@ void vVessel::UpdateBoundingBox() if (lvl==0.0) continue; vessel->GetExhaustSpec(i, &es); VECTOR3 e = (*es.lpos) - (*es.ldir) * (es.lofs + es.lsize*lvl); - D3DXVECTOR3 ext(float(e.x), float(e.y), float(e.z)); + FVECTOR3 ext(float(e.x), float(e.y), float(e.z)); D9AddPointAABB(&BBox, &ext); } for (DWORD i=0;iGetExhaustSpec(i, &es); VECTOR3 r = (*es.lpos); - D3DXVECTOR3 ref(float(r.x), float(r.y), float(r.z)); + FVECTOR3 ref(float(r.x), float(r.y), float(r.z)); D9AddPointAABB(&BBox, &ref); } } @@ -1848,10 +2254,10 @@ void vVessel::UpdateBoundingBox() // =========================================================================================== // -D3D9Pick vVessel::Pick(const D3DXVECTOR3 *vDir) +D3D9Pick vVessel::Pick(const FVECTOR3 *vDir, const PickProp *p) { - D3DXMATRIX mWT; - LPD3DXMATRIX pWT = NULL; + FMATRIX4 mWT; + FMATRIX4* pWT = NULL; DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); DWORD displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); @@ -1877,18 +2283,31 @@ D3D9Pick vVessel::Pick(const D3DXVECTOR3 *vDir) // check if mesh should be rendered in this pass WORD vismode = meshlist[i].vismode; - if (vismode==0) continue; + if (DebugControls::IsActive() && (DebugControls::debugFlags & DBG_FLAGS_RENDEREXT)) + { + vismode |= MESHVIS_EXTPASS; + } + + if (vismode == 0) continue; + + bool bRender = false; if (bCockpit) { - if (!(vismode & MESHVIS_COCKPIT)) { - if ((!bVC) || (!(vismode & MESHVIS_VC))) continue; + // Internal View + if (vismode & MESHVIS_COCKPIT) bRender = true; + if (vismode & MESHVIS_EXTPASS) bRender = true; + if (bVC) { + if (vismode & MESHVIS_VC) bRender = true; } } else { - if (!(vismode & MESHVIS_EXTERNAL)) continue; + // External view + if (vismode & MESHVIS_EXTERNAL) bRender = true; } - D3D9Pick pick = hMesh->Pick(&mWorld, meshlist[i].trans, vDir); + if (!bRender) continue; + + D3D9Pick pick = hMesh->Pick(&mWorld, meshlist[i].trans, vDir, p); if (pick.pMesh) if (pick.dist= pMesh->GetGroupCount()) return -3; - if (func == gcCore::MatrixId::MESH) memcpy_s(pMat, sizeof(FMATRIX4), ptr(pMesh->GetTransform(-1, false)), sizeof(D3DXMATRIX)); - if (func == gcCore::MatrixId::GROUP) memcpy_s(pMat, sizeof(FMATRIX4), ptr(pMesh->GetTransform(gi, false)), sizeof(D3DXMATRIX)); + if (func == gcCore::MatrixId::MESH) memcpy_s(pMat, sizeof(FMATRIX4), ptr(pMesh->GetTransform(-1, false)), sizeof(FMATRIX4)); + if (func == gcCore::MatrixId::GROUP) memcpy_s(pMat, sizeof(FMATRIX4), ptr(pMesh->GetTransform(gi, false)), sizeof(FMATRIX4)); if (func == gcCore::MatrixId::OFFSET) { - if (meshlist[mi].trans) memcpy_s(pMat, sizeof(FMATRIX4), meshlist[mi].trans, sizeof(D3DXMATRIX)); + if (meshlist[mi].trans) memcpy_s(pMat, sizeof(FMATRIX4), meshlist[mi].trans, sizeof(FMATRIX4)); else { - D3DXMATRIX Ident; - D3DXMatrixIdentity(&Ident); - memcpy_s(pMat, sizeof(FMATRIX4), &Ident, sizeof(D3DXMATRIX)); + FMATRIX4 Ident; + oapiMatrixIdentity(&Ident); + memcpy_s(pMat, sizeof(FMATRIX4), &Ident, sizeof(FMATRIX4)); } return 0; } if (func == gcCore::MatrixId::COMBINED) { - D3DXMATRIX MeshGrp = pMesh->GetTransform(gi, true); + FMATRIX4 MeshGrp = pMesh->GetTransform(gi, true); if (meshlist[mi].trans) { - D3DXMATRIX MeshGrpTrans; - D3DXMatrixMultiply(&MeshGrpTrans, &MeshGrp, meshlist[mi].trans); - memcpy_s(pMat, sizeof(FMATRIX4), &MeshGrpTrans, sizeof(D3DXMATRIX)); + FMATRIX4 MeshGrpTrans; + oapiMatrixMultiply(&MeshGrpTrans, &MeshGrp, meshlist[mi].trans); + memcpy_s(pMat, sizeof(FMATRIX4), &MeshGrpTrans, sizeof(FMATRIX4)); } - else memcpy_s(pMat, sizeof(FMATRIX4), &MeshGrp, sizeof(D3DXMATRIX)); + else memcpy_s(pMat, sizeof(FMATRIX4), &MeshGrp, sizeof(FMATRIX4)); } return 0; @@ -1942,12 +2361,12 @@ int vVessel::SetMatrixTransform(gcCore::MatrixId func, DWORD mi, DWORD gi, const if (gi >= pMesh->GetGroupCount()) return -3; if (func == gcCore::MatrixId::OFFSET) { - if (meshlist[mi].trans == NULL) meshlist[mi].trans = new D3DXMATRIX; - memcpy_s(meshlist[mi].trans, sizeof(D3DXMATRIX), pMat, sizeof(FMATRIX4)); + if (meshlist[mi].trans == NULL) meshlist[mi].trans = new FMATRIX4; + memcpy_s(meshlist[mi].trans, sizeof(FMATRIX4), pMat, sizeof(FMATRIX4)); } - if (func == gcCore::MatrixId::MESH) if (!pMesh->SetTransform(-1, (LPD3DXMATRIX)pMat)) return -4; - if (func == gcCore::MatrixId::GROUP) if (!pMesh->SetTransform(gi, (LPD3DXMATRIX)pMat)) return -5; + if (func == gcCore::MatrixId::MESH) if (!pMesh->SetTransform(-1, (FMATRIX4*)pMat)) return -4; + if (func == gcCore::MatrixId::GROUP) if (!pMesh->SetTransform(gi, (FMATRIX4*)pMat)) return -5; return 0; } @@ -1968,17 +2387,18 @@ void vVessel::ReloadTextures() SurfNative * vVessel::defreentrytex = 0; SurfNative * vVessel::defexhausttex = 0; +ShaderClass* vVessel::pRenderZone = 0; // ============================================================== // Nonmember helper functions -void TransformPoint (VECTOR3 &p, const D3DXMATRIX &T) +void TransformPoint (VECTOR3 &p, const FMATRIX4 &T) { - double x = p.x*T._11 + p.y*T._21 + p.z*T._31 + T._41; - double y = p.x*T._12 + p.y*T._22 + p.z*T._32 + T._42; - double z = p.x*T._13 + p.y*T._23 + p.z*T._33 + T._43; - double w = 1.0/(p.x*T._14 + p.y*T._24 + p.z*T._34 + T._44); + double x = p.x*T.m11 + p.y*T.m21 + p.z*T.m31 + T.m41; + double y = p.x*T.m12 + p.y*T.m22 + p.z*T.m32 + T.m42; + double z = p.x*T.m13 + p.y*T.m23 + p.z*T.m33 + T.m43; + double w = 1.0/(p.x*T.m14 + p.y*T.m24 + p.z*T.m34 + T.m44); p.x = x*w; p.y = y*w; p.z = z*w; @@ -1986,11 +2406,11 @@ void TransformPoint (VECTOR3 &p, const D3DXMATRIX &T) // =============================================================== // -void TransformDirection (VECTOR3 &a, const D3DXMATRIX &T, bool normalise) +void TransformDirection (VECTOR3 &a, const FMATRIX4 &T, bool normalise) { - double x = a.x*T._11 + a.y*T._21 + a.z*T._31; - double y = a.x*T._12 + a.y*T._22 + a.z*T._32; - double z = a.x*T._13 + a.y*T._23 + a.z*T._33; + double x = a.x*T.m11 + a.y*T.m21 + a.z*T.m31; + double y = a.x*T.m12 + a.y*T.m22 + a.z*T.m32; + double z = a.x*T.m13 + a.y*T.m23 + a.z*T.m33; a.x = x, a.y = y, a.z = z; if (normalise) { double len = 1.0/sqrt (x*x + y*y + z*z); diff --git a/OVP/D3D9Client/VVessel.h b/OVP/D3D9Client/VVessel.h index 2ad3b2e24..a9bf0c453 100644 --- a/OVP/D3D9Client/VVessel.h +++ b/OVP/D3D9Client/VVessel.h @@ -9,6 +9,8 @@ #ifndef __VVESSEL_H #define __VVESSEL_H +#define MAX_INTCAM 6 + #include "VObject.h" #include "Mesh.h" #include "gcCore.h" @@ -44,6 +46,19 @@ typedef struct { class vVessel: public vObject { public: friend class D3D9Client; + + enum Errors { NoVC, ITEMS }; // ITEMS must be last entry + + enum class SMI { VC, Visual, Mesh }; + + struct Render { + static const int VC = 0x1; // Camera in VC view + static const int D2 = 0x2; // Camera in 2D panel view + static const int IP = 0x4; // Internal pass + }; + + vVessel* vRoot = nullptr; + /** * \brief Creates a new vessel visual for a scene * \param _hObj vessel object handle @@ -66,15 +81,24 @@ class vVessel: public vObject { void clbkEvent(DWORD evnt, DWORD_PTR context); - MESHHANDLE GetMesh (UINT idx); + D3D9Mesh* GetMesh (UINT idx); + DWORD GetMeshVisMode(UINT idx); bool GetMinMaxDistance(float *zmin, float *zmax, float *dmin); - void GetMinMaxLightDist(float *mind, float *maxd); int GetMatrixTransform(gcCore::MatrixId matrix_id, DWORD mesh, DWORD group, FMATRIX4 *pMat); int SetMatrixTransform(gcCore::MatrixId matrix_id, DWORD mesh, DWORD group, const FMATRIX4 *pMat); + bool GetVCPos(FVECTOR3* cpos, FVECTOR3* lpos, float* rad); + bool GetMeshPosition(int idx, FVECTOR3* cpos, FVECTOR3* lpos, float* rad); + void BakeLights(ImageProcessing* pBaker); + void ErrorOnce(Errors e); void UpdateBoundingBox(); - bool IsInsideShadows(); - bool IntersectShadowVolume(); - bool IntersectShadowTarget(); + + // Shadow Map Methods + bool IsInsideShadows(const SMapInput* shd); + bool IntersectShadowVolume(const SMapInput* shd); + bool IntersectShadowTarget(const SMapInput* shd); + void GetMinMaxLightDist(const SMapInput* shd, float* mind, float* maxd); + bool GetSMapRenderData(SMI type, int idx, SMapInput* sm); + void ReloadTextures(); inline DWORD GetMeshCount(); @@ -113,7 +137,8 @@ class vVessel: public vObject { * in cockpit camera mode. * \sa Render(LPDIRECT3DDEVICE9) */ - bool Render (LPDIRECT3DDEVICE9 dev, bool bInternalPass); + bool Render (LPDIRECT3DDEVICE9 dev, const SHADOWMAP *sm, DWORD flags); + bool Render(LPDIRECT3DDEVICE9 dev, bool internalpass, const SHADOWMAP* shd); bool RenderExhaust(); @@ -121,22 +146,30 @@ class vVessel: public vObject { * \brief Render the vessel's active light beacons * \param dev render device */ - void RenderLightCone (LPD3DXMATRIX pWT); + void RenderLightCone (FMATRIX4* pWT); void RenderBeacons (LPDIRECT3DDEVICE9 dev); void RenderReentry (LPDIRECT3DDEVICE9 dev); void RenderGrapplePoints (LPDIRECT3DDEVICE9 dev); void RenderGroundShadow (LPDIRECT3DDEVICE9 dev, OBJHANDLE hPlanet, float depth); void RenderVectors (LPDIRECT3DDEVICE9 dev, D3D9Pad *pSkp); - bool RenderENVMap (LPDIRECT3DDEVICE9 pDev, DWORD cnt=2, DWORD flags=0xFF); - bool ProbeIrradiance(LPDIRECT3DDEVICE9 pDev, DWORD cnt = 2, DWORD flags = 0xFF); + void RenderClickZones(); + bool RenderENVMap (LPDIRECT3DDEVICE9 pDev, ENVCAMREC* ec, DWORD cnt = 2, DWORD flags = 0xFF); + bool RenderInteriorENVMap(LPDIRECT3DDEVICE9 pDev, ENVCAMREC* ec, SHADOWMAP* sm); + bool ProcessEnvMaps(LPDIRECT3DDEVICE9 pDev, DWORD cnt, DWORD flags); + + ENVCAMREC* GetExteriorEnvMap(); + ENVCAMREC* CreateEnvCam(EnvCamType ec, int idx = -1); + ENVCAMREC* GetEnvCam(EnvCamType ec, int idx = -1); + bool GetInteriorCams(std::list* pCams); + bool HasOwnEnvCam(EnvCamType ec); - LPDIRECT3DCUBETEXTURE9 GetEnvMap(int idx); - LPDIRECT3DCUBETEXTURE9 GetIrradEnv() { return pIrdEnv; } - LPDIRECT3DTEXTURE9 GetIrradianceMap() { return pIrrad; } + bool IsRoot() const; + vVessel* GetRoot() const { return vRoot; } + float GetExhaustLength() const { return ExhaustLength; } - D3D9Pick Pick(const D3DXVECTOR3 *vDir); + D3D9Pick Pick(const FVECTOR3 *vDir, const PickProp* p); bool HasExtPass(); bool HasShadow(); @@ -153,6 +186,10 @@ class vVessel: public vObject { */ void UpdateAnimations(int mshidx = -1); + void SetVisualProperty(VisualProp prp, int idx, const type_info& t, const void* val); + bool GetVisualProperty(VisualProp prp, int idx, const type_info& t, void* val); + + protected: void LoadMeshes(); @@ -185,7 +222,7 @@ class vVessel: public vObject { bool ModLighting(); void Animate (UINT an, UINT mshidx); - void AnimateComponent (ANIMATIONCOMP *comp, const D3DXMATRIX &T); + void AnimateComponent (ANIMATIONCOMP *comp, const FMATRIX4 &T); void RestoreDefaultState(ANIMATIONCOMP *AC); void StoreDefaultState(ANIMATIONCOMP *AC); void DeleteDefaultState(ANIMATIONCOMP *AC); @@ -198,21 +235,20 @@ class vVessel: public vObject { std::map defstate; std::unordered_set applyanim; std::map currentstate; + ENVCAMREC* InteriorCams[MAX_INTCAM] = { nullptr }; + // Default eCam configurations + ENVCAMREC ecDefExt; + FVECTOR3 BakedLightsControl[16]; + FVECTOR3 VCAmbient; + bool bMustRebake; VESSEL *vessel; // access instance for the vessel class MatMgr *pMatMgr; - LPDIRECT3DCUBETEXTURE9 pEnv[4], pIrdEnv; - LPDIRECT3DTEXTURE9 pIrrad; - - int nEnv; // Number of environmental maps - int iFace; // EnvMap Face index that is to be rendered next - int eFace; - struct MESHREC { D3D9Mesh *mesh; // DX9 mesh representation - D3DXMATRIX *trans; // mesh transformation matrix (rel. to vessel frame) + FMATRIX4 *trans; // mesh transformation matrix (rel. to vessel frame) WORD vismode; } *meshlist; // list of associated meshes @@ -223,6 +259,7 @@ class vVessel: public vObject { float ExhaustLength; static class SurfNative *defreentrytex, *defexhausttex; + static class ShaderClass* pRenderZone; }; #endif // !__VVESSEL_H diff --git a/OVP/D3D9Client/VectorHelpers.h b/OVP/D3D9Client/VectorHelpers.h index 843084014..4e2b8d55a 100644 --- a/OVP/D3D9Client/VectorHelpers.h +++ b/OVP/D3D9Client/VectorHelpers.h @@ -65,21 +65,6 @@ template inline _constexpr_ T sign (T val) return val < T(0) ? T(-1) : T(1); } -template inline _constexpr_ T lerp (T a, T b, T x) -{ - return a + (b - a)*x; -} -// ...slightly faster than the above, but not available in Visual Studio 2012 (I think)? -//template inline _constexpr_ T lerp(T v0, T v1, T t) { -// return fma(t, v1, fma(-t, v0, v0)); -//} - -template inline _constexpr_ T saturate (T val) -{ - return (val > T(1)) ? T(1) - : (val < T(0)) ? T(0) - : val; -} template inline _constexpr_ T clamp(T x, T a, T b) { @@ -93,15 +78,6 @@ template inline _constexpr_ T ilerp(T a, T b, T x) return saturate((x - a) / (b - a)); } -template inline _constexpr_ T sqr(T a) -{ - return a * a; -} - -template inline _constexpr_ T hermite(T a) -{ - return a * a * (T(3) - T(2)*a); -} // VECTOR3 Helpers ================================================================== @@ -226,11 +202,6 @@ inline VECTOR4 exp2(const VECTOR4 &v) return _V(exp2(v.x), exp2(v.y), exp2(v.z), exp2(v.w)); } -inline VECTOR4 rcp(const VECTOR4 &v) -{ - return _V(1.0/v.x, 1.0/v.y, 1.0/v.z, 1.0/v.w); -} - inline VECTOR4 vmax(const VECTOR4 &v, const VECTOR4 &w) { return _V(std::max(v.x, w.x), std::max(v.y, w.y), std::max(v.z, w.z), std::max(v.w, w.w)); @@ -241,102 +212,14 @@ inline VECTOR4 vmin(const VECTOR4 &v, const VECTOR4 &w) return _V(std::min(v.x, w.x), std::min(v.y, w.y), std::min(v.z, w.z), std::min(v.w, w.w)); } - -// D3DXVECTOR3 Helpers ================================================================== -// -// - -inline D3DXVECTOR3 exp2(const D3DXVECTOR3 &v) -{ - return D3DXVECTOR3(exp2(v.x), exp2(v.y), exp2(v.z)); -} - -inline D3DXVECTOR3 _D3DXVECTOR3(const VECTOR3 &v) -{ - return D3DXVECTOR3(float(v.x), float(v.y), float(v.z)); -} - -inline D3DXVECTOR3 _D3DXVECTOR3(double x, double y, double z) -{ - return D3DXVECTOR3(float(x), float(y), float(z)); -} - -inline D3DXVECTOR3 operator* (const D3DXVECTOR3 &a, const D3DXVECTOR3 &b) +inline float MaxRGB(const FVECTOR3& v) { - return D3DXVECTOR3(a.x*b.x, a.y*b.y, a.z*b.z); + return (std::max)(v.r, (std::max)(v.g, v.b)); } -inline D3DXVECTOR3 operator+ (const D3DXVECTOR3 &a, float d) +inline float MaxRGB(const FVECTOR4& v) { - return D3DXVECTOR3(a.x+d, a.y+d, a.z+d); -} - -inline D3DXVECTOR3 &operator*= (D3DXVECTOR3 &a, const D3DXVECTOR3 &b) -{ - a.x *= b.x; a.y *= b.y; a.z *= b.z; - return a; -} - -inline D3DXVECTOR3 &operator+= (D3DXVECTOR3 &a, float d) -{ - a.x += d; a.y += d; a.z += d; - return a; -} - -inline D3DXVECTOR3 rcp(const D3DXVECTOR3 &v) -{ - return D3DXVECTOR3(1.0f/v.x, 1.0f/v.y, 1.0f/v.z); -} - -inline D3DXVECTOR3 vmax(const D3DXVECTOR3 &v, const D3DXVECTOR3 &w) -{ - return D3DXVECTOR3(std::max(v.x, w.x), std::max(v.y, w.y), std::max(v.z, w.z)); -} - -inline D3DXVECTOR3 vmin(const D3DXVECTOR3 &v, const D3DXVECTOR3 &w) -{ - return D3DXVECTOR3(std::min(v.x, w.x), std::min(v.y, w.y), std::min(v.z, w.z)); -} - -inline D3DXVECTOR3 lerp(const D3DXVECTOR3 &v, const D3DXVECTOR3 &w, float x) -{ - return D3DXVECTOR3(v.x+(w.x-v.x)*x, v.y+(w.y-v.y)*x, v.z+(w.z-v.z)*x); -} - - - -// D3DXVECTOR4 Helpers ================================================================== -// -// -inline D3DXVECTOR4 abs(D3DXVECTOR4 &a) -{ - return D3DXVECTOR4(abs(a.x), abs(a.y), abs(a.z), abs(a.w)); -} - -inline D3DXVECTOR4 sign(D3DXVECTOR4 &a) -{ - return D3DXVECTOR4(sign(a.x), sign(a.y), sign(a.z), sign(a.w)); -} - -inline D3DXVECTOR4 pow(float x, D3DXVECTOR4 &y) -{ - return D3DXVECTOR4(pow(x, y.x), pow(x, y.y), pow(x, y.z), pow(x, y.w)); -} - -inline D3DXVECTOR4 operator- (const D3DXVECTOR4 &v, float d) -{ - return D3DXVECTOR4(v.x-d, v.y-d, v.z-d, v.w-d); -} - -inline D3DXVECTOR4 operator+ (const D3DXVECTOR4 &v, float d) -{ - return D3DXVECTOR4(v.x+d, v.y+d, v.z+d, v.w+d); -} - -inline D3DXVECTOR4 &operator*= (D3DXVECTOR4 &v, const D3DXVECTOR4 &d) -{ - v.x*=d.x; v.y*=d.y; v.z*=d.z; v.w*=d.w; - return v; + return (std::max)(v.r, (std::max)(v.g, v.b)); } #endif diff --git a/OVP/D3D9Client/VideoTab.cpp b/OVP/D3D9Client/VideoTab.cpp index 6b360133b..b21f15b11 100644 --- a/OVP/D3D9Client/VideoTab.cpp +++ b/OVP/D3D9Client/VideoTab.cpp @@ -713,6 +713,11 @@ void VideoTab::InitSetupDialog(HWND hWnd) //SendDlgItemMessageA(hWnd, IDC_SHADOWFILTER, CB_ADDSTRING, 0, (LPARAM)"40 samples"); //SendDlgItemMessageA(hWnd, IDC_SHADOWFILTER, CB_ADDSTRING, 0, (LPARAM)"40s dither"); + SendDlgItemMessage(hWnd, IDC_CASCOUNT, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_CASCOUNT, CB_ADDSTRING, 0, (LPARAM)"5m 1-cas low quality"); + SendDlgItemMessageA(hWnd, IDC_CASCOUNT, CB_ADDSTRING, 0, (LPARAM)"8m 2-cas hight quality"); + SendDlgItemMessageA(hWnd, IDC_CASCOUNT, CB_ADDSTRING, 0, (LPARAM)"24m 3-cas hight quality"); + SendDlgItemMessage(hWnd, IDC_TERRAIN, CB_RESETCONTENT, 0, 0); SendDlgItemMessageA(hWnd, IDC_TERRAIN, CB_ADDSTRING, 0, (LPARAM)"None"); SendDlgItemMessageA(hWnd, IDC_TERRAIN, CB_ADDSTRING, 0, (LPARAM)"Stencil"); @@ -807,6 +812,7 @@ void VideoTab::InitSetupDialog(HWND hWnd) SendDlgItemMessage(hWnd, IDC_SHADOWFILTER, CB_SETCURSEL, Config->ShadowFilter, 0); SendDlgItemMessage(hWnd, IDC_TERRAIN, CB_SETCURSEL, Config->TerrainShadowing, 0); SendDlgItemMessage(hWnd, IDC_GUIMODE, CB_SETCURSEL, Config->gcGUIMode, 0); + SendDlgItemMessage(hWnd, IDC_CASCOUNT, CB_SETCURSEL, Config->VCCascadeCount - 1, 0); SendDlgItemMessage(hWnd, IDC_DEMAND, BM_SETCHECK, Config->PlanetPreloadMode==0, 0); SendDlgItemMessage(hWnd, IDC_SRFPRELOAD, BM_SETCHECK, Config->PlanetPreloadMode==1, 0); @@ -879,10 +885,12 @@ void VideoTab::SaveSetupState(HWND hWnd) Config->ShadowMapMode = (int)SendDlgItemMessage(hWnd, IDC_SELFSHADOWS, CB_GETCURSEL, 0, 0); Config->ShadowFilter = (int)SendDlgItemMessage(hWnd, IDC_SHADOWFILTER, CB_GETCURSEL, 0, 0); Config->TerrainShadowing = (int)SendDlgItemMessage(hWnd, IDC_TERRAIN, CB_GETCURSEL, 0, 0); - Config->gcGUIMode = (int)SendDlgItemMessage(hWnd, IDC_GUIMODE, CB_GETCURSEL, 0, 0); + Config->gcGUIMode = (int)SendDlgItemMessage(hWnd, IDC_GUIMODE, CB_GETCURSEL, 0, 0); + Config->VCCascadeCount = (int)SendDlgItemMessage(hWnd, IDC_CASCOUNT, CB_GETCURSEL, 0, 0) + 1; Config->MeshRes = int(SendDlgItemMessage(hWnd, IDC_MESHRES, CB_GETCURSEL, 0, 0)); Config->MaxTiles = int(SendDlgItemMessage(hWnd, IDC_TILECOUNT, CB_GETCURSEL, 0, 0)); + if (Config->gcGUIMode == 1) Config->gcGUIMode = 0; // Check boxes diff --git a/OVP/D3D9Client/WindowMgr.cpp b/OVP/D3D9Client/WindowMgr.cpp index 1d86a8fc3..0c040e0e5 100644 --- a/OVP/D3D9Client/WindowMgr.cpp +++ b/OVP/D3D9Client/WindowMgr.cpp @@ -21,7 +21,7 @@ #include -#include +#include "MathAPI.h" #include "WindowMgr.h" #include "windows.h" #include "WindowsX.h" diff --git a/OVP/D3D9Client/gcConst.cpp b/OVP/D3D9Client/gcConst.cpp index e319f9e3c..258e16ad1 100644 --- a/OVP/D3D9Client/gcConst.cpp +++ b/OVP/D3D9Client/gcConst.cpp @@ -18,7 +18,7 @@ #include -#include +#include "MathAPI.h" #include #include "gcConst.h" #include "D3D9Surface.h" @@ -357,5 +357,5 @@ DWORD gcConst::Color(const oapi::FVECTOR4* c) // COLOUR4 gcConst::Colour4(DWORD dwABGR) { - return FVECTOR4(dwABGR).Colour4(); + return FVECTOR4(dwABGR); } diff --git a/OVP/D3D9Client/gcConst.h b/OVP/D3D9Client/gcConst.h index 9c4461c83..0abd24062 100644 --- a/OVP/D3D9Client/gcConst.h +++ b/OVP/D3D9Client/gcConst.h @@ -362,7 +362,7 @@ class gcConst * \param hMesh Handle to a devmesh containing the material * \param idx Material index * \param prop material property identifier (\ref MeshMaterialFlags) - * \param value a pointer to COLOUR4 structure containing/receiving the data, or \e NULL to reset a default value or to unspecify a property. + * \param value a pointer to FVECTOR4 structure containing/receiving the data, or \e NULL to reset a default value or to unspecify a property. * \param bSet \e true to set material value, \e false to get a material value * \return -4 = Invalid handle \n -3 = Unknown property flag \n -2 = Property not specified cannot get it \n -1 = Index out of range \n 0 = Success */ @@ -668,7 +668,7 @@ class gcConst inline void WorldMatrix(FMATRIX4 *mat, const FVECTOR3 &pos, const FVECTOR3 &x, const FVECTOR3 &z, float scale = 1.0f) { - FVECTOR3 y = cross(x, z); + FVECTOR3 y = crossp(x, z); mat->m11 = (x.x * scale); mat->m12 = (x.y * scale); mat->m13 = (x.z * scale); mat->m14 = 0.0f; mat->m21 = (y.x * scale); mat->m22 = (y.y * scale); mat->m23 = (y.z * scale); mat->m24 = 0.0f; mat->m31 = (z.x * scale); mat->m32 = (z.y * scale); mat->m33 = (z.z * scale); mat->m34 = 0.0f; diff --git a/OVP/D3D9Client/gcCore.cpp b/OVP/D3D9Client/gcCore.cpp index 76c0d04be..8e5a1f6d8 100644 --- a/OVP/D3D9Client/gcCore.cpp +++ b/OVP/D3D9Client/gcCore.cpp @@ -5,7 +5,7 @@ #include -#include +#include "MathAPI.h" #include "gcCore.h" #include "D3D9Surface.h" #include "D3D9Client.h" @@ -386,11 +386,11 @@ void gcCore::RenderMesh(DEVMESHHANDLE hMesh, const oapi::FMATRIX4* pWorld) bool gcCore::PickMesh(PickMeshStruct* pm, DEVMESHHANDLE hMesh, const FMATRIX4* pWorld, short x, short y) { Scene* pScene = g_client->GetScene(); - D3D9Pick pk = pScene->PickMesh(hMesh, (const LPD3DXMATRIX)pWorld, x, y); + D3D9Pick pk = pScene->PickMesh(hMesh, (const FMATRIX4*)pWorld, x, y); if (pk.group >= 0) { if (pk.dist < pm->dist) { - pm->pos = _FV(pk.pos); - pm->normal = _FV(pk.normal); + pm->pos = pk.pos; + pm->normal = pk.normal; pm->grp_inst = pk.group; pm->dist = pk.dist; return true; @@ -449,7 +449,7 @@ SURFHANDLE gcCore::CompressSurface(SURFHANDLE hSurface, DWORD flags) // void gcCore::RenderLines(const FVECTOR3* pVtx, const WORD* pIdx, int nVtx, int nIdx, const FMATRIX4* pWorld, DWORD color) { - D3D9Effect::RenderLines((const D3DXVECTOR3*)pVtx, pIdx, nVtx, nIdx, (const D3DXMATRIX*)pWorld, color); + D3D9Effect::RenderLines((const FVECTOR3*)pVtx, pIdx, nVtx, nIdx, (const FMATRIX4*)pWorld, color); } @@ -475,7 +475,7 @@ bool gcCore::ClearSurfaceInScene(SURFHANDLE tgt, DWORD color, LPRECT tr) if (S_OK == g_client->BeginScene()) { LPDIRECT3DSURFACE9 pts = SURFACE(tgt)->GetSurface(); - HRESULT hr = g_client->GetDevice()->ColorFill(pts, tr, (D3DCOLOR)color); + HRESULT hr = g_client->GetDevice()->ColorFill(pts, tr, (DWORD)color); g_client->EndScene(); return hr == S_OK; } @@ -520,8 +520,8 @@ gcCore::PickGround gcCore::ScanScreen(int scr_x, int scr_y) pg.elev = tp.elev; pg.level = pTile->Level(); pg.hTile = HTILE(pTile); - pg.normal = _FV(tp._n); - pg.pos = _FV(tp._p); + pg.normal = tp._n; + pg.pos = tp._p; } return pg; } @@ -612,7 +612,7 @@ gcCore::PickGround gcCore2::GetTileData(HPLANETMGR vPl, double lng, double lat, pTile->GetElevation(lng, lat, &pg.elev, &pg.normal, NULL, true, false); VECTOR3 pos = vP->GetUnitSurfacePos(lng, lat) * (vP->GetSize() + pg.elev); - MATRIX3 mRot; oapiGetRotationMatrix(vP->Object(), &mRot); + MATRIX3 mRot; oapiGetRotationMatrix(vP->GetObjHandle(), &mRot); pos = mul(mRot, pos) + vP->PosFromCamera(); diff --git a/OVP/D3D9Client/gcCore.h b/OVP/D3D9Client/gcCore.h index f8634acab..426f52e5e 100644 --- a/OVP/D3D9Client/gcCore.h +++ b/OVP/D3D9Client/gcCore.h @@ -726,39 +726,6 @@ INTERFACE_BUILDER class gcCore gc_interface bool LockSurface(SURFHANDLE hSrf, Lock* pOut, bool bWait = false); gc_interface void ReleaseLock(SURFHANDLE hSrf); - /** - * \brief Convert a floating point color to DWORD color value - * \param c A pointer to a color - * \return DWORD color in 0xAABBGGRR - * \note Alpha will range from 1 to 255. Zero is never returned because of backwards compatibility issues 0-alpha is mapped to 255 - */ - inline DWORD Color(const COLOUR4* c) - { - return FVECTOR4(*c).dword_abgr(); - } - - /** - * \brief Convert a floating point color to DWORD color value - * \param c A pointer to a color - * \return DWORD color in 0xAABBGGRR - * \note Alpha will range from 1 to 255. Zero is never returned because of backwards compatibility issues 0-alpha is mapped to 255 - */ - inline DWORD Color(const oapi::FVECTOR4* c) - { - return c->dword_abgr(); - } - - /** - * \brief Convert a DWORD color to floating point COLOUR4 value - * \param dwABGR A color in 0xAABBGGRR - * \return COLOUR4 - * \note Alpha will range from 1 to 255. Zero is never used because of backwards compatibility issues 0-alpha is mapped to 255 - */ - inline COLOUR4 Colour4(DWORD dwABGR) - { - return FVECTOR4(dwABGR).Colour4(); - } - /** * \brief Alters objects position. Matrix must be initially valid. * \param mat [in/out] Pointer to a matrix to change @@ -793,7 +760,7 @@ INTERFACE_BUILDER class gcCore inline void WorldMatrix(FMATRIX4* mat, const FVECTOR3& pos, const FVECTOR3& x, const FVECTOR3& z, float scale = 1.0f) { - FVECTOR3 y = cross(x, z); + FVECTOR3 y = crossp(x, z); mat->m11 = (x.x * scale); mat->m12 = (x.y * scale); mat->m13 = (x.z * scale); mat->m14 = 0.0f; mat->m21 = (y.x * scale); mat->m22 = (y.y * scale); mat->m23 = (y.z * scale); mat->m24 = 0.0f; mat->m31 = (z.x * scale); mat->m32 = (z.y * scale); mat->m33 = (z.z * scale); mat->m34 = 0.0f; diff --git a/OVP/D3D9Client/resource.h b/OVP/D3D9Client/resource.h index 83d89d8c0..4c6be38a3 100644 --- a/OVP/D3D9Client/resource.h +++ b/OVP/D3D9Client/resource.h @@ -136,7 +136,6 @@ #define IDC_FLATS 2166 #define IDC_DBG_DEFSHADER 2167 #define IDC_DBG_DATAVIEW2 2168 -#define IDC_DBG_MESHGRP 2169 #define IDC_GFX_INTENSITY 2170 #define IDC_GFX_DISTANCE 2171 #define IDC_GFX_GAMMA 2173 @@ -177,6 +176,36 @@ #define IDC_EARTHVISCFG 3033 #define IDC_ESCACHE 3034 #define IDC_EAQUALITY 3035 +#define IDC_ATM_TRLIGHTSHAD 3036 +#define IDC_ATD_TRLIGHTSHAD 3037 +#define IDC_DBG_BKLID 3038 +#define IDC_DBG_BKLADJ 3039 +#define IDC_CASCOUNT 3040 +#define IDC_DBG_NOSHADOW 3041 +#define IDC_DBG_NORENDER 3042 +#define IDC_DBG_NOLIGHT 3043 +#define IDC_DBG_ADDITIVE 3044 +#define IDC_DBG_NOCOLOR 3045 +#define IDC_DBG_OIT 3046 +#define IDC_DBG_MATIDX 3047 +#define IDC_DBG_TEXIDX 3048 +#define IDC_DBG_MESHSAVE 3049 +#define IDC_DBG_NEXT 3050 +#define IDC_DBG_GRPLABEL 3051 +#define IDC_DBG_EXTVC 3052 +#define IDC_DBG_CLIPDIST 3053 +#define IDC_DBG_VISMODE 3054 +#define IDC_DBG_PICKCURRENT 3055 +#define IDC_DBG_DYNAMIC 3056 +#define IDC_DBG_ISVCMESH 3057 +#define IDC_DBG_VCSHADOW 3058 +#define IDC_DBG_AMBDIR 3059 +#define IDC_DBG_NOSUNAMB 3060 +#define IDC_DBG_NOPLNAMB 3061 +#define IDC_DBG_DATASRC 3062 +#define IDC_DBG_NODYNSUN 3063 +#define IDC_DBG_BKLGROUP 3064 +#define IDC_DBG_VCZONES 3065 #define IDC_TILECOUNT 3036 #define IDC_ATM_S1 4000 #define IDC_ATM_S2 4001 diff --git a/OVP/D3D9Client/samples/DrawOrbits/Draw.cpp b/OVP/D3D9Client/samples/DrawOrbits/Draw.cpp index 1e981566d..0f1cd2f24 100644 --- a/OVP/D3D9Client/samples/DrawOrbits/Draw.cpp +++ b/OVP/D3D9Client/samples/DrawOrbits/Draw.cpp @@ -11,6 +11,7 @@ #include "VesselAPI.h" #include "ModuleAPI.h" #include "DrawAPI.h" +#include "MathAPI.h" #include #include "gcCoreAPI.h" #include "Orbit.h" diff --git a/OVP/D3D9Client/shaders/BakedVC.fx b/OVP/D3D9Client/shaders/BakedVC.fx new file mode 100644 index 000000000..6c9b52ab3 --- /dev/null +++ b/OVP/D3D9Client/shaders/BakedVC.fx @@ -0,0 +1,238 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// licensed under LGPL v2 +// ============================================================== + + + + + +// ============================================================================ +// A Shader for a typical "Metalness" PBR workflow. +// ============================================================================ + +float4 BakedVC_PS(float4 sc : VPOS, PBRData frg) : COLOR +{ + float3 nrmT; + float3 nrmW; + float3 cEmis; + float4 cDiff; + float3 cBL; + float3 cBA; + float3 cBAO; + float fSmth, fMetal; + + // ====================================================================== + // Start fetching texture data + // ====================================================================== + + // Fetch a normal map + // + if (gCfg.Norm) nrmT = tex2D(Nrm0S, frg.tex0.xy).rgb; + + if (gTextured) cDiff = tex2D(WrapS, frg.tex0.xy); + else cDiff = 1; + + // Fetch Smoothness map (i.e. *_rghn.dds) + // + if (gCfg.Rghn) fSmth = tex2D(RghnS, frg.tex0.xy).g; + else fSmth = 1.0f; + + // Fetch Roughness map + // + if (gCfg.Metl) fMetal = tex2D(MetlS, frg.tex0.xy).g; + else fMetal = gMtrl.metalness; + + // Sample emission map. (Note: Emissive materials and textures need to go different stages, material is added to light) + // + if (gCfg.Emis) cEmis = tex2D(EmisS, frg.tex0.xy).rgb; + else cEmis = 0; + + // Load baked light emitters + // + if (gCfg.Baked) cBL = tex2D(BakedLightS, frg.tex0.xy).rgb; + else cBL = 0.0f; + + // Load baked ambient lights + // + if (gCfg.BakedAmb) cBA = tex2D(BakedSunS, frg.tex0.xy).rgb; + else cBA = 1.0f; + + // Load baked ambient occlusion (note: gCfg.BakedAO ahouldn't be enabled at the same time with gCfg.BakedAmb) + // + if (gCfg.BakedAO) cBAO = tex2D(BakedAOS, frg.tex0.xy).rgb; + else cBAO = 1.0f; + + + + // ---------------------------------------------------------------------- + // Now do other calculations while textures are being fetched + // ---------------------------------------------------------------------- + + float3 camW = normalize(frg.camW); + float3 cSun = gSun.Color; + + + // ====================================================================== + // Construct a proper world space normal + // ====================================================================== + + float3 tanW = frg.tanW.xyz; + float3 bitW = cross(tanW, frg.nrmW) * frg.tanW.w; + + if (gCfg.Norm) { + nrmT.rg = nrmT.rg * 2.0f - 1.0f; + nrmW = frg.nrmW*nrmT.z + tanW*nrmT.x + bitW*nrmT.y; + } + else nrmW = frg.nrmW; + + nrmW = normalize(nrmW); + + + + // ====================================================================== + // Typical compatibility requirements + // ====================================================================== + + cDiff.a = saturate(cDiff.a * gMtrlAlpha); + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); + + + // ====================================================================== + // Some Precomputations + // ====================================================================== + + float3 sunW = -gSun.Dir; + float3 cEnv = 0; + + float3 rflW = reflect(-camW, nrmW); + float3 hlvW = normalize(camW + sunW); + + // Dot Products + float uLN = dot(sunW, nrmW); + float uLC = dot(sunW, camW); + float dLN = saturate(uLN); + float dLH = saturate(dot(sunW, hlvW)); + float dCN = saturate(dot(camW, nrmW)); + float dHN = saturate(dot(hlvW, nrmW)); + + // Apply a proper curve to a texture data, modulate with material value and clamp + fSmth = pow(abs(fSmth), gMtrl.roughness.y) * gMtrl.roughness.x; + + // Apply fresnel and Fresnell cut-off to fSmth + fSmth = fSmth + ((1.0f - fSmth) * pow(abs(1.0f - dCN), 4.0f)) * pow(abs(fSmth), 0.5f); + + float fRgh = saturate(1.0f - fSmth); + float fRgh3 = fRgh*fRgh*fRgh; + + + // ====================================================================== + // Sample reflections and irradiance + // ====================================================================== + +#if defined(_ENVMAP) + if (gEnvMapEnable) + { + SampleEnvMap(cEnv, dCN, fRgh, fMetal, rflW, nrmW); + + // Use dynamic ambient light if baked doesn't exists + if (!gCfg.BakedAmb) { + float3 cP = Paraboloidal_LVLH(IrradS, nrmW).rgb; + cBA *= pow(abs(cP), gVCIrrad.w) * cBAO * gVCIrrad.rgb; + } + } +#else +#endif + + cBA *= (1.0f - fMetal); // No ambient for metals + + + // ====================================================================== + // Add vessel self-shadows + // ====================================================================== + +#if SHDMAP > 0 + if (gCockpit) { + cSun *= smoothstep(0, 0.72, ComputeShadowVC(frg.shdH, dLN, sc)); + } + else { + cSun *= smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); + } +#endif + + + + // ====================================================================== + // Main shader core MetalnessPS + // ====================================================================== + + float fD = GGX_NDF(dHN, lerp(0.01f, 1.0f, fRgh3)); + float fG = SchlickBeckmanGSF(dLN, dCN, fRgh); + float fR = DiffuseRetroReflectance(dLN, dCN, dLH, fRgh, fMetal); + + + // Base material color for reflections. Use cDiff for metals and very rough plastics, white for the rest. + // cDiff for rough plastics is to avoid washed-out(white) look of black and rough parts. + float3 cSpec = lerp(cDiff.rgb, float3(1, 1, 1), (1.0f - fMetal) * (1.0f - fRgh3)); + + // Fresnel power 2.5 for glossy, 5.0 for rough + float fFrs = pow(1.0f - dCN, fRgh*2.5 + 2.5f); + + // Fresnel cut-off below X of fSmth + fFrs *= saturate(0.3f - fRgh*fRgh) * 3.3f; + + // Assume that plastics absorve 50-90% of specular light + float fP = lerp(0.1f + (1.0f - fRgh)*0.4f, 1.0f, fMetal); + + // Fresnel color shift + float3 cF = cSpec + (1.0f - cSpec) * fFrs; + + // Specular Color + float3 cS = (fD * cF * fG * fP) * 0.25f; // (4.0f*dLN*dCN) removed to avoid division by zero, compensation in GSF + + + // How plastics reflect the environment + float R = 0.1f * fSmth; + float frP = R + (1.0f - R) * fFrs; + + float3 cE = (cEnv * cF * lerp(frP, 1.0f, fMetal)); + + // Attennuate diffuse color for Metals & Fresnel + float fA = (1.0f - fFrs) * (1.0f - fMetal); + + // Add a faint diffuse hue for rough metals. Rough metal doesn't look good if it's totally black + fA += fRgh * fMetal * 0.05f; + + // Light + float3 zL = Sq(cSun * fR * dLN) + Sq(cBL) + Sq(cBA) + Sq(gVCAmbient); + + // gVCAmbient is an application and debug controls controllable variable + float3 zD = cDiff.rgb * fA * LightFXSq(gMtrl.diffuse.rgb * zL + Sq(gMtrl.emissive.rgb)); + + // Combine specular terms + float3 zS = cS * (cSun * dLN); + + // Combine Diffuse, Specular and Environment + cDiff.rgb = zD + zS + cE; + + // Override material alpha to make reflections visible + cDiff.a = saturate(cDiff.a + cmax(zS + cE)); + + // Add emission texture to output, modulate with material + cDiff.rgb = max(cDiff.rgb, cEmis * gMtrl.emission2.rgb); + + +#if defined(_DEBUG) + cDiff = cDiff * (1.0f - gColor*0.5f) + gColor; +#endif + + + // Is post-processing shader enabled on not ? + // +#if defined(_LIGHTGLOW) + return cDiff; +#else + float3 h2 = cDiff.rgb*cDiff.rgb; + return float4(cDiff.rgb * pow(max(0, 1.0f + h2*h2), -0.25), cDiff.a); +#endif +} diff --git a/OVP/D3D9Client/shaders/Common.hlsl b/OVP/D3D9Client/shaders/Common.hlsl index 85a5a2132..ec2c81cbf 100644 --- a/OVP/D3D9Client/shaders/Common.hlsl +++ b/OVP/D3D9Client/shaders/Common.hlsl @@ -1,8 +1,7 @@ #define KERNEL_RADIUS 2.0f -#define SHADOW_THRESHOLD 0.1f // 0.3 to 7.0 - +sampler tShadowMap[3] : register(s13); // ============================================================================ // @@ -26,6 +25,14 @@ float4 Sq(float4 x) return x*x; } +bool PointInRect(float2 pt) +{ + if (pt.x < 0) return false; + if (pt.x > 1) return false; + if (pt.y < 0) return false; + if (pt.y > 1) return false; + return true; +} // ========================================================================================================== // Local light sources @@ -254,62 +261,30 @@ void LocalLightsEx(out float3 cDiffLocal, out float3 cSpecLocal, in float3 nrmW, - - // ========================================================================================================== // Object Self Shadows // ========================================================================================================== -/* -float ProjectShadows(float2 sp) -{ - if (!gShadowsEnabled) return 0.0f; - - if (sp.x < 0 || sp.y < 0) return 0.0f; - if (sp.x > 1 || sp.y > 1) return 0.0f; - - float2 dx = float2(gSHD[1], 0) * 1.5f; - float2 dy = float2(0, gSHD[1]) * 1.5f; - float va = 0; - float pd = 1e-4; - - sp -= dy; - if ((tex2D(ShadowS, sp - dx).r) > pd) va++; - if ((tex2D(ShadowS, sp).r) > pd) va++; - if ((tex2D(ShadowS, sp + dx).r) > pd) va++; - sp += dy; - if ((tex2D(ShadowS, sp - dx).r) > pd) va++; - if ((tex2D(ShadowS, sp).r) > pd) va++; - if ((tex2D(ShadowS, sp + dx).r) > pd) va++; - sp += dy; - if ((tex2D(ShadowS, sp - dx).r) > pd) va++; - if ((tex2D(ShadowS, sp).r) > pd) va++; - if ((tex2D(ShadowS, sp + dx).r) > pd) va++; - - return va / 9.0f; -}*/ - -// --------------------------------------------------------------------------------------------------- -// -float SampleShadows(float2 sp, float pd) +float SampleShadows(float2 sp, float pd, int sid) { - float2 dx = float2(gSHD[1], 0) * 1.5f; - float2 dy = float2(0, gSHD[1]) * 1.5f; + float2 dxa = float2(gSHD[1], 0) * 0.75f; + float2 dya = float2(0, gSHD[1]) * 0.75f; + float2 dxb = dxa * 0.707f; + float2 dyb = dya * 0.707f; float va = 0; - sp -= dy; - if ((tex2D(ShadowS, sp - dx).r) > pd) va++; - if ((tex2D(ShadowS, sp).r) > pd) va++; - if ((tex2D(ShadowS, sp + dx).r) > pd) va++; - sp += dy; - if ((tex2D(ShadowS, sp - dx).r) > pd) va++; - if ((tex2D(ShadowS, sp).r) > pd) va++; - if ((tex2D(ShadowS, sp + dx).r) > pd) va++; - sp += dy; - if ((tex2D(ShadowS, sp - dx).r) > pd) va++; - if ((tex2D(ShadowS, sp).r) > pd) va++; - if ((tex2D(ShadowS, sp + dx).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp - dxb - dyb).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp - dya).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp + dxb - dyb).r) > pd) va++; + + if ((tex2D(tShadowMap[sid], sp - dxa).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp + dxa).r) > pd) va++; + + if ((tex2D(tShadowMap[sid], sp - dxb + dyb).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp + dya).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp + dxb + dyb).r) > pd) va++; return va * 0.1111111f; } @@ -317,14 +292,14 @@ float SampleShadows(float2 sp, float pd) // --------------------------------------------------------------------------------------------------- // -float SampleShadows2(float2 sp, float pd) +float SampleShadows2(float2 sp, float pd, int sid) { float val = 0; float m = KERNEL_RADIUS * gSHD[1]; [unroll] for (int i = 0; i < KERNEL_SIZE; i++) { - if ((tex2D(ShadowS, sp + kernel[i].xy * m).r) > pd) val += kernel[i].z; + if ((tex2D(tShadowMap[sid], sp + kernel[i].xy * m).r) > pd) val += kernel[i].z; } return saturate(val * KERNEL_WEIGHT); @@ -333,7 +308,7 @@ float SampleShadows2(float2 sp, float pd) // --------------------------------------------------------------------------------------------------- // -float SampleShadows3(float2 sp, float pd, float4 frame) +float SampleShadows3(float2 sp, float pd, float4 frame, int sid) { float val = 0; @@ -341,7 +316,7 @@ float SampleShadows3(float2 sp, float pd, float4 frame) [unroll] for (int i = 0; i < KERNEL_SIZE; i++) { float2 ofs = frame.xy*kernel[i].x + frame.zw*kernel[i].y; - if (tex2D(ShadowS, sp + ofs).r > pd) val += kernel[i].z; + if (tex2D(tShadowMap[sid], sp + ofs).r > pd) val += kernel[i].z; } return saturate(val * KERNEL_WEIGHT); @@ -350,22 +325,23 @@ float SampleShadows3(float2 sp, float pd, float4 frame) // --------------------------------------------------------------------------------------------------- // -float SampleShadowsEx(float2 sp, float pd, float4 sc) +float SampleShadowsEx(float2 sp, float pd, float4 sc, int sid) { #if SHDMAP == 1 - return SampleShadows(sp, pd); + return SampleShadows(sp, pd, sid); #elif SHDMAP == 2 || SHDMAP == 4 - return SampleShadows2(sp, pd); + return SampleShadows2(sp, pd, sid); #else float si, co; sc += (gSHD[2] * 2.0f); sincos(sc.y + sc.x*149.0f, si, co); - return SampleShadows3(sp, pd, float4(si, co, co, -si)); + return SampleShadows3(sp, pd, float4(si, co, co, -si), sid); #endif } + // --------------------------------------------------------------------------------------------------- // float ComputeShadow(float4 shdH, float dLN, float4 sc) @@ -385,15 +361,93 @@ float ComputeShadow(float4 shdH, float dLN, float4 sc) float kr = gSHD[0] * KERNEL_RADIUS; float dx = rsqrt(1.0 - dLN*dLN); - float ofs = kr / (dLN * dx); - float omx = min(0.05 + ofs, 0.5); - + float ofs = 0.33f * kr / (dLN * dx); + float omx = min(gSHD[0] * 2.0f + max(0, ofs), 0.25); + float pd = shdH.z + omx * gSHD[3]; if (pd < 0) pd = 0; if (pd > 1) pd = 1; - fShadow = SampleShadowsEx(sp, pd, sc); + fShadow = SampleShadowsEx(sp, pd, sc, 0); return 1 - fShadow; -} \ No newline at end of file +} + + +// --------------------------------------------------------------------------------------------------- +// +float ComputeShadowVC(float4 shdH, float dLN, float4 sc) +{ + if (!gShadowsEnabled) return 1.0f; + + shdH.xyz /= shdH.w; + shdH.z = 1 - shdH.z; + float2 sp = shdH.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + float2 c[3]; + int sid = 0; + +#if CASCOUNT >= 1 + c[0] = (sp + gSHDSubRect[0].xy) * gSHDSubRect[0].zw; + if (!PointInRect(c[0])) return 1.0f; // Sample outside of cascade '0' +#endif +#if CASCOUNT >= 2 + c[1] = (sp + gSHDSubRect[1].xy) * gSHDSubRect[1].zw; + if (PointInRect(c[1])) sid = 1; +#endif +#if CASCOUNT >= 3 + c[2] = (sp + gSHDSubRect[2].xy) * gSHDSubRect[2].zw; + if (PointInRect(c[2])) sid = 2; +#endif + + float kr = gSHDPx[sid]; + float omx = max(0.0015f, kr * sqrt(rcp(dLN * dLN) - 1.0f)); + float pd = shdH.z + min(omx, 0.02f) * gSHD[3]; + + if (pd < 0) pd = 0; + if (pd > 1) pd = 1; + + float fShadow[3]; + +#if CASCOUNT >= 1 + fShadow[0] = SampleShadows(c[0], pd, 0); +#endif +#if CASCOUNT >= 2 + fShadow[1] = SampleShadows(c[1], pd, 1); +#endif +#if CASCOUNT >= 3 + fShadow[2] = SampleShadows(c[2], pd, 2); +#endif + + return 1 - fShadow[sid]; +} + + +// --------------------------------------------------------------------------------------------------- +// +float3 VisualizeCascades(float4 shdH) +{ + if (!gShadowsEnabled) return float3(1, 1, 1); + + shdH.xyz /= shdH.w; + shdH.z = 1 - shdH.z; + float2 sp = shdH.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + + sp += gSHD[1] * 0.5f; // Shift 0.5 pixels aside + + float2 c0 = (sp + gSHDSubRect[0].xy) * gSHDSubRect[0].zw; + float2 c1 = (sp + gSHDSubRect[1].xy) * gSHDSubRect[1].zw; + float2 c2 = (sp + gSHDSubRect[2].xy) * gSHDSubRect[2].zw; + +#if CASCOUNT >= 3 + if (PointInRect(c2)) return float3(0, 0, 1); +#endif +#if CASCOUNT >= 2 + if (PointInRect(c1)) return float3(0, 1, 0); +#endif +#if CASCOUNT >= 1 + if (PointInRect(c0)) return float3(1, 0, 0); +#endif + + return float3(1, 1, 1); +} diff --git a/OVP/D3D9Client/shaders/Custom.hlsl b/OVP/D3D9Client/shaders/Custom.hlsl new file mode 100644 index 000000000..fd15cb10d --- /dev/null +++ b/OVP/D3D9Client/shaders/Custom.hlsl @@ -0,0 +1,105 @@ +// =================================================== +// Copyright (C) 2022 Jarmo Nikkanen +// licensed under MIT +// =================================================== + + +float ilerp(float a, float b, float x) +{ + return saturate((x - a) / (b - a)); +} + + +struct MESH_VERTEX { + float3 posL : POSITION0; + float3 nrmL : NORMAL0; + float3 tanL : TANGENT0; + float3 tex0 : TEXCOORD0; +}; + +struct MData { + float4 posH : POSITION0; + float3 camW : TEXCOORD0; +}; + + + +// ================================================================================================== +// Stage-Set for renderin auxiliary camera views +// ================================================================================================== + +sampler tTex; + +uniform extern struct { + float4x4 mVP; + float4x4 mW; +} cbPS; + +MData StageVS(MESH_VERTEX vrt) +{ + MData outVS = (MData)0; + float3 posW = mul(float4(vrt.posL, 1.0f), cbPS.mW).xyz; + outVS.posH = mul(float4(posW, 1.0f), cbPS.mVP); + outVS.camW = -posW; + return outVS; +} + +float4 StagePS(MData frg) : COLOR +{ + float3 cA = texCUBElod(tTex, float4(normalize(-frg.camW), 0.0)).rgb; + return float4(cA, 1); +} + + + +// ================================================================================================== +// Quadrilateral VC Click Zones +// ================================================================================================== + +struct { + float3 pt[4]; + float4 color; + float4x4 mW; + float4x4 mVP; + bool bSphere; +} cb; + +MData QuadVS(MESH_VERTEX vrt) +{ + MData outVS = (MData)0; + float3 pt; + float3 posL = vrt.posL; + + if (cb.bSphere) { + pt = cb.pt[0] + posL * cb.pt[1].x; + } + else { + posL = (posL + 1.0f) * 0.5f; + float3 h0 = normalize(cross(cb.pt[1] - cb.pt[0], cb.pt[2] - cb.pt[0])) * posL.z * 0.02f; + float3 p0 = lerp(cb.pt[0], cb.pt[1], posL.x); + float3 p1 = lerp(cb.pt[2], cb.pt[3], posL.x); + pt = lerp(p0, p1, posL.y) + h0; + } + + float3 nrmW = mul(float4(vrt.nrmL, 0.0f), cb.mW).xyz; + float3 posW = mul(float4(pt, 1.0f), cb.mW).xyz; + outVS.posH = mul(float4(posW, 1.0f), cb.mVP); + + if (cb.bSphere) outVS.camW = 1.0f - abs(dot(normalize(posW), nrmW)) * 0.9f; + else outVS.camW = posL; + + return outVS; +} + +float4 QuadPS(MData frg) : COLOR +{ + if (cb.bSphere) { + float b = frg.camW.z; + return float4(cb.color.rgb, cb.color.a * b); + } + else { + float a = 1 - frg.camW.z; + float b = a > 0.9 ? 0.2 : a; + return float4(cb.color.rgb, cb.color.a * b); + } +} diff --git a/OVP/D3D9Client/shaders/D3D9Client.fx b/OVP/D3D9Client/shaders/D3D9Client.fx index 471df002a..73a6cce38 100644 --- a/OVP/D3D9Client/shaders/D3D9Client.fx +++ b/OVP/D3D9Client/shaders/D3D9Client.fx @@ -63,27 +63,15 @@ struct Flow bool Emis; // Enable Emission Maps bool Spec; // Enable Specular Maps bool Refl; // Enable Reflection Maps - bool Transl; // Enble translucent effect + bool Transl; // Enable translucent effect bool Transm; // Enable transmissive effect bool Rghn; // Enable roughness map bool Norm; // Enable normal map bool Metl; // Enable metalness map bool Heat; // Enable heat map -}; - - -// Must match with counterpart D3D9Tune in D3D9Util.h - -struct Tune -{ - float4 Albe; // Tune Diffese Maps - float4 Emis; // Tune Emission Maps - float4 Spec; // Tune Specular Maps - float4 Refl; // Tune Reflection Maps - float4 Transl; // Tune translucent effect - float4 Transm; // Tune transmissive effect - float4 Norm; // Tune normal map - float4 Rghn; // Tune roughness map + bool Baked; // Enable pre-baked local light map + bool BakedAO; // Enable pre-baked AO map + bool BakedAmb; // Enable pre-baked Ambient light map }; @@ -111,17 +99,20 @@ uniform extern float4 gAtmColor; // Earth glow color uniform extern float4 gTexOff; // Texture offsets used by surface manager uniform extern float4 gRadius; // PlanetRad, AtmOuterLimit, CameraRad, CameraAlt uniform extern float4 gSHD; // ShadowMap data +uniform extern float4 gSHDPx; // Shadow resolution [Pixels / meter] for each cascade +uniform extern float4 gSHDSubRect[3]; // Shadow cascade sub-rects +uniform extern float4 gVCIrrad; // Virtual Cockpit ambient lighting control uniform extern float3 gCameraPos; // Planet relative camera position, Unit vector uniform extern float3 gNorth; uniform extern float3 gEast; +uniform extern float3 gVCAmbient; // Ambient level inside virtual cockpit +uniform extern float3 gNoColor; // No Color option. uniform extern Sun gSun; // Sun light direction uniform extern Mat gMat; // Material input structure TODO: Remove all reference to this. Use gMtrl uniform extern Mat gWater; // Water material input structure uniform extern Mtrl gMtrl; // Material input structure -uniform extern Tune gTune; // Texture tuning parameters uniform extern Light gLights[MAX_LIGHTS]; uniform extern bool gLightsEnabled; -uniform extern bool gTuneEnabled; uniform extern bool gModAlpha; // Configuration input uniform extern bool gFullyLit; // Always fully lit bypass lighting calculations uniform extern bool gTextured; // Enable Diffuse Texturing @@ -132,9 +123,9 @@ uniform extern bool gNight; // Nighttime/Daytime uniform extern bool gShadowsEnabled; // Enable shadow maps uniform extern bool gEnvMapEnable; // Enable Environment mapping uniform extern bool gInSpace; // True if a mesh is located in space -uniform extern bool gNoColor; // No color flag uniform extern bool gBaseBuilding; uniform extern bool gOITEnable; +uniform extern bool gCockpit; uniform extern int gSpecMode; uniform extern int gHazeMode; uniform extern float gProxySize; // Cosine of the angular size of the Proxy Gbody. (one half) @@ -164,8 +155,10 @@ uniform extern texture gMetlMap; // Metalness Map uniform extern texture gHeatMap; // Heat Map uniform extern texture gTranslMap; // Translucence Map uniform extern texture gTransmMap; // Transmittance Map -uniform extern texture gShadowMap; // Shadow Map uniform extern texture gIrradianceMap; // Irradiance Map +uniform extern texture gAmbientMap; // Baked Ambient occlusion map +uniform extern texture gCombinedMap; // Combined baked light map +uniform extern texture gCombinedSunMap; // Combined baked light map // Legacy Atmosphere -------------------------------------------------------- @@ -262,16 +255,6 @@ sampler IrradS = sampler_state // Irradiance map sampler AddressV = CLAMP; }; -sampler ShadowS = sampler_state // Shadow map sampler -{ - Texture = ; - MinFilter = POINT; - MagFilter = POINT; - MipFilter = POINT; - AddressU = CLAMP; - AddressV = CLAMP; -}; - sampler WrapS = sampler_state // Primary Mesh texture sampler { Texture = ; @@ -320,6 +303,43 @@ sampler EmisS = sampler_state // Primary Mesh texture sampler AddressV = WRAP; }; +sampler BakedLightS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler BakedSunS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler BakedAOS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + + sampler ReflS = sampler_state // Primary Mesh texture sampler { Texture = ; diff --git a/OVP/D3D9Client/shaders/EnvMapBlur.hlsl b/OVP/D3D9Client/shaders/EnvMapBlur.hlsl index 899bccedd..20f791458 100644 --- a/OVP/D3D9Client/shaders/EnvMapBlur.hlsl +++ b/OVP/D3D9Client/shaders/EnvMapBlur.hlsl @@ -38,4 +38,10 @@ float4 PSBlur(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR } color /= (a*2.0f); return float4(color, 1); -} \ No newline at end of file +} + + +float4 PS2DBlur(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + return float4(0.5, 0.5, 0.5, 1.0); +} diff --git a/OVP/D3D9Client/shaders/IrradianceInteg.hlsl b/OVP/D3D9Client/shaders/IrradianceInteg.hlsl index 83eb47c14..209dbd861 100644 --- a/OVP/D3D9Client/shaders/IrradianceInteg.hlsl +++ b/OVP/D3D9Client/shaders/IrradianceInteg.hlsl @@ -1,5 +1,5 @@ -#define IKernelSize 150 +#define IKernelSize 120 uniform extern float4 Kernel[IKernelSize]; uniform extern float3 vNr; // North @@ -11,6 +11,7 @@ uniform extern bool bUp; sampler tCube; sampler tSrc; +sampler tRandom; float3 Paraboloidal_to_World(float3 i) @@ -22,48 +23,54 @@ float3 Paraboloidal_to_World(float3 i) } -float4 PSPreInteg(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR -{ - float3 color = 0; - float2 p = float2(x, y); - for (int j = 0; j < 8; j++) { - for (int i = 0; i < 8; i++) { - color += tex2D(tSrc, p + (float2(i, j) - 4) * fD).rgb; - } - } - return float4(color * (1.0f/64.0f), 1); -} - - float4 PSInteg(float x : TEXCOORD0, float y : TEXCOORD1, float2 sc : VPOS) : COLOR { + float a = tex2D(tRandom, float2(x*3,y*1.5)).r * 6.283185307; float2 qw = float2(x, y) * 2.0f - 1.0f; float3 vz = Paraboloidal_to_World(float3(qw.xy, (bUp ? 1 : -1))); - - float3 q = lerp(vUp, vCp, frac(x * 21)); // Randomize rotation - float3 w = lerp(q, vNr, frac(y * 17)); // Randomize rotation - float3 vx = normalize(cross(vz, w)); - float3 vy = normalize(cross(vz, vx)); + float3 qx = normalize(cross(vz, vNr)); + float3 qy = normalize(cross(vz, qx)); + float3 vx = qx * sin(a) + qy * cos(a); + float3 vy = qx * cos(a) - qy * sin(a); float3 sum = 0; - for (int i = 0; i < IKernelSize; i++) { + [unroll] for (int i = 0; i < IKernelSize; i++) { float3 d = (vx*Kernel[i].x) + (vy*Kernel[i].y) + (vz*Kernel[i].z); - sum += texCUBE(tCube, d).rgb * Kernel[i].w; + float3 k = texCUBElod(tCube, float4(d, 0)).rgb; + sum += k; // *Kernel[i].w; } - - return float4(sqrt(sum * fIntensity * (0.7f / IKernelSize)), 1.0f); -} + sum = sum * (1.0f / IKernelSize); + sum = sum * fIntensity; + + return float4(sum, 1.0f); +} +// Exterior Irradiance blur +// float4 PSPostBlur(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR { float3 color = 0; float2 p = float2(x, y); - color += tex2D(tSrc, p).rgb; - color += tex2D(tSrc, p + float2(0, 1)*fD).rgb; - color += tex2D(tSrc, p + float2(0, -1)*fD).rgb; - color += tex2D(tSrc, p + float2(-1, 0)*fD).rgb; - color += tex2D(tSrc, p + float2(-1, 0)*fD).rgb; - return float4(color * 0.2, 1); -} \ No newline at end of file + [unroll] for (int k = -2; k < 3; k++) { + [unroll] for (int i = -2; i < 3; i++) { + color += tex2D(tSrc, p + float2(i+0.5, k+0.5) * fD).rgb; + } + } + return float4(color / 25, 1); +} + +// Interior Irradiance blur +// +float4 PSPostBlurIntr(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float3 color = 0; + float2 p = float2(x, y); + [unroll] for (int k = -5; k < 6; k++) { + [unroll] for (int i = -5; i < 6; i++) { + color += tex2D(tSrc, p + float2(i + 0.5, k + 0.5) * fD).rgb; + } + } + return float4(color / 121, 1); +} diff --git a/OVP/D3D9Client/shaders/Mesh.fx b/OVP/D3D9Client/shaders/Mesh.fx index 970908d24..ee9477438 100644 --- a/OVP/D3D9Client/shaders/Mesh.fx +++ b/OVP/D3D9Client/shaders/Mesh.fx @@ -61,10 +61,9 @@ float4 TinyMeshTechPS(MeshVS frg) : COLOR float4 cSpec = gMtrl.specular; float4 cTex = 1; - if (gTextured) { - if (gNoColor) cTex.a = tex2D(WrapS, frg.tex0.xy).a; - else cTex = tex2D(WrapS, frg.tex0.xy); - } + if (gTextured) cTex = tex2D(WrapS, frg.tex0.xy); + + cTex.rgb = saturate(cTex.rgb + gNoColor.rgb); if (gFullyLit) return float4(cTex.rgb*saturate(gMtrl.diffuse.rgb + gMtrl.emissive.rgb), cTex.a); diff --git a/OVP/D3D9Client/shaders/Metalness.fx b/OVP/D3D9Client/shaders/Metalness.fx index 618ba0b61..82953c790 100644 --- a/OVP/D3D9Client/shaders/Metalness.fx +++ b/OVP/D3D9Client/shaders/Metalness.fx @@ -4,6 +4,7 @@ // ============================================================== #define eps 0.001f +//#define _VISCASCADES // ============================================================================ // Vertex shader for physics based rendering @@ -126,6 +127,7 @@ void Transmittance(in out float4 cDiff, float uLN, float uLC, float2 uv, float3 cDiff.rgb += cTransm.rgb * (sunSpotFromBehind * cSun); } +#include "BakedVC.fx" // ============================================================================ // A Shader for a typical "Metalness" PBR workflow. @@ -136,7 +138,7 @@ float4 MetalnessPS(float4 sc : VPOS, PBRData frg) : COLOR float3 nrmT; float3 nrmW; float3 cEmis; - float4 cSpecularMap; // Added + float4 cSpecularMap; float4 cDiff; float fHeat; float fSmth, fMetal; @@ -150,6 +152,11 @@ float4 MetalnessPS(float4 sc : VPOS, PBRData frg) : COLOR if (gTextured) cDiff = tex2D(WrapS, frg.tex0.xy); else cDiff = 1; +#if defined(_VISCASCADES) + cDiff.rgb *= VisualizeCascades(frg.shdH); + return cDiff; +#endif + if (gOITEnable) if (cDiff.a < 0.5f) clip(-1); // Fetch a normal map @@ -209,8 +216,8 @@ float4 MetalnessPS(float4 sc : VPOS, PBRData frg) : COLOR // Typical compatibility requirements // ====================================================================== - if (gNoColor) cDiff.rgb = 1; - cDiff = saturate(cDiff * float4(gMtrl.diffuse.rgb, gMtrlAlpha)); + cDiff.a = saturate(cDiff.a * gMtrlAlpha); + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); // ====================================================================== @@ -284,7 +291,12 @@ float4 MetalnessPS(float4 sc : VPOS, PBRData frg) : COLOR // Add vessel self-shadows // ====================================================================== #if SHDMAP > 0 - cSun *= smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); + if (gCockpit) { + cSun *= smoothstep(0, 0.72, ComputeShadowVC(frg.shdH, dLN, sc)); + } + else { + cSun *= smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); + } #endif @@ -345,7 +357,10 @@ float4 MetalnessPS(float4 sc : VPOS, PBRData frg) : COLOR // Add a faint diffuse hue for rough metals. Rough metal doesn't look good if it's totally black fA += fRgh * fMetal * 0.05f; - float3 zD = cDiff.rgb * fA * LightFXSq(Sq(cSun * fR * dLN) + cDiffLocal + Sq(cAmbient) + Sq(gMtrl.emissive.rgb)); + // Light terms + float3 zL = Sq(cSun * fR * dLN) + cDiffLocal + Sq(cAmbient); + + float3 zD = cDiff.rgb * fA * LightFXSq(gMtrl.diffuse.rgb * zL + Sq(gMtrl.emissive.rgb)); // Combine specular terms // float3 zS = cS * (cSun * dLN) + cSpec * LightFX(cSpecLocal) * 0.5f; diff --git a/OVP/D3D9Client/shaders/PBR.fx b/OVP/D3D9Client/shaders/PBR.fx index e09b23c87..febfc9f33 100644 --- a/OVP/D3D9Client/shaders/PBR.fx +++ b/OVP/D3D9Client/shaders/PBR.fx @@ -111,29 +111,6 @@ float4 PBR_PS(float4 sc : VPOS, PBRData frg) : COLOR float3 cSun = saturate(gSun.Color); - // ---------------------------------------------------------------------- - // Texture tuning controls for add-on developpers - // ---------------------------------------------------------------------- - -#if defined(_DEBUG) - if (gTuneEnabled) { - - nrmT *= gTune.Norm.rgb; - - cDiff.rgb = pow(abs(cDiff.rgb), gTune.Albe.a) * gTune.Albe.rgb; - cRefl.rgb = pow(abs(cRefl.rgb), gTune.Refl.a) * gTune.Refl.rgb; - cEmis.rgb = pow(abs(cEmis.rgb), gTune.Emis.a) * gTune.Emis.rgb; - fRghn = pow(abs(fRghn), gTune.Rghn.a) * gTune.Rghn.g; - cSpec.rgba = cSpec.rgba * gTune.Spec.rgba; - - cDiff = saturate(cDiff); - cRefl = saturate(cRefl); - fRghn = saturate(fRghn); - cSpec = min(cSpec, sMask); - } -#endif - - // Use alpha zero to mask off specular reflections cSpec.rgb *= saturate(cSpec.a); @@ -195,7 +172,7 @@ float4 PBR_PS(float4 sc : VPOS, PBRData frg) : COLOR // ---------------------------------------------------------------------- #if SHDMAP > 0 - cSun *= smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); + if (!gCockpit) cSun *= smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); #endif @@ -262,7 +239,7 @@ float4 PBR_PS(float4 sc : VPOS, PBRData frg) : COLOR // Special alpha only texture in use, set the .rgb to 1.0f // Used for panel background lighting in Delta Glider - if (gNoColor) cDiff.rgb = 1; + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); // ------------------------------------------------------------------------ cDiff.rgb *= diffBaked; // Lit the texture @@ -431,7 +408,7 @@ float4 FAST_PS(float4 sc : VPOS, FASTData frg) : COLOR if (gOITEnable) if (cDiff.a < 0.5f) clip(-1); if (gFullyLit) { - if (gNoColor) cDiff.rgb = 1; + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); cDiff.rgb *= saturate(gMtrl.diffuse.rgb + gMtrl.emissive.rgb); } else { @@ -445,16 +422,15 @@ float4 FAST_PS(float4 sc : VPOS, FASTData frg) : COLOR float3 cSun = saturate(gSun.Color); float dLN = saturate(-dot(gSun.Dir, nrmW)); - //cSpec.rgb *= 0.33333f; - - if (gNoColor) cDiff.rgb = 1; + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); // ---------------------------------------------------------------------- // Add vessel self-shadows // ---------------------------------------------------------------------- #if SHDMAP > 0 - float fShadow = smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); + float fShadow = 1.0f; + if (!gCockpit) fShadow = smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); dLN *= fShadow; #endif diff --git a/OVP/D3D9Client/shaders/PreBakeLights.hlsl b/OVP/D3D9Client/shaders/PreBakeLights.hlsl new file mode 100644 index 000000000..3430c5f95 --- /dev/null +++ b/OVP/D3D9Client/shaders/PreBakeLights.hlsl @@ -0,0 +1,78 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// licensed under MIT +// ============================================================== + +uniform extern float3 vParTexPos[6]; // Pre-computed paraboloidal map coords for prime directions +uniform extern float3 fControl[16]; +uniform extern int iCount; +uniform extern bool bEnabled[6]; +uniform extern float fShine; +uniform extern bool bShine; + +sampler tMap[16] : register(s0); // Lightmaps +sampler tAO[6] : register(s0); // AO Textures for prime directions +sampler tIrrad : register(s6); // Paraboloidal irradiance map + +float cmax(float3 a) +{ + return max(a.x, max(a.y, a.z)); +} + +// ============================================================================ +// +float3 LightFX(float3 c) +{ + float q = cmax(c); + return c * 1.2f * rsqrt(2 + q * q); +} + + +// ============================================================================ +// +float4 ParaboloidalSampler(sampler s, float3 p) +{ + float4 A = tex2D(s, p.xy + float2(0.25f, 0.5f)); + float4 B = tex2D(s, p.xy + float2(0.75f, 0.5f)); + return lerp(A, B, smoothstep(-0.03, 0.03, p.z)); +} + + + +// ============================================================================ +// Combine multiple (regular) baked lightmaps into a single map +// +float4 PSMain(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float3 color = 0; + [unroll] for (int i = 0; i < iCount; i++) color += tex2D(tMap[i], float2(x, y)).rgb * fControl[i]; + return float4(LightFX(color), 1.0f); +} + + + +// ============================================================================ +// Combine multiple baked (ambient) lightmaps into a single map +// Include sun and planet shine coming through windows +// +float4 PSSunAO(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float3 color = 0; + + // For a given pixel in texture (x,y) + [unroll] for (int i = 0; i < 6; i++) { // Browse through direction + if (bEnabled[i]) { + // Get ambient distribution inside VC for a given light direction 'i' + float3 ad = tex2D(tAO[i], float2(x, y)).rgb; + + float3 cShine = 0.0f; + // Get planet shine color for a given direction 'i' + if (bShine) cShine = ParaboloidalSampler(tIrrad, vParTexPos[i]).rgb; + // Compute sunlight and planet shine factors + float3 shineAO = ad * cShine * fShine; + float3 sunAO = ad * fControl[i]; + color += (sunAO + shineAO); + } + } + return float4(LightFX(color), 1.0f); +} diff --git a/OVP/D3D9Client/shaders/Vessel.fx b/OVP/D3D9Client/shaders/Vessel.fx index ea4691f5b..b23e7b03b 100644 --- a/OVP/D3D9Client/shaders/Vessel.fx +++ b/OVP/D3D9Client/shaders/Vessel.fx @@ -25,6 +25,7 @@ inline float cmax(float3 color) // Must be included here #include "Metalness.fx" + // ============================================================================ // Vertex shader for physics based rendering // @@ -115,7 +116,7 @@ float4 AdvancedPS(float4 sc : VPOS, PBRData frg) : COLOR if (dLN == 0) fSun = 0; // Special alpha only texture in use - if (gNoColor) cTex.rgb = 1; + cTex.rgb = saturate(cTex.rgb + gNoColor.rgb); // ---------------------------------------------------------------------- @@ -123,7 +124,7 @@ float4 AdvancedPS(float4 sc : VPOS, PBRData frg) : COLOR // ---------------------------------------------------------------------- #if SHDMAP > 0 - cSun.rgb *= ComputeShadow(frg.shdH, dLN, sc); + if (!gCockpit) cSun.rgb *= ComputeShadow(frg.shdH, dLN, sc); #endif @@ -158,14 +159,7 @@ float4 AdvancedPS(float4 sc : VPOS, PBRData frg) : COLOR if (gCfg.Transl) { cTransl = tex2D(TranslS, frg.tex0.xy).rgb; - } - - // Texture Tuning ------------------------------------------------------- - // - if (gTuneEnabled) { - cTransm *= gTune.Transm.rgba; - cTransl *= gTune.Transl.rgb; - } + } float sunLightFromBehind = saturate(dot(gSun.Dir, nrmW)); float sunSpotFromBehind = pow(saturate(dot(gSun.Dir, CamD)), cTransm.a); @@ -315,6 +309,7 @@ technique VesselTech DestBlend = InvSrcAlpha; ZWriteEnable = true; } + pass P4 { vertexShader = compile vs_3_0 MetalnessVS(); @@ -327,4 +322,17 @@ technique VesselTech DestBlend = InvSrcAlpha; ZWriteEnable = true; } + + pass P5 + { + vertexShader = compile vs_3_0 MetalnessVS(); + pixelShader = compile ps_3_0 BakedVC_PS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + } } diff --git a/OVP/GDIClient/GDIClient.h b/OVP/GDIClient/GDIClient.h index f25f4067b..a20f0e188 100644 --- a/OVP/GDIClient/GDIClient.h +++ b/OVP/GDIClient/GDIClient.h @@ -520,4 +520,4 @@ class GDICLIENTEXPORT GDIBrush: public oapi::Brush { HBRUSH hBrush; }; -#endif // !__GDICLIENT_H \ No newline at end of file +#endif // !__GDICLIENT_H diff --git a/OVP/VulkanClient/.editorconfig b/OVP/VulkanClient/.editorconfig new file mode 100644 index 000000000..7872255e5 --- /dev/null +++ b/OVP/VulkanClient/.editorconfig @@ -0,0 +1,23 @@ +# https://EditorConfig.org +root = true + +# DOS-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = crlf +trim_trailing_whitespace = true +insert_final_newline = true + +# RC files need ANSI (windows) codepage +[*.rc] +charset = latin1 + +# Tab indentation (size 4 spaces) +[*.{cpp,hpp,c,h,rc,filters,sln,vcxproj,vcproj}] +indent_style = tab +indent_size = 4 + +# 4 space indentation for txt files +[*.txt] +indent_style = space +indent_size = 4 diff --git a/OVP/VulkanClient/AABBUtil.cpp b/OVP/VulkanClient/AABBUtil.cpp new file mode 100644 index 000000000..115b98714 --- /dev/null +++ b/OVP/VulkanClient/AABBUtil.cpp @@ -0,0 +1,416 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2013-2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + +#include "AABBUtil.h" +#include "OrbiterAPI.h" +#include "VectorHelpers.h" +#include "Log.h" +#include "Client.h" +#include "MathAPI.h" + +#pragma warning(push) +#pragma warning(disable : 4838) +#include +#pragma warning(pop) + +using std::min; + +// ================================================================================================================================= +// +bool SolveLUSystem(int n, double *A, double *b, double *x, double *det) +{ + int e=0, *p = new int[n]; + for (int i=0;id) { d = fabs(A[s*n+k]); r = s; } + if (d == 0.0) { LogErr("Singular Matrix in SolveLUSystem()"); delete []p; p = NULL; return false; } + if (r!=k) { // Do Swaps + for (int i=0;i=0;i--) { for (int j=i+1;jm11); + y = float((y*2.0-1.0)/mProj->m22); + XMVECTOR pick = FVECTOR3(x, 1.0f, y).XM(); + XMMATRIX mViewI = XMMatrixInverse(NULL, mView->XM()); + XMVECTOR R = XMVector3Normalize(XMVector3TransformNormal(pick, mViewI)); + return FVECTOR3(R); +} + + +void D9ZeroAABB(D9BBox *box) +{ + memset(box, 0, sizeof(D9BBox)); +} + +void D9InitAABB(D9BBox *box) +{ + box->mn = FVECTOR4( 1e12f, 1e12f, 1e12f, 0.0f); + box->mx = FVECTOR4(-1e12f, -1e12f, -1e12f, 0.0f); + box->bs = FVECTOR4(); +} + + +void D9AddPointAABB(D9BBox *box, FVECTOR3* point) +{ + XMVECTOR q = XMLoadFloat4((const XMFLOAT4*)&box->mn); + XMVECTOR w = XMLoadFloat4((const XMFLOAT4*)&box->mx); + XMVECTOR p = XMLoadFloat3((const XMFLOAT3*)point); + XMStoreFloat4((XMFLOAT4*)&box->mn, XMVectorMin(q,p)); + XMStoreFloat4((XMFLOAT4*)&box->mx, XMVectorMax(w,p)); +} + + +FVECTOR4 D9LinearFieldOfView(const FMATRIX4 *pProj) +{ + float a = 1.0f/pProj->m22; + float s = (pProj->m11/pProj->m22); + float l = 1.0f/cos(atan(a)); + return FVECTOR4(l, l/s, a, a/s); +} + + +float D9NearPlane(LPDIRECT3DDEVICE9 pDev, float znear, float zfar, float dmin, const FMATRIX4 *pProj, bool bReduced) +{ + float b = 1.0f/pProj->m11; + float a = 1.0f/pProj->m22; + float q = atan(sqrt(a*a+b*b)); + + D3DVIEWPORT9 vp; pDev->GetViewport(&vp); + + dmin = dmin * cos(q); + + if (znear>0) dmin = znear; + + float fact = 1500.0f/min(10e3f, zfar); + float zmax = 500.0f; + + if (bReduced) zmax = 25.0f; + + float vmaxi = zmax / (1.0f + fact*fact); + float value = dmin / (1.0f + fact*fact); + + if (value>vmaxi) value=vmaxi; + + return value; +} + + +FVECTOR4 D9OffsetRange(double R, double r) +{ + double t = r*0.5; + double r2 = r*r; + double h1 = sqrt(R*R+r2)-R; + double h2 = sqrt(R*R+t*t)-R; + double a = (h2 - h1*0.0625)/(0.1875*r2); + double b = -(h2 - h1*0.25)/(0.1875*r2*r2); + return FVECTOR4(float(a), float(b), 1.0f/float(r2), 0.0f); +} + + +bool D9IsAABBVisible(const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F) +{ + + FVECTOR3 bv = oapiTransformCoord(&in->bs.xyz, pWV); + float w = in->bs.w; + + if (bv.z<-w) return false; // Not visible + float zz = fabs(bv.z); + float tol = F->z*0.0015f; + + if ((w/zz)x*w) > zz*F->z) return false; // Not visible + if (fabs(bv.x)-(F->y*w) > zz*F->w) return false; // Not visible + + FVECTOR4 size = (in->mx - in->mn)*0.5f; + FVECTOR3 xv = oapiTransformNormal(&in->a.xyz, pWV); + FVECTOR3 yv = oapiTransformNormal(&in->b.xyz, pWV); + FVECTOR3 zv = oapiTransformNormal(&in->c.xyz, pWV); + + float dx = dotp(xv, bv); + float dy = dotp(yv, bv); + float dz = dotp(zv, bv); + + float adx = fabs(dx) - size.x; + float ady = fabs(dy) - size.y; + float adz = fabs(dz) - size.z; + + float sdx,sdy,sdz; + + if (dx<0) sdx=dx+size.x; + else sdx=dx-size.x; + if (dy<0) sdy=dy+size.y; + else sdy=dy-size.y; + if (dz<0) sdz=dz+size.z; + else sdz=dz-size.z; + + float fov = sin(atan(sqrt(F->z*F->z + F->w*F->w))); + + if (fabs(xv.z)>fov && (sdx*xv.z)<0 && adx>0) return false; + if (fabs(yv.z)>fov && (sdy*yv.z)<0 && ady>0) return false; + if (fabs(zv.z)>fov && (sdz*zv.z)<0 && adz>0) return false; + + return true; +} + + +bool D9IsBSVisible(const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F) +{ + FVECTOR3 bv = FVECTOR3(in->bs.x, in->bs.y, in->bs.z); + bv = oapiTransformCoord(&bv, pWV); + float r = in->bs.w; + + if (bv.z<-r) return false; // Not visible + bv.z=fabs(bv.z); + + float tol = F->z*0.0015f; + + if ((r/bv.z)x*r) > (bv.z*F->z)) return false; // Not visible + if (fabs(bv.x)-(F->y*r) > (bv.z*F->w)) return false; // Not visible + + return true; +} + +int D9ComputeMinMaxDistance(LPDIRECT3DDEVICE9 pDev, const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F, float *zmin, float *zmax, float *dst) +{ + + FVECTOR3 bv = FVECTOR3(in->bs.x, in->bs.y, in->bs.z); + bv = oapiTransformCoord(&bv, pWV); + float r = in->bs.w; + + if (bv.z<-r) return -1; // Not visible + float zz = fabs(bv.z); + float tol = F->z*0.0015f; + + if ((r/zz)x*r) > zz*F->z) return -3; // Not visible + if (fabs(bv.x)-(F->y*r) > zz*F->w) return -4; // Not visible + + FVECTOR4 size = (in->mx - in->mn)*0.5; + FVECTOR3 xv = oapiTransformNormal(&in->a.xyz, pWV); + FVECTOR3 yv = oapiTransformNormal(&in->b.xyz, pWV); + FVECTOR3 zv = oapiTransformNormal(&in->c.xyz, pWV); + + float dx = dotp(xv, bv); + float dy = dotp(yv, bv); + float dz = dotp(zv, bv); + + float adx = fabs(dx) - size.x; + float ady = fabs(dy) - size.y; + float adz = fabs(dz) - size.z; + + if (adx>0 || ady>0 || adz>0) { + + float sdx,sdy,sdz; + + if (dx<0) sdx=dx+size.x; + else sdx=dx-size.x; + if (dy<0) sdy=dy+size.y; + else sdy=dy-size.y; + if (dz<0) sdz=dz+size.z; + else sdz=dz-size.z; + + float fov = sin(atan(sqrt(F->z*F->z + F->w*F->w))); + + if (fabs(xv.z)>fov && (sdx*xv.z)<0 && adx>0) return -5; + if (fabs(yv.z)>fov && (sdy*yv.z)<0 && ady>0) return -6; + if (fabs(zv.z)>fov && (sdz*zv.z)<0 && adz>0) return -7; + } + + if (adx<0) adx=0; + if (ady<0) ady=0; + if (adz<0) adz=0; + + float d = sqrt(adx*adx + ady*ady + adz*adz); + + if (d<*dst) *dst=d; + + + float x = xv.z * size.x; + float y = yv.z * size.y; + float z = zv.z * size.z; + float e = bv.z; + + + float q[8]; + + q[0] = e + (+x+y+z); + q[1] = e + (-x+y+z); + q[2] = e + (+x-y+z); + q[3] = e + (-x-y+z); + q[4] = e + (+x+y-z); + q[5] = e + (-x+y-z); + q[6] = e + (+x-y-z); + q[7] = e + (-x-y-z); + + float mx = q[0]; + float mi = q[0]; + + for (int i=1;i<8;i++) { + float qq = q[i]; + if (qq>mx) mx=qq; + if (qq*zmax) *zmax = mx; + + return 0; +} + + +void D9UpdateAABB(D9BBox *box, const FMATRIX4 *pFirst, const FMATRIX4 *pSecond) +{ + + XMVECTOR x = XMVectorSet(1, 0, 0, 0); + XMVECTOR y = XMVectorSet(0, 1, 0, 0); + XMVECTOR z = XMVectorSet(0, 0, 1, 0); + XMVECTOR q = XMLoadFloat4((const XMFLOAT4*)&box->mn); + XMVECTOR w = XMLoadFloat4((const XMFLOAT4*)&box->mx); + + if (pFirst) { + XMMATRIX MF = XMLoadFloat4x4((const XMFLOAT4X4*)pFirst); + x = XMVector3TransformNormal(x, MF); + y = XMVector3TransformNormal(y, MF); + z = XMVector3TransformNormal(z, MF); + q = XMVector3TransformCoord(q, MF); + w = XMVector3TransformCoord(w, MF); + } + + if (pSecond) { + XMMATRIX MS = XMLoadFloat4x4((const XMFLOAT4X4*)pSecond); + x = XMVector3TransformNormal(x, MS); + y = XMVector3TransformNormal(y, MS); + z = XMVector3TransformNormal(z, MS); + q = XMVector3TransformCoord(q, MS); + w = XMVector3TransformCoord(w, MS); + } + + XMVECTOR p = XMVectorScale(XMVectorAdd(q,w), 0.5f); + + XMStoreFloat4((XMFLOAT4*)&box->bs, p); + XMStoreFloat4((XMFLOAT4*)&box->a, x); + XMStoreFloat4((XMFLOAT4*)&box->b, y); + XMStoreFloat4((XMFLOAT4*)&box->c, z); + + box->bs.w = XMVectorGetX(XMVector3Length(XMVectorSubtract(q,w))) * 0.5f; +} + + + + + +void D9AddAABB(const D9BBox *in, const FMATRIX4 *pM, D9BBox *out, bool bReset) +{ + + XMVECTOR x,mi,mx; + + if (bReset) { + mi = XMVectorSet( 1e12f, 1e12f, 1e12f, 0); + mx = XMVectorSet(-1e12f, -1e12f, -1e12f, 0); + } + else { + mi = XMLoadFloat4((const XMFLOAT4*)&out->mn); + mx = XMLoadFloat4((const XMFLOAT4*)&out->mx); + } + + XMVECTOR q = XMLoadFloat4((const XMFLOAT4*)&in->mn); + XMVECTOR w = XMLoadFloat4((const XMFLOAT4*)&in->mx); + + q = XMVectorSetW(q, 0); + w = XMVectorSetW(w, 0); + + if (pM) { + + XMVECTOR L[8]; + + L[0] = XMVectorSelectControl(0,0,0,0); + L[1] = XMVectorSelectControl(1,1,1,0); + L[2] = XMVectorSelectControl(0,0,1,0); + L[3] = XMVectorSelectControl(0,1,0,0); + L[4] = XMVectorSelectControl(0,1,1,0); + L[5] = XMVectorSelectControl(1,0,0,0); + L[6] = XMVectorSelectControl(1,0,1,0); + L[7] = XMVectorSelectControl(1,1,0,0); + + + XMMATRIX M = XMLoadFloat4x4((const XMFLOAT4X4*)pM); + + for (int k=0;k<8;k++) { + x = XMVector3TransformCoord(XMVectorSelect(q, w, L[k]), M); + mi = XMVectorMin(mi,x); + mx = XMVectorMax(mx,x); + } + } + else { + mi = XMVectorMin(mi, XMVectorMin(q,w)); + mx = XMVectorMax(mx, XMVectorMax(q,w)); + } + + XMStoreFloat4((XMFLOAT4*)&out->mn, mi); + XMStoreFloat4((XMFLOAT4*)&out->mx, mx); +} + + +void EnvMapDirection(int dir, FVECTOR3 *Dir, FVECTOR3 *Up) +{ + switch (dir) { + case 0: + *Dir = FVECTOR3(1.0f, 0.0f, 0.0f); + *Up = FVECTOR3(0.0f, 1.0f, 0.0f); + break; + case 1: + *Dir = FVECTOR3(-1.0f, 0.0f, 0.0f); + *Up = FVECTOR3( 0.0f, 1.0f, 0.0f); + break; + case 2: + *Dir = FVECTOR3(0.0f, 1.0f, 0.0f); + *Up = FVECTOR3(0.0f, 0.0f, -1.0f); + break; + case 3: + *Dir = FVECTOR3(0.0f, -1.0f, 0.0f); + *Up = FVECTOR3(0.0f, 0.0f, 1.0f); + break; + case 4: + *Dir = FVECTOR3(0.0f, 0.0f, 1.0f); + *Up = FVECTOR3(0.0f, 1.0f, 0.0f); + break; + case 5: + *Dir = FVECTOR3(0.0f, 0.0f, -1.0f); + *Up = FVECTOR3(0.0f, 1.0f, 0.0f); + break; + default: + *Dir = FVECTOR3(0.0f, 0.0f, 0.0f); + *Up = FVECTOR3(0.0f, 0.0f, 0.0f); + break; + } +} diff --git a/OVP/VulkanClient/AABBUtil.h b/OVP/VulkanClient/AABBUtil.h new file mode 100644 index 000000000..66d8cfd02 --- /dev/null +++ b/OVP/VulkanClient/AABBUtil.h @@ -0,0 +1,49 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2013-2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + + +#include +#include "MathAPI.h" + +#ifndef __vkTK_H +#define __vkTK_H + +#define SctPwr 1.0 +#define SctPwr2 2.0 + +using namespace oapi; + +struct D9BBox { + FVECTOR4 mn, mx, bs, a, b, c; +}; + +float D9NearPlane(LPDIRECT3DDEVICE9 pDev, float zmin, float zmax, float dmax, const FMATRIX4 *pProj, bool bReduced); +int D9ComputeMinMaxDistance(LPDIRECT3DDEVICE9 pDev, const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F, float *zmin, float *zmax, float *dmin); +void D9AddAABB(const D9BBox *in, const FMATRIX4 *pM, D9BBox *out, bool bReset=false); +void D9UpdateAABB(D9BBox *box, const FMATRIX4 *pFisrt=NULL, const FMATRIX4 *pSecond=NULL); +void D9ZeroAABB(D9BBox *box); +void D9InitAABB(D9BBox *box); +void D9AddPointAABB(D9BBox *box, FVECTOR3* point); +bool D9IsAABBVisible(const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F); +bool D9IsBSVisible(const D9BBox *in, const FMATRIX4 *pWV, const FVECTOR4 *F); +FVECTOR4 D9LinearFieldOfView(const FMATRIX4 *pProj); +FVECTOR4 D9OffsetRange(double R, double r); +void EnvMapDirection(int dir, FVECTOR3 *Dir, FVECTOR3 *Up); +FVECTOR3 WorldPickRay(float x, float y, const FMATRIX4* mProj, const FMATRIX4* mView); +bool SolveLUSystem(int n, double *A, double *b, double *x, double *det=NULL); +#endif diff --git a/OVP/VulkanClient/AtmoControls.cpp b/OVP/VulkanClient/AtmoControls.cpp new file mode 100644 index 000000000..8d8ef36ca --- /dev/null +++ b/OVP/VulkanClient/AtmoControls.cpp @@ -0,0 +1,584 @@ +// ============================================================== +// Atmospheric controls implementation +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2014 - 2016 Jarmo Nikkanen +// ============================================================== + +#include "Client.h" +#include "resource.h" +#include "Config.h" +#include "AtmoControls.h" +#include "Commctrl.h" +#include "vObject.h" +#include "vPlanet.h" +#include "Mesh.h" +#include "Scene.h" +#include + +using namespace oapi; + +extern HINSTANCE g_hInst; +extern vkClient *g_client; + +// ============================================================== + +// Defaut c'tor to init members +ScatterParams::ScatterParams() : + red ( 0.650 ), // 0.400 ... 0.700 + green ( 0.560 ), // 0.400 ... 0.700 + blue ( 0.480 ), // 0.400 ... 0.700 + rpow ( 4.0 ), // -8.0 ... 8.0 + mpow ( 1.0 ), // -2.0 ... 2.0 + rheight ( 8.0 ), // 4.0 ... 40.0 [km] + mheight ( 1.0 ), // 0.0 ... 1.5 + // ---------------------------------------- + trb ( 1.0 ), // 0.2 ... 3.0 + tr3D ( 1.0 ), + // ---------------------------------------- + rayrat ( 1.0 ), // 0.0 ... 3.0 + ray ( 1.0 ), // 0.0 ... 4.0 + tw_bld ( 0.0 ), // 0.0 ... 3.5 + // ---------------------------------------- + mie ( 0.0869 ), // 0.0 ... 8.0 + mphase ( 0.9831 ), // 0.85 ... 0.999 + // ---------------------------------------- + mierat ( 1.0 ), // 0.0 ... 2.0 + aux2 ( 1.0 ), // 0.0 ... 2.0 + aux3 ( 2.0 ), + tgamma ( 1.0 ), + mphaseb ( 1.0 ), + hazei ( 1.0 ), + tw_bri ( 0.0 ), + tw_dst ( 0.0 ), + // ---------------------------------------- + orbalt ( 250e3 ), + visalt ( 70e3 ), + wtrans ( 0.1 ), + wspec ( 0.8 ), + wnrml ( 1.0 ), + wboost ( 0.0 ), + zcolor (1.0f, 1.0f, 0.9f), + hcolor (1.0f, 0.7f, 0.0f), + acolor (0.9f, 0.9f, 1.0f), + suni (1.0) +{ +} + +// ============================================================== + +namespace AtmoControls { + + + +ScatterParams defs; +ScatterParams *param = NULL; + +std::vector Slider; +std::vector Values; + +DWORD atmpage = 0; +DWORD atmmode = 0; +DWORD dwCmd = NULL; +HWND hDlg = NULL; +vPlanet *vObj = NULL; + + +// ============================================================== + +void SetToolTip(int vid, PTSTR pszText) +{ + if (!pszText) return; + for (auto& x : Values) if (x.sprm == vid) { + x.tooltip = string(pszText); + break; + } +} + +// ============================================================== + +void InitToolTips() +{ + if (!hDlg) return; + + for (auto& s : Slider) + { + if (!s.hWnd || !s.hwndTip) return; + + if (s.val && s.val->tooltip.size() > 2) + { + TOOLINFO toolInfo = { 0 }; + toolInfo.cbSize = sizeof(toolInfo); + toolInfo.hwnd = hDlg; + toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolInfo.uId = (UINT_PTR)s.hWnd; + toolInfo.lpszText = LPSTR(s.val->tooltip.c_str()); + SendMessage(s.hwndTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&toolInfo); + SendMessage(s.hwndTip, TTM_ACTIVATE, TRUE, 0); + } + else { + SendMessage(s.hwndTip, TTM_ACTIVATE, FALSE, 0); + } + } +} + +// ============================================================== + +void Create() +{ + vObj = NULL; + hDlg = NULL; + + dwCmd = oapiRegisterCustomCmd((char*)"vk Atmospheric Controls", (char*)"This dialog allows to control various atmospheric parameters and effects", OpenDlgClbk, NULL); + + Slider.resize(ATM_SLIDER_COUNT); + + for (int i = 0; i < ATM_SLIDER_COUNT; i++) { + Slider[i].res = IDC_ATM_S1 + i; + Slider[i].dsp = IDC_ATD_S1 + i; + Slider[i].lbl = IDC_ATL_S1 + i; + } + + // Style flags + // 0x1 = unit in [km] + // 0x2 = same for orbital and surface setup + // 0x8 = x^2 "linearization" + // 0x10 = x^0.5 "linearization" + // 0x20 = x^4 "linearization" + // 0x40 = no 'lerp', special handling + + // Slider ID, Value ID, Page ID, Label, min, max, flags + + // PAGE 0 ------------------------------------------------ + ConfigValue(1, 0, 0, "Distance", 0.0, 0.2); + ConfigValue(2, 2, 0, "Terrain", 0.01, 3.0, 0x8); + ConfigValue(3, 6, 0, "Building", 0.01, 1.0, 0x8); + // ------------------------------------------------------- + ConfigValue(4, 1, 0, "Green", 0.46, 0.65); + ConfigValue(5, 3, 0, "R-Pow", -8.0, 12.0); + ConfigValue(6, 12, 0, "M-Pow", -8.0, 12.0); + ConfigValue(7, 9, 0, "R-Height", 6.0, 600.0, 0x1 | 0x8); + ConfigValue(8, 11, 0, "M-Height", 0.5, 10.0, 0x1 | 0x8); + // ------------------------------------------------------- + ConfigValue(9, 13, 0, "Brightness", 0.1, 8.0, 0x8); + ConfigValue(10, 16, 0, "Gamma", 0.2, 1.5); + ConfigValue(11, 19, 0, "Light/Shad", 0.0, 5.0, 32); + // ------------------------------------------------------- + ConfigValue(12, 5, 0, "Density", 0.0, 10.0, 8); + ConfigValue(13, 4, 0, "Ratio", 0.2, 5.0, 0x20); + // ------------------------------------------------------- + ConfigValue(14, 7, 0, "Density", 0.01, 10.0, 0x8); + ConfigValue(15, 8, 0, "Phase-A", 0.02, 0.999, 0x8); + ConfigValue(16, 17, 0, "Phase-B", 0.0, 8.0); + ConfigValue(17, 14, 0, "Ratio", 0.2, 5.0, 0x20); + // ------------------------------------------------------- + ConfigValue(18, 10, 0, "CloudAlt", 0.2, 10.0, 0x1 | 0x40); // Clouds altitude (km) + ConfigValue(19, 15, 0, "HDR", 0.1, 4.0, 0x8); // HDR + ConfigValue(20, 18, 0, "Intensity", 0.0, 5.0, 0x8 | 0x40); // Clouds intensity + + // PAGE 1 ------------------------------------------------ + ConfigValue(14, 20, 1, "Normals", 0.2, 2.0, 0x8); + ConfigValue(15, 21, 1, "Spec", 0.5, 1.5); + ConfigValue(16, 22, 1, "Color", 0.01, 0.4); + ConfigValue(17, 23, 1, " ", 0.01, 1.0); + + + SetToolTip(0, (char*)"Light travel distance behind terminator"); + SetToolTip(1, (char*)"Green wave lenght. (Green balance)"); + SetToolTip(2, (char*)"Terrain brightness during twilight"); + SetToolTip(3, (char*)"Main control for atmospheric rayleigh color composition (4.0 for the Earth)"); + SetToolTip(12, (char*)"Main control for atmospheric mie color composition"); + SetToolTip(9, (char*)"Atmosphere Ray scale height (7km - 9km for the Earth)"); + SetToolTip(11, (char*)"Atmosphere Mie scale height (0.6km - 2km for the Earth)"); + // ------------------------------------------------------- + SetToolTip(13, (char*)"Terrain/Ocean brightness control (default 1.0)"); + SetToolTip(16, (char*)"Terrain/Ocean gamma control value (default 1.0)"); + SetToolTip(19, (char*)"Terrain light and shadow boost"); + // ------------------------------------------------------- + SetToolTip(5, (char*)"Overall control for rayleigh scattering (i.e. Haze stickness, atmosphere transparency, optical depth"); + SetToolTip(4, (char*)"Rayleigh in-scatter out-scatter ratio (1.0 nominal)"); + SetToolTip(6, (char*)"Building lighting level"); + // ------------------------------------------------------- + SetToolTip(7, (char*)"Overall scale factor for mie scattering. (Mie-particle density)"); + SetToolTip(8, (char*)"Directional strength of Henyey-Greenstein phase function"); + SetToolTip(14, (char*)"Mie in-scatter out-scatter ratio (1.0 nominal)"); + // ------------------------------------------------------- + SetToolTip(10, (char*)"Altitude for cloud lighting calculations"); + SetToolTip(15, (char*)"'HDR' Exposure factor"); + SetToolTip(17, (char*)"Omnidirectional mie scattering scale factor"); + SetToolTip(18, (char*)"[Dual purpose] Clouds intensity [on surface]. Multiscatter light level [on orbit]"); + // ------------------------------------------------------- + SetToolTip(20, (char*)"Water normal map strength"); + SetToolTip(21, (char*)"Water reflectivity"); + SetToolTip(22, (char*)"Water color / transparency"); + SetToolTip(23, (char*)"Unused"); +} + +// ============================================================== + +bool IsActive() +{ + return (hDlg!=NULL); +} + +// ============================================================== + +bool Visualize() +{ + if (!hDlg) return false; + return SendDlgItemMessage(hDlg, IDC_ATM_DISPLAY, BM_GETCHECK, 0, 0) == BST_CHECKED; +} + +// ============================================================== + +void Release() +{ + if (dwCmd) oapiUnregisterCustomCmd(dwCmd); + dwCmd = NULL; +} + +// ============================================================== + +void OpenDlgClbk(void *context) +{ + HWND l_hDlg = oapiOpenDialog(g_hInst, IDD_vkSCATTER, WndProc); + + if (l_hDlg) hDlg = l_hDlg; // otherwise open already + else return; + + Scene *scene = g_client->GetScene(); + + if (scene) vObj = scene->GetCameraNearVisual(); + + if (vObj) param = vObj->GetAtmoParams(atmmode); + else param = &defs; + + SendDlgItemMessageA(hDlg, IDC_ATM_MODE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_ATM_MODE, CB_ADDSTRING, 0, (LPARAM)"Auto"); + SendDlgItemMessageA(hDlg, IDC_ATM_MODE, CB_ADDSTRING, 0, (LPARAM)"Surface"); + SendDlgItemMessageA(hDlg, IDC_ATM_MODE, CB_ADDSTRING, 0, (LPARAM)"Low Orbit"); + SendDlgItemMessageA(hDlg, IDC_ATM_MODE, CB_ADDSTRING, 0, (LPARAM)"High Orbit"); + SendDlgItemMessageA(hDlg, IDC_ATM_MODE, CB_SETCURSEL, atmmode, 0); + + SendDlgItemMessageA(hDlg, IDC_ATM_PAGE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_ATM_PAGE, CB_ADDSTRING, 0, (LPARAM)"Atmospheric Controls"); + SendDlgItemMessageA(hDlg, IDC_ATM_PAGE, CB_ADDSTRING, 0, (LPARAM)"Water and Sun-glare"); + SendDlgItemMessageA(hDlg, IDC_ATM_PAGE, CB_SETCURSEL, atmpage, 0); + + for (auto& s : Slider) + { + s.hWnd = GetDlgItem(hDlg, s.res); + s.hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hDlg, NULL, g_hInst, NULL); + TOOLINFO toolInfo = { 0 }; + toolInfo.cbSize = sizeof(toolInfo); + toolInfo.hwnd = hDlg; + toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolInfo.uId = (UINT_PTR)s.hWnd; + toolInfo.lpszText = "ToolTip"; + SendMessage(s.hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); + } + + SetTimer(hDlg, 0, 1000, NULL); + + atmpage = 0; + InitPage(atmpage); +} + +// ============================================================== + +void InitPage(int p) +{ + for (auto& s : Slider) s.val = nullptr; + + for (auto& v : Values) if (v.page == p) { + assert(v.slider >= 0 && v.slider < ATM_SLIDER_COUNT); + Slider[v.slider].val = &v; + } + + for (auto& s : Slider) { + if (s.val) SetWindowTextA(GetDlgItem(hDlg, s.lbl), s.val->lbl.c_str()); + else SetWindowTextA(GetDlgItem(hDlg, s.lbl), " "); + } + + if (p == 0) { + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG1), "Twilight and Ambient settings"); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG2), "Wavelenght and Scale height settings"); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG3), "Terrain"); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG4), "Rayleigh settings"); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG5), "Mie settings"); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG6), "Custom settings"); + } + + if (p == 1) { + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG1), " "); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG2), " "); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG3), " "); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG4), " "); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG5), "Water Rendering"); + SetWindowTextA(GetDlgItem(hDlg, IDC_ATG6), " "); + } + + InitToolTips(); + UpdateSliders(); +} + +// ============================================================== + +double GetValue(int id) +{ + for (auto& s : Values) if (s.id == id) { + assert(s.sprm >= 0 && s.sprm < ATM_DATA_COUNT); + return param->data[s.sprm]; + } + LogErr("Invalid Slider ID in AtmoControls"); + return 0.0; +} + +// ============================================================== + +void ConfigValue(int sid, int vid, int pid, string lbl, double min, double max, int style) +{ + // Slider ID, Value ID, Page ID, Label, min, max, flags + sValue v; + v.max = max; + v.min = min; + v.style = style; + v.page = pid; + v.sprm = vid; + v.slider = sid - 1; + v.lbl = lbl; + Values.push_back(v); +} + +// ============================================================== + +void SetSlider(sSlider& s) +{ + if (!vObj) return; + if (!s.val) return; + + auto pos = SendDlgItemMessage(hDlg, s.res, TBM_GETPOS, 0, 0); + auto v = s.val; + double x = (1000.0-double(pos))/1000.0; + + assert(v->sprm >= 0 && v->sprm < ATM_DATA_COUNT); + + if (v->style & 8) x = x * x; + if (v->style & 16) x = sqrt(x); + if (v->style & 32) x = pow(x, 4.0); + + double val = v->min * (1.0 - x) + v->max * x; + + if (v->style&2) { + vObj->GetAtmoParams(1)->data[v->sprm] = val; + vObj->GetAtmoParams(2)->data[v->sprm] = val; + vObj->GetAtmoParams(3)->data[v->sprm] = val; + } + else { + param->data[v->sprm] = val; + } + + UpdateSlider(s, false); // Update value display + return; +} + +// ============================================================== + +void UpdateSliders() +{ + for (auto& s : Slider) UpdateSlider(s, true); +} + +// ============================================================== + +void UpdateSlider(sSlider& q, bool bSetPos) +{ + char buf[128]; + if (!param) return; + + if (q.val) + { + auto v = q.val; + assert(v->sprm >= 0 && v->sprm < ATM_DATA_COUNT); + double val = param->data[v->sprm]; + + if (bSetPos) { + + SendDlgItemMessage(hDlg, q.res, TBM_SETRANGEMAX, 1, 1000); + SendDlgItemMessage(hDlg, q.res, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hDlg, q.res, TBM_SETTICFREQ, 1, 0); + + double x = (val - v->min) / (v->max - v->min); + if (v->style & 8) x = sqrt(x); + if (v->style & 16) x = x * x; + if (v->style & 32) x = pow(x, 0.25); + DWORD dpos = 1000 - DWORD(x * 1000.0); + + SendDlgItemMessage(hDlg, q.res, TBM_SETPOS, 1, dpos); + } + + if (v->style & 1) sprintf_s(buf, 128, "%.1lf k", val); + else sprintf_s(buf, 128, "%.3lf", val); + } + else { + strcpy_s(buf, 32, " "); + SendDlgItemMessage(hDlg, q.res, TBM_SETPOS, 1, 0); + } + + SetWindowTextA(GetDlgItem(hDlg, q.dsp), buf); + return; +} + +// ============================================================== + +vPlanet * GetVisual() +{ + return vObj; +} + +// ============================================================== + +void SetVisual(vObject *vo) +{ + if (!vo) { + vObj = NULL; + param = &defs; + return; + } + + if (!hDlg || !dwCmd) return; + + OBJHANDLE hObj = vo->GetObjHandle(); + + if (oapiGetObjectType(hObj)!=OBJTP_PLANET) { + LogErr("Invalid Object Type in AtmoControls"); + vObj = NULL; + param = &defs; + return; + } + + vObj = static_cast(vo); + + if (vObj) param = vObj->GetAtmoParams(0); + else param = &defs; + + UpdateSliders(); +} + + +// ============================================================== +// Dialog message handler + +INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static bool bOrbOld = false; + + switch (uMsg) { + + case WM_INITDIALOG: + { + vObject *vPl = g_client->GetScene()->GetCameraNearVisual(); + SetVisual(vPl); + return true; + } + + case WM_TIMER: + { + if (vObj) { + char title[256]; string file = "??"; + sprintf_s(title, 256, "Atmospheric Controls [%s]", vObj->GetName()); + + auto cfg = Config->AtmoCfg.find(vObj->GetName()); + if (cfg != Config->AtmoCfg.end()) file = cfg->second; + + param = vObj->GetAtmoParams(atmmode); + + double vd = 0.5f; + if (atmmode == 0) vd = param->cfg_alt < 0.9999f ? param->cfg_alt : 1.0 - param->cfg_halt; + + if (vObj->GetAtmoMode() == 1) sprintf_s(title, 256, "Atmospheric Controls [%s][%s] [Surface] (%3.1f%%)", file.c_str(), vObj->GetName(), (1.0 - vd) * 100.0); + if (vObj->GetAtmoMode() == 2) sprintf_s(title, 256, "Atmospheric Controls [%s][%s] [LowOrbit] (%3.1f%%)", file.c_str(), vObj->GetName(), vd * 100.0); + if (vObj->GetAtmoMode() == 3) sprintf_s(title, 256, "Atmospheric Controls [%s][%s] [HighOrbit] (%3.1f%%)", file.c_str(), vObj->GetName(), (1.0 - vd) * 100.0); + + SetWindowTextA(hDlg, title); + UpdateSliders(); + } + break; + } + + case WM_VSCROLL: + { + if (LOWORD(wParam) == TB_THUMBTRACK || LOWORD(wParam) == TB_ENDTRACK || LOWORD(wParam) == TB_THUMBPOSITION) { + for (auto& s : Slider) if (s.hWnd == HWND(lParam)) { + SetSlider(s); + return false; + } + } + return false; + } + + case WM_COMMAND: + + switch (LOWORD(wParam)) + { + case IDCANCEL: + case IDOK: + oapiCloseDialog(hWnd); + hDlg = NULL; + return TRUE; + + case IDC_ATM_LOAD: + if (vObj) { + vObj->LoadAtmoConfig(); + UpdateSliders(); + } + break; + + case IDC_ATM_SAVE: + if (vObj) vObj->SaveAtmoConfig(); + break; + + case IDC_ATM_COPYTO: + if (vObj) { + if (vObj->GetAtmoParams(1) != param) memcpy(vObj->GetAtmoParams(1), param, sizeof(ScatterParams)); + } + break; + + case IDC_ATM_COPYLOW: + if (vObj) { + if (vObj->GetAtmoParams(2) != param) memcpy(vObj->GetAtmoParams(2), param, sizeof(ScatterParams)); + } + break; + + case IDC_ATM_COPYHIGH: + if (vObj) { + if (vObj->GetAtmoParams(3) != param) memcpy(vObj->GetAtmoParams(3), param, sizeof(ScatterParams)); + } + break; + + case IDC_ATM_MODE: + if (HIWORD(wParam)==CBN_SELCHANGE) { + atmmode = DWORD(SendDlgItemMessage(hWnd, IDC_ATM_MODE, CB_GETCURSEL, 0, 0)); + } + break; + + case IDC_ATM_PAGE: + if (HIWORD(wParam) == CBN_SELCHANGE) { + atmpage = DWORD(SendDlgItemMessage(hWnd, IDC_ATM_PAGE, CB_GETCURSEL, 0, 0)); + InitPage(atmpage); + } + break; + + default: + //LogErr("LOWORD(%hu), HIWORD(0x%hX)",LOWORD(wParam),HIWORD(wParam)); + break; + } + break; + } + + return oapiDefDialogProc(hWnd, uMsg, wParam, lParam);; +} + +} //namespace + + diff --git a/OVP/VulkanClient/AtmoControls.h b/OVP/VulkanClient/AtmoControls.h new file mode 100644 index 000000000..613132161 --- /dev/null +++ b/OVP/VulkanClient/AtmoControls.h @@ -0,0 +1,107 @@ +// ============================================================== +// Atmospheric controls implementation +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2014-2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __ATMOCONTROLS_H +#define __ATMOCONTROLS_H + +#define ATM_SLIDER_COUNT 20 +#define ATM_DATA_COUNT 24 + + +typedef struct ScatterParams { + ScatterParams(); ///< Defaut c'tor + union { + double data[ATM_DATA_COUNT]; // ATTENTION: Order of params must match with slider indexes + struct { + double tw_dst; ///< 0 Twilight distance + double green; ///< 1 Green wavw length + double tw_bri; ///< 2 Twilight brightness + double rpow; ///< 3 Rayleigh power + double rayrat; ///< 4 Rayleigh ratio + double ray; ///< 5 Rayleigh out-scatter strength + double tw_bld; ///< 6 Building ambient level at twilight + double mie; ///< 7 scale factor for mie out-scattering + double mphase; ///< 8 g-constant in HG phase function + double rheight; ///< 9 atmospheric rayleigh scale height + double aux2; ///< 10 cloud lighting altitude [km] + double mheight; ///< 11 Mie scale height + double mpow; ///< 12 Mie power + double trb; ///< 13 Terrain brightness + double mierat; ///< 14 Mie ratio + double aux3; ///< 15 auxiliary parameter + double tgamma; ///< 16 Terrain gamma + double mphaseb; ///< 17 MiePhase-B + double hazei; ///< 18 cloud intensity + double tr3D; ///< 19 Terrain light and shadow boost + // PAGE 1 + double wnrml; ///< 20 Water normal strength + double wspec; ///< 21 Water specular color + double wtrans; ///< 22 Water color / transparency + double wboost; ///< 23 Terrain light and shadow boost + }; + }; + double orbalt; + double visalt; + double red; + double blue; + double suni; + FVECTOR3 wcolor; // Water color + FVECTOR3 zcolor; // sun-glare color at zenith (camera at sealevel) + FVECTOR3 hcolor; // sun-glare color at horizon (camera at sealevel) + FVECTOR3 acolor; // Abmient color at sealevel + double cfg_alt; + double cfg_halt; +} ScatterParams; + +struct sValue { + double min, max; + WORD id; // User id + WORD sprm; // Value index (ScatterTable) + WORD style; + WORD page; + WORD slider; + string lbl; + string tooltip; +}; + +struct sSlider { + HWND hWnd; + HWND hwndTip; // ToolTip + int res; // Slider resource id + int dsp; // Slider display resource id + int lbl; // Slider label resource id + sValue* val; +}; + + +class vPlanet; +class vObject; + +// ============================================================== + +namespace AtmoControls { + + void Create(); + void Release(); + + void OpenDlgClbk(void *context); + void SetVisual(vObject *vo); + vPlanet * GetVisual(); + bool IsActive(); + + void InitPage(int p); + double GetValue(int id); + void UpdateSlider(sSlider& s, bool bSetPos = true); + void ConfigValue(int sid, int vid, int pid, string lbl, double min, double max, int style = 0); + void SetSlider(sSlider& s); + void UpdateSliders(); + bool Visualize(); + + INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +}; + +#endif // !__ATMOCONTROLS_H diff --git a/OVP/VulkanClient/BeaconArray.cpp b/OVP/VulkanClient/BeaconArray.cpp new file mode 100644 index 000000000..c7c4d4702 --- /dev/null +++ b/OVP/VulkanClient/BeaconArray.cpp @@ -0,0 +1,147 @@ +// =========================================================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// =========================================================================================== + +#include "BeaconArray.h" +#include "Log.h" +#include "Scene.h" +#include "Surface.h" +#include "Config.h" +#include "vPlanet.h" +#include "vBase.h" + +using namespace oapi; + +// =========================================================================================== +// +BeaconArray::BeaconArray(BeaconArrayEntry *pEnt, DWORD nEntry, vBase *_vB) + : vkEffect() + , nVert(nEntry) + , vB(_vB) + , pVB(NULL) + , hBase(NULL) + , bidx(0) + , base_elev() +{ + _TRACE; + + if (vB) hBase = vB->GetObjHandle(); + + pBeaconPos = new BeaconPos[nEntry]; + + HR(gc->GetDevice()->CreateVertexBuffer(nEntry*sizeof(BAVERTEX), D3DUSAGE_DYNAMIC|D3DUSAGE_POINTS, 0, D3DPOOL_DEFAULT, &pVB, NULL)); + + BAVERTEX *pVrt = LockVertexBuffer(); + + if (pVrt) { + + for (DWORD i=0;iToLocal(pEnt[i].pos, &pBeaconPos[i].lng, &pBeaconPos[i].lat)); + + pVrt[i].pos = _F(pEnt[i].pos); + pVrt[i].dir = _F(pEnt[i].dir); + + pVrt[i].color = pEnt[i].color; + pVrt[i].size = pEnt[i].size; + pVrt[i].angle = cos(pEnt[i].angle * 0.0174532925f * 0.5f); + + pVrt[i].on = pEnt[i].lon; + pVrt[i].off = pEnt[i].loff; + + pVrt[i].bright = pEnt[i].bright; + pVrt[i].falloff = pEnt[i].fall; + } + UnLockVertexBuffer(); + } + else { + LogErr("Failed to lock a vertex buffer in BeaconArray()"); + } + + pBright = gc->clbkLoadTexture("D3D9RwyLight.dds"); + + if (pBright==NULL) LogErr("D3D9RwyLight.dds is Missing"); +} + + +// =========================================================================================== +// +BeaconArray::~BeaconArray() +{ + SAFE_DELETEA(pBeaconPos); + SAFE_RELEASE(pVB); + gc->clbkReleaseTexture(pBright); +} + + +// =========================================================================================== +// +void BeaconArray::Update(DWORD nCount, vPlanet *vP) +{ + if (nCount>nVert) nCount = nVert; + double meanelev = vP->GetSize(); + BAVERTEX *pVrt = LockVertexBuffer(); + if (!pVrt) return; + for (DWORD i=0;iGetElevation(pBeaconPos[bidx].lng, pBeaconPos[bidx].lat, &elv)==1) { + VECTOR3 vLoc = pBeaconPos[bidx].vLoc * (meanelev+elv); + vB->FromLocal(vLoc, &pVrt[bidx].pos); + } + bidx++; if (bidx>=nVert) bidx=0; + } + UnLockVertexBuffer(); +} + + +// =========================================================================================== +// +BAVERTEX * BeaconArray::LockVertexBuffer() +{ + if (!pVB) return NULL; + BAVERTEX *pVert; + if (pVB->Lock(0, nVert*sizeof(BAVERTEX), (LPVOID*)&pVert, 0)==S_OK) return pVert; + return NULL; +} + + +// =========================================================================================== +// +void BeaconArray::UnLockVertexBuffer() +{ + if (!pVB) return; + HR(pVB->Unlock()); +} + + +// =========================================================================================== +// +void BeaconArray::Render(LPDIRECT3DDEVICE9 dev, const FMATRIX4* pW, float time) +{ + if (!pVB) return; + + UINT numPasses = 0; + HR(FX->SetTechnique(eBeaconArrayTech)); + HR(FX->SetMatrix(eW, _DX(pW))); + HR(FX->SetTexture(eTex0, SURFACE(pBright)->GetTexture())); + HR(FX->SetFloat(eTime, time)); + HR(FX->SetFloat(eMix, float(Config->RwyBrightness))); + + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + + //dev->SetRenderState(D3DRS_ZENABLE, 0); + + dev->SetVertexDeclaration(pBAVertexDecl); + dev->SetStreamSource(0, pVB, 0, sizeof(BAVERTEX)); + dev->DrawPrimitive(D3DPT_POINTLIST, 0, nVert); + + dev->SetRenderState(D3DRS_ZENABLE, 1); + + HR(FX->EndPass()); + HR(FX->End()); + + dev->SetRenderState(D3DRS_POINTSPRITEENABLE, 0); +} diff --git a/OVP/VulkanClient/BeaconArray.h b/OVP/VulkanClient/BeaconArray.h new file mode 100644 index 000000000..d6b5e6bb6 --- /dev/null +++ b/OVP/VulkanClient/BeaconArray.h @@ -0,0 +1,82 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + + +#ifndef __BEACONARRAY_H +#define __BEACONARRAY_H + +#include "Client.h" +#include "Effect.h" +#include +#include "MathAPI.h" + + +/** + * \brief BeaconArrayEntry structure describing one individual beacon light + */ +typedef struct { + VECTOR3 pos; ///< Beacon position relative to visual origin (base origin) + VECTOR3 dir; ///< Light direction. (1,0,0)=South (0,1,0)=Up (0,0,1)=East + DWORD color; ///< Light color + float size; ///< Size of the beacon in meters + float angle; ///< Light cone angle in degrees + float lon; ///< Light on time + float loff; ///< Light off time if (lon=0, loff=1) light always on + float bright; ///< Light brightness factor. 1.0 = default + float fall; ///< Spotlight falloff speed 2.0 to 0.1; +} BeaconArrayEntry; + +typedef struct { + double lng, lat; + VECTOR3 vLoc; +} BeaconPos; + + +/** + * \brief BeaconArray object with vk-specific vertex buffer + */ +class BeaconArray : private vkEffect +{ + +public: + // Disable copy construct & copy assign + BeaconArray (BeaconArray const&) = delete; + BeaconArray & operator= (BeaconArray const&) = delete; + + /** + * \brief Create a BeaconArray object for rendering multiple beacons at the same time + * \param pArray Pointer into a BeaconArrayEntry list. + * \param nArray Number of entries in the array + */ + BeaconArray(BeaconArrayEntry *pArray, DWORD nArray, class vBase *vP=NULL); + ~BeaconArray(); + + void UnLockVertexBuffer(); ///< Unlocks the vertex buffer after manipulation is finished + BAVERTEX * LockVertexBuffer(); ///< Locks the vertex buffer for manipulation + + /** + * \brief Render all beacons. + * \param dev Pointer to the Direct 3D 9 device + * \param pW 3DX matrix to operate on + * \param time Seconds-only part of the simulation elapsed time (0...1.0) + */ + void Render(LPDIRECT3DDEVICE9 dev, const FMATRIX4* pW, float time=0.5f); + + void Update(DWORD nCount, class vPlanet *vP); + +private: + + DWORD nVert; ///< Number of beacons + DWORD bidx; ///< Update index + LPDIRECT3DVERTEXBUFFER9 pVB; ///< Vertex buffer pointer + SURFHANDLE pBright; ///< vkRwyLight.dds texture handle + OBJHANDLE hBase; + class vBase *vB; + BeaconPos *pBeaconPos; + double base_elev; +}; + +#endif // !__BEACONARRAY_H diff --git a/OVP/VulkanClient/CMakeLists.txt b/OVP/VulkanClient/CMakeLists.txt new file mode 100644 index 000000000..34edaba82 --- /dev/null +++ b/OVP/VulkanClient/CMakeLists.txt @@ -0,0 +1,269 @@ +# set the project name and version +project(VulkanClient VERSION 0.0) + +configure_file(ClientConfig.h.in ClientConfig.h) + +# specify the C++ standard +# set(CMAKE_CXX_STANDARD 11) +# set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(DXSDK_LIB_DIR ${DXSDK_DIR}/lib/${ARCH}) +set(DXSDK_LIB_DIR2 "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.19041.0/um/${ARCH}") #Must acquire d3d9.lib from here + +set(VULKAN_SOURCE_DIR ${CMAKE_SOURCE_DIR}/OVP/VulkanClient) +set(ShaderDir ${VULKAN_SOURCE_DIR}/shaders) +set(INCLUDE_TARGET_DIR ${ORBITER_BINARY_SDK_DIR}/include) + +set(InterfaceBuilder ${ORBITER_SOURCE_ROOT_DIR}/OVP/D3D9Client/samples/InterfaceBuilder/InterfaceBuilder.exe) + +if (${ARCH} STREQUAL "x64") + set(VULKAN_LIB_DIR "${VULKAN_DIR}/Lib") +else() + set(VULKAN_LIB_DIR "${VULKAN_DIR}/Lib32") +endif() + +set(SourceFiles + AABBUtil.cpp + AtmoControls.cpp + BeaconArray.cpp + CelSphere.cpp + CloudMgr.cpp + Cloudmgr2.cpp + CSphereMgr.cpp + Client.cpp + Config.cpp + ControlPanel.cpp + Effect.cpp + Frame.cpp + Pad.cpp + Pad2.cpp + Pad3.cpp + Surface.cpp + TextMgr.cpp + Util.cpp + DebugControls.cpp + gcCore.cpp + gcConst.cpp + GDIPad.cpp + HazeMgr.cpp + IProcess.cpp + Junction.cpp + Log.cpp + MaterialMgr.cpp + Mesh.cpp + MeshMgr.cpp + OapiExtension.cpp + Particle.cpp + RingMgr.cpp + RunwayLights.cpp + Scene.cpp + Spherepatch.cpp + SurfMgr.cpp + Surfmgr2.cpp + TileLabel.cpp + TileMgr.cpp + Tilemgr2.cpp + VBase.cpp + VideoTab.cpp + VObject.cpp + VPlanet.cpp + VPlanetAtmo.cpp + VStar.cpp + VVessel.cpp + WindowMgr.cpp + ZTreeMgr.cpp + Tilemgr2_imp.hpp +) + +set(IncludeFiles + AABBUtil.h + AtmoControls.h + BeaconArray.h + CelSphere.h + CloudMgr.h + Cloudmgr2.h + CSphereMgr.h + Catalog.h + Client.h + Config.h + Effect.h + Frame.h + Pad.h + Surface.h + TextMgr.h + Util.h + DebugControls.h + GDIPad.h + HazeMgr.h + IProcess.h + Junction.h + Log.h + MaterialMgr.h + Mesh.h + MeshMgr.h + OapiExtension.h + Particle.h + Qtree.h + resource.h + RingMgr.h + RunwayLights.h + Scene.h + Spherepatch.h + SurfMgr.h + Surfmgr2.h + TileLabel.h + TileMgr.h + Tilemgr2.h + VBase.h + VectorHelpers.h + VideoTab.h + VObject.h + VPlanet.h + VStar.h + VVessel.h + WindowMgr.h + ZTreeMgr.h + gcConst.h + gcCore.h +) + +set(APIHeaders + ${INCLUDE_TARGET_DIR}/gcGUI.h + ${INCLUDE_TARGET_DIR}/gcCoreAPI.h + ${CMAKE_SOURCE_DIR}/Orbitersdk/include/MathAPI.h + ${CMAKE_SOURCE_DIR}/Orbitersdk/include/DrawAPI.h + ${CMAKE_SOURCE_DIR}/Orbitersdk/include/OrbiterAPI.h + ${CMAKE_SOURCE_DIR}/Orbitersdk/include/GraphicsAPI.h + ${EXTERN_DIR}/dxmath/Inc/DirectXMath.h + ${EXTERN_DIR}/dxmath/Inc/DirectXCollision.h + ${VULKAN_DIR}/Include/vulkan/vulkan.hpp + ${VULKAN_DIR}/Include/vulkan/vk_sdk_platform.h +) + +set(ShaderFiles + ${ShaderDir}/BeaconArray.fx + ${ShaderDir}/Common.hlsl + ${ShaderDir}/vkClient.fx + ${ShaderDir}/EnvMapBlur.hlsl + ${ShaderDir}/GDIOverlay.hlsl + ${ShaderDir}/HorizonHaze.fx + ${ShaderDir}/IPI.hlsl + ${ShaderDir}/IrradianceInteg.hlsl + ${ShaderDir}/LensFlare.hlsl + ${ShaderDir}/LightBlur.hlsl + ${ShaderDir}/CelSphere.hlsl + ${ShaderDir}/NewMesh.hlsl + ${ShaderDir}/NewPlanet.hlsl + ${ShaderDir}/Scatter.hlsl + ${ShaderDir}/Glare.hlsl + ${ShaderDir}/PreBakeLights.hlsl + ${ShaderDir}/Custom.hlsl + ${ShaderDir}/Mesh.fx + ${ShaderDir}/Metalness.fx + ${ShaderDir}/Particle.fx + ${ShaderDir}/PBR.fx + ${ShaderDir}/Planet.fx + ${ShaderDir}/SceneTech.fx + ${ShaderDir}/Sketchpad.fx + ${ShaderDir}/Vessel.fx + ${ShaderDir}/BakedVC.fx +) + +source_group(APIHeaders FILES ${APIHeaders}) + +source_group(Shaders FILES ${ShaderFiles}) + +set_property(SOURCE ${ShaderFiles} + PROPERTY VS_SETTINGS "ExcludedFromBuild=true" +) + +add_library(VulkanClient MODULE + ${SourceFiles} + ${IncludeFiles} + ${APIHeaders} + ${ShaderFiles} + Client.rc +) + +target_include_directories(VulkanClient PUBLIC + ${ORBITER_SOURCE_SDK_INCLUDE_DIR} + ${DXSDK_DIR}/Include + ${EXTERN_DIR}/dxmath/Inc + ${VULKAN_DIR}/Include +) + +target_link_directories(VulkanClient PUBLIC + ${ORBITER_BINARY_SDK_DIR}/lib + ${DXSDK_LIB_DIR2} + ${DXSDK_LIB_DIR} + ${VULKAN_LIB_DIR} +) + +target_link_libraries(VulkanClient + orbiter.lib + orbitersdk.lib + d3d9.lib + debug d3dx9d.lib + optimized d3dx9.lib + kernel32.lib + user32.lib + gdi32.lib + winspool.lib + comdlg32.lib + advapi32.lib + shell32.lib + ole32.lib + oleaut32.lib + uuid.lib + odbc32.lib + odbccp32.lib + version.lib + msimg32.lib +) + +set_target_properties(VulkanClient + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Modules/Plugin + LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Modules/Plugin + COMPILE_DEFINITIONS vkCLIENT_EXPORTS + FOLDER OVP +) + +add_custom_command( + TARGET VulkanClient PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${VULKAN_SOURCE_DIR}/shaders/ ${ORBITER_BINARY_MODULE_DIR}/vkShaders +) + +add_custom_command(OUTPUT ${INCLUDE_TARGET_DIR}/gcCoreAPI.h ${INCLUDE_TARGET_DIR}/gcGUI.h + DEPENDS ${VULKAN_SOURCE_DIR}/gcGUI.h ${VULKAN_SOURCE_DIR}/gcCore.h ${VULKAN_SOURCE_DIR}/gcCore.cpp + COMMAND ${InterfaceBuilder} ${VULKAN_SOURCE_DIR}/gcCore.h ${INCLUDE_TARGET_DIR}/gcCoreAPI.h ${VULKAN_SOURCE_DIR}/gcCore.cpp + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VULKAN_SOURCE_DIR}/gcGUI.h ${INCLUDE_TARGET_DIR}/gcGUI.h + VERBATIM +) + +add_dependencies(VulkanClient + ${OrbiterTgt} + Orbitersdk + D3D9Client_Interface +) + +install(TARGETS VulkanClient + LIBRARY + DESTINATION ${ORBITER_INSTALL_PLUGIN_DIR} +) + +# add_subdirectory(samples/DrawOrbits) +# add_subdirectory(samples/DX9ExtMFD) +# add_subdirectory(samples/GenericCamera) +# add_subdirectory(samples/TerrainToolKit) + +set(Exclude + PATTERN CMakeLists.txt EXCLUDE +) + +install(DIRECTORY ${VULKAN_SOURCE_DIR}/shaders/ DESTINATION ${ORBITER_INSTALL_ROOT_DIR}/Modules/vkShaders) +# install(DIRECTORY ${VULKAN_SOURCE_DIR}/samples/DrawOrbits/ DESTINATION ${ORBITER_INSTALL_ROOT_DIR}/Orbitersdk/samples/DrawOrbits ${Exclude}) +# install(DIRECTORY ${VULKAN_SOURCE_DIR}/samples/DX9ExtMFD/ DESTINATION ${ORBITER_INSTALL_ROOT_DIR}/Orbitersdk/samples/DX9ExtMFD ${Exclude}) +# install(DIRECTORY ${VULKAN_SOURCE_DIR}/samples/GenericCamera/ DESTINATION ${ORBITER_INSTALL_ROOT_DIR}/Orbitersdk/samples/GenericCamera ${Exclude}) +install(FILES ${INCLUDE_TARGET_DIR}/gcCoreAPI.h DESTINATION ${ORBITER_INSTALL_ROOT_DIR}/Orbitersdk/include) +install(FILES ${INCLUDE_TARGET_DIR}/gcGUI.h DESTINATION ${ORBITER_INSTALL_ROOT_DIR}/Orbitersdk/include) diff --git a/OVP/VulkanClient/CSphereMgr.cpp b/OVP/VulkanClient/CSphereMgr.cpp new file mode 100644 index 000000000..73b6a08ac --- /dev/null +++ b/OVP/VulkanClient/CSphereMgr.cpp @@ -0,0 +1,554 @@ +// ======================================================================= +// CSphereMgr: Rendering of the celestial sphere background at variable +// resolutions. +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007 - 2016 Martin Schweiger +// Copyright (C) 2011 - 2016 Jarmo Nikkanen (vkClient modification) +// ============================================================== + +#include "Util.h" +#include "CSphereMgr.h" +#include "Scene.h" +#include "Config.h" +#include "Catalog.h" +#include "Spherepatch.h" + +using namespace oapi; + + +// ======================================================================= +// Externals + +DWORD CSphereManager::vpX0, CSphereManager::vpX1, CSphereManager::vpY0, CSphereManager::vpY1; +double CSphereManager::diagscale; +int *CSphereManager::patchidx = 0; +int **CSphereManager::NLNG = 0; +int *CSphereManager::NLAT = 0; +const vkConfig *CSphereManager::cfg = NULL; + +VBMESH CSphereManager::PATCH_TPL_1; +VBMESH CSphereManager::PATCH_TPL_2; +VBMESH CSphereManager::PATCH_TPL_3; +VBMESH CSphereManager::PATCH_TPL_4[2]; +VBMESH CSphereManager::PATCH_TPL_5; +VBMESH CSphereManager::PATCH_TPL_6[2]; +VBMESH CSphereManager::PATCH_TPL_7[4]; +VBMESH CSphereManager::PATCH_TPL_8[8]; +VBMESH CSphereManager::PATCH_TPL_9[16]; +VBMESH CSphereManager::PATCH_TPL_10[32]; +VBMESH CSphereManager::PATCH_TPL_11[64]; +VBMESH CSphereManager::PATCH_TPL_12[128]; +VBMESH CSphereManager::PATCH_TPL_13[256]; +VBMESH CSphereManager::PATCH_TPL_14[512]; +VBMESH *CSphereManager::PATCH_TPL[15] = { + 0, &PATCH_TPL_1, &PATCH_TPL_2, &PATCH_TPL_3, PATCH_TPL_4, &PATCH_TPL_5, + PATCH_TPL_6, PATCH_TPL_7, PATCH_TPL_8, PATCH_TPL_9, PATCH_TPL_10, + PATCH_TPL_11, PATCH_TPL_12, PATCH_TPL_13, PATCH_TPL_14 +}; + + +void ReleaseTex(LPDIRECT3DTEXTURE9 pTex); + + +// ======================================================================= +// Class CSphereManager + +CSphereManager::CSphereManager(vkClient *gc, const Scene *scene) : gc(gc), texname(), RenderParam() +{ + scn = scene; + + gc->OutputLoadStatus("Loading Celestial Sphere...",0); + + patchidx = TileManager::patchidx; + NLNG = TileManager::NLNG; + NLAT = TileManager::NLAT; + + pShader = new ShaderClass(gc->GetDevice(), "Modules/vkShaders/CelSphere.hlsl", "CelVS", "CelPS", "CelSphere", ""); + + // Get Handles for faster access + hTexA = pShader->GetPSHandle("tTexA"); + hTexB = pShader->GetPSHandle("tTexB"); + hVSConst = pShader->GetVSHandle("Const"); + + m_bBkgImg = *(bool*)gc->GetConfigParam(CFGPRM_CSPHEREUSEBGIMAGE); + if (m_bBkgImg) { + char* c = (char*)gc->GetConfigParam(CFGPRM_CSPHERETEXTURE); + if (c[0]) strncpy(texname, c, 128); + else m_bBkgImg = false; + } + + m_bStarImg = *(bool*)gc->GetConfigParam(CFGPRM_CSPHEREUSESTARIMAGE); + if (m_bStarImg) { + char* c = (char*)gc->GetConfigParam(CFGPRM_CSPHERESTARTEXTURE); + if (c[0]) strncpy(starfieldname, c, 128); + else m_bStarImg = false; + } + + m_bDisabled = true; + if (!m_bBkgImg && !m_bStarImg) + return; + + double tmp; + tmp = *(double*)gc->GetConfigParam (CFGPRM_CSPHEREINTENS); + intensity = (float)tmp; + + maxlvl = 8; // g_pOrbiter->Cfg()->CSphereMaxLevel; + maxbaselvl = min ((DWORD)8, maxlvl); + int maxidx = patchidx[maxbaselvl]; + bPreloadTile = (Config->PlanetPreloadMode != 0); + nhitex = nhispec = 0; + + tiledesc = new TILEDESC[maxidx]; + + memset (tiledesc, 0, maxidx*sizeof(TILEDESC)); + + LoadPatchData (); + LoadTileData (); + LoadTextures (); + + // rotation from galactic to ecliptic frame + double lambda = 173.60 * RAD; + double phi = 90.03 * RAD; + double theta = 60.19 * RAD; + double sint = sin(theta), cost = cos(theta); + double sinp = sin(phi), cosp = cos(phi); + double sinl = sin(lambda), cosl = cos(lambda); + ecl2gal = _M(cosp,0,sinp, 0,1,0, -sinp,0,cosp); + ecl2gal = mul (_M(1,0,0, 0,cost,sint, 0,-sint,cost), ecl2gal); + ecl2gal = mul (_M(cosl,0,sinl, 0,1,0, -sinl,0,cosl), ecl2gal); + + oapiMatrixIdentity (&trans); + trans.m11 = float(ecl2gal.m11); + trans.m12 = float(ecl2gal.m12); + trans.m13 = float(ecl2gal.m13); + trans.m21 = float(ecl2gal.m21); + trans.m22 = float(ecl2gal.m22); + trans.m23 = float(ecl2gal.m23); + trans.m31 = float(ecl2gal.m31); + trans.m32 = float(ecl2gal.m32); + trans.m33 = float(ecl2gal.m33); + + LogAlw("CSphere Manager constructed"); +} + +// ======================================================================= + +CSphereManager::~CSphereManager () +{ + if (!m_bDisabled) { + + if (m_bBkgImg) { + for (auto tex : m_texbuf) + ReleaseTex(tex); + m_texbuf.clear(); + } + if (m_bStarImg) { + for (auto tex : m_starbuf) + ReleaseTex(tex); + m_starbuf.clear(); + } + + delete[]tiledesc; + tiledesc = NULL; + } + delete pShader; +} + +// ======================================================================= + +void CSphereManager::GlobalInit(oapi::vkClient *gclient) +{ + LogAlw("CSphereManager::GlobalInit()..."); + + LPDIRECT3DDEVICE9 dev = gclient->GetDevice(); + D3DVIEWPORT9 vp; + dev->GetViewport (&vp); + vpX0 = vp.X, vpX1 = vpX0 + vp.Width; + vpY0 = vp.Y, vpY1 = vpY0 + vp.Height; + // viewport size for clipping calculations + + diagscale = (double)vp.Width/(double)vp.Height; + diagscale = sqrt(1.0 + diagscale*diagscale); + + // Level 1 patch template + CreateSphere (dev, PATCH_TPL_1, 6, false, 0, 64); + + // Level 2 patch template + CreateSphere (dev, PATCH_TPL_2, 8, false, 0, 128); + + // Level 3 patch template + CreateSphere (dev, PATCH_TPL_3, 12, false, 0, 256); + + // Level 4 patch templates + CreateSphere (dev, PATCH_TPL_4[0], 16, true, 0, 256); + CreateSphere (dev, PATCH_TPL_4[1], 16, true, 1, 256); + + // Level 5 patch template + CreateSpherePatch (dev, PATCH_TPL_5, 4, 1, 0, 18); + + // Level 6 patch templates + CreateSpherePatch (dev, PATCH_TPL_6[0], 8, 2, 0, 10, 16); + CreateSpherePatch (dev, PATCH_TPL_6[1], 4, 2, 1, 12); + + // Level 7 patch templates + CreateSpherePatch (dev, PATCH_TPL_7[0], 16, 4, 0, 12, 12, false); + CreateSpherePatch (dev, PATCH_TPL_7[1], 16, 4, 1, 12, 12, false); + CreateSpherePatch (dev, PATCH_TPL_7[2], 12, 4, 2, 10, 16, true); + CreateSpherePatch (dev, PATCH_TPL_7[3], 6, 4, 3, 12, -1, true); + + int r = 16; + if (Config->LODBias<0) r = 8; + + // Level 8 patch templates + CreateSpherePatch (dev, PATCH_TPL_8[0], 32, 8, 0, (12*r)>>4, (15*r)>>4, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[1], 32, 8, 1, (12*r)>>4, (15*r)>>4, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[2], 30, 8, 2, (12*r)>>4, (16*r)>>4, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[3], 28, 8, 3, (12*r)>>4, (12*r)>>4, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[4], 24, 8, 4, (12*r)>>4, (12*r)>>4, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[5], 18, 8, 5, (12*r)>>4, (12*r)>>4, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[6], 12, 8, 6, (10*r)>>4, (16*r)>>4, true, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[7], 6, 8, 7, (12*r)>>4, -1, true, true, true); + + + // Patch templates for level 9 and beyond + /* + const int n = 8; + const int nlng8[8] = {32,32,30,28,24,18,12,6}; + const int res8[8] = {15,15,16,12,12,12,12,12}; + int mult = 2, idx, lvl, i, j; + for (lvl = 9; lvl <= SURF_MAX_PATCHLEVEL; lvl++) { + idx = 0; + for (i = 0; i < 8; i++) { + for (j = 0; j < mult; j++) { + if (idx < n*mult) + CreateSpherePatch (dev, PATCH_TPL[lvl][idx], nlng8[i]*mult, n*mult, idx, 12, res8[i], false, true, true, true); + else + CreateSpherePatch (dev, PATCH_TPL[lvl][idx], nlng8[i]*mult, n*mult, idx, 12, -1, true, true, true, true); + + idx++; + } + } + mult *= 2; + }*/ +} + +// ============================================================== + +void CSphereManager::GlobalExit () +{ + int i; + + ClearVBMesh(PATCH_TPL_1); + ClearVBMesh(PATCH_TPL_2); + ClearVBMesh(PATCH_TPL_3); + for (i = 0; i < 2; i++) ClearVBMesh(PATCH_TPL_4[i]); + ClearVBMesh(PATCH_TPL_5); + for (i = 0; i < 2; i++) ClearVBMesh(PATCH_TPL_6[i]); + for (i = 0; i < 4; i++) ClearVBMesh(PATCH_TPL_7[i]); + for (i = 0; i < 8; i++) ClearVBMesh(PATCH_TPL_8[i]); + + /* + const int n = 8; + int mult = 2, lvl; + for (lvl = 9; lvl <= SURF_MAX_PATCHLEVEL; lvl++) { + for (i = 0; i < n*mult; i++) DestroyVBMesh (PATCH_TPL[lvl][i]); + mult *= 2; + }*/ +} + +// ======================================================================= + +void CSphereManager::CreateDeviceObjects (LPDIRECT3D9 d3d, LPDIRECT3DDEVICE9 dev) +{ + D3DVIEWPORT9 vp; + dev->GetViewport (&vp); + vpX0 = vp.X, vpX1 = vpX0 + vp.Width; + vpY0 = vp.Y, vpY1 = vpY0 + vp.Height; + // viewport size for clipping calculations + + diagscale = (double)vp.Width/(double)vp.Height; + diagscale = sqrt(1.0 + diagscale*diagscale); +} + +// ======================================================================= + +void CSphereManager::DestroyDeviceObjects () +{ +} + +// ======================================================================= + +bool CSphereManager::LoadPatchData () +{ + // OBSOLETE + int i; + for (i = 0; i < patchidx[maxbaselvl]; i++) tiledesc[i].flag = 1; + return false; +} + +// ======================================================================= + +bool CSphereManager::LoadTileData () +{ + if (maxlvl <= 8) // no tile data required + return false; + + // TODO + return true; +} + +// ======================================================================= + +void CSphereManager::LoadTextures () +{ + int texlvl = 0, starlvl = 0; + + if (m_bBkgImg) { + // pre-load level 1-8 background image textures + char fname[256]; + strcpy(fname, texname); + strcat(fname, ".tex"); + gc->OutputLoadStatus(fname, 1); + int lvl = maxbaselvl; + int ntex = patchidx[lvl]; + m_texbuf.resize(ntex); + if (ntex = LoadPlanetTextures(fname, m_texbuf.data(), 0, ntex)) { + while (ntex < patchidx[lvl]) + --lvl; + while (ntex > patchidx[lvl]) + ReleaseTex(m_texbuf[--ntex]); + if (ntex < m_texbuf.size()) + m_texbuf.resize(ntex); + } + else { + m_texbuf.clear(); + m_bBkgImg = false; + } + texlvl = lvl; + } + + if (m_bStarImg) { + // pre-load level 1-8 starfield image textures + char fname[256]; + strcpy(fname, starfieldname); + strcat(fname, ".tex"); + gc->OutputLoadStatus(fname, 1); + int lvl = maxbaselvl; + int ntex = patchidx[lvl]; + m_starbuf.resize(ntex); + if (ntex = LoadPlanetTextures(fname, m_starbuf.data(), 0, ntex)) { + while (ntex < patchidx[lvl]) + --lvl; + while (ntex > patchidx[lvl]) + ReleaseTex(m_starbuf[--ntex]); + if (ntex < m_starbuf.size()) + m_starbuf.resize(ntex); + } + else { + m_starbuf.clear(); + m_bStarImg = false; + } + starlvl = lvl; + } + + // make sure the two texture sets support the same resolution range + if (m_bBkgImg && m_bStarImg && texlvl != starlvl) { + if (texlvl < starlvl) { + for (int i = m_texbuf.size(); i < m_starbuf.size(); i++) + ReleaseTex(m_starbuf[i]); + m_starbuf.resize(m_texbuf.size()); + starlvl = texlvl; + } + else { + for (int i = m_starbuf.size(); i < m_texbuf.size(); i++) + ReleaseTex(m_texbuf[i]); + m_texbuf.resize(m_starbuf.size()); + texlvl = starlvl; + } + } + maxbaselvl = maxlvl = (m_bBkgImg ? texlvl : m_bStarImg ? starlvl : 0); + + for (int i = 0; i < patchidx[maxbaselvl]; i++) { + if (m_bBkgImg) + tiledesc[i].tex = m_texbuf[i]; + if (m_bStarImg) + tiledesc[i].ltex = m_starbuf[i]; + } + + m_bDisabled = !m_bBkgImg && !m_bStarImg; + + // pre-load highres tile textures + if (bPreloadTile && nhitex) { + //TILEDESC *tile8 = tiledesc + patchidx[7]; + //PreloadTileTextures (tile8, nhitex, nhispec); + } + LogAlw("CSphereManager:: Textures Loaded"); +} + +// ======================================================================= + +void CSphereManager::SetBgBrightness(double val) +{ + intensity = (float)val; +} + +// ======================================================================= + +void CSphereManager::Render (LPDIRECT3DDEVICE9 dev, int level, double bglvl) +{ + if (m_bDisabled) return; + + float intens = intensity; + float bgscale = (float)exp(-bglvl * 12.5); + + if (bglvl) intens *= bgscale; + + + level = min ((DWORD)level, maxlvl); + + RenderParam.dev = dev; + RenderParam.tgtlvl = level; + RenderParam.wmat = trans; + RenderParam.viewap = atan(diagscale * tan(scn->GetCameraAperture())); + + int startlvl = min (level, 8); + int hemisp, ilat, ilng, idx; + int nlat = NLAT[startlvl]; + int *nlng = NLNG[startlvl]; + int texofs = patchidx[startlvl-1]; + TILEDESC *td = tiledesc + texofs; + TEXCRDRANGE range = {0,1,0,1}; + tilebuf = TileManager::tilebuf; + + MATRIX3 rcam; + oapiCameraRotationMatrix(&rcam); + + rcam = mul(ecl2gal, rcam); + + RenderParam.camdir = _V(rcam.m13, rcam.m23, rcam.m33); + + WaitForSingleObject (tilebuf->hQueueMutex, INFINITE); + + CelFlow.bAlpha = m_bBkgImg; + CelFlow.bBeta = m_bStarImg; + CelData.fAlpha = intens; + CelData.fBeta = bgscale; + CelData.mViewProj = *scn->GetProjectionViewMatrix(); + + pShader->Setup(pPatchVertexDecl, false, 0); + pShader->ClearTextures(); + pShader->SetPSConstants("Flow", &CelFlow, sizeof(CelFlow)); + pShader->SetPSConstants("Const", &CelData, sizeof(CelData)); + + for (hemisp = idx = 0; hemisp < 2; hemisp++) { + if (hemisp) { // flip world transformation to southern hemisphere + oapiMatrixMultiply(&RenderParam.wmat, &TileManager::Rsouth, &RenderParam.wmat); + } + for (ilat = nlat-1; ilat >= 0; ilat--) { + for (ilng = 0; ilng < nlng[ilat]; ilng++) { + ProcessTile (startlvl, hemisp, ilat, nlat, ilng, nlng[ilat], td+idx, + range, td[idx].tex, td[idx].ltex, td[idx].flag, + range, td[idx].tex, td[idx].ltex, td[idx].flag); + idx++; + } + } + } + + ReleaseMutex (tilebuf->hQueueMutex); +} + +// ======================================================================= + +void CSphereManager::ProcessTile (int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, TILEDESC *tile, + const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag, + const TEXCRDRANGE &bkp_range, LPDIRECT3DTEXTURE9 bkp_tex, LPDIRECT3DTEXTURE9 bkp_ltex, DWORD bkp_flag) +{ + + static const double rad0 = sqrt(2.0)*PI05; + VECTOR3 cnt = TileCentre (hemisp, ilat, nlat, ilng, nlng); + double rad = rad0/(double)nlat; + double alpha = acos (dotp (RenderParam.camdir, cnt)); + double adist = alpha - rad; + + if (adist > RenderParam.viewap) return; + + SetWorldMatrix (ilng, nlng, ilat, nlat); + + // Check if patch bounding box intersects viewport + + if (!TileInView (lvl, ilat)) return; + + RenderTile(lvl, hemisp, ilat, nlat, ilng, nlng, tile, range, tex, ltex, flag); +} + +// ======================================================================= + +void CSphereManager::SetWorldMatrix (int ilng, int nlng, int ilat, int nlat) +{ + // set up world transformation matrix + FMATRIX4 rtile; + + double lng = PI2 * (double)ilng/(double)nlng + PI; // add pi so texture wraps at +-180° + D3DMAT_RotY (&rtile, lng); + oapiMatrixMultiply(&mWorld, &rtile, &RenderParam.wmat); +} + +// ======================================================================= + +void CSphereManager::RenderTile (int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, + TILEDESC *tile, const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag) +{ + VBMESH &mesh = PATCH_TPL[lvl][ilat]; // patch template + + CelData.mWorld = mWorld; + + pShader->SetTexture(hTexA, tex, IPF_CLAMP | IPF_ANISOTROPIC); + pShader->SetTexture(hTexB, ltex, IPF_CLAMP | IPF_ANISOTROPIC); + pShader->SetVSConstants(hVSConst, &CelData, sizeof(CelData)); + pShader->UpdateTextures(); + + LPDIRECT3DDEVICE9 pDev = pShader->GetDevice(); + pDev->SetStreamSource(0, mesh.pVB, 0, sizeof(VERTEX_2TEX)); + pDev->SetIndices(mesh.pIB); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh.nv, 0, mesh.nf); +} + +// ======================================================================= +// returns the direction of the tile centre from the planet centre in local +// planet coordinates + +VECTOR3 CSphereManager::TileCentre (int hemisp, int ilat, int nlat, int ilng, int nlng) +{ + double cntlat = PI05 * ((double)ilat+0.5)/(double)nlat, slat = sin(cntlat), clat = cos(cntlat); + double cntlng = PI2 * ((double)ilng+0.5)/(double)nlng + PI, slng = sin(cntlng), clng = cos(cntlng); + if (hemisp) return _V(clat*clng, -slat, -clat*slng); + else return _V(clat*clng, slat, clat*slng); +} + +// ======================================================================= + +void CSphereManager::TileExtents (int hemisp, int ilat, int nlat, int ilng, int nlng, double &lat1, double &lat2, double &lng1, double &lng2) const +{ + lat1 = PI05 * (double)ilat/(double)nlat; + lat2 = lat1 + PI05/(double)nlat; + lng1 = PI2 * (double)ilng/(double)nlng + PI; + lng2 = lng1 + PI2/nlng; + if (hemisp) { + double tmp = lat1; lat1 = -lat2; lat2 = -tmp; + tmp = lng1; lng1 = -lng2; lng2 = -tmp; + if (lng2 < 0) lng1 += PI2, lng2 += PI2; + } +} + + +// ======================================================================= + +bool CSphereManager::TileInView (int lvl, int ilat) +{ + VBMESH &mesh = PATCH_TPL[lvl][ilat]; + FVECTOR3 vP = oapiTransformCoord(&mesh.bsCnt, &mWorld); + return gc->GetScene()->IsVisibleInCamera(&vP, mesh.bsRad); +} + + diff --git a/OVP/VulkanClient/CSphereMgr.h b/OVP/VulkanClient/CSphereMgr.h new file mode 100644 index 000000000..c7fefb637 --- /dev/null +++ b/OVP/VulkanClient/CSphereMgr.h @@ -0,0 +1,155 @@ +// ======================================================================= +// CSphereMgr: Rendering of the celestial sphere background at variable +// resolutions. +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2011-2016 Jarmo Nikkanen (vkClient modification) +// ======================================================================= + +#ifndef __CSPHEREMGR_H +#define __CSPHEREMGR_H + +#define STRICT 1 +#include "TileMgr.h" + +class vkConfig; + +// ======================================================================= +// Class CSphereManager + +class CSphereManager +{ +public: + +#pragma pack(push, 4) + struct CelDataStruct + { + float4x4 mWorld; + float4x4 mViewProj; + float fAlpha; + float fBeta; + } CelData; + + struct CelDataFlow + { + BOOL bAlpha; + BOOL bBeta; + } CelFlow; +#pragma pack(pop) + + /** + * \brief Constructs a new sphere manager object + * \param gclient client instance pointer + * \param scene scene to which the visual is added + */ + CSphereManager (oapi::vkClient *gclient, const Scene *scene); + + /** + * \brief Destroys the sphere manager object + */ + ~CSphereManager (); + + /** + * \brief Set up global parameters shared by all instances + * \param gclient client instance pointer + */ + static void GlobalInit (oapi::vkClient *gclient); + + /** + * \brief Release global parameters + */ + static void GlobalExit (); + + static void CreateDeviceObjects(LPDIRECT3D9 d3d, LPDIRECT3DDEVICE9 dev); + static void DestroyDeviceObjects(); + + /** + * \brief Set the visual brightness of the background image. + * \param val brightness value (0-1) + */ + void SetBgBrightness(double val); + + void Render (LPDIRECT3DDEVICE9 dev, int level, double bglvl); + +protected: + bool LoadPatchData (); + bool LoadTileData (); + void LoadTextures (); + + void ProcessTile (int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, TILEDESC *tile, + const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag, + const TEXCRDRANGE &bkp_range, LPDIRECT3DTEXTURE9 bkp_tex, LPDIRECT3DTEXTURE9 bkp_ltex, DWORD bkp_flag); + + void RenderTile (int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, + TILEDESC *tile, const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag); + + void SetWorldMatrix (int ilng, int nlng, int ilat, int nlat); + + VECTOR3 TileCentre (int hemisp, int ilat, int nlat, int ilng, int nlng); + // returns the direction of the tile centre from the planet centre in local + // planet coordinates + + void TileExtents (int hemisp, int ilat, int nlat, int ilg, int nlng, double &lat1, double &lat2, double &lng1, double &lng2) const; + + bool TileInView (int lvl, int ilat); + // Check if specified tile intersects viewport + + static const vkConfig *cfg; // configuration parameters + const Scene *scn; + static int *patchidx; // texture offsets for different LOD levels + static VBMESH PATCH_TPL_1; + static VBMESH PATCH_TPL_2; + static VBMESH PATCH_TPL_3; + static VBMESH PATCH_TPL_4[2]; + static VBMESH PATCH_TPL_5; + static VBMESH PATCH_TPL_6[2]; + static VBMESH PATCH_TPL_7[4]; + static VBMESH PATCH_TPL_8[8]; + static VBMESH PATCH_TPL_9[16]; + static VBMESH PATCH_TPL_10[32]; + static VBMESH PATCH_TPL_11[64]; + static VBMESH PATCH_TPL_12[128]; + static VBMESH PATCH_TPL_13[256]; + static VBMESH PATCH_TPL_14[512]; + static VBMESH *PATCH_TPL[15]; + static int **NLNG; + static int *NLAT; + +private: + HANDLE hTexA, hTexB, hVSConst; + ShaderClass* pShader; + vkClient* gc; + char texname[128]; + char starfieldname[128]; + float intensity; // opacity of background image + bool m_bBkgImg; ///< background image available? + bool m_bStarImg; ///< starfield image available? + bool m_bDisabled; ///< background disabled? + DWORD maxlvl; // max. patch resolution level + DWORD maxbaselvl; // max. resolution level, capped at 8 + DWORD ntex; // total number of loaded textures for levels <= 8 + DWORD nhitex; // number of textures for levels > 8 + DWORD nhispec; // number of specular reflection masks (level > 8) + TILEDESC *tiledesc; // tile descriptors for levels 1-8 + std::vector m_texbuf; // texture buffer for surface textures (level <= 8) + std::vector m_starbuf; // texture buffer for starfield textures (level <= 8) + bool bPreloadTile; // preload high-resolution tile textures + MATRIX3 ecl2gal; // rotates from ecliptic to galactic frame + FMATRIX4 trans; // transformation from ecliptic to galactic frame + FMATRIX4 mWorld; + + TileBuffer *tilebuf; + struct RENDERPARAM { + LPDIRECT3DDEVICE9 dev; // render device + int tgtlvl; // target resolution level + FMATRIX4 wmat; // world matrix + VECTOR3 camdir; // camera direction in galactic frame + double viewap; // viewport aperture (semi-diagonal) + } RenderParam; + + static DWORD vpX0, vpX1, vpY0, vpY1; // viewport boundaries + static double diagscale; +}; + +#endif // !__CSPHEREMGR_H diff --git a/OVP/VulkanClient/Catalog.h b/OVP/VulkanClient/Catalog.h new file mode 100644 index 000000000..3e8a9531a --- /dev/null +++ b/OVP/VulkanClient/Catalog.h @@ -0,0 +1,367 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __vkCATALOG_H +#define __vkCATALOG_H + +#include +#include +#include +#include +#include +#include +#include "MathAPI.h" +#include "OrbiterAPI.h" + +template +class vkCatalog { +public: + vkCatalog () {} + ~vkCatalog () { Clear(); } + + void Add (T entry) { _data.insert(entry); } + void Clear () { _data.clear(); } +// T Seek (T entry) const { return _data.find(entry) - _data.begin(); } + size_t CountEntries () const { return _data.size(); } + bool Remove (T entry) { return _data.erase(entry) == 1; } + + typedef std::set TSet; + typedef typename TSet::iterator iterator; + typedef typename TSet::const_iterator const_iterator; + + iterator begin () const { return _data.begin(); } + iterator end () const { return _data.end(); } + const_iterator cbegin () const { return _data.cbegin(); } + const_iterator cend () const { return _data.cend(); } + +// iterator rbegin() const { return _data.rbegin(); } +// iterator rend() const { return _data.rend(); } +// const_iterator crbegin() const { return _data.crbegin(); } +// const_iterator crend() const { return _data.crend(); } + +private: + TSet _data; +}; + + +// --------------------------------------------------------------- +// Memory Manager +// --------------------------------------------------------------- + +template +class Memgr +{ +private: + + std::string name; + std::map> Fre; + std::map Rsv; + std::mutex mm; + +public: + Memgr(std::string n) : name(n) + { + } + + ~Memgr() + { +#ifdef _DEBUG + for (auto x : Fre) { + size_t size = 0; DWORD ent = 0; + for (auto y : x.second) { ent++; size += x.first; delete y; } + oapiWriteLogV("Memgr[%s] Size[%u]: Total of %u bytes in %u entries", name.c_str(), x.first, size * sizeof(T), ent); + } + if (Rsv.size() == 0) oapiWriteLogV("Memgr[%s] All clear",name.c_str()); + else for (auto x : Rsv) { + oapiWriteLogV("Memgr[%s] Leaking %u bytes", name.c_str(), x.second * sizeof(T)); + delete x.first; + } +#else + for (auto x : Fre) for (auto y : x.second) delete y; + for (auto x : Rsv) delete x.first; +#endif // DEBUG + } + + T* New(DWORD size) + { + mm.lock(); + if (Fre.find(size) != Fre.end()) { // Do we have entries of size 'size' + auto& r = Fre[size]; + if (r.size() > 0) { // Any any unused exists ? + auto p = r.front(); // Get top entry + r.pop_front(); // Remove it + Rsv[p] = size; // List it as used + mm.unlock(); + return p; + } + } + auto x = new T[size]; // Allocate new entry + Rsv[x] = size; // List it as used + mm.unlock(); + return x; + } + + void Free(T* p) + { + mm.lock(); + auto it = Rsv.find(p); // Find the entry (log2 complexity) + assert(it != Rsv.end()); + Fre[it->second].push_front(p); // Add it in a fron of free entries + Rsv.erase(it); // Remove from used (reserved) + mm.unlock(); + } + + size_t UsedSize() + { + mm.lock(); + size_t ec = 0; + for (auto x : Rsv) ec += x.second * sizeof(T); + mm.unlock(); + return ec; + } + + size_t FreeSize() + { + mm.lock(); + size_t ec = 0; + for (auto x : Fre) for (auto y : x.second) { es += x.first * sizeof(T); } + mm.unlock(); + return ec; + } +}; + + + +// --------------------------------------------------------------- +// Object Manager, Base +// --------------------------------------------------------------- + +template +class Objmgr +{ + +public: + Objmgr(LPDIRECT3DDEVICE9 pD, std::string n) : name(n), pDev(pD) { } + ~Objmgr() { + Fre.clear(); + Rsv.clear(); + } + + void CleanUp() + { + mm.lock(); +#ifdef _DEBUG + for (auto x : Fre) { + size_t size = 0; DWORD ent = 0; + for (auto y : x.second) { ent++; size += UnitSize(x.first); Delete(y); } + oapiWriteLogV("Objmgr[%s] Size[%u]: Total of %u bytes in %u entries", name.c_str(), x.first, size, ent); + } + if (Rsv.size() == 0) oapiWriteLogV("Objmgr[%s] All clear", name.c_str()); + else for (auto x : Rsv) { + oapiWriteLogV("Objmgr[%s] Leaking %u bytes", name.c_str(), UnitSize(x.second)); + Delete(x.first); + } +#else + for (auto x : Fre) for (auto y : x.second) Delete(y); + for (auto x : Rsv) Delete(x.first); +#endif // DEBUG + mm.unlock(); + } + + + T New(DWORD size) + { + mm.lock(); + auto a = Fre.find(size); + auto b = Fre.end(); + if (a != b) { // Do we have entries of size 'size' + auto& r = Fre[size]; + if (r.size() > 0) { // Any any unused exists ? + auto p = r.front(); // Get top entry + r.pop_front(); // Remove it + Rsv[p] = size; // List it as used + mm.unlock(); + return p; + } + } + auto x = Alloc(size); + Rsv[x] = size; // List it as used + mm.unlock(); + return x; + } + + void Free(T p) + { + mm.lock(); + auto it = Rsv.find(p); // Find the entry (log2 complexity) + assert(it != Rsv.end()); + Fre[it->second].push_front(p); // Add it in a fron of free entries + Rsv.erase(it); // Remove from used (reserved) + mm.unlock(); + } + + size_t UsedSize() + { + mm.lock(); + size_t s = 0; + for (auto x : Rsv) s += UnitSize(x.second); + mm.unlock(); + return s; + } + + size_t FreeSize() + { + mm.lock(); + size_t s = 0; + for (auto x : Fre) for (auto y : x.second) { s += UnitSize(x.first); } + mm.unlock(); + return s; + } + + int UsedCount() + { + mm.lock(); + int c = Rsv.size(); + mm.unlock(); + return c; + } + + int FreeCount() + { + mm.lock(); + int c = 0; + for (auto x : Fre) c += x.second.size(); + mm.unlock(); + return c; + } + +protected: + virtual T Alloc(DWORD size) { assert(false); return nullptr; }; + virtual void Delete(T x) { assert(false); }; + virtual size_t UnitSize(DWORD size) { assert(false); return size; } + LPDIRECT3DDEVICE9 pDev; + +private: + std::string name; + std::map> Fre; + std::map Rsv; + std::mutex mm; +}; + + + +// --------------------------------------------------------------- +// Tile Texture Manager +// --------------------------------------------------------------- + +template +class Texmgr : public Objmgr +{ +public: + Texmgr(LPDIRECT3DDEVICE9 pD, std::string n) : Objmgr(pD, n) { } + ~Texmgr() {} + + T New(DWORD size, D3DFORMAT Format) + { + DWORD fmt = 0; + if (Format == D3DFMT_X8B8G8R8) fmt = 1; + if (Format == D3DFMT_DXT1) fmt = 2; + if (Format == D3DFMT_DXT3) fmt = 3; + if (Format == D3DFMT_DXT5) fmt = 4; + + return Objmgr::New(size + (fmt << 16)); + } + +protected: + + T Alloc(DWORD prm) + { + LPDIRECT3DTEXTURE9 pT = nullptr; + D3DFORMAT Format = D3DFMT_A8B8G8R8; + + UINT size = prm & 0xFFFF; + UINT frmt = prm >> 16; + UINT Mips = (Config->TileMipmaps == 1) ? 6 : 1; + + if (frmt == 1) Format = D3DFMT_X8B8G8R8; + if (frmt == 2) Format = D3DFMT_DXT1; + if (frmt == 3) Format = D3DFMT_DXT3; + if (frmt == 4) Format = D3DFMT_DXT5; + + if(D3DXCreateTexture(pDev, size, size, Mips, 0, Format, D3DPOOL_DEFAULT, &pT) != S_OK) + { + oapiWriteLog("Failed to create texture for surface tile. Likely [Out of Video Memory]"); + abort(); + } + return (T)pT; + } + + void Delete(T x) { + UINT q = x->Release(); + assert(q == 0); + } + size_t UnitSize(DWORD size) { return (size & 0xFFFF) * (size & 0xFFFF); } +}; + + + +// --------------------------------------------------------------- +// Tile VertexBuffer Manager +// --------------------------------------------------------------- + +template +class Vtxmgr : public Objmgr +{ +public: + Vtxmgr(LPDIRECT3DDEVICE9 pD, std::string n) : Objmgr(pD, n) { } +protected: + T Alloc(DWORD size) + { + LPDIRECT3DVERTEXBUFFER9 pVB = nullptr; + if (pDev->CreateVertexBuffer(size * sizeof(VERTEX_2TEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &pVB, NULL) != S_OK) + { + oapiWriteLog("Failed to create vertex buffer for surface tile. Likely [Out of Video Memory]"); + abort(); + } + return (T)pVB; + } + void Delete(T x) { + UINT q = x->Release(); + assert(q == 0); + } + size_t UnitSize(DWORD size) { return size * sizeof(VERTEX_2TEX); } +}; + + + +// --------------------------------------------------------------- +// Tile IndexBuffer Manager +// --------------------------------------------------------------- + +template +class Idxmgr : public Objmgr +{ +public: + Idxmgr(LPDIRECT3DDEVICE9 pD, std::string n) : Objmgr(pD, n) { } +protected: + T Alloc(DWORD size) + { + LPDIRECT3DINDEXBUFFER9 pIB = nullptr; + if (pDev->CreateIndexBuffer(size * sizeof(WORD) * 3, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &pIB, NULL) != S_OK) + { + oapiWriteLog("Failed to create index buffer for surface tile. Likely [Out of Video Memory]"); + abort(); + } + return (T)pIB; + } + void Delete(T x) { + UINT q = x->Release(); + assert(q == 0); + } + size_t UnitSize(DWORD size) { return size * sizeof(WORD) * 3; } +}; + +#endif // !__vkEXTRA_H diff --git a/OVP/VulkanClient/CelSphere.cpp b/OVP/VulkanClient/CelSphere.cpp new file mode 100644 index 000000000..4c697e272 --- /dev/null +++ b/OVP/VulkanClient/CelSphere.cpp @@ -0,0 +1,668 @@ +// ============================================================== +// CelSphere.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +// ============================================================== +// Class CelestialSphere (implementation) +// +// This class is responsible for rendering the celestial sphere +// background (stars, constellations, grids, labels, etc.) +// ============================================================== + +#include "CelSphere.h" +#include "CSphereMgr.h" +#include "Scene.h" +#include "Config.h" +#include "Surface.h" +#include "Effect.h" +#include "Pad.h" +#include "AABBUtil.h" + +using std::min; + +#define NSEG 64 // number of segments in celestial grid lines + +using namespace oapi; + +// ============================================================== + +vkCelestialSphere::vkCelestialSphere(vkClient *gc, Scene *scene) + : oapi::CelestialSphere(gc) + , m_gc(gc) + , m_scene(scene) + , m_pDevice(gc->GetDevice()) + , maxNumVertices(gc->GetHardwareCaps()->MaxPrimitiveCount) +{ + for (auto&& vtx : m_azGridLabelVtx) + vtx = nullptr; + m_elGridLabelVtx = nullptr; + m_GridLabelIdx = nullptr; + m_GridLabelTex = nullptr; + + InitStars(); + InitConstellationLines(); + InitConstellationBoundaries(); + LoadConstellationLabels(); + AllocGrids(); + m_bkgImgMgr = new CSphereManager(gc, scene); + + m_textBlendAdditive = true; + m_mjdPrecessionChecked = -1e10; + + SURFHANDLE hSurf = m_gc->clbkLoadTexture("gridlabel.dds", 0); + if (hSurf) m_GridLabelTex = SURFACE(hSurf); + if (!m_GridLabelTex) + oapiWriteLogError("Failed to load texture gridlabel.dds"); +} + +// ============================================================== + +vkCelestialSphere::~vkCelestialSphere() +{ + ClearStars(); + m_clVtx->Release(); + m_cbVtx->Release(); + m_grdLngVtx->Release(); + m_grdLatVtx->Release(); + + for (auto vtx : m_azGridLabelVtx) + if (vtx) vtx->Release(); + if (m_elGridLabelVtx) + m_elGridLabelVtx->Release(); + if (m_GridLabelIdx) + m_GridLabelIdx->Release(); + + delete m_bkgImgMgr; + if (m_GridLabelTex) + DELETE_SURFACE(m_GridLabelTex); +} + +// ============================================================== + +void vkCelestialSphere::InitCelestialTransform() +{ + m_rotCelestial = Ecliptic_CelestialAtEpoch(); + + m_transformCelestial.m11 = (float)m_rotCelestial.m11; m_transformCelestial.m12 = (float)m_rotCelestial.m12; m_transformCelestial.m13 = (float)m_rotCelestial.m13; m_transformCelestial.m14 = 0.0f; + m_transformCelestial.m21 = (float)m_rotCelestial.m21; m_transformCelestial.m22 = (float)m_rotCelestial.m22; m_transformCelestial.m23 = (float)m_rotCelestial.m23; m_transformCelestial.m24 = 0.0f; + m_transformCelestial.m31 = (float)m_rotCelestial.m31; m_transformCelestial.m32 = (float)m_rotCelestial.m32; m_transformCelestial.m33 = (float)m_rotCelestial.m33; m_transformCelestial.m34 = 0.0f; + m_transformCelestial.m41 = 0.0f; m_transformCelestial.m42 = 0.0f; m_transformCelestial.m43 = 0.0f; m_transformCelestial.m44 = 1.0f; + + m_mjdPrecessionChecked = oapiGetSimMJD(); +} + +// ============================================================== + +bool vkCelestialSphere::LocalHorizonTransform(MATRIX3& R, FMATRIX4& T) +{ + MATRIX3 rot; + if (LocalHorizon_Ecliptic(rot)) { + R = transp(rot); + T = { + (float)R.m11, (float)R.m12, (float)R.m13, 0.0f, + (float)R.m21, (float)R.m22, (float)R.m23, 0.0f, + (float)R.m31, (float)R.m32, (float)R.m33, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + return true; + } + return false; +} + +// ============================================================== + +void vkCelestialSphere::InitStars () +{ + ClearStars(); + + if (*(bool*)m_gc->GetConfigParam(CFGPRM_CSPHEREUSESTARDOTS)) { + + const std::vector sList = LoadStars(); + m_nsVtx = sList.size(); + if (!m_nsVtx) return; + + DWORD i, j, nv, idx = 0; + + // convert star database to vertex buffers + DWORD nbuf = (m_nsVtx + maxNumVertices - 1) / maxNumVertices; // number of buffers required + m_sVtx.resize(nbuf); + for (auto it = m_sVtx.begin(); it != m_sVtx.end(); it++) { + nv = min((DWORD)maxNumVertices, m_nsVtx - idx); + m_pDevice->CreateVertexBuffer(UINT(nv * sizeof(VERTEX_XYZC)), D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &*it, NULL); + VERTEX_XYZC* vbuf; + (*it)->Lock(0, 0, (LPVOID*)&vbuf, 0); + for (j = 0; j < nv; j++) { + const oapi::CelestialSphere::StarRenderRec& rec = sList[idx]; + VERTEX_XYZC& v = vbuf[j]; + v.x = (float)rec.pos.x; + v.y = (float)rec.pos.y; + v.z = (float)rec.pos.z; + v.col = FVECTOR4(rec.col.x, rec.col.y, rec.col.z, 1.0).dword_abgr(); + idx++; + } + (*it)->Unlock(); + } + + m_starCutoffIdx = ComputeStarBrightnessCutoff(sList); + + } +} + +// ============================================================== + +void vkCelestialSphere::ClearStars() +{ + for (auto it = m_sVtx.begin(); it != m_sVtx.end(); it++) + (*it)->Release(); + m_sVtx.clear(); + m_nsVtx = 0; +} + +// ============================================================== + +int vkCelestialSphere::MapLineBuffer(const std::vector& lineVtx, LPDIRECT3DVERTEXBUFFER9& buf) const +{ + size_t nv = lineVtx.size(); + if (!nv) return 0; + + // create vertex buffer + m_pDevice->CreateVertexBuffer(sizeof(VERTEX_XYZ) * nv, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &buf, NULL); + VERTEX_XYZ* vbuf; + buf->Lock(0, 0, (LPVOID*)&vbuf, 0); + for (size_t i = 0; i < nv; i++) { + vbuf[i].x = (float)lineVtx[i].x; + vbuf[i].y = (float)lineVtx[i].y; + vbuf[i].z = (float)lineVtx[i].z; + } + buf->Unlock(); + + return nv; +} + +// ============================================================== + +void vkCelestialSphere::InitConstellationLines() +{ + m_nclVtx = MapLineBuffer(LoadConstellationLines(), m_clVtx); +} + +// ============================================================== + +void vkCelestialSphere::InitConstellationBoundaries() +{ + m_ncbVtx = MapLineBuffer(LoadConstellationBoundaries(), m_cbVtx); +} + +// ============================================================== + +void vkCelestialSphere::AllocGrids () +{ + int i, j, idx; + double lng, lat, xz, y; + VERTEX_XYZ *vbuf; + + m_pDevice->CreateVertexBuffer(sizeof(VERTEX_XYZ)*(NSEG+1)*11, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_grdLngVtx, NULL); + m_grdLngVtx->Lock (0, 0, (LPVOID*)&vbuf, 0); + for (j = idx = 0; j <= 10; j++) { + lat = (j-5)*15*RAD; + xz = cos(lat); + y = sin(lat); + for (i = 0; i <= NSEG; i++) { + lng = 2.0*PI * (double)i/(double)NSEG; + vbuf[idx].x = (float)(xz * cos(lng)); + vbuf[idx].z = (float)(xz * sin(lng)); + vbuf[idx].y = (float)y; + idx++; + } + } + m_grdLngVtx->Unlock(); + + m_pDevice->CreateVertexBuffer(sizeof(VERTEX_XYZ)*(NSEG+1)*12, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_grdLatVtx, NULL); + m_grdLatVtx->Lock (0, 0, (LPVOID*)&vbuf, 0); + for (j = idx = 0; j < 12; j++) { + lng = j*15*RAD; + for (i = 0; i <= NSEG; i++) { + lat = 2.0*PI * (double)i/(double)NSEG; + xz = cos(lat); + y = sin(lat); + vbuf[idx].x = (float)(xz * cos(lng)); + vbuf[idx].z = (float)(xz * sin(lng)); + vbuf[idx].y = (float)y; + idx++; + } + } + m_grdLatVtx->Unlock(); +} + +// ============================================================== + +void vkCelestialSphere::AllocGridLabels() +{ + VERTEX_XYZ_TEX* vbuf; + WORD* ibuf; + + const MESHHANDLE hMesh = GridLabelMesh(); + MESHGROUP* grp = oapiMeshGroup(hMesh, 0); + + // create vertex buffers for longitude labels (azimuth/hour angle/longitude) + for (size_t idx = 0; idx < m_azGridLabelVtx.size(); idx++) { + LPDIRECT3DVERTEXBUFFER9& vb = m_azGridLabelVtx[idx]; + m_pDevice->CreateVertexBuffer(sizeof(VERTEX_XYZ_TEX) * grp->nVtx, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &vb, NULL); + vb->Lock(0, 0, (LPVOID*)&vbuf, 0); + for (int i = 0; i < grp->nVtx; i++) { + vbuf[i].x = (float)grp->Vtx[i].x; + vbuf[i].y = (float)grp->Vtx[i].y; + vbuf[i].z = (float)grp->Vtx[i].z; + vbuf[i].tu = (float)(grp->Vtx[i].tu + idx * 0.1015625); + vbuf[i].tv = (float)grp->Vtx[i].tv; + } + vb->Unlock(); + } + + // the index list is used for both azimuth and elevation grid labels + m_pDevice->CreateIndexBuffer(sizeof(WORD) * grp->nIdx, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_GridLabelIdx, NULL); + m_GridLabelIdx->Lock(0, 0, (LPVOID*)&ibuf, 0); + memcpy(ibuf, grp->Idx, sizeof(WORD) * grp->nIdx); + m_GridLabelIdx->Unlock(); + + // create vertex buffer for latitude labels (just one shared between all grids) + grp = oapiMeshGroup(hMesh, 1); + m_pDevice->CreateVertexBuffer(sizeof(VERTEX_XYZ_TEX) * grp->nVtx, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_elGridLabelVtx, NULL); + m_elGridLabelVtx->Lock(0, 0, (LPVOID*)&vbuf, 0); + for (int i = 0; i < grp->nVtx; i++) { + vbuf[i].x = (float)grp->Vtx[i].x; + vbuf[i].y = (float)grp->Vtx[i].y; + vbuf[i].z = (float)grp->Vtx[i].z; + vbuf[i].tu = (float)grp->Vtx[i].tu; + vbuf[i].tv = (float)grp->Vtx[i].tv; + } + m_elGridLabelVtx->Unlock(); +} + +// ============================================================== + +void vkCelestialSphere::OnOptionChanged(DWORD cat, DWORD item) +{ + switch (cat) { + case OPTCAT_CELSPHERE: + switch (item) { + case OPTITEM_CELSPHERE_ACTIVATESTARDOTS: + case OPTITEM_CELSPHERE_STARDISPLAYPARAM: + InitStars(); + break; + case OPTITEM_CELSPHERE_ACTIVATESTARIMAGE: + case OPTITEM_CELSPHERE_STARIMAGECHANGED: + case OPTITEM_CELSPHERE_ACTIVATEBGIMAGE: + case OPTITEM_CELSPHERE_BGIMAGECHANGED: + delete m_bkgImgMgr; + m_bkgImgMgr = new CSphereManager(m_gc, m_scene); + break; + case OPTITEM_CELSPHERE_BGIMAGEBRIGHTNESS: + if (m_bkgImgMgr) { + double intens = *(double*)m_gc->GetConfigParam(CFGPRM_CSPHEREINTENS); + m_bkgImgMgr->SetBgBrightness(intens); + } + break; + } + break; + } +} + +// ============================================================== + +void vkCelestialSphere::Render(LPDIRECT3DDEVICE9 pDevice, const VECTOR3& skyCol) +{ + SetSkyColour(skyCol); + + // Get celestial sphere render flags + DWORD renderFlag = *(DWORD*)m_gc->GetConfigParam(CFGPRM_PLANETARIUMFLAG); + + // celestial sphere background image + RenderBkgImage(pDevice); + + if (renderFlag & PLN_ENABLE) { + + HR(s_FX->SetTechnique(s_eLine)); + HR(s_FX->SetMatrix(s_eWVP, _DX(m_scene->GetProjectionViewMatrix()))); + + // render ecliptic grid + if (renderFlag & PLN_EGRID) { + FVECTOR4 baseCol1(0.0f, 0.2f, 0.3f, 1.0f); + FVECTOR4 vColor1 = ColorAdjusted(baseCol1); + HR(s_FX->SetVector(s_eColor, _DX(vColor1))); + RenderGrid(s_FX, false); + FVECTOR4 baseCol2(0.0f, 0.4f, 0.6f, 1.0f); + FVECTOR4 vColor2 = ColorAdjusted(baseCol2); + HR(s_FX->SetVector(s_eColor, _DX(vColor2))); + RenderGreatCircle(s_FX); + MATRIX3 ident = _M(1, 0, 0, 0, 1, 0, 0, 0, 1); + double dphi = ElevationScaleRotation(ident); + RenderGridLabels(s_FX, 2, baseCol2, ident, dphi); + } + + // render galactic grid + if (renderFlag & PLN_GGRID) { + static const MATRIX3& R = Ecliptic_Galactic(); + static FMATRIX4 T = { (float)R.m11, (float)R.m12, (float)R.m13, 0.0f, + (float)R.m21, (float)R.m22, (float)R.m23, 0.0f, + (float)R.m31, (float)R.m32, (float)R.m33, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + FMATRIX4 rot; + oapiMatrixMultiply(&rot, &T, m_scene->GetProjectionViewMatrix()); + HR(s_FX->SetMatrix(s_eWVP, _DX(rot))); + FVECTOR4 baseCol1(0.3f, 0.0f, 0.0f, 1.0f); + FVECTOR4 vColor1 = ColorAdjusted(baseCol1); + HR(s_FX->SetVector(s_eColor, _DX(vColor1))); + RenderGrid(s_FX, false); + FVECTOR4 baseCol2(0.7f, 0.0f, 0.0f, 1.0f); + FVECTOR4 vColor2 = ColorAdjusted(baseCol2); + HR(s_FX->SetVector(s_eColor, _DX(vColor2))); + RenderGreatCircle(s_FX); + double dphi = ElevationScaleRotation(R); + RenderGridLabels(s_FX, 2, baseCol2, R, dphi); + } + + // render celestial grid + if (renderFlag & PLN_CGRID) { + if (fabs(m_mjdPrecessionChecked - oapiGetSimMJD()) > 1e3) + InitCelestialTransform(); + FMATRIX4 rot; + oapiMatrixMultiply(&rot, &m_transformCelestial, m_scene->GetProjectionViewMatrix()); + HR(s_FX->SetMatrix(s_eWVP, _DX(rot))); + FVECTOR4 baseCol1(0.3f, 0.0f, 0.3f, 1.0f); + FVECTOR4 vColor1 = ColorAdjusted(baseCol1); + HR(s_FX->SetVector(s_eColor, _DX(vColor1))); + RenderGrid(s_FX, false); + FVECTOR4 baseCol2(0.7f, 0.0f, 0.7f, 1.0f); + FVECTOR4 vColor2 = ColorAdjusted(baseCol2); + HR(s_FX->SetVector(s_eColor, _DX(vColor2))); + RenderGreatCircle(s_FX); + double dphi = ElevationScaleRotation(m_rotCelestial); + RenderGridLabels(s_FX, 1, baseCol2, m_rotCelestial, dphi); + } + + // render local horizon grid + if (renderFlag & PLN_HGRID) { + MATRIX3 R; + FMATRIX4 T, rot; + if (LocalHorizonTransform(R, T)) { + oapiMatrixMultiply(&rot, &T, m_scene->GetProjectionViewMatrix()); + HR(s_FX->SetMatrix(s_eWVP, _DX(rot))); + oapi::FVECTOR4 baseCol1(0.2f, 0.2f, 0.0f, 1.0f); + FVECTOR4 vColor1 = ColorAdjusted(baseCol1); + HR(s_FX->SetVector(s_eColor, _DX(vColor1))); + RenderGrid(s_FX, false); + oapi::FVECTOR4 baseCol2(0.5f, 0.5f, 0.0f, 1.0f); + FVECTOR4 vColor2 = ColorAdjusted(baseCol2); + HR(s_FX->SetVector(s_eColor, _DX(vColor2))); + RenderGreatCircle(s_FX); + double dphi = ElevationScaleRotation(R); + RenderGridLabels(s_FX, 0, baseCol2, R, dphi); + } + } + + // render equator of target celestial body + if (renderFlag & PLN_EQU) { + OBJHANDLE hRef = oapiCameraProxyGbody(); + if (hRef) { + MATRIX3 R; + oapiGetRotationMatrix(hRef, &R); + FMATRIX4 iR = { + (float)R.m11, (float)R.m21, (float)R.m31, 0.0f, + (float)R.m12, (float)R.m22, (float)R.m32, 0.0f, + (float)R.m13, (float)R.m23, (float)R.m33, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + FMATRIX4 rot; + oapiMatrixMultiply(&rot, &iR, m_scene->GetProjectionViewMatrix()); + HR(s_FX->SetMatrix(s_eWVP, _DX(rot))); + FVECTOR4 baseCol(0.0f, 0.6f, 0.0f, 1.0f); + FVECTOR4 vColor = ColorAdjusted(baseCol); + HR(s_FX->SetVector(s_eColor, _DX(vColor))); + RenderGreatCircle(s_FX); + } + } + + // render constellation boundaries ---------------------------------------- + if (renderFlag & PLN_CNSTBND) { // for now, hijack the constellation line flag + HR(s_FX->SetMatrix(s_eWVP, _DX(m_scene->GetProjectionViewMatrix()))); + RenderConstellationBoundaries(s_FX); + } + + // render constellation lines --------------------------------------------- + if (renderFlag & PLN_CONST) { + HR(s_FX->SetMatrix(s_eWVP, _DX(m_scene->GetProjectionViewMatrix()))); + RenderConstellationLines(s_FX); + } + } + + // render stars + HR(s_FX->SetTechnique(s_eStar)); + HR(s_FX->SetMatrix(s_eWVP, _DX(m_scene->GetProjectionViewMatrix()))); + RenderStars(s_FX); + + // render markers and labels + if (renderFlag & PLN_ENABLE) { + + // Sketchpad for planetarium mode labels and markers + vkPad* pSketch = m_scene->GetPooledSketchpad(0); + + if (pSketch) { + + // constellation labels -------------------------------------------------- + if (renderFlag & PLN_CNSTLABEL) { + RenderConstellationLabels((oapi::Sketchpad**)&pSketch, renderFlag & PLN_CNSTLONG); + } + // celestial marker (stars) names ---------------------------------------- + if (renderFlag & PLN_CCMARK) { + RenderCelestialMarkers((oapi::Sketchpad**)&pSketch); + } + pSketch->EndDrawing(); //SKETCHPAD_LABELS + } + } + +} + +// ============================================================== + +void vkCelestialSphere::RenderStars(ID3DXEffect *FX) +{ + _TRACE; + + if (!m_nsVtx) return; // nothing to do + + // render in chunks, because some graphics cards have a limit in the + // vertex list size + UINT i, j, numPasses = 0; + int bgidx = min(255, (int)(GetSkyBrightness() * 256.0)); + int ns = m_starCutoffIdx[bgidx]; + + HR(m_pDevice->SetVertexDeclaration(pPosColorDecl)); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + for (i = j = 0; i < ns; i += maxNumVertices, j++) { + HR(m_pDevice->SetStreamSource(0, m_sVtx[j], 0, sizeof(VERTEX_XYZC))); + HR(m_pDevice->DrawPrimitive(D3DPT_POINTLIST, 0, min (ns-i, maxNumVertices))); + } + HR(FX->EndPass()); + HR(FX->End()); +} + +// ============================================================== + +void vkCelestialSphere::RenderConstellationLines(ID3DXEffect *FX) +{ + const FVECTOR4 baseCol(0.5f, 0.3f, 0.2f, 1.0f); + FVECTOR4 vColor = ColorAdjusted(baseCol); + HR(s_FX->SetVector(s_eColor, _DX(vColor))); + + _TRACE; + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + HR(m_pDevice->SetStreamSource(0, m_clVtx, 0, sizeof(VERTEX_XYZ))); + HR(m_pDevice->SetVertexDeclaration(pPositionDecl)); + HR(m_pDevice->DrawPrimitive(D3DPT_LINELIST, 0, m_nclVtx)); + HR(FX->EndPass()); + HR(FX->End()); +} + +// ============================================================== + +void vkCelestialSphere::RenderConstellationBoundaries(ID3DXEffect* FX) +{ + const FVECTOR4 baseCol(0.25f, 0.2f, 0.15f, 1.0f); + FVECTOR4 vColor = ColorAdjusted(baseCol); + HR(s_FX->SetVector(s_eColor, _DX(vColor))); + + _TRACE; + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + HR(m_pDevice->SetStreamSource(0, m_cbVtx, 0, sizeof(VERTEX_XYZ))); + HR(m_pDevice->SetVertexDeclaration(pPositionDecl)); + HR(m_pDevice->DrawPrimitive(D3DPT_LINELIST, 0, m_ncbVtx)); + HR(FX->EndPass()); + HR(FX->End()); +} + +// ============================================================== + +void vkCelestialSphere::RenderGreatCircle(ID3DXEffect *FX) +{ + _TRACE; + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + HR(m_pDevice->SetStreamSource(0, m_grdLngVtx, 0, sizeof(VERTEX_XYZ))); + HR(m_pDevice->SetVertexDeclaration(pPositionDecl)); + HR(m_pDevice->DrawPrimitive(D3DPT_LINESTRIP, 5*(NSEG+1), NSEG)); + HR(FX->EndPass()); + HR(FX->End()); + +} + +// ============================================================== + +void vkCelestialSphere::RenderGrid(ID3DXEffect *FX, bool eqline) +{ + _TRACE; + int i; + UINT numPasses = 0; + HR(m_pDevice->SetVertexDeclaration(pPositionDecl)); + HR(m_pDevice->SetStreamSource(0, m_grdLngVtx, 0, sizeof(VERTEX_XYZ))); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + for (i = 0; i <= 10; i++) + if (eqline || i != 5) + HR(m_pDevice->DrawPrimitive(D3DPT_LINESTRIP, i*(NSEG+1), NSEG)); + HR(m_pDevice->SetStreamSource(0, m_grdLatVtx, 0, sizeof(VERTEX_XYZ))); + for (i = 0; i < 12; i++) + HR(m_pDevice->DrawPrimitive(D3DPT_LINESTRIP, i * (NSEG+1), NSEG)); + HR(FX->EndPass()); + HR(FX->End()); +} + +// ============================================================== + +void vkCelestialSphere::RenderGridLabels(ID3DXEffect* FX, int az_idx, const oapi::FVECTOR4& baseCol, const MATRIX3& R, double dphi) +{ + if (!m_GridLabelTex) return; + if (az_idx >= m_azGridLabelVtx.size()) return; + if (!m_azGridLabelVtx[az_idx]) + AllocGridLabels(); + + UINT numPasses = 0; + HR(m_pDevice->SetVertexDeclaration(pPosTexDecl)); + HR(m_pDevice->SetStreamSource(0, m_azGridLabelVtx[az_idx], 0, sizeof(VERTEX_XYZ_TEX))); + HR(m_pDevice->SetIndices(m_GridLabelIdx)); + HR(FX->SetTechnique(s_eLabel)); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + HR(m_pDevice->SetTexture(0, m_GridLabelTex->GetTexture())); + HR(m_pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 24 * 4, 0, 24 * 2)); + HR(FX->EndPass()); + HR(FX->End()); + + FMATRIX4 T0, T1; + if (dphi) { + FX->GetMatrix(s_eWVP, (D3DXMATRIX*)&T0); + double cosp = cos(dphi), sinp = sin(dphi); + FMATRIX4 R2 = { + (float)(cosp * R.m11 + sinp * R.m31), (float)(cosp * R.m12 + sinp * R.m32), (float)(cosp * R.m13 + sinp * R.m33), 0.0f, + (float)R.m21, (float)R.m22, (float)R.m23, 0.0f, + (float)(-sinp * R.m11 + cosp * R.m31), (float)(-sinp * R.m12 + cosp * R.m32), (float)(-sinp * R.m13 + cosp * R.m33), 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + oapiMatrixMultiply(&T1, &R2, m_scene->GetProjectionViewMatrix()); + FX->SetMatrix(s_eWVP, _DX(T1)); + } + + HR(m_pDevice->SetStreamSource(0, m_elGridLabelVtx, 0, sizeof(VERTEX_XYZ_TEX))); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + HR(m_pDevice->SetTexture(0, m_GridLabelTex->GetTexture())); + HR(m_pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 11 * 4, 0, 11 * 2)); + HR(FX->EndPass()); + HR(FX->End()); + + HR(FX->SetTechnique(s_eLine)); // Restore default tech + + if (dphi) + FX->SetMatrix(s_eWVP, _DX(T0)); +} + +// ============================================================== + +void vkCelestialSphere::RenderBkgImage(LPDIRECT3DDEVICE9 dev) +{ + m_bkgImgMgr->Render(dev, 8, GetSkyBrightness()); + dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); +} + +// ============================================================== + +bool vkCelestialSphere::EclDir2WindowPos(const VECTOR3& dir, int& x, int& y) const +{ + FVECTOR3 fdir((float)dir.x, (float)dir.y, (float)dir.z); + FVECTOR3 homog = oapiTransformCoord(&fdir, m_scene->GetProjectionViewMatrix()); + + if (homog.x >= -1.0f && homog.x <= 1.0f && + homog.y >= -1.0f && homog.y <= 1.0f && + homog.z < 1.0f) { + + if (std::hypot(homog.x, homog.y) < 1e-6) { + x = m_scene->ViewW() / 2; + y = m_scene->ViewH() / 2; + } + else { + x = (int)(m_scene->ViewW() * 0.5 * (1.0 + homog.x)); + y = (int)(m_scene->ViewH() * 0.5 * (1.0 - homog.y)); + } + return true; + } + else { + return false; + } +} + +void vkCelestialSphere::vkTechInit(ID3DXEffect* fx) +{ + s_FX = fx; + s_eStar = fx->GetTechniqueByName("StarTech"); + s_eLine = fx->GetTechniqueByName("LineTech"); + s_eLabel = fx->GetTechniqueByName("LabelTech"); + s_eColor = fx->GetParameterByName(0, "gColor"); + s_eWVP = fx->GetParameterByName(0, "gWVP"); +} + +ID3DXEffect* vkCelestialSphere::s_FX = 0; +D3DXHANDLE vkCelestialSphere::s_eStar = 0; +D3DXHANDLE vkCelestialSphere::s_eLine = 0; +D3DXHANDLE vkCelestialSphere::s_eLabel = 0; +D3DXHANDLE vkCelestialSphere::s_eColor = 0; +D3DXHANDLE vkCelestialSphere::s_eWVP = 0; diff --git a/OVP/VulkanClient/CelSphere.h b/OVP/VulkanClient/CelSphere.h new file mode 100644 index 000000000..032129309 --- /dev/null +++ b/OVP/VulkanClient/CelSphere.h @@ -0,0 +1,191 @@ +// ============================================================== +// CelSphere.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// ============================================================== + +#ifndef __vkCELSPHERE_H +#define __vkCELSPHERE_H + +#include "CelSphereAPI.h" +#include "Client.h" +#include "Util.h" + + +// ============================================================== +// Class CelestialSphere (interface) +// ============================================================== + +/** + * \brief Rendering methods for the background celestial sphere. + * + * Loads star and constellation information from data bases and uses them to + * render the celestial sphere background (stars, constellations, grids, + * labels, etc.) + */ +class vkCelestialSphere : public oapi::CelestialSphere { + +public: + /** + * \brief Create a new celestial sphere object. + * \param gc pointer to graphics client + */ + explicit vkCelestialSphere(oapi::vkClient *gc, Scene* scene); + + /** + * \brief Destructor + */ + ~vkCelestialSphere(); + + /** + * \brief Notification of in-simulation user option change. + * \param cat option category, see \ref optcat + * \param item option item, see \ref optitem + */ + void OnOptionChanged(DWORD cat, DWORD item); + + /** + * \brief Render the celestial sphere background. + * \param pDevice pointer to graphics device + * \param skyCol sky background colour (atmospheric tint) + */ + void Render(LPDIRECT3DDEVICE9 pDevice, const VECTOR3& skyCol); + + /** + * \brief Render stars as pixels on the celestial sphere + * \param fx render effect + * \param nmax max. number of stars (default is all available stars) + * \note if a background colour is passed into this function, the rendering + * of stars darker than the background is suppressed. + * \note All device parameters are assumed to be set correctly on call. + */ + void RenderStars(ID3DXEffect *fx); + + /** + * \brief Render constellation lines on the celestial sphere + * \param fx render effect + * \note All device parameters are assumed to be set correctly on call. + * \note Suggestion: render additively onto background, so that the lines + * are never darker than the background sky. + */ + void RenderConstellationLines(ID3DXEffect *fx); + + /** + * \brief Render constellation boundaries on the celestial sphere + * \param dev render device + * \note All device parameters are assumed to be set correctly on call. + * \note Suggestion: render additively onto background, so that the lines + * are never darker than the background sky. + */ + void RenderConstellationBoundaries(ID3DXEffect* fx); + + /** + * \brief Render a great circle on the celestial sphere in a given colour. + * \param fx render effect + * \note By default (i.e. for identity world matrix), the circle is + * drawn along the plane of the ecliptic. To render a circle in any + * other orientation, the world matrix must be adjusted accordingly + * before the call. + */ + void RenderGreatCircle(ID3DXEffect *fx); + + /** + * \brief Render grid lines on the celestial sphere in a given colour. + * \param fx render effect + * \param eqline indicates if the equator line should be drawn + * \note By default (i.e. for identity world matrix), this draws the + * ecliptic grid. To render a grid for any other reference frame, + * the world matrix must be adjusted accordingly before call. + * \note if eqline==false, then the latitude=0 line is not drawn. + * this is useful if the line should be drawn in a different colour + * with RenderGreatCircle(). + */ + void RenderGrid(ID3DXEffect *fx, bool eqline = true); + + void RenderGridLabels(ID3DXEffect* FX, int az_idx, const oapi::FVECTOR4& baseCol, const MATRIX3& R, double dphi); + + /** + * \brief Render a background image on the celestial sphere. + * \param dev render device + */ + void RenderBkgImage(LPDIRECT3DDEVICE9 dev); + + static void vkTechInit(ID3DXEffect* fx); + +protected: + /** + * \brief Prepare the star vertex list from the star database. + */ + void InitStars (); + + /** + * \brief Free the vertex buffers for star pixel rendering + */ + void ClearStars(); + + /** + * \brief Load constellation line data from file + */ + void InitConstellationLines (); + + /** + * \brief Map constellation boundary database to vertex buffer. + */ + void InitConstellationBoundaries(); + + /** + * \brief Allocate vertex list for rendering grid lines + * (e.g. celestial or ecliptic) + */ + void AllocGrids(); + + void AllocGridLabels(); + + void InitCelestialTransform(); + + bool LocalHorizonTransform(MATRIX3& R, FMATRIX4& T); + + /** + * \brief Convert a direction into viewport coordinates + * \param dir direction in the ecliptic frame provided as a point on the + * celestial sphere. + * \param x x-position in the viewport window [pixel] + * \param y y-position in the viewport window [pixel] + * \return true if point is visible in the viewport, false otherwise. + */ + virtual bool EclDir2WindowPos(const VECTOR3& dir, int& x, int& y) const; + + int MapLineBuffer(const std::vector& lineVtx, LPDIRECT3DVERTEXBUFFER9& buf) const; + +private: + oapi::vkClient *m_gc; ///< pointer to graphics client + CSphereManager* m_bkgImgMgr; ///< background image manager + Scene* m_scene; ///< pointer to scene object + LPDIRECT3DDEVICE9 m_pDevice; ///< DirectX9 device + UINT maxNumVertices; ///< number of vertices to use for one chunk at star-drawing + DWORD m_nsVtx; ///< total number of vertices over all buffers + std::vector m_sVtx; ///< star vertex buffers + std::array m_starCutoffIdx; ///< list of star render cutoff indices + DWORD m_nclVtx; ///< number of constellation line vertices + LPDIRECT3DVERTEXBUFFER9 m_clVtx; ///< constellation line vertex buffer + DWORD m_ncbVtx; ///< number of constellation boundary vertices + LPDIRECT3DVERTEXBUFFER9 m_cbVtx; ///< vertex buffer for constellation boundaries + LPDIRECT3DVERTEXBUFFER9 m_grdLngVtx, m_grdLatVtx; ///< vertex buffers for grid lines + lpSurfNative m_GridLabelTex; ///< texture for grid labels + std::array m_azGridLabelVtx; ///< vertex buffers for azimuth grid labels + LPDIRECT3DVERTEXBUFFER9 m_elGridLabelVtx; ///< vertex buffer for elevation grid labels + LPDIRECT3DINDEXBUFFER9 m_GridLabelIdx; ///< index list for azimuth/elevation grid labels + MATRIX3 m_rotCelestial; ///< rotation matrix for celestial grid rendering + FMATRIX4 m_transformCelestial; ///< rotation for celestial grid rendering + double m_mjdPrecessionChecked; + + static ID3DXEffect* s_FX; + static D3DXHANDLE s_eStar; + static D3DXHANDLE s_eLine; + static D3DXHANDLE s_eLabel; + static D3DXHANDLE s_eColor; + static D3DXHANDLE s_eWVP; +}; + +#endif // !__vkCELSPHERE_H diff --git a/OVP/VulkanClient/Client.cpp b/OVP/VulkanClient/Client.cpp new file mode 100644 index 000000000..3e1014375 --- /dev/null +++ b/OVP/VulkanClient/Client.cpp @@ -0,0 +1,3348 @@ +// ============================================================== +// vkClient.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + + +#define STRICT 1 +#define ORBITER_MODULE + +#include // ...for Brush-, Pen- and Font-accounting +#include "Orbitersdk.h" +#include "Client.h" +#include "Config.h" +#include "Util.h" +#include "Catalog.h" +#include "Surface.h" +#include "TextMgr.h" +#include "Frame.h" +#include "Pad.h" +#include "CSphereMgr.h" +#include "Scene.h" +#include "Mesh.h" +#include "VVessel.h" +#include "VStar.h" +#include "MeshMgr.h" +#include "Particle.h" +#include "TileMgr.h" +#include "RingMgr.h" +#include "HazeMgr.h" +#include "Log.h" +#include "VideoTab.h" +#include "GDIPad.h" +#include "OapiExtension.h" +#include "DebugControls.h" +#include "Surfmgr2.h" +#include "gcCore.h" +#include "gcConst.h" +#include +#include + +#include +#include +#include + +#if defined(_MSC_VER) && (_MSC_VER <= 1700 ) // Microsoft Visual Studio Version 2012 and lower +#define round(v) floor(v+0.5) +#endif + +// ============================================================== +// Structure definitions + +struct vkClient::RenderProcData { + __gcRenderProc proc; + void *pParam; + DWORD id; +}; + +struct vkClient::GenericProcData { + __gcGenericProc proc; + void *pParam; + DWORD id; +}; + +using namespace oapi; + +HINSTANCE g_hInst = 0; +vkClient *g_client = 0; +class gcConst* g_pConst = 0; +IDirect3D9* g_pD3DObject = 0; // Made valid when VideoTab is created + +typedef IDirect3D9* (__stdcall* __Direct3DCreate9On12)(UINT SDKVersion, D3D9ON12_ARGS* pOverrideList, UINT NumOverrideEntries); + + +// Resource tracking +// +Memgr* g_pMemgr_f = nullptr; +Memgr* g_pMemgr_i = nullptr; +Memgr* g_pMemgr_u = nullptr; +Memgr* g_pMemgr_w = nullptr; +Memgr* g_pMemgr_vtx = nullptr; +Texmgr* g_pTexmgr_tt = nullptr; +Vtxmgr* g_pVtxmgr_vb = nullptr; +Idxmgr* g_pIdxmgr_ib = nullptr; +set MeshCatalog; +set SurfaceCatalog; +unordered_map SharedTextures; +unordered_map ClonedTextures; +unordered_map MeshMap; +unordered_map MicroTextures; +std::set g_fonts; +std::set g_pens; +std::set g_brushes; + + + +DWORD g_uCurrentMesh = 0; +vObject *g_pCurrentVisual = nullptr; +_vkStats vkStats; + +#ifdef _NVAPI_H + StereoHandle pStereoHandle = 0; +#endif + +bool bFreeze = false; +bool bFreezeEnable = false; +bool bFreezeRenderAll = false; + + + +extern list g_gcGUIAppList; + +extern "C" { + _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +} + +// ============================================================== +// API interface +// ============================================================== + +// ============================================================== +// Initialise module + +DLLCLBK void InitModule(HINSTANCE hDLL) +{ + +#ifdef _DEBUG + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + // _CrtSetBreakAlloc(8351); + + assert(sizeof(FVECTOR4) == 16); + assert(sizeof(FVECTOR4::data) == 16); + + assert(sizeof(FVECTOR4::rgb) == 12); + assert(sizeof(FVECTOR4::xyz) == 12); + + auto dut = FVECTOR4(1.2, 3.4, 5.6, 7.8); + assert(dut.data[0] == dut.r); + assert(dut.data[1] == dut.g); + assert(dut.data[2] == dut.b); + assert(dut.data[3] == dut.a); + + assert(dut.data[0] == dut.x); + assert(dut.data[1] == dut.y); + assert(dut.data[2] == dut.z); + assert(dut.data[3] == dut.w); + + assert(dut.rgb.x == dut.xyz.x); + assert(dut.rgb.y == dut.xyz.y); + assert(dut.rgb.z == dut.xyz.z); + + assert(dut.a == dut.w); + + // Check that 'a' and 'w' are placed behind 'rgb' rsp. 'xyz' and unaffected + dut.rgb = 0; + assert(dut.a == dut.w); +#endif + + vkInitLog("vkClientLog.html"); + + g_pMemgr_f = new Memgr("float"); + g_pMemgr_i = new Memgr("UINT16"); + g_pMemgr_u = new Memgr("UINT8"); + g_pMemgr_w = new Memgr("WORD"); + g_pMemgr_vtx = new Memgr("VERTEX_2TEX"); + if (!D3DXCheckVersion(D3D_SDK_VERSION, D3DX_SDK_VERSION)) { + MissingRuntimeError(); + return; + } + +#ifdef _NVAPI_H + if (NvAPI_Initialize()==NVAPI_OK) { + LogAlw("[nVidia API Initialized]"); + bNVAPI = true; + } + else LogAlw("[nVidia API Not Available]"); +#else + LogAlw("[Not Compiled With nVidia API]"); +#endif + + Config = new vkConfig(); + + if (Config->ShaderCacheUse) { + DWORD fa = GetFileAttributesA("Cache"); + if (fa == INVALID_FILE_ATTRIBUTES) CreateDirectoryA("Cache", NULL); + fa = GetFileAttributesA("Cache/vkShaders"); + if (fa == INVALID_FILE_ATTRIBUTES) CreateDirectoryA("Cache/vkShaders", NULL); + } + + g_pConst = new gcConst(); + + DebugControls::Create(); + AtmoControls::Create(); + vPlanet::ParseMicroTexturesFile(); + + vkStats.TilesRendered[RENDERPASS_MAINSCENE] = 0; + vkStats.TilesRendered[RENDERPASS_CUSTOMCAM] = 0; + vkStats.TilesRendered[RENDERPASS_ENVCAM] = 0; + + g_hInst = hDLL; + g_client = new vkClient(hDLL); + + if (oapiRegisterGraphicsClient(g_client)==false) { + delete g_client; + g_client = 0; + } +} + +// ============================================================== +// Clean up module + +DLLCLBK void ExitModule(HINSTANCE hDLL) +{ + LogVerbose("[vk] === ExitModule ==="); + + delete Config; + delete g_pConst; + + if (g_client) { + oapiUnregisterGraphicsClient(g_client); + delete g_client; + g_client = 0; + } + + DebugControls::Release(); + AtmoControls::Release(); + +#ifdef _NVAPI_H + if (bNVAPI) if (NvAPI_Unload()==NVAPI_OK) LogAlw("[nVidia API Unloaded]"); +#endif + + LogAlw("Log Closed"); + vkCloseLog(); + + SAFE_DELETE(g_pMemgr_f); + SAFE_DELETE(g_pMemgr_i); + SAFE_DELETE(g_pMemgr_u); + SAFE_DELETE(g_pMemgr_w); + SAFE_DELETE(g_pMemgr_vtx); +} + +DLLCLBK gcGUIBase * gcGetGUICore() +{ + return dynamic_cast(g_pWM); +} + + +DLLCLBK gcConst * gcGetCoreAPI() +{ + return dynamic_cast(g_pConst); +} + + + +// ============================================================== +// vkClient class implementation +// ============================================================== + +vkClient::vkClient (HINSTANCE hInstance) : + GraphicsClient(hInstance), + vtab(NULL), + scenarioName("(none selected)"), + pLoadLabel(""), + pLoadItem(""), + pDevice (NULL), + pDefaultTex (NULL), + pScatterTest(NULL), + pFramework (NULL), + pItemsSkp (NULL), + hLblFont1 (NULL), + hLblFont2 (NULL), + hMainThread (NULL), + pCaps (NULL), + pWM (NULL), + pBltSkp (NULL), + pBltGrpTgt (NULL), + pCustomSplashScreen(NULL), + pSplashTextColor(0xE0A0A0), + hRenderWnd(), + scene (), + meshmgr (), + bControlPanel (false), + bScatterUpdate(false), + bFullscreen (false), + bAAEnabled (false), + bFailed (false), + bRunning (false), + bVertexTex (false), + bVSync (false), + bRendering (false), + bGDIClear (true), + viewW (0), + viewH (0), + viewBPP (0), + frame_timer (0), + loadd_x (0), + loadd_y (0), + loadd_w (0), + loadd_h (0), + LabelPos (0) + +{ +} + +// ============================================================== + +vkClient::~vkClient() +{ + LogVerbose("[vk] === destructor called ==="); + SAFE_DELETE(vtab); + SAFE_RELEASE(g_pD3DObject); +} + + +// ============================================================== +// +bool vkClient::ChkDev(const char *fnc) const +{ + if (pDevice) return false; + LogErr("Call [%s] Failed. vk Graphics services off-line", fnc); + return true; +} + + +// ============================================================== +// Overridden +// +const void *vkClient::GetConfigParam (DWORD paramtype) const +{ + return (paramtype >= CFGPRM_TILELOADTHREAD) + ? (paramtype >= CFGPRM_GETSELECTEDMESH) + ? DebugControls::GetConfigParam(paramtype) + : OapiExtension::GetConfigParam(paramtype) + : GraphicsClient::GetConfigParam(paramtype); +} + + +// ============================================================== +// This is called only once when the launchpad will appear +// This callback will initialize the Video tab only +// +bool vkClient::clbkInitialise() +{ + _TRACE; + LogVerbose("[vk] === clbkInitialise ==="); + LogAlw("Orbiter Version = %d",oapiGetOrbiterVersion()); + + D3D9ON12_ARGS args = {}; + args.Enable9On12 = Config->Enable9On12 != 0; + + HMODULE hDXMod = GetModuleHandle("vk.dll"); + __Direct3DCreate9On12 pDirect3DCreate9On12 = nullptr; + + if (hDXMod) pDirect3DCreate9On12 = (__Direct3DCreate9On12)GetProcAddress(hDXMod, "Direct3DCreate9On12"); + + if (OapiExtension::RunsUnderWINE() || pDirect3DCreate9On12 == nullptr) { + g_pD3DObject = Direct3DCreate9(D3D_SDK_VERSION); + oapiWriteLog("[vk] Native Interface"); + } + else { + g_pD3DObject = pDirect3DCreate9On12(D3D_SDK_VERSION, &args, 1); + if (Config->Enable9On12) oapiWriteLog("[vk] DX9 emulation via DX12"); + else oapiWriteLog("[vk] Native Interface"); + } + + if (g_pD3DObject) oapiWriteLog("[vk] DirectX9 Created..."); + else { + oapiWriteLog("[vk][ERROR] Failed to create DirectX9"); + FailedDeviceError(); + return false; + } + + // Perform default setup + if (GraphicsClient::clbkInitialise()==false) return false; + + //Create the Launchpad video tab interface + oapiWriteLog("[vk] Initialize VideoTab..."); + vtab = new VideoTab(this, ModuleInstance(), OrbiterInstance(), LaunchpadVideoTab()); + return vtab->Initialise(); +} + + +// ============================================================== +// This is called when a simulation session will begin +// +HWND vkClient::clbkCreateRenderWindow() +{ + _TRACE; + + LogVerbose("[vk] === clbkCreateRenderWindow ==="); + + if (!g_pD3DObject) return NULL; + + Config->WriteParams(); + + uEnableLog = Config->DebugLvl; + pSplashScreen = NULL; + pBackBuffer = NULL; + pTextScreen = NULL; + hRenderWnd = NULL; + pDefaultTex = NULL; + hLblFont1 = NULL; + hLblFont2 = NULL; + bControlPanel = false; + bFullscreen = false; + bFailed = false; + bRunning = false; + bVertexTex = false; + viewW = viewH = 0; + viewBPP = 0; + frame_timer = 0; + scene = NULL; + meshmgr = NULL; + pFramework = NULL; + pDevice = NULL; + pBltGrpTgt = NULL; // Let's set this NULL here, constructor is called only once. Not when exiting and restarting a simulation. + pNoiseTex = NULL; + surfBltTgt = NULL; // This variable is not used, set it to NULL anyway + hMainThread = GetCurrentThread(); + + oapiMatrixIdentity(&ident); + + oapiDebugString()[0] = '\0'; + + MeshCatalog.clear(); + SurfaceCatalog.clear(); + + hRenderWnd = GraphicsClient::clbkCreateRenderWindow(); + + LogAlw("Window Handle = %s",_PTR(hRenderWnd)); + SetWindowText(hRenderWnd, "[vkClient]"); + + LogOk("Starting to initialize device and 3D environment..."); + + pFramework = new CD3DFramework9(); + + WriteLog("[DirectX 9 Initialized]"); + + HRESULT hr = pFramework->Initialize(hRenderWnd, GetVideoData()); + + if (hr!=S_OK) { + LogErr("ERROR: Failed to initialize 3D Framework"); + return NULL; + } + + RECT rect; + GetClientRect(hRenderWnd, &rect); + HDC hWnd = GetDC(hRenderWnd); + HBRUSH hBr = CreateSolidBrush(RGB(0,0,0)); + FillRect(hWnd, &rect, hBr); + DeleteObject(hBr); + ReleaseDC(hRenderWnd, hWnd); + ValidateRect(hRenderWnd, NULL); // avoids white flash after splash screen + + pCaps = pFramework->GetCaps(); + + WriteLog("[3DDevice Initialized]"); + + pDevice = pFramework->GetD3DDevice(); + viewW = pFramework->GetWidth(); + viewH = pFramework->GetHeight(); + bFullscreen = (pFramework->IsFullscreen() == TRUE); + bAAEnabled = (pFramework->IsAAEnabled() == TRUE); + viewBPP = 32; + bVertexTex = (pFramework->HasVertexTextureSup() == TRUE); + bVSync = (pFramework->GetVSync() == TRUE); + + char fld[] = "vkShaders"; + + g_pTexmgr_tt = new Texmgr(pDevice, "TileTextures"); + g_pVtxmgr_vb = new Vtxmgr(pDevice, "TileVertex"); + g_pIdxmgr_ib = new Idxmgr(pDevice, "TileIndices"); + + HR(D3DXCreateTextureFromFileA(pDevice, "Textures/D3D9Noise.dds", &pNoiseTex)); + + HR(pDevice->GetRenderTarget(0, &pBackBuffer)); + HR(pDevice->GetDepthStencilSurface(&pDepthStencil)); + + LogAlw("Render Target = %s", _PTR(pBackBuffer)); + LogAlw("DepthStencil = %s", _PTR(pDepthStencil)); + + meshmgr = new MeshManager(this); + + // Bring Sketchpad Online + vkPadFont::vkTechInit(pDevice); + vkPadPen::vkTechInit(pDevice); + vkPadBrush::vkTechInit(pDevice); + vkText::vkTechInit(this, pDevice); + vkPad::vkTechInit(this, pDevice); + + deffont = (oapi::Font*) new vkPadFont(20, true, "fixed"); + defpen = (oapi::Pen*) new vkPadPen(1, 1, 0x00FF00); + + pDefaultTex = SURFACE(clbkLoadTexture("Null.dds")); + if (pDefaultTex==NULL) LogErr("Null.dds not found"); + + int x=0; + if (viewW>1282) x=4; + + hLblFont1 = CreateFont(24+x, 0, 0, 0, 700, false, false, 0, 0, 3, 2, 1, 49, "Courier New"); + hLblFont2 = CreateFont(18+x, 0, 0, 0, 700, false, false, 0, 0, 3, 2, 1, 49, "Courier New"); + + SplashScreen(); // Warning SurfNative is not yet fully initialized here + + ShowWindow(hRenderWnd, SW_SHOW); + + OutputLoadStatus("Building Shader Programs...",0); + + vkEffect::vkTechInit(this, pDevice, fld); + + // Device-specific initialisations + + TileManager::GlobalInit(this); + TileManager2Base::GlobalInit(this); + RingManager::GlobalInit(this); + HazeManager::GlobalInit(this); + HazeManager2::GlobalInit(this); + vkParticleStream::GlobalInit(this); + CSphereManager::GlobalInit(this); + vStar::GlobalInit(this); + vObject::GlobalInit(this); + vVessel::GlobalInit(this); + vPlanet::GlobalInit(this); + OapiExtension::GlobalInit(*Config); + + OutputLoadStatus("SceneTech.fx",1); + Scene::vkTechInit(pDevice, fld); + vkMesh::GlobalInit(pDevice); + + // Create scene instance + scene = new Scene(this, viewW, viewH); + + WriteLog("[vkClient Initialized]"); + LogOk("...3D environment initialised"); + +#ifdef _NVAPI_H + if (bNVAPI) { + NvU8 bEnabled = 0; + NvAPI_Status nvStereo = NvAPI_Stereo_IsEnabled(&bEnabled); + + if (nvStereo!=NVAPI_OK) { + if (nvStereo==NVAPI_STEREO_NOT_INITIALIZED) LogWrn("Stereo API not initialized"); + if (nvStereo==NVAPI_API_NOT_INITIALIZED) LogErr("nVidia API not initialized"); + if (nvStereo==NVAPI_ERROR) LogErr("nVidia API ERROR"); + } + + if (bEnabled) { + LogAlw("[nVidia Stereo mode is Enabled]"); + if (NvAPI_Stereo_CreateHandleFromIUnknown(pDevice, &pStereoHandle)!=NVAPI_OK) { + LogErr("Failed to get StereoHandle"); + } + else { + if (pStereoHandle) { + if (NvAPI_Stereo_SetConvergence(pStereoHandle, float(Config->Convergence))!=NVAPI_OK) { + LogErr("SetConvergence Failed"); + } + if (NvAPI_Stereo_SetSeparation(pStereoHandle, float(Config->Separation))!=NVAPI_OK) { + LogErr("SetSeparation Failed"); + } + } + } + } + else LogAlw("[nVidia Stereo mode is Disabled]"); + } +#endif + + // Create status queries ----------------------------------------- + // + if (pDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, NULL) == S_OK) LogAlw("D3DQUERYTYPE_OCCLUSION is supported by device"); + else LogAlw("D3DQUERYTYPE_OCCLUSION not supported by device"); + + if (pDevice->CreateQuery(D3DQUERYTYPE_PIPELINETIMINGS, NULL)==S_OK) LogAlw("D3DQUERYTYPE_PIPELINETIMINGS is supported by device"); + else LogAlw("D3DQUERYTYPE_PIPELINETIMINGS not supported by device"); + + if (pDevice->CreateQuery(D3DQUERYTYPE_BANDWIDTHTIMINGS, NULL) == S_OK) LogAlw("D3DQUERYTYPE_BANDWIDTHTIMINGS is supported by device"); + else LogAlw("D3DQUERYTYPE_BANDWIDTHTIMINGS not supported by device"); + + if (pDevice->CreateQuery(D3DQUERYTYPE_PIXELTIMINGS, NULL) == S_OK) LogAlw("D3DQUERYTYPE_PIXELTIMINGS is supported by device"); + else LogAlw("D3DQUERYTYPE_PIXELTIMINGS not supported by device"); + + return hRenderWnd; +} + + +// ============================================================== +// This is called when the simulation is ready to go but the clock +// is not yet ticking +// +void vkClient::clbkPostCreation() +{ + _TRACE; + + LogVerbose("[vk] === clbkPostCreation ==="); + + if (scene) scene->clbkInitialise(); + + // Create Window Manager ----------------------------------------- + // + if (Config->gcGUIMode != 0) { + pWM = new WindowManager(hRenderWnd, ModuleInstance(), !(GetVideoData()->fullscreen)); + if (pWM->IsOK() == false) SAFE_DELETE(pWM); + } + + bRunning = true; + +#ifdef _DEBUG + SketchPadTest(); +#endif + + WriteLog("[Scene Initialized]"); +} + +// ============================================================== +// Perform some routine tests with sketchpad +// +void vkClient::SketchPadTest() +{ + SURFHANDLE hSrc = clbkLoadSurface("D3D9/SketchpadTest.dds", OAPISURFACE_TEXTURE); + SURFHANDLE hTgt = clbkLoadSurface("D3D9/SketchpadTest.dds", OAPISURFACE_RENDERTARGET); + + if (!hSrc || !hTgt) return; + + oapiSetSurfaceColourKey(hSrc, 0xFFFFFFFF); + + Sketchpad *pSkp = oapiGetSketchpad(hTgt); + + RECT r = { 17, 1, 31, 15 }; + RECT t = { 1, 33, 31, 63 }; + RECT q = { 0, 16, 16, 32 }; + RECT w = { 49, 1, 63, 15 }; + + pSkp->CopyRect(hSrc, &r, 1, 1); + pSkp->ColorKey(hSrc, &r, 33, 1); + pSkp->SetBlendState((Sketchpad::BlendState)(Sketchpad::BlendState::ALPHABLEND | Sketchpad::BlendState::FILTER_POINT)); + pSkp->StretchRect(hSrc, &r, &t); + pSkp->SetBlendState(); + pSkp->StretchRect(hSrc, &r, &w); + pSkp->RotateRect(hSrc, &q, 24, 24, float(PI05)); + pSkp->RotateRect(hSrc, &q, 40, 24, float(PI)); + pSkp->RotateRect(hSrc, &q, 56, 24, float(-PI05)); + + RECT tr = { 33, 49, 47, 63 }; + pSkp->ColorFill(0xFFFF00FF, &tr); + + pSkp->QuickPen(0xFF000000); + pSkp->QuickBrush(0x80FFFF00); + pSkp->Rectangle(49, 33, 63, 47); // 1px margin + pSkp->Ellipse(49, 49, 63, 63); // 1px margin + + // No Pen, Brush Only + pSkp->SetPen(NULL); + pSkp->QuickBrush(0xFF00FF00); + pSkp->Rectangle(65, 33, 79, 47); // 1px tlm 1px brm + pSkp->Ellipse(65, 49, 79, 63); // 1px tlm 1px brm + + pSkp->QuickPen(0xFFFFFFFF); + pSkp->MoveTo(65, 1); + pSkp->LineTo(65, 14); + pSkp->LineTo(78, 14); + pSkp->LineTo(78, 1); + pSkp->LineTo(65, 1); + + RECT cr = { 33, 33, 47, 47 }; + pSkp->ClipRect(&cr); + pSkp->ColorFill(0xFFFFFF00, NULL); + pSkp->ClipRect(); + + oapiReleaseSketchpad(pSkp); + + oapiSaveSurface("SketchpadOutput", hTgt, ImageFileFormat::IMAGE_PNG); + + oapiReleaseTexture(hSrc); + oapiReleaseTexture(hTgt); + + + // Run Different Kind of Tests + // + + hTgt = oapiCreateSurfaceEx(768, 512, OAPISURFACE_RENDERTARGET); + + if (!hTgt) return; + + gcCore2* pCore = gcGetCoreInterface(); + + if (!pCore) return; + + float a = 0.0f; + float s = float(PI2 / 6.0); + + gcCore::clrVtx Vtx[8]; + oapi::FVECTOR2 Pol[6]; + + for (int i = 0; i < 6; i++) { + Pol[i].x = cos(a); + Pol[i].y = sin(a); + Vtx[i + 1].pos.x = Pol[i].x; + Vtx[i + 1].pos.y = Pol[i].y; + a += s; + } + + // AABBGGRR + Vtx[1].color = 0xFFFF0000; + Vtx[2].color = 0xFFFFFF00; + Vtx[3].color = 0xFF00FF00; + Vtx[4].color = 0xFF00FFFF; + Vtx[5].color = 0xFF0000FF; + Vtx[6].color = 0xFFFF00FF; + + // Center vertex + Vtx[0].pos.x = 0.0f; + Vtx[0].pos.y = 0.0f; + Vtx[0].color = 0xFFFFFFFF; // White + Vtx[7] = Vtx[1]; + + HPOLY hColors = pCore->CreateTriangles(NULL, Vtx, 8, PF_FAN); + HPOLY hOutline = pCore->CreatePoly(NULL, Pol, 6, PF_CONNECT); + HPOLY hOutline2 = pCore->CreatePoly(NULL, Pol, 6); + + Vtx[0].color = 0xFFFF0000; + Vtx[1].color = 0xFFFFFF00; + Vtx[2].color = 0xFFF0FF00; + Vtx[3].color = 0xFF00FFFF; + Vtx[4].color = 0xFF0000FF; + Vtx[5].color = 0xFFFF00FF; + + Vtx[0].pos = FVECTOR2(-1, 0); + Vtx[1].pos = FVECTOR2(-1, 1); + Vtx[2].pos = FVECTOR2(0, 0); + Vtx[3].pos = FVECTOR2(0, 1); + Vtx[4].pos = FVECTOR2(1, 0); + Vtx[5].pos = FVECTOR2(1, 1); + + HPOLY hStrip = pCore->CreateTriangles(NULL, Vtx, 6, PF_STRIP); + + + Vtx[0].color = 0xFF00FF00; // Green + Vtx[1].color = 0xFF00FF00; // Green + Vtx[2].color = 0xFFFF00FF; // Mangenta + Vtx[3].color = 0xFFFF00FF; // Mangenta + Vtx[4].color = 0xFF0000FF; // Blue + Vtx[5].color = 0xFF0000FF; // Blue + + Vtx[0].pos = FVECTOR2(-1, 1); + Vtx[1].pos = FVECTOR2(-1, 0); + Vtx[2].pos = FVECTOR2(0, 1); + Vtx[3].pos = FVECTOR2(0, 0); + Vtx[4].pos = FVECTOR2(1, 1); + Vtx[5].pos = FVECTOR2(1, 0); + + HPOLY hStrip2 = pCore->CreateTriangles(NULL, Vtx, 6, PF_STRIP); + + + IVECTOR2 pos0 = { 128, 128 }; + IVECTOR2 pos1 = { 128, 384 }; + IVECTOR2 pos2 = { 384, 128 }; + IVECTOR2 pos3 = { 640, 128 }; + IVECTOR2 pos4 = { 384, 384 }; + IVECTOR2 pos5 = { 640, 384 }; + + pSkp = oapiGetSketchpad(hTgt); + + pSkp->ColorFill(0xFFFFFFFF, NULL); + + pSkp->QuickBrush(0xA0000088); + pSkp->QuickPen(0xA0000000, 3.0f); + pSkp->PushWorldTransform(); + + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos0); + pSkp->DrawPoly(hColors); + pSkp->DrawPoly(hOutline); + + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos1); + pSkp->DrawPoly(hOutline2); + + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos2); + pSkp->DrawPoly(hStrip); + + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos3); + pSkp->DrawPoly(hStrip2); + + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(100.0f, 100.0f)), &pos4); + pSkp->QuickPen(0xFF000000, 25.0f); + pSkp->DrawPoly(hOutline); + + hSrc = clbkLoadSurface("generic/noisep.dds", OAPISURFACE_TEXTURE); + + pSkp->SetWorldScaleTransform2D(&(FVECTOR2(1.0f, 1.0f)), &pos5); + + FVECTOR2 pt[4]; + pt[0] = FVECTOR2(-100.0f, -100.0f); + pt[1] = FVECTOR2(-100.0f, 50.0f); + pt[2] = FVECTOR2(100.0f, 100.0f); + pt[3] = FVECTOR2(100.0f, -50.0f); + + pSkp->CopyTetragon(hSrc, NULL, pt); + pSkp->PopWorldTransform(); + + + oapiReleaseTexture(hSrc); + oapiReleaseSketchpad(pSkp); + + + pCore->DeletePoly(hColors); // Must release Sketchpad before releasing any sketchpad resources + pCore->DeletePoly(hOutline); + pCore->DeletePoly(hOutline2); + pCore->DeletePoly(hStrip); + pCore->DeletePoly(hStrip2); + + oapiSaveSurface("SketchpadOutput2", hTgt, ImageFileFormat::IMAGE_DDS); + + oapiReleaseTexture(hTgt); +} + + + + +// ============================================================== +// Called when simulation session is about to be closed +// +void vkClient::clbkCloseSession(bool fastclose) +{ + LogVerbose("[vk] === clbkCloseSession ==="); + + // Post shutdown signals for gcGUI applications + // + for (auto pApp : g_gcGUIAppList) pApp->clbkShutdown(); + + // Post shutdown signals for user applications + // + if (IsGenericProcEnabled(GENERICPROC_SHUTDOWN)) MakeGenericProcCall(GENERICPROC_SHUTDOWN, 0, NULL); + + + // Check the status of RenderTarget Stack ------------------------------------------------ + // + if (RenderStack.empty() == false) { + LogErr("RenderStack contains %d items:", RenderStack.size()); + while (!RenderStack.empty()) { + LogErr("RenderTarget=%s, DepthStencil=%s", _PTR(RenderStack.front().pColor), _PTR(RenderStack.front().pDepthStencil)); + RenderStack.pop_front(); + } + } + + // Disable rendering and some other systems + // + bRunning = false; + + + // At first, shutdown tile loaders ------------------------------------------------------- + // + if (TileBuffer::ShutDown()==false) LogErr("Failed to Shutdown TileBuffer()"); + if (TileManager2Base::ShutDown()==false) LogErr("Failed to Shutdown TileManager2Base()"); + + // Close dialog if Open and disconnect a visual form debug controls + DebugControls::Close(); + + // Disconnect textures from pipeline (Unlikely nesseccary) + vkEffect::ShutDown(); + + // DEBUG: List all textures connected to meshes + /* DWORD cnt = MeshCatalog->CountEntries(); + for (DWORD i=0;iGet(i); + if (x) x->DumpTextures(); + } */ + //GraphicsClient::clbkCloseSession(fastclose); + pCustomSplashScreen = NULL; + pSplashTextColor = 0xE0A0A0; + + SAFE_DELETE(pWM); + LogAlw("================= Deleting Scene ================"); + Scene::GlobalExit(); + SAFE_DELETE(scene); + LogAlw("============== Deleting Mesh Manager ============"); + SAFE_DELETE(meshmgr); + WriteLog("[Session Closed. Scene deleted.]"); + +} + +// ============================================================== + +void vkClient::clbkDestroyRenderWindow (bool fastclose) +{ + _TRACE; + oapiWriteLog((char*)"vk: [Destroy Render Window Called]"); + + LogVerbose("[vk] === clbkDestroyRenderWindow ==="); + +#ifdef _NVAPI_H + if (bNVAPI) { + if (pStereoHandle) { + if (NvAPI_Stereo_DestroyHandle(pStereoHandle)!=NVAPI_OK) { + LogErr("Failed to destroy stereo handle"); + } + } + } +#endif + + LogAlw("===== Calling GlobalExit() for sub-systems ======"); + HazeManager::GlobalExit(); + HazeManager2::GlobalExit(); + TileManager::GlobalExit(); + TileManager2Base::GlobalExit(); + vkParticleStream::GlobalExit(); + CSphereManager::GlobalExit(); + vStar::GlobalExit(); + vVessel::GlobalExit(); + vPlanet::GlobalExit(); + vObject::GlobalExit(); + vkMesh::GlobalExit(); + + SAFE_DELETE(defpen); + SAFE_DELETE(deffont); + + DeleteObject(hLblFont1); + DeleteObject(hLblFont2); + + vkPad::GlobalExit(); + vkText::GlobalExit(); + vkEffect::GlobalExit(); + + SAFE_RELEASE(pSplashScreen); // Splash screen related + SAFE_RELEASE(pTextScreen); // Splash screen related + DELETE_SURFACE(pDefaultTex); + SAFE_RELEASE(pNoiseTex); + + SURFHANDLE hBackBuffer = GetBackBufferHandle(); + + DELETE_SURFACE(hBackBuffer); + + LogAlw("============ Checking Object Catalogs ==========="); + + // Clear microtextures -------------------------------------------------------------------------------------- + // + for (auto& it : MicroTextures) SAFE_RELEASE(it.second); + MicroTextures.clear(); + + + // Check surface catalog -------------------------------------------------------------------------------------- + // + if (SharedTextures.size() > 0) + { + LogWrn("Texture Repository has %u entries... Releasing...", (DWORD)SharedTextures.size()); + auto Undeleted(SharedTextures); + for (auto srf : Undeleted) { + LogWrn("Texture [%s]", SURFACE(srf.second)->GetName()); + delete lpSurfNative(srf.second); + } + } + + // Check surface catalog -------------------------------------------------------------------------------------- + // + if (SurfaceCatalog.size() > 0) + { + LogErr("UnDeleted Surfaces(s) Detected %u... Releasing...", (DWORD)SurfaceCatalog.size()); + auto Undeleted(SurfaceCatalog); + for (auto srf : Undeleted) { + LogErr("Surface [%s] (%u, %u)", srf->GetName(), srf->GetWidth(), srf->GetHeight()); + delete srf; + } + } + + // Check mesh catalog -------------------------------------------------------------------------------------- + // + if (MeshCatalog.size() > 0) + { + LogErr("UnDeleted Meshe(s) Detected %u", (DWORD)MeshCatalog.size()); + auto Undeleted(MeshCatalog); + for (auto msh : Undeleted) + { + LogErr("Mesh[%s] Handle = %s ", msh->GetName(), _PTR(msh)); + delete msh; + } + } + + // Check Fonts catalog -------------------------------------------------------------------------------------- + // + if (g_fonts.size()) { + LogWrn("%u un-released fonts!", g_fonts.size()); + for (auto it = g_fonts.begin(); it != g_fonts.end(); ) { + clbkReleaseFont(*it++); + } + g_fonts.clear(); + } + + // --- Brushes + if (g_brushes.size()) { + LogWrn("%u un-released brushes!", g_brushes.size()); + for (auto it = g_brushes.begin(); it != g_brushes.end(); ) { + clbkReleaseBrush(*it++); + } + g_brushes.clear(); + } + + // --- Pens + if (g_pens.size()) { + LogWrn("%u un-released pens!", g_pens.size()); + for (auto it = g_pens.begin(); it != g_pens.end(); ) { + clbkReleasePen(*it++); + } + g_pens.clear(); + } + + // Check tile catalog -------------------------------------------------------------------------------------- + // + + for (auto it : MeshMap) SAFE_DELETE(it.second); + + MeshMap.clear(); + SharedTextures.clear(); + SurfaceCatalog.clear(); + MeshCatalog.clear(); + + g_pTexmgr_tt->CleanUp(); + g_pVtxmgr_vb->CleanUp(); + g_pIdxmgr_ib->CleanUp(); + SAFE_DELETE(g_pTexmgr_tt); + SAFE_DELETE(g_pVtxmgr_vb); + SAFE_DELETE(g_pIdxmgr_ib); + + pFramework->DestroyObjects(); + + SAFE_DELETE(pFramework); + + // Close Render Window ----------------------------------------- + GraphicsClient::clbkDestroyRenderWindow(fastclose); + + hRenderWnd = NULL; + pDevice = NULL; + bFailed = false; + viewW = viewH = 0; + viewBPP = 0; + +} + +// ============================================================== + +void vkClient::clbkDebugString(const char* str) +{ + vkDebugLog("%s", str); +} + + +// ============================================================== + +void vkClient::PushSketchpad(SURFHANDLE surf, vkPad *pSkp) const +{ + if (surf) { + LPDIRECT3DSURFACE9 pTgt = SURFACE(surf)->GetSurface(); + LPDIRECT3DSURFACE9 pDep = SURFACE(surf)->GetDepthStencil(); + PushRenderTarget(pTgt, pDep, RENDERPASS_SKETCHPAD); + RenderStack.front().pSkp = pSkp; + } +} + + +// ============================================================== + +void vkClient::PushRenderTarget(LPDIRECT3DSURFACE9 pColor, LPDIRECT3DSURFACE9 pDepthStencil, int code) const +{ + static const char *labels[] = { "NULL", "MAIN", "ENV", "CUSTOMCAM", "SHADOWMAP", "PICK", "SKETCHPAD", "OVERLAY" }; + + RenderTgtData data; + data.pColor = pColor; + data.pDepthStencil = pDepthStencil; + data.pSkp = NULL; + data.code = code; + + if (pColor) { + D3DSURFACE_DESC desc; + pColor->GetDesc(&desc); + D3DVIEWPORT9 vp = { 0, 0, desc.Width, desc.Height, 0.0f, 1.0f }; + pDevice->SetViewport(&vp); + } + + // If pDepthStencil is NULL set NULL + if (pDevice->SetDepthStencilSurface(pDepthStencil) != S_OK) assert(false); + if (pColor) if (pDevice->SetRenderTarget(0, pColor) != S_OK) assert(false); + + RenderStack.push_front(data); + LogDbg("Plum", "PUSH:RenderStack[%lu]={%s, %s} %s", RenderStack.size(), _PTR(data.pColor), _PTR(data.pDepthStencil), labels[data.code]); +} + +// ============================================================== + +void vkClient::AlterRenderTarget(LPDIRECT3DSURFACE9 pColor, LPDIRECT3DSURFACE9 pDepthStencil) +{ + D3DSURFACE_DESC desc; + pColor->GetDesc(&desc); + D3DVIEWPORT9 vp = { 0, 0, desc.Width, desc.Height, 0.0f, 1.0f }; + + pDevice->SetViewport(&vp); + pDevice->SetRenderTarget(0, pColor); + pDevice->SetDepthStencilSurface(pDepthStencil); +} + +// ============================================================== + +void vkClient::PopRenderTargets() const +{ + static const char *labels[] = { "NULL", "MAIN", "ENV", "CUSTOMCAM", "SHADOWMAP", "PICK", "SKETCHPAD", "OVERLAY" }; + + assert(RenderStack.empty() == false); + + RenderStack.pop_front(); + + if (RenderStack.empty()) { + LogDbg("Orange", "POP: Last one out ------------------------------------"); + return; + } + + RenderTgtData data = RenderStack.front(); + + if (data.pColor) { + D3DSURFACE_DESC desc; + data.pColor->GetDesc(&desc); + D3DVIEWPORT9 vp = { 0, 0, desc.Width, desc.Height, 0.0f, 1.0f }; + + pDevice->SetViewport(&vp); + pDevice->SetRenderTarget(0, data.pColor); + pDevice->SetDepthStencilSurface(data.pDepthStencil); + } + + LogDbg("Plum", "POP:RenderStack[%lu]={%s, %s, %s} %s", RenderStack.size(), _PTR(data.pColor), _PTR(data.pDepthStencil), _PTR(data.pSkp), labels[data.code]); +} + +// ============================================================== + +void vkClient::HackFriendlyHack() +{ + // Try to make the application more hackable by setting the D3D Device in 'more' expected state. + + D3DSURFACE_DESC desc; + GetBackBuffer()->GetDesc(&desc); + D3DVIEWPORT9 vp = { 0, 0, desc.Width, desc.Height, 0.0f, 1.0f }; + pDevice->SetViewport(&vp); + pDevice->SetRenderTarget(0, GetBackBuffer()); + pDevice->SetDepthStencilSurface(GetDepthStencil()); +} + +// ============================================================== + +LPDIRECT3DSURFACE9 vkClient::GetTopDepthStencil() +{ + if (RenderStack.empty()) return NULL; + return RenderStack.front().pDepthStencil; +} + +// ============================================================== + +LPDIRECT3DSURFACE9 vkClient::GetTopRenderTarget() +{ + if (RenderStack.empty()) return NULL; + return RenderStack.front().pColor; +} + +// ============================================================== + +vkPad *vkClient::GetTopInterface() const +{ + if (RenderStack.empty()) return NULL; + return RenderStack.front().pSkp; +} + + +// ============================================================== + +void vkClient::clbkUpdate(bool running) +{ + _TRACE; + double tot_update = vkGetTime(); + if (bFailed==false && bRunning) scene->clbkUpdate(); + vkSetTime(vkStats.Timer.Update, tot_update); +} + +// ============================================================== + +double frame_time = 0.0; +double scene_time = 0.0; + +void vkClient::clbkRenderScene() +{ + _TRACE; + + if (pDevice==NULL || scene==NULL) return; + if (bFailed) return; + if (!bRunning) return; + + if (pWM) pWM->Animate(); + + if (Config->PresentLocation == 1) PresentScene(); + + scene_time = vkGetTime(); + + if (pDevice->TestCooperativeLevel()!=S_OK) { + bFailed=true; + MessageBoxA(pFramework->GetRenderWindow(),"Connection to Direct3DDevice is lost\nExit the simulation with Ctrl+Q and restart.\n\nAlt-Tabing not supported in a true fullscreen mode.\nDialog windows won't work with multi-sampling in a true fullscreen mode.","vkClient: Lost Device",0); + return; + } + + UINT mem = pDevice->GetAvailableTextureMem()>>20; + if (mem<32) TileBuffer::HoldThread(true); + + scene->clbkRenderMainScene(); // Render the main scene + + VESSEL *hVes = oapiGetFocusInterface(); + + if (hVes && Config->LabelDisplayFlags) + { + char Label[7] = ""; + if (Config->LabelDisplayFlags & vkConfig::LABEL_DISPLAY_RECORD && hVes->Recording()) strcpy_s(Label, 7, "Record"); + if (Config->LabelDisplayFlags & vkConfig::LABEL_DISPLAY_REPLAY && hVes->Playback()) strcpy_s(Label, 7, "Replay"); + + if (Label[0]!=0) { + pDevice->BeginScene(); + RECT rect2 = _RECT(0, viewH - 60, viewW, viewH - 20); + pFramework->GetLargeFont()->DrawTextA(0, Label, 6, &rect2, DT_CENTER | DT_TOP, D3DCOLOR_XRGB(0, 0, 0)); + rect2.left-=4; rect2.top-=4; + pFramework->GetLargeFont()->DrawTextA(0, Label, 6, &rect2, DT_CENTER | DT_TOP, D3DCOLOR_XRGB(255, 255, 255)); + pDevice->EndScene(); + } + } + + if (bFreeze) { + RECT rect2 = _RECT(0, viewH - 60, viewW, viewH - 20); + pFramework->GetLargeFont()->DrawTextA(0, "Frozen", 6, &rect2, DT_CENTER | DT_TOP, D3DCOLOR_XRGB(0, 255, 255)); + } + + vkSetTime(vkStats.Timer.Scene, scene_time); + + + if (bControlPanel) RenderControlPanel(); + + // Compute total frame time + vkSetTime(vkStats.Timer.FrameTotal, frame_time); + frame_time = vkGetTime(); +} + +// ============================================================== + +void vkClient::clbkTimeJump(double simt, double simdt, double mjd) +{ + _TRACE; + GraphicsClient::clbkTimeJump (simt, simdt, mjd); +} + +// ============================================================== + +void vkClient::PresentScene() +{ + double time = vkGetTime(); + + if (bFullscreen == false) { + RenderWithPopupWindows(); + pDevice->Present(0, 0, 0, 0); + } + else { + if (!RenderWithPopupWindows()) pDevice->Present(0, 0, 0, 0); + } + + vkSetTime(vkStats.Timer.Display, time); +} + +// ============================================================== + +double framer_rater_limit = 0.0; + +bool vkClient::clbkDisplayFrame() +{ + _TRACE; +// static int iRefrState = 0; + double time = vkGetTime(); + + if (!bRunning && pDevice) { + RECT txt = _RECT( loadd_x, loadd_y, loadd_x+loadd_w, loadd_y+loadd_h ); + pDevice->StretchRect(pSplashScreen, NULL, pBackBuffer, NULL, D3DTEXF_POINT); + pDevice->StretchRect(pTextScreen, NULL, pBackBuffer, &txt, D3DTEXF_POINT); + } + + if (Config->PresentLocation == 0) PresentScene(); + + double frmt = (1000000.0/Config->FrameRate) - (time - framer_rater_limit); + + framer_rater_limit = time; + + if (Config->EnableLimiter && Config->FrameRate>0 && bVSync==false) { + if (frmt>0) frame_timer++; + else frame_timer--; + if (frame_timer>40) frame_timer=40; + Sleep(frame_timer); + } + + return true; +} + +// ============================================================== + +void vkClient::clbkPreOpenPopup () +{ + _TRACE; + GetDevice()->SetDialogBoxMode(true); +} + +// ======================================================================= + +static DWORD g_lastPopupWindowCount = 0; +static void FixOutOfScreenPositions (const HWND *hWnd, DWORD count) +{ + // Only check if a popup window is *added* + if (count > g_lastPopupWindowCount) + { + for (DWORD i=0; i monitorWidth) { + x = monitorWidth - (rect.right - rect.left); + y = rect.top; + } + if (rect.bottom > monitorHeight) { + x = rect.left; + y = monitorHeight - (rect.bottom - rect.top); + } + + if (x != -1) { + w = rect.right - rect.left, + h = rect.bottom - rect.top; + MoveWindow(hWnd[i], x, y, w, h, FALSE); + } + } + + } + g_lastPopupWindowCount = count; +} + +// ======================================================================= + +bool vkClient::RenderWithPopupWindows() +{ + _TRACE; + + const HWND *hPopupWnd; + DWORD count = GetPopupList(&hPopupWnd); + + if (bFullscreen) { + if (count) GetDevice()->SetDialogBoxMode(true); + else GetDevice()->SetDialogBoxMode(false); + } + + FixOutOfScreenPositions(hPopupWnd, count); + + if (!bFullscreen) { + for (DWORD i=0;iAddParticleStream (es); + return es; +} + +// ======================================================================= + +ParticleStream *vkClient::clbkCreateExhaustStream(PARTICLESTREAMSPEC *pss, + OBJHANDLE hVessel, const double *lvl, const VECTOR3 &ref, const VECTOR3 &dir) +{ + _TRACE; + ExhaustStream *es = new ExhaustStream (this, hVessel, lvl, ref, dir, pss); + scene->AddParticleStream (es); + return es; +} + +// ====================================================================== + +ParticleStream *vkClient::clbkCreateReentryStream (PARTICLESTREAMSPEC *pss, + OBJHANDLE hVessel) +{ + _TRACE; + ReentryStream *rs = new ReentryStream (this, hVessel, pss); + scene->AddParticleStream (rs); + return rs; +} + +#pragma endregion + +// ============================================================== + +ScreenAnnotation* vkClient::clbkCreateAnnotation() +{ + _TRACE; + return GraphicsClient::clbkCreateAnnotation(); +} + +#pragma region Mesh functions + +// ============================================================== + +void vkClient::clbkStoreMeshPersistent(MESHHANDLE hMesh, const char *fname) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return; + + if (fname) { + LogAlw("Storing a mesh %s (%s)", _PTR(hMesh), fname); + if (hMesh==NULL) LogErr("vkClient::clbkStoreMeshPersistent(%s) hMesh is NULL",fname); + } + else { + LogAlw("Storing a mesh %s", _PTR(hMesh)); + if (hMesh==NULL) LogErr("vkClient::clbkStoreMeshPersistent() hMesh is NULL"); + } + + if (hMesh==NULL) return; + + int idx = meshmgr->StoreMesh(hMesh, fname); +} + +// ============================================================== + +DEVMESHHANDLE vkClient::GetDevMesh(MESHHANDLE hMesh) +{ + const vkMesh *pDevMesh = meshmgr->GetMesh(hMesh); + if (!pDevMesh) { + meshmgr->StoreMesh(hMesh, "GetDevMesh()"); + pDevMesh = meshmgr->GetMesh(hMesh); + } + + // Create a new Instance from a template + return DEVMESHHANDLE(new vkMesh(hMesh, *pDevMesh)); +} + +// ============================================================== + +bool vkClient::clbkSetMeshTexture(DEVMESHHANDLE hMesh, DWORD texidx, SURFHANDLE surf) +{ + _TRACE; + if (hMesh && surf) return ((vkMesh*)hMesh)->SetTexture(texidx, SURFACE(surf)); + return false; +} + +// ============================================================== + +int vkClient::clbkSetMeshMaterial(DEVMESHHANDLE hMesh, DWORD matidx, const MATERIAL *mat) +{ + _TRACE; + if (!hMesh) return 3; + vkMesh *mesh = (vkMesh*)hMesh; + DWORD nmat = mesh->GetMaterialCount(); + if (matidx >= nmat) return 4; // "index out of range" + vkMatExt meshmat; + //mesh->GetMaterial(&meshmat, matidx); + CreateMatExt((const D3DMATERIAL9 *)mat, &meshmat); + mesh->SetMaterial(&meshmat, matidx); + return 0; +} + +// ============================================================== + +int vkClient::clbkMeshMaterial (DEVMESHHANDLE hMesh, DWORD matidx, MATERIAL *mat) +{ + _TRACE; + if (!hMesh) return 3; + vkMesh *mesh = (vkMesh*)hMesh; + DWORD nmat = mesh->GetMaterialCount(); + if (matidx >= nmat) return 4; // "index out of range" + const vkMatExt *meshmat = mesh->GetMaterial(matidx); + if (meshmat) GetMatExt(meshmat, (D3DMATERIAL9 *)mat); + return 0; +} + +// ============================================================== + +int vkClient::clbkSetMeshMaterialEx(DEVMESHHANDLE hMesh, DWORD matidx, MatProp mat, const oapi::FVECTOR4* in) +{ + if (!hMesh) return 3; + vkMesh* mesh = (vkMesh*)hMesh; + return mesh->SetMaterialEx(matidx, mat, in); +} + +// ============================================================== + +int vkClient::clbkMeshMaterialEx(DEVMESHHANDLE hMesh, DWORD matidx, MatProp mat, oapi::FVECTOR4* out) +{ + if (!hMesh) return 3; + vkMesh* mesh = (vkMesh*)hMesh; + return mesh->GetMaterialEx(matidx, mat, out); +} + +// ============================================================== + +bool vkClient::clbkSetMeshProperty(DEVMESHHANDLE hMesh, DWORD prop, DWORD value) +{ + _TRACE; + vkMesh *mesh = (vkMesh*)hMesh; + switch (prop) { + case MESHPROPERTY_MODULATEMATALPHA: + mesh->EnableMatAlpha(value!=0); + return true; + } + return false; +} + +// ============================================================== + +void vkClient::clbkSetVisualProperty(VISHANDLE vis, VisualProp prp, int idx, const type_info& t, const void* val) +{ + vVessel* vV = (vVessel*)vis; + if (vV && vV->Type() == OBJTP_VESSEL) vV->SetVisualProperty(prp, idx, t, val); +} + +// ============================================================== +// Returns a dev-mesh for a visual + +MESHHANDLE vkClient::clbkGetMesh(VISHANDLE vis, UINT idx) +{ + _TRACE; + if (vis==NULL) { + LogErr("NULL visual in clbkGetMesh(NULL,%u)",idx); + return NULL; + } + MESHHANDLE hMesh = (MESHHANDLE)((vObject*)vis)->GetMesh(idx); + if (hMesh==NULL) LogWrn("clbkGetMesh() returns NULL"); + return hMesh; +} + +// ======================================================================= + +int vkClient::clbkEditMeshGroup(DEVMESHHANDLE hMesh, DWORD grpidx, GROUPEDITSPEC *ges) +{ + _TRACE; + return ((vkMesh*)hMesh)->EditGroup(grpidx, ges); +} + +// ======================================================================= + + +int vkClient::clbkGetMeshGroup (DEVMESHHANDLE hMesh, DWORD grpidx, GROUPREQUESTSPEC *grs) +{ + _TRACE; + return ((vkMesh*)hMesh)->GetGroup (grpidx, grs); +} + +#pragma endregion + +// ============================================================== + +void vkClient::clbkNewVessel(OBJHANDLE hVessel) +{ + _TRACE; + if (scene) scene->clbkNewVessel(hVessel); +} + +// ============================================================== + +void vkClient::clbkDeleteVessel(OBJHANDLE hVessel) +{ + if (scene) scene->clbkDeleteVessel(hVessel); +} + + +// ============================================================== +// copy video options from the video tab + +void vkClient::clbkRefreshVideoData() +{ + _TRACE; + if (vtab) vtab->UpdateConfigData(); +} + +// ============================================================== + +void vkClient::clbkOptionChanged(DWORD cat, DWORD item) +{ + switch (cat) { + case OPTCAT_CELSPHERE: + if (scene) scene->clbkOnOptionChanged(cat, item); + return; + } +} + +// ============================================================== + +void vkClient::clbkScenarioChanged(OBJHANDLE hVesselA, ScnChgEvent type) +{ + if (scene) scene->clbkScenarioChanged(hVesselA, type); +} + +// ============================================================== + +bool vkClient::clbkUseLaunchpadVideoTab() const +{ + _TRACE; + return true; +} + +// ============================================================== +// Fullscreen mode flag + +bool vkClient::clbkFullscreenMode() const +{ + _TRACE; + return bFullscreen; +} + +// ============================================================== +// return the dimensions of the render viewport + +void vkClient::clbkGetViewportSize(DWORD *width, DWORD *height) const +{ + _TRACE; + *width = viewW, *height = viewH; +} + +// ============================================================== +// Returns a specific render parameter + +bool vkClient::clbkGetRenderParam(DWORD prm, DWORD *value) const +{ + _TRACE; + switch (prm) { + case RP_COLOURDEPTH: + *value = viewBPP; + return true; + + case RP_ZBUFFERDEPTH: + *value = GetFramework()->GetZBufferBitDepth(); + return true; + + case RP_STENCILDEPTH: + *value = GetFramework()->GetStencilBitDepth(); + return true; + + case RP_MAXLIGHTS: + *value = MAX_SCENE_LIGHTS; + return true; + + case RP_REQUIRETEXPOW2: + *value = 0; + return true; + } + return false; +} + +// ============================================================== +// Responds to visual events + +int vkClient::clbkVisEvent(OBJHANDLE hObj, VISHANDLE vis, DWORD msg, DWORD_PTR context) +{ + _TRACE; + VisObject *vo = (VisObject*)vis; + vo->clbkEvent(msg, context); + if (DebugControls::IsActive()) { + if (msg==EVENT_VESSEL_INSMESH || msg==EVENT_VESSEL_DELMESH) { + if (DebugControls::GetVisual()==vo) DebugControls::UpdateVisual(); + } + } + return 1; +} + + +// ============================================================== +// +void vkClient::PickTerrain(DWORD uMsg, int xpos, int ypos) +{ + bool bUD = (uMsg == WM_LBUTTONUP || uMsg == WM_RBUTTONUP || uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN); + bool bPrs = IsGenericProcEnabled(GENERICPROC_PICK_TERRAIN) && bUD; + bool bHov = IsGenericProcEnabled(GENERICPROC_HOVER_TERRAIN) && (uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL); + + if (bPrs || bHov) { + gcCore::PickGround pg = gcCore2::ScanScreen(xpos, ypos); + pg.msg = uMsg; + if (bPrs) MakeGenericProcCall(GENERICPROC_PICK_TERRAIN, sizeof(gcCore::PickGround), &pg); + if (bHov) MakeGenericProcCall(GENERICPROC_HOVER_TERRAIN, sizeof(gcCore::PickGround), &pg); + } +} + + +// ============================================================== +// Message handler for render window + +LRESULT vkClient::RenderWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static bool bTrackMouse = false; + static short xpos=0, ypos=0; + + if (hRenderWnd!=hWnd && uMsg!= WM_NCDESTROY) { + LogErr("Invalid Window !! RenderWndProc() called after calling clbkDestroyRenderWindow() uMsg=0x%X", uMsg); + return 0; + } + + if (bRunning && DebugControls::IsActive()) { + // Must update camera to correspond MAIN_SCENE due to Pick() function, + // because env-maps have altered camera settings + // GetScene()->UpdateCameraFromOrbiter(RENDERPASS_PICKSCENE); + // Obsolete: since moving env/cam stuff in pre-scene + } + + if (pWM) if (pWM->MainWindowProc(hWnd, uMsg, wParam, lParam)) return 0; + + + switch (uMsg) + { + case WM_MOUSELEAVE: + { + if (bTrackMouse && bRunning) GraphicsClient::RenderWndProc (hWnd, WM_LBUTTONUP, 0, 0); + return 0; + } + + case WM_MBUTTONDOWN: + { + break; + } + + case WM_RBUTTONUP: + case WM_RBUTTONDOWN: + { + int xp = GET_X_LPARAM(lParam); + int yp = GET_Y_LPARAM(lParam); + PickTerrain(uMsg, xp, yp); + break; + } + + + case WM_LBUTTONDOWN: + { + bTrackMouse = true; + xpos = GET_X_LPARAM(lParam); + ypos = GET_Y_LPARAM(lParam); + + GetScene()->vPickRay = GetScene()->GetPickingRay(xpos, ypos); + + TRACKMOUSEEVENT te; te.cbSize = sizeof(TRACKMOUSEEVENT); te.dwFlags = TME_LEAVE; te.hwndTrack = hRenderWnd; + TrackMouseEvent(&te); + PickProp prp = { NULL, 0.1f, false }; + + bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0; + bool bCtrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; + bool bPckVsl = IsGenericProcEnabled(GENERICPROC_PICK_VESSEL); + + if (DebugControls::IsActive() || bPckVsl || (bShift && bCtrl)) + { + if (DebugControls::IsActive()) { + if (DebugControls::debugFlags & DBG_FLAGS_PICKCURRENT) prp.pMesh = DebugControls::GetMesh(); + if (DebugControls::debugFlags & DBG_FLAGS_DUALSIDED) prp.bDualSided = true; + } + + vkPick pick = GetScene()->PickScene(xpos, ypos, &prp); + + if (bPckVsl) { + gcCore::PickData out; + out.hVessel = pick.vObj->GetObjHandle(); + out.mesh = MESHHANDLE(pick.pMesh); + out.group = pick.group; + out.pos = pick.pos; + out.normal = pick.normal; + out.dist = pick.dist; + MakeGenericProcCall(GENERICPROC_PICK_VESSEL, sizeof(gcCore::PickData), &out); + } + + + // No Debug Controls + if (bShift && bCtrl && !DebugControls::IsActive() && !oapiCameraInternal()) { + + if (!pick.pMesh) break; + + OBJHANDLE hObj = pick.vObj->Object(); + if (oapiGetObjectType(hObj) == OBJTP_VESSEL) { + oapiSetFocusObject(hObj); + } + + break; + } + + // With Debug Controls + if (DebugControls::IsActive()) { + + DWORD flags = *(DWORD*)GetConfigParam(CFGPRM_GETDEBUGFLAGS); + + if (flags & DBG_FLAGS_PICK) { + + if (!pick.pMesh) break; + + if (bShift && bCtrl) { + OBJHANDLE hObj = pick.vObj->Object(); + if (oapiGetObjectType(hObj) == OBJTP_VESSEL) { + oapiSetFocusObject(hObj); + break; + } + } + else if (pick.group >= 0) { + DebugControls::SetVisual(pick.vObj); + DebugControls::SelectMesh(pick.pMesh); + DebugControls::SelectGroup(pick.group); + DebugControls::SetGroupHighlight(true); + DebugControls::SetPickPos(pick.pos); + } + } + } + } + + PickTerrain(uMsg, xpos, ypos); + + break; + } + + case WM_LBUTTONUP: + { + int xp = GET_X_LPARAM(lParam); + int yp = GET_Y_LPARAM(lParam); + + PickTerrain(uMsg, xp, yp); + + if (DebugControls::IsActive()) { + DWORD flags = *(DWORD*)GetConfigParam(CFGPRM_GETDEBUGFLAGS); + if (flags&DBG_FLAGS_PICK) { + DebugControls::SetGroupHighlight(false); + } + } + bTrackMouse = false; + break; + } + + case WM_KEYDOWN: + { + bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000)!=0; + bool bCtrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000)!=0; + if (wParam == 'C' && bShift && bCtrl) bControlPanel = !bControlPanel; + if (wParam == 'N' && bShift && bCtrl) Config->bCloudNormals = !Config->bCloudNormals; + if (wParam == 'V' && bShift && bCtrl) GetScene()->bStageSet = !GetScene()->bStageSet; + break; + } + + case WM_MOUSEWHEEL: + { + if (DebugControls::IsActive()) { + short d = GET_WHEEL_DELTA_WPARAM(wParam); + if (d<-1) d=-1; + if (d>1) d=1; + double speed = *(double *)GetConfigParam(CFGPRM_GETCAMERASPEED); + speed *= (DebugControls::GetVisualSize()/100.0); + if (scene->CameraPan(_V(0,0,double(d))*2.0, speed)) return 0; + } + + PickTerrain(uMsg, xpos, ypos); + break; + } + + case WM_MOUSEMOVE: + + if (DebugControls::IsActive()) + { + + double x = double(GET_X_LPARAM(lParam) - xpos); + double y = double(GET_Y_LPARAM(lParam) - ypos); + xpos = GET_X_LPARAM(lParam); + ypos = GET_Y_LPARAM(lParam); + + if (bTrackMouse) { + double speed = *(double *)GetConfigParam(CFGPRM_GETCAMERASPEED); + speed *= (DebugControls::GetVisualSize() / 100.0); + if (scene->CameraPan(_V(-x, y, 0)*0.05, speed)) return 0; + } + } + + xpos = GET_X_LPARAM(lParam); + ypos = GET_Y_LPARAM(lParam); + + PickTerrain(uMsg, xpos, ypos); + + break; + + case WM_MOVE: + // If in windowed mode, move the Framework's window + break; + + case WM_SYSCOMMAND: + switch (wParam) { + case SC_KEYMENU: + // trap Alt system keys + return 1; + case SC_MOVE: + case SC_SIZE: + case SC_MAXIMIZE: + case SC_MONITORPOWER: + // Prevent moving/sizing and power loss in fullscreen mode + if (bFullscreen) return 1; + break; + } + break; + + case WM_SYSKEYUP: + if (bFullscreen) return 0; // trap Alt-key + break; + } + + if (!bRunning && uMsg>=0x0200 && uMsg<=0x020E) return 0; + return GraphicsClient::RenderWndProc (hWnd, uMsg, wParam, lParam); +} + + +// ============================================================== +// Message handler for Launchpad "video" tab + +INT_PTR vkClient::LaunchpadVideoWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + _TRACE; + if (vtab) return vtab->WndProc(hWnd, uMsg, wParam, lParam); + else return false; +} + +// ======================================================================= + +void vkClient::clbkRender2DPanel (SURFHANDLE *hSurf, MESHHANDLE hMesh, MATRIX3 *T, float alpha, bool additive) +{ + _TRACE; + + SURFHANDLE surf = NULL; + DWORD ngrp = oapiMeshGroupCount(hMesh); + + if (ngrp==0) return; + + float sx = 1.0f/(float)(T->m11), dx = (float)(T->m13); + float sy = 1.0f/(float)(T->m22), dy = (float)(T->m23); + float vw = (float)viewW; + float vh = (float)viewH; + + FMATRIX4 mVP; + D3DMAT_OrthoOffCenterRH(&mVP, (0.0f-dx)*sx, (vw-dx)*sx, (vh-dy)*sy, (0.0f-dy)*sy, -100.0f, 100.0f); + vkEffect::SetViewProjMatrix(&mVP); + + for (DWORD i=0;iUsrFlag & 2) continue; // skip this group + + DWORD TexIdx = gr->TexIdx; + + if (TexIdx >= TEXIDX_MFD0) { + int mfdidx = TexIdx - TEXIDX_MFD0; + surf = GetMFDSurface(mfdidx); + if (!surf) surf = (SURFHANDLE)pDefaultTex; + } else if (hSurf) { + surf = hSurf[TexIdx]; + } + else surf = oapiGetTextureHandle (hMesh, gr->TexIdx+1); + + for (unsigned int k=0;knVtx;k++) gr->Vtx[k].z = 0.0f; + + vkEffect::Render2DPanel(gr, SURFACE(surf), &ident, alpha, scale, additive); + } +} + +// ======================================================================= + +void vkClient::clbkRender2DPanel (SURFHANDLE *hSurf, MESHHANDLE hMesh, MATRIX3 *T, bool additive) +{ + _TRACE; + clbkRender2DPanel (hSurf, hMesh, T, 1.0f, additive); +} + +// ======================================================================= + +DWORD vkClient::clbkGetDeviceColour (BYTE r, BYTE g, BYTE b) +{ + _TRACE; + return ((DWORD)r << 16) + ((DWORD)g << 8) + (DWORD)b; +} + + + +#pragma region Surface, Blitting and Filling Functions + + + +// ======================================================================= +// Surface functions +// ======================================================================= + +bool vkClient::clbkSaveSurfaceToImage(SURFHANDLE surf, const char *fname, ImageFileFormat fmt, float quality) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return false; + + if (surf==NULL) surf = pFramework->GetBackBufferHandle(); + + LPDIRECT3DSURFACE9 pRTG = NULL; + LPDIRECT3DSURFACE9 pSystem = NULL; + LPDIRECT3DSURFACE9 pSurf = SURFACE(surf)->GetSurface(); + + if (pSurf==NULL) return false; + + bool bRet = false; + const D3DSURFACE_DESC *desc = SURFACE(surf)->GetDesc(); + D3DLOCKED_RECT pRect; + + if (fmt == ImageFileFormat::IMAGE_DDS) { + char path[MAX_PATH]; + sprintf_s(path, "%s.dds", fname); + return NatSaveSurface(path, pSurf); + } + + if (desc->Pool != D3DPOOL_SYSTEMMEM) + { + HR(pDevice->CreateRenderTarget(desc->Width, desc->Height, D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, false, &pRTG, NULL)); + HR(pDevice->CreateOffscreenPlainSurface(desc->Width, desc->Height, D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &pSystem, NULL)); + HR(pDevice->StretchRect(pSurf, NULL, pRTG, NULL, D3DTEXF_NONE)); + HR(pDevice->GetRenderTargetData(pRTG, pSystem)); + + if (pSystem->LockRect(&pRect, NULL, 0)==S_OK) + { + if (fname == NULL) { + // copy device-dependent bitmap to clipboard + bRet = SaveSurfaceToClipboard(desc); + } else { + // save as file + bRet = SaveSurfaceToFile(desc, pRect, fname, fmt, quality); + } + pSystem->UnlockRect(); + } + + pRTG->Release(); + pSystem->Release(); + return bRet; + } + + if (pSurf->LockRect(&pRect, NULL, D3DLOCK_READONLY)==S_OK) + { + if (fname == NULL) { + // copy device-dependent bitmap to clipboard + bRet = SaveSurfaceToClipboard(desc); + } else { + // save as file + bRet = SaveSurfaceToFile(desc, pRect, fname, fmt, quality); + } + pSystem->UnlockRect(); + } + + return bRet; +} + +// ============================================================== + +bool oapi::vkClient::SaveSurfaceToFile (const D3DSURFACE_DESC* desc, D3DLOCKED_RECT& pRect, + const char* fname, oapi::ImageFileFormat fmt, float quality) +{ + bool bRet = false; + ImageData ID; + + ID.bpp = 24; + ID.height = desc->Height; + ID.width = desc->Width; + ID.stride = ((ID.width * ID.bpp + 31) & ~31) >> 3; + ID.bufsize = ID.stride * ID.height; + + BYTE* tgt = ID.data = new BYTE[ID.bufsize]; + BYTE* src = (BYTE*)pRect.pBits; + + for (DWORD k = 0; kHeight; k++) { + for (DWORD i = 0; iWidth; i++) { + tgt[0 + i * 3] = src[0 + i * 4]; + tgt[1 + i * 3] = src[1 + i * 4]; + tgt[2 + i * 3] = src[2 + i * 4]; + } + tgt += ID.stride; + src += pRect.Pitch; + } + + bRet = WriteImageDataToFile(ID, fname, fmt, quality); + + delete[]ID.data; + ID.data = NULL; + + return bRet; +} + +// ============================================================== + +bool oapi::vkClient::SaveSurfaceToClipboard (const D3DSURFACE_DESC* desc) +{ + if (OpenClipboard(hRenderWnd)) + { + HDC hDC = GetDC(hRenderWnd); + HDC hdcmem = CreateCompatibleDC(hDC); + HBITMAP hBm = CreateCompatibleBitmap(hDC, desc->Width, desc->Height); + + SelectObject(hdcmem, hBm); + BitBlt(hdcmem, 0, 0, desc->Width, desc->Height, hDC, 0, 0, SRCCOPY); + + EmptyClipboard(); + SetClipboardData(CF_BITMAP, hBm); + CloseClipboard(); + return true; + } + return false; +} + +// ============================================================== + +SURFHANDLE vkClient::clbkLoadTexture(const char *fname, DWORD flags) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return NULL; + + DWORD attrib = OAPISURFACE_TEXTURE; + if (flags & 0x1) attrib |= OAPISURFACE_SYSMEM; + if (flags & 0x2) attrib |= OAPISURFACE_UNCOMPRESS | OAPISURFACE_RENDERTARGET; + if (flags & 0x4) attrib |= OAPISURFACE_NOMIPMAPS; + if (flags & 0x8) attrib |= OAPISURFACE_SHARED; + + return clbkLoadSurface(fname, attrib); +} + +// ============================================================== + +SURFHANDLE vkClient::clbkLoadMaps(const char* diff, const char* maps, bool bPath, SURFHANDLE hOld, bool bAll) +{ + char mpath[MAX_PATH]; + + if (diff != NULL && hOld != NULL) { + oapiWriteLog((char*)"oapiLoadAdditionalTextureMaps() FAILED. Used either 'diff' or 'hOld'. The other one must be NULL"); + return NULL; + } + if (maps) { + if (bPath) strcpy_s(mpath, MAX_PATH, maps); + else if (!g_client->TexturePath(maps, mpath)) return NULL; + } + if (diff) { + SURFHANDLE hSrf = NatLoadSurface(diff, OAPISURFACE_TEXTURE | OAPISURFACE_SHARED | OAPISURFACE_DIFFUSE_ONLY, bPath); + if (hSrf && maps) { + if (bAll) NatLoadMaps(SURFACE(hSrf), mpath); + else NatLoadMap(SURFACE(hSrf), mpath); + } + return hSrf; + } + else if (maps) { + if (bAll) NatLoadMaps(SURFACE(hOld), mpath); + else NatLoadMap(SURFACE(hOld), mpath); + } + return hOld; +} + +// ============================================================== + +SURFHANDLE vkClient::clbkLoadSurface (const char *fname, DWORD attrib, bool bPath) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return NULL; + + static const DWORD val = OAPISURFACE_RENDERTARGET | OAPISURFACE_GDI | OAPISURFACE_SYSMEM; + static const DWORD exclude = ~(OAPISURFACE_SHARED | OAPISURFACE_ORIGIN); + + if (!(attrib & val)) + { + // It's a regular texture, let's manage it + // + string name(fname); + + if (attrib & OAPISURFACE_SHARED) + { + auto ent = SharedTextures.find(name); + + if (ent == SharedTextures.end()) + { + SURFHANDLE hSrf = NatLoadSurface(fname, attrib, bPath); + if (hSrf) SharedTextures[name] = hSrf; + return hSrf; + } + else return ent->second; + } + + /* + auto ent = ClonedTextures.find(name); + + if (ent == ClonedTextures.end()) + { + SURFHANDLE hSrf = NatLoadSurface(fname, attrib); + if (hSrf) SharedTextures[name] = hSrf; + return hSrf; + } + else + { + DWORD original = SURFACE(ent->second)->GetOAPIFlags(); + + if (original & OAPISURFACE_ORIGIN) + { + if ((attrib & exclude) == (original & exclude)) + { + return SURFHANDLE(new SurfNative(SURFACE(ent->second))); // Clone it + } + } + else + { + // Create "origin" for cloning + SURFHANDLE hSrf = NatLoadSurface(fname, attrib | OAPISURFACE_ORIGIN); + if (hSrf) { + ent->second = hSrf; + return SURFHANDLE(new SurfNative(SURFACE(hSrf))); // Clone it + } + else return NULL; + } + } + */ + } + + return NatLoadSurface(fname, attrib, bPath); +} + +// ============================================================== + +HBITMAP vkClient::gcReadImageFromFile(const char *_path) +{ + char path[MAX_PATH]; + sprintf_s(path, sizeof(path), "%s\\%s", OapiExtension::GetTextureDir(), _path); + return ReadImageFromFile(path); +} + +// ============================================================== + +void vkClient::clbkReleaseTexture(SURFHANDLE hTex) +{ + clbkReleaseSurface(hTex); +} + +// ============================================================== + +SURFHANDLE vkClient::clbkCreateSurfaceEx(DWORD w, DWORD h, DWORD attrib) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return NULL; + +#ifdef _DEBUG + LogAttribs(attrib, w, h, "CreateSrfEx"); +#endif // _DEBUG + + if (w == 0 || h == 0) return NULL; // Inline engine returns NULL for a zero surface + + SURFHANDLE hNew = NatCreateSurface(w, h, attrib); + SURFACE(hNew)->SetName("clbkCreateSurfaceEx"); + return hNew; +} + + +// ======================================================================= + +SURFHANDLE vkClient::clbkCreateSurface(DWORD w, DWORD h, SURFHANDLE hTemplate) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return NULL; + if (w == 0 || h == 0) return NULL; // Inline engine returns NULL for a zero surface + + DWORD attrib = OAPISURFACE_PF_XRGB | OAPISURFACE_RENDERTARGET | OAPISURFACE_TEXTURE; + + if (hTemplate) attrib = SURFACE(hTemplate)->GetOAPIFlags(); + + SURFHANDLE hNew = NatCreateSurface(w, h, attrib); + SURFACE(hNew)->SetName("clbkCreateSurface"); + return hNew; +} + +// ======================================================================= + +SURFHANDLE vkClient::clbkCreateSurface(HBITMAP hBmp) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return NULL; + + SURFHANDLE hSurf = GraphicsClient::clbkCreateSurface(hBmp); + SURFACE(hSurf)->SetName("clbkCreateSurface_HBITMAP"); + return hSurf; +} + +// ======================================================================= + +SURFHANDLE vkClient::clbkCreateTexture(DWORD w, DWORD h) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return NULL; + if (w == 0 || h == 0) return NULL; // Inline engine returns NULL for a zero surface + + SURFHANDLE hNew = NatCreateSurface(w, h, OAPISURFACE_PF_XRGB | OAPISURFACE_RENDERTARGET | OAPISURFACE_TEXTURE); + SURFACE(hNew)->SetName("clbkCreateTexture"); + return hNew; +} + +// ======================================================================= + +void vkClient::clbkIncrSurfaceRef(SURFHANDLE surf) +{ + _TRACE; + if (surf) SURFACE(surf)->IncRef(); +} + +// ======================================================================= + +bool vkClient::clbkReleaseSurface(SURFHANDLE surf) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return false; + + // Do not release 'origin' (i.e. reference) for cloned surfaces. + if (SURFACE(surf)->GetOAPIFlags() & OAPISURFACE_ORIGIN) return false; + + // Do not release surfaces stored in repository + for (auto ent : SharedTextures) if (ent.second == surf) return false; + + // Don't release surfaces used by meshes + for (auto mesh : MeshCatalog) if (mesh && mesh->HasTexture(surf)) return false; + + // If the surface exists, delete it. + if (SURFACE(surf)->DecRef()) + { + if (SurfaceCatalog.count(SURFACE(surf))) + { + delete SURFACE(surf); + return true; + } + } + return false; +} + +// ======================================================================= + +bool vkClient::clbkGetSurfaceSize(SURFHANDLE surf, DWORD *w, DWORD *h) +{ + _TRACE; + if (!w || !h) return false; + if (surf==NULL) surf = pFramework->GetBackBufferHandle(); + *w = SURFACE(surf)->GetWidth(); + *h = SURFACE(surf)->GetHeight(); + return true; +} + +// ======================================================================= + +bool vkClient::clbkSetSurfaceColourKey(SURFHANDLE surf, DWORD ckey) +{ + _TRACE; + if (surf==NULL) { LogErr("Surface is NULL"); return false; } + SURFACE(surf)->SetColorKey(ckey); + return true; +} + + + +// ======================================================================= +// Blitting functions +// ======================================================================= + +int vkClient::clbkBeginBltGroup(SURFHANDLE tgt) +{ + _TRACE; + if (pBltGrpTgt) return -1; + + if (tgt == RENDERTGT_NONE) { + pBltGrpTgt = NULL; + return -2; + } + + if (tgt == RENDERTGT_MAINWINDOW) pBltGrpTgt = pFramework->GetBackBufferHandle(); + else pBltGrpTgt = tgt; + + if (!SURFACE(tgt)->IsRenderTarget()) { + pBltGrpTgt = NULL; + return -3; + } + + //pBltSkp = SURFACE(tgt)->GetPooledSketchPad(); + //pBltSkp->BeginDrawing(); + return 0; +} + +// ======================================================================= + +int vkClient::clbkEndBltGroup() +{ + _TRACE; + if (pBltGrpTgt==NULL) return -2; + //pBltSkp->EndDrawing(); + pBltSkp = NULL; + pBltGrpTgt = NULL; + return 0; +} + +// ======================================================================= + +bool vkClient::clbkBlt(SURFHANDLE tgt, DWORD tgtx, DWORD tgty, SURFHANDLE src, DWORD flag) const +{ + _TRACE; + const D3DSURFACE_DESC* sd = SURFACE(src)->GetDesc(); + return clbkScaleBlt(tgt, tgtx, tgty, sd->Width, sd->Height, src, 0, 0, sd->Width, sd->Height, flag); +} + +// ======================================================================= + +bool vkClient::clbkBlt(SURFHANDLE tgt, DWORD tgtx, DWORD tgty, SURFHANDLE src, DWORD srcx, DWORD srcy, DWORD w, DWORD h, DWORD flag) const +{ + _TRACE; + return clbkScaleBlt(tgt, tgtx, tgty, w, h, src, srcx, srcy, w, h, flag); +} + +// ======================================================================= + +bool vkClient::clbkScaleBlt (SURFHANDLE tgt, DWORD tgtx, DWORD tgty, DWORD tgtw, DWORD tgth, + SURFHANDLE src, DWORD srcx, DWORD srcy, DWORD srcw, DWORD srch, DWORD flag) const +{ + + if (src==NULL) { oapiWriteLog((char*)"ERROR: oapiBlt() Source surface is NULL"); return false; } + + if (tgt==NULL) tgt = pFramework->GetBackBufferHandle(); + + + RECT rs = _RECT(srcx, srcy, srcx + srcw, srcy + srch); + RECT rt = _RECT(tgtx, tgty, tgtx + tgtw, tgty + tgth); + + + // Can't blit in a clone, declone.. + // + if (SURFACE(tgt)->IsClone()) SURFACE(tgt)->DeClone(); + + // Can't blit in a compressed surface, decompress.. + // + if (!SURFACE(tgt)->Decompress()) + { + HALT(); + } + + POINT tp = { (long)tgtx, (long)tgty }; + + const D3DSURFACE_DESC* td = SURFACE(tgt)->GetDesc(); + const D3DSURFACE_DESC* sd = SURFACE(src)->GetDesc(); + + + // Check failure and abort conditions, Match with know DX7 behavior --------------------- + // + if (rt.right > (long)td->Width || rt.bottom > (long)td->Height) return true; + if (rt.left < 0 || rt.top < 0) return true; + + if (rs.right > (long)sd->Width || rs.bottom > (long)sd->Height) return true; + if (rs.left < 0 || rs.top < 0) return true; + + if (rs.left > rs.right) return true; + if (rt.left > rt.right) return true; + if (rs.top > rs.bottom) return true; + if (rt.top > rt.bottom) return true; + + if (srcw == 0 || srch == 0 || tgtw == 0 || tgth == 0) return true; + + + // Check Blt conditions + // + bool bCK = (SURFACE(src)->GetColorKey() != SURF_NO_CK) && (SURFACE(src)->GetColorKey() != 0); // ColorKey In Use + bool bCL = (srcw != tgtw) || (srch != tgth); // Scaling In Use + bool bSC = SURFACE(src)->IsCompressed(); // Compressed source + + LPDIRECT3DSURFACE9 pss = SURFACE(src)->GetSurface(); + LPDIRECT3DSURFACE9 pts = SURFACE(tgt)->GetSurface(); + + + + if ((sd->Format == td->Format) && !bCK && !bSC) + { + + // Most common case: Target is a render-target and source is in a video memory + // + if ((td->Usage & D3DUSAGE_RENDERTARGET) && (sd->Pool == D3DPOOL_DEFAULT)) + { + if (src != tgt) + { + if (S_OK == pDevice->StretchRect(pss, &rs, pts, &rt, D3DTEXF_POINT)) return true; + + LogErr("oapiBlt() StretchRect() Failed 1"); + BltError(src, tgt, &rs, &rt); + return false; + } + else + { + // Source and Target are the same surface, reroute through temp. + // + LPDIRECT3DSURFACE9 tmp = SURFACE(src)->GetTempSurface(); + + if (S_OK == pDevice->StretchRect(pss, &rs, tmp, &rs, D3DTEXF_POINT)) + { + if (S_OK == pDevice->StretchRect(tmp, &rs, pts, &rt, D3DTEXF_POINT)) return true; + } + + LogErr("oapiBlt() StretchRect() Failed 2"); + BltError(src, tgt, &rs, &rt); + return false; + } + } + } + + if ((sd->Format == td->Format) && !bCK && !bSC && !bCL) + { + + // Texture Update: Source is in system memory and target is a texture + // + if (sd->Pool == D3DPOOL_SYSTEMMEM) + { + if (S_OK == pDevice->UpdateSurface(pss, &rs, pts, &tp)) return true; + + LogErr("oapiBlt() UpdateSurface() Failed"); + BltError(src, tgt, &rs, &rt); + return false; + } + + + // Screen Capture: Target is in system memory and source is a render taeget + // + if ((td->Pool == D3DPOOL_SYSTEMMEM) && (sd->Usage & D3DUSAGE_RENDERTARGET)) + { + if (S_OK == pDevice->GetRenderTargetData(pss, pts)) { + SURFACE(tgt)->Flags |= OAPISURFACE_CAPTURE; + return true; + } + + LogErr("oapiBlt() GetRenderTargetData() Failed"); + BltError(src, tgt, &rs, &rt); + return false; + } + } + + + // Scaling.. Format mismatch.. ColorKey.. Compressed Source.. + // Go for SketchPad + // + if (src != tgt) + { + if ((td->Usage & D3DUSAGE_RENDERTARGET) && (SURFACE(src)->GetType() == D3DRTYPE_TEXTURE) && (sd->Pool == D3DPOOL_DEFAULT)) + { + Sketchpad* pSkp = clbkGetSketchpad_const(tgt); + + if (bCK) + { + if (bCL) ((vkPad*)pSkp)->ColorKeyStretch(src, &rs, &rt); + else pSkp->ColorKey(src, &rs, tgtx, tgty); + clbkReleaseSketchpad_const(pSkp); + return true; + } + else + { + pSkp->StretchRect(src, &rs, &rt); + clbkReleaseSketchpad_const(pSkp); + return true; + } + } + } + + LogErr("oapiBlt() Failed (End)"); + BltError(src, tgt, &rs, &rt); + return false; +} + +// ======================================================================= + +bool vkClient::clbkCopyBitmap(SURFHANDLE pdds, HBITMAP hbm, int x, int y, int dx, int dy) +{ + HDC hdcImage; + HDC hdc; + BITMAP bm; + + if (hbm == NULL || pdds == NULL) return false; + + // Select bitmap into a memoryDC so we can use it. + // + hdcImage = CreateCompatibleDC(NULL); + + if (!hdcImage) OutputDebugString("createcompatible dc failed\n"); + + SelectObject(hdcImage, hbm); + + // Get size of the bitmap + // + GetObject(hbm, sizeof(bm), &bm); + dx = dx == 0 ? bm.bmWidth : dx; // Use the passed size, unless zero + dy = dy == 0 ? bm.bmHeight : dy; + + + // Get size of surface. + // + DWORD surfW = SURFACE(pdds)->GetWidth(); + DWORD surfH = SURFACE(pdds)->GetHeight(); + + RECT r = { 0, 0, (long)surfW, (long)surfH }; + POINT tp = { 0, 0 }; + + if (SURFACE(pdds)->IsGDISurface()) + { + if (hdc = clbkGetSurfaceDC(pdds)) { + StretchBlt(hdc, 0, 0, surfW, surfH, hdcImage, x, y, dx, dy, SRCCOPY); + clbkReleaseSurfaceDC(pdds, hdc); + } + DeleteDC(hdcImage); + SURFACE(pdds)->SetName("clbkCopyBitmap"); + return true; + } + else + { + LPDIRECT3DTEXTURE9 pTemp = NULL; + LPDIRECT3DSURFACE9 pSrf = NULL; + + if (SURFACE(pdds)->IsRenderTarget()) pTemp = SURFACE(pdds)->GetGDICache(0); + else pTemp = SURFACE(pdds)->GetGDICache(OAPISURFACE_SYSMEM); + + if (S_OK == pTemp->GetSurfaceLevel(0, &pSrf)) + { + if (S_OK == pSrf->GetDC(&hdc)) + { + StretchBlt(hdc, 0, 0, surfW, surfH, hdcImage, x, y, dx, dy, SRCCOPY); + + pSrf->ReleaseDC(hdc); + + if (SURFACE(pdds)->IsRenderTarget()) { + HR(pDevice->StretchRect(pSrf, &r, SURFACE(pdds)->GetSurface(), &r, D3DTEXF_LINEAR)); + } + else { + HR(pDevice->UpdateSurface(pSrf, &r, SURFACE(pdds)->GetSurface(), &tp)); + } + + DeleteDC(hdcImage); + pSrf->Release(); + SURFACE(pdds)->SetName("clbkCopyBitmap"); + return true; + } + else { + pSrf->Release(); + assert(false); + } + } + } + DeleteDC(hdcImage); + return false; +} + +// ======================================================================= + +bool vkClient::clbkFillSurface(SURFHANDLE tgt, DWORD col) const +{ + _TRACE; + if (tgt==NULL) tgt = pFramework->GetBackBufferHandle(); + bool ret = SURFACE(tgt)->Fill(NULL, col); + return ret; +} + +// ======================================================================= + +bool vkClient::clbkFillSurface(SURFHANDLE tgt, DWORD tgtx, DWORD tgty, DWORD w, DWORD h, DWORD col) const +{ + _TRACE; + if (tgt==NULL) tgt = pFramework->GetBackBufferHandle(); + RECT r = _RECT(tgtx, tgty, tgtx+w, tgty+h); + bool ret = SURFACE(tgt)->Fill(&r, col); + return ret; +} + +// ======================================================================= + +void vkClient::BltError(SURFHANDLE src, SURFHANDLE tgt, const LPRECT s, const LPRECT t, bool bHalt) const +{ + LogErr("Source Rect (%d,%d,%d,%d) (w=%u,h=%u)", s->left, s->top, s->right, s->bottom, abs(s->left - s->right), abs(s->top - s->bottom)); + LogErr("Target Rect (%d,%d,%d,%d) (w=%u,h=%u)", t->left, t->top, t->right, t->bottom, abs(t->left - t->right), abs(t->top - t->bottom)); + LogErr("Source Data Below: ----------------------------------"); + SURFACE(src)->LogSpecs(); + LogErr("Target Data Below: ----------------------------------"); + SURFACE(tgt)->LogSpecs(); + if (bHalt) HALT(); +} + + + +#pragma endregion + +// ======================================================================= +// GDI functions +// ======================================================================= + +HDC vkClient::clbkGetSurfaceDC(SURFHANDLE surf) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return NULL; + + if (surf == NULL) { + if (Config->GDIOverlay) { + LPDIRECT3DSURFACE9 pGDI = GetScene()->GetBuffer(GBUF_GDI); + HDC hDC; + if (pGDI) if (pGDI->GetDC(&hDC) == S_OK) { + if (bGDIClear) { + bGDIClear = false; + DWORD color = 0xF08040; // BGR "Color Key" value for transparency + HBRUSH hBrush = CreateSolidBrush((COLORREF)color); + RECT r = _RECT( 0, 0, viewW, viewH ); + FillRect(hDC, &r, hBrush); + DeleteObject(hBrush); + } + return hDC; + } + } + return NULL; + } + HDC hDC = SURFACE(surf)->GetDC(); + return hDC; +} + +// ======================================================================= + +void vkClient::clbkReleaseSurfaceDC(SURFHANDLE surf, HDC hDC) +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return; + + if (hDC == NULL) { LogErr("vkClient::clbkReleaseSurfaceDC() Input hDC is NULL"); return; } + if (surf == NULL) { + if (Config->GDIOverlay) { + LPDIRECT3DSURFACE9 pGDI = GetScene()->GetBuffer(GBUF_GDI); + if (pGDI) pGDI->ReleaseDC(hDC); + } + return; + } + SURFACE(surf)->ReleaseDC(hDC); +} + +// ======================================================================= + +bool vkClient::clbkFilterElevation(OBJHANDLE hPlanet, int ilat, int ilng, int lvl, double elev_res, INT16* elev) +{ + _TRACE; + return FilterElevationPhysics(hPlanet, lvl, ilat, ilng, elev_res, elev); +} + +// ======================================================================= + +bool vkClient::clbkSplashLoadMsg (const char *msg, int line) +{ + _TRACE; + return OutputLoadStatus (msg, line); +} + +// ======================================================================= + +lpSurfNative vkClient::GetDefaultTexture() const +{ + return pDefaultTex; +} + +// ======================================================================= + +HWND vkClient::GetWindow() +{ + return pFramework->GetRenderWindow(); +} + +// ======================================================================= + +SURFHANDLE vkClient::GetBackBufferHandle() const +{ + _TRACE; + return pFramework->GetBackBufferHandle(); +} + +// ======================================================================= + +void vkClient::MakeRenderProcCall(Sketchpad *pSkp, DWORD id, const FMATRIX4* pV, const FMATRIX4* pP) +{ + for (auto it = RenderProcs.cbegin(); it != RenderProcs.cend(); ++it) { + if (it->id == id) { + vkPad *pSkp2 = (vkPad *)pSkp; + pSkp2->LoadDefaults(); + if (id == RENDERPROC_EXTERIOR || id == RENDERPROC_PLANETARIUM) { + pSkp2->SetViewMode(Sketchpad::USER); + } + pSkp2->SetViewProj(pV, pP); + it->proc(pSkp, it->pParam); + pSkp2->FlushAll(); // Flush render queue + } + } +} + +// ======================================================================= + +void vkClient::MakeGenericProcCall(DWORD id, int iUser, void *pUser) const +{ + for (auto it = GenericProcs.cbegin(); it != GenericProcs.cend(); ++it) { + if (it->id == id) it->proc(iUser, pUser, it->pParam); + } +} + +// ======================================================================= + +bool vkClient::RegisterRenderProc(__gcRenderProc proc, DWORD id, void *pParam) +{ + if (id) { // register (add) + RenderProcData data = { proc, pParam, id }; + RenderProcs.push_back(data); + return true; + } + else { // unregister, mark as unused (remove later) + for (auto it = RenderProcs.begin(); it != RenderProcs.end(); ++it) { + if (it->proc == proc) { + it->id = 0; + it->pParam = NULL; + it->proc = NULL; + return true; + } + } + } + return false; +} + +// ======================================================================= + +bool vkClient::RegisterGenericProc(__gcGenericProc proc, DWORD id, void *pParam) +{ + if (id) { // register (add) + GenericProcData data = { proc, pParam, id }; + GenericProcs.push_back(data); + return true; + } + else { // unregister, mark as unused (remove later) + for (auto it = GenericProcs.begin(); it != GenericProcs.end(); ++it) { + if (it->proc == proc) { + it->id = 0; + it->pParam = NULL; + it->proc = NULL; + return true; + } + } + } + return false; +} + +// ======================================================================= + +bool vkClient::IsGenericProcEnabled(DWORD id) const +{ + for (const auto &val : GenericProcs) if (val.id == id) return true; + return false; +} + +// ======================================================================= + +void vkClient::WriteLog(const char *msg) const +{ + _TRACE; + char cbuf[256]; + sprintf_s(cbuf, 256, "vk: %s", msg); + oapiWriteLog(cbuf); +} + +// ======================================================================= + +bool vkClient::OutputLoadStatus(const char *txt, int line) +{ + + if (bRunning) return false; + + if (line == 1) strcpy_s(pLoadItem, 127, txt); else + if (line == 0) strcpy_s(pLoadLabel, 127, txt), pLoadItem[0] = '\0'; // New top line => clear 2nd line + + if (pTextScreen) { + + if (pDevice->TestCooperativeLevel()!=S_OK) { + LogErr("TestCooperativeLevel() Failed"); + return false; + } + + RECT txt = _RECT( loadd_x, loadd_y, loadd_x+loadd_w, loadd_y+loadd_h ); + + pDevice->StretchRect(pSplashScreen, &txt, pTextScreen, NULL, D3DTEXF_POINT); + + HDC hDC; + HR(pTextScreen->GetDC(&hDC)); + + HFONT hO = (HFONT)SelectObject(hDC, hLblFont1); + SetTextColor(hDC, pSplashTextColor); + SetBkMode(hDC,TRANSPARENT); + SetTextAlign(hDC, TA_LEFT|TA_TOP); + + TextOut(hDC, 2, 2, pLoadLabel, lstrlen(pLoadLabel)); + + SelectObject(hDC, hLblFont2); + TextOut(hDC, 2, 36, pLoadItem, lstrlen(pLoadItem)); + + HPEN pen = CreatePen(PS_SOLID,1,pSplashTextColor); + HPEN po = (HPEN)SelectObject(hDC, pen); + + MoveToEx(hDC, 0, 32, NULL); + LineTo(hDC, loadd_w, 32); + + SelectObject(hDC, po); + SelectObject(hDC, hO); + DeleteObject(pen); + + HR(pTextScreen->ReleaseDC(hDC)); + HR(pDevice->StretchRect(pSplashScreen, NULL, pBackBuffer, NULL, D3DTEXF_POINT)); + HR(pDevice->StretchRect(pTextScreen, NULL, pBackBuffer, &txt, D3DTEXF_POINT)); + + IDirect3DSwapChain9 *pSwap; + + if (pDevice->GetSwapChain(0, &pSwap)==S_OK) { + pSwap->Present(0, 0, 0, 0, D3DPRESENT_DONOTWAIT); + pSwap->Release(); + return true; + } + + // Prevent "Not Responding" during loading + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg); + } + return false; +} + +// ======================================================================= +void vkClient::clbkSetSplashScreen(const char *filename, DWORD textCol) +{ + pCustomSplashScreen = filename; + pSplashTextColor = textCol; +} + +void vkClient::SplashScreen() +{ + + loadd_x = 279*viewW/1280; + loadd_y = 545*viewH/800; + loadd_w = viewW/3; + loadd_h = 80; + + RECT rS; + + GetWindowRect(hRenderWnd, &rS); + + LogAlw("Splash Window Size = [%u, %u]", rS.right - rS.left, rS.bottom - rS.top); + LogAlw("Splash Window LeftTop = [%d, %d]", rS.left, rS.top); + + HR(pDevice->TestCooperativeLevel()); + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, 0x0, 1.0f, 0L)); + HR(pDevice->CreateOffscreenPlainSurface(loadd_w, loadd_h, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pTextScreen, NULL)); + HR(pDevice->CreateOffscreenPlainSurface(viewW, viewH, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pSplashScreen, NULL)); + + + if(pCustomSplashScreen != NULL) { + D3DXIMAGE_INFO info; + HR(D3DXGetImageInfoFromFile(pCustomSplashScreen, &info)); + + double imageW = info.Width; + double imageH = info.Height; + + double scale = min(viewW / imageW, viewH / imageH); + double _w = (imageW * scale); + double _h = (imageH * scale); + double _l = abs(viewW - _w)/2.0; + double _t = abs(viewH - _h)/2.0; + RECT imgRect = { + static_cast( round(_l) ), + static_cast( round(_t) ), + static_cast( round(_w + _l) ), + static_cast( round(_h + _t) ) + }; + HR(pDevice->ColorFill(pSplashScreen, NULL, D3DCOLOR_XRGB(0, 0, 0))); + HR(D3DXLoadSurfaceFromFile(pSplashScreen, NULL, &imgRect, pCustomSplashScreen, NULL, D3DX_FILTER_LINEAR, 0, NULL)); + } else { + D3DXIMAGE_INFO Info; + HMODULE hOrbiter = GetModuleHandleA("orbiter.exe"); + HRSRC hRes = FindResourceA(hOrbiter, MAKEINTRESOURCEA(292), "IMAGE"); + HGLOBAL hImage = LoadResource(hOrbiter, hRes); + LPVOID pData = LockResource(hImage); + DWORD size = SizeofResource(hOrbiter, hRes); + + // Splash screen image is 1920 x 1200 pixel + double scale = min(viewW / 1920.0, viewH / 1200.0); + double _w = (1920.0 * scale); + double _h = (1200.0 * scale); + double _l = abs(viewW - _w)/2.0; + double _t = abs(viewH - _h)/2.0; + RECT imgRect = { + static_cast( round(_l) ), + static_cast( round(_t) ), + static_cast( round(_w + _l) ), + static_cast( round(_h + _t) ) + }; + HR(pDevice->ColorFill(pSplashScreen, NULL, D3DCOLOR_XRGB(0, 0, 0))); + HR(D3DXLoadSurfaceFromFileInMemory(pSplashScreen, NULL, &imgRect, pData, size, NULL, D3DX_FILTER_LINEAR, 0, &Info)); + } + + HDC hDC; + HR(pSplashScreen->GetDC(&hDC)); + + LOGFONTA fnt; memset((void *)&fnt, 0, sizeof(LOGFONT)); + + fnt.lfHeight = 18; + fnt.lfWeight = 700; + fnt.lfCharSet = ANSI_CHARSET; + fnt.lfOutPrecision = OUT_DEFAULT_PRECIS; + fnt.lfClipPrecision = CLIP_DEFAULT_PRECIS; + fnt.lfQuality = ANTIALIASED_QUALITY; + fnt.lfPitchAndFamily = DEFAULT_PITCH; + strcpy_s(fnt.lfFaceName, "Courier New"); + + HFONT hF = CreateFontIndirect(&fnt); + + HFONT hO = (HFONT)SelectObject(hDC, hF); + SetTextColor(hDC, pSplashTextColor); + SetBkMode(hDC,TRANSPARENT); + + const char *months[]={"???","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","???"}; + + DWORD d = oapiGetOrbiterVersion(); + DWORD y = d/10000; d-=y*10000; + DWORD m = d/100; d-=m*100; + if (m>12) m=0; + + char dataA[256]; + strcpy(dataA, "vkClient"); + if (Config->Enable9On12) strcat_s(dataA, 256, " via vkon12 emulator"); + +#ifdef _DEBUG + strcat_s(dataA, 256, " (Debug Build)"); +#else + strcat_s(dataA, 256, " (Release Build)"); +#endif + + char dataB[128]; sprintf_s(dataB,128,"Build %s %lu 20%lu [%u]", months[m], d, y, oapiGetOrbiterVersion()); + char dataD[] = { "Warning: Config folder not present in /Modules/Server/. Please create symbolic link." }; + //char dataE[] = { "Note: Cubic Interpolation is use... Consider using linear for better elevation matching" }; + //char dataF[] = { "Note: Terrain flattening offline due to cubic interpolation" }; + + int xc = viewW*750/1280; + int yc = viewH*545/800; + + TextOut(hDC, xc, yc + 0*20, "ORBITER Space Flight Simulator",30); + TextOut(hDC, xc, yc + 1*20, dataB, lstrlen(dataB)); + TextOut(hDC, xc, yc + 2*20, dataA, lstrlen(dataA)); + + DWORD VPOS = viewH - 50; + DWORD LSPACE = 20; + + SetTextAlign(hDC, TA_CENTER); + DWORD cattrib = GetFileAttributes("Modules/Server/Config"); + + if ((cattrib&0x10)==0 || cattrib==INVALID_FILE_ATTRIBUTES) { + TextOut(hDC, viewW/2, VPOS, dataD, lstrlen(dataD)); + VPOS -= LSPACE; + } + + SelectObject(hDC, hO); + DeleteObject(hF); + + HR(pSplashScreen->ReleaseDC(hDC)); + + + RECT src = _RECT( loadd_x, loadd_y, loadd_x+loadd_w, loadd_y+loadd_h ); + pDevice->StretchRect(pSplashScreen, &src, pTextScreen, NULL, D3DTEXF_POINT); + pDevice->StretchRect(pSplashScreen, NULL, pBackBuffer, NULL, D3DTEXF_POINT); + pDevice->Present(0, 0, 0, 0); +} + +// ======================================================================= + +HRESULT vkClient::BeginScene() +{ + bRendering = false; + HRESULT hr = pDevice->BeginScene(); + if (hr == S_OK) bRendering = true; + return hr; +} + +// ======================================================================= + +void vkClient::EndScene() +{ + pDevice->EndScene(); + bRendering = false; +} + +// ======================================================================= + +#pragma region Drawing_(Sketchpad)_Interface + + +double sketching_time; + +// ======================================================================= +// 2D Drawing Interface +// +oapi::Sketchpad *vkClient::clbkGetSketchpad_const(SURFHANDLE surf) const +{ + if (ChkDev(__FUNCTION__)) return NULL; + + if (GetCurrentThread() != hMainThread) { + LogErr("Sketchpad called from a worker thread !"); + HALT(); + } + + if (surf == RENDERTGT_MAINWINDOW) surf = GetBackBufferHandle(); + + if (SURFACE(surf)->IsRenderTarget()) + { + // Get Pooled Sketchpad + vkPad *pPad = SURFACE(surf)->GetPooledSketchPad(); + + // Get Current interface if any + vkPad *pCur = GetTopInterface(); + + // Do we have an existing SketchPad interface in use + if (pCur) { + if (pCur == pPad) { + LogErr("Sketchpad already exists for this surface"); + HALT(); + } + pCur->EndDrawing(); // Put the current one in hold + LogDbg("Red", "Switching to another sketchpad in a middle"); + } + + // Push a new Sketchpad onto a stack + PushSketchpad(surf, pPad); + + pPad->BeginDrawing(); + pPad->LoadDefaults(); + + return pPad; + } + else { + HDC hDC = SURFACE(surf)->GetDC(); + if (hDC) return new GDIPad(surf, hDC); + } + + return NULL; +} + +// ======================================================================= +// 2D Drawing Interface +// +oapi::Sketchpad* vkClient::clbkGetSketchpad(SURFHANDLE surf) +{ + return clbkGetSketchpad_const(surf); +} + +// ======================================================================= + +void vkClient::clbkReleaseSketchpad_const(oapi::Sketchpad* sp) const +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return; + + if (!sp) return; + + SURFHANDLE hSrf = sp->GetSurface(); + + if (SURFACE(hSrf)->IsRenderTarget()) { + + vkPad* pPad = ((vkPad*)sp); + + if (GetTopInterface() != pPad) { + LogErr("Sketchpad release failed. Not a top one."); + HALT(); + } + + pPad->EndDrawing(); + + PopRenderTargets(); + + // Do we have an old interface ? + vkPad* pOld = GetTopInterface(); + if (pOld) { + pOld->BeginDrawing(); // Continue with the old one + LogDbg("Red", "Continue Previous Sketchpad"); + } + } + else { + GDIPad* pGDI = (GDIPad*)sp; + SURFACE(hSrf)->ReleaseDC(pGDI->GetDC()); + delete pGDI; + } +} + +// ======================================================================= + +void vkClient::clbkReleaseSketchpad(oapi::Sketchpad *sp) +{ + clbkReleaseSketchpad_const(sp); +} + +// ======================================================================= + +Font *vkClient::clbkCreateFont(int height, bool prop, const char *face, FontStyle style, int orientation) const +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return NULL; + return *g_fonts.insert(new vkPadFont(height, prop, face, style, orientation)).first; +} + +Font* vkClient::clbkCreateFontEx(int height, char* face, int width, int weight, FontStyle style, float spacing) const +{ + _TRACE; + if (ChkDev(__FUNCTION__)) return NULL; + return *g_fonts.insert(new vkPadFont(height, face, width, weight, style, spacing)).first; +} + + +// ======================================================================= + +void vkClient::clbkReleaseFont(Font *font) const +{ + _TRACE; + if (!g_fonts.count(font)) return; + g_fonts.erase(font); + delete ((vkPadFont*)font); +} + +// ======================================================================= + +Pen *vkClient::clbkCreatePen(int style, int width, DWORD col) const +{ + _TRACE; + return *g_pens.insert(new vkPadPen(style, width, col)).first; +} + +// ======================================================================= + +void vkClient::clbkReleasePen(Pen *pen) const +{ + _TRACE; + if (!g_pens.count(pen)) return; + g_pens.erase(pen); + delete ((vkPadPen*)pen); +} + +// ======================================================================= + +Brush *vkClient::clbkCreateBrush(DWORD col) const +{ + _TRACE; + return *g_brushes.insert(new vkPadBrush(col)).first; +} + +// ======================================================================= + +void vkClient::clbkReleaseBrush(Brush *brush) const +{ + _TRACE; + if (!g_brushes.count(brush)) return; + g_brushes.erase(brush); + delete ((vkPadBrush*)brush); +} + +#pragma endregion + + +// ====================================================================== +// class VisObject + +VisObject::VisObject(OBJHANDLE hObj) : hObj(hObj) +{ + _TRACE; +} + +// ======================================================================= + +VisObject::~VisObject () +{ +} + +// ======================================================================= + +SHADOWMAP::SHADOWMAP(LPDIRECT3DDEVICE9 pDevice, sMapType t, DWORD sz) : SMapInput(), tp(t) +{ + if (Config->ShadowMapMode == 0) return; + + // Single fixed size shadow map + if (tp == sMapType::SingleLod) { + HR(pDevice->CreateTexture(sz, sz, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptShmRT[0], NULL)); + HR(ptShmRT[0]->GetSurfaceLevel(0, &psShmRT[0])); + } + + // Exterior shadows + if (tp == sMapType::MultiLod) { + UINT size = Config->ShadowMapSize; + for (int i = 0; i < SHM_LOD_COUNT; i++) { + HR(pDevice->CreateTexture(size, size, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptShmRT[i], NULL)); + HR(ptShmRT[i]->GetSurfaceLevel(0, &psShmRT[i])); + size >>= 1; + } + } + + // VC Shadows + if (tp == sMapType::Cascaded) { + UINT size = Config->ShadowMapSize; + for (int i = 0; i < SHM_CASCADE_COUNT; i++) { + HR(pDevice->CreateTexture(size, size, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptShmRT[i], NULL)); + HR(ptShmRT[i]->GetSurfaceLevel(0, &psShmRT[i])); + } + } +} + +// ======================================================================= + +SHADOWMAP::~SHADOWMAP() +{ + for (auto& x : psShmRT) SAFE_RELEASE(x); + for (auto& x : ptShmRT) SAFE_RELEASE(x); +} diff --git a/OVP/VulkanClient/Client.h b/OVP/VulkanClient/Client.h new file mode 100644 index 000000000..bb0c3c9cd --- /dev/null +++ b/OVP/VulkanClient/Client.h @@ -0,0 +1,1595 @@ +// ============================================================== +// D3DClient.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __vkCLIENT_H +#define __vkCLIENT_H + +// must be defined before windows includes to fix warnins on VS 2003+ +#if defined(_MSC_VER) && (_MSC_VER >= 1300 ) // Microsoft Visual Studio Version 2003 and higher +//#define _CRT_SECURE_NO_DEPRECATE +//#define _CRT_NONSTDC_NO_WARNINGS +#include +#else // older MSVC++ versions +#include +#endif + +#include +#include "MathAPI.h" +#include "Catalog.h" +#include "GraphicsAPI.h" +#include "Util.h" +#include +#include +#include "OrbiterAPI.h" +#include "Frame.h" +//#include "gcCore.h" +#include +#include +#include +#include "WindowMgr.h" + +#define SHM_CASCADE_COUNT 3 +#define SHM_LOD_COUNT 6 + + +#define PP_DEFAULT 0x1 +#define PP_LENSFLARE 0x2 + +#define MAX_SCENE_LIGHTS (DWORD)24 +#define MAX_MESH_LIGHTS 8 // Must match the setting in vkClient.fx + +#ifdef _NVAPI_H +extern StereoHandle pStereoHandle; +#endif + +class vkConfig; +class MeshManager; +class TextureManager; +class Scene; +class VideoTab; +class SurfNative; +class CD3DFramework9; +class vkMesh; +class vkAnnotation; +class vkText; +class CSphereManager; +class FileParser; +class OapiExtension; +class vkPad; + +typedef char * LPCHAR; +typedef void * CAMERAHANDLE; +typedef class vkMesh * HMESH; +typedef class SurfNative* lpSurfNative; + + +enum class EnvCamType { Undefined, Exterior, Interior, Mesh }; +enum class ShdPackage { None, Main, VC, Stage }; + + +/** + * \brief Storage structure to keep reflection camera information. + */ +struct ENVCAMREC +{ + ~ENVCAMREC() + { + SAFE_RELEASE(pCube); + SAFE_RELEASE(pIrrad); + SAFE_RELEASE(pPlane); + oapiWriteLog("ENVCAMREC::Destruct"); + } + + std::vector omitAttc = {}; + std::vector omitDock = {}; + DWORD flags = 0; ///< Camera flags + EnvCamType type = EnvCamType::Undefined; + FVECTOR3 lPos = { 0,0,0 }; ///< Camera local position + FVECTOR3 lDir = { 1,0,0 }; ///< Camera local direction (in 'PLANE' mode only) + float near_clip = 0.1f; ///< Near clip-plane distance + float da_curve = 0.4f; + float da_bounch = 0.35f; + float da_force = 0.2f; + int mesh_idx = -1; ///< Camera is attached to a mesh + int group_idx = -1; ///< Camera is attached to a group + int id = -1; ///< User Id, for binding + BYTE iSide = 0; ///< [Private] Current side being rendered + bool bRendered = false; ///< [Private] Rendering of camera view is completed + LPDIRECT3DCUBETEXTURE9 pCube = nullptr; ///< Reflection cube map + LPDIRECT3DTEXTURE9 pIrrad = nullptr; ///< Irradiance map (baraboloidal) + LPDIRECT3DTEXTURE9 pPlane = nullptr; ///< Reflection 2D map if (flags & ENVCAM_PLANE) +}; + +/** + * \brief Statistical data storage + */ +struct _vkStats { + + _vkStats() + { + memset(&Mesh, 0, sizeof(Mesh)); + memset(&Timer, 0, sizeof(Timer)); + TilesAllocated = 0; + } + + struct { + DWORD Vertices; ///< Number of vertices rendered + DWORD MeshGrps; ///< Number of mesh groups rendered + DWORD Meshes; ///< Number of meshes rendered + DWORD TexChanges; ///< Number of texture changes + DWORD MtrlChanges; ///< Number of material changes + } Mesh; ///< Mesh related statistics + + struct { + vkTime Update; ///< clbkUpdate + vkTime Scene; ///< clbkRenderScene + vkTime Display; ///< clbkDisplayFrame + vkTime FrameTotal; ///< Total frame time + //------------------------------------------------------------ + vkTime HUDOverlay; ///< Total time spend in HUD, 2D Panel overlay + vkTime CamVis; ///< Object/camera updates + vkTime Surface; ///< Surface + vkTime Clouds; ///< Clouds + //------------------------------------------------------------- + vkTime LockWait; ///< Time waiting GetDC or vertex buffer lock + vkTime BlitTime; ///< + vkTime GetDC; ///< + } Timer; ///< Render timing related statistics + + DWORD TilesAllocated; ///< Number of allocated tiles + std::map TilesRendered; ///< Number of rendered tiles +}; + + + +struct RenderTgtData { + LPDIRECT3DSURFACE9 pColor; + LPDIRECT3DSURFACE9 pDepthStencil; + class vkPad *pSkp; + int code; +}; + +struct PickProp { + vkMesh* pMesh; // Mesh to pick, or NULL for full scene + float fnear; // Near clip distance, ignore entities closer than this + bool bDualSided; // Pick also back-facing triangles +}; + +struct SMapInput { + FVECTOR3 pos = { }; ///< Shadow map center in camera centric coords (ecl frame) + FVECTOR3 ld = { }; ///< Direction of sunlight (ecl frame) + float rad = { }; ///< Radius of shadow mapped area +}; + +class SHADOWMAP : public SMapInput +{ +public: + + enum class sMapType { MultiLod, Cascaded, SingleLod }; + + SHADOWMAP(LPDIRECT3DDEVICE9 pDevice, sMapType tp, DWORD sz = 1024); + ~SHADOWMAP(); + + LPDIRECT3DTEXTURE9 Map(int idx) const + { + if (tp == sMapType::MultiLod) { + if (idx == 0) return ptShmRT[lod]; + else return nullptr; + } + return ptShmRT[idx]; + } + + void Clear() { bValid = false; } + bool IsValid() const { return bValid && (Map(0) != nullptr); } + + + + FMATRIX4 mVP[SHM_CASCADE_COUNT] = {}; + FVECTOR4 Subrect[SHM_CASCADE_COUNT] = {}; + FVECTOR4 SubrectTF[SHM_CASCADE_COUNT] = {}; + FVECTOR2 Center[SHM_CASCADE_COUNT] = {}; + float SubPx[SHM_CASCADE_COUNT] = {}; + FMATRIX4 mLVP = {}; + float dist = 0.0f; // Shadow camera distance from shadow origin + float depth = 0.0f; // near to far plane distance. i.r. depth of the field + int lod = 0; // level of detail, 0 = highest + int size = 0; // Map size in pixels + int cascades = 0; // Number of active cascades + bool bValid = false; + sMapType tp; + + + // Cascades are located in entries 0, 1, 2 + // Active entry in MultiLod map is located in index pointed by 'lod' + // Active entry in SingleLod map is in index 0, 'lod' entry is 0 + LPDIRECT3DSURFACE9 psShmRT[max(SHM_LOD_COUNT, SHM_CASCADE_COUNT)] = { nullptr }; + LPDIRECT3DTEXTURE9 ptShmRT[max(SHM_LOD_COUNT, SHM_CASCADE_COUNT)] = { nullptr }; +}; + +struct LVLH { + FVECTOR3 Up; + FVECTOR3 North; + FVECTOR3 East; +}; + + +extern _vkStats vkStats; +extern bool bFreeze; +extern bool bFreezeEnable; +extern bool bFreezeRenderAll; +extern DWORD g_uCurrentMesh; +extern class vObject* g_pCurrentVisual; +extern set MeshCatalog; +extern set SurfaceCatalog; +extern IDirect3D9* g_pD3DObject; +extern Memgr* g_pMemgr_f; +extern Memgr* g_pMemgr_i; +extern Memgr* g_pMemgr_u; +extern Memgr* g_pMemgr_w; +extern Memgr* g_pMemgr_vtx; +extern Texmgr* g_pTexmgr_tt; +extern Vtxmgr* g_pVtxmgr_vb; +extern Idxmgr* g_pIdxmgr_ib; + + +namespace oapi { + + + +// ============================================================== +// vkClient class interface +// The DX9 render client for Orbiter +// ============================================================== + +class vkClient : public GraphicsClient +{ + + friend class ::Scene; // <= likes to call Render2DOverlay() + +public: + + /** + * \brief Create a graphics object. + * + * The graphics object is typically created during module initialisation + * (see \ref InitModule). Once the client is created, it must be registered + * with the Orbiter core via the oapiRegisterGraphicsClient function. + * \param hInstance module instance handle (as passed to InitModule) + */ + explicit vkClient (HINSTANCE hInstance); + + /** + * \brief Destroy the graphics object. + * + * Usually, the graphics object is destroyed when the module is unloaded + * (see opcDLLExit), after is has been detached from the Orbiter core + * via a call to oapiUnregisterGraphicsClient. + */ + ~vkClient (); + + + HBITMAP gcReadImageFromFile(const char *path); + + /** + * \brief Perform any one-time setup tasks. + * + * This includes enumerating drivers, graphics modes, etc. + * Derived classes should also call the base class method to allow + * default setup. + * \default Initialises the VideoData structure from the Orbiter.cfg + * file + * \par Calling sequence: + * Called during processing of oapiRegisterGraphicsClient, after the + * Launchpad Video tab has been inserted (if clbkUseLaunchpadVideoTab + * returns true). + */ + bool clbkInitialise (); + + /** + * \brief Request for video configuration data + * + * Called by Orbiter before the render window is opened or configuration + * parameters are written to file. Applications should here either update + * the provided VIDEODATA structure from any user selections made in the + * Launchpad Video tab and leave it to Orbiter to write these parameters + * to Orbiter.cfg, or write the current video settings to their own + * configuration file. + * \default None. + */ + void clbkRefreshVideoData (); + + /** + * \brief Called when a config setting is changed by the user during + * a simulation setting, to give the client opportunity to respond to the change. + * \param cat option category, see \ref optcat + * \param item option item, see \ref optitem + */ + void clbkOptionChanged(DWORD cat, DWORD item); + + void clbkDebugString(const char* str); + + /** + * \brief Texture request + * + * Load a texture from a file into a device-specific texture object, and + * return a generic SURFHANDLE for it. Derived classes should overload this + * method to add texture support. + * Usually, the client should read Orbiter's default texture files (in + * DXT? format). However, the client also has the option to load its own + * texture files stored in a different format, and pass them back via the + * SUFHANDLE interface. + * \param fname texture file name with path relative to orbiter + * texture folders; can be used as input for OpenTextureFile. + * \param flags request for texture properties + * \return Texture handle, cast into generic SURFHANDLE, or NULL if texture + * could not be loaded. + * \default Return NULL. + * \note If the client loads its own of texture files, they can either be + * installed in the default locations, replacing Orbiter's set of + * textures, or stored alongside the original textures, using different + * names or directory locations. In the latter case, the fname parameter + * passed to clbkLoadTexture must be adapted accordingly (for example, + * by replacing the dds extension with jpg, or by adding an 'OGL/' + * prefix to the path name, etc). Not overwriting the original texture + * set has the advantage that other graphics clients relying on the + * original textures can still be used. + * \note The following flags are supported: + * - bit 0 set: force creation in system memory + * - bit 1 set: decompress, even if format is supported by device + * - bit 2 set: don't load mipmaps, even if supported by device + * - bit 3 set: load as global resource (can be managed by graphics client) + * \note If bit 3 of flags is set, orbiter will not try to modify or release + * the texture. The client should manage the texture (i.e. keep it in a + * repository and release it at destruction). Any further call of + * clbkLoadTexture should first scan the repository. If the texture is + * already present, the function should just return a pointer to it. + */ + SURFHANDLE clbkLoadTexture (const char *fname, DWORD flags = 0); + + /** + * \brief Load a surface from file into a surface object, and return a SURFHANDLE for it. + * \param fname texture file name with path relative to orbiter texture folders + * \param attrib \ref surfacecaps (see notes) + * \return A SURFHANDLE for the loaded surface, for example a pointer to the surface object. + * \note If the request refers to a static surface that has already be loaded, or if the + * client buffers the unmodified surfaces after loading, it can simply return a handle to + * the existing surface object, instead of reading it again from file. + * \note The attrib bitflag can contain one of the following main attributes: + * - OAPISURFACE_RO: Load the surface to be readable by the GPU pipeline + * - OAPISURFACE_RW: Load the surface to be readable and writable by the GPU pipeline + * - OAPISURFACE_GDI: Load the surface to be readable and writable by the CPU, and can be blitted into an uncompressed RO or RW surface without alpha channel + * - OAPISURFACE_STATIC: Load the surface to be readable by the GPU pipeline + * In addition, the flag can contain any of the following auxiliary attributes: + * - OAPISURFACE_MIPMAPS: Load the mipmaps for the surface from file, or create them if necessary + * - OAPISURFACE_NOMIPMAPS: Don't load mipmaps, even if they are available in the file + * - OAPISURFACE_NOALPHA: Load the surface without an alpha channel + * - OAPISURFACE_UNCOMPRESS: Uncompress the surface on loading. + * \sa oapiCreateSurface(DWORD,DWORD,DWORD) + */ + SURFHANDLE clbkLoadSurface (const char *fname, DWORD attrib, bool bPath = false); + SURFHANDLE clbkLoadMaps(const char* diff, const char* maps, bool bPath, SURFHANDLE hOld = NULL, bool bAll = true); + + + + /** + * \brief Save the contents of a surface to a formatted image file or to the clipboard + * \param surf surface handle (0 for primary render surface) + * \param fname image file path relative to orbiter root directory (excluding file extension), or NULL to save to clipboard + * \param fmt output file format + * \param quality quality request if the format supports it (0-1) + * \return Should return true on success + * \default Nothing, returns false + * \note Implementations can make use of the \ref WriteImageDataToFile method to write to + * a file in the desired format once a pointer to the image data in 24-bit uncompressed + * format has been obtained. + */ + bool clbkSaveSurfaceToImage (SURFHANDLE surf, const char *fname, ImageFileFormat fmt, float quality=0.7f); + + /** + * \brief Write surface to file (sub-function of \ref clbkSaveSurfaceToImage) + * + * \param surface size and format description + * \param fname image file path relative to orbiter root directory (excluding file extension) + * \param fmt output file format + * \param quality quality request if the format supports it (0-1) + * \return Should return true on success + */ + bool SaveSurfaceToFile (const D3DSURFACE_DESC* desc, D3DLOCKED_RECT& pRect, + const char* fname, ImageFileFormat fmt, float quality); + + /** + * \brief Store device-dependent bitmap to clipboard (sub-function of \ref clbkSaveSurfaceToImage) + * + * \param surface size and format description + * \return Should return true on success + */ + bool SaveSurfaceToClipboard (const D3DSURFACE_DESC * desc); + + /** + * \brief Texture release request + * + * Called by Orbiter when a previously loaded texture can be released + * from memory. The client can use the appropriate device-specific method + * to release the texture. + * \param hTex texture handle + * \default None. + */ + void clbkReleaseTexture (SURFHANDLE hTex); + + /** + * \brief Replace a texture in a device-specific mesh. + * \param hMesh device mesh handle + * \param texidx texture index (>= 0) + * \param tex texture handle + * \return Should return \e true if operation successful, \e false otherwise. + * \default None, returns \e false. + */ + bool clbkSetMeshTexture (DEVMESHHANDLE hMesh, DWORD texidx, SURFHANDLE tex); + + /** + * \brief Replace properties of an existing mesh material. + * \param hMesh device mesh handle + * \param matidx material index (>= 0) + * \param mat pointer to material structure + * \return Overloaded functions should return an integer error flag, with + * the following codes: 0="success", 3="invalid mesh handle", 4="material index out of range" + * \default, None, returns 2 ("client does not support operation"). + */ + int clbkSetMeshMaterial(DEVMESHHANDLE hMesh, DWORD matidx, const MATERIAL* mat); + int clbkSetMeshMaterialEx(DEVMESHHANDLE hMesh, DWORD matidx, MatProp mat, const oapi::FVECTOR4* in); + + /** + * \brief Retrieve the properties of one of the mesh materials. + * \param hMesh device mesh handle + * \param matidx material index (>= 0) + * \param mat [out] pointer to MATERIAL structure to be filled by the method. + * \return true if successful, false on error (index out of range) + * \default None, returns 2 ("client does not support operation"). + */ + int clbkMeshMaterial(DEVMESHHANDLE hMesh, DWORD matidx, MATERIAL* mat); + int clbkMeshMaterialEx(DEVMESHHANDLE hMesh, DWORD matidx, MatProp mat, oapi::FVECTOR4* out); + + /** + * \brief Set custom properties for a device-specific mesh. + * \param hMesh device mesh handle + * \param property property tag + * \param value new mesh property value + * \return The method should return \e true if the property tag was recognised + * and the request could be executed, \e false otherwise. + * \note Currently only a single mesh property request type will be sent, but this may be + * extended in future versions: + * - \c MESHPROPERTY_MODULATEMATALPHA \n \n + * if value==0 (default) disable material alpha information in textured mesh groups (only use texture alpha channel).\n + * if value<>0 modulate (mix) material alpha values with texture alpha maps. + * \default None, returns \e false. + */ + bool clbkSetMeshProperty (DEVMESHHANDLE hMesh, DWORD property, DWORD value); + void clbkSetVisualProperty(VISHANDLE vis, VisualProp prp, int idx, const type_info& t, const void* val); + + /** + * \brief React to vessel docking, attaching events + * \param hVesselA object handle of first vessel + * \param hVesselB object handle of second vessel + * \param type Event type + */ + void clbkScenarioChanged(OBJHANDLE hVessel, ScnChgEvent type); + + /** + * \brief React to vessel creation + * \param hVessel object handle of new vessel + * \note Calls Scene::NewVessel() to check for visual + */ + void clbkNewVessel (OBJHANDLE hVessel); + + /** + * \brief React to vessel destruction + * \param hVessel object handle of vessel to be destroyed + * \note Calls Scene::DeleteVessel() to remove the visual + */ + void clbkDeleteVessel (OBJHANDLE hVessel); + + + /** + * \brief Message callback for a visual object. + * \param hObj handle of the object that created the message + * \param vis client-supplied identifier for the visual + * \param msg event identifier + * \param context message context + * \return Function should return 1 if it processes the message, 0 otherwise. + * \default None, returns 0. + * \note Messages are generated by Orbiter for objects that have been + * registered with \ref RegisterVisObject by the client, until they are + * un-registered with \ref UnregisterVisObject. + * \note Currently only vessel objects create visual messages. + * \note For currently supported event types, see \ref visevent. + * \note The \e vis pointer passed to this function is the same as that provided + * by RegisterVisObject. It can be used by the client to identify the visual + * object for which the message was created. + * \sa RegisterVisObject, UnregisterVisObject, visevent + */ + int clbkVisEvent (OBJHANDLE hObj, VISHANDLE vis, DWORD msg, DWORD_PTR context); + + /** + * \brief Return a DEVMESHHANDLE handle for a visual, defined by its index. (! Return type incorrect !) + * \param vis visual identifier + * \param idx mesh index (>= 0) + * \return Mesh handle (client-specific) + * \note Derived clients should return a handle that identifies a + * mesh for the visual (in client-specific format). + * \note Orbiter calls this method in response to a \ref VESSEL::GetMesh + * call by an vessel module. + */ + virtual MESHHANDLE clbkGetMesh (VISHANDLE vis, UINT idx); + + /** + * \brief Mesh group data retrieval interface for device-specific meshes. + * \param hMesh device mesh handle + * \param grpidx mesh group index (>= 0) + * \param grs data buffers and buffer size information. See \ref oapiGetMeshGroup + * for details. + * \return Should return 0 on success, or error flags > 0. + * \default None, returns -2. + */ + int clbkGetMeshGroup (DEVMESHHANDLE hMesh, DWORD grpidx, GROUPREQUESTSPEC *grs); + + /** + * \brief Mesh group editing interface for device-specific meshes. + * \param hMesh device mesh handle + * \param grpidx mesh group index (>= 0) + * \param ges mesh group modification specs + * \return Should return 0 on success, or error flags > 0. + * \default None, returns -2. + * \note Clients should implement this method to allow the modification + * of individual groups in a device-specific mesh. Modifications may + * include vertex values, index lists, texture and material indices, + * and user flags. + */ + int clbkEditMeshGroup (DEVMESHHANDLE hMesh, DWORD grpidx, GROUPEDITSPEC *ges); + + + // ================================================================== + /// \name Dialog interface + //@{ + /** + * \brief Popup window open notification. + * \note This method is called just before a popup window (e.g. dialog + * box) is opened. It allows the client to prepare for subsequent + * rendering of the window, if necessary. + */ + void clbkPreOpenPopup (); + + //@} + + // ================================================================== + /// \name Particle stream methods + // @{ + + /** + * \brief Create a generic particle stream. + * \param pss particle stream parameters + * \return Pointer to new particle stream. + * \default None, returns NULL. Derived classes should overload this method + * to return a ParticleStream-derived class instance in order to support + * particle streams. + * \sa ParticleStream + */ + ParticleStream *clbkCreateParticleStream (PARTICLESTREAMSPEC *pss); + + /** + * \brief Create a particle stream associated with a vessel. + * + * Typically used for exhaust and plasma effects, but can also be used + * for other types of particles. + * \param pss particle stream parameters + * \param hVessel vessel handle + * \param lvl pointer to exhaust level control variable + * \param ref pointer to stream source position (vessel frame) [m] + * \param dir pointer to stream direction (vessel frame) + * \return Pointer to new particle stream + * \default None, returns NULL. Derived classes should overload this method + * to return a ParticleStream-derived class instance in order to support + * exhaust streams. + * \note The lvl, ref and dir parameters may be modified by orbiter after + * the stream has been created, e.g. to reflect changes in engine thrust + * level or gimballing. + */ + ParticleStream *clbkCreateExhaustStream (PARTICLESTREAMSPEC *pss, OBJHANDLE hVessel, const double *lvl, const VECTOR3 *ref, const VECTOR3 *dir); + + /** + * \brief Create a particle stream associated with a vessel. + * + * Typically used for exhaust and plasma effects, but can also be used + * for other types of particles. + * \param pss particle stream parameters + * \param hVessel vessel handle + * \param lvl pointer to exhaust level control variable + * \param ref pointer to stream source position (vessel frame) [m] + * \param dir pointer to stream direction (vessel frame) + * \return Pointer to new particle stream + * \default None, returns NULL. Derived classes should overload this method + * to return a ParticleStream-derived class instance in order to support + * exhaust streams. + * \note The lvl parameter may be modified by orbiter after + * the stream has been created, e.g. to reflect changes in engine thrust + * level. + * \note The ref and dir parameters are fixed in this version of the method. + */ + ParticleStream *clbkCreateExhaustStream (PARTICLESTREAMSPEC *pss, OBJHANDLE hVessel, const double *lvl, const VECTOR3 &ref, const VECTOR3 &dir); + + /** + * \brief Create a vessel particle stream for reentry heating effect + * \param pss particle stream parameters + * \param hVessel vessel handle + * \return Pointer to new particle stream + * \default None, returns NULL. Derived classes should overload this method + * to return a ParticleStream-derived class instance in order to support + * reentry streams. + */ + ParticleStream *clbkCreateReentryStream (PARTICLESTREAMSPEC *pss, OBJHANDLE hVessel); + // @} + + /** + * \brief Create an annotation object for displaying on-screen text. + * \return Pointer to new screen annotation object. + * \default Dynamically allocates a 'ScreenAnnotation' instance and returns + * a pointer to it. + */ + ScreenAnnotation* clbkCreateAnnotation(); + + /** + * \brief Render window message handler + * + * Derived classes should also call the base class method to allow + * default message processing. + * \param hWnd render window handle + * \param uMsg Windows message identifier + * \param wParam WPARAM message parameter + * \param lParam LPARAM message parameter + * \return The return value depends on the message being processed. + * \note This is the standard Windows message handler for the render + * window. + * \note This method currently intercepts only the WM_CLOSE and WM_DESTROY + * messages, and passes everything else to the Orbiter core message + * handler. + */ + LRESULT RenderWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + /** + * \brief Message handler for 'video' tab in Orbiter Launchpad dialog + * + * Overload this method to display and retrieve video parameters using + * the Launchpad video tab. This method acts like a standard Windows dialog + * message handler. + * \param hWnd window handle for video tab + * \param uMsg Windows message + * \param wParam WPARAM message value + * \param lParam LPARAM message value + * \return The return value depends on the message type and the action taken. + * \default Do nothing, return FALSE. + */ + INT_PTR LaunchpadVideoWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + /** + * \brief Fullscreen mode flag + * \return true if the client is set up for running in fullscreen + * mode, false for windowed mode. + */ + bool clbkFullscreenMode () const; + /** + * \brief Returns the dimensions of the render viewport + * \param width render viewport width [pixel] + * \param height render viewport height [pixel] + * \note This function is called by orbiter after the render window or + * fullscreen renderer has been created (see \ref clbkCreateRenderWindow). + * \note This should normally return the screen resolution in fullscreen + * mode, and the size of the render window client area in windowed mode, + * clients can also return smaller values if they only use part of the + * screen area for scene rendering. + */ + void clbkGetViewportSize (DWORD *width, DWORD *height) const; + + /** + * \brief Returns a specific render parameter + * \param[in] prm parameter identifier (see \sa renderprm) + * \param[out] value value of the queried parameter + * \return true if the specified parameter is supported by the client, + * false if not. + */ + bool clbkGetRenderParam (DWORD prm, DWORD *value) const; + + /** + * \brief Render an instrument panel in cockpit view as a 2D billboard. + * \param hSurf array of texture handles for the panel surface + * \param hMesh billboard mesh handle + * \param T transformation matrix for panel mesh vertices (2D) + * \param additive If true, panel should be rendered additive (transparent) + * \default None. + * \note The texture index of each group in the mesh is interpreted as index into the + * hSurf array. Special indices are TEXIDX_MFD0 and above, which specify the + * surfaces representing the MFD displays. These are obtained separately and + * don't need to be present in the hSurf list. + * \note The \e additive flag is used when rendering the default "glass + * cockpit" if the user requested. "transparent MFDs". The renderer can + * then use e.g. additive blending for rendering the panel. + */ + void clbkRender2DPanel (SURFHANDLE *hSurf, MESHHANDLE hMesh, MATRIX3 *T, bool transparent = false); + + /** + * \brief Render an instrument panel in cockpit view as a 2D billboard. + * \param hSurf array of texture handles for the panel surface + * \param hMesh billboard mesh handle + * \param T transformation matrix for panel mesh vertices (2D) + * \param alpha opacity value, between 0 (transparent) and 1 (opaque) + * \param additive If true, panel should be rendered additive (transparent) + * \default None. + * \note The texture index of each group in the mesh is interpreted as index into the + * hSurf array. Special indices are TEXIDX_MFD0 and above, which specify the + * surfaces representing the MFD displays. These are obtained separately and + * don't need to be present in the hSurf list. + * \note The \e additive flag is used when rendering the default "glass + * cockpit" if the user requested. "transparent MFDs". The renderer can + * then use e.g. additive blending for rendering the panel. + */ + void clbkRender2DPanel (SURFHANDLE *hSurf, MESHHANDLE hMesh, MATRIX3 *T, float alpha, bool additive = false); + + // ================================================================== + /// \name Surface-related methods + // @{ + + /** + * \brief Create a surface for texturing, as a blitting source, etc. + * + * Surfaces are used for offscreen bitmap and texture manipulation, + * blitting and rendering. + * Derived classes should create a device-specific surface, and + * return a cast to a generic Orbiter SURFHANDLE. + * \param w surface width [pixels] + * \param h surface height [pixels] + * \param attrib \ref surfacecaps (bitflags). See notes. + * \return Surface handle (in the simplest case, just a pointer to the + * surface, cast to a SURFHANDLE). On failure, this method should + * return NULL. + * \default None, returns NULL. + * \note The attribute flag can contain one of the following main attributes: + * - OAPISURFACE_RO: create a surface that can be read by the GPU pipeline, and that can be updated from system memory. + * - OAPISURFACE_RW: create a surface that can be read and written by the GPU pipeline, and that can be updated from system memory. + * - OAPISURFACE_GDI: create a surface that can be read and written from the CPU, and can be blitted into an uncompressed RO or RW surface without an alpha channel + * In addition, the flag can contain any combination of the following auxiliary attributes: + * - OAPISURFACE_MIPMAPS: create a full chain of mipmaps for the surface if possible + * - OAPISURFACE_NOALPHA: create a surface without an alpha channel + */ + SURFHANDLE clbkCreateSurfaceEx (DWORD w, DWORD h, DWORD attrib); + + /** + * \brief Create an offscreen surface + * + * Surfaces are used for offscreen bitmap and texture manipulation, + * blitting and rendering. + * Derived classes should create a device-specific surface, and + * return a cast to a generic Orbiter SURFHANDLE. + * \param w surface width [pixels] + * \param h surface height [pixels] + * \param hTemplate surface format template + * \return pointer to surface, cast into a SURFHANDLE, or NULL to + * indicate failure. + * \default None, returns NULL. + * \note If \e hTemplate is provided, this method should create the new + * surface with the same pixel format. + * \sa clbkCreateTexture, clbkReleaseSurface + */ + SURFHANDLE clbkCreateSurface (DWORD w, DWORD h, SURFHANDLE hTemplate = NULL); + + /** + * \brief Create a texture for rendering + * \param w texture width + * \param h texture height + * \return pointer to texture, returned as generic SURFHANDLE. NULL + * indicates failure. + * \note This method is similar to \ref clbkCreateSurface, but the + * returned surface handle must be usable as a texture when rendering + * the scene. Clients which don't differentiate between offscreen + * surfaces and textures may use identical code for both functions. + * \note Some clients may put restrictions on the texture format (e.g. + * require square size (w=h), and/or powers of two (w=2^n). If the + * texture cannot be created with the requested size, this method + * should return NULL. + * \sa clbkCreateSurface, clbkReleaseSurface + */ + SURFHANDLE clbkCreateTexture (DWORD w, DWORD h); + + /** + * \brief Create an offscreen surface from a bitmap + * \param hBmp bitmap handle + * \return surface handle, or NULL to indicate failure + * \default Creates a surface of the same size as the bitmap, and + * uses clbkCopyBitmap to copy the bitmap over. + * \note The reference counter for the new surface is set to 1. + * \sa clbkIncrSurfaceRef, clbkReleaseSurface + */ + SURFHANDLE clbkCreateSurface (HBITMAP hBmp); + + /** + * \brief Increment the reference counter of a surface. + * \param surf surface handle + * \default None. + * \note Derived classes should keep track on surface references, and + * overload this function to increment the reference counter. + */ + void clbkIncrSurfaceRef (SURFHANDLE surf); + + /** + * \brief Decrement surface reference counter, release surface if counter + * reaches 0. + * \param surf surface handle + * \return true on success + * \default None, returns false. + * \note Derived classes should overload this function to decrement a + * surface reference counter and release the surface if required. + * \sa clbkCreateSurface, clbkIncrSurfaceRef + */ + bool clbkReleaseSurface (SURFHANDLE surf); + + /** + * \brief Return the width and height of a surface + * \param[in] surf surface handle + * \param[out] w surface width + * \param[out] h surface height + * \return true if surface dimensions could be obtained. + * \default Sets w and h to 0 and returns false. + * \sa clbkCreateSurface + */ + bool clbkGetSurfaceSize (SURFHANDLE surf, DWORD *w, DWORD *h); + + /** + * \brief Set transparency colour key for a surface. + * \param surf surface handle + * \param ckey transparency colour key value + * \default None, returns false. + * \note Derived classes should overload this method if the renderer + * supports colour key transparency for surfaces. + */ + bool clbkSetSurfaceColourKey (SURFHANDLE surf, DWORD ckey); + + /** + * \brief Convert an RGB colour triplet into a device-specific colour value. + * \param r red component + * \param g green component + * \param b blue component + * \return colour value + * \note Derived classes should overload this method to convert RGB colour + * definitions into device-compatible colour values, taking into account + * the colour depth of the render device etc. + * \default Packs the RGB values into a DWORD of the form 0x00RRGGBB, with + * 8 bits per colour component. + * \sa clbkFillSurface + */ + DWORD clbkGetDeviceColour (BYTE r, BYTE g, BYTE b); + // @} + + // ================================================================== + /// \name Surface blitting methods + // @{ + + /** + * \brief Copy one surface into an area of another one. + * \param tgt target surface handle + * \param tgtx left edge of target rectangle + * \param tgty top edge of target rectangle + * \param src source surface handle + * \param flag blitting parameters (see notes) + * \return true on success, false if the blit cannot be performed. + * \default None, returns false. + * \note By convention, tgt==NULL is valid and refers to the primary render + * surface (e.g. for copying 2-D overlay surfaces). + * \note The following bit-flags are defined: + * + * + * + *
BLT_SRCCOLORKEYUse the colour key defined by the source surface for transparency
BLT_TGTCOLORKEYUse the colour key defined by the target surface for transparency
+ * If a client doesn't support some of the flags, it should quietly ignore it. + * \sa clbkBlt(SURFHANDLE,DWORD,DWORD,SURFHANDLE,DWORD,DWORD,DWORD,DWORD,DWORD) + */ + bool clbkBlt (SURFHANDLE tgt, DWORD tgtx, DWORD tgty, SURFHANDLE src, DWORD flag = 0) const; + + /** + * \brief Copy a rectangle from one surface to another. + * \param tgt target surfac handle + * \param tgtx left edge of target rectangle + * \param tgty top edge of target rectangle + * \param src source surface handle + * \param srcx left edge of source rectangle + * \param srcy top edge of source rectangle + * \param w width of rectangle + * \param h height of rectangle + * \param flag blitting parameters (see notes) + * \return true on success, false if the blit cannot be performed. + * \default None, returns false. + * \note By convention, tgt==NULL is valid and refers to the primary render + * surface (e.g. for copying 2-D overlay surfaces). + * \note The following bit-flags are defined: + * + * + * + *
BLT_SRCCOLORKEYUse the colour key defined by the source surface for transparency
BLT_TGTCOLORKEYUse the colour key defined by the target surface for transparency
+ * If a client doesn't support some of the flags, it should quietly ignore it. + * \sa clbkBlt(SURFHANDLE,DWORD,DWORD,SURFHANDLE,DWORD) + */ + bool clbkBlt (SURFHANDLE tgt, DWORD tgtx, DWORD tgty, SURFHANDLE src, DWORD srcx, DWORD srcy, DWORD w, DWORD h, DWORD flag = 0) const; + + /** + * \brief Copy a rectangle from one surface to another, stretching or shrinking as required. + * \param tgt target surface handle + * \param tgtx left edge of target rectangle + * \param tgty top edge of target rectangle + * \param tgtw width of target rectangle + * \param tgth height of target rectangle + * \param src source surface handle + * \param srcx left edge of source rectangle + * \param srcy top edge of source rectangle + * \param srcw width of source rectangle + * \param srch height of source rectangle + * \param flag blitting parameters + * \return true on success, fals if the blit cannot be performed. + * \default None, returns false. + * \note By convention, tgt==NULL is valid and refers to the primary render + * surface (e.g. for copying 2-D overlay surfaces). + * \sa clbkBlt(SURFHANDLE,DWORD,DWORD,SURFHANDLE,DWORD), + * clbkBlt(SURFHANDLE,DWORD,DWORD,SURFHANDLE,DWORD,DWORD,DWORD,DWORD,DWORD) + */ + bool clbkScaleBlt (SURFHANDLE tgt, DWORD tgtx, DWORD tgty, DWORD tgtw, DWORD tgth, + SURFHANDLE src, DWORD srcx, DWORD srcy, DWORD srcw, DWORD srch, DWORD flag = 0) const; + + + /** + * \brief Begins a block of blitting operations to the same target surface. + * \param tgt Target surface for subsequent blitting calls. + * \return Should return an error code (0 on success, the return value from + * the base class call, or a client-specific code) + * \default If no target is currently set, stores tgt in surfBltTgt and + * returns 0. Returns -2 if a target was already set. Returns -3 if + * tgt==RENDERTGT_NONE + * \note All blitting calls following this function use the same target until + * \ref clbkEndBltGroup is called. Clients which have to perform initialisations + * for a new blitting target can do this here, and then assume for the + * following oapiBlt calls that the target is already initialised. + * \note The special target RENDERTGT_MAINWINDOW refers to the main render surface. + * \note Within the blitting block, multiple source surfaces may be used. No + * particular order or grouping according to source surface can be guaranteed. + * \note Blitting calls using a different target within a blitting group are not + * filtered out. It is up to the client how to handle this (honour the call, + * ignore it or throw an error) + * \sa clbkEndBltGroup + */ + int clbkBeginBltGroup (SURFHANDLE tgt); + + /** + * \brief Ends a block of blitting operations to the same target surface. + * \return Should return an error code (0 on success, the return value from + * the base class call, or a client-specific code) + * \default If surfBltTgt was set, clears it (to RENDERTGT_NONE) and returns 0. + * Otherwise returns -2; + * \sa clbkBeginBltGroup + */ + int clbkEndBltGroup (); + + /** + * \brief Fill a surface with a uniform colour + * \param surf surface handle + * \param col colour value + * \return true on success, false if the fill operation cannot be performed. + * \default None, returns false. + * \note Parameter col is a device-dependent colour value + * (see \ref clbkGetDeviceColour). + * \sa clbkFillSurface(SURFHANDLE,DWORD,DWORD,DWORD,DWORD,DWORD) + */ + bool clbkFillSurface (SURFHANDLE surf, DWORD col) const; + + /** + * \brief Fill an area in a surface with a uniform colour + * \param surf surface handle + * \param tgtx left edge of target rectangle + * \param tgty top edge of target rectangle + * \param w width of rectangle + * \param h height of rectangle + * \param col colour value + * \return true on success, false if the fill operation cannot be performed. + * \default None, returns false. + * \note Parameter col is a device-dependent colour value + * (see \ref clbkGetDeviceColour). + * \sa clbkFillSurface(SURFHANDLE,DWORD) + */ + bool clbkFillSurface (SURFHANDLE surf, DWORD tgtx, DWORD tgty, DWORD w, DWORD h, DWORD col) const; + + + /** + * \brief Copy a bitmap object into a surface + * \param pdds surface handle + * \param hbm bitmap handle + * \param x left edge of source bitmap area to be copied + * \param y top edge of source bitmap area to be copied + * \param dx width of source bitmap area to be copied + * \param dy height of source bitmap area to be copied + * \return \e true on success, \e false if surface or bitmap handle are invalid. + * \note The source bitmap area is stretched as required to fit the area of + * the target surface. + */ + bool clbkCopyBitmap (SURFHANDLE pdds, HBITMAP hbm, int x, int y, int dx, int dy); + // @} + + + // ================================================================== + /// \name 2-D drawing interface + //@{ + /** + * \brief Create a 2-D drawing object ("sketchpad") associated with a surface. + * \param surf surface handle + * \return Pointer to drawing object. + * \default None, returns NULL. + * \note Clients should overload this function to provide 2-D drawing + * support. This requires an implementation of a class derived from + * \ref Sketchpad which provides the drawing context and drawing + * primitives. + * \sa Sketchpad, clbkReleaseSketchpad + */ + Sketchpad* clbkGetSketchpad_const(SURFHANDLE surf) const; + Sketchpad * clbkGetSketchpad (SURFHANDLE surf); + + /** + * \brief Release a drawing object. + * \param sp pointer to drawing object + * \default None. + * \sa Sketchpad, clbkGetSketchpad + */ + void clbkReleaseSketchpad_const(Sketchpad* sp) const; + void clbkReleaseSketchpad (Sketchpad *sp); + + /** + * \brief Create a font resource for 2-D drawing. + * \param height cell or character height [pixel] + * \param prop proportional/fixed width flag + * \param face font face name + * \param style font decoration style + * \param orientation text orientation [1/10 deg] + * \return Pointer to font resource + * \default None, returns NULL. + * \note For a description of the parameters, see Font constructor + * \ref oapi::Font::Font + * \sa clbkReleaseFont, oapi::Font + */ + Font* clbkCreateFont(int height, bool prop, const char* face, FontStyle style = FontStyle::FONT_NORMAL, int orientation = 0) const; + Font* clbkCreateFontEx(int height, char* face, int width, int weight, FontStyle style, float spacing) const; + + /** + * \brief De-allocate a font resource. + * \param font pointer to font resource + * \default None. + * \sa clbkCreateFont, oapi::Font + */ + void clbkReleaseFont (Font *font) const; + + /** + * \brief Create a pen resource for 2-D drawing. + * \param style line style (0=invisible, 1=solid, 2=dashed) + * \param width line width [pixel] + * \param col line colour (format: 0xBBGGRR) + * \return Pointer to pen resource + * \default None, returns NULL. + * \sa clbkReleasePen, oapi::Pen + */ + Pen *clbkCreatePen (int style, int width, DWORD col) const; + + /** + * \brief De-allocate a pen resource. + * \param pen pointer to pen resource + * \default None. + * \sa clbkCreatePen, oapi::Pen + */ + void clbkReleasePen (Pen *pen) const; + + /** + * \brief Create a brush resource for 2-D drawing. + * \param col line colour (format: 0xBBGGRR) + * \return Pointer to brush resource + * \default None, returns NULL. + * \sa clbkReleaseBrush, oapi::Brush + */ + Brush *clbkCreateBrush (DWORD col) const; + + /** + * \brief De-allocate a brush resource. + * \param brush pointer to brush resource + * \default None. + * \sa clbkCreateBrush, oapi::Brush + */ + void clbkReleaseBrush (Brush *brush) const; + //@} + + + // ================================================================== + /// \name GDI-related methods + // @{ + + /** + * \brief Return a Windows graphics device interface handle for a surface + * \param surf surface handle + * \return GDI handle, or NULL on failure + * \default None, returns NULL. + * \note Clients which can obtain a Windows GDI handle for a surface should + * overload this method. + * \todo This method should be moved into the GDIClient class + */ + HDC clbkGetSurfaceDC (SURFHANDLE surf); + + /** + * \brief Release a Windows graphics device interface + * \param surf surface handle + * \param hDC GDI handle + * \default None. + * \note Clients which can obtain a Windows GDI handle for a surface should + * overload this method to release an existing GDI. + * \todo This method should be moved into the GDIClient class + */ + void clbkReleaseSurfaceDC (SURFHANDLE surf, HDC hDC); + // @} + + /** + * \brief Filter elevation grid data + * \param hPlanet object handle of the planet the data belongs to + * \param ilat patch latitude index + * \param ilng patch longitude index + * \param lvl patch resolution level + * \param elev_res elevation level resolution + * \param elev pointer to array with elevation grid data + * \default None. + * \note Clients that manipulate elevation file data in memory (e.g. for flattening + * features) for visuals should overload this method in order to manipulate the + * terrain collision data in the same way. As soon as the internal collision tile + * is loaded in the core, the callback is invoked. + */ + bool clbkFilterElevation(OBJHANDLE hPlanet, int ilat, int ilng, int lvl, double elev_res, INT16* elev); + // @} + + + HWND GetRenderWindow () const { return hRenderWnd; } + CD3DFramework9 * GetFramework() const { return pFramework; } + Scene * GetScene() const { return scene; } + MeshManager * GetMeshMgr() const { return meshmgr; } + void WriteLog (const char *msg) const; + LPDIRECT3DDEVICE9 GetDevice() const { return pDevice; } + lpSurfNative GetDefaultTexture() const; + SURFHANDLE GetBackBufferHandle() const; + LPDIRECT3DTEXTURE9 GetNoiseTex() const { return pNoiseTex; } + void SplashScreen(); + inline bool IsControlPanelOpen() const { return bControlPanel; } + inline bool IsRunning() const { return bRunning; } + inline bool IsLimited() const { return ((pCaps->TextureCaps&D3DPTEXTURECAPS_POW2) && (pCaps->TextureCaps&D3DPTEXTURECAPS_NONPOW2CONDITIONAL)); } + HWND GetWindow(); + bool HasVertexTextureSupport() const { return bVertexTex; } + const D3DCAPS9 * GetHardwareCaps() const { return pCaps; } + //FileParser * GetFileParser() const { return parser; } + LPDIRECT3DSURFACE9 GetBackBuffer() const { return pBackBuffer; } + LPDIRECT3DSURFACE9 GetDepthStencil() const { return pDepthStencil; } + const void * GetConfigParam (DWORD paramtype) const; + bool RegisterRenderProc(__gcRenderProc proc, DWORD id, void *pParam = NULL); + bool RegisterGenericProc(__gcGenericProc proc, DWORD id, void *pParam = NULL); + void MakeRenderProcCall(Sketchpad *pSkp, DWORD id, const FMATRIX4* pV, const FMATRIX4* pP); + void MakeGenericProcCall(DWORD id, int iUser, void *pUser) const; + bool IsGenericProcEnabled(DWORD id) const; + void SetScenarioName(const std::string &path) { scenarioName = path; }; + void HackFriendlyHack(); + void PickTerrain(DWORD uMsg, int xpos, int ypos); + DEVMESHHANDLE GetDevMesh(MESHHANDLE hMesh); + HANDLE GetMainThread() const { return hMainThread; } + + + // ================================================================== + // + HRESULT BeginScene(); + void EndScene(); + bool IsInScene() const { return bRendering; } + void PushSketchpad(SURFHANDLE surf, vkPad* pSkp) const; + void PushRenderTarget(LPDIRECT3DSURFACE9 pColor, LPDIRECT3DSURFACE9 pDepthStencil = NULL, int code = 0) const; + void AlterRenderTarget(LPDIRECT3DSURFACE9 pColor, LPDIRECT3DSURFACE9 pDepthStencil = NULL); + void PopRenderTargets() const; + LPDIRECT3DSURFACE9 GetTopDepthStencil(); + LPDIRECT3DSURFACE9 GetTopRenderTarget(); + class vkPad * GetTopInterface() const; + + +protected: + + /** \brief Launchpad video tab indicator + * + * Indicate if the the default video tab in the Orbiter launchpad dialog + * is to be used for obtaining user video preferences. If a derived + * class returns false here, the video tab is not shown. + * \return true if the module wants to use the video tab in the launchpad + * dialog, false otherwise. + * \default Return true. + */ + bool clbkUseLaunchpadVideoTab () const; + + /** + * \brief Simulation session start notification + * + * Called at the beginning of a simulation session to allow the client + * to create the 3-D rendering window (or to switch into fullscreen + * mode). + * \return Should return window handle of the rendering window. + * \default For windowed mode, opens a window of the size specified by the + * VideoData structure (for fullscreen mode, opens a small dummy window) + * and returns the window handle. + * \note For windowed modes, the viewW and viewH parameters should return + * the window client area size. For fullscreen mode, they should contain + * the screen resolution. + * \note Derived classes should perform any required per-session + * initialisation of the 3D render environment here. + */ + HWND clbkCreateRenderWindow (); + + /** + * \brief Simulation startup finalisation + * + * Called at the beginning of a simulation session after the scenarion has + * been parsed and the logical object have been created. + * \default None + */ + void clbkPostCreation (); + + /** + * \brief End of simulation session notification + * + * Called before the end of a simulation session. At the point of call, + * logical objects still exist (OBJHANDLEs valid), and external modules + * are still loaded. + * \param fastclose Indicates a "fast shutdown" request (see notes) + * \default None. + * \note Derived clients can use this function to perform cleanup operations + * for which the simulation objects are still required. + * \note If fastclose == true, the user has selected one of the fast + * shutdown options (terminate Orbiter, or respawn Orbiter process). In + * this case, the current process will terminate, and the graphics client + * can skip object cleanup and deallocation in order to speed up the + * closedown process. + * \sa clbkDestroyRenderWindow + */ + void clbkCloseSession (bool fastclose); + + /** + * \brief Render window closure notification + * + * Called at the end of a simulation session to allow the client to close + * the 3-D rendering window (or to switch out of fullscreen mode) and + * clean up the session environment. At the point of call, all logical + * simulation objects have been destroyed, and object modules have been + * unloaded. This method should not access any OBJHANDLE or VESSEL + * objects any more. For closedown operations that require access to the + * simulation objects, use clbkCloseSession instead. + * \param fastclose Indicates a "fast shutdown" request (see notes) + * \default None. + * \note Derived classes should perform any required cleanup of the 3D + * render environment here. + * \note The user may change the video parameters before starting a new + * simulation session. Therefore, device-specific options should be + * destroyed and re-created at the start of the next session. + * \note If fastclose == true, the user has selected one of the fast + * shutdown options (terminate Orbiter, or respawn Orbiter process). In + * this case, the current process will terminate, and the graphics client + * can skip object cleanup and deallocation in order to speed up the + * closedown process. + * \sa clbkCloseSession + */ + void clbkDestroyRenderWindow (bool fastclose); + + /** + * \brief Per-frame update notification + * + * Called once per frame, after the logical world state has been updated, + * but before clbkRenderScene(), to allow the client to perform any + * logical state updates. + * \param running true if simulation is running, false if paused. + * \default None. + * \note Unlike clbkPreStep and clbkPostStep, this method is also called + * while the simulation is paused. + */ + void clbkUpdate (bool running); + + /** + * \brief Per-frame render notification + * + * Called once per frame, after the logical world state has been updated, + * to allow the client to render the current scene. + * \note This method is also called continuously while the simulation is + * paused, to allow camera panning (although in that case the logical + * world state won't change between frames). + * \note After the 3D scene has been rendered, this function should call + * \ref Render2DOverlay to initiate rendering of 2D elements (2D instrument + * panel, HUD, etc.) + */ + void clbkRenderScene (); + + /** + * \brief React to a discontinous jump in simulation time. + * \param simt new simulation time relative to session start [s] + * \param simdt jump interval [s] + * \param mjd new absolute simulation time in MJD format [days] + * \note Currently, this method does nothing. + */ + void clbkTimeJump (double simt, double simdt, double mjd); + + /** + * \brief Display a scene on screen after rendering it. + * + * Called after clbkRenderScene to allow the client to display the rendered + * scene (e.g. by page-flipping, or blitting from background to primary + * frame buffer. This method can also be used by the client to display any + * top-level 2-D overlays (e.g. dialogs) on the primary frame buffer. + * \return Should return true on successful operation, false on failure or + * if no operation was performed. + * \default None, returns false. + */ + bool clbkDisplayFrame (); + + /** + * \brief Display a load status message on the splash screen + * + * Called repeatedly while a simulation session is loading, to allow the + * client to echo load status messages on its splash screen if desired. + * \param msg Pointer to load status message string + * \param line message line to be displayed (0 or 1), where 0 indicates + * a group or category heading, and 1 indicates an individual action + * relating to the most recent group. + * \return Should return true if it displays the message, false if not. + * \default None, returns false. + */ + bool clbkSplashLoadMsg (const char *msg, int line); + + void clbkSetSplashScreen(const char *filename, DWORD textCol) override; + + /** + * \brief Store a persistent mesh template + * + * Called when a plugin loads a mesh with oapiLoadMeshGlobal, to allow the + * client to store a copy of the mesh in client-specific format. Whenever + * the mesh is required later, the client can create an instance as a copy + * of the template, rather than creating it by converting from Orbiter's + * mesh format. + * \param hMesh mesh handle + * \param fname mesh file name + * \default None. + * \note Use \ref oapiMeshGroup to to obtain mesh data and convert them to + * a suitable format. + * \note the mesh templates loaded with \ref oapiLoadMeshGlobal are shared between + * all vessel instances and should never be edited. Vessels should make + * individual copies of the mesh before modifying them (e.g. for animations) + * \note The file name is provide to allow the client to parse the mesh directly + * from file, rather than copying it from the hMesh object, or to use an + * alternative mesh file. + * \note The file name contains a path relative to Orbiter's main mesh + * directory. + */ + void clbkStoreMeshPersistent (MESHHANDLE hMesh, const char *fname); + + /** + * \brief Renders the fullscreen viewport in the presence of popup windows. + * \return \e true if render window has been updated and no more page flipping + * is required, \e false if page still needs to be flipped. + */ + bool RenderWithPopupWindows (); + + /// \brief Output 2D graphics on top of the render window + /// + /// Obtains the GDI of the render surface to output 2D data after + /// rendering the 3D scene (glass cockpit, date info, etc.) + //void Output2DOverlay (); + +public: + + /** + * \brief Displays a message on the splash screen. + * \param msg Message to be displayed. + * \param line line number (0 or 1) + * \return true if load status was initialised, false if not. + * \sa clbkSplashLoadMsg + */ + bool OutputLoadStatus (const char *msg, int line); + +private: + void BltError(SURFHANDLE src, SURFHANDLE tgt, const LPRECT s, const LPRECT t, bool bHalt = true) const; + void SketchPadTest(); + void PresentScene(); + void Label(const char *format, ...); + void DrawTimeBar(double t, double scale, double frames, DWORD color, const char *label=NULL); + bool ChkDev(const char *fnc) const; + + SURFHANDLE pBltGrpTgt; + vkPad* pBltSkp; + + + LPDIRECT3DDEVICE9 pDevice; + lpSurfNative pDefaultTex; + lpSurfNative pScatterTest; + LPDIRECT3DTEXTURE9 pNoiseTex; + LPDIRECT3DSURFACE9 pSplashScreen; + LPDIRECT3DSURFACE9 pTextScreen; + LPDIRECT3DSURFACE9 pBackBuffer; + LPDIRECT3DSURFACE9 pDepthStencil; + CD3DFramework9 * pFramework; + const D3DCAPS9 * pCaps; + std::string scenarioName; + HANDLE hMainThread; + WindowManager * pWM; + const char * pCustomSplashScreen; + DWORD pSplashTextColor; + + HWND hRenderWnd; // render window handle + + bool bControlPanel; + bool bScatterUpdate; + bool bFullscreen; // fullscreen render mode flag + bool bAAEnabled; + bool bFailed; + bool bRunning; + bool bVertexTex; + bool bVSync; + bool bRendering; + bool bGDIClear; + + DWORD viewW, viewH; // dimensions of the render viewport + DWORD viewBPP; // bit depth of render viewport + DWORD frame_timer; + + // device enumeration callback function + + VideoTab *vtab; // video selection user interface + Scene *scene; // Scene description + + MeshManager *meshmgr; // mesh manager + FMATRIX4 ident; + + struct RenderProcData; + struct GenericProcData; + + std::vector RenderProcs; + std::vector GenericProcs; + mutable std::list RenderStack; + + HFONT hLblFont1; + HFONT hLblFont2; + + char pLoadLabel[128]; + char pLoadItem[128]; + + // Control Panel + void RenderControlPanel(); + bool ControlPanelMsg(WPARAM wParam); + + Sketchpad *pItemsSkp; + + DWORD loadd_x, loadd_y, loadd_w, loadd_h; + int LabelPos; + +}; // class vkClient + + +// ====================================================================== +// class VisObject +// ====================================================================== +/** + * \brief Visual object representation. + * + * A VisObject is the visual representation of an Orbiter object (vessel, + * planet, etc.). The 'logical' object representation resides in the Orbiter + * core, while its 'visual' representation is located in the graphics client. + * + * Visual representations should be non-permanent: they should be created + * when the object enters the visual range of the camera, and deleted when + * they leave it. + * + * Only a single VisObject instance should be created per object, even if + * the visual is present in multiple views. If the graphics client supports + * multiple views, the view-specific parameters (e.g. visibility flags) should + * be implemented by the client, e.g. by deriving a class from VisObject that + * holds an array of the view-specific data. In that case, the VisObject should + * be created when the object becomes visible in any one view, and destroyed + * when the object disappears from the last view. + */ +class VisObject { +public: + /** + * \brief Creates a visual for object hObj. + * \param hObj object handle + * \sa oapi::GraphicsClient::RegisterVisObject + */ + explicit VisObject (OBJHANDLE hObj); + + /** + * \brief Destroys the visual. + * \sa oapi::GraphicsClient::UnregisterVisObject + */ + virtual ~VisObject (); + + /** + * \brief Returns the object handle associated with the visual. + * \return Object handle + */ + OBJHANDLE GetObjHandle () const { return hObj; } + + /** + * \brief Message callback. + * \param event message identifier + * \param context message content (message-specific) + * \default None. + * \note This method is called by the Orbiter core to notify the visual + * of certain events (e.g. adding and deleting meshes) + * \note For currently supported event types, see \ref visevent. + */ + virtual void clbkEvent (DWORD event, DWORD_PTR context) {} + +protected: + OBJHANDLE hObj; ///< Object handle associated with the visual +}; + +}; // namespace oapi + + + +class RenderState +{ +public: + + RenderState(LPDIRECT3DDEVICE9 pD) : pDev(pD) + { + bkABE = bkZEN = bkZW = bkCULL = 0; + bkCW = bkSE = bkFM = bkSTE = bkATE = 0; + bkBO = bkSB = bkDB = 0; + bCaptured = false; + } + + void Capture() + { + bCaptured = true; + HR(pDev->GetScissorRect(&bkSR)); + HR(pDev->GetRenderState(D3DRS_ALPHABLENDENABLE, &bkABE)); + HR(pDev->GetRenderState(D3DRS_ZENABLE, &bkZEN)); + HR(pDev->GetRenderState(D3DRS_ZWRITEENABLE, &bkZW)); + HR(pDev->GetRenderState(D3DRS_CULLMODE, &bkCULL)); + HR(pDev->GetRenderState(D3DRS_COLORWRITEENABLE, &bkCW)); + HR(pDev->GetRenderState(D3DRS_SCISSORTESTENABLE, &bkSE)); + HR(pDev->GetRenderState(D3DRS_FILLMODE, &bkFM)); + HR(pDev->GetRenderState(D3DRS_STENCILENABLE, &bkSTE)); + HR(pDev->GetRenderState(D3DRS_ALPHATESTENABLE, &bkATE)); + HR(pDev->GetRenderState(D3DRS_BLENDOP, &bkBO)); + HR(pDev->GetRenderState(D3DRS_SRCBLEND, &bkSB)); + HR(pDev->GetRenderState(D3DRS_DESTBLEND, &bkDB)); + } + + void Restore() + { + assert(bCaptured); + HR(pDev->SetScissorRect(&bkSR)); + HR(pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, bkABE)); + HR(pDev->SetRenderState(D3DRS_ZENABLE, bkZEN)); + HR(pDev->SetRenderState(D3DRS_ZWRITEENABLE, bkZW)); + HR(pDev->SetRenderState(D3DRS_CULLMODE, bkCULL)); + HR(pDev->SetRenderState(D3DRS_COLORWRITEENABLE, bkCW)); + HR(pDev->SetRenderState(D3DRS_SCISSORTESTENABLE, bkSE)); + HR(pDev->SetRenderState(D3DRS_FILLMODE, bkFM)); + HR(pDev->SetRenderState(D3DRS_STENCILENABLE, bkSTE)); + HR(pDev->SetRenderState(D3DRS_ALPHATESTENABLE, bkATE)); + HR(pDev->SetRenderState(D3DRS_BLENDOP, bkBO)); + HR(pDev->SetRenderState(D3DRS_SRCBLEND, bkSB)); + HR(pDev->SetRenderState(D3DRS_DESTBLEND, bkDB)); + } + + RECT bkSR = {}; + DWORD bkABE, bkZEN, bkZW, bkCULL, bkCW, bkSE, bkFM, bkSTE, bkATE, bkBO, bkSB, bkDB; + LPDIRECT3DDEVICE9 pDev; + bool bCaptured; +}; + +#endif // !__vkCLIENT_H diff --git a/OVP/VulkanClient/Client.rc b/OVP/VulkanClient/Client.rc new file mode 100644 index 000000000..c17daa989 --- /dev/null +++ b/OVP/VulkanClient/Client.rc @@ -0,0 +1,560 @@ +// Generated by ResEdit 1.6.6 +// Copyright (C) 2006-2015 +// http://www.resedit.net + +#include +#include +#include +#include "resource.h" + + + + +// +// Bitmap resources +// +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +IDB_BITMAP1 BITMAP ".\\res\\Undo.bmp" + + + +// +// Dialog resources +// +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +IDD_vkCREDITS DIALOG 0, 0, 451, 321 +STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU +CAPTION "VulkanClient Credits" +FONT 8, "Ms Shell Dlg" +{ + DEFPUSHBUTTON "Close", IDOK, 415, 305, 34, 14, 0, WS_EX_LEFT + CONTROL "Rich Edit", IDC_CREDITSTEXT, RICHEDIT_CLASS, WS_TABSTOP | WS_VSCROLL | WS_BORDER | ES_AUTOVSCROLL | ES_NUMBER | ES_MULTILINE | ES_READONLY, 9, 6, 435, 295, WS_EX_STATICEDGE +} + + + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +IDD_vkMESHDEBUG DIALOG 0, 0, 373, 465 +STYLE DS_3DLOOK | DS_CENTER | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_THICKFRAME | WS_SYSMENU +EXSTYLE WS_EX_WINDOWEDGE +CAPTION "Debug Controls" +FONT 8, "Ms Shell Dlg" +{ + GROUPBOX "Mesh Options and Flags", IDC_STATIC, 5, 165, 176, 60, 0, WS_EX_LEFT + PUSHBUTTON "Close", IDCANCEL, 124, 429, 51, 14, 0, WS_EX_LEFT + PUSHBUTTON "Open", IDC_DBG_OPEN, 387, 34, 30, 14, 0, WS_EX_LEFT + PUSHBUTTON "Execute", IDC_DBG_EXECUTE, 490, 147, 56, 14, 0, WS_EX_LEFT + LTEXT "Selected Visual: Cape Canaveral", IDC_DBG_VISUAL, 7, 2, 104, 8, SS_LEFT, WS_EX_LEFT + COMBOBOX IDC_DBG_DISPLAY, 48, 27, 120, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Display", IDC_STATIC, 18, 30, 24, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Camera", IDC_STATIC, 18, 43, 25, 11, SS_LEFT, WS_EX_LEFT + COMBOBOX IDC_DBG_CAMERA, 48, 42, 120, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + EDITTEXT IDC_DBG_MESH, 54, 73, 44, 12, ES_AUTOHSCROLL, WS_EX_LEFT + LTEXT "Group Idx", IDC_STATIC, 18, 92, 32, 12, SS_LEFT, WS_EX_LEFT + LTEXT "Mesh Idx", IDC_STATIC, 18, 76, 30, 8, SS_LEFT, WS_EX_LEFT + EDITTEXT IDC_DBG_GROUP, 54, 89, 44, 12, WS_GROUP | ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_FILE, 421, 35, 125, 12, WS_GROUP | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + AUTOCHECKBOX "Highlight selected group", IDC_DBG_HSG, 18, 107, 92, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Highlight selected mesh", IDC_DBG_HSM, 18, 117, 91, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Boxes", IDC_DBG_BOXES, 208, 217, 35, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Normalize", IDC_DBG_NORM, 400, 79, 47, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Mipmap fade", IDC_DBG_FADE, 400, 92, 57, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Reduce seams", IDC_DBG_SEAMS, 460, 79, 63, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Spheres", IDC_DBG_SPHERES, 208, 226, 42, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Surface Tiles", IDC_DBG_TILEBB, 208, 235, 57, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Selected group only", IDC_DBG_GRPO, 274, 217, 79, 8, 0, WS_EX_LEFT + GROUPBOX "Bounding geometry", IDC_STATIC, 195, 205, 175, 45, 0, WS_EX_LEFT + GROUPBOX "Visualize local light cones", IDC_STATIC, 195, 255, 175, 35, 0, WS_EX_LEFT + GROUPBOX "Selection and Display Options", IDC_STATIC, 5, 15, 175, 145, 0, WS_EX_LEFT + GROUPBOX "Misc.", IDC_STATIC, 195, 10, 173, 70, 0, WS_EX_LEFT + GROUPBOX "Scene Debugger", IDC_STATIC, 195, 85, 173, 115, 0, WS_EX_LEFT + GROUPBOX "Texture Tools", IDC_STATIC, 380, 15, 173, 151, 0, WS_EX_LEFT + AUTOCHECKBOX "Selected mesh only", IDC_DBG_MSHO, 274, 226, 77, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Dual sided", IDC_DBG_DUAL, 115, 107, 49, 8, 0, WS_EX_LEFT + PUSHBUTTON "<", IDC_DBG_GRPUP, 102, 90, 9, 12, BS_CENTER, WS_EX_LEFT + PUSHBUTTON ">", IDC_DBG_GRPDN, 114, 90, 9, 12, BS_CENTER, WS_EX_LEFT + PUSHBUTTON "<", IDC_DBG_MSHUP, 102, 73, 9, 12, BS_CENTER, WS_EX_LEFT + PUSHBUTTON ">", IDC_DBG_MSHDN, 114, 73, 9, 12, BS_CENTER, WS_EX_LEFT + AUTOCHECKBOX "Selected visual only", IDC_DBG_VISO, 274, 235, 79, 8, 0, WS_EX_LEFT + CONTROL "", IDC_DBG_SPEED, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 42, 58, 108, 13, WS_EX_LEFT + CONTROL "", IDC_DBG_RESBIAS, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_BOTH, 205, 55, 155, 20, WS_EX_LEFT + LTEXT "Speed", IDC_STATIC, 18, 58, 22, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Static", IDC_DBG_SPEEDDSP, 150, 58, 21, 10, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "Add ambient light", IDC_DBG_AMBIENT, 18, 127, 70, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Wireframe", IDC_DBG_WIRE, 115, 117, 48, 8, 0, WS_EX_LEFT + GROUPBOX "Material", IDC_DBG_MATGRP, 5, 230, 175, 165, 0, WS_EX_LEFT + COMBOBOX IDC_DBG_MATPRP, 77, 240, 96, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_DEFSHADER, 75, 177, 96, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_CONES, 200, 270, 165, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Material property", IDC_STATIC, 18, 242, 53, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Mesh Shader", IDC_STATIC, 15, 179, 43, 9, SS_LEFT, WS_EX_LEFT + EDITTEXT IDC_DBG_RED, 31, 260, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_GREEN, 67, 260, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_BLUE, 103, 260, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_ALPHA, 139, 260, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_VARA, 465, 127, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_VARC, 415, 127, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_VARB, 515, 127, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + COMBOBOX IDC_DBG_ENVMAP, 275, 115, 86, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_SCENEDBG, 255, 100, 106, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_ACTION, 450, 58, 95, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_TARGET, 420, 107, 126, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Debug Mode", IDC_STATIC, 205, 102, 42, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Display special buffer", IDC_STATIC, 205, 117, 68, 9, SS_LEFT, WS_EX_LEFT + LTEXT "Resolution Bias", IDC_STATIC, 205, 40, 50, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Select Action", IDC_STATIC, 401, 60, 43, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Target", IDC_STATIC, 392, 109, 25, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Fb:", IDC_STATIC, 502, 129, 11, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Fa:", IDC_STATIC, 452, 129, 11, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Noise:", IDC_STATIC, 390, 129, 21, 8, SS_LEFT, WS_EX_LEFT + CONTROL "", IDC_DBG_MATADJ, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 19, 277, 156, 14, WS_EX_LEFT + PUSHBUTTON "Save materials", IDC_DBG_MATSAVE, 5, 429, 51, 14, 0, WS_EX_LEFT + PUSHBUTTON "Create kernel", IDC_DBG_KERNEL, 315, 430, 47, 14, 0, WS_EX_LEFT + PUSHBUTTON "Data window", IDC_DBG_DATAWND, 5, 446, 50, 14, 0, WS_EX_LEFT + PUSHBUTTON "Save envmap", IDC_DBG_ENVSAVE, 300, 167, 60, 12, 0, WS_EX_LEFT + PUSHBUTTON ">>>", IDC_DBG_MORE, 124, 446, 50, 14, 0, WS_EX_LEFT + PUSHBUTTON "Reload Shader", IDC_DBG_RELOADSHD, 60, 446, 60, 14, 0, WS_EX_LEFT + PUSHBUTTON "Reload Textures", IDC_DBG_RELOADTEX, 60, 430, 60, 14, 0, WS_EX_LEFT + LTEXT "Texture: None", IDC_DBG_TEXTURE, 9, 347, 168, 10, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "Pick", IDC_DBG_PICK, 115, 127, 30, 8, 0, WS_EX_LEFT + LTEXT "Mesh: None", IDC_DBG_MESHNAME, 9, 358, 168, 10, SS_LEFT, WS_EX_LEFT + LTEXT "Group Status:", IDC_DBG_GROUPSTAT, 10, 369, 170, 10, SS_LEFT, WS_EX_LEFT + PUSHBUTTON "Paste", IDC_DBG_PASTE, 133, 295, 37, 14, 0, WS_EX_LEFT + PUSHBUTTON "Copy", IDC_DBG_COPY, 90, 295, 39, 14, 0, WS_EX_LEFT + AUTOCHECKBOX "Link channels", IDC_DBG_LINK, 19, 302, 60, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Use and Save the property", IDC_DBG_DEFINED, 19, 324, 101, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Extend material range", IDC_DBG_EXTEND, 19, 313, 84, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "FPS Limiter", IDC_DBG_FPSLIM, 205, 25, 51, 8, 0, WS_EX_LEFT + PUSHBUTTON "Export textures", IDC_DBG_EXPTEX, 315, 445, 52, 14, 0, WS_EX_LEFT + COMBOBOX IDC_DBG_BKLID, 11, 407, 50, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + GROUPBOX "Baked Lights", IDC_DBG_BKLGROUP, 6, 395, 175, 30, 0, WS_EX_LEFT + CONTROL "", IDC_DBG_BKLADJ, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 65, 405, 115, 15, WS_EX_LEFT + GROUPBOX "Mesh Group Information", IDC_STATIC, 195, 295, 175, 130, 0, WS_EX_LEFT + AUTOCHECKBOX "No Shadow", IDC_DBG_NOSHADOW, 200, 310, 53, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Do not render", IDC_DBG_NORENDER, 200, 320, 59, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Always Lit", IDC_DBG_NOLIGHT, 200, 330, 47, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Additive Blend", IDC_DBG_ADDITIVE, 200, 340, 61, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Texture Alpha Only (No Color)", IDC_DBG_NOCOLOR, 200, 350, 109, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Order Independent Transparency", IDC_DBG_OIT, 200, 360, 121, 8, 0, WS_EX_LEFT + EDITTEXT IDC_DBG_MATIDX, 225, 375, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_TEXIDX, 225, 390, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + LTEXT "MatIdx", IDC_STATIC, 200, 376, 22, 9, SS_LEFT, WS_EX_LEFT + LTEXT "TexIdx", IDC_STATIC, 200, 392, 22, 9, SS_LEFT, WS_EX_LEFT + PUSHBUTTON "Save Mesh", IDC_DBG_MESHSAVE, 200, 430, 55, 14, 0, WS_EX_LEFT + PUSHBUTTON "Unrendered", IDC_DBG_NEXT, 127, 90, 48, 12, 0, WS_EX_LEFT + LTEXT "Label", IDC_STATIC, 201, 407, 18, 9, SS_LEFT, WS_EX_LEFT + EDITTEXT IDC_DBG_GRPLABEL, 225, 405, 140, 12, ES_AUTOHSCROLL, WS_EX_LEFT + AUTOCHECKBOX "Exterior in VC", IDC_DBG_EXTVC, 115, 137, 58, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "2cm near clip distance", IDC_DBG_CLIPDIST, 18, 136, 87, 8, 0, WS_EX_LEFT + LTEXT "MeshVisMode: ", IDC_DBG_VISMODE, 10, 380, 162, 9, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "Pick only current mesh", IDC_DBG_PICKCURRENT, 18, 146, 87, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Dynamic", IDC_DBG_DYNAMIC, 285, 310, 43, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "This is VC mesh", IDC_DBG_ISVCMESH, 15, 193, 66, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Cast VC shadow", IDC_DBG_VCSHADOW, 15, 203, 68, 8, 0, WS_EX_LEFT + COMBOBOX IDC_DBG_AMBDIR, 275, 130, 86, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "VC Ambient View Dir", IDC_STATIC, 205, 132, 66, 9, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "No ambient sunlight", IDC_DBG_NOSUNAMB, 205, 161, 78, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "No planet glow", IDC_DBG_NOPLNAMB, 205, 172, 63, 8, 0, WS_EX_LEFT + COMBOBOX IDC_DBG_DATASRC, 275, 145, 86, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Probe Data Source", IDC_STATIC, 206, 147, 62, 9, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "No dynamic sunlight", IDC_DBG_NODYNSUN, 205, 184, 79, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "VC Click Zones", IDC_DBG_VCZONES, 115, 147, 60, 8, 0, WS_EX_LEFT +} + + + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +IDD_vkSCATTER DIALOG 0, 0, 823, 303 +STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_GROUP | WS_POPUP | WS_SYSMENU +CAPTION "Render Controls" +FONT 8, "Ms Shell Dlg" +{ + GROUPBOX "Twilight and Ambient settings", IDC_ATG1, 5, 25, 115, 250, 0, WS_EX_LEFT + AUTOCHECKBOX "Visualize Scatter Tables", IDC_ATM_DISPLAY, 356, 288, 91, 8, 0, WS_EX_LEFT + CONTROL "", IDC_ATM_S16, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 612, 50, 15, 200, WS_EX_LEFT + PUSHBUTTON "Copy To Surface", IDC_ATM_COPYTO, 135, 285, 60, 14, 0, WS_EX_LEFT + GROUPBOX "Wavelength, Scale height settings", IDC_ATG2, 125, 25, 190, 250, 0, WS_EX_LEFT + CTEXT "Configuration", IDC_STATIC, 9, 287, 42, 8, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S1, 15, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S1, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 20, 50, 15, 200, WS_EX_LEFT + CTEXT "Distance", IDC_ATL_S1, 15, 40, 29, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S4, 135, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S4, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 141, 50, 15, 200, WS_EX_LEFT + CTEXT "Green", IDC_ATL_S4, 140, 40, 20, 9, SS_CENTER, WS_EX_LEFT + GROUPBOX "Rayleigh Settings", IDC_ATG4, 445, 25, 75, 250, 0, WS_EX_LEFT + EDITTEXT IDC_ATD_S5, 170, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S5, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 175, 50, 15, 200, WS_EX_LEFT + CTEXT "R-Pow", IDC_ATL_S5, 170, 40, 22, 8, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S6, 205, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S6, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 210, 50, 15, 200, WS_EX_LEFT + CTEXT "M-Pow", IDC_ATL_S6, 205, 40, 23, 8, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S13, 490, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S13, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 495, 50, 15, 200, WS_EX_LEFT + CTEXT "Ratio", IDC_ATL_S13, 493, 40, 18, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S12, 453, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S12, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 459, 50, 15, 200, WS_EX_LEFT + CTEXT "Density", IDC_ATL_S12, 455, 40, 24, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S3, 86, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S3, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 94, 50, 15, 200, WS_EX_LEFT + CTEXT "Building", IDC_ATL_S3, 89, 40, 26, 9, SS_CENTER, WS_EX_LEFT + GROUPBOX "Mie Settings", IDC_ATG5, 525, 25, 150, 250, 0, WS_EX_LEFT + GROUPBOX "Terrain", IDC_ATG3, 320, 25, 120, 250, 0, WS_EX_LEFT + EDITTEXT IDC_ATD_S14, 536, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + EDITTEXT IDC_ATD_S15, 571, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S15, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 576, 50, 15, 200, WS_EX_LEFT + CONTROL "", IDC_ATM_S14, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 541, 50, 15, 200, WS_EX_LEFT + CTEXT "Density", IDC_ATL_S14, 533, 40, 30, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S2, 50, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S2, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 58, 50, 15, 200, WS_EX_LEFT + CTEXT "Terrain", IDC_ATL_S2, 55, 40, 23, 9, SS_CENTER, WS_EX_LEFT + CTEXT "Phase-A", IDC_ATL_S15, 572, 40, 28, 9, SS_CENTER, WS_EX_LEFT + GROUPBOX "Custom Settings", IDC_ATG6, 680, 25, 115, 250, 0, WS_EX_LEFT + EDITTEXT IDC_ATD_S17, 640, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + EDITTEXT IDC_ATD_S18, 690, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S18, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 695, 50, 15, 200, WS_EX_LEFT + CONTROL "", IDC_ATM_S17, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 645, 50, 15, 200, WS_EX_LEFT + CTEXT "Ratio", IDC_ATL_S17, 642, 40, 25, 9, SS_CENTER, WS_EX_LEFT + CTEXT "CloudAlt", IDC_ATL_S18, 690, 40, 27, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S19, 725, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S19, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 730, 50, 15, 200, WS_EX_LEFT + CTEXT "HDR", IDC_ATL_S19, 725, 40, 25, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S16, 605, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CTEXT "Phase-B", IDC_ATL_S16, 606, 40, 28, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S20, 760, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S20, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 767, 50, 15, 200, WS_EX_LEFT + CTEXT "Intensity", IDC_ATL_S20, 760, 40, 27, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S7, 240, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S7, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 245, 50, 15, 200, WS_EX_LEFT + EDITTEXT IDC_ATD_S10, 368, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CTEXT "Gamma", IDC_ATL_S10, 370, 40, 25, 8, SS_CENTER, WS_EX_LEFT + CONTROL "", IDC_ATM_S10, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 375, 50, 15, 200, WS_EX_LEFT + EDITTEXT IDC_ATD_S9, 329, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S9, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 335, 50, 15, 200, WS_EX_LEFT + CTEXT "Brightness", IDC_ATL_S9, 325, 40, 34, 8, SS_CENTER, WS_EX_LEFT + CTEXT "R-Height", IDC_ATL_S7, 237, 40, 29, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S8, 275, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + CONTROL "", IDC_ATM_S8, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 280, 50, 15, 200, WS_EX_LEFT + CTEXT "M-Height", IDC_ATL_S8, 272, 40, 30, 9, SS_CENTER, WS_EX_LEFT + PUSHBUTTON "Close", IDOK, 725, 285, 50, 14, 0, WS_EX_LEFT + PUSHBUTTON "Save Settings", IDC_ATM_SAVE, 655, 285, 60, 14, 0, WS_EX_LEFT + COMBOBOX IDC_ATM_MODE, 54, 285, 65, 14, CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_LEFT + PUSHBUTTON "Load Settings", IDC_ATM_LOAD, 590, 285, 60, 14, 0, WS_EX_LEFT + PUSHBUTTON "Copy to Low Orbit", IDC_ATM_COPYLOW, 200, 285, 65, 14, 0, WS_EX_LEFT + PUSHBUTTON "Copy to High Orbit", IDC_ATM_COPYHIGH, 270, 285, 70, 14, 0, WS_EX_LEFT + CONTROL "", IDC_ATM_S11, TRACKBAR_CLASS, WS_TABSTOP | TBS_VERT | TBS_BOTH | TBS_NOTICKS, 412, 50, 15, 200, WS_EX_LEFT + CTEXT "Light/Shad", IDC_ATL_S11, 402, 40, 36, 9, SS_CENTER, WS_EX_LEFT + EDITTEXT IDC_ATD_S11, 405, 255, 30, 12, ES_CENTER | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + COMBOBOX IDC_ATM_PAGE, 10, 5, 215, 14, CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_LEFT +} + + + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +IDD_vkSETUP DIALOGEX 0, 0, 545, 378 +STYLE DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU +CAPTION "Vulkan Advanced Setup" +FONT 8, "MS Sans Serif", 0, 0, 0 +{ + LTEXT "Blend Mode", IDC_STATIC, 380, 197, 39, 8, SS_LEFT, WS_EX_LEFT + GROUPBOX "Surface micro textures", IDC_STATIC, 371, 151, 169, 95, 0, WS_EX_LEFT + AUTORADIOBUTTON "Load on demand (recommended)", IDC_DEMAND, 16, 16, 121, 10, WS_GROUP | WS_TABSTOP, WS_EX_LEFT + EDITTEXT IDC_HZ, 116, 27, 35, 14, ES_AUTOHSCROLL, WS_EX_LEFT + AUTORADIOBUTTON "Pre-load at session start", IDC_SRFPRELOAD, 16, 42, 94, 10, WS_TABSTOP, WS_EX_LEFT + COMBOBOX IDC_AA, 85, 110, 35, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_MICROMODE, 440, 165, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_MICROFILTER, 440, 180, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_BLENDMODE, 440, 195, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Texture filter", IDC_STATIC, 380, 183, 40, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Mipmap Bias", IDC_STATIC, 381, 228, 41, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Sharp", IDC_STATIC, 435, 212, 20, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Smooth", IDC_STATIC, 507, 213, 25, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Render Mode", IDC_STATIC, 380, 166, 44, 8, SS_LEFT, WS_EX_LEFT + COMBOBOX IDC_AF, 85, 125, 35, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + EDITTEXT IDC_PLANETGLOW, 85, 142, 35, 14, ES_AUTOHSCROLL, WS_EX_LEFT + AUTOCHECKBOX "Pre-load base visuals at startup", IDC_BASEVIS, 20, 240, 114, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable cloud micro-textures", IDC_CLOUDMICRO, 20, 292, 103, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable cloud normal maps", IDC_CLOUDNORM, 20, 305, 99, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable GDI Overlay", IDC_GDIOVERLAY, 20, 318, 79, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable absolute animation handling", IDC_ABSANIM, 20, 331, 127, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable terrain flattening", IDC_FLATS, 370, 286, 90, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable advanced texture maps", IDC_NORMALMAPS, 20, 252, 114, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable improved glass shading", IDC_GLASSSHADE, 20, 265, 113, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Disable near clip plane compatibility mode", IDC_NEARPLANE, 20, 279, 147, 8, 0, WS_EX_LEFT + CONTROL "", IDC_LODBIAS, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_TOP, 256, 147, 95, 20, WS_EX_LEFT + CONTROL "", IDC_MICROBIAS, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_TOP, 435, 222, 95, 20, WS_EX_LEFT + PUSHBUTTON "vkClient Credits", IDC_CREDITS, 5, 360, 80, 14, BS_FLAT, WS_EX_LEFT + PUSHBUTTON "Create symbolic links", IDC_SYMBOLIC, 95, 360, 85, 14, BS_FLAT, WS_EX_LEFT + COMBOBOX IDC_FONT, 270, 23, 80, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DEBUG, 270, 65, 80, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_MIPMAPS, 263, 200, 91, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_LIGHTCONFIG, 201, 255, 150, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_SELFSHADOWS, 196, 310, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_SHADOWFILTER, 291, 310, 65, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_TERRAIN, 290, 335, 65, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + AUTOCHECKBOX "Enable Development Tools", IDC_MESH_DEBUGGER, 200, 85, 101, 8, 0, WS_EX_LEFT + CONTROL "", IDC_CONVERGENCE, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_TOP, 425, 20, 85, 20, WS_EX_LEFT + CONTROL "", IDC_SEPARATION, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_TOP, 425, 40, 85, 20, WS_EX_LEFT + COMBOBOX IDC_ENVMODE, 440, 90, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_TEXMIPS, 85, 160, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_POSTPROCESS, 85, 177, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_GUIMODE, 85, 194, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_ARCHIVE, 85, 60, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_CAMMODE, 440, 106, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_ENVFACES, 440, 122, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + DEFPUSHBUTTON "OK", IDOK, 460, 355, 75, 19, 0, WS_EX_LEFT + GROUPBOX "Surface texture load options", IDC_STATIC, 5, 5, 175, 75, 0, WS_EX_LEFT + GROUPBOX "SketchPad settings", IDC_STATIC, 190, 5, 170, 40, 0, WS_EX_LEFT + LTEXT "Max. load frequency [Hz]:", IDC_STATIC, 26, 29, 85, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Font rendering", IDC_STATIC, 200, 24, 46, 8, SS_LEFT, WS_EX_LEFT + GROUPBOX "Graphics options", IDC_STATIC, 5, 90, 175, 265, 0, WS_EX_LEFT + GROUPBOX "Generic configuration", IDC_STATIC, 190, 50, 170, 65, 0, WS_EX_LEFT + LTEXT "Anti-aliasing", IDC_STATIC, 15, 111, 38, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Tile Archive", IDC_STATIC, 15, 62, 38, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Debug level", IDC_STATIC, 200, 68, 39, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Surface mipmaps", IDC_STATIC, 200, 202, 55, 9, SS_LEFT, WS_EX_LEFT + LTEXT "Anisotropic filtering", IDC_STATIC, 15, 127, 60, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Planet glow", IDC_STATIC, 16, 144, 38, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Vessel mapping", IDC_STATIC, 196, 300, 50, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Terrain mapping", IDC_STATIC, 290, 325, 52, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Filter options", IDC_STATIC, 291, 300, 40, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Texture Mipmaps", IDC_STATIC, 16, 162, 55, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Post Processing", IDC_STATIC, 17, 179, 52, 8, SS_LEFT, WS_EX_LEFT + LTEXT "gcGUI Mode", IDC_STATIC, 17, 196, 42, 8, SS_LEFT, WS_EX_LEFT + GROUPBOX "StereoScopic 3D", IDC_STATIC, 370, 5, 170, 60, 0, WS_EX_LEFT + LTEXT "Convergence", IDC_STATIC, 375, 25, 44, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Static", IDC_CONV_DSP, 511, 27, 22, 10, SS_LEFT, WS_EX_LEFT + LTEXT "Field Depth", IDC_STATIC, 375, 45, 37, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Static", IDC_SEPA_DSP, 512, 47, 22, 10, SS_LEFT, WS_EX_LEFT + GROUPBOX "Reflections and Custom camera settings", IDC_STATIC, 370, 75, 170, 70, 0, WS_EX_LEFT + LTEXT "Reflection Mode", IDC_STATIC, 380, 92, 53, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Custom Cameras", IDC_STATIC, 380, 109, 54, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Update Rate", IDC_STATIC, 380, 125, 42, 8, SS_LEFT, WS_EX_LEFT + GROUPBOX "Terrain Resolution", IDC_STATIC, 191, 125, 170, 95, 0, WS_EX_LEFT + GROUPBOX "Local lights", IDC_STATIC, 191, 240, 170, 40, 0, WS_EX_LEFT + GROUPBOX "Self Shadows", IDC_STATIC, 191, 285, 170, 70, 0, WS_EX_LEFT + LTEXT "Lower", IDC_STATIC, 256, 138, 20, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Higher", IDC_STATIC, 331, 138, 22, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Texture Bias", IDC_STATIC, 201, 153, 40, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Mesh resolution", IDC_STATIC, 200, 172, 50, 8, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "Enable debug break", IDC_BREAK, 200, 99, 80, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable sun glare", IDC_ESUNGLARE, 370, 274, 69, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable local lights glare", IDC_ELIGHTSGLARE, 370, 299, 90, 8, NOT WS_VISIBLE | WS_DISABLED, WS_EX_LEFT + AUTOCHECKBOX "Enable dynamic ambient light", IDC_EIRRAD, 370, 262, 107, 8, 0, WS_EX_LEFT + COMBOBOX IDC_EARTHVISCFG, 85, 212, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Earth visual config", IDC_STATIC, 17, 215, 59, 9, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "Enable shader cache for faster startup", IDC_ESCACHE, 370, 250, 136, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Enable increased atmosphere quality", IDC_EAQUALITY, 20, 344, 131, 8, 0, WS_EX_LEFT + COMBOBOX IDC_MESHRES, 263, 170, 50, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_TILECOUNT, 263, 185, 50, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Max tiles allocated", IDC_STATIC, 200, 187, 59, 9, SS_LEFT, WS_EX_LEFT + LTEXT "Virtual cockpit shadows", IDC_STATIC, 195, 326, 76, 9, SS_LEFT, WS_EX_LEFT + COMBOBOX IDC_CASCOUNT, 195, 335, 90, 15, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT +} + + + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +IDD_DEBUGVIEW DIALOG 0, 0, 455, 173 +STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU +CAPTION "Dialog" +FONT 8, "Ms Shell Dlg" +{ + CONTROL "Rich Edit", IDC_DBG_DATAVIEW, RICHEDIT_CLASS, WS_TABSTOP | WS_VSCROLL | WS_BORDER | ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY, 5, 4, 215, 162, WS_EX_LEFT + CONTROL "Rich Edit", IDC_DBG_DATAVIEW2, RICHEDIT_CLASS, WS_TABSTOP | WS_VSCROLL | WS_BORDER | ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY, 235, 5, 215, 162, WS_EX_LEFT +} + + + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +IDD_GRAPHICS DIALOG 0, 0, 277, 214 +STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU +CAPTION "Graphics Controls" +FONT 8, "Ms Shell Dlg" +{ + LTEXT "Light glow intensity", IDC_STATIC, 15, 30, 60, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Value", IDC_GFX_VAL1, 235, 30, 19, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Value", IDC_GFX_VAL2, 235, 45, 19, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Value", IDC_GFX_VAL5, 234, 116, 19, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Value", IDC_GFX_VAL6, 233, 131, 19, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Value", IDC_GFX_VAL7, 234, 146, 19, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Value", IDC_GFX_VAL4, 235, 75, 19, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Value", IDC_GFX_VAL3, 235, 60, 19, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Light glow distance", IDC_STATIC, 15, 45, 62, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Sunlight Intensity", IDC_STATIC, 12, 115, 54, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Indirect Lighting", IDC_STATIC, 12, 130, 51, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Local Lights Max", IDC_STATIC, 12, 145, 54, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Gamma", IDC_STATIC, 15, 75, 25, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Glow threshold", IDC_STATIC, 15, 60, 48, 8, SS_LEFT, WS_EX_LEFT + GROUPBOX "Post Processing Configuration", IDC_STATIC, 5, 10, 266, 80, 0, WS_EX_LEFT + GROUPBOX "Light Configuration", IDC_STATIC, 5, 100, 266, 80, 0, WS_EX_LEFT + PUSHBUTTON "ReCreate Sun/Glares", IDC_GFX_RECOMPILE, 200, 195, 73, 14, 0, WS_EX_LEFT + CONTROL "", IDC_GFX_INTENSITY, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 85, 30, 144, 12, WS_EX_LEFT + CONTROL "", IDC_GFX_DISTANCE, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 85, 45, 144, 12, WS_EX_LEFT + CONTROL "", IDC_GFX_SUNLIGHT, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 85, 115, 144, 12, WS_EX_LEFT + CONTROL "", IDC_GFX_IRRADIANCE, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 85, 130, 144, 12, WS_EX_LEFT + CONTROL "", IDC_GFX_LOCALMAX, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 85, 146, 144, 12, WS_EX_LEFT + CONTROL "", IDC_GFX_GAMMA, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 85, 75, 144, 12, WS_EX_LEFT + CONTROL "", IDC_GFX_THRESHOLD, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 85, 60, 144, 12, WS_EX_LEFT + PUSHBUTTON "", IDC_GFX_INTENSITY_RESET, 255, 27, 12, 12, BS_BITMAP, WS_EX_LEFT + PUSHBUTTON "", IDC_GFX_DISTANCE_RESET, 255, 42, 12, 12, BS_BITMAP, WS_EX_LEFT + PUSHBUTTON "", IDC_GFX_SUNLIGHT_RESET, 254, 114, 12, 12, BS_BITMAP, WS_EX_LEFT + PUSHBUTTON "", IDC_GFX_IRRADIANCE_RESET, 254, 130, 12, 12, BS_BITMAP, WS_EX_LEFT + PUSHBUTTON "", IDC_GFX_LOCALMAX_RESET, 254, 145, 12, 12, BS_BITMAP, WS_EX_LEFT + PUSHBUTTON "", IDC_GFX_GAMMA_RESET, 255, 73, 12, 12, BS_BITMAP, WS_EX_LEFT + PUSHBUTTON "", IDC_GFX_THRESHOLD_RESET, 255, 57, 12, 12, BS_BITMAP, WS_EX_LEFT + LTEXT "Sun glare intensity", IDC_STATIC, 12, 161, 58, 9, SS_LEFT, WS_EX_LEFT + CONTROL "", IDC_GFX_GLARE, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 85, 161, 144, 12, WS_EX_LEFT + LTEXT "Value", IDC_GFX_VAL8, 233, 161, 19, 8, SS_LEFT, WS_EX_LEFT + PUSHBUTTON "", IDC_GFX_GLARE_RESET, 254, 160, 12, 12, BS_BITMAP, WS_EX_LEFT +} + + + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +IDD_MATERIAL DIALOG 0, 0, 180, 148 +STYLE DS_CENTER | DS_SHELLFONT | WS_CHILDWINDOW | WS_CLIPCHILDREN +FONT 8, "Ms Shell Dlg" +{ + PUSHBUTTON "Save materials", IDC_DBG_MATSAVE, 5, 130, 51, 14, 0, WS_EX_LEFT + PUSHBUTTON "Data window", IDC_DBG_DATAWND, 64, 130, 50, 14, 0, WS_EX_LEFT + COMBOBOX IDC_DBG_MATPRP, 73, 3, 96, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Material property", IDC_STATIC, 12, 5, 53, 8, SS_LEFT, WS_EX_LEFT + EDITTEXT IDC_DBG_RED, 25, 21, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_GREEN, 61, 21, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_BLUE, 97, 21, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_ALPHA, 133, 21, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + CONTROL "", IDC_DBG_MATADJ, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 13, 38, 156, 14, WS_EX_LEFT + LTEXT "Texture: None", IDC_DBG_TEXTURE, 8, 90, 168, 10, SS_LEFT, WS_EX_LEFT + LTEXT "Mesh: None", IDC_DBG_MESHNAME, 9, 101, 168, 10, SS_LEFT, WS_EX_LEFT + LTEXT "Group Status:", IDC_DBG_GROUPSTAT, 9, 113, 165, 10, SS_LEFT, WS_EX_LEFT + PUSHBUTTON "Paste", IDC_DBG_PASTE, 127, 56, 37, 14, 0, WS_EX_LEFT + PUSHBUTTON "Copy", IDC_DBG_COPY, 84, 56, 39, 14, 0, WS_EX_LEFT + AUTOCHECKBOX "Link channels", IDC_DBG_LINK, 15, 58, 60, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Use and Save the property", IDC_DBG_DEFINED, 15, 73, 101, 8, 0, WS_EX_LEFT +} + + + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +IDD_MESHDEBUG DIALOG 0, 0, 180, 193 +STYLE DS_CENTER | DS_SHELLFONT | WS_CHILDWINDOW | WS_CLIPCHILDREN +FONT 8, "Ms Shell Dlg" +{ + LTEXT "Selected Visual: Cape Canaveral", IDC_DBG_VISUAL, 14, 5, 104, 8, SS_LEFT, WS_EX_LEFT + COMBOBOX IDC_DBG_DISPLAY, 43, 20, 120, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Display", IDC_STATIC, 13, 23, 24, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Camera", IDC_STATIC, 13, 38, 25, 11, SS_LEFT, WS_EX_LEFT + COMBOBOX IDC_DBG_CAMERA, 43, 38, 120, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + EDITTEXT IDC_DBG_MESH, 49, 74, 44, 12, ES_AUTOHSCROLL, WS_EX_LEFT + LTEXT "Group Idx", IDC_STATIC, 13, 92, 32, 12, SS_LEFT, WS_EX_LEFT + LTEXT "Mesh Idx", IDC_STATIC, 13, 77, 30, 8, SS_LEFT, WS_EX_LEFT + EDITTEXT IDC_DBG_GROUP, 49, 92, 44, 12, WS_GROUP | ES_AUTOHSCROLL, WS_EX_LEFT + AUTOCHECKBOX "Highlight selected group", IDC_DBG_HSG, 13, 110, 92, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Highlight selected mesh", IDC_DBG_HSM, 13, 122, 91, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Boxes", IDC_DBG_BOXES, 14, 155, 35, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Spheres", IDC_DBG_SPHERES, 14, 166, 42, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Surface Tiles", IDC_DBG_TILEBB, 14, 178, 57, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Selected group only", IDC_DBG_GRPO, 80, 155, 79, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Selected mesh only", IDC_DBG_MSHO, 80, 167, 77, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Dual sided", IDC_DBG_DUAL, 115, 110, 49, 8, 0, WS_EX_LEFT + PUSHBUTTON "<", IDC_DBG_GRPUP, 97, 92, 9, 12, BS_CENTER, WS_EX_LEFT + PUSHBUTTON ">", IDC_DBG_GRPDN, 109, 92, 9, 12, BS_CENTER, WS_EX_LEFT + PUSHBUTTON "<", IDC_DBG_MSHUP, 97, 74, 9, 12, BS_CENTER, WS_EX_LEFT + PUSHBUTTON ">", IDC_DBG_MSHDN, 109, 74, 9, 12, BS_CENTER, WS_EX_LEFT + AUTOCHECKBOX "Selected visual only", IDC_DBG_VISO, 80, 179, 79, 8, 0, WS_EX_LEFT + CONTROL "", IDC_DBG_SPEED, TRACKBAR_CLASS, WS_TABSTOP | TBS_BOTH | TBS_NOTICKS, 37, 56, 108, 13, WS_EX_LEFT + LTEXT "Speed", IDC_STATIC, 13, 56, 22, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Static", IDC_DBG_SPEEDDSP, 145, 56, 21, 10, SS_LEFT, WS_EX_LEFT + AUTOCHECKBOX "Add ambient light", IDC_DBG_AMBIENT, 13, 134, 70, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Wireframe", IDC_DBG_WIRE, 115, 122, 48, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Pick", IDC_DBG_PICK, 115, 134, 30, 8, 0, WS_EX_LEFT +} + + + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +IDD_MICROTEXTOOLS DIALOG 0, 0, 180, 134 +STYLE DS_CENTER | DS_SHELLFONT | WS_CHILDWINDOW | WS_CLIPCHILDREN +FONT 8, "Ms Shell Dlg" +{ + PUSHBUTTON "Open", IDC_DBG_OPEN, 7, 5, 30, 14, 0, WS_EX_LEFT + PUSHBUTTON "Execute", IDC_DBG_EXECUTE, 111, 116, 56, 14, 0, WS_EX_LEFT + EDITTEXT IDC_DBG_FILE, 42, 6, 125, 12, WS_GROUP | ES_AUTOHSCROLL | ES_READONLY, WS_EX_LEFT + AUTOCHECKBOX "Normalize", IDC_DBG_NORM, 21, 48, 47, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Mipmap fade", IDC_DBG_FADE, 21, 61, 57, 8, 0, WS_EX_LEFT + AUTOCHECKBOX "Reduce seams", IDC_DBG_SEAMS, 81, 48, 63, 8, 0, WS_EX_LEFT + EDITTEXT IDC_DBG_VARA, 86, 96, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_VARC, 36, 96, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDC_DBG_VARB, 136, 96, 30, 12, ES_AUTOHSCROLL, WS_EX_LEFT + COMBOBOX IDC_DBG_ACTION, 71, 27, 95, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_TARGET, 41, 76, 126, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Select Action", IDC_STATIC, 22, 29, 43, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Target", IDC_STATIC, 13, 78, 25, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Fb:", IDC_STATIC, 123, 98, 11, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Fa:", IDC_STATIC, 73, 98, 11, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Noise:", IDC_STATIC, 11, 98, 21, 8, SS_LEFT, WS_EX_LEFT +} + + + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +IDD_SCENEDEBUG DIALOG 0, 0, 180, 102 +STYLE DS_CENTER | DS_SHELLFONT | WS_CHILDWINDOW | WS_CLIPCHILDREN +FONT 8, "Ms Shell Dlg" +{ + CONTROL "", IDC_DBG_RESBIAS, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_BOTH, 15, 37, 155, 16, WS_EX_LEFT + COMBOBOX IDC_DBG_ENVMAP, 105, 77, 66, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + COMBOBOX IDC_DBG_SCENEDBG, 65, 60, 106, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT + LTEXT "Debug Mode", IDC_STATIC, 15, 62, 42, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Display env map for level:", IDC_STATIC, 15, 80, 82, 8, SS_LEFT, WS_EX_LEFT + LTEXT "Resolution Bias", IDC_STATIC, 15, 20, 50, 8, SS_LEFT, WS_EX_LEFT + PUSHBUTTON "Create kernel", IDC_DBG_KERNEL, 110, 5, 60, 12, 0, WS_EX_LEFT + PUSHBUTTON "Save envmap", IDC_DBG_ENVSAVE, 110, 20, 60, 12, 0, WS_EX_LEFT + AUTOCHECKBOX "FPS Limiter", IDC_DBG_FPSLIM, 15, 5, 51, 8, 0, WS_EX_LEFT +} + + + +// +// String Table resources +// +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +STRINGTABLE +{ + IDS_INFO "Vulkan Graphics Client: *** Do not use with Orbiter.exe! ***\r\n\r\nA render engine plugin for Orbiter_ng." + IDS_TYPE "Graphics engines" +} + + + +// +// Version Information resources +// +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +1 VERSIONINFO + FILEVERSION 30,7,0,0 + PRODUCTVERSION 30,7,0,0 + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN + FILEFLAGSMASK VS_FF_DEBUG | VS_FF_PRERELEASE | VS_FF_PATCHED | VS_FF_PRIVATEBUILD | VS_FF_INFOINFERRED | VS_FF_SPECIALBUILD + FILEFLAGS 0 +{ + BLOCK "StringFileInfo" + { + BLOCK "080904E4" + { + VALUE "FileDescription", "Render engine plugin for Orbiter Space Flight Simulator" + VALUE "FileVersion", "0.0.0.1" + VALUE "InternalName", "VulkanClient" + VALUE "LegalCopyright", "Copyright © 2010 - 2025" + VALUE "OriginalFilename", "VulkanClientvkClient" + VALUE "ProductName", "VulkanClient" + VALUE "ProductVersion", "0.0.0.1" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0809, 0x04E4 + } +} diff --git a/OVP/VulkanClient/ClientConfig.h.in b/OVP/VulkanClient/ClientConfig.h.in new file mode 100644 index 000000000..57cff58d1 --- /dev/null +++ b/OVP/VulkanClient/ClientConfig.h.in @@ -0,0 +1,2 @@ +#define D3D9_VERSION_MAJOR @D3D9_VERSION_MAJOR@ +#define D3D9_VERSION_MINOR @D3D9_VERSION_MINOR@ \ No newline at end of file diff --git a/OVP/VulkanClient/CloudMgr.cpp b/OVP/VulkanClient/CloudMgr.cpp new file mode 100644 index 000000000..d31764a98 --- /dev/null +++ b/OVP/VulkanClient/CloudMgr.cpp @@ -0,0 +1,190 @@ +// ============================================================== +// CloudMgr.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007 - 2016 Martin Schweiger +// ============================================================== + +// ============================================================== +// class CloudManager (implementation) +// +// Planetary rendering management for cloud layers, including a simple +// LOD (level-of-detail) algorithm for patch resolution. +// ============================================================== + +#include "CloudMgr.h" +#include "VPlanet.h" +#include "Surface.h" + +using namespace oapi; + +// ======================================================================= + +CloudManager::CloudManager(vkClient *gclient, const vPlanet *vplanet) +: TileManager (gclient, vplanet) +{ + size_t len = strlen(objname)+7; + char *texname = new char[len]; + strcpy_s(texname, len, objname); + strcat_s(texname, len, "_cloud"); + delete []objname; + objname = texname; + + maxlvl = min (*(int*)gc->GetConfigParam (CFGPRM_SURFACEMAXLEVEL), // global setting + *(int*)oapiGetObjectParam (obj, OBJPRM_PLANET_SURFACEMAXLEVEL)); // planet-specific setting + maxbaselvl = min (8, maxlvl); + pcdir = _V(1,0,0); + lightfac = *(double*)gc->GetConfigParam (CFGPRM_SURFACELIGHTBRT); + nmask = 0; + nhitex = nhispec = 0; + + atmc = oapiGetPlanetAtmConstants (obj); + + int maxidx = patchidx[maxbaselvl]; + tiledesc = new TILEDESC[maxidx]; + memset (tiledesc, 0, maxidx*sizeof(TILEDESC)); + + for (int i = 0; i < patchidx[maxbaselvl]; i++) tiledesc[i].flag = 1; +} + +// ======================================================================= + +void CloudManager::LoadData() +{ + if (ntex!=0 || bNoTextures) return; + LoadTextures(); +} + +// ======================================================================= + +void CloudManager::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap) +{ + LoadData(); + if (bNoTextures) return; + + D3DMATERIAL9 def_mat = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{0,0,0,1},0}; + + HR(FX->SetTechnique(eCloudTech)); + HR(FX->SetValue(eMat, &def_mat, sizeof(D3DMATERIAL9))); + HR(FX->SetValue(eSun, gc->GetScene()->GetSun(), sizeof(vkSun))); + HR(FX->SetInt(eSpecularMode, 0)); + + if (microtex) { + HR(FX->SetTexture(eTex3, SURFACE(microtex)->GetTexture())); + } + + bool do_micro = (microtex && microlvl > 0.01); + + if (do_micro) { + HR(FX->SetFloat(eMix, float(microlvl))); + } + else { + HR(FX->SetFloat(eMix, 0.0f)); + } + + TileManager::Render(dev, wmat, scale, level, viewap); +} + + +void CloudManager::RenderShadow(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap, float shadowalpha) +{ + LoadData(); + if (bNoTextures) return; + + D3DMATERIAL9 cloudmat = {{0,0,0,1},{0,0,0,1},{0,0,0,0},{0,0,0,0},0}; + cloudmat.Diffuse.a = cloudmat.Ambient.a = shadowalpha; + + HR(FX->SetTechnique(eCloudShadow)) + HR(FX->SetValue(eMat, &cloudmat, sizeof(D3DMATERIAL9))); + HR(FX->SetValue(eSun, gc->GetScene()->GetSun(), sizeof(vkSun))); + HR(FX->SetInt(eSpecularMode, 0)); + + if (microtex) { + HR(FX->SetTexture(eTex3, SURFACE(microtex)->GetTexture())); + } + + bool do_micro = (microtex && microlvl > 0.01); + + if (do_micro) { + HR(FX->SetFloat(eMix, float(microlvl))); + } + else { + HR(FX->SetFloat(eMix, 0.0f)); + } + + TileManager::Render(dev, wmat, scale, level, viewap); +} + +// ============================================================== + +void CloudManager::RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWrld) +{ + LoadData(); + if (bNoTextures) return; + + // render complete sphere (used at low LOD levels) + LPDIRECT3DDEVICE9 pDev = gc->GetDevice(); + pDev->SetVertexDeclaration(pPatchVertexDecl); + + HR(FX->SetMatrix(eW, _DX(mWrld))); + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + + for (int idx = 0; idx < npatch; idx++) { + VBMESH &mesh = PATCH_TPL[level][idx]; // patch template + HR(FX->SetTexture(eTex0, tile[idx].tex)); + FX->CommitChanges(); + + pDev->SetStreamSource(0, mesh.pVB, 0, sizeof(VERTEX_2TEX)); + pDev->SetIndices(mesh.pIB); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh.nv, 0, mesh.nf); + } + + HR(FX->EndPass()); + HR(FX->End()); +} + + +void CloudManager::InitRenderTile() +{ + LPDIRECT3DDEVICE9 pDev = gc->GetDevice(); + pDev->SetVertexDeclaration(pPatchVertexDecl); + + HR(FX->SetFloat(eTime, float(fmod(oapiGetSimTime(),60.0)))); + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); +} + +void CloudManager::EndRenderTile() +{ + HR(FX->EndPass()); + HR(FX->End()); +} + + +// ======================================================================= + +void CloudManager::RenderTile (int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, double sdist, + TILEDESC *tile, const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag) +{ + VBMESH &mesh = PATCH_TPL[lvl][ilat]; // patch template + + if (range.tumin == 0 && range.tumax == 1) { + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(1.0f, 0.0f, 1.0f, 0.0f)))); + } + else { + float tuscale = range.tumax-range.tumin, tuofs = range.tumin; + float tvscale = range.tvmax-range.tvmin, tvofs = range.tvmin; + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(tuscale,tuofs,tvscale,tvofs)))); + } + HR(FX->SetMatrix(eW, _DX(mWorld))); + HR(FX->SetTexture(eTex0, tex)); // Diffuse Texture + + FX->CommitChanges(); + + pDev->SetStreamSource(0, mesh.pVB, 0, sizeof(VERTEX_2TEX)); + pDev->SetIndices(mesh.pIB); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh.nv, 0, mesh.nf); +} diff --git a/OVP/VulkanClient/CloudMgr.h b/OVP/VulkanClient/CloudMgr.h new file mode 100644 index 000000000..7e3dec22b --- /dev/null +++ b/OVP/VulkanClient/CloudMgr.h @@ -0,0 +1,39 @@ +// ============================================================== +// CloudMgr.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// ============================================================== + +#ifndef __CLOUDMGR_H +#define __CLOUDMGR_H + +#include "TileMgr.h" + +/** + * \brief Planetary rendering management for clouds. + * + * Planetary rendering management for cloud layers, including a simple + * LOD (level-of-detail) algorithm for patch resolution. + */ +class CloudManager: public TileManager { +public: + CloudManager (oapi::vkClient *gclient, const vPlanet *vplanet); + + void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap = 0.0); + void RenderShadow(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap, float shadowalpha); + void LoadData(); + +protected: + + void InitRenderTile(); + void EndRenderTile(); + void RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWorld); + void RenderTile(int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, double sdist, + TILEDESC *tile, const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag); + +//private: +// int cloudtexidx; +}; + +#endif // !__CLOUDMGR_H diff --git a/OVP/VulkanClient/Cloudmgr2.cpp b/OVP/VulkanClient/Cloudmgr2.cpp new file mode 100644 index 000000000..29fcb9f2a --- /dev/null +++ b/OVP/VulkanClient/Cloudmgr2.cpp @@ -0,0 +1,300 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// ============================================================== +// cloudmgr2.cpp +// Rendering of planetary cloud layers, engine v2, including a simple +// LOD (level-of-detail) algorithm for cloud patch resolution. +// ============================================================== + +#include "cloudmgr2.h" +#include "Catalog.h" +#include "Config.h" + +// ======================================================================= +// ======================================================================= + +CloudTile::CloudTile (TileManager2Base *_mgr, int _lvl, int _ilat, int _ilng) +: Tile (_mgr, _lvl, _ilat, _ilng) +{ + cmgr = static_cast* > (_mgr); + node = 0; + imicrolvl = 6; // Cloud micro resolution level + cloudalt = mgr->GetPlanet()->prm.cloudalt; + if (Config->TileMipmaps == 2) bMipmaps = true; + if (Config->TileMipmaps == 1 && _lvl < 10) bMipmaps = true; +} + +// ----------------------------------------------------------------------- + +CloudTile::~CloudTile () +{ + if (tex && owntex) g_pTexmgr_tt->Free(tex); +} + +// ----------------------------------------------------------------------- + +void CloudTile::PreLoad() +{ + assert(tex == nullptr); + + // Configure microtexture range for "Water texture" and "Cloud microtexture". + GetParentMicroTexRange(µrange); + + LPDIRECT3DDEVICE9 pDev = mgr->Dev(); + LPDIRECT3DTEXTURE9 pSysSrf = nullptr; + + if (cmgr->DoLoadIndividualFiles(0)) { // try loading from individual tile file + char path[MAX_PATH]; + sprintf_s (path, MAX_PATH, "%s\\Cloud\\%02d\\%06d\\%06d.dds", mgr->DataRootDir().c_str(), lvl+4, ilat, ilng); + LoadTextureFile(path, &pSysSrf); + } + if (!pSysSrf && cmgr->ZTreeManager(0)) { // try loading from compressed archive + BYTE *buf; + DWORD ndata = cmgr->ZTreeManager(0)->ReadData(lvl+4, ilat, ilng, &buf); + if (ndata) { + LoadTextureFromMemory(buf, ndata, &pSysSrf); + cmgr->ZTreeManager(0)->ReleaseData(buf); + } + } + + owntex = true; + + if (CreateTexture(pDev, pSysSrf, &tex) != true) { + if (GetParentSubTexRange(&texrange)) { + tex = getParent()->Tex(); + owntex = false; + } + else tex = nullptr; + } + + SAFE_RELEASE(pSysSrf); +} + + +// ----------------------------------------------------------------------- + +void CloudTile::Load () +{ + + bool shift_origin = (lvl >= 4); + int res = mgr->GridRes(); + + if (!lvl) { + // create hemisphere mesh for western or eastern hemispheres + mesh = CreateMesh_hemisphere (res, 0, cloudalt); + //} else if (ilat == 0 || ilat == (1<Dev(); + vPlanet* vPlanet = mgr->GetPlanet(); + PlanetShader* pShader = mgr->GetShader(); + ShaderParams* sp = vPlanet->GetTerrainParams(); + + int cfg = vPlanet->GetShaderID(); + + // --------------------------------------------------------------------- + // Feed tile specific data to shaders + // + // ---------------------------------------------------------------------- + + bool bTexture = (tex && vPlanet->HasTextures()); + + if (bTexture) + { + pShader->SetTexture(pShader->tDiff, tex, IPF_ANISOTROPIC | IPF_CLAMP, Config->Anisotrophy); + + sp->vCloudOff = GetTexRangeDX(&texrange); + sp->vMicroOff = GetTexRangeDX(µrange); + sp->fAlpha = 1.0f; + sp->fBeta = 1.0f; + sp->mWorld = mWorld; + + // ------------------------------------------------------------------- + // render surface mesh + + pShader->SetPSConstants(pShader->Prm, sp, sizeof(ShaderParams)); + pShader->SetVSConstants(pShader->PrmVS, sp, sizeof(ShaderParams)); + + pShader->UpdateTextures(); + + pDev->SetStreamSource(0, mesh->pVB, 0, sizeof(VERTEX_2TEX)); + pDev->SetIndices(mesh->pIB); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh->nv, 0, mesh->nf); + } +} + + +// ======================================================================= +// ======================================================================= + +template<> +void TileManager2::Render (MATRIX4 &dwmat, bool use_zbuf, const vPlanet::RenderPrm &rprm) +{ + // set generic parameters + SetRenderPrm (dwmat, rprm.cloudrot, use_zbuf, rprm); + + int i; + class Scene *scene = GetClient()->GetScene(); + + // adjust scaling parameters (can only be done if no z-buffering is in use) + if (!use_zbuf) { + double R = obj_size; + double Rc = R+rprm.cloudalt; + double D = prm.cdist*R; + double zmin, zmax; + if (D > Rc) { + zmax = sqrt(D*D-Rc*Rc); + zmin = D-Rc; + } else { + zmax = sqrt(D*D-R*R) + sqrt(Rc*Rc-R*R); + zmin = Rc-D; + } + zmin = max (2.0, min (zmax*1e-4, zmin)); + + vp->GetScatterConst()->mVP = scene->PushCameraFrustumLimits(zmin, zmax); + } + + // build a transformation matrix for frustum testing + MATRIX4 Mproj = _MATRIX4(scene->GetProjectionMatrix()); + Mproj.m33 = 1.0; Mproj.m43 = -1.0; // adjust near plane to 1, far plane to infinity + MATRIX4 Mview = _MATRIX4(scene->GetViewMatrix()); + prm.dviewproj = mul(Mview,Mproj); + + // --------------------------------------------------------------------- + // Initialize shading technique and feed planet specific data to shaders + // + + ElevMode = eElevMode::Spherical; + ElevModeLvl = 0; + + ShaderParams* sp = vp->GetTerrainParams(); + FlowControlPS* fc = vp->GetFlowControl(); + fc->bBelowClouds = vp->CameraAltitude() < rprm.cloudalt; + + int cfg = vp->GetShaderID(); + + // Select cloud layer shader + pShader = (cfg == PLT_GIANT ? vp->GetShader(PLT_G_CLOUDS) : vp->GetShader(PLT_CLOUDS)); + + pShader->Setup(pPatchVertexDecl, false, 1); + pShader->ClearTextures(); + + + // Check Eclipse conditions ------------------------------------------- + // + + vp->InitEclipse(pShader); + + pShader->SetPSConstants("Const", vp->GetScatterConst(), sizeof(ConstParams)); + pShader->SetVSConstants("Const", vp->GetScatterConst(), sizeof(ConstParams)); + pShader->SetPSConstants("Flow", fc, sizeof(FlowControlPS)); + + if (cfg != PLT_GIANT) + { + if (Config->CloudMicro) { + pShader->SetTexture("tCloudMicro", hCloudMicro, IPF_ANISOTROPIC | IPF_WRAP, Config->Anisotrophy); + if (Config->bCloudNormals) + pShader->SetTexture("tCloudMicroNorm", hCloudMicroNorm, IPF_ANISOTROPIC | IPF_WRAP, Config->Anisotrophy); + } + pShader->SetTexture("tSun", vp->GetScatterTable(SUN_COLOR), IPF_LINEAR | IPF_CLAMP); + pShader->SetTexture("tLndRay", vp->GetScatterTable(RAY_LAND), IPF_LINEAR | IPF_CLAMP); + pShader->SetTexture("tLndAtn", vp->GetScatterTable(ATN_LAND), IPF_LINEAR | IPF_CLAMP); + //pShader->SetTexture("tSkyRayColor", vp->GetScatterTable(RAY_COLOR), IPF_LINEAR | IPF_CLAMP); + //pShader->SetTexture("tSkyMieColor", vp->GetScatterTable(MIE_COLOR), IPF_LINEAR | IPF_CLAMP); + } + + // TODO: render full sphere for levels < 4 + + loader->WaitForMutex(); + + // update the tree + for (i = 0; i < 2; i++) + ProcessNode (tiletree+i); + + // render the tree + for (i = 0; i < 2; i++) + RenderNode (tiletree+i); + + loader->ReleaseMutex (); + + // Pop previous frustum configuration, must initialize mVP + if (!use_zbuf) vp->GetScatterConst()->mVP = scene->PopCameraFrustumLimits(); +} + +// ----------------------------------------------------------------------- + +template<> +int TileManager2::Coverage (double latmin, double latmax, double lngmin, double lngmax, int maxlvl, const Tile **tbuf, int nt) const +{ + double crot = GetPlanet()->prm.cloudrot; + //double crot = prm.rprm->cloudrot; + lngmin = lngmin + crot; + lngmax = lngmax + crot; + if (lngmin > PI) { + lngmin -= PI2; + lngmax -= PI2; + } + int nfound = 0; + for (int i = 0; i < 2; i++) { + CheckCoverage (tiletree+i, latmin, latmax, lngmin, lngmax, maxlvl, tbuf, nt, &nfound); + } + return nfound; +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::LoadZTrees() +{ + treeMgr = new ZTreeMgr*[ntreeMgr = 1](); + if (cprm.tileLoadFlags & 0x0002) { + treeMgr[0] = ZTreeMgr::CreateFromFile(m_dataRootDir.c_str(), ZTreeMgr::LAYER_CLOUD); + } +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::InitHasIndividualFiles() +{ + hasIndividualFiles = new bool[ntreeMgr](); + if (cprm.tileLoadFlags & 0x0001) { + char path[MAX_PATH], dummy[MAX_PATH]; + sprintf_s(path, MAX_PATH, "%s\\Cloud", m_dataRootDir.c_str()); + hasIndividualFiles[0] = FileExists(path); + } +} + +// ----------------------------------------------------------------------- + +template<> +Tile * TileManager2::SearchTile (double lng, double lat, int maxlvl, bool bOwntex) const +{ + if (lng<0) return SearchTileSub(&tiletree[0], lng, lat, maxlvl, bOwntex); + else return SearchTileSub(&tiletree[1], lng, lat, maxlvl, bOwntex); +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::Unload(int lvl) +{ + tiletree[0].DelAbove(lvl); + tiletree[1].DelAbove(lvl); +} diff --git a/OVP/VulkanClient/Cloudmgr2.h b/OVP/VulkanClient/Cloudmgr2.h new file mode 100644 index 000000000..57485256c --- /dev/null +++ b/OVP/VulkanClient/Cloudmgr2.h @@ -0,0 +1,47 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +#ifndef __CLOUDMGR2_H +#define __CLOUDMGR2_H + +#include "Tilemgr2_imp.hpp" + +/** + * \brief Visual representation of planetary cloud tile. + * + * Rendering of planetary cloud layers using texture tiles at + * variable resolutions (new version). + */ +class CloudTile: public Tile { + friend class TileManager2Base; + template friend class TileManager2; + +public: + CloudTile (TileManager2Base *_mgr, int _lvl, int _ilat, int _ilng); + ~CloudTile (); + + inline QuadTreeNode* GetNode() const { return node; } + inline void SetNode (QuadTreeNode *_node) { node = _node; } + // Register the tile to a quad tree node + + double GetMinElev() const { return cloudalt; } // virtual from Tile:: + double GetMaxElev() const { return cloudalt; } // virtual from Tile:: + double GetMeanElev() const { return cloudalt; } // virtual from Tile:: + +protected: + virtual Tile *getParent() const { return node && node->Parent() ? node->Parent()->Entry() : NULL; } + // Return pointer to parent tile, if exists + + virtual void Load (); + virtual void PreLoad (); + virtual void Render (); + + double cloudalt; + TileManager2 *cmgr; // cloud tile manager interface + QuadTreeNode *node; // my node in the quad tree, if I'm part of a tree +}; + +#endif // !__CLOUDMGR2_H diff --git a/OVP/VulkanClient/Config.cpp b/OVP/VulkanClient/Config.cpp new file mode 100644 index 000000000..59c49ef16 --- /dev/null +++ b/OVP/VulkanClient/Config.cpp @@ -0,0 +1,312 @@ +// ============================================================== +// Configuration Manager +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007-2016 Martin Schweiger +// 2011-2016 Jarmo Nikkanen +// ============================================================== + +#include "Config.h" +#include "Orbitersdk.h" + +using std::min; +using std::max; + +static const char* cfgfile = "vkClient.cfg"; + +class vkConfig *Config; // configuration manager + +// ============================================================== + +vkConfig::vkConfig() +{ + Reset(); + ReadParams(); +} + +vkConfig::~vkConfig () +{ + WriteParams (); + delete []DebugFont; + DebugFont = NULL; + delete []SolCfg; + SolCfg = NULL; +} + +void vkConfig::Reset () +{ + Enable9On12 = 0; + OrbitalShadowMult = 0.85; + PlanetPreloadMode = 0; + PlanetLoadFrequency = 40; + Anisotrophy = 4; + SceneAntialias = 4; + DebugLvl = 1; + VCNearPlane = 0.1; + LightConfig = 2; + NearClipPlane = 0; + NVPerfHUD = 0; + PreLBaseVis = 0; + DebugFontSize = 18; + UseNormalMap = 1; + SketchpadFont = 1; + RwyLightAnimate = 1; + RwyLightAngle = 120.0; + RwyBrightness = 1.0; + Convergence = 0.2; + Separation = 65.0; + SunAngle = 10.0; + BumpAmp = 1.0; + PlanetGlow = 1.0; + EnvMapSize = 256; + EnvMapMode = 1; + EnvMapFaces = 1; + EnableGlass = 1; + EnableMeshDbg = 1; + ShadowMapMode = 1; + ShadowFilter = 0; + ShadowMapSize = 2048; + FrameRate = 200.0; + EnableLimiter = 0; + CustomCamMode = 1; + TileMipmaps = 1; + TextureMips = 1; + LODBias = 0.0; + MeshRes = 1; + MaxTiles = 1; + TileDebug = 0; + MicroMode = 1; + MicroFilter = 2; + BlendMode = 1; + MicroBias = 3; + PostProcess = 0; + ShaderDebug = 0; + PresentLocation = 1; + PlanetTileLoadFlags = 0x3; + TerrainShadowing = 2; + LabelDisplayFlags = LABEL_DISPLAY_RECORD | LABEL_DISPLAY_REPLAY; + CloudMicro = 1; + GDIOverlay = 0; + gcGUIMode = 0; + bAbsAnims = 0; + bCloudNormals = 1; + bFlats = 1; + bGlares = 1; + bLocalGlares = 0; + DebugBreak = 0; + ShaderCacheUse = 0; + bIrradiance = 1; + bAtmoQuality = 1; + NoPlanetAA = 0; + VCCascadeCount = 2; + ExpVCLight = 0; + + GFXIntensity = 0.5; + GFXDistance = 0.8; + GFXThreshold = 1.1; + GFXGamma = 1.0; + GFXSunIntensity = 1.2; + GFXLocalMax = 0.5; + GFXGlare = 0.3; + + DisableDriverManagement = 0; + DisableVisualHelperReadout = 0; + + AtmoCfg["Earth"] = "Earth.atm.cfg"; + + SolCfg = new char[64]; strcpy_s(SolCfg,64,"Sol"); + DebugFont = new char[64]; strcpy_s(DebugFont,64,"Fixed"); +} + +int vkConfig::MaxLights() +{ + if (LightConfig == 0) return 1; + if (LightConfig <= 2) return 4; + return 8; +} + +bool vkConfig::ReadParams () +{ + int i; + double d; + + FILEHANDLE hFile = oapiOpenFile(cfgfile, FILE_IN_ZEROONFAIL, ROOT); + if (!hFile) return false; + + if (oapiReadItem_int (hFile, (char*)"EnableDX12Wrapper", i)) Enable9On12 = max(0, min(1, i)); + if (oapiReadItem_float (hFile, (char*)"FrameRate", d)) FrameRate = max(0.0, min(300.0, d)); + if (oapiReadItem_int (hFile, (char*)"EnableLimiter", i)) EnableLimiter = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"CustomCamMode", i)) CustomCamMode = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"PlanetPreloadMode", i)) PlanetPreloadMode = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"PlanetTexLoadFreq", i)) PlanetLoadFrequency = max(1, min(1000, i)); + if (oapiReadItem_int (hFile, (char*)"Anisotrophy", i)) Anisotrophy = max(1, min(16, i)); + if (oapiReadItem_int (hFile, (char*)"SceneAntialias", i)) SceneAntialias = i; + if (oapiReadItem_int (hFile, (char*)"SketchpadFont", i)) SketchpadFont = max(0, min(2, i)); + if (oapiReadItem_int (hFile, (char*)"PreLoadBaseVisuals", i)) PreLBaseVis = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"EnableNormalMapping", i)) UseNormalMap = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"NearClipPlaneMode", i)) NearClipPlane = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"RwyLightAnimate", i)) RwyLightAnimate = max(0, min(1, i)); + if (oapiReadItem_float (hFile, (char*)"RwyLightAngle", d)) RwyLightAngle = max(10.0, min(180.0, d)); + if (oapiReadItem_float (hFile, (char*)"RwyBrightness", d)) RwyBrightness = max(0.3, min(3.0, d)); + if (oapiReadItem_float (hFile, (char*)"NightLightsAngle", d)) SunAngle = max(0.1, min(20.0, d)); + if (oapiReadItem_float (hFile, (char*)"BumpMapAmplitude", d)) BumpAmp = max(0.1, min(10.0, d)); + if (oapiReadItem_float (hFile, (char*)"PlanetGlow", d)) PlanetGlow = max(0.01, min(2.5, d)); + if (oapiReadItem_int (hFile, (char*)"EnvMapSize", i)) EnvMapSize = max(64, min(512, i)); + if (oapiReadItem_int (hFile, (char*)"EnvMapMode", i)) EnvMapMode = max(0, min(2, i)); + if (oapiReadItem_int (hFile, (char*)"EnvMapFaces", i)) EnvMapFaces = max(1, min(3, i)); + if (oapiReadItem_int (hFile, (char*)"ShadowMapMode", i)) ShadowMapMode = max(0, min(3, i)); + if (oapiReadItem_int (hFile, (char*)"ShadowMapFilter", i)) ShadowFilter = max(0, min(5, i)); + if (oapiReadItem_int (hFile, (char*)"ShadowMapSize", i)) ShadowMapSize = max(512, min(4096, i)); + if (oapiReadItem_int (hFile, (char*)"EnableGlass", i)) EnableGlass = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"TerrainShadowing", i)) TerrainShadowing = max(0, min(2, i)); + if (oapiReadItem_int (hFile, (char*)"EnableMeshDbg", i)) EnableMeshDbg = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"TileMipmaps", i)) TileMipmaps = max(0, min(2, i)); + if (oapiReadItem_int (hFile, (char*)"TextureMips", i)) TextureMips = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"TileDebug", i)) TileDebug = max(0, min(1, i)); + if (oapiReadItem_float (hFile, (char*)"StereoSeparation", d)) Separation = max(10.0, min(100.0, d)); + if (oapiReadItem_float (hFile, (char*)"StereoConvergence", d)) Convergence = max(0.05, min(1.0, d)); + if (oapiReadItem_int (hFile, (char*)"DebugLvl", i)) DebugLvl = i; + if (oapiReadItem_float (hFile, (char*)"VCNearPlane", d)) VCNearPlane = max(-1.0, min(1.0, d)); + if (oapiReadItem_int (hFile, (char*)"LightCongiguration", i)) LightConfig = max(min(4, i), 0); // Old typo stored? + if (oapiReadItem_int (hFile, (char*)"LightConfiguration", i)) LightConfig = max(min(4, i), 0); // ...this will override it anyhow + if (oapiReadItem_int (hFile, (char*)"DisableDrvMgm", i)) DisableDriverManagement = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"NVPerfHUD", i)) NVPerfHUD = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"DebugLineFontSize", i)) DebugFontSize = i; + if (oapiReadItem_int (hFile, (char*)"DisableVisualHelperReadout", i)) DisableVisualHelperReadout = max(0, min(1, i)); + if (oapiReadItem_float (hFile, (char*)"LODBias", d)) LODBias = max(-2.0, min(2.0, d)); + if (oapiReadItem_int (hFile, (char*)"MeshRes", i)) MeshRes = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"MaxTiles", i)) MaxTiles = max(0, min(2, i)); + if (oapiReadItem_int (hFile, (char*)"MicroMode", i)) MicroMode = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"MicroFilter", i)) MicroFilter = max(0, min(5, i)); + if (oapiReadItem_int (hFile, (char*)"BlendMode", i)) BlendMode = max(0, min(2, i)); + if (oapiReadItem_int (hFile, (char*)"MicroBias", i)) MicroBias = max(0, min(10, i)); + if (oapiReadItem_int (hFile, (char*)"CloudMicro", i)) CloudMicro = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"PostProcess", i)) PostProcess = max(0, min(2, i)); + if (oapiReadItem_int (hFile, (char*)"ShaderDebug", i)) ShaderDebug = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"PresentLocation", i)) PresentLocation = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"PlanetTileLoadFlags", i)) PlanetTileLoadFlags = max(1, min(3, i)); + if (oapiReadItem_int (hFile, (char*)"LabelDisplayFlags", i)) LabelDisplayFlags = max(0, min(3, i)); + if (oapiReadItem_int (hFile, (char*)"GDIOverlay", i)) GDIOverlay = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"gcGUIMode", i)) gcGUIMode = max(0, min(3, i)); + if (oapiReadItem_int (hFile, (char*)"AbsoluteAnimations", i)) bAbsAnims = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"NormalmappedClouds", i)) bCloudNormals = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"TerrainFlats", i)) bFlats = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"SunGlare", i)) bGlares = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"LightsGlare", i)) bLocalGlares = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"Irradiance", i)) bIrradiance = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"AtmoQuality", i)) bAtmoQuality = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"DebugBreak", i)) DebugBreak = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"ShaderCacheUse", i)) ShaderCacheUse = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"NoPlanetAA", i)) NoPlanetAA = max(0, min(1, i)); + if (oapiReadItem_int (hFile, (char*)"VCCascadeCount", i)) VCCascadeCount = max(1, min(3, i)); + if (oapiReadItem_int (hFile, (char*)"ExperimentalVCLight", i)) ExpVCLight = max(0, min(1, i)); + if (oapiReadItem_float (hFile, (char*)"OrbitalShadowMult", d)) OrbitalShadowMult = max(0.5, min(10.0, d)); + + if (oapiReadItem_float (hFile, (char*)"GFXIntensity", d)) GFXIntensity = max(0.0, min(1.0, d)); + if (oapiReadItem_float (hFile, (char*)"GFXDistance", d)) GFXDistance = max(0.0, min(1.0, d)); + if (oapiReadItem_float (hFile, (char*)"GFXThreshold", d)) GFXThreshold = max(0.5, min(2.0, d)); + if (oapiReadItem_float (hFile, (char*)"GFXGamma", d)) GFXGamma = max(0.3, min(2.5, d)); + if (oapiReadItem_float (hFile, (char*)"GFXSunIntensity", d)) GFXSunIntensity = max(0.5, min(2.5, d)); + if (oapiReadItem_float (hFile, (char*)"GFXLocalMax", d)) GFXLocalMax = max(0.001, min(1.0, d)); + if (oapiReadItem_float (hFile, (char*)"GFXGlare", d)) GFXGlare = max(0.1, min(10.0, d)); + + + oapiReadItem_string (hFile, (char*)"SolCfg", SolCfg); + oapiReadItem_string (hFile, (char*)"DebugLineFont", DebugFont); + + char Temp[256]; + if (oapiReadItem_string(hFile, (char*)"EarthAtmoCfg", Temp)) AtmoCfg["Earth"] = Temp; + + oapiCloseFile (hFile, FILE_IN_ZEROONFAIL); + + return true; +} + + +void vkConfig::WriteParams () +{ + FILEHANDLE hFile = oapiOpenFile (cfgfile, FILE_OUT, ROOT); + + oapiWriteItem_int (hFile, (char*)"EnableDX12Wrapper", Enable9On12); + oapiWriteItem_float (hFile, (char*)"FrameRate", FrameRate); + oapiWriteItem_int (hFile, (char*)"EnableLimiter", EnableLimiter); + oapiWriteItem_int (hFile, (char*)"CustomCamMode", CustomCamMode); + oapiWriteItem_int (hFile, (char*)"PlanetPreloadMode", PlanetPreloadMode); + oapiWriteItem_int (hFile, (char*)"PlanetTexLoadFreq", PlanetLoadFrequency); + oapiWriteItem_int (hFile, (char*)"Anisotrophy", Anisotrophy); + oapiWriteItem_int (hFile, (char*)"SceneAntialias", SceneAntialias); + oapiWriteItem_int (hFile, (char*)"SketchpadFont", SketchpadFont); + oapiWriteItem_int (hFile, (char*)"PreLoadBaseVisuals", PreLBaseVis); + oapiWriteItem_int (hFile, (char*)"EnableNormalMapping", UseNormalMap); + oapiWriteItem_int (hFile, (char*)"NearClipPlaneMode", NearClipPlane); + oapiWriteItem_int (hFile, (char*)"RwyLightAnimate", RwyLightAnimate); + oapiWriteItem_float (hFile, (char*)"RwyLightAngle", RwyLightAngle); + oapiWriteItem_float (hFile, (char*)"RwyBrightness", RwyBrightness); + oapiWriteItem_float (hFile, (char*)"NightLightsAngle", SunAngle); + oapiWriteItem_float (hFile, (char*)"BumpMapAmplitude", BumpAmp); + oapiWriteItem_float (hFile, (char*)"PlanetGlow", PlanetGlow); + oapiWriteItem_int (hFile, (char*)"EnvMapSize", EnvMapSize); + oapiWriteItem_int (hFile, (char*)"EnvMapMode", EnvMapMode); + oapiWriteItem_int (hFile, (char*)"EnvMapFaces", EnvMapFaces); + oapiWriteItem_int (hFile, (char*)"ShadowMapMode", ShadowMapMode); + oapiWriteItem_int (hFile, (char*)"ShadowMapFilter", ShadowFilter); + oapiWriteItem_int (hFile, (char*)"ShadowMapSize", ShadowMapSize); + oapiWriteItem_int (hFile, (char*)"TerrainShadowing", TerrainShadowing); + oapiWriteItem_int (hFile, (char*)"EnableGlass", EnableGlass); + oapiWriteItem_int (hFile, (char*)"EnableMeshDbg", EnableMeshDbg); + oapiWriteItem_int (hFile, (char*)"TileMipmaps", TileMipmaps); + oapiWriteItem_int (hFile, (char*)"TextureMips", TextureMips); + oapiWriteItem_int (hFile, (char*)"TileDebug", TileDebug); + oapiWriteItem_float (hFile, (char*)"StereoSeparation", Separation); + oapiWriteItem_float (hFile, (char*)"StereoConvergence", Convergence); + oapiWriteItem_int (hFile, (char*)"DebugLvl", DebugLvl); + oapiWriteItem_float (hFile, (char*)"VCNearPlane", VCNearPlane); + oapiWriteItem_int (hFile, (char*)"LightConfiguration", LightConfig); + oapiWriteItem_int (hFile, (char*)"DisableDrvMgm", DisableDriverManagement); + oapiWriteItem_int (hFile, (char*)"NVPerfHUD", NVPerfHUD); + oapiWriteItem_int (hFile, (char*)"DebugLineFontSize", DebugFontSize); + oapiWriteItem_int (hFile, (char*)"DisableVisualHelperReadout", DisableVisualHelperReadout); + oapiWriteItem_float (hFile, (char*)"LODBias", LODBias); + oapiWriteItem_int (hFile, (char*)"MeshRes", MeshRes); + oapiWriteItem_int (hFile, (char*)"MaxTiles", MaxTiles); + oapiWriteItem_int (hFile, (char*)"MicroMode", MicroMode); + oapiWriteItem_int (hFile, (char*)"MicroFilter", MicroFilter); + oapiWriteItem_int (hFile, (char*)"BlendMode", BlendMode); + oapiWriteItem_int (hFile, (char*)"MicroBias", MicroBias); + oapiWriteItem_int (hFile, (char*)"CloudMicro", CloudMicro); + oapiWriteItem_int (hFile, (char*)"PostProcess", PostProcess); + oapiWriteItem_int (hFile, (char*)"ShaderDebug", ShaderDebug); + oapiWriteItem_int (hFile, (char*)"PresentLocation", PresentLocation); + oapiWriteItem_int (hFile, (char*)"PlanetTileLoadFlags", PlanetTileLoadFlags); + oapiWriteItem_int (hFile, (char*)"LabelDisplayFlags", LabelDisplayFlags); + oapiWriteItem_int (hFile, (char*)"GDIOverlay", GDIOverlay); + oapiWriteItem_int (hFile, (char*)"gcGUIMode", gcGUIMode); + oapiWriteItem_int (hFile, (char*)"AbsoluteAnimations", bAbsAnims); + oapiWriteItem_int (hFile, (char*)"NormalmappedClouds", bCloudNormals); + oapiWriteItem_int (hFile, (char*)"TerrainFlats", bFlats); + oapiWriteItem_int (hFile, (char*)"SunGlare", bGlares); + oapiWriteItem_int (hFile, (char*)"LightsGlare", bLocalGlares); + oapiWriteItem_int (hFile, (char*)"Irradiance", bIrradiance); + oapiWriteItem_int (hFile, (char*)"AtmoQuality", bAtmoQuality); + oapiWriteItem_int (hFile, (char*)"DebugBreak", DebugBreak); + oapiWriteItem_int (hFile, (char*)"ShaderCacheUse", ShaderCacheUse); + oapiWriteItem_int (hFile, (char*)"NoPlanetAA", NoPlanetAA); + oapiWriteItem_int (hFile, (char*)"VCCascadeCount", VCCascadeCount); + oapiWriteItem_int (hFile, (char*)"ExperimentalVCLight", ExpVCLight); + + oapiWriteItem_float (hFile, (char*)"OrbitalShadowMult", OrbitalShadowMult); + oapiWriteItem_float (hFile, (char*)"GFXIntensity", GFXIntensity); + oapiWriteItem_float (hFile, (char*)"GFXDistance", GFXDistance); + oapiWriteItem_float (hFile, (char*)"GFXThreshold", GFXThreshold); + oapiWriteItem_float (hFile, (char*)"GFXGamma", GFXGamma); + oapiWriteItem_float (hFile, (char*)"GFXSunIntensity", GFXSunIntensity); + oapiWriteItem_float (hFile, (char*)"GFXLocalMax", GFXLocalMax); + oapiWriteItem_float (hFile, (char*)"GFXGlare", GFXGlare); + + oapiWriteItem_string (hFile,(char*) "SolCfg", SolCfg); + oapiWriteItem_string (hFile,(char*) "DebugLineFont", DebugFont); + + oapiWriteItem_string(hFile, (char*)"EarthAtmoCfg", (char *)AtmoCfg["Earth"].c_str()); + + oapiCloseFile (hFile, FILE_OUT); +} diff --git a/OVP/VulkanClient/Config.h b/OVP/VulkanClient/Config.h new file mode 100644 index 000000000..c72e9d9cd --- /dev/null +++ b/OVP/VulkanClient/Config.h @@ -0,0 +1,119 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __vkCONFIG_H +#define __vkCONFIG_H +#include +#include + +extern class vkConfig *Config; + +/** + * \brief Configuration Manager + * + * This class provides access to config-parameters that were read from the + * config file. + */ +class vkConfig { +public: + + vkConfig (); + ~vkConfig (); + + void Reset(); + bool ReadParams(); + void WriteParams(); + int MaxLights(); + + /// Bit flags for "LabelDisplayFlags" parameter. + static const int LABEL_DISPLAY_RECORD = 0x1; ///< Display label "Record" on active recording session + static const int LABEL_DISPLAY_REPLAY = 0x2; ///< Display label "Replay" on active playback session + + int Enable9On12; ///< Enable DX9 through DX12 + int PlanetPreloadMode; ///< Planet preload mode setting (0=load on demand, 1=preload) + int PlanetLoadFrequency; ///< Load frequency for on-demand textures \[Hz\] (1...1000) + int Anisotrophy; ///< Anisotropic filtering setting \[factor\] (1...16) + int SceneAntialias; ///< Antialiasing setting \[factor\] (0...) + int DisableDriverManagement; ///< Disable the vk driver management \[sets the D3DCREATE_DISABLE_DRIVER_MANAGEMENT behavior flag\] (0=default, 1:disabled) + int DisableVisualHelperReadout; ///< Disable the hooking of the visual helper windows, to allow access to config parameter that Orbiter core doesn't provide (0=normal mode, 1=disable any hooking) + int NearClipPlane; ///< Near clip plane mode (0,1) + int DebugBreak; ///< Enable Debug Break + int PreLBaseVis; ///< Preload base visuals (0=load on demand, 1=preload) + int DebugFontSize; ///< Debug font height \[pixel\] (default=18px) + int UseNormalMap; ///< Enable normal mapping (0,1) + int SketchpadFont; ///< Sketchpad Font (0=Crisp, 1=Default, 2=Cleartype, 3=Proof Quality) + int RwyLightAnimate; ///< Runway light animate (0,1) + double RwyLightAngle; ///< Runway light angle \[deg\] (10...180) + double RwyBrightness; ///< Runway light brightness (0.3...3.0) + double VCNearPlane; ///< Virtual cockpit near clip-plane distance \[m\] (-1.0...1.0, default=0.1) + double Convergence; ///< StereoScopic 3D convergence distance \[m\] (0.05...1.0, default=0.2) + double Separation; ///< StereoScopic 3D depth of field separation \[m\] (10.0...100.0, default=65) + double SunAngle; ///< Sun-angle above horizon when night-lights set it \[deg\] (0.1...20.0, default=10) + double BumpAmp; ///< Bump map amplification setting (0.1...10.0, default=1) + double PlanetGlow; ///< Intensity of planet glow effect (0.01...2.0, default=0.7) + double FrameRate; ///< Frame-rate limiter + double OrbitalShadowMult; ///< Multiplier for cloud shadows for Orbital flight + int EnableLimiter; ///< Enable frame-rate limiter + int DebugLvl; ///< Level of debug output 'verbosity'. Higher values create more detailed output (0...4, default=1) + int LabelDisplayFlags; ///< Label display option flags. For example the "Record" and "Replay" labels (0=all disabled, 1=show record label, 2=show replay label, 3=show both \[default\]) + int LightConfig; ///< Light emitter configuration + int NVPerfHUD; ///< ??? (0,1) + int EnvMapSize; ///< Environment map size (64...512) + int EnvMapMode; ///< Environment map mode (0=disabled, 1=planet only, 2=full scene) + int EnvMapFaces; ///< Number of environment map faces render per frame (1..6, default=1) + int EnableGlass; ///< Enable improved glass shading (Fresnel reflection) + int EnableMeshDbg; ///< Enable mesh debugger + int ShadowMapMode; ///< Shadow Mapping Mode + int ShadowFilter; ///< Shadow Mapping Filter + int ShadowMapSize; ///< Shadow Map size + int TerrainShadowing; ///< Terrain Shadowing mode (0=None, 1=Stencil, 2=Projected, default=1) + int CustomCamMode; ///< Custom Camera Mode + int TileMipmaps; ///< Enable surface tile mipmaps + int ShaderDebug; ///< Shader Debug Logging enable flag (0=disabled, 1=enabled) + double LODBias; ///< 3D Terrain resolution bias + int MeshRes; ///< Tile patch mesh resolution + int MaxTiles; + int TileDebug; ///< Enable tile debugger + int TextureMips; ///< Texture mipmap auto-gen policy + int PostProcess; ///< Enable post processing effects + int MicroMode; ///< Surface micro textures enable flag (0=disabled, 1=enabled) + int MicroFilter; ///< Surface micro texture filter mode (0=Point, 1=Linear ,2=Anisotropic 2x ,3=Anisotropic 4x, 4=Anisotropic 8x, Anisotropic 16x) + int BlendMode; ///< Surface micro texture light blend mode (0=Soft, 1=Normal, 2=Hard) + int PresentLocation; ///< PresentScene call-location (0=at clbkDisplayFrame, 1=at clbkRenderScene) + int ShaderCacheUse; ///< Shader cache usage flag (0=disabled, 1=enabled) + int MicroBias; ///< Mipmap LOD Bias for surface micro textures + int CloudMicro; ///< Cloud layer micro textures + int PlanetTileLoadFlags; ///< Planet Tile Load Flags (0x1=load tiles from directory tree, 0x2=load tiles from compressed archive, 0x3=both \[try directory tree first, then archive\]) + int GDIOverlay; ///< GDI Overlay + int gcGUIMode; ///< gcGUI Operation Mode + int bAbsAnims; ///< Absolute animations + int bCloudNormals; ///< Felix24's Cloud normals implementation test + int bFlats; ///< Face's terrain flattening + int bGlares; + int bLocalGlares; + int bIrradiance; + int bAtmoQuality; + int NoPlanetAA; ///< Disable planet surface anti-aliasing to prevent white pixels at horizon + int VCCascadeCount; + int ExpVCLight; + char *DebugFont; ///< Font face for debug lines (default="Fixed") + char *SolCfg; ///< Solar system to use (default="Sol") + double GFXIntensity; ///< Post Processing | Light glow intensity (0.0...1.0, default=0.5) + double GFXDistance; ///< Post Processing | Light glow distance (0.0...1.0, default=0.8) + double GFXThreshold; ///< Post Processing | Glow threshold (0.5...2.0, default=1.1) + double GFXGamma; ///< Post Processing | Gamma (0.3...2.5, default=1.0) + double GFXSunIntensity; ///< Light Configuration| Sunlight Intensity (0.5...2.5, default=1.2) + double GFXLocalMax; ///< Light Configuration| Local Lights Max (0.001...1.0, default=0.5) + double GFXGlare; ///< Sun glare intensity| (0.001...1.0, default=0.5) + + std::map AtmoCfg; + +private: + +}; + +#endif // !__vkCONFIG_H diff --git a/OVP/VulkanClient/ControlPanel.cpp b/OVP/VulkanClient/ControlPanel.cpp new file mode 100644 index 000000000..d64be3da6 --- /dev/null +++ b/OVP/VulkanClient/ControlPanel.cpp @@ -0,0 +1,350 @@ +// =========================================================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// =========================================================================================== + + +#include "Scene.h" +#include "Frame.h" +#include "Util.h" +#include "Config.h" +#include "Client.h" +#include "Surface.h" +#include "Catalog.h" +#include "Mesh.h" +#include "psapi.h" +#include "DebugControls.h" +#include +#include + +using namespace oapi; + + +DWORD TextureSizeInBytes(LPDIRECT3DTEXTURE9 pTex) +{ + D3DSURFACE_DESC desc; + pTex->GetLevelDesc(0, &desc); + DWORD lev = pTex->GetLevelCount(); + DWORD size = desc.Height*desc.Width; + if (desc.Format==D3DFMT_DXT1) size=size>>1; + if (desc.Format==D3DFMT_A8R8G8B8) size=size<<2; + if (desc.Format==D3DFMT_X8R8G8B8) size=size<<2; + if (desc.Format==D3DFMT_R5G6B5) size=size<<1; + if (desc.Format==D3DFMT_A4R4G4B4) size=size<<1; + if (desc.Format==D3DFMT_R8G8B8) size=size*3; + + if (lev) size += (size>>2) + (size>>4) + (size>>8); + return size; +} + + +void vkClient::Label(const char *format, ...) +{ + char buffer[256]; + va_list args; + va_start(args, format); + + _vsnprintf_s(buffer, 255, 255, format, args); + + va_end(args); + + int len = lstrlen(buffer); + pItemsSkp->Text(20, LabelPos, buffer, len); + LabelPos += 22; +} + +double Get(vkTime &t) +{ + return t.time / max(1.0, min(1000.0, t.count)); +} + +void Reset(vkTime &t) +{ + t.count = t.time = t.peak = 0.0; +} + +void vkClient::DrawTimeBar(double t, double s, double f, DWORD color, const char *label) +{ + static double x = 8; + static int z = 100; + char legend[256]; + + if (color == 0) { + x = 8; + z = 100; + return; + } + + sprintf_s(legend, 256, "%.64s, %0.2fms (%.2f%%)", label, (t/f)*0.001, t*0.0001); + + vkPadBrush brush(color); + int y = viewH - 20; + int q = viewW - 640; + pItemsSkp->SetBrush(&brush); + pItemsSkp->Rectangle(int(x), y, int(x + t*s), y + 16); + pItemsSkp->Rectangle(q, z, q + 32, z + 16); + pItemsSkp->Text(q+40, z, legend, -1); + pItemsSkp->SetBrush(NULL); + x += t*s; + z += 32; +} + +void vkClient::RenderControlPanel() +{ + static std::map TileBuf; + static const char *OnOff[]={"Off","On"}; + static const char *SkpU[]={"Auto","GDI"}; + + LPDIRECT3DDEVICE9 dev = pDevice; + + PROCESS_MEMORY_COUNTERS_EX memstats; + memstats.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX); + GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&memstats, sizeof(memstats)); + + + + pItemsSkp = oapiGetSketchpad(GetBackBufferHandle()); + + oapi::Pen * pen = oapiCreatePen(1, 10, 0xFFFF00); + oapi::Pen * nullp = oapiCreatePen(0, 1, 0xFFFFFF); + oapi::Brush *brush = oapiCreateBrush(0xA0000000); + oapi::Font *largef = oapiCreateFont(38,false,(char*)"Arial", FONT_BOLD); + oapi::Font *smallf = oapiCreateFont(22,false,(char*)"Fixed"); + + pItemsSkp->SetPen(nullp); + + pItemsSkp->SetBrush(brush); + pItemsSkp->Rectangle(-1, -1, viewW+1, viewH+1); + + + pItemsSkp->SetTextColor(0x00FF00); + pItemsSkp->SetFont(largef); + pItemsSkp->Text(20,70,"vkClient Statistics",21); + pItemsSkp->SetFont(smallf); + LabelPos = 130; + + DWORD plain_count = 0, plain_size = 0; + DWORD textr_count = 0, textr_size = 0; + DWORD rendt_count = 0, rendt_size = 0; + DWORD rttex_count = 0, rttex_size = 0; + DWORD dyntx_count = 0, dyntx_size = 0; + DWORD sysme_count = 0, sysme_size = 0; + DWORD duall_count = 0, duall_size = 0; + + size_t nSurf = SurfaceCatalog.size(); + + for (auto pSurf : SurfaceCatalog) + { + if (pSurf->desc.Pool==D3DPOOL_DEFAULT) { + if (pSurf->IsRenderTarget()) { + if (pSurf->IsTexture()) { + rttex_count++; + rttex_size += pSurf->GetSizeInBytes(); + } + else { + rendt_count++; + rendt_size += pSurf->GetSizeInBytes(); + } + } + else { + textr_count++; + textr_size += pSurf->GetSizeInBytes(); + } + } + else { + sysme_count++; + sysme_size += pSurf->GetSizeInBytes(); + } + } + + Label("Application Size.....: %lu MB", memstats.PrivateUsage >> 20); + Label("Available video mem..: %u MB", dev->GetAvailableTextureMem()>>20); + Label("Surface Handles......: %lu", nSurf); + Label("SystemMem Surfaces...: %u (%u MB)", sysme_count, sysme_size>>20); + Label("Dynamic Textures.....: %u (%u MB)", dyntx_count, dyntx_size>>20); + Label("Render Targets.......: %u (%u MB)", rendt_count, rendt_size>>20); + Label("Render Textures......: %u (%u MB)", rttex_count, rttex_size>>20); + Label("Dual Layer Surfaces..: %u (%u MB)", duall_count, duall_size>>20); + Label("Plain Textures.......: %u (%u MB)", textr_count, textr_size>>20); + + LabelPos += 22; + + size_t tt_c = g_pTexmgr_tt->UsedSize() + g_pTexmgr_tt->FreeSize(); + size_t tv_c = g_pVtxmgr_vb->UsedSize() + g_pVtxmgr_vb->FreeSize(); + + std::stringstream tiles{}; + + for (auto& x : vkStats.TilesRendered) { + if (x.second != 0) TileBuf[x.first] = x.second; + x.second = 0; + } + + if (vkStats.TilesRendered.size()) { + if (TileBuf.count(RENDERPASS_MAINSCENE)) tiles << "Main[" << TileBuf[RENDERPASS_MAINSCENE] << "] "; + if (TileBuf.count(RENDERPASS_CUSTOMCAM)) tiles << "Cams[" << TileBuf[RENDERPASS_CUSTOMCAM] << "] "; + if (TileBuf.count(RENDERPASS_ENVCAM)) tiles << "Env[" << TileBuf[RENDERPASS_ENVCAM] << "] "; + } + + Label("Tile Texture Cache...: Used[%u] Free[%u] Capacity (%u MB)", g_pTexmgr_tt->UsedCount(), g_pTexmgr_tt->FreeCount(), tt_c >> 20); + Label("Tile Vertex Cache....: Used[%u] Free[%u] Capacity (%u MB)", g_pVtxmgr_vb->UsedCount(), g_pVtxmgr_vb->FreeCount(), tv_c >> 20); + Label("Tiles Allocated......: %u", vkStats.TilesAllocated); + Label("Tiles Renderred......: %s", tiles.str().c_str()); + + DWORD tot_verts = 0; + DWORD tot_trans = 0; + DWORD tot_group = 0; + + for (auto pMesh : MeshCatalog) + { + if (pMesh) { + tot_verts += pMesh->GetVertexCount(); + tot_trans += pMesh->GetGroupTransformCount(); + tot_group += pMesh->GetGroupCount(); + } + } + + static DWORD matchg = 0, texchg = 0; + static DWORD verts = 0, grps = 0, meshes = 0; + static double DCPeak = 0.0; + static double LockPeak = 0.0; + + LabelPos += 22; + Label("Mesh Vtx Allocated...: %u (%u MB)", tot_verts, (tot_verts*sizeof(NMVERTEX))>>20); + Label("Groups Allocated.....: %u", tot_group); + Label("Group Tarnsforms.....: %u", tot_trans); + Label("Mesh vertices render.: %u", verts); + Label("Mesh groups rendered.: %u", grps); + Label("Meshes rendered......: %u", meshes); + Label("Texture changes......: %u", texchg); + Label("Material changes.....: %u", matchg); + Label("GetDC peak time......: %0.2fms", DCPeak*0.001); + Label("Lock wait peak time..: %0.2fms", LockPeak*0.001); + + if (DebugControls::IsActive()) { + + if (DebugControls::IsSelectedGroupRendered()) Label("Group is rendered....: Yes"); + else Label("Group is rendered....: No"); + + vObject *vObj = DebugControls::GetVisual(); + if (vObj) { + DWORD nMesh = vObj->GetMeshCount(); + for (DWORD i = 0; i < nMesh; i++) { + vkMesh *hMesh = vObj->GetMesh(i); + hMesh->ResetRenderStatus(); + } + } + } + + // DRAW TIME LINE ------------------------------------------------------------------ + + static double systime = 0.0; + static double frames = 1.0; + static double lock; + static double blit; + static double getdc; + static double scene; + static double scale; + static double outside; + static double update; + static double display; + // ------------------------------------- +// static double pln_srf; +// static double pln_cld; + // ------------------------------------- +// static double scn_pst; +// static double scn_ves; +// static double scn_vc; +// static double scn_hud; +// static double scn_cam; + // ------------------------------------- +// static double prt_cam; +// static double prt_env; +// static double prt_blr; + + if (oapiGetSysMJD() > systime) { + + systime = oapiGetSysMJD() + 0.8 / 86400.0; + + frames = vkStats.Timer.Scene.count; + double iframes = 1.0 / frames; + + matchg = DWORD(double(vkStats.Mesh.MtrlChanges) * iframes); + texchg = DWORD(double(vkStats.Mesh.TexChanges) * iframes); + verts = DWORD(double(vkStats.Mesh.Vertices) * iframes); + grps = DWORD(double(vkStats.Mesh.MeshGrps) * iframes); + meshes = DWORD(double(vkStats.Mesh.Meshes) * iframes); + DCPeak = vkStats.Timer.GetDC.peak; + LockPeak = vkStats.Timer.LockWait.peak; + + double total = vkStats.Timer.FrameTotal.time; + + scene = vkStats.Timer.Scene.time; + update = vkStats.Timer.Update.time; + display = vkStats.Timer.Display.time; + lock = vkStats.Timer.LockWait.time; + blit = vkStats.Timer.BlitTime.time; + getdc = vkStats.Timer.GetDC.time; + + // Time spend outside of client + outside = total - (scene + update + display + lock + blit + getdc); + + scale = double(viewW - 16) / total; + + // ------------------------------------- + Reset(vkStats.Timer.CamVis); + Reset(vkStats.Timer.Scene); + Reset(vkStats.Timer.Update); + Reset(vkStats.Timer.Display); + Reset(vkStats.Timer.LockWait); + Reset(vkStats.Timer.BlitTime); + Reset(vkStats.Timer.GetDC); + Reset(vkStats.Timer.FrameTotal); + // ------------------------------------- + Reset(vkStats.Timer.HUDOverlay); + // ------------------------------------- + memset(&vkStats.Mesh, 0, sizeof(vkStats.Mesh)); + } + + + DrawTimeBar(0, 0, 0, 0); + DrawTimeBar(outside, scale, frames, 0x774411, "Non-client specific tasks"); + DrawTimeBar(blit, scale, frames, 0xFF5555, "Time used in Bliting"); + DrawTimeBar(getdc, scale, frames, 0x33FFFF, "Time used in GetDC"); + DrawTimeBar(lock, scale, frames, 0x0066FF, "Waiting Locks (CPU-IDLE)"); + DrawTimeBar(display, scale, frames, 0x000099, "Waiting Present (CPU-IDLE)"); + DrawTimeBar(scene, scale, frames, 0x00CC00, "Drawing Scene"); + + //------------------------------ + /*DrawTimeBar(scn_cam, scale, frames, 0x000055, "Visual updates"); + DrawTimeBar(pln_srf, scale, frames, 0x005500, "Planet's surface"); + DrawTimeBar(pln_cld, scale, frames, 0x55FF55, "Planet's cloud layer"); + DrawTimeBar(scn_ves, scale, frames, 0xFF0000, "Vessel objects"); + DrawTimeBar(scn_vc, scale, frames, 0xFFFF00, "Virtual cockpit"); + DrawTimeBar(scn_hud, scale, frames, 0x777700, "HUD and 2D panels"); + DrawTimeBar(scn_pst, scale, frames, 0xFF00FF, "Post-processing effects"); + //------------------------------ + DrawTimeBar(prt_cam, scale, frames, 0x00FFFF, "Custom cameras"); + DrawTimeBar(prt_env, scale, frames, 0x007777, "Environment map"); + DrawTimeBar(prt_blr, scale, frames, 0x004444, "EnvMap blur"); + //------------------------------- + DrawTimeBar(scene, scale, frames, 0x777777, "Other rendering tasks");*/ + + pItemsSkp->SetPen(NULL); + pItemsSkp->SetBrush(NULL); + + oapiReleaseFont(smallf); + oapiReleaseFont(largef); + oapiReleasePen(pen); + oapiReleasePen(nullp); + oapiReleaseBrush(brush); + + oapiReleaseSketchpad(pItemsSkp); +} + + + +bool vkClient::ControlPanelMsg(WPARAM wParam) +{ + return false; +} diff --git a/OVP/VulkanClient/DebugControls.cpp b/OVP/VulkanClient/DebugControls.cpp new file mode 100644 index 000000000..1eacf5330 --- /dev/null +++ b/OVP/VulkanClient/DebugControls.cpp @@ -0,0 +1,2712 @@ +// =========================================================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// =========================================================================================== + + +#include "Client.h" +#include "resource.h" +#include "Config.h" +#include "Surface.h" +#include "DebugControls.h" +#include "Commctrl.h" +#include "vObject.h" +#include "vVessel.h" +#include "vPlanet.h" +#include "Mesh.h" +#include "MaterialMgr.h" +#include "VectorHelpers.h" +#include "OapiExtension.h" +#include +#include + +enum scale { LIN, SQRT, SQR }; + +using namespace oapi; +using std::min; +using std::max; + +extern HINSTANCE g_hInst; +extern vkClient *g_client; + +// Little binary helper +#define SETFLAG(bitmap, bit, value) (value ? bitmap |= bit : bitmap &= ~bit) +#define CLAMP(x,a,b) min(max(a,x),b) + +namespace DebugControls { + +DbgDisplay dbgdsp = {}; +int ambdir = -1; +int bkl_id = 0; +int probe_id = -1; +DWORD dwGFX, dwCmd, nMesh, nGroup, sMesh, sGroup, debugFlags, dspMode, camMode, SelColor, sEmitter; +double camSpeed; +float cpr, cpg, cpb, cpa; +double resbias = 4.0; +char visual[64]; +int origwidth; +HWND hGfxDlg = NULL; +HWND hDlg = NULL; +HWND hDataWnd = NULL; +vObject *vObj = NULL; +vkMesh* hSelMesh = NULL; +std::string buffer(""); +std::string buffer2(""); +FVECTOR3 PickLocation; + +std::map Emitters; + +HWND hTipRed, hTipGrn, hTipBlu, hTipAlp; + +OPENFILENAMEA OpenTex, SaveTex, SaveMesh; +char OpenFileName[255]; +char SaveFileName[255]; +char SaveMeshName[255]; +char SaveMeshTitle[255]; + +void UpdateMaterialDisplay(bool bSetup=false); +void SetGFXSliders(); +void UpdateLightsSlider(); +INT_PTR CALLBACK WndProcGFX(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void OpenGFXDlgClbk(void *context); + + +struct _Variable { + float min, max, extmax, def; + scale Scl; + bool bUsed; + bool bGamma; + char tip[80]; +}; + +struct MatParams { + MatParams(string n, DWORD i) : name(n), id(i) {} + string name; + DWORD id; +}; + +std::vector PrmList; +std::vector Dropdown; + +struct _Params { + _Variable var[4]; +}; + + +_Params Params[10] = { 0 }; + +struct DbgMeshGrp { + vector Lines; + vector Vtx; + vector Idx; +}; + + +class DbgMesh +{ +public: + + DbgMesh(const char* file) + { + enum sec { header, group, geom, materials, textures }; + sec sec = header; + char buf[256]; + DWORD ng = 0, nm = 0, nt = 0, nv = 0, ni = 0, gi = 0; + std::ifstream is(file); + + if (is.is_open()) + { + while (is.getline(buf, 256)) + { + if (!_strnicmp(buf, "MATERIALS", 9)) sec = materials; + if (!_strnicmp(buf, "TEXTURES", 8)) sec = textures; + + if (sec == header) Header.push_back(buf); + if (sec == materials) Materials.push_back(buf); + if (sec == textures) Textures.push_back(buf); + + if (!_strnicmp(buf, "GROUPS", 6)) + { + if (sscanf(buf + 6, "%d", &ng) != 1) throw std::invalid_argument("End of file"); + Groups.resize(ng); + + for (gi = 0;gi> key >> x; + return x; + } + } + return string(""); + } + + void SetMeshFlags(DWORD f) + { + std::stringstream s; + s << std::hex << f; + for (auto &a : Header) { + if (a.find("MESHFLAGS") != string::npos) { + a = string("MESHFLAGS ") + s.str(); + return; + } + } + Header.insert(Header.begin() + 1, string("MESHFLAGS ") + s.str()); + } + + void SetGroupFlags(DWORD grp, DWORD f) + { + std::stringstream s; + s << std::hex << f; + for (auto &a : Groups[grp].Lines) { + if (a.find("FLAG") != string::npos) { + a = string("FLAG ") + s.str(); + return; + } + } + auto b = Groups[grp].Lines.begin(); + Groups[grp].Lines.insert(b, string("FLAG ") + s.str()); + } + + void SetGroupMaterial(DWORD grp, DWORD x) + { + for (auto& a : Groups[grp].Lines) { + if (a.find("MATERIAL") != string::npos) { + a = string("MATERIAL ") + std::to_string(x + 1); + return; + } + } + auto b = Groups[grp].Lines.begin(); + Groups[grp].Lines.insert(b, string("MATERIAL ") + std::to_string(x)); + } + + void SetGroupTexture(DWORD grp, DWORD x) + { + for (auto& a : Groups[grp].Lines) { + if (a.find("TEXTURE") != string::npos) { + a = string("TEXTURE ") + std::to_string(x); + return; + } + } + auto b = Groups[grp].Lines.begin(); + Groups[grp].Lines.insert(b, string("TEXTURE ") + std::to_string(x)); + } + + void SetGroupLabel(DWORD grp, string x) + { + for (auto& a : Groups[grp].Lines) { + if (a.find("LABEL") != string::npos) { + a = string("LABEL ") + x; + return; + } + } + auto b = Groups[grp].Lines.begin(); + Groups[grp].Lines.insert(b, string("LABEL ") + x); + } + + vector Header; + vector Materials; + vector Textures; + vector Groups; + string key; +}; + + +map dbgMsh; +DbgMesh* hDbgMsh = NULL; + + +// =========================================================================== +// Same functionality than 'official' GetConfigParam, but for non-provided +// debug-control config parameters +// +const void *GetConfigParam (DWORD paramtype) +{ + switch (paramtype) { + case CFGPRM_GETSELECTEDMESH : return (void*)&sMesh; + case CFGPRM_GETSELECTEDGROUP : return (void*)&sGroup; + case CFGPRM_GETDEBUGFLAGS : return (void*)&debugFlags; + case CFGPRM_GETDISPLAYMODE : return (void*)&dspMode; + case CFGPRM_GETCAMERAMODE : return (void*)&camMode; + case CFGPRM_GETCAMERASPEED : return (void*)&camSpeed; + default : return NULL; + } +} + +// ============================================================================================= +// +float GetFloatFromBox(HWND hWnd, int item) +{ + char lbl[32]; + GetWindowTextA(GetDlgItem(hWnd, item), lbl, 32); + return float(atof(lbl)); +} + +// ============================================================================================= +// +HWND CreateToolTip(int toolID, HWND hDlg, PTSTR pszText) +{ + if (!toolID || !hDlg || !pszText) return NULL; + + // Get the window of the tool. + HWND hwndTool = GetDlgItem(hDlg, toolID); + // Create the tooltip. g_hInst is the global instance handle. + HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL, WS_POPUP |TTS_ALWAYSTIP | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hDlg, NULL, g_hInst, NULL); + + if (!hwndTool || !hwndTip) return NULL; + + // Associate the tooltip with the tool. + TOOLINFO toolInfo = { 0 }; + toolInfo.cbSize = sizeof(toolInfo); + toolInfo.hwnd = hDlg; + toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolInfo.uId = (UINT_PTR)hwndTool; + toolInfo.lpszText = pszText; + SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); + + return hwndTip; +} + + +void SetToolTip(int toolID, HWND hTip, const char *a) +{ + HWND hwndTool = GetDlgItem(hDlg, toolID); + TOOLINFO toolInfo = { 0 }; + toolInfo.cbSize = sizeof(toolInfo); + toolInfo.hwnd = hDlg; + toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolInfo.uId = (UINT_PTR)hwndTool; + toolInfo.lpszText = (PTSTR)a; + SendMessage(hTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&toolInfo); +} + +// ============================================================================================= +// +void Create() +{ + vObj = NULL; + hDlg = NULL; + hGfxDlg = NULL; + nMesh = 0; + nGroup = 0; + sMesh = 0; + sGroup = 0; + debugFlags = 0; + camSpeed = 0.5; + camMode = 0; + dspMode = 0; + SelColor = 0; + PickLocation = FVECTOR3(0,0,0); + + cpr = cpg = cpb = cpa = 0.0f; + + if (Config->EnableMeshDbg) { + dwCmd = oapiRegisterCustomCmd((char*)"vk Debug Controls", (char*)"This dialog allows to control various debug and development features", OpenDlgClbk, NULL); + } + else { + dwCmd = 0; + } + + dwGFX = oapiRegisterCustomCmd((char*)"vk Graphics Controls", (char*)"This dialog allows to control various graphics options", OpenGFXDlgClbk, NULL); + + resbias = 4.0 + Config->LODBias; + + memset(&OpenTex, 0, sizeof(OPENFILENAME)); + memset(OpenFileName, 0, sizeof(OpenFileName)); + + OpenTex.lStructSize = sizeof(OPENFILENAME); + OpenTex.lpstrFile = OpenFileName; + OpenTex.lpstrInitialDir = "Textures\0"; + OpenTex.nMaxFile = sizeof(OpenFileName); + OpenTex.lpstrFilter = "*.dds;*.jpg;*.png;*.hdr;*.bmp;*.tga\0"; + OpenTex.nFilterIndex = 0; + OpenTex.lpstrFileTitle = NULL; + OpenTex.nMaxFileTitle = 0; + OpenTex.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; + + memset(&SaveTex, 0, sizeof(OPENFILENAME)); + memset(SaveFileName, 0, sizeof(SaveFileName)); + + SaveTex.lStructSize = sizeof(OPENFILENAME); + SaveTex.lpstrFile = SaveFileName; + SaveTex.lpstrInitialDir = "Textures\0"; + SaveTex.nMaxFile = sizeof(SaveFileName); + SaveTex.lpstrFilter = "*.dds\0"; + SaveTex.nFilterIndex = 0; + SaveTex.lpstrFileTitle = NULL; + SaveTex.nMaxFileTitle = 0; + SaveTex.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; + + memset(&SaveMesh, 0, sizeof(OPENFILENAME)); + memset(SaveMeshName, 0, sizeof(SaveMeshName)); + memset(SaveMeshTitle, 0, sizeof(SaveMeshTitle)); + + SaveMesh.lStructSize = sizeof(OPENFILENAME); + SaveMesh.lpstrFile = SaveMeshName; + SaveMesh.lpstrInitialDir = "Meshes\0"; + SaveMesh.nMaxFile = sizeof(SaveMeshName); + SaveMesh.lpstrFilter = "*.msh\0"; + SaveMesh.nFilterIndex = 0; + SaveMesh.lpstrFileTitle = SaveMeshTitle; + SaveMesh.nMaxFileTitle = 0; + SaveMesh.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; + + PrmList.push_back(MatParams("Diffuse", 0)); + PrmList.push_back(MatParams("Ambient", 1)); + PrmList.push_back(MatParams("Specular", 2)); + PrmList.push_back(MatParams("Emission", 3)); + PrmList.push_back(MatParams("Reflect", 4)); + PrmList.push_back(MatParams("Smoothness", 5)); + PrmList.push_back(MatParams("Fresnel", 6)); + PrmList.push_back(MatParams("Emission2", 7)); + PrmList.push_back(MatParams("Metalness", 8)); + PrmList.push_back(MatParams("SpecialFX", 9)); +} + +// ============================================================================================= +// +void Close() +{ + if (hDlg != NULL) { + oapiCloseDialog(hDlg); + hDlg = NULL; + } + vObj = NULL; +} + +// ============================================================================================= +// +bool IsActive() +{ + return (hDlg!=NULL); +} + +// ============================================================================================= +// +int GetSceneDebug() +{ + if (!hDlg) return -1; + return (int)SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_GETCURSEL, 0, 0); +} + +// ============================================================================================= +// +DbgDisplay GetSelectedEnvMap() +{ + if (!hDlg) return DbgDisplay(0); + return (DbgDisplay)SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_GETCURSEL, 0, 0); +} + +// ============================================================================================= +// +void Release() +{ + vObj = NULL; + hDlg = NULL; + hGfxDlg = NULL; + if (dwCmd) oapiUnregisterCustomCmd(dwCmd); + if (dwGFX) oapiUnregisterCustomCmd(dwGFX); + dwCmd = NULL; + dwGFX = NULL; + for (auto x : dbgMsh) SAFE_DELETE(x.second); +} + +// ============================================================================================= +// +void UpdateFlags() +{ + SETFLAG(debugFlags, DBG_FLAGS_SELGRPONLY, (SendDlgItemMessageA(hDlg, IDC_DBG_GRPO, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_SELMSHONLY, (SendDlgItemMessageA(hDlg, IDC_DBG_MSHO, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_TILEBOXES, (SendDlgItemMessageA(hDlg, IDC_DBG_TILEBB, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_BOXES, (SendDlgItemMessageA(hDlg, IDC_DBG_BOXES, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_SPHERES, (SendDlgItemMessageA(hDlg, IDC_DBG_SPHERES, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_HLMESH, (SendDlgItemMessageA(hDlg, IDC_DBG_HSM, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_HLGROUP, (SendDlgItemMessageA(hDlg, IDC_DBG_HSG, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_SELVISONLY, (SendDlgItemMessageA(hDlg, IDC_DBG_VISO, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_AMBIENT, (SendDlgItemMessageA(hDlg, IDC_DBG_AMBIENT, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_WIREFRAME, (SendDlgItemMessageA(hDlg, IDC_DBG_WIRE, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_DUALSIDED, (SendDlgItemMessageA(hDlg, IDC_DBG_DUAL, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_PICK, (SendDlgItemMessageA(hDlg, IDC_DBG_PICK, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_FPSLIM, (SendDlgItemMessageA(hDlg, IDC_DBG_FPSLIM, BM_GETCHECK, 0, 0)==BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_NEARCLIP, (SendDlgItemMessageA(hDlg, IDC_DBG_CLIPDIST, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_RENDEREXT, (SendDlgItemMessageA(hDlg, IDC_DBG_EXTVC, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_PICKCURRENT, (SendDlgItemMessageA(hDlg, IDC_DBG_PICKCURRENT, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_NOSUNAMB, (SendDlgItemMessageA(hDlg, IDC_DBG_NOSUNAMB, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_NOPLNAMB, (SendDlgItemMessageA(hDlg, IDC_DBG_NOPLNAMB, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_NODYNSUN, (SendDlgItemMessageA(hDlg, IDC_DBG_NODYNSUN, BM_GETCHECK, 0, 0) == BST_CHECKED)); + SETFLAG(debugFlags, DBG_FLAGS_VCZONES, (SendDlgItemMessageA(hDlg, IDC_DBG_VCZONES, BM_GETCHECK, 0, 0) == BST_CHECKED)); + + Config->EnableLimiter = (int)((debugFlags&DBG_FLAGS_FPSLIM)>0); +} + +// ============================================================================================= +// +void SetGroupHighlight(bool bStat) +{ + SETFLAG(debugFlags, DBG_FLAGS_HLGROUP, bStat); +} + +inline _Variable DefVar(float min, float max, float extmax, scale scl, const char *tip, bool bGamma = false) +{ + _Variable var; + var.bUsed = true; + var.Scl = scl; + var.bGamma = bGamma; + var.max = max; + var.extmax = extmax; + var.min = min; + strncpy_s(var.tip, 80, tip, 80); + return var; +} + +inline _Variable DefVar(float min, float max, scale scl, const char *tip, bool bGamma=false) +{ + _Variable var; + var.bUsed = true; + var.Scl = scl; + var.bGamma = bGamma; + var.max = max; + var.extmax = max; + var.min = min; + strncpy_s(var.tip, 80, tip, 80); + return var; +} + +DWORD DropdownList(DWORD x) +{ + if (x >= Dropdown.size()) return 0; + return Dropdown[x].id; +} + +// ============================================================================================= +// +void InitMatList(WORD shader) +{ + LRESULT idx = SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_GETCURSEL, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_RESETCONTENT, 0, 0); + + Dropdown.clear(); + + if (shader == SHADER_NULL) { + std::list list = { 0, 1, 2, 3, 4, 5, 6, 7, 9 }; + for (auto x : list) Dropdown.push_back(PrmList[x]); + for (auto x : Dropdown) SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_ADDSTRING, 0, (LPARAM)x.name.c_str()); + } + + if (shader == SHADER_METALNESS) { + std::list list = { 0, 3, 5, 7, 8, 9 }; + for (auto x : list) Dropdown.push_back(PrmList[x]); + for (auto x : Dropdown) SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_ADDSTRING, 0, (LPARAM)x.name.c_str()); + } + + if (shader == SHADER_BAKED_VC) { + std::list list = { 0, 3, 5, 7, 8, 9 }; + for (auto x : list) Dropdown.push_back(PrmList[x]); + for (auto x : Dropdown) SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_ADDSTRING, 0, (LPARAM)x.name.c_str()); + } + + SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_SETCURSEL, idx, 0); + + switch (shader) { + case SHADER_NULL: + Params[6].var[1] = DefVar(0, 1, LIN, "Maximum intensity"); + Params[6].var[2] = DefVar(10.0f, 4096.0f, SQRT, "Specular lobe size"); + break; + case SHADER_METALNESS: + case SHADER_BAKED_VC: + Params[6].var[1] = DefVar(0, 1, LIN, "Fresnel effect attennuation 1.0 = disabled, 0.0 = max intensity"); + Params[6].var[2].bUsed = false; + break; + } +} + + + +// ============================================================================================= +// +void OpenDlgClbk(void *context) +{ + char buf[64]; + DWORD idx = 0; + HWND l_hDlg = oapiOpenDialog(g_hInst, IDD_vkMESHDEBUG, WndProc); + + if (l_hDlg) hDlg = l_hDlg; // otherwise open already + else return; + + RECT rect; + GetWindowRect(hDlg, &rect); + SetWindowPos(hDlg, NULL, rect.left, rect.top, 298, rect.bottom - rect.top, SWP_SHOWWINDOW); + origwidth = rect.right - rect.left; + + SendDlgItemMessage(hDlg, IDC_DBG_FPSLIM, BM_SETCHECK, Config->EnableLimiter==1, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_DISPLAY, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_DISPLAY, CB_ADDSTRING, 0, (LPARAM)"Everything"); + SendDlgItemMessageA(hDlg, IDC_DBG_DISPLAY, CB_ADDSTRING, 0, (LPARAM)"Selected Visual"); + SendDlgItemMessageA(hDlg, IDC_DBG_DISPLAY, CB_ADDSTRING, 0, (LPARAM)"Selected Mesh"); + SendDlgItemMessageA(hDlg, IDC_DBG_DISPLAY, CB_ADDSTRING, 0, (LPARAM)"Selected Group"); + SendDlgItemMessageA(hDlg, IDC_DBG_DISPLAY, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_CAMERA, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_CAMERA, CB_ADDSTRING, 0, (LPARAM)"Center on visual"); + SendDlgItemMessageA(hDlg, IDC_DBG_CAMERA, CB_ADDSTRING, 0, (LPARAM)"Wheel Fly/Pan Cam"); + SendDlgItemMessageA(hDlg, IDC_DBG_CAMERA, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_ADDSTRING, 0, (LPARAM)"PBR (Old)"); + SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_ADDSTRING, 0, (LPARAM)"Metalness PBR"); + SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_ADDSTRING, 0, (LPARAM)"Baked VC"); + SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_ADDSTRING, 0, (LPARAM)"None"); + SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_ADDSTRING, 0, (LPARAM)"Normals Global"); + SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_ADDSTRING, 0, (LPARAM)"Normals Tangent"); + SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_ADDSTRING, 0, (LPARAM)"Height"); + SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_ADDSTRING, 0, (LPARAM)"Height Mk2"); + SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_ADDSTRING, 0, (LPARAM)"Tile Level"); + SendDlgItemMessageA(hDlg, IDC_DBG_SCENEDBG, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_ACTION, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_ACTION, CB_ADDSTRING, 0, (LPARAM)"Convert to DXT5"); + SendDlgItemMessageA(hDlg, IDC_DBG_ACTION, CB_ADDSTRING, 0, (LPARAM)"Convert to RGB8"); + SendDlgItemMessageA(hDlg, IDC_DBG_ACTION, CB_ADDSTRING, 0, (LPARAM)"Convert to RGB4"); + SendDlgItemMessageA(hDlg, IDC_DBG_ACTION, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_TARGET, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_TARGET, CB_ADDSTRING, 0, (LPARAM)"Save"); + SendDlgItemMessageA(hDlg, IDC_DBG_TARGET, CB_ADDSTRING, 0, (LPARAM)"Assign to slot 0"); + SendDlgItemMessageA(hDlg, IDC_DBG_TARGET, CB_ADDSTRING, 0, (LPARAM)"Assign to slot 1"); + SendDlgItemMessageA(hDlg, IDC_DBG_TARGET, CB_ADDSTRING, 0, (LPARAM)"Assign to slot 2"); + SendDlgItemMessageA(hDlg, IDC_DBG_TARGET, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"None"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Mirror"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Blur 1"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Blur 2"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Blur 3"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Blur 4"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"SS_ShadowMap"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"VC_ShadowMap"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"EX_ShadowMap"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Irradiance"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"GlowMask"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"ScreenDepth"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"Normals"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"LightVisbil."); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"BakedLightMap"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_ADDSTRING, 0, (LPARAM)"EclipseTbl"); + SendDlgItemMessageA(hDlg, IDC_DBG_ENVMAP, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"Omnidir"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Up (+y)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Down (-y)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Left (-x)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Right (+x)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Fwd (+z)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_ADDSTRING, 0, (LPARAM)"From Aft (-z)"); + SendDlgItemMessageA(hDlg, IDC_DBG_AMBDIR, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Exterior"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Interior 0"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Interior 1"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Interior 2"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_ADDSTRING, 0, (LPARAM)"Interior 3"); + SendDlgItemMessageA(hDlg, IDC_DBG_DATASRC, CB_SETCURSEL, 0, 0); + + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_RESETCONTENT, 0, 0); + for (int i = 0; i < 16; i++) { + sprintf_s(buf, 64, "Baked_%d", i); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)buf); + } + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)"Ambient"); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)"DA.Curve"); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)"DA.Bounch"); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_ADDSTRING, 0, (LPARAM)"DA.Force"); + SendDlgItemMessageA(hDlg, IDC_DBG_BKLID, CB_SETCURSEL, 0, 0); + + + SetWindowText(GetDlgItem(hDlg, IDC_DBG_VARA), "1.3"); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_VARB), "0.01"); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_VARC), "0.00"); + + // BakedLights slider + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETPOS, 1, 0); + + // Resolution bias slider + SendDlgItemMessage(hDlg, IDC_DBG_RESBIAS, TBM_SETRANGEMAX, 1, 10); + SendDlgItemMessage(hDlg, IDC_DBG_RESBIAS, TBM_SETRANGEMIN, 1, -10); + SendDlgItemMessage(hDlg, IDC_DBG_RESBIAS, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hDlg, IDC_DBG_RESBIAS, TBM_SETPOS, 1, int((resbias-4.0)*5.0)); + + // Speed slider + SendDlgItemMessage(hDlg, IDC_DBG_SPEED, TBM_SETRANGEMAX, 1, 200); + SendDlgItemMessage(hDlg, IDC_DBG_SPEED, TBM_SETRANGEMIN, 1, 1); + SendDlgItemMessage(hDlg, IDC_DBG_SPEED, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hDlg, IDC_DBG_SPEED, TBM_SETPOS, 1, 75); + SetWindowTextA(GetDlgItem(hDlg, IDC_DBG_SPEEDDSP), "29"); + + // Meterial slider + SendDlgItemMessage(hDlg, IDC_DBG_MATADJ, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hDlg, IDC_DBG_MATADJ, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hDlg, IDC_DBG_MATADJ, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hDlg, IDC_DBG_MATADJ, TBM_SETPOS, 1, 0); + + // Set the "pick" checked + SendDlgItemMessage(hDlg, IDC_DBG_PICK, BM_SETCHECK, 1, 0); + + camMode = 0; + dspMode = 0; + + OBJHANDLE hTgt = oapiCameraTarget(); + + SetVisual(g_client->GetScene()->GetVisObject(hTgt)); // This will call SetupMeshGroups() + + UpdateFlags(); + + CreateToolTip(IDC_DBG_TARGET, hDlg, (char*)"Select a target where the resulting image is assigned"); + CreateToolTip(IDC_DBG_SEAMS, hDlg, (char*)"Enable seams reduction at each mipmap level"); + CreateToolTip(IDC_DBG_FADE, hDlg, (char*)"Enable mipmap post processing. Contrast and detail is reduced from each mipmap to prevent 'stripes' (See:Fa,Fb)"); + CreateToolTip(IDC_DBG_NORM, hDlg, (char*)"Center color channels at 0.5f to prevent lightening/darkening the results"); + CreateToolTip(IDC_DBG_VARA, hDlg, (char*)"Attennuates high contrast components. Leaves low contrast parts unchanged [1.0 to 1.6]"); + CreateToolTip(IDC_DBG_VARB, hDlg, (char*)"Attennuates everything equally. Typical range [0.00 to 0.03]"); + CreateToolTip(IDC_DBG_VARC, hDlg, (char*)"Apply noise to main level and all mipmaps before attennuation (Fa,Fb)"); + CreateToolTip(IDC_DBG_MORE, hDlg, (char*)"Click to show/hide more options"); + CreateToolTip(IDC_DBG_EXTEND, hDlg, (char*)"Extend Diffuse/Roughess material range beyond 1.0f to allow texture fine tuning."); + CreateToolTip(IDC_DBG_LINK, hDlg, (char*)"Adjust all color channels at the same time"); + CreateToolTip(IDC_DBG_DEFINED, hDlg, (char*)"Use the material property for rendering and save it"); + + hTipRed = CreateToolTip(IDC_DBG_RED, hDlg, (char*)"Red"); + hTipGrn = CreateToolTip(IDC_DBG_GREEN, hDlg, (char*)"Green"); + hTipBlu = CreateToolTip(IDC_DBG_BLUE, hDlg, (char*)"Blue"); + hTipAlp = CreateToolTip(IDC_DBG_ALPHA, hDlg, (char*)"Alpha"); + + // Diffuse + Params[0].var[0] = DefVar(0, 1, 2, SQRT, "Red"); + Params[0].var[1] = DefVar(0, 1, 2, SQRT, "Green"); + Params[0].var[2] = DefVar(0, 1, 2, SQRT, "Blue"); + Params[0].var[3] = DefVar(0, 1, 2, LIN, "Alpha"); + + // Ambient + Params[1].var[0] = DefVar(0, 1, LIN, "Red"); + Params[1].var[1] = DefVar(0, 1, LIN, "Green"); + Params[1].var[2] = DefVar(0, 1, LIN, "Blue"); + + // Specular + Params[2].var[0] = DefVar(0, 1, SQRT, "Red"); + Params[2].var[1] = DefVar(0, 1, SQRT, "Green"); + Params[2].var[2] = DefVar(0, 1, SQRT, "Blue"); + Params[2].var[3] = DefVar(1, 4096.0f, SQRT, "Specular power"); + + // Emission + Params[3].var[0] = DefVar(0, 1, LIN, "Red"); + Params[3].var[1] = DefVar(0, 1, LIN, "Green"); + Params[3].var[2] = DefVar(0, 1, LIN, "Blue"); + + // Reflectivity + Params[4].var[0] = DefVar(0, 1, LIN, "Red"); + Params[4].var[1] = DefVar(0, 1, LIN, "Green"); + Params[4].var[2] = DefVar(0, 1, LIN, "Blue"); + + // Smoothness + Params[5].var[0] = DefVar(0, 1, 2, LIN, "Smoothness"); + Params[5].var[1] = DefVar(0, 3, SQRT, "Texture linearity (default 1.0)"); + + // Fresnel + Params[6].var[0] = DefVar(0.5f, 2, LIN, "Angle dependency"); + Params[6].var[1] = DefVar(0, 1, LIN, "Maximum intensity"); + Params[6].var[2] = DefVar(10.0f, 4096.0f, SQRT, "Specular lobe size"); + + // Emission2 + Params[7].var[0] = DefVar(0, 2, LIN, "Red"); + Params[7].var[1] = DefVar(0, 2, LIN, "Green"); + Params[7].var[2] = DefVar(0, 2, LIN, "Blue"); + + // Metalness + Params[8].var[0] = DefVar(0, 1, LIN, "Metalness"); + + // SpecialFX + Params[9].var[0] = DefVar(0, 1, LIN, "Part Temperature"); +} + + +// ============================================================================================= +// +float _Clamp(float value, DWORD p, DWORD v) +{ + bool bExtend = (SendDlgItemMessageA(hDlg, IDC_DBG_EXTEND, BM_GETCHECK, 0, 0) == BST_CHECKED); + return CLAMP(value, Params[p].var[v].min, (bExtend ? Params[p].var[v].extmax : Params[p].var[v].max)); +} + + + + + +// ============================================================================================= +// +void UpdateShader() +{ + OBJHANDLE hObj = vObj->GetObjHandle(); + + if (!oapiIsVessel(hObj)) return; + + if (!hSelMesh) return; + + DWORD Shader = DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_GETCURSEL, 0, 0)); + + vVessel *vVes = (vVessel *)vObj; + MatMgr *pMgr = vVes->GetMaterialManager(); + + switch (Shader) { + case 0: + hSelMesh->SetDefaultShader(SHADER_NULL); + pMgr->RegisterShaderChange(hSelMesh, SHADER_NULL); + break; + case 1: + hSelMesh->SetDefaultShader(SHADER_METALNESS); + pMgr->RegisterShaderChange(hSelMesh, SHADER_METALNESS); + break; + case 2: + hSelMesh->SetDefaultShader(SHADER_BAKED_VC); + pMgr->RegisterShaderChange(hSelMesh, SHADER_BAKED_VC); + break; + } + + InitMatList(hSelMesh->GetDefaultShader()); +} + + +// ============================================================================================= +// +void UpdateGroup(DWORD grp) +{ + if (!hSelMesh) return; + if (grp >= hSelMesh->GetGroupCount()) return; + auto g = hSelMesh->GetGroup(grp); + + if (g) { + SendDlgItemMessage(hDlg, IDC_DBG_NOSHADOW, BM_SETCHECK, (g->UsrFlag & 0x1) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_NORENDER, BM_SETCHECK, (g->UsrFlag & 0x2) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_NOLIGHT, BM_SETCHECK, (g->UsrFlag & 0x4) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_ADDITIVE, BM_SETCHECK, (g->UsrFlag & 0x8) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_NOCOLOR, BM_SETCHECK, (g->UsrFlag & 0x10) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_OIT, BM_SETCHECK, (g->UsrFlag & 0x20) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_DYNAMIC, BM_SETCHECK, 0, 0); + + char buf[64]; + sprintf_s(buf, 64, "%d", g->MtrlIdx); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_MATIDX), buf); + sprintf_s(buf, 64, "%d", g->TexIdx); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_TEXIDX), buf); + string s = hDbgMsh->GetGroupLabel(grp); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_GRPLABEL), s.c_str()); + } +} + + +// ============================================================================================= +// +void ValidateGroup(DWORD grp) +{ + if (!hSelMesh) return; + if (!hDbgMsh) return; + + if (grp >= hSelMesh->GetGroupCount()) return; + auto g = hSelMesh->GetGroup(grp); + DWORD f = 0; + + if (g) + { + if (SendDlgItemMessage(hDlg, IDC_DBG_NOSHADOW, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x1; + if (SendDlgItemMessage(hDlg, IDC_DBG_NORENDER, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x2; + if (SendDlgItemMessage(hDlg, IDC_DBG_NOLIGHT, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x4; + if (SendDlgItemMessage(hDlg, IDC_DBG_ADDITIVE, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x8; + if (SendDlgItemMessage(hDlg, IDC_DBG_NOCOLOR, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x10; + if (SendDlgItemMessage(hDlg, IDC_DBG_OIT, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x20; + if (SendDlgItemMessage(hDlg, IDC_DBG_DYNAMIC, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= 0x0; // TODO: To be implemented + + g->UsrFlag = f; + hDbgMsh->SetGroupFlags(grp, f); + + char buf[64]; + GetWindowText(GetDlgItem(hDlg, IDC_DBG_MATIDX), buf, 64); + g->MtrlIdx = atoi(buf); + if (g->MtrlIdx >= hSelMesh->GetMaterialCount()) g->MtrlIdx = hSelMesh->GetMaterialCount() - 1; + hDbgMsh->SetGroupMaterial(grp, g->MtrlIdx); + + GetWindowText(GetDlgItem(hDlg, IDC_DBG_TEXIDX), buf, 64); + g->TexIdx = atoi(buf); + if (g->TexIdx >= hSelMesh->GetTextureCount()) g->TexIdx = hSelMesh->GetTextureCount() - 1; + hDbgMsh->SetGroupTexture(grp, g->TexIdx); + + GetWindowText(GetDlgItem(hDlg, IDC_DBG_GRPLABEL), buf, 64); + hDbgMsh->SetGroupLabel(grp, string(buf)); + } +} + + +// ============================================================================================= +// +void NextDoNotRender() +{ + if (!hSelMesh) return; + DWORD bak = sGroup; + sGroup++; + for (; sGroup < hSelMesh->GetGroupCount(); sGroup++) { + auto g = hSelMesh->GetGroup(sGroup); + if (g->UsrFlag & 0x2) { + SetupMeshGroups(); + return; + } + } + for (sGroup = 0; sGroup < hSelMesh->GetGroupCount(); sGroup++) { + auto g = hSelMesh->GetGroup(sGroup); + if (g->UsrFlag & 0x2) { + SetupMeshGroups(); + return; + } + } + sGroup = bak; +} + + +// ============================================================================================= +// +void UpdateMeshMaterial(float value, DWORD MatPrp, DWORD clr) +{ + OBJHANDLE hObj = vObj->GetObjHandle(); + if (!oapiIsVessel(hObj)) return; + if (!hSelMesh) return; + + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); + DWORD texidx = hSelMesh->GetMeshGroupTextureIdx(sGroup); + + vkMatExt Mat; + if (!hSelMesh->GetMaterial(&Mat, matidx)) return; + + switch(MatPrp) { + + case 0: // Diffuse + { + Mat.ModFlags |= vkMATEX_DIFFUSE; + Mat.Diffuse[clr] = _Clamp(value, MatPrp, clr); + break; + } + + case 1: // Ambient + { + Mat.ModFlags |= vkMATEX_AMBIENT; + Mat.Ambient[clr] = _Clamp(value, MatPrp, clr); + break; + } + + case 2: // Specular + { + Mat.ModFlags |= vkMATEX_SPECULAR; + Mat.Specular[clr] = _Clamp(value, MatPrp, clr); + break; + } + + case 3: // Emission + { + Mat.ModFlags |= vkMATEX_EMISSIVE; + Mat.Emissive[clr] = _Clamp(value, MatPrp, clr); + break; + } + + case 4: // Reflectivity + { + Mat.ModFlags |= vkMATEX_REFLECT; + Mat.Reflect[clr] = _Clamp(value, MatPrp, clr); + break; + } + + case 5: // Smoothness + { + Mat.ModFlags |= vkMATEX_ROUGHNESS; + Mat.Roughness[clr] = _Clamp(value, MatPrp, clr); + break; + } + + case 6: // Fresnel + { + Mat.ModFlags |= vkMATEX_FRESNEL; + Mat.Fresnel[clr] = _Clamp(value, MatPrp, clr); + break; + } + + case 7: // Emission2 + { + Mat.ModFlags |= vkMATEX_EMISSION2; + Mat.Emission2[clr] = _Clamp(value, MatPrp, clr); + break; + } + + case 8: // Metalness + { + Mat.ModFlags |= vkMATEX_METALNESS; + Mat.Metalness = _Clamp(value, MatPrp, clr); + break; + } + + case 9: // SpecialFX + { + Mat.ModFlags |= vkMATEX_SPECIALFX; + Mat.SpecialFX[clr] = _Clamp(value, MatPrp, clr); + break; + } + } + + hSelMesh->SetMaterial(&Mat, matidx); + vVessel *vVes = (vVessel *)vObj; + vVes->GetMaterialManager()->RegisterMaterialChange(hSelMesh, matidx, &Mat); +} + + +// ============================================================================================= +// +DWORD GetModFlags(DWORD MatPrp) +{ + switch (MatPrp) { + case 0: return vkMATEX_DIFFUSE; + case 1: return vkMATEX_AMBIENT; + case 2: return vkMATEX_SPECULAR; + case 3: return vkMATEX_EMISSIVE; + case 4: return vkMATEX_REFLECT; + case 5: return vkMATEX_ROUGHNESS; + case 6: return vkMATEX_FRESNEL; + case 7: return vkMATEX_EMISSION2; + case 8: return vkMATEX_METALNESS; + case 9: return vkMATEX_SPECIALFX; + } + return 0; +} + + +// ============================================================================================= +// +bool IsMaterialModified(DWORD MatPrp) +{ + OBJHANDLE hObj = vObj->GetObjHandle(); + if (!oapiIsVessel(hObj)) return false; + if (!hSelMesh) return false; + + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); + vkMatExt Mat; + + if (!hSelMesh->GetMaterial(&Mat, matidx)) return false; + + return (Mat.ModFlags & GetModFlags(MatPrp)) != 0; +} + + +// ============================================================================================= +// +void SetMaterialModified(DWORD MatPrp, bool bState) +{ + OBJHANDLE hObj = vObj->GetObjHandle(); + + if (!oapiIsVessel(hObj)) return; + if (!hSelMesh) return; + + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); + vkMatExt Mat; + + if (!hSelMesh->GetMaterial(&Mat, matidx)) return; + + if (bState) Mat.ModFlags |= GetModFlags(MatPrp); + else Mat.ModFlags &= (~GetModFlags(MatPrp)); + + hSelMesh->SetMaterial(&Mat, matidx); +} + + +// ============================================================================================= +// +float GetMaterialValue(DWORD MatPrp, DWORD clr) +{ + OBJHANDLE hObj = vObj->GetObjHandle(); + + if (!oapiIsVessel(hObj)) return 0.0f; + if (!hSelMesh) return 0.0f; + + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); + DWORD texidx = hSelMesh->GetMeshGroupTextureIdx(sGroup); + + const vkMatExt *pMat = hSelMesh->GetMaterial(matidx); + if (!pMat) return 0.0f; + + switch(MatPrp) { + + case 0: // Diffuse + { + switch(clr) { + case 0: return pMat->Diffuse.x; + case 1: return pMat->Diffuse.y; + case 2: return pMat->Diffuse.z; + case 3: return pMat->Diffuse.w; + } + break; + } + + case 1: // Ambient + { + switch(clr) { + case 0: return pMat->Ambient.x; + case 1: return pMat->Ambient.y; + case 2: return pMat->Ambient.z; + } + break; + } + + case 2: // Specular + { + switch(clr) { + case 0: return pMat->Specular.x; + case 1: return pMat->Specular.y; + case 2: return pMat->Specular.z; + case 3: return pMat->Specular.w; + } + break; + } + + case 3: // Emission + { + switch(clr) { + case 0: return pMat->Emissive.x; + case 1: return pMat->Emissive.y; + case 2: return pMat->Emissive.z; + } + break; + } + + case 4: // Reflectivity + { + switch(clr) { + case 0: return pMat->Reflect.x; + case 1: return pMat->Reflect.y; + case 2: return pMat->Reflect.z; + } + break; + } + + case 5: // Roughness + { + switch (clr) { + case 0: return pMat->Roughness.x; + case 1: return pMat->Roughness.y; + } + break; + } + + case 6: // Fresnel + { + switch(clr) { + case 0: return pMat->Fresnel.x; // Angle + case 1: return pMat->Fresnel.y; // Multiplier + case 2: return pMat->Fresnel.z; // SpecPower + } + break; + } + + case 7: // Emission2 + { + switch (clr) { + case 0: return pMat->Emission2.x; + case 1: return pMat->Emission2.y; + case 2: return pMat->Emission2.z; + } + break; + } + + case 8: // Metalness + { + switch (clr) { + case 0: return pMat->Metalness; + } + break; + } + + case 9: // SpecialFX + { + switch (clr) { + case 0: return pMat->SpecialFX.x; + case 1: return pMat->SpecialFX.y; + case 2: return pMat->SpecialFX.z; + case 3: return pMat->SpecialFX.w; + } + break; + } + } + + return 0.0f; +} + +// ============================================================================================= +// +void SetColorSlider() +{ + DWORD MatPrp = DropdownList(DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_GETCURSEL, 0, 0))); + bool bExtend = (SendDlgItemMessageA(hDlg, IDC_DBG_EXTEND, BM_GETCHECK, 0, 0) == BST_CHECKED); + + float mi = Params[MatPrp].var[SelColor].min; + float mx = (bExtend ? Params[MatPrp].var[SelColor].extmax : Params[MatPrp].var[SelColor].max); + + float val = GetMaterialValue(MatPrp, SelColor); + + val -= mi; val /= (mx - mi); + + if (Params[MatPrp].var[SelColor].Scl == scale::SQRT) val = sqrt(val); + if (Params[MatPrp].var[SelColor].Scl == scale::SQR) val = val*val; + + SendDlgItemMessage(hDlg, IDC_DBG_MATADJ, TBM_SETPOS, 1, WORD(val*255.0f)); +} + +// ============================================================================================= +// +void DisplayMat(bool bRed, bool bGreen, bool bBlue, bool bAlpha) +{ + char lbl[32]; + + DWORD MatPrp = DropdownList(DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_GETCURSEL, 0, 0))); + + float r = GetMaterialValue(MatPrp, 0); + float g = GetMaterialValue(MatPrp, 1); + float b = GetMaterialValue(MatPrp, 2); + float a = GetMaterialValue(MatPrp, 3); + + if (bRed) sprintf_s(lbl,32,"%3.3f", r); + else sprintf_s(lbl,32,""); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_RED), lbl); + + if (bGreen) sprintf_s(lbl,32,"%3.3f", g); + else sprintf_s(lbl,32,""); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_GREEN), lbl); + + if (bBlue) sprintf_s(lbl,32,"%3.3f", b); + else sprintf_s(lbl,32,""); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_BLUE), lbl); + + if (bAlpha) sprintf_s(lbl,32,"%3.3f", a); + else sprintf_s(lbl,32,""); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_ALPHA), lbl); + + if (bRed) EnableWindow(GetDlgItem(hDlg, IDC_DBG_RED), true); + else EnableWindow(GetDlgItem(hDlg, IDC_DBG_RED), false); + if (bGreen) EnableWindow(GetDlgItem(hDlg, IDC_DBG_GREEN), true); + else EnableWindow(GetDlgItem(hDlg, IDC_DBG_GREEN), false); + if (bBlue) EnableWindow(GetDlgItem(hDlg, IDC_DBG_BLUE), true); + else EnableWindow(GetDlgItem(hDlg, IDC_DBG_BLUE), false); + if (bAlpha) EnableWindow(GetDlgItem(hDlg, IDC_DBG_ALPHA), true); + else EnableWindow(GetDlgItem(hDlg, IDC_DBG_ALPHA), false); + + bool bModified = IsMaterialModified(MatPrp); + + SendDlgItemMessageA(hDlg, IDC_DBG_DEFINED, BM_SETCHECK, bModified, 0); +} + +// ============================================================================================= +// +void UpdateMaterialDisplay(bool bSetup) +{ + char lbl[256]; + char lbl2[64]; + + OBJHANDLE hObj = vObj->GetObjHandle(); + if (!oapiIsVessel(hObj)) return; + if (!hSelMesh) return; + + WORD Shader = hSelMesh->GetDefaultShader(); + if (Shader == SHADER_NULL) SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_SETCURSEL, 0, 0); + if (Shader == SHADER_METALNESS) SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_SETCURSEL, 1, 0); + if (Shader == SHADER_BAKED_VC) SendDlgItemMessageA(hDlg, IDC_DBG_DEFSHADER, CB_SETCURSEL, 2, 0); + + DWORD matidx = hSelMesh->GetMeshGroupMaterialIdx(sGroup); + + // Set material info + const char *skin = NULL; + if (skin) sprintf_s(lbl, 256, "Material %u: [Skin %s]", matidx, skin); + else sprintf_s(lbl, 256, "Material %u:", matidx); + + GetWindowText(GetDlgItem(hDlg, IDC_DBG_MATGRP), lbl2, 64); + if (strcmp(lbl, lbl2)) SetWindowText(GetDlgItem(hDlg, IDC_DBG_MATGRP), lbl); // Avoid causing flashing + + if (bSetup) SelColor = 0; + + DWORD MatPrp = DropdownList(DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_GETCURSEL, 0, 0))); + + DisplayMat(Params[MatPrp].var[0].bUsed, Params[MatPrp].var[1].bUsed, Params[MatPrp].var[2].bUsed, Params[MatPrp].var[3].bUsed); + + SetToolTip(IDC_DBG_RED, hTipRed, Params[MatPrp].var[0].tip); + SetToolTip(IDC_DBG_GREEN, hTipGrn, Params[MatPrp].var[1].tip); + SetToolTip(IDC_DBG_BLUE, hTipBlu, Params[MatPrp].var[2].tip); + SetToolTip(IDC_DBG_ALPHA, hTipAlp, Params[MatPrp].var[3].tip); + + DWORD texidx = hSelMesh->GetMeshGroupTextureIdx(sGroup); + + if (texidx==0) SetWindowText(GetDlgItem(hDlg, IDC_DBG_TEXTURE), "Texture: None"); + else { + SURFHANDLE hSrf = hSelMesh->GetTexture(texidx); + if (hSrf) { + sprintf_s(lbl, 256, "Texture: %s [%u]", RemovePath(SURFACE(hSrf)->GetName()), texidx); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_TEXTURE), lbl); + } + } + + sprintf_s(lbl, 256, "Mesh: %s", RemovePath(hSelMesh->GetName())); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_MESHNAME), lbl); + + string str = ""; + DWORD vis = vObj->GetMeshVisMode(sMesh); + + if (vis == 0) str = "NEVER"; + if (vis == MESHVIS_ALWAYS) str = "ALWAYS"; + + if (vis & MESHVIS_COCKPIT) str.append("COCKPIT "); + if (vis & MESHVIS_VC) str.append("VC "); + if (vis & MESHVIS_EXTERNAL) str.append("EXTERNAL "); + if (vis & MESHVIS_EXTPASS) str.append("EXTPASS "); + + sprintf_s(lbl, 256, "MeshVisMode: %s", str.c_str()); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_VISMODE), lbl); +} + +// ============================================================================================= +// +bool IsSelectedGroupRendered() +{ + if (!vObj) return false; + if (hSelMesh) return hSelMesh->IsGroupRendered(sGroup); + return false; +} + +// ============================================================================================= +// +void UpdateColorSlider(WORD pos) +{ + float val = float(pos)/255.0f; + + DWORD MatPrp = DropdownList(DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_GETCURSEL, 0, 0))); + + bool bLink = (SendDlgItemMessageA(hDlg, IDC_DBG_LINK, BM_GETCHECK, 0, 0)==BST_CHECKED); + bool bExtend = (SendDlgItemMessageA(hDlg, IDC_DBG_EXTEND, BM_GETCHECK, 0, 0) == BST_CHECKED); + + float mi = Params[MatPrp].var[SelColor].min; + float mx = (bExtend ? Params[MatPrp].var[SelColor].extmax : Params[MatPrp].var[SelColor].max); + + if (MatPrp==5 || MatPrp==6) bLink = false; // Roughness, Fresnel + if (SelColor==3) bLink = false; // Alpha, Specular power + + if (Params[MatPrp].var[SelColor].Scl == scale::SQRT) val = (val*val); + if (Params[MatPrp].var[SelColor].Scl == scale::SQR) val = sqrt(val); + + val *= (mx - mi); val += mi; + + float old = GetMaterialValue(MatPrp, SelColor); + float fct = val/old; + + if (old<1e-4) fct = val; + + if (bLink) { + float r = GetMaterialValue(MatPrp, 0); + float g = GetMaterialValue(MatPrp, 1); + float b = GetMaterialValue(MatPrp, 2); + if (r<1e-4) r=1.0f; + if (g<1e-4) g=1.0f; + if (b<1e-4) b=1.0f; + UpdateMeshMaterial(r*fct, MatPrp, 0); + UpdateMeshMaterial(g*fct, MatPrp, 1); + UpdateMeshMaterial(b*fct, MatPrp, 2); + } + else UpdateMeshMaterial(val, MatPrp, SelColor); +} + +// ============================================================================================= +// +DWORD GetSelectedMesh() +{ + return sMesh; +} + +void SetPickPos(FVECTOR3 pos) +{ + PickLocation = pos; +} + +// ============================================================================================= +// +void SelectGroup(DWORD idx) +{ + if (idxGetName()); + dbgMsh[pMesh] = new DbgMesh(MeshFile); + } + hSelMesh = pMesh; + hDbgMsh = dbgMsh[pMesh]; + } +} + +// ============================================================================================= +// +void SelectMesh(vkMesh *pMesh) +{ + sMesh = 0; + for (DWORD i = 0; i < nMesh; i++) { + if (vObj->GetMesh(i) == pMesh) { + sMesh = i; + ValidateMesh(pMesh); + SetupMeshGroups(); + return; + } + } +} + +// ============================================================================================= +// +void UpdateMeshFlags() +{ + if (!hSelMesh) return; + DWORD f = 0; + + if (SendDlgItemMessage(hDlg, IDC_DBG_ISVCMESH, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= MESHFLAG_VC; + if (SendDlgItemMessage(hDlg, IDC_DBG_VCSHADOW, BM_GETCHECK, 0, 0) == BST_CHECKED) f |= MESHFLAG_SHADOW_VC; + hSelMesh->MeshFlags = f | 0x1; + hDbgMsh->SetMeshFlags(hSelMesh->MeshFlags); +} + +// ============================================================================================= +// +void SetupMeshGroups() +{ + char lbl[256]; + + if (!vObj) return; + + SetWindowText(GetDlgItem(hDlg, IDC_DBG_VISUAL), visual); + + if (nMesh!=0) { + if (sMesh>0xFFFF) sMesh = nMesh-1; + if (sMesh>=nMesh) sMesh = 0; + } + else { + sMesh=0, sGroup=0, nGroup=0; + SetWindowText(GetDlgItem(hDlg, IDC_DBG_MESH), "N/A"); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_GROUP), "N/A"); + return; + } + + sprintf_s(lbl,256,"%u/%u",sMesh,nMesh-1); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_MESH), lbl); + + ValidateMesh(vObj->GetMesh(sMesh)); + + if (hSelMesh) nGroup = hSelMesh->GetGroupCount(); + else nGroup = 0; + + if (nGroup!=0) { + if (sGroup>0xFFFF) sGroup = nGroup-1; + if (sGroup>=nGroup) sGroup = 0; + UpdateGroup(sGroup); + } + else { + sGroup=0; + SetWindowText(GetDlgItem(hDlg, IDC_DBG_GROUP), "N/A"); + return; + } + + sprintf_s(lbl,256,"%u/%u",sGroup,nGroup-1); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_GROUP), lbl); + + if (hSelMesh) { + SendDlgItemMessage(hDlg, IDC_DBG_ISVCMESH, BM_SETCHECK, (hSelMesh->MeshFlags & MESHFLAG_VC) != 0, 0); + SendDlgItemMessage(hDlg, IDC_DBG_VCSHADOW, BM_SETCHECK, (hSelMesh->MeshFlags & MESHFLAG_SHADOW_VC) != 0, 0); + } + + UpdateMaterialDisplay(); + SetColorSlider(); + UpdateLightsSlider(); + InitMatList(hSelMesh->GetDefaultShader()); +} + + +// ============================================================================================= +// +void UpdateBakedLights(float lvl) +{ + vVessel* vV = (vVessel*)vObj; + if (vObj->Type() == OBJTP_VESSEL) + { + if (bkl_id < 16 && bkl_id >= 0) + vV->SetVisualProperty(VisualProp::BAKED_LIGHT, bkl_id, typeid(FVECTOR3), &FVECTOR3(lvl, lvl, lvl)); + if (bkl_id == 16) vV->SetVisualProperty(VisualProp::AMBIENT, 0, typeid(FVECTOR3), &FVECTOR3(lvl, lvl, lvl)); + if (bkl_id == 17) vV->SetVisualProperty(VisualProp::DA_CURVE, 0, typeid(float), &lvl); + if (bkl_id == 18) vV->SetVisualProperty(VisualProp::DA_BOUNCH, 0, typeid(float), &lvl); + if (bkl_id == 19) vV->SetVisualProperty(VisualProp::DA_FORCE, 0, typeid(float), &lvl); + + char lbl[128]; sprintf_s(lbl, 128, "Light Controls (%1.3f)", lvl); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_BKLGROUP), lbl); + } +} + +// ============================================================================================= +// +void UpdateLightsSlider() +{ + FVECTOR3 val = 0.0f; + float fVal = 0.0f; + vVessel* vV = (vVessel*)vObj; + if (vObj->Type() == OBJTP_VESSEL) + { + if (bkl_id < 16 && bkl_id >= 0) vV->GetVisualProperty(VisualProp::BAKED_LIGHT, bkl_id, typeid(val), &val); + if (bkl_id == 16) vV->GetVisualProperty(VisualProp::AMBIENT, 0, typeid(val), &val); + fVal = val.x; + if (bkl_id == 17) vV->GetVisualProperty(VisualProp::DA_CURVE, 0, typeid(float), &fVal); + if (bkl_id == 18) vV->GetVisualProperty(VisualProp::DA_BOUNCH, 0, typeid(float), &fVal); + if (bkl_id == 19) vV->GetVisualProperty(VisualProp::DA_FORCE, 0, typeid(float), &fVal); + SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_SETPOS, 1, WORD(255.0f * fVal)); + char lbl[128]; sprintf_s(lbl, 128, "Light Controls (%1.3f)", fVal); + SetWindowText(GetDlgItem(hDlg, IDC_DBG_BKLGROUP), lbl); + } +} + +// ============================================================================================= +// +LPDIRECT3DTEXTURE9 GetCombinedMap() +{ + if (hSelMesh) { + DWORD texidx = hSelMesh->GetMeshGroupTextureIdx(sGroup); + return hSelMesh->GetCombinedMap(texidx); + } + return NULL; +} + +// ============================================================================================= +// +double GetVisualSize() +{ + if (hDlg && vObj) { + OBJHANDLE hObj = vObj->GetObjHandle(); + if (hObj) return oapiGetSize(hObj); + } + return 1.0; +} + +// ============================================================================================= +// +vObject * GetVisual() +{ + return vObj; +} + +// ============================================================================================= +// +vkMesh* GetMesh() +{ + return hSelMesh; +} + +// ============================================================================================= +// +void SetVisual(vObject *vo) +{ + if (!hDlg) { + vObj = NULL; // Always set the visual to NULL if the dialog isn't open + return; + } + vObj = vo; + UpdateVisual(); +} + +// ============================================================================================= +// +void UpdateVisual() +{ + if (!vObj || !hDlg) return; + nMesh = vObj->GetMeshCount(); + sprintf_s(visual, 64, "Visual: %s", vObj->GetName()); + SetupMeshGroups(); + + SendDlgItemMessageA(hDlg, IDC_DBG_CONES, CB_RESETCONTENT, 0, 0); + Emitters.clear(); + + if (vObj->Type() == OBJTP_VESSEL) { + + SendDlgItemMessageA(hDlg, IDC_DBG_CONES, CB_ADDSTRING, 0, (LPARAM)"NONE"); + Emitters[0] = NULL; + + char line[64]; strcpy(line, ""); + + vVessel *vV = static_cast(vObj); + VESSEL *vessel = vV->GetInterface(); + DWORD nemitter = vessel->LightEmitterCount(); + + for (DWORD j = 0; j < nemitter; j++) { + + const LightEmitter *em = vessel->GetLightEmitter(j); + + if (em->GetType() == LightEmitter::LT_SPOT) { + const SpotLight *sl = static_cast(em); + double P = sl->GetPenumbra()*DEG; + double U = sl->GetUmbra()*DEG; + double R = sl->GetRange(); + sprintf_s(line, 64, "%s P%1.0f U%1.0f R%1.0f", _PTR(em), P, U, R); + } + + if (em->GetType() == LightEmitter::LT_POINT) { + const PointLight *pl = static_cast(em); + double R = pl->GetRange(); + sprintf_s(line, 64, "%s R%1.0f", _PTR(em), R); + } + + switch (em->GetVisibility()) + { + case LightEmitter::VIS_EXTERNAL: strcat_s(line, 64, " EXT"); break; + case LightEmitter::VIS_COCKPIT: strcat_s(line, 64, " VC"); break; + case LightEmitter::VIS_ALWAYS: strcat_s(line, 64, " ALW"); break; + } + + if ((em->GetType() == LightEmitter::LT_SPOT) || (em->GetType() == LightEmitter::LT_POINT)) { + SendDlgItemMessageA(hDlg, IDC_DBG_CONES, CB_ADDSTRING, 0, (LPARAM)line); + Emitters[j + 1] = em; + } + } + } + + SendDlgItemMessageA(hDlg, IDC_DBG_CONES, CB_SETCURSEL, 0, 0); +} + +// ============================================================================================= +// +void RemoveVisual(vObject *vo) +{ + if (vObj==vo) vObj=NULL; +} + +// ============================================================================================= +// +void SetColorValue(const char *lbl) +{ + DWORD MatPrp = DropdownList(DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_GETCURSEL, 0, 0))); + UpdateMeshMaterial(float(atof(lbl)), MatPrp, SelColor); + SetColorSlider(); +} + +// ============================================================================================= +// +float reduce(float v, float q) +{ + if (v>0) return max(0.0f, v-q); + else return min(0.0f, v+q); +} + +struct PCParam { + DWORD Action; + DWORD Func; + DWORD Mip; + float a,b,c; +}; + +// ============================================================================================= +// +FVECTOR4 ProcessColor(FVECTOR4 C, PCParam *prm, int x, int y) +{ + float fMip = float(prm->Mip); + float a = prm->a; + float b = prm->b; + float c = prm->c; + + // Swap color channels + C = FVECTOR4(C.z, C.y, C.z, C.x); + + if (c>0.001) { + FVECTOR4 rnd((float)oapiRand(),(float)oapiRand(), (float)oapiRand(), (float)oapiRand()); + C += (rnd*2.0f-1.0f) * (c/(2.0f+fMip)); + } + + // Reduce contrast + if (prm->Func & 0x2) { + + if (prm->Mip==0) return FVECTOR4(C.x, C.y, C.z, C.w); // Do nothing for the main level + + C = C*2.0f - 1.0f; // Expand to [-1, 1] + C *= pow(a, -abs(C)*fMip); + + float k = b * fMip; + + C = FVECTOR4(reduce(C.x, k), reduce(C.y, k), reduce(C.z, k), reduce(C.w, k)); + C = C*0.5f + 0.5f; // Back to [0, 1] + } + + return FVECTOR4(C.x, C.y, C.z, C.w); +} + +// ============================================================================================= +// +bool Execute(HWND hWnd, LPOPENFILENAME pOF) +{ + LPDIRECT3DDEVICE9 pDevice = g_client->GetDevice(); + + SaveTex.hwndOwner = hWnd; + + D3DLOCKED_RECT in, out; + PCParam prm; + + DWORD Func = 0; + DWORD Action = DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_ACTION, CB_GETCURSEL, 0, 0)); + DWORD Target = DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_TARGET, CB_GETCURSEL, 0, 0)); + + if (SendDlgItemMessageA(hDlg, IDC_DBG_NORM, BM_GETCHECK, 0, 0)==BST_CHECKED) Func |= 0x1; + if (SendDlgItemMessageA(hDlg, IDC_DBG_FADE, BM_GETCHECK, 0, 0)==BST_CHECKED) Func |= 0x2; + if (SendDlgItemMessageA(hDlg, IDC_DBG_SEAMS, BM_GETCHECK, 0, 0)==BST_CHECKED) Func |= 0x4; + + if (Action>=0 && Action<=2) { + + LPDIRECT3DTEXTURE9 pTex = NULL; + LPDIRECT3DTEXTURE9 pWork = NULL; + LPDIRECT3DTEXTURE9 pSave = NULL; + D3DXIMAGE_INFO info; + + HR(D3DXCreateTextureFromFileExA(pDevice, pOF->lpstrFile, D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, D3DX_DEFAULT, D3DX_DEFAULT, 0, &info, NULL, &pTex)); + + if (!pTex) { + LogErr("Failed to open a file [%s]", pOF->lpstrFile); + return false; + } + + DWORD mips = pTex->GetLevelCount(); + + if (true) { + + HR(D3DXCreateTexture(pDevice, info.Width, info.Height, mips, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pWork)); + if (!pWork) return false; + + if (Action==0) { HR(D3DXCreateTexture(pDevice, info.Width, info.Height, mips, 0, D3DFMT_DXT5, D3DPOOL_SYSTEMMEM, &pSave)); } + if (Action==1) { HR(D3DXCreateTexture(pDevice, info.Width, info.Height, mips, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pSave)); } + if (Action==2) { HR(D3DXCreateTexture(pDevice, info.Width, info.Height, mips, 0, D3DFMT_A4R4G4B4, D3DPOOL_SYSTEMMEM, &pSave)); } + + if (!pSave) return false; + + + // Process texture ---------------------------------------------- + // + for (DWORD n=0;n>n; + DWORD h = info.Height>>n; + HR(pTex->LockRect(n, &in, NULL, 0)); + HR(pWork->LockRect(n, &out, NULL, 0)); + DWORD *pIn = (DWORD *)in.pBits; + DWORD *pOut = (DWORD *)out.pBits; + + prm.Mip = n; + prm.Action = Action; + prm.Func = Func; + prm.a = GetFloatFromBox(hDlg, IDC_DBG_VARA); + prm.b = GetFloatFromBox(hDlg, IDC_DBG_VARB); + prm.c = GetFloatFromBox(hDlg, IDC_DBG_VARC); + + for (DWORD y=0;yUnlockRect(n)); + HR(pWork->UnlockRect(n)); + } + + SAFE_RELEASE(pTex); + + // Convert texture format -------------------------------------- + // + for (DWORD n=0;nGetSurfaceLevel(n, &pIn); + pSave->GetSurfaceLevel(n, &pOut); + if (D3DXLoadSurfaceFromSurface(pOut, NULL, NULL, pIn, NULL, NULL, D3DX_FILTER_LINEAR, 0)!=S_OK) { + LogErr("D3DXLoadSurfaceFromSurface Failed"); + return false; + } + pIn->Release(); + pOut->Release(); + } + SAFE_RELEASE(pWork); + } + else { + // Copy input to output + pSave = pTex; + } + + // Save the texture into a file ------------------------------- + // + if (Target==0) { + strcpy_s(SaveTex.lpstrFile, 255, OpenTex.lpstrFile); + if (GetSaveFileName(&SaveTex)) { + if (D3DXSaveTextureToFileA(SaveTex.lpstrFile, D3DXIFF_DDS, pSave, NULL)!=S_OK) { + LogErr("Failed to create a file [%s]",SaveTex.lpstrFile); + return false; + } + SAFE_RELEASE(pSave); + return true; + } + } + + // Assign the texture for rendering ----------------------------- + // + if (Target==1 || Target==2 || Target==3) { + vPlanet *vP = g_client->GetScene()->GetCameraProxyVisual(); + if (vP) vP->SetMicroTexture(pSave, Target-1); + SAFE_RELEASE(pSave); + return true; + } + } + + return false; +} + + +// ============================================================================================= +// +void SaveEnvMap() +{ + LPDIRECT3DDEVICE9 pDevice = g_client->GetDevice(); + + OBJHANDLE hObj = vObj->Object(); + + if (oapiIsVessel(hObj)) { + vVessel *vVes = (vVessel *)vObj; + auto pTex = vVes->GetExteriorEnvMap() ? vVes->GetExteriorEnvMap()->pCube : nullptr; + if (pTex->GetType() == D3DRTYPE_CUBETEXTURE) { + if (D3DXSaveTextureToFileA("EnvMap.dds", D3DXIFF_DDS, (LPDIRECT3DCUBETEXTURE9)pTex, NULL) != S_OK) { + LogErr("Failed to save envmap"); + } + } + } +} + +//------------------------------------------------------------------------------------------- +// +void Append(const char *format, ...) +{ + if (hDataWnd == NULL) return; + char buf[256]; + va_list args; + va_start(args, format); + _vsnprintf_s(buf, 256, 256, format, args); + va_end(args); + buffer += buf; +} + +//------------------------------------------------------------------------------------------- +// +void Append2(const char *format, ...) +{ + if (hDataWnd == NULL) return; + char buf[256]; + va_list args; + va_start(args, format); + _vsnprintf_s(buf, 256, 256, format, args); + va_end(args); + buffer2 += buf; +} + +//------------------------------------------------------------------------------------------- +// +void Refresh2() +{ + if (hDataWnd == NULL) return; + + Append2("LocalPos = [%f, %f, %f]", PickLocation.x, PickLocation.y, PickLocation.z); + + SetWindowTextA(GetDlgItem(hDataWnd, IDC_DBG_DATAVIEW2), buffer2.c_str()); + buffer2.clear(); +} + +//------------------------------------------------------------------------------------------- +// +void Refresh() +{ + if (hDataWnd == NULL) return; + SetWindowTextA(GetDlgItem(hDataWnd, IDC_DBG_DATAVIEW), buffer.c_str()); + buffer.clear(); + Refresh2(); +} + +//------------------------------------------------------------------------------------------- +// +void CreateSamplingKernel() +{ + int s = 27; + int k = 0; + + VECTOR3 *data = new VECTOR3[s]; + + double d = 0.0; + double a = 0.0; + + for (int i = 0; i < s; i++) { + double z = (oapiRand()*2.0 - 1.0) * (PI2 / 8); + data[i].x = sqrt(d) * sin(a+z); + data[i].y = sqrt(d) * cos(a+z); + //data[i].x = d * sin(a + z); + //data[i].y = d * cos(a + z); + data[i].z = sqrt(data[i].x*data[i].x + data[i].y*data[i].y); + data[i].z = sqrt(data[i].z); + data[i].z = 1.0; + a += PI2 / 4; + d += 1.0 / double(s); + } + + for (int i = 0; i < s; i++) { + oapiWriteLogV("{%4.4ff, %4.4ff, %4.4ff},", data[i].x, data[i].y, data[i].z); + } + + delete[]data; +} + +/* +//------------------------------------------------------------------------------------------- +// +void CreateSamplingKernel() +{ +VECTOR3 *data = new VECTOR3[64]; + +while (true) { + +double ava = 0, avb = 0; + +for (int i = 0; i < 64; i++) { + +double a = oapiRand(); +double b = oapiRand() * PI2; +double c = cos(a*PI05); + +data[i] = _V(sin(b)*a, cos(b)*a, c); + +ava += data[i].x; +avb += data[i].y; +} + +// Ensure proper balance +if (ava < 0.1 && avb < 0.1) break; +} + +double w = 0.0f; + +for (int i = 0; i < 64; i++) { +oapiWriteLogV("{%4.4ff, %4.4ff, %4.4ff},", data[i].x, data[i].y, data[i].z); +w += data[i].z; +} + +oapiWriteLogV("TotalWeight = %f", w); +} +*/ + + +// ============================================================== +// Dialog message handler + +INT_PTR CALLBACK ViewProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static bool isOpen = false; // IDC_DBG_MORE (full or reduced width) + + DWORD Prp = DropdownList(DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_GETCURSEL, 0, 0))); + + switch (uMsg) { + + case WM_INITDIALOG: + { + SetWindowTextA(GetDlgItem(hWnd, IDC_DBG_DATAVIEW), "-- Select a mesh group --"); + buffer.clear(); + return TRUE; // All Init actions are done in OpenDlgClbk(); + } + + case WM_COMMAND: + + switch (LOWORD(wParam)) { + + case IDCANCEL: + oapiCloseDialog(hWnd); + if (!buffer.empty()) buffer.clear(); + hDataWnd = NULL; + return TRUE; + } + break; + } + + return oapiDefDialogProc(hWnd, uMsg, wParam, lParam); +} + + +// ============================================================== +// Dialog message handler + +INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + char lbl[32]; + RECT rect; + bool bPaused; + static bool isOpen = false; // IDC_DBG_MORE (full or reduced width) + + OpenTex.hwndOwner = hWnd; + + DWORD Prp = DropdownList(DWORD(SendDlgItemMessageA(hDlg, IDC_DBG_MATPRP, CB_GETCURSEL, 0, 0))); + + switch (uMsg) { + + case WM_INITDIALOG: + { + isOpen = false; // We always start with reduced width + return TRUE; // All Init actions are done in OpenDlgClbk(); + } + + case WM_HSCROLL: + { + if (LOWORD(wParam)==TB_THUMBTRACK || LOWORD(wParam)==TB_ENDTRACK) { + WORD pos = HIWORD(wParam); + + if (HWND(lParam)==GetDlgItem(hWnd, IDC_DBG_SPEED)) { + if (pos==0) pos = WORD(SendDlgItemMessage(hDlg, IDC_DBG_SPEED, TBM_GETPOS, 0, 0)); + double fpos = pow(2.0,double(pos)*13.0/200.0); + sprintf_s(lbl,32,"%1.0f",fpos); + SetWindowTextA(GetDlgItem(hWnd, IDC_DBG_SPEEDDSP), lbl); + camSpeed = fpos/50.0; + } + + if (HWND(lParam) == GetDlgItem(hWnd, IDC_DBG_RESBIAS)) { + resbias = 4.0 + 0.2 * double(SendDlgItemMessage(hDlg, IDC_DBG_RESBIAS, TBM_GETPOS, 0, 0)); + } + + if (HWND(lParam)==GetDlgItem(hWnd, IDC_DBG_MATADJ)) { + if (pos==0) pos = WORD(SendDlgItemMessage(hDlg, IDC_DBG_MATADJ, TBM_GETPOS, 0, 0)); + UpdateColorSlider(pos); + UpdateMaterialDisplay(); + } + + if (HWND(lParam) == GetDlgItem(hWnd, IDC_DBG_BKLADJ)) { + if (pos == 0) pos = WORD(SendDlgItemMessage(hDlg, IDC_DBG_BKLADJ, TBM_GETPOS, 0, 0)); + float val = float(pos) / 255.0f; + UpdateBakedLights(val); + } + } + return false; + } + + case WM_COMMAND: + + switch (LOWORD(wParam)) { + + case IDCANCEL: + Close(); + break; + + case IDC_DBG_KERNEL: + { + CreateSamplingKernel(); + break; + } + + case IDC_DBG_NEXT: + { + NextDoNotRender(); + break; + } + + case IDC_DBG_MATSAVE: + { + OBJHANDLE hObj = vObj->GetObjHandle(); + if (oapiIsVessel(hObj)) { + vVessel *vVes = (vVessel *)vObj; + vVes->GetMaterialManager()->SaveConfiguration(); + } + break; + } + + case IDC_DBG_DATAWND: + { + HWND hW = oapiOpenDialog(g_hInst, IDD_DEBUGVIEW, ViewProc); + if (hW) hDataWnd = hW; + } + break; + + case IDC_DBG_LINK: + break; + + case IDC_DBG_DEFINED: + SetMaterialModified(Prp, (SendDlgItemMessageA(hDlg, IDC_DBG_DEFINED, BM_GETCHECK, 0, 0) == BST_CHECKED)); + break; + + case IDC_DBG_COPY: + { + cpr = GetMaterialValue(Prp, 0); + cpg = GetMaterialValue(Prp, 1); + cpb = GetMaterialValue(Prp, 2); + break; + } + + case IDC_DBG_PASTE: + { + UpdateMeshMaterial(cpr, Prp, 0); + UpdateMeshMaterial(cpg, Prp, 1); + UpdateMeshMaterial(cpb, Prp, 2); + UpdateMaterialDisplay(); + SetColorSlider(); + break; + } + + case IDC_DBG_RED: + if (HIWORD(wParam)==EN_SETFOCUS) { + SelColor = 0; + UpdateMaterialDisplay(); + SetColorSlider(); + } + if (HIWORD(wParam)==EN_KILLFOCUS) { + GetWindowTextA(HWND(lParam), lbl, 32); + SetColorValue(lbl); + } + break; + + case IDC_DBG_GREEN: + if (HIWORD(wParam)==EN_SETFOCUS) { + SelColor = 1; + UpdateMaterialDisplay(); + SetColorSlider(); + } + if (HIWORD(wParam)==EN_KILLFOCUS) { + GetWindowTextA(HWND(lParam), lbl, 32); + SetColorValue(lbl); + } + break; + + case IDC_DBG_BLUE: + if (HIWORD(wParam)==EN_SETFOCUS) { + SelColor = 2; + UpdateMaterialDisplay(); + SetColorSlider(); + } + if (HIWORD(wParam)==EN_KILLFOCUS) { + GetWindowTextA(HWND(lParam), lbl, 32); + SetColorValue(lbl); + } + break; + + case IDC_DBG_ALPHA: + if (HIWORD(wParam)==EN_SETFOCUS) { + SelColor = 3; + UpdateMaterialDisplay(); + SetColorSlider(); + } + if (HIWORD(wParam)==EN_KILLFOCUS) { + GetWindowTextA(HWND(lParam), lbl, 32); + SetColorValue(lbl); + } + break; + + case IDC_DBG_MATPRP: + if (HIWORD(wParam)==CBN_SELCHANGE) { + UpdateMaterialDisplay(true); + SetColorSlider(); + } + break; + + case IDC_DBG_CONES: + if (HIWORD(wParam) == CBN_SELCHANGE) { + sEmitter = DWORD(SendDlgItemMessage(hDlg, IDC_DBG_CONES, CB_GETCURSEL, 0, 0)); + } + break; + + case IDC_DBG_DEFSHADER: + if (HIWORD(wParam) == CBN_SELCHANGE) { + UpdateShader(); + } + break; + + case IDC_DBG_BKLID: + if (HIWORD(wParam) == CBN_SELCHANGE) { + bkl_id = int(SendDlgItemMessage(hDlg, IDC_DBG_BKLID, CB_GETCURSEL, 0, 0)); + UpdateLightsSlider(); + } + break; + + case IDC_DBG_DATASRC: + if (HIWORD(wParam) == CBN_SELCHANGE) { + probe_id = int(SendDlgItemMessage(hDlg, IDC_DBG_DATASRC, CB_GETCURSEL, 0, 0)) - 1; + } + break; + + case IDC_DBG_AMBDIR: + if (HIWORD(wParam) == CBN_SELCHANGE) { + ambdir = int(SendDlgItemMessage(hDlg, IDC_DBG_AMBDIR, CB_GETCURSEL, 0, 0)) - 1; + } + break; + + case IDC_DBG_ENVMAP: + if (HIWORD(wParam) == CBN_SELCHANGE) { + dbgdsp = DbgDisplay(SendDlgItemMessage(hDlg, IDC_DBG_ENVMAP, CB_GETCURSEL, 0, 0)); + } + break; + + case IDC_DBG_DISPLAY: + if (HIWORD(wParam)==CBN_SELCHANGE) dspMode = DWORD(SendDlgItemMessage(hWnd, IDC_DBG_DISPLAY, CB_GETCURSEL, 0, 0)); + break; + + case IDC_DBG_CAMERA: + if (HIWORD(wParam)==CBN_SELCHANGE) camMode = DWORD(SendDlgItemMessage(hWnd, IDC_DBG_CAMERA, CB_GETCURSEL, 0, 0)); + break; + + case IDC_DBG_MSHUP: + if (HIWORD(wParam)==BN_CLICKED) sMesh--; + SetupMeshGroups(); + break; + + case IDC_DBG_MSHDN: + if (HIWORD(wParam)==BN_CLICKED) sMesh++; + SetupMeshGroups(); + break; + + case IDC_DBG_GRPUP: + if (HIWORD(wParam)==BN_CLICKED) sGroup--; + SetupMeshGroups(); + break; + + case IDC_DBG_GRPDN: + if (HIWORD(wParam)==BN_CLICKED) sGroup++; + SetupMeshGroups(); + break; + + + case IDC_DBG_MESH: + if (HIWORD(wParam)==EN_KILLFOCUS) { + char cbuf[32]; + GetWindowText(GetDlgItem(hWnd, IDC_DBG_MESH), cbuf, 32); + for (int i=0; i<32;i++) if (cbuf[i]==0 || cbuf[i]=='/') { cbuf[i]=0; break; } + sMesh = atoi(cbuf); + SetupMeshGroups(); + } + break; + + case IDC_DBG_GROUP: + if (HIWORD(wParam)==EN_KILLFOCUS) { + char cbuf[32]; + GetWindowText(GetDlgItem(hWnd, IDC_DBG_GROUP), cbuf, 32); + for (int i=0; i<32;i++) if (cbuf[i]==0 || cbuf[i]=='/') { cbuf[i]=0; break; } + sGroup = atoi(cbuf); + SetupMeshGroups(); + } + break; + + case IDC_DBG_OPEN: + bPaused = oapiGetPause(); + oapiSetPause(true); + if (GetOpenFileNameA(&OpenTex)) { + SetWindowText(GetDlgItem(hWnd, IDC_DBG_FILE), OpenTex.lpstrFile); + } + oapiSetPause(bPaused); + break; + + case IDC_DBG_EXECUTE: + bPaused = oapiGetPause(); + oapiSetPause(true); + if (Execute(hWnd, &OpenTex)==false) MessageBox(hWnd,"Failed :(","vk Controls", MB_OK); + oapiSetPause(bPaused); + break; + + case IDC_DBG_MESHSAVE: + if (dbgMsh[hSelMesh]) { + bPaused = oapiGetPause(); + oapiSetPause(true); + strcpy(SaveMesh.lpstrFileTitle, hSelMesh->GetName()); + SaveMesh.hwndOwner = hWnd; + if (GetSaveFileName(&SaveMesh)) { + dbgMsh[hSelMesh]->Save(SaveMesh.lpstrFile); + return true; + } + oapiSetPause(bPaused); + } + break; + + case IDC_DBG_ACTION: + break; + + case IDC_DBG_MORE: + GetWindowRect(hDlg, &rect); + SetWindowPos(hDlg, NULL, rect.left, rect.top, isOpen ? 298 : origwidth, rect.bottom - rect.top, SWP_SHOWWINDOW); + SetWindowText(GetDlgItem(hWnd, IDC_DBG_MORE), isOpen ? ">>>" : "<<<"); + isOpen = !isOpen; + break; + + case IDC_DBG_RELOADSHD: + vkEffect::vkTechInit(g_client, g_client->GetDevice(), "vkClient"); + break; + + case IDC_DBG_RELOADTEX: + if (vObj) { + if (vObj->Type() == OBJTP_VESSEL) { + ((vVessel *)vObj)->ReloadTextures(); + } + } + break; + + case IDC_DBG_ENVSAVE: + SaveEnvMap(); + break; + + case IDC_DBG_EXTEND: + SetColorSlider(); + break; + + case IDC_DBG_ISVCMESH: + case IDC_DBG_VCSHADOW: + UpdateMeshFlags(); + break; + + case IDC_DBG_GRPO: + case IDC_DBG_VISO: + case IDC_DBG_MSHO: + case IDC_DBG_BOXES: + case IDC_DBG_SPHERES: + case IDC_DBG_HSM: + case IDC_DBG_HSG: + case IDC_DBG_AMBIENT: + case IDC_DBG_WIRE: + case IDC_DBG_DUAL: + case IDC_DBG_PICK: + case IDC_DBG_FPSLIM: + case IDC_DBG_TILEBB: + case IDC_DBG_CLIPDIST: + case IDC_DBG_EXTVC: + case IDC_DBG_PICKCURRENT: + case IDC_DBG_NOSUNAMB: + case IDC_DBG_NOPLNAMB: + case IDC_DBG_NODYNSUN: + case IDC_DBG_VCZONES: + UpdateFlags(); + break; + + case IDC_DBG_NOSHADOW: + case IDC_DBG_NORENDER: + case IDC_DBG_NOLIGHT: + case IDC_DBG_ADDITIVE: + case IDC_DBG_NOCOLOR: + case IDC_DBG_OIT: + case IDC_DBG_DYNAMIC: + if (HIWORD(wParam) == BN_CLICKED) { + ValidateGroup(sGroup); + } + break; + + case IDC_DBG_MATIDX: + case IDC_DBG_TEXIDX: + case IDC_DBG_GRPLABEL: + if (HIWORD(wParam) == EN_KILLFOCUS) { + ValidateGroup(sGroup); + } + break; + + case IDC_DBG_VARA: + case IDC_DBG_VARB: + case IDC_DBG_VARC: + case IDC_DBG_FILE: + break; + + default: + //LogErr("LOWORD(%hu), HIWORD(0x%hX)",LOWORD(wParam),HIWORD(wParam)); + break; + } + break; + } + + return oapiDefDialogProc(hWnd, uMsg, wParam, lParam); +} + + + + + + +// ============================================================================================= +// +void CloseGFX() +{ + if (hGfxDlg != NULL) { + oapiCloseDialog(hGfxDlg); + hGfxDlg = NULL; + } +} + +// ============================================================================================= +// +void OpenGFXDlgClbk(void *context) +{ + HWND l_hDlg = oapiOpenDialog(g_hInst, IDD_GRAPHICS, WndProcGFX); + if (l_hDlg) hGfxDlg = l_hDlg; // otherwise open already + else return; + + // slider + SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETPOS, 1, 0); + + // slider + SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETPOS, 1, 0); + + // slider + SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETPOS, 1, 0); + + // slider + SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETPOS, 1, 0); + + // slider + SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETPOS, 1, 0); + + // slider + SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETPOS, 1, 0); + + // slider + SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETPOS, 1, 0); + + // slider + SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETRANGEMAX, 1, 255); + SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETPOS, 1, 0); + + + + // reset-button(s) + HANDLE hImg = LoadImage(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_LOADTRANSPARENT | LR_DEFAULTSIZE); + SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_INTENSITY_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); + SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_DISTANCE_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); + SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_THRESHOLD_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); + SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_GAMMA_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); + + SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_SUNLIGHT_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); + SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_IRRADIANCE_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); + SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_LOCALMAX_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); + SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_GLARE_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); + + CreateToolTip(IDC_GFX_INTENSITY_RESET, hGfxDlg, (char*)"Reset to default"); + CreateToolTip(IDC_GFX_DISTANCE_RESET, hGfxDlg, (char*)"Reset to default"); + CreateToolTip(IDC_GFX_THRESHOLD_RESET, hGfxDlg, (char*)"Reset to default"); + CreateToolTip(IDC_GFX_GAMMA_RESET, hGfxDlg, (char*)"Reset to default"); + CreateToolTip(IDC_GFX_SUNLIGHT_RESET, hGfxDlg, (char*)"Reset to default"); + CreateToolTip(IDC_GFX_IRRADIANCE_RESET, hGfxDlg, (char*)"Reset to default"); + CreateToolTip(IDC_GFX_LOCALMAX_RESET, hGfxDlg, (char*)"Reset to default"); + CreateToolTip(IDC_GFX_GLARE_RESET, hGfxDlg, (char*)"Reset to default"); + + SetGFXSliders(); +} + +void SetGFXSliders() +{ + char lbl[32]; + double fpos; + + fpos = Config->GFXIntensity; + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL1), lbl); + SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETPOS, 1, WORD(fpos*255.0)); + + fpos = Config->GFXDistance; + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL2), lbl); + SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETPOS, 1, WORD(fpos*255.0)); + + fpos = Config->GFXThreshold; + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL3), lbl); + SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETPOS, 1, WORD(fpos*255.0 / 2.0)); + + fpos = Config->GFXGamma; + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL4), lbl); + SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETPOS, 1, WORD(fpos*255.0 / 2.5)); + + fpos = Config->GFXSunIntensity; + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL5), lbl); + SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETPOS, 1, WORD(fpos*255.0 / 2.0)); + + fpos = Config->PlanetGlow; + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL6), lbl); + SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETPOS, 1, WORD(fpos*255.0 / 2.0)); + + fpos = Config->GFXLocalMax; + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL7), lbl); + SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETPOS, 1, WORD(fpos*255.0)); + + fpos = Config->GFXGlare; + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL8), lbl); + SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETPOS, 1, WORD(fpos * 255.0)); +} + +void ReadGFXSliders() +{ + char lbl[32]; + double fpos; + + fpos = (1.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_GETPOS, 0, 0)); + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL1), lbl); + Config->GFXIntensity = fpos; + + fpos = (1.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_GETPOS, 0, 0)); + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL2), lbl); + Config->GFXDistance = fpos; + + fpos = (2.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_GETPOS, 0, 0)); + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL3), lbl); + Config->GFXThreshold = fpos; + + fpos = (2.5 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_GETPOS, 0, 0)); + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL4), lbl); + Config->GFXGamma = fpos; + + fpos = (2.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_GETPOS, 0, 0)); + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL5), lbl); + Config->GFXSunIntensity = fpos; + + fpos = (2.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_GETPOS, 0, 0)); + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL6), lbl); + Config->PlanetGlow = fpos; + + fpos = (1.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_GETPOS, 0, 0)); + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL7), lbl); + Config->GFXLocalMax = fpos; + + fpos = (1.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_GETPOS, 0, 0)); + sprintf_s(lbl, 32, "%1.2f", fpos); + SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL8), lbl); + Config->GFXGlare = fpos; +} + +INT_PTR CALLBACK WndProcGFX(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + + case WM_INITDIALOG: + { + return TRUE; // All Init actions are done in OpenDlgClbk(); + } + + case WM_HSCROLL: + { + if (LOWORD(wParam) == TB_THUMBTRACK || LOWORD(wParam) == TB_ENDTRACK) { + if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_INTENSITY)) ReadGFXSliders(); + if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_DISTANCE)) ReadGFXSliders(); + if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_THRESHOLD)) ReadGFXSliders(); + if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_GAMMA)) ReadGFXSliders(); + if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_SUNLIGHT)) ReadGFXSliders(); + if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_IRRADIANCE)) ReadGFXSliders(); + if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_LOCALMAX)) ReadGFXSliders(); + if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_GLARE)) ReadGFXSliders(); + } + return false; + } + + case WM_COMMAND: + + switch (LOWORD(wParam)) { + + case IDCANCEL: + CloseGFX(); + break; + + case IDC_GFX_RECOMPILE: + g_client->GetScene()->CreateSunGlare(); + break; + + + case IDC_GFX_INTENSITY_RESET: + Config->GFXIntensity = 0.5; + SetGFXSliders(); + break; + + case IDC_GFX_DISTANCE_RESET: + Config->GFXDistance = 0.8; + SetGFXSliders(); + break; + + case IDC_GFX_THRESHOLD_RESET: + Config->GFXThreshold = 1.1; + SetGFXSliders(); + break; + + case IDC_GFX_GAMMA_RESET: + Config->GFXGamma = 1.0; + SetGFXSliders(); + break; + + case IDC_GFX_SUNLIGHT_RESET: + Config->GFXSunIntensity = 1.2; + SetGFXSliders(); + break; + + case IDC_GFX_IRRADIANCE_RESET: + Config->PlanetGlow = 1.0; + SetGFXSliders(); + break; + + case IDC_GFX_LOCALMAX_RESET: + Config->GFXLocalMax = 0.5; + SetGFXSliders(); + break; + + case IDC_GFX_GLARE_RESET: + Config->GFXGlare = 0.3; + SetGFXSliders(); + break; + + default: + //LogErr("WndProcGFX() LOWORD(%hu), HIWORD(0x%hX)", LOWORD(wParam), HIWORD(wParam)); + break; + } + break; + } + + return oapiDefDialogProc(hWnd, uMsg, wParam, lParam); +} + + + + +} //namespace + + diff --git a/OVP/VulkanClient/DebugControls.h b/OVP/VulkanClient/DebugControls.h new file mode 100644 index 000000000..e2520bd90 --- /dev/null +++ b/OVP/VulkanClient/DebugControls.h @@ -0,0 +1,153 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __DEBUGCONTROLS_H +#define __DEBUGCONTROLS_H + +/// \defgroup dbgprm Debug control configuration parameter identifiers +/// Used by DebugControls::GetConfigParam() +/// @{ + +/// Boolean flag for "Highlight selected mesh" element. +/// \par Parameter type: +/// DWORD +#define CFGPRM_GETSELECTEDMESH 0x2001 + +/// Boolean flag for "Highlight selected group" element. +/// \par Parameter type: +/// DWORD +#define CFGPRM_GETSELECTEDGROUP 0x2002 + +/// Bit flag for "debug-flags" elements. +/// For a description of the available bit flags, see \ref dbgflgs +/// \par Parameter type: +/// DWORD +#define CFGPRM_GETDEBUGFLAGS 0x2003 + +/// Display mode setting +/// \par Parameter type: +/// DWORD +#define CFGPRM_GETDISPLAYMODE 0x2004 + +/// Camera mode setting +/// \par Parameter type: +/// DWORD +#define CFGPRM_GETCAMERAMODE 0x2005 + +/// Value of camera speed setting +/// \par Parameter type: +/// double +#define CFGPRM_GETCAMERASPEED 0x2006 + +/// @} + + +/// \defgroup dbgflgs Debug control parameter identifiers +/// @{ +#define DBG_FLAGS_SELMSHONLY 0x0001 ///< Selected mesh only +#define DBG_FLAGS_SELGRPONLY 0x0002 ///< Selected group only +#define DBG_FLAGS_BOXES 0x0004 ///< Boxes +#define DBG_FLAGS_SPHERES 0x0008 ///< Spheres +#define DBG_FLAGS_HLMESH 0x0010 ///< Highlight selected mesh +#define DBG_FLAGS_HLGROUP 0x0020 ///< Highlight selected group +#define DBG_FLAGS_TILES 0x0040 ///< Planet tile debugger +#define DBG_FLAGS_SELVISONLY 0x0080 ///< Selected visual only +#define DBG_FLAGS_AMBIENT 0x0100 ///< Add Ambient Light for Visual +#define DBG_FLAGS_WIREFRAME 0x0200 ///< Enable WireFrame +#define DBG_FLAGS_DUALSIDED 0x0400 ///< Dual Sided +#define DBG_FLAGS_PICK 0x1000 ///< Enable mesh picking with mouse +#define DBG_FLAGS_FPSLIM 0x2000 ///< FPS Limiter enabled +#define DBG_FLAGS_TILEBOXES 0x4000 ///< Tile Boxes +#define DBG_FLAGS_NEARCLIP 0x8000 ///< Set Clip distance to 2cm +#define DBG_FLAGS_RENDEREXT 0x10000 ///< Render exterior meshes for VC view +#define DBG_FLAGS_PICKCURRENT 0x20000 ///< Only use Pick for current mesh +#define DBG_FLAGS_NOSUNAMB 0x40000 ///< Disable sun ambient effect in VC +#define DBG_FLAGS_NOPLNAMB 0x80000 ///< Disable planet shine ambient effect in VC +#define DBG_FLAGS_NODYNSUN 0x100000 ///< Disable dynamic sunlight +#define DBG_FLAGS_VCZONES 0x200000 ///< Display VC Click Zones +/// @} + + +class vObject; + +enum class DbgDisplay { + None, Mirror, Blur1, Blur2, Blur3, Blur4, + smSS, smVC, smEX, Irradiance, GlowMask, + ScreenDepth, Normals, LightVisbil, BakedLightMap, Eclipse +}; + +// ============================================================== + +namespace DebugControls { + + extern DbgDisplay dbgdsp; + extern int ambdir; + extern int probe_id; + extern DWORD sMesh; + extern DWORD sGroup; + extern DWORD debugFlags; + extern DWORD dspMode; + extern DWORD camMode; + extern DWORD sEmitter; + extern double camSpeed; + extern double resbias; + extern std::map Emitters; + + /** + * \brief Same functionality than 'official' GetConfigParam, but for + * non-provided debug-control config parameters + * + * This function can be used to access various configuration parameters + * defined in the DebugControls core (e.g. debugFlags or camera settings). + * \param paramtype Parameter identifier (see \ref dbgprm) + * \return Pointer to parameter + * \note The pointer must be cast into the appropriate variable type. + * The variable types can be found in the parameter type list (\ref + * cfgprm). + * \par Example: + * \code + * double speed = *(double*)GetConfigParam(CFGPRM_GETCAMERASPEED); + * \endcode + */ + const void *GetConfigParam (DWORD paramtype); + + void Create(); + void Close(); + void Release(); + void OpenDlgClbk(void *context); + + void SetVisual(vObject *vo); + void RemoveVisual(vObject *vo); + vObject * GetVisual(); + void SetPickPos(FVECTOR3 pos); + vkMesh* GetMesh(); + + void SetupMeshGroups(); + void UpdateVisual(); + void UpdateFlags(); + double GetVisualSize(); + DWORD GetSelectedMesh(); + void SelectGroup(DWORD idx); + void SelectMesh(vkMesh *pMesh); + void SetGroupHighlight(bool bStat); + int GetSceneDebug(); + DbgDisplay GetSelectedEnvMap(); + + bool IsActive(); + bool IsSelectedGroupRendered(); + + void Append(const char *format, ...); + void Refresh(); + + inline int GetProbeId() { return probe_id; } + + LPDIRECT3DTEXTURE9 GetCombinedMap(); + + INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + INT_PTR CALLBACK ViewProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +}; + +#endif // !__DEBUGCONTROLS_H diff --git a/OVP/VulkanClient/Effect.cpp b/OVP/VulkanClient/Effect.cpp new file mode 100644 index 000000000..50dcd818a --- /dev/null +++ b/OVP/VulkanClient/Effect.cpp @@ -0,0 +1,1028 @@ +// =========================================================================================== +// vkEffect.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2011 - 2016 Jarmo Nikkanen +// =========================================================================================== + +#include "Effect.h" +#include "Log.h" +#include "Scene.h" +#include "Surface.h" +#include "Config.h" +#include "Mesh.h" +#include "VectorHelpers.h" + +vkClient * vkEffect::gc = 0; +ID3DXEffect * vkEffect::FX = 0; +LPDIRECT3DVERTEXBUFFER9 vkEffect::VB = 0; +FVECTOR4 vkEffect::atm_color; // Earth glow color + +vkMatExt vkEffect::mfdmat; +vkMatExt vkEffect::defmat; +vkMatExt vkEffect::night_mat; +vkMatExt vkEffect::emissive_mat; + +// Some general rendering techniques +D3DXHANDLE vkEffect::ePanelTech = 0; // Used to draw a new style 2D panel +D3DXHANDLE vkEffect::ePanelTechB = 0; // Used to draw a new style 2D panel +D3DXHANDLE vkEffect::eVesselTech = 0; // Vessel exterior, surface bases. +D3DXHANDLE vkEffect::eBBTech = 0; // Bounding Box Tech +D3DXHANDLE vkEffect::eTBBTech = 0; +D3DXHANDLE vkEffect::eBSTech = 0; // Bounding Sphere Tech +D3DXHANDLE vkEffect::eSimple = 0; +D3DXHANDLE vkEffect::eBaseShadowTech = 0; // Used to draw transparent surface without texture +D3DXHANDLE vkEffect::eBeaconArrayTech = 0; +D3DXHANDLE vkEffect::eExhaust = 0; // Render engine exhaust texture +D3DXHANDLE vkEffect::eSpotTech = 0; // Vessel beacons +D3DXHANDLE vkEffect::eBaseTile = 0; +D3DXHANDLE vkEffect::eRingTech = 0; // Planet rings technique +D3DXHANDLE vkEffect::eRingTech2 = 0; // Planet rings technique +D3DXHANDLE vkEffect::eShadowTech = 0; // Vessel ground shadows +D3DXHANDLE vkEffect::eArrowTech = 0; // (Grapple point) arrows +D3DXHANDLE vkEffect::eAxisTech = 0; +D3DXHANDLE vkEffect::eSimpMesh = 0; +D3DXHANDLE vkEffect::eGeometry = 0; + +// Planet Rendering techniques +D3DXHANDLE vkEffect::ePlanetTile = 0; +D3DXHANDLE vkEffect::eCloudTech = 0; +D3DXHANDLE vkEffect::eCloudShadow = 0; +D3DXHANDLE vkEffect::eSkyDomeTech = 0; +D3DXHANDLE vkEffect::eHazeTech = 0; + +// Particle effect texhniques +D3DXHANDLE vkEffect::eDiffuseTech = 0; +D3DXHANDLE vkEffect::eEmissiveTech = 0; + + + +D3DXHANDLE vkEffect::eVP = 0; // Combined View & Projection Matrix +D3DXHANDLE vkEffect::eW = 0; // World matrix +D3DXHANDLE vkEffect::eLVP = 0; // Light view projection +D3DXHANDLE vkEffect::eGT = 0; // Mesh group transformation matrix +D3DXHANDLE vkEffect::eMat = 0; // Material +D3DXHANDLE vkEffect::eWater = 0; // Water +D3DXHANDLE vkEffect::eMtrl = 0; +D3DXHANDLE vkEffect::eSun = 0; +D3DXHANDLE vkEffect::eNight = 0; +D3DXHANDLE vkEffect::eLights = 0; // Additional light sources + +D3DXHANDLE vkEffect::eTex0 = 0; // Primary texture +D3DXHANDLE vkEffect::eTex1 = 0; // Secondary texture +D3DXHANDLE vkEffect::eTex3 = 0; // Tertiary texture +D3DXHANDLE vkEffect::eSpecMap = 0; +D3DXHANDLE vkEffect::eEmisMap = 0; +D3DXHANDLE vkEffect::eEnvMapA = 0; +D3DXHANDLE vkEffect::eReflMap = 0; +D3DXHANDLE vkEffect::eRghnMap = 0; +D3DXHANDLE vkEffect::eMetlMap = 0; +D3DXHANDLE vkEffect::eHeatMap = 0; +D3DXHANDLE vkEffect::eTranslMap = 0; +D3DXHANDLE vkEffect::eTransmMap = 0; +D3DXHANDLE vkEffect::eIrradMap = 0; +D3DXHANDLE vkEffect::eAmbientMap = 0; +D3DXHANDLE vkEffect::eCombinedMap = 0; +D3DXHANDLE vkEffect::eCombSunMap = 0; + +D3DXHANDLE vkEffect::eSpecularMode = 0; +D3DXHANDLE vkEffect::eHazeMode = 0; +D3DXHANDLE vkEffect::eColor = 0; // Auxiliary color input +D3DXHANDLE vkEffect::eFogColor = 0; // Fog color input +D3DXHANDLE vkEffect::eTexOff = 0; // Surface tile texture offsets +D3DXHANDLE vkEffect::eTime = 0; // FLOAT Simulation elapsed time +D3DXHANDLE vkEffect::eMix = 0; // FLOAT Auxiliary factor/multiplier +D3DXHANDLE vkEffect::eFogDensity = 0; // +D3DXHANDLE vkEffect::ePointScale = 0; +D3DXHANDLE vkEffect::eSHD = 0; +D3DXHANDLE vkEffect::eSHDPx = 0; +D3DXHANDLE vkEffect::eSHDSubRect = 0; + +D3DXHANDLE vkEffect::eAtmColor = 0; +D3DXHANDLE vkEffect::eProxySize = 0; +D3DXHANDLE vkEffect::eMtrlAlpha = 0; +D3DXHANDLE vkEffect::eKernel = 0; +D3DXHANDLE vkEffect::eAtmoParams = 0; + +// Shader Flow Controls +D3DXHANDLE vkEffect::eFlow = 0; +D3DXHANDLE vkEffect::eModAlpha = 0; // BOOL if true multiply material alpha with texture alpha +D3DXHANDLE vkEffect::eFullyLit = 0; // BOOL +D3DXHANDLE vkEffect::eTextured = 0; // BOOL +D3DXHANDLE vkEffect::eFresnel = 0; // BOOL +D3DXHANDLE vkEffect::eSwitch = 0; // BOOL +D3DXHANDLE vkEffect::eRghnSw = 0; // BOOL +D3DXHANDLE vkEffect::eShadowToggle = 0; // BOOL +D3DXHANDLE vkEffect::eEnvMapEnable = 0; // BOOL +D3DXHANDLE vkEffect::eInSpace = 0; // BOOL +D3DXHANDLE vkEffect::eLightsEnabled = 0; // BOOL +D3DXHANDLE vkEffect::eBaseBuilding = 0; // BOOL +D3DXHANDLE vkEffect::eCockpit = 0; // BOOL +D3DXHANDLE vkEffect::eOITEnable = 0; // BOOL +// -------------------------------------------------------------- +D3DXHANDLE vkEffect::eExposure = 0; +D3DXHANDLE vkEffect::eCameraPos = 0; +D3DXHANDLE vkEffect::eNorth = 0; +D3DXHANDLE vkEffect::eEast = 0; +D3DXHANDLE vkEffect::eVCAmbient = 0; +D3DXHANDLE vkEffect::eDistScale = 0; +D3DXHANDLE vkEffect::eRadius = 0; +D3DXHANDLE vkEffect::eAttennuate = 0; +D3DXHANDLE vkEffect::eInScatter = 0; +D3DXHANDLE vkEffect::eInvProxySize = 0; +D3DXHANDLE vkEffect::eGlowConst = 0; +D3DXHANDLE vkEffect::eNoColor = 0; +D3DXHANDLE vkEffect::eVCIrrad = 0; +// -------------------------------------------------------------- +D3DXHANDLE vkEffect::eGlobalAmb = 0; +D3DXHANDLE vkEffect::eSunAppRad = 0; +D3DXHANDLE vkEffect::eAmbient0 = 0; +D3DXHANDLE vkEffect::eDispersion = 0; + +LPDIRECT3DDEVICE9 vkEffect::pDev = 0; + +static D3DMATERIAL9 _emissive_mat = { + {0,0,0,1}, + {0,0,0,1}, + {0,0,0,1}, + {1,1,1,1}, + 0.0 +}; + +static D3DMATERIAL9 _defmat = { + {1,1,1,1}, + {1,1,1,1}, + {0,0,0,1}, + {0,0,0,1},10.0f +}; + +static D3DMATERIAL9 _mfdmat = { + {1,1,1,1}, + {1,1,1,1}, + {0,0,0,1}, + {1,1,1,1},10.0f +}; + +static D3DMATERIAL9 _night_mat = { + {1,1,1,1}, + {0,0,0,1}, + {0,0,0,1}, + {1,1,1,1},10.0f +}; + +static WORD billboard_idx[6] = {0,1,2, 3,2,1}; + +static NTVERTEX billboard_vtx[4] = { + {0,-1, 1, -1,0,0, 0,0}, + {0, 1, 1, -1,0,0, 0,1}, + {0,-1,-1, -1,0,0, 1,0}, + {0, 1,-1, -1,0,0, 1,1} +}; + +static WORD exhaust_idx[12] = {0,1,2, 3,2,1, 4,5,6, 7,6,5}; + + +NTVERTEX exhaust_vtx[8] = { + {0,0,0, 0,0,0, 0.24f,0}, + {0,0,0, 0,0,0, 0.24f,1}, + {0,0,0, 0,0,0, 0.01f,0}, + {0,0,0, 0,0,0, 0.01f,1}, + {0,0,0, 0,0,0, 0.50390625f, 0.00390625f}, + {0,0,0, 0,0,0, 0.99609375f, 0.00390625f}, + {0,0,0, 0,0,0, 0.50390625f, 0.49609375f}, + {0,0,0, 0,0,0, 0.99609375f, 0.49609375f} +}; + + +// TotalWeight = 21.337518, Count = 27, x - balance = -0.145075, y - balance = -0.012734 +/* +static FVECTOR3 shadow_kernel[27] = { + { -0.2607f, -0.9643f, 0.9995f }, + { -0.7879f, -0.5824f, 0.9898f }, + { -0.3796f, -0.6389f, 0.8620f }, + { -0.1287f, -0.6631f, 0.8219f }, + { 0.4393f, -0.6945f, 0.9065f }, + { -0.5448f, -0.4505f, 0.8408f }, + { -0.2330f, -0.3496f, 0.6482f }, + { -0.1185f, -0.2572f, 0.5322f }, + { 0.4340f, -0.4138f, 0.7744f }, + { 0.6976f, -0.4110f, 0.8998f }, + { -0.5738f, 0.0418f, 0.7585f }, + { -0.3732f, -0.1313f, 0.6290f }, + { -0.1242f, 0.0847f, 0.3877f }, + { 0.4157f, 0.0066f, 0.6448f }, + { 0.7117f, 0.0726f, 0.8458f }, + { 0.8990f, 0.0801f, 0.9500f }, + { -0.8740f, 0.2448f, 0.9527f }, + { -0.5545f, 0.3910f, 0.8237f }, + { -0.3458f, 0.3380f, 0.6954f }, + { -0.1168f, 0.2083f, 0.4887f }, + { 0.2998f, 0.4449f, 0.7325f }, + { 0.5572f, 0.2251f, 0.7752f }, + { -0.2250f, 0.5790f, 0.7882f }, + { 0.0300f, 0.5853f, 0.7656f }, + { 0.3031f, 0.6734f, 0.8594f }, + { 0.7652f, 0.6273f, 0.9947f }, + { -0.0571f, 0.9407f, 0.9708f } +}; +*/ + +static FVECTOR3 shadow_kernel[27] = { + { -0.0000f, 0.0000f, 1.0000f }, + { 0.1915f, 0.0188f, 1.0000f }, + { 0.0558f, -0.2664f, 1.0000f }, + { -0.2608f, -0.2076f, 1.0000f }, + { -0.0706f, 0.3784f, 1.0000f }, + { 0.3207f, -0.2869f, 1.0000f }, + { -0.2438f, -0.4035f, 1.0000f }, + { -0.4869f, -0.1488f, 1.0000f }, + { -0.3108f, 0.4468f, 1.0000f }, + { 0.4330f, 0.3819f, 1.0000f }, + { -0.4208f, -0.4396f, 1.0000f }, + { -0.6056f, -0.2017f, 1.0000f }, + { -0.0612f, 0.6638f, 1.0000f }, + { 0.6489f, -0.2457f, 1.0000f }, + { 0.1850f, -0.6959f, 1.0000f }, + { -0.7254f, 0.1711f, 1.0000f }, + { -0.3309f, 0.6950f, 1.0000f }, + { 0.6890f, -0.3937f, 1.0000f }, + { -0.5610f, -0.5933f, 1.0000f }, + { -0.7326f, -0.4086f, 1.0000f }, + { -0.2997f, 0.8068f, 1.0000f }, + { 0.8774f, -0.0892f, 1.0000f }, + { -0.1133f, -0.8955f, 1.0000f }, + { -0.9205f, -0.0673f, 1.0000f }, + { 0.4487f, 0.8292f, 1.0000f }, + { 0.7320f, 0.6245f, 1.0000f }, + { 0.6067f, -0.7713f, 1.0000f } +}; + + +// =========================================================================================== +// +vkEffect::vkEffect() : d3d9id('vk') +{ + +} + +// =========================================================================================== +// +vkEffect::~vkEffect() +{ + +} + +// =========================================================================================== +// +void vkEffect::GlobalExit() +{ + LogAlw("====== vkEffect Global Exit ======="); + SAFE_RELEASE(FX); + SAFE_RELEASE(VB); +} + +// =========================================================================================== +// +void vkEffect::ShutDown() +{ + if (!FX) return; + FX->SetTexture(eTex0, NULL); + FX->SetTexture(eTex1, NULL); + FX->SetTexture(eTex3, NULL); + FX->SetTexture(eSpecMap, NULL); + FX->SetTexture(eEmisMap, NULL); +} + + +// =========================================================================================== +// +void vkEffect::vkTechInit(vkClient *_gc, LPDIRECT3DDEVICE9 _pDev, const char *folder) +{ + char name[256]; + + pDev = _pDev; + gc = _gc; + + gc->OutputLoadStatus("vkClient.fx",1); + + LogAlw("Starting to initialize vkClient.fx a rendering technique..."); + + // Create the Effect from a .fx file. + ID3DXBuffer* errors = 0; + D3DXMACRO macro[18]; memset(¯o, 0, 16*sizeof(D3DXMACRO)); + + sprintf_s(name,256,"Modules/vkShaders/vkClient.fx"); + + if (Config->ShadowMapMode == 0) Config->ShadowFilter = -1; + + // ------------------------------------------------------------------------------ + macro[0].Name = "ANISOTROPY_MACRO"; + macro[0].Definition = new char[32]; + sprintf_s((char*)macro[0].Definition,32,"%d",max(2,Config->Anisotrophy)); + // ------------------------------------------------------------------------------ + macro[1].Name = "LMODE"; + macro[1].Definition = new char[32]; + sprintf_s((char*)macro[1].Definition, 32, "%d", Config->LightConfig); + // ------------------------------------------------------------------------------ + macro[2].Name = "MAX_LIGHTS"; + macro[2].Definition = new char[32]; + sprintf_s((char*)macro[2].Definition, 32, "%d", Config->MaxLights()); + // ------------------------------------------------------------------------------ + macro[3].Name = "SHDMAP"; + macro[3].Definition = new char[32]; + sprintf_s((char*)macro[3].Definition, 32, "%d", Config->ShadowFilter + 1); + // ------------------------------------------------------------------------------ + macro[4].Name = "KERNEL_SIZE"; + macro[4].Definition = new char[32]; + if (Config->ShadowFilter >= 3) sprintf_s((char*)macro[4].Definition, 32, "%d", 35); + else sprintf_s((char*)macro[4].Definition, 32, "%d", 27); + // ------------------------------------------------------------------------------ + macro[5].Name = "KERNEL_WEIGHT"; + macro[5].Definition = new char[32]; + if (Config->ShadowFilter >= 3) sprintf_s((char*)macro[5].Definition, 32, "%f", 0.0285f); + else sprintf_s((char*)macro[5].Definition, 32, "%f", 1.0f / 27.0f); // 0.04634f); + // ------------------------------------------------------------------------------ + macro[6].Name = "CASCOUNT"; + macro[6].Definition = new char[32]; + sprintf_s((char*)macro[6].Definition, 32, "%d", Config->VCCascadeCount); + // ------------------------------------------------------------------------------ + + int m = 7; + if (Config->EnableGlass) macro[m++].Name = "_GLASS"; + if (Config->EnableMeshDbg) macro[m++].Name = "_DEBUG"; + if (Config->EnvMapMode) macro[m++].Name = "_ENVMAP"; + if (Config->PostProcess == PP_DEFAULT) macro[m++].Name = "_LIGHTGLOW"; + if (Config->bIrradiance && Config->EnvMapMode) macro[m++].Name = "_IRRADIANCE"; + + + HR(D3DXCreateEffectFromFileA(pDev, name, macro, 0, D3DXSHADER_NO_PRESHADER|D3DXSHADER_PREFER_FLOW_CONTROL, 0, &FX, &errors)); + + delete []macro[0].Definition; + delete []macro[1].Definition; + delete []macro[2].Definition; + delete []macro[3].Definition; + delete []macro[4].Definition; + delete []macro[5].Definition; + macro[0].Definition = NULL; + macro[1].Definition = NULL; + macro[2].Definition = NULL; + macro[3].Definition = NULL; + macro[4].Definition = NULL; + macro[5].Definition = NULL; + + if (errors) { + LogErr("Effect Error: %s",(char*)errors->GetBufferPointer()); + MessageBoxA(0, (char*)errors->GetBufferPointer(), "vkClient.fx Error", 0); + FatalAppExitA(0,"Critical error has occured. See Orbiter.log for details"); + } + + if (FX==0) { + LogErr("Failed to create an Effect (%s)",name); + return; + } + + if (Config->ShaderDebug) { + LPD3DXBUFFER pBuffer = NULL; + if (D3DXDisassembleEffect(FX, true, &pBuffer) == S_OK) { + FILE *fp = NULL; + if (!fopen_s(&fp, "D9D9Effect_asm.html", "w")) { + fwrite(pBuffer->GetBufferPointer(), 1, pBuffer->GetBufferSize(), fp); + fclose(fp); + } + pBuffer->Release(); + } + } + + + // Techniques -------------------------------------------------------------- + + eHazeTech = FX->GetTechniqueByName("HazeTech"); + ePlanetTile = FX->GetTechniqueByName("PlanetTech"); + eBaseTile = FX->GetTechniqueByName("BaseTileTech"); + eSimple = FX->GetTechniqueByName("SimpleTech"); + eBBTech = FX->GetTechniqueByName("BoundingBoxTech"); + eTBBTech = FX->GetTechniqueByName("TileBoxTech"); + eBSTech = FX->GetTechniqueByName("BoundingSphereTech"); + eRingTech = FX->GetTechniqueByName("RingTech"); + eRingTech2 = FX->GetTechniqueByName("RingTech2"); + eExhaust = FX->GetTechniqueByName("ExhaustTech"); + eSpotTech = FX->GetTechniqueByName("SpotTech"); + eShadowTech = FX->GetTechniqueByName("ShadowTech"); + ePanelTech = FX->GetTechniqueByName("PanelTech"); + ePanelTechB = FX->GetTechniqueByName("PanelTechB"); + eCloudTech = FX->GetTechniqueByName("PlanetCloudTech"); + eCloudShadow = FX->GetTechniqueByName("PlanetCloudShadowTech"); + eSkyDomeTech = FX->GetTechniqueByName("SkyDomeTech"); + eArrowTech = FX->GetTechniqueByName("ArrowTech"); + eAxisTech = FX->GetTechniqueByName("AxisTech"); + eSimpMesh = FX->GetTechniqueByName("SimplifiedTech"); + eGeometry = FX->GetTechniqueByName("GeometryTech"); + eVesselTech = FX->GetTechniqueByName("VesselTech"); + eBaseShadowTech = FX->GetTechniqueByName("BaseShadowTech"); + eBeaconArrayTech = FX->GetTechniqueByName("BeaconArrayTech"); + eDiffuseTech = FX->GetTechniqueByName("ParticleDiffuseTech"); + eEmissiveTech = FX->GetTechniqueByName("ParticleEmissiveTech"); + + // Flow Control Booleans ----------------------------------------------- + eModAlpha = FX->GetParameterByName(0,"gModAlpha"); + eFullyLit = FX->GetParameterByName(0,"gFullyLit"); + eFlow = FX->GetParameterByName(0,"gCfg"); + eShadowToggle = FX->GetParameterByName(0,"gShadowsEnabled"); + eEnvMapEnable = FX->GetParameterByName(0,"gEnvMapEnable"); + eTextured = FX->GetParameterByName(0,"gTextured"); + eFresnel = FX->GetParameterByName(0,"gFresnel"); + eSwitch = FX->GetParameterByName(0,"gPBRSw"); + eRghnSw = FX->GetParameterByName(0,"gRghnSw"); + eInSpace = FX->GetParameterByName(0,"gInSpace"); + eLightsEnabled = FX->GetParameterByName(0,"gLightsEnabled"); + eBaseBuilding = FX->GetParameterByName(0,"gBaseBuilding"); + eCockpit = FX->GetParameterByName(0,"gCockpit"); + eOITEnable = FX->GetParameterByName(0,"gOITEnable"); + + // General parameters -------------------------------------------------- + eNoColor = FX->GetParameterByName(0, "gNoColor"); + eVCIrrad = FX->GetParameterByName(0, "gVCIrrad"); + eSpecularMode = FX->GetParameterByName(0,"gSpecMode"); + eLights = FX->GetParameterByName(0,"gLights"); + eColor = FX->GetParameterByName(0,"gColor"); + eDistScale = FX->GetParameterByName(0,"gDistScale"); + eProxySize = FX->GetParameterByName(0,"gProxySize"); + eTexOff = FX->GetParameterByName(0,"gTexOff"); + eRadius = FX->GetParameterByName(0,"gRadius"); + eCameraPos = FX->GetParameterByName(0,"gCameraPos"); + eNorth = FX->GetParameterByName(0,"gNorth"); + eEast = FX->GetParameterByName(0,"gEast"); + eVCAmbient = FX->GetParameterByName(0,"gVCAmbient"); + ePointScale = FX->GetParameterByName(0,"gPointScale"); + eMix = FX->GetParameterByName(0,"gMix"); + eTime = FX->GetParameterByName(0,"gTime"); + eMtrlAlpha = FX->GetParameterByName(0,"gMtrlAlpha"); + eGlowConst = FX->GetParameterByName(0,"gGlowConst"); + eSHD = FX->GetParameterByName(0,"gSHD"); + eSHDPx = FX->GetParameterByName(0,"gSHDPx"); + eSHDSubRect = FX->GetParameterByName(0,"gSHDSubRect"); + eKernel = FX->GetParameterByName(0,"kernel"); + eAtmoParams = FX->GetParameterByName(0,"gAtmo"); + // ---------------------------------------------------------------------- + eVP = FX->GetParameterByName(0,"gVP"); + eW = FX->GetParameterByName(0,"gW"); + eLVP = FX->GetParameterByName(0,"gLVP"); + eGT = FX->GetParameterByName(0,"gGrpT"); + // ---------------------------------------------------------------------- + eSun = FX->GetParameterByName(0,"gSun"); + eMat = FX->GetParameterByName(0,"gMat"); + eWater = FX->GetParameterByName(0,"gWater"); + eMtrl = FX->GetParameterByName(0,"gMtrl"); + // ---------------------------------------------------------------------- + eTex0 = FX->GetParameterByName(0,"gTex0"); + eTex1 = FX->GetParameterByName(0,"gTex1"); + eTex3 = FX->GetParameterByName(0,"gTex3"); + eSpecMap = FX->GetParameterByName(0,"gSpecMap"); + eEmisMap = FX->GetParameterByName(0,"gEmisMap"); + eEnvMapA = FX->GetParameterByName(0,"gEnvMapA"); + eReflMap = FX->GetParameterByName(0,"gReflMap"); + eRghnMap = FX->GetParameterByName(0,"gRghnMap"); + eMetlMap = FX->GetParameterByName(0,"gMetlMap"); + eHeatMap = FX->GetParameterByName(0,"gHeatMap"); + eTranslMap = FX->GetParameterByName(0, "gTranslMap"); + eTransmMap = FX->GetParameterByName(0, "gTransmMap"); + eIrradMap = FX->GetParameterByName(0,"gIrradianceMap"); + eAmbientMap = FX->GetParameterByName(0, "gAmbientMap"); + eCombinedMap = FX->GetParameterByName(0, "gCombinedMap"); + eCombSunMap = FX->GetParameterByName(0, "gCombinedSunMap"); + + // Atmosphere ----------------------------------------------------------- + eGlobalAmb = FX->GetParameterByName(0,"gGlobalAmb"); + eSunAppRad = FX->GetParameterByName(0,"gSunAppRad"); + eAmbient0 = FX->GetParameterByName(0,"gAmbient0"); + eDispersion = FX->GetParameterByName(0,"gDispersion"); + eFogDensity = FX->GetParameterByName(0,"gFogDensity"); + eAttennuate = FX->GetParameterByName(0,"gAttennuate"); + eInScatter = FX->GetParameterByName(0,"gInScatter"); + eInvProxySize = FX->GetParameterByName(0,"gInvProxySize"); + eFogColor = FX->GetParameterByName(0,"gFogColor"); + eAtmColor = FX->GetParameterByName(0,"gAtmColor"); + eHazeMode = FX->GetParameterByName(0,"gHazeMode"); + eNight = FX->GetParameterByName(0, "gNightTime"); + + // Initialize default values -------------------------------------- + // + FX->SetInt(eHazeMode, 0); + FX->SetBool(eInSpace, false); + FX->SetVector(eAttennuate, _DX(F4_One)); + FX->SetVector(eInScatter, _DX(F4_Zero)); + FX->SetVector(eColor, _DX(F4_Zero)); + + //if (Config->ShadowFilter>=3) FX->SetValue(eKernel, &shadow_kernel2, sizeof(shadow_kernel2)); + FX->SetValue(eKernel, &shadow_kernel, sizeof(shadow_kernel)); + + CreateMatExt(&_mfdmat, &mfdmat); + CreateMatExt(&_defmat, &defmat); + CreateMatExt(&_night_mat, &night_mat); + CreateMatExt(&_emissive_mat, &emissive_mat); + + // Create a Circle Mesh -------------------------------------------- + // + if (!VB) { + HR(pDev->CreateVertexBuffer(256 * sizeof(FVECTOR3), 0, 0, D3DPOOL_DEFAULT, &VB, NULL)); + + FVECTOR3 *pVert; + + if (VB->Lock(0, 0, (void **)&pVert, 0) == S_OK) { + float angle = 0.0f, step = float(PI2) / 255.0f; + pVert[0] = FVECTOR3(0, 0, 0); + for (int i = 1; i < 256; i++) { + pVert[i].x = 0; + pVert[i].y = cos(angle); + pVert[i].z = sin(angle); + angle += step; + } + VB->Unlock(); + } + else LogErr("Failed to Lock vertex buffer"); + } +} + + +void vkEffect::SetViewProjMatrix(FMATRIX4* pVP) +{ + FX->SetMatrix(eVP, _DX(pVP)); +} + + +void vkEffect::UpdateEffectCamera(OBJHANDLE hPlanet) +{ + if (!hPlanet) return; + VECTOR3 cam, pla, sun; + OBJHANDLE hSun = oapiGetGbodyByIndex(0); // generalise later + cam = gc->GetScene()->GetCameraGPos(); + oapiGetGlobalPos(hPlanet, &pla); + oapiGetGlobalPos(hSun, &sun); + + double len = length(cam - pla); + double rad = oapiGetSize(hPlanet); + + sun = unit(sun - cam); // Vector pointing to sun from camera + cam = unit(cam - pla); // Vector pointing to cam from planet + + DWORD width, height; + oapiGetViewportSize(&width, &height); // BUG: Custom Camera may have different view size + + + float radlimit = float(rad) + 1.0f; + float rho0 = 1.0f; + + atm_color = FVECTOR4(0.5f, 0.5f, 0.5f, 1.0f); + + const ATMCONST *atm = oapiGetPlanetAtmConstants(hPlanet); + VESSEL *hVessel = oapiGetFocusInterface(); + + OBJHANDLE hGRef = hVessel->GetGravityRef(); + MATRIX3 grot; + + oapiGetRotationMatrix(hGRef, &grot); + + VECTOR3 polaraxis = mul(grot, _V(0, 1, 0)); + VECTOR3 east = unit(crossp(polaraxis, cam)); + VECTOR3 north = unit(crossp(cam, east)); + + if (hVessel==NULL) { + LogErr("hVessel = NULL in UpdateEffectCamera()"); + return; + } + + if (atm) { + radlimit = float(atm->radlimit); + atm_color = FVECTOR4(FVECTOR3(atm->color0), 1.0f); + rho0 = float(atm->rho0); + } + + float av = (atm_color.x + atm_color.y + atm_color.z) * 0.3333333f; + float fc = 1.5f; + float alt = 1.0f - pow(float(hVessel->GetAtmDensity()/rho0), 0.2f); + atm_color += FVECTOR4(av,av,av,1.0f)*fc; + atm_color *= 1.0f/(fc+1.0f); + atm_color *= float(Config->PlanetGlow) * alt; + + float ap = gc->GetScene()->GetCameraAperture(); + float rl = float(rad/len); + float proxy_size = asin(min(1.0f, rl)) + float(40.0*PI/180.0); + + if (rl>1e-3) atm_color *= pow(rl, 1.5f); + else atm_color = FVECTOR4(0.0f, 0.0f, 0.0f, 1.0f); + + FX->SetValue(eEast, _DX(FVECTOR3(east)), sizeof(FVECTOR3)); + FX->SetValue(eNorth, _DX(FVECTOR3(north)), sizeof(FVECTOR3)); + FX->SetValue(eCameraPos, _DX(FVECTOR3(cam)), sizeof(FVECTOR3)); + FX->SetVector(eRadius, _DX(FVECTOR4((float)rad, radlimit, (float)len, (float)(len-rad)))); + FX->SetFloat(ePointScale, 0.5f*float(height)/tan(ap)); + FX->SetFloat(eProxySize, cos(proxy_size)); + FX->SetFloat(eInvProxySize, 1.0f/(1.0f-cos(proxy_size))); + FX->SetFloat(eGlowConst, saturate(float(dotp(cam, sun)))); +} + + +// =========================================================================================== +// +void vkEffect::EnablePlanetGlow(bool bEnabled) +{ + if (bEnabled) FX->SetVector(eAtmColor, _DX(atm_color)); + else FX->SetVector(eAtmColor, _DX(F4_Zero)); +} + + +// =========================================================================================== +// +void vkEffect::InitLegacyAtmosphere(OBJHANDLE hPlanet, float GlobalAmbient) +{ + VECTOR3 GS, GP; + + OBJHANDLE hS = oapiGetGbodyByIndex(0); // the central star + oapiGetGlobalPos (hS, &GS); // sun position + oapiGetGlobalPos (hPlanet, &GP); // planet position + + float rs = (float)(oapiGetSize(hS) / length(GS-GP)); + + const ATMCONST *atm = (oapiGetObjectType(hPlanet)==OBJTP_PLANET ? oapiGetPlanetAtmConstants (hPlanet) : NULL); + + FX->SetFloat(eGlobalAmb, GlobalAmbient); + FX->SetFloat(eSunAppRad, rs); + + if (atm) { + FX->SetFloat(eAmbient0, float(min(0.7, log1p(atm->rho0)*0.4))); + FX->SetFloat(eDispersion, float(max(0.02, min(0.9, log1p(atm->rho0))))); + } + else { + FX->SetFloat(eAmbient0, 0.0f); + FX->SetFloat(eDispersion, 0.0f); + } +} + + + +// =========================================================================================== +// +void vkEffect::Render2DPanel(const MESHGROUP *mg, const SURFHANDLE pTex, const FMATRIX4* pW, float alpha, float scale, bool additive) +{ + UINT numPasses = 0; + if (!pTex || !mg || !pW) return; + + if (SURFACE(pTex)->IsPowerOfTwo() || (!gc->IsLimited())) FX->SetTechnique(ePanelTech); // ANISOTROPIC filter + else FX->SetTechnique(ePanelTechB); // POINT filter (for non-pow2 conditional) + + HR(FX->SetMatrix(eW, _DX(pW))); + + if (pTex) FX->SetTexture(eTex0, SURFACE(pTex)->GetTexture()); + else FX->SetTexture(eTex0, NULL); + + HR(FX->SetFloat(eMix, alpha)); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + + if (additive) pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + else pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + pDev->SetVertexDeclaration(pNTVertexDecl); + pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, mg->nVtx, mg->nIdx/3, mg->Idx, D3DFMT_INDEX16, mg->Vtx, sizeof(NTVERTEX)); + + HR(FX->EndPass()); + HR(FX->End()); +} + + + + +// =========================================================================================== +// +void vkEffect::RenderReEntry(const SURFHANDLE pTex, const FVECTOR3* vPosA, const FVECTOR3* vPosB, const FVECTOR3* vDir, float alpha_a, float alpha_b, float size) +{ + static WORD ReentryIdx[6] = {0,1,2, 3,2,1}; + + static NTVERTEX ReentryVtxB[4] = { + {0,-1,-1, 0,0,0, 0.51f, 0.01f}, + {0,-1, 1, 0,0,0, 0.99f, 0.01f}, + {0, 1,-1, 0,0,0, 0.51f, 0.49f}, + {0, 1, 1, 0,0,0, 0.99f, 0.49f} + }; + + float x = 4.5f + sin(fmod(float(oapiGetSimTime())*60.0f, 6.283185f)) * 0.5f; + + NTVERTEX ReentryVtxA[4] = { + {0, 1, 1, 0,0,0, 0.49f, 0.01f}, + {0, 1,-x, 0,0,0, 0.49f, 0.99f}, + {0,-1, 1, 0,0,0, 0.01f, 0.01f}, + {0,-1,-x, 0,0,0, 0.01f, 0.99f}, + }; + + UINT numPasses = 0; + FMATRIX4 WA, WB; + FVECTOR3 vCam = unit(*vPosA); + D3DMAT_CreateX_Billboard(&vCam, vPosB, size*(0.8f+x*0.02f), &WB); + D3DMAT_CreateX_Billboard(&vCam, vPosA, vDir, size, size, &WA); + + pDev->SetVertexDeclaration(pNTVertexDecl); + + FX->SetTechnique(eExhaust); + FX->SetTexture(eTex0, SURFACE(pTex)->GetTexture()); + FX->SetFloat(eMix, alpha_b); + FX->SetMatrix(eW, _DX(WB)); + FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); + FX->BeginPass(0); + pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &ReentryIdx, D3DFMT_INDEX16, &ReentryVtxB, sizeof(NTVERTEX)); + FX->SetFloat(eMix, alpha_a); + FX->SetMatrix(eW, _DX(WA)); + FX->CommitChanges(); + pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &ReentryIdx, D3DFMT_INDEX16, &ReentryVtxA, sizeof(NTVERTEX)); + + FX->EndPass(); + FX->End(); +} + + +// =========================================================================================== +// This is a special rendering routine used to render beacons +// +void vkEffect::RenderSpot(float alpha, const FVECTOR4* pColor, const FMATRIX4* pW, SURFHANDLE pTex) +{ + UINT numPasses = 0; + HR(pDev->SetVertexDeclaration(pNTVertexDecl)); + HR(FX->SetTechnique(eSpotTech)); + HR(FX->SetFloat(eMix, alpha)); + HR(FX->SetValue(eColor, pColor, sizeof(FVECTOR4))); + HR(FX->SetMatrix(eW, _DX(pW))); + HR(FX->SetTexture(eTex0, SURFACE(pTex)->GetTexture())); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + HR(pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, billboard_idx, D3DFMT_INDEX16, billboard_vtx, sizeof(NTVERTEX))); + HR(FX->EndPass()); + HR(FX->End()); +} + + +// =========================================================================================== +// Used by Render Star only +// +void vkEffect::RenderBillboard(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex, float alpha) +{ + UINT numPasses = 0; + + HR(pDev->SetVertexDeclaration(pNTVertexDecl)); + HR(FX->SetTechnique(eSimple)); + HR(FX->SetMatrix(eW, _DX(pW))); + HR(FX->SetFloat(eMix, alpha)); + HR(FX->SetTexture(eTex0, pTex)); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + HR(pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, billboard_idx, D3DFMT_INDEX16, billboard_vtx, sizeof(NTVERTEX))); + HR(FX->EndPass()); + HR(FX->End()); +} + + +// =========================================================================================== +// This is a special rendering routine used to render engine exhaust +// +void vkEffect::RenderExhaust(const FMATRIX4* pW, VECTOR3 &cdir, EXHAUSTSPEC *es, SURFHANDLE def) +{ + + SURFHANDLE pTex = SURFACE(es->tex); + if (!pTex) pTex = def; + + double alpha = *es->level; + if (es->modulate) alpha *= ((1.0 - es->modulate)+(double)rand()* es->modulate/(double)RAND_MAX); + + VECTOR3 edir = -(*es->ldir); + VECTOR3 ref = (*es->lpos) - (*es->ldir)*es->lofs; + + const float flarescale = 7.0; + VECTOR3 sdir = crossp(cdir, edir); normalise(sdir); + VECTOR3 tdir = crossp(cdir, sdir); normalise(tdir); + float rx = (float)ref.x; + float ry = (float)ref.y; + float rz = (float)ref.z; + float sx = (float)(sdir.x*es->wsize); + float sy = (float)(sdir.y*es->wsize); + float sz = (float)(sdir.z*es->wsize); + float ex = (float)(edir.x*es->lsize); + float ey = (float)(edir.y*es->lsize); + float ez = (float)(edir.z*es->lsize); + + exhaust_vtx[1].x = (exhaust_vtx[0].x = rx + sx) + ex; + exhaust_vtx[1].y = (exhaust_vtx[0].y = ry + sy) + ey; + exhaust_vtx[1].z = (exhaust_vtx[0].z = rz + sz) + ez; + exhaust_vtx[3].x = (exhaust_vtx[2].x = rx - sx) + ex; + exhaust_vtx[3].y = (exhaust_vtx[2].y = ry - sy) + ey; + exhaust_vtx[3].z = (exhaust_vtx[2].z = rz - sz) + ez; + + double wscale = es->wsize; + wscale *= flarescale, sx *= flarescale, sy *= flarescale, sz *= flarescale; + + float tx = (float)(tdir.x*wscale); + float ty = (float)(tdir.y*wscale); + float tz = (float)(tdir.z*wscale); + exhaust_vtx[4].x = rx - sx + tx; exhaust_vtx[5].x = rx + sx + tx; + exhaust_vtx[4].y = ry - sy + ty; exhaust_vtx[5].y = ry + sy + ty; + exhaust_vtx[4].z = rz - sz + tz; exhaust_vtx[5].z = rz + sz + tz; + exhaust_vtx[6].x = rx - sx - tx; exhaust_vtx[7].x = rx + sx - tx; + exhaust_vtx[6].y = ry - sy - ty; exhaust_vtx[7].y = ry + sy - ty; + exhaust_vtx[6].z = rz - sz - tz; exhaust_vtx[7].z = rz + sz - tz; + + UINT numPasses = 0; + HR(pDev->SetVertexDeclaration(pNTVertexDecl)); + HR(FX->SetTechnique(eExhaust)); + HR(FX->SetFloat(eMix, float(alpha))); + HR(FX->SetMatrix(eW, _DX(pW))); + HR(FX->SetTexture(eTex0, SURFACE(pTex)->GetTexture())); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + HR(pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 8, 4, exhaust_idx, D3DFMT_INDEX16, exhaust_vtx, sizeof(NTVERTEX))); + HR(FX->EndPass()); + HR(FX->End()); +} + + +void vkEffect::RenderBoundingBox(const FMATRIX4* pW, const FMATRIX4* pGT, const FVECTOR4 *bmin, const FVECTOR4 *bmax, const FVECTOR4 *color) +{ + FMATRIX4 ident; + oapiMatrixIdentity(&ident); + + static FVECTOR3 poly[10] = { + {0, 0, 0}, + {1, 0, 0}, + {1, 1, 0}, + {0, 1, 0}, + {0, 0, 0}, + {0, 0, 1}, + {1, 0, 1}, + {1, 1, 1}, + {0, 1, 1}, + {0, 0, 1} + }; + + static FVECTOR3 list[6] = { + {1, 0, 0}, + {1, 0, 1}, + {1, 1, 0}, + {1, 1, 1}, + {0, 1, 0}, + {0, 1, 1} + }; + + pDev->SetVertexDeclaration(pPositionDecl); + + FX->SetMatrix(eW, _DX(pW)); + FX->SetMatrix(eGT, _DX(pGT)); + FX->SetVector(eAttennuate, _DX(bmin)); + FX->SetVector(eInScatter, _DX(bmax)); + FX->SetVector(eColor, _DX(color)); + FX->SetTechnique(eBBTech); + + UINT numPasses = 0; + FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); + FX->BeginPass(0); + + pDev->DrawPrimitiveUP(D3DPT_LINESTRIP, 9, &poly, sizeof(FVECTOR3)); + pDev->DrawPrimitiveUP(D3DPT_LINELIST, 3, &list, sizeof(FVECTOR3)); + + FX->EndPass(); + FX->End(); +} + +void vkEffect::RenderTileBoundingBox(const FMATRIX4* pW, VECTOR4 *pVtx, const FVECTOR4* color) +{ + FVECTOR3 poly[8]; + + for (int i=0;i<8;i++) poly[i] = FVECTOR4(pVtx[i]).xyz; + + WORD idc1[10] = { 0, 1, 3, 2, 0, 4, 5, 7, 6, 4 }; + WORD idc2[6] = { 1, 5, 3, 7, 2, 6}; + + pDev->SetVertexDeclaration(pPositionDecl); + + FX->SetMatrix(eW, _DX(pW)); + FX->SetVector(eColor, _DX(color)); + FX->SetTechnique(eTBBTech); + + UINT numPasses = 0; + FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); + FX->BeginPass(0); + pDev->DrawIndexedPrimitiveUP(D3DPT_LINESTRIP, 0, 8, 9, &idc1, D3DFMT_INDEX16, &poly, sizeof(FVECTOR3)); + pDev->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, 8, 3, &idc2, D3DFMT_INDEX16, &poly, sizeof(FVECTOR3)); + FX->EndPass(); + FX->End(); +} + + +void vkEffect::RenderLines(const FVECTOR3 *pVtx, const WORD *pIdx, int nVtx, int nIdx, const FMATRIX4 *pW, DWORD color) +{ + UINT numPasses = 0; + pDev->SetVertexDeclaration(pPositionDecl); + FX->SetMatrix(eW, _DX(pW)); + FX->SetVector(eColor, _DX(FVECTOR4(color))); + FX->SetTechnique(eTBBTech); + FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); + FX->BeginPass(0); + pDev->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, nVtx, nIdx/2, pIdx, D3DFMT_INDEX16, pVtx, sizeof(FVECTOR3)); + FX->EndPass(); + FX->End(); +} + + +void vkEffect::RenderBoundingSphere(const FMATRIX4* pW, const FMATRIX4* pGT, const FVECTOR4 *bs, const FVECTOR4 *color) +{ + FMATRIX4 mW; + FVECTOR3 vPos = oapiTransformCoord(&(FVECTOR3(bs->x, bs->y, bs->z)), pW); + FVECTOR3 vCam = unit(vPos); + + D3DMAT_CreateX_Billboard(&vCam, &vPos, bs->w, &mW); + + pDev->SetVertexDeclaration(pPositionDecl); + + FX->SetMatrix(eW, _DX(mW)); + FX->SetVector(eColor, _DX(color)); + FX->SetTechnique(eBSTech); + + UINT numPasses = 0; + FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); + FX->BeginPass(0); + + pDev->SetStreamSource(0, VB, 0, sizeof(FVECTOR3)); + pDev->DrawPrimitive(D3DPT_LINESTRIP, 0, 255); + + FX->EndPass(); + FX->End(); +} + +// =========================================================================================== +// This is a special rendering routine used to render (grapple point) arrows +// +void vkEffect::RenderArrow(OBJHANDLE hObj, const VECTOR3 *ofs, const VECTOR3 *dir, const VECTOR3 *rot, float size, const FVECTOR4 *pColor) +{ + static FVECTOR3 arrow[18] = { + // Head (front- & back-face) + {0.0, 0.0, 0.0}, + {0.0,-1.0, 1.0}, + {0.0, 1.0, 1.0}, + {0.0, 0.0, 0.0}, + {0.0, 1.0, 1.0}, + {0.0,-1.0, 1.0}, + // Body first triangle (front- & back-face) + {0.0,-0.5, 1.0}, + {0.0,-0.5, 2.0}, + {0.0, 0.5, 2.0}, + {0.0,-0.5, 1.0}, + {0.0, 0.5, 2.0}, + {0.0,-0.5, 2.0}, + // Body second triangle (front- & back-face) + {0.0,-0.5, 1.0}, + {0.0, 0.5, 2.0}, + {0.0, 0.5, 1.0}, + {0.0,-0.5, 1.0}, + {0.0, 0.5, 1.0}, + {0.0, 0.5, 2.0} + }; + + MATRIX3 grot; + FMATRIX4 W; + VECTOR3 camp, gpos; + + oapiGetRotationMatrix(hObj, &grot); + oapiGetGlobalPos(hObj, &gpos); + camp = gc->GetScene()->GetCameraGPos(); + + VECTOR3 pos = gpos - camp; + if (ofs) pos += mul (grot, *ofs); + + VECTOR3 z = mul (grot, unit(*dir)) * double(size); + VECTOR3 y = mul (grot, unit(*rot)) * double(size); + VECTOR3 x = mul (grot, unit(crossp(*dir, *rot))) * double(size); + + oapiMatrixIdentity(&W); + + W.m11 = float(x.x); + W.m12 = float(x.y); + W.m13 = float(x.z); + + W.m21 = float(y.x); + W.m22 = float(y.y); + W.m23 = float(y.z); + + W.m31 = float(z.x); + W.m32 = float(z.y); + W.m33 = float(z.z); + + W.m41 = float(pos.x); + W.m42 = float(pos.y); + W.m43 = float(pos.z); + + UINT numPasses = 0; + HR(pDev->SetVertexDeclaration(pPositionDecl)); // Position only vertex decleration + HR(FX->SetTechnique(eArrowTech)); // Use arrow shader + HR(FX->SetValue(eColor, pColor, sizeof(FVECTOR4))); // Setup arrow color + HR(FX->SetMatrix(eW, _DX(W))); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + HR(pDev->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 6, &arrow, sizeof(FVECTOR3))); // Draw 6 triangles un-indexed + HR(FX->EndPass()); + HR(FX->End()); +} diff --git a/OVP/VulkanClient/Effect.h b/OVP/VulkanClient/Effect.h new file mode 100644 index 000000000..8c96369c1 --- /dev/null +++ b/OVP/VulkanClient/Effect.h @@ -0,0 +1,200 @@ +// =========================================================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2011 - 2016 Jarmo Nikkanen +// =========================================================================================== + +#ifndef __vkEFFECT_H +#define __vkEFFECT_H + +#define vkSM_SPHERE 0x01 +#define vkSM_ARROW 0x02 +#define vkSM_BOX 0x03 + +#include "Client.h" +#include +#include "MathAPI.h" + +// NOTE: a "bool" in HLSL is 32bits (i.e. int) +// Must match with counterpart in vkClient.fx + +struct TexFlow { + BOOL Emis; // Enable Emission Maps + BOOL Spec; // Enable Specular Maps + BOOL Refl; // Enable Reflection Maps + BOOL Transl; // Enable translucent effect + BOOL Transm; // Enable transmissive effect + BOOL Rghn; // Enable roughness map + BOOL Norm; // Enable normal map + BOOL Metl; // Enable metalness map + BOOL Heat; // Enable heat map + BOOL Baked; // Enable pre-baked maps + BOOL BakedAO; // Enable pre-baked AO map + BOOL BakedAmb; // Enable pre-baked Ambient light map +}; + + + +using namespace oapi; + +class vkEffect { + + DWORD d3d9id; + +public: + static void vkTechInit(vkClient *gc, LPDIRECT3DDEVICE9 pDev, const char *folder); + + /** + * \brief Release global parameters + */ + static void GlobalExit(); + + static void ShutDown(); + + vkEffect(); + ~vkEffect(); + + static void EnablePlanetGlow(bool bEnabled); + static void UpdateEffectCamera(OBJHANDLE hPlanet); + static void InitLegacyAtmosphere(OBJHANDLE hPlanet, float GlobalAmbient); + static void SetViewProjMatrix(FMATRIX4* pVP); + + static void RenderLines(const FVECTOR3 *pVtx, const WORD *pIdx, int nVtx, int nIdx, const FMATRIX4 *pW, DWORD color); + static void RenderTileBoundingBox(const FMATRIX4* pW, VECTOR4 *pVtx, const FVECTOR4* color); + static void RenderBoundingBox(const FMATRIX4* pW, const FMATRIX4* pGT, const FVECTOR4 *bmin, const FVECTOR4 *bmax, const FVECTOR4 *color); + static void RenderBoundingSphere(const FMATRIX4* pW, const FMATRIX4* pGT, const FVECTOR4 *bs, const FVECTOR4 *color); + static void RenderBillboard(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex, float alpha = 1.0f); + static void RenderExhaust(const FMATRIX4* pW, VECTOR3 &cdir, EXHAUSTSPEC *es, SURFHANDLE def); + static void RenderSpot(float intens, const FVECTOR4* color, const FMATRIX4* pW, SURFHANDLE pTex); + static void Render2DPanel(const MESHGROUP *mg, const SURFHANDLE pTex, const FMATRIX4* pW, float alpha, float scale, bool additive); + static void RenderReEntry(const SURFHANDLE pTex, const FVECTOR3* vPosA, const FVECTOR3* vPosB, const FVECTOR3* vDir, float alpha_a, float alpha_b, float size); + static void RenderArrow(OBJHANDLE hObj, const VECTOR3 *ofs, const VECTOR3 *dir, const VECTOR3 *rot, float size, const FVECTOR4 *pColor); + + static LPDIRECT3DDEVICE9 pDev; ///< Static (global) render device + static LPDIRECT3DVERTEXBUFFER9 VB; ///< Static (global) Vertex buffer pointer + + static FVECTOR4 atm_color; ///< Earth glow color + + // Rendering Technique related parameters + static ID3DXEffect *FX; + static vkClient *gc; ///< The graphics client instance + + static vkMatExt mfdmat; + static vkMatExt defmat; + static vkMatExt night_mat; + static vkMatExt emissive_mat; + + // Techniques ---------------------------------------------------- + static D3DXHANDLE eVesselTech; ///< Vessel exterior, surface bases + static D3DXHANDLE eSimple; + static D3DXHANDLE eBBTech; ///< Bounding Box Tech + static D3DXHANDLE eTBBTech; ///< Bounding Box Tech + static D3DXHANDLE eBSTech; ///< Bounding Sphere Tech + static D3DXHANDLE eExhaust; ///< Render engine exhaust texture + static D3DXHANDLE eSpotTech; ///< Vessel beacons + static D3DXHANDLE ePanelTech; ///< Used to draw a new style 2D panel + static D3DXHANDLE ePanelTechB; ///< Used to draw a new style 2D panel + static D3DXHANDLE eBaseTile; + static D3DXHANDLE eRingTech; ///< Planet rings technique + static D3DXHANDLE eRingTech2; ///< Planet rings technique + static D3DXHANDLE eShadowTech; ///< Vessel ground shadows + static D3DXHANDLE eGeometry; + static D3DXHANDLE eBaseShadowTech; ///< Used to draw transparent surface without texture + static D3DXHANDLE eBeaconArrayTech; + static D3DXHANDLE eArrowTech; ///< (Grapple point) arrows + static D3DXHANDLE eAxisTech; + static D3DXHANDLE ePlanetTile; + static D3DXHANDLE eCloudTech; + static D3DXHANDLE eCloudShadow; + static D3DXHANDLE eSkyDomeTech; + static D3DXHANDLE eDiffuseTech; + static D3DXHANDLE eEmissiveTech; + static D3DXHANDLE eHazeTech; + static D3DXHANDLE eSimpMesh; + + // Transformation Matrices ---------------------------------------- + static D3DXHANDLE eVP; ///< Combined View & Projection Matrix + static D3DXHANDLE eW; ///< World Matrix + static D3DXHANDLE eLVP; ///< Light view projection + static D3DXHANDLE eGT; ///< MeshGroup transformation matrix + + // Lighting related parameters ------------------------------------ + static D3DXHANDLE eMtrl; + static D3DXHANDLE eMat; ///< Material + static D3DXHANDLE eWater; ///< Water + static D3DXHANDLE eSun; ///< Sun + static D3DXHANDLE eLights; ///< Additional light sources + static D3DXHANDLE eKernel; + static D3DXHANDLE eAtmoParams; + + // Auxiliary params ---------------------------------------------- + static D3DXHANDLE eModAlpha; ///< BOOL multiply material alpha with texture alpha + static D3DXHANDLE eFullyLit; ///< BOOL + static D3DXHANDLE eFlow; ///< BOOL + static D3DXHANDLE eShadowToggle; ///< BOOL + static D3DXHANDLE eEnvMapEnable; ///< BOOL + static D3DXHANDLE eInSpace; ///< BOOL + static D3DXHANDLE eLightsEnabled;///< BOOL + static D3DXHANDLE eBaseBuilding; ///< BOOL + static D3DXHANDLE eCockpit; ///< BOOL + static D3DXHANDLE eFresnel; ///< BOOL + static D3DXHANDLE eSwitch; ///< BOOL + static D3DXHANDLE eRghnSw; ///< BOOL + static D3DXHANDLE eTextured; ///< BOOL + static D3DXHANDLE eOITEnable; ///< BOOL + static D3DXHANDLE eInvProxySize; + static D3DXHANDLE eNoColor; + static D3DXHANDLE eVCIrrad; + static D3DXHANDLE eMix; ///< FLOAT Auxiliary factor/multiplier + static D3DXHANDLE eColor; ///< Auxiliary color input + static D3DXHANDLE eFogColor; ///< Fog color input + static D3DXHANDLE eTexOff; ///< Surface tile texture offsets + static D3DXHANDLE eSpecularMode; + static D3DXHANDLE eHazeMode; + static D3DXHANDLE eTime; ///< FLOAT Simulation elapsed time + static D3DXHANDLE eExposure; + static D3DXHANDLE eCameraPos; + static D3DXHANDLE eNorth; + static D3DXHANDLE eEast; + static D3DXHANDLE eVCAmbient; + static D3DXHANDLE eDistScale; + static D3DXHANDLE eGlowConst; + static D3DXHANDLE eRadius; + static D3DXHANDLE eFogDensity; + static D3DXHANDLE ePointScale; + static D3DXHANDLE eAtmColor; + static D3DXHANDLE eProxySize; + static D3DXHANDLE eMtrlAlpha; + static D3DXHANDLE eAttennuate; + static D3DXHANDLE eInScatter; + static D3DXHANDLE eSHD; + static D3DXHANDLE eSHDPx; + static D3DXHANDLE eSHDSubRect; + static D3DXHANDLE eNight; + + // Textures -------------------------------------------------------- + static D3DXHANDLE eTex0; ///< Primary texture + static D3DXHANDLE eTex1; ///< Secondary texture + static D3DXHANDLE eTex3; ///< Tertiary texture + static D3DXHANDLE eSpecMap; + static D3DXHANDLE eEmisMap; + static D3DXHANDLE eEnvMapA; + static D3DXHANDLE eReflMap; + static D3DXHANDLE eMetlMap; + static D3DXHANDLE eHeatMap; + static D3DXHANDLE eRghnMap; + static D3DXHANDLE eTranslMap; + static D3DXHANDLE eTransmMap; + static D3DXHANDLE eIrradMap; + static D3DXHANDLE eAmbientMap; + static D3DXHANDLE eCombinedMap; + static D3DXHANDLE eCombSunMap; + + // Legacy Atmosphere ----------------------------------------------- + static D3DXHANDLE eGlobalAmb; + static D3DXHANDLE eSunAppRad; + static D3DXHANDLE eAmbient0; + static D3DXHANDLE eDispersion; +}; + +#endif // !__vkEFFECT_H diff --git a/OVP/VulkanClient/Frame.cpp b/OVP/VulkanClient/Frame.cpp new file mode 100644 index 000000000..d30096856 --- /dev/null +++ b/OVP/VulkanClient/Frame.cpp @@ -0,0 +1,640 @@ +// ============================================================== +// File: vkframe.cpp +// Desc: Class functions to implement a Direct3D app framework. +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007 - 2016 Martin Schweiger +// 2011 - 2016 Jarmo Nikkanen +// ============================================================== + +#define STRICT + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include "GraphicsAPI.h" +#include "Frame.h" +#include "Util.h" +#include "AABBUtil.h" +#include "Surface.h" +#include "Log.h" +#include "Config.h" +#include "OapiExtension.h" + +using namespace oapi; + +IDirect3DVertexDeclaration9 *pMeshVertexDecl = NULL; +IDirect3DVertexDeclaration9 *pHazeVertexDecl = NULL; +IDirect3DVertexDeclaration9 *pNTVertexDecl = NULL; +IDirect3DVertexDeclaration9 *pBAVertexDecl = NULL; +IDirect3DVertexDeclaration9 *pPosColorDecl = NULL; +IDirect3DVertexDeclaration9 *pPositionDecl = NULL; +IDirect3DVertexDeclaration9 *pVector4Decl = NULL; +IDirect3DVertexDeclaration9 *pPosTexDecl = NULL; +IDirect3DVertexDeclaration9 *pPatchVertexDecl = NULL; +IDirect3DVertexDeclaration9 *pSketchpadDecl = NULL; +IDirect3DVertexDeclaration9 *pLocalLightsDecl = NULL; + +static const char *d3dmessage={"Required DirectX version (June 2010 or newer) not found\0"}; + +//----------------------------------------------------------------------------- +// Name: CD3DFramework9() +// Desc: The constructor. Clears static variables +//----------------------------------------------------------------------------- + +CD3DFramework9::CD3DFramework9() +{ + Clear(); + + if (g_pD3DObject == NULL) { + LogErr("ERROR: [Direct3D9 Creation Failed]"); + LogErr(d3dmessage); + MessageBoxA(NULL, d3dmessage, "vkClient Initialization Failed",MB_OK); + } +} + +//----------------------------------------------------------------------------- +// Name: ~CD3DFramework9() +// Desc: The destructor. Deletes all objects +//----------------------------------------------------------------------------- +CD3DFramework9::~CD3DFramework9 () +{ + LogAlw("Deleting Framework"); +} + +void CD3DFramework9::Clear() +{ + hWnd = NULL; + bIsFullscreen = false; + bVertexTexture = false; + bAAEnabled = false; + bNoVSync = false; + Alpha = false; + SWVert = false; + Pure = true; + DDM = false; + nvPerfHud = false; + dwRenderWidth = 0; + dwRenderHeight = 0; + dwFSMode = 0; + pDevice = NULL; + pLargeFont = NULL; + pSmallFont = NULL; + dwZBufferBitDepth = 0; + dwStencilBitDepth = 0; + Adapter = 0; + Mode = 0; + MultiSample = 0; + pRenderTarget = NULL; + pBackBuffer = NULL; + pDepthStencil = NULL; + + memset((void *)&rcScreenRect, 0, sizeof(RECT)); + memset((void *)&d3dPP, 0, sizeof(D3DPRESENT_PARAMETERS)); + memset((void *)&caps, 0, sizeof(D3DCAPS9)); +} + +//----------------------------------------------------------------------------- +// Name: DestroyObjects() +// Desc: Objects created in Initialize() section are destroyed in here +//----------------------------------------------------------------------------- +HRESULT CD3DFramework9::DestroyObjects () +{ + _TRACE; + LogAlw("========== Destroying framework objects =========="); + + SAFE_RELEASE(pRenderTarget); + SAFE_RELEASE(pDepthStencil); + SAFE_RELEASE(pLargeFont); + SAFE_RELEASE(pSmallFont); + SAFE_RELEASE(pNTVertexDecl); + SAFE_RELEASE(pBAVertexDecl); + SAFE_RELEASE(pPosColorDecl); + SAFE_RELEASE(pPositionDecl); + SAFE_RELEASE(pVector4Decl); + SAFE_RELEASE(pPosTexDecl); + SAFE_RELEASE(pHazeVertexDecl); + SAFE_RELEASE(pMeshVertexDecl); + SAFE_RELEASE(pPatchVertexDecl); + SAFE_RELEASE(pSketchpadDecl); + SAFE_RELEASE(pLocalLightsDecl); + + HR(pDevice->EvictManagedResources()); + + Sleep(200); + + if (pDevice->Reset(&d3dPP)==S_OK) LogAlw("[DirectX Device Reset Succesfull]"); + else LogAlw("[Failed to Reset DirectX Device] (Likely blocked by undeleted resources)"); + + SAFE_RELEASE(pDevice); + + return S_OK; +} + +//----------------------------------------------------------------------------- +// Name: Initialize() +// Desc: Creates the internal objects for the framework +//----------------------------------------------------------------------------- +HRESULT CD3DFramework9::Initialize(HWND _hWnd, GraphicsClient::VIDEODATA *vData) +{ + _TRACE; + + Clear(); + + DDM = (Config->DisableDriverManagement != 0); + nvPerfHud = (Config->NVPerfHUD != 0); + + bool bFail = false; + + if (_hWnd==NULL || vData==NULL) { + LogErr("ERROR: Invalid input parameter in CD3DFramework9::Initialize()"); + return E_INVALIDARG; + } + + // Setup state for windowed/fullscreen mode + // + hWnd = _hWnd; + bAAEnabled = (Config->SceneAntialias != 0); + bIsFullscreen = vData->fullscreen; + bNoVSync = vData->novsync; + dwFSMode = vData->style; + Adapter = vData->deviceidx; + Mode = vData->modeidx; + + LogAlw("[VideoConfiguration] Adapter=%u, ModeIndex=%u", Adapter, Mode); + + D3DADAPTER_IDENTIFIER9 info; + g_pD3DObject->GetAdapterIdentifier(Adapter, 0, &info); + LogOapi("3D-Adapter.............. : %s",info.Description); + LogAlw("dwFSMode................ : %u",dwFSMode); + + // Get DisplayMode Resolution --------------------------------------- + // + if (bIsFullscreen) { + + switch(dwFSMode) { + + // True Fullscreen + case 0: + { + dwDisplayMode = 0; + D3DDISPLAYMODE mode; + if (AdapterGetAdapterCount()) { + if (ModeGetAdapterModeCount(Adapter, D3DFMT_X8R8G8B8)) { + g_pD3DObject->EnumAdapterModes(Adapter, D3DFMT_X8R8G8B8, Mode, &mode); + dwRenderWidth = mode.Width; + dwRenderHeight = mode.Height; + } + } + } + break; + + // Fullscreen Window + case 1: + { + dwDisplayMode = 1; + int x = GetSystemMetrics(SM_CXSCREEN); + int y = GetSystemMetrics(SM_CYSCREEN); + if (vData->pageflip) x = GetSystemMetrics(SM_CXVIRTUALSCREEN); + SetWindowLongA(hWnd, GWL_STYLE, WS_CLIPCHILDREN | WS_VISIBLE); + SetWindowPos(hWnd,0, 0,0, x, y, SWP_SHOWWINDOW); + bIsFullscreen = false; + } + break; + + // Fullscreen Window with Taskbar + case 2: + { + dwDisplayMode = 1; + RECT rect; + SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0); + SetWindowLongA(hWnd, GWL_STYLE, WS_CLIPCHILDREN | WS_VISIBLE); + int x = GetSystemMetrics(SM_CXSCREEN); + if (vData->pageflip) x = rect.right-rect.left; + SetWindowPos(hWnd,0,rect.left, rect.top, x, rect.bottom - rect.top, SWP_SHOWWINDOW); + bIsFullscreen = false; + } + break; + } + } + else { + dwDisplayMode = 2; + LONG x = GetWindowLongA(hWnd, GWL_STYLE); + SetWindowLongA(hWnd, GWL_STYLE, x | WS_CLIPCHILDREN | WS_VISIBLE); + if (vData->trystencil) + SetWindowPos(hWnd, 0, 0, 0, vData->winw, vData->winh, SWP_SHOWWINDOW | SWP_NOSENDCHANGING); + } + + // Hardware CAPS Checks -------------------------------------------------- + // + HRESULT hr = g_pD3DObject->GetDeviceCaps(Adapter, D3DDEVTYPE_HAL, &caps); + + if (hr!=S_OK) { + LogErr("g_pD3DObject->GetDeviceCaps(Adapter, D3DDEVTYPE_HAL, &caps)"); + return hr; + } + + // AA CAPS Checks -------------------------------------------------- + // + DWORD aamax = 0; + if (g_pD3DObject->CheckDeviceMultiSampleType(Adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, true, D3DMULTISAMPLE_2_SAMPLES, NULL)==S_OK) aamax=2; + if (g_pD3DObject->CheckDeviceMultiSampleType(Adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, true, D3DMULTISAMPLE_4_SAMPLES, NULL)==S_OK) aamax=4; + if (g_pD3DObject->CheckDeviceMultiSampleType(Adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, true, D3DMULTISAMPLE_8_SAMPLES, NULL)==S_OK) aamax=8; + + MultiSample = min(aamax, DWORD(Config->SceneAntialias)); + + if (MultiSample==1) MultiSample = 0; + + LogAlw("MaxTextureBlendStages... : %u",caps.MaxTextureBlendStages); + LogOapi("MaxTextureWidth......... : %u",caps.MaxTextureWidth); + LogOapi("MaxTextureHeight........ : %u",caps.MaxTextureHeight); + LogOapi("MaxTextureRepeat........ : %u",caps.MaxTextureRepeat); + LogOapi("VolTexAddressCaps....... : 0x%X",caps.VolumeTextureAddressCaps); + LogAlw("MaxVolumeExtent......... : %u",caps.MaxVolumeExtent); + LogAlw("MaxPrimitiveCount....... : %u",caps.MaxPrimitiveCount); + LogAlw("MaxVertexIndex.......... : %u",caps.MaxVertexIndex); + LogAlw("MaxAnisotropy........... : %u",caps.MaxAnisotropy); + LogAlw("MaxSimultaneousTextures. : %u",caps.MaxSimultaneousTextures); + LogAlw("MaxStreams.............. : %u",caps.MaxStreams); + LogAlw("MaxStreamStride......... : %u",caps.MaxStreamStride); + LogAlw("MaxVertexBlendMatrices.. : %u",caps.MaxVertexBlendMatrices); + LogAlw("MaxVShaderInstrExecuted. : %u",caps.MaxVShaderInstructionsExecuted); + LogAlw("MaxPointSize............ : %f",caps.MaxPointSize); + LogAlw("VertexShaderVersion..... : 0x%hX",(caps.VertexShaderVersion&0xFFFF)); + LogAlw("PixelShaderVersion...... : 0x%hX",(caps.PixelShaderVersion&0xFFFF)); + LogOapi("NumSimultaneousRTs...... : %u",caps.NumSimultaneousRTs); + LogAlw("D3DPTEXTURECAPS_POW2.... : %d",(caps.TextureCaps&D3DPTEXTURECAPS_POW2)>0); + LogAlw("NONPOW2CONDITIONAL...... : %d",(caps.TextureCaps&D3DPTEXTURECAPS_NONPOW2CONDITIONAL)>0); + LogOapi("VertexDeclCaps.......... : 0x%X",caps.DeclTypes); + LogOapi("MiscCaps................ : 0x%X", caps.PrimitiveMiscCaps); + LogAlw("DevCaps................. : 0x%X",caps.DevCaps); + LogAlw("DevCaps2................ : 0x%X",caps.DevCaps2); + + + // Check non power of two texture support + if ((caps.PrimitiveMiscCaps&D3DPMISCCAPS_COLORWRITEENABLE)==0) { + LogErr("D3DRS_COLORWRITEENABLE Not Suppoertd"); + bFail = true; + } + + // Check non power of two texture support + if ((caps.TextureCaps&D3DPTEXTURECAPS_POW2) && !(caps.TextureCaps&D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) { + LogErr("[Non-Power of 2 texture support required]"); + bFail=true; + } + + if ((caps.TextureCaps&D3DPTEXTURECAPS_POW2) && (caps.TextureCaps&D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) { + LogWrn("[Hardware has only a limited non-power of 2 texture support]"); + } + + if ((caps.PixelShaderVersion&0xFFFF)<0x0300 || (caps.VertexShaderVersion&0xFFFF)<0x0300) { + LogErr("[Pixel Shader Version 3.0 is required (i.e. DirectX 9.0c)]"); + bFail=true; + } + + + + // Do Some Additional Hardware Checks =================================================================== + + if (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) { + LogOapi("Separate AlphaBlend..... : Yes"); + } + else { + LogOapi("Separate AlphaBlend..... : No"); + } + + + // Check shadow mapping support + // + bool bShadowMap = true; + if (g_pD3DObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_R32F)!=S_OK) bShadowMap = false; + if (g_pD3DObject->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DFMT_R32F, D3DFMT_D24X8)!=S_OK) bShadowMap = false; + + if (bShadowMap) LogOapi("Shadow Mapping.......... : Yes"); + else LogOapi("Shadow Mapping.......... : No"); + + bool bFloat16BB = true; + if (g_pD3DObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F)!=S_OK) bFloat16BB = false; + if (g_pD3DObject->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DFMT_A16B16G16R16F, D3DFMT_D24X8)!=S_OK) bFloat16BB = false; + + if (bFloat16BB) LogOapi("D3DFMT_A16B16G16R16F.... : Yes"); + else LogOapi("D3DFMT_A16B16G16R16F.... : No"); + + HRESULT VT16BB = g_pD3DObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F); + HRESULT VT32BB = g_pD3DObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_A32B32G32R32F); + HRESULT VT16BC = g_pD3DObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_R16F); + HRESULT VT32BC = g_pD3DObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_R32F); + + if (VT16BB == S_OK) LogOapi("Vertex_A16B16G16R16F.... : Yes"); + else LogOapi("Vertex_A16B16G16R16F.... : No"); + if (VT32BB == S_OK) LogOapi("Vertex_A32B32G32R32F.... : Yes"); + else LogOapi("Vertex_A32B32G32R32F.... : No"); + if (VT16BC == S_OK) LogOapi("Vertex_R16F............. : Yes"); + else LogOapi("Vertex_R16F............. : No"); + if (VT32BC == S_OK) LogOapi("Vertex_R32F............. : Yes"); + else LogOapi("Vertex_R32F............. : No"); + + if (VT16BB != S_OK || VT32BB != S_OK || VT16BC != S_OK || VT32BC != S_OK) bFail = true; + + + bool bFloat32BB = true; + if (g_pD3DObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_A32B32G32R32F)!=S_OK) bFloat32BB = false; + + if (bFloat32BB) LogOapi("D3DFMT_A32B32G32R32F.... : Yes"); + else LogOapi("D3DFMT_A32B32G32R32F.... : No"); + + HRESULT D32F = g_pD3DObject->CheckDeviceFormat(Adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D32F_LOCKABLE); + + if (D32F==S_OK) LogOapi("D3DFMT_D32F_LOCKABLE.... : Yes"); + else LogOapi("D3DFMT_D32F_LOCKABLE.... : No"); + + HRESULT AR10 = g_pD3DObject->CheckDeviceFormat(Adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_A2R10G10B10); + + if (AR10==S_OK) LogOapi("D3DFMT_A2R10G10B10...... : Yes"); + else LogOapi("D3DFMT_A2R10G10B10...... : No"); + + HRESULT L8 = g_pD3DObject->CheckDeviceFormat(Adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_L8); + if (g_pD3DObject->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DFMT_L8, D3DFMT_D24X8) != S_OK) L8 = S_FALSE;//-1; + + if (L8 == S_OK) LogOapi("D3DFMT_L8............... : Yes"); + else LogOapi("D3DFMT_L8............... : No"); + + + + if (caps.DeclTypes&D3DDTCAPS_DEC3N) LogOapi("D3DDTCAPS_DEC3N......... : Yes"); + else LogOapi("D3DDTCAPS_DEC3N......... : No"); + + if (caps.DeclTypes&D3DDTCAPS_FLOAT16_2) LogOapi("D3DDTCAPS_FLOAT16_2..... : Yes"); + else LogOapi("D3DDTCAPS_FLOAT16_2..... : No"); + + if (caps.DeclTypes&D3DDTCAPS_FLOAT16_4) LogOapi("D3DDTCAPS_FLOAT16_4..... : Yes"); + else LogOapi("D3DDTCAPS_FLOAT16_4..... : No"); + + // Check (Log) whether orbiter runs on WINE + // + LogOapi("Runs under WINE......... : %s", OapiExtension::RunsUnderWINE() ? "Yes" : "No"); + LogOapi("vkBuild Date.......... : %u", BuildDate()); + + // Log some locale information + // + //int size = max( GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SDECIMAL, NULL, 0), + // GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SENGLISHDISPLAYNAME, NULL, 0) ); + //auto buff = new WCHAR[size]; + + //GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SDECIMAL, buff, size); + //LogOapi("Decimal separator....... : %ls", buff); + //GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SENGLISHDISPLAYNAME, buff, size); + //LogOapi("Locale.................. : %ls", buff); + + //delete[] buff; + + // Check MipMap autogeneration + // + if ((caps.Caps2&D3DCAPS2_CANAUTOGENMIPMAP)==0) { + LogWrn("[No Hardware MipMap auto generation]"); + oapiWriteLog((char*)"vk: WARNING: [No Hardware MipMap auto generation]"); + } + + if (bFail) { + oapiWriteLog((char*)"vk: FAIL: !! Graphics card doesn't meet the minimum requirements to run !!"); + MessageBoxA(NULL, "Graphics card doesn't meet the minimum requirements to run vkClient.", "vkClient Error",MB_OK); + return -1; + } + + if (bIsFullscreen) hr = CreateFullscreenMode(); + else hr = CreateWindowedMode(); + + if (FAILED(hr)) { + LogErr("[Device Initialization Failed]"); + return hr; + } + + LogOapi("Available Texture Memory : %u MB", pDevice->GetAvailableTextureMem() >> 20); + + D3DVIEWPORT9 vp; + + vp.X = 0; + vp.Y = 0; + vp.Width = dwRenderWidth; + vp.Height = dwRenderHeight; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + + pDevice->SetViewport(&vp); + + HR(pDevice->CreateVertexDeclaration(NTVertexDecl, &pNTVertexDecl)); + HR(pDevice->CreateVertexDeclaration(BAVertexDecl, &pBAVertexDecl)); + HR(pDevice->CreateVertexDeclaration(PosColorDecl, &pPosColorDecl)); + HR(pDevice->CreateVertexDeclaration(PositionDecl, &pPositionDecl)); + HR(pDevice->CreateVertexDeclaration(Vector4Decl, &pVector4Decl)); + HR(pDevice->CreateVertexDeclaration(PosTexDecl, &pPosTexDecl)); + HR(pDevice->CreateVertexDeclaration(HazeVertexDecl, &pHazeVertexDecl)); + HR(pDevice->CreateVertexDeclaration(MeshVertexDecl, &pMeshVertexDecl)); + HR(pDevice->CreateVertexDeclaration(PatchVertexDecl, &pPatchVertexDecl)); + HR(pDevice->CreateVertexDeclaration(SketchpadDecl, &pSketchpadDecl)); + HR(pDevice->CreateVertexDeclaration(LocalLightsDecl, &pLocalLightsDecl)); + + // Setup some default fonts + // + D3DXFONT_DESC fontDesc; + fontDesc.Height = 30; + fontDesc.Width = 22; + fontDesc.Weight = FW_BOLD; + fontDesc.MipLevels = 0; + fontDesc.Italic = false; + fontDesc.CharSet = DEFAULT_CHARSET; + fontDesc.OutputPrecision = OUT_DEFAULT_PRECIS; + fontDesc.Quality = ANTIALIASED_QUALITY; + fontDesc.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + strcpy(fontDesc.FaceName, "Arial"); + + HR(D3DXCreateFontIndirect(pDevice, &fontDesc, &pLargeFont)); + + fontDesc.Height = 16; + fontDesc.Width = 10; + fontDesc.Weight = FW_NORMAL; + fontDesc.MipLevels = 0; + fontDesc.Italic = false; + fontDesc.CharSet = DEFAULT_CHARSET; + fontDesc.OutputPrecision = OUT_DEFAULT_PRECIS; + fontDesc.Quality = ANTIALIASED_QUALITY; + fontDesc.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + strcpy(fontDesc.FaceName, "Arial"); + + HR(D3DXCreateFontIndirect(pDevice, &fontDesc, &pSmallFont)); + + LogAlw("=== [3DDevice Initialized] ==="); + return S_OK; +} + +//----------------------------------------------------------------------------- +// Name: CreateFullscreenBuffers() +// Desc: Creates the primary and (optional) backbuffer for rendering. +// Windowed mode and fullscreen mode are handled differently. +//----------------------------------------------------------------------------- +HRESULT CD3DFramework9::CreateFullscreenMode() +{ + + // Get the dimensions of the screen bounds + // Store the rectangle which contains the renderer + SetRect(&rcScreenRect, 0, 0, dwRenderWidth, dwRenderHeight); + + HR(g_pD3DObject->CheckDeviceType(Adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, false)); + + LogAlw("[FULLSCREEN MODE] %u x %u, hWindow=%s", dwRenderWidth, dwRenderHeight, _PTR(hWnd)); + + dwZBufferBitDepth = 24; + dwStencilBitDepth = 8; + + d3dPP.BackBufferWidth = dwRenderWidth; + d3dPP.BackBufferHeight = dwRenderHeight; + d3dPP.BackBufferFormat = D3DFMT_X8R8G8B8; + d3dPP.BackBufferCount = 1; + d3dPP.MultiSampleType = D3DMULTISAMPLE_NONE; + d3dPP.MultiSampleQuality = 0; + d3dPP.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dPP.hDeviceWindow = hWnd; + d3dPP.Windowed = false; + d3dPP.EnableAutoDepthStencil = true; + d3dPP.AutoDepthStencilFormat = D3DFMT_D24S8; + d3dPP.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; // Must be lockable to allow GDI dialogs to work + d3dPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; + + if (bNoVSync) d3dPP.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + else d3dPP.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + + DWORD devBehaviorFlags = 0; + + if (SWVert) devBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; + else devBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; + + if (Pure) devBehaviorFlags |= D3DCREATE_PUREDEVICE; + + if (DDM) devBehaviorFlags |= D3DCREATE_DISABLE_DRIVER_MANAGEMENT; + + HR(g_pD3DObject->CreateDevice( + Adapter, // primary adapter + D3DDEVTYPE_HAL, // device type + hWnd, // window associated with device + devBehaviorFlags|D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE, + &d3dPP, // present parameters + &pDevice)); // return created device + + // Get Backbuffer + if (pDevice) { + pDevice->GetRenderTarget(0, &pRenderTarget); + pDevice->GetDepthStencilSurface(&pDepthStencil); + pBackBuffer = (SURFHANDLE) new SurfNative(pRenderTarget, OAPISURFACE_BACKBUFFER | OAPISURFACE_RENDER3D | OAPISURFACE_RENDERTARGET, pDepthStencil); + SURFACE(pBackBuffer)->SetName("BackBuffer"); + return S_OK; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Name: CreateWindowedBuffers() +// Desc: Creates the primary and (optional) backbuffer for rendering. +// Windowed mode and fullscreen mode are handled differently. +//----------------------------------------------------------------------------- +HRESULT CD3DFramework9::CreateWindowedMode() +{ + _TRACE; + + // Get the dimensions of the viewport and screen bounds + GetClientRect(hWnd, &rcScreenRect); + + // What is this ?!! + //ClientToScreen(hWnd, (POINT*)&rcScreenRect.left); + //ClientToScreen(hWnd, (POINT*)&rcScreenRect.right); + + dwRenderWidth = rcScreenRect.right - rcScreenRect.left; + dwRenderHeight = rcScreenRect.bottom - rcScreenRect.top; + dwZBufferBitDepth = 24; + dwStencilBitDepth = 8; + + LogAlw("Window Size = [%u, %u]", dwRenderWidth, dwRenderHeight); + LogAlw("Window LeftTop = [%d, %d]", rcScreenRect.left, rcScreenRect.top); + + if (MultiSample) { + + DWORD level; + HR(g_pD3DObject->CheckDeviceMultiSampleType(Adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, true, (D3DMULTISAMPLE_TYPE)MultiSample, &level)); + + d3dPP.BackBufferWidth = dwRenderWidth; + d3dPP.BackBufferHeight = dwRenderHeight; + d3dPP.BackBufferFormat = D3DFMT_X8R8G8B8; + d3dPP.BackBufferCount = 1; + d3dPP.MultiSampleType = (D3DMULTISAMPLE_TYPE)MultiSample; + d3dPP.MultiSampleQuality = level - 1; + d3dPP.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dPP.hDeviceWindow = hWnd; + d3dPP.Windowed = true; + d3dPP.EnableAutoDepthStencil = true; + d3dPP.AutoDepthStencilFormat = D3DFMT_D24S8; + d3dPP.Flags = 0; + d3dPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; + } + else { + d3dPP.BackBufferWidth = dwRenderWidth; + d3dPP.BackBufferHeight = dwRenderHeight; + d3dPP.BackBufferFormat = D3DFMT_X8R8G8B8; + d3dPP.BackBufferCount = 1; + d3dPP.MultiSampleType = D3DMULTISAMPLE_NONE; + d3dPP.MultiSampleQuality = 0; + d3dPP.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dPP.hDeviceWindow = hWnd; + d3dPP.Windowed = true; + d3dPP.EnableAutoDepthStencil = true; + d3dPP.AutoDepthStencilFormat = D3DFMT_D24S8; + d3dPP.Flags = 0; + d3dPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; + } + + if (bNoVSync) d3dPP.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + else d3dPP.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + + DWORD devBehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING; + + if (Pure) devBehaviorFlags |= D3DCREATE_PUREDEVICE; + if (DDM) devBehaviorFlags |= D3DCREATE_DISABLE_DRIVER_MANAGEMENT; + + HRESULT hr = 0; + + if (nvPerfHud && Adapter==1) { + + LogErr("[WARNING] NVPerfHUD mode is Active (Disable from vkClient.cfg) [WARNING]"); + + hr = g_pD3DObject->CreateDevice( + Adapter, // primary adapter + D3DDEVTYPE_REF, // device type + hWnd, // window associated with device + devBehaviorFlags|D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE, + &d3dPP, // present parameters + &pDevice); // return created device + } + else { + + hr = g_pD3DObject->CreateDevice( + Adapter, // primary adapter + D3DDEVTYPE_HAL, // device type + hWnd, // window associated with device + devBehaviorFlags|D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE, + &d3dPP, // present parameters + &pDevice); // return created device + } + + if (hr!=S_OK) LogErr("CreateDevice() Failed HR=0x%X",hr); + + // Get Backbuffer + if (pDevice) { + pDevice->GetRenderTarget(0, &pRenderTarget); + pDevice->GetDepthStencilSurface(&pDepthStencil); + pBackBuffer = (SURFHANDLE) new SurfNative(pRenderTarget, OAPISURFACE_BACKBUFFER | OAPISURFACE_RENDER3D | OAPISURFACE_RENDERTARGET, pDepthStencil); + SURFACE(pBackBuffer)->SetName("BackBuffer"); + return S_OK; + } + + return -1; +} + diff --git a/OVP/VulkanClient/Frame.h b/OVP/VulkanClient/Frame.h new file mode 100644 index 000000000..05091a16c --- /dev/null +++ b/OVP/VulkanClient/Frame.h @@ -0,0 +1,145 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +// ============================================================================ +// File: vkframe.h +// Desc: Class to manage the Direct3D environment objects +// +// The class is initialized with the Initialize() function, after which +// the Get????() functions can be used to access the objects needed for +// rendering. If the device or display needs to be changed, the +// ChangeDevice() function can be called. If the display window is moved +// the changes need to be reported with the Move() function. +// +// After rendering a frame, the ShowFrame() function flips or blits the +// backbuffer contents to the primary. If surfaces are lost, they can be +// restored with the RestoreSurfaces() function. Finally, if normal +// Windows output is needed, the FlipToGDISurface() provides a GDI +// surface to draw on. +// ============================================================================ + +#ifndef vkFRAME_H +#define vkFRAME_H + +#include +#include "MathAPI.h" +#include "Orbitersdk.h" +#include "Client.h" + +class SurfNative; + +//----------------------------------------------------------------------------- +// Name: CD3DFramework9 +// Desc: The Direct3D sample framework class for DX9. Maintains the D3D +// surfaces and device used for 3D rendering. +//----------------------------------------------------------------------------- +class CD3DFramework9 +{ + +private: + + // Internal variables for the framework class + HWND hWnd; // The window object + BOOL bIsFullscreen; // Fullscreen vs. windowed + BOOL bVertexTexture; + BOOL bAAEnabled; + BOOL bNoVSync; // don't use vertical sync in fullscreen + BOOL Alpha; + BOOL SWVert; + BOOL Pure; + BOOL DDM; + BOOL nvPerfHud; + DWORD dwRenderWidth; // Dimensions of the render target + DWORD dwRenderHeight; // Dimensions of the render target + DWORD dwFSMode; + LPDIRECT3DDEVICE9 pDevice; // The D3D device + LPD3DXFONT pLargeFont; + LPD3DXFONT pSmallFont; + DWORD dwZBufferBitDepth; // Bit depth of z-buffer + DWORD dwStencilBitDepth; // Bit depth of stencil buffer (0 if none) + DWORD Adapter; + DWORD Mode; + DWORD MultiSample; + DWORD dwDisplayMode; + LPDIRECT3DSURFACE9 pRenderTarget; + LPDIRECT3DSURFACE9 pDepthStencil; + SURFHANDLE pBackBuffer; + D3DPRESENT_PARAMETERS d3dPP; + D3DCAPS9 caps; + RECT rcScreenRect; // Screen rect for window + + // Internal functions for the framework class + + HRESULT CreateFullscreenMode(); + HRESULT CreateWindowedMode(); + void Clear(); + +public: + + // Access functions for DirectX objects + inline HWND GetRenderWindow() const { return hWnd; } + inline LPDIRECT3DDEVICE9 GetD3DDevice() const { return pDevice; } + inline DWORD GetZBufferBitDepth() const { return dwZBufferBitDepth; } + inline DWORD GetStencilBitDepth() const { return dwStencilBitDepth; } + inline DWORD GetWidth() const { return dwRenderWidth; } // Dimensions of the render target + inline DWORD GetHeight() const { return dwRenderHeight; } // Dimensions of the render target + inline const RECT GetScreenRect() const { return rcScreenRect; } + inline LPDIRECT3DSURFACE9 GetBackBuffer() const { return pRenderTarget; } + inline SURFHANDLE GetBackBufferHandle() const { return pBackBuffer; } + inline LPD3DXFONT GetLargeFont() const { return pLargeFont; } + inline LPD3DXFONT GetSmallFont() const { return pSmallFont; } + inline BOOL IsFullscreen() const { return bIsFullscreen; } + inline BOOL IsAAEnabled() const { return bAAEnabled; } + inline const D3DCAPS9 * GetCaps() const { return ∩︀ } + inline BOOL HasVertexTextureSup() const { return bVertexTexture; } + inline BOOL GetVSync() const { return (bNoVSync==FALSE); } + + // GetDisplayMode 0=True Fullscreen, 1=Fullscreen Window, 2=Windowed + inline DWORD GetDisplayMode() const { return dwDisplayMode; } + + // Creates the Framework + HRESULT Initialize(HWND hWnd, struct oapi::GraphicsClient::VIDEODATA *vData); + + HRESULT DestroyObjects(); + + CD3DFramework9(); + ~CD3DFramework9(); +}; + + +//----------------------------------------------------------------------------- +// Flags used for the Initialize() method of a CD3DFramework object +//----------------------------------------------------------------------------- +#define D3DFW_FULLSCREEN 0x00000001 // Use fullscreen mode +#define D3DFW_STEREO 0x00000002 // Use stereo-scopic viewing +#define D3DFW_ZBUFFER 0x00000004 // Create and use a zbuffer +#define D3DFW_NO_FPUSETUP 0x00000008 // Don't use default DDSCL_FPUSETUP flag +#define D3DFW_NOVSYNC 0x00000010 // Don't use vertical sync in fullscreen +#define D3DFW_PAGEFLIP 0x00000020 // Allow page flipping in fullscreen + + +//----------------------------------------------------------------------------- +// Errors that the Initialize() and ChangeDriver() calls may return +//----------------------------------------------------------------------------- +#define D3DFWERR_INITIALIZATIONFAILED 0x82000000 +#define D3DFWERR_NODIRECTDRAW 0x82000001 +#define D3DFWERR_COULDNTSETCOOPLEVEL 0x82000002 +#define D3DFWERR_NODIRECT3D 0x82000003 +#define D3DFWERR_NO3DDEVICE 0x82000004 +#define D3DFWERR_NOZBUFFER 0x82000005 +#define D3DFWERR_INVALIDZBUFFERDEPTH 0x82000006 +#define D3DFWERR_NOVIEWPORT 0x82000007 +#define D3DFWERR_NOPRIMARY 0x82000008 +#define D3DFWERR_NOCLIPPER 0x82000009 +#define D3DFWERR_BADDISPLAYMODE 0x8200000a +#define D3DFWERR_NOBACKBUFFER 0x8200000b +#define D3DFWERR_NONZEROREFCOUNT 0x8200000c +#define D3DFWERR_NORENDERTARGET 0x8200000d +#define D3DFWERR_INVALIDMODE 0x8200000e +#define D3DFWERR_NOTINITIALIZED 0x8200000f + +#endif // !vkFRAME_H diff --git a/OVP/VulkanClient/GDIPad.cpp b/OVP/VulkanClient/GDIPad.cpp new file mode 100644 index 000000000..3e9c9ef54 --- /dev/null +++ b/OVP/VulkanClient/GDIPad.cpp @@ -0,0 +1,275 @@ +// ============================================================== +// GDIClient.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006 - 2016 Martin Schweiger +// ============================================================== + +#include "GDIPad.h" +#include "Pad.h" +#include "Client.h" +#include "Surface.h" +#include "Util.h" +#include "Config.h" +#include "Log.h" + +using namespace oapi; + + +// =============================================================================================== +// class GDIPad +// =============================================================================================== + +GDIPad::GDIPad (SURFHANDLE s, HDC hdc): Sketchpad (s) +{ + LogOk("Creating GDI SketchPad... for Surface %s", _PTR(s)); + + hDC = hdc; + cfont = NULL; + cpen = NULL; + cbrush = NULL; + hFont0 = NULL; + hFontA = NULL; + + // Default initial drawing settings + SetBkMode (hDC, TRANSPARENT); // transparent text background + SelectObject(hDC, GetStockObject (NULL_PEN)); + SelectObject(hDC, GetStockObject (NULL_BRUSH)); +} + +// =============================================================================================== +// +GDIPad::~GDIPad () +{ + // make sure to deselect custom resources before destroying the DC + if (hFont0) SelectObject (hDC, hFont0); + SelectObject (hDC, GetStockObject (NULL_PEN)); + SelectObject (hDC, GetStockObject (NULL_BRUSH)); + if (hFontA) DeleteObject(hFontA); + LogOk("...GDI SketchPad Released for surface %s", _PTR(GetSurface())); +} + +// =============================================================================================== +// +HDC GDIPad::GetDC() +{ + return hDC; +} + +// =============================================================================================== +// +Font *GDIPad::SetFont (Font *font) +{ + Font *pfont = cfont; + if (font) { + HFONT hFont = (HFONT)SelectObject (hDC, static_cast(font)->hFont); + if (!cfont) hFont0 = hFont; + } else if (hFont0) { // restore original font + SelectObject (hDC, hFont0); + hFont0 = 0; + } + cfont = font; + return pfont; +} + +// =============================================================================================== +// +Pen *GDIPad::SetPen (Pen *pen) +{ + Pen *ppen = cpen; + if (pen) cpen = pen; + else cpen = NULL; + if (cpen) SelectObject (hDC, static_cast(cpen)->hPen); + else SelectObject (hDC, GetStockObject (NULL_PEN)); + return ppen; +} + +// =============================================================================================== +// +Brush *GDIPad::SetBrush (Brush *brush) +{ + Brush *pbrush = cbrush; + cbrush = brush; + if (brush) SelectObject (hDC, static_cast(cbrush)->hBrush); + else SelectObject (hDC, GetStockObject (NULL_BRUSH)); + return pbrush; +} + +// =============================================================================================== +// +void GDIPad::SetTextAlign (TAlign_horizontal tah, TAlign_vertical tav) +{ + UINT align = 0; + switch (tah) { + case LEFT: align |= TA_LEFT; break; + case CENTER: align |= TA_CENTER; break; + case RIGHT: align |= TA_RIGHT; break; + } + switch (tav) { + case TOP: align |= TA_TOP; break; + case BASELINE: align |= TA_BASELINE; break; + case BOTTOM: align |= TA_BOTTOM; break; + } + ::SetTextAlign (hDC, align); +} + +// =============================================================================================== +// +DWORD GDIPad::SetTextColor (DWORD col) +{ + return (DWORD)::SetTextColor (hDC, (COLORREF)(col&0xFFFFFF)); +} + +// =============================================================================================== +// +DWORD GDIPad::SetBackgroundColor (DWORD col) +{ + return (DWORD)SetBkColor (hDC, (COLORREF)(col&0xFFFFFF)); +} + +// =============================================================================================== +// +void GDIPad::SetBackgroundMode (BkgMode mode) +{ + int bkmode; + switch (mode) { + case BK_TRANSPARENT: bkmode = TRANSPARENT; break; + case BK_OPAQUE: bkmode = OPAQUE; break; + default: return; + } + SetBkMode (hDC, bkmode); +} + +// =============================================================================================== +// +DWORD GDIPad::GetCharSize () +{ + TEXTMETRIC tm; + GetTextMetrics (hDC, &tm); + return MAKELONG(tm.tmHeight-tm.tmInternalLeading, tm.tmAveCharWidth); +} + +// =============================================================================================== +// +DWORD GDIPad::GetTextWidth (const char *str, int len) +{ + if (str) if (str[0] == '_') if (strcmp(str, "_SkpVerInfo") == 0) return 1; + SIZE size; + if (!len) len = lstrlen(str); + GetTextExtentPoint32 (hDC, str, len, &size); + return (DWORD)size.cx; +} + +// =============================================================================================== +// +void GDIPad::SetOrigin (int x, int y) +{ + SetViewportOrgEx (hDC, x, y, NULL); +} + +// =============================================================================================== +// +void GDIPad::GetOrigin (int *x, int *y) const +{ + POINT point; + GetViewportOrgEx (hDC, &point); + if (x) *x = point.x; + if (y) *y = point.y; +} + +// =============================================================================================== +// +bool GDIPad::Text (int x, int y, const char *str, int len) +{ + return (TextOut (hDC, x, y, str, len) != FALSE); +} + +// =============================================================================================== +// +bool GDIPad::TextW (int x, int y, const LPWSTR str, int len) +{ + return (TextOutW(hDC, x, y, str, len) != FALSE); +} + +// =============================================================================================== +// +bool GDIPad::TextBox (int x1, int y1, int x2, int y2, const char *str, int len) +{ + RECT r; + r.left = x1; + r.top = y1; + r.right = x2; + r.bottom = y2; + return (DrawText (hDC, str, len, &r, DT_LEFT|DT_NOPREFIX|DT_WORDBREAK) != 0); +} + +// =============================================================================================== +// +void GDIPad::Pixel (int x, int y, DWORD col) +{ + SetPixel (hDC, x, y, (COLORREF)col); +} + +// =============================================================================================== +// +void GDIPad::MoveTo (int x, int y) +{ + MoveToEx (hDC, x, y, NULL); +} + +// =============================================================================================== +// +void GDIPad::LineTo (int x, int y) +{ + ::LineTo (hDC, x, y); +} + +// =============================================================================================== +// +void GDIPad::Line (int x0, int y0, int x1, int y1) +{ + MoveToEx (hDC, x0, y0, NULL); + ::LineTo (hDC, x1, y1); +} + +// =============================================================================================== +// +void GDIPad::Rectangle (int x0, int y0, int x1, int y1) +{ + ::Rectangle (hDC, x0, y0, x1, y1); +} + +// =============================================================================================== +// +void GDIPad::Ellipse (int x0, int y0, int x1, int y1) +{ + ::Ellipse (hDC, x0, y0, x1, y1); +} + +// =============================================================================================== +// +void GDIPad::Polygon (const IVECTOR2 *pt, int npt) +{ + ::Polygon (hDC, (const POINT*)pt, npt); +} + +// =============================================================================================== +// +void GDIPad::Polyline (const IVECTOR2 *pt, int npt) +{ + ::Polyline (hDC, (const POINT*)pt, npt); +} + +// =============================================================================================== +// +void GDIPad::PolyPolygon (const IVECTOR2 *pt, const int *npt, const int nline) +{ + ::PolyPolygon (hDC, (const POINT*)pt, npt, nline); +} + +// =============================================================================================== +// +void GDIPad::PolyPolyline (const IVECTOR2 *pt, const int *npt, const int nline) +{ + ::PolyPolyline (hDC, (const POINT*)pt, (const DWORD*)npt, nline); +} diff --git a/OVP/VulkanClient/GDIPad.h b/OVP/VulkanClient/GDIPad.h new file mode 100644 index 000000000..f66cab41e --- /dev/null +++ b/OVP/VulkanClient/GDIPad.h @@ -0,0 +1,278 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// ============================================================== + +#ifndef __GDIPAD_H +#define __GDIPAD_H + +#include "OrbiterAPI.h" +#include "Client.h" +#include +#include "MathAPI.h" + + +// ====================================================================== +// class GDIPad +// ====================================================================== +/** + * \brief The GDIPad class defines the context for 2-D drawing using + * Windows GDI calls. + */ +class GDIPad: public oapi::Sketchpad { +public: + + GDIPad (SURFHANDLE s, HDC hdc); + ~GDIPad (); + + /** + * \brief Selects a new font to use. + * \param font pointer to font resource + * \return Previously selected font. + * \sa GDIFont, GDIClient::clbkCreateFont + */ + oapi::Font *SetFont (oapi::Font *font); + + /** + * \brief Selects a new pen to use. + * \param pen pointer to pen resource, or NULL to disable outlines + * \return Previously selected pen. + * \sa GDIPen, GDIClient::clbkCreatePen + */ + oapi::Pen *SetPen (oapi::Pen *pen); + + /** + * \brief Selects a new brush to use. + * \param brush pointer to brush resource, or NULL to disable fill mode + * \return Previously selected brush. + * \sa GDIBrush, GDIClient::clbkCreateBrush + */ + oapi::Brush *SetBrush (oapi::Brush *brush); + + /** + * \brief Set horizontal and vertical text alignment. + * \param tah horizontal alignment + * \param tav vertical alignment + */ + void SetTextAlign (TAlign_horizontal tah=LEFT, TAlign_vertical tav=TOP); + + /** + * \brief Set the foreground colour for text output. + * \param col colour description (format: 0xBBGGRR) + * \return Previous colour setting. + */ + DWORD SetTextColor (DWORD col); + + /** + * \brief Set the background colour for text output. + * \param col background colour description (format: 0xBBGGRR) + * \return Previous colour setting + * \note The background colour is only used if the background mode + * is set to BK_OPAQUE. + * \sa SetBackgroundMode + */ + DWORD SetBackgroundColor (DWORD col); + + /** + * \brief Set the background mode for text output. + * \param mode background mode (see \ref BkgMode) + * \note In opaque background mode, the text background is drawn + * in the current background colour (see SetBackgroundColor). + * \note The default background mode (before the first call of + * SetBackgroundMode) should be transparent. + * \sa SetBackgroundColor, SetTextColor + */ + void SetBackgroundMode (BkgMode mode); + + /** brief Returns height and (average) width of a character in the currently + * selected font. + * \return Height of character cell [pixel] in the lower 16 bit of the return value, + * and (average) width of character cell [pixel] in the upper 16 bit. + * \note The height value is given by tmHeight-tmInternalLeading from the + * TEXTMETRIC structure returned by the GDI GetTextMetrics function. + * \note The width value is given by tmAveCharWidth from the + * TEXTMETRIC structure returned by the GDI GetTextMetrics function. + */ + DWORD GetCharSize (); + + /** + * \brief Returns the width of a text string in the currently selected font. + * \param str text string + * \param len string length, or 0 for auto (0-terminated string) + * \return width of the string, drawn in the currently selected font [pixel] + * \sa SetFont + */ + DWORD GetTextWidth (const char *str, int len = 0); + + /** + * \brief Set the position in the surface bitmap which is mapped to the + * origin of the coordinate system for all drawing functions. + * \param x horizontal position of the origin [pixel] + * \param y vertical position of the origin [pixel] + * \note By default, the reference point for drawing function coordinates is + * the top left corner of the bitmap, with positive x-axis to the right, + * and positive y-axis down. + * \note SetOrigin can be used to shift the logical reference point to a + * different position in the surface bitmap (but not to change the + * orientation of the axes). + */ + void SetOrigin (int x, int y); + + /** + * \brief Returns the position in the surface bitmap which is mapped to + * the origin of the coordinate system for all drawing functions. + * \param [out] x pointer to integer receiving horizontal position of the origin [pixel] + * \param [out] y pointer to integer receiving vertical position of the origin [pixel] + * \default Returns (0,0) + * \sa SetOrigin + */ + void GetOrigin (int *x, int *y) const; + + /** + * \brief Draws a text string. + * \param x reference x position [pixel] + * \param y reference y position [pixel] + * \param str text string + * \param len string length for output + * \return \e true on success, \e false on failure. + */ + bool Text (int x, int y, const char *str, int len); + + /** + * \brief Draw a text string into a rectangle. + * \param x1 left edge [pixel] + * \param y1 top edge [pixel] + * \param x2 right edge [pixel] + * \param y2 bottom edge [pixel] + * \param str text string + * \param len string length for output + * \return \e true on success, \e false on failure. + */ + bool TextBox (int x1, int y1, int x2, int y2, const char *str, int len); + + /** + * \brief Draws a single pixel in a specified colour. + * \param x x-coordinate of point [pixel] + * \param y y-coordinate of point [pixel] + * \param col pixel colour (format: 0xBBGGRR) + */ + void Pixel (int x, int y, DWORD col); + + /** + * \brief Moves the drawing reference to a new point. + * \param x x-coordinate of new reference point [pixel] + * \param y y-coordinate of new reference point [pixel] + * \note Some methods use the drawing reference point for + * drawing operations, e.g. \ref LineTo. + * \sa LineTo + */ + void MoveTo (int x, int y); + + /** + * \brief Draws a line to a specified point. + * \param x x-coordinate of line end point [pixel] + * \param y y-coordinate of line end point [pixel] + * \note The line starts at the current drawing reference + * point. + * \sa MoveTo + */ + void LineTo (int x, int y); + + /** + * \brief Draws a line between two points. + * \param x0 x-coordinate of first point [pixel] + * \param y0 y-coordinate of first point [pixel] + * \param x1 x-coordinate of second point [pixel] + * \param y1 y-coordinate of second point [pixel] + * \note The line is drawn with the currently selected pen. + * \sa SetPen + */ + void Line (int x0, int y0, int x1, int y1); + + /** + * \brief Draw a rectangle (filled or outline). + * \param x0 left edge of rectangle [pixel] + * \param y0 top edge of rectangle [pixel] + * \param x1 right edge of rectangle [pixel] + * \param y1 bottom edge of rectangle [pixel] + * \note The rectangle is filled with the currently selected + * brush resource. + * \sa Ellipse + */ + void Rectangle (int x0, int y0, int x1, int y1); + + /** + * \brief Draw an ellipse from its bounding box. + * \param x0 left edge of bounding box [pixel] + * \param y0 top edge of bounding box [pixel] + * \param x1 right edge of bounding box [pixel] + * \param y1 bottom edge of bounding box [pixel] + * \note The ellipse is filled with the currently selected + * brush resource. + * \sa Rectangle + */ + void Ellipse (int x0, int y0, int x1, int y1); + + /** + * \brief Draw a closed polygon given by vertex points. + * \param pt list of vertex points + * \param npt number of points in the list + * \note The polygon is outlined with the current pen, and + * filled with the current brush. + * \note The polygon is closed, i.e. the last point is + * joined with the first one. + * \sa Polyline, PolyPolygon, Rectangle, Ellipse + */ + void Polygon (const oapi::IVECTOR2 *pt, int npt); + + /** + * \brief Draw a line of piecewise straight segments. + * \param pt list of vertex points + * \param npt number of points in the list + * \note The line is drawn with the currently selected pen. + * \note Polylines are open figures: the end points are + * not connected, and no fill operation is performed. + * \sa Polygon, PolyPolyline Rectangle, Ellipse + */ + void Polyline (const oapi::IVECTOR2 *pt, int npt); + + /** + * \brief Draw a set of polygons. + * \param pt list of vertex points for all polygons + * \param npt list of number of points for each polygon + * \param nline number of polygons + * \note The number of entries in npt must be >= nline, and + * the number of points in pt must be at least the sum of + * the values in npt. + * \sa Polygon, Polyline, PolyPolyline + */ + void PolyPolygon (const oapi::IVECTOR2 *pt, const int *npt, const int nline); + + /** + * \brief Draw a set of polylines. + * \param pt list of vertex points for all lines + * \param npt list of number of points for each line + * \param nline number of lines + * \note The number of entries in npt must be >= nline, and + * the number of points in pt must be at least the sum of + * the values in npt. + * \sa Polyline, Polygon, PolyPolygon + */ + void PolyPolyline (const oapi::IVECTOR2 *pt, const int *npt, const int nline); + + HDC GetDC(); + + bool TextW(int x, int y, const LPWSTR str, int len); + +private: + HDC hDC; + HFONT hFontA; + + HFONT hFont0; // original GDI font + oapi::Font *cfont; // currently selected font (NULL if none) + oapi::Pen *cpen; // currently selected pen (NULL if none) + oapi::Brush *cbrush; // currently selected brush (NULL if none) +}; + +#endif // !__GDICLIENT_H diff --git a/OVP/VulkanClient/GPL.txt b/OVP/VulkanClient/GPL.txt new file mode 100644 index 000000000..85b8e5992 --- /dev/null +++ b/OVP/VulkanClient/GPL.txt @@ -0,0 +1,620 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + diff --git a/OVP/VulkanClient/HazeMgr.cpp b/OVP/VulkanClient/HazeMgr.cpp new file mode 100644 index 000000000..bc35b5d16 --- /dev/null +++ b/OVP/VulkanClient/HazeMgr.cpp @@ -0,0 +1,531 @@ +// ============================================================================ +// HazeMgr.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007 - 2016 Martin Schweiger +// 2011 - 2016 Jarmo Nikkanen +// ============================================================================ + +// ============================================================================ +// class HazeManager (implementation) +// +// Planetary atmospheric haze rendering +// Implemented as transparent overlay on planetary disc +// ============================================================================ + +#include "HazeMgr.h" +#include "VPlanet.h" +#include "Surface.h" +#include "Util.h" +#include "VectorHelpers.h" +#include "Effect.h" +#include "Config.h" + +using namespace oapi; + +HazeManager::HazeManager (const vkClient *gclient, const vPlanet *vplanet) : vkEffect() +{ + vp = vplanet; + obj = vp->Object(); + rad = oapiGetSize (obj); + const ATMCONST *atmc = oapiGetPlanetAtmConstants (obj); + if (atmc) { + basecol = *(VECTOR3*)oapiGetObjectParam (obj, OBJPRM_PLANET_HAZECOLOUR); + hralt = (float)(atmc->horizonalt / rad); + dens0 = (float)(min (1.0, atmc->horizonalt/64e3 * *(double*)oapiGetObjectParam(obj, OBJPRM_PLANET_HAZEDENSITY))); + } else { + basecol = _V(1,1,1); + hralt = 0.01f; + dens0 = 1.0f; + } + if (*(bool*)oapiGetObjectParam (obj, OBJPRM_PLANET_HASCLOUDS)) { + hshift = *(double*)oapiGetObjectParam (obj, OBJPRM_PLANET_HAZESHIFT); + cloudalt = *(double*)oapiGetObjectParam (obj, OBJPRM_PLANET_CLOUDALT); + } else + hshift = 0; + hscale = (float)(1.0 - *(double*)oapiGetObjectParam (obj, OBJPRM_PLANET_HAZEEXTENT)); +} + +// ----------------------------------------------------------------------- + +void HazeManager::GlobalInit(vkClient *gclient) +{ + int i; + for (i = 0; i < HORIZON_NSEG; i++) Idx[i*2] = i*2+1, Idx[i*2+1] = i*2; + Idx[i*2] = 1, Idx[i*2+1] = 0; + + for (i = 0; i < HORIZON_NSEG; i++) { + Vtx[i*2].tu = Vtx[i*2+1].tu = (float)(i%2); + Vtx[i*2].tv = 1.0f; + Vtx[i*2+1].tv = 0.0f; + double phi = (double)i/(double)HORIZON_NSEG * PI*2.0; + CosP[i] = (float)cos(phi), SinP[i] = (float)sin(phi); + } + horizon = gclient->clbkLoadTexture("Horizon.dds"); +} + +// ----------------------------------------------------------------------- + +void HazeManager::GlobalExit() +{ + DELETE_SURFACE(horizon); +} + +// ----------------------------------------------------------------------- + +void HazeManager::Render(LPDIRECT3DDEVICE9 pDev, FMATRIX4 &wmat, bool dual) +{ + FMATRIX4 imat, transm; + + VECTOR3 psun; + int i, j; + double phi, csun, alpha, colofs; + float cosp, sinp, cost, sint, h1, h2, r1, r2, intr, intg, intb; + + oapiMatrixInverse(&imat, NULL, &wmat); + + VECTOR3 rpos = {imat.m41, imat.m42, imat.m43}; // camera in local coords (planet radius = 1) + double cdist = length (rpos); + + alpha = dens0 * min (1.0, (cdist-1.0)*200.0); + if (!dual) alpha = 1.0-alpha; + if (alpha <= 0.0) return; // nothing to do + + // Problem: the top part of horizon haze is rendered twice + if (dual && cdist<1.001) return; // Enabled 04.06.2011 + + VECTOR3 cpos = {0,cdist,0}; + double id = 1.0 / max (cdist, 1.001); + double visrad = acos (id); // aperture of visibility sector + double sinv = sin(visrad); + + h1 = (float)id; + h2 = h1 + (float)(hralt*id); + r1 = (float)sinv, r2 = (1.0f+hralt)*r1; + + if (!dual) { // pull lower horizon edge below surface to avoid problems with elevations < 0 + h1 *= (float)(vp->prm.horizon_minrad); + r1 *= (float)(vp->prm.horizon_minrad); + } + + if (hshift) { + if (cdist-1.0 > cloudalt/rad) { + float dr = (float)(hshift*sinv); + float dh = (float)(hshift*id); + h1 += dh, h2 += dh; + r1 += dr, r2 += dr; + } + } + + float dens = (float)max (1.0, 1.4 - 0.3/hralt*(cdist-1.0)); // saturate haze colour at low altitudes + if (dual) dens *= (float)(0.5 + 0.5/cdist); // scale down intensity at large distances + + normalise (rpos); + cost = (float)rpos.y, sint = (float)sqrt (1.0-cost*cost); + phi = atan2 (rpos.z, rpos.x), cosp = (float)cos(phi), sinp = (float)sin(phi); + + FMATRIX4 rmat = FMATRIX4(cost*cosp, -sint, cost*sinp, 0, + sint*cosp, cost, sint*sinp, 0, + -sinp, 0, cosp, 0, + 0, 0, 0, 1); + + oapiMatrixMultiply(&transm, &rmat, &wmat); + + MATRIX3 rrmat = {cost*cosp, -sint, cost*sinp, + sint*cosp, cost, sint*sinp, + -sinp, 0, cosp }; + MATRIX3 grot; + VECTOR3 gpos; + oapiGetRotationMatrix (obj, &grot); + oapiGetGlobalPos (obj, &gpos); + psun = tmul (grot, -gpos); // sun in planet coords + psun = mul (rrmat, psun); // sun in camera-relative horizon coords + VECTOR3 cs = psun-cpos; normalise(cs); // camera->sun + normalise (psun); + // float psunx = (float)psun.x, psuny = (float)psun.y, psunz = (float)psun.z; + + colofs = (dual ? 0.4 : 0.3); + + for (i = j = 0; i < HORIZON_NSEG; i++) { + VECTOR3 hp = {Vtx[j].x = r1*CosP[i], Vtx[j].y = h1, Vtx[j].z = r1*SinP[i]}; + csun = dotp (hp, psun); + VECTOR3 cp = hp-cpos; normalise(cp); + double colsh = 0.5*(dotp (cp,cs) + 1.0); + + // compose a colourful sunset + double maxred = colofs-0.18*colsh, minred = maxred-0.4; + double maxgreen = colofs-0.1*colsh, mingreen = maxgreen-0.4; + double maxblue = colofs/*+0.0*colsh*/, minblue = maxblue-0.4; + if (csun > maxred) intr = 1.0f; + else if (csun < minred) intr = 0.0f; + else intr = (float)((csun-minred)*2.5); + if (csun > maxgreen) intg = 1.0f; + else if (csun < mingreen) intg = 0.0f; + else intg = (float)((csun-mingreen)*2.5); + if (csun > maxblue) intb = 1.0f; + else if (csun < minblue) intb = 0.0f; + else intb = (float)((csun-minblue)*2.5); + + FVECTOR4 col = FVECTOR4(intr*min(1.0f,dens*(float)basecol.x), intg*min(1.0f,dens*(float)basecol.y), intb*min(1.0f,dens*(float)basecol.z), (float)alpha); + //FVECTOR4 col = FVECTOR4(intr*min(1.0f,dens*(float)basecol.x), intg*min(1.0f,dens*(float)basecol.y), intb*min(1.0f,dens*(float)basecol.z), 1.0f); + + Vtx[j].dcol = col.dword_abgr(); + j++; + Vtx[j].x = r2*CosP[i]; + Vtx[j].y = h2; + Vtx[j].z = r2*SinP[i]; + Vtx[j].dcol = col.dword_abgr(); + j++; + } + + HR(FX->SetTechnique(eHazeTech)); + HR(FX->SetMatrix(eW, _DX(transm))); + HR(FX->SetTexture(eTex0, SURFACE(horizon)->GetTexture())); + + HR(pDev->SetVertexDeclaration(pHazeVertexDecl)); + + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + + pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLESTRIP, 0, 2*HORIZON_NSEG, 2*HORIZON_NSEG, Idx, D3DFMT_INDEX16, Vtx, sizeof(HVERTEX)); + + if (dual) { + + h2 = h1; + r2 = hscale * r1*r1; + + for (i = j = 0; i < HORIZON_NSEG; i++) { + j++; + Vtx[j].x = r2*CosP[i]; + Vtx[j].y = h2; + Vtx[j].z = r2*SinP[i]; + j++; + } + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); + pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLESTRIP,0, 2*HORIZON_NSEG, 2*HORIZON_NSEG, Idx, D3DFMT_INDEX16, Vtx, sizeof(HVERTEX)); + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + } + + HR(FX->EndPass()); + HR(FX->End()); +} + +// ============================================================================ +// static member initialisation + +WORD HazeManager::Idx[HORIZON_NSEG*2+2]; +struct HazeManager::HVERTEX HazeManager::Vtx[HORIZON_NSEG*2]; +DWORD HazeManager::nIdx = HORIZON_NSEG*2+2; +float HazeManager::CosP[HORIZON_NSEG]; +float HazeManager::SinP[HORIZON_NSEG]; + +SURFHANDLE HazeManager::horizon = 0; + + + + + + + + + + + + +// ============================================================================ +// class HazeManager2 (implementation) +// +// Planetary atmospheric haze rendering with scattering technique +// ============================================================================ + +HazeManager2::HazeManager2(vPlanet *vplanet) +{ + vp = vplanet; + obj = vp->Object(); + rad = oapiGetSize(obj); +} + +// ----------------------------------------------------------------------- + +HazeManager2::~HazeManager2() +{ + +} + +// ----------------------------------------------------------------------- + +void HazeManager2::GlobalInit(vkClient *gclient) +{ + pDev = gclient->GetDevice(); + pNoise = gclient->GetNoiseTex(); + for (int i=0;i<6;i++) pSkyVB[i]=NULL; + CreateRingBuffers(); + CreateSkydomeBuffers(0); + CreateSkydomeBuffers(1); + CreateSkydomeBuffers(2); + CreateSkydomeBuffers(3); + CreateSkydomeBuffers(4); + CreateSkydomeBuffers(5); + + pDome = new ShaderClass(pDev, "Modules/vkShaders/NewPlanet.hlsl", "HorizonVS", "HorizonPS", "Dome", NULL); + pRing = new ShaderClass(pDev, "Modules/vkShaders/NewPlanet.hlsl", "HorizonVS", "HorizonRingPS", "Ring", NULL); +} + +// ----------------------------------------------------------------------- + +void HazeManager2::GlobalExit() +{ + for (int i=0;i<6;i++) { SAFE_RELEASE(pSkyVB[i]); } + SAFE_RELEASE(pRingVB); + SAFE_DELETE(pDome); + SAFE_DELETE(pRing); +} + +// ----------------------------------------------------------------------- + +void HazeManager2::Render(FMATRIX4 &wmat, float horizontal_aperture_deg) +{ + Scene* scn = vp->GetScene(); + VECTOR3 cdir = scn->GetCameraGDir(); + double calt = vp->CamDist() - rad; // Camera altitude + double halt = vp->GetHorizonAlt(); + double melv = vp->GetMinElevation(); + + melv -= 1000.0; + + if (calt>halt) RenderRing(vp->PosFromCamera(), cdir, rad+melv, halt); + else RenderSky(vp->PosFromCamera(), cdir, rad+melv, horizontal_aperture_deg); +} + +// ----------------------------------------------------------------------- + +void HazeManager2::RenderSky(VECTOR3 cpos, VECTOR3 cdir, double rad, double apr) +{ + cpos = -cpos; + + double cr = length(cpos); if (cr<(rad+100.0)) cr=rad+100.0; + double hd = sqrt(cr*cr - rad*rad) * 10.0; + double al = asin(rad/cr); + + VECTOR3 ur = unit(cpos); + VECTOR3 ux = unit(crossp(cdir, ur)); + VECTOR3 uy = unit(crossp(ur, ux)); + + FMATRIX4 mWL, mL; + oapiMatrixIdentity(&mWL); + D3DMAT_FromAxisT(&mWL, ptr(_F(ux)), ptr(_F(ur)), ptr(_F(uy))); + + double a = 15.0*RAD; + double b = (PI-asin(rad/cr))/6.0; + + FVECTOR3 vTileCenter = FVECTOR3(float(sin(15.0*RAD)), 1.0f, float(1.0+cos(15.0*RAD))) * 0.5; + oapiMatrixRotationAxis(&mL, ptr(_F(ur)), float(-a*0.5)); + oapiMatrixMultiply(&mWL, &mWL, &mL); + oapiMatrixRotationAxis(&mL, ptr(_F(ur)), float(-a)); + + + //vp->GetScatterConst()->mVP = vp->GetScene()->PushCameraFrustumLimits(hd * 0.1, hd * 5.0); + + pDome->Setup(pPositionDecl, false, 2); + pDome->ClearTextures(); + + pDome->SetTexture("tSkyRayColor", vp->GetScatterTable(RAY_COLOR), IPF_LINEAR | IPF_CLAMP); + pDome->SetTexture("tSkyMieColor", vp->GetScatterTable(MIE_COLOR), IPF_LINEAR | IPF_CLAMP); + pDome->SetTexture("tGlare", vp->GetScene()->GetSunGlareAtm(), IPF_LINEAR | IPF_CLAMP); + pDome->SetTexture("tNoise", pNoise, IPF_POINT | IPF_WRAP); + pDome->SetPSConstants("Const", vp->GetScatterConst(), sizeof(ConstParams)); + pDome->SetVSConstants("Const", vp->GetScatterConst(), sizeof(ConstParams)); + pDome->UpdateTextures(); + + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + + for (int i=0;i<24;i++) { + double x = al; + oapiMatrixMultiply(&mWL, &mWL, &mL); + for (int j=0;j<6;j++) { + float r1 = float(sin(x)); float h1 = -float(cos(x)); + float r2 = float(sin(x+b)); float h2 = -float(cos(x+b)); + FVECTOR3 vCnt = vTileCenter * FVECTOR3((r1+r2)*0.5f, 1.0f, (r1+r2)*0.5f); vCnt.y = (h1+h2)*0.5f; + vCnt = oapiTransformCoord(&vCnt, &mWL); + if (vp->GetScene()->IsVisibleInCamera(&vCnt, float(sin(a*0.5)*1.5))) RenderSkySegment(mWL, hd, x, x+b, j); + x+=b; + } + } + + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + + //vp->GetScatterConst()->mVP = vp->GetScene()->PopCameraFrustumLimits(); +} + +// ----------------------------------------------------------------------- + +void HazeManager2::RenderSkySegment(FMATRIX4 &wmat, double rad, double dmin, double dmax, int index) +{ + float r1 = float(rad * sin(dmin)); + float h1 = -float(rad * cos(dmin)); + float r2 = float(rad * sin(dmax)); + float h2 = -float(rad * cos(dmax)); + + ShaderParams sprm; + memcpy_s(&sprm.mWorld, sizeof(sprm.mWorld), &wmat, sizeof(wmat)); + sprm.vTexOff = FVECTOR4(r1, r2, h1, h2); + + int xres = xreslvl[index]; + int yres = yreslvl[index]; + + UINT prims = xres * yres * 2 - 2; + + pDome->SetVSConstants("Prm", &sprm, sizeof(ShaderParams)); + pDev->SetStreamSource(0, pSkyVB[index], 0, sizeof(FVECTOR3)); + pDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, prims); +} + + +// ----------------------------------------------------------------------- + +void HazeManager2::RenderRing(VECTOR3 cpos, VECTOR3 cdir, double rad, double hralt) +{ + cpos = -cpos; + double cr = length(cpos); + double hd = sqrt(cr*cr - rad*rad); + double al = asin(rad/cr); + double mx = hralt * 4.0; + + double r1 = hd * sin(al); + double h1 = -hd * cos(al); + double qw = (hralt + (cr-rad)) * 0.8; + if (qw>mx) qw=mx; + + double r2 = r1 + qw * cos(al); + double h2 = h1 + qw * sin(al); + + vp->GetScatterConst()->mVP = vp->GetScene()->PushCameraFrustumLimits(hd * 0.1, hd * 5.0); + + VECTOR3 ur = unit(cpos); + VECTOR3 ux = unit(crossp(cdir, ur)); + VECTOR3 uy = unit(crossp(ur, ux)); + + FMATRIX4 mW; + oapiMatrixIdentity(&mW); + D3DMAT_FromAxisT(&mW, ptr(_F(ux)), ptr(_F(ur)), ptr(_F(uy))); + + ShaderParams sprm; + pRing->Setup(pPositionDecl, false, 2); + pRing->ClearTextures(); + vp->InitEclipse(pRing); + + memcpy_s(&sprm, sizeof(ShaderParams), vp->GetTerrainParams(), sizeof(ShaderParams)); + memcpy_s(&sprm.mWorld, sizeof(sprm.mWorld), &mW, sizeof(mW)); + + sprm.vTexOff = FVECTOR4(r1, r2, h1, h2); + sprm.fAlpha = float(qw); + + pRing->SetTexture("tSkyRayColor", vp->GetScatterTable(RAY_COLOR), IPF_LINEAR | IPF_CLAMP); + pRing->SetTexture("tSkyMieColor", vp->GetScatterTable(MIE_COLOR), IPF_LINEAR | IPF_CLAMP); + pRing->SetPSConstants("Const", vp->GetScatterConst(), sizeof(ConstParams)); + pRing->SetVSConstants("Const", vp->GetScatterConst(), sizeof(ConstParams)); + pRing->SetVSConstants("Prm", &sprm, sizeof(ShaderParams)); + pRing->SetPSConstants("Prm", &sprm, sizeof(ShaderParams)); + pRing->SetPSConstants("Flow", vp->GetFlowControl(), sizeof(FlowControlPS)); + pRing->UpdateTextures(); + + UINT nPrims = HORIZON2_NSEG * HORIZON2_NRING * 2 - 2; + + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + pDev->SetStreamSource(0, pRingVB, 0, sizeof(FVECTOR3)); + pDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, nPrims); + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + + // Pop previous frustum configuration, must initialize mVP + vp->GetScatterConst()->mVP = vp->GetScene()->PopCameraFrustumLimits(); +} + + +// ----------------------------------------------------------------------- + +void HazeManager2::CreateRingBuffers() +{ + int v = 0; + int nvrt = HORIZON2_NSEG * 2 * HORIZON2_NRING + 2; + + FVECTOR3 *pVrt = new FVECTOR3[nvrt]; + FVECTOR3 *pBuf = NULL; + + float d = 1.0f/float(HORIZON2_NRING); + double phi = 0.0; + double dphi = PI2/double(HORIZON2_NSEG-1); + float x = float(cos(phi)); + float z = float(sin(phi)); + float y = 0.0f; + + for (int k=0;kCreateVertexBuffer(v*sizeof(FVECTOR3), 0, 0, D3DPOOL_DEFAULT, &pRingVB, NULL)); + + if (pRingVB->Lock(0, 0, (void **)&pBuf,0)==S_OK) { + memcpy(pBuf, pVrt, v*sizeof(FVECTOR3)); + pRingVB->Unlock(); + } + + delete []pVrt; + pVrt = NULL; +} + +// ----------------------------------------------------------------------- +void HazeManager2::CreateSkydomeBuffers(int index) +{ + int k = 0; + + int xseg = xreslvl[index]; + int yseg = yreslvl[index]; + + FVECTOR3 *pVrt = new FVECTOR3[xseg*yseg*2+2]; + FVECTOR3 *pBuf = NULL; + + double sa = 0.0, ca = 1.0; + double db = 1.0/double(yseg); + double dc = (1.0-cos(15.0*RAD))/double(xseg-1); + double ds = sin(15.0*RAD)/double(xseg-1); + double b = 0.0; + + for (int s=0;sCreateVertexBuffer(k*sizeof(FVECTOR3), 0, 0, D3DPOOL_DEFAULT, &pSkyVB[index], NULL)); + + if (pSkyVB[index]->Lock(0, 0, (void **)&pBuf,0)==S_OK) { + memcpy(pBuf, pVrt, k*sizeof(FVECTOR3)); + pSkyVB[index]->Unlock(); + } + + delete []pVrt; + pVrt = NULL; +} + +// ----------------------------------------------------------------------- + +ShaderClass* HazeManager2::pDome; +ShaderClass* HazeManager2::pRing; +LPDIRECT3DDEVICE9 HazeManager2::pDev; +LPDIRECT3DTEXTURE9 HazeManager2::pNoise; +LPDIRECT3DVERTEXBUFFER9 HazeManager2::pSkyVB[6]; +LPDIRECT3DVERTEXBUFFER9 HazeManager2::pRingVB = NULL; +int HazeManager2::xreslvl[6] = {9,6,5,4,3,2}; +int HazeManager2::yreslvl[6] = {11,8,6,5,5,4}; + diff --git a/OVP/VulkanClient/HazeMgr.h b/OVP/VulkanClient/HazeMgr.h new file mode 100644 index 000000000..4679a0804 --- /dev/null +++ b/OVP/VulkanClient/HazeMgr.h @@ -0,0 +1,117 @@ +// ============================================================== +// HazeMgr.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +// ============================================================== +// class HazeManager (interface) +// +// Planetary atmospheric haze rendering +// Implemented as transparent overlay on planetary disc +// ============================================================== + +#ifndef __HAZEMGR_H +#define __HAZEMGR_H + +#include "Client.h" +#include "Effect.h" + +#define HORIZON_NSEG 32 // number of mesh segments +#define HORIZON2_NSEG 200 // Horizon ring segments +#define HORIZON2_NRING 5 // Horizon ring ring count + +class vPlanet; + +class HazeManager : private vkEffect +{ +public: + /** + * \brief Constructs a new haze manager object + * \param gclient pointer to graphics client + * \param vPlanet planet instance pointer + */ + HazeManager (const oapi::vkClient *gclient, const vPlanet *vplanet); + + /** + * \brief Set up global parameters shared by all instances + * \param gclient client instance pointer + */ + static void GlobalInit (oapi::vkClient *gclient); + + /** + * \brief Release global parameters + */ + static void GlobalExit(); + + void Render (LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, bool dual = false); + +private: + OBJHANDLE obj; + const vPlanet *vp; + VECTOR3 basecol; + double rad; // planet radius + float hralt; // relative horizon altitude + float dens0; // atmosphere density factor + double hshift; // horizon reference shift factor + double cloudalt; // cloud layer altitude + float hscale; // inner haze ring radius (in planet radii) + static WORD Idx[HORIZON_NSEG*2+2]; + static DWORD nIdx; + static struct HVERTEX { + float x,y,z; + DWORD dcol; + float tu, tv; } Vtx[HORIZON_NSEG*2]; + static float CosP[HORIZON_NSEG], SinP[HORIZON_NSEG]; + static SURFHANDLE horizon; +}; + + + +// ============================================================== +// class HazeManager2 (interface) +// +// Planetary atmospheric haze rendering with scattering technique +// HazeManager2 is used with TileManager2 +// ============================================================== + +class HazeManager2 +{ +public: + HazeManager2 (vPlanet *vplanet); + ~HazeManager2(); + + + static void GlobalInit (oapi::vkClient *gclient); + static void GlobalExit(); + static void CreateSkydomeBuffers(int index); + static void CreateRingBuffers(); + + void Render(FMATRIX4 &wmat, float hz_aperture_deg); + +private: + + void RenderRing(VECTOR3 cpos, VECTOR3 cdir, double rad, double hralt); + void RenderSky(VECTOR3 cpos, VECTOR3 cdir, double rad, double apr); + void RenderSkySegment(FMATRIX4 &wmat, double rad, double dmin, double dmax, int index); + + OBJHANDLE obj; + vPlanet *vp; + double rad; + + static ShaderClass* pRing; + static ShaderClass* pDome; + static LPDIRECT3DDEVICE9 pDev; + static LPDIRECT3DTEXTURE9 pNoise; + static int xreslvl[6]; + static int yreslvl[6]; + static LPDIRECT3DVERTEXBUFFER9 pSkyVB[6]; + static LPDIRECT3DVERTEXBUFFER9 pRingVB; +}; + + + + +#endif // !__HAZEMGR_H diff --git a/OVP/VulkanClient/IProcess.cpp b/OVP/VulkanClient/IProcess.cpp new file mode 100644 index 000000000..dfea2cc28 --- /dev/null +++ b/OVP/VulkanClient/IProcess.cpp @@ -0,0 +1,829 @@ + +// =================================================== +// Copyright (C) 2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + +#include +#include "IProcess.h" +#include "Util.h" +#include "Surface.h" +#include + + +// ================================================================================================ +// +ImageProcessing::ImageProcessing(LPDIRECT3DDEVICE9 pDev, const char *_file, const char *_psentry, const char *_ppf, const char *_vsentry) + : pDevice(pDev) + , pVSConst(NULL) + , pPSConst(NULL) + , pDepth(NULL) + , pDepthBak(NULL) + , pMesh(NULL) + , hPos(NULL) + , hSiz(NULL) + , hVP(NULL) + , mesh_cull(gcIPInterface::ipicull::None) + , desc() + , iVP() + , mesh_tex_idx(-1) +{ + for (int i=0;iGetConstantByName(NULL, "mVP"); + hPos = pVSConst->GetConstantByName(NULL, "vPos"); + hSiz = pVSConst->GetConstantByName(NULL, "vTgtSize"); + SetTemplate(); + } + + if (!hVP || !hPos) LogErr("Failed to get ImageProcessing::hVP handle"); + + double w = 22.5 * RAD; + double q = w; + double r = 1.0 / cos(w); + + pOcta[0].x = 0.0f; + pOcta[0].y = 0.0f; + pOcta[0].z = 0.0f; + pOcta[0].tu = 0.0f; + pOcta[0].tv = 0.0f; + + for (int i = 1; i < 10; i++) { + pOcta[i].x = float(cos(q) * r); + pOcta[i].y = float(sin(q) * r); + pOcta[i].z = 0.0f; + pOcta[i].tu = pOcta[i].x; + pOcta[i].tv = pOcta[i].y; + q += w*2.0; + } + + strcpy_s(file, 256, _file); + strcpy_s(entry, 32, _psentry); + if (_ppf) strcpy_s(ppf, 256, _ppf); + else strcpy_s(ppf, 32, ""); + + // Create a database of defines ---------------------------------------------------------------- + std::string line; + std::ifstream fs(_file); + while (std::getline(fs, line)) { + if (!line.length() || line.find("//") == 0) continue; + if (line.find("#define") == 0) def.push_front(line.substr(line.find("#define") + 8)); + } + fs.close(); +} + + +// ================================================================================================ +// +ImageProcessing::~ImageProcessing() +{ + SAFE_RELEASE(pVSConst); + SAFE_RELEASE(pVertex); + SAFE_DELETEA(pOcta); + + for (auto x : Shaders) { + SAFE_RELEASE(x.second.pPixel); + SAFE_RELEASE(x.second.pPSConst); + } + Shaders.clear(); +} + + +// ================================================================================================ +// +bool ImageProcessing::CompileShader(const char *Entry) +{ + string name(Entry); + LPD3DXCONSTANTTABLE pPSC = NULL; + Shaders[name].pPixel = CompilePixelShader(pDevice, file, Entry, "IPIPS2", ppf, &pPSC); + Shaders[name].pPSConst = pPSC; + return ((Shaders[name].pPixel != NULL) && (Shaders[name].pPSConst != NULL)); +} + + +// ================================================================================================ +// +bool ImageProcessing::Activate(const char *Entry) +{ + SetTemplate(); + if (!Entry) return Activate(entry); + string name(Entry); + if (Shaders.count(name) == 0) { + LogErr("ImageProcessing::Activate() FAILED Entry=%s", Entry); + return false; + } + strcpy_s(entry, 31, Entry); + pPixel = Shaders[name].pPixel; + pPSConst = Shaders[name].pPSConst; + return true; +} + + +// ================================================================================================ +// +int ImageProcessing::FindDefine(const char *_key) +{ + int retval; + std::string key; + auto it = def.begin(); + while (it!=def.end()) { + std::istringstream iss(*it); + iss >> key >> retval; + if (key.compare(_key) == 0) return retval; + it++; + } + return 0; +} + +// ================================================================================================ +// +bool ImageProcessing::SetupViewPort() +{ + + // Check that the first render target is valid + // + if (pRtg[0]) pRtg[0]->GetDesc(&desc); + else { + LogErr("ImageProcessing(%s): No render target is set", _PTR(this)); + return false; + } + + D3DSURFACE_DESC ds; + + // Check that all additional render targets have the same size + // + for (int i=1;i<4;i++) { + if (pRtg[i]) { + pRtg[i]->GetDesc(&ds); + if ((ds.Height!=desc.Height) || (ds.Width!=desc.Width)) { + LogErr("ImageProcessing(%s): All render targets must be same the size", _PTR(this)); + return false; + } + } + else break; + } + + // Setup view-projection matrix and viewport + // + D3DMAT_OrthoOffCenterLH(&mVP, 0.0f, (float)desc.Width, (float)desc.Height, 0.0f, 0.0f, 1.0f); + + iVP.X = 0; + iVP.Y = 0; + iVP.Width = desc.Width; + iVP.Height = desc.Height; + iVP.MinZ = 0.0f; + iVP.MaxZ = 1.0f; + + HR(pDevice->SetViewport(&iVP)); + HR(pVSConst->SetMatrix(pDevice, hVP, _DX(mVP))); + HR(pVSConst->SetVector(pDevice, hSiz, _DX(FVECTOR4(float(desc.Width), float(desc.Height), 1.0f/float(desc.Width), 1.0f/float(desc.Height))))); + HR(pVSConst->SetVector(pDevice, hPos, _DX(vTemplate))); + return true; +} + + +// ================================================================================================ +// +void ImageProcessing::SetTemplate(float w, float h, float x, float y) +{ + vTemplate = FVECTOR4(w, h, x, y); +} + + +// ================================================================================================ +// +void ImageProcessing::SetMesh(const MESHHANDLE hMesh, const char *tex, gcIPInterface::ipicull cull) +{ + pMesh = GetSketchMesh(hMesh); + + mesh_cull = cull; + + if (tex) { + D3DXHANDLE hVar = pPSConst->GetConstantByName(NULL, tex); + if (!hVar) { + LogErr("IPInterface::SetSketchMesh() Invalid variable name [%s]", tex); + return; + } + mesh_tex_idx = pPSConst->GetSamplerIndex(hVar); + } + else mesh_tex_idx = -1; +} + + +// ================================================================================================ +// +bool ImageProcessing::Execute(bool bInScene) +{ + return Execute(0, bInScene, gcIPInterface::ipitemplate::Rect); +} + + +// ================================================================================================ +// +bool ImageProcessing::Execute(const char *shader, bool bInScene, DWORD blendop) +{ + Activate(shader); + return Execute(blendop, bInScene, gcIPInterface::ipitemplate::Rect); +} + + +// ================================================================================================ +// +bool ImageProcessing::Execute(DWORD blendop, bool bInScene, gcIPInterface::ipitemplate mode, int grp) +{ + if (!IsOK()) return false; + if (!SetupViewPort()) return false; + + // Set device state ------------------------------------------------------- + // + HR(pDevice->SetVertexShader(pVertex)); + HR(pDevice->SetPixelShader(pPixel)); + HR(pDevice->SetVertexDeclaration(pPosTexDecl)); + + DWORD BakFill; + pDevice->GetRenderState(D3DRS_FILLMODE, &BakFill); + + HR(pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID)); + HR(pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE)); + HR(pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, (blendop!=0))); + HR(pDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false)); + HR(pDevice->SetRenderState(D3DRS_STENCILENABLE, false)); + HR(pDevice->SetRenderState(D3DRS_COLORWRITEENABLE, 0xF)); + HR(pDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, false)); + + if (blendop == 1) { + HR(pDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD)); + HR(pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA)); + HR(pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA)); + } + + // Define vertices -------------------------------------------------------- + // + static const SMVERTEX Vertex[4] = { + {0, 0, 0, 0, 0}, + {0, 1, 0, 0, 1}, + {1, 1, 0, 1, 1}, + {1, 0, 0, 1, 0} + }; + + static const WORD cIndex[6] = {0, 2, 1, 0, 3, 2}; + + // Set render targets ----------------------------------------------------- + // + for (int i=0;i<4;i++) { + pDevice->GetRenderTarget(i, &pRtgBak[i]); + pDevice->SetRenderTarget(i, pRtg[i]); + if (pRtg[i]) { + if (i == 1) { HR(pDevice->SetRenderState(D3DRS_COLORWRITEENABLE1, 0xF)); } + if (i == 2) { HR(pDevice->SetRenderState(D3DRS_COLORWRITEENABLE2, 0xF)); } + if (i == 3) { HR(pDevice->SetRenderState(D3DRS_COLORWRITEENABLE3, 0xF)); } + } + } + + // Set Depth-Stencil surface ---------------------------------------------- + // + + pDevice->GetDepthStencilSurface(&pDepthBak); + + if (pDepth) { + HR(pDevice->SetRenderState(D3DRS_ZENABLE, true)); + HR(pDevice->SetRenderState(D3DRS_ZWRITEENABLE, true)); + HR(pDevice->SetDepthStencilSurface(pDepth)); + } + else { + HR(pDevice->SetRenderState(D3DRS_ZENABLE, false)); + HR(pDevice->SetRenderState(D3DRS_ZWRITEENABLE, false)); + HR(pDevice->SetDepthStencilSurface(NULL)); + } + + // Set textures and samplers ----------------------------------------------- + // + for (int idx=0;idxSetSamplerState(idx, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + else if (flags&IPF_MIRROR_U) pDevice->SetSamplerState(idx, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR); + else pDevice->SetSamplerState(idx, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); + + if (flags&IPF_CLAMP_V) pDevice->SetSamplerState(idx, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + else if (flags&IPF_MIRROR_V) pDevice->SetSamplerState(idx, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR); + else pDevice->SetSamplerState(idx, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); + + if (flags&IPF_CLAMP_W) pDevice->SetSamplerState(idx, D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP); + else if (flags&IPF_MIRROR_W) pDevice->SetSamplerState(idx, D3DSAMP_ADDRESSW, D3DTADDRESS_MIRROR); + else pDevice->SetSamplerState(idx, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP); + + DWORD filter = D3DTEXF_POINT; + + if (flags&IPF_LINEAR) filter = D3DTEXF_LINEAR; + if (flags&IPF_PYRAMIDAL) filter = D3DTEXF_PYRAMIDALQUAD; + if (flags&IPF_GAUSSIAN) filter = D3DTEXF_GAUSSIANQUAD; + + HR(pDevice->SetSamplerState(idx, D3DSAMP_MAGFILTER, filter)); + HR(pDevice->SetSamplerState(idx, D3DSAMP_MINFILTER, filter)); + HR(pDevice->SetSamplerState(idx, D3DSAMP_MIPFILTER, D3DTEXF_NONE)); + + HR(pDevice->SetTexture(idx, pTextures[idx].hTex)); + } + + // Execute ---------------------------------------------------------------- + // + if (!bInScene) HR(pDevice->BeginScene()); + + + if (mode == gcIPInterface::ipitemplate::Rect) + { + HR(pDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &cIndex, D3DFMT_INDEX16, &Vertex, sizeof(SMVERTEX))); + } + + + if (mode == gcIPInterface::ipitemplate::Octagon) + { + HR(pDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 8, pOcta, sizeof(SMVERTEX))); + } + + + if (mode == gcIPInterface::ipitemplate::Mesh) + { + + HR(pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW)); + + pMesh->Init(); + + DWORD nGrp = pMesh->GroupCount(); + if (grp < 0) for (DWORD i=0;i= 0) { + SURFHANDLE hTex = pMesh->GetTexture(i); + HR(pDevice->SetTexture(mesh_tex_idx, SURFACE(hTex)->GetTexture())); + } + pMesh->RenderGroup(i); + } + else { + if (mesh_tex_idx >= 0) { + SURFHANDLE hTex = pMesh->GetTexture(grp); + HR(pDevice->SetTexture(mesh_tex_idx, SURFACE(hTex)->GetTexture())); + } + pMesh->RenderGroup(grp); + } + } + + if (!bInScene) HR(pDevice->EndScene()); + + // Disconnect render targets ---------------------------------------------- + // + pDevice->SetDepthStencilSurface(pDepthBak); + SAFE_RELEASE(pDepthBak); + + + // Disconnect render targets ---------------------------------------------- + // + for (int i=0;i<4;i++) { + HR(pDevice->SetRenderTarget(i, pRtgBak[i])); + SAFE_RELEASE(pRtgBak[i]); + } + + // Disconnect textures ----------------------------------------------------- + // + for (int idx=0;idxSetTexture(idx, NULL)); + } + + HR(pDevice->SetRenderState(D3DRS_FILLMODE, BakFill)); + + return true; +} + + +// ================================================================================================ +// +void ImageProcessing::SetFloat(const char *var, const void *val, int bytes) +{ + D3DXHANDLE hVar = pPSConst->GetConstantByName(NULL, var); + + if (!hVar) { + LogErr("IPInterface::SetFloat() Invalid variable name [%s]. File[%s], Entrypoint[%s]", var, file, entry); + return; + } + + if (pPSConst->SetFloatArray(pDevice, hVar, (const FLOAT *)val, bytes>>2)!=S_OK) { + LogErr("IPInterface::SetFloat() Failed. Variable[%s], File[%s], Entrypoint[%s]", var, file, entry); + } +} + + +// ================================================================================================ +// +void ImageProcessing::SetInt(const char *var, const int *val, int bytes) +{ + D3DXHANDLE hVar = pPSConst->GetConstantByName(NULL, var); + + if (!hVar) { + LogErr("IPInterface::SetInt() Invalid variable name [%s]. File[%s], Entrypoint[%s]", var, file, entry); + return; + } + + if (pPSConst->SetIntArray(pDevice, hVar, val, bytes>>2)!=S_OK) { + LogErr("IPInterface::SetInt() Failed. Variable[%s], File[%s], Entrypoint[%s]", var, file, entry); + } +} + + +// ================================================================================================ +// +void ImageProcessing::SetBool(const char *var, const bool *val, int bytes) +{ + D3DXHANDLE hVar = pPSConst->GetConstantByName(NULL, var); + + if (!hVar) { + LogErr("IPInterface::SetBool() Invalid variable name [%s]. File[%s], Entrypoint[%s]", var, file, entry); + return; + } + + int *data = new int[bytes]; + for (int i=0;iSetBoolArray(pDevice, hVar, (const BOOL *)data, bytes)!=S_OK) { + LogErr("IPInterface::SetBool() Failed. Variable[%s], File[%s], Entrypoint[%s]", var, file, entry); + } + + delete []data; + data = NULL; +} + + +// ================================================================================================ +// +void ImageProcessing::SetStruct(const char *var, const void *val, int bytes) +{ + D3DXHANDLE hVar = pPSConst->GetConstantByName(NULL, var); + + if (!hVar) { + LogErr("IPInterface::SetStruct() Invalid variable name [%s]. File[%s], Entrypoint[%s]", var, file, entry); + return; + } + + if (pPSConst->SetValue(pDevice, hVar, val, bytes)!=S_OK) { + LogErr("IPInterface::SetStruct() Failed. Variable[%s], File[%s], Entrypoint[%s]", var, file, entry); + } +} + + +// ================================================================================================ +// +void ImageProcessing::SetFloat(const char *var, float val) +{ + SetFloat(var, (const float*)&val, sizeof(float)); +} + + +// ================================================================================================ +// +void ImageProcessing::SetInt(const char *var, int val) +{ + SetInt(var, (const int*)&val, sizeof(int)); +} + + +// ================================================================================================ +// +void ImageProcessing::SetBool(const char *var, bool val) +{ + SetBool(var, (const bool*)&val, sizeof(bool)); +} + +// ================================================================================================ +// +void ImageProcessing::SetVSFloat(const char *var, const void *val, int bytes) +{ + D3DXHANDLE hVar = pVSConst->GetConstantByName(NULL, var); + + if (pPSConst->SetFloatArray(pDevice, hVar, (const FLOAT *)val, bytes >> 2) != S_OK) { + LogErr("IPInterface::SetFloat() Failed. Variable[%s], File[%s], Entrypoint[%s]", var, file, entry); + } +} + + +// ================================================================================================ +// +void ImageProcessing::SetVSInt(const char *var, const int *val, int bytes) +{ + D3DXHANDLE hVar = pVSConst->GetConstantByName(NULL, var); + + if (pPSConst->SetIntArray(pDevice, hVar, val, bytes >> 2) != S_OK) { + LogErr("IPInterface::SetInt() Failed. Variable[%s], File[%s], Entrypoint[%s]", var, file, entry); + } +} + + +// ================================================================================================ +// +void ImageProcessing::SetVSBool(const char *var, const bool *val, int bytes) +{ + D3DXHANDLE hVar = pVSConst->GetConstantByName(NULL, var); + + if (!hVar) return; + int *data = new int[bytes]; + for (int i = 0; iSetBoolArray(pDevice, hVar, (const BOOL *)data, bytes) != S_OK) { + LogErr("IPInterface::SetBool() Failed. Variable[%s], File[%s], Entrypoint[%s]", var, file, entry); + } + + delete[]data; +} + + +// ================================================================================================ +// +void ImageProcessing::SetVSStruct(const char *var, const void *val, int bytes) +{ + D3DXHANDLE hVar = pVSConst->GetConstantByName(NULL, var); + if (!hVar) return; + if (pPSConst->SetValue(pDevice, hVar, val, bytes) != S_OK) { + LogErr("IPInterface::SetStruct() Failed. Variable[%s], File[%s], Entrypoint[%s]", var, file, entry); + } +} + + +// ================================================================================================ +// +void ImageProcessing::SetVSFloat(const char *var, float val) +{ + SetVSFloat(var, (const float*)&val, sizeof(float)); +} + + +// ================================================================================================ +// +void ImageProcessing::SetVSInt(const char *var, int val) +{ + SetVSInt(var, (const int*)&val, sizeof(int)); +} + + +// ================================================================================================ +// +void ImageProcessing::SetVSBool(const char *var, bool val) +{ + SetVSBool(var, (const bool*)&val, sizeof(bool)); +} + + +// ================================================================================================ +// +void ImageProcessing::SetTexture(const char *var, SURFHANDLE hTex, DWORD flags) +{ + D3DXHANDLE hVar = pPSConst->GetConstantByName(NULL, var); + + if (!hVar) { + LogErr("IPInterface::SetTexture() Invalid variable name [%s]. File[%s], Entrypoint[%s]", var, file, entry); + return; + } + + DWORD idx = pPSConst->GetSamplerIndex(hVar); + + if (!hTex) { + pTextures[idx].hTex = NULL; + pTextures[idx].flags = 0; + return; + } + + pTextures[idx].hTex = SURFACE(hTex)->GetTexture(); + pTextures[idx].flags = flags; +} + + +// ================================================================================================ +// +void ImageProcessing::SetTextureNative(const char *var, LPDIRECT3DBASETEXTURE9 hTex, DWORD flags) +{ + D3DXHANDLE hVar = pPSConst->GetConstantByName(NULL, var); + + if (!hVar) { + LogErr("IPInterface::SetTextureNative() Invalid variable name [%s]. File[%s], Entrypoint[%s]", var, file, entry); + return; + } + + DWORD idx = pPSConst->GetSamplerIndex(hVar); + + if (!hTex) { + pTextures[idx].hTex = NULL; + pTextures[idx].flags = 0; + return; + } + + pTextures[idx].hTex = hTex; + pTextures[idx].flags = flags; +} + + +// ================================================================================================ +// +void ImageProcessing::SetTextureNative(int idx, LPDIRECT3DBASETEXTURE9 hTex, DWORD flags) +{ + if (idx < 0 || idx>15) return; + + if (!hTex) { + pTextures[idx].hTex = NULL; + pTextures[idx].flags = 0; + return; + } + + pTextures[idx].hTex = hTex; + pTextures[idx].flags = flags; +} + + +// ================================================================================================ +// +void ImageProcessing::SetOutput(int id, SURFHANDLE hTex) +{ + if (id<0) id=0; + if (id>3) id=3; + + if (hTex) pRtg[id] = SURFACE(hTex)->GetSurface(); + else pRtg[id] = NULL; +} + + +// ================================================================================================ +// +void ImageProcessing::SetDepthStencil(LPDIRECT3DSURFACE9 hSrf) +{ + pDepth = hSrf; +} + + +// ================================================================================================ +// +void ImageProcessing::SetOutputNative(int id, LPDIRECT3DSURFACE9 hSrf) +{ + if (id<0) id=0; + if (id>3) id=3; + pRtg[id] = hSrf; +} + + +// ================================================================================================ +// +bool ImageProcessing::IsOK() +{ + for (auto x : Shaders) { + if (x.second.pPixel == NULL) return false; + if (x.second.pPSConst == NULL) return false; + } + return (pVertex && pVSConst && pDevice && hVP && hPos && hSiz); +} + + + + + + + + + +// ================================================================================================ +// PUBLIC INTERFACE +// ================================================================================================ +// + +gcIPInterface::~gcIPInterface() +{ + +} + +bool gcIPInterface::CompileShader(const char *Entry) +{ + return pIPI->CompileShader(Entry); +} + +bool gcIPInterface::Activate(const char *Shader) +{ + return pIPI->Activate(Shader); +} + +void gcIPInterface::SetFloat(const char *var, float val) +{ + pIPI->SetFloat(var, val); +} + +void gcIPInterface::SetInt(const char *var, int val) +{ + pIPI->SetInt(var, val); +} + +void gcIPInterface::SetBool(const char *var, bool val) +{ + pIPI->SetBool(var, val); +} + +void gcIPInterface::SetFloat(const char *var, const void *val, int bytes) +{ + pIPI->SetFloat(var, val, bytes); +} + +void gcIPInterface::SetInt(const char *var, const int *val, int bytes) +{ + pIPI->SetInt(var, val, bytes); +} + +void gcIPInterface::SetBool(const char *var, const bool *val, int bytes) +{ + pIPI->SetBool(var, val, bytes); +} + +void gcIPInterface::SetStruct(const char *var, const void *val, int bytes) +{ + pIPI->SetStruct(var, val, bytes); +} + +void gcIPInterface::SetVSFloat(const char *var, float val) +{ + pIPI->SetVSFloat(var, val); +} + +void gcIPInterface::SetVSInt(const char *var, int val) +{ + pIPI->SetVSInt(var, val); +} + +void gcIPInterface::SetVSBool(const char *var, bool val) +{ + pIPI->SetVSBool(var, val); +} + +void gcIPInterface::SetVSFloat(const char *var, const void *val, int bytes) +{ + pIPI->SetVSFloat(var, val, bytes); +} + +void gcIPInterface::SetVSInt(const char *var, const int *val, int bytes) +{ + pIPI->SetVSInt(var, val, bytes); +} + +void gcIPInterface::SetVSBool(const char *var, const bool *val, int bytes) +{ + pIPI->SetVSBool(var, val, bytes); +} + +void gcIPInterface::SetVSStruct(const char *var, const void *val, int bytes) +{ + pIPI->SetVSStruct(var, val, bytes); +} + +void gcIPInterface::SetTexture(const char *var, SURFHANDLE hTex, DWORD flags) +{ + pIPI->SetTexture(var, hTex, flags); +} + +void gcIPInterface::SetOutput(int id, SURFHANDLE hSrf) +{ + pIPI->SetOutput(id, hSrf); +} + +bool gcIPInterface::IsOK() +{ + return pIPI->IsOK(); +} + +void gcIPInterface::SetOutputRegion(float w, float h, float x, float y) +{ + pIPI->SetTemplate(w, h, x, y); +} + +void gcIPInterface::SetMesh(MESHHANDLE hMesh, const char *tex, ipicull cull) +{ + pIPI->SetMesh(hMesh, tex, cull); +} + +bool gcIPInterface::Execute(bool bInScene) +{ + return pIPI->Execute(bInScene); +} + +bool gcIPInterface::Execute(DWORD blendop, bool bInScene, ipitemplate mde) +{ + return pIPI->Execute(blendop, bInScene, mde); +} + +int gcIPInterface::FindDefine(const char *key) +{ + return pIPI->FindDefine(key); +} diff --git a/OVP/VulkanClient/IProcess.h b/OVP/VulkanClient/IProcess.h new file mode 100644 index 000000000..4512ac30d --- /dev/null +++ b/OVP/VulkanClient/IProcess.h @@ -0,0 +1,162 @@ +// =================================================== +// Copyright (C) 2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + +#ifndef __IPROCESS_H +#define __IPROCESS_H + +#include +#include "MathAPI.h" +#include +#include +#include "OrbiterAPI.h" +#include "Util.h" +#include "gcCore.h" + +// Address mode WRAP is assumed by default +// Filter POINT is assumed by default +/* +#define IPF_WRAP 0x0000 +#define IPF_WRAP_U 0x0000 +#define IPF_WRAP_V 0x0000 +#define IPF_WRAP_W 0x0000 +#define IPF_CLAMP 0x0007 +#define IPF_CLAMP_U 0x0001 +#define IPF_CLAMP_V 0x0002 +#define IPF_CLAMP_W 0x0004 +#define IPF_MIRROR 0x0038 +#define IPF_MIRROR_U 0x0008 +#define IPF_MIRROR_V 0x0010 +#define IPF_MIRROR_W 0x0020 +#define IPF_POINT 0x0000 +#define IPF_LINEAR 0x0040 +#define IPF_PYRAMIDAL 0x0080 +#define IPF_GAUSSIAN 0x0100*/ + + +class ImageProcessing { + +public: + + + // ---------------------------------------------------------------------------------- + // Create a IPI (Image processing interface) which allows to process and create data via GPU + // _file is the filename where user shader code exists + // _entry is the function entry point "myFunc". e.g. float4 myFunc(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR + // which contains the executed code with two input variables x, y + // ppf is a list of preprocessor directives e.g. "_MYSECTION;_DEBUG" used like #if defined(_MYSECTION) ..code.. #endif + // ---------------------------------------------------------------------------------- + ImageProcessing(LPDIRECT3DDEVICE9 pDev, const char *_file, const char *_entry, const char *ppf = NULL, const char *_vsentry = NULL); + ~ImageProcessing(); + + bool CompileShader(const char *Entry); + bool Activate(const char *Shader = NULL); + + // ---------------------------------------------------------------------------------- + // Use the 'Set' functions to assign a value into a shader constants ( e.g. uniform extern float4 myVector; ) + // If the variable "var" is defined but NOT used by the shader code, the variable "var" doesn't exists in + // a constant table and an error is printed when trying to assign a value to it. + // ---------------------------------------------------------------------------------- + void SetFloat(const char *var, float val); + void SetInt(const char *var, int val); + void SetBool(const char *var, bool val); + // ---------------------------------------------------------------------------------- + void SetFloat(const char *var, const void *val, int bytes); + void SetInt(const char *var, const int *val, int bytes); + void SetBool(const char *var, const bool *val, int bytes); + void SetStruct(const char *var, const void *val, int bytes); + + // ---------------------------------------------------------------------------------- + // Use the 'Set' functions to assign a value into a shader constants ( e.g. uniform extern float4 myVector; ) + // If the variable "var" is defined but NOT used by the shader code, the variable "var" doesn't exists in + // a constant table and an error is printed when trying to assign a value to it. + // ---------------------------------------------------------------------------------- + void SetVSFloat(const char *var, float val); + void SetVSInt(const char *var, int val); + void SetVSBool(const char *var, bool val); + // ---------------------------------------------------------------------------------- + void SetVSFloat(const char *var, const void *val, int bytes); + void SetVSInt(const char *var, const int *val, int bytes); + void SetVSBool(const char *var, const bool *val, int bytes); + void SetVSStruct(const char *var, const void *val, int bytes); + + // ---------------------------------------------------------------------------------- + // SetTexture can be used to assign a texture and a sampler state flags to a sampler + // In a shader code sampler is defined as (e.g. sampler mySamp; ) where "mySamp" is + // the variable passed to SetTexture function. It's then used in a shader code like + // tex2D(mySamp, float2(x,y)) + // ---------------------------------------------------------------------------------- + void SetTexture(const char *var, SURFHANDLE hTex, DWORD flags); + + // ---------------------------------------------------------------------------------- + // SetOutput assigns a render target to the IP interface. "id" is an index of the render + // target with a maximum value of 3. It is possible to render in four different targets + // at the same time. Multisample AA is only supported with one render target. Unbound + // a render target by setting it to NULL. After a NULL render target all later targets + // are ignored. + // ---------------------------------------------------------------------------------- + void SetOutput(int id, SURFHANDLE hTex); + + // ---------------------------------------------------------------------------------- + bool IsOK(); + void SetTemplate(float w = 1.0f, float h = 1.0f, float x = 0.0f, float y = 0.0f); + void SetMesh(const MESHHANDLE hMesh, const char *tex = NULL, gcIPInterface::ipicull = gcIPInterface::ipicull::None); + + bool Execute(bool bInScene = false); + bool Execute(const char *shader, bool bInScene, DWORD blendop); + bool Execute(DWORD blendop, bool bInScene = false, gcIPInterface::ipitemplate tmp = gcIPInterface::ipitemplate::Rect, int gpr = -1); + + // ---------------------------------------------------------------------------------- + int FindDefine(const char *key); + + // Native DirectX calls ------------------------------------------------------------- + // + void SetDepthStencil(LPDIRECT3DSURFACE9 hSrf = NULL); + void SetOutputNative(int id, LPDIRECT3DSURFACE9 hSrf); + void SetTextureNative(const char *var, LPDIRECT3DBASETEXTURE9 hTex, DWORD flags); + void SetTextureNative(int idx, LPDIRECT3DBASETEXTURE9 hTex, DWORD flags); + +private: + + bool SetupViewPort(); + + typedef struct { + LPDIRECT3DPIXELSHADER9 pPixel; + LPD3DXCONSTANTTABLE pPSConst; + } SHADER; + + struct { + LPDIRECT3DBASETEXTURE9 hTex; + DWORD flags; + } pTextures[16]; + + gcIPInterface::ipicull mesh_cull; + + SketchMesh *pMesh; + std::map Shaders; + LPDIRECT3DDEVICE9 pDevice; + LPDIRECT3DSURFACE9 pRtg[4], pRtgBak[4]; + LPDIRECT3DSURFACE9 pDepth, pDepthBak; + LPD3DXCONSTANTTABLE pVSConst; + LPD3DXCONSTANTTABLE pPSConst; + LPDIRECT3DVERTEXSHADER9 pVertex; + LPDIRECT3DPIXELSHADER9 pPixel; + D3DSURFACE_DESC desc; + FMATRIX4 mVP; + FVECTOR4 vTemplate; + D3DVIEWPORT9 iVP; + D3DXHANDLE hVP; + D3DXHANDLE hPos; + D3DXHANDLE hSiz; + SMVERTEX *pOcta; + + int mesh_tex_idx; + char file[256]; + char ppf[256]; + char entry[32]; + + std::list def; +}; + +#endif diff --git a/OVP/VulkanClient/Junction.cpp b/OVP/VulkanClient/Junction.cpp new file mode 100644 index 000000000..acc3de51b --- /dev/null +++ b/OVP/VulkanClient/Junction.cpp @@ -0,0 +1,151 @@ +// ============================================================== +// Junction.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Peter Schneider (Kuddel) +// +// With a lot of help from Microsoft and some very important ideas +// regarding 'union padding' from Mahmoud Al-Qudsi of NeoSmart +// (Under MIT License) +// ============================================================== + +#include "Junction.h" +#include "Util.h" +#include "AABBUtil.h" +#include +#include +#include + +// This construct is used to get the right size independent of the union padding +#define REPARSE_DATA_BUFFER_HEADER_SIZE offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer) + + +namespace junction { + + +// =========================================================================== +// SafeHandle +#pragma pack(push, 1) + typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; + } REPARSE_DATA_BUFFER; +#pragma pack(pop) + + +// =========================================================================== +// Junction methods + bool CreateJunctionPoint(LPCSTR origin, LPCSTR junction) + { + char* buffer = new char[_MAX_PATH]; + + GetFullPathName(origin, 256, buffer, NULL); + + // Prepend \??\ to path to mark it as not-for-parsing + // and convert char -> w_char + std::string str(buffer); + std::wstring nativeTarget = L"\\??\\" + std::wstring( str.begin(), str.end() ); + + delete[] buffer; + + // Make sure there's a trailing slash + if (nativeTarget[ nativeTarget.length()-1 ] != L'\\') { + nativeTarget += L'\\'; + } + + // + // O.K. Now let's fill the REPARSE_DATA_BUFFER + // + size_t size = sizeof(REPARSE_DATA_BUFFER) - sizeof(WCHAR) + nativeTarget.length() * sizeof(WCHAR); + std::vector vec(size, 0); + REPARSE_DATA_BUFFER* reparseBuffer = (REPARSE_DATA_BUFFER*)&vec[0]; + + reparseBuffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + reparseBuffer->Reserved = NULL; + reparseBuffer->MountPointReparseBuffer.SubstituteNameOffset = 0; + reparseBuffer->MountPointReparseBuffer.SubstituteNameLength = static_cast( nativeTarget.length() * sizeof(WCHAR) ); + + // No substitute name, point it outside the bounds of the string + reparseBuffer->MountPointReparseBuffer.PrintNameOffset = reparseBuffer->MountPointReparseBuffer.SubstituteNameLength + (int) sizeof(WCHAR); + reparseBuffer->MountPointReparseBuffer.PrintNameLength = 0; + + // Copy the actual string + memcpy(reparseBuffer->MountPointReparseBuffer.PathBuffer, (LPCTSTR) nativeTarget.c_str(), reparseBuffer->MountPointReparseBuffer.SubstituteNameLength); + + // Set ReparseDataLength to the size of the MountPointReparseBuffer + // Kind in mind that with the padding for the union (given that SymbolicLinkReparseBuffer is larger), + // this is NOT equal to sizeof(MountPointReparseBuffer) + reparseBuffer->ReparseDataLength = sizeof(REPARSE_DATA_BUFFER) - REPARSE_DATA_BUFFER_HEADER_SIZE - sizeof(WCHAR) + reparseBuffer->MountPointReparseBuffer.SubstituteNameLength; + + // Create the junction directory first, we 'convert' it to junction later + CreateDirectory(junction, NULL); + + // Set the reparse point + AutoHandle hDir; + hDir.Handle = CreateFile(junction, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hDir.IsInvalid()) { + return false; // Failed to open directory! + } + + DWORD bytesReturned = 0; // dummy + if (!DeviceIoControl(hDir.Handle, FSCTL_SET_REPARSE_POINT, reparseBuffer, (unsigned int) size, NULL, 0, &bytesReturned, NULL)) { + return false; // Error issuing DeviceIoControl FSCTL_SET_REPARSE_POINT + } + + return true; + } + + + bool TargetDirectoryExists(LPCSTR path, DWORD attributes/* = 0*/) + { + if (attributes == 0) { + attributes = ::GetFileAttributes(path); + } + if (attributes == INVALID_FILE_ATTRIBUTES) { + return false; // Doesn't exist + } + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) { + return false; // Not a directory + } + return true; + } + + + bool IsDirectoryJunction(LPCSTR path, DWORD attributes/* = 0*/) + { + if (attributes == 0) { + attributes = ::GetFileAttributes(path); + } + if (attributes == INVALID_FILE_ATTRIBUTES) { + return false; // Doesn't exist + } + if ((attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) != (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) { + return false; // Not a directory or not a reparse point + } + return true; + } + +} // end-of namespace junction + +// --- eof --- \ No newline at end of file diff --git a/OVP/VulkanClient/Junction.h b/OVP/VulkanClient/Junction.h new file mode 100644 index 000000000..174a582f9 --- /dev/null +++ b/OVP/VulkanClient/Junction.h @@ -0,0 +1,58 @@ +// ============================================================== +// Junction.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012-2016 Peter Schneider (Kuddel) +// +// With a lot of help from Microsoft and some very important ideas +// regarding 'union padding' from Mahmoud Al-Qudsi of NeoSmart +// (Under MIT License) +// ============================================================== + +#ifndef __JUNCTION_H +#define __JUNCTION_H + +#include + +/** + * \brief Junction point handling API. + * Functions to create junction points (a.k.a. symbolic links) as required by + * some addons like the 'spacecraft3' + */ +namespace junction +{ + /** + * \brief Creates a junction point. + * This functions creates a junction point (a.k.a symbolic link). + * \param origin The path the junction points to. + * \param junction The path to the junction. + * \return \e true on success, \e false otherwise. + * \note All relative paths are relative to the Orbiter root directory! + */ + bool CreateJunctionPoint(LPCSTR origin, LPCSTR junction); + + + /** + * \brief Checks whether a (target-)directory exists. + * This function checks whether the directory \p path exists and is a + * directory. + * \param path The path to check + * \param attributes Optional file attributes + * \return Whether the directory exists. + * \note All relative paths are relative to the Orbiter root directory! + */ + bool TargetDirectoryExists(LPCSTR path, DWORD attributes = 0); + + + /** + * \brief Checks whether a directory is a junction point. + * This function checks whether the directory \p path is a junction point. + * \param path The path to check + * \param attributes Optional file attributes + * \return Whether the directory is a junction point. + * \note All relative paths are relative to the Orbiter root directory! + */ + bool IsDirectoryJunction(LPCSTR path, DWORD attributes = 0); +} + +#endif // !__JUNCTION_H diff --git a/OVP/VulkanClient/LGPL.txt b/OVP/VulkanClient/LGPL.txt new file mode 100644 index 000000000..02bbb60bc --- /dev/null +++ b/OVP/VulkanClient/LGPL.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/OVP/VulkanClient/Log.cpp b/OVP/VulkanClient/Log.cpp new file mode 100644 index 000000000..213b1cd29 --- /dev/null +++ b/OVP/VulkanClient/Log.cpp @@ -0,0 +1,552 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + +#include +#include "Log.h" +#include "Util.h" +#include "Config.h" +#include "Client.h" + +FILE *d3d9client_log = NULL; + +#define LOG_MAX_LINES 100000 +#define ERRBUF 8000 +#define OPRBUF 512 +#define TIMEBUF 63 + +extern class vkClient* g_client; + +char ErrBuf[ERRBUF+1]; +char OprBuf[OPRBUF+1]; +char TimeBuf[TIMEBUF+1]; + +time_t ltime; +int uEnableLog = 1; // This value is controlling log opeation ( Config->DebugLvl ) +int iEnableLog = 0; // Index into EnableLogStack +int EnableLogStack[16]; +int iLine = 0; // Line number counter (iLine <= LOG_MAX_LINES) + +__int64 qpcFrq = 0; // Performance counter frequency +__int64 qpcRef = 0; // Performance counter reference value (for "delta t") +__int64 qpcStart = 0; // Performance counter start value ("zero") + +std::queue vkDebugQueue; + +CRITICAL_SECTION LogCrit; + + +//------------------------------------------------------------------------------------------- +// +void MissingRuntimeError() +{ + MessageBoxA(NULL, + "DirectX Runtimes may be missing. See /Doc/vkClient.pdf for more information", + "vkClient Initialization Failed", MB_OK); +} + +//------------------------------------------------------------------------------------------- +// +void FailedDeviceError() +{ + MessageBoxA(NULL, + "DirectX9 Device Failed. Try to enable EnableDX12Wrapper from vkClient.cfg", + "vkClient Initialization Failed", MB_OK); +} + +//------------------------------------------------------------------------------------------- +// +void RuntimeError(const char* File, const char* Fnc, UINT Line) +{ + if (Config->DebugLvl == 0) return; + char buf[256]; + sprintf_s(buf, 256, "[%s] [%s] Line: %u See Orbiter.log for details.", File, Fnc, Line); + MessageBoxA(g_client->GetRenderWindow(), buf, "Critical Error:", MB_OK); + DebugBreak(); +} + +//------------------------------------------------------------------------------------------- +// +/* +int PrintModules(DWORD pAdr) +{ + HMODULE hMods[1024]; + HANDLE hProcess; + DWORD cbNeeded; + unsigned int i; + + // Get a handle to the process. + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetProcessId(GetCurrentProcess())); + + if (NULL == hProcess) return 1; + + if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { + for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) { + char szModName[MAX_PATH]; + if (GetModuleFileNameExA(hProcess, hMods[i], szModName, sizeof(szModName))) { + MODULEINFO mi; + GetModuleInformation(hProcess, hMods[i], &mi, sizeof(MODULEINFO)); + DWORD Base = (DWORD)mi.lpBaseOfDll; + if (pAdr > Base && pAdr < (Base + mi.SizeOfImage)) LogErr("%s EntryPoint=0x%8.8X, Base=0x%8.8X, Size=%u", szModName, mi.EntryPoint, mi.lpBaseOfDll, mi.SizeOfImage); + else LogOk("%s EntryPoint=0x%8.8X, Base=0x%8.8X, Size=%u", szModName, mi.EntryPoint, mi.lpBaseOfDll, mi.SizeOfImage); + } + } + } + CloseHandle(hProcess); + return 0; +} +*/ + + +//------------------------------------------------------------------------------------------- +// Log OAPISURFACE_xxx attributes +void LogAttribs(DWORD attrib, DWORD w, DWORD h, LPCSTR origin) +{ + char buf[512]; + sprintf_s(buf, 512, "%s (%d,%d)[0x%X]: ", origin, w, h, attrib); + if (attrib&OAPISURFACE_TEXTURE) strcat_s(buf, 512, "OAPISURFACE_TEXTURE "); + if (attrib&OAPISURFACE_RENDERTARGET) strcat_s(buf, 512, "OAPISURFACE_RENDERTARGET "); + if (attrib&OAPISURFACE_GDI) strcat_s(buf, 512, "OAPISURFACE_GDI "); + if (attrib&OAPISURFACE_SKETCHPAD) strcat_s(buf, 512, "OAPISURFACE_SKETCHPAD "); + if (attrib&OAPISURFACE_MIPMAPS) strcat_s(buf, 512, "OAPISURFACE_MIPMAPS "); + if (attrib&OAPISURFACE_NOMIPMAPS) strcat_s(buf, 512, "OAPISURFACE_NOMIPMAPS "); + if (attrib&OAPISURFACE_ALPHA) strcat_s(buf, 512, "OAPISURFACE_ALPHA "); + if (attrib&OAPISURFACE_NOALPHA) strcat_s(buf, 512, "OAPISURFACE_NOALPHA "); + if (attrib&OAPISURFACE_UNCOMPRESS) strcat_s(buf, 512, "OAPISURFACE_UNCOMPRESS "); + if (attrib&OAPISURFACE_SYSMEM) strcat_s(buf, 512, "OAPISURFACE_SYSMEM "); + LogDbg("BlueViolet", buf); +} + +//------------------------------------------------------------------------------------------- +// +void vkDebugLog(const char *format, ...) +{ + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + vkDebugQueue.push(std::string(ErrBuf)); +} + +//------------------------------------------------------------------------------------------- +// +void vkDebugLogVec(const char* lbl, oapi::FVECTOR4 &v) +{ + sprintf_s(ErrBuf, ERRBUF, "%s = [%f, %f, %f, %f]", lbl, v.x, v.y, v.z, v.w); + vkDebugQueue.push(std::string(ErrBuf)); +} + +//------------------------------------------------------------------------------------------- +// +void vkDebugLogMatrix(const char* name, FMATRIX4* pM) +{ + vkDebugQueue.push(std::string(name)); + sprintf_s(ErrBuf, ERRBUF, "[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m11, pM->m12, pM->m13, pM->m14); + vkDebugQueue.push(std::string(ErrBuf)); + sprintf_s(ErrBuf, ERRBUF, "[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m21, pM->m22, pM->m23, pM->m24); + vkDebugQueue.push(std::string(ErrBuf)); + sprintf_s(ErrBuf, ERRBUF, "[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m31, pM->m32, pM->m33, pM->m34); + vkDebugQueue.push(std::string(ErrBuf)); + sprintf_s(ErrBuf, ERRBUF, "[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m41, pM->m42, pM->m43, pM->m44); + vkDebugQueue.push(std::string(ErrBuf)); +} + +//------------------------------------------------------------------------------------------- +// +void vkInitLog(const char *file) +{ + QueryPerformanceFrequency((LARGE_INTEGER*)&qpcFrq); + QueryPerformanceCounter((LARGE_INTEGER*)&qpcStart); + + if (fopen_s(&d3d9client_log,file,"w+")) { d3d9client_log=NULL; } // Failed + else { + QueryPerformanceCounter((LARGE_INTEGER*)&qpcRef); + InitializeCriticalSectionAndSpinCount(&LogCrit, 256); + fprintf_s(d3d9client_log,"VulkanClient Log"); + fprintf_s(d3d9client_log,"

VulkanClient Log


"); + fprintf_s(d3d9client_log,"



"); + } +} + +//------------------------------------------------------------------------------------------- +// +void vkCloseLog() +{ + if (d3d9client_log) { + fprintf(d3d9client_log,""); + fclose(d3d9client_log); + d3d9client_log = NULL; + DeleteCriticalSection(&LogCrit); + } +} + +//------------------------------------------------------------------------------------------- +// +double vkGetTime() +{ + __int64 qpcCurrent; + QueryPerformanceCounter((LARGE_INTEGER*)&qpcCurrent); + return double(qpcCurrent) * 1e6 / double(qpcFrq); +} + +//------------------------------------------------------------------------------------------- +// +void vkSetTime(vkTime &inout, double ref) +{ + __int64 qpcCurrent; + QueryPerformanceCounter((LARGE_INTEGER*)&qpcCurrent); + double time = double(qpcCurrent) * 1e6 / double(qpcFrq); + inout.time += (time - ref); + inout.count += 1.0; + inout.peak = max((time - ref), inout.peak); +} + +//------------------------------------------------------------------------------------------- +// +char *my_ctime() +{ + __int64 qpcCurrent; + QueryPerformanceCounter((LARGE_INTEGER*)&qpcCurrent); + double time = double(qpcCurrent-qpcRef) * 1e3 / double(qpcFrq); + double start = double(qpcCurrent-qpcStart) / double(qpcFrq); + sprintf_s(OprBuf,OPRBUF,"%d: %.1fs %05.2fms", iLine++, start, time); + qpcRef = qpcCurrent; + return OprBuf; +} + +//------------------------------------------------------------------------------------------- +// +void escape_ErrBuf () { + std::string buf(ErrBuf); + size_t n = 0; + n += replace_all(buf, "&", "&"); + n += replace_all(buf, "<", "<"); + n += replace_all(buf, ">", ">"); + if (n) { + strcpy_s(ErrBuf, ARRAYSIZE(ErrBuf), buf.c_str()); + } +} + +//------------------------------------------------------------------------------------------- +// +void LogTrace(const char *format, ...) +{ + if (d3d9client_log==NULL) return; + if (iLine>LOG_MAX_LINES) return; + if (uEnableLog>3) { + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log, "(%s)(0x%lX) ", my_ctime(), th); + + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + escape_ErrBuf(); + fputs(ErrBuf,d3d9client_log); + fputs("
\n",d3d9client_log); + fflush(d3d9client_log); + LeaveCriticalSection(&LogCrit); + } +} + +//------------------------------------------------------------------------------------------- +// +void LogAlw(const char *format, ...) +{ + if (d3d9client_log==NULL) return; + if (iLine>LOG_MAX_LINES) return; + if (uEnableLog>0) { + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log, "(%s)(0x%lX) ", my_ctime(), th); + + va_list args; + va_start(args, format); + + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + + va_end(args); + + escape_ErrBuf(); + fputs(ErrBuf,d3d9client_log); + fputs("
\n",d3d9client_log); + fflush(d3d9client_log); + LeaveCriticalSection(&LogCrit); + } +} + +//------------------------------------------------------------------------------------------- +// +void LogDbg(const char *color, const char *format, ...) +{ + if (d3d9client_log == NULL) return; + if (iLine>LOG_MAX_LINES) return; + if (uEnableLog>2) { + EnterCriticalSection(&LogCrit); + + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log, "(%s)(0x%lX) ", my_ctime(), th, color); + + va_list args; + va_start(args, format); + + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + + va_end(args); + + escape_ErrBuf(); + fputs(ErrBuf, d3d9client_log); + fputs("
\n", d3d9client_log); + fflush(d3d9client_log); + + LeaveCriticalSection(&LogCrit); + } +} + +//------------------------------------------------------------------------------------------- +// +void LogClr(const char *color, const char *format, ...) +{ + if (d3d9client_log == NULL) return; + if (iLine>LOG_MAX_LINES) return; + if (uEnableLog>1) { + EnterCriticalSection(&LogCrit); + + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log, "(%s)(0x%lX) ", my_ctime(), th, color); + + va_list args; + va_start(args, format); + + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + + va_end(args); + + escape_ErrBuf(); + fputs(ErrBuf, d3d9client_log); + fputs("
\n", d3d9client_log); + fflush(d3d9client_log); + + LeaveCriticalSection(&LogCrit); + } +} + +//------------------------------------------------------------------------------------------- +// +void LogOapi(const char *format, ...) +{ + + if (d3d9client_log==NULL) return; + if (iLine>LOG_MAX_LINES) return; + if (uEnableLog>0) { + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log, "(%s)(0x%lX) ", my_ctime(), th); + + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + oapiWriteLogV("vk: %s", ErrBuf); + + escape_ErrBuf(); + fputs(ErrBuf,d3d9client_log); + fputs("
\n",d3d9client_log); + fflush(d3d9client_log); + LeaveCriticalSection(&LogCrit); + } +} + +//------------------------------------------------------------------------------------------- +// +void LogVerbose(const char* format, ...) +{ + + if (d3d9client_log == NULL) return; + if (iLine > LOG_MAX_LINES) return; + if (uEnableLog > 0) { + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log, "(%s)(0x%lX) ", my_ctime(), th); + + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + oapiWriteLogVerbose(ErrBuf); + + escape_ErrBuf(); + fputs(ErrBuf, d3d9client_log); + fputs("
\n", d3d9client_log); + fflush(d3d9client_log); + LeaveCriticalSection(&LogCrit); + } +} + +// --------------------------------------------------- +// +void LogErr(const char *format, ...) +{ + if (d3d9client_log==NULL) return; + if (iLine>LOG_MAX_LINES) return; + if (uEnableLog>0) { + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log,"(%s)(0x%lX) [ERROR] ", my_ctime(), th); + + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + oapiWriteLogV("vkERROR: %s", ErrBuf); + + escape_ErrBuf(); + fputs(ErrBuf,d3d9client_log); + fputs("
\n",d3d9client_log); + fflush(d3d9client_log); + LeaveCriticalSection(&LogCrit); + } +} + +// --------------------------------------------------- +// +void LogBlu(const char *format, ...) +{ + if (d3d9client_log==NULL) return; + if (iLine>LOG_MAX_LINES) return; + if (uEnableLog>1) { + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log,"(%s)(0x%lX) ", my_ctime(), th); + + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + escape_ErrBuf(); + fputs(ErrBuf,d3d9client_log); + fputs("
\n",d3d9client_log); + fflush(d3d9client_log); + LeaveCriticalSection(&LogCrit); + } +} + +// --------------------------------------------------- +// +void LogWrn(const char *format, ...) +{ + if (d3d9client_log==NULL) return; + if (iLine>LOG_MAX_LINES) return; + if (uEnableLog>1) { + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log,"(%s)(0x%lX) [WARNING] ", my_ctime(), th); + + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + escape_ErrBuf(); + fputs(ErrBuf,d3d9client_log); + fputs("
\n",d3d9client_log); + fflush(d3d9client_log); + oapiWriteLogV("vkInfo: %s", ErrBuf); + LeaveCriticalSection(&LogCrit); + } +} + +// --------------------------------------------------- +// +void LogBreak(const char* format, ...) +{ + if (d3d9client_log == NULL) return; + if (iLine > LOG_MAX_LINES) return; + if (uEnableLog > 1) { + + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log, "(%s)(0x%lX) [WARNING] ", my_ctime(), th); + + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + escape_ErrBuf(); + fputs(ErrBuf, d3d9client_log); + fputs("
\n", d3d9client_log); + fflush(d3d9client_log); + oapiWriteLogV("vkDebug: %s", ErrBuf); + LeaveCriticalSection(&LogCrit); + + if (Config->DebugBreak) DebugBreak(); + } +} + +// --------------------------------------------------- +// +void LogOk(const char *format, ...) +{ + /*if (d3d9client_log==NULL) return; + if (iLine>LOG_MAX_LINES) return; + if (uEnableLog>2) { + EnterCriticalSection(&LogCrit); + DWORD th = GetCurrentThreadId(); + fprintf(d3d9client_log,"(%s)(0x%lX) ", my_ctime(), th); + + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, ERRBUF, ERRBUF, format, args); + va_end(args); + + escape_ErrBuf(); + fputs(ErrBuf,d3d9client_log); + fputs("
\n",d3d9client_log); + fflush(d3d9client_log); + LeaveCriticalSection(&LogCrit); + }*/ +} + +void LogMatrix(FMATRIX4* pM, const char* name) +{ + LogAlw("%s", name); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m11, pM->m12, pM->m13, pM->m14); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m21, pM->m22, pM->m23, pM->m24); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m31, pM->m32, pM->m33, pM->m34); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->m41, pM->m42, pM->m43, pM->m44); +} + +void LogVec(FVECTOR4* pM, const char* name) +{ + LogAlw("%s", name); + LogAlw("[%9.9g, %9.9g, %9.9g, %9.9g]", pM->x, pM->y, pM->z, pM->w); +} + +void LogVec(VECTOR3* pM, const char* name) +{ + LogAlw("%s", name); + LogAlw("[%9.9g, %9.9g, %9.9g]", pM->x, pM->y, pM->z); +} diff --git a/OVP/VulkanClient/Log.h b/OVP/VulkanClient/Log.h new file mode 100644 index 000000000..453c5bf65 --- /dev/null +++ b/OVP/VulkanClient/Log.h @@ -0,0 +1,78 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2012-2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= +#ifndef __LOGGING_H +#define __LOGGING_H + +#include +#include +#include +#include "DrawAPI.h" +#define WIN32_LEAN_AND_MEAN +#include // DWORD, LPCSTR +#undef WIN32_LEAN_AND_MEAN + +typedef struct { + double time; + double count; + double peak; +} vkTime; + +extern int uEnableLog; // This value is controlling log operation ( Config->DebugLvl ) +extern int iEnableLog; +extern int EnableLogStack[16]; +extern std::queue vkDebugQueue; + +#define _PUSHLOG EnableLogStack[iEnableLog++] = uEnableLog; +#define _SETLOG(x) { EnableLogStack[iEnableLog++] = uEnableLog; if (uEnableLog>0) uEnableLog=x; } +#define _POPLOG uEnableLog = EnableLogStack[--iEnableLog]; + +#define _UNDEBUGED LogWrn("[Undebuged/Unfinished code section reached in %s (File %s, Line %d)]",__FUNCTION__,__FILE__,__LINE__); +//#define _UNDEBUGED + +void RuntimeError(const char* File, const char* Fnc, UINT Line); +void vkDebugLog(const char *format, ...); +void vkDebugLogVec(const char* lbl, oapi::FVECTOR4 &v); +void vkDebugLogMatrix(const char* name, oapi::FMATRIX4* pM); +void vkInitLog(const char *file); +void vkCloseLog(); +void LogTrace(const char *format, ...); +void LogErr(const char *format, ...); +void LogWrn(const char *format, ...); +void LogOk (const char *format, ...); +void LogBreak(const char* format, ...); +void LogBlu(const char *format, ...); +void LogOapi(const char *format, ...); +void LogVerbose(const char* format, ...); +void LogAlw(const char *format, ...); +void LogDbg(const char *color, const char *format, ...); +void LogClr(const char *color, const char *format, ...); + + +double vkGetTime(); +void vkSetTime(vkTime &inout, double ref); + +void MissingRuntimeError(); +void FailedDeviceError(); +void LogAttribs(DWORD attrib, DWORD w, DWORD h, LPCSTR origin); +void LogVec(oapi::FVECTOR4* pM, const char* name); +void LogVec(VECTOR3* pM, const char* name); +void LogMatrix(oapi::FMATRIX4* pM, const char* name); + +#define HALT() { RuntimeError(__FILE__,__FUNCTION__,__LINE__); } + +#endif diff --git a/OVP/VulkanClient/MaterialMgr.cpp b/OVP/VulkanClient/MaterialMgr.cpp new file mode 100644 index 000000000..62a4c8ffd --- /dev/null +++ b/OVP/VulkanClient/MaterialMgr.cpp @@ -0,0 +1,451 @@ +// =========================================================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2013 - 2016 Jarmo Nikkanen +// =========================================================================================== + + +#include "MaterialMgr.h" +#include "Surface.h" +#include "OapiExtension.h" +#include "vVessel.h" + + + +// =========================================================================================== +// +MatMgr::MatMgr(class vObject *v, class vkClient *_gc) +{ + gc = _gc; + vObj = v; + + Shaders.push_back(SHADER("PBR-Old", SHADER_NULL)); + Shaders.push_back(SHADER("Metalness", SHADER_METALNESS)); + Shaders.push_back(SHADER("BakedVC", SHADER_BAKED_VC)); +} + +// =========================================================================================== +// +MatMgr::~MatMgr() +{ + MeshConfig.clear(); +} + +// =========================================================================================== +// +void MatMgr::RegisterMaterialChange(vkMesh *pMesh, DWORD midx, const vkMatExt *pM) +{ + if (!pMesh || !pM) return; + MeshConfig[pMesh->GetName()].material[midx] = *pM; +} + +// =========================================================================================== +// +void MatMgr::RegisterShaderChange(vkMesh *pMesh, WORD id) +{ + if (!pMesh) return; + for (auto y : Shaders) if (y.id == id) { + MeshConfig[pMesh->GetName()].shader = id; + break; + } +} + + +// =========================================================================================== +// +void MatMgr::ApplyConfiguration(vkMesh *pMesh) +{ + if (pMesh==NULL) return; + + const char *name = pMesh->GetName(); + + LogAlw("Applying custom configuration to a mesh (%s)",name); + + if (MeshConfig.count(name)) + { + pMesh->SetDefaultShader(MeshConfig[name].shader); + + for (auto x : MeshConfig[name].material) + { + auto rec = x.second; + + if (x.first >= int(pMesh->GetMaterialCount())) { + LogErr("MatMgr::ApplyConfiguration: Matrial Idx out of range [%s.msh]", name); + continue; + } + + vkMatExt Mat; + auto RecMat = x.second; + DWORD flags = RecMat.ModFlags; + + if (!pMesh->GetMaterial(&Mat, x.first)) continue; + + if (flags&vkMATEX_AMBIENT) Mat.Ambient = RecMat.Ambient; + if (flags&vkMATEX_DIFFUSE) Mat.Diffuse = RecMat.Diffuse; + if (flags&vkMATEX_EMISSIVE) Mat.Emissive = RecMat.Emissive; + if (flags&vkMATEX_REFLECT) Mat.Reflect = RecMat.Reflect; + if (flags&vkMATEX_SPECULAR) Mat.Specular = RecMat.Specular; + if (flags&vkMATEX_FRESNEL) Mat.Fresnel = RecMat.Fresnel; + if (flags&vkMATEX_EMISSION2) Mat.Emission2 = RecMat.Emission2; + if (flags&vkMATEX_ROUGHNESS) Mat.Roughness = RecMat.Roughness; + if (flags&vkMATEX_METALNESS) Mat.Metalness = RecMat.Metalness; + + Mat.ModFlags = flags; + + pMesh->SetMaterial(&Mat, x.first); + + LogBlu("Material %u setup applied to mesh (%s) Flags=0x%X", x.first, name, flags); + } + } +} + +// =========================================================================================== +// +bool MatMgr::HasMesh(const char *name) +{ + if (MeshConfig.count(name)) return true; + return false; +} + +// =========================================================================================== +// +void parse_vessel_classname(char *lbl) +{ + int i = -1; + while (lbl[++i]!=0) if (lbl[i]=='/' || lbl[i]=='\\') lbl[i]='_'; +} + +// =========================================================================================== +// +bool MatMgr::LoadConfiguration(bool bAppend) +{ + _TRACE; + + char cbuf[256]; + char path[256]; + char classname[256]; + char meshname[64]; + char shadername[64]; + + OBJHANDLE hObj = vObj->GetObjHandle(); + + if (oapiGetObjectType(hObj)!=OBJTP_VESSEL) return false; + + const char *cfgdir = OapiExtension::GetConfigDir(); + + VESSEL *vessel = oapiGetVesselInterface(hObj); + strcpy_s(classname, 256, vessel->GetClassNameA()); + parse_vessel_classname(classname); + + AutoFile file; + + if (file.IsInvalid()) { + sprintf_s(path, 256, "%sGC\\%s.cfg", cfgdir, classname); + fopen_s(&file.pFile, path, "r"); + } + + if (file.IsInvalid()) return true; + + LogAlw("Reading a custom configuration file for a vessel %s (%s)", vessel->GetName(), vessel->GetClassNameA()); + + DWORD n = 0; + int mat_idx = -1; + + while (fgets2(cbuf, 256, file.pFile, 0x0A)>=0) + { + float a, b, c, d; + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "MESH", 4)) { + mat_idx = -1; + if (sscanf_s(cbuf, "MESH %s", meshname, 64)!=1) LogErr("Invalid Line in (%s): %s", path, cbuf); + if (strncmp(meshname, "???", 3) == 0) meshname[0] = 0; + if (HasMesh(meshname) && bAppend) meshname[0] = 0; // Mesh is loaded already skip all entries related to it. + continue; + } + + // -------------------------------------------------------------------------------------------- + if (meshname[0] == 0) continue; // Do not continue without a valid mesh + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "SHADER", 6)) { + MeshConfig[meshname].shader = SHADER_NULL; + if (sscanf_s(cbuf, "SHADER %s", shadername, 64) != 1) LogErr("Invalid Line in (%s): %s", path, cbuf); + for (auto x : Shaders) + if (string(shadername) == x.name) { + MeshConfig[meshname].shader = x.id; + LogOapi("NewShader [%s]=%hX", meshname, x.id); + } + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "MATERIAL", 8)) { + if (sscanf_s(cbuf, "MATERIAL %d", &mat_idx)!=1) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + + // -------------------------------------------------------------------------------------------- + if (mat_idx == -1) continue; // Do not continue without a valid material idx + + auto &Mat = MeshConfig[meshname].material[mat_idx]; + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "SPECULAR", 8)) { + if (sscanf_s(cbuf, "SPECULAR %f %f %f %f", &a, &b, &c, &d)!=4) LogErr("Invalid Line in (%s): %s", path, cbuf); + Mat.Specular = FVECTOR4(a, b, c, d); + Mat.ModFlags |= vkMATEX_SPECULAR; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "DIFFUSE", 7)) { + if (sscanf_s(cbuf, "DIFFUSE %f %f %f %f", &a, &b, &c, &d)!=4) LogErr("Invalid Line in (%s): %s", path, cbuf); + Mat.Diffuse = FVECTOR4(a, b, c, d); + Mat.ModFlags |= vkMATEX_DIFFUSE; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "EMISSIVE", 8)) { + if (sscanf_s(cbuf, "EMISSIVE %f %f %f", &a, &b, &c)!=3) LogErr("Invalid Line in (%s): %s", path, cbuf); + Mat.Emissive = FVECTOR3(a, b, c); + Mat.ModFlags |= vkMATEX_EMISSIVE; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "EMISSION2", 9)) { + if (sscanf_s(cbuf, "EMISSION2 %f %f %f", &a, &b, &c) != 3) LogErr("Invalid Line in (%s): %s", path, cbuf); + Mat.Emission2 = FVECTOR3(a, b, c); + Mat.ModFlags |= vkMATEX_EMISSION2; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "AMBIENT", 7)) { + if (sscanf_s(cbuf, "AMBIENT %f %f %f", &a, &b, &c)!=3) LogErr("Invalid Line in (%s): %s", path, cbuf); + Mat.Ambient = FVECTOR3(a, b, c); + Mat.ModFlags |= vkMATEX_AMBIENT; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "REFLECT", 7)) { + if (sscanf_s(cbuf, "REFLECT %f %f %f", &a, &b, &c) != 3) LogErr("Invalid Line in (%s): %s", path, cbuf); + Mat.Reflect = FVECTOR3(a, b, c); + Mat.ModFlags |= vkMATEX_REFLECT; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "FRESNEL", 7)) { + if (sscanf_s(cbuf, "FRESNEL %f %f %f", &a, &b, &c) != 3) LogErr("Invalid Line in (%s): %s", path, cbuf); + if (b < 10.0f) b = 1024.0f; + Mat.Fresnel = FVECTOR3(a, c, b); + Mat.ModFlags |= vkMATEX_FRESNEL; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "ROUGHNESS", 9)) { + int cnt = sscanf_s(cbuf, "ROUGHNESS %f %f", &a, &b); + if (cnt == 1) Mat.Roughness = FVECTOR2(a, 1.0f); + else if (cnt == 2) Mat.Roughness = FVECTOR2(a, b); + else LogErr("Invalid Line in (%s): %s", path, cbuf); + Mat.ModFlags |= vkMATEX_ROUGHNESS; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "SMOOTHNESS", 10)) { + int cnt = sscanf_s(cbuf, "SMOOTHNESS %f %f", &a, &b); + if (cnt == 1) Mat.Roughness = FVECTOR2(a, 1.0f); + else if (cnt == 2) Mat.Roughness = FVECTOR2(a, b); + else LogErr("Invalid Line in (%s): %s", path, cbuf); + Mat.ModFlags |= vkMATEX_ROUGHNESS; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "METALNESS", 9)) { + if (sscanf_s(cbuf, "METALNESS %f", &a) != 1) LogErr("Invalid Line in (%s): %s", path, cbuf); + Mat.Metalness = a; + Mat.ModFlags |= vkMATEX_METALNESS; + continue; + } + } + + return true; +} + + +// =========================================================================================== +// +bool MatMgr::SaveConfiguration() +{ + _TRACE; + bool bIfStatement = false; + + char path[256]; + char classname[256]; + + + OBJHANDLE hObj = vObj->GetObjHandle(); + + if (oapiGetObjectType(hObj)!=OBJTP_VESSEL) return false; + + VESSEL *vessel = oapiGetVesselInterface(hObj); + const char *cfgdir = OapiExtension::GetConfigDir(); + + strcpy_s(classname, 256, vessel->GetClassNameA()); + parse_vessel_classname(classname); + + AutoFile file; + sprintf_s(path, 256, "%sGC\\%s.cfg", cfgdir, classname); + + // If the target file contains configurations those are not loaded into the editor, + // Load them before overwriting the file + LoadConfiguration(true); + + fopen_s(&file.pFile, path, "w"); + + if (file.IsInvalid()) { + LogErr("Failed to write a file"); + return false; + } + + fprintf(file.pFile, "CONFIG_VERSION 3\n"); + + for (auto x : MeshConfig) + { + string current = x.first; + + fprintf(file.pFile,"; =============================================\n"); + fprintf(file.pFile, "MESH %s\n", current.c_str()); + + for (auto y : Shaders) if (y.id == x.second.shader) fprintf(file.pFile, "SHADER %s\n", y.name.c_str()); + + for (auto rec : x.second.material) + { + DWORD flags = rec.second.ModFlags; + vkMatExt *pM = &rec.second; + + if (flags==0) continue; + + fprintf(file.pFile,"; ---------------------------------------------\n"); + fprintf(file.pFile,"MATERIAL %u\n", rec.first); + + if (flags&vkMATEX_AMBIENT) fprintf(file.pFile,"AMBIENT %f %f %f\n", pM->Ambient.x, pM->Ambient.y, pM->Ambient.z); + if (flags&vkMATEX_DIFFUSE) fprintf(file.pFile,"DIFFUSE %f %f %f %f\n", pM->Diffuse.x, pM->Diffuse.y, pM->Diffuse.z, pM->Diffuse.w); + if (flags&vkMATEX_SPECULAR) fprintf(file.pFile,"SPECULAR %f %f %f %f\n", pM->Specular.x, pM->Specular.y, pM->Specular.z, pM->Specular.w); + if (flags&vkMATEX_EMISSIVE) fprintf(file.pFile,"EMISSIVE %f %f %f\n", pM->Emissive.x, pM->Emissive.y, pM->Emissive.z); + if (flags&vkMATEX_REFLECT) fprintf(file.pFile,"REFLECT %f %f %f\n", pM->Reflect.x, pM->Reflect.y, pM->Reflect.z); + if (flags&vkMATEX_FRESNEL) fprintf(file.pFile,"FRESNEL %f %f %f\n", pM->Fresnel.x, pM->Fresnel.z, pM->Fresnel.y); + if (flags&vkMATEX_EMISSION2) fprintf(file.pFile, "EMISSION2 %f %f %f\n", pM->Emission2.x, pM->Emission2.y, pM->Emission2.z); + if (flags&vkMATEX_ROUGHNESS) fprintf(file.pFile, "SMOOTHNESS %f %f\n", pM->Roughness.x, pM->Roughness.y); + if (flags&vkMATEX_METALNESS) fprintf(file.pFile, "METALNESS %f\n", pM->Metalness); + } + } + return true; +} + + +// =========================================================================================== +// +bool MatMgr::LoadCameraConfig() +{ + _TRACE; + + char cbuf[256]; + char path[256]; + char classname[256]; + + OBJHANDLE hObj = vObj->GetObjHandle(); + + if (oapiGetObjectType(hObj)!=OBJTP_VESSEL) return false; + + const char *cfgdir = OapiExtension::GetConfigDir(); + + VESSEL *vessel = oapiGetVesselInterface(hObj); + strcpy_s(classname, 256, vessel->GetClassNameA()); + parse_vessel_classname(classname); + + AutoFile file; + + sprintf_s(path, 256, "%sGC\\%s_ecam.cfg", cfgdir, classname); + fopen_s(&file.pFile, path, "r"); + + if (file.IsInvalid()) return true; + + LogAlw("Reading a camera configuration file for a vessel %s (%s)", vessel->GetName(), vessel->GetClassNameA()); + + ENVCAMREC* pCamera = NULL; + + while(fgets2(cbuf, 256, file.pFile, 0x08)>=0) + { + float a, b, c; + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "END_CAMERA", 10)) { + pCamera = NULL; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "BEGIN_CAMERA", 12)) { + int idx = -1; + if (sscanf_s(cbuf, "BEGIN_CAMERA %d", &idx)!=1) LogErr("Invalid Line in (%s): %s", path, cbuf); + if (idx == 0) { + pCamera = ((vVessel*)vObj)->CreateEnvCam(EnvCamType::Exterior); + pCamera->id = -1; + pCamera->flags = 0; // Clear default flags + } + if (idx == 1) { + pCamera = ((vVessel*)vObj)->CreateEnvCam(EnvCamType::Interior); + pCamera->id = -1; + pCamera->flags = 0; // Clear default flags + } + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "LPOS", 4)) { + if (sscanf_s(cbuf, "LPOS %g %g %g", &a, &b, &c)!=3) LogErr("Invalid Line in (%s): %s", path, cbuf); + pCamera->lPos = FVECTOR3(a,b,c); + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "CLIPDIST", 8)) { + if (sscanf_s(cbuf, "CLIPDIST %g", &a)!=1) LogErr("Invalid Line in (%s): %s", path, cbuf); + pCamera->near_clip = a; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "OMIT_ALL_ATTC", 13)) { + pCamera->flags |= ENVCAM_OMIT_ATTC; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "DO_NOT_OMIT_FOCUS", 17)) { + pCamera->flags |= ENVCAM_FOCUS; + continue; + } + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "OMIT_ALL_DOCKS", 14)) { + pCamera->flags |= ENVCAM_OMIT_DOCKS; + continue; + } + + if (cbuf[0]!=';') LogErr("Invalid Line in (%s): %s", path, cbuf); + } + + return true; +} + + + diff --git a/OVP/VulkanClient/MaterialMgr.h b/OVP/VulkanClient/MaterialMgr.h new file mode 100644 index 000000000..f535ede63 --- /dev/null +++ b/OVP/VulkanClient/MaterialMgr.h @@ -0,0 +1,60 @@ +// ============================================================== +// MaterialMgr.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __MATERIALMGR_H +#define __MATERIALMGR_H + +#include +#include "MathAPI.h" + +#include "Mesh.h" +#include "Client.h" +#include "Util.h" +#include "vObject.h" + +/** + * \brief Management of custom configurations for vessel materials + */ +class MatMgr { + +public: + // Disable copy construct & copy assign + MatMgr (MatMgr const&) = delete; + MatMgr & operator= (MatMgr const&) = delete; + + MatMgr(class vObject *vObj, class vkClient *_gc); + ~MatMgr(); + + void RegisterMaterialChange(vkMesh *pMesh, DWORD midx, const vkMatExt *pM); + void RegisterShaderChange(vkMesh *pMesh, WORD id); + void ApplyConfiguration(vkMesh *pMesh); + bool SaveConfiguration(); + bool LoadConfiguration(bool bAppend=false); + bool LoadCameraConfig(); + bool HasMesh(const char *name); + +private: + + vObject *vObj; + vkClient *gc; + + struct SHADER { + SHADER(string x, WORD i) { name = x; id = i; } + string name; + WORD id; + }; + + struct MESHREC { + WORD shader; + map material; + }; + + std::map MeshConfig; + std::list Shaders; +}; + +#endif diff --git a/OVP/VulkanClient/Mesh.cpp b/OVP/VulkanClient/Mesh.cpp new file mode 100644 index 000000000..084dbbaa9 --- /dev/null +++ b/OVP/VulkanClient/Mesh.cpp @@ -0,0 +1,3756 @@ +// ============================================================== +// Mesh.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006 - 2016 Martin Schweiger +// 2010 - 2022 Jarmo Nikkanen (vkClient implementation) +// ============================================================== + +#define VISIBILITY_TOL 0.0015f + +#include "Mesh.h" +#include "Log.h" +#include "Scene.h" +#include "Surface.h" +#include "Catalog.h" +#include "Config.h" +#include "DebugControls.h" +#include "VectorHelpers.h" + +#pragma warning(push) +#pragma warning(disable : 4838) +#include "DirectXMath.h" +#include "DirectXCollision.h" +#pragma warning(pop) + +using namespace oapi; + +LPDIRECT3DTEXTURE9 vkMesh::pShadowMap[SHM_CASCADE_COUNT] = {}; +FVECTOR4 vkMesh::ShdSubRect[SHM_CASCADE_COUNT] = {}; +MeshShader* vkMesh::s_pShader[16] = {}; + +MeshShader::VSConst MeshShader::vs_const = {}; +MeshShader::PSConst MeshShader::ps_const = {}; +MeshShader::PSBools MeshShader::ps_bools = {}; + + +int compare_lights(const void * a, const void * b) +{ + float fa = static_cast(a)->illuminace; + float fb = static_cast(b)->illuminace; + if (fa < fb * 0.9995f) return 1; + if (fa > fb * 1.0005f) return -1; + return 0; +} + + + +// ====================================================================================== +// Buffer Object Implementation +// ====================================================================================== +// + +MeshBuffer::MeshBuffer(DWORD _nVtx, DWORD _nFace, const class vkMesh *_pRoot) +{ + nVtx = _nVtx; + nIdx = _nFace * 3; + nRef = 1; + + pVB = NULL; + pIB = NULL; + pGB = NULL; + pSB = NULL; + + pVBSys = new NMVERTEX[nVtx]; + pIBSys = new WORD[nIdx]; + pGBSys = new XMVECTOR[nVtx]; + pSBSys = new SMVERTEX[nVtx]; + + pRoot = _pRoot; + mapMode = MAPMODE_STATIC; + bMustRemap = true; +} + + +MeshBuffer::MeshBuffer(MeshBuffer *pSrc, const class vkMesh *_pRoot) +{ + nVtx = pSrc->nVtx; + nIdx = pSrc->nIdx; + nRef = 1; + + pVB = NULL; + pIB = NULL; + pGB = NULL; + pSB = NULL; + + pVBSys = new NMVERTEX[nVtx]; + pIBSys = new WORD[nIdx]; + pGBSys = new XMVECTOR[nVtx]; + pSBSys = new SMVERTEX[nVtx]; + + memcpy(pSBSys, pSrc->pSBSys, sizeof(SMVERTEX) * nVtx); + memcpy(pVBSys, pSrc->pVBSys, sizeof(NMVERTEX) * nVtx); + memcpy(pGBSys, pSrc->pGBSys, sizeof(XMVECTOR) * nVtx); + memcpy(pIBSys, pSrc->pIBSys, sizeof(WORD) * nIdx); + + pRoot = _pRoot; + mapMode = MAPMODE_STATIC; + bMustRemap = true; +} + + +MeshBuffer::~MeshBuffer() +{ + SAFE_DELETEA(pGBSys); + SAFE_DELETEA(pIBSys); + SAFE_DELETEA(pVBSys); + SAFE_DELETEA(pSBSys); + SAFE_RELEASE(pIB); + SAFE_RELEASE(pVB); + SAFE_RELEASE(pGB); + SAFE_RELEASE(pSB); + oapiWriteLog("MeshBuffer::Destruct"); +} + +bool MeshBuffer::Release() +{ + nRef--; + return nRef <= 0; +} + +MeshBuffer* MeshBuffer::Reference() +{ + nRef++; + return this; +} + +void MeshBuffer::MustRemap(DWORD mode) +{ + if (mode == MAPMODE_CURRENT) mode = mapMode; + + if (mode != mapMode) { + SAFE_RELEASE(pIB); + SAFE_RELEASE(pVB); + SAFE_RELEASE(pGB); + SAFE_RELEASE(pSB); + mapMode = mode; + } + + bMustRemap = true; +} + +void MeshBuffer::Map(LPDIRECT3DDEVICE9 pDev) +{ + + if (!bMustRemap) return; + + bMustRemap = false; + + DWORD Usage = 0; + DWORD Lock = 0; + + if (mapMode == MAPMODE_DYNAMIC) Usage = D3DUSAGE_DYNAMIC, Lock = D3DLOCK_DISCARD; + + if (!pSB) { + HR(pDev->CreateVertexBuffer(nVtx * sizeof(SMVERTEX), Usage, 0, D3DPOOL_DEFAULT, &pSB, NULL)); + } + if (!pVB) { + HR(pDev->CreateVertexBuffer(nVtx * sizeof(NMVERTEX), Usage, 0, D3DPOOL_DEFAULT, &pVB, NULL)); + } + if (!pGB) { + HR(pDev->CreateVertexBuffer(nVtx * sizeof(FVECTOR4), Usage, 0, D3DPOOL_DEFAULT, &pGB, NULL)); + } + if (!pIB) { + HR(pDev->CreateIndexBuffer(nIdx * sizeof(WORD), Usage, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &pIB, NULL)); + } + + LPVOID pTgt; + + HR(pSB->Lock(0, 0, (LPVOID*)&pTgt, Lock)); + memcpy(pTgt, pSBSys, nVtx * sizeof(SMVERTEX)); + HR(pSB->Unlock()); + + HR(pVB->Lock(0, 0, (LPVOID*)&pTgt, Lock)); + memcpy(pTgt, pVBSys, nVtx * sizeof(NMVERTEX)); + HR(pVB->Unlock()); + + HR(pGB->Lock(0, 0, (LPVOID*)&pTgt, Lock)); + memcpy(pTgt, pGBSys, nVtx * sizeof(FVECTOR4)); + HR(pGB->Unlock()); + + HR(pIB->Lock(0, 0, (LPVOID*)&pTgt, Lock)); + memcpy(pTgt, pIBSys, nIdx * sizeof(WORD)); + HR(pIB->Unlock()); +} + + + + + + + + +// ====================================================================================== +// Mesh Implementation +// ====================================================================================== +// +void vkMesh::Null(const char *meshName /* = NULL */) +{ + nGrp = 0; + Grp = NULL; + nTex = 0; + Tex = NULL; + nMtrl = 0; + pBuf = NULL; + Mtrl = NULL; + pGrpTF = NULL; + cAmbient = { 0.0f, 0.0f, 0.0f }; + MaxFace = 0; + MaxVert = 0; + vClass = 0; + DefShader = SHADER_LEGACY; + hOapiMesh = NULL; + MeshFlags = 0; + Flags = 0; + + bIsTemplate = false; + bGlobalTF = false; + bBSRecompute = true; + bBSRecomputeAll = true; + bModulateMatAlpha = false; + bIsReflective = false; + bCanRenderFast = false; + bMtrlModidied = false; + + bli = BakedLights.begin(); + + Locals = new LightStruct[Config->MaxLights()]; + + for (int i = 0; i < SHM_CASCADE_COUNT; i++) { + pShadowMap[i] = NULL; + ShdSubRect[i] = _V4(0,0,1,1); + } + + memset(Locals, 0, sizeof(LightStruct) * Config->MaxLights()); + memset(LightList, 0, sizeof(LightList)); + strcpy_s(this->name, ARRAYSIZE(this->name), meshName ? meshName : "???"); +} + +// =========================================================================================== +// +vkMesh::vkMesh(const char *fname) : vkEffect() +{ + Null(); + MESHHANDLE hMesh = oapiLoadMesh(fname); + + if (hMesh) { + LoadMeshFromHandle(hMesh); + oapiDeleteMesh(hMesh); + } + else { + LogErr("Failed to load [%s]", fname); + } + + MeshCatalog.insert(this); + if (pBuf) pBuf->Map(pDev); +} + +// =========================================================================================== +// +vkMesh::vkMesh(MESHHANDLE hMesh, bool asTemplate, FVECTOR3 *reorig, float *scale) : vkEffect() +{ + Null(); + LoadMeshFromHandle(hMesh, reorig, scale); + bIsTemplate = asTemplate; + MeshCatalog.insert(this); + if (pBuf) pBuf->Map(pDev); +} + + +// =========================================================================================== +// +vkMesh::vkMesh(DWORD groups, const MESHGROUPEX **hGroup, const SURFHANDLE *hSurf) : vkEffect() +{ + Null(); + nGrp = groups; + Grp = new GROUPREC[nGrp]; memset(Grp, 0, sizeof(GROUPREC) * nGrp); + + for (DWORD i=0;iMap(pDev); +} + + +// =========================================================================================== +// +vkMesh::vkMesh(const MESHGROUPEX *pGroup, const MATERIAL *pMat, SurfNative *pTex) : vkEffect() +{ + Null(); + + // template meshes are stored in system memory + nGrp = 1; + Grp = new GROUPREC[nGrp]; memset(Grp, 0, sizeof(GROUPREC) * nGrp); + nTex = 2; + Tex = new lpSurfNative[nTex]; + Tex[0] = 0; // 'no texture' + Tex[1] = pTex; + nMtrl = 1; + Mtrl = new vkMatExt[nMtrl]; + pGrpTF = new FMATRIX4[nGrp]; + + SetGroupRec(0, pGroup); + + pBuf = new MeshBuffer(MaxVert, MaxFace, this); + + SetMaterial((const D3DMATERIAL9*)pMat, 0, false); + CopyVertices(&Grp[0], pGroup); + + oapiMatrixIdentity(&mTransform); + oapiMatrixIdentity(&mTransformInv); + MeshCatalog.insert(this); + + UpdateBoundingBox(); + CheckMeshStatus(); + + pBuf->Map(pDev); +} + + +// =========================================================================================== +// Create new Instance from a template mesh +// +vkMesh::vkMesh(MESHHANDLE hMesh, const vkMesh &hTemp) +{ + Null(oapiGetMeshFilename(hMesh)); + hOapiMesh = hMesh; + + // Confirm the source is global template + assert(hTemp.bIsTemplate == true); + + nGrp = oapiMeshGroupCount(hMesh); assert(nGrp == hTemp.nGrp); + + if (nGrp == 0) return; + + strcpy_s(name, ARRAYSIZE(name), hTemp.name); + + // Use Template's Vertex Data directly, no need for a local copy unless locally modified. + pBuf = hTemp.pBuf->Reference(); + + BBox = hTemp.BBox; + MaxVert = hTemp.MaxVert; + MaxFace = hTemp.MaxFace; + MeshFlags = hTemp.MeshFlags; + Flags = hTemp.Flags; + + // Clone group records from a tremplate + Grp = new GROUPREC[nGrp]; + memcpy(Grp, hTemp.Grp, sizeof(GROUPREC)*nGrp); + + if (MaxVert == 0 || MaxFace == 0) return; + + // ----------------------------------------------------------------------- + nTex = oapiMeshTextureCount(hMesh) + 1; assert(nTex == hTemp.nTex); + Tex = new lpSurfNative[nTex]; + Tex[0] = 0; // 'no texture' + for (DWORD i = 1; iMap(pDev)" here, source template is already mapped +} + + +// =========================================================================================== +// +vkMesh::~vkMesh() +{ + _TRACE; + + if (MeshCatalog.erase(this)) LogAlw("Mesh %s Removed from catalog", _PTR(this)); + else LogErr("Mesh %s wasn't in meshcatalog", _PTR(this)); + + Release(); + + LogOk("Mesh %s Deleted successfully -------------------------------", _PTR(this)); +} + + +// =========================================================================================== +// +void vkMesh::Release() +{ + SAFE_DELETEA(Locals); + SAFE_DELETEA(Grp); + SAFE_DELETEA(Tex); + SAFE_DELETEA(Mtrl); + SAFE_DELETEA(pGrpTF); + + if (pBuf) if (pBuf->Release()) delete pBuf; + pBuf = nullptr; + + for (auto x : BakedLights) { + for (auto y : x.second.pMap) SAFE_RELEASE(y); + for (auto y : x.second.pSunAO) SAFE_RELEASE(y); + SAFE_RELEASE(x.second.pCombined); + SAFE_RELEASE(x.second.pSunAOComb); + } +} + + +// =========================================================================================== +// Reload a mesh file in response to VESSEL::MeshModified() call +// + +void vkMesh::ReLoadMeshFromHandle(MESHHANDLE hMesh) +{ + hOapiMesh = hMesh; + const char* meshn = oapiGetMeshFilename(hMesh); + strcpy_s(name, 128, meshn ? meshn : "???"); + + // Relese buffers, Tex, Mtrl and Grp counts may have changed. + SAFE_DELETEA(Tex); + SAFE_DELETEA(Mtrl); + SAFE_DELETEA(pGrpTF); + SAFE_DELETEA(Grp); + + nGrp = oapiMeshGroupCount(hMesh); + + if (nGrp == 0) return; + + Grp = new GROUPREC[nGrp]; + memset(Grp, 0, sizeof(GROUPREC) * nGrp); + + MaxFace = 0; // Incremented in SetGroupRec() + MaxVert = 0; + + for (DWORD i = 0; iIsLocalTo(this)) { + if (pBuf->Release()) SAFE_DELETE(pBuf); + } + return; + } + + // If this is an instance, Create a local vertex buffers... + if (pBuf->IsLocalTo(this) == false) pBuf = new MeshBuffer(MaxVert, MaxFace, this); + + // ----------------------------------------------------------------------- + nTex = oapiMeshTextureCount(hMesh) + 1; + Tex = new lpSurfNative[nTex]; + Tex[0] = 0; // 'no texture' + for (DWORD i = 1; iMap(pDev); +} + +// =========================================================================================== +// +const char* vkMesh::GetDirName(int i, int v) +{ + static const char* names[] = { "up", "down", "left", "right", "fwd", "aft" }; + static const char* named[] = { "+y", "-y", "-x", "+x", "+z", "-z" }; + if (v == 0) return names[i]; + return named[i]; +} + +// =========================================================================================== +// +FVECTOR3 vkMesh::GetDir(int i) +{ + switch (i) { + case 0: return FVECTOR3(0, 1, 0); // Up + case 1: return FVECTOR3(0, -1, 0); // Down + case 2: return FVECTOR3(-1, 0, 0); // Left + case 3: return FVECTOR3( 1, 0, 0); // Right + case 4: return FVECTOR3(0, 0, 1); // Fwd + case 5: return FVECTOR3(0, 0, -1); // Aft + } + return FVECTOR3(0, 0, 1); +} + +// =========================================================================================== +// +void vkMesh::ClearBake(int i) +{ + for (int k = 0; k < 16; k++) BakedLights[i].pMap[k] = NULL; + for (int k = 0; k < 6; k++) BakedLights[i].pSunAO[k] = NULL; + BakedLights[i].pCombined = NULL; + BakedLights[i].pSunAOComb = NULL; +} + +// =========================================================================================== +// +void vkMesh::LoadBakedLights() +{ + if (BakedLights.size()) return; // Already Loaded, skip the rest + char id[32]; + + for (int i = 0; i < nTex; i++) + { + if (!Tex[i]) continue; // No base texture, pick next + + for (int j = 0; j < 16; j++) + { + sprintf_s(id, "_bkl%d", j); + LPDIRECT3DTEXTURE9 pTex = NatLoadSpecialTexture(Tex[i]->GetPath(), id, true); + if (pTex) { + if (BakedLights.find(i) == BakedLights.end()) ClearBake(i); + BakedLights[i].pMap[j] = pTex; + } + } + + if (Config->ExpVCLight == 0) + { + for (int j = 0; j < 6; j++) + { + sprintf_s(id, " baked sunlight %s", GetDirName(j, 0)); + LPDIRECT3DTEXTURE9 pTex = NatLoadSpecialTexture(Tex[i]->GetPath(), id, true); + if (pTex) { + if (BakedLights.find(i) == BakedLights.end()) ClearBake(i); + BakedLights[i].pSunAO[j] = pTex; + } + else { + sprintf_s(id, "_bs%s", GetDirName(j, 1)); + LPDIRECT3DTEXTURE9 pTex = NatLoadSpecialTexture(Tex[i]->GetPath(), id, true); + if (pTex) { + if (BakedLights.find(i) == BakedLights.end()) ClearBake(i); + BakedLights[i].pSunAO[j] = pTex; + } + } + } + } + } + + // Construct render surface for all combined maps + for (auto& a : BakedLights) { + int i = a.first; + if (Tex[i]) { + HR(D3DXCreateTexture(pDev, Tex[i]->GetWidth(), Tex[i]->GetHeight(), 0, D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &a.second.pCombined)); + HR(D3DXCreateTexture(pDev, Tex[i]->GetWidth(), Tex[i]->GetHeight(), 0, D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &a.second.pSunAOComb)); + } + } + + bli = BakedLights.begin(); +} + + +// =========================================================================================== +// +void vkMesh::BakeLights(ImageProcessing* pBaker, const FVECTOR3* BakedLightsControl) +{ + if (!pBaker->IsOK()) return; // Baker not initialized + if (DefShader != SHADER_BAKED_VC) return; // Not supported by shader + if (BakedLights.size() == 0) return; // Nothing to bake + + DWORD flags = IPF_POINT | IPF_CLAMP; + FVECTOR3 control[16]; + + pBaker->Activate("PSMain"); + + for (auto x : BakedLights) + { + int slot = 0; + for (int i = 0; i < 16; i++) + { + auto y = x.second.pMap[i]; + if (y) + { + pBaker->SetTextureNative(slot, y, flags); + control[slot] = BakedLightsControl[i]; + slot++; + } + } + + pBaker->SetFloat("fControl", control, slot * sizeof(FVECTOR3)); + pBaker->SetInt("iCount", slot); + + LPDIRECT3DSURFACE9 pSrf = NULL; + + if (x.second.pCombined) + { + if (x.second.pCombined->GetSurfaceLevel(0, &pSrf) == S_OK) + { + pBaker->SetOutputNative(0, pSrf); + pBaker->Execute(true); + SAFE_RELEASE(pSrf); + } + } + } +} + + +// =========================================================================================== +// +void vkMesh::BakeAO(ImageProcessing* pBaker, const FVECTOR3 &vSun, const LVLH &lvlh, const LPDIRECT3DTEXTURE9 pIrrad) +{ + if (!pBaker->IsOK()) return; // Baker not initialized + if (DefShader != SHADER_BAKED_VC) return; // Not supported by shader + if (BakedLights.size() == 0) return; // Nothing to bake + + DWORD flags = IPF_POINT | IPF_CLAMP; + FVECTOR3 control[6]; + FVECTOR3 ParTexCoord[6]; + bool bSE[6]; + float fShine = 1.0f; + bool bShine = true; + + // Compute texcoords for Irradiance lookup for prime directions + // + for (int i = 0; i < 6; i++) { + FVECTOR3 DirL = GetDir(i); + float z = dotp(lvlh.Up, DirL); // lvlh is in a local vessel frame + FVECTOR2 p = FVECTOR2(dotp(lvlh.East, DirL), dotp(lvlh.North, DirL)) / (1.0f + abs(z)); + p *= FVECTOR2(0.2273f, 0.4545f); + ParTexCoord[i] = FVECTOR3(p.x, p.y, z); + } + + pBaker->Activate("PSSunAO"); + + // Compute sunligh intensity factors for prime directions + // + for (int i = 0; i < 6; i++) + { + bSE[i] = false; + control[i] = 0.0f; + + if (DebugControls::IsActive()) + if ((DebugControls::ambdir != i) && (DebugControls::ambdir != -1)) continue; + + auto tex = bli->second.pSunAO[i]; + bSE[i] = (tex != NULL); + if (tex) + { + pBaker->SetTextureNative(i, tex, flags); + control[i] = saturate(dotp(GetDir(i), vSun)); + control[i] *= control[i]; + } + } + + if (DebugControls::IsActive()) { + if (DebugControls::debugFlags & DBG_FLAGS_NOSUNAMB) + for (int i = 0; i < 6; i++) control[i] = 0; + if (DebugControls::debugFlags & DBG_FLAGS_NOPLNAMB) + fShine = 0.0f, bShine = false; + } + + if (pIrrad == nullptr) fShine = 0.0f, bShine = false; + + pBaker->SetTextureNative("tIrrad", pIrrad, IPF_LINEAR | IPF_CLAMP); + pBaker->SetFloat("fControl", control, sizeof(control)); + pBaker->SetFloat("vParTexPos", ParTexCoord, sizeof(ParTexCoord)); + pBaker->SetFloat("fShine", &fShine, sizeof(fShine)); + pBaker->SetBool("bEnabled", bSE, sizeof(bSE)); + pBaker->SetBool("bShine", &bShine, sizeof(bShine)); + + LPDIRECT3DSURFACE9 pSrf = NULL; + + if (bli->second.pSunAOComb) + { + if (bli->second.pSunAOComb->GetSurfaceLevel(0, &pSrf) == S_OK) + { + pBaker->SetOutputNative(0, pSrf); + pBaker->Execute(true); + SAFE_RELEASE(pSrf); + } + } + + bli++; + if (bli == BakedLights.end()) bli = BakedLights.begin(); +} + + +// =========================================================================================== +// +LPDIRECT3DTEXTURE9 vkMesh::GetCombinedMap(int tex_idx) +{ + if (tex_idx < 0 ) for (auto a : BakedLights) if (a.second.pCombined) return a.second.pCombined; + auto it = BakedLights.find(tex_idx); + if (it != BakedLights.end()) return it->second.pCombined; + return NULL; +} + + +// =========================================================================================== +// +void vkMesh::LoadMeshFromHandle(MESHHANDLE hMesh, FVECTOR3 *reorig, float *scale) +{ + hOapiMesh = hMesh; + const char* meshn = oapiGetMeshFilename(hMesh); + strcpy_s(name, 128, meshn ? meshn : "???"); + + nGrp = oapiMeshGroupCount(hMesh); + + if (nGrp == 0) return; + + Grp = new GROUPREC[nGrp]; memset(Grp, 0, sizeof(GROUPREC) * nGrp); + + for (DWORD i = 0; iReload(); +} + +// =========================================================================================== +// +void vkMesh::SetName(const char *name_) +{ + if (name_) strcpy_s(this->name, ARRAYSIZE(this->name), name_); +} + +// =========================================================================================== +// +void vkMesh::SetName(UINT idx) +{ + if ((strncmp(name, "???", 3) == 0) || (name[0] == 0)) sprintf_s(name, ARRAYSIZE(name), "MeshIdx-%u", idx); +} + +// =========================================================================================== +// +bool vkMesh::HasShadow() const +{ + if (!IsOK()) return false; + for (DWORD g=0; g=nTex) { + LogErr("Mesh(%s) has a texture index %u in group %u out of range.", _PTR(this), Grp[i].TexIdx, i); + Grp[i].TexIdx = 0; + bPopUp = true; + } + if (Grp[i].TexIdxEx[0]>=nTex) { + LogErr("Mesh(%s) has a night texture index %u in group %u out of range.", _PTR(this), Grp[i].TexIdxEx[0], i); + Grp[i].TexIdxEx[0] = 0; + bPopUp = true; + } + + if (Grp[i].MtrlIdx!=SPEC_DEFAULT) { + if (Grp[i].MtrlIdx>=nMtrl) { + LogErr("Mesh(%s) has a material index %u in group %u out of range.", _PTR(this), Grp[i].MtrlIdx, i); + Grp[i].MtrlIdx = SPEC_DEFAULT; + bPopUp = true; + } + } + } + if (bPopUp) MessageBoxA(NULL, "Invalid Mesh Detected", "vkClient Error:",MB_OK); +} + + +// =========================================================================================== +// +FVECTOR3 vkMesh::GetGroupSize(DWORD idx) const +{ + if (!IsOK()) return FVECTOR3(0,0,0); + if (idx>=nGrp) return FVECTOR3(0,0,0); + if (Grp[idx].nVert<2) return FVECTOR3(0,0,0); + return _F(Grp[idx].BBox.mx - Grp[idx].BBox.mn); +} + + +// =========================================================================================== +// +void vkMesh::ResetTransformations() +{ + _TRACE; + if (!IsOK()) return; + oapiMatrixIdentity(&mTransform); + oapiMatrixIdentity(&mTransformInv); + bGlobalTF = false; + bBSRecompute = true; + bBSRecomputeAll = true; + for (DWORD i=0;i=nGrp) return; + memcpy(Grp[i].TexIdxEx, mg->TexIdxEx, MAXTEX*sizeof(DWORD)); + memcpy(Grp[i].TexMixEx, mg->TexMixEx, MAXTEX*sizeof(float)); + Grp[i].TexIdx = mg->TexIdx; + Grp[i].MtrlIdx = mg->MtrlIdx; + Grp[i].IdexOff = MaxFace*3; + Grp[i].VertOff = MaxVert; + Grp[i].nFace = mg->nIdx/3; + Grp[i].nVert = mg->nVtx; + Grp[i].UsrFlag = mg->UsrFlag; + Grp[i].IntFlag = mg->Flags; + Grp[i].zBias = mg->zBias; + + oapiMatrixIdentity(&Grp[i].Transform); + + MaxFace += Grp[i].nFace; + MaxVert += Grp[i].nVert; +} + + +// =========================================================================================== +// +bool vkMesh::CopyVertices(GROUPREC *grp, const MESHGROUPEX *mg, FVECTOR3 *reorig, float *scale) +{ + if (!pBuf) return false; + NTVERTEX *pNT = mg->Vtx; + SMVERTEX *pShad = pBuf->pSBSys + grp->VertOff; + NMVERTEX *pVert = pBuf->pVBSys + grp->VertOff; + XMVECTOR *pGeo = pBuf->pGBSys + grp->VertOff; + WORD *pIndex = pBuf->pIBSys + grp->IdexOff; + + for (DWORD i=0;inIdx;i++) pIndex[i] = mg->Idx[i]; + + for (DWORD i=0;inVtx; i++) { + float x = pNT[i].nx; float y = pNT[i].ny; float z = pNT[i].nz; + float b = 1.0f/sqrt(y*y+z*z+x*x); + pVert[i].nx = (x*b); + pVert[i].ny = (y*b); + pVert[i].nz = (z*b); + + if (scale) { + pVert[i].x = pNT[i].x * (*scale); + pVert[i].y = pNT[i].y * (*scale); + pVert[i].z = pNT[i].z * (*scale); + } + else { + pVert[i].x = pNT[i].x; + pVert[i].y = pNT[i].y; + pVert[i].z = pNT[i].z; + } + + pVert[i].u = pNT[i].tu; + pVert[i].v = pNT[i].tv; + pVert[i].w = 1.0f; + pVert[i].tx = 1.0f; + pVert[i].ty = 0.0f; + pVert[i].tz = 0.0f; + + if (reorig) { + pVert[i].x += reorig->x; + pVert[i].y += reorig->y; + pVert[i].z += reorig->z; + } + + pGeo[i] = FVECTOR4(pVert[i].x, pVert[i].y, pVert[i].z, 0.0f).XM(); + + pShad[i].x = pVert[i].x; pShad[i].y = pVert[i].y; pShad[i].z = pVert[i].z; + pShad[i].tu = pVert[i].u; pShad[i].tv = pVert[i].v; + } + + // Check vertex index errors (This is important) + // + for (DWORD i=0;i<(mg->nIdx/3);i++) { + DWORD v0 = i*3; DWORD v1 = v0+1; DWORD v2 = v0+2; + if (pIndex[v0]>=mg->nVtx || pIndex[v1]>=mg->nVtx || pIndex[v2]>=mg->nVtx) { + pIndex[v0] = pIndex[v1] = pIndex[v2] = 0; + } + } + + // For un-instanced mesh the base-offset is zero + if (Config->UseNormalMap) UpdateTangentSpace(pVert, pIndex, mg->nVtx, mg->nIdx/3, grp->TexIdx!=0); + + if (mg->nVtx>0) BoundingBox(pVert, mg->nVtx, &grp->BBox); + else D9ZeroAABB(&grp->BBox); + + return true; +} + + +// =========================================================================================== +// This is required by Client implementation see clbkEditMeshGroup +// +int vkMesh::EditGroup(DWORD grp, GROUPEDITSPEC *ges) +{ + _TRACE; + if (!IsOK()) return 1; + if (grp >= nGrp) return 1; + if (!pBuf) return 1; + + bBSRecompute = true; + + GROUPREC *g = &Grp[grp]; + DWORD flag = ges->flags; + DWORD old = g->UsrFlag; + + if (flag & GRPEDIT_SETUSERFLAG) g->UsrFlag = ges->UsrFlag; + else if (flag & GRPEDIT_ADDUSERFLAG) g->UsrFlag |= ges->UsrFlag; + else if (flag & GRPEDIT_DELUSERFLAG) g->UsrFlag &= ~ges->UsrFlag; + + if (flag & GRPEDIT_VTX) { + + if (pBuf->IsLocalTo(this) == false) + { + // Can't make modifications to a global template + // Create a local copy of the Mesh + // TODO: Create local copy of the group only + pBuf = new MeshBuffer(pBuf, this); + } + + pBuf->MustRemap(MAPMODE_CURRENT); + + SMVERTEX *pShad = pBuf->pSBSys + g->VertOff; + XMVECTOR *pGeo = pBuf->pGBSys + g->VertOff; + NMVERTEX *vtx = pBuf->pVBSys + g->VertOff; + WORD *idx = pBuf->pIBSys + g->IdexOff; + + DWORD i, vi; + if (vtx) { + for (i = 0; i < ges->nVtx; i++) { + vi = (ges->vIdx ? ges->vIdx[i] : i); + if (vi < g->nVert) { + + if (flag & GRPEDIT_VTXCRDX) vtx[vi].x = ges->Vtx[i].x; + else if (flag & GRPEDIT_VTXCRDADDX) vtx[vi].x += ges->Vtx[i].x; + if (flag & GRPEDIT_VTXCRDY) vtx[vi].y = ges->Vtx[i].y; + else if (flag & GRPEDIT_VTXCRDADDY) vtx[vi].y += ges->Vtx[i].y; + if (flag & GRPEDIT_VTXCRDZ) vtx[vi].z = ges->Vtx[i].z; + else if (flag & GRPEDIT_VTXCRDADDZ) vtx[vi].z += ges->Vtx[i].z; + if (flag & GRPEDIT_VTXNMLX) vtx[vi].nx = ges->Vtx[i].nx; + else if (flag & GRPEDIT_VTXNMLADDX) vtx[vi].nx += ges->Vtx[i].nx; + if (flag & GRPEDIT_VTXNMLY) vtx[vi].ny = ges->Vtx[i].ny; + else if (flag & GRPEDIT_VTXNMLADDY) vtx[vi].ny += ges->Vtx[i].ny; + if (flag & GRPEDIT_VTXNMLZ) vtx[vi].nz = ges->Vtx[i].nz; + else if (flag & GRPEDIT_VTXNMLADDZ) vtx[vi].nz += ges->Vtx[i].nz; + if (flag & GRPEDIT_VTXTEXU) vtx[vi].u = ges->Vtx[i].tu; + else if (flag & GRPEDIT_VTXTEXADDU) vtx[vi].u += ges->Vtx[i].tu; + if (flag & GRPEDIT_VTXTEXV) vtx[vi].v = ges->Vtx[i].tv; + else if (flag & GRPEDIT_VTXTEXADDV) vtx[vi].v += ges->Vtx[i].tv; + + if ((flag & GRPEDIT_VTXCRD)!=0 || (flag & GRPEDIT_VTXCRDADD)!=0) { + pGeo[vi] = FVECTOR4(vtx[vi].x, vtx[vi].y, vtx[vi].z, 0.0f).XM(); + + pShad[vi].x = vtx[vi].x; pShad[vi].y = vtx[vi].y; pShad[vi].z = vtx[vi].z; + pShad[vi].tu = vtx[vi].u; pShad[vi].tv = vtx[vi].v; + } + } + } + + if (Config->UseNormalMap) UpdateTangentSpace(vtx, idx, g->nVert, g->nFace, g->TexIdx!=0); + + if (g->nVert>0) BoundingBox(vtx, g->nVert, &g->BBox); + else D9ZeroAABB(&g->BBox); + } + } + + UpdateFlags(); + return 0; +} + + +NTVERTEX Convert(NMVERTEX &v) +{ + NTVERTEX n; + n.x = v.x; n.y = v.y; n.z = v.z; + n.nx = v.nx; n.ny = v.ny; n.nz = v.nz; + n.tu = v.u; n.tv = v.v; + return n; +} + + +int vkMesh::GetGroup (DWORD grp, GROUPREQUESTSPEC *grs) +{ + if (!pBuf) return 1; + + static NTVERTEX zero = {0,0,0, 0,0,0, 0,0}; + if (grp >= nGrp) return 1; + DWORD nv = Grp[grp].nVert; + DWORD ni = Grp[grp].nFace*3; + DWORD i, vi; + int ret = 0; + + if (grs->nVtx && grs->Vtx) { // vertex data requested + NMVERTEX *vtx = pBuf->pVBSys + Grp[grp].VertOff; + if (vtx) { + if (grs->VtxPerm) { // random access data request + for (i = 0; i < grs->nVtx; i++) { + vi = grs->VtxPerm[i]; + if (vi < nv) { + grs->Vtx[i] = Convert(vtx[vi]); + } else { + grs->Vtx[i] = zero; + ret = 1; + } + } + } else { + if (grs->nVtx > nv) grs->nVtx = nv; + for (i=0;inVtx;i++) grs->Vtx[i] = Convert(vtx[i]); + } + } + else return 1; + } + + if (grs->nIdx && grs->Idx) { // index data requested + WORD *idx = pBuf->pIBSys + Grp[grp].IdexOff; + if (idx) { + if (grs->IdxPerm) { // random access data request + for (i = 0; i < grs->nIdx; i++) { + vi = grs->IdxPerm[i]; + if (vi < ni) { + grs->Idx[i] = idx[vi]; + } else { + grs->Idx[i] = 0; + ret = 1; + } + } + } else { + if (grs->nIdx > ni) grs->nIdx = ni; + for (i=0;inIdx;i++) grs->Idx[i] = idx[i]; + } + } + else return 1; + } + + grs->MtrlIdx = Grp[grp].MtrlIdx; + grs->TexIdx = Grp[grp].TexIdx; + return ret; +} + +// =========================================================================================== +// +void vkMesh::UpdateFlags() +{ + Flags = 0; + for (DWORD i = 0; i < nGrp; i++) + { + if (Grp[i].UsrFlag & 0x20) Flags |= 0x20; + } +} + +// =========================================================================================== +// +void vkMesh::SetMFDScreenId(DWORD idx, WORD id) +{ + if (idx= nTex) { + LogErr("vkMesh::SetTexture(%u, %s) index out of range",texidx, _PTR(tex)); + return false; + } + Tex[texidx] = (lpSurfNative)tex; + LogBlu("vkMesh(%s)::SetTexture(%u, %s) (%s)", _PTR(this), texidx, _PTR(tex), SURFACE(tex)->GetName()); + CheckMeshStatus(); + return true; +} + +// =========================================================================================== +// +DWORD vkMesh::GetMeshGroupMaterialIdx(DWORD idx) const +{ + if (!IsOK()) return 0; + if (idx>=nGrp) return 0; + return Grp[idx].MtrlIdx; +} + +// =========================================================================================== +// +DWORD vkMesh::GetMeshGroupTextureIdx(DWORD idx) const +{ + if (!IsOK()) return 0; + if (idx>=nGrp) return 0; + return Grp[idx].TexIdx; +} + +// =========================================================================================== +// +bool vkMesh::HasTexture(SURFHANDLE hSurf) const +{ + if (!IsOK()) return false; + for (DWORD i=0;i= nMtrl) return NULL; + return &Mtrl[idx]; +} + +// =========================================================================================== +// +bool vkMesh::GetMaterial(vkMatExt *pMat, DWORD idx) const +{ + if (pMat && idx= nMtrl) return 4; + + // SET --------------------------------------------------------------- + if (value) { + switch (mid) { + case MatProp::Diffuse: + Mtrl[idx].Diffuse = *((FVECTOR4*)value); + Mtrl[idx].ModFlags |= vkMATEX_DIFFUSE; + bMtrlModidied = true; + return 0; + case MatProp::Ambient: + Mtrl[idx].Ambient = *((FVECTOR3*)value); + Mtrl[idx].ModFlags |= vkMATEX_AMBIENT; + bMtrlModidied = true; + return 0; + case MatProp::Specular: + Mtrl[idx].Specular = *((FVECTOR4*)value); + Mtrl[idx].ModFlags |= vkMATEX_SPECULAR; + bMtrlModidied = true; + return 0; + case MatProp::Light: + Mtrl[idx].Emissive = *((FVECTOR3*)value); + Mtrl[idx].ModFlags |= vkMATEX_EMISSIVE; + bMtrlModidied = true; + return 0; + case MatProp::Emission: + Mtrl[idx].Emission2 = *((FVECTOR3*)value); + Mtrl[idx].ModFlags |= vkMATEX_EMISSION2; + bMtrlModidied = true; + return 0; + case MatProp::Reflect: + Mtrl[idx].Reflect = *((FVECTOR3*)value); + Mtrl[idx].ModFlags |= vkMATEX_REFLECT; + bMtrlModidied = true; + return 0; + case MatProp::Smooth: + Mtrl[idx].Roughness = FVECTOR2(value->g, value->r); + Mtrl[idx].ModFlags |= vkMATEX_ROUGHNESS; + bMtrlModidied = true; + return 0; + case MatProp::Fresnel: + Mtrl[idx].Fresnel = *((FVECTOR3*)value); + Mtrl[idx].ModFlags |= vkMATEX_FRESNEL; + bMtrlModidied = true; + return 0; + case MatProp::Metal: + Mtrl[idx].Metalness = value->r; + Mtrl[idx].ModFlags |= vkMATEX_METALNESS; + bMtrlModidied = true; + return 0; + case MatProp::SpecialFX: + Mtrl[idx].SpecialFX = *((FVECTOR4*)value); + Mtrl[idx].ModFlags |= vkMATEX_SPECIALFX; + bMtrlModidied = true; + return 0; + } + return 5; + } + + // CLEAR ------------------------------------------------------------- + + if (value == NULL) + { + switch (mid) { + case MatProp::Diffuse: + Mtrl[idx].Diffuse = FVECTOR4(1, 1, 1, 1); + Mtrl[idx].ModFlags &= (~vkMATEX_DIFFUSE); + bMtrlModidied = true; + return 0; + case MatProp::Ambient: + Mtrl[idx].Ambient = FVECTOR3(1, 1, 1); + Mtrl[idx].ModFlags &= (~vkMATEX_AMBIENT); + bMtrlModidied = true; + return 0; + case MatProp::Specular: + Mtrl[idx].Specular = FVECTOR4(1, 1, 1, 20); + Mtrl[idx].ModFlags &= (~vkMATEX_SPECULAR); + bMtrlModidied = true; + return 0; + case MatProp::Light: + Mtrl[idx].Emissive = FVECTOR3(0, 0, 0); + Mtrl[idx].ModFlags &= (~vkMATEX_EMISSIVE); + bMtrlModidied = true; + return 0; + case MatProp::Emission: + Mtrl[idx].Emission2 = FVECTOR3(1, 1, 1); + Mtrl[idx].ModFlags &= (~vkMATEX_EMISSION2); + bMtrlModidied = true; + return 0; + case MatProp::Reflect: + Mtrl[idx].Reflect = FVECTOR3(0.0f, 0, 0); + Mtrl[idx].ModFlags &= (~vkMATEX_REFLECT); + bMtrlModidied = true; + return 0; + case MatProp::Smooth: + Mtrl[idx].Roughness = FVECTOR2(0.0f, 1.0f); + Mtrl[idx].ModFlags &= (~vkMATEX_ROUGHNESS); + bMtrlModidied = true; + return 0; + case MatProp::Fresnel: + Mtrl[idx].Fresnel = FVECTOR3(1, 0, 1024.0f); + Mtrl[idx].ModFlags &= (~vkMATEX_FRESNEL); + bMtrlModidied = true; + return 0; + case MatProp::Metal: + Mtrl[idx].Metalness = 0.0f; + Mtrl[idx].ModFlags &= (~vkMATEX_METALNESS); + bMtrlModidied = true; + return 0; + case MatProp::SpecialFX: + Mtrl[idx].SpecialFX = FVECTOR4(0.0f, 0, 0, 0); + Mtrl[idx].ModFlags &= (~vkMATEX_SPECIALFX); + bMtrlModidied = true; + return 0; + } + return 5; + } + return 5; +} + + +// =========================================================================================== +//0 = success, 1=no graphics engine attached, +//2 = graphics engine does not support operation, 3 = invalid mesh handle, +//4 = material index out of range, 5 = material property not supported by shader used by the mesh. +// +int vkMesh::GetMaterialEx(DWORD idx, MatProp mid, FVECTOR4* value) +{ + if (idx >= nMtrl) return 4; + + if (value) + { + switch (mid) + { + case MatProp::Diffuse: + *((FVECTOR4*)value) = Mtrl[idx].Diffuse; + return 0; + case MatProp::Ambient: + *((FVECTOR3*)value) = Mtrl[idx].Ambient; + return 0; + case MatProp::Specular: + *((FVECTOR4*)value) = Mtrl[idx].Specular; + return 0; + case MatProp::Light: + *((FVECTOR3*)value) = Mtrl[idx].Emissive; + return 0; + case MatProp::Emission: + if ((Mtrl[idx].ModFlags&vkMATEX_EMISSION2) == 0) return -2; + *((FVECTOR3*)value) = Mtrl[idx].Emission2; + return 0; + case MatProp::Reflect: + if ((Mtrl[idx].ModFlags&vkMATEX_REFLECT) == 0) return -2; + *((FVECTOR3*)value) = Mtrl[idx].Reflect; + return 0; + case MatProp::Smooth: + if ((Mtrl[idx].ModFlags&vkMATEX_ROUGHNESS) == 0) return -2; + value->g = Mtrl[idx].Roughness.x; + value->r = Mtrl[idx].Roughness.y; + return 0; + case MatProp::Fresnel: + if ((Mtrl[idx].ModFlags&vkMATEX_FRESNEL) == 0) return -2; + *((FVECTOR3*)value) = Mtrl[idx].Fresnel; + return 0; + case MatProp::Metal: + if ((Mtrl[idx].ModFlags& vkMATEX_METALNESS) == 0) return -2; + value->r = Mtrl[idx].Metalness; + return 0; + case MatProp::SpecialFX: + if ((Mtrl[idx].ModFlags& vkMATEX_SPECIALFX) == 0) return -2; + *((FVECTOR4*)value) = Mtrl[idx].SpecialFX; + return 0; + } + return 5; + } + return 5; +} + +// =========================================================================================== +// +void vkMesh::SetAmbientColor(const FVECTOR3& c) +{ + _TRACE; + if (!IsOK()) return; + cAmbient = c; +} + +// =========================================================================================== +// +const FVECTOR3& vkMesh::GetAmbientColor() +{ + _TRACE; + return cAmbient; +} + +// =========================================================================================== +// +void vkMesh::SetupFog(const FMATRIX4* pW) +{ + _TRACE; + if (!IsOK()) return; + FX->SetVector(eAttennuate, _DX(FVECTOR4(1.0f, 1, 1, 1))); + FX->SetVector(eInScatter, _DX(FVECTOR4(0.0f, 0, 0, 0))); +} + +// =========================================================================================== +// +void vkMesh::RenderGroup(int idx) +{ + RenderGroup(GetGroup(idx)); +} + +// =========================================================================================== +// +void vkMesh::RenderGroup(const GROUPREC *grp) +{ + _TRACE; + if (!IsOK()) return; + if (!grp) return; + + pBuf->Map(pDev); + + pDev->SetVertexDeclaration(pMeshVertexDecl); + pDev->SetStreamSource(0, pBuf->pVB, 0, sizeof(NMVERTEX)); + pDev->SetIndices(pBuf->pIB); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, grp->VertOff, 0, grp->nVert, grp->IdexOff, grp->nFace); + vkStats.Mesh.Vertices += grp->nVert; + vkStats.Mesh.MeshGrps++; +} + + +// reset stucts template +template void reset (T& _p) +#if __cplusplus > 201103L // C++11 + { _p = { 0 }; } +#else + { static const T _Empty = { 0 }; _p = _Empty; } +#endif + + +// =========================================================================================== +// +void vkMesh::ResetRenderStatus() +{ + for (DWORD g = 0; g < nGrp; g++) Grp[g].bRendered = false; +} + + +// =========================================================================================== +// +bool vkMesh::IsGroupRendered(DWORD idx) const +{ + return Grp[idx].bRendered; +} + + +// ================================================================================================ +// Analyze mesh. Must be called when ever there is a change in textures or materials +// +void vkMesh::CheckMeshStatus() +{ + UpdateFlags(); + + bCanRenderFast = true; + bIsReflective = false; + vkMatExt *mat = NULL; + + for (DWORD g = 0; g < nGrp; g++) { + + Grp[g].Shader = SHADER_PBR; + Grp[g].PBRStatus = 0; + + DWORD ti = Grp[g].TexIdx; + + if (Tex[ti] != NULL) { + if (Tex[ti]->IsAdvanced()) { + bCanRenderFast = false; + if (Tex[ti]->GetMap(MAP_METALNESS) && (DefShader == SHADER_NULL)) DefShader = SHADER_METALNESS; + if (Tex[ti]->GetMap(MAP_SPECULAR)) Grp[g].PBRStatus |= 0x2; + if (Tex[ti]->GetMap(MAP_ROUGHNESS)) Grp[g].PBRStatus |= 0x4; + if (Tex[ti]->GetMap(MAP_REFLECTION)) Grp[g].PBRStatus |= 0x8; + if (Tex[ti]->GetMap(MAP_TRANSLUCENCE)) Grp[g].Shader = SHADER_ADV; + if (Tex[ti]->GetMap(MAP_TRANSMITTANCE)) Grp[g].Shader = SHADER_ADV; + } + } + + if (Grp[g].MtrlIdx == SPEC_DEFAULT) mat = &defmat; + else mat = &Mtrl[Grp[g].MtrlIdx]; + + if (mat->ModFlags&vkMATEX_SPECULAR) Grp[g].PBRStatus |= 0x1; + if (mat->ModFlags&vkMATEX_REFLECT) Grp[g].PBRStatus |= 0x8; + if (mat->ModFlags&vkMATEX_ROUGHNESS) Grp[g].PBRStatus |= 0x4; + if (mat->ModFlags&vkMATEX_FRESNEL) Grp[g].PBRStatus |= 0x10; + } + + + for (DWORD g = 0; g < nGrp; g++) { + if (Grp[g].PBRStatus >= (0x8|0x2)) bIsReflective = true; + if (Grp[g].PBRStatus >= 0x2) bCanRenderFast = false; + } + + if (DefShader == SHADER_METALNESS) { + bIsReflective = true; + for (DWORD g = 0; g < nGrp; g++) Grp[g].Shader = SHADER_METALNESS; + } + + if (DefShader == SHADER_BAKED_VC) { + bIsReflective = true; + for (DWORD g = 0; g < nGrp; g++) Grp[g].Shader = SHADER_BAKED_VC; + } +} + + +// =========================================================================================== +// +void vkMesh::SetDefaultShader(WORD shader) +{ + DefShader = shader; + bMtrlModidied = true; + CheckMeshStatus(); + + if (shader == SHADER_BAKED_VC) { + LoadBakedLights(); + } +} + + +// =========================================================================================== +// +void vkMesh::ConfigureAtmo() +{ + //LogSunLight(sunLight); + float x = 1.0f - saturate(max(sunLight.Color.r, sunLight.Color.b) * 2.0f); + FX->SetFloat(eNight, x); + if (DebugControls::IsActive() && DebugControls::debugFlags & DBG_FLAGS_NODYNSUN) + sunLight.Color = sunLight.Ambient = 0; + FX->SetValue(eSun, &sunLight, sizeof(vkSun)); +} + + +// =========================================================================================== +// Setup shadow map samplers and textures +// +void vkMesh::ConfigureShadows() +{ + for (int i = 0; i < SHM_CASCADE_COUNT; i++) + { + int idx = 13 + i; // Sampler index + HR(pDev->SetSamplerState(idx, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP)); + HR(pDev->SetSamplerState(idx, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP)); + HR(pDev->SetSamplerState(idx, D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP)); + HR(pDev->SetSamplerState(idx, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR)); + HR(pDev->SetSamplerState(idx, D3DSAMP_MINFILTER, D3DTEXF_LINEAR)); + HR(pDev->SetSamplerState(idx, D3DSAMP_MIPFILTER, D3DTEXF_NONE)); + HR(pDev->SetTexture(idx, pShadowMap[i])); + } + HR(FX->SetValue(vkEffect::eSHDSubRect, ShdSubRect, sizeof(ShdSubRect))); +} + + +// =========================================================================================== +// static: +void vkMesh::SetShadows(const SHADOWMAP *sprm) +{ + if (sprm) { + for (int i = 0; i < SHM_CASCADE_COUNT; i++) { + pShadowMap[i] = sprm->Map(i); + ShdSubRect[i] = sprm->SubrectTF[i]; + } + } + else { + for (int i = 0; i < SHM_CASCADE_COUNT; i++) { + pShadowMap[i] = NULL; + ShdSubRect[i] = _F4(0, 0, 0, 0); + } + } +} + + +// ================================================================================================ +// This is a rendering routine for a Exterior Mesh, non-spherical moons/asteroids +// +void vkMesh::Render(const FMATRIX4* pW, const ENVCAMREC* em, int iTech) +{ + _TRACE; + + if (!IsOK()) return; + + pBuf->Map(pDev); + + // Check material status + // + if (bMtrlModidied) { + CheckMeshStatus(); + bMtrlModidied = false; + } + + if (DebugControls::IsActive() == false) { + if (bCanRenderFast && vClass != VCLASS_XR2) { + RenderFast(pW, iTech); + return; + } + } + + DWORD flags=0, selmsh=0, selgrp=0, displ=0; // Debug Variables + bool bActiveVisual = false; + + const VCHUDSPEC *hudspec; + + if (DebugControls::IsActive()) { + flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + selmsh = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDMESH); + selgrp = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDGROUP); + displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); + bActiveVisual = (g_pCurrentVisual == DebugControls::GetVisual()); + if (displ>0 && !bActiveVisual) return; + if ((displ==2 || displ==3) && g_uCurrentMesh!=selmsh) return; + } + + Scene *scn = gc->GetScene(); + + bool bWorldMesh = false; + bool bMeshCull = true; + bool bTextured = true; + bool bGroupCull = true; + bool bUpdateFlow = true; + bool bShadowProjection = false; + bool bVirtualCockpit = false; + + + + switch (iTech) { + case RENDER_VC: + EnablePlanetGlow(false); + bVirtualCockpit = true; + break; + case RENDER_BASE: + EnablePlanetGlow(false); + bMeshCull = false; + bShadowProjection = true; + break; + case RENDER_BASEBS: + EnablePlanetGlow(false); + bMeshCull = false; + bShadowProjection = true; + break; + case RENDER_ASTEROID: + EnablePlanetGlow(false); + bMeshCull = false; + bGroupCull = false; + bShadowProjection = true; + break; + case RENDER_VESSEL: + EnablePlanetGlow(true); + break; + } + + HR(vkEffect::FX->SetBool(vkEffect::eBaseBuilding, bShadowProjection)); + HR(vkEffect::FX->SetBool(vkEffect::eCockpit, bVirtualCockpit)); + + FVECTOR4 Field; + FMATRIX4 mWorldView, q; + + oapiMatrixMultiply(&mWorldView, pW, scn->GetViewMatrix()); + + if (bMeshCull || bGroupCull) Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); + + if (bMeshCull) if (!D9IsAABBVisible(&BBox, &mWorldView, &Field)) { + if (flags&(DBG_FLAGS_BOXES|DBG_FLAGS_SPHERES)) RenderBoundingBox(pW); + return; + } + + FMATRIX4 mWorldMesh; + + if (bGlobalTF) oapiMatrixMultiply(&mWorldMesh, &mTransform, pW); + else mWorldMesh = *pW; + + vkStats.Mesh.Meshes++; + + vkMatExt *mat, *old_mat = NULL; + SURFHANDLE old_tex = NULL; + + pDev->SetVertexDeclaration(pMeshVertexDecl); + pDev->SetStreamSource(0, pBuf->pVB, 0, sizeof(NMVERTEX)); + pDev->SetIndices(pBuf->pIB); + + ConfigureShadows(); + + if (flags&DBG_FLAGS_DUALSIDED) pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + + FX->SetTechnique(eVesselTech); + FX->SetBool(eFresnel, false); + FX->SetBool(eLightsEnabled, false); + FX->SetBool(eOITEnable, false); + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0, 0, 0))); + FX->SetValue(eVCAmbient, &cAmbient, sizeof(FVECTOR3)); + + ConfigureAtmo(); + + + TexFlow FC; reset(FC); + + + // Setup Local lights ------------------------------------------- + // + const vkLight *pLights = gc->GetScene()->GetLights(); + int nSceneLights = gc->GetScene()->GetLightCount(); + + for (int i = 0; i < Config->MaxLights(); i++) memcpy(&Locals[i], &null_light, sizeof(LightStruct)); + + int nMeshLights = 0; + + if (pLights && nSceneLights>0) { + + FVECTOR3 pos = oapiTransformCoord(&_F(BBox.bs), pW); + + // Find all local lights effecting this mesh ------------------------------------------ + // + for (int i = 0; i < nSceneLights; i++) { + float il = pLights[i].GetIlluminance(pos, BBox.bs.w); + if (il > 0.005f) { + LightList[nMeshLights].illuminace = il; + LightList[nMeshLights++].idx = i; + } + } + + if (nMeshLights > 0) { + + FX->SetBool(eLightsEnabled, true); + + // If any, Sort the list based on illuminance ------------------------------------------- + qsort(LightList, nMeshLights, sizeof(_LightList), compare_lights); + + nMeshLights = min(nMeshLights, Config->MaxLights()); + + // Create a list of N most effective lights --------------------------------------------- + for (int i = 0; i < nMeshLights; i++) { + memcpy(&Locals[i], &pLights[LightList[i].idx], sizeof(LightStruct)); + + // Override application configuration to prevent oversaturation of lights at point plank range. + if (scn->GetRenderPass() == RENDERPASS_MAINSCENE) + Locals[i].Attenuation.x = max(Locals[i].Attenuation.x, float(Config->GFXLocalMax)); + } + } + } + + FX->SetValue(eLights, Locals, sizeof(LightStruct) * Config->MaxLights()); + + + // Setup Env Maps ------------------------------------------- + // + if (em && em->pCube && em->pIrrad) { + FX->SetBool(eEnvMapEnable, true); + FX->SetTexture(eEnvMapA, em->pCube); + FX->SetTexture(eIrradMap, em->pIrrad); + } + else { + FX->SetBool(eEnvMapEnable, false); + } + + bool bNoAmbient = (Config->ExpVCLight == 1); + + /*if (DebugControls::IsActive()) { + bNoAmbient = (flags & DBG_FLAGS_NOSUNAMB) != 0 & (flags & DBG_FLAGS_NOPLNAMB) != 0; + }*/ + + + + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + + WORD CurrentShader = SHADER_NULL; + + bool bRefl = true; + int iEnvCam = -1; + + for (DWORD g=0; gEndPass()); } + HR(FX->BeginPass(Grp[g].Shader)); + CurrentShader = Grp[g].Shader; + if (iTech == RENDER_BASEBS) pDev->SetRenderState(D3DRS_ZENABLE, 0); // Must be here because BeginPass() sets it enabled + } + + + + // Mesh Debugger ------------------------------------------------------------------------------------------- + // + if (DebugControls::IsActive()) { + + if (bActiveVisual) { + + if (displ==3 && g!=selgrp) continue; + + FX->SetVector(eColor, _DX(_F4(0, 0, 0, 0))); + + if (flags&DBG_FLAGS_HLMESH) { + if (g_uCurrentMesh==selmsh) { + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0.0f, 0.5f, 0.5f))); + } + } + if (flags&DBG_FLAGS_HLGROUP) { + if (g == selgrp && g_uCurrentMesh == selmsh) { + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0.5f, 0.0f, 0.5f))); + } + } + } + } + + + // --------------------------------------------------------------------------------------------------------- + // + if (Tex[ti] != old_tex) { + reset(FC); + bUpdateFlow = true; + } + + if (Tex[ti]==NULL) bTextured = false, old_tex = NULL; + else bTextured = true; + + + + // Setup Textures and Normal Maps ========================================================================== + // + if (bTextured) { + + if (Tex[ti]!=old_tex) { + + vkStats.Mesh.TexChanges++; + + old_tex = Tex[ti]; + + FX->SetTexture(eTex0, Tex[ti]->GetTexture()); + + if (CurrentShader == SHADER_LEGACY) { + if (tni && Grp[g].TexMixEx[0] < 0.5f) tni = 0; + if (tni && Tex[tni]) FX->SetTexture(eEmisMap, Tex[tni]->GetTexture()); + } + else { + LPDIRECT3DTEXTURE9 pTransl = NULL; + LPDIRECT3DTEXTURE9 pTransm = NULL; + LPDIRECT3DTEXTURE9 pMetl = NULL; + LPDIRECT3DTEXTURE9 pRefl = Tex[ti]->GetMap(MAP_REFLECTION); + LPDIRECT3DTEXTURE9 pNorm = Tex[ti]->GetMap(MAP_NORMAL); + LPDIRECT3DTEXTURE9 pRghn = Tex[ti]->GetMap(MAP_ROUGHNESS); + LPDIRECT3DTEXTURE9 pEmis = Tex[ti]->GetMap(MAP_EMISSION); + LPDIRECT3DTEXTURE9 pHeat = Tex[ti]->GetMap(MAP_HEAT); + LPDIRECT3DTEXTURE9 pSpec = Tex[ti]->GetMap(MAP_SPECULAR); + + if (tni && Grp[g].TexMixEx[0] < 0.5f) tni = 0; + if (!pEmis && tni && Tex[tni]) pEmis = Tex[tni]->GetTexture(); + + if (pNorm) FX->SetTexture(eTex3, pNorm); + if (pRghn) FX->SetTexture(eRghnMap, pRghn); + if (pMetl) FX->SetTexture(eMetlMap, pMetl); + if (pEmis) FX->SetTexture(eEmisMap, pEmis); + if (pHeat) FX->SetTexture(eHeatMap, pHeat); + if (pSpec) FX->SetTexture(eSpecMap, pSpec); + if (pRefl) FX->SetTexture(eReflMap, pRefl); + + if (CurrentShader == SHADER_ADV + || CurrentShader == SHADER_METALNESS + || CurrentShader == SHADER_BAKED_VC) + { + pTransl = Tex[ti]->GetMap(MAP_TRANSLUCENCE); + pTransm = Tex[ti]->GetMap(MAP_TRANSMITTANCE); + pMetl = Tex[ti]->GetMap(MAP_METALNESS); + + if (pTransl) FX->SetTexture(eTranslMap, pTransl); + if (pTransm) FX->SetTexture(eTransmMap, pTransm); + if (pMetl) FX->SetTexture(eMetlMap, pMetl); + + FC.Transl = (pTransl != NULL); + FC.Transm = (pTransm != NULL); + FC.Metl = (pMetl != NULL); + } + else { + FC.Transl = false; + FC.Transm = false; + FC.Metl = false; + } + + if (CurrentShader == SHADER_BAKED_VC) + { + auto bm = BakedLights.find(ti); + + LPDIRECT3DTEXTURE9 pAmbi = Tex[ti]->GetMap(MAP_AMBIENT); + LPDIRECT3DTEXTURE9 pComb = (bm == BakedLights.end() ? NULL : bm->second.pCombined); + LPDIRECT3DTEXTURE9 pSun = (bm == BakedLights.end() ? NULL : bm->second.pSunAOComb); + + if (pAmbi) FX->SetTexture(eAmbientMap, pAmbi); + if (pComb) FX->SetTexture(eCombinedMap, pComb); + if (pSun) FX->SetTexture(eCombSunMap, pSun); + + FC.Baked = (pComb != NULL); + FC.BakedAO = (pAmbi != NULL); + FC.BakedAmb = (pSun != NULL) & !bNoAmbient; + } + + FC.Emis = (pEmis != NULL); + FC.Norm = (pNorm != NULL); + FC.Rghn = (pRghn != NULL); + FC.Heat = (pHeat != NULL); + FC.Spec = (pSpec != NULL); + FC.Refl = (pRefl != NULL); + } + } + } + + // Apply MFD Screen Override ================================================================================ + // + if (Grp[g].MFDScreenId) { + + bTextured = true; + old_tex = NULL; + old_mat = NULL; + reset(FC); + bUpdateFlow = true; + + SURFHANDLE hMFD; + if (bHUD) hMFD = gc->GetVCHUDSurface(&hudspec); + else hMFD = gc->GetMFDSurface(Grp[g].MFDScreenId - 1); + + if (hMFD) FX->SetTexture(eTex0, SURFACE(hMFD)->GetTexture()); + else FX->SetTexture(eTex0, gc->GetDefaultTexture()->GetTexture()); + + if (Grp[g].MtrlIdx==SPEC_DEFAULT) mat = &mfdmat; + else mat = &Mtrl[Grp[g].MtrlIdx]; + + if (bModulateMatAlpha || bTextured==false) FX->SetFloat(eMtrlAlpha, mat->Diffuse.w); + else FX->SetFloat(eMtrlAlpha, 1.0f); + + FX->SetValue(eMtrl, mat, sizeof(vkMatExt)-4); + } + + + // Setup Mesh group material ========================================================================== + // + else { + + if (Grp[g].MtrlIdx==SPEC_DEFAULT) mat = &defmat; + else mat = &Mtrl[Grp[g].MtrlIdx]; + + if (mat!=old_mat) { + + vkStats.Mesh.MtrlChanges++; + + old_mat = mat; + + FX->SetValue(eMtrl, mat, sizeof(vkMatExt)-4); + + if (bModulateMatAlpha || bTextured==false) FX->SetFloat(eMtrlAlpha, mat->Diffuse.w); + else FX->SetFloat(eMtrlAlpha, 1.0f); + } + } + + + // Apply Animations ========================================================================================= + // + if (Grp[g].bTransform) { + bWorldMesh = false; + FX->SetMatrix(eW, _DX(oapiMatrixMultiply(&q, &pGrpTF[g], pW))); + } + else if (!bWorldMesh) { + FX->SetMatrix(eW, _DX(mWorldMesh)); + bWorldMesh = true; + } + + if (bUpdateFlow) { + bUpdateFlow = false; + HR(FX->SetValue(eFlow, &FC, sizeof(TexFlow))); + } + + bool bPBR = (Grp[g].PBRStatus & 0xF) == (0x8 + 0x4); + bool bRGH = (Grp[g].PBRStatus & 0xE) == (0x8 + 0x2); + bool bNoL = (Grp[g].UsrFlag & 0x04) != 0; + bool bNoC = (Grp[g].UsrFlag & 0x10) != 0; + bool bOIT = (Grp[g].UsrFlag & 0x20) != 0; + bool bENV = false; + bool bFRS = false; + + // Setup Mesh drawing options ================================================================================= + // + FX->SetBool(eOITEnable, bOIT); + FX->SetBool(eTextured, bTextured); + FX->SetBool(eFullyLit, bNoL); + FX->SetBool(eSwitch, bPBR); + FX->SetBool(eRghnSw, bRGH); + + if (bNoC) FX->SetValue(eNoColor, &FVECTOR3(1, 1, 1), sizeof(FVECTOR3)); + else FX->SetValue(eNoColor, &FVECTOR3(0, 0, 0), sizeof(FVECTOR3)); + + // Update envmap and fresnel status as required + if (bRefl) { + bFRS = (Grp[g].PBRStatus & 0x1E) >= 0x10; + FX->SetBool(eFresnel, bFRS); + if (IsReflective()) { + bENV = ((Grp[g].PBRStatus & 0x1E) >= 0xA) | (Grp[g].Shader == SHADER_METALNESS) | (Grp[g].Shader == SHADER_BAKED_VC); + FX->SetBool(eEnvMapEnable, bENV); + } + } + + + + FX->CommitChanges(); + + + + // Mesh Debugger ------------------------------------------------------------------------------------------- + // + if (DebugControls::IsActive()) { + + if ((bActiveVisual) && (g == selgrp) && (g_uCurrentMesh == selmsh)) { + + bool bAdd = (Grp[g].UsrFlag & 0x08) != 0; + bool bNoS = (Grp[g].UsrFlag & 0x01) != 0; + + static const char *YesNo[2] = { "No", "Yes" }; + static const char *LPW[2] = { "Legacy", "PBR" }; + static const char *RGH[2] = { "Disabled", "Enabled" }; + static const char *Shaders[7] = { "PBR", "PBR-ADV", "FAST", "XR2", "METALNESS", "BAKED_VC", "???"}; + + DebugControls::Append("MeshIdx = %d, GrpIdx = %d\n", g_uCurrentMesh, g); + DebugControls::Append("MtrlIdx = %d, TexIdx = %d\n", Grp[g].MtrlIdx, Grp[g].TexIdx); + DebugControls::Append("FaceCnt = %d, VtxCnt = %d\n", Grp[g].nFace, Grp[g].nVert); + + DebugControls::Append("GroupFlags.. = 0x%X\n", Grp[g].UsrFlag); + DebugControls::Append("Shader...... = %s\n", Shaders[CurrentShader]); + DebugControls::Append("Textured.... = %s\n", YesNo[bTextured]); + DebugControls::Append("ModMatAlpha. = %s\n", YesNo[bModulateMatAlpha]); + DebugControls::Append("NoColor..... = %s\n", YesNo[bNoC]); + DebugControls::Append("NoLighting.. = %s\n", YesNo[bNoL]); + DebugControls::Append("NoShadow.... = %s\n", YesNo[bNoS]); + DebugControls::Append("Additive.... = %s\n", YesNo[bAdd]); + + if (CurrentShader == SHADER_PBR || CurrentShader == SHADER_ADV || CurrentShader == SHADER_METALNESS) + { + if (CurrentShader != SHADER_METALNESS) { + DebugControls::Append("\nPBR-Shader State:\n"); + DebugControls::Append("PBR-Switch.. = %s\n", LPW[bPBR]); + DebugControls::Append("Rghn-Conver. = %s\n", RGH[bRGH]); + DebugControls::Append("Fresnel Mode = %s\n", RGH[bFRS]); + } + + DebugControls::Append("Env Mapping. = %s\n", RGH[bENV]); + + DebugControls::Append("TextureMaps = [ "); + if (FC.Emis) DebugControls::Append("emis "); + if (FC.Metl) DebugControls::Append("metl "); + if (FC.Norm) DebugControls::Append("norm "); + if (FC.Rghn) DebugControls::Append("rghn "); + if (FC.Spec) DebugControls::Append("spec "); + if (FC.Refl) DebugControls::Append("refl "); + if (FC.Transl) DebugControls::Append("transl "); + if (FC.Transm) DebugControls::Append("transm "); + DebugControls::Append("]\n"); + } + + if (CurrentShader == SHADER_BAKED_VC) { + DebugControls::Append("BakedLights = [ "); + for (auto& a : BakedLights) DebugControls::Append(" %d", a.first); + DebugControls::Append("]\n"); + } + + DebugControls::Append("Local Lights = %d\n", nMeshLights); + + DebugControls::Refresh(); + } + } + + + // Start rendering ------------------------------------------------------------------------------------------- + // + if (bHUD) { + pDev->SetRenderState(D3DRS_ZENABLE, 0); + pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + } + + if (Grp[g].bDualSided) { + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); + pDev->SetRenderState(D3DRS_ZWRITEENABLE, 0); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + } + + DWORD dwMSAA; + + if (bOIT) { + pDev->GetRenderState(D3DRS_MULTISAMPLEANTIALIAS, &dwMSAA); + pDev->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, 0); + } + + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + + Grp[g].bRendered = true; + + if (bOIT && dwMSAA) { + pDev->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, dwMSAA); + } + + if (Grp[g].bDualSided) { + pDev->SetRenderState(D3DRS_ZWRITEENABLE, 1); + } + + if (bHUD) { + pDev->SetRenderState(D3DRS_ZENABLE, 1); + pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } + + vkStats.Mesh.Vertices += Grp[g].nVert; + vkStats.Mesh.MeshGrps++; + + } + + if (CurrentShader != 0xFFFF) { + HR(FX->EndPass()); + } + + HR(FX->End()); + + if (flags&(DBG_FLAGS_BOXES|DBG_FLAGS_SPHERES)) RenderBoundingBox(pW); + FX->SetVector(eColor, _DX(_F4(0, 0, 0, 0))); + if (flags&DBG_FLAGS_DUALSIDED) pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); +} + + + +// ================================================================================================ +// Render without animations +// +void vkMesh::RenderSimplified(const FMATRIX4* pW, LPDIRECT3DCUBETEXTURE9 *pEnv, int nEnv, bool bSP) +{ + if (!IsOK()) return; + + pBuf->Map(pDev); + + // Check material status + // + if (bMtrlModidied) { + CheckMeshStatus(); + bMtrlModidied = false; + } + + Scene *scn = gc->GetScene(); + + bool bMeshCull = true; + bool bTextured = true; + bool bGroupCull = true; + bool bUpdateFlow = true; + + EnablePlanetGlow(true); + + HR(vkEffect::FX->SetBool(vkEffect::eBaseBuilding, bSP)); + + vkMatExt *mat, *old_mat = NULL; + SURFHANDLE old_tex = NULL; + TexFlow FC; reset(FC); + + pDev->SetVertexDeclaration(pMeshVertexDecl); + pDev->SetStreamSource(0, pBuf->pVB, 0, sizeof(NMVERTEX)); + pDev->SetIndices(pBuf->pIB); + + FX->SetTechnique(eVesselTech); + FX->SetBool(eFresnel, false); + FX->SetBool(eEnvMapEnable, false); + FX->SetBool(eLightsEnabled, false); + FX->SetVector(eColor, _DX(_F4(0, 0, 0, 0))); + FX->SetMatrix(eW, _DX(pW)); + + ConfigureAtmo(); + + // Process Local Light Sources ------------------------------------------------------------ + // + const vkLight *pLights = gc->GetScene()->GetLights(); + int nSceneLights = gc->GetScene()->GetLightCount(); + + for (int i = 0; i < Config->MaxLights(); i++) memcpy(&Locals[i], &null_light, sizeof(LightStruct)); + + //vkDebugLog("Mesh=[%s], nLights=%d", GetName(), nSceneLights); + + if (pLights && nSceneLights>0) { + + int nMeshLights = 0; + FVECTOR3 pos = oapiTransformCoord(&_F(BBox.bs), pW); + + // Find all local lights effecting this mesh ------------------------------------------ + // + for (int i = 0; i < nSceneLights; i++) { + float il = pLights[i].GetIlluminance(pos, BBox.bs.w); + if (il > 0.005f) { + LightList[nMeshLights].illuminace = il; + LightList[nMeshLights++].idx = i; + } + } + + if (nMeshLights > 0) { + + FX->SetBool(eLightsEnabled, true); + + // If any, Sort the list based on illuminance ------------------------------------------- + qsort(LightList, nMeshLights, sizeof(_LightList), compare_lights); + + nMeshLights = min(nMeshLights, Config->MaxLights()); + + // Create a list of N most effective lights --------------------------------------------- + for (int i = 0; i < nMeshLights; i++) memcpy(&Locals[i], &pLights[LightList[i].idx], sizeof(LightStruct)); + } + } + + FX->SetValue(eLights, Locals, sizeof(LightStruct) * Config->MaxLights()); + + if (nEnv >= 1 && pEnv[0]) FX->SetTexture(eEnvMapA, pEnv[0]); + + bool bRefl = true; + WORD CurrentShader = 0xFFFF; + UINT numPasses = 0; + + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + + + // Render MeshGroups ---------------------------------------------------- + // + for (DWORD g = 0; gEndPass()); } + HR(FX->BeginPass(Grp[g].Shader)); + CurrentShader = Grp[g].Shader; + } + + // ------------------------------------------------------------------- + // + if (Tex[ti] != old_tex) { + if (Tex[ti] == NULL) { + reset(FC); + bUpdateFlow = true; + } + } + + if (Tex[ti] == NULL) bTextured = false, old_tex = NULL; + else bTextured = true; + + + // Setup Textures and Normal Maps ===================================== + // + if (bTextured) { + + if (Tex[ti] != old_tex) { + + old_tex = Tex[ti]; + FX->SetTexture(eTex0, Tex[ti]->GetTexture()); + bUpdateFlow = true; // Fix this later + + LPDIRECT3DTEXTURE9 pTransl = NULL; + LPDIRECT3DTEXTURE9 pTransm = NULL; + LPDIRECT3DTEXTURE9 pSpec = Tex[ti]->GetMap(MAP_SPECULAR); + LPDIRECT3DTEXTURE9 pNorm = Tex[ti]->GetMap(MAP_NORMAL); + LPDIRECT3DTEXTURE9 pRefl = Tex[ti]->GetMap(MAP_REFLECTION); + LPDIRECT3DTEXTURE9 pRghn = Tex[ti]->GetMap(MAP_ROUGHNESS); + LPDIRECT3DTEXTURE9 pMetl = Tex[ti]->GetMap(MAP_METALNESS); + LPDIRECT3DTEXTURE9 pEmis = Tex[ti]->GetMap(MAP_EMISSION); + + if (pNorm) FX->SetTexture(eTex3, pNorm); + if (pRghn) FX->SetTexture(eRghnMap, pRghn); + if (pRefl) FX->SetTexture(eReflMap, pRefl); + if (pMetl) FX->SetTexture(eMetlMap, pMetl); + if (pSpec) FX->SetTexture(eSpecMap, pSpec); + if (pEmis) FX->SetTexture(eEmisMap, pEmis); + + if (CurrentShader == SHADER_ADV) { + + pTransl = Tex[ti]->GetMap(MAP_TRANSLUCENCE); + pTransm = Tex[ti]->GetMap(MAP_TRANSMITTANCE); + + if (pTransl) FX->SetTexture(eTranslMap, pTransl); + if (pTransm) FX->SetTexture(eTransmMap, pTransm); + + FC.Transl = (pTransl != NULL); + FC.Transm = (pTransm != NULL); + } + else { + FC.Transl = false; + FC.Transm = false; + } + + FC.Emis = (pEmis != NULL); + FC.Metl = (pMetl != NULL); + FC.Norm = (pNorm != NULL); + FC.Rghn = (pRghn != NULL); + FC.Spec = (pSpec != NULL); + FC.Refl = (pRefl != NULL); + } + } + + + // Setup Mesh group material ========================================== + // + else { + if (Grp[g].MtrlIdx == SPEC_DEFAULT) mat = &defmat; + else mat = &Mtrl[Grp[g].MtrlIdx]; + if (mat != old_mat) { + old_mat = mat; + FX->SetValue(eMtrl, mat, sizeof(vkMatExt) - 4); + if (bModulateMatAlpha || bTextured == false) FX->SetFloat(eMtrlAlpha, mat->Diffuse.w); + else FX->SetFloat(eMtrlAlpha, 1.0f); + } + } + + // Must update FlowControl ? + // + if (bUpdateFlow) { + bUpdateFlow = false; + HR(FX->SetValue(eFlow, &FC, sizeof(TexFlow))); + } + + bool bPBR = (Grp[g].PBRStatus & 0xF) == (0x8 + 0x4); + bool bRGH = (Grp[g].PBRStatus & 0xE) == (0x8 + 0x2); + bool bNoL = (Grp[g].UsrFlag & 0x04) != 0; + bool bNoC = (Grp[g].UsrFlag & 0x10) != 0; + bool bOIT = (Grp[g].UsrFlag & 0x20) != 0; + bool bENV = false; + bool bFRS = false; + + + // Setup Mesh drawing options ================================================================================= + // + FX->SetBool(eOITEnable, bOIT); + FX->SetBool(eTextured, bTextured); + FX->SetBool(eFullyLit, bNoL); + FX->SetBool(eSwitch, bPBR); + FX->SetBool(eRghnSw, bRGH); + + if (bNoC) FX->SetValue(eNoColor, &FVECTOR3(1, 1, 1), sizeof(FVECTOR3)); + else FX->SetValue(eNoColor, &FVECTOR3(0, 0, 0), sizeof(FVECTOR3)); + + + // Update envmap and fresnel status as required + // + if (bRefl) { + bFRS = (Grp[g].PBRStatus & 0x1E) >= 0x10; + FX->SetBool(eFresnel, bFRS); + if (IsReflective()) { + bENV = (Grp[g].PBRStatus & 0x1E) >= 0xA; + FX->SetBool(eEnvMapEnable, bENV); + } + } + + // Start rendering ------------------------------------------------------------------------------------------- + // + FX->CommitChanges(); + + DWORD dwMSAA = 0; + + if (bOIT) { + pDev->GetRenderState(D3DRS_MULTISAMPLEANTIALIAS, &dwMSAA); + pDev->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, 0); + } + + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + Grp[g].bRendered = true; + + if (bOIT && dwMSAA) { + pDev->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, dwMSAA); + } + } + + if (CurrentShader != 0xFFFF) HR(FX->EndPass()); + HR(FX->End()); +} + + +// ================================================================================================ +// Render a legacy orbiter mesh without any additional textures +// +void vkMesh::RenderFast(const FMATRIX4* pW, int iTech) +{ + + _TRACE; + DWORD flags = 0, selmsh = 0, selgrp = 0, displ = 0; // Debug Variables + bool bActiveVisual = false; + + const VCHUDSPEC *hudspec; + + if (!IsOK()) return; + + pBuf->Map(pDev); + + if (DebugControls::IsActive()) { + flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + selmsh = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDMESH); + selgrp = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDGROUP); + displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); + bActiveVisual = (g_pCurrentVisual == DebugControls::GetVisual()); + if (displ>0 && !bActiveVisual) return; + if ((displ == 2 || displ == 3) && g_uCurrentMesh != selmsh) return; + } + + Scene *scn = gc->GetScene(); + + bool bWorldMesh = false; + bool bMeshCull = true; + bool bTextured = true; + bool bGroupCull = true; + bool bUpdateFlow = true; + bool bShadowProjection = false; + bool bVirtualCockpit = false; + + switch (iTech) { + case RENDER_VC: + bVirtualCockpit = true; + EnablePlanetGlow(false); + break; + case RENDER_BASE: + EnablePlanetGlow(false); + bMeshCull = false; + bShadowProjection = true; + break; + case RENDER_BASEBS: + EnablePlanetGlow(false); + bMeshCull = false; + bShadowProjection = true; + break; + case RENDER_ASTEROID: + EnablePlanetGlow(false); + bMeshCull = false; + bGroupCull = false; + bShadowProjection = true; + break; + case RENDER_VESSEL: + EnablePlanetGlow(true); + break; + } + + HR(vkEffect::FX->SetBool(vkEffect::eBaseBuilding, bShadowProjection)); + HR(vkEffect::FX->SetBool(vkEffect::eCockpit, bVirtualCockpit)); + + FVECTOR4 Field; + FMATRIX4 mWorldView, q; + + oapiMatrixMultiply(&mWorldView, pW, scn->GetViewMatrix()); + + if (bMeshCull || bGroupCull) Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); + + if (bMeshCull) if (!D9IsAABBVisible(&BBox, &mWorldView, &Field)) { + if (flags&(DBG_FLAGS_BOXES | DBG_FLAGS_SPHERES)) RenderBoundingBox(pW); + return; + } + + FMATRIX4 mWorldMesh; + + if (bGlobalTF) oapiMatrixMultiply(&mWorldMesh, &mTransform, pW); + else mWorldMesh = *pW; + + vkStats.Mesh.Meshes++; + + vkMatExt *mat, *old_mat = NULL; + SURFHANDLE old_tex = NULL; + LPDIRECT3DTEXTURE9 pEmis_old = NULL; + + pDev->SetVertexDeclaration(pMeshVertexDecl); + pDev->SetStreamSource(0, pBuf->pVB, 0, sizeof(NMVERTEX)); + pDev->SetIndices(pBuf->pIB); + + if (flags&DBG_FLAGS_DUALSIDED) pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + + + FX->SetTechnique(eVesselTech); + FX->SetBool(eLightsEnabled, false); + FX->SetVector(eColor, _DX(_F4(0, 0, 0, 0))); + + ConfigureAtmo(); + TexFlow FC; reset(FC); + + const vkLight *pLights = gc->GetScene()->GetLights(); + int nSceneLights = gc->GetScene()->GetLightCount(); + + for (int i = 0; i < Config->MaxLights(); i++) memcpy(&Locals[i], &null_light, sizeof(LightStruct)); + + //vkDebugLog("Mesh=[%s], nLights=%d", GetName(), nSceneLights); + + if (pLights && nSceneLights>0) { + + int nMeshLights = 0; + FVECTOR3 pos = oapiTransformCoord(ptr(_F(BBox.bs)), pW); + + // Find all local lights effecting this mesh ------------------------------------------ + // + for (int i = 0; i < nSceneLights; i++) { + float il = pLights[i].GetIlluminance(pos, BBox.bs.w); + if (il > 0.0) { + LightList[nMeshLights].illuminace = il; + LightList[nMeshLights++].idx = i; + } + } + + if (nMeshLights > 0) { + + FX->SetBool(eLightsEnabled, true); + + // If any, Sort the list based on illuminance ------------------------------------------- + qsort(LightList, nMeshLights, sizeof(_LightList), compare_lights); + + nMeshLights = min(nMeshLights, Config->MaxLights()); + + // Create a list of N most effective lights --------------------------------------------- + int i; + for (i = 0; i < nMeshLights; i++) memcpy(&Locals[i], &pLights[LightList[i].idx], sizeof(LightStruct)); + } + } + + FX->SetValue(eLights, Locals, sizeof(LightStruct) * Config->MaxLights()); + + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + + // Begin rendering of a specified pass ------------------------------ + // + HR(FX->BeginPass(2)); + + if (iTech == RENDER_BASEBS) pDev->SetRenderState(D3DRS_ZENABLE, 0); // Must be here because BeginPass() sets it enabled + + for (DWORD g = 0; gSetVector(eColor, _DX(F4_Zero)); + + if (flags&DBG_FLAGS_HLMESH) { + if (g_uCurrentMesh == selmsh) { + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0.0f, 0.5f, 0.5f))); + } + } + if (flags&DBG_FLAGS_HLGROUP) { + if (g == selgrp && g_uCurrentMesh == selmsh) { + FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0.5f, 0.0f, 0.5f))); + } + } + } + } + + + // --------------------------------------------------------------------------------------------------------- + // + DWORD ti = Grp[g].TexIdx; + DWORD tni = Grp[g].TexIdxEx[0]; + + if (ti == 0 && tni != 0) continue; + + if (Tex[ti] == NULL || ti == 0) bTextured = false, old_tex = NULL; + else bTextured = true; + + + // Cull unvisible geometry ================================================================================= + // + if (bGroupCull) if (!D9IsBSVisible(&Grp[g].BBox, &mWorldView, &Field)) continue; + + + // Setup Textures and Normal Maps ========================================================================== + // + if (bTextured) { + + if (Tex[ti] != old_tex) { + + old_tex = Tex[ti]; + FX->SetTexture(eTex0, Tex[ti]->GetTexture()); + + LPDIRECT3DTEXTURE9 pEmis = Tex[ti]->GetMap(MAP_EMISSION); + + if (tni && Grp[g].TexMixEx[0]<0.5f) tni = 0; + if (!pEmis && tni && Tex[tni]) pEmis = Tex[tni]->GetTexture(); + + if (pEmis != pEmis_old) { + FX->SetTexture(eEmisMap, pEmis); + pEmis_old = pEmis; + FC.Emis = (pEmis != NULL); + bUpdateFlow = true; + } + } + } + + // Apply MFD Screen Override ================================================================================ + // + if (Grp[g].MFDScreenId) { + bTextured = true; + old_tex = NULL; + old_mat = NULL; + reset(FC); + bUpdateFlow = true; + + SURFHANDLE hMFD; + if (bHUD) hMFD = gc->GetVCHUDSurface(&hudspec); + else hMFD = gc->GetMFDSurface(Grp[g].MFDScreenId - 1); + + if (hMFD) FX->SetTexture(eTex0, SURFACE(hMFD)->GetTexture()); + else FX->SetTexture(eTex0, gc->GetDefaultTexture()->GetTexture()); + + if (Grp[g].MtrlIdx == SPEC_DEFAULT) mat = &mfdmat; + else mat = &Mtrl[Grp[g].MtrlIdx]; + + if (bModulateMatAlpha || bTextured == false) FX->SetFloat(eMtrlAlpha, mat->Diffuse.w); + else FX->SetFloat(eMtrlAlpha, 1.0f); + + FX->SetValue(eMtrl, mat, sizeof(vkMatExt)-4); + FX->SetBool(eEnvMapEnable, false); + FX->SetBool(eFresnel, false); + } + + // Setup Mesh group material ========================================================================== + // + else { + + if (Grp[g].MtrlIdx == SPEC_DEFAULT) mat = &defmat; + else mat = &Mtrl[Grp[g].MtrlIdx]; + + if (mat != old_mat) { + + vkStats.Mesh.MtrlChanges++; + + old_mat = mat; + + FX->SetValue(eMtrl, mat, sizeof(vkMatExt)-4); + + if (bModulateMatAlpha || bTextured == false) FX->SetFloat(eMtrlAlpha, mat->Diffuse.w); + else FX->SetFloat(eMtrlAlpha, 1.0f); + } + } + + + // Apply Animations ========================================================================================= + // + if (Grp[g].bTransform) { + bWorldMesh = false; + FX->SetMatrix(eW, _DX(oapiMatrixMultiply(&q, &pGrpTF[g], pW))); + } + else if (!bWorldMesh) { + FX->SetMatrix(eW, _DX(mWorldMesh)); + bWorldMesh = true; + } + + if (bUpdateFlow) { + bUpdateFlow = false; + HR(FX->SetValue(eFlow, &FC, sizeof(TexFlow))); + } + + // Setup Mesh drawing options ================================================================================= + // + FX->SetBool(eTextured, bTextured); + FX->SetBool(eFullyLit, (Grp[g].UsrFlag & 0x4) != 0); + FX->SetBool(eOITEnable, (Grp[g].UsrFlag & 0x20) != 0); + + if ((Grp[g].UsrFlag & 0x10) != 0) FX->SetValue(eNoColor, &FVECTOR3(1, 1, 1), sizeof(FVECTOR3)); + else FX->SetValue(eNoColor, &FVECTOR3(0, 0, 0), sizeof(FVECTOR3)); + + FX->CommitChanges(); + + if (bHUD) { + pDev->SetRenderState(D3DRS_ZENABLE, 0); + pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + } + + if (Grp[g].bDualSided) { + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); + pDev->SetRenderState(D3DRS_ZWRITEENABLE, 0); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + } + + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + + Grp[g].bRendered = true; + + if (Grp[g].bDualSided) { + pDev->SetRenderState(D3DRS_ZWRITEENABLE, 1); + } + + if (bHUD) { + pDev->SetRenderState(D3DRS_ZENABLE, 1); + pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } + + vkStats.Mesh.Vertices += Grp[g].nVert; + vkStats.Mesh.MeshGrps++; + } + + HR(FX->EndPass()); + HR(FX->End()); + + if (flags&(DBG_FLAGS_BOXES | DBG_FLAGS_SPHERES)) RenderBoundingBox(pW); + FX->SetVector(eColor, _DX(F4_Zero)); + if (flags&DBG_FLAGS_DUALSIDED) pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); +} + + +// =========================================================================================== +// +FMATRIX4 vkMesh::GetTransform(int g, bool bCombined) +{ + if (g < 0) return mTransform; + + if (Grp[g].bTransform) { + if (bCombined) return pGrpTF[g]; + else return Grp[g].Transform; + } + + FMATRIX4 Ident; oapiMatrixIdentity(&Ident); + return Ident; +} + + +// =========================================================================================== +// +bool vkMesh::SetTransform(int g, const FMATRIX4* pMat) +{ + if (g >= int(nGrp)) return false; + + // Set Mesh Transform if g < 0 + if (g < 0) { + mTransform = *pMat; + bGlobalTF = true; + bBSRecompute = true; + bBSRecomputeAll = true; + oapiMatrixInverse(&mTransformInv, NULL, &mTransform); + for (DWORD i = 0; iGetScene(); + + bool bTextured = true; + bool bGroupCull = true; + bool bUseNormalMap = (Config->UseNormalMap==1); + + FMATRIX4 mWorldView, q; + oapiMatrixMultiply(&mWorldView, pW, scn->GetViewMatrix()); + + FVECTOR4 Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); + + vkMatExt *mat, *old_mat = NULL; + SURFHANDLE old_tex = NULL; + LPDIRECT3DTEXTURE9 pNorm = NULL; + + pDev->SetVertexDeclaration(pMeshVertexDecl); + pDev->SetStreamSource(0, pBuf->pVB, 0, sizeof(NMVERTEX)); + pDev->SetIndices(pBuf->pIB); + + + FX->SetTechnique(eBaseTile); + FX->SetVector(eColor, _DX(F4_Zero)); + FX->SetMatrix(eGT, _DX(FMATRIX_Identity)); + FX->SetMatrix(eW, _DX(pW)); + + ConfigureAtmo(); + + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + + for (DWORD pass=0;passBeginPass(pass)); + + for (DWORD g=0; gGetMap(MAP_NORMAL); + if (pNorm==NULL && pass==0) continue; + if (pNorm!=NULL && pass==1) continue; + } + else { + if (pass==0) continue; + pNorm=NULL; + old_tex=NULL; + } + + // Cull unvisible geometry ------------------------------------------------------ + // + if (bGroupCull) if (!D9IsBSVisible(&Grp[g].BBox, &mWorldView, &Field)) continue; + + + // Setup Textures and Normal Maps ========================================================================== + // + if (bTextured) { + + if (Tex[ti]!=old_tex) { + + if (tni && Grp[g].TexMixEx[0]<0.5f) tni=0; + + old_tex = Tex[ti]; + FX->SetTexture(eTex0, Tex[ti]->GetTexture()); + + if (tni && Tex[tni]) { + FX->SetTexture(eEmisMap, Tex[tni]->GetTexture()); + //FX->SetBool(eUseEmis, true); + } //else FX->SetBool(eUseEmis, false); + + if (bUseNormalMap) if (pNorm) FX->SetTexture(eTex3, pNorm); + } + } + + // Setup Mesh group material ============================================================================== + // + if (Grp[g].MtrlIdx==SPEC_DEFAULT) mat = &defmat; + else mat = &Mtrl[Grp[g].MtrlIdx]; + + if (mat!=old_mat) { + old_mat = mat; + FX->SetValue(eMtrl, mat, sizeof(vkMatExt)-4); + if (bModulateMatAlpha || bTextured==false) FX->SetFloat(eMtrlAlpha, mat->Diffuse.w); + else FX->SetFloat(eMtrlAlpha, 1.0f); + } + + // Setup Mesh drawing options ================================================================================= + // + FX->SetBool(eTextured, bTextured); + FX->SetBool(eFullyLit, (Grp[g].UsrFlag&0x4)!=0); + + FX->CommitChanges(); + + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + + vkStats.Mesh.Vertices += Grp[g].nVert; + vkStats.Mesh.MeshGrps++; + } + HR(FX->EndPass()); + } + HR(FX->End()); +} + + +// ================================================================================================ +// +void vkMesh::RenderShadowMap(const FMATRIX4* pW, const FMATRIX4* pVP, int opt, bool bNoCull) +{ + if (!IsOK()) return; + + pBuf->Map(pDev); + + FMATRIX4 GroupMatrix, mWorldMesh; + + MeshShader* pShader = nullptr; + + MeshShader::vs_const.mVP = pVP ? *pVP : FMATRIX4(); + + oapiMatrixIdentity(&MeshShader::vs_const.mW); + + if (bGlobalTF) oapiMatrixMultiply(&mWorldMesh, &mTransform, pW); + else mWorldMesh = *pW; + + if (opt == 1) { + // Screenspace Depth and Normal buffer rendering + pDev->SetStreamSource(0, pBuf->pVB, 0, sizeof(NMVERTEX)); + pShader = s_pShader[SHADER_NORMAL_DEPTH]; + pShader->Setup(pMeshVertexDecl, true, 0); + } + else { + if (Flags & 0x20) { + // Regular shadowmap with OIT support + pDev->SetStreamSource(0, pBuf->pSB, 0, sizeof(SMVERTEX)); + pShader = s_pShader[SHADER_SHADOWMAP_OIT]; + pShader->Setup(pPosTexDecl, true, 0); + } + else { + // Regular shadowmap for self shadowing + pDev->SetStreamSource(0, pBuf->pGB, 0, sizeof(FVECTOR4)); + pShader = s_pShader[SHADER_SHADOWMAP]; + pShader->Setup(pVector4Decl, true, 0); + } + } + pDev->SetIndices(pBuf->pIB); + pShader->ClearTextures(); + + bool bInit = true; + bool bCurrentState = false; + + for (DWORD g = 0; g < nGrp; g++) + { + if (Grp[g].UsrFlag & 0x2) continue; + if (Grp[g].UsrFlag & 0x1) continue; + + MeshShader::ps_bools.bOIT = (Grp[g].UsrFlag & 0x20) != 0; + + if (MeshShader::ps_bools.bOIT) { + DWORD ti = Grp[g].TexIdx; + if (ti) { + auto hTex = Tex[ti]->GetTexture(); + if (hTex) pShader->SetTexture(pShader->hPST[0], hTex, IPF_WRAP | IPF_POINT); + else MeshShader::ps_bools.bOIT = false; + } + else MeshShader::ps_bools.bOIT = false; + } + + if (Grp[g].bTransform) { + oapiMatrixMultiply(&MeshShader::vs_const.mW, &pGrpTF[g], pW); // Apply Animations to instance matrices + bInit = true; + } + else { + if (bInit) MeshShader::vs_const.mW = mWorldMesh; + bInit = false; + } + + if (pShader->hVSC) pShader->SetVSConstants(pShader->hVSC, &MeshShader::vs_const, sizeof(MeshShader::vs_const)); + if (pShader->hPSC) pShader->SetPSConstants(pShader->hPSC, &MeshShader::ps_const, sizeof(MeshShader::ps_const)); + if (pShader->hPSB) pShader->SetPSConstants(pShader->hPSB, &MeshShader::ps_bools, sizeof(MeshShader::ps_bools)); + pShader->UpdateTextures(); + + DWORD oc; + if (bNoCull) { + pDev->GetRenderState(D3DRS_CULLMODE, &oc); + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + } + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + if (bNoCull) pDev->SetRenderState(D3DRS_CULLMODE, oc); + } +} + + +// ================================================================================================ +// +void vkMesh::RenderStencilShadows(float alpha, const FMATRIX4* pP, const FMATRIX4* pW, bool bShadowMap, const FVECTOR4 *elev) +{ + if (!IsOK()) return; + + DWORD Pass = 0; + FMATRIX4 GroupMatrix, mWorldMesh; UINT numPasses = 0; + + if (bGlobalTF) oapiMatrixMultiply(&mWorldMesh, &mTransform, pW); + else mWorldMesh = *pW; + + pDev->SetIndices(pBuf->pIB); + pDev->SetVertexDeclaration(pPosTexDecl); + pDev->SetStreamSource(0, pBuf->pSB, 0, sizeof(SMVERTEX)); + FX->SetTechnique(eShadowTech); + + if (elev) FX->SetVector(eInScatter, _DX(elev)); + else FX->SetVector(eInScatter, _DX(FVECTOR4(0.0f, 1, 0, 0))); + + FX->SetFloat(eMix, alpha); + FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); + + FX->BeginPass(Pass); + + if (pP) FX->SetValue(eGT, pP, sizeof(FMATRIX4)); // Shadow Projection + + bool bInit = true; + bool bCurrentState = false; + + for (DWORD g = 0; g < nGrp; g++) { + + if (Grp[g].UsrFlag & 0x2) continue; + if ((Grp[g].UsrFlag & 0x1) && (bShadowMap == false)) continue; + + bool bOIT = (Grp[g].UsrFlag & 0x20) != 0; + + if (bOIT) { + DWORD ti = Grp[g].TexIdx; + if (ti) { + auto hTex = Tex[ti]->GetTexture(); + if (hTex) { + HR(FX->SetTexture(eTex0, hTex)); + } else bOIT = false; + } else bOIT = false; + } + + FX->SetBool(eOITEnable, bOIT); + + if (Grp[g].bTransform) { + oapiMatrixMultiply(&GroupMatrix, &pGrpTF[g], pW); // Apply Animations to instance matrices + FX->SetValue(eW, &GroupMatrix, sizeof(FMATRIX4)); + bInit = true; + } + else { + if (bInit) { + FX->SetValue(eW, &mWorldMesh, sizeof(FMATRIX4)); + } + bInit = false; + } + + FX->CommitChanges(); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + } + + FX->EndPass(); + FX->End(); +} + + +// ================================================================================================ +// +void vkMesh::RenderShadowsEx(float alpha, const FMATRIX4* pP, const FMATRIX4* pW, const FVECTOR4 *light, const FVECTOR4 *param) +{ + if (!IsOK()) return; + + vkStats.Mesh.Meshes++; + + pDev->SetVertexDeclaration(pPosTexDecl); + pDev->SetStreamSource(0, pBuf->pSB, 0, sizeof(SMVERTEX)); + pDev->SetIndices(pBuf->pIB); + + FX->SetTechnique(eShadowTech); + FX->SetMatrix(eW, _DX(pW)); + FX->SetMatrix(eGT, _DX(pP)); + FX->SetFloat(eMix, alpha); + if (light) FX->SetVector(eColor, _DX(light)); + else FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 1, 0, 0))); + FX->SetVector(eTexOff, _DX(param)); + + + UINT numPasses = 0; + FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); + FX->BeginPass(1); + + for (DWORD g = 0; gGetTexture() : NULL); + if (hTex) { + HR(FX->SetTexture(eTex0, hTex)); + } + else bOIT = false; + } + else bOIT = false; + } + + FX->SetBool(eOITEnable, bOIT); + FX->CommitChanges(); + + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[g].VertOff, 0, Grp[g].nVert, Grp[g].IdexOff, Grp[g].nFace); + + vkStats.Mesh.Vertices += Grp[g].nVert; + vkStats.Mesh.MeshGrps++; + } + + FX->EndPass(); + FX->End(); +} + + + +// ================================================================================================ +// This is a rendering routine for a Exterior Mesh, non-spherical moons/asteroids +// +void vkMesh::RenderBoundingBox(const FMATRIX4* pW) +{ + _TRACE; + + if (!IsOK()) return; + if (DebugControls::IsActive()==false) return; + + FMATRIX4 q, qq; + + static FVECTOR3 poly[10] = { + {0, 0, 0}, + {1, 0, 0}, + {1, 1, 0}, + {0, 1, 0}, + {0, 0, 0}, + {0, 0, 1}, + {1, 0, 1}, + {1, 1, 1}, + {0, 1, 1}, + {0, 0, 1} + }; + + static FVECTOR3 list[6] = { + {1, 0, 0}, + {1, 0, 1}, + {1, 1, 0}, + {1, 1, 1}, + {0, 1, 0}, + {0, 1, 1} + }; + + + + DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + DWORD selmsh = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDMESH); + DWORD selgrp = *(DWORD*)gc->GetConfigParam(CFGPRM_GETSELECTEDGROUP); + bool bSel = (g_uCurrentMesh==selmsh); + + + if (flags&(DBG_FLAGS_SELVISONLY|DBG_FLAGS_SELMSHONLY|DBG_FLAGS_SELGRPONLY) && DebugControls::GetVisual()!=g_pCurrentVisual) return; + if (flags&DBG_FLAGS_SELMSHONLY && !bSel) return; + if (flags&DBG_FLAGS_SELGRPONLY && !bSel) return; + + if (flags&DBG_FLAGS_BOXES) { + + pDev->SetVertexDeclaration(pPositionDecl); + + // ---------------------------------------------------------------- + FX->SetMatrix(eW, _DX(pW)); + FX->SetVector(eColor, _DX(FVECTOR4(0, 1, 0, 0.5f))); + FX->SetTechnique(eBBTech); + // ---------------------------------------------------------------- + + UINT numPasses = 0; + FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE); + FX->BeginPass(0); + + for (DWORD g=0; gSetVector(eAttennuate, _DX(Grp[g].BBox.mn)); + FX->SetVector(eInScatter, _DX(Grp[g].BBox.mx)); + + // Apply Animations ========================================================================================= + // + if (Grp[g].bTransform) { + if (bGlobalTF) FX->SetMatrix(eGT, _DX(oapiMatrixMultiply(&q, &mTransform, &Grp[g].Transform))); + else FX->SetMatrix(eGT, _DX(Grp[g].Transform)); + } + else FX->SetMatrix(eGT, _DX(mTransform)); + + + // Setup Mesh drawing options ================================================================================= + // + FX->CommitChanges(); + + pDev->DrawPrimitiveUP(D3DPT_LINESTRIP, 9, &poly, sizeof(FVECTOR3)); + pDev->DrawPrimitiveUP(D3DPT_LINELIST, 3, &list, sizeof(FVECTOR3)); + } + + FX->EndPass(); + FX->End(); + } + + if (flags&DBG_FLAGS_SPHERES) { + for (DWORD g=0; gmn.x, XMVectorSetW(mi, 0)); + XMStoreFloat4((XMFLOAT4 *)&box->mx.x, XMVectorSetW(mx, 0)); +} + +// =========================================================================================== +// +void vkMesh::TransformGroup(DWORD n, const FMATRIX4 *m) +{ + if (!IsOK()) return; + + bBSRecompute = true; + oapiMatrixMultiply(&Grp[n].Transform, &Grp[n].Transform, m); + + Grp[n].bTransform = true; + Grp[n].bUpdate = true; + + oapiMatrixMultiply(&pGrpTF[n], &mTransform, &Grp[n].Transform); +} + +// =========================================================================================== +// +void vkMesh::Transform(const FMATRIX4 *m) +{ + if (!IsOK()) return; + + bBSRecompute = true; + bBSRecomputeAll = true; + bGlobalTF = true; + + oapiMatrixMultiply(&mTransform, &mTransform, m); + oapiMatrixInverse(&mTransformInv, NULL, &mTransform); + + for (DWORD i=0;ipMesh) if (p->pMesh != this) return result; + + if (!pBuf->pGBSys || !pBuf->pIBSys) { + LogErr("vkMesh::Pick() Failed: No Geometry Available"); + return result; + } + + UpdateBoundingBox(); + + XMVECTOR Zero = FVECTOR3(0).XM(); + XMVECTOR Dir = vDir->XM(); + + XMMATRIX mW, mWT, mWorldMesh; + + if (pT) mWT = XMMatrixMultiply(pT->XM(), pW->XM()); + else mWT = pW->XM(); + + if (bGlobalTF) mWorldMesh = XMMatrixMultiply(mTransform.XM(), mWT); + else mWorldMesh = mWT; + + FMATRIX4 fmWT(mWT); + + for (DWORD g=0;g rad) continue; + + if (Grp[g].bTransform) mW = XMMatrixMultiply(pGrpTF[g].XM(), mWT); + else mW = mWorldMesh; + + XMVECTOR _a, _b, _c, cp; + + WORD *pIdc = pBuf->pIBSys + Grp[g].IdexOff; + XMVECTOR *pVrt = pBuf->pGBSys + Grp[g].VertOff; + + XMMATRIX mWI = XMMatrixInverse(nullptr, mW); + + XMVECTOR pos = XMVector3TransformCoord(Zero, mWI); + XMVECTOR dir = XMVector3TransformNormal(Dir, mWI); + + for (DWORD i=0;ibDualSided) { + if (DirectX::TriangleTests::Intersects(pos, dir, _a, _b, _c, dst)) { + if (dst > p->fnear) { + if (dst < result.dist) { + result.dist = dst; + result.group = int(g); + result.pMesh = this; + result.idx = int(i); + } + } + } + } + } + } + + + if (result.idx >= 0 && result.group >= 0) { + result.pos = (*vDir) * result.dist; + } + + return result; +} + + + +// =========================================================================================== +// SPECIAL RENDER FUNCTIONS SECTION +// =========================================================================================== +// + +// This is a special rendering routine used to render 3D arrow -------------------------------- +// +void vkMesh::RenderAxisVector(FMATRIX4* pW, const FVECTOR4 *pColor, float len) +{ + UINT numPasses = 0; + HR(FX->SetTechnique(eAxisTech)); + HR(FX->SetFloat(eMix, len)); + HR(FX->SetValue(eColor, pColor, sizeof(FVECTOR4))); + HR(FX->SetMatrix(eW, _DX(pW))); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); + RenderGroup(0); + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + HR(FX->EndPass()); + HR(FX->End()); +} + +// Used only by ring manager -------------------------------------------------------------------- +// +void vkMesh::RenderRings(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex) +{ + _TRACE; + if (!IsOK()) return; + if (!pTex) return; + + vkStats.Mesh.Vertices += Grp[0].nVert; + vkStats.Mesh.MeshGrps++; + + UINT numPasses = 0; + HR(FX->SetTechnique(eRingTech)); + HR(FX->SetMatrix(eW, _DX(pW))); + HR(FX->SetTexture(eTex0, pTex)); + FX->SetValue(eSun, &sunLight, sizeof(vkSun)); + HR(FX->SetValue(eMtrl, &defmat, sizeof(vkMatExt)-4)); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + RenderGroup(0); + HR(FX->EndPass()); + HR(FX->End()); +} + +// Used only by ring manager -------------------------------------------------------------------- +// +void vkMesh::RenderRings2(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex, float irad, float orad) +{ + _TRACE; + if (!IsOK()) return; + if (!pTex) return; + + vkStats.Mesh.Vertices += Grp[0].nVert; + vkStats.Mesh.MeshGrps++; + + UINT numPasses = 0; + HR(FX->SetTechnique(eRingTech2)); + HR(FX->SetMatrix(eW, _DX(pW))); + HR(FX->SetTexture(eTex0, pTex)); + FX->SetValue(eSun, &sunLight, sizeof(vkSun)); + HR(FX->SetValue(eMtrl, &defmat, sizeof(vkMatExt)-4)); + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(irad, orad, 0, 0)))); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + RenderGroup(0); + HR(FX->EndPass()); + HR(FX->End()); +} + + +// =========================================================================================== +// +void vkMesh::GlobalInit(LPDIRECT3DDEVICE9 pDev) +{ + memset(s_pShader, 0, sizeof(s_pShader)); + + s_pShader[SHADER_SHADOWMAP] = new MeshShader(pDev, "Modules/vkShaders/NewMesh.hlsl", "ShdMapVS", "ShdMapPS"); + s_pShader[SHADER_SHADOWMAP_OIT] = new MeshShader(pDev, "Modules/vkShaders/NewMesh.hlsl", "ShdMapOIT_VS", "ShdMapOIT_PS"); + s_pShader[SHADER_NORMAL_DEPTH] = new MeshShader(pDev, "Modules/vkShaders/NewMesh.hlsl", "NormalDepth_VS", "NormalDepth_PS"); +} + + +// =========================================================================================== +// +void vkMesh::GlobalExit() +{ + for (auto x : s_pShader) if (x) delete x; +} diff --git a/OVP/VulkanClient/Mesh.h b/OVP/VulkanClient/Mesh.h new file mode 100644 index 000000000..83fde294c --- /dev/null +++ b/OVP/VulkanClient/Mesh.h @@ -0,0 +1,409 @@ +// ============================================================== +// Mesh.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2023 Jarmo Nikkanen +// ============================================================== + +// ============================================================== +// class vkMesh (interface) +// +// This class represents a mesh in terms of DX7 interface elements +// (vertex buffers, index lists, materials, textures) which allow +// it to be rendered to the vk device. +// ============================================================== + +#ifndef __MESH_H +#define __MESH_H + +#include "Client.h" +#include "Effect.h" +#include "AABBUtil.h" +#include "IProcess.h" +#include +#include "MathAPI.h" +#include +#include + +const DWORD SPEC_DEFAULT = (DWORD)(-1); // "default" material/texture flag +const DWORD SPEC_INHERIT = (DWORD)(-2); // "inherit" material/texture flag + +#define RENDER_VESSEL 0 +#define RENDER_BASE 1 +#define RENDER_ASTEROID 2 +#define RENDER_BASETILES 3 +#define RENDER_VC 4 +#define RENDER_BASEBS 5 +#define RENDER_CUSTOM 6 + +#define ENVMAP_MAIN 0 + + +// Mesh Shaders +#define SHADER_NULL 0xFFFF +#define SHADER_PBR 0 +#define SHADER_ADV 1 +#define SHADER_LEGACY 2 // Shader most compatible with DX7 Inline +#define SHADER_XR2HUD 3 // XR2 HUD shader +#define SHADER_METALNESS 4 +#define SHADER_BAKED_VC 5 +#define SHADER_SHADOWMAP 10 +#define SHADER_SHADOWMAP_OIT 11 +#define SHADER_NORMAL_DEPTH 12 + +#define VCLASS_AMSO 1 +#define VCLASS_XR2 2 +#define VCLASS_ULTRA 3 +#define VCLASS_SSU_CENTAUR 4 + + +// Mesh memory mapping mode +#define MAPMODE_UNKNOWN 0 +#define MAPMODE_CURRENT 1 +#define MAPMODE_STATIC 2 +#define MAPMODE_DYNAMIC 3 + + +#define ENVCAM_OMIT_ATTC 0x0001 ///< Do not render attachments, rendered by default +#define ENVCAM_OMIT_DOCKS 0x0002 ///< Do not render docked vessels, rendered by default +#define ENVCAM_FOCUS 0x0004 ///< Force rendering of focus object, omitted by default +#define ENVCAM_PLANE 0x0008 ///< Camera view is 160deg square plane, 360deg cube-map by default +#define ENVCAM_USER 0x0010 ///< User supplied setup + + + + +struct _LightList { + int idx; + float illuminace; +}; + +struct _BakedLights { + LPDIRECT3DTEXTURE9 pMap[16]; + LPDIRECT3DTEXTURE9 pSunAO[6]; + LPDIRECT3DTEXTURE9 pCombined; + LPDIRECT3DTEXTURE9 pSunAOComb; +}; + +class MeshShader : public ShaderClass +{ +public: + + static struct VSConst { + float4x4 mVP; // View Projection Matrix + float4x4 mW; // World Matrix + } vs_const; + + static struct PSConst { + float3 Cam_X; + float3 Cam_Y; + float3 Cam_Z; + } ps_const; + + static struct PSBools { + BOOL bOIT; // Enable order independent transparency + } ps_bools; + + + MeshShader(LPDIRECT3DDEVICE9 pDev, const char *file, const char *vs, const char *ps, const char *opt = NULL) : + ShaderClass(pDev, "Modules/vkShaders/NewMesh.hlsl", vs, ps, "MeshShader", opt) + { + memset(hPST, 0, sizeof(hPST)); + hVSC = GetVSHandle("vs_const"); + hPSC = GetPSHandle("ps_const"); + hPSB = GetPSHandle("ps_bools"); + hPST[0] = GetPSHandle("tDiff"); + } + + ~MeshShader() + { + + } + + HANDLE hPSB, hVSC, hPSC, hPST[16]; +}; + + + +class MeshBuffer +{ +public: + + MeshBuffer(MeshBuffer *pSrc, const class vkMesh *_pRoot); + MeshBuffer(DWORD nVtx, DWORD nIdx, const class vkMesh *_pRoot); + ~MeshBuffer(); + + void Map(LPDIRECT3DDEVICE9 pDev); + bool IsLocalTo(const class vkMesh *_pRoot) const { return (_pRoot == pRoot); } + void MustRemap(DWORD mode); + bool Release(); + MeshBuffer* Reference(); + + LPDIRECT3DVERTEXBUFFER9 pVB; + LPDIRECT3DVERTEXBUFFER9 pGB; + LPDIRECT3DINDEXBUFFER9 pIB; + LPDIRECT3DVERTEXBUFFER9 pSB; + + NMVERTEX *pVBSys; + XMVECTOR *pGBSys; + WORD *pIBSys; + SMVERTEX *pSBSys; + + DWORD nRef; + DWORD nVtx; + DWORD nIdx; + DWORD mapMode; + bool bMustRemap; + + const class vkMesh *pRoot; +}; + + + + + + +/** + * \brief Mesh object with vk-specific vertex buffer + * + * Meshes consist of one or more vertex groups, and a set of materials and + * textures. + */ + +class vkMesh : private vkEffect +{ + +public: + + bool bCanRenderFast; // Mesh doesn't contain any advanced features in any group + bool bIsReflective; // Mesh has a reflective material in one or more groups + bool bMtrlModidied; + bool bIsTemplate; + + DWORD MeshFlags; + D9BBox BBox; + MeshBuffer *pBuf; + MESHHANDLE hOapiMesh; + + struct GROUPREC { // mesh group definition + DWORD VertOff; // Main mesh Vertex Offset + DWORD IdexOff; // Main mesh Index Offset + //------------------------------------------------ + DWORD nFace; // Face/Primitive count + DWORD nVert; // Vertex count + //------------------------------------------------ + DWORD MtrlIdx; // material index + DWORD TexIdx; // texture index 0=None + DWORD UsrFlag; // user-defined flag + WORD IntFlag; // internal flags + WORD zBias; + WORD MFDScreenId; // MFD screen ID + 1 + WORD PBRStatus; + WORD Shader; + bool bTransform; + bool bUpdate; // Bounding box update required + bool bDualSided; + bool bDeleted; // This entry is deleted by DelGroup() + bool bRendered; + FMATRIX4 Transform; // Group specific transformation matrix + D9BBox BBox; + DWORD TexIdxEx[MAXTEX]; + float TexMixEx[MAXTEX]; + }; + + + vkMesh(const char *fname); + vkMesh(DWORD nGrp, const MESHGROUPEX **hGroup, const SURFHANDLE *hSurf); + vkMesh(const MESHGROUPEX *pGroup, const MATERIAL *pMat, SurfNative *pTex); + vkMesh(MESHHANDLE hMesh, bool asTemplate = false, FVECTOR3 *reorig = NULL, float *scale = NULL); + vkMesh(MESHHANDLE hMesh, const vkMesh &hTemp); + ~vkMesh(); + + bool IsOK() const { return pBuf != NULL; } + MESHHANDLE GetOapiHandle() { return hOapiMesh; } + void Release(); + void ClearBake(int i); + void LoadBakedLights(); + void BakeLights(ImageProcessing *pBaker, const FVECTOR3* BakedLightsControl); + void BakeAO(ImageProcessing* pBaker, const FVECTOR3 &vSun, const LVLH& lvlh, const LPDIRECT3DTEXTURE9 pIrrad); + void LoadMeshFromHandle(MESHHANDLE hMesh, FVECTOR3 *reorig = NULL, float *scale = NULL); + void ReLoadMeshFromHandle(MESHHANDLE hMesh); + void ReloadTextures(); + + void SetName(const char *name); + void SetName(UINT idx); + const char * GetName() const { return name; } + const char * GetDirName(int i, int v); + FVECTOR3 GetDir(int i); + + void SetDefaultShader(WORD shader); + WORD GetDefaultShader() const { return DefShader; } + + void SetClass(DWORD cl) { vClass = cl; } + + + /** + * \brief Check if a mesh is casting shadows + * \return Returns true if the mesh is casting shadows. + */ + bool HasShadow() const; + + /** + * \brief Returns a pointer to a mesh group. + * \param idx group index (>= 0) + * \return Pointer to group structure. + */ + const GROUPREC * GetGroup(DWORD idx) const; + GROUPREC* GetGroup(DWORD idx); + void SetMFDScreenId(DWORD idx, WORD id); + void SetDualSided(DWORD idx, bool bState) { Grp[idx].bDualSided = bState; } + + /** + * \brief Returns number of material specifications. + * \return Number of materials. + */ + SURFHANDLE GetTexture(DWORD idx) const { return (SURFHANDLE)Tex[idx]; } + bool HasTexture(SURFHANDLE hSurf) const; + bool IsReflective() const { return bIsReflective | (DefShader==SHADER_METALNESS); } + + /** + * \brief returns a pointer to a material definition. + * \param idx material index (>= 0) + * \return Pointer to material object. + */ + const vkMatExt * GetMaterial(DWORD idx) const; + bool GetMaterial(vkMatExt *pMat, DWORD idx) const; + void SetMaterial(const vkMatExt *pMat, DWORD idx, bool bUpdateStatus = true); + void SetMaterial(const D3DMATERIAL9 *pMat, DWORD idx, bool bUpdateStatus = true); + int SetMaterialEx(DWORD idx, MatProp mid, const FVECTOR4* in); + int GetMaterialEx(DWORD idx, MatProp mid, FVECTOR4* out); + + DWORD GetGroupCount() const { return nGrp; } + DWORD GetMaterialCount() const { return nMtrl; } + DWORD GetTextureCount() const { return nTex; } + DWORD GetVertexCount(int grp=-1) const; + DWORD GetIndexCount(int grp=-1) const; + bool IsGroupRendered(DWORD grp) const; + + DWORD GetMeshGroupMaterialIdx(DWORD grp) const; + DWORD GetMeshGroupTextureIdx(DWORD grp) const; + DWORD GetGroupTransformCount() const; + FVECTOR3 GetBoundingSpherePos(); + float GetBoundingSphereRadius(); + D9BBox * GetAABB(); + FVECTOR3 GetGroupSize(DWORD idx) const; + FMATRIX4* GetTransform() { if (bGlobalTF) return &mTransform; else return NULL; } + + FMATRIX4 GetTransform(int grp, bool bCombined); + bool SetTransform(int grp, const FMATRIX4* pMat); + + void SetPosition(VECTOR3 &pos); + void SetRotation(FMATRIX4 &rot); + + /** + * \brief Replace a mesh texture. + * \param texidx texture index (>= 0) + * \param tex texture handle + * \return \e true on success, \e false otherwise. + */ + bool SetTexture(DWORD texidx, SURFHANDLE tex); + void SetTexMixture (DWORD ntex, float mix); + + void RenderGroup(const GROUPREC *grp); + void RenderGroup(int idx); + void RenderBaseTile(const FMATRIX4* pW); + void RenderBoundingBox(const FMATRIX4* pW); + void Render(const FMATRIX4* pW, const ENVCAMREC* em = NULL, int iTech = RENDER_VESSEL); + void RenderFast(const FMATRIX4* pW, int iTech); + void RenderShadowMap(const FMATRIX4* pW, const FMATRIX4* pVP, int flags, bool bNoCull = false); + void RenderStencilShadows(float alpha, const FMATRIX4* pP, const FMATRIX4* pW, bool bShadowMap = false, const FVECTOR4 *elev = NULL); + void RenderShadowsEx(float alpha, const FMATRIX4* pP, const FMATRIX4* pW, const FVECTOR4 *light, const FVECTOR4 *param); + void RenderRings(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex); + void RenderRings2(const FMATRIX4* pW, LPDIRECT3DTEXTURE9 pTex, float irad, float orad); + void RenderAxisVector(FMATRIX4* pW, const FVECTOR4 *pColor, float len); + void RenderSimplified(const FMATRIX4* pW, LPDIRECT3DCUBETEXTURE9 *pEnv = NULL, int nEnv = 0, bool bSP = false); + void CheckMeshStatus(); + void ResetTransformations(); + void TransformGroup(DWORD n, const FMATRIX4 *m); + void Transform(const FMATRIX4 *m); + int GetGroup (DWORD grp, GROUPREQUESTSPEC *grs); + int EditGroup (DWORD grp, GROUPEDITSPEC *ges); + + void SetSunLight(const vkSun *pLight); + + vkPick Pick(const FMATRIX4* pW, const FMATRIX4* pT, const FVECTOR3 *vDir, const PickProp* p); + + void UpdateBoundingBox(); + void BoundingBox(const NMVERTEX *vtx, DWORD n, D9BBox *box); + + void SetAmbientColor(const FVECTOR3& c); + const FVECTOR3& GetAmbientColor(); + void SetupFog(const FMATRIX4* pW); + void ResetRenderStatus(); + + LPDIRECT3DTEXTURE9 GetCombinedMap(int tex_idx = -1); + + /** + * \brief Enable/disable material alpha value for transparency calculation. + * \param enable flag for enabling/disabling material alpha calculation. + * \note By default, material alpha values are ignored for mesh groups + * with textures, and the texture alpha values are used instead. + * By enabling material alpha calculation, the final alpha value is + * calculated as the product of material and texture alpha value. + */ + inline void EnableMatAlpha (bool enable) { bModulateMatAlpha = enable; } + + static void GlobalInit(LPDIRECT3DDEVICE9 pDev); + static void GlobalExit(); + static void SetShadows(const SHADOWMAP* sprm); + +private: + + + void UpdateTangentSpace(NMVERTEX *pVrt, WORD *pIdx, DWORD nVtx, DWORD nFace, bool bTextured); + void ProcessInherit(); + bool CopyVertices(GROUPREC *grp, const MESHGROUPEX *mg, FVECTOR3 *reorig = NULL, float *scale = NULL); + void SetGroupRec(DWORD i, const MESHGROUPEX *mg); + void Null(const char *meshName = NULL); + void UpdateFlags(); + void ConfigureAtmo(); + void ConfigureShadows(); + + WORD DefShader; + DWORD MaxVert; + DWORD MaxFace; + GROUPREC *Grp; // list of mesh groups + DWORD nGrp; // number of mesh groups + DWORD nMtrl; // number of mesh materials + DWORD nTex; // number of mesh textures + DWORD vClass; + DWORD Flags; + vkMatExt *Mtrl; // list of mesh materials + SurfNative **Tex; // list of mesh textures + std::map BakedLights; + std::map::const_iterator bli; + std::vector env_cams; + + FMATRIX4 mTransform; + FMATRIX4 mTransformInv; + FMATRIX4 *pGrpTF; + vkSun sunLight; + FVECTOR3 cAmbient; + LightStruct null_light; + + _LightList LightList[MAX_SCENE_LIGHTS]; + LightStruct *Locals; + bool bBSRecompute; // Bounding sphere must be recomputed + bool bBSRecomputeAll; + bool bModulateMatAlpha; // mix material and texture alpha channels + bool bGlobalTF; // Mesh has a valid mTransform matrix + + char name[128]; + + static LPDIRECT3DTEXTURE9 pShadowMap[SHM_CASCADE_COUNT]; + static FVECTOR4 ShdSubRect[SHM_CASCADE_COUNT]; + static MeshShader* s_pShader[16]; +}; + +#endif // !__MESH_H diff --git a/OVP/VulkanClient/MeshMgr.cpp b/OVP/VulkanClient/MeshMgr.cpp new file mode 100644 index 000000000..7e91c0a10 --- /dev/null +++ b/OVP/VulkanClient/MeshMgr.cpp @@ -0,0 +1,79 @@ +// ============================================================== +// MeshMgr.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007 - 2016 Martin Schweiger +// ============================================================== + +// ============================================================== +// class MeshManager (implementation) +// Simple management of persistent mesh templates +// ============================================================== + +#include "Meshmgr.h" + +using namespace oapi; + +MeshManager::MeshManager(vkClient *gclient) +{ + gc = gclient; + mlist = NULL; + nmlist = nmlistbuf = 0; +} + +MeshManager::~MeshManager() +{ + DeleteAll(); +} + +void MeshManager::DeleteAll() +{ + int i; + for (i=0;iSetName(name); + nmlist++; + + float lim = 1e3; + DWORD count = mlist[nmlist-1].mesh->GetGroupCount(); + + for (DWORD i=0;iGetGroupSize(i); + if (fabs(s.x)>lim || fabs(s.y)>lim || fabs(s.z)>lim) return i; + } + + return -1; +} + +const vkMesh *MeshManager::GetMesh (MESHHANDLE hMesh) +{ + int i; + for (i=0;i +#include "Util.h" +#include "OapiExtension.h" +#include "Config.h" +#include "OrbiterAPI.h" +#include + + +// =========================================================================== +// Class statics initialization + +DWORD OapiExtension::elevationMode = 0; +// Orbiters default directories +std::string OapiExtension::configDir(".\\Config\\"); +std::string OapiExtension::meshDir(".\\Meshes\\"); +std::string OapiExtension::textureDir(".\\Textures\\"); +std::string OapiExtension::hightexDir(".\\Textures2\\"); +std::string OapiExtension::scenarioDir(".\\Scenarios\\"); + +std::string OapiExtension::startupScenario = OapiExtension::ScanCommandLine(); + +bool OapiExtension::configParameterRead = OapiExtension::GetConfigParameter(); + +// 2010 100606 +// 2010-P1 100830 +// 2010-P2 110822 +// 2010-P2.1 110824 +bool OapiExtension::isOrbiter2010 = (oapiGetOrbiterVersion() <= 110824 && oapiGetOrbiterVersion() >= 100606); + +bool OapiExtension::orbiterSound40 = false; +bool OapiExtension::tileLoadThread = true; +bool OapiExtension::runsUnderWINE = false; +bool OapiExtension::runsSpacecraftDll = false; + + +// =========================================================================== +// Construction +// +OapiExtension::OapiExtension(void) { +} + +// =========================================================================== +// Destruction +// +OapiExtension::~OapiExtension(void) +{ +} + + +/* +------------------------------------------------------------------------------ + PUBLIC INTERFACE METHODS +------------------------------------------------------------------------------ +*/ + +// =========================================================================== +// Initialization +// +void OapiExtension::GlobalInit(const vkConfig &Config) +{ +} + +// =========================================================================== +// Same functionality than 'official' GetConfigParam, but for non-provided +// config parameters +// +const void *OapiExtension::GetConfigParam (DWORD paramtype) +{ + switch (paramtype) { + case CFGPRM_ELEVATIONINTERPOLATION : return (void*)&elevationMode; + case CFGPRM_TILELOADTHREAD : return (void*)&tileLoadThread; + default : return NULL; + } +} + +/* +------------------------------------------------------------------------------ + PRIVATE METHODS +------------------------------------------------------------------------------ +*/ + +// =========================================================================== +// Logs loaded vk DLLs and their versions to Orbiter.log +// +void OapiExtension::LogvkModules(void) +{ + HMODULE hMods[1024]; + HANDLE hProcess; + DWORD cbNeeded; + + // Get a handle to the process. + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId()); + if (NULL == hProcess) { + return; + } + + // Get a list of all the modules in this process. + if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) + { + for (unsigned int i = 0, n = 0; i < (cbNeeded / sizeof(HMODULE)); ++i) + { + TCHAR szModName[MAX_PATH]; + + if (GetModuleBaseName(hProcess, hMods[i], szModName, ARRAYSIZE(szModName))) + { + std::string name = std::string(szModName); toUpper(name); + // Module of interest? + if (name == "vk.DLL" || 0 == name.compare(0, 6, "D3DX9_")) + { + // Get the full path to the module's file. + if (GetModuleFileNameEx(hProcess, hMods[i], szModName, ARRAYSIZE(szModName))) + { + + /*DWORD crc = 0; + FILE *hFile = 0; + if (fopen_s(&hFile, szModName, "rb") == 0) { + while (true) { + int data = fgetc(hFile); + if (data == EOF) break; + crc = crc ^ ((data&0xFF) << 8); + for (int j = 0; j < 8; j++) { + if (crc & 0x8000) crc = (crc << 1) ^ 0x1021; + else crc = (crc << 1); + crc &= 0xFFFF; + } + } + crc &= 0xFFFF; + fclose(hFile); + }*/ + + TCHAR versionString[128] = ""; + LPDWORD pDummy = 0; + DWORD versionInfoSize = GetFileVersionInfoSize(szModName, pDummy); + if (versionInfoSize) { + DWORD dummy = 0; + char *data = new char[versionInfoSize](); + if (GetFileVersionInfo(szModName, dummy, versionInfoSize, data)) + { + UINT size = 0; + VS_FIXEDFILEINFO *verInfo; + + if (VerQueryValue(data, "\\", (LPVOID*)&verInfo, &size) && size) + { + sprintf_s(versionString, ARRAYSIZE(versionString), + " [v %d.%d.%d.%d]", + HIWORD( verInfo->dwProductVersionMS ), + LOWORD( verInfo->dwProductVersionMS ), + HIWORD( verInfo->dwProductVersionLS ), + LOWORD( verInfo->dwProductVersionLS ) + ); + } + } + delete[] data; + } + + // Print the module name. + auto prefix = (n++ ? " " : "vk DLLs"); + oapiWriteLogV("%s : %s%s", prefix, szModName, versionString); + } + } + } + } + } + + // Release the handle to the process. + CloseHandle( hProcess ); +} + + + +// =========================================================================== +// Tries to get the initial settings from Orbiter_NG.cfg file +// +bool OapiExtension::GetConfigParameter(void) +{ + char *pLine; + bool orbiterSoundModuleEnabled = false; + + FILEHANDLE f = oapiOpenFile("Orbiter_NG.cfg", FILE_IN_ZEROONFAIL, ROOT); + if (f) { + char string[MAX_PATH]; + DWORD flags; + float scale, opacity; + + // General check for OrbiterSound module enabled + while (oapiReadScenario_nextline(f, pLine)) { + if (NULL != strstr(pLine, "OrbiterSound")) { + orbiterSoundModuleEnabled = true; + break; + } + } + + if (oapiReadItem_string(f, (char*)"ElevationMode", string)) { + if (1 == sscanf_s(string, "%lu", &flags)) { + elevationMode = flags; + } + } + + // Get planet rendering parameters + oapiReadItem_bool(f, (char*)"TileLoadThread", tileLoadThread); + + // Get directory config + if (oapiReadItem_string(f, (char*)"ConfigDir", string)) { + configDir = string; + } + if (oapiReadItem_string(f, (char*)"MeshDir", string)) { + meshDir = string; + } + if (oapiReadItem_string(f, (char*)"TextureDir", string)) { + textureDir = string; + } + if (oapiReadItem_string(f, (char*)"HightexDir", string)) { + hightexDir = string; + } + if (oapiReadItem_string(f, (char*)"ScenarioDir", string)) { + scenarioDir = string; + } + + oapiCloseFile(f, FILE_IN_ZEROONFAIL); + + // Log directory config + auto logPath = [](const char *name, const std::string &path) { + TCHAR buff[MAX_PATH]; + if (GetFullPathName(path.c_str(), MAX_PATH, buff, NULL)) { + DWORD ftyp = GetFileAttributes(buff); + auto result = (ftyp == INVALID_FILE_ATTRIBUTES || !(ftyp & FILE_ATTRIBUTE_DIRECTORY) ? " [[DIR NOT FOUND!]]" : ""); + oapiWriteLogV("%-11s: %s%s", name, buff, result); + } + }; + oapiWriteLog((char*)"---------------------------------------------------------------"); + logPath("BaseDir" , ".\\"); + logPath("ConfigDir" , configDir); + logPath("MeshDir" , meshDir); + logPath("TextureDir" , textureDir); + logPath("HightexDir" , hightexDir); + logPath("ScenarioDir", scenarioDir); + oapiWriteLog((char*)"---------------------------------------------------------------"); + LogvkModules(); + oapiWriteLog((char*)"---------------------------------------------------------------"); + + } + + // Check for the OrbiterSound version + if (orbiterSoundModuleEnabled) { + orbiterSound40 = false; + + f = oapiOpenFile("Sound\\version.txt", FILE_IN_ZEROONFAIL, ROOT); + while (f && oapiReadScenario_nextline(f, pLine)) { + if (NULL != strstr(pLine, "OrbiterSound 4.0 (3D)")) { + orbiterSound40 = true; + break; + } + } + oapiCloseFile(f, FILE_IN_ZEROONFAIL); + } + + // Check for WINE environment + HMODULE hntdll = GetModuleHandle("ntdll.dll"); + if (NULL != hntdll) + { + // static const char * (CDECL *pwine_get_version)(void); + void *pWineGetVersion = (void *)GetProcAddress(hntdll, "wine_get_version"); + if (NULL != pWineGetVersion) { + runsUnderWINE = true; + } // else { Not running WINE } + } // else { Not running on NT ?! } + + return true; +} + +// =========================================================================== +// Try to read a startup scenario given by "-s" command line parameter +// +std::string OapiExtension::ScanCommandLine (void) +{ + std::string commandLine(GetCommandLine()); + + // Is there a "-s " option at all? + size_t pos = rfind_ci(commandLine, "-s"); + if (pos != std::string::npos) + { + std::string scenarioName = commandLine.substr(pos+2, std::string::npos); + trim(scenarioName); + + // Remove (optional) quotes + std::replace(scenarioName.begin(), scenarioName.end(), '"', ' '); + + // Build the path (like ".\\Scenarios\\(Current State).scn" + //startupScenario = GetScenarioDir() + trim(scenarioName) + ".scn"; + return GetScenarioDir() + trim(scenarioName) + ".scn"; + } + return ""; +} + +// --- eof --- diff --git a/OVP/VulkanClient/OapiExtension.h b/OVP/VulkanClient/OapiExtension.h new file mode 100644 index 000000000..36952d47b --- /dev/null +++ b/OVP/VulkanClient/OapiExtension.h @@ -0,0 +1,174 @@ +// ============================================================== +// OapiExtension.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012-2018 Peter Schneider (Kuddel) +// ============================================================== + +#ifndef __OAPIEXTENSION_H +#define __OAPIEXTENSION_H + +#include +#include +#include "Util.h" + +class vkConfig; + +/// \defgroup cfgprm Configuration parameter identifiers +/// Used by OapiExtension::GetConfigParam() +/// @{ + +/// Load tiles in separate thread? +/// \par Parameter type: +/// bool +#define CFGPRM_TILELOADTHREAD 0x1006 + +/// Elevation interpolation mode 0-cubic 1-linear +/// \par Parameter type: +/// DWORD +#define CFGPRM_ELEVATIONINTERPOLATION 0x1007 + +/// @} + + +/** + * \brief Configuration parameter provider for non-API parameters + * + * This class provides access to config-parameters that are not (yet) available + * through the 'official' API of Orbiter. Currently the switches and values of + * the 'Visual helpers' dialog. + */ +class OapiExtension +{ +public: + + /** + * \brief Initializes the OapiExtension. + * + * This function should be called early on, because it defines whether this + * class will install any hooking functions depending on the \ref + * vkConfig::DisableVisualHelperReadout value. + * \param Config A reference to the configuration class, to get the \ref + * vkConfig::DisableVisualHelperReadout value. + */ + static void GlobalInit(const vkConfig &Config); + + /** + * \brief Same functionality than 'official' GetConfigParam, but for + * non-provided config parameters + * + * This function can be used to access various configuration parameters + * defined in the OapiExtension core (e.g. body force vector display mode). + * \param paramtype Parameter identifier (see \ref cfgprm) + * \return Pointer to parameter + * \note The pointer must be cast into the appropriate variable type. + * The variable types can be found in the parameter type list (\ref + * cfgprm). + * \par Example: + * \code + * float scale = *(float*)GetConfigParam(CFGPRM_COORDINATEAXESSCALE); + * \endcode + */ + static const void *GetConfigParam (DWORD paramtype); + + /** + * \brief Returns whether we run Orbiter 2010 (and derivatives) + * + * \return Whether we run Orbiter 2010 + */ + static const bool RunsOrbiter2010 () { return isOrbiter2010; } + + /** + * \brief Returns whether OrbiterSound 4.0 is up and running + * + * \return Whether OrbiterSound 4.0 is active + */ + static const bool RunsOrbiterSound40() { return orbiterSound40; } + + /** + * \brief Returns whether Orbiter runs under WINE + * + * \return Whether Orbiter runs under WINE + */ + static const bool RunsUnderWINE() { return runsUnderWINE; } + + /** + * \brief Returns whether the current Scenario uses Spacecraft.dll + * + * \return Whether the current Scenario uses Spacecraft.dll + */ + static const bool RunsSpacecraftDll() { return runsSpacecraftDll; } + + /** + * \brief Returns the current path to Config folder + * + * \return Path to Config folder + */ + static const char *GetConfigDir() { return configDir.c_str(); } + + /** + * \brief Returns the current path to Mesh folder + * + * \return Path to Mesh folder + */ + static const char *GetMeshDir() { return meshDir.c_str(); } + + /** + * \brief Returns the current path to Texture folder + * + * \return Path to Texture folder + */ + static const char *GetTextureDir() { return textureDir.c_str(); } + + /** + * \brief Returns the current path to High Texture folder + * + * \return Path to High Texture folder + */ + static const char *GetHightexDir() { return hightexDir.c_str(); } + + /** + * \brief Returns the current path to Scenario folder + * + * \return Path to Scenario folder + */ + static const char *GetScenarioDir() { return scenarioDir.c_str(); } + + /** + * \brief Returns the value of a Startup Scenario (-s CLI option) + * + * \return Path to Startup Scenario ("" if not given via CLI) + */ + static const char *GetStartupScenario() { return startupScenario.c_str(); } + + // NOT REALLY PUBLIC! (Only to be used by FileParser class!) + static void SetSpacecraftDllUsed(bool value = true) { runsSpacecraftDll = value; } + +private: + OapiExtension(void); // avoid default constructor creation & instantiation + ~OapiExtension(void); + + // Planet rendering parameters + static DWORD elevationMode; + static bool tileLoadThread; ///< Whether to load planet tiles in separate thread [true|false] + // OrbiterSound 4.0 helper + static bool isOrbiter2010; ///< Whether we run Orbiter 2010 (and derivatives) + static bool orbiterSound40; + static std::string configDir; ///< Value of Orbiters ConfigDir parameter + static std::string meshDir; ///< Value of Orbiters MeshDir parameter + static std::string textureDir; ///< Value of Orbiters TextureDir parameter + static std::string hightexDir; ///< Value of Orbiters HightexDir parameter + static std::string scenarioDir; ///< Value of Orbiters ScenarioDir config parameter + static std::string startupScenario; ///< Scenario-Path if Orbiters was started with "-s {Scenario}" command line parameter + // WINE detection + static bool runsUnderWINE; ///< Whether Orbiter runs under WINE + // Spacecraft.dll detection + static bool runsSpacecraftDll; ///< Whether the current Scenario uses Spacecraft.dll + static void LogvkModules(void); ///< Logs loaded vk DLLs and their versions + + static bool configParameterRead; ///< Indication that Orbiter_NG.cfg has been read + static bool GetConfigParameter(void); ///< Tries to read parameter from Orbiter_NG.cfg + static std::string ScanCommandLine(void); ///< Tries to read a Startup Scenario given by "-s" command line parameter +}; + +#endif // !__OAPIEXTENSION_H diff --git a/OVP/VulkanClient/Pad.cpp b/OVP/VulkanClient/Pad.cpp new file mode 100644 index 000000000..4f344469f --- /dev/null +++ b/OVP/VulkanClient/Pad.cpp @@ -0,0 +1,2054 @@ +// =================================================== +// Copyright (C) 2012-2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + + +#include "Pad.h" +#include "Client.h" +#include "Surface.h" +#include "Util.h" +#include "TextMgr.h" +#include "Config.h" +#include "Log.h" +#include "Mesh.h" + +using namespace oapi; + + +// =============================================================================================== +// Font cache +// =============================================================================================== + +struct FontCache { + int height; + int orient; + bool prop; + char face[64]; + FontStyle style; + vkTextPtr pFont; +}; + +struct QFontCache { + int height; + int width; + int weight; + char face[64]; + FontStyle style; + float spacing; + vkTextPtr pFont; +}; + +std::vector qcache; +std::vector fcache; + + +oapi::Font * deffont = 0; +oapi::Pen * defpen = 0; + + +// =============================================================================================== +// +void vkPad::SinCos(int n, int k) +{ + pSinCos[k] = new FVECTOR2[n]; + float s = float(PI2) / float(n); + float q = -s / 2.0f; + for (int i = 0; iGetBufferPointer()); + MessageBoxA(0, (char*)errors->GetBufferPointer(), "Sketchpad.fx Error", 0); + FatalAppExitA(0,"Critical error has occured. See Orbiter.log for details"); + } + + if (FX==0) { + LogErr("Failed to create an Effect (%s)",name); + MissingRuntimeError(); + return; + } + + if (Config->ShaderDebug) { + LPD3DXBUFFER pBuffer = NULL; + if (D3DXDisassembleEffect(FX, true, &pBuffer) == S_OK) { + FILE *fp = NULL; + if (!fopen_s(&fp, "Sketchpad_asm.html", "w")) { + fwrite(pBuffer->GetBufferPointer(), 1, pBuffer->GetBufferSize(), fp); + fclose(fp); + } + pBuffer->Release(); + } + } + + + pNoise = gc->GetNoiseTex(); + + eDrawMesh = FX->GetTechniqueByName("SketchMesh"); + eSketch = FX->GetTechniqueByName("SketchTech"); + eVP = FX->GetParameterByName(0, "gVP"); + eTex0 = FX->GetParameterByName(0, "gTex0"); + eFnt0 = FX->GetParameterByName(0, "gFnt0"); + eDashEn = FX->GetParameterByName(0, "gDashEn"); + eW = FX->GetParameterByName(0, "gW"); + eKey = FX->GetParameterByName(0, "gKey"); + ePen = FX->GetParameterByName(0, "gPen"); + eWVP = FX->GetParameterByName(0, "gWVP"); + eFov = FX->GetParameterByName(0, "gFov"); + eRandom = FX->GetParameterByName(0, "gRandom"); + eTarget = FX->GetParameterByName(0, "gTarget"); + eTexEn = FX->GetParameterByName(0, "gTexEn"); + eFntEn = FX->GetParameterByName(0, "gFntEn"); + eKeyEn = FX->GetParameterByName(0, "gKeyEn"); + eWide = FX->GetParameterByName(0, "gWide"); + eWidth = FX->GetParameterByName(0, "gWidth"); + eSize = FX->GetParameterByName(0, "gSize"); + eMtrl = FX->GetParameterByName(0, "gMtrl"); + eShade = FX->GetParameterByName(0, "gShade"); + ePos = FX->GetParameterByName(0, "gPos"); + ePos2 = FX->GetParameterByName(0, "gPos2"); + eCov = FX->GetParameterByName(0, "gCov"); + eCovEn = FX->GetParameterByName(0, "gClipEn"); + eClearEn = FX->GetParameterByName(0, "gClearEn"); + eEffectsEn= FX->GetParameterByName(0, "gEffectsEn"); + eNoiseTex = FX->GetParameterByName(0, "gNoiseTex"); + eGamma = FX->GetParameterByName(0, "gGamma"); + eNoiseColor = FX->GetParameterByName(0, "gNoiseColor"); + eColorMatrix = FX->GetParameterByName(0, "gColorMatrix"); + +} + + +// =============================================================================================== +// +void vkPad::GlobalExit() +{ + LogAlw("Clearing Font Cache... %d Fonts are stored in the cache",fcache.size() + qcache.size()); + for (auto it = fcache.begin(); it != fcache.end(); ++it) { + delete *it; + } + for (auto it = qcache.begin(); it != qcache.end(); ++it) { + delete *it; + } + fcache.clear(); + qcache.clear(); + + SAFE_RELEASE(FX); + SAFE_DELETEA(Idx); + SAFE_DELETEA(Vtx); + for (int i=0;i<4;i++) SAFE_DELETEA(pSinCos[i]); + + if (log) fclose(log); + log = NULL; + + DeleteCriticalSection(&LogCrit); +} + +// =============================================================================================== +// +void vkPad::Log(const char *format, ...) const +{ + if (log == NULL) return; + EnterCriticalSection(&LogCrit); + char ErrBuf[1024]; + DWORD th = GetCurrentThreadId(); + va_list args; + va_start(args, format); + _vsnprintf_s(ErrBuf, 1024, 1024, format, args); + va_end(args); + fprintf_s(log, "<0x%X> [%s] %s\n", th, _PTR(this), ErrBuf); + fflush(log); + LeaveCriticalSection(&LogCrit); +} + +// =============================================================================================== +// +void vkPad::Reset() +{ + bBeginDraw = false; + bMustEndScene = false; + vI = 0; + iI = 0; + oapiMatrixIdentity(&mO); + oapiMatrixIdentity(&mVOrig); + oapiMatrixIdentity(&mPOrig); + vTarget = F4_One; + pTgt = NULL; + pDep = NULL; + zfar = 1.0f; + tgt = { 0,0,0,0 }; +} + + + +// =============================================================================================== +// Restore Default Settings: Fonts, Pens, Colors, etc... +// +void vkPad::LoadDefaults() +{ + assert(vI == 0); + + cfont = deffont; + cpen = NULL; + cbrush = NULL; + hTexture = NULL; + hFontTex = NULL; + cx = 0; + cy = 0; + linescale = 1.0f; + pattern = 1.0f; + Enable = 0; + RenderConfig = 0; + + tah = LEFT; + tav = TOP; + vmode = ORTHO; + tCurrent = NONE; + Change = SKPCHG_ALL; + bkmode = TRANSPARENT; + dwBlendState = Sketchpad::BlendState::ALPHABLEND; + + bColorComp = true; + bLine = false; + bEnableScissor = false; + bDepthEnable = false; + bColorKey = false; + QPen.bEnabled = false; + QBrush.bEnabled = false; + + memset(ClipData, 0, sizeof(ClipData)); + ScissorRect = { 0,0,0,0 }; + + cColorKey = FVECTOR4(DWORD(0)); + brushcolor = SkpColor(0xFF00FF00); + bkcolor = SkpColor(0xFF000000); + textcolor = SkpColor(0xFF00FF00); + pencolor = SkpColor(0xFF00FF00); + + oapiMatrixIdentity(&mVP); + oapiMatrixIdentity(&mW); + oapiMatrixIdentity(&mP); + oapiMatrixIdentity(&mV); + oapiMatrixIdentity((FMATRIX4*)&ColorMatrix); + + Gamma = F4_One; + Noise = F4_Zero; +} + + +// =============================================================================================== +// class vkPad +// =============================================================================================== +// Constructor will create vkPad interface but doesn't prepare it for drawing. +// BeginDrawing() must be called +// +vkPad::vkPad(SURFHANDLE s, const char *_name) : Sketchpad(s), + _isSaveBuffer(false), + _saveBuffer(NULL), + _saveBufferSize(0) +{ +#ifdef SKPDBG + Log("#### Sketchpad Interface Created"); +#endif + pRState = new RenderState(pDev); + if (_name) strcpy_s(name, 32, _name); + else strcpy_s(name, 32, "NoName"); + Reset(); + LoadDefaults(); +} + + +// =============================================================================================== +// class vkPad +// =============================================================================================== +// Constructor will create vkPad interface but doesn't prepare it for drawing. +// BeginDrawing() must be called +// +vkPad::vkPad(const char *_name) : Sketchpad(NULL), + _isSaveBuffer(false), + _saveBuffer(NULL), + _saveBufferSize(0) +{ +#ifdef SKPDBG + Log("#### Sketchpad Interface Created (NoTgt)"); +#endif + if (_name) strcpy_s(name, 32, _name); + else strcpy_s(name, 32, "NoName"); + pRState = new RenderState(pDev); + Reset(); + LoadDefaults(); +} + + + +// =============================================================================================== +// +vkPad::~vkPad () +{ +#ifdef SKPDBG + Log("#### Sketchpad Interface Deleted"); +#endif + if (pRState) delete pRState; + assert(bBeginDraw == false); + SAFE_DELETEA(_saveBuffer); +} + + +// =============================================================================================== +// Private +// +void vkPad::SetViewProj(const FMATRIX4* pV, const FMATRIX4* pP) +{ + mV = mVOrig = *pV; + mP = mPOrig = *pP; +} + + +// =============================================================================================== +// Bind existing Sketchpad interface to TOP render targets and prepare for rendering +// +void vkPad::BeginDrawing() +{ + // Acquire render targets from Stack + BeginDrawing(gc->GetTopRenderTarget(), gc->GetTopDepthStencil()); +} + + + +// =============================================================================================== +// Bind existing Sketchpad interface to render targets and prepare for rendering +// +void vkPad::BeginDrawing(LPDIRECT3DSURFACE9 pRenderTgt, LPDIRECT3DSURFACE9 pDepthStensil) +{ +#ifdef SKPDBG + Log("==== BeginDrawing %s, %s ====\n", _PTR(pRenderTgt), _PTR(pDepthStensil)); +#endif + + assert(pRenderTgt != NULL); + + if (vI != 0) LogErr("Sketchpad %s has received drawing commands outside Begin() End() pair", _PTR(this)); + + if (bBeginDraw == true) { + LogErr("vkPad::BeginDrawing() called multiple times"); + HALT(); + } + + // Capture current device state + pRState->Capture(); + + Reset(); + + bBeginDraw = true; + + // If not already in scene, then start a new one + if (gc->IsInScene() == false) { + gc->BeginScene(); + bMustEndScene = true; + } + else bMustEndScene = false; + + pRenderTgt->GetDesc(&tgt_desc); + zfar = float(max(tgt_desc.Width, tgt_desc.Height)); + D3DMAT_OrthoOffCenterLH(&mO, 0.0f, (float)tgt_desc.Width, (float)tgt_desc.Height, 0.0f, 0.0f, zfar); + vTarget = FVECTOR4(2.0f / (float)tgt_desc.Width, 2.0f / (float)tgt_desc.Height, (float)tgt_desc.Width, (float)tgt_desc.Height); + + + + pTgt = pRenderTgt; + pDep = pDepthStensil; + tgt = { 0, 0, (long)tgt_desc.Width, (long)tgt_desc.Height }; + Change = SKPCHG_ALL; +} + + + +// =============================================================================================== +// +void vkPad::EndDrawing() +{ +#ifdef SKPDBG + Log("==== EndDrawing ====\n"); +#endif + + if (bBeginDraw == false) { + LogErr("vkPad::EndDrawing() called without BeginDrawing()"); + HALT(); + } + + Flush(); + + bBeginDraw = false; + + if (pRState) pRState->Restore(); + + if (bMustEndScene) { + gc->EndScene(); + bMustEndScene = false; + } +} + + + +// =============================================================================================== +// +bool vkPad::Flush(HPOLY hPoly) +{ + if (bBeginDraw == false) { + LogErr("vkPad::Flush() called without BeginDrawing()"); + HALT(); + } + + UINT numPasses; + static DWORD bkALPHA, bkZEN, bkZW, bkCULL; + + if ((iI == 0) && (hPoly == NULL)) { +#ifdef SKPDBG + Log("Flush (Nothing)", hPoly, iI); +#endif + return false; + } + + DWORD dwBlend = dwBlendState & 0xF; + DWORD dwFilter = dwBlendState & 0xF0; + +#ifdef SKPDBG + char buf[128]; strcpy_s(buf, 128, ""); + char buf2[128]; strcpy_s(buf2, 128, ""); + + if (dwBlend == SKPBS_ALPHABLEND) strcpy_s(buf, 128, "SKPBS_ALPHABLEND"); + if (dwBlend == SKPBS_COPY) strcpy_s(buf, 128, "SKPBS_COPY"); + if (dwBlend == SKPBS_COPY_ALPHA) strcpy_s(buf, 128, "SKPBS_COPY_ALPHA"); + if (dwBlend == SKPBS_COPY_COLOR) strcpy_s(buf, 128, "SKPBS_COPY_COLOR"); + + if (bDepthEnable && pDep) strcpy_s(buf2, 128, "DEPTH_ENABLED"); + else strcpy_s(buf2, 128, "DEPTH_DISABLED"); + + Log("Flush [%s] [%s] hPloy=%s, iI=%hu", buf, buf2, _PTR(hPoly), iI); +#endif + + HR(pDev->GetRenderState(D3DRS_ALPHABLENDENABLE, &bkALPHA)); + + //HR(pDev->GetRenderState(D3DRS_ZENABLE, &bkZEN)); + //HR(pDev->GetRenderState(D3DRS_ZWRITEENABLE, &bkZW)); + //HR(pDev->GetRenderState(D3DRS_CULLMODE, &bkCULL)); + + HR(pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE)); + HR(pDev->SetVertexDeclaration(pSketchpadDecl)); + + HR(FX->SetFloat(eRandom, float(oapiRand()))); + HR(FX->SetVector(eTarget, _DX(vTarget))); + HR(FX->SetTechnique(eSketch)); + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + + if (vmode == ORTHO) { + HR(FX->BeginPass(0)); + } + else { + HR(FX->BeginPass(1)); + } + + if (dwBlend == Sketchpad::BlendState::ALPHABLEND) { + pDev->SetRenderState(D3DRS_COLORWRITEENABLE, 0x7); + HR(pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE)); + } + else if (dwBlend == Sketchpad::BlendState::COPY) { + pDev->SetRenderState(D3DRS_COLORWRITEENABLE, 0xF); + HR(pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE)); + } + else if (dwBlend == Sketchpad::BlendState::COPY_ALPHA) { + pDev->SetRenderState(D3DRS_COLORWRITEENABLE, 0x8); + HR(pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE)); + } + else if (dwBlend == Sketchpad::BlendState::COPY_COLOR) { + pDev->SetRenderState(D3DRS_COLORWRITEENABLE, 0x7); + HR(pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE)); + } + + if (dwFilter) { + if (dwFilter == Sketchpad::BlendState::FILTER_POINT) { + pDev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + pDev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + } + if (dwFilter == Sketchpad::BlendState::FILTER_ANISOTROPIC) { + pDev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC); + pDev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC); + HR(pDev->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 8)); + } + } + + if (bDepthEnable && pDep) { + pDev->SetRenderState(D3DRS_ZENABLE, 1); + pDev->SetRenderState(D3DRS_ZWRITEENABLE, 1); + } + else { + pDev->SetRenderState(D3DRS_ZENABLE, 0); + pDev->SetRenderState(D3DRS_ZWRITEENABLE, 0); + } + + if (hPoly) { + assert(iI == 0); + vkPolyBase *pBase = static_cast(hPoly); + pBase->Draw(this, pDev); + } + else { + if (tCurrent == TRIANGLE) HR(pDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, vI, iI / 3, Idx, D3DFMT_INDEX16, Vtx, sizeof(SkpVtx))); + if (tCurrent == LINE) HR(pDev->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, vI, iI / 2, Idx, D3DFMT_INDEX16, Vtx, sizeof(SkpVtx))); + } + + HR(FX->EndPass()); + HR(FX->End()); + + HR(pDev->SetRenderState(D3DRS_COLORWRITEENABLE, 0xF)); + HR(pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, bkALPHA)); + + //HR(pDev->SetRenderState(D3DRS_ZENABLE, bkZEN)); + //HR(pDev->SetRenderState(D3DRS_ZWRITEENABLE, bkZW)); + //HR(pDev->SetRenderState(D3DRS_CULLMODE, bkCULL)); + + HR(pDev->SetRenderState(D3DRS_SCISSORTESTENABLE, 0)); + + if (dwFilter) { + pDev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + pDev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + } + + iI = vI = 0; + + return true; + +} + + +// =============================================================================================== +// +bool vkPad::Topology(Topo tRequest) +{ + if (tRequest == LINE) { + if (bLine) { // Can we do LINE Topology ? + if (tRequest != tCurrent) Change |= SKPCHG_TOPOLOGY; + SetupDevice(tRequest); + return true; + } + return false; + } + + if (tRequest != tCurrent) Change |= SKPCHG_TOPOLOGY; + + SetupDevice(tRequest); + + return true; +} + + +// =============================================================================================== +// +void vkPad::SetupDevice(Topo tNew) +{ + + // Check that this is the top one. Only do the check for ones created with oapiGetSketchpad() + //if (GetSurface()) assert(gc->GetTopInterface() == this); + + // If the queue is filling up, Flush it + // + if (iI > (nQueueMax >> 1)) Flush(); + + + // Has something changed ? + // + if (!Change) return; + + + // Flush pending drawing instructions before changing a device state + // + Flush(); + + tCurrent = tNew; + +#ifdef SKPDBG + char buf[512]; + strcpy_s(buf, 512, ""); + if (Change&SKPCHG_TOPOLOGY) strcat_s(buf, 512, "SKPCHG_TOPOLOGY "); + if (Change&SKPCHG_TRANSFORM) strcat_s(buf, 512, "SKPCHG_TRANSFORM "); + if (Change&SKPCHG_CLIPCONE) strcat_s(buf, 512, "SKPCHG_CLIPCONE "); + if (Change&SKPCHG_PEN) strcat_s(buf, 512, "SKPCHG_PEN "); + if (Change&SKPCHG_EFFECTS) strcat_s(buf, 512, "SKPCHG_EFFECTS "); + if (Change&SKPCHG_CLIPRECT) strcat_s(buf, 512, "SKPCHG_CLIPRECT "); + if (Change&SKPCHG_TEXTURE) strcat_s(buf, 512, "SKPCHG_TEXTURE "); + if (Change&SKPCHG_FONT) strcat_s(buf, 512, "SKPCHG_FONT "); + if (Change&SKPCHG_DEPTH) strcat_s(buf, 512, "SKPCHG_DEPTH "); + if (Change&SKPCHG_PATTERN) strcat_s(buf, 512, "SKPCHG_PATTERN "); + Log("StateChange = [%s]", buf); +#endif + + + // Apply a new setup ----------------------------------------------------------------- + // + if (Change == SKPCHG_TOPOLOGY) { + HR(FX->SetBool(eWide, (tCurrent == TRIANGLE))); + Change = 0; + return; + } + + + // Apply a new setup ----------------------------------------------- + // + if (Change & (SKPCHG_TRANSFORM | SKPCHG_CLIPCONE)) { + + if (vmode == ORTHO) { + FMATRIX4 mWVP; + oapiMatrixMultiply(&mWVP, &mW, &mO); + HR(FX->SetMatrix(eWVP, _DX(mWVP))); + HR(FX->SetMatrix(eVP, _DX(mO))); + HR(FX->SetMatrix(eW, _DX(mW))); + HR(FX->SetBool(eCovEn, false)); + } + else { + float d = float(tgt_desc.Height) * mP.m22; + float f = atan(1.0f / d) * 1.7f; + oapiMatrixMultiply(&mVP, &mV, &mP); + HR(FX->SetMatrix(eVP, _DX(mVP))); + HR(FX->SetMatrix(eW, _DX(mW))); + HR(FX->SetFloat(eFov, f)); + HR(FX->SetBool(eCovEn, ClipData[0].bEnable || ClipData[1].bEnable)); + } + } + + + // Apply a new setup ----------------------------------------------------------------- + // + if (Change & SKPCHG_PEN) { + float offset = 0.0f; + int w = int(ceil(GetPenWidth())); + if ((w & 1) == 0) offset = 0.5f; + HR(FX->SetValue(ePen, &pencolor.fclr, sizeof(FVECTOR4))); + HR(FX->SetBool(eDashEn, IsDashed())); + HR(FX->SetValue(eWidth, &(FVECTOR3(GetPenWidth(), pattern*0.13f, offset)), sizeof(FVECTOR3))); + } + + + // Apply a new setup ----------------------------------------------------------------- + // + if (Change & SKPCHG_EFFECTS) { + HR(FX->SetValue(eNoiseColor, &Noise, sizeof(FVECTOR4))); + HR(FX->SetValue(eGamma, &Gamma, sizeof(FVECTOR4))); + HR(FX->SetTexture(eNoiseTex, pNoise)); + HR(FX->SetValue(eColorMatrix, &ColorMatrix, sizeof(FMATRIX4))); + HR(FX->SetBool(eEffectsEn, Enable != 0)); + } + + + // Apply a new setup ----------------------------------------------------------------- + // + if (Change & SKPCHG_CLIPRECT) { + if (bEnableScissor) { + pDev->SetScissorRect(&ScissorRect); + pDev->SetRenderState(D3DRS_SCISSORTESTENABLE, 1); + } + else { + pDev->SetRenderState(D3DRS_SCISSORTESTENABLE, 0); + } + } + + + // Apply a new setup ----------------------------------------------------------------- + // + if (Change & SKPCHG_CLIPCONE) { + if (ClipData[0].bEnable || ClipData[1].bEnable) { + HR(FX->SetValue(ePos, &ClipData[0].uDir, sizeof(FVECTOR3))); + HR(FX->SetValue(ePos2, &ClipData[1].uDir, sizeof(FVECTOR3))); + HR(FX->SetValue(eCov, &(FVECTOR4(ClipData[0].ca, ClipData[0].dst, ClipData[1].ca, ClipData[1].dst)), sizeof(FVECTOR4))); + } + } + + + // Apply a new setup ----------------------------------------------------------------- + // + if (Change & (SKPCHG_TEXTURE | SKPCHG_FONT)) { + + float tw = 1.0f, th = 1.0f; + + D3DSURFACE_DESC desc; + + if (hTexture) { + + hTexture->GetLevelDesc(0, &desc); + + tw = 1.0f / float(desc.Width); + th = 1.0f / float(desc.Height); + + if (Change & SKPCHG_TEXTURE) { + HR(FX->SetTexture(eTex0, hTexture)); + HR(FX->SetBool(eKeyEn, bColorKey)); + HR(FX->SetValue(eKey, &cColorKey, sizeof(FVECTOR4))); + } + } + + if (hFontTex) { + if (Change & SKPCHG_FONT) { + HR(FX->SetTexture(eFnt0, hFontTex)); + } + } + + HR(FX->SetVector(eSize, _DX(FVECTOR4(tw, th, 1.0f, 1.0f)))); + HR(FX->SetBool(eTexEn, (hTexture != NULL))); + HR(FX->SetBool(eFntEn, (hFontTex != NULL))); + } + + + // Apply a new setup ----------------------------------------------------------------- + // + if (Change & SKPCHG_TOPOLOGY) { + HR(FX->SetBool(eWide, (tCurrent == TRIANGLE))); + } + + // All Clear + Change = 0; + + return; +} + + +// =============================================================================================== +// +DWORD vkPad::ColorComp(DWORD c) const +{ + if (bColorComp) if ((c & 0xFF000000) == 0) return c | 0xFF000000; + return c; +} + +// =============================================================================================== +// +SkpColor vkPad::ColorComp(const SkpColor &c) const +{ + if (bColorComp) { + if ((c.dclr & 0xFF000000) == 0) { + DWORD q = c.dclr | 0xFF000000; + return SkpColor(q); + } + } + return c; +} + + + + + + +// =============================================================================================== +// +HDC vkPad::GetDC() +{ + DWORD *cf = SURFACE(GetSurface())->GetClientFlags(); + + if ((*cf & OAPISURF_SKP_GDI_WARN) == 0) { + *cf |= OAPISURF_SKP_GDI_WARN; + LogErr("Call to obsolete Sketchpad::GetDC() detected. Returned NULL"); + if (Config->DebugBreak) DebugBreak(); + } + + return NULL; +} + + +// =============================================================================================== +// +Font *vkPad::SetFont(Font *font) +{ + if (cfont == font) return font; + +#ifdef SKPDBG + LOGFONTA lf; + GetObjHandle(font->GetGDIFont(), sizeof(LOGFONT), &lf); + Log("SetFont(%s) Face=[%s] Height=%d Weight=%d", _PTR(font), lf.lfFaceName, lf.lfHeight, lf.lfWeight); +#endif + // No "Change" falgs required here, covered in SetFontTextureNative() + + Font *pfont = cfont; + if (font) cfont = font; + else cfont = deffont; + return pfont; +} + + +// =============================================================================================== +// +Brush *vkPad::SetBrush (Brush *brush) +{ + if (cbrush == brush && QBrush.bEnabled == false) return brush; + +#ifdef SKPDBG + Log("SetBrush(%s)", _PTR(brush)); +#endif + + // No "Change" falgs required here, color stored in vertex data + + QBrush.bEnabled = false; + + Brush *pbrush = cbrush; + cbrush = brush; + if (cbrush) brushcolor = ColorComp((static_cast(cbrush))->clr); + else brushcolor = SkpColor(0); + + IsLineTopologyAllowed(); + + return const_cast(pbrush); +} + + +// =============================================================================================== +// +Pen *vkPad::SetPen (Pen *pen) +{ + if (cpen == pen && QPen.bEnabled == false) return pen; + +#ifdef SKPDBG + Log("SetPen(%s)", _PTR(pen)); +#endif + + // Change required due to pen width and style change + Change |= SKPCHG_PEN; + + QPen.bEnabled = false; + + Pen *ppen = cpen; + if (pen) cpen = pen; + else cpen = NULL; + if (cpen) pencolor = ColorComp(static_cast(cpen)->clr); + + IsLineTopologyAllowed(); + + return ppen; +} + + +// =============================================================================================== +// +void vkPad::SetTextAlign (TAlign_horizontal _tah, TAlign_vertical _tav) +{ + // No Change flags + tah = _tah; + tav = _tav; +} + + +// =============================================================================================== +// +DWORD vkPad::SetTextColor(DWORD col) +{ + // Color stored in vertex data, no Change required + DWORD prev = textcolor.dclr; + textcolor = SkpColor(ColorComp(col)); + return prev; +} + + +// =============================================================================================== +// +DWORD vkPad::SetBackgroundColor(DWORD col) +{ + // Color stored in vertex data, no Change required + DWORD prev = bkcolor.dclr; + bkcolor = SkpColor(ColorComp(col)); + return prev; +} + + +// =============================================================================================== +// +void vkPad::SetBackgroundMode(BkgMode mode) +{ + // No Change required + + switch (mode) { + case BK_TRANSPARENT: bkmode = TRANSPARENT; break; + case BK_OPAQUE: bkmode = OPAQUE; break; + } +} + + +// =============================================================================================== +// +DWORD vkPad::GetCharSize () +{ + TEXTMETRIC tm; + if (cfont==NULL) return 0; + static_cast(cfont)->pFont->GetvkTextMetrics(&tm); + return MAKELONG(tm.tmHeight-tm.tmInternalLeading, tm.tmAveCharWidth); +} + + +// =============================================================================================== +// +DWORD vkPad::GetLineHeight () // ... *with* "internal leading" +{ + TEXTMETRIC tm; + if (cfont == NULL) return 0; + static_cast(cfont)->pFont->GetvkTextMetrics(&tm); + return tm.tmHeight; +} + + +// =============================================================================================== +// +DWORD vkPad::GetTextWidth (const char *str, int len) +{ + if (str) if (str[0] == '_') if (strcmp(str, "_SkpVerInfo") == 0) return 2; + if (cfont==NULL) return 0; + return DWORD(static_cast(cfont)->pFont->Length2(str, len)); +} + + +// =============================================================================================== +// +void vkPad::SetOrigin (int x, int y) +{ +#ifdef SKPDBG + Log("SetOrigin(%d, %d)", x, y); +#endif + Change |= SKPCHG_TRANSFORM; + + mW.m41 = float(x); + mW.m42 = float(y); +} + + +// =============================================================================================== +// +void vkPad::GetOrigin(int *x, int *y) const +{ + if (x) *x = int(mW.m41); + if (y) *y = int(mW.m42); +} + + +// =============================================================================================== +// +bool vkPad::HasPen() const +{ + if (QPen.bEnabled) return true; + if (cpen==NULL) return false; + if (static_cast(cpen)->style==PS_NULL) return false; + return true; +} + + +// =============================================================================================== +// +void vkPad::IsLineTopologyAllowed() +{ + bLine = false; + if ((HasPen() == true) && (GetPenWidth() < 1.1f)) bLine = true; +} + + +// =============================================================================================== +// +bool vkPad::IsDashed() const +{ + if (QPen.bEnabled) return QPen.style == 2; + if (cpen==NULL) return false; + if (static_cast(cpen)->style==PS_DOT) return true; + return false; +} + + +// =============================================================================================== +// +bool vkPad::IsAlphaTarget() const +{ + if (tgt_desc.Format == D3DFMT_A8R8G8B8) return true; + if (tgt_desc.Format == D3DFMT_A16B16G16R16F) return true; + if (tgt_desc.Format == D3DFMT_A32B32G32R32F) return true; + return false; +} + + +// =============================================================================================== +// +bool vkPad::HasBrush() const +{ + if (QBrush.bEnabled) return true; + return (cbrush != NULL); +} + + +// =============================================================================================== +// +float vkPad::GetPenWidth() const +{ + if (QPen.bEnabled) return linescale * QPen.width; + if (cpen==NULL) return 1.0f; + return float(static_cast(cpen)->width*linescale); +} + + +// =============================================================================================== +// +void vkPad::WrapOneLine (char* str, int len, int maxWidth) +{ + vkTextPtr pText = static_cast(cfont)->pFont; + if (pText->Length2(str) > maxWidth) { + char *pStr = str, // sub-string start + *it = pStr, // 'iterator' char + *pEnd = str + len, // <= point to terminating zero + *pLastSpace = NULL; + float currentWidth = 0; + while (it < pEnd) + { + while (it < pEnd && currentWidth < maxWidth) { + if (*it == ' ') { pLastSpace = it; } + currentWidth = pText->Length2( pStr, int(it - pStr + 1) ); + ++it; + } + // only split if we have space for it AND we have to (avoids cutting the last word) + if (pLastSpace != NULL && currentWidth >= maxWidth) { + *pLastSpace = '\n'; + pStr = pLastSpace + 1; // skip the space (now a newline) + currentWidth = 0; + pLastSpace = NULL; + } + } + } +} + +// =============================================================================================== +// +bool vkPad::TextBox (int x1, int y1, int x2, int y2, const char *str, int len) +{ +#ifdef SKPDBG + Log("TextBox()"); +#endif + + // No "Setup" required, done on PrintSkp + + if (cfont==NULL) return false; + + bool result = true; + int lineSpace = static_cast(cfont)->pFont->GetLineSpace(); + + ToSaveBuffer(str, len); + + char *pch, *pEnd =_saveBuffer+len; // <= point to terminating zero + for (pch = strtok(_saveBuffer, "\n"); pch != NULL; pch = strtok(NULL, "\n")) + { + int _len = lstrlen(pch); + if (_len>1) { WrapOneLine(pch, _len, x2-x1); } + if (pch+_len < pEnd) { *(pch+_len) = '\n'; } // strtok splits by inserting '\0's => revert'em + } + + // "forEach(line...)" split multi-lines + for (pch = strtok(_saveBuffer, "\n"); pch != NULL; pch = strtok(NULL, "\n")) { + result = Text(x1, y1, pch, -1); // len is irrelevant for pointer into 'save' buffer + y1 += lineSpace; + } + + ReleaseSaveBuffer(); + return result; +} + +// =============================================================================================== +// +bool vkPad::Text (int x, int y, const char *str, int len) +{ +#ifdef SKPDBG + Log("Text(%s)", str); +#endif + // No "Setup" required, done on PrintSkp + + if (cfont==NULL) return false; + + vkTextPtr pText = static_cast(cfont)->pFont; + + switch(tah) { + default: + case LEFT: pText->SetTextHAlign(0); break; + case CENTER: pText->SetTextHAlign(1); break; + case RIGHT: pText->SetTextHAlign(2); break; + } + + switch(tav) { + default: + case TOP: pText->SetTextVAlign(0); break; + case BASELINE: pText->SetTextVAlign(1); break; + case BOTTOM: pText->SetTextVAlign(2); break; + } + + pText->SetRotation(static_cast(cfont)->rotation); + pText->SetScaling(1.0f); + pText->PrintSkp(this, float(x - 1), float(y - 1), str, len, (bkmode == OPAQUE)); + + return true; +} + + +// =============================================================================================== +// +void SwapRB(DWORD *c) +{ + DWORD r = ((*c) & 0x00FF0000) >> 16; + DWORD b = ((*c) & 0x000000FF) << 16; + *c = ((*c) & 0xFF00FF00) | b | r; +} + + +// =============================================================================================== +// +void vkPad::Pixel (int x, int y, DWORD col) +{ + FillRect(x, y, x + 1, y + 2, ColorComp(SkpColor(col))); +} + + +// =============================================================================================== +// +void vkPad::MoveTo (int x, int y) +{ + cx = x; + cy = y; +} + + +// =============================================================================================== +// +void vkPad::LineTo (int tx, int ty) +{ + if (!HasPen()) return; +#ifdef SKPDBG + Log("LineTo()"); +#endif + Line(cx, cy, tx, ty); + cx=tx; cy=ty; +} + + +// =============================================================================================== +// +void vkPad::Line (int x0, int y0, int x1, int y1) +{ + if (!HasPen()) return; +#ifdef SKPDBG + Log("Line()"); +#endif + IVECTOR2 pt[2]; + + pt[0].x = x0; pt[0].y = y0; + pt[1].x = x1; pt[1].y = y1; + + AppendLineVertexList(pt); + + cx = x1; cy = y1; +} + + +// =============================================================================================== +// +void vkPad::FillRect(int l, int t, int r, int b, const SkpColor &c) +{ + if (r == l) return; + if (b == t) return; + if (r < l) swap(r, l); + if (b < t) swap(t, b); + +#ifdef SKPDBG + Log("FillRect()"); +#endif + if (Topology(TRIANGLE)) { + AddRectIdx(vI); + SkpVtxIC(Vtx[vI++], l, t, c); + SkpVtxIC(Vtx[vI++], r, t, c); + SkpVtxIC(Vtx[vI++], r, b, c); + SkpVtxIC(Vtx[vI++], l, b, c); + } +} + + +// =============================================================================================== +// +void vkPad::Rectangle (int l, int t, int r, int b) +{ + if (r == l) return; + if (b == t) return; + if (r < l) swap(r, l); + if (b < t) swap(t, b); + +#ifdef SKPDBG + Log("Rectangle()"); +#endif + r--; + b--; + + // Fill interion ---------------------------------------------- + // + if (HasBrush()) FillRect(l, t, r, b, brushcolor); + + // Draw outline ------------------------------------------ + // + if (HasPen()) { + + IVECTOR2 pts[4]; + pts[0].x = pts[3].x = l; + pts[0].y = pts[1].y = t; + pts[1].x = pts[2].x = r; + pts[2].y = pts[3].y = b; + + AppendLineVertexList(pts, 4, true); + } +} + + +// =============================================================================================== +// +void vkPad::Ellipse (int x0, int y0, int x1, int y1) +{ + if (x1 == x0) return; + if (y1 == y0) return; + if (x1 < x0) swap(x0, x1); + if (y1 < y0) swap(y0, y1); + +#ifdef SKPDBG + Log("Ellipse()"); +#endif + + float w = float(x1 - x0); float h = float(y1 - y0); float fx0 = float(x0); float fy0 = float(y0); + DWORD z = max((x1-x0), (y1-y0)); + + w *= 0.5f; + h *= 0.5f; + //fl += w; + //ft += h; + + IVECTOR2 pts[65] = {}; + + WORD n = 8; + WORD q = 0; + + if (z > 16) q = 1, n = 16; + if (z > 32) q = 2, n = 32; + if (z > 64) q = 3, n = 64; + + for (WORD i = 0; i(pts, n, true); +} + + +// =============================================================================================== +// +void vkPad::Polygon (const IVECTOR2 *pt, int npt) +{ +#ifdef SKPDBG + Log("Polygon(%d)", npt); +#endif + + if (npt<3) return; + if (HasBrush() && npt > 64) return; + + + // Create filled polygon interior ----------------------------------------- + // + if (HasBrush()) { + if (Topology(TRIANGLE)) { + + int sIdx = vI; + + // File a vertex buffer. + for (int i = 0; i < npt; i++) SkpVtxIC(Vtx[vI++], pt[i].x, pt[i].y, brushcolor); + + WORD qIdx[256]; + int nIdx = CreatePolyIndexList(pt, npt, qIdx); + + // Add indices to index buffer + for (int i = 0; i < nIdx; i++) Idx[iI++] = qIdx[i] + sIdx; + } + } + + // Draw outline ------------------------------------------ + // + if (HasPen()) AppendLineVertexList(pt, npt, true); +} + + +// =============================================================================================== +// +void vkPad::Polyline (const IVECTOR2 *pt, int npt) +{ +#ifdef SKPDBG + Log("Polyline(%d)", npt); +#endif + if (npt < 2) return; + if (HasPen()) AppendLineVertexList(pt, npt, false); +} + + +// =============================================================================================== +// +void vkPad::DrawPoly (HPOLY hPoly, DWORD flags) +{ +#ifdef SKPDBG + Log("DrawPoly(%s, 0x%X)", _PTR(hPoly), flags); +#endif + + if (hPoly) { + + vkPolyBase *pBase = static_cast(hPoly); + int PolyType = pBase->type; + + if ((PolyType == 0) && !HasPen()) return; + + // Flush pending graphics before a use of different interface + Flush(); + + if (Topology(TRIANGLE)) { + + // Flush the poly object + Flush(hPoly); + } + } +} + + +// =============================================================================================== +// +void vkPad::Lines(const FVECTOR2 *pt, int nlines) +{ +#ifdef SKPDBG + Log("Lines(%d)", nlines); +#endif + if (!HasPen()) return; + for (int i = 0; i < nlines; i++) { + AppendLineVertexList(pt); + pt += 2; + } +} + + + + +// ----------------------------------------------------------------------------------------------- +// Save buffer helpers (null-terminated string for Text & TextBox) +// ----------------------------------------------------------------------------------------------- + +// =============================================================================================== +// Copy string to internal 'save' buffer, so it can be changed (adding terminating zeroes, etc.) +void vkPad::ToSaveBuffer (const char *str, int len) +{ + if (_saveBufferSize < len) + { // re-allloc bigger space + if (_saveBuffer) { delete[] _saveBuffer; } + _saveBuffer = new char[len + 1]; + _saveBufferSize = len; + } + strncpy_s(_saveBuffer, len + 1, str, len); + _isSaveBuffer = true; +} + +// =============================================================================================== +// +void vkPad::ReleaseSaveBuffer () { + _isSaveBuffer = false; +} + + +// ----------------------------------------------------------------------------------------------- +// Subroutines Section +// ----------------------------------------------------------------------------------------------- + +// =============================================================================================== +// +short mod(short a, short b) +{ + if (a<0) return b-1; + if (a>=b) return 0; + return a; +} + + +// =============================================================================================== +// +template +int CheckTriangle(short x, const Type *pt, const WORD *Idx, float hd, short npt, bool bSharp) +{ + WORD A = Idx[x]; + WORD B = Idx[mod(x-1,npt)]; + WORD C = Idx[mod(x+1,npt)]; + + float bx = float(pt[B].x - pt[A].x); + float by = float(pt[B].y - pt[A].y); + float ax = float(pt[C].x - pt[A].x); + float ay = float(pt[C].y - pt[A].y); + + if ((bx*ay-by*ax)*hd > 0.0f) return 0; // Check handiness + + float aa = ax*ax + ay*ay; // dot(a,a) + float ab = ax*bx + ay*by; // dot(a,b) + float bb = bx*bx + by*by; // dot(b,b) + + float qw = fabs(ab) / sqrt(aa*bb); // abs(cos(a,b)) + if (bSharp && qw>0.9f) return 0; // Bad Ear + + float id = 1.0f / (aa * bb - ab * ab); + + for (int i=0;i-0.0001) && (v>-0.0001) && ((u+v)<1.0001f)) return 0; + } + + return 1; // It's an ear +} + + +// =============================================================================================== +// +template +int CreatePolyIndexList(const Type *pt, short npt, WORD *Out) +{ + if (npt > 255) return 0; + if (npt==3) { Out[0]=0; Out[1]=1; Out[2]=2; return 3; } + + short idx = 0; // Number of indices written in the output + short x = npt-1; // First ear to test is the last one in the list + bool bSharp = false;// Avoid sharp ears + + // Build initial index list + WORD In[256]; + for (int i=0;i0) sum=1.0; else sum=-1.0; + + while (npt>3) { + + switch (CheckTriangle(x, pt, In, sum, npt, bSharp)) { + + case 0: + { + x--; + if (x<0) { // Restart + if (!bSharp && nr>10) return idx; + bSharp = false; + x = npt - 1; + nr++; + } + break; + } + + case 1: + { + Out[idx] = In[mod(x-1,npt)]; idx++; + Out[idx] = In[mod(x,npt)]; idx++; + Out[idx] = In[mod(x+1,npt)]; idx++; + npt--; + for (int i=x;i +void vkPad::AppendLineVertexList(const Type *pt, int _npt, bool bLoop) +{ + if (_npt < 2) return; + + + // ---------------------------------------------------------------------- + // Draw a thin hairline + // ---------------------------------------------------------------------- + + if (Topology(LINE)) { + + WORD npt = WORD(_npt); + WORD wL = vI; + WORD li = WORD(npt - 1); + WORD aV; + float length = 0.0f; + + // Create line segments ------------------------------------------------- + // + for (WORD i = 0; i 0) { + Idx[iI++] = cV; Idx[iI++] = aV; + Idx[iI++] = dV; Idx[iI++] = dV; + Idx[iI++] = aV; Idx[iI++] = bV; + } + + cV = aV; + dV = bV; + + pp = _DXV2(pt[i]); + + if (IsDashed()) length += ::length(np - pp); + } + + // Last segment --------------------------------------------------------- + // + if (bLoop) { + Idx[iI++] = wL; Idx[iI++] = aV; + Idx[iI++] = wL + 1; Idx[iI++] = wL + 1; + Idx[iI++] = aV; Idx[iI++] = bV; + } + } +} + + +// =============================================================================================== +// +template +void vkPad::AppendLineVertexList(const Type *pt) +{ + + // ---------------------------------------------------------------------- + // Draw a thin hairline + // ---------------------------------------------------------------------- + + if (Topology(LINE)) { + + Vtx[vI].x = float(pt[0].x); + Vtx[vI].y = float(pt[0].y); + Vtx[vI].fnc = SKPSW_CENTER | SKPSW_FRAGMENT; + Vtx[vI].l = 0.0f; + Vtx[vI].clr = pencolor.dclr; + Idx[iI++] = vI; + vI++; + + Vtx[vI].x = float(pt[1].x); + Vtx[vI].y = float(pt[1].y); + Vtx[vI].px = float(pt[0].x); + Vtx[vI].py = float(pt[0].y); + Vtx[vI].fnc = SKPSW_CENTER | SKPSW_LENGTH | SKPSW_FRAGMENT; + Vtx[vI].clr = pencolor.dclr; + Idx[iI++] = vI; + vI++; + + return; + } + + + // ---------------------------------------------------------------------- + // Wide line mode + // ---------------------------------------------------------------------- + + if (Topology(TRIANGLE)) { + + FVECTOR2 pp = _DXV2(pt[0]) * 2.0 - _DXV2(pt[1]); + FVECTOR2 np; + + WORD vF = vI; + + for (int i = 0; i < 2; i++) { + + if (i == 0) np = _DXV2(pt[1]); + else np = _DXV2(pt[1]) * 2.0 - _DXV2(pt[0]); + + WORD vII = vI + 1; + + // -------------------------------------- + Vtx[vI].x = Vtx[vII].x = float(pt[i].x); + Vtx[vI].y = Vtx[vII].y = float(pt[i].y); + Vtx[vI].nx = Vtx[vII].nx = np.x; + Vtx[vI].ny = Vtx[vII].ny = np.y; + Vtx[vI].px = Vtx[vII].px = pp.x; + Vtx[vI].py = Vtx[vII].py = pp.y; + Vtx[vI].l = Vtx[vII].l = 0.0f; + Vtx[vI].clr = Vtx[vII].clr = pencolor.dclr; + // -------------------------------------- + Vtx[vI].fnc = SKPSW_WIDEPEN_L | SKPSW_FRAGMENT; + if (i) Vtx[vI].fnc |= SKPSW_LENGTH; + vI++; + Vtx[vI].fnc = SKPSW_WIDEPEN_R | SKPSW_FRAGMENT; + if (i) Vtx[vI].fnc |= SKPSW_LENGTH; + vI++; + // -------------------------------------- + + pp = _DXV2(pt[i]); + } + + Idx[iI++] = vF + 0; + Idx[iI++] = vF + 1; + Idx[iI++] = vF + 2; + Idx[iI++] = vF + 1; + Idx[iI++] = vF + 3; + Idx[iI++] = vF + 2; + } +} + + +// =============================================================================================== +// +D3DXHANDLE vkPad::eSketch = 0; +D3DXHANDLE vkPad::eDrawMesh = 0; +D3DXHANDLE vkPad::eVP = 0; +D3DXHANDLE vkPad::eW = 0; +D3DXHANDLE vkPad::eKey = 0; +D3DXHANDLE vkPad::ePen = 0; +D3DXHANDLE vkPad::eWVP = 0; +D3DXHANDLE vkPad::eFov = 0; +D3DXHANDLE vkPad::eRandom = 0; +D3DXHANDLE vkPad::eTarget = 0; +D3DXHANDLE vkPad::eTexEn = 0; +D3DXHANDLE vkPad::eFntEn = 0; +D3DXHANDLE vkPad::eKeyEn = 0; +D3DXHANDLE vkPad::eWidth = 0; +D3DXHANDLE vkPad::eTex0 = 0; +D3DXHANDLE vkPad::eFnt0 = 0; +D3DXHANDLE vkPad::eDashEn = 0; +D3DXHANDLE vkPad::eSize = 0; +D3DXHANDLE vkPad::eWide = 0; +D3DXHANDLE vkPad::eMtrl = 0; +D3DXHANDLE vkPad::eShade = 0; +D3DXHANDLE vkPad::ePos = 0; +D3DXHANDLE vkPad::ePos2 = 0; +D3DXHANDLE vkPad::eCov = 0; +D3DXHANDLE vkPad::eCovEn = 0; +D3DXHANDLE vkPad::eClearEn = 0; +D3DXHANDLE vkPad::eEffectsEn = 0; + +D3DXHANDLE vkPad::eNoiseTex = 0; +D3DXHANDLE vkPad::eNoiseColor = 0; +D3DXHANDLE vkPad::eColorMatrix = 0; +D3DXHANDLE vkPad::eGamma = 0; + +ID3DXEffect* vkPad::FX = 0; +vkClient * vkPad::gc = 0; +WORD * vkPad::Idx = 0; +SkpVtx * vkPad::Vtx = 0; +FVECTOR2* vkPad::pSinCos[]; +LPDIRECT3DDEVICE9 vkPadFont::pDev = 0; +LPDIRECT3DDEVICE9 vkPad::pDev = 0; +LPDIRECT3DTEXTURE9 vkPad::pNoise = 0; + +FILE* vkPad::log = 0; +CRITICAL_SECTION vkPad::LogCrit; + + +// ====================================================================== +// class GDIFont +// ====================================================================== +using namespace oapi; + +vkPadFont::vkPadFont(int height, bool prop, const char *face, FontStyle style, int orientation, DWORD flags) : Font(height, prop, face, style, orientation) +{ + const char *def_fixedface = "Courier New"; + const char *def_sansface = "Arial"; + const char *def_serifface = "Times New Roman"; + + if (face[0]!='*') { + if (!_stricmp (face, "fixed")) face = def_fixedface; + else if (!_stricmp (face, "sans")) face = def_sansface; + else if (!_stricmp (face, "serif")) face = def_serifface; + else if (_stricmp (face, def_fixedface) && _stricmp (face, def_sansface) && _stricmp (face, def_serifface)) face = (prop ? def_sansface : def_fixedface); + } + else face++; + + hFont = NULL; + + if (orientation!=0) rotation = float(orientation) * 0.1f; + else rotation = 0.0f; + + // Browse cache --------------------------------------------------- + // + + for (size_t i = 0; i < fcache.size(); ++i) { + if (fcache[i]->height!=height) continue; + if (fcache[i]->style!=style) continue; + if (fcache[i]->prop!=prop) continue; + if (_stricmp(fcache[i]->face,face)!=0) continue; + pFont = fcache[i]->pFont; + break; + } + + int weight = (style & FONT_BOLD) ? FW_BOLD : FW_NORMAL; + DWORD italic = (style & FONT_ITALIC) ? TRUE : FALSE; + DWORD underline = (style & FONT_UNDERLINE) ? TRUE : FALSE; + DWORD strikeout = (style & FONT_STRIKEOUT) ? TRUE : FALSE; + + Quality = NONANTIALIASED_QUALITY; + + if ((flags & 0xF) == 0) { + if (Config->SketchpadFont == 1) Quality = PROOF_QUALITY; + if (Config->SketchpadFont == 2) Quality = CLEARTYPE_QUALITY; + } + else { + if (flags&SKP_FONT_ANTIALIAS) Quality = PROOF_QUALITY; + if (flags&SKP_FONT_CLEARTYPE) Quality = CLEARTYPE_QUALITY; + } + + // Create DirectX accelerated font for a use with vkPad ------------------ + // + if (pFont==NULL) { + + HFONT hNew = CreateFont(height, 0, 0, 0, weight, italic, underline, strikeout, 0, 0, 2, Quality, 49, face); + + pFont = std::make_shared(pDev); + pFont->Init(hNew); + + DeleteObject(hNew); + + pFont->SetRotation(rotation); + + // Fill the cache -------------------------------- + FontCache *p = new FontCache(); + p->pFont = pFont; + p->height = height; + p->style = style; + p->prop = prop; + strcpy_s(p->face, 64, face); + fcache.push_back(p); + } + + // Create Rotated windows GDI Font for a use with GDIPad --------------------------- + // + hFont = CreateFontA(height, 0, orientation, orientation, weight, italic, underline, strikeout, 0, 0, 2, Quality, 49, face); + + if (hFont==NULL) { + face = (prop ? def_sansface : def_fixedface); + hFont = CreateFont(height, 0, orientation, orientation, weight, italic, underline, strikeout, 0, 0, 2, Quality, 49, face); + } +} + + + +vkPadFont::vkPadFont(int height, char *face, int width, int weight, FontStyle style, float spacing) +: Font(height, false, face, style, 0) +{ + + hFont = NULL; + rotation = 0.0f; + + // Browse cache --------------------------------------------------- + // + for (size_t i = 0; i < qcache.size(); ++i) { + if (qcache[i]->height != height) continue; + if (qcache[i]->style != style) continue; + if (qcache[i]->width != width) continue; + if (qcache[i]->weight != weight) continue; + if (qcache[i]->spacing != spacing) continue; + if (_stricmp(qcache[i]->face, face) != 0) continue; + pFont = qcache[i]->pFont; + break; + } + + DWORD italic = (style & FONT_ITALIC) ? TRUE : FALSE; + DWORD underline = (style & FONT_UNDERLINE) ? TRUE : FALSE; + DWORD strikeout = (style & FONT_STRIKEOUT) ? TRUE : FALSE; + + Quality = NONANTIALIASED_QUALITY; + if (Config->SketchpadFont == 1) Quality = ANTIALIASED_QUALITY; + if (Config->SketchpadFont == 2) Quality = PROOF_QUALITY; + + if (style & FONT_CRISP) Quality = NONANTIALIASED_QUALITY; + if (style & FONT_ANTIALIAS) Quality = ANTIALIASED_QUALITY; + + + // Create DirectX accelerated font for a use with vkPad ------------------ + // + if (pFont == NULL) { + + hFont = CreateFont(height, width, 0, 0, weight, italic, underline, strikeout, 0, 0, 2, Quality, 49, face); + + pFont = std::make_shared(pDev); + pFont->Init(hFont); + pFont->SetRotation(0.0f); + pFont->SetTextSpace(spacing); + + // Fill the cache -------------------------------- + QFontCache *p = new QFontCache(); + p->pFont = pFont; + p->height = height; + p->width = width; + p->weight = weight; + p->style = style; + p->spacing = spacing; + strcpy_s(p->face, 64, face); + qcache.push_back(p); + } + else { + // Create windows GDI Font for a use with GDIPad --------------------------- + // + hFont = CreateFont(height, width, 0, 0, weight, italic, underline, strikeout, 0, 0, 2, Quality, 49, face); + } +} + +// ----------------------------------------------------------------------------------------------- +// +vkPadFont::~vkPadFont () +{ + if (pFont) pFont->SetRotation(0.0f), pFont.reset(); + if (hFont) DeleteObject(hFont); +} + + +// ----------------------------------------------------------------------------------------------- +// +HFONT vkPadFont::GetGDIFont () const +{ + return hFont; +} + + +// ----------------------------------------------------------------------------------------------- +// +int vkPadFont::GetTextLength(const char *pText, int len) const +{ + return int(pFont->Length2(pText, len)); +} + + +// ----------------------------------------------------------------------------------------------- +// +int vkPadFont::GetIndexByPosition(const char *pText, int pos, int len) const +{ + return int(pFont->GetIndex(pText, float(pos), len)); +} + +// ----------------------------------------------------------------------------------------------- +// +void vkPadFont::vkTechInit(LPDIRECT3DDEVICE9 pDevice) +{ + pDev = pDevice; +} + + + +// ====================================================================== +// class GDIPen +// ====================================================================== + +vkPadPen::vkPadPen (int s, int w, DWORD col): oapi::Pen (style, width, col) +{ + switch (s) { + case 0: style = PS_NULL; break; + case 2: style = PS_DOT; break; + default: style = PS_SOLID; break; + } + width = w; + if (width<1) width = 1; + hPen = CreatePen(style, width, COLORREF(col&0xFFFFFF)); + clr = SkpColor(col); +} + +// ----------------------------------------------------------------------------------------------- +// +vkPadPen::~vkPadPen () +{ + DeleteObject(hPen); +} + +// ----------------------------------------------------------------------------------------------- +// +void vkPadPen::vkTechInit(LPDIRECT3DDEVICE9 pDevice) +{ + //pDev = pDevice; +} + + + +// ====================================================================== +// class GDIBrush +// ====================================================================== + +vkPadBrush::vkPadBrush (DWORD col): oapi::Brush (col) +{ + hBrush = CreateSolidBrush(COLORREF(col&0xFFFFFF)); + clr = SkpColor(col); +} + +// ----------------------------------------------------------------------------------------------- +// +vkPadBrush::~vkPadBrush () +{ + DeleteObject(hBrush); +} + +// ----------------------------------------------------------------------------------------------- +// +void vkPadBrush::vkTechInit(LPDIRECT3DDEVICE9 pDevice) +{ + //pDev = pDevice; +} diff --git a/OVP/VulkanClient/Pad.h b/OVP/VulkanClient/Pad.h new file mode 100644 index 000000000..fc12243b9 --- /dev/null +++ b/OVP/VulkanClient/Pad.h @@ -0,0 +1,890 @@ +// =================================================== +// Copyright (C) 2012-2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + +#ifndef __vkPAD_H +#define __vkPAD_H + +#include "OrbiterAPI.h" +#include "Client.h" +#include +#include "MathAPI.h" +#include +#include +#include "DrawAPI.h" + +using namespace oapi; + +extern oapi::Font *deffont; +extern oapi::Pen *defpen; + +#define SKPCHG_ALL 0xFFFF +#define SKPCHG_PEN 0x0001 +#define SKPCHG_FONT 0x0004 +#define SKPCHG_TEXTURE 0x0008 +#define SKPCHG_CLIPCONE 0x0010 +#define SKPCHG_CLIPRECT 0x0020 +#define SKPCHG_TRANSFORM 0x0040 +#define SKPCHG_EFFECTS 0x0080 +#define SKPCHG_DEPTH 0x0100 +#define SKPCHG_TOPOLOGY 0x0200 +#define SKPCHG_PATTERN 0x0400 + + +#define SKETCHPAD_NONE 0x0000 +#define SKETCHPAD_GDI 0x0001 +#define SKETCHPAD_DIRECTX 0x0002 + +// =============================================================================================== +// Feature Switches //AARRGGBB +// +// Color source: +#define SKPSW_FRAGMENT 0x00000000 // Index 2 +#define SKPSW_PENCOLOR 0x00000080 // Index 2 +#define SKPSW_TEXTURE 0x000000FF // Index 2 + +// Specials: +#define SKPSW_FONT 0x0000FF00 // Index 1 +#define SKPSW_COLORKEY 0x00008000 // Index 1 +#define SKPSW_LENGTH 0x00FF0000 // Index 0 + +// Vertex alignment: +#define SKPSW_CENTER 0x80000000 // Index 3 +#define SKPSW_WIDEPEN_L 0x00000000 // Index 3 +#define SKPSW_WIDEPEN_R 0xFF000000 // Index 3 + + + +// =============================================================================================== +// Special properties +// +#define SKP3E_GAMMA 0x00000001 +#define SKP3E_NOISE 0x00000002 +#define SKP3E_CMATR 0x00000004 + + +// =============================================================================================== +// +#define nLow 17 +#define nHigh 65 +#define nQueueMax 2048 +#define nIndexMax (nQueueMax * 3) + +typedef std::shared_ptr vkTextPtr; + +struct SkpColor { + + SkpColor() { + dclr = 0; + fclr = FVECTOR4(); + } + + explicit SkpColor (DWORD c) { + dclr = c; + fclr = FVECTOR4(c); + D3DXCOLORSWAP(&fclr); + } + + explicit SkpColor(const FVECTOR4 &c) { + dclr = c.dword_argb(); + fclr.r = c.r; fclr.g = c.g; fclr.b = c.b; fclr.a = c.a; + D3DXCOLORSWAP(&fclr); + } + + DWORD dclr; + FVECTOR4 fclr; +}; + + + +struct SkpVtx { + + SkpVtx() { + memset(this, 0, sizeof(SkpVtx)); + }; + + float x, y, l; // vertex x, y, length + float nx, ny; // next point + float px, py; // previous point + DWORD clr, fnc; +}; + +// GradientFillRect [only] +inline void SkpVtxGF(SkpVtx &v, int _x, int _y, DWORD c) +{ + v.x = float(_x) - 0.5f; + v.y = float(_y) - 0.5f; + v.clr = c; + v.fnc = SKPSW_CENTER | SKPSW_FRAGMENT; + v.l = 0.0f; +} + +inline void SkpVtxFC(SkpVtx& v, float _x, float _y, DWORD c) +{ + v.x = _x - 0.5f; + v.y = _y - 0.5f; + v.clr = c; + v.fnc = SKPSW_CENTER | SKPSW_FRAGMENT; + v.l = 0.0f; +} + +// Fill Rect, Ellipse, Polygon [only] +inline void SkpVtxIC(SkpVtx &v, int _x, int _y, const SkpColor &c) +{ + v.x = float(_x) - 0.5f; + v.y = float(_y) - 0.5f; + v.clr = c.dclr; + v.fnc = SKPSW_CENTER | SKPSW_FRAGMENT; + v.l = 0.0f; +} + +// Copy, Stretch, Colorkey Rect [only] +inline void SkpVtxII(SkpVtx &v, int _tx, int _ty, int _sx, int _sy) +{ + v.x = float(_tx) - 0.5f; + v.y = float(_ty) - 0.5f; + v.nx = float(_sx); + v.ny = float(_sy); + v.l = 0.0f; +}; + +// Pattern Fill [only] +inline void SkpVtxPF(SkpVtx &v, int _x, int _y, DWORD c) +{ + v.x = float(_x) - 0.5f; + v.y = float(_y) - 0.5f; + v.l = 0.0f; + v.clr = c; + v.fnc = SKPSW_FRAGMENT | SKPSW_CENTER; +}; + + +// Rotate Rect [only] +inline void SkpVtxFI(SkpVtx &v, float _x, float _y, int _tx, int _ty) +{ + v.x = _x - 0.5f; + v.y = _y - 0.5f; + v.nx = float(_tx); + v.ny = float(_ty); + v.l = 0.0f; +}; + +// D3DTextManager Print Font [only] +inline void SkpVtxFF(SkpVtx &v, float _x, float _y, float _tx, float _ty) +{ + v.x = _x - 0.5f; + v.y = _y - 0.5f; + v.nx = _tx; + v.ny = _ty; + v.l = 0.0f; +}; + +template int CheckTriangle(short x, const Type *pt, const WORD *Idx, float hd, short npt, bool bSharp); +template int CreatePolyIndexList(const Type *pt, short npt, WORD *Out); + +/** + * \brief The vkPad class defines the context for 2-D drawing using + * DirectX calls. + */ +class vkPad : public Sketchpad +{ + friend class vkText; + + mutable struct { + DWORD style; + DWORD color; + float width; + bool bEnabled; + } QPen; + + mutable struct { + bool bEnabled; + } QBrush; + + struct { + FVECTOR3 uDir; + float ca, dst; + bool bEnable; + } ClipData[2]; + + enum Topo { NONE, TRIANGLE, LINE }; + +public: + /** + * \brief Constructs a drawing object for a given surface. + * \param s surface handle + */ + explicit vkPad(SURFHANDLE s, const char *name = NULL); + explicit vkPad(const char *name = NULL); + + /** + * \brief Destructor. Destroys a drawing object. + */ + ~vkPad(); + + /** + * \brief Set up global parameters shared by all instances + * \param gclient client instance pointer + * \param pDev direct 3D device instance pointer + * \param folder shader folder (based on Orbiter's "Modules" path). + * Usually this should be set to "vkClient") + */ + static void vkTechInit(vkClient *gc, LPDIRECT3DDEVICE9 pDev); + static void SinCos(int n, int i); + /** + * \brief Release global parameters + */ + static void GlobalExit(); + + /** + * \brief Selects a new font to use. + * \param font pointer to font resource + * \return Previously selected font. + * \default None, returns NULL. + * \sa oapi::Font, oapi::GraphicsClient::clbkCreateFont + */ + oapi::Font *SetFont (oapi::Font *font); + + /** + * \brief Selects a new pen to use. + * \param pen pointer to pen resource, or NULL to disable outlines + * \return Previously selected pen. + * \default None, returns NULL. + * \sa oapi::Pen, oapi::GraphicsClient::clbkCreatePen + */ + oapi::Pen *SetPen (oapi::Pen *pen); + + /** + * \brief Selects a new brush to use. + * \param brush pointer to brush resource, or NULL to disable fill mode + * \return Previously selected brush. + * \default None, returns NULL. + * \sa oapi::Brush, oapi::GraphicsClient::clbkCreateBrush + */ + oapi::Brush *SetBrush (oapi::Brush *brush); + + /** + * \brief Set horizontal and vertical text alignment. + * \param tah horizontal alignment + * \param tav vertical alignment + * \default None. + */ + void SetTextAlign(TAlign_horizontal tah=LEFT, TAlign_vertical tav=TOP); + + /** + * \brief Set the foreground colour for text output. + * \param col colour description (format: 0xBBGGRR) + * \return Previous colour setting. + * \default None, returns 0. + */ + DWORD SetTextColor (DWORD col); + + /** + * \brief Set the background colour for text output. + * \param col background colour description (format: 0xBBGGRR) + * \return Previous colour setting + * \default None, returns 0. + * \note The background colour is only used if the background mode + * is set to BK_OPAQUE. + * \sa SetBackgroundMode + */ + DWORD SetBackgroundColor (DWORD col); + + /** + * \brief Set the background mode for text output. + * \param mode background mode (see \ref BkgMode) + * \default None. + * \note In opaque background mode, the text background is drawn + * in the current background colour (see SetBackgroundColor). + * \note The default background mode (before the first call of + * SetBackgroundMode) should be transparent. + * \sa SetBackgroundColor, SetTextColor + */ + void SetBackgroundMode (BkgMode mode); + + /** + * \brief Return height and (average) width of a character in the currently + * selected font. + * \return Height of character cell [pixel] in the lower 16 bit of the return value, + * and (average) width of character cell [pixel] in the upper 16 bit. + * \default None, returns 0. + * \note The height value should describe the height of the character cell (i.e. + * the smallest box circumscribing all characters in the font), but without any + * "internal leading", i.e. the gap between characters in two consecutive lines. + * \note For proportional fonts, the width value should be an approximate average + * character width. + */ + DWORD GetCharSize (); + + /** + * \brief Return the width of a text string in the currently selected font. + * \param str text string + * \param len string length, or 0 for auto (0-terminated string) + * \return width of the string, drawn in the currently selected font [pixel] + * \default None, returns 0. + * \sa SetFont + */ + DWORD GetTextWidth (const char *str, int len = 0); + + /** + * \brief Move the drawing reference to a new point. + * \param x x-coordinate of new reference point [pixel] + * \param y y-coordinate of new reference point [pixel] + * \note Some methods use the drawing reference point for + * drawing operations, e.g. \ref LineTo. + * \default None. + * \sa LineTo + */ + void MoveTo (int x, int y); + + /** + * \brief Draw a line to a specified point. + * \param x x-coordinate of line end point [pixel] + * \param y y-coordinate of line end point [pixel] + * \default None. + * \note The line starts at the current drawing reference + * point. + * \sa MoveTo + */ + void LineTo (int x, int y); + + /** + * \brief Set the position in the surface bitmap which is mapped to the + * origin of the coordinate system for all drawing functions. + * \param x horizontal position of the origin [pixel] + * \param y vertical position of the origin [pixel] + * \default None. + * \note By default, the reference point for drawing function coordinates is + * the top left corner of the bitmap, with positive x-axis to the right, + * and positive y-axis down. + * \note SetOrigin can be used to shift the logical reference point to a + * different position in the surface bitmap (but not to change the + * orientation of the axes). + * \sa GetOrigin + */ + void SetOrigin (int x, int y); + + /** + * \brief Returns the position in the surface bitmap which is mapped to + * the origin of the coordinate system for all drawing functions. + * \param [out] x pointer to integer receiving horizontal position of the origin [pixel] + * \param [out] y pointer to integer receiving vertical position of the origin [pixel] + * \default Returns (0,0) + * \sa SetOrigin + */ + void GetOrigin (int *x, int *y) const; + + /** + * \brief Draw a text string. + * \param x reference x position [pixel] + * \param y reference y position [pixel] + * \param str text string + * \param len string length for output + * \return \e true on success, \e false on failure. + * \default None, returns false. + */ + bool Text (int x, int y, const char *str, int len); + + /** + * \brief Draw a text string into a rectangle. + * \param x1 left edge [pixel] + * \param y1 top edge [pixel] + * \param x2 right edge [pixel] + * \param y2 bottom edge [pixel] + * \param str text string + * \param len string length for output + * \return \e true on success, \e false on failure. + */ + bool TextBox (int x1, int y1, int x2, int y2, const char *str, int len); + + /** + * \brief Draw a single pixel in a specified colour. + * \param x x-coordinate of point [pixel] + * \param y y-coordinate of point [pixel] + * \param col pixel colour (format: 0xBBGGRR) + */ + void Pixel (int x, int y, DWORD col); + + /** + * \brief Draw a line between two points. + * \param x0 x-coordinate of first point [pixel] + * \param y0 y-coordinate of first point [pixel] + * \param x1 x-coordinate of second point [pixel] + * \param y1 y-coordinate of second point [pixel] + * \default None. + * \note The line is drawn with the currently selected pen. + * \sa SetPen + */ + void Line (int x0, int y0, int x1, int y1); + + /** + * \brief Draw a rectangle (filled or outline). + * \param x0 left edge of rectangle [pixel] + * \param y0 top edge of rectangle [pixel] + * \param x1 right edge of rectangle [pixel] + * \param y1 bottom edge of rectangle [pixel] + * \default Draws the rectangle from 4 line segments and + * fills the rectangle with the currently selected brush resource. + * \sa MoveTo, LineTo, Ellipse, Polygon + */ + void Rectangle (int x0, int y0, int x1, int y1); + + /** + * \brief Draw an ellipse from its bounding box. + * \param x0 left edge of bounding box [pixel] + * \param y0 top edge of bounding box [pixel] + * \param x1 right edge of bounding box [pixel] + * \param y1 bottom edge of bounding box [pixel] + * \default None. + * \note The ellipse is filled with the currently selected + * brush resource. + * \sa Rectangle, Polygon + */ + void Ellipse (int x0, int y0, int x1, int y1); + + /** + * \brief Draw a closed polygon given by vertex points. + * \param pt list of vertex points + * \param npt number of points in the list + * \default None. + * \note The polygon should be closed, i.e. the last point + * joined with the first one. + * \note The outline of the polygon is drawn with the + * current pen, and filled with the current brush. + * \note Filled polygon has a maximum of 64 points. + * \sa Polyline, PolyPolygon, Rectangle, Ellipse + */ + void Polygon (const oapi::IVECTOR2 *pt, int npt); + + /** + * \brief Draw a line of piecewise straight segments. + * \param pt list of vertex points + * \param npt number of points in the list + * \default None + * \note The line is drawn with the currently selected pen. + * \note Polylines are open figures: the end points are + * not connected, and no fill operation is performed. + * \sa Polygon, PolyPolyline, Rectangle, Ellipse + */ + void Polyline (const oapi::IVECTOR2 *pt, int npt); + + /** + * \brief Obsolete. Will return NULL + * \return null + */ + HDC GetDC(); + + bool TextW(int x, int y, const LPWSTR str, int len = -1); + + // =============================================================================== + // Sketchpad2 Additions + // =============================================================================== + + void GetRenderSurfaceSize(LPSIZE size); + void QuickPen(DWORD color, float width = 1.0f, DWORD style = 0); + void QuickBrush(DWORD color); + void SetGlobalLineScale(float width = 1.0f, float pattern = 1.0f); + void SetWorldTransform(const FMATRIX4 *pWT = NULL); + void SetWorldTransform2D(float scale=1.0f, float rot=0.0f, const IVECTOR2 *c=NULL, const IVECTOR2 *t=NULL); + int DrawMeshGroup(const MESHHANDLE hMesh, DWORD grp, Sketchpad::MeshFlags flags, const SURFHANDLE hTex = NULL); + void CopyRect(const SURFHANDLE hSrc, const LPRECT src, int tx, int ty); + void StretchRect(const SURFHANDLE hSrc, const LPRECT src, const LPRECT tgt); + void RotateRect(const SURFHANDLE hSrc, const LPRECT src, int cx, int cy, float angle, float sw = 1.0f, float sh = 1.0f); + void ColorKey(const SURFHANDLE hSrc, const LPRECT src, int tx, int ty); + void TextEx(float x, float y, const char *str, float scale = 100.0f, float angle = 0.0f); + void ClipRect(const LPRECT clip = NULL); + void Clipper(int idx, const VECTOR3 *pPos = NULL, double cos_angle = 0.0, double dist = 0.0); + void DrawPoly(const HPOLY hPoly, DWORD flags = 0); + void Lines(const FVECTOR2 *pt1, int nlines); + void DepthEnable(bool bEnable); + + void SetViewMode(SkpView mode = ORTHO); + //----------------------------------------- + const FMATRIX4 *ViewMatrix() const; + const FMATRIX4 *ProjectionMatrix() const; + const FMATRIX4 *GetViewProjectionMatrix() const; + void SetViewMatrix(const FMATRIX4 *pV = NULL); + void SetProjectionMatrix(const FMATRIX4 *pP = NULL); + + + + + // =============================================================================== + // Sketchpad3 Additions + // =============================================================================== + + const FMATRIX4 *GetColorMatrix(); + void SetColorMatrix(const FMATRIX4 *pMatrix = NULL); + void SetBrightness(const FVECTOR4 *pBrightness = NULL); + FVECTOR4 GetRenderParam(RenderParam param); + void SetRenderParam(RenderParam param, const FVECTOR4 *data = NULL); + void SetBlendState(BlendState dwState); + FMATRIX4 GetWorldTransform() const; + void PushWorldTransform(); + void PopWorldTransform(); + void SetWorldScaleTransform2D(const FVECTOR2 *scl = NULL, const IVECTOR2 *trl = NULL); + void GradientFillRect(const LPRECT rect, DWORD c1, DWORD c2, bool bVertical = false); + void ColorFill(DWORD color, const LPRECT tgt); + void StretchRegion(const skpRegion *rgn, const SURFHANDLE hSrc, const LPRECT out); + void CopyTetragon(SURFHANDLE pSrc, const LPRECT _s, const FVECTOR2 pt[4]); + void ColorCompatibility(bool bEnable); + void FillTetragon(DWORD c, const FVECTOR2 pt[4]); + void Clear(DWORD color = 0, bool bColor = true, bool bDepth = true); + void SetClipDistance(float _near, float _far); + void ColorKeyStretch(const SURFHANDLE hSrc, const LPRECT _s = NULL, const LPRECT t = NULL); + void SetWorldBillboard(const FVECTOR3& wpos, float utp = 1.0f, bool bFixed = true, const FVECTOR3* index = NULL); + + + // =============================================================================== + // vkClient Privates + // =============================================================================== + + /** + * BeginDrawing(), EndDrawing(). + * - All the other vkPad functions can be only called/used between the BeginDrawing(), EndDrawing() pairs. + * - All rendering that is NOT a part of vkPad is prohibited between the Begin/End pairs. + * - Calling Begin/End doesn't alter the vkPad state such as Pens, Brushes, etc... + * - EndDrawing() will flush all pending instructions from the draw queue. + */ + void EndDrawing(); + void BeginDrawing(LPDIRECT3DSURFACE9 pRenderTgt, LPDIRECT3DSURFACE9 pDepthStensil = NULL); + void BeginDrawing(); + + inline void FlushAll() { Flush(NULL); } + + void SetViewProj(const FMATRIX4* pV, const FMATRIX4* pP); + + FMATRIX4* WorldMatrix(); + DWORD GetLineHeight(); ///< Return height of a character in the currently selected font with "internal leading" + const char *GetName() const { return name; } + LPDIRECT3DSURFACE9 GetRenderTarget() const { return pTgt; } + bool IsStillDrawing() const { return bBeginDraw; } + void LoadDefaults(); + + void CopyRectNative(LPDIRECT3DTEXTURE9 pSrc, const LPRECT s, int tx, int ty); + void StretchRectNative(LPDIRECT3DTEXTURE9 pSrc, const RECT *s, const RECT *t); + + +private: + + bool Topology(Topo tRequest); + void SetEnable(DWORD config); + void ClearEnable(DWORD config); + + bool HasPen() const; + bool HasBrush() const; + bool IsDashed() const; + bool IsAlphaTarget() const; + + float GetPenWidth() const; + + void Reset(); + bool Flush(HPOLY hPoly = NULL); + void AddRectIdx(WORD aV); + void FillRect(int l, int t, int r, int b, const SkpColor &c); + void TexChange(SURFHANDLE hNew); + bool TexChangeNative(LPDIRECT3DTEXTURE9 hNew); + RECT GetFullRectNative(LPDIRECT3DTEXTURE9 hSrc); + void SetFontTextureNative(LPDIRECT3DTEXTURE9 hNew); + void SetupDevice(Topo tNew); + RECT GetFullRect(SURFHANDLE hSrc); + void IsLineTopologyAllowed(); + DWORD ColorComp(DWORD c) const; + SkpColor ColorComp(const SkpColor &c) const; + + template void AppendLineVertexList(const Type *pt, int npt, bool bLoop); + template void AppendLineVertexList(const Type *pt); + + oapi::Font *cfont; ///< currently selected font (NULL if none) + oapi::Pen *cpen; ///< currently selected pen (NULL if none) + oapi::Brush *cbrush; ///< currently selected brush (NULL if none) + + SkpColor pencolor; + SkpColor brushcolor; + DWORD Change; + bool bLine; + + SkpColor textcolor; + SkpColor bkcolor; + + bool bColorComp; + bool bColorKey; + bool bEnableScissor; + bool bDepthEnable; + bool bMustEndScene; + bool bBeginDraw; + RECT ScissorRect; + FVECTOR4 cColorKey; + SkpView vmode; + Topo tCurrent; + RenderState* pRState; + + + WORD vI = 0, iI = 0; + mutable FMATRIX4 mVP; + FMATRIX4 mV, mP, mW, mO; + FMATRIX4 mVOrig, mPOrig; + FVECTOR4 vTarget; + DWORD bkmode; + BlendState dwBlendState; + TAlign_horizontal tah; + TAlign_vertical tav; + float linescale, pattern; + float zfar; + int cx, cy; + RECT tgt; + + D3DSURFACE_DESC tgt_desc; + LPDIRECT3DSURFACE9 pTgt; + LPDIRECT3DSURFACE9 pDep; + LPDIRECT3DTEXTURE9 hTexture; + LPDIRECT3DTEXTURE9 hFontTex; + + + + // Sketchpad3 -------------------------------------------------------------- + DWORD RenderConfig; + DWORD Enable; + FMATRIX4 ColorMatrix; + FVECTOR4 Gamma, Noise; + + std::stack mWStack; + + + + // ------------------------------------------------------------------------- + bool _isSaveBuffer; ///< Flag indicating that the 'save buffer' can be used + char* _saveBuffer; ///< 'Save' string buffer (null-terminated @ len) + int _saveBufferSize; ///< Current size of the 'save' string buffer + + void ToSaveBuffer (const char *str, int len); ///< Store len sized string into internal 'save' buffer + inline void ReleaseSaveBuffer (); ///< Mark the buffer as "not save" + void WrapOneLine (char* str, int len, int maxWidth); ///< Wraps one text line at maxLenght pixels + // ------------------------------------------------------------------------- + char name[32]; + + void Log(const char *format, ...) const; + static FILE *log; + static CRITICAL_SECTION LogCrit; + static std::map< MESHHANDLE, class SketchMesh*> MeshMap; + static WORD *Idx; // List of indices + static SkpVtx *Vtx; // List of vertices + static vkClient *gc; + static LPDIRECT3DDEVICE9 pDev; + static FVECTOR2* pSinCos[5]; + static LPDIRECT3DTEXTURE9 pNoise; + // ------------------------------------------- + + + // Rendering pipeline configuration. Applies to every instance of this class + // + static ID3DXEffect* FX; + static D3DXHANDLE eDrawMesh; + static D3DXHANDLE eSketch; + static D3DXHANDLE eWVP; // Transformation matrix + static D3DXHANDLE eTex0; + static D3DXHANDLE eFnt0; + static D3DXHANDLE eNoiseTex; + static D3DXHANDLE eNoiseColor; + static D3DXHANDLE eColorMatrix; + static D3DXHANDLE eGamma; + static D3DXHANDLE eW; + static D3DXHANDLE ePen; + static D3DXHANDLE eVP; + static D3DXHANDLE eFov; + static D3DXHANDLE eRandom; + static D3DXHANDLE eTarget; + static D3DXHANDLE eKey; + static D3DXHANDLE eDashEn; + static D3DXHANDLE eTexEn; + static D3DXHANDLE eKeyEn; + static D3DXHANDLE eFntEn; + static D3DXHANDLE eWidth; + static D3DXHANDLE eWide; + static D3DXHANDLE eSize; + static D3DXHANDLE eMtrl; + static D3DXHANDLE eShade; + static D3DXHANDLE ePos; + static D3DXHANDLE ePos2; + static D3DXHANDLE eCov; + static D3DXHANDLE eCovEn; + static D3DXHANDLE eClearEn; + static D3DXHANDLE eEffectsEn; +}; + + + + + +#define SKP_FONT_CRISP 0x1 +#define SKP_FONT_ANTIALIAS 0x2 +#define SKP_FONT_CLEARTYPE 0x3 +#define SKP_FONT_CP_GREEK 0x10 + + + +class vkPadFont: public oapi::Font { + + friend class vkPad; + friend class GDIPad; + +public: + + static void vkTechInit(LPDIRECT3DDEVICE9 pDev); + + /** + * \brief Font constructor. + * \param height cell or character height [pixel] + * \param prop proportional/fixed width flag + * \param face font face name + * \param style font decoration + * \param orientation text orientation [1/10 deg] + * \note if \e height > 0, it represents the font cell height. if height < 0, + * its absolute value represents the character height. + * \note The \e style parameter can be any combination of the \ref Style + * enumeration items. + * \note The following face names are currently recognised: 'Courier New', + * 'Arial' and 'Times New Roman'. The generic names 'fixed', 'sans' and + * 'serif' are mapped to those specific type names, respectively. + * \note if the specified face name is not recognised, then 'sans' is + * selected for \e prop==true, and 'fixed' is selected for \e prop==false. + */ + vkPadFont (int height, bool prop, const char *face, FontStyle style = FontStyle::FONT_NORMAL, int orientation=0, DWORD flags=0); + vkPadFont (int height, char *face, int width = 0, int weight = 400, FontStyle style = FontStyle::FONT_NORMAL, float spacing = 0.0f); + /** + * \brief Font destructor. + */ + ~vkPadFont (); + + HFONT GetGDIFont () const; + DWORD GetQuality() const { return Quality; } + int GetTextLength(const char *pText, int len) const; + int GetIndexByPosition(const char *pText, int pos, int len) const; + +private: + vkTextPtr pFont; + HFONT hFont; + DWORD Quality; + float rotation; + static LPDIRECT3DDEVICE9 pDev; +}; + + + + +class vkPadPen: public oapi::Pen { + + friend class vkPad; + friend class GDIPad; + +public: + static void vkTechInit(LPDIRECT3DDEVICE9 pDev); + + /** + * \brief Pen constructor. + * \param style line style (0=invisible, 1=solid, 2=dashed) + * \param width line width [pixel] + * \param col line colour (format: 0xAABBGGRR) + * \note if \e width=0, the pen is drawn with a width of 1 pixel. + * \note Dashed line styles are only valid if the width parameter is <= 1. + */ + vkPadPen (int style, int width, DWORD col); + + /** + * \brief Pen destructor. + */ + ~vkPadPen (); + +private: + int style; + int width; + SkpColor clr; + HPEN hPen; +}; + + + + + +class vkPadBrush: public oapi::Brush { + friend class vkPad; + friend class GDIPad; + +public: + static void vkTechInit(LPDIRECT3DDEVICE9 pDev); + + /** + * \brief Brush constructor. + * \param col line colour (format: 0xAABBGGRR) + * \Only solid GDI brushes are supported. + */ + explicit vkPadBrush (DWORD col); + + /** + * \brief Brush destructor. + */ + ~vkPadBrush (); + +private: + SkpColor clr; + HBRUSH hBrush; +}; + + + +class vkPolyBase +{ + DWORD alloc_id; +public: + vkPolyBase(int _type) : alloc_id('POLY') { type = _type; version = 1; } + virtual ~vkPolyBase() { } + virtual void Draw(vkPad*, LPDIRECT3DDEVICE9 pDev) = 0; + virtual void Release() = 0; + + int version; + int type; +}; + + + + +class vkPolyLine : public vkPolyBase +{ + +public: + vkPolyLine(LPDIRECT3DDEVICE9 pDev, const FVECTOR2 *pt, int npt, bool bConnect); + ~vkPolyLine(); + + void Update(const FVECTOR2 *pt, int npt, bool bConnect); + void Draw(vkPad*, LPDIRECT3DDEVICE9 pDev); + void Release(); + +private: + bool bLoop; + WORD nVtx, nPt, nIdx, iI, vI; + LPDIRECT3DVERTEXBUFFER9 pVB; ///< (Local) Vertex buffer pointer + LPDIRECT3DINDEXBUFFER9 pIB; +}; + + + +class vkTriangle : public vkPolyBase +{ + +public: + vkTriangle(LPDIRECT3DDEVICE9 pDev, const gcCore::clrVtx *pt, int npt, int style); + ~vkTriangle(); + + void Update(const gcCore::clrVtx *pt, int npt); + void Draw(vkPad*, LPDIRECT3DDEVICE9 pDev); + void Release(); + +private: + int style; + WORD nPt; + LPDIRECT3DVERTEXBUFFER9 pVB; +}; + +#endif + diff --git a/OVP/VulkanClient/Pad2.cpp b/OVP/VulkanClient/Pad2.cpp new file mode 100644 index 000000000..335e64e01 --- /dev/null +++ b/OVP/VulkanClient/Pad2.cpp @@ -0,0 +1,1017 @@ + +// =================================================== +// Copyright (C) 2012-2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + +#include "Pad.h" +#include "TextMgr.h" +#include "Surface.h" +#include "Scene.h" +#include "Mesh.h" +#include "MathAPI.h" +#include + + + + +// =============================================================================================== +// Sketchpad2 Interface +// =============================================================================================== + +// =============================================================================================== +// +void vkPad::GetRenderSurfaceSize(LPSIZE size) +{ + size->cx = tgt_desc.Width; + size->cy = tgt_desc.Height; +} + + +// =============================================================================================== +// +void vkPad::QuickPen(DWORD color, float width, DWORD style) +{ +#ifdef SKPDBG + Log("QuickPen(0x%X, %d, %u)", color, width, style); +#endif + + if (QPen.bEnabled) { + if ((QPen.style == style) && (QPen.width == width) && (QPen.color == color)) return; + } + + Change |= SKPCHG_PEN; + + cpen = NULL; + if (color == 0) QPen.bEnabled = false; + else QPen.bEnabled = true; + QPen.style = style; + QPen.width = width; + QPen.color = color; + pencolor = SkpColor(ColorComp(color)); + + IsLineTopologyAllowed(); +} + + +// =============================================================================================== +// +void vkPad::QuickBrush(DWORD color) +{ +#ifdef SKPDBG + Log("QuickBrush(0x%X)", color); +#endif + + // No Change flags here + cbrush = NULL; + if (color == 0) QBrush.bEnabled = false; + else QBrush.bEnabled = true; + brushcolor = SkpColor(ColorComp(color)); + + IsLineTopologyAllowed(); +} + + +// =============================================================================================== +// +void vkPad::AddRectIdx(WORD aV) +{ + Idx[iI++] = aV; + Idx[iI++] = aV + 1; + Idx[iI++] = aV + 2; + Idx[iI++] = aV; + Idx[iI++] = aV + 2; + Idx[iI++] = aV + 3; +} + + +// =============================================================================================== +// +RECT vkPad::GetFullRect(SURFHANDLE hSrc) +{ + return {0, 0, static_cast(SURFACE(hSrc)->GetWidth()), static_cast(SURFACE(hSrc)->GetHeight())}; +} + + +// =============================================================================================== +// +void vkPad::CopyRect(const SURFHANDLE hSrc, const LPRECT _s, int tx, int ty) +{ +#ifdef SKPDBG + Log("CopyRect(0x%X)", DWORD(hSrc)); +#endif + + TexChange(hSrc); + + if (Topology(TRIANGLE)) { + + auto s = _s ? *_s : GetFullRect(hSrc); + + int h = std::abs(s.bottom - s.top); + int w = std::abs(s.right - s.left); + + AddRectIdx(vI); + + SkpVtxII(Vtx[vI++], tx , ty , s.left , s.top ); + SkpVtxII(Vtx[vI++], tx , ty + h, s.left , s.bottom); + SkpVtxII(Vtx[vI++], tx + w, ty + h, s.right, s.bottom); + SkpVtxII(Vtx[vI++], tx + w, ty , s.right, s.top ); + + DWORD x = SKPSW_TEXTURE | SKPSW_CENTER; + + Vtx[vI - 1].fnc = x; + Vtx[vI - 2].fnc = x; + Vtx[vI - 3].fnc = x; + Vtx[vI - 4].fnc = x; + } +} + + +// =============================================================================================== +// +void vkPad::StretchRect(const SURFHANDLE hSrc, const LPRECT _s, const LPRECT _t) +{ +#ifdef SKPDBG + Log("StretchRect(0x%X)", DWORD(hSrc)); +#endif + + TexChange(hSrc); + + if (Topology(TRIANGLE)) { + + auto s = _s ? *_s : GetFullRect(hSrc); + auto t = _t ? *_t : tgt; + + AddRectIdx(vI); + + SkpVtxII(Vtx[vI++], t.left , t.top , s.left , s.top ); + SkpVtxII(Vtx[vI++], t.left , t.bottom, s.left , s.bottom); + SkpVtxII(Vtx[vI++], t.right, t.bottom, s.right, s.bottom); + SkpVtxII(Vtx[vI++], t.right, t.top , s.right, s.top ); + + DWORD x = SKPSW_TEXTURE | SKPSW_CENTER; + + Vtx[vI - 1].fnc = x; + Vtx[vI - 2].fnc = x; + Vtx[vI - 3].fnc = x; + Vtx[vI - 4].fnc = x; + } +} + + +// =============================================================================================== +// +void vkPad::RotateRect(const SURFHANDLE hSrc, const LPRECT _s, int tcx, int tcy, float angle, float sw, float sh) +{ +#ifdef SKPDBG + Log("RotateRect(0x%X)", DWORD(hSrc)); +#endif + + TexChange(hSrc); + + if (Topology(TRIANGLE)) { + + auto s = _s ? *_s : GetFullRect(hSrc); + + float w = float(s.right - s.left) * sw; + float h = float(s.bottom - s.top) * sh; + + float san = sin(angle) * 0.5f; + float can = cos(angle) * 0.5f; + + float ax = float(tcx) + (-w * can + h * san); + float ay = float(tcy) + (-w * san - h * can); + + float bx = float(tcx) + (-w * can - h * san); + float by = float(tcy) + (-w * san + h * can); + + float cx = float(tcx) + (+w * can - h * san); + float cy = float(tcy) + (+w * san + h * can); + + float dx = float(tcx) + (+w * can + h * san); + float dy = float(tcy) + (+w * san - h * can); + + AddRectIdx(vI); + + SkpVtxFI(Vtx[vI++], ax, ay, s.left , s.top ); + SkpVtxFI(Vtx[vI++], bx, by, s.left , s.bottom); + SkpVtxFI(Vtx[vI++], cx, cy, s.right, s.bottom); + SkpVtxFI(Vtx[vI++], dx, dy, s.right, s.top ); + + DWORD x = SKPSW_TEXTURE | SKPSW_CENTER; + + Vtx[vI - 1].fnc = x; + Vtx[vI - 2].fnc = x; + Vtx[vI - 3].fnc = x; + Vtx[vI - 4].fnc = x; + } +} + + +// =============================================================================================== +// +void vkPad::ColorKey(const SURFHANDLE hSrc, const LPRECT _s, int tx, int ty) +{ +#ifdef SKPDBG + Log("ColorKey(0x%X)", DWORD(hSrc)); +#endif + + TexChange(hSrc); + + if (Topology(TRIANGLE)) { + + auto s = _s ? *_s : GetFullRect(hSrc); + + int h = std::abs(s.bottom - s.top); + int w = std::abs(s.right - s.left); + + AddRectIdx(vI); + + SkpVtxII(Vtx[vI++], tx , ty , s.left , s.top ); + SkpVtxII(Vtx[vI++], tx , ty + h, s.left , s.bottom); + SkpVtxII(Vtx[vI++], tx + w, ty + h, s.right, s.bottom); + SkpVtxII(Vtx[vI++], tx + w, ty , s.right, s.top ); + + DWORD f = SKPSW_TEXTURE | SKPSW_COLORKEY | SKPSW_CENTER; + + Vtx[vI - 1].fnc = f; + Vtx[vI - 2].fnc = f; + Vtx[vI - 3].fnc = f; + Vtx[vI - 4].fnc = f; + } +} + + +// =============================================================================================== +// +void vkPad::ColorKeyStretch(const SURFHANDLE hSrc, const LPRECT _s, const LPRECT _t) +{ +#ifdef SKPDBG + Log("StretchRect(0x%X)", DWORD(hSrc)); +#endif + + TexChange(hSrc); + + DWORD dwBak = dwBlendState; + + SetBlendState(BlendState((dwBak & 0xF) | BlendState::FILTER_POINT)); + + if (Topology(TRIANGLE)) { + + auto s = _s ? *_s : GetFullRect(hSrc); + auto t = _t ? *_t : tgt; + + AddRectIdx(vI); + + SkpVtxII(Vtx[vI++], t.left , t.top , s.left , s.top ); + SkpVtxII(Vtx[vI++], t.left , t.bottom, s.left , s.bottom); + SkpVtxII(Vtx[vI++], t.right, t.bottom, s.right, s.bottom); + SkpVtxII(Vtx[vI++], t.right, t.top , s.right, s.top ); + + DWORD x = SKPSW_TEXTURE | SKPSW_COLORKEY | SKPSW_CENTER; + + Vtx[vI - 1].fnc = x; + Vtx[vI - 2].fnc = x; + Vtx[vI - 3].fnc = x; + Vtx[vI - 4].fnc = x; + } + + SetBlendState(BlendState(dwBak)); +} + + +// =============================================================================================== +// +void vkPad::CopyRectNative(const LPDIRECT3DTEXTURE9 pSrc, const LPRECT _s, int tx, int ty) +{ +#ifdef SKPDBG + Log("CopyRectNative(0x%X)", DWORD(pSrc)); +#endif + + TexChangeNative(pSrc); + + if (Topology(TRIANGLE)) { + + auto s = _s ? *_s : GetFullRectNative(pSrc); + + int h = std::abs(s.bottom - s.top); + int w = std::abs(s.right - s.left); + + AddRectIdx(vI); + + SkpVtxII(Vtx[vI++], tx , ty , s.left , s.top ); + SkpVtxII(Vtx[vI++], tx , ty + h, s.left , s.bottom - 1); + SkpVtxII(Vtx[vI++], tx + w, ty + h, s.right - 1, s.bottom - 1); + SkpVtxII(Vtx[vI++], tx + w, ty , s.right - 1, s.top ); + + DWORD x = SKPSW_TEXTURE | SKPSW_CENTER; + + Vtx[vI - 1].fnc = x; + Vtx[vI - 2].fnc = x; + Vtx[vI - 3].fnc = x; + Vtx[vI - 4].fnc = x; + } +} + + +// =============================================================================================== +// +void vkPad::StretchRectNative(const LPDIRECT3DTEXTURE9 pSrc, const RECT *_s, const RECT *_t) +{ +#ifdef SKPDBG + Log("StretchRectNative(0x%X)", DWORD(pSrc)); +#endif + + TexChangeNative(pSrc); + + if (Topology(TRIANGLE)) { + + auto s = _s ? *_s : GetFullRectNative(pSrc); + auto t = _t ? *_t : tgt; + + AddRectIdx(vI); + + SkpVtxII(Vtx[vI++], t.left , t.top , s.left , s.top ); + SkpVtxII(Vtx[vI++], t.left , t.bottom, s.left , s.bottom - 1); + SkpVtxII(Vtx[vI++], t.right, t.bottom, s.right - 1, s.bottom - 1); + SkpVtxII(Vtx[vI++], t.right, t.top , s.right - 1, s.top ); + + DWORD x = SKPSW_TEXTURE | SKPSW_CENTER; + + Vtx[vI - 1].fnc = x; + Vtx[vI - 2].fnc = x; + Vtx[vI - 3].fnc = x; + Vtx[vI - 4].fnc = x; + } +} + + +// =============================================================================================== +// +void vkPad::CopyTetragon(const SURFHANDLE hSrc, const LPRECT _s, const FVECTOR2 tp[4]) +{ +#ifdef SKPDBG + Log("CopyTetragon(0x%X)", DWORD(pSrc)); +#endif + FVECTOR2 sp[4]; + FVECTOR2 a, b, c, d; + static const int n = 6; + static const float step = 1.0 / float(n - 1); + + DWORD fn = SKPSW_TEXTURE | SKPSW_CENTER; + + TexChange(hSrc); + + if (Topology(TRIANGLE)) + { + auto s = _s ? *_s : GetFullRect(hSrc); + + sp[0] = FVECTOR2{s.left , s.top }; + sp[1] = FVECTOR2{s.left , s.bottom}; + sp[2] = FVECTOR2{s.right, s.bottom}; + sp[3] = FVECTOR2{s.right, s.top }; + + // Create indices + for (int j = 0; j < (n-1); j++) + { + for (int i = 0; i < (n-1); i++) + { + WORD q = vI + i + j * n; + Idx[iI++] = q + 0; Idx[iI++] = q + 1; Idx[iI++] = q + (n + 1); + Idx[iI++] = q + 0; Idx[iI++] = q + (n + 1); Idx[iI++] = q + n; + } + } + + // Create grid points + int j = 0; + for (int i = 0; i < n; i++) + { + float x = float(i) * step; + + a = lerp(tp[0], tp[3], x); + b = lerp(tp[1], tp[2], x); + c = lerp(sp[0], sp[3], x); + d = lerp(sp[1], sp[2], x); + + for (int k = 0; k < n; k++) { + FVECTOR2 tv = lerp(a, b, float(k) * step); + FVECTOR2 sv = lerp(c, d, float(k) * step); + SkpVtxFF(Vtx[vI], tv.x, tv.y, sv.x, sv.y); + Vtx[vI].fnc = fn; + vI++; + } + } + } +} + + +// =============================================================================================== +// +void vkPad::FillTetragon(DWORD c, const FVECTOR2 pt[4]) +{ +#ifdef SKPDBG + Log("CopyTetragon(0x%X)", DWORD(pSrc)); +#endif + + DWORD fn = SKPSW_TEXTURE | SKPSW_CENTER; + + if (Topology(TRIANGLE)) { + AddRectIdx(vI); + SkpVtxFC(Vtx[vI++], pt[0].x, pt[0].y, c); + SkpVtxFC(Vtx[vI++], pt[1].x, pt[1].y, c); + SkpVtxFC(Vtx[vI++], pt[2].x, pt[2].y, c); + SkpVtxFC(Vtx[vI++], pt[3].x, pt[3].y, c); + } +} + + +// =============================================================================================== +// +bool vkPad::TextW (int x, int y, const LPWSTR str, int len) +{ +#ifdef SKPDBG + Log("TextW()"); +#endif + + // No "Setup" here, done in PrintSkp() + + if (!cfont) return false; + if (len == -1) len = int(wcslen(str)); + if (!len) return true; + + vkTextPtr pText = static_cast(cfont)->pFont; + + int lineSpace = pText->GetLineSpace(); + + std::wstring _str(str, str + size_t(len)); + + std::wistringstream f(_str); + std::wstring s; + int _y = y; + while (getline(f, s, L'\n')) { + pText->PrintSkp(this, x - 1.0f, _y - 1.0f, s.c_str(), -1); + _y += lineSpace; + } + + return true; +} + +// =============================================================================================== +// +void vkPad::TextEx(float x, float y, const char *str, float scale, float angle) +{ +#ifdef SKPDBG + Log("TextEx()"); +#endif + + // No "Setup" here, done in PrintSkp() + + if (cfont == NULL) return; + + vkTextPtr pText = static_cast(cfont)->pFont; + + switch (tah) { + default: + case LEFT: pText->SetTextHAlign(0); break; + case CENTER: pText->SetTextHAlign(1); break; + case RIGHT: pText->SetTextHAlign(2); break; + } + + switch (tav) { + default: + case TOP: pText->SetTextVAlign(0); break; + case BASELINE: pText->SetTextVAlign(1); break; + case BOTTOM: pText->SetTextVAlign(2); break; + } + + pText->SetRotation(angle); + pText->SetScaling(scale); + pText->PrintSkp(this, x - 1.0f, y - 1.0f, str, -1, (bkmode == OPAQUE)); +} + + +// =============================================================================================== +// +void vkPad::ClipRect(const LPRECT clip) +{ +#ifdef SKPDBG + Log("ClipRect(0x%X)", DWORD(clip)); +#endif + + Change |= SKPCHG_CLIPRECT; + + bEnableScissor = (clip != NULL); + if (clip) ScissorRect = (*clip); + else ScissorRect = { 0,0,0,0 }; +} + + +// =============================================================================================== +// +void vkPad::Clipper(int idx, const VECTOR3 *uDir, double cos_angle, double dist) +{ +#ifdef SKPDBG + Log("Clipper()"); +#endif + + Change |= SKPCHG_CLIPCONE; + + if (idx < 0) idx = 0; + if (idx > 1) idx = 1; + + if (uDir) { + ClipData[idx].uDir = _F(uDir); + ClipData[idx].ca = float(cos_angle); + ClipData[idx].dst = float(dist); + ClipData[idx].bEnable = true; + } + else { + ClipData[idx].uDir = FVECTOR3(0,0,1); + ClipData[idx].ca = 2.0f; + ClipData[idx].dst = 0.0f; + ClipData[idx].bEnable = false; + } +} + + +// =============================================================================================== +// +void vkPad::DepthEnable(bool bEnable) +{ +#ifdef SKPDBG + Log("DepthEnable(%u)", DWORD(bEnable)); +#endif + + Flush(); // Must Flush() here before a mode change + + if (pDep) { + Change |= SKPCHG_DEPTH; + bDepthEnable = bEnable; + } + else bDepthEnable = false; +} + + +// =============================================================================================== +// +const FMATRIX4 *vkPad::ViewMatrix() const +{ + return (const FMATRIX4*)&mV; +} + + +// =============================================================================================== +// +const FMATRIX4 *vkPad::ProjectionMatrix() const +{ + return (const FMATRIX4*)&mP; +} + + +// =============================================================================================== +// +const FMATRIX4 *vkPad::GetViewProjectionMatrix() const +{ + oapiMatrixMultiply(&mVP, &mV, &mP); + return (const FMATRIX4 *)&mVP; +} + + +// =============================================================================================== +// +void vkPad::SetViewMatrix(const FMATRIX4 *pV) +{ +#ifdef SKPDBG + Log("SetViewMatrix(0x%X)", DWORD(pV)); +#endif + Change |= SKPCHG_TRANSFORM; + if (pV) memcpy(&mV, pV, sizeof(FMATRIX4)); + else mV = mVOrig; +} + + +// =============================================================================================== +// +void vkPad::SetProjectionMatrix(const FMATRIX4 *pP) +{ +#ifdef SKPDBG + Log("SetProjectionMatrix(0x%X)", DWORD(pP)); +#endif + Change |= SKPCHG_TRANSFORM; + if (pP) memcpy(&mP, pP, sizeof(FMATRIX4)); + else mP = mPOrig; +} + + +// =============================================================================================== +// +void vkPad::SetViewMode(SkpView mode) +{ +#ifdef SKPDBG + Log("SetViewMode(0x%X)", DWORD(mode)); +#endif + Flush(); // Must Flush() here before a mode change + Change |= SKPCHG_TRANSFORM; + vmode = mode; +} + + +// =============================================================================================== +// !! For a private use in vkClient !! +// +FMATRIX4* vkPad::WorldMatrix() +{ +#ifdef SKPDBG + Log("WorldMatrix() ! - ! - !"); +#endif + Change |= SKPCHG_TRANSFORM; + return (FMATRIX4*)&mW; +} + + +// =============================================================================================== +// +void vkPad::SetWorldTransform2D(float scale, float rot, const IVECTOR2 *c, const IVECTOR2 *t) +{ +#ifdef SKPDBG + Log("SetWorldTransform2D(%f, %f, 0x%X, 0x%X)", scale, rot, DWORD(c), DWORD(t)); +#endif + Change |= SKPCHG_TRANSFORM; + + FVECTOR2 ctr = FVECTOR2(0, 0); + FVECTOR2 trl = FVECTOR2(0, 0); + + if (c) ctr = FVECTOR2(float(c->x), float(c->y)); + if (t) trl = FVECTOR2(float(t->x), float(t->y)); + + D3DMAT_AffineTransformation2D(&mW, scale, &ctr, rot, &trl); +} + + +// =============================================================================================== +// +void vkPad::SetWorldTransform(const FMATRIX4 *pWT) +{ +#ifdef SKPDBG + Log("SetWorldTransform(0x%X)", DWORD(pWT)); +#endif + Change |= SKPCHG_TRANSFORM; + + if (pWT) memcpy(&mW, pWT, sizeof(FMATRIX4)); + else oapiMatrixIdentity(&mW); +} + + +// =============================================================================================== +// +void vkPad::SetWorldBillboard(const FVECTOR3& wpos, float scale, bool bFixed, const FVECTOR3* index) +{ +#ifdef SKPDBG + Log("SetWorldBillboard()"); +#endif + scale *= (mP.m11 + mP.m22) * 0.5f; + Change |= SKPCHG_TRANSFORM; + FVECTOR3 up = unit(wpos); + FVECTOR3 y = unit(crossp((index ? *index : FVECTOR3(mV.m11, mV.m21, mV.m31)), up)); + FVECTOR3 x = crossp(up, y); + float d = (bFixed ? dotp(up, wpos) / float(tgt_desc.Width) : 1.0f) * scale; + FMATRIX4 mWorld; + mWorld._x.xyz = x * d; + mWorld._y.xyz = y * d; + mWorld._z.xyz = up * d; + mWorld._p.xyz = wpos; + mWorld.m44 = 1.0f; + memcpy(&mW, &mWorld, sizeof(FMATRIX4)); +} + + +// =============================================================================================== +// +void vkPad::SetGlobalLineScale(float width, float pat) +{ +#ifdef SKPDBG + Log("SetGlobalLineScale(%f, %f)", width, pat); +#endif + Change |= SKPCHG_PEN; + linescale = width; + pattern = pat; +} + + +// =============================================================================================== +// +void vkPad::SetFontTextureNative(LPDIRECT3DTEXTURE9 hNew) +{ + if (hNew == hFontTex) return; + Change |= SKPCHG_FONT; + hFontTex = hNew; +} + + +// =============================================================================================== +// +bool vkPad::TexChangeNative(LPDIRECT3DTEXTURE9 hNew) +{ + if (hNew == hTexture) return false; + Change |= SKPCHG_TEXTURE; + hTexture = hNew; + return true; +} + + +// =============================================================================================== +// +void vkPad::TexChange(SURFHANDLE hNew) +{ + if (!SURFACE(hNew)->IsTexture()) { + LogErr("Sketchpad2: Source is not a texture"); + HALT(); + return; + } + + TexChangeNative(SURFACE(hNew)->GetTexture()); + + if (SURFACE(hNew)->IsColorKeyEnabled()) { + bColorKey = true; + cColorKey = FVECTOR4(SURFACE(hNew)->ColorKey); + } + else { + bColorKey = false; + cColorKey = FVECTOR4(DWORD(0)); + } +} + + +// =============================================================================================== +// +int vkPad::DrawMeshGroup(const MESHHANDLE hMesh, DWORD grp, Sketchpad::MeshFlags flags, const SURFHANDLE hTex) +{ +#ifdef SKPDBG + Log("DrawMeshGroup(0x%X, gpr=%u, flags=0x%X, hTex=0x%X)", DWORD(hMesh), grp, flags, DWORD(hTex)); +#endif + UINT num; + SketchMesh* pMesh = GetSketchMesh(hMesh); + + if (!pMesh) return -1; + + DWORD nGrp = pMesh->GroupCount(); + + if (grp >= nGrp) return -1; + + // Flush Pending graphics ------------------------------------ + // + SetupDevice(tCurrent); + + // Initialize device for drawing a mesh ---------------------- + // + pMesh->Init(); + + if (flags & Sketchpad::MeshFlags::CULL_NONE) pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + else pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + + HR(FX->SetTechnique(eDrawMesh)); + HR(FX->Begin(&num, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + + HR(FX->SetBool(eShade, (flags & Sketchpad::MeshFlags::SMOOTH_SHADE) != 0)); + + if (flags & Sketchpad::MeshFlags::RENDER_ALL) grp = 0; + + // Draw a mesh group(s) ---------------------------------------- + // + while (grp < nGrp) + { + SURFHANDLE pTex = hTex ? hTex : pMesh->GetTexture(grp); + FVECTOR4 Mat = pMesh->GetMaterial(grp); + + if (pTex) + { + HR(FX->SetTexture(eTex0, SURFACE(pTex)->GetTexture())); + HR(FX->SetBool(eTexEn, true)); + } + else + { + HR(FX->SetBool(eTexEn, false)); + } + + HR(FX->SetValue(eMtrl, &Mat, sizeof(FVECTOR4))); + HR(FX->CommitChanges()); + + pMesh->RenderGroup(grp); + + grp++; + + if (!(flags & Sketchpad::MeshFlags::RENDER_ALL)) break; + } + + HR(FX->EndPass()); + HR(FX->End()); + + return nGrp; +} + + +// =============================================================================================== +// +RECT vkPad::GetFullRectNative(LPDIRECT3DTEXTURE9 hSrc) +{ + D3DSURFACE_DESC desc; + hSrc->GetLevelDesc(0, &desc); + return {0, 0, static_cast(desc.Width), static_cast(desc.Height)}; +} + + + +// ====================================================================================== +// Polyline Interface +// ====================================================================================== + + + +vkPolyLine::vkPolyLine(LPDIRECT3DDEVICE9 pDev, const FVECTOR2 *pt, int npt, bool bConnect) : vkPolyBase(0) +{ + nPt = npt + 2; + nVtx = 2 * nPt; + nIdx = 6 * nPt; + + HR(pDev->CreateVertexBuffer(nVtx * sizeof(SkpVtx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &pVB, NULL)); + HR(pDev->CreateIndexBuffer(nIdx * sizeof(WORD), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &pIB, NULL)); + + WORD *Idx = NULL; + + HR(pIB->Lock(0, 0, (LPVOID*)&Idx, D3DLOCK_DISCARD)); + + iI = 0; + + for (WORD i=0,p=0;pUnlock()); + + if (pt) Update(pt, npt, bConnect); +} + + +// =============================================================================================== +// +vkPolyLine::~vkPolyLine() +{ + +} + + +// =============================================================================================== +// +void vkPolyLine::Release() +{ + SAFE_RELEASE(pVB); + SAFE_RELEASE(pIB); +} + + +// =============================================================================================== +// +void vkPolyLine::Draw(vkPad *pSkp, LPDIRECT3DDEVICE9 pDev) +{ + pDev->SetStreamSource(0, pVB, 0, sizeof(SkpVtx)); + pDev->SetIndices(pIB); + pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, vI, 0, vI-2); +} + + +// =============================================================================================== +// +void vkPolyLine::Update(const FVECTOR2 *_pt, int _npt, bool bConnect) +{ + SkpVtx *Vtx = NULL; + FVECTOR2 *pt = (FVECTOR2 *)_pt; + + HR(pVB->Lock(0, 0, (LPVOID*)&Vtx, D3DLOCK_DISCARD)); + + WORD npt = WORD(_npt); + WORD li = WORD(npt - 1); + + vI = 0; + float length = 0.0f; + + FVECTOR2 pp; // Prev point + FVECTOR2 np; // Next point + + bLoop = bConnect; + + // Line Init ------------------------------------------------------------ + // + if (bLoop) pp = pt[li]; + else pp = pt[0] * 2.0 - pt[1]; + + // Create line segments ------------------------------------------------- + // + for (WORD i = 0; iUnlock()); +} + + + + + + + + +// ====================================================================================== +// Triangle Interface +// ====================================================================================== + + + +vkTriangle::vkTriangle(LPDIRECT3DDEVICE9 pDev, const gcCore::clrVtx *pt, int npt, int _style) : vkPolyBase(1) +{ + nPt = npt; + style = _style; + HR(pDev->CreateVertexBuffer(nPt * sizeof(SkpVtx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &pVB, NULL)); + if (pt) Update(pt, npt); +} + + +// =============================================================================================== +// +vkTriangle::~vkTriangle() +{ + +} + + +// =============================================================================================== +// +void vkTriangle::Release() +{ + SAFE_RELEASE(pVB); +} + + +// =============================================================================================== +// +void vkTriangle::Draw(vkPad* pSkp, LPDIRECT3DDEVICE9 pDev) +{ + pDev->SetStreamSource(0, pVB, 0, sizeof(SkpVtx)); + if (style == PF_TRIANGLES) pDev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, nPt / 3); + if (style == PF_FAN) pDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, nPt - 2); + if (style == PF_STRIP) pDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, nPt - 2); +} + + +// =============================================================================================== +// +void vkTriangle::Update(const gcCore::clrVtx *pt, int npt) +{ + SkpVtx *Vtx = NULL; + HR(pVB->Lock(0, 0, (LPVOID*)&Vtx, D3DLOCK_DISCARD)); + + memset(Vtx, 0, sizeof(SkpVtx)*npt); + + for (int i = 0; i < npt; i++) { + Vtx[i].x = pt[i].pos.x; + Vtx[i].y = pt[i].pos.y; + Vtx[i].clr = pt[i].color; + Vtx[i].fnc = SKPSW_CENTER | SKPSW_FRAGMENT; + } + HR(pVB->Unlock()); +} diff --git a/OVP/VulkanClient/Pad3.cpp b/OVP/VulkanClient/Pad3.cpp new file mode 100644 index 000000000..d3d10789b --- /dev/null +++ b/OVP/VulkanClient/Pad3.cpp @@ -0,0 +1,304 @@ + +// =================================================== +// Copyright (C) 2012-2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + +#include "Pad.h" +#include "Surface.h" +#include "MathAPI.h" +#include + + + +// =============================================================================================== +// Sketchpad3 Interface +// =============================================================================================== + +void vkPad::ColorCompatibility(bool bEnable) +{ +#ifdef SKPDBG + Log("ColorCompatibility(%u)", DWORD(bEnable)); +#endif + bColorComp = bEnable; +} + +// =============================================================================================== +// +const FMATRIX4 *vkPad::GetColorMatrix() +{ + return &ColorMatrix; +} + + +// =============================================================================================== +// +void vkPad::SetColorMatrix(const FMATRIX4 *pMatrix) +{ +#ifdef SKPDBG + Log("SetColorMatrix(0x%X)", DWORD(pMatrix)); +#endif + if (pMatrix) { + memcpy_s(&ColorMatrix, sizeof(FMATRIX4), pMatrix, sizeof(FMATRIX4)); + SetEnable(SKP3E_CMATR); + } + else { + memset(&ColorMatrix, 0, sizeof(FMATRIX4)); + ColorMatrix.m11 = 1.0f; + ColorMatrix.m22 = 1.0f; + ColorMatrix.m33 = 1.0f; + ColorMatrix.m44 = 1.0f; + ClearEnable(SKP3E_CMATR); + } +} + + +// =============================================================================================== +// +void vkPad::SetBrightness(const FVECTOR4 *pBrightness) +{ +#ifdef SKPDBG + Log("SetBrightness(0x%X)", DWORD(pBrightness)); +#endif + if (pBrightness == NULL) SetColorMatrix(NULL); + else { + memset(&ColorMatrix, 0, sizeof(FMATRIX4)); + ColorMatrix.m11 = pBrightness->r; + ColorMatrix.m22 = pBrightness->g; + ColorMatrix.m33 = pBrightness->b; + ColorMatrix.m44 = pBrightness->a; + SetEnable(SKP3E_CMATR); + } +} + + +// =============================================================================================== +// +FVECTOR4 vkPad::GetRenderParam(RenderParam param) +{ + switch (param) { + case Sketchpad::RenderParam::PRM_GAMMA: return Gamma; + case Sketchpad::RenderParam::PRM_NOISE: return Noise; + } + return F4_Zero; +} + + +// =============================================================================================== +// +void vkPad::SetRenderParam(RenderParam param, const FVECTOR4 *d) +{ +#ifdef SKPDBG + Log("SetRenderParam(%u, 0x%X)", param, DWORD(d)); +#endif + if (d == NULL) { + switch (param) { + case Sketchpad::RenderParam::PRM_GAMMA: Gamma = F4_One; ClearEnable(SKP3E_GAMMA); break; + case Sketchpad::RenderParam::PRM_NOISE: Noise = F4_Zero; ClearEnable(SKP3E_NOISE); break; + } + return; + } + + switch (param) { + case Sketchpad::RenderParam::PRM_GAMMA: Gamma = FVECTOR4(d->r, d->g, d->b, d->a); SetEnable(SKP3E_GAMMA); break; + case Sketchpad::RenderParam::PRM_NOISE: Noise = *d; SetEnable(SKP3E_NOISE); break; + } +} + +// =============================================================================================== +// +void vkPad::SetEnable(DWORD config) +{ + Change |= SKPCHG_EFFECTS; + Enable |= config; +} + + +// =============================================================================================== +// +void vkPad::ClearEnable(DWORD config) +{ + Change |= SKPCHG_EFFECTS; + Enable &= (~config); +} + + +// =============================================================================================== +// +void vkPad::SetBlendState(BlendState dwState) +{ +#ifdef SKPDBG + Log("SetBlendState(%u)", dwState); +#endif + // Must Flush() here before a mode change + Flush(); + dwBlendState = dwState; +} + + +// =============================================================================================== +// +FMATRIX4 vkPad::GetWorldTransform() const +{ + FMATRIX4 fm; + memcpy_s(&fm, sizeof(FMATRIX4), &mW, sizeof(FMATRIX4)); + return fm; +} + + +// =============================================================================================== +// +void vkPad::PushWorldTransform() +{ +#ifdef SKPDBG + Log("PushWorldTransform()"); +#endif + mWStack.push(mW); +} + + +// =============================================================================================== +// +void vkPad::PopWorldTransform() +{ +#ifdef SKPDBG + Log("PopWorldTransform()"); +#endif + if (mWStack.empty() == false) { + mW = mWStack.top(); + mWStack.pop(); + Change |= SKPCHG_TRANSFORM; + } +} + + +// =============================================================================================== +// +void vkPad::SetWorldScaleTransform2D(const FVECTOR2 *scl, const IVECTOR2 *trl) +{ +#ifdef SKPDBG + Log("SetWorldScaleTransform2D(0x%X, 0x%X)", DWORD(scl), DWORD(trl)); +#endif + Change |= SKPCHG_TRANSFORM; + + float sx = 1.0f, sy = 1.0f; + + if (scl) sx = scl->x, sy = scl->y; + + FVECTOR3 t; + + t.x = 0; + t.y = 0; + t.z = 0; + + if (trl) t.x = float(trl->x), t.y = float(trl->y); + + D3DMAT_Scale(&mW, sx, sy, 1.0f); + D3DMAT_SetTranslation(&mW, &t); +} + + +// =============================================================================================== +// +void vkPad::GradientFillRect(const LPRECT R, DWORD c1, DWORD c2, bool bVertical) +{ +#ifdef SKPDBG + Log("GradientFillRect()"); +#endif + + DWORD a, b, c, d; + + a = d = c1; b = c = c2; + + if (bVertical) { a = b = c1; c = d = c2; } + + int l = R->left; int r = R->right; + int t = R->top; int m = R->bottom; + + r--; m--; + + if (Topology(TRIANGLE)) { + AddRectIdx(vI); + SkpVtxGF(Vtx[vI++], l, t, a); + SkpVtxGF(Vtx[vI++], r, t, b); + SkpVtxGF(Vtx[vI++], r, m, c); + SkpVtxGF(Vtx[vI++], l, m, d); + } +} + + +// =============================================================================================== +// +void vkPad::ColorFill(DWORD color, const LPRECT tgt) +{ +#ifdef SKPDBG + Log("ColorFill()"); +#endif + if (tgt) FillRect(tgt->left, tgt->top, tgt->right, tgt->bottom, SkpColor(color)); + else FillRect(0, 0, tgt_desc.Width, tgt_desc.Height, SkpColor(color)); +} + +// =============================================================================================== +// +void vkPad::StretchRegion(const skpRegion *rgn, const SURFHANDLE hSrc, const LPRECT out) +{ +#ifdef SKPDBG + Log("StretchRegion()"); +#endif + const RECT *ext = &(rgn->outr); + const RECT *itr = &(rgn->intr); + + int x0 = ext->left; + int x1 = itr->left; + int x2 = itr->right; + int x3 = ext->right; + + int y0 = ext->top; + int y1 = itr->top; + int y2 = itr->bottom; + int y3 = ext->bottom; + + int tx0 = out->left; + int tx3 = out->right; + int ty0 = out->top; + int ty3 = out->bottom; + + int tx1 = tx0 + (x1 - x0); + int tx2 = tx3 - (x3 - x2); + int ty1 = ty0 + (y1 - y0); + int ty2 = ty3 - (y3 - y2); + + // Corners + if (x0 != x1 && y0 != y1) CopyRect(hSrc, &(_R(x0, y0, x1, y1)), tx0, ty0); // TOP-LEFT + if (x2 != x3 && y0 != y1) CopyRect(hSrc, &(_R(x2, y0, x3, y1)), tx2, ty0); // TOP-RIGHT + if (x0 != x1 && y2 != y3) CopyRect(hSrc, &(_R(x0, y2, x1, y3)), tx0, ty2); // BTM-LEFT + if (x2 != x3 && y2 != y3) CopyRect(hSrc, &(_R(x2, y2, x3, y3)), tx2, ty2); // BTM-RIGHT + + // Sides + if (x1 != x2 && y0 != y1) StretchRect(hSrc, &(_R(x1, y0, x2, y1)), &(_R(tx1, ty0, tx2, ty1))); // TOP + if (x1 != x2 && y2 != y3) StretchRect(hSrc, &(_R(x1, y2, x2, y3)), &(_R(tx1, ty2, tx2, ty3))); // BOTTOM + if (x0 != x1 && y1 != y2) StretchRect(hSrc, &(_R(x0, y1, x1, y2)), &(_R(tx0, ty1, tx1, ty2))); // LEFT + if (x2 != x3 && y1 != y2) StretchRect(hSrc, &(_R(x2, y1, x3, y2)), &(_R(tx2, ty1, tx3, ty2))); // RIGHT + + // Center + StretchRect(hSrc, &(_R(x1, y1, x2, y2)), &(_R(tx1, ty1, tx2, ty2))); +} + +// =============================================================================================== +// +void vkPad::Clear(DWORD color, bool bColor, bool bDepth) +{ + DWORD flags = 0; + if (bColor) flags |= D3DCLEAR_TARGET; + if (bDepth) flags |= D3DCLEAR_ZBUFFER; + pDev->Clear(0, NULL, flags, color, 1.0f, 0); +} + +// =============================================================================================== +// +void vkPad::SetClipDistance(float nr, float fr) +{ + D3DMAT_OrthoOffCenterLH(&mO, 0.0f, (float)tgt_desc.Width, (float)tgt_desc.Height, 0.0f, nr, fr); + mP.m33 = fr / (fr - nr); + mP.m43 = -nr * mP.m33; +} diff --git a/OVP/VulkanClient/Particle.cpp b/OVP/VulkanClient/Particle.cpp new file mode 100644 index 000000000..01c80c9e1 --- /dev/null +++ b/OVP/VulkanClient/Particle.cpp @@ -0,0 +1,850 @@ +// ============================================================== +// Particle.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006 - 2016 Martin Schweiger +// 2011 - 2016 Jarmo Nikkanen (vkClient modification) +// ============================================================== + +#define STRICT 1 + +#include "Particle.h" +#include "Scene.h" +#include "Surface.h" +#include "Config.h" +#include + +static bool needsetup = true; + +static VERTEX_XYZ_TEX evtx[MAXPARTICLE*4]; // vertex list for emissive trail (no normals) +static NTVERTEX dvtx[MAXPARTICLE*4]; // vertex list for diffusive trail +static WORD idx[MAXPARTICLE*6]; // index list + +static float tu[8*4] = {0.0,0.5,0.5,0.0, 0.5,1.0,1.0,0.5, 0.0,0.5,0.5,0.0, 0.5,1.0,1.0,0.5, + 0.5,0.5,0.0,0.0, 1.0,1.0,0.5,0.5, 0.5,0.5,0.0,0.0, 1.0,1.0,0.5,0.5}; + +static float tv[8*4] = {0.0,0.0,0.5,0.5, 0.0,0.0,0.5,0.5, 0.5,0.5,1.0,1.0, 0.5,0.5,1.0,1.0, + 0.0,0.5,0.5,0.0, 0.0,0.5,0.5,0.0, 0.5,1.0,1.0,0.5, 0.5,1.0,1.0,0.5}; + +using namespace oapi; + +static PARTICLESTREAMSPEC DefaultParticleStreamSpec = { + 0, // flags + 8.0, // creation size + 0.5, // creation rate + 100, // emission velocity + 0.3, // velocity randomisation + 8.0, // lifetime + 0.5, // growth rate + 3.0, // atmospheric slowdown + PARTICLESTREAMSPEC::DIFFUSE, // render lighting method + PARTICLESTREAMSPEC::LVL_SQRT, // mapping from level to alpha + 0, 1, // lmin and lmax levels for mapping + PARTICLESTREAMSPEC::ATM_PLOG, // mapping from atmosphere to alpha + 1e-4, 1 // amin and amax densities for mapping +}; + +SURFHANDLE vkParticleStream::deftex = 0; +SURFHANDLE vkParticleStream::deftexems = 0; +bool vkParticleStream::bShadows = false; + +vkParticleStream::vkParticleStream(GraphicsClient *_gc, PARTICLESTREAMSPEC *pss) : ParticleStream (_gc, pss), vkEffect() +{ + pGC = (vkClient*)_gc; + + //cam_ref = &gc->GetScene()->GetCameraGPos(); + //src_ref = 0; + //src_ofs = _V(0,0,0); + + interval = 0.1; + SetSpecs (pss ? pss : &DefaultParticleStreamSpec); + t0 = oapiGetSimTime(); + //active = false; + pfirst = NULL; + plast = NULL; + np = 0; + oapiMatrixIdentity(&mWorld); + + if (needsetup) { + int i, j, k, r, ofs; + for (i = j = 0; i < MAXPARTICLE; i++) { + ofs = i*4; + idx[j++] = ofs; + idx[j++] = ofs+2; + idx[j++] = ofs+1; + idx[j++] = ofs+2; + idx[j++] = ofs; + idx[j++] = ofs+3; + r = rand() & 7; + for (k = 0; k < 4; k++) { + evtx[ofs+k].tu = dvtx[ofs+k].tu = tu[r*4+k]; + evtx[ofs+k].tv = dvtx[ofs+k].tv = tv[r*4+k]; + } + } + needsetup = false; + } +} + +vkParticleStream::~vkParticleStream() +{ + while (pfirst) { + ParticleSpec *tmp = pfirst; + pfirst = pfirst->next; + delete tmp; + } +} + +void vkParticleStream::GlobalInit (oapi::vkClient *gclient) +{ + deftex = SURFACE(gclient->clbkLoadTexture("Contrail1.dds", 0)); + deftexems = SURFACE(gclient->clbkLoadTexture("Contrail1.dds", 0)); + bShadows = *(bool*)gclient->GetConfigParam (CFGPRM_VESSELSHADOWS); +} + +void vkParticleStream::GlobalExit () +{ + DELETE_SURFACE(deftex); + DELETE_SURFACE(deftexems); +} + +void vkParticleStream::SetSpecs(PARTICLESTREAMSPEC *pss) +{ + SetParticleHalflife (pss->lifetime); + size0 = pss->srcsize; + speed = pss->v0; + vrand = pss->srcspread; + alpha = pss->growthrate; + beta = pss->atmslowdown; + pdensity = pss->srcrate; + diffuse = (pss->ltype == PARTICLESTREAMSPEC::DIFFUSE); + lmap = pss->levelmap; + lmin = pss->lmin, lmax = pss->lmax; + amap = pss->atmsmap; + amin = pss->amin; + + switch (amap) { + case PARTICLESTREAMSPEC::ATM_PLIN: afac = 1.0/(pss->amax-amin); break; + case PARTICLESTREAMSPEC::ATM_PLOG: afac = 1.0/log(pss->amax/amin); break; + } + + if (diffuse) tex = (SURFACE(pss->tex) ? SURFACE(pss->tex) : deftex); + else tex = (SURFACE(pss->tex) ? SURFACE(pss->tex) : deftexems); +} + +void vkParticleStream::SetParticleHalflife (double pht) +{ + exp_rate = RAND_MAX/pht; + stride = max (1, min (20,(int)pht)); + ipht2 = 0.5/pht; +} + +void vkParticleStream::SetObserverRef (const VECTOR3 *cam) +{ + LogErr("vkParticleStream::SetObserverRef() NOT IMPLEMENTED"); + //cam_ref = cam; +} + +void vkParticleStream::SetSourceRef (const VECTOR3 *src) +{ + LogErr("vkParticleStream::SetSourceRef() NOT IMPLEMENTED"); + //src_ref = src; +} + +void vkParticleStream::SetSourceOffset (const VECTOR3 &ofs) +{ + LogErr("vkParticleStream::SetSourceOffset() NOT IMPLEMENTED"); + //src_ofs = ofs; +} + +void vkParticleStream::SetIntensityLevelRef (double *lvl) +{ + level = lvl; +} + +double vkParticleStream::Level2Alpha(double level) const +{ + switch (lmap) { + case PARTICLESTREAMSPEC::LVL_FLAT: return lmin; + case PARTICLESTREAMSPEC::LVL_LIN: return level; + case PARTICLESTREAMSPEC::LVL_SQRT: return sqrt (level); + case PARTICLESTREAMSPEC::LVL_PLIN: return max (0.0, min (1.0, (level-lmin)/(lmax-lmin))); + case PARTICLESTREAMSPEC::LVL_PSQRT: return (level <= lmin ? 0 : level >= lmax ? 1 : sqrt ((level-lmin)/(lmax-lmin))); + } + return 0; // should not happen +} + +double vkParticleStream::Atm2Alpha(double prm) const +{ + switch (amap) { + case PARTICLESTREAMSPEC::ATM_FLAT: return amin; + case PARTICLESTREAMSPEC::ATM_PLIN: return max (0.0, min (1.0, (prm-amin)*afac)); + case PARTICLESTREAMSPEC::ATM_PLOG: return max (0.0, min (1.0, log(prm/amin)*afac)); + } + return 0; // should not happen +} + +ParticleSpec *vkParticleStream::CreateParticle (const VECTOR3 &pos, const VECTOR3 &vel, double size, double alpha) +{ + ParticleSpec *p = new ParticleSpec; + p->pos = pos; + p->vel = vel; + p->size = size; + p->alpha0 = alpha; + p->t0 = oapiGetSimTime(); + p->texidx = (rand() & 7) * 4; + p->flag = 0; + p->next = NULL; + p->prev = plast; + if (plast) plast->next = p; + else pfirst = p; + plast = p; + np++; + + if (np > MAXPARTICLE) + DeleteParticle (pfirst); + + return p; +} + +void vkParticleStream::DeleteParticle (ParticleSpec *p) +{ + if (p->prev) p->prev->next = p->next; + else pfirst = p->next; + if (p->next) p->next->prev = p->prev; + else plast = p->prev; + delete p; + np--; +} + +void vkParticleStream::Update () +{ + ParticleSpec *p, *tmp; + double dt = oapiGetSimStep(); + + for (p = pfirst; p;) { + if (dt * exp_rate > rand()) { + tmp = p; + p = p->next; + DeleteParticle (tmp); + } else { + p->pos += p->vel*dt; + p = p->next; + } + } +} + +void vkParticleStream::Timejump() +{ + while (pfirst) { + ParticleSpec *tmp = pfirst; + pfirst = pfirst->next; + delete tmp; + } + pfirst = NULL; + plast = NULL; + np = 0; + t0 = oapiGetSimTime(); +} + +void vkParticleStream::SetDParticleCoords(const VECTOR3 &ppos, double scale, NTVERTEX *vtx) +{ + VECTOR3 cdir = ppos; + double ux, uy, uz, vx, vy, vz, len; + if (cdir.y || cdir.z) { + ux = 0; + uy = cdir.z; + uz = -cdir.y; + len = scale / sqrt (uy*uy + uz*uz); + uy *= len; + uz *= len; + vx = cdir.y*cdir.y + cdir.z*cdir.z; + vy = -cdir.x*cdir.y; + vz = -cdir.x*cdir.z; + len = scale / sqrt(vx*vx + vy*vy + vz*vz); + vx *= len; + vy *= len; + vz *= len; + } else { + ux = 0; + uy = scale; + uz = 0; + vx = 0; + vy = 0; + vz = scale; + } + vtx[0].x = (float)(ppos.x-ux-vx); + vtx[0].y = (float)(ppos.y-uy-vy); + vtx[0].z = (float)(ppos.z-uz-vz); + vtx[1].x = (float)(ppos.x-ux+vx); + vtx[1].y = (float)(ppos.y-uy+vy); + vtx[1].z = (float)(ppos.z-uz+vz); + vtx[2].x = (float)(ppos.x+ux+vx); + vtx[2].y = (float)(ppos.y+uy+vy); + vtx[2].z = (float)(ppos.z+uz+vz); + vtx[3].x = (float)(ppos.x+ux-vx); + vtx[3].y = (float)(ppos.y+uy-vy); + vtx[3].z = (float)(ppos.z+uz-vz); +} + +void vkParticleStream::SetEParticleCoords (const VECTOR3 &ppos, double scale, VERTEX_XYZ_TEX *vtx) +{ + VECTOR3 cdir = ppos; + double ux, uy, uz, vx, vy, vz, len; + if (cdir.y || cdir.z) { + ux = 0; + uy = cdir.z; + uz = -cdir.y; + len = scale / sqrt (uy*uy + uz*uz); + uy *= len; + uz *= len; + vx = cdir.y*cdir.y + cdir.z*cdir.z; + vy = -cdir.x*cdir.y; + vz = -cdir.x*cdir.z; + len = scale / sqrt(vx*vx + vy*vy + vz*vz); + vx *= len; + vy *= len; + vz *= len; + } else { + ux = 0; + uy = scale; + uz = 0; + vx = 0; + vy = 0; + vz = scale; + } + vtx[0].x = (float)(ppos.x-ux-vx); + vtx[0].y = (float)(ppos.y-uy-vy); + vtx[0].z = (float)(ppos.z-uz-vz); + vtx[1].x = (float)(ppos.x-ux+vx); + vtx[1].y = (float)(ppos.y-uy+vy); + vtx[1].z = (float)(ppos.z-uz+vz); + vtx[2].x = (float)(ppos.x+ux+vx); + vtx[2].y = (float)(ppos.y+uy+vy); + vtx[2].z = (float)(ppos.z+uz+vz); + vtx[3].x = (float)(ppos.x+ux-vx); + vtx[3].y = (float)(ppos.y+uy-vy); + vtx[3].z = (float)(ppos.z+uz-vz); +} + +void vkParticleStream::SetShadowCoords(const VECTOR3 &ppos, const VECTOR3 &cdir, double scale, VERTEX_XYZ_TEX *vtx) +{ + double ux, uy, uz, vx, vy, vz, len; + + if (cdir.y || cdir.z) { + ux = 0; + uy = cdir.z; + uz = -cdir.y; + len = scale / sqrt (uy*uy + uz*uz); + uy *= len; + uz *= len; + vx = cdir.y*cdir.y + cdir.z*cdir.z; + vy = -cdir.x*cdir.y; + vz = -cdir.x*cdir.z; + len = scale / sqrt(vx*vx + vy*vy + vz*vz); + vx *= len; + vy *= len; + vz *= len; + } + else { + ux = 0; + uy = scale; + uz = 0; + vx = 0; + vy = 0; + vz = scale; + } + vtx[0].x = (float)(ppos.x-ux-vx); + vtx[0].y = (float)(ppos.y-uy-vy); + vtx[0].z = (float)(ppos.z-uz-vz); + vtx[1].x = (float)(ppos.x-ux+vx); + vtx[1].y = (float)(ppos.y-uy+vy); + vtx[1].z = (float)(ppos.z-uz+vz); + vtx[2].x = (float)(ppos.x+ux+vx); + vtx[2].y = (float)(ppos.y+uy+vy); + vtx[2].z = (float)(ppos.z+uz+vz); + vtx[3].x = (float)(ppos.x+ux-vx); + vtx[3].y = (float)(ppos.y+uy-vy); + vtx[3].z = (float)(ppos.z+uz-vz); +} + +void vkParticleStream::CalcNormals(const VECTOR3 &ppos, NTVERTEX *vtx) +{ + VECTOR3 cdir = unit (ppos); + double ux, uy, uz, vx, vy, vz, len; + if (cdir.y || cdir.z) { + ux = 0; + uy = cdir.z; + uz = -cdir.y; + len = 3.0 / sqrt (uy*uy + uz*uz); + uy *= len; + uz *= len; + vx = cdir.y*cdir.y + cdir.z*cdir.z; + vy = -cdir.x*cdir.y; + vz = -cdir.x*cdir.z; + len = 3.0 / sqrt(vx*vx + vy*vy + vz*vz); + vx *= len; + vy *= len; + vz *= len; + } + else { + ux = 0; + uy = 1.0; + uz = 0; + vx = 0; + vy = 0; + vz = 1.0; + } + static float scale = (float)(1.0/sqrt(19.0)); + vtx[0].nx = scale*(float)(-cdir.x-ux-vx); + vtx[0].ny = scale*(float)(-cdir.y-uy-vy); + vtx[0].nz = scale*(float)(-cdir.z-uz-vz); + vtx[1].nx = scale*(float)(-cdir.x-ux+vx); + vtx[1].ny = scale*(float)(-cdir.y-uy+vy); + vtx[1].nz = scale*(float)(-cdir.z-uz+vz); + vtx[2].nx = scale*(float)(-cdir.x+ux+vx); + vtx[2].ny = scale*(float)(-cdir.y+uy+vy); + vtx[2].nz = scale*(float)(-cdir.z+uz+vz); + vtx[3].nx = scale*(float)(-cdir.x+ux-vx); + vtx[3].ny = scale*(float)(-cdir.y+uy-vy); + vtx[3].nz = scale*(float)(-cdir.z+uz-vz); +} + +void vkParticleStream::Render(LPDIRECT3DDEVICE9 dev) +{ + if (!pfirst) return; + if (diffuse) RenderDiffuse(dev); + else RenderEmissive(dev); +} + +void vkParticleStream::RenderDiffuse(LPDIRECT3DDEVICE9 dev) +{ + static D3DMATERIAL9 smokemat = { // emissive material for engine exhaust + {1,1,1,1}, + {0,0,0,1}, + {0,0,0,1}, + {0.2f,0.2f,0.2f,1}, + 0.0 + }; + UINT numPasses=0; + ParticleSpec *p; + int i0, j, n, stride = np/16+1; + float *u, *v; + NTVERTEX *vtx; + + VECTOR3 camera_gpos = pGC->GetScene()->GetCameraGPos(); + + CalcNormals(plast->pos - camera_gpos, dvtx); + + HR(dev->SetVertexDeclaration(pNTVertexDecl)); + HR(FX->SetTechnique(eDiffuseTech)); + HR(FX->SetMatrix(eW, _DX(mWorld))); + + if (tex) HR(FX->SetTexture(eTex0, SURFACE(tex)->GetTexture())); + + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + + for (p = pfirst, vtx = dvtx, n = i0 = 0; p; p = p->next) { + + SetDParticleCoords(p->pos - camera_gpos, p->size, vtx); + + u = tu + p->texidx; + v = tv + p->texidx; + + for (j = 0; j < 4; j++, vtx++) { + vtx->nx = dvtx[j].nx; + vtx->ny = dvtx[j].ny; + vtx->nz = dvtx[j].nz; + vtx->tu = u[j]; + vtx->tv = v[j]; + } + + if (++n == stride || n+i0 == np) { + float alpha = (float)max (0.1, p->alpha0*(1.0-(oapiGetSimTime()-p->t0)*ipht2)); + HR(FX->SetFloat(eMix, alpha)); + HR(FX->CommitChanges()); + HR(dev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, n*4, n*2, idx, D3DFMT_INDEX16, dvtx+i0*4, sizeof(NTVERTEX))); + i0 += n; + n = 0; + } + } + + HR(FX->EndPass()); + HR(FX->End()); +} + + +void vkParticleStream::RenderEmissive(LPDIRECT3DDEVICE9 dev) +{ + static D3DMATERIAL9 smokemat = { // emissive material for engine exhaust + {0,0,0,1}, + {0,0,0,1}, + {0,0,0,1}, + {1,1,1,1}, + 0.0 + }; + UINT numPasses=0; + ParticleSpec *p = NULL; + int i0, j, n; + float *u, *v; + VERTEX_XYZ_TEX *vtx; + + VECTOR3 camera_gpos = pGC->GetScene()->GetCameraGPos(); + + HR(dev->SetVertexDeclaration(pPosTexDecl)); + HR(FX->SetTechnique(eEmissiveTech)); + HR(FX->SetMatrix(eW, _DX(mWorld))); + + if (tex) HR(FX->SetTexture(eTex0, SURFACE(tex)->GetTexture())); + + D3DCOLORVALUE color; + SetMaterial(color); + + HR(FX->SetValue(eColor, &color, sizeof(D3DCOLORVALUE))); + + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + + for (p = pfirst, vtx = evtx, n = i0 = 0; p; p = p->next) { + + SetEParticleCoords(p->pos - camera_gpos, p->size, vtx); + + u = tu + p->texidx; + v = tv + p->texidx; + for (j = 0; j < 4; j++, vtx++) { + vtx->tu = u[j]; + vtx->tv = v[j]; + } + + if (++n == stride || n+i0 == np) { + + float alpha = (float)max (0.1, p->alpha0*(1.0-(oapiGetSimTime()-p->t0)*ipht2)); + HR(FX->SetFloat(eMix, alpha)); + HR(FX->CommitChanges()); + HR(dev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, n*4, n*2, idx, D3DFMT_INDEX16, evtx+i0*4, sizeof(VERTEX_XYZ_TEX))); + i0 += n; + n = 0; + } + } + + HR(FX->EndPass()); + HR(FX->End()); +} + + + + + + + + + + + + + +// ======================================================================= + +ExhaustStream::ExhaustStream (oapi::GraphicsClient *_gc, OBJHANDLE hV, + const double *srclevel, const VECTOR3 *thref, const VECTOR3 *thdir, + PARTICLESTREAMSPEC *pss) +: vkParticleStream (_gc, pss) +{ + Attach (hV, thref, thdir, srclevel); + hPlanet = 0; +} + +ExhaustStream::ExhaustStream (oapi::GraphicsClient *_gc, OBJHANDLE hV, + const double *srclevel, const VECTOR3 &ref, const VECTOR3 &_dir, + PARTICLESTREAMSPEC *pss) +: vkParticleStream (_gc, pss) +{ + Attach (hV, ref, _dir, srclevel); + hPlanet = 0; +} + +void ExhaustStream::Update () +{ + vkParticleStream::Update(); + + double simt = oapiGetSimTime(); + double dt = oapiGetSimStep(); + double alpha0; + + VESSEL *vessel = (hRef ? oapiGetVesselInterface (hRef) : 0); + + if (np) { + ParticleSpec *p; + double lng, lat, r1, r2, rad, pref, slow; + int i; + if (vessel) hPlanet = vessel->GetSurfaceRef(); + if (hPlanet) { + VECTOR3 pp; + oapiGetGlobalPos (hPlanet, &pp); + rad = oapiGetSize (hPlanet); + VECTOR3 dv = pp-plast->pos; // gravitational dv + double d = length (dv); + dv *= GGRAV * oapiGetMass(hPlanet)/(d*d*d) * dt; + + ATMPARAM prm; + oapiGetPlanetAtmParams (hPlanet, d, &prm); + if (prm.rho) { + pref = sqrt(prm.rho) / 1.1371; + slow = exp(-beta*pref*dt); + dv *= exp(-prm.rho*2.0); // reduce gravitational effect in atmosphere (buoyancy) + } else { + // pref = 0.0; + slow = 1.0; + } + oapiGlobalToEqu (hPlanet, pfirst->pos, &lng, &lat, &r1); + VECTOR3 av1 = oapiGetWindVector (hPlanet, lng, lat, r1-rad, 3); + oapiGlobalToEqu (hPlanet, plast->pos, &lng, &lat, &r2); + VECTOR3 av2 = oapiGetWindVector (hPlanet, lng, lat, r2-rad, 3); + VECTOR3 dav = (av2-av1)/np; + double r = oapiGetSize (hPlanet); + if (vessel) r += vessel->GetSurfaceElevation(); + + for (p = pfirst, i = 0; p; p = p->next, i++) { + p->vel += dv; + VECTOR3 av = dav*i + av1; // atmosphere velocity + VECTOR3 vv = p->vel-av; // velocity difference + p->vel = vv*slow + av; + p->size += alpha * dt; + + VECTOR3 s (p->pos - pp); + if (length(s) < r) { + VECTOR3 dp = s * (r/length(s)-1.0); + p->pos += dp; + + static double dv_scale = length(vv)*0.2; + VECTOR3 dv = {((double)rand()/(double)RAND_MAX-0.5)*dv_scale, + ((double)rand()/(double)RAND_MAX-0.5)*dv_scale, + ((double)rand()/(double)RAND_MAX-0.5)*dv_scale}; + dv += vv; + + normalise(s); + VECTOR3 vv2 = dv - s*dotp(s,dv); + if (length(vv2)) vv2 *= 0.5*length(vv)/length(vv2); + vv2 += s*(((double)rand()/(double)RAND_MAX)*dv_scale); + p->vel = vv2*1.0/*2.0*/+av; + double r = (double)rand()/(double)RAND_MAX; + p->pos += (vv2-vv) * dt * r; + //p->size *= (1.0+r); + } + } + } + } + + if (level && *level > 0 && vessel && (alpha0 = Level2Alpha(*level) * Atm2Alpha (vessel->GetAtmDensity())) > 0.01) { + if (simt > t0+interval) { + VECTOR3 vp, vv; + MATRIX3 vR; + vessel->GetRotationMatrix (vR); + vessel->GetGlobalPos (vp); + vessel->GetGlobalVel (vv); + VECTOR3 vr = mul (vR, *dir) * (-speed); + while (simt > t0+interval) { + // create new particle + double dt = simt-t0-interval; + double dv_scale = speed*vrand; // exhaust velocity randomisation + VECTOR3 dv = {((double)rand()/(double)RAND_MAX-0.5)*dv_scale, + ((double)rand()/(double)RAND_MAX-0.5)*dv_scale, + ((double)rand()/(double)RAND_MAX-0.5)*dv_scale}; + ParticleSpec *p = CreateParticle (mul (vR, *pos) + vp + (vr+dv)*dt, + vv + vr+dv, size0, alpha0); + p->size += alpha * dt; + + if (diffuse && hPlanet && bShadows) { // check for shadow render + double lng, lat, alt; + static const double eps = 1e-2; + oapiGlobalToEqu (hPlanet, p->pos, &lng, &lat, &alt); + //planet->GlobalToEquatorial (MakeVector(p->pos), lng, lat, alt); + alt -= oapiGetSize(hPlanet); + if (vessel) alt -= vessel->GetSurfaceElevation(); + if (alt*eps < vessel->GetSize()) p->flag |= 1; // render shadow + } + + // determine next interval (pretty hacky) + t0 += interval; + if (speed > 10) { + interval = max (0.015, size0 / (pdensity * (0.1*vessel->GetAirspeed() + size0))); + } else { + interval = 1.0/pdensity; + } + interval *= (double)rand()/(double)RAND_MAX + 0.5; + } + } + } else t0 = simt; + +} + + +void ExhaustStream::RenderGroundShadow (LPDIRECT3DDEVICE9 dev, LPDIRECT3DTEXTURE9 &prevtex) +{ + if (!diffuse || !hPlanet || !pfirst) return; + if (Config->TerrainShadowing == 0) return; + + ParticleSpec *p = pfirst; + + VESSEL *vessel = (hRef ? oapiGetVesselInterface (hRef) : 0); + + double R; + float *u, *v, alpha; + int n, j, i0; + VECTOR3 sd, hn; + + VERTEX_XYZ_TEX *vtx; + + VECTOR3 pp,gcam; + oapiGetGlobalPos (hPlanet, &pp); + gcam = pGC->GetScene()->GetCameraGPos(); + + R = oapiGetSize(hPlanet); + if (vessel) R += vessel->GetSurfaceElevation(); + sd = unit(p->pos); // shadow projection direction + VECTOR3 pv0 = p->pos - pp; // rel. particle position + // calculate the intersection of the vessel's shadow with the planet surface + double fac1 = dotp (sd, pv0); + if (fac1 > 0.0) return; // shadow doesn't intersect planet surface + double arg = fac1*fac1 - (dotp (pv0, pv0) - R*R); + if (arg <= 0.0) return; // shadow doesn't intersect with planet surface + double a = -fac1 - sqrt(arg); + VECTOR3 shp = sd*a; // projection point in global frame + hn = unit (shp + pv0); // horizon normal in global frame + + HR(dev->SetVertexDeclaration(pPosTexDecl)); + HR(FX->SetTechnique(eEmissiveTech)); + HR(FX->SetMatrix(eW, _DX(mWorld))); + + if (tex) FX->SetTexture(eTex0, SURFACE(tex)->GetTexture()); + + UINT numPasses = 0; + + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(1)); + + for (p = pfirst, vtx = evtx, n = i0 = 0; p; p = p->next) { + + if (!(p->flag & 1)) continue; + + VECTOR3 pvr = p->pos - pp; // rel. particle position + + // calculate the intersection of the vessel's shadow with the planet surface + double fac1 = dotp (sd, pvr); + if (fac1 > 0.0) break; // shadow doesn't intersect planet surface + double arg = fac1*fac1 - (dotp (pvr, pvr) - R*R); + if (arg <= 0.0) break; // shadow doesn't intersect with planet surface + double a = -fac1 - sqrt(arg); + + SetShadowCoords (p->pos - gcam + sd*a, -hn, p->size, vtx); + + u = tu + p->texidx; + v = tv + p->texidx; + for (j = 0; j < 4; j++, vtx++) { + vtx->tu = u[j]; + vtx->tv = v[j]; + } + if (++n == stride || n+i0 == np) { + alpha = (float)max (0.1, 0.60 * p->alpha0*(1.0-(oapiGetSimTime()-p->t0)*ipht2)); + if (alpha>0.01f) { + HR(FX->SetFloat(eMix, alpha)); + HR(FX->CommitChanges()); + HR(dev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, n*4, n*2, idx, D3DFMT_INDEX16, evtx+i0*4, sizeof(VERTEX_XYZ_TEX))); + } + i0 += n; + n = 0; + } + } + + HR(FX->EndPass()); + HR(FX->End()); +} + + +// ======================================================================= + +ReentryStream::ReentryStream (oapi::GraphicsClient *_gc, OBJHANDLE hV, PARTICLESTREAMSPEC *pss) +: vkParticleStream (_gc, pss) +{ + llevel = 1.0; + Attach (hV, _V(0,0,0), _V(0,0,0), &llevel); + hPlanet = 0; +} + +void ReentryStream::SetMaterial (D3DCOLORVALUE &col) +{ + // should be heating-dependent + col.r = 1.0f; + col.g = 0.7f; + col.b = 0.5f; +} + +void ReentryStream::Update () +{ + vkParticleStream::Update (); + VESSEL *vessel = (hRef ? oapiGetVesselInterface (hRef) : 0); + + double simt = oapiGetSimTime(); + double simdt = oapiGetSimStep(); + double friction = vessel + ? 0.5 * pow(vessel->GetAtmDensity(), 0.6) + * pow(vessel->GetAirspeed() , 3 ) + : 0.0; + double alpha0; + + if (np) { + ParticleSpec *p; + double lng, lat, r1, r2, rad; + int i; + if (vessel) hPlanet = vessel->GetSurfaceRef(); + if (hPlanet) { + rad = oapiGetSize (hPlanet); + oapiGlobalToEqu (hPlanet, pfirst->pos, &lng, &lat, &r1); + VECTOR3 av1 = oapiGetWindVector (hPlanet, lng, lat, r1-rad, 3); + oapiGlobalToEqu (hPlanet, plast->pos, &lng, &lat, &r2); + VECTOR3 av2 = oapiGetWindVector (hPlanet, lng, lat, r2-rad, 3); + VECTOR3 dav = (av2-av1)/np; + // double r = oapiGetSize (hPlanet); + + for (p = pfirst, i = 0; p; p = p->next, i++) { + VECTOR3 av = dav*i + av1; + VECTOR3 vv = p->vel-av; + double slow = exp(-beta*simdt); + p->vel = vv*slow + av; + p->size += alpha * simdt; + } + } + } + + if (friction > 0 && (alpha0 = Atm2Alpha (friction)) > 0.01) { + if (simt > t0+interval) { + VECTOR3 vp, vv, av; + vessel->GetGlobalPos (vp); + vessel->GetGlobalVel (vv); + + if (hPlanet) { + double lng, lat, r, rad; + rad = oapiGetSize (hPlanet); + oapiGlobalToEqu (hPlanet, vp, &lng, &lat, &r); + av = oapiGetWindVector (hPlanet, lng, lat, r-rad, 3); + } else + av = vv; + + while (simt > t0+interval) { + // create new particle + double dt = simt-t0-interval; + double ebt = exp(-beta*dt); + double dv_scale = vessel->GetAirspeed()*vrand; // exhaust velocity randomisation + VECTOR3 dv = {((double)rand()/(double)RAND_MAX-0.5)*dv_scale, + ((double)rand()/(double)RAND_MAX-0.5)*dv_scale, + ((double)rand()/(double)RAND_MAX-0.5)*dv_scale}; + VECTOR3 dx = (vv-av) * (1.0-ebt)/beta + av*dt; + CreateParticle (vp + dx - vv*dt, (vv+dv-av)*ebt + av, size0, alpha0); + // determine next interval + t0 += interval; + interval = max (0.015, size0 / (pdensity * (0.1*vessel->GetAirspeed() + size0))); + interval *= (double)rand()/(double)RAND_MAX + 0.5; + } + } + } else t0 = simt; +} diff --git a/OVP/VulkanClient/Particle.h b/OVP/VulkanClient/Particle.h new file mode 100644 index 000000000..19eb5cfd7 --- /dev/null +++ b/OVP/VulkanClient/Particle.h @@ -0,0 +1,159 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2025 Jarmo Nikkanen +// ============================================================== + +#ifndef __PARTICLE_H +#define __PARTICLE_H + +#include "Effect.h" +#include "Client.h" +#include "Util.h" + +#define MAXPARTICLE 3000 + +struct ParticleSpec { + VECTOR3 pos, vel; + double size; + double alpha0; // alpha value at creation + double t0; + int texidx; + DWORD flag; + ParticleSpec *prev, *next; +}; + +class vkParticleStream : public oapi::ParticleStream, public vkEffect +{ + +public: + /** + * \brief Constructs a new particle stream object + * \param _gc pointer to graphics client + * \param pss particle stream parameters + */ + vkParticleStream (oapi::GraphicsClient *_gc, PARTICLESTREAMSPEC *pss = 0); + + /** + * \brief Destroys the particle stream object + */ + virtual ~vkParticleStream(); + + /** + * \brief Set up global parameters shared by all instances + * \param gclient pointer to graphics client + */ + static void GlobalInit(oapi::vkClient *gclient); + + /** + * \brief Release global parameters + */ + static void GlobalExit(); + + void SetObserverRef (const VECTOR3 *cam); + void SetSourceRef (const VECTOR3 *src); + void SetSourceOffset (const VECTOR3 &ofs); + void SetIntensityLevelRef (double *lvl); + + //void Activate (bool _active) { active = _active; } + // activate/deactivate the particle source + + bool IsActive() const { return (pfirst!=NULL); } + + void Timejump (); + // register a discontinuity + + bool Expired () const { return !level && !np; } + // stream is dead + + ParticleSpec *CreateParticle (const VECTOR3 &pos, const VECTOR3 &vel, double size, double alpha); + + void DeleteParticle (ParticleSpec *p); + virtual void Update (); + + void Render(LPDIRECT3DDEVICE9 dev); + //void Render(LPDIRECT3DDEVICE9 dev, LPDIRECT3DTEXTURE9 &prevtex); + + virtual void RenderGroundShadow (LPDIRECT3DDEVICE9 dev, LPDIRECT3DTEXTURE9 &prevtex) {} + + + ParticleSpec * GetPlast() const { return plast; } + +protected: + + void SetSpecs (PARTICLESTREAMSPEC *pss); + void SetParticleHalflife (double pht); + double Level2Alpha (double level) const; // map a level (0..1) to alpha (0..1) for given mapping + double Atm2Alpha (double prm) const; // map atmospheric parameter (e.g. density) to alpha (0..1) for given mapping + void SetDParticleCoords(const VECTOR3 &ppos, double scale, NTVERTEX *vtx); + void SetEParticleCoords(const VECTOR3 &ppos, double scale, VERTEX_XYZ_TEX *vtx); + void SetShadowCoords(const VECTOR3 &ppos, const VECTOR3 &cdir, double scale, VERTEX_XYZ_TEX *vtx); + void CalcNormals(const VECTOR3 &ppos, NTVERTEX *vtx); + virtual void SetMaterial (D3DCOLORVALUE &col) { col.r = col.g = col.b = 1; } + void RenderDiffuse (LPDIRECT3DDEVICE9 dev); + void RenderEmissive (LPDIRECT3DDEVICE9 dev); + //const VECTOR3 *cam_ref; + //const VECTOR3 *src_ref; + //VECTOR3 src_ofs; + double interval; + double exp_rate; + double pdensity; + double speed; // emission velocity + double vrand; // velocity randomisation + double alpha; // particle growth rate + double beta; // atmospheric slowdown rate + double size0; // particle base size at creation + double t0; // time of last particle created + //bool active; // source emitting particles? + bool diffuse; // particles have diffuse component (need normals) + + PARTICLESTREAMSPEC::LEVELMAP lmap; // level mapping method + double lmin, lmax; // used for level mapping + PARTICLESTREAMSPEC::ATMSMAP amap; // atmosphere mapping method + double amin, afac; // used for atmosphere mapping + + ParticleSpec *pfirst, *plast; + int np; // number of current particles + int stride; // number of particles rendered simultaneously + FMATRIX4 mWorld; // ground shadow related matrix + SURFHANDLE tex; // particle texture + double ipht2; + +protected: + oapi::vkClient *pGC; // pointer to graphics client + static SURFHANDLE deftex; // default particle texture + static SURFHANDLE deftexems; // default particle texture + static bool bShadows; // render particle shadows +}; + +class ExhaustStream: public vkParticleStream { +public: + ExhaustStream (oapi::GraphicsClient *_gc, OBJHANDLE hV, + const double *srclevel, const VECTOR3 *thref, const VECTOR3 *thdir, + PARTICLESTREAMSPEC *pss = 0); + ExhaustStream (oapi::GraphicsClient *_gc, OBJHANDLE hV, + const double *srclevel, const VECTOR3 &ref, const VECTOR3 &_dir, + PARTICLESTREAMSPEC *pss = 0); + void RenderGroundShadow (LPDIRECT3DDEVICE9 dev, LPDIRECT3DTEXTURE9 &prevtex); + void Update (); + +private: + OBJHANDLE hPlanet; +}; + +class ReentryStream: public vkParticleStream { +public: + ReentryStream (oapi::GraphicsClient *_gc, OBJHANDLE hV, + PARTICLESTREAMSPEC *pss = 0); + void Update (); + +protected: + void SetMaterial (D3DCOLORVALUE &col); + +private: + OBJHANDLE hPlanet; + double llevel; +}; + +#endif // !__PARTICLE_H diff --git a/OVP/VulkanClient/Qtree.h b/OVP/VulkanClient/Qtree.h new file mode 100644 index 000000000..be76e1470 --- /dev/null +++ b/OVP/VulkanClient/Qtree.h @@ -0,0 +1,156 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// ============================================================== +// qtree.h +// Quad-tree framework for planet render engines +// ============================================================== + +#ifndef __QTREE_H +#define __QTREE_H + +template +class QuadTreeNode { +public: + QuadTreeNode (QuadTreeNode *_parent = NULL, T *_entry = NULL); + + ~QuadTreeNode (); + // Deleting a node + + T *Entry() const { return entry; } + //const T *Entry() const { return entry; } + // Returns the node contents + + inline void SetEntry (T *newentry) { entry = newentry; entry->SetNode (this); } + // Transfers ownership of the entry to the tree + + inline QuadTreeNode *Parent() const { return parent; } + // Returns the node's parent, or 0 if node is root + + QuadTreeNode *Ancestor(int dlvl); + // Returns an ancestor node from the tree. dlvl specifies how many nodes to step back + // Ancestor(1) is equivalent to Parent() + // If the ancestor does not exist (e.g. trying to step beyond tree root), returns 0 + + inline QuadTreeNode *Child (int idx) { _ASSERT(idx < 4); return child[idx]; } + inline const QuadTreeNode *Child (int idx) const { _ASSERT(idx < 4); return child[idx]; } + // Returns the node's idx-th child (0<=idx<4), or 0 if child doesn't exist + + QuadTreeNode *AddChild (int idx, T *childentry); + // If child idx already exists, it and its entire subtree are deleted + + bool DelChild (int idx); + // Delete a child and its subtree. False indicates that a node in the subtree + // was locked and not the entire subtree could be deleted. + + bool DelChildren (); + // Delete all children and their subtrees. False indicates that a node in the subtree + // was locked and not the entire subtree could be deleted. + + void DelAbove(int lvl); + +private: + T *entry; + QuadTreeNode *parent; + QuadTreeNode *child[4]; +}; + +template +QuadTreeNode::QuadTreeNode (QuadTreeNode *_parent, T *_entry): +parent(_parent), entry(_entry) +{ + for (int i = 0; i < 4; ++i) { + child[i] = NULL; + } + if (entry) { + entry->SetNode (this); + } +} + +template +QuadTreeNode::~QuadTreeNode () +{ + if (parent) { parent = NULL; } + for (int i = 0; i < 4; ++i) { + if (child[i]) { + delete child[i]; + } + } + if (entry) { + delete entry; + } +} + +template +QuadTreeNode *QuadTreeNode::Ancestor(int dlvl) +{ + QuadTreeNode *ancestor = this; + for (int i = 0; i < dlvl && ancestor; ++i) { + ancestor = ancestor->parent; + } + return ancestor; +} + +template +QuadTreeNode *QuadTreeNode::AddChild (int idx, T *childentry) +{ + _ASSERT(idx < 4); + if (child[idx]) { + delete child[idx]; + } + child[idx] = new QuadTreeNode (this, childentry); + return child[idx]; +} + +template +bool QuadTreeNode::DelChild (int idx) +{ + _ASSERT(idx < 4); + bool ok = true; + if (child[idx]) { + if (child[idx]->DelChildren() && child[idx]->entry->PreDelete()) { + delete child[idx]; + child[idx] = NULL; + } else { + ok = false; + } + } + return ok; +} + +template +bool QuadTreeNode::DelChildren () +{ + // recursively delete the child trees extending from the node + bool ok = true; + for (int i = 0; i < 4; ++i) { + if (child[i]) { + if (child[i]->DelChildren() && child[i]->Entry()->PreDelete()) { + delete child[i]; + child[i] = NULL; + } else { + ok = false; + } + } + } + return ok; +} + +template +void QuadTreeNode::DelAbove(int lvl) +{ + for (auto c : child) { + if (c) { + if (c->entry && c->entry->Level() >= lvl) { + c->DelChildren(); + continue; + } + c->DelAbove(lvl); + } + } +} + +#endif // !__QTREE_H diff --git a/OVP/VulkanClient/README.txt b/OVP/VulkanClient/README.txt new file mode 100644 index 000000000..0b17b83e5 --- /dev/null +++ b/OVP/VulkanClient/README.txt @@ -0,0 +1,150 @@ +ORBITER VISUALISATION PROJECT - +D3D9 Client Build Instructions + +This Document describes the steps needed to build D3D9Client from the sources. +This document uses Visual Studio 2015 (Community Edition) as the main compiler, +but it should be very similar with Visual Studio 2017. As new versions will +appear (Visual Studio 2019) and time goes by there might be some difference in +the procedures. +In case your current Visual Studio versions needs some changed procedures, +please notify the developers at the orbiter-forums "D3D9Client Development" +thread (https://www.orbiter-forum.com/showthread.php?t=18431). + + +1. Required components +---------------------- + +To be able to compile D3D9 graphics client from the sources, you need: + +* SVN (Subversion / TortoiseSVN) + Many scripts (located in Utils/D3D9Client) will use 'svn' command to get + their job done[1]. So the easiest way is to have TortoiseSVN installed. + Make sure you enable the "command line client tools" when running the setup. + TortoiseSVN can be downloaded from + https://tortoisesvn.net/ + + [1] Strictly speaking, subversion is not *absolutely* needed to build the + client; and getting the needed headers, libraries and resources can be + done "by hand" as well. In this case the required files must be obtained + from Orbiter 2016 (ZIP package) rsp. the Orbiter BETA install. + +* The fitting Orbiter libraries and headers. + As a minimal build environment you only need the files from + - Orbitersdk/include + - Orbitersdk/lib + - Orbitersdk/resources (Orbiter 2016) + rsp. + Orbitersdk/VS2015 (Orbiter BETA) + + The easiest way to get these directories is to run the get_orbiter_libs.bat + script from Utils/D3D9Client. + + Another option is just to install the complete Orbiter installation, which + will also have the benefits of being able to instantly debug the client. + +* A Windows C++ compiler (Visual Studio 2015 Community Edition is ok, later + versions should be fine, clang should also work, other compilers may or may + not work). + +* For a DirectX graphic client, you obviously also need the respective 3-D + graphics SDKs. The D3D9 client builds with the MS DirectX June 2010 + SDK. + You need the DirectX Software Development Kit (Version 9.29.1962 or newer) to + build the client. It can be downloaded at Microsoft Download Center at + https://www.microsoft.com/en-us/download/details.aspx?id=6812 + + If you have an existing Microsoft Visual C++ 2010 Redistributable installed + on your computer, you may receive an "S1023" error when you install the June + 2010 DirectX SDK. + To resolve this issue follow the instructions from + https://support.microsoft.com/en-us/help/2728613/s1023-error-when-you-install-the-directx-sdk-june-2010 + + To run the client later on you also need the DirectX End-User Runtimes (June + 2010 or newer). These can be downloaded at Microsoft Download Center at + https://www.microsoft.com/en-us/download/details.aspx?id=8109 + (See http://users.kymp.net/~p501474a/D3D9Client/#DirectX%20Runtimes for + details on that). + +* OPTIONAL (nVIDIA) + NVIDIA API (needed since stereoscopic option was introduced in the Client) + The NVAPI can be downloaded from the nVIDIA developer site at + https://developer.nvidia.com/nvapi + + If you don't like to install the NVIDIA API (it's not needed if stereoscopic + option should not be available in the client), you do not need to do + anything. + During the build process (pre compiling) a dummy header "nvapi.h" will be + generated into the main source directory (Orbitersdk/D3D9Client) + automatically if it does not yet exist. + This also means that if you *have* installed the NVIDIA API and copied the + header file into the main source directory (Orbitersdk/D3D9Client) it will + *not* be touched or overwritten by any further build process. + +* OPTIONAL (Documentation) + To recompile the D3D9 source documentation you need Doxygen from + http://www.stack.nl/~dimitri/doxygen/ + + There is a doxygen configuration file in D3D9Client/doc/Doxyfile which + can be used to generate the compressed html help file + Orbitersdk/D3D9Client/doc/D3D9Client.chm included in the repository. + Additionally to doxygen itself some other packets might be needed (depending + on the output format you would like to generate) to create the 'chm' file + you need: + - Microsoft HTML Help Workshop (compiler for .CHM files) + http://go.microsoft.com/fwlink/?LinkId=14188 + - Graphviz (for the 'dot' tool to generate graphs) + http://www.graphviz.org/ + +* OPTIONAL (7-Zip) + In order to create release-packages (ZIPs) a convenient build_release.bat + script is used. If you like to be able to run that script without any errors + you also need 7-Zip to be installed: + https://www.7-zip.org/ + + The build_release.bat script expects the executable to be at + "C:\Program Files\7-Zip\7z.exe", so to minimize any issues with that you + should keep that (default) location. + + +2. Building the D3D9 client +--------------------------- + +To compile the sources of the D3D9 graphics client, you have two options: + +* Via IDE + Just start the solution file that fits your compiler version. + - Orbitersdk\D3D9Client\D3D9ClientVS2010.sln (Visual Studio 2010) + - Orbitersdk\D3D9Client\D3D9ClientVS2012.sln (Visual Studio 2012) + - Orbitersdk\D3D9Client\D3D9ClientVS2015.sln (Visual Studio 2015) + - Orbitersdk\D3D9Client\D3D9ClientVS2017.sln (Visual Studio 2017) + + In case you have a more recent version of Visual Studio (like Visual Studio + 2019 for example), you should take the solution file with the highest number. + Visual Studio will usually be able to migrate the project- and solution-files + to the current version (The names however will not match your Visual Studio + version, though). + +* Via build_release.bat + Just run build_release.bat from the folder Utils/D3D9Client and the client + should be build without any further action required. + The script likes to creates two ZIP release-packages at the end of a + (successful) build, so to make that part work you need 7-Zip as well (see + Required components). + + +3. Testing the D3D9 client +-------------------------- + +* If the compilation of the D3D9 client was successful, it has created a + plugin in Modules/Plugin/D3D9Client.dll. + +* Run the "no-graphics" version of orbiter (orbiter_ng) and activate + the D3D9Client plugin from the Modules tab. + +* You should now get a video tab in the Launchpad dialog. Configure your + graphics driver and screen options, then launch a scenario. + +* The Visual Studio project files should all be able to just run the Debug- + Button. This however always starts with the "(Current state)" Scenario, so + you might need to start at least once via orbiter_ng.exe to setup a fitting + scenario to work with when debugging. diff --git a/OVP/VulkanClient/RingMgr.cpp b/OVP/VulkanClient/RingMgr.cpp new file mode 100644 index 000000000..cdbfb67f2 --- /dev/null +++ b/OVP/VulkanClient/RingMgr.cpp @@ -0,0 +1,198 @@ +// ============================================================== +// RingMgr.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007 - 2016 Martin Schweiger +// 2011 - 2106 Jarmo Nikkanen (vkClient modification) +// ============================================================== + +// ============================================================== +// class RingManager (implementation) +// ============================================================== + +#define D3D_OVERLOADS +#include "RingMgr.h" +#include "Catalog.h" + +using namespace oapi; + +void ReleaseTex(LPDIRECT3DTEXTURE9 pTex); + + + +RingManager::RingManager (const vPlanet *vplanet, double inner_rad, double outer_rad) +{ + vp = vplanet; + irad = inner_rad; + orad = outer_rad; + rres = (DWORD)-1; + tres = 0; + ntex = 0; + pTex = NULL; + + for (DWORD i = 0; i < MAXRINGRES; i++) { + mesh[i] = 0; + tex[i] = 0; + } +} + +RingManager::~RingManager () +{ + DWORD i; + for (i = 0; i < 3; i++) if (mesh[i]) delete mesh[i]; + for (i = 0; i < ntex; i++) ReleaseTex(tex[i]); + if (pTex) pTex->Release(); +} + +void RingManager::GlobalInit(vkClient *gclient) +{ + gc = gclient; +} + +void RingManager::SetMeshRes(DWORD res) +{ + if (res != rres) { + rres = res; + if (!mesh[res]) mesh[res] = CreateRing (irad, orad, 8+res*4); + if (!ntex) ntex = LoadTextures(); + tres = min (rres, ntex-1); + } +} + + +DWORD RingManager::LoadTextures () +{ + char fname[128] = { '\0' }; + char temp[128]; + char path[MAX_PATH] = { '\0' }; + + + oapiGetObjectName (vp->Object(), fname, ARRAYSIZE(fname)); + + LPDIRECT3DDEVICE9 pDev = gc->GetDevice(); + + const D3DCAPS9 *caps = gc->GetHardwareCaps(); + + int size = max(min((int)caps->MaxTextureWidth, 8192), 2048); + + sprintf_s(temp, ARRAYSIZE(temp), "%s_ring_%d.dds", fname, size); + if (gc->TexturePath(temp, path) && + D3DXCreateTextureFromFileExA(pDev, path, 0, 0, D3DFMT_FROM_FILE, 0, D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pTex) == S_OK) + { + LogAlw("High resolution ring texture loaded [%s]", path); + } + + // Fallback for old method + strcat_s(fname, ARRAYSIZE(fname), "_ring.tex"); + + return LoadPlanetTextures(fname, tex, 0, MAXRINGRES); +} + +bool RingManager::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &mWorld, bool front) +{ + MATRIX3 grot; + static FMATRIX4 imat; + FVECTOR3 q(mWorld.m11, mWorld.m21, mWorld.m31); + float scale = length(q); + + oapiGetRotationMatrix(vp->Object(), &grot); + + VECTOR3 gdir; oapiCameraGlobalDir(&gdir); + + VECTOR3 yaxis = mul(grot, _V(0,1,0)); + VECTOR3 xaxis = unit(crossp(gdir, yaxis)); + VECTOR3 zaxis = unit(crossp(xaxis, yaxis)); + + if (!front) { + xaxis = -xaxis; + zaxis = -zaxis; + } + + FVECTOR3 x(float(xaxis.x), float(xaxis.y), float(xaxis.z)); + FVECTOR3 y(float(yaxis.x), float(yaxis.y), float(yaxis.z)); + FVECTOR3 z(float(zaxis.x), float(zaxis.y), float(zaxis.z)); + + FMATRIX4 World = mWorld; + + x*=scale; y*=scale; z*=scale; + + D3DMAT_FromAxisT(&World, &x, &y, &z); + + float rad = float(vp->GetSize()); + + if (pTex) { + mesh[rres]->RenderRings2(&World, pTex, float(irad)*rad, float(orad)*rad); + } + else mesh[rres]->RenderRings(&World, tex[tres]); + return true; +} + +// ======================================================================= +// CreateRing +// Creates mesh for rendering planetary ring system. Creates a ring +// with nsect quadrilaterals. Smoothing the corners of the mesh is +// left to texture transparency. Nsect should be an even number. +// Disc is in xz-plane centered at origin facing up. Size is such that +// a ring of inner radius irad (>=1) and outer radius orad (>irad) +// can be rendered on it. + +vkMesh *RingManager::CreateRing(double irad, double orad, int nsect) +{ + int i, j; + + MESHGROUPEX *grp = new MESHGROUPEX; + + memset(grp,0,sizeof(MESHGROUPEX)); + + int count = nsect/2 + 1; + grp->nVtx = 2*count; + grp->nIdx = 6*(count-1); + grp->Idx = new WORD[grp->nIdx+12]; + grp->Vtx = new NTVERTEX[grp->nVtx+4]; + grp->TexIdx = 1; + + NTVERTEX *Vtx = grp->Vtx; + WORD *Idx = grp->Idx; + + double alpha = PI/(double)nsect; + float nrad = (float)(orad/cos(alpha)); // distance for outer nodes + float ir = (float)irad; + float fo = (float)(0.5*(1.0-orad/nrad)); + float fi = (float)(0.5*(1.0-irad/nrad)); + + for (i = j = 0; i < count; i++) { + double phi = i*2.0*alpha; + float cosp = (float)cos(phi), sinp = (float)sin(phi); + Vtx[i*2].x = nrad*cosp; Vtx[i*2+1].x = ir*cosp; + Vtx[i*2].z = nrad*sinp; Vtx[i*2+1].z = ir*sinp; + Vtx[i*2].y = Vtx[i*2+1].y = 0.0; + Vtx[i*2].nx = Vtx[i*2+1].nx = Vtx[i*2].nz = Vtx[i*2+1].nz = 0.0; + Vtx[i*2].ny = Vtx[i*2+1].ny = 1.0; + + if (!(i&1)) Vtx[i*2].tu = fo, Vtx[i*2+1].tu = fi; //fac; + else Vtx[i*2].tu = 1.0f-fo, Vtx[i*2+1].tu = 1.0f-fi; //1.0f-fac; + + //Vtx[i*2].tv = 0.05f, Vtx[i*2+1].tv = 1.00f; + Vtx[i*2].tv = 0.0f, Vtx[i*2+1].tv = 1.00f; + + if ((DWORD)j<=grp->nIdx-6) { + Idx[j++] = i*2; + Idx[j++] = i*2+1; + Idx[j++] = i*2+2; + Idx[j++] = i*2+3; + Idx[j++] = i*2+2; + Idx[j++] = i*2+1; + } + } + + MATERIAL mat = {{1,1,1,1},{0,0,0,1},{0,0,0,1},{0,0,0,0},20.0f}; + + vkMesh *msh = new vkMesh(grp, &mat, NULL); + + delete grp->Idx; + delete grp->Vtx; + delete grp; + return msh; +} + +oapi::vkClient *RingManager::gc = 0; diff --git a/OVP/VulkanClient/RingMgr.h b/OVP/VulkanClient/RingMgr.h new file mode 100644 index 000000000..726300380 --- /dev/null +++ b/OVP/VulkanClient/RingMgr.h @@ -0,0 +1,65 @@ +// ============================================================== +// RingMgr.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007-2016 Martin Schweiger +// ============================================================== + +#ifndef __RINGMGR_H +#define __RINGMGR_H + +#include "Client.h" +#include "VPlanet.h" +#include "Mesh.h" + +#define MAXRINGRES 3 + +// ============================================================== +// class RingManager (interface) +// ============================================================== +/** + * \brief Rendering of planet rings at different resolutions + */ +class RingManager { +public: + /** + * \brief Constructs a new ring manager object + * \param vPlanet planet instance pointer + * \param inner_rad inner radius of the ring (>=1) [unit planet radius] + * \param outer_rad outer radius of the ring (>inner_rad) [unit planet radius] + */ + RingManager (const vPlanet *vplanet, double inner_rad, double outer_rad); + + /** + * \brief Destroys the ring manager object + */ + ~RingManager (); + + /** + * \brief Set up global parameters shared by all instances + * \param gclient client instance pointer + */ + static void GlobalInit (oapi::vkClient *gclient); + + void SetMeshRes (DWORD res); + + inline double InnerRad() const { return irad; } + inline double OuterRad() const { return orad; } + + bool Render (LPDIRECT3DDEVICE9 dev, FMATRIX4 &mWorld, bool zenable); + +protected: + vkMesh *CreateRing (double irad, double orad, int nsect); + DWORD LoadTextures (); + +private: + static oapi::vkClient *gc; + const vPlanet *vp; + vkMesh *mesh[MAXRINGRES]; + LPDIRECT3DTEXTURE9 tex[MAXRINGRES]; + LPDIRECT3DTEXTURE9 pTex; + DWORD rres, tres, ntex; + double irad, orad; +}; + +#endif // !__RINGMGR_H diff --git a/OVP/VulkanClient/RunwayLights.cpp b/OVP/VulkanClient/RunwayLights.cpp new file mode 100644 index 000000000..06b8f895d --- /dev/null +++ b/OVP/VulkanClient/RunwayLights.cpp @@ -0,0 +1,1076 @@ +// ============================================================== +// RunwayLights.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) vk Client +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Émile "Bibi Uncle" Grégoire +// 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +// ============================================================== +// class RunwayLights +// Defines runway lights used in vBase. +// ============================================================== + +#include "RunwayLights.h" +#include "Scene.h" +#include "BeaconArray.h" +#include "Config.h" +#include "Util.h" +#include "vBase.h" +#include + +RunwayLights::RunwayLights(class vBase *_vB, const class Scene *scn) +{ + vB = _vB; + scene = scn; + end1 = _V(0, 0, 0); + end2 = _V(0, 0, 0); + width = 50.0; + td_disp = 0.0; + td_disp2 = 0.0; + td_length = 600.0; + apr_start = 900.0; + apr_length = 257.0; + iCategory = 0; + nPAPI = 0; + hObj = vB->GetObjHandle(); + nVASI = 0; + bSingleEnded = false; + bDisp2 = false; + + for (int i=0; i<12; ++i) { + PAPI_pos[i] = _V(0,0,0); + PAPI_disp[i] = 0.0; + PAPI_end[i] = 0; + papi[i] = NULL; + } + for (int i=0; i<2; ++i) { + VASI[i] = _V(0,0,0); + VASI_end[i] = 0; + vasi[i] = NULL; + } + + beacons1 = beacons2 = NULL; + currentTime = 0.0f; +} + +RunwayLights::~RunwayLights() +{ + SAFE_DELETE(beacons1); + SAFE_DELETE(beacons2); + for (int i=0;i<12;i++) SAFE_DELETE(papi[i]); + for (int i=0;i<2;i++) SAFE_DELETE(vasi[i]); + +} + +void RunwayLights::SetCategory(int cat) +{ + iCategory = cat; +} + +void RunwayLights::SetSignleEnded(bool bEnd) +{ + bSingleEnded = bEnd; +} + +void RunwayLights::SetEnd1(VECTOR3 pos) +{ + end1 = pos; +} + +void RunwayLights::SetEnd2(VECTOR3 pos) +{ + end2 = pos; +} + +void RunwayLights::SetWidth(double w) +{ + width = w; +} + +void RunwayLights::SetTouchZoneDisplacement(double disp) +{ + td_disp = disp; +} + +void RunwayLights::SetTouchZoneDisplacement2(double disp) +{ + bDisp2 = true; + td_disp2 = disp; +} + +void RunwayLights::SetTouchZoneLength(double disp) +{ + td_length = disp; +} + +void RunwayLights::SetDecisionDist(double dist) +{ + apr_length = dist; +} + +void RunwayLights::SetApproachStart(double dist) +{ + apr_start = dist; +} + +void RunwayLights::AddPAPI(VECTOR3 pos, float disp, DWORD end) +{ + if (nPAPI>=12) return; + PAPI_pos[nPAPI] = pos; + PAPI_disp[nPAPI] = disp; + PAPI_end[nPAPI] = end; + nPAPI++; +} + +void RunwayLights::AddVASI(VECTOR3 pos, DWORD end) +{ + if (nVASI>=2) return; + VASI[nVASI] = pos; + VASI_end[nVASI] = end; + nVASI++; +} + + +void RunwayLights::Init() +{ + _TRACE; + + beacons1 = BuildLights(end1, end2, td_disp); + + if (!bSingleEnded) { + if (bDisp2) beacons2 = BuildLights(end2, end1, td_disp2); + else beacons2 = BuildLights(end2, end1, td_disp); + } + + for (DWORD i=0;i59.0) iCategory = 2; + else iCategory = 1; + } + + // Not a critical. Must be higher than actual number of lights. Only used for memory allocation + int numLights = 2 * (numLightsEdge*4 + numLightsEnd*3 + numLightsTouch*3*2 + numLightsDecision*3*2 + numLightsApproach*5); // total lights + + float lightAngle = float(Config->RwyLightAngle); + float brightness = float(Config->RwyBrightness); + + // Main lights vectors + VECTOR3 _space; // Vector between each light + VECTOR3 _current; // Incremented in the for loop + VECTOR3 _shift; + VECTOR3 _widthDir = unit(crossp(_dir, _V(0, 1, 0))); // used to calculate the edge lights + + BeaconArrayEntry* beaconsEntry1 = new BeaconArrayEntry[numLights]; + + int i=0, k=0; + + DWORD red = 0xFFFF4444; + DWORD green = 0xFF00FF88; + DWORD white = 0xFFFFEECC; + DWORD yellow = 0xFFFFBB33; + + BeaconArrayEntry edgeLight, centerLight, endLight, beaconLight, papiLight; + + centerLight.angle = lightAngle; + centerLight.size = lightSize; + centerLight.lon = 0.0f; + centerLight.loff = 1.0f; + centerLight.bright = 1.5f * brightness; + centerLight.fall = 0.5; + centerLight.pos = _V(0,0,0); + centerLight.color = 0; + centerLight.dir = _dir*cos(upAngle*RAD) + _V(0, 1, 0)*sin(upAngle*RAD); + + endLight = centerLight; + edgeLight = centerLight; + edgeLight.size = lightSize*1.0f; + + beaconLight = centerLight; + beaconLight.size = lightSize*2.0f; + beaconLight.angle = min(180.0f, lightAngle * 4.0f); + beaconLight.bright = 4.0f * brightness; + + papiLight = centerLight; + papiLight.size = lightSize*3.0f; + papiLight.angle = min(180.0f, lightAngle * 4.0f); + papiLight.bright = 4.0f * brightness; + + + // end lights -------------------------------------- + + _space = _widthDir * width/(numLightsEnd-1); + _current = _end - _space*((numLightsEnd-1)/2); + + for(k=0;k610) beaconsEntry1[i].color = beaconsEntry1[i+1].color = white; + else beaconsEntry1[i].color = beaconsEntry1[i+1].color = yellow; + + // Center color + if(distanceBack<305) + { + beaconsEntry1[i+2].color = red; + beaconsEntry1[i+3].color = red; + } + + else if (distanceBack>305 && distanceBack<914) + { + beaconsEntry1[i+2].color = white; + beaconsEntry1[i+3].color = red; + } + + else + { + beaconsEntry1[i+2].color = white; + beaconsEntry1[i+3].color = white; + } + + _current -= _space; + } + + // touch zone lights --------------------------------- + + _space = _dir * spacing; + _current = _start + _space * double(numLightsTouch); + + if (iCategory==2) _shift = _widthDir * 17.0; + else _shift = _widthDir * floor(width/3.5); + + for(k=0; k<(numLightsTouch*6); k+=6,i+=6) + { + beaconsEntry1[i] = centerLight; + beaconsEntry1[i].color = white; + beaconsEntry1[i+5] = beaconsEntry1[i+4] = beaconsEntry1[i+3] = beaconsEntry1[i+2] = beaconsEntry1[i+1] = beaconsEntry1[i]; + + beaconsEntry1[i+0].pos = _current + _shift; + beaconsEntry1[i+1].pos = _current + _shift - (_widthDir*2.0); + beaconsEntry1[i+2].pos = _current + _shift - (_widthDir*4.0); + beaconsEntry1[i+3].pos = _current - _shift; + beaconsEntry1[i+4].pos = _current - _shift + (_widthDir*2.0); + beaconsEntry1[i+5].pos = _current - _shift + (_widthDir*4.0); + _current -= _space; + } + + // start lights -------------------------------------- + + _space = _widthDir * width / double(numLightsEnd-1); + + if (iCategory==2) { + _current = _start - _space*(double(numLightsEnd-1)/2.0) - _space * 3; + count = numLightsEnd+6; + } + else { + _current = _start - _space*(double(numLightsEnd-1)/2.0); + count = numLightsEnd; + } + + for(k=0; kRwyLightAnimate) { + beaconsEntry1[i].lon = (1.0f-float(k)*0.10f)-0.10f; + beaconsEntry1[i].loff = (1.0f-float(k)*0.10f); + } + else { + beaconsEntry1[i].lon = 0.0f; + beaconsEntry1[i].loff = 0.0f; + } + + beaconsEntry1[i].color = white; + beaconsEntry1[i].pos = _current; + + _current -= _space; + } + } + + // Snap to ground + for (int k=0;kRwyLightAngle) * 2.0f); + papiLight.size = 3.0f * lightSize; + papiLight.lon = 0.0f; + papiLight.loff = 1.0f; + papiLight.bright = 4.0f * float(Config->RwyBrightness); + papiLight.fall = 0.5; + papiLight.pos = _V(0,0,0); + papiLight.color = 0; + papiLight.dir = dir*cos(upAngle*RAD) + _V(0, 1, 0)*sin(upAngle*RAD); + + BeaconArrayEntry entryPAPI[4]; + + for(int j=0; j<4; j++) + { + entryPAPI[j] = papiLight; + entryPAPI[j].dir = _V(-entryPAPI[j].dir.x, entryPAPI[j].dir.y, -entryPAPI[j].dir.z); + entryPAPI[j].pos = start + dir*PAPI_pos[i].z + widthDir * double(disp) + widthDir * double(j*papi_separation) - widthDir * double(papi_separation*1.5); + } + + BeaconArray *beacons = new BeaconArray(entryPAPI, 4); + return beacons; +} + + +BeaconArray *RunwayLights::BuildVASI(VECTOR3 _start, VECTOR3 _end, DWORD idx) +{ + _TRACE; + const float lightSize = 4.0f; + const float upAngle = 12.0f; + + DWORD e = VASI_end[idx]; + + // Helping vectors + VECTOR3 _direction = _end - _start; // Vector of the runway + VECTOR3 _dir = _direction; // Normalized direction + normalise(_dir); + VECTOR3 _td_disp = _dir * td_disp; // Touch zone displacement vector + + _start += _td_disp; + _direction = _end - _start; + + // double len = length(_direction); // Length of the runway + // double limit = 59.0; + // float lightAngle = float(Config->RwyLightAngle); + // float brightness = float(Config->RwyBrightness); + + // Main lights vectors + VECTOR3 _current; // Incremented in the for loop + VECTOR3 _widthDir = crossp(_direction, _V(0, 1, 0)); // used to calculate the edge lights + normalise(_widthDir); + + BeaconArrayEntry* beaconsEntry1 = new BeaconArrayEntry[30]; + + int i=0, k=0; + + DWORD red = 0xFFFF4444; + DWORD white = 0xFFFFEECC; + + BeaconArrayEntry vasiLight; + + vasiLight.angle = min(180.0f, float(Config->RwyLightAngle) * 2.0f); + vasiLight.size = 1.5f * lightSize; + vasiLight.lon = 0.0f; + vasiLight.loff = 1.0f; + vasiLight.bright = 3.0f * float(Config->RwyBrightness); + vasiLight.fall = 0.1f; + vasiLight.pos = _V(0,0,0); + vasiLight.color = 0; + vasiLight.dir = _dir*cos(upAngle*RAD) + _V(0, 1, 0)*sin(upAngle*RAD); + + _current = _start + _dir * VASI[e].z + _widthDir * (width/2.0 + 30.0); + _current.y = 0; + + for (k=0;k<20;k++, i++) { + beaconsEntry1[i] = vasiLight; + beaconsEntry1[i].color = red; + beaconsEntry1[i].size = 1.0f * lightSize; + beaconsEntry1[i].pos = _current + _widthDir * 2.0 * double(k) + _V(0,1,0); + } + + _current -= _dir * VASI[e].y; + + for (k=0;k<5;k++, i++) { + beaconsEntry1[i] = vasiLight; + beaconsEntry1[i].color = white; + beaconsEntry1[i].pos = _current + _widthDir * 2.0 * double(k) + _V(0,1,0) + _V(0,1,0)*(sin(VASI[e].x*RAD)*VASI[e].y); + } + + // Post process lights ------------------------------------------ + + OBJHANDLE hPlanet = oapiGetBasePlanet(hObj); + double size = oapiGetSize(hPlanet); + + for (int k=0;kLockVertexBuffer(); + + if (pVrt) { + + DWORD red = 0xFFFF4444; + DWORD white = 0xFFFFEECC; + + FVECTOR3 vRef1 = pVrt[0].pos; + FVECTOR3 vUp = oapiTransformNormal(ptr(FVECTOR3(0,1,0)), world); + FVECTOR3 vPos = oapiTransformCoord(&vRef1, world); + FVECTOR3 vFront = unit(vPos); + + float slope = float(-asin(dotp(vFront, vUp))*DEG); + + VECTOR3 P = PAPI_pos[i]; + + if (PAPI_disp[i]<0) { + if (slopeUnLockVertexBuffer(); + } +} + + +void RunwayLights::Update(class vPlanet *vP) +{ + if (beacons1) beacons1->Update(50, vP); + if (beacons2) beacons2->Update(50, vP); +} + + +void RunwayLights::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4* world, bool night) +{ + _TRACE; + currentTime = float(fmod(1.7*oapiGetSimTime(), 1.0)); + if (currentTime<0) currentTime = 1.0f + currentTime; + + if (Config->RwyLightAnimate==0) currentTime = 0.5f; + + VECTOR3 dir = unit(end2 - end1); // Vector of the runway + VECTOR3 camDir = scene->GetCameraGDir(); + FVECTOR3 dirGlo = oapiTransformNormal(&_F(dir), world); + + if (dotp(_F(camDir), dirGlo) > 0) + { + if (night && beacons1) beacons1->Render(dev, world, currentTime); + + for (DWORD i=0;iRender(dev, world); + } + for (DWORD i=0;iRender(dev, world); + } + } + } + else if (!bSingleEnded) + { + if (night && beacons2) beacons2->Render(dev, world, currentTime); + for (DWORD i=0;iRender(dev, world); + } + for (DWORD i=0;iRender(dev, world); + } + } + } +} + + +int RunwayLights::CreateRunwayLights(class vBase *vB, const class Scene *scn, const char *filename, RunwayLights**& out) +{ + int numRunwayLights = 0; + std::vector lights; + char cbuf[256]; + + FILE* file = NULL; + fopen_s(&file, filename, "r"); + + if (file == NULL) { + LogErr("Could not open %s file.", filename); + return 0; + } + + LogAlw("Creating Runway Lights from %s",filename); + + while(fgets2(cbuf, 256, file)>=0) + { + if(!strncmp(cbuf, "RUNWAYLIGHTS", 12)) + { + numRunwayLights++; + lights.push_back(new RunwayLights(vB, scn)); + + for(;;) + { + if (fgets2(cbuf, 256, file)<0) break; + + if(!strncmp(cbuf, "END1", 4)) + { + VECTOR3 vec; + sscanf(cbuf, "END1 %lf %lf %lf", &vec.x, &vec.y, &vec.z); + lights[numRunwayLights-1]->SetEnd1(vec); + } + + else if(!strncmp(cbuf, "END2", 4)) + { + VECTOR3 vec; + sscanf(cbuf, "END2 %lf %lf %lf", &vec.x, &vec.y, &vec.z); + lights[numRunwayLights-1]->SetEnd2(vec); + } + + else if(!strncmp(cbuf, "WIDTH", 5)) + { + double width; + sscanf(cbuf, "WIDTH %lf", &width); + lights[numRunwayLights-1]->SetWidth(width); + } + + else if(!strncmp(cbuf, "TD_DISP ", 8)) + { + double disp; + sscanf(cbuf, "TD_DISP %lf", &disp); + lights[numRunwayLights-1]->SetTouchZoneDisplacement(disp); + } + + else if(!strncmp(cbuf, "TD_DISP2", 8)) + { + double disp; + sscanf(cbuf, "TD_DISP2 %lf", &disp); + lights[numRunwayLights-1]->SetTouchZoneDisplacement2(disp); + } + + else if(!strncmp(cbuf, "TD_LENGTH", 9)) + { + double disp; + sscanf(cbuf, "TD_LENGTH %lf", &disp); + lights[numRunwayLights-1]->SetTouchZoneLength(disp); + } + + else if(!strncmp(cbuf, "DECISION_DIST", 13)) + { + double disp; + sscanf(cbuf, "DECISION_DIST %lf", &disp); + lights[numRunwayLights-1]->SetDecisionDist(disp); + } + + else if(!strncmp(cbuf, "APPROACH_START", 14)) + { + double disp; + sscanf(cbuf, "APPROACH_START %lf", &disp); + lights[numRunwayLights-1]->SetApproachStart(disp); + } + + else if(!strncmp(cbuf, "PAPI", 4)) + { + VECTOR3 vec; DWORD u, q; + + int n = sscanf(cbuf, "PAPI %lf %lf %lf %lu %lu", &vec.x, &vec.y, &vec.z, &u, &q); + + if (n==3) { + lights[numRunwayLights-1]->AddPAPI(vec, 1, 0); + lights[numRunwayLights-1]->AddPAPI(vec, -1, 0); + lights[numRunwayLights-1]->AddPAPI(vec, 1, 1); + lights[numRunwayLights-1]->AddPAPI(vec, -1, 1); + } + else if (n==4) { + if (!u) lights[numRunwayLights-1]->AddPAPI(vec, 0, 0); + if (u&1) lights[numRunwayLights-1]->AddPAPI(vec, 1, 0); + if (u&2) lights[numRunwayLights-1]->AddPAPI(vec, -1, 0); + if (!u) lights[numRunwayLights-1]->AddPAPI(vec, 0, 1); + if (u&1) lights[numRunwayLights-1]->AddPAPI(vec, 1, 1); + if (u&2) lights[numRunwayLights-1]->AddPAPI(vec, -1, 1); + } + else if (n==5) { + if (!u) lights[numRunwayLights-1]->AddPAPI(vec, 0, q); + if (u&1) lights[numRunwayLights-1]->AddPAPI(vec, 1, q); + if (u&2) lights[numRunwayLights-1]->AddPAPI(vec, -1, q); + } + else LogErr("RUNWAYLIGHTS: Invalid parameter count in PAPI entry in (%s)",filename); + } + + else if(!strncmp(cbuf, "VASI", 4)) + { + VECTOR3 vec; DWORD e; + int n = sscanf(cbuf, "VASI %lf %lf %lf %lu", &vec.x, &vec.y, &vec.z, &e); + if (n==3) { + lights[numRunwayLights-1]->AddVASI(vec, 0); + lights[numRunwayLights-1]->AddVASI(vec, 1); + } + else lights[numRunwayLights-1]->AddVASI(vec, e); + } + + else if(!strncmp(cbuf, "SINGLEENDED", 11)) + { + lights[numRunwayLights-1]->SetSignleEnded(true); + } + + else if(!strncmp(cbuf, "CATEGORY", 8)) + { + int cat; + sscanf(cbuf, "CATEGORY %d", &cat); + if (cat<0) cat = 0; + if (cat>3) cat = 3; + lights[numRunwayLights-1]->SetCategory(cat); + } + + else if(!strncmp(cbuf, "END", 3)) + { + break; + } + } + } + } + + fclose(file); + + out = new RunwayLights*[numRunwayLights]; + + int i; + for(i=0; iInit(); + } + + return numRunwayLights; +} + + + +// ============================================================== +// class TaxiLights +// Defines runway lights used in vBase. +// ============================================================== + + +TaxiLights::TaxiLights(OBJHANDLE handle, const class Scene *scn) +{ + scene = scn; + end1 = _V(0, 0, 0); + end2 = _V(0, 0, 0); + color = _V(1, 1, 1); + size = 1.0; + count = 10; + hObj = handle; + beacons1 = NULL; + currentTime = 0.0f; +} + +TaxiLights::~TaxiLights() +{ + SAFE_DELETE(beacons1); + +} + +void TaxiLights::SetEnd1(VECTOR3 pos) +{ + end1 = pos; +} + +void TaxiLights::SetEnd2(VECTOR3 pos) +{ + end2 = pos; +} + +void TaxiLights::SetSize(double s) +{ + size = s; +} + +void TaxiLights::SetCount(int c) +{ + count = c; +} + +void TaxiLights::SetColor(VECTOR3 clr) +{ + color = clr; +} + + +void TaxiLights::Init() +{ + _TRACE; + // Helping vectors + VECTOR3 direction = end2 - end1; // Vector of the runway + VECTOR3 dir = direction; // Normalized direction + normalise(dir); + double len = length(direction); // Length of the runway + + BeaconArrayEntry* beaconsEntry1 = new BeaconArrayEntry[count]; + BeaconArrayEntry taxiLight; + + taxiLight.angle = 360.0f; + taxiLight.size = float(9.0*size); + taxiLight.lon = 0.0f; + taxiLight.loff = 1.0f; + taxiLight.bright = 5.0f; + taxiLight.fall = 0.5; + taxiLight.dir = _V(0, 1, 0); + taxiLight.pos = _V(0, 0, 0); + //taxiLight.lat = taxiLight.lng = 0.0; + taxiLight.color = FVECTOR4(float(color.x), float(color.y), float(color.z), 1.0f).dword_abgr(); + + VECTOR3 space = dir * len / (count-1); + VECTOR3 current = end1; + + for (int i=0;iRender(dev, world, 0.5f); +} + +int TaxiLights::CreateTaxiLights(OBJHANDLE base, const class Scene *scn, const char *filename, TaxiLights**& out) +{ + int numTaxiLights = 0; + std::vector lights; + char cbuf[256]; + + FILE* file = NULL; + fopen_s(&file, filename, "r"); + + if (file == NULL) { + LogErr("Could not open %s file.", filename); + return 0; + } + + while(fgets2(cbuf, 256, file)>=0) + { + if(!strncmp(cbuf, "BEACONARRAY", 11)) + { + numTaxiLights++; + lights.push_back(new TaxiLights(base, scn)); + + for(;;) + { + if (fgets2(cbuf, 256, file)<0) break; + + if(!strncmp(cbuf, "END1", 4)) + { + VECTOR3 vec; + sscanf(cbuf, "END1 %lf %lf %lf", &vec.x, &vec.y, &vec.z); + lights[numTaxiLights-1]->SetEnd1(vec); + } + + else if(!strncmp(cbuf, "END2", 4)) + { + VECTOR3 vec; + sscanf(cbuf, "END2 %lf %lf %lf", &vec.x, &vec.y, &vec.z); + lights[numTaxiLights-1]->SetEnd2(vec); + } + + else if(!strncmp(cbuf, "COL", 3)) + { + VECTOR3 vec; + sscanf(cbuf, "COL %lf %lf %lf", &vec.x, &vec.y, &vec.z); + lights[numTaxiLights-1]->SetColor(vec); + } + + else if(!strncmp(cbuf, "SIZE", 4)) + { + double size; + sscanf(cbuf, "SIZE %lf", &size); + lights[numTaxiLights-1]->SetSize(size); + } + + else if(!strncmp(cbuf, "COUNT", 5)) + { + int count; + sscanf(cbuf, "COUNT %d", &count); + lights[numTaxiLights-1]->SetCount(count); + } + + else if(!strncmp(cbuf, "END", 3)) + { + break; + } + } + } + } + + fclose(file); + + out = new TaxiLights*[numTaxiLights]; + + int i; + for(i=0; iInit(); + } + + return numTaxiLights; +} diff --git a/OVP/VulkanClient/RunwayLights.h b/OVP/VulkanClient/RunwayLights.h new file mode 100644 index 000000000..a41a3ad5c --- /dev/null +++ b/OVP/VulkanClient/RunwayLights.h @@ -0,0 +1,128 @@ +// ============================================================== +// RunwayLights.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) vk Client +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012-2016 Émile "Bibi Uncle" Grégoire +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +// ============================================================== +// class RunwayLights +// +// Defines runway lights used in vBase. +// ============================================================== + +#ifndef __RUNWAYLIGHTS_H +#define __RUNWAYLIGHTS_H + +#include "OrbiterAPI.h" +#include +#include "MathAPI.h" + +class BeaconArray; + +class RunwayLights +{ +public: + RunwayLights(class vBase *vB, const class Scene *snc); + virtual ~RunwayLights(); + + void SetEnd1(VECTOR3 pos); + void SetEnd2(VECTOR3 pos); + void SetWidth(double width); + void SetTouchZoneDisplacement(double disp); + void SetTouchZoneDisplacement2(double disp); + void SetTouchZoneLength(double length); + void SetDecisionDist(double dist); + void SetApproachStart(double dist); + void AddPAPI(VECTOR3 pos, float disp=0.0f, DWORD end=0); + void AddVASI(VECTOR3 pos, DWORD end=0); + void SetSignleEnded(bool bSingleEnded); + void SetCategory(int cat); + + void Init(); + void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4* world, bool night); + void Update(class vPlanet *vP); + + float GetWidth() const { return float(width); } + + static int CreateRunwayLights(class vBase *vB, const class Scene *scn, const char *file, RunwayLights**& out); + +protected: + + void SetPAPIColors(BeaconArray *pPAPI, FMATRIX4* world, int idx); + + class BeaconArray *BuildLights(VECTOR3 start, VECTOR3 end, double disp); + class BeaconArray *BuildVASI(VECTOR3 start, VECTOR3 end, DWORD idx); + class BeaconArray *BuildPAPI(VECTOR3 start, VECTOR3 end, DWORD idx); + + VECTOR3 end1; + VECTOR3 end2; + double width; + double td_disp; + double td_disp2; + double td_length; + double apr_length; + double apr_start; + bool bSingleEnded; + bool bDisp2; + int iCategory; + OBJHANDLE hObj; + + DWORD nPAPI; + VECTOR3 PAPI_pos[12]; + float PAPI_disp[12]; + DWORD PAPI_end[12]; + + DWORD nVASI; + VECTOR3 VASI[2]; + DWORD VASI_end[2]; + + BeaconArray* beacons1; + BeaconArray* beacons2; + BeaconArray* vasi[2]; + BeaconArray* papi[12]; + + const class Scene * scene; + class vBase *vB; + float currentTime; +}; + + + + + +class TaxiLights +{ +public: + TaxiLights(OBJHANDLE handle, const class Scene *scn); + virtual ~TaxiLights(); + + void SetEnd1(VECTOR3 pos); + void SetEnd2(VECTOR3 pos); + void SetSize(double width); + void SetCount(int count); + void SetColor(VECTOR3 color); + + void Init(); + void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4* world, bool night); + + static int CreateTaxiLights(OBJHANDLE base, const class Scene *scn, const char *file, TaxiLights**& out); + +protected: + OBJHANDLE hObj; + VECTOR3 end1; + VECTOR3 end2; + VECTOR3 color; + double size; + int count; + + BeaconArray* beacons1; + const class Scene * scene; + float currentTime; +}; + + + + +#endif // __RUNWAYLIGHTS_H diff --git a/OVP/VulkanClient/Scene.cpp b/OVP/VulkanClient/Scene.cpp new file mode 100644 index 000000000..20b5041da --- /dev/null +++ b/OVP/VulkanClient/Scene.cpp @@ -0,0 +1,4369 @@ +// ============================================================== +// Scene.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007 - 2016 Martin Schweiger +// 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +#include "Scene.h" +#include "VPlanet.h" +#include "VVessel.h" +#include "VBase.h" +#include "Particle.h" +#include "CSphereMgr.h" +#include "Util.h" +#include "Config.h" +#include "Surface.h" +#include "TextMgr.h" +#include "Catalog.h" +#include "AABBUtil.h" +#include "OapiExtension.h" +#include "DebugControls.h" +#include "IProcess.h" +#include "VectorHelpers.h" +#include +#include + +#define IKernelSize 120 + +using namespace oapi; + +static FMATRIX4 ident; + +const double LABEL_DISTLIMIT = 0.6; + +ID3DXEffect * Scene::FX = 0; +D3DXHANDLE Scene::eLine = 0; +D3DXHANDLE Scene::eStar = 0; +D3DXHANDLE Scene::eWVP = 0; +D3DXHANDLE Scene::eColor = 0; +D3DXHANDLE Scene::eTex0 = 0; + + +FVECTOR4 IKernel[IKernelSize]; + +bool sort_tgt_dist(const vObject *a, const vObject *b) +{ + return a->CameraTgtDist() < b->CameraTgtDist(); +} + +bool sort_cdist(const vObject* a, const vObject* b) +{ + return a->CamDist() > b->CamDist(); +} + +float Rand() +{ + return float(rand()) / 32768.0f; +} + +void DebugMatrix(FMATRIX4* pM, const char* name) +{ + vkDebugLog("[%3.3f, %3.3f, %3.3f, %3.3f]", pM->m41, pM->m42, pM->m43, pM->m44); + vkDebugLog("[%3.3f, %3.3f, %3.3f, %3.3f]", pM->m31, pM->m32, pM->m33, pM->m34); + vkDebugLog("[%3.3f, %3.3f, %3.3f, %3.3f]", pM->m21, pM->m22, pM->m23, pM->m24); + vkDebugLog("[%3.3f, %3.3f, %3.3f, %3.3f]", pM->m11, pM->m12, pM->m13, pM->m14); + vkDebugLog("%s", name); +} + + +// =========================================================================================== +// +Scene::Scene(vkClient *_gc, DWORD w, DWORD h) +{ + _TRACE; + + gc = _gc; + m_celSphere = NULL; + Lights = NULL; + hSun = NULL; + pAxisFont = NULL; + pLabelFont = NULL; + pDebugFont = NULL; + pBlur = NULL; + pBlur2D = NULL; + pOffscreenTarget = NULL; + pLocalCompute = NULL; + pRenderGlares = NULL; + pCreateGlare = NULL; + viewH = h; + viewW = w; + nLights = 0; + dwTurn = 0; + dwFrameId = 0; + surfLabelsActive = false; + + pSunTex = NULL; + pLightGlare = NULL; + pSunGlare = NULL; + pSunGlareAtm = NULL; + pEnvDS = NULL; + pIrradiance = NULL; + pIrradTemp = NULL; + pDepthNormalDS = NULL; + pVisDepth = NULL; + pLocalResults = NULL; + pLocalResultsSL = NULL; + pBakeLights = NULL; + ptRandom = NULL; + dmCubeMesh = NULL; + pRenderStage = NULL; + + vobjEnv = eCamRenderList.cend(); + itIC = InteriorCams.cend(); + + fDisplayScale = float(viewH) / 1080.0f; + + for (auto& a : DepthSampleKernel) a = FVECTOR2(0, 0); + + memset(&psShmDS, 0, sizeof(psShmDS)); + memset(&Camera, 0, sizeof(Camera)); + + pDevice = _gc->GetDevice(); + + oapiMatrixIdentity(&ident); + + SetCameraAperture(float(RAD*50.0), float(viewH)/float(viewW)); + SetCameraFrustumLimits(2.5f, 5e6f); // initial limits + + m_celSphere = new vkCelestialSphere(gc, this); + Lights = new vkLight[MAX_SCENE_LIGHTS]; + + bLocalLight = *(bool*)gc->GetConfigParam(CFGPRM_LOCALLIGHT); + + memset(&sunLight, 0, sizeof(vkSun)); + + + CLEARARRAY(pBlrTemp); + CLEARARRAY(pBlrTemp2D); + CLEARARRAY(pTextures); + CLEARARRAY(ptgBuffer); + CLEARARRAY(psgBuffer); + + nstream = 0; + iVCheck = 0; + + InitGDIResources(); + + while (true) { + float dx = 0; + float dy = 0; + for (int i = 0; i < IKernelSize; i++) { + double r = sqrt(oapiRand()); + double a = oapiRand() * PI2; + IKernel[i].x = float(cos(a) * r); + IKernel[i].y = float(sin(a) * r); + dx += IKernel[i].x; + dy += IKernel[i].y; + } + if ((abs(dx) < 1.0) && (abs(dy) < 1.0)) break; + } + + for (int i = 0; i < IKernelSize; i++) { + float d = IKernel[i].x*IKernel[i].x + IKernel[i].y*IKernel[i].y; + IKernel[i].z = sqrt(1.0f - saturate(d)); + IKernel[i].w = IKernel[i].z; + } + + + + // ------------------------------------------------------------------------------ + // Read Sun glare sampling kernel file + + ifstream fs("Modules/vkShaders/GKernel.txt"); + if (fs.good()) { + string line; vector data; + while (getline(fs, line)) { + std::istringstream iss(line); + char c; float a, b; iss >> a >> c >> b; + data.push_back(FVECTOR2(a, b)); + } + if (data.size() != ARRAYSIZE(DepthSampleKernel)) LogErr("Modules/vkShaders/GKernel.txt Size missmatch. Expecting 57 entries"); + else for (int i = 0; i < ARRAYSIZE(DepthSampleKernel); i++) DepthSampleKernel[i] = data[i]; + data.clear(); + } else LogErr("Failed to read: Modules/vkShaders/GKernel.txt"); + fs.close(); + + CreateSunGlare(); + + + // ------------------------------------------------------------------------------ + // Load a mesh to create a stage + // + dmCubeMesh = (DEVMESHHANDLE) new vkMesh("D3D9Cube"); + pRenderStage = new ShaderClass(pDevice, "Modules/vkShaders/Custom.hlsl", "StageVS", "StagePS", "RenderStage", ""); + + + // ------------------------------------------------------------------------------ + // Initialize a shaders for local lights visibility checks and rendering + // + if (Config->bGlares || Config->bLocalGlares) + { + pRenderGlares = new ShaderClass(pDevice, "Modules/vkShaders/Glare.hlsl", "GlareVS", "GlarePS", "RenderGlares", ""); + pLocalCompute = new ShaderClass(pDevice, "Modules/vkShaders/Glare.hlsl", "VisibilityVS", "VisibilityPS", "LocalVisCheck", ""); + D3DXCreateTexture(pDevice, 32, 1, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R16F, D3DPOOL_DEFAULT, &pLocalResults); + HR(pLocalResults->GetSurfaceLevel(0, &pLocalResultsSL)); + } + + + // Render screen depth and screen space normals + // + + if (Config->bGlares || Config->bLocalGlares) { + pVisDepth = new ImageProcessing(pDevice, "Modules/vkShaders/LightBlur.hlsl", "PSDepth", NULL); + pVisDepth->CompileShader("PSNormal"); + } + + // Initialize envmapping and shadow maps ----------------------------------------------------------------------------------------------- + // + DWORD EnvMapSize = Config->EnvMapSize; + DWORD ShmMapSize = Config->ShadowMapSize; + + if (Config->EnvMapMode) { + HR(pDevice->CreateDepthStencilSurface(EnvMapSize, EnvMapSize, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, true, &pEnvDS, NULL)); + } + + + // Create Depth Stencil buffers for shadow maps + // + if (Config->ShadowMapMode) { + UINT size = ShmMapSize; + for (int i = 0; i < SHM_LOD_COUNT; i++) { + HR(pDevice->CreateDepthStencilSurface(size, size, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, true, &psShmDS[i], NULL)); + size >>= 1; + } + } + + // Create shadow map for vessel exterior shadowing + smEX = new SHADOWMAP(pDevice, SHADOWMAP::sMapType::MultiLod); + // Create shadow map for virtual cockpit shadowing + smVC = new SHADOWMAP(pDevice, SHADOWMAP::sMapType::Cascaded); + // Create shadow map for shadowing in a stage-set + smSS = new SHADOWMAP(pDevice, SHADOWMAP::sMapType::SingleLod); + + + // Create auxiliary color buffer for on screen GDI + // + if (Config->GDIOverlay) { + HR(D3DXCreateTexture(pDevice, viewW, viewH, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &ptgBuffer[GBUF_GDI])); + pGDIOverlay = new ImageProcessing(pDevice, "Modules/vkShaders/GDIOverlay.hlsl", "PSMain"); + } + else pGDIOverlay = NULL; + + + // Create an auxiliary screen space normal and depth buffer (i.e. Shader readable depth buffer) + // + if (Config->bGlares || Config->bLocalGlares) { + HR(pDevice->CreateDepthStencilSurface(viewW, viewH, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, true, &pDepthNormalDS, NULL)); + HR(D3DXCreateTexture(pDevice, viewW, viewH, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &ptgBuffer[GBUF_DEPTH])); + } + + + // Create a random number table -------------------------------------------------------------------------------------------------- + // + HR(D3DXCreateTexture(pDevice, 128, 128, 1, D3DUSAGE_DYNAMIC, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptRandom)); + D3DLOCKED_RECT rect; + if (ptRandom->LockRect(0, &rect, 0, 0) == S_OK) { + for (int i = 0; i < (128 * 128); i++) ((float*)rect.pBits)[i] = oapiRand(); + ptRandom->UnlockRect(0); + } else LogErr("Failed to create random table"); + + // Initialize post processing effects -------------------------------------------------------------------------------------------------- + // + pLightBlur = NULL; + + if (Config->PostProcess) { + + int BufSize = 1; + int BufFmt = 0; + + // Get the actual back buffer description + D3DSURFACE_DESC desc; + gc->GetBackBuffer()->GetDesc(&desc); + + char flags[32] = { 0 }; + if (Config->ShaderDebug) strcpy_s(flags, 32, "DISASM"); + + // Load postprocessing effects + if (Config->PostProcess == PP_DEFAULT) + pLightBlur = new ImageProcessing(pDevice, "Modules/vkShaders/LightBlur.hlsl", "PSMain", flags); + + if (pLightBlur) { + BufSize = pLightBlur->FindDefine("BufferDivider"); + BufFmt = pLightBlur->FindDefine("BufferFormat"); + } + + D3DFORMAT BackBuffer = desc.Format; + if (BufFmt == 1) BackBuffer = D3DFMT_A16B16G16R16F; + if (BufFmt == 2) BackBuffer = D3DFMT_A2R10G10B10; + + // Create auxiliary color buffer for color operations + HR(D3DXCreateTexture(pDevice, viewW, viewH, 1, D3DUSAGE_RENDERTARGET, BackBuffer, D3DPOOL_DEFAULT, &ptgBuffer[GBUF_COLOR])); + + // Load some textures + char buff[MAX_PATH]; + if (gc->TexturePath("D3D9Noise.dds", buff)) HR(D3DXCreateTextureFromFileA(pDevice, buff, &pTextures[TEX_NOISE])); + if (gc->TexturePath("D3D9CLUT.dds", buff)) HR(D3DXCreateTextureFromFileA(pDevice, buff, &pTextures[TEX_CLUT])); + + if (pLightBlur) { + HR(D3DXCreateTexture(pDevice, viewW / BufSize, viewH / BufSize, 1, D3DUSAGE_RENDERTARGET, BackBuffer, D3DPOOL_DEFAULT, &ptgBuffer[GBUF_BLUR])); + HR(D3DXCreateTexture(pDevice, viewW / BufSize, viewH / BufSize, 1, D3DUSAGE_RENDERTARGET, BackBuffer, D3DPOOL_DEFAULT, &ptgBuffer[GBUF_TEMP])); + } + + if (pLightBlur) { + // Construct an offscreen backbuffer with custom pixel format + if (pDevice->CreateRenderTarget(viewW, viewH, BackBuffer, desc.MultiSampleType, desc.MultiSampleQuality, false, &pOffscreenTarget, NULL) != S_OK) { + LogErr("Creation of Offscreen render target failed"); + SAFE_DELETE(pLightBlur); + } + } + } + + for (int i = 0; i < ARRAYSIZE(ptgBuffer);i++) if (ptgBuffer[i]) ptgBuffer[i]->GetSurfaceLevel(0, &psgBuffer[i]); + + + if (Config->GDIOverlay) { + HDC hDC; + // Clear the GDI Overlay with transparency + if (psgBuffer[GBUF_GDI]->GetDC(&hDC) == S_OK) { + DWORD color = 0xF08040; // BGR "Color Key" value for transparency + HBRUSH hBrush = CreateSolidBrush((COLORREF)color); + RECT r = _RECT( 0, 0, viewW, viewH ); + FillRect(hDC, &r, hBrush); + DeleteObject(hBrush); + psgBuffer[GBUF_GDI]->ReleaseDC(hDC); + } + } + + pBakeLights = new ImageProcessing(pDevice, "Modules/vkShaders/PreBakeLights.hlsl", "PSMain"); + pBakeLights->CompileShader("PSSunAO"); + + LogAlw("================ Scene Created ==============="); +} + +// =========================================================================================== +// +Scene::~Scene () +{ + _TRACE; + + pDevice->SetRenderTarget(0, NULL); + pDevice->SetRenderTarget(1, NULL); + pDevice->SetRenderTarget(2, NULL); + pDevice->SetRenderTarget(3, NULL); + + for (int i = 0; i < ARRAYSIZE(psgBuffer); i++) SAFE_RELEASE(psgBuffer[i]); + for (int i = 0; i < ARRAYSIZE(ptgBuffer); i++) SAFE_RELEASE(ptgBuffer[i]); + for (int i = 0; i < ARRAYSIZE(pTextures); i++) SAFE_RELEASE(pTextures[i]); + + SAFE_DELETE(pGDIOverlay); + SAFE_DELETE(pBlur); + SAFE_DELETE(pVisDepth); + SAFE_DELETE(pLightBlur); + SAFE_DELETE(pIrradiance); + SAFE_DELETE(m_celSphere); + SAFE_DELETE(pLocalCompute); + SAFE_DELETE(pRenderGlares); + SAFE_DELETE(pCreateGlare); + SAFE_DELETE(pBakeLights); + SAFE_DELETE(pRenderStage); + + SAFE_RELEASE(pOffscreenTarget); + SAFE_RELEASE(pEnvDS); + SAFE_RELEASE(pIrradTemp); + SAFE_RELEASE(pDepthNormalDS); + SAFE_RELEASE(pLocalResults); + SAFE_RELEASE(pLocalResultsSL); + SAFE_RELEASE(pSunTex); + SAFE_RELEASE(pLightGlare); + SAFE_RELEASE(pSunGlare); + SAFE_RELEASE(pSunGlareAtm); + SAFE_RELEASE(ptRandom); + + SAFE_DELETE(smEX); + SAFE_DELETE(smVC); + SAFE_DELETE(smSS); + + for (int i = 0; i < ARRAYSIZE(psShmDS); i++) SAFE_RELEASE(psShmDS[i]); + for (int i = 0; i < ARRAYSIZE(pBlrTemp); i++) SAFE_RELEASE(pBlrTemp[i]); + for (int i = 0; i < ARRAYSIZE(pBlrTemp2D); i++) SAFE_RELEASE(pBlrTemp2D[i]); + + if (Lights) { + delete []Lights; + Lights = NULL; + } + + // Particle Streams + if (nstream) { + for (DWORD j=0;jCompileShader("CreateLocalGlarePS"); + pCreateGlare->CompileShader("CreateSunGlareAtmPS"); + pCreateGlare->CompileShader("CreateSunTexPS"); + + + if (!pSunTex) { + UINT ts = (viewH >> 4) & 0xFFFC; // "ts" will be 64 for a Full HD display; + HR(D3DXCreateTexture(pDevice, ts * 5, ts * 5, 0, D3DUSAGE_RENDERTARGET | D3DUSAGE_AUTOGENMIPMAP, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pSunTex)); + HR(D3DXCreateTexture(pDevice, ts * 4, ts * 4, 0, D3DUSAGE_RENDERTARGET | D3DUSAGE_AUTOGENMIPMAP, D3DFMT_R16F, D3DPOOL_DEFAULT, &pLightGlare)); + HR(D3DXCreateTexture(pDevice, ts * 12, ts * 12, 0, D3DUSAGE_RENDERTARGET | D3DUSAGE_AUTOGENMIPMAP, D3DFMT_R16F, D3DPOOL_DEFAULT, &pSunGlare)); + HR(D3DXCreateTexture(pDevice, ts * 12, ts * 12, 0, D3DUSAGE_RENDERTARGET | D3DUSAGE_AUTOGENMIPMAP, D3DFMT_R16F, D3DPOOL_DEFAULT, &pSunGlareAtm)); + } + + LPDIRECT3DSURFACE9 pTgt = NULL; + + pCreateGlare->Activate("CreateSunGlarePS"); + pSunGlare->GetSurfaceLevel(0, &pTgt); + pCreateGlare->SetOutputNative(0, pTgt); + if (!pCreateGlare->Execute(false)) LogErr("pCreateGlare Execute Failed (CreateSunGlarePS)"); + SAFE_RELEASE(pTgt); + + pCreateGlare->Activate("CreateSunGlareAtmPS"); + pSunGlareAtm->GetSurfaceLevel(0, &pTgt); + pCreateGlare->SetOutputNative(0, pTgt); + if (!pCreateGlare->Execute(false)) LogErr("pCreateGlare Execute Failed (CreateSunGlareAtmPS)"); + SAFE_RELEASE(pTgt); + + pCreateGlare->Activate("CreateLocalGlarePS"); + pLightGlare->GetSurfaceLevel(0, &pTgt); + pCreateGlare->SetOutputNative(0, pTgt); + if (!pCreateGlare->Execute(false)) LogErr("pCreateGlare Execute Failed (CreateLocalGlarePS)"); + SAFE_RELEASE(pTgt); + + pCreateGlare->Activate("CreateSunTexPS"); + pSunTex->GetSurfaceLevel(0, &pTgt); + pCreateGlare->SetOutputNative(0, pTgt); + if (!pCreateGlare->Execute(false)) LogErr("pCreateGlare Execute Failed (CreateSunTexPS)"); + SAFE_RELEASE(pTgt); +} + + +// =========================================================================================== +// +void Scene::clbkInitialise() +{ + _TRACE; + + hSun = oapiGetGbodyByIndex(0); // generalise later + + DWORD ambient = *(DWORD*)gc->GetConfigParam(CFGPRM_AMBIENTLEVEL); + + // Setup sunlight ------------------------------- + // + sunLight.Color = 1.0f; + sunLight.Ambient = float(ambient)*0.0039f; + sunLight.Transmission = 1.0f; + sunLight.Incatter = 0.0f; + + // Update Sunlight direction ------------------------------------- + // + VECTOR3 rpos, cpos; + oapiGetGlobalPos(hSun, &rpos); + oapiCameraGlobalPos(&cpos); rpos-=cpos; + sunLight.Dir = -unit(rpos); + + // Do not "pre-create" visuals here. Will cause changed call order for vessel callbacks +} + + +// =========================================================================================== +// Pooled Sketchpad API +// =========================================================================================== + +static vkPad *_pad = NULL; + +#define SKETCHPAD_LABELS 0 ///< Sketchpad for planetarium mode labels and markers +#define SKETCHPAD_2D_OVERLAY 1 ///< Sketchpad for HUD Overlay render to backbuffer directly +#define SKETCHPAD_DEBUG_TEXT 2 ///< Sketchpad to draw Debug String on a bottom of the screen +#define SKETCHPAD_PLANETARIUM 3 ///< Sketchpad to draw user defined planetarium + +// =========================================================================================== + +void Scene::clbkOnOptionChanged(int cat, int item) +{ + if (cat == OPTCAT_CELSPHERE) + m_celSphere->OnOptionChanged(cat, item); +} + +// =========================================================================================== +// Get pooled Sketchpad instance +// +vkPad *Scene::GetPooledSketchpad (int id) // one of SKETCHPAD_xxx +{ + assert(id <= SKETCHPAD_PLANETARIUM); + + if (!_pad) _pad = new vkPad("POOLED_SKETCHPAD"); + + // Automatically binds a Sketchpad to a top render target + _pad->BeginDrawing(); + _pad->LoadDefaults(); + + switch (id) + { + case SKETCHPAD_LABELS: + _pad->SetFont(pLabelFont); + _pad->SetTextAlign(Sketchpad::CENTER, Sketchpad::BOTTOM); + break; + + case SKETCHPAD_2D_OVERLAY: + break; + + case SKETCHPAD_DEBUG_TEXT: + _pad->SetFont(pDebugFont); + _pad->SetTextColor(0xFFFFFF); + _pad->SetTextAlign(Sketchpad::LEFT, Sketchpad::BOTTOM); + _pad->QuickPen(0xFF000000); + _pad->QuickBrush(0xB0000000); + break; + + case SKETCHPAD_PLANETARIUM: + break; + } + + return _pad; +} + +// =========================================================================================== +// Release pooled Sketchpad instances +void Scene::FreePooledSketchpads() +{ + SAFE_DELETE(_pad); +} + +// =========================================================================================== +// +double Scene::GetObjectAppRad(OBJHANDLE hObj) const +{ + VECTOR3 pos,cam; + oapiGetGlobalPos (hObj, &pos); + oapiCameraGlobalPos(&cam); // must use oapiCam.. here. called before camera setup + double rad = oapiGetSize (hObj); + double dst = dist (pos, cam); + return (rad*double(viewH))/(dst*tan(oapiCameraAperture())); +} + +// =========================================================================================== +// +double Scene::GetObjectAppRad2(OBJHANDLE hObj) const +{ + VECTOR3 pos; + oapiGetGlobalPos (hObj, &pos); + VECTOR3 cam = GetCameraGPos(); + double rad = oapiGetSize (hObj); + double dst = dist (pos, cam); + return (rad*double(viewH))/(dst*tan(oapiCameraAperture())); +} + +// =========================================================================================== +// +void Scene::CheckVisual(OBJHANDLE hObj) +{ + _TRACE; + + if (hObj==NULL) return; + + VOBJREC *pv = FindVisual(hObj); + if (!pv) pv = AddVisualRec(hObj); + + pv->apprad = float(GetObjectAppRad(hObj)); + + if (pv->type == OBJTP_STAR) { + pv->vobj->Activate(true); + return; + } + + if (pv->vobj->IsActive()) { + if (pv->apprad < 1.0) pv->vobj->Activate(false); + } else { + if (pv->apprad > 2.0) pv->vobj->Activate(true); + } + // the range check has a small hysteresis to avoid continuous + // creation/deletion for objects at the edge of visibility +} + +// =========================================================================================== +// +const vkLight *Scene::GetLight(int index) const +{ + if ((DWORD)index=0) return &Lights[index]; + return NULL; +} + +// =========================================================================================== +// +Scene::VOBJREC *Scene::FindVisual(OBJHANDLE hObj) const +{ + if (hObj==NULL) return NULL; + for (auto v : Visuals) if (v->vobj->Object() == hObj) return v; + return NULL; +} + +// =========================================================================================== +// +class vObject *Scene::GetVisObject(OBJHANDLE hObj) const +{ + if (hObj == NULL) return NULL; + for (auto v : Visuals) if (v->vobj->Object() == hObj) return v->vobj; + return NULL; +} + +// =========================================================================================== +// +std::set Scene::GetVessels(double max_dst, bool bAct) +{ + std::set List; + for (auto v : Visuals) { + if (v->type != OBJTP_VESSEL) continue; + if (bAct && v->vobj->IsActive() == false) continue; + if (v->vobj->CamDist() < max_dst) List.insert((vVessel *)v->vobj); + } + return List; +} + +// =========================================================================================== +// +void Scene::DelVisualRec (VOBJREC *pv) +{ + // delete the visual, its children and the entry itself + DebugControls::RemoveVisual(pv->vobj); + gc->UnregisterVisObject(pv->vobj->GetObjHandle()); + if (pv->type == OBJTP_VESSEL) gc->clbkScenarioChanged(pv->vobj, ScnChgEvent::VisualDeleted); + delete pv->vobj; + Visuals.remove(pv); +} + +// =========================================================================================== +// +void Scene::DeleteAllVisuals() +{ + for (auto v : Visuals) + { + DebugControls::RemoveVisual(v->vobj); + gc->UnregisterVisObject(v->vobj->GetObjHandle()); + LogAlw("Deleting Visual %s", _PTR(v->vobj)); + delete v->vobj; + } + Visuals.clear(); +} + +// =========================================================================================== +// +Scene::VOBJREC *Scene::AddVisualRec(OBJHANDLE hObj) +{ + _TRACE; + + char buf[256]; + + // create the visual and entry + VOBJREC *pv = new VOBJREC; + + memset(pv, 0, sizeof(VOBJREC)); + + Visuals.push_back(pv); + + pv->vobj = vObject::Create(hObj, this); + pv->type = oapiGetObjectType(hObj); + + oapiGetObjectName(hObj, buf, 255); + + VESSEL *hVes=NULL; + if (pv->type==OBJTP_VESSEL) hVes = oapiGetVesselInterface(hObj); + + LogAlw("RegisteringVisual (%s) hVessel=%s, hObj=%s, Vis=%s, Rec=%s, Type=%d", buf, _PTR(hVes), _PTR(hObj), _PTR(pv->vobj), _PTR(pv), pv->type); + + gc->RegisterVisObject(hObj, (VISHANDLE)pv->vobj); + + // Initialize Meshes + pv->vobj->PreInitObject(); + + if (pv->type == OBJTP_VESSEL) gc->clbkScenarioChanged(pv->vobj, ScnChgEvent::VisualCreated); + + return pv; +} + +// =========================================================================================== +// +DWORD Scene::GetActiveParticleEffectCount() +{ + // render exhaust particle system + DWORD count = 0; + for (DWORD n = 0; n < nstream; n++) if (pstream[n]->IsActive()) count++; + return count; +} + +// =========================================================================================== +// +VECTOR3 Scene::SkyColour () +{ + VECTOR3 col = {0,0,0}; + OBJHANDLE hProxy = oapiCameraProxyGbody(); + if (hProxy && oapiPlanetHasAtmosphere (hProxy)) { + const ATMCONST *atmp = oapiGetPlanetAtmConstants (hProxy); + VECTOR3 rc, rp, pc; + rc = GetCameraGPos(); + oapiGetGlobalPos (hProxy, &rp); + pc = rc-rp; + double cdist = length (pc); + if (cdist < atmp->radlimit) { + ATMPARAM prm; + oapiGetPlanetAtmParams (hProxy, cdist, &prm); + normalise (rp); + double coss = dotp (pc, rp) / -cdist; + double intens = min (1.0,(1.0839*coss+0.4581)) * sqrt (prm.rho/atmp->rho0); + // => intensity=0 at sun zenith distance 115? + // intensity=1 at sun zenith distance 60? + if (intens > 0.0) + col += _V(atmp->color0.x*intens, atmp->color0.y*intens, atmp->color0.z*intens); + } + for (int i=0;i<3;i++) if (col.data[i] > 1.0) col.data[i] = 1.0; + } + return col; +} + +// =========================================================================================== +// +void Scene::clbkUpdate () +{ + _TRACE; + + // update particle streams - should be skipped when paused + if (!oapiGetPause()) { + for (DWORD i=0;iExpired()) DelParticleStream(i); + else pstream[i++]->Update(); + } + } + + static bool bFirstUpdate = true; + + // check object visibility (one object per frame in the interest + // of scalability) + DWORD nobj = oapiGetObjectCount(); + + if (bFirstUpdate) { + bFirstUpdate = false; + for (DWORD i=0;i= nobj) iVCheck = 0; + + // This function will browse through vessels and planets. (not bases) + // Base visuals don't exist in the visual record. + OBJHANDLE hObj = oapiGetObjectByIndex(iVCheck++); + CheckVisual(hObj); + } + + + // If Camera target has changed, setup mesh debugger + // + OBJHANDLE hTgt = oapiCameraTarget(); + + if (hTgt!=Camera.hTarget && hTgt!=NULL) { + + Camera.hTarget = hTgt; + + if (DebugControls::IsActive()) { + if (oapiGetObjectType(hTgt) == OBJTP_SURFBASE) { + OBJHANDLE hPlanet = oapiGetBasePlanet(hTgt); + vPlanet *vp = static_cast(GetVisObject(hPlanet)); + if (vp) { + vBase *vb = vp->GetBaseByHandle(hTgt); + if (vb) { + DebugControls::SetVisual(vb); + } + } + return; // why? + } + } + + vObject *vo = GetVisObject(hTgt); + + if (vo) { + + if (DebugControls::IsActive()) { + DebugControls::SetVisual(vo); + } + + // Why is this here ? + // + // kuddel: OrbiterSound 4.0 did not play the sounds of the 'focused' + // Vessel when focus changed during playback. Therfore the + // vkClient does a oapiSetFocusObject call when playback + // is running. Is a OrbiterSound error, but we can work-around + // this, so we do! To reproduce, just disable the following + // code and run the 'Welcome.scn'. + // See also: http://www.orbiter-forum.com/showthread.php?p=392689&postcount=18 + // and following... + + // OrbiterSound 4.0 'playback helper' + if (OapiExtension::RunsOrbiter2010() && + OapiExtension::RunsOrbiterSound40() && + oapiIsVessel(hTgt) && // oapiGetObjectType(vo->Object()) == OBJTP_VESSEL && + dynamic_cast(vo)->Playback() + ) + { + // Orbiter doesn't do this when (only) camera focus changes + // during playback, therfore we do it ;) + oapiSetFocusObject(hTgt); + } + } + } +} + +// =========================================================================================== +// +double Scene::GetTargetElevation() const +{ + VESSEL *hVes = oapiGetVesselInterface(Camera.hTarget); + if (hVes) return hVes->GetSurfaceElevation(); + return 0.0; +} + + +// =========================================================================================== +// +double Scene::GetFocusGroundAltitude() const +{ + VESSEL *hVes = oapiGetFocusInterface(); + if (hVes) return hVes->GetAltitude() - hVes->GetSurfaceElevation(); + return 0.0; +} + + + +// =========================================================================================== +// +double Scene::GetTargetGroundAltitude() const +{ + VESSEL *hVes = oapiGetVesselInterface(Camera.hTarget); + if (hVes) return hVes->GetAltitude() - hVes->GetSurfaceElevation(); + return 0.0; +} + + + +// ============================================================================================ +// Up, North, Forward in Ecliptic frame +// +void Scene::GetLVLH(vVessel *vV, FVECTOR3 *up, FVECTOR3 *nr, FVECTOR3 *fw) +{ + if (!vV || !up || !nr || !fw) return; + + MATRIX3 grot; VECTOR3 rpos; + VESSEL *hV = vV->GetInterface(); assert(hV); + OBJHANDLE hRef = hV->GetGravityRef(); + oapiGetRotationMatrix(hRef, &grot); + hV->GetRelativePos(hRef, rpos); + VECTOR3 axis = mul(grot, _V(0, 1, 0)); + normalise(rpos); + *up = _F(rpos); + *fw = _F(unit(crossp(axis, rpos))); + *nr = crossp(*up, *fw); + normalize(*nr); +} + + + +// =========================================================================================== +// Compute a distance to a near/far plane +// =========================================================================================== + +float Scene::ComputeNearClipPlane() +{ + float zsurf = 1000.0f; + VOBJREC *pv = NULL; + + OBJHANDLE hObj = Camera.hObj_proxy; + OBJHANDLE hTgt = Camera.hTarget; + VESSEL *hVes = oapiGetVesselInterface(hTgt); + + if (hObj && hVes) { + VECTOR3 pos; + oapiGetGlobalPos(hObj,&pos); + double g = atan(Camera.apsq); + double t = dotp(unit(Camera.pos-pos), unit(Camera.dir)); + if (t<-1.0) t=1.0; if (t>1.0) t=1.0f; + double a = PI - acos(t); + double R = oapiGetSize(hObj) + hVes->GetSurfaceElevation(); + double r = length(Camera.pos-pos); + double h = r - R; + if (h<10e3) { + double d = a - g; if (d<0) d=0; + zsurf = float(h*cos(g)/cos(d)); + if (zsurf>1000.0f || zsurf<0.0f) zsurf=1000.0f; + } + } + + float zmin = 1.0f; + if (GetCameraAltitude()>10e3) zmin = 0.1f; + + int count = 0; + int actbase = 0; + vPlanet *pl = NULL; + + float farpoint = 0.0f; + float nearpoint = 10e3f; + float neardist = 10e3f; + + for (auto pv : Visuals) { + + float nr = 10e3f; + float fr = 0.0f; + float dn = 10e3f; + + bool bCockpit = false; + + if (pv->type==OBJTP_VESSEL) { + if (pv->vobj==vFocus) { + bCockpit = oapiCameraInternal(); + } + } + + if (pv->apprad>0.01 && pv->vobj->IsActive()) { + + vObject *obj = pv->vobj; + + if (pv->type==OBJTP_PLANET) { + if (obj->Object()==hObj) pl = (vPlanet*)obj; + obj->GetMinMaxDistance(&nr, &fr, &dn); + if (dnfarpoint) farpoint = fr; + continue; + } + + if (pv->type==OBJTP_VESSEL) { + + if (obj->IsVisible()) { + + if (bCockpit) if (vFocus->HasExtPass()==false) continue; // Ignore MinMax + + obj->GetMinMaxDistance(&nr, &fr, &dn); + + if (dnfarpoint) farpoint = fr; + count++; + } + } + } + } + + if (pl) { + + float nr = 10e3; + float fr = 0.0f; + float dn = 10e3; + + DWORD bc = pl->GetBaseCount(); + for (DWORD i=0;iGetBaseByIndex(i); + if (vb) { + if (vb->IsActive() && vb->IsVisible()) { + vb->GetMinMaxDistance(&nr, &fr, &dn); + if (dnfarpoint) farpoint = fr; + actbase++; + } + } + } + } + + DWORD prteff = GetActiveParticleEffectCount(); + + if (farpoint==0.0) farpoint = 20e4; + + float znear = D9NearPlane(pDevice, nearpoint, farpoint, neardist, GetProjectionMatrix(), (prteff!=0)); + + if (oapiCameraInternal()) { + if (Config->NearClipPlane==0) zmin = 1.0f; + else zmin = 0.1f; + } + + znear = min(znear, zsurf); + znear = max(znear, zmin); + + return znear; +} + + + + + +// =========================================================================================== +// Prepare scene for rendering +// +// - Update camera for rendering of the main scene +// - Update all visuals +// - Distance sort planets +// - Setup sky color +// - Setup local light sources +// =========================================================================================== + +bool Scene::UpdateCamVis() +{ + + // Update camera parameters -------------------------------------- + // and call vObject::Update() for all visuals + // + bool bRet = UpdateCameraFromOrbiter(RENDERPASS_MAINSCENE); + + if (Camera.hObj_proxy) vkEffect::UpdateEffectCamera(Camera.hObj_proxy); + + // Update Sunlight direction ------------------------------------- + // + VECTOR3 rpos; + oapiGetGlobalPos(hSun, &rpos); + rpos -= Camera.pos; + sunLight.Dir = -unit(rpos); + + // Get focus visual ----------------------------------------------- + // + OBJHANDLE hFocus = oapiGetFocusObject(); + vFocus = NULL; + for (auto pv : Visuals) { + if (pv->type==OBJTP_VESSEL) if (pv->vobj->Object()==hFocus) { + vFocus = (vVessel *)pv->vobj; + break; + } + } + + // Compute SkyColor ----------------------------------------------- + // + sky_color = SkyColour(); + bglvl = (sky_color.x + sky_color.y + sky_color.z) / 3.0; + bg_rgba = D3DCOLOR_RGBA ((int)(sky_color.x*255), (int)(sky_color.y*255), (int)(sky_color.z*255), 255); + + + // ---------------------------------------------------------------- + // render solar system celestial objects (planets and moons) + // we render without z-buffer, so need to distance-sort the objects + // ---------------------------------------------------------------- + + Planets.clear(); + + for (auto pv : Visuals) { + if (pv->apprad < 0.01 && pv->type != OBJTP_STAR) continue; + if (pv->type == OBJTP_PLANET || pv->type == OBJTP_STAR) Planets.push_back(pv->vobj); + } + + Planets.sort(sort_cdist); + return bRet && (vFocus != nullptr); +} + +// =========================================================================================== +// +void Scene::ClearLocalLights() +{ + nLights = 0; + lmaxdst2 = 0.0f; + + // Clear active local lisghts list ------------------------------- + for (int i = 0; i < MAX_SCENE_LIGHTS; i++) Lights[i].Reset(); +} + +// =========================================================================================== +// +void Scene::AddLocalLight(const LightEmitter *le, const vObject *vo) +{ + if (Lights==NULL) return; + if (le->IsActive()==false || le->GetIntensity()==0.0) return; + + assert(vo != NULL); + + vkLight lght(le, vo); + + // ----------------------------------------------------------------------------- + // Replace or Add + // + if (nLights == MAX_SCENE_LIGHTS) { + if (lght.Dst2 > lmaxdst2) return; + DWORD imax = 0; + for (DWORD i = 0; i < MAX_SCENE_LIGHTS; i++) if (Lights[i].Dst2 > lmaxdst2) imax = i; + Lights[imax] = lght; + lmaxdst2 = lght.Dst2; + } + else { + Lights[nLights] = lght; + if (lght.Dst2>lmaxdst2) lmaxdst2 = lght.Dst2; + nLights++; + } +} + +// =========================================================================================== +// +void Scene::ActivateAllLocalLights(bool bInterior) +{ + if (bLocalLight) { + ClearLocalLights(); + for (auto pv : Visuals) if (pv) ActivateLocalLights(pv->vobj, bInterior); + } +} + + +// =========================================================================================== +// +void Scene::ActivateLocalLights(vObject *vO, bool bInterior) +{ + if (!vO) return; + if (!vO->IsActive()) return; + if (vO->Type() == OBJTP_VESSEL) { + VESSEL* vessel = ((vVessel*)vO)->GetInterface(); + DWORD nemitter = vessel->LightEmitterCount(); + for (DWORD j = 0; j < nemitter; j++) { + const LightEmitter* em = vessel->GetLightEmitter(j); + if (em->GetVisibility() == LightEmitter::VIS_ALWAYS) AddLocalLight(em, vO); + else { + if ((em->GetVisibility() == LightEmitter::VIS_COCKPIT) && bInterior) AddLocalLight(em, vO); + if ((em->GetVisibility() == LightEmitter::VIS_EXTERNAL) && !bInterior) AddLocalLight(em, vO); + } + } + } +} + + +// =========================================================================================== +// +void Scene::ComputeLocalLightsVisibility() +{ + + if (!ptgBuffer[GBUF_DEPTH] || !pLocalCompute) { + Config->bGlares = false; + Config->bLocalGlares = false; + return; + } + + VECTOR3 gsun; + oapiGetGlobalPos(oapiGetObjectByIndex(0), &gsun); + + // Put the Sun on a top of the list + LLCBuf[0].index = 0.0f; + LLCBuf[0].pos = FVECTOR3(unit(gsun - Camera.pos)) * 10e4; + LLCBuf[0].cone = 1.0f; + + int nGlares = 1; + + for (int i = 0; i < nLights; i++) + { + if (Lights[i].cone > 0.0f) { + LLCBuf[nGlares].index = float(nGlares); + LLCBuf[nGlares].pos = Lights[i].Position; + LLCBuf[nGlares].cone = Lights[i].cone; + Lights[i].GPUId = nGlares; + nGlares++; + } + } + + struct { + FMATRIX4 mVP; + FMATRIX4 mSVP; + FVECTOR4 vSrc; + FVECTOR3 vDir; + } ComputeData; + + D3DSURFACE_DESC desc; + pLocalResultsSL->GetDesc(&desc); + + D3DMAT_OrthoOffCenterLH(&ComputeData.mVP, 0.0f, (float)desc.Width, (float)desc.Height, 0.0f, 0.0f, 1.0f); + + psgBuffer[GBUF_DEPTH]->GetDesc(&desc); + + ComputeData.vSrc = FVECTOR4((float)desc.Width, (float)desc.Height, 1.0f / (float)desc.Width, 1.0f / (float)desc.Height); + ComputeData.vDir = Camera.z; + ComputeData.mSVP = Camera.mProjView; + + // Must setup render target before calling Setup() + gc->PushRenderTarget(pLocalResultsSL, NULL, RENDERPASS_UNKNOWN); + + pLocalCompute->ClearTextures(); + pLocalCompute->SetPSConstants("cbPS", &ComputeData, sizeof(ComputeData)); + pLocalCompute->SetPSConstants("cbKernel", DepthSampleKernel, sizeof(DepthSampleKernel)); + pLocalCompute->SetVSConstants("cbPS", &ComputeData, sizeof(ComputeData)); + pLocalCompute->SetTexture("tDepth", ptgBuffer[GBUF_DEPTH], IPF_CLAMP | IPF_POINT); + pLocalCompute->Setup(pLocalLightsDecl, false, 0); + pLocalCompute->UpdateTextures(); + + // Compute local lights visibility + HR(pDevice->DrawPrimitiveUP(D3DPT_POINTLIST, nGlares, &LLCBuf, sizeof(LocalLightsCompute))); + + pLocalCompute->DetachTextures(); + gc->PopRenderTargets(); +} + + +// =========================================================================================== +// +void Scene::RecallDefaultState() +{ + HR(pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID)); + HR(pDevice->SetRenderState(D3DRS_STENCILENABLE, false)); + HR(pDevice->SetRenderState(D3DRS_COLORWRITEENABLE, 0xF)); + HR(pDevice->SetRenderState(D3DRS_ZENABLE, true)); + HR(pDevice->SetRenderState(D3DRS_ZWRITEENABLE, true)); + HR(pDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false)); + HR(pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false)); + HR(pDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD)); + HR(pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA)); + HR(pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA)); + HR(pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW)); +} + + +// =========================================================================================== +// +void Scene::clbkNewVessel(OBJHANDLE hVessel) +{ + CheckVisual(hVessel); +} + +// =========================================================================================== +// +void Scene::clbkDeleteVessel(OBJHANDLE hVessel) +{ + VOBJREC* pv = FindVisual(hVessel); + if (pv) DelVisualRec(pv); +} + +// =========================================================================================== +// +void Scene::clbkScenarioChanged(OBJHANDLE hVessel, ScnChgEvent e) +{ + LogVerbose("=== clbkScenarioChanged(%d) Event ===", e); + + // Acquire list of vessel visuals + // + Vessels.clear(); + for (auto v : Visuals) + if (v->type == OBJTP_VESSEL) Vessels.insert((vVessel*)v->vobj); + + + // Update Attachment hierarchy + // + RootList.clear(); + for (auto v : Vessels) { + OBJHANDLE hRoot = v->GetInterface()->GetAttachmentRoot(); + v->vRoot = (vVessel*)GetVisObject(hRoot); + RootList.insert(v->vRoot); + } + + // Update eCam render list + // + eCamRenderList.clear(); + + // Render env-map for all root vessels in range + for (auto v : RootList) eCamRenderList.insert(v); + + // Render env-map for all vessels with user defined eCam setup, if enabled + if (Config->EnvMapFaces > 1) + for (auto v : Vessels) + if (v->HasOwnEnvCam(EnvCamType::Exterior)) eCamRenderList.insert(v); + + + // --------------------------------------------------------------------------------------- + // Return critical iterators to a start of the list. List context may have changed + // --------------------------------------------------------------------------------------- + + vobjEnv = eCamRenderList.begin(); + vobjIP = eCamRenderList.begin(); + + if (e == ScnChgEvent::Deleted) { + InteriorCams.clear(); + itIC = InteriorCams.begin(); + } +} + + +// =========================================================================================== +// +void Scene::clbkRenderMainScene() +{ + _TRACE; + + dwFrameId++; // Advance to a next frame + + double scene_time = vkGetTime(); + vkSetTime(vkStats.Timer.CamVis, scene_time); + + if (!UpdateCamVis()) { + if (SUCCEEDED(gc->BeginScene())) { + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 1.0f, 0L)); + gc->EndScene(); + } + return; // Scene not yet properly inilialized, return + } + + ActivateAllLocalLights(false); + + set Active; + for (auto v : Vessels) if (v->IsActive()) Active.insert(v); + + + // Update Vessel Animations + // + for (auto v : Active) v->UpdateAnimations(); + + + if (vFocus == nullptr) return; + + LPDIRECT3DSURFACE9 pBackBuffer = nullptr; + LPDIRECT3DTEXTURE9 pExtShdMap = nullptr; + + if (pOffscreenTarget) pBackBuffer = pOffscreenTarget; + else pBackBuffer = gc->GetBackBuffer(); + + + // Begin a Scene ------------------------------------------------------------------------------------ + // + if (FAILED (gc->BeginScene())) return; + + + + // ------------------------------------------------------------------------------------------------------- + // Render Custom Camera and Environment Views + // ------------------------------------------------------------------------------------------------------- + bool bIrrad = Config->EnvMapMode && Config->bIrradiance; + + if (Config->CustomCamMode == 0 && dwTurn == RENDERTURN_CUSTOMCAM) dwTurn++; + if (Config->EnvMapMode == 0 && dwTurn == RENDERTURN_ENVCAM) dwTurn++; + if (dwTurn > RENDERTURN_LAST) dwTurn = 0; + + int RenderCount = max(1, Config->EnvMapFaces); + + + // -------------------------------------------------------------------------------------------------------- + // Render Custom Camera view for a focus vessel + // -------------------------------------------------------------------------------------------------------- + + if (dwTurn == RENDERTURN_CUSTOMCAM) + { + if (Config->CustomCamMode && (CustomCams.size() > 0)) + { + if (camCurrent == CustomCams.cend()) camCurrent = CustomCams.cbegin(); + + OBJHANDLE hVessel = vFocus->GetObjHandle(); + + vObject *vO = GetVisObject((*camCurrent)->hVessel); + double maxd = min(500e3, GetCameraAltitude() + 15e3); + + if (vO->CamDist() < maxd && (*camCurrent)->bActive) + { + RenderCustomCameraView((*camCurrent)); // Note: World origin is changed here + + if ((*camCurrent)->pRenderProc) { + vkPad *pSkp = (vkPad * )gc->clbkGetSketchpad((*camCurrent)->hSurface); + pSkp->LoadDefaults(); + (*camCurrent)->pRenderProc(pSkp, (*camCurrent)->pUser); + gc->clbkReleaseSketchpad(pSkp); + } + } + camCurrent++; + } + } + + + // ------------------------------------------------------------------------------------------------------- + // Render reflection cube maps for vessels + // ------------------------------------------------------------------------------------------------------- + + if (dwTurn == RENDERTURN_ENVCAM && Config->EnvMapMode) + { + DWORD flags = 0; + if (Config->EnvMapMode == 1) flags |= SCN_PLANETS; + if (Config->EnvMapMode == 2) flags |= SCN_PLANETS | SCN_VESSELS | SCN_BASESTRUCT; + + if (vobjEnv == eCamRenderList.end()) vobjEnv = eCamRenderList.begin(); + + while (vobjEnv != eCamRenderList.end()) { + auto vV = (*vobjEnv); + if (vV->IsVisible()) { + if (vV->CamDist() < 10e3) { + // Note: World origin is changed here + if (vV->ProcessEnvMaps(pDevice, RenderCount, flags) == false) break; + } + } + vobjEnv++; + } + } + + + // ------------------------------------------------------------------------------------------------------- + // Render reflection cube maps and irradiance for interior parts + // ------------------------------------------------------------------------------------------------------- + + if (Config->EnvMapMode) + { + if (vobjIP == eCamRenderList.end()) vobjIP = eCamRenderList.begin(); + + while (vobjIP != eCamRenderList.end()) { + auto vV = (*vobjIP); + if (vV->IsVisible()) { + if (vV->CamDist() < 500.0) { + if (RenderVCProbes(vV) == false) break; // Note: World origin is changed here + } + } + vobjIP++; + } + } + + + // --------------------------------------------------------------------------------------------- + // Init. camera setup and create a render list + // --------------------------------------------------------------------------------------------- + + ResetOrigin(Camera.pos); // Restore world origin to main camera position + + RenderList.clear(); + + for (auto vV : Active) { + if (!vV->IsVisible()) continue; + vV->bStencilShadow = true; + vV->BakeLights(pBakeLights); + RenderList.push_back(vV); + } + + float znear_for_vessels = ComputeNearClipPlane(); + + + + + // --------------------------------------------------------------------------------------------- + // Start Rendering of Normal and Depth Buffer for SSAO and (point in scene) visibility checks + // --------------------------------------------------------------------------------------------- + + if (psgBuffer[GBUF_DEPTH] && pDepthNormalDS) + { + SetCameraFrustumLimits(0.1f, 1e6f); + BeginPass(RENDERPASS_NORMAL_DEPTH); + + gc->PushRenderTarget(psgBuffer[GBUF_DEPTH], pDepthNormalDS, RENDERPASS_NORMAL_DEPTH); + + RecallDefaultState(); + + // Clear buffers + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0L)); + + // Render vessels + for (auto vVes : RenderList) vVes->Render(pDevice, false, nullptr); + + // Render Cockpit + if (oapiCameraInternal() && vFocus) vFocus->Render(pDevice, true, nullptr); + + gc->PopRenderTargets(); + PopPass(); + } + + // --------------------------------------------------------------------------------------------- + // Compute visibility of the Sun and Local light sources. After field depth render ! ! ! + // --------------------------------------------------------------------------------------------- + + ComputeLocalLightsVisibility(); + ActivateAllLocalLights(false); + + + // ------------------------------------------------------------------------------------------------------- + // Start Main Scene Rendering + // ------------------------------------------------------------------------------------------------------- + + RenderFlags = 0xFFFFFFFF; // Not used for main scene, set to 0xFFFFFFFF + + // Push main render target and depth surfaces + // + gc->PushRenderTarget(pBackBuffer, gc->GetDepthStencil(), RENDERPASS_MAINSCENE); // Main Scene + + + if (DebugControls::IsActive()) { + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 1.0f, 0L)); + DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + if (flags&DBG_FLAGS_WIREFRAME) pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); + else pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + } + else { + // Clear the viewport + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 1.0f, 0L)); + } + + + + // Do we use z-clear render mode or not ? + bool bClearZBuffer = false; + if ( (GetTargetGroundAltitude() > 2e3) && (oapiCameraInternal() == false)) bClearZBuffer = true; + if (IsProxyMesh()) bClearZBuffer = false; + + + if (DebugControls::IsActive()) { + DWORD camMode = *(DWORD*)gc->GetConfigParam(CFGPRM_GETCAMERAMODE); + if (camMode!=0) znear_for_vessels = 0.1f; + } + + // ------------------------------------------------------------------------------------------------------- + // render celestial sphere background + // ------------------------------------------------------------------------------------------------------- + + pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); + + bool bEnableAtmosphere = false; + + vPlanet *vPl = GetCameraProxyVisual(); + + // ------------------------------------------------------------------------------------------------------- + // Render the celestial sphere (background image, stars, planetarium features) + // ------------------------------------------------------------------------------------------------------- + + // Set generic clip plane distances for celestial sphere + SetCameraFrustumLimits(0.1, 10); + + m_celSphere->Render(pDevice, sky_color); + + // Set Initial Near clip plane distance + if (bClearZBuffer) SetCameraFrustumLimits(1e3, 3e8f); + else SetCameraFrustumLimits(znear_for_vessels, 3e8f); + + pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + + // --------------------------------------------------------------------------------------------- + // Create a caster list for shadow mapping + // --------------------------------------------------------------------------------------------- + + ObjectsToShadowMap.clear(); + for (auto v : Active) ObjectsToShadowMap.push_back(v); + + ObjectsToShadowMap.sort(sort_tgt_dist); + + + + // --------------------------------------------------------------------------------------------- + // Render shadow map for vFocus early for surface base and planet rendering + // --------------------------------------------------------------------------------------------- + + int shadow_lod = -1; + float bouble_rad = 10.0f; // Terrain shadow mapping coverage + + + if (Config->ShadowMapMode >= 1 && Config->TerrainShadowing == 2) + { + // Create a list of objects casting shadows on vFocus + Casters.clear(); + Casters.push_back(vFocus); + + FVECTOR3 ld = sunLight.Dir; + FVECTOR3 pos = vFocus->GetBoundingSpherePosDX(); + float rad = vFocus->GetBoundingSphereRadius(); + float frad = rad; + + vFocus->bStencilShadow = false; + + + // What else should be included besides vFocus ? + + for (auto v : ObjectsToShadowMap) + { + if (v == vFocus) continue; + if (v->HasShadow() == false) continue; + + FVECTOR3 bs_pos = v->GetBoundingSpherePosDX(); + float bs_rad = v->GetBoundingSphereRadius(); + + if (bs_rad > 80.0) continue; + + FVECTOR3 bc = bs_pos - pos; + float z = dotp(ld, bc); + if (fabs(z) > 1e3) continue; + FVECTOR3 fbc = bc - ld * z; + float dst = length(fbc); + if (dst > 1e3) continue; + + float nrd = (rad + dst + bs_rad) * 0.5f; + + bool bInclude = false; + + if (dst < (bs_rad + frad)) bInclude = true; + if (nrd < bouble_rad) bInclude = true; + + if (bInclude) { + + v->bStencilShadow = false; + Casters.push_back(v); + + if (nrd < rad) continue; + + if (nrd < bs_rad) { + pos = bs_pos; + rad = bs_rad; + } + else { + if (dst > 0.001f) pos += fbc * ((nrd - rad) / dst); + rad = nrd; + } + } + } + + SMapInput smi = { pos, ld, rad }; + RenderShadowMap(&smi, smEX, Casters, true); + } + + + + // --------------------------------------------------------------------------------------------- + // Render Planets + // --------------------------------------------------------------------------------------------- + + DWORD plnmode = *(DWORD*)gc->GetConfigParam(CFGPRM_PLANETARIUMFLAG); + DWORD mkrmode = *(DWORD*)gc->GetConfigParam(CFGPRM_SURFMARKERFLAG); + + for (auto pl : Planets) + { + // double nplane, fplane; + // plist[i].vo->RenderZRange (&nplane, &fplane); + // cam->SetFrustumLimits (nplane, fplane); + // since we are not using z-buffers here, we can adjust the projection + // matrix at will to make sure the object is within the viewing frustum + + OBJHANDLE hObj = pl->Object(); + bool isActive = pl->IsActive(); + + if (isActive) pl->Render(pDevice); + else pl->RenderDot(pDevice); + + + vkPad *pSketch = GetPooledSketchpad(SKETCHPAD_LABELS); + + if (pSketch) { + + if (isActive) pl->RenderVectors(pDevice, pSketch); + + if (mkrmode & MKR_ENABLE) { + + if (mkrmode & MKR_CMARK) { + VECTOR3 pp; + char name[256]; + oapiGetObjectName(hObj, name, 256); + oapiGetGlobalPos(hObj, &pp); + + m_celSphere->EnsureMarkerDrawingContext((oapi::Sketchpad**)&pSketch, 0, m_celSphere->MarkerColor(0), m_celSphere->MarkerPen(0)); + RenderObjectMarker(pSketch, pp, std::string(name), std::string(), 0, viewH / 80); + } + + if (isActive && (mkrmode & MKR_SURFMARK) && (oapiGetObjectType(hObj) == OBJTP_PLANET)) + { + int label_format = *(int*)oapiGetObjectParam(hObj, OBJPRM_PLANET_LABELENGINE); + if (label_format < 2 && (mkrmode & MKR_LMARK)) // user-defined planetary surface labels + { + double rad = oapiGetSize(hObj); + double apprad = rad / (pl->CamDist() * tan(GetCameraAperture())); + const GraphicsClient::LABELLIST *list; + DWORD n, nlist; + MATRIX3 prot; + VECTOR3 ppos, cpos; + + nlist = gc->GetSurfaceMarkers(hObj, &list); + + oapiGetRotationMatrix(hObj, &prot); + oapiGetGlobalPos(hObj, &ppos); + VECTOR3 cp = GetCameraGPos(); + cpos = tmul(prot, cp - ppos); // camera in local planet coords + + for (n = 0; n < nlist; n++) { + + if (list[n].active && apprad*list[n].distfac > LABEL_DISTLIMIT) { + + int size = (int)(viewH / 80.0*list[n].size + 0.5); + int col = list[n].colour; + + m_celSphere->EnsureMarkerDrawingContext((oapi::Sketchpad**)&pSketch, 0, m_celSphere->MarkerColor(col), m_celSphere->MarkerPen(col)); + const std::vector& ls = list[n].marker; + VECTOR3 sp; + for (int j = 0; j < ls.size(); j++) { + if (dotp(ls[j].pos, cpos - ls[j].pos) >= 0.0) { // surface point visible? + sp = mul(prot, ls[j].pos) + ppos; + RenderObjectMarker(pSketch, sp, ls[j].label[0], ls[j].label[1], list[n].shape, size); + } + } + } + } + } + + if (mkrmode & MKR_BMARK) { + + DWORD n = oapiGetBaseCount(hObj); + MATRIX3 prot; + oapiGetRotationMatrix(hObj, &prot); + int size = (int)(viewH / 80.0); + + m_celSphere->EnsureMarkerDrawingContext((oapi::Sketchpad**)&pSketch, 0, m_celSphere->MarkerColor(0), m_celSphere->MarkerPen(0)); + + for (DWORD i = 0; i < n; i++) { + + OBJHANDLE hBase = oapiGetBaseByIndex(hObj, i); + + VECTOR3 ppos, cpos, bpos; + + oapiGetGlobalPos(hObj, &ppos); + oapiGetGlobalPos(hBase, &bpos); + VECTOR3 cp = GetCameraGPos(); + cpos = tmul(prot, cp - ppos); // camera in local planet coords + bpos = tmul(prot, bpos - ppos); + + double apprad = 8000e3 / (length(cpos - bpos) * tan(GetCameraAperture())); + + if (dotp(bpos, cpos - bpos) >= 0.0 && apprad > LABEL_DISTLIMIT) { // surface point visible? + char name[64]; oapiGetObjectName(hBase, name, 63); + VECTOR3 sp = mul(prot, bpos) + ppos; + RenderObjectMarker(pSketch, sp, std::string(name), std::string(), 0, size); + } + } + } + } + } + + pSketch->EndDrawing(); // SKETCHPAD_LABELS + } + } + + + // ------------------------------------------------------------------------------------------------------- + // render a user defined planetarium art + // ------------------------------------------------------------------------------------------------------- + + if (plnmode & PLN_ENABLE) { + vkPad *pSketch = GetPooledSketchpad(SKETCHPAD_PLANETARIUM); + gc->MakeRenderProcCall(pSketch, RENDERPROC_PLANETARIUM, GetViewMatrix(), GetProjectionMatrix()); + pSketch->EndDrawing(); // SKETCHPAD_PLANETARIUM + } + + + // ------------------------------------------------------------------------------------------------------- + // render a user defined exterior art + // ------------------------------------------------------------------------------------------------------- + + if (oapiCameraInternal() == false) { + vkPad *pSketch = GetPooledSketchpad(SKETCHPAD_PLANETARIUM); + gc->MakeRenderProcCall(pSketch, RENDERPROC_EXTERIOR, GetViewMatrix(), GetProjectionMatrix()); + pSketch->EndDrawing(); // SKETCHPAD_PLANETARIUM + } + + /*for (DWORD i = 0; i < nplanets; ++i) + { + OBJHANDLE hObj = plist[i].vo->Object(); + if (oapiGetObjectType(hObj) != OBJTP_PLANET) continue; + vkPad* pSketch = GetPooledSketchpad(SKETCHPAD_PLANETARIUM); + pSketch->LoadDefaults(); + pSketch->SetViewMode(Sketchpad::USER); + pSketch->SetViewProj(GetViewMatrix(), GetProjectionMatrix()); + static_cast(plist[i].vo)->TestComputations(pSketch); + pSketch->EndDrawing(); // SKETCHPAD_PLANETARIUM + }*/ + + // ------------------------------------------------------------------------------------------------------- + // render new-style surface markers + // ------------------------------------------------------------------------------------------------------- + + if ((mkrmode & MKR_ENABLE) && (mkrmode & MKR_LMARK)) + { + vkPad* pSketch = GetPooledSketchpad(SKETCHPAD_LABELS); + m_celSphere->EnsureMarkerDrawingContext((oapi::Sketchpad**)&pSketch, 0, 0, m_celSphere->MarkerPen(6)); + + int fontidx = -1; + for (auto pl : Planets) + { + OBJHANDLE hObj = pl->Object(); + if (oapiGetObjectType(hObj) != OBJTP_PLANET) { continue; } + if (!surfLabelsActive) { + static_cast(pl)->ActivateLabels(true); + } + + int label_format = *(int*)oapiGetObjectParam(hObj, OBJPRM_PLANET_LABELENGINE); + + if (label_format == 2) + { + static_cast(pl)->RenderLabels(pDevice, pSketch, label_font, &fontidx); + } + } + + pSketch->EndDrawing(); // SKETCHPAD_LABELS + + surfLabelsActive = true; + } + else { + surfLabelsActive = false; + } + + + // ------------------------------------------------------------------------------------------------------- + // render the vessel objects + // ------------------------------------------------------------------------------------------------------- + + // Set near clip plane for vessel exterior rendering + if (bClearZBuffer) { + pDevice->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, 1.0f, 0L); // clear z-buffer + SetCameraFrustumLimits(znear_for_vessels, 1e8f); + } + + + vkEffect::UpdateEffectCamera(Camera.hObj_proxy); + + auto RenderMarkers = RenderList; + vkPad* pSketch = GetPooledSketchpad(SKETCHPAD_LABELS); + m_celSphere->EnsureMarkerDrawingContext((oapi::Sketchpad**)&pSketch, 0, m_celSphere->MarkerColor(0), m_celSphere->MarkerPen(0)); + + + // Render the vessels inside the shadows ( Phase 1 ) ================================ + // + if (Config->ShadowMapMode >= 1) + { + // Get shadowing params for vFocus + SMapInput smi; + if (vFocus->GetSMapRenderData(vVessel::SMI::Visual, 0, &smi)) { + pExtShdMap = RenderObjectsInShadow(&smi, RenderList, pSketch); + } + else pExtShdMap = nullptr; + } + + + // Render additional shadows ( Phase 2 ) ============================================= + // + if ((Config->ShadowMapMode >= 2) && (DebugControls::IsActive()==false)) + { + // Don't render more shadows if debug controls are open to keep pExtShdMap valid + + SMapInput smf; + vFocus->GetSMapRenderData(vVessel::SMI::Visual, 0, &smf); + + list RenderThese; + + // Select objects to render with shadow map + // + if (Config->ShadowMapMode >= 3) for (auto v : RenderList) if (v->CamDist() < 1e3) RenderThese.push_back(v); + else for (auto v : RenderList) if (v->IntersectShadowTarget(&smf)) RenderThese.push_back(v); + + // Erase objects from render list + for (auto v : RenderThese) RenderList.remove(v); + + while (RenderThese.size()) + { + SMapInput smi; + if (RenderThese.front()->GetSMapRenderData(vVessel::SMI::Visual, 0, &smi)) { + RenderObjectsInShadow(&smi, RenderThese, pSketch); + } + } + } + + + // Render the remaining vessels those are not yet renderred + // + smEX->Clear(); + + while (RenderList.empty()==false) { + RenderList.front()->Render(pDevice); + RenderList.pop_front(); + } + + if (pSketch) { + m_celSphere->EnsureMarkerDrawingContext((oapi::Sketchpad**)&pSketch, 0, m_celSphere->MarkerColor(0), m_celSphere->MarkerPen(0)); + for (auto x : RenderMarkers) RenderVesselMarker(x, pSketch); + pSketch->EndDrawing(); // SKETCHPAD_LABELS + } + + + + // ------------------------------------------------------------------------------------------------------- + // render custom user objects + // ------------------------------------------------------------------------------------------------------- + + if (oapiCameraInternal() == false) { + if (gc->IsGenericProcEnabled(GENERICPROC_RENDER_EXTERIOR)) { + gc->MakeGenericProcCall(GENERICPROC_RENDER_EXTERIOR, 0, NULL); + } + } + + + // ------------------------------------------------------------------------------------------------------- + // render the vessel sub-systems + // ------------------------------------------------------------------------------------------------------- + + // render exhausts + // + for (auto v : Active) { + if (!v->IsVisible() || v->GetMeshCount() < 1) continue; + v->RenderExhaust(); + } + + // render beacons and grapple points + // + for (auto v : Active) v->RenderBeacons(pDevice); + for (auto v : Active) v->RenderGrapplePoints(pDevice); + + + // render exhaust particle system + // + for (DWORD n = 0; n < nstream; n++) pstream[n]->Render(pDevice); + + + // ------------------------------------------------------------------------------------------------------- + // Render vessel axis vectors + // ------------------------------------------------------------------------------------------------------- + + DWORD bfvmode = *(DWORD*)gc->GetConfigParam(CFGPRM_FORCEVECTORFLAG); + DWORD favmode = *(DWORD*)gc->GetConfigParam(CFGPRM_FRAMEAXISFLAG); + + if (bfvmode & BFV_ENABLE || favmode & FAV_ENABLE) + { + + pDevice->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, 1.0f, 0L); // clear z-buffer + + pSketch = GetPooledSketchpad(SKETCHPAD_LABELS); + pSketch->SetFont(pAxisFont); + pSketch->SetTextAlign(Sketchpad::LEFT, Sketchpad::TOP); + + for (auto pv : Visuals) { + if (!pv->vobj->IsActive()) continue; + if (!pv->vobj->IsVisible()) continue; + if (oapiCameraInternal() && vFocus==pv->vobj) continue; + + pv->vobj->RenderVectors(pDevice, pSketch); + } + + pSketch->EndDrawing(); // SKETCHPAD_LABELS + } + + + // ------------------------------------------------------------------------------------------------------- + // render the internal parts of the focus object in a separate render pass + // ------------------------------------------------------------------------------------------------------- + + if (oapiCameraInternal() && vFocus && (oapiCockpitMode() == COCKPIT_VIRTUAL)) + { + // Activate local lights for interior + ClearLocalLights(); + ActivateLocalLights(vFocus, true); + + pDevice->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, 1.0f, 0L); // clear z-buffer + + double znear = Config->VCNearPlane; + if (DebugControls::IsActive()) if (DebugControls::debugFlags & DBG_FLAGS_NEARCLIP) znear = 0.02; + + if (znear<0.01) znear=0.01; + if (znear>1.0) znear=1.0; + + OBJHANDLE hFocus = oapiGetFocusObject(); + SetCameraFrustumLimits(znear, oapiGetSize(hFocus)*2.0); + + if (DebugControls::IsActive()) { + if (DebugControls::debugFlags & DBG_FLAGS_RENDEREXT) { + // vFocus->Render(pDevice, false, nullptr); + } + } + + if (Config->ShadowMapMode >= 1) + { + cascfg[0].dist = 2.5f; + cascfg[0].size = 2.5f; + float sa = sin(GetCameraApertureCorner()); + + if (Config->VCCascadeCount == 2) { + cascfg[0].size = 4.0f; + cascfg[0].dist = 4.0f; + cascfg[1].size = 1.0f; + cascfg[1].dist = 1.0f; + } + + if (Config->VCCascadeCount == 3) { + cascfg[0].size = 12.0f; + cascfg[0].dist = 12.0f; + cascfg[1].size = 3.0f; + cascfg[1].dist = 3.0f; + cascfg[2].size = 1.0f; + cascfg[2].dist = 1.0f; //cascfg[2].size * (1.0f + sa) / (2.0f * sa); + } + + cascfg[1].dist = min(cascfg[0].dist, cascfg[1].dist); + cascfg[2].dist = min(cascfg[1].dist, cascfg[2].dist); + + //vkDebugLog("Aperture = %f, dist=%f", GetCameraApertureCorner() * 2.0 * 180.0 / PI, cascfg[2].dist- cascfg[2].size); + + FVECTOR3 ld = sunLight.Dir; + Casters.clear(); + RenderVCShadowMap(Camera.z, ld, Casters); + } + + // Render VC + vFocus->Render(pDevice, true, smVC); + } + + pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + + + // End Of Main Scene Rendering --------------------------------------------- + // + + + + + + + + + // ------------------------------------------------------------------------------------------------------- + // Copy Offscreen render target to backbuffer + // ------------------------------------------------------------------------------------------------------- + + + if (pOffscreenTarget && pLightBlur) { + + int iGensPerFrame = pLightBlur->FindDefine("PassCount"); + + D3DSURFACE_DESC colr; + D3DSURFACE_DESC blur; + + psgBuffer[GBUF_BLUR]->GetDesc(&blur); + psgBuffer[GBUF_COLOR]->GetDesc(&colr); + + FVECTOR2 scr = FVECTOR2(1.0f / float(colr.Width), 1.0f / float(colr.Height)); + FVECTOR2 sbf = FVECTOR2(1.0f / float(blur.Width), 1.0f / float(blur.Height)); + + + if (pLightBlur->IsOK()) + { + float fInt = float(Config->GFXIntensity); + float fDst = float(Config->GFXDistance); + float fThr = float(Config->GFXThreshold); + float fGam = float(Config->GFXGamma); + + // Grap a copy of a backbuffer + pDevice->StretchRect(pOffscreenTarget, NULL, psgBuffer[GBUF_COLOR], NULL, D3DTEXF_POINT); + + pLightBlur->SetFloat("vSB", &sbf, sizeof(FVECTOR2)); + pLightBlur->SetBool("bBlendIn", false); + pLightBlur->SetBool("bBlur", false); + + pLightBlur->SetFloat("fIntensity", &fInt, sizeof(float)); + pLightBlur->SetFloat("fDistance", &fDst, sizeof(float)); + pLightBlur->SetFloat("fThreshold", &fThr, sizeof(float)); + pLightBlur->SetFloat("fGamma", &fGam, sizeof(float)); + + // ----------------------------------------------------- + pLightBlur->SetBool("bSample", true); + pLightBlur->SetTextureNative("tBack", ptgBuffer[GBUF_COLOR], IPF_POINT | IPF_CLAMP); + pLightBlur->SetOutputNative(0, psgBuffer[GBUF_BLUR]); + + if (!pLightBlur->Execute(true)) LogErr("pLightBlur Execute Failed"); + + // ----------------------------------------------------- + pLightBlur->SetBool("bSample", false); + pLightBlur->SetBool("bBlur", true); + + for (int i = 0; i < iGensPerFrame; i++) { + + pLightBlur->SetBool("bDir", false); + pLightBlur->SetTextureNative("tBlur", ptgBuffer[GBUF_BLUR], IPF_POINT | IPF_CLAMP); + pLightBlur->SetOutputNative(0, psgBuffer[GBUF_TEMP]); + + if (!pLightBlur->Execute(true)) LogErr("pLightBlur Execute Failed"); + + pLightBlur->SetBool("bDir", true); + pLightBlur->SetTextureNative("tBlur", ptgBuffer[GBUF_TEMP], IPF_POINT | IPF_CLAMP); + pLightBlur->SetOutputNative(0, psgBuffer[GBUF_BLUR]); + + if (!pLightBlur->Execute(true)) LogErr("pLightBlur Execute Failed"); + } + + pLightBlur->SetBool("bBlendIn", true); + pLightBlur->SetBool("bBlur", false); + pLightBlur->SetTextureNative("tBack", ptgBuffer[GBUF_COLOR], IPF_LINEAR | IPF_CLAMP); + pLightBlur->SetTextureNative("tBlur", ptgBuffer[GBUF_BLUR], IPF_LINEAR | IPF_CLAMP); + pLightBlur->SetOutputNative(0, gc->GetBackBuffer()); + + if (!pLightBlur->Execute(true)) LogErr("pLightBlur Execute Failed"); + } + else { + LogErr("pLightBlur is not o.k."); + } + } + + + // ------------------------------------------------------------------------------------------------------- + // Render glares for the Sun and local lights + // ------------------------------------------------------------------------------------------------------- + + gc->PushRenderTarget(gc->GetBackBuffer(), gc->GetDepthStencil(), RENDERPASS_MAINSCENE); + RenderGlares(); + gc->PopRenderTargets(); + + + // ------------------------------------------------------------------------------------------------------- + // Render GDI Overlay to backbuffer directly + // ------------------------------------------------------------------------------------------------------- + + if (pGDIOverlay) + { + if (pGDIOverlay->IsOK()) + { + gc->bGDIClear = true; // Must clear background before continuing drawing into overlay + FVECTOR4 clr(0xFF4080F0ul); // ARGB ColorKey + pGDIOverlay->SetTextureNative("tSrc", ptgBuffer[GBUF_GDI], IPF_POINT | IPF_CLAMP); + pGDIOverlay->SetFloat("vColorKey", &clr, sizeof(clr)); + pGDIOverlay->SetOutputNative(0, gc->GetBackBuffer()); + if (!pGDIOverlay->Execute(true)) LogErr("pGDIOverlay Execute Failed"); + } + else + { + LogErr("pGDIOverlay is not OK."); + } + } + + /*if (Camera.vNear) { + vkDebugLog("vNear = %s", Camera.vNear->GetName()); + vkDebugLog("vProxy = %s", Camera.vProxy->GetName()); + vkDebugLog("vGravRef = %s", Camera.vGravRef->GetName()); + }*/ + + // ------------------------------------------------------------------------------------------------------- + // Render HUD Overlay to backbuffer directly + // ------------------------------------------------------------------------------------------------------- + + + gc->PushRenderTarget(gc->GetBackBuffer(), gc->GetDepthStencil(), RENDERPASS_MAINOVERLAY); // Overlay + + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + + if (pSketch) { + gc->MakeRenderProcCall(pSketch, RENDERPROC_HUD_1ST, NULL, NULL); + pSketch->EndDrawing(); // SKETCHPAD_2D_OVERLAY + } + gc->Render2DOverlay(); + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + if (pSketch) { + gc->MakeRenderProcCall(pSketch, RENDERPROC_HUD_2ND, NULL, NULL); + pSketch->EndDrawing(); // SKETCHPAD_2D_OVERLAY + } + + + // Enable Freeze mode after the main scene is complete + // + if (bFreezeEnable) bFreeze = true; + + + bool bVC = oapiCameraInternal() && (oapiCockpitMode() == COCKPIT_VIRTUAL); + + // ------------------------------------------------------------------------------------------------------- + // EnvMap Debugger + // ------------------------------------------------------------------------------------------------------- + + if (DebugControls::IsActive()) + { + auto sel = DebugControls::GetSelectedEnvMap(); + int probeid = DebugControls::GetProbeId(); + + EnvCamType tp = probeid < 0 ? EnvCamType::Exterior : EnvCamType::Interior; + if (probeid < 0) probeid = 0; + + switch (sel) { + case DbgDisplay::Mirror: + case DbgDisplay::Blur1: + case DbgDisplay::Blur2: + case DbgDisplay::Blur3: + case DbgDisplay::Blur4: + { + int idx = int(sel) - int(DbgDisplay::Mirror); + auto ec = vFocus->GetEnvCam(tp, probeid); + auto pData = ec ? ec->pCube : nullptr; + if (pData) VisualizeCubeMap((LPDIRECT3DCUBETEXTURE9)pData, idx); + + /*else { + auto ec = vFocus->GetExteriorEnvMap(); + auto pData = ec ? ec->pCube : nullptr; + if (pData) if (pData->GetType() == D3DRTYPE_CUBETEXTURE) + VisualizeCubeMap((LPDIRECT3DCUBETEXTURE9)pData, idx); + }*/ + break; + } + + case DbgDisplay::smSS: + VisualizeShadowMap(smSS); + break; + + case DbgDisplay::smVC: + VisualizeShadowMap(smVC); + break; + + case DbgDisplay::smEX: + VisualizeShadowMap(smEX); + break; + + case DbgDisplay::Irradiance: + { + auto ec = vFocus->GetEnvCam(tp, probeid); + auto pData = ec ? ec->pIrrad : nullptr; + if (pData) { + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + pSketch->CopyRectNative(pData, NULL, 0, 0); + pSketch->EndDrawing(); + } + /*else { + auto ec = vFocus->GetExteriorEnvMap(); + auto pData = ec ? ec->pIrrad : nullptr; + if (pData) { + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + pSketch->CopyRectNative(pData, NULL, 0, 0); + pSketch->EndDrawing(); + } + }*/ + break; + } + + case DbgDisplay::GlowMask: + if (ptgBuffer[GBUF_BLUR]) { + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + pSketch->CopyRectNative(ptgBuffer[GBUF_BLUR], NULL, 0, 0); + pSketch->EndDrawing(); + } + break; + + case DbgDisplay::ScreenDepth: + if (ptgBuffer[GBUF_DEPTH]) { + if (pVisDepth) { + if (pVisDepth->IsOK()) { + pVisDepth->Activate("PSDepth"); + pVisDepth->SetTextureNative("tBack", ptgBuffer[GBUF_DEPTH], IPF_POINT | IPF_CLAMP); + pVisDepth->SetOutputNative(0, gc->GetBackBuffer()); + pVisDepth->Execute(true); + } + } + } + break; + + case DbgDisplay::Normals: + if (ptgBuffer[GBUF_DEPTH]) { + if (pVisDepth) { + if (pVisDepth->IsOK()) { + pVisDepth->Activate("PSNormal"); + pVisDepth->SetTextureNative("tBack", ptgBuffer[GBUF_DEPTH], IPF_POINT | IPF_CLAMP); + pVisDepth->SetOutputNative(0, gc->GetBackBuffer()); + pVisDepth->Execute(true); + } + } + } + break; + + case DbgDisplay::LightVisbil: + if (pLocalResults) { + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + pSketch->SetBlendState(Sketchpad::BlendState::FILTER_POINT); + pSketch->StretchRectNative(pLocalResults, NULL, &(_RECT(0, 0, viewW, 10))); + pSketch->SetBlendState(Sketchpad::BlendState::FILTER_LINEAR); + pSketch->EndDrawing(); + } + break; + + case DbgDisplay::BakedLightMap: + { + LPDIRECT3DTEXTURE9 pTex = DebugControls::GetCombinedMap(); + if (pTex) { + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + pSketch->StretchRectNative(pTex, NULL, &(_RECT(0, 0, viewH, viewH))); + pSketch->EndDrawing(); + } + break; + } + + case DbgDisplay::Eclipse: + if (Camera.vNear) { + auto ptE = Camera.vNear->GetEclipse(); + if (ptE) { + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + pSketch->SetBlendState(Sketchpad::BlendState::FILTER_POINT); + pSketch->StretchRectNative(ptE, NULL, &(_RECT(0, 0, viewW, 10))); + pSketch->SetBlendState(Sketchpad::BlendState::FILTER_LINEAR); + pSketch->EndDrawing(); + } + } + break; + default: + break; + } + } + + + if (AtmoControls::Visualize()) + { + vPlanet* vP = GetCameraProxyVisual(); + pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + pSketch->SetBlendState(Sketchpad::COPY); + int x = 0, y = ViewH(); + + LPDIRECT3DTEXTURE9 pTab = vP->GetScatterTable(RAY_LAND); + D3DSURFACE_DESC desc; + if (pTab) { + pTab->GetLevelDesc(0, &desc); + pSketch->StretchRectNative(pTab, NULL, &(_R(0, y - desc.Height, desc.Width, y))); + y -= (desc.Height + 5); + } + pTab = vP->GetScatterTable(MIE_LAND); + if (pTab) { + pTab->GetLevelDesc(0, &desc); + pSketch->StretchRectNative(pTab, NULL, &(_R(0, y - desc.Height, desc.Width, y))); + y -= (desc.Height + 5); + } + pTab = vP->GetScatterTable(ATN_LAND); + if (pTab) { + pTab->GetLevelDesc(0, &desc); + pSketch->StretchRectNative(pTab, NULL, &(_R(0, y - desc.Height, desc.Width, y))); + y -= (desc.Height + 5); + } + for (int i=0;i<9;i++) + { + if (i == RAY_LAND || i == MIE_LAND || i == ATN_LAND) continue; + pTab = vP->GetScatterTable(i); + if (!pTab) continue; + pTab->GetLevelDesc(0, &desc); + pSketch->CopyRectNative(pTab, NULL, x, y - desc.Height); + x += desc.Width + 5; + } + pSketch->SetBlendState(Sketchpad::ALPHABLEND); + pSketch->EndDrawing(); + } + + + // ------------------------------------------------------------------------------------------------------- + // Draw Debug String on a bottom of the screen + // ------------------------------------------------------------------------------------------------------- + + const char* dbgString = oapiDebugString(); + int len = lstrlen(dbgString); + + if (len>0 || !vkDebugQueue.empty()) { + + pSketch = GetPooledSketchpad(SKETCHPAD_DEBUG_TEXT); + + DWORD height = Config->DebugFontSize; + + // Display Orbiter's debug string + if (len > 0) { + DWORD width = pSketch->GetTextWidth(dbgString, len); + pSketch->Rectangle(-1, viewH - height - 1, width + 4, viewH); + pSketch->Text(2, viewH - 2, dbgString, len); + } + + DWORD pos = viewH; + + // Display additional debug string queue + // + while (!vkDebugQueue.empty()) { + pos -= (height * 3) / 2; + std::string str = vkDebugQueue.front(); + len = lstrlen(str.c_str()); + DWORD width = pSketch->GetTextWidth(str.c_str(), len); + pSketch->Rectangle(-1, pos - height - 1, width + 4, pos); + pSketch->Text(2, pos - 2, str.c_str(), len); + vkDebugQueue.pop(); + } + + pSketch->EndDrawing(); // SKETCHPAD_DEBUG_TEXT + } + + + gc->PopRenderTargets(); // Overlay + gc->PopRenderTargets(); // Main Scene + + gc->HackFriendlyHack(); + gc->EndScene(); + + dwTurn++; +} + + +// =========================================================================================== +// +float Scene::GetLODLevel(SMapInput* smi) +{ + float dist = length(smi->pos); + float tanap = float(GetTanAp()); + float viewh = float(ViewH()); + float rsmax = viewh * smi->rad / (tanap * dist); + return log2f(float(Config->ShadowMapSize) / (rsmax * 1.5f)); +} + + +// =========================================================================================== +// +void Scene::CombineSMaps(SMapInput* a, SMapInput* b, SMapInput* out) +{ + FVECTOR3 ab = b->pos - a->pos; + FVECTOR3 ap = a->pos; + FVECTOR3 bp = a->pos - b->ld * dotp(ab, b->ld); // Project 'b' to a plane of 'a' + + ab = bp - ap; + float le = length(ab); // a-b distance + FVECTOR3 uab = ab / le; // Unit vector from a to b + float arad = a->rad; // Store 'a'-rad just in case if out == a + + out->rad = (le + a->rad + b->rad) * 0.5f; + out->pos = ap - uab * arad + uab * out->rad; + out->ld = a->ld; // ld should be identical in a,b +} + + +// =========================================================================================== +// +LPDIRECT3DTEXTURE9 Scene::RenderObjectsInShadow(SMapInput* smi, list& rList, vkPad* pSkp) +{ + Casters.clear(); // Clear shadow casters to auto-generate the list + Shadowed.clear(); // Clear list of objects receiving shadow + + int shadow_lod = RenderShadowMap(smi, smEX, Casters); + + if (shadow_lod >= 0) { + + // Create a list of objects that can be shadowed in a defined shadow volume + for (auto x : rList) + if (x->Type() == OBJTP_VESSEL) + if (((vVessel*)x)->IsInsideShadows(smi)) Shadowed.push_back(x); + + // Render objects in shadow + for (auto it : Shadowed) { + it->Render(pDevice, false, smEX); + if (pSkp) RenderVesselMarker(it, pSkp); + rList.remove(it); // Remove from a list of "objects needing rendering" + } + + // Get the map for debugging + return smEX->ptShmRT[shadow_lod]; + } + return nullptr; +} + + +// =========================================================================================== +// +void Scene::RenderVesselMarker(vVessel *vV, vkPad *pSketch) +{ + DWORD mkrmode = *(DWORD*)gc->GetConfigParam(CFGPRM_SURFMARKERFLAG); + if ((mkrmode & (MKR_ENABLE | MKR_VMARK)) == (MKR_ENABLE | MKR_VMARK)) { + RenderObjectMarker(pSketch, vV->GlobalPos(), std::string(vV->GetName()), std::string(), 0, viewH / 80); + } +} + + +// =========================================================================================== +// Lens flare code (SolarLiner) +// +Scene::SUNVISPARAMS Scene::GetSunScreenVisualState() +{ + SUNVISPARAMS result = SUNVISPARAMS(); + + VECTOR3 cam = GetCameraGPos(); + VECTOR3 sunGPos; + oapiGetGlobalPos(oapiGetGbodyByIndex(0), &sunGPos); + sunGPos -= cam; + + DWORD w, h; + oapiGetViewportSize(&w, &h); + + FVECTOR4 pos; + const FMATRIX4* pVP = GetProjectionViewMatrix(); + FVECTOR4 sun = FVECTOR4(float(sunGPos.x), float(sunGPos.y), float(sunGPos.z), 1.0f); + + D3DMAT_Transform(&pos, &sun, pVP); + + result.brightness = saturate(pos.z); + + FVECTOR2 scrPos = FVECTOR2(pos.x, pos.y); + scrPos /= pos.w; + scrPos *= 0.5f; + scrPos.x *= w / h; + + result.position = scrPos; + result.position.x *= 1.8f; + + short xpos = short((scrPos.x + 0.5f) * w); + short ypos = short((1.0f - (scrPos.y + 0.5f)) * h); + + PickProp prp = { NULL, 0.1f, false }; + vkPick pick = PickScene(xpos, ypos, &prp); + + if (pick.pMesh != NULL) + { + DWORD matIndex = pick.pMesh->GetMeshGroupMaterialIdx(pick.group); + vkMatExt material; + pick.pMesh->GetMaterial(&material, matIndex); + FVECTOR4 surfCol(material.Diffuse.x, material.Diffuse.y, material.Diffuse.z, material.Diffuse.w); + + result.visible = (surfCol.a != 1.0f); + if (result.visible) + { + FVECTOR4 color = FVECTOR4(surfCol.r*surfCol.a, surfCol.g*surfCol.a, surfCol.b*surfCol.a, 1.0f); + color += GetSunDiffColor() * (1 - surfCol.a); + + result.color = color; + } + return result; + } + result.visible = true; + result.color = GetSunDiffColor(); + + return result; +} + + +// =========================================================================================== +// Lens flare code (SolarLiner) +// +FVECTOR4 Scene::GetSunDiffColor() +{ + vPlanet *vP = Camera.vProxy; + + FVECTOR3 _one(1, 1, 1); + VECTOR3 GS, GP, GO; + oapiCameraGlobalPos(&GO); + + OBJHANDLE hS = oapiGetGbodyByIndex(0); // the central star + OBJHANDLE hP = vP->Object(); // the planet object + oapiGetGlobalPos(hS, &GS); // sun position + oapiGetGlobalPos(hP, &GP); // planet position + + VECTOR3 S = GS - GO; // sun's position from object + VECTOR3 P = GO - GP; + + double s = length(S); + + float pwr = 1.0f; + + if (hP == hS) return _F4(GetSun()->Color, 1.0f); + + double r = length(P); + double pres = 1000.0; + double size = oapiGetSize(hP) + vP->GetMinElevation(); + double grav = oapiGetMass(hP) * 6.67259e-11 / (size*size); + + float aalt = 1.0f; + //float amb0 = 0.0f; + float disp = 0.0f; + float amb = 0.0f; + float aq = 0.342f; + float ae = 0.242f; + float al = 0.0f; + float k = float(sqrt(r*r - size*size)); // Horizon distance + float alt = float(r - size); + float rs = float(oapiGetSize(hS) / s); + float ac = float(-dotp(S, P) / (r*s)); // sun elevation + + // Avoid some fault conditions + if (alt<0) alt = 0, k = 1e3, size = r; + + if (ac>1.0f) ac = 1.0f; if (ac<-1.0f) ac = -1.0f; + + ac = acos(ac) - asin(float(size / r)); + + if (ac>1.39f) ac = 1.39f; + if (ac<-1.39f) ac = -1.39f; + + float h = tan(ac); + + const ATMCONST *atm = (oapiGetObjectType(hP) == OBJTP_PLANET ? oapiGetPlanetAtmConstants(hP) : NULL); + + if (atm) { + aalt = float(atm->p0 * log(atm->p0 / pres) / (atm->rho0*grav)); + //amb0 = float(min(0.7, log(atm->rho0 + 1.0f)*0.4)); + disp = float(max(0.02, min(0.9, log(atm->rho0 + 1.0)))); + } + + if (alt>10e3f) al = aalt / k; + else al = 0.173f; + + FVECTOR3 lcol(1, 1, 1); + //FVECTOR3 r0 = _one - FVECTOR3(0.65f, 0.75f, 1.0f) * disp; + FVECTOR3 r0 = _one - FVECTOR3(1.15f, 1.65f, 2.35f) * disp; + + if (atm) { + float x = sqrt(saturate(h / al)); + float y = sqrt(saturate((h + rs) / (2.0f*rs))); + lcol = (r0 + (_one - r0) * x) * y; + } + else { + lcol = r0 * saturate((h + rs) / (2.0f*rs)); + } + + return FVECTOR4(lcol.x, lcol.y, lcol.z, 1); +} + + + +// =========================================================================================== +// +int Scene::RenderShadowMap(SMapInput* smi, SHADOWMAP *sm, std::list& Casters, bool bInterior) +{ + assert(sm->tp != SHADOWMAP::sMapType::Cascaded); + + float rad = smi->rad * 1.1f; + + sm->pos = smi->pos; + sm->ld = smi->ld; + sm->rad = rad; + sm->cascades = 1; + sm->Center[0] = { 0, 0 }; + sm->Subrect[0] = _F4( 0, 0, 1, 1 ); + sm->SubrectTF[0] = _F4( 0, 0, 1, 1 ); + + float dst = length(smi->pos); + float mnd = 1e16f; + float mxd = -1e16f; + float rsmax = 0.0f; + float tanap = float(GetTanAp()); + float viewh = float(ViewH()); + bool bExists = Casters.size() != 0; + D3DSURFACE_DESC desc; + + if (!bExists) + { + // browse through vessels to find shadowers -------------------------- + // + for (auto pv : Visuals) { + if (pv->type != OBJTP_VESSEL) continue; + vVessel *vV = (vVessel *)pv->vobj; + if (!vV->IsActive()) continue; + if (vV->IntersectShadowVolume(smi)) { + Casters.push_back(vV); + vV->GetMinMaxLightDist(smi, &mnd, &mxd); + } + } + } + else for (auto vV : Casters) vV->GetMinMaxLightDist(smi, &mnd, &mxd); + + + if (Casters.size() == 0) return -1; // The list is empty, Nothing to render + + // Compute shadow lod + rsmax = viewh * rad / (tanap * dst); + + sm->depth = (mxd + rad); + + FMATRIX4 mProj, mView; + D3DMAT_OrthoOffCenterRH(&mProj, -rad, rad, rad, -rad, -rad, sm->depth); + + sm->dist = mxd; + + FVECTOR3 lp = smi->pos - smi->ld * sm->dist; + FVECTOR3 pos = smi->pos; + + D3DMAT_LookAtRH(&mView, &lp, &pos, &FVECTOR3(0, 1, 0)); + oapiMatrixMultiply(&sm->mLVP, &mView, &mProj); + + sm->mVP[0] = sm->mLVP; + + WORD Pass = RENDERPASS_SHADOWMAP; + + if (sm->tp == SHADOWMAP::sMapType::SingleLod) // Manual LOD selection + { + Pass = RENDERPASS_VC_SHADOWMAP; + sm->psShmRT[0]->GetDesc(&desc); + sm->lod = 0; + sm->size = desc.Width; + auto psDS = GetDepthStencilMatch(sm->psShmRT[0]); + gc->PushRenderTarget(sm->psShmRT[0], psDS, Pass); + } + else // Automatic LOD selection + { + int lod = round(log2f(float(Config->ShadowMapSize) / (rsmax * 1.5f))); + lod = min(lod, SHM_LOD_COUNT - 1); + lod = max(lod, 0); + sm->psShmRT[lod]->GetDesc(&desc); + sm->lod = lod; + sm->size = desc.Width; + gc->PushRenderTarget(sm->psShmRT[lod], psShmDS[lod], Pass); + } + + sm->bValid = true; + + // Clear the viewport + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0L)); + + + // render the vessel objects -------------------------------- + // + BeginPass(Pass); + + for (auto vV : Casters) { + if (vV == vFocus) { + if (bInterior) vV->Render(pDevice, sm, vVessel::Render::VC); + else vV->Render(pDevice, sm, 0); + } + else vV->Render(pDevice, sm, 0); + } + + PopPass(); + gc->PopRenderTargets(); + + return sm->lod; +} + + + +// =========================================================================================== +// +int Scene::RenderVCShadowMap(FVECTOR3& cdir, FVECTOR3& ld, std::list& Casters) +{ + SHADOWMAP* sm = smVC; + + FVECTOR3 pos = cdir * cascfg[0].dist; + float rad = cascfg[0].size; + sm->pos = pos; + sm->ld = ld; + sm->rad = rad; + + float mnd = 1e16f; + float mxd = -1e16f; + float rsmax = 0.0f; + float tanap = float(GetTanAp()); + float viewh = float(ViewH()); + + bool bExists = Casters.size() != 0; + + if (!bExists) { + + // browse through vessels to find shadowers -------------------------- + // + for (auto pv : Visuals) { + if (pv->type != OBJTP_VESSEL) continue; + vVessel* vV = (vVessel*)pv->vobj; + if (!vV->IsActive()) continue; + if (vV->IntersectShadowVolume(sm)) { + Casters.push_back(vV); + vV->GetMinMaxLightDist(sm, &mnd, &mxd); + } + } + } + else { + for (auto vV : Casters) + { + // Get shadow min-max distances + vV->GetMinMaxLightDist(sm, &mnd, &mxd); + } + } + + if (Casters.size() == 0) return -1; // The list is empty, Nothing to render + + // Compute shadow lod + rsmax = viewh * rad / (tanap * length(pos)); + + sm->dist = mxd; + sm->lod = 0; + sm->size = Config->ShadowMapSize; + sm->cascades = Config->VCCascadeCount; + sm->depth = (mxd + vFocus->GetSize()); + sm->bValid = true; + + FMATRIX4 mProj, mView; + + for (int i = 0; i < sm->cascades; i++) + { + // Compute mLVP needed to render this cascade. + + pos = cdir * cascfg[i].dist; + rad = cascfg[i].size; + + // Project pos to a shadow plane + FVECTOR3 sp = pos - ld * dotp(pos, ld); + FVECTOR3 ep = sp - ld * sm->dist; + D3DMAT_OrthoOffCenterRH(&mProj, -rad, rad, rad, -rad, 0, sm->depth); + D3DMAT_LookAtRH(&mView, &ep, &sp, &(FVECTOR3(0, 1, 0))); + oapiMatrixMultiply(&sm->mVP[i], &mView, &mProj); + + FVECTOR3 xy = oapiTransformCoord(&pos, &sm->mVP[0]); + float s = 0.5f * rad / sm->rad; + + xy.y = -xy.y; + xy = (xy + 1.0f) * 0.5f; + + // Cascade's center, subrect and subrect-transform within the main level (i.e '0') + // All cascades share the same near and far plane and exists inside the previous cascade. + sm->Center[i] = { xy.x, xy.y }; + float l = xy.x - s; float t = xy.y - s; float r = xy.x + s; float b = xy.y + s; + sm->Subrect[i] = FVECTOR4(l, t, r, b); + sm->SubrectTF[i] = FVECTOR4(-l, -t, 1.0f / (r - l), 1.0f / (b - t)); + sm->SubPx[i] = rad / float(sm->size); + +#ifdef CASCADE_DEBUG + vkDebugLog("Cascade %i pos=[%f, %f]", i, xy.x, xy.y); + vkDebugLog("Cascade %i rect=[%f, %f, %f, %f]", i, xy.x-r, xy.y-r, xy.x+r, xy.y+r); +#endif + gc->PushRenderTarget(sm->psShmRT[i], psShmDS[0], RENDERPASS_SHADOWMAP); + + // Clear the viewport + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0L)); + + // render the vessel objects -------------------------------- + // + BeginPass(RENDERPASS_VC_SHADOWMAP); + + // NOTE: sm.mLVP must containg the projection needed for rendering of the map + sm->mLVP = sm->mVP[i]; + + for (auto &a : Casters) + { + a->Render(pDevice, sm, vVessel::Render::VC | vVessel::Render::IP); + } + + PopPass(); + + gc->PopRenderTargets(); + } + + // sm.mLVP must point to cascade '0' by default. That's where subrects lie + sm->mLVP = sm->mVP[0]; + + return 0; +} + + +// =========================================================================================== +// Return 'true' if rendering for the vessel is done +// +bool Scene::RenderVCProbes(vVessel* vV) +{ + // Render only focus if in debug mode + if (DebugControls::IsActive() && vV != vFocus) return true; + + if (InteriorCams.empty()) // Get Interior cams and render shadow map + { + if (vV->GetInteriorCams(&InteriorCams)) + { + } + else return true; + + itIC = InteriorCams.cbegin(); + } + + + if (itIC != InteriorCams.cend()) + { + auto ec = *itIC; + VECTOR3 gp; + vV->GetInterface()->Local2Global(ec->lPos._V(), gp); + ResetOrigin(gp); + + SMapInput smi; + + if (vV->GetSMapRenderData(vVessel::SMI::VC, 0, &smi)) + { + if (ec->bRendered) { + vV->RenderInteriorENVMap(pDevice, ec, smSS); + } + else { + Casters.clear(); + if (RenderShadowMap(&smi, smSS, Casters, true) < 0) { + LogErr("Failed to render shadow map for stage-set"); + } + else { + vV->RenderInteriorENVMap(pDevice, ec, smSS); + return false; + } + } + } + + itIC++; // Pick next camera + } + + if (itIC == InteriorCams.cend()) // All done for this vessel + { + InteriorCams.clear(); + return true; + } + + return false; // Not done yet, Continue this vessel next frame +} + + +// =========================================================================================== +// +LPDIRECT3DSURFACE9 Scene::GetDepthStencilMatch(LPDIRECT3DSURFACE9 pRef) +{ + D3DSURFACE_DESC desc; + pRef->GetDesc(&desc); + for (int i = 0; i < (SHM_LOD_COUNT - 1); i++) { + D3DSURFACE_DESC d; psShmDS[i]->GetDesc(&d); + if ((d.Width == desc.Width) && (d.Height == desc.Height)) return psShmDS[i]; + } + return nullptr; +} + + +// =========================================================================================== +// +LPDIRECT3DSURFACE9 Scene::GetDepthStencil(DWORD size) +{ + for (int i = 0; i < (SHM_LOD_COUNT - 1); i++) { + D3DSURFACE_DESC d; psShmDS[i]->GetDesc(&d); + if (d.Width == size) return psShmDS[i]; + } + return nullptr; +} + + +// =========================================================================================== +// +void Scene::RenderSecondaryScene(std::set &RndList, + std::set &LightsList, DWORD flags, + const LPDIRECT3DCUBETEXTURE9 pCT, SHADOWMAP *sm) +{ + _TRACE; + RenderFlags = flags; + + // Process Local Light Sources ------------------------------------- + // And toggle external lights on + // + if (bLocalLight) + { + ClearLocalLights(); + for (auto vVes : RndList) { + ActivateLocalLights(vVes, (flags && SCN_VC) != 0); + } + for (auto vVes : LightsList) { + if (!vVes->IsActive()) continue; + if (RndList.count(vVes)) continue; // Already included skip it + ActivateLocalLights(vVes, (flags && SCN_VC) != 0); + } + } + + vkEffect::UpdateEffectCamera(GetCameraProxyBody()); + + // Clear the viewport + if ((flags & SCN_NOCLEAR) == 0) { + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFF000000, 1.0f, 0L)); + } + + // render stage around the scene ---------------------------- + // + if (flags & SCN_STAGE) { + RenderStage(pCT); + } + + // Don't combine these + if ((flags & SCN_STAGE) && (flags & SCN_PLANETS)) assert(false); + if ((flags & SCN_VC) && (flags & SCN_ALLEXT)) assert(false); + + // render planets ------------------------------------------- + // + if (flags & SCN_PLANETS) { + for (auto pl : Planets) { + bool isActive = pl->IsActive(); + if (isActive) pl->Render(pDevice); + else pl->RenderDot(pDevice); + } + } + + // render the vessel objects -------------------------------- + // + if (flags & SCN_VESSELS) { + for (auto vVes : RndList) { + if (!vVes->IsActive()) continue; + if (!vVes->IsVisible()) continue; + vVes->Render(pDevice, nullptr, 0); + } + } + + // render exhausts ------------------------------------------- + // + if (flags & SCN_EXHAUST) { + for (auto vVes : RndList) { + if (!vVes->IsActive()) continue; + if (!vVes->IsVisible()) continue; + vVes->RenderExhaust(); + } + } + + // render beacons ------------------------------------------- + // + if (flags & SCN_BEACONS) { + for (auto vVes : RndList) { + if (!vVes->IsActive()) continue; + vVes->RenderBeacons(pDevice); + } + } + + // render exhaust particle system ---------------------------- + if (flags & SCN_PARTICLES) { + for (DWORD n = 0; n < nstream; n++) pstream[n]->Render(pDevice); + } + + if (flags & SCN_VC) + { + vFocus->Render(pDevice, smSS, vVessel::Render::VC | vVessel::Render::IP); + } + + // Flags 0x20 = BaseStructures +} + + +// =========================================================================================== +// +void Scene::RenderStageSet(const LPDIRECT3DCUBETEXTURE9 pCT) +{ + ClearLocalLights(); + ActivateLocalLights(vFocus, true); + + HR(pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFF000000, 1.0f, 0L)); + + RenderStage(pCT); + + vFocus->Render(pDevice, smSS, vVessel::Render::VC | vVessel::Render::IP); +} + + +// =========================================================================================== +// Rernder a stage/set to contain some meshes. +// +void Scene::RenderStage(LPDIRECT3DCUBETEXTURE9 pCT) +{ + if (!pRenderStage) return; + + struct { + FMATRIX4 mVP; + FMATRIX4 mW; + } ShaderData; + + ShaderData.mVP = Camera.mProjView; + oapiMatrixIdentity(&ShaderData.mW); + ShaderData.mW.m11 = 1e3f; + ShaderData.mW.m22 = 1e3f; + ShaderData.mW.m33 = 1e3f; + + HR(pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE)); + + pRenderStage->ClearTextures(); + pRenderStage->SetPSConstants("cbPS", &ShaderData, sizeof(ShaderData)); + pRenderStage->SetVSConstants("cbPS", &ShaderData, sizeof(ShaderData)); + pRenderStage->SetTexture("tTex", pCT, IPF_CLAMP | IPF_LINEAR); + pRenderStage->Setup(nullptr, false, 0); + pRenderStage->UpdateTextures(); + + ((vkMesh*)dmCubeMesh)->RenderGroup(0); + + pRenderStage->DetachTextures(); +} + +// =========================================================================================== +// +bool Scene::RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DCUBETEXTURE9 pSrc) +{ + bool bQuality = true; + + if (!pSrc) return false; + + if (!pBlur) { + pBlur = new ImageProcessing(pDev, "Modules/vkShaders/EnvMapBlur.hlsl", "PSBlur"); + } + + if (!pBlur->IsOK()) { + LogErr("pBlur is not OK"); + return false; + } + + if (!pEnvDS) { + LogErr("EnvDepthStencil doesn't exists"); + return false; + } + + D3DSURFACE_DESC desc; + pEnvDS->GetDesc(&desc); + DWORD width = min((UINT)512, desc.Width); + + + if (!pBlrTemp[0]) { + if (D3DXCreateCubeTexture(pDev, width >> 0, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp[0]) != S_OK) return false; + if (D3DXCreateCubeTexture(pDev, width >> 1, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp[1]) != S_OK) return false; + if (D3DXCreateCubeTexture(pDev, width >> 2, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp[2]) != S_OK) return false; + if (D3DXCreateCubeTexture(pDev, width >> 3, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp[3]) != S_OK) return false; + if (D3DXCreateCubeTexture(pDev, width >> 4, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp[4]) != S_OK) return false; + } + + + FVECTOR3 dir, up, cp; + LPDIRECT3DSURFACE9 pSrf = NULL; + LPDIRECT3DSURFACE9 pTmp = NULL; + + // Copy Src to Temp + // + for (DWORD i = 0; i < 6; i++) { + pSrc->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &pSrf); + pBlrTemp[0]->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &pTmp); + pDevice->StretchRect(pSrf, NULL, pTmp, NULL, D3DTEXF_POINT); + SAFE_RELEASE(pSrf); + SAFE_RELEASE(pTmp); + } + + + // Create blurred mip sub-levels + // + for (int mip = 1; mip < 5; mip++) { + + pBlur->SetFloat("fD", (4.0f / float(256 >> (mip - 1)))); + pBlur->SetBool("bDir", false); + pBlur->SetTextureNative("tCube", pBlrTemp[mip-1], IPF_LINEAR); + + for (DWORD i = 0; i < 6; i++) { + + EnvMapDirection(i, &dir, &up); + cp = unit(crossp(up, dir)); + + pSrc->GetCubeMapSurface(D3DCUBEMAP_FACES(i), mip, &pSrf); + + pBlur->SetOutputNative(0, pSrf); + pBlur->SetFloat("vDir", &dir, sizeof(FVECTOR3)); + pBlur->SetFloat("vUp", &up, sizeof(FVECTOR3)); + pBlur->SetFloat("vCp", &cp, sizeof(FVECTOR3)); + + if (!pBlur->Execute(true)) { + LogErr("pBlur Execute Failed"); + return false; + } + + pBlrTemp[mip-1]->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &pTmp); + pDevice->StretchRect(pSrf, NULL, pTmp, NULL, D3DTEXF_POINT); + SAFE_RELEASE(pSrf); + SAFE_RELEASE(pTmp); + } + + pBlur->SetBool("bDir", true); + + for (DWORD i = 0; i < 6; i++) { + + EnvMapDirection(i, &dir, &up); + cp = unit(crossp(up, dir)); + + pSrc->GetCubeMapSurface(D3DCUBEMAP_FACES(i), mip, &pSrf); + + pBlur->SetOutputNative(0, pSrf); + pBlur->SetFloat("vDir", &dir, sizeof(FVECTOR3)); + pBlur->SetFloat("vUp", &up, sizeof(FVECTOR3)); + pBlur->SetFloat("vCp", &cp, sizeof(FVECTOR3)); + + if (!pBlur->Execute(true)) { + LogErr("pBlur Execute Failed"); + return false; + } + + pBlrTemp[mip]->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &pTmp); + pDevice->StretchRect(pSrf, NULL, pTmp, NULL, D3DTEXF_POINT); + SAFE_RELEASE(pSrf); + SAFE_RELEASE(pTmp); + } + } + + return true; +} + +// =========================================================================================== +// +bool Scene::RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DTEXTURE9 pSrc) +{ + bool bQuality = true; + + if (!pSrc) return false; + + if (!pBlur2D) { + pBlur2D = new ImageProcessing(pDev, "Modules/vkShaders/EnvMapBlur.hlsl", "PS2DBlur"); + } + + if (!pBlur2D->IsOK()) { + LogErr("pBlur is not OK"); + return false; + } + + if (!pEnvDS) { + LogErr("EnvDepthStencil doesn't exists"); + return false; + } + + D3DSURFACE_DESC desc; + pEnvDS->GetDesc(&desc); + DWORD width = min((UINT)512, desc.Width); + DWORD height = min((UINT)512, desc.Height); + + if (!pBlrTemp2D[0]) { + if (D3DXCreateTexture(pDev, width >> 0, height >> 0, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[0]) != S_OK) return false; + if (D3DXCreateTexture(pDev, width >> 1, height >> 1, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[1]) != S_OK) return false; + if (D3DXCreateTexture(pDev, width >> 2, height >> 2, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[2]) != S_OK) return false; + if (D3DXCreateTexture(pDev, width >> 3, height >> 3, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[3]) != S_OK) return false; + if (D3DXCreateTexture(pDev, width >> 4, height >> 4, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &pBlrTemp2D[4]) != S_OK) return false; + } + + LPDIRECT3DSURFACE9 pSrf = NULL; + LPDIRECT3DSURFACE9 pTmp = NULL; + + // Copy Src to Temp + // + pSrc->GetSurfaceLevel(0, &pSrf); + pBlrTemp2D[0]->GetSurfaceLevel(0, &pTmp); + pDevice->StretchRect(pSrf, NULL, pTmp, NULL, D3DTEXF_POINT); + SAFE_RELEASE(pSrf); + SAFE_RELEASE(pTmp); + + // Create blurred mip sub-levels + // + for (int mip = 1; mip < 5; mip++) { + + pBlur2D->SetFloat("fD", (4.0f / float(256 >> (mip - 1)))); + pBlur2D->SetBool("bDir", false); + pBlur2D->SetTextureNative("tTex", pBlrTemp2D[mip - 1], IPF_LINEAR); + + pSrc->GetSurfaceLevel(mip, &pSrf); + pBlur2D->SetOutputNative(0, pSrf); + + if (!pBlur2D->Execute(true)) { + LogErr("pBlur2D Execute Failed"); + return false; + } + + pBlrTemp2D[mip - 1]->GetSurfaceLevel(0, &pTmp); + pDevice->StretchRect(pSrf, NULL, pTmp, NULL, D3DTEXF_POINT); + SAFE_RELEASE(pTmp); + + pBlur2D->SetBool("bDir", true); + pSrc->GetSurfaceLevel(mip, &pSrf); + pBlur2D->SetOutputNative(0, pSrf); + + if (!pBlur2D->Execute(true)) { + LogErr("pBlur Execute Failed"); + return false; + } + + pBlrTemp2D[mip]->GetSurfaceLevel(0, &pTmp); + pDevice->StretchRect(pSrf, NULL, pTmp, NULL, D3DTEXF_POINT); + SAFE_RELEASE(pTmp); + SAFE_RELEASE(pSrf); + } + + return true; +} + +// =========================================================================================== +// +bool Scene::IntegrateIrradiance(vVessel *vV, ENVCAMREC *ec, bool bInterior) +{ + if (!ec) return false; + + LPDIRECT3DCUBETEXTURE9 pSrc = ec->pCube; + LPDIRECT3DTEXTURE9 pOut = ec->pIrrad; + + if (!pIrradiance) { + pIrradiance = new ImageProcessing(pDevice, "Modules/vkShaders/IrradianceInteg.hlsl", "PSInteg"); + pIrradiance->CompileShader("PSPostBlur"); + pIrradiance->CompileShader("PSPostBlurIntr"); + } + + if (!pIrradiance->IsOK()) { + LogErr("pIrradiance is not OK"); + return false; + } + + D3DSURFACE_DESC desc; + LPDIRECT3DSURFACE9 pOuts = NULL; + + HR(pOut->GetSurfaceLevel(0, &pOuts)); + HR(pOuts->GetDesc(&desc)); + + + FVECTOR3 nr, up, cp; + LPDIRECT3DSURFACE9 pTmp = NULL; + + UINT size = bInterior ? 3 : 1; + UINT W = desc.Width * size; + UINT H = desc.Height * size; + + + + if (!pIrradTemp) { + if (D3DXCreateTexture(pDevice, W, H, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pIrradTemp) != S_OK) { + LogErr("Failed to create irradiance temp"); + return false; + } + } + + HR(pIrradTemp->GetSurfaceLevel(0, &pTmp)); + + // --------------------------------------------------------------------- + // Main Integration + // + GetLVLH(vV, &up, &nr, &cp); + + float Glow = float(Config->PlanetGlow); + + if (bInterior) Glow = 4.0f; + + pIrradiance->Activate("PSInteg"); + pIrradiance->SetOutputNative(0, pTmp); + pIrradiance->SetTextureNative("tCube", pSrc, IPF_LINEAR); + pIrradiance->SetTextureNative("tRandom", ptRandom, IPF_POINT); + pIrradiance->SetFloat("Kernel", IKernel, sizeof(IKernel)); + pIrradiance->SetFloat("vNr", &nr, sizeof(FVECTOR3)); + pIrradiance->SetFloat("vUp", &up, sizeof(FVECTOR3)); + pIrradiance->SetFloat("vCp", &cp, sizeof(FVECTOR3)); + pIrradiance->SetFloat("fIntensity", &Glow, sizeof(float)); + pIrradiance->SetBool("bUp", false); + pIrradiance->SetTemplate(0.5f, 1.0f, 0.0f, 0.0f); + + if (!pIrradiance->Execute(true)) { + LogErr("pIrradiance Execute Failed"); + return false; + } + + pIrradiance->SetBool("bUp", true); + pIrradiance->SetTemplate(0.5f, 1.0f, 0.5f, 0.0f); + + if (!pIrradiance->Execute(true)) { + LogErr("pIrradiance Execute Failed"); + return false; + } + + SAFE_RELEASE(pTmp); + + // --------------------------------------------------------------------- + // Post Blur + // + if (bInterior) pIrradiance->Activate("PSPostBlurIntr"); + else pIrradiance->Activate("PSPostBlur"); + + pIrradiance->SetFloat("fD", &(FVECTOR2(1.0f / float(W), 1.0f / float(H))), sizeof(FVECTOR2)); + pIrradiance->SetOutputNative(0, pOuts); + pIrradiance->SetTextureNative("tSrc", pIrradTemp, IPF_POINT | IPF_WRAP); + + if (!pIrradiance->Execute(true)) { + LogErr("pIrradiance Execute Failed"); + return false; + } + + SAFE_RELEASE(pOuts); + + return true; +} + + + +// =========================================================================================== +// +void Scene::ClearOmitFlags() +{ + for (auto pv : Visuals) pv->vobj->bOmit = false; +} + + +// =========================================================================================== +// +void Scene::VisualizeCubeMap(LPDIRECT3DCUBETEXTURE9 pCube, int mip) +{ + if (!pCube) return; + + LPDIRECT3DSURFACE9 pSrf = NULL; + LPDIRECT3DSURFACE9 pBack = gc->GetBackBuffer(); + + D3DSURFACE_DESC bdesc; + + if (!pBack) return; + + HR(pBack->GetDesc(&bdesc)); + + DWORD x, y, h = bdesc.Height / 3; + + for (DWORD i=0;i<6;i++) { + + HR(pCube->GetCubeMapSurface(D3DCUBEMAP_FACES(i), mip, &pSrf)); + + switch (i) { + case 0: x = 2*h; y=h; break; + case 1: x = 0; y=h; break; + case 2: x = 1*h; y=0; break; + case 3: x = 1*h; y=2*h; break; + case 4: x = 1*h; y=h; break; + case 5: x = 3*h; y=h; break; + } + + RECT dr; + dr.left = x; + dr.top = y; + dr.bottom = y+h; + dr.right = x+h; + + HR(pDevice->StretchRect(pSrf, NULL, pBack, &dr, D3DTEXF_POINT)); + + SAFE_RELEASE(pSrf); + } +} + +// =========================================================================================== +// +const SHADOWMAP* Scene::GetSMapData(ShdPackage tp) const +{ + if (tp == ShdPackage::Main) return smEX; + if (tp == ShdPackage::VC) return smVC; + if (tp == ShdPackage::Stage) return smSS; + return nullptr; +} + + +// =========================================================================================== +// Optional parameters 'sm' or 'pTex' not both +// +void Scene::VisualizeShadowMap(SHADOWMAP *sm) +{ + LPDIRECT3DTEXTURE9 pTex = sm ? sm->Map(0) : nullptr; + if (!pTex) return; + + D3DSURFACE_DESC desc; + pTex->GetLevelDesc(0, &desc); + + DWORD s = 0; + float w, h; + if (desc.Height > viewH) w = h = float(s = viewH); + else w = h = float(s = desc.Height); + + vkPad *pSketch = GetPooledSketchpad(SKETCHPAD_2D_OVERLAY); + + if (sm) { + for (int i = 0; i < sm->cascades; i++) + { + int l = int(w * sm->Subrect[i].x); + int t = int(h * sm->Subrect[i].y); + int r = int(w * sm->Subrect[i].z); + int b = int(h * sm->Subrect[i].w); + + RECT tgt = { l, t, r, b }; + auto map = sm->Map(i); + if (map) { + pSketch->SetRenderParam(Sketchpad::RenderParam::PRM_GAMMA, &(FVECTOR4(0.25f, 0.25f, 0.25f, 1.0f))); + pSketch->StretchRectNative(map, nullptr, &tgt); + pSketch->SetRenderParam(Sketchpad::RenderParam::PRM_GAMMA, nullptr); + } + pSketch->QuickBrush(0); + pSketch->QuickPen(0xFFFF00FF); + pSketch->Rectangle(l, t, r, b); + } + } + else if (pTex) { + RECT tgt = { 0, 0, long(s), long(s) }; + pSketch->SetRenderParam(Sketchpad::RenderParam::PRM_GAMMA, &(FVECTOR4(0.25f, 0.25f, 0.25f, 1.0f))); + pSketch->StretchRectNative(pTex, nullptr, &tgt); + pSketch->SetRenderParam(Sketchpad::RenderParam::PRM_GAMMA, nullptr); + } + pSketch->EndDrawing(); +} + + + +// =========================================================================================== +// +void Scene::RenderVesselShadows (OBJHANDLE hPlanet, float depth) const +{ + // If this planet is not a proxy body skip the rest + if (hPlanet != oapiCameraProxyGbody()) return; + + // render vessel shadows + for (auto pv : Visuals) { + if (!pv->vobj->IsActive()) continue; + if (oapiGetObjectType(pv->vobj->Object()) == OBJTP_VESSEL) + ((vVessel*)(pv->vobj))->RenderGroundShadow(pDevice, hPlanet, depth); + } + + // reset device parameters + pDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); + + // render particle shadows + LPDIRECT3DTEXTURE9 tex = 0; + for (DWORD j=0;jRenderGroundShadow(pDevice, tex); +} + + +// =========================================================================================== +// +void Scene::RenderMesh(DEVMESHHANDLE hMesh, const oapi::FMATRIX4 *pWorld) +{ + vkMesh *pMesh = (vkMesh *)hMesh; + + float s = float(smEX->size); + float sr = 2.0f * smEX->rad / s; + + HR(vkEffect::FX->SetMatrix(vkEffect::eLVP, _DX(smEX->mLVP))); + + if (smEX->IsValid()) { + pMesh->SetShadows(smEX); + HR(vkEffect::FX->SetVector(vkEffect::eSHD, _DX(FVECTOR4(sr, 1.0f / s, float(oapiRand()), 1.0f / smEX->depth)))); + HR(vkEffect::FX->SetBool(vkEffect::eShadowToggle, true)); + } + else { + pMesh->SetShadows(NULL); + HR(vkEffect::FX->SetBool(vkEffect::eShadowToggle, false)); + } + + pMesh->SetSunLight(&sunLight); + pMesh->RenderSimplified(pWorld); +} + + +// =========================================================================================== +// +bool Scene::WorldToScreenSpace(const VECTOR3 &wpos, oapi::IVECTOR2 *pt, FMATRIX4 *pVP, float clip) +{ + FVECTOR4 homog; + FVECTOR3 pos(float(wpos.x), float(wpos.y), float(wpos.z)); + + if (pVP) D3DMAT_Transform(&homog, &_F4(pos, 1.0f), pVP); + else D3DMAT_Transform(&homog, &_F4(pos, 1.0f), GetProjectionViewMatrix()); + + if (homog.w < 0.0f) return false; + + homog.x /= homog.w; + homog.y /= homog.w; + + bool bClip = false; + if (homog.x < -clip || homog.x > clip || homog.y < -clip || homog.y > clip) bClip = true; + + if (std::hypot(homog.x, homog.y) < 1e-6) { + pt->x = viewW / 2; + pt->y = viewH / 2; + } + else { + pt->x = (long)((float(viewW) * 0.5f * (1.0f + homog.x)) + 0.5f); + pt->y = (long)((float(viewH) * 0.5f * (1.0f - homog.y)) + 0.5f); + } + + return !bClip; +} + + +// =========================================================================================== +// +bool Scene::WorldToScreenSpace2(const VECTOR3& wpos, oapi::FVECTOR2* pt, FMATRIX4* pVP, float clip) +{ + FVECTOR4 homog; + FVECTOR3 pos(float(wpos.x), float(wpos.y), float(wpos.z)); + + if (pVP) D3DMAT_Transform(&homog, &_F4(pos, 1.0f), pVP); + else D3DMAT_Transform(&homog, &_F4(pos, 1.0f), GetProjectionViewMatrix()); + + homog.x /= homog.w; + homog.y /= homog.w; + + bool bClip = false; + if (homog.w < 0.0f) bClip = true; + if (homog.x < -clip || homog.x > clip || homog.y < -clip || homog.y > clip) bClip = true; + + if (std::hypot(homog.x, homog.y) < 1e-6) { + pt->x = viewW / 2; + pt->y = viewH / 2; + } + else { + pt->x = (float(viewW) * 0.5f * (1.0f + homog.x)) + 0.5f; + pt->y = (float(viewH) * 0.5f * (1.0f - homog.y)) + 0.5f; + } + + return !bClip; +} + +// =========================================================================================== +// +void Scene::RenderObjectMarker(oapi::Sketchpad *pSkp, const VECTOR3 &gpos, const std::string& label1, const std::string& label2, int mode, int scale) +{ + VECTOR3 dp (gpos - GetCameraGPos()); + normalise (dp); + m_celSphere->RenderMarker(pSkp, dp, label1, label2, mode, scale); +} + +// =========================================================================================== +// +void Scene::AddParticleStream (class vkParticleStream *_pstream) +{ + + vkParticleStream **tmp = new vkParticleStream*[nstream+1]; + if (nstream) { + memcpy (tmp, pstream, nstream*sizeof(vkParticleStream*)); + delete []pstream; + } + pstream = tmp; + pstream[nstream++] = _pstream; + +} + +// =========================================================================================== +// +void Scene::DelParticleStream (DWORD idx) +{ + + vkParticleStream **tmp; + if (nstream > 1) { + DWORD i, j; + tmp = new vkParticleStream*[nstream-1]; + for (i = j = 0; i < nstream; i++) + if (i != idx) tmp[j++] = pstream[i]; + } else tmp = 0; + delete pstream[idx]; + delete []pstream; + pstream = tmp; + nstream--; + +} + +// =========================================================================================== +// +void Scene::InitGDIResources () +{ + char dbgfnt[64]; sprintf_s(dbgfnt,64,"*%s",Config->DebugFont); + pAxisFont = oapiCreateFont(24, false, "Arial", FONT_NORMAL, 0); + pLabelFont = oapiCreateFont(15, false, "Arial", FONT_NORMAL, 0); + pDebugFont = oapiCreateFont(Config->DebugFontSize, true, dbgfnt, FONT_NORMAL, 0); + + const int fsize[4] = { 12, 16, 20, 26 }; + for (int i = 0; i < 4; ++i) { + label_font[i] = gc->clbkCreateFont(fsize[i], true, "Arial", FONT_BOLD); + } + //@todo: different pens for different fonts? +} + +// =========================================================================================== +// +void Scene::ExitGDIResources () +{ + oapiReleaseFont(pAxisFont); + oapiReleaseFont(pLabelFont); + oapiReleaseFont(pDebugFont); + + for (int i = 0; i < 4; ++i) { + gc->clbkReleaseFont(label_font[i]); + } +} + +// =========================================================================================== +// +float Scene::GetDepthResolution(float dist) const +{ + return fabs( (Camera.nearplane-Camera.farplane)*(dist*dist) / (Camera.farplane * Camera.nearplane * 16777215.0f) ); +} + +// =========================================================================================== +// +float Scene::CameraInSpace() const +{ + if (Camera.vProxy) { + if (Camera.vProxy->HasAtmosphere()) { + ConstParams* cp = Camera.vProxy->GetScatterConst(); + if (cp) return 1.0f - exp(-cp->CamAlt * cp->iH.x); + } + } + return 1.0f; +} + +// =========================================================================================== +// +void Scene::GetCameraLngLat(double *lng, double *lat) const +{ + if (lng) *lng = Camera.lng; + if (lat) *lat = Camera.lat; +} + +// =========================================================================================== +// +void Scene::PushCamera() +{ + CameraStack.push(Camera); +} + +// =========================================================================================== +// +void Scene::PopCamera() +{ + Camera = CameraStack.top(); + CameraStack.pop(); +} + +// =========================================================================================== +// +FMATRIX4 Scene::PushCameraFrustumLimits(float nearlimit, float farlimit) +{ + FRUSTUM fr = { Camera.nearplane, Camera.farplane }; + FrustumStack.push(fr); + SetCameraFrustumLimits(nearlimit, farlimit); + return FMATRIX4(GetProjectionViewMatrix()); +} + +// =========================================================================================== +// +FMATRIX4 Scene::PopCameraFrustumLimits() +{ + SetCameraFrustumLimits(FrustumStack.top().znear, FrustumStack.top().zfar); + FrustumStack.pop(); + return FMATRIX4(GetProjectionViewMatrix()); +} + +// =========================================================================================== +// +void Scene::BeginPass(DWORD dwPass) +{ + PassStack.push(dwPass); +} + +// =========================================================================================== +// +void Scene::PopPass() +{ + PassStack.pop(); +} + +// =========================================================================================== +// +DWORD Scene::GetRenderPass() const +{ + if (PassStack.empty()) return RENDERPASS_MAINSCENE; + return PassStack.top(); +} + +// =========================================================================================== +// +FVECTOR3 Scene::GetPickingRay(short xpos, short ypos) +{ + float x = 2.0f*float(xpos) / float(ViewW()) - 1.0f; + float y = 2.0f*float(ypos) / float(ViewH()) - 1.0f; + FVECTOR3 vPick = Camera.x * (x / Camera.mProj.m11) + Camera.y * (-y / Camera.mProj.m22) + Camera.z; + normalize(vPick); + return vPick; +} + +// =========================================================================================== +// +TILEPICK Scene::PickSurface(short xpos, short ypos) +{ + TILEPICK tp; memset(&tp, 0, sizeof(TILEPICK)); + vPlanet *vp = GetCameraProxyVisual(); + if (!vp) return tp; + FVECTOR3 vRay = GetPickingRay(xpos, ypos); + vp->PickSurface(vRay, &tp); + return tp; +} + +// =========================================================================================== +// +vkPick Scene::PickScene(short xpos, short ypos, const PickProp *p) +{ + FVECTOR3 vPick = GetPickingRay(xpos, ypos); + + vkPick result; + result.dist = 1e30f; + result.pMesh = NULL; + result.vObj = NULL; + result.group = -1; + result.idx = -1; + + for (auto pv : Visuals) { + + if (pv->type!=OBJTP_VESSEL) continue; + if (!pv->vobj->IsActive()) continue; + if (!pv->vobj->IsVisible()) continue; + + vVessel *vVes = (vVessel *)pv->vobj; + double cd = vVes->CamDist(); + + if (cd<5e3 && cd>1e-3) { + vkPick pick = vVes->Pick(&vPick, p); + if (pick.pMesh) if (pick.distPick(pW, NULL, &(GetPickingRay(xpos, ypos)), &prp); +} + +// =========================================================================================== +// +void Scene::GetAdjProjViewMatrix(FMATRIX4* pMP, float znear, float zfar) +{ + float tanap = tan(Camera.aperture); + ZeroMemory(pMP, sizeof(FMATRIX4)); + pMP->m11 = (Camera.aspect / tanap); + pMP->m22 = (1.0f / tanap); + pMP->m43 = (pMP->m33 = zfar / (zfar - znear)) * (-znear); + pMP->m34 = 1.0f; +} + +// =========================================================================================== +// +void Scene::SetCameraAperture(float ap, float as) +{ + Camera.aperture = ap; + Camera.aspect = as; + + float tanap = tan(ap); + float cor = sqrt(tanap * tanap + tanap * tanap * as * as); + Camera.corner = atan(cor); + + ZeroMemory(&Camera.mProj, sizeof(FMATRIX4)); + + Camera.mProj.m11 = (as / tanap); + Camera.mProj.m22 = (1.0f / tanap); + Camera.mProj.m43 = (Camera.mProj.m33 = Camera.farplane / (Camera.farplane-Camera.nearplane)) * (-Camera.nearplane); + Camera.mProj.m34 = 1.0f; + + float x = tanap / as; + float y = tanap; + float z = as / tanap; + + Camera.apsq = sqrt(x*x*x*z + y*y); + + Camera.vh = tan(ap); + Camera.vw = Camera.vh/as; + Camera.vhf = 1.0f / cos(ap); + Camera.vwf = Camera.vhf/as; + + oapiMatrixMultiply(&Camera.mProjView, &Camera.mView, &Camera.mProj); + vkEffect::SetViewProjMatrix(&Camera.mProjView); +} + +// =========================================================================================== +// +void Scene::SetCameraFrustumLimits (double nearlimit, double farlimit) +{ + Camera.nearplane = (float)nearlimit; + Camera.farplane = (float)farlimit; + SetCameraAperture(Camera.aperture, Camera.aspect); +} + +// =========================================================================================== +// +bool Scene::IsProxyMesh() +{ + return Camera.vProxy ? Camera.vProxy->IsMesh() : false; +} + +// =========================================================================================== +// +bool Scene::CameraPan(VECTOR3 pan, double speed) +{ + DWORD camMode = *(DWORD *)gc->GetConfigParam(CFGPRM_GETCAMERAMODE); + OBJHANDLE hTgt = oapiCameraTarget(); + + if (DebugControls::IsActive()==true && hTgt) { + if (camMode==1) { + VECTOR3 pos; + oapiGetGlobalPos(hTgt, &pos); + Camera.pos = pos + Camera.relpos; + Camera.pos += Camera.dir * (pan.z*speed) + _V(Camera.x) * (pan.x*speed) + _V(Camera.y) * (pan.y*speed); + Camera.relpos = Camera.pos - pos; + return true; + } + } + return false; +} + + +// =========================================================================================== +// +bool Scene::UpdateCameraFromOrbiter(DWORD dwPass) +{ + MATRIX3 grot; + VECTOR3 pos; + + DWORD camMode = *(DWORD *)gc->GetConfigParam(CFGPRM_GETCAMERAMODE); + + OBJHANDLE hTgt = oapiCameraTarget(); + + if (hTgt) { + if (DebugControls::IsActive()==false || camMode==0) { + // Acquire camera information from Orbiter + oapiGetGlobalPos(hTgt, &pos); + oapiCameraGlobalPos(&Camera.pos); + Camera.relpos = Camera.pos - pos; // camera_relpos is a mesh debugger paramater + } + else { + // Mesh debugger camera mode active + oapiGetGlobalPos(hTgt, &pos); + Camera.pos = pos + Camera.relpos; // Compute from target pos and offset + } + } + else { + // Camera target doesn't exist. (Should not happen) + oapiCameraGlobalPos(&Camera.pos); + Camera.relpos = _V(0,0,0); + } + + oapiCameraGlobalDir(&Camera.dir); + oapiCameraRotationMatrix(&grot); + oapiMatrixIdentity(&Camera.mView); + D3DMAT_SetRotation(&Camera.mView, &grot); + + // note: in render space, the camera is always placed at the origin, + // so that render coordinates are precise in the vicinity of the + // observer (before they are translated into D3D single-precision + // format). However, the orientation of the render space is the same + // as orbiter's global coordinate system. Therefore there is a + // translational transformation between orbiter global coordinates + // and render coordinates. + + for (auto pv : Visuals) pv->vobj->Update(true); + + return SetupInternalCamera(&Camera.mView, NULL, oapiCameraAperture(), double(viewH)/double(viewW)); +} + + +// =========================================================================================== +// +void Scene::CameraOffOrigin90(FMATRIX4 *mView, FVECTOR3 pos) +{ + Camera.mView = *mView; + Camera.x = FVECTOR3(Camera.mView.m11, Camera.mView.m21, Camera.mView.m31); + Camera.y = FVECTOR3(Camera.mView.m12, Camera.mView.m22, Camera.mView.m32); + Camera.z = FVECTOR3(Camera.mView.m13, Camera.mView.m23, Camera.mView.m33); + + auto x = FVECTOR3(Camera.mView.m11, Camera.mView.m12, Camera.mView.m13); + auto y = FVECTOR3(Camera.mView.m21, Camera.mView.m22, Camera.mView.m23); + auto z = FVECTOR3(Camera.mView.m31, Camera.mView.m32, Camera.mView.m33); + + Camera.mView.m14 = -dotp(x, pos); + Camera.mView.m24 = -dotp(y, pos); + Camera.mView.m34 = -dotp(z, pos); + + SetCameraAperture(0.7853981634, 1.0f); +} + + +// =========================================================================================== +// +bool Scene::SetupInternalCamera(FMATRIX4 *mNew, VECTOR3 *gpos, double apr, double asp) +{ + + // Update camera orientation if a new matrix is provided + if (mNew) { + Camera.mView = *mNew; + Camera.x = FVECTOR3(Camera.mView.m11, Camera.mView.m21, Camera.mView.m31); + Camera.y = FVECTOR3(Camera.mView.m12, Camera.mView.m22, Camera.mView.m32); + Camera.z = FVECTOR3(Camera.mView.m13, Camera.mView.m23, Camera.mView.m33); + Camera.dir = _V(Camera.z); + } + + if (gpos) Camera.pos = *gpos; + + Camera.upos = _F(unit(Camera.pos)); + + // find a logical reference body + Camera.hObj_proxy = oapiCameraProxyGbody(); + Camera.hNear = NULL; + Camera.hGravRef = NULL; + Camera.vGravRef = NULL; + + // find the planet closest to the current camera position + double closest = 0; + int n = oapiGetGbodyCount(); + for (int i = 1; i < n; i++) { + VECTOR3 gp; OBJHANDLE hB = oapiGetGbodyByIndex(i); + oapiGetGlobalPos(hB, &gp); + VECTOR3 dst = gp - Camera.pos; + double l = pow(oapiGetMass(hB), 0.33) / dotp(dst, dst); + if (l > closest) { + closest = l; + Camera.hNear = hB; + } + } + + // find the planet closest to the current camera position + closest = 0; + for (int i = 0; i < n; i++) { + VECTOR3 gp; OBJHANDLE hB = oapiGetGbodyByIndex(i); + oapiGetGlobalPos(hB, &gp); + VECTOR3 dst = gp - Camera.pos; + double l = oapiGetMass(hB) / dotp(dst, dst); + if (l > closest) { + closest = l; + Camera.hGravRef = hB; + } + } + + /*if (Camera.hNear) { + // If the near body is not visible enough, switch to proxy. + double apr = oapiGetSize(Camera.hNear) / closest; + if (apr < 4e-3) Camera.hNear = Camera.hObj_proxy; + }*/ + + // find the visual + if (oapiGetObjectType(Camera.hObj_proxy) == OBJTP_PLANET) + Camera.vProxy = (vPlanet *)GetVisObject(Camera.hObj_proxy); + else Camera.vProxy = nullptr; + + if (oapiGetObjectType(Camera.hNear) == OBJTP_PLANET) + Camera.vNear = (vPlanet *)GetVisObject(Camera.hNear); + else Camera.vNear = nullptr; + + Camera.vGravRef = GetVisObject(Camera.hGravRef); + + + if (Camera.hGravRef == NULL || Camera.hObj_proxy == NULL || Camera.hNear == NULL) { + assert(false); return false; + } + if (Camera.vGravRef == NULL || Camera.vProxy == NULL || Camera.vNear == NULL) { + return false; + } + + // Camera altitude over the proxy + VECTOR3 pos; MATRIX3 grot; double rad; + oapiGetGlobalPos(Camera.hObj_proxy, &pos); + oapiGetRotationMatrix(Camera.hObj_proxy, &grot); + + oapiLocalToEqu(Camera.hObj_proxy, tmul(grot, Camera.pos - pos), &Camera.lng, &Camera.lat, &rad); + + Camera.alt_proxy = dist(Camera.pos, pos) - oapiGetSize(Camera.hObj_proxy); + + if (Camera.vProxy) { + if (Camera.vProxy->Type() == OBJTP_PLANET) + rad = oapiSurfaceElevation(Camera.hObj_proxy, Camera.lng, Camera.lat); + } + + Camera.elev = Camera.alt_proxy - rad; + + // Camera altitude over the proxy + oapiGetGlobalPos(Camera.hNear, &pos); + Camera.alt_near = dist(Camera.pos, pos) - oapiGetSize(Camera.hNear); + + // Call SetCameraAparture to update ViewProj Matrix + SetCameraAperture(float(apr), float(asp)); + + // Finally update world matrices from all visuals + ResetOrigin(Camera.pos); + + return true; +} + + +// =========================================================================================== +// +void Scene::ResetOrigin(VECTOR3 pos) +{ + // Update world matrices from all visuals + if (origin.x != pos.x || origin.y != pos.y || origin.z != pos.z) + for (auto pv : Visuals) pv->vobj->ReOrigin(pos); + origin = pos; +} + + +// =========================================================================================== +// CUSTOM CAMERA INTERFACE +// =========================================================================================== + +int Scene::DeleteCustomCamera(CAMERAHANDLE hCam) +{ + if (!hCam) return 0; + int iError = CAMERA(hCam)->iError; + CustomCams.erase(CAMERA(hCam)); + delete CAMERA(hCam); + camCurrent = CustomCams.cbegin(); + return iError; +} + +// =========================================================================================== +// +void Scene::DeleteAllCustomCameras() +{ + for (auto x : CustomCams) delete CAMERA(x); + CustomCams.clear(); +} + +// =========================================================================================== +// +CAMERAHANDLE Scene::SetupCustomCamera(CAMERAHANDLE hCamera, OBJHANDLE hVessel, MATRIX3 &mRot, VECTOR3 &pos, double fov, SURFHANDLE hSurf, DWORD flags) +{ + CAMREC *pv = NULL; + + if (!hSurf) return NULL; + if (Config->CustomCamMode==0) return NULL; + if (SURFACE(hSurf)->Is3DRenderTarget()==false) return NULL; + + if (hCamera==NULL) { + pv = new CAMREC; memset(pv, 0, sizeof(CAMREC)); + CustomCams.insert(pv); + camCurrent = CustomCams.cbegin(); + } + else { + pv = (CAMREC *)hCamera; + } + + if (!pv) return NULL; + + pv->bActive = true; + pv->dAperture = fov; + pv->dwFlags = flags; + pv->hSurface = hSurf; + pv->mRotation = mRot; + pv->vPosition = pos; + pv->hVessel = hVessel; + pv->iError = 0; + + return (CAMERAHANDLE)pv; +} + +// =========================================================================================== +// +void Scene::CustomCameraOnOff(CAMERAHANDLE hCamera, bool bOn) +{ + if (!hCamera) return; + CAMERA(hCamera)->bActive = bOn; +} + +// =========================================================================================== +// +void Scene::RenderCustomCameraView(CAMREC *cCur) +{ + VESSEL *pVes = oapiGetVesselInterface(cCur->hVessel); + + DWORD w = SURFACE(cCur->hSurface)->GetWidth(); + DWORD h = SURFACE(cCur->hSurface)->GetHeight(); + + LPDIRECT3DSURFACE9 pSrf = SURFACE(cCur->hSurface)->GetSurface(); + LPDIRECT3DSURFACE9 pDSs = SURFACE(cCur->hSurface)->GetDepthStencil(); + + if (!pSrf) cCur->iError = -1; + if (!pDSs) cCur->iError = -2; + + if (cCur->iError!=0) return; + + MATRIX3 grot; + VECTOR3 gpos; + + pVes->GetRotationMatrix(grot); + pVes->Local2Global(cCur->vPosition, gpos); + + FMATRIX4 mEnv, mGlo; + + oapiMatrixIdentity(&mGlo); + D3DMAT_SetRotation(&mGlo, &grot); + oapiMatrixIdentity(&mEnv); + D3DMAT_SetRotation(&mEnv, &cCur->mRotation); + oapiMatrixMultiply(&mEnv, &mGlo, &mEnv); + + PushCamera(); + + SetCameraFrustumLimits(0.1, 2e7); + SetupInternalCamera(&mEnv, &gpos, cCur->dAperture, double(h)/double(w)); + + std::set List; + std::set Lights; + + for (auto pv : Visuals) if (pv->type == OBJTP_VESSEL) List.insert((vVessel *)pv->vobj); + + BeginPass(RENDERPASS_CUSTOMCAM); + + gc->PushRenderTarget(pSrf, pDSs, RENDERPASS_CUSTOMCAM); + + RenderSecondaryScene(List, Lights, SCN_ALLEXT); + + gc->PopRenderTargets(); + + PopPass(); + PopCamera(); +} + + +// =========================================================================================== +// +void Scene::RenderGlares() +{ + // ------------------------------------------------------------------------------------------------------- + // Render glares for the Sun and local lights + // ------------------------------------------------------------------------------------------------------- + + if (pRenderGlares && pLocalResultsSL) + { + static SMVERTEX Vertex[4] = { {-1, -1, 0, 0, 0}, {-1, 1, 0, 0, 1}, {1, 1, 0, 1, 1}, {1, -1, 0, 1, 0} }; + static WORD cIndex[6] = { 0, 2, 1, 0, 3, 2 }; + D3DSURFACE_DESC desc; FVECTOR2 pt; + struct { FMATRIX4 mVP; float4 Pos, Color; float GPUId, Alpha, Blend; } Const; + + Const.Color = _F4(1, 1, 1, 1); + D3DMAT_OrthoOffCenterLH(&Const.mVP, 0.0f, (float)viewW, (float)viewH, 0.0f, 0.0f, 1.0f); + pLocalResultsSL->GetDesc(&desc); + + pRenderGlares->ClearTextures(); + pRenderGlares->Setup(pPosTexDecl, false, 1); + pRenderGlares->SetTextureVS("tVis", pLocalResults, IPF_CLAMP | IPF_POINT); // Set texture containing pre-cumputed visibility factors + + if (Config->bGlares && pSunGlare) + { + pRenderGlares->SetTexture("tTex0", pSunGlare, IPF_CLAMP | IPF_LINEAR); + pRenderGlares->UpdateTextures(); + + // Render Sun glare + VECTOR3 gsun; oapiGetGlobalPos(oapiGetObjectByIndex(0), &gsun); + double sdst = length(gsun - Camera.pos); + VECTOR3 usun = (gsun - Camera.pos) / sdst; + VECTOR3 pos = usun * 10e4; + + if (WorldToScreenSpace2(pos, &pt)) + { + float cis = 1.0f, glare = float(Config->GFXGlare) * saturate(8.0 * AU / sdst); + FVECTOR4 clr = _F4(1, 1, 1, 1); + + vPlanet* vp = GetCameraNearVisual(); + + if (vp && vp->IsActive()) + { + VECTOR3 crp = vp->CameraPos(); + clr = vp->SunLightColor(crp, 2.0); + cis = CameraInSpace(); + glare *= pow(MaxRGB(clr), 0.33f) * cis; + } + + float cd = length(pt - FVECTOR2(viewW, viewH) * 0.5f) / float(viewW); // Glare distance from a screen center + float alpha = 2.0f * glare * max(0.5f, 1.0f - cd); + float size = 300.0f * GetDisplayScale() * pow(alpha, 0.25f); + + Const.GPUId = 0.5f / float(desc.Width); + Const.Pos = FVECTOR4(pt.x, pt.y, size, size); + Const.Color.rgb = clr.rgb / (MaxRGB(clr) + 0.0001f); + Const.Alpha = alpha * 2.0f; + Const.Blend = sqrt(cis); + + pRenderGlares->SetVSConstants("Const", &Const, sizeof(Const)); + pRenderGlares->SetPSConstants("Const", &Const, sizeof(Const)); + HR(pDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &cIndex, D3DFMT_INDEX16, &Vertex, sizeof(SMVERTEX))); + } + } + + if (Config->bLocalGlares && pLightGlare) + { + pRenderGlares->SetTexture("tTex0", pLightGlare, IPF_CLAMP | IPF_LINEAR); + pRenderGlares->UpdateTextures(); + + // Render glares for local lights + for (int i = 0; i < nLights; ++i) { + int GPUId = Lights[i].GPUId; + if (GPUId >= 0) { + if (WorldToScreenSpace2(_V(Lights[i].Position), &pt)) { + float size = 40.0f; + Const.GPUId = (float(GPUId) + 0.5f) / desc.Width; + Const.Pos = FVECTOR4(pt.x, pt.y, size, size); + Const.Alpha = Lights[i].cone; + Const.Color = Lights[i].Diffuse; + Const.Blend = 1.0f; + pRenderGlares->SetVSConstants("Const", &Const, sizeof(Const)); + pRenderGlares->SetPSConstants("Const", &Const, sizeof(Const)); + HR(pDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 4, 2, &cIndex, D3DFMT_INDEX16, &Vertex, sizeof(SMVERTEX))); + } + } + } + } + pRenderGlares->DetachTextures(); + } +} + + +// =========================================================================================== +// +bool Scene::IsVisibleInCamera(const FVECTOR3 *pCnt, float radius) +{ + float z = Camera.z.x*pCnt->x + Camera.z.y*pCnt->y + Camera.z.z*pCnt->z; + if (z<(-radius)) return false; + if (z<0) z=-z; + float y = Camera.y.x*pCnt->x + Camera.y.y*pCnt->y + Camera.y.z*pCnt->z; + if (y<0) y=-y; + if (y-(radius*Camera.vhf) > (Camera.vh*z)) return false; + float x = Camera.x.x*pCnt->x + Camera.x.y*pCnt->y + Camera.x.z*pCnt->z; + if (x<0) x=-x; + if (x-(radius*Camera.vwf) > (Camera.vw*z)) return false; + return true; +} + +// =========================================================================================== +// +bool Scene::CameraDirection2Viewport(const VECTOR3 &dir, int &x, int &y) +{ + + FVECTOR3 idir = FVECTOR3( -float(dir.x), -float(dir.y), -float(dir.z) ); + FVECTOR3 homog = oapiTransformCoord(&idir, &Camera.mProjView); + + if (homog.x >= -1.0f && homog.y <= 1.0f && homog.z >= 0.0) { + if (std::hypot(homog.x, homog.y) < 1e-6) { + x = viewW / 2, y = viewH / 2; + } else { + x = (int)(viewW*0.5f*(1.0f + homog.x)); + y = (int)(viewH*0.5f*(1.0f - homog.y)); + } + return true; + } + return false; +} + +// =========================================================================================== +// +void Scene::GlobalExit() +{ + SAFE_RELEASE(FX); +} + +// =========================================================================================== +// +void Scene::vkTechInit(LPDIRECT3DDEVICE9 pDev, const char *folder) +{ + char name[256]; + sprintf_s(name,256,"Modules/%s/SceneTech.fx", folder); + + // Create the Effect from a .fx file. + ID3DXBuffer* errors = 0; + + HR(D3DXCreateEffectFromFile(pDev, name, 0, 0, 0, 0, &FX, &errors)); + + if (errors) { + + // It's an error + // + if (strstr((char*)errors->GetBufferPointer(),"warning")==NULL) { + LogErr("Effect Error: %s",(char*)errors->GetBufferPointer()); + MessageBoxA(0, (char*)errors->GetBufferPointer(), "SceneTech.fx Error", 0); + return; + } + + // It's a warning + // + else { + LogErr("[Effect Warning: %s]",(char*)errors->GetBufferPointer()); + //MessageBoxA(0, (char*)errors->GetBufferPointer(), "CelSphereTech.fx Warning", 0); + } + } + + if (FX==0) { + LogErr("Failed to create an Effect (%s)",name); + return; + } + + eLine = FX->GetTechniqueByName("LineTech"); + eStar = FX->GetTechniqueByName("StarTech"); + eWVP = FX->GetParameterByName(0,"gWVP"); + eTex0 = FX->GetParameterByName(0,"gTex0"); + eColor = FX->GetParameterByName(0,"gColor"); + + vkCelestialSphere::vkTechInit(FX); +} diff --git a/OVP/VulkanClient/Scene.h b/OVP/VulkanClient/Scene.h new file mode 100644 index 000000000..e24019025 --- /dev/null +++ b/OVP/VulkanClient/Scene.h @@ -0,0 +1,606 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +// ============================================================== +// Class Scene (interface) +// +// A "Scene" represents the 3-D world as seen from a specific +// viewpoint ("camera"). Each scene therefore has a camera object +// associated with it. The Orbiter core supports a single +// camera, but in principle a graphics client could define +// multiple scenes and render them simultaneously into separate +// windows (or into MFD display surfaces, etc.) +// ============================================================== + +#ifndef __SCENE_H +#define __SCENE_H + +#include "Client.h" +#include "CelSphere.h" +#include "VObject.h" +#include +#include +#include + +class vObject; +class vPlanet; +class vVessel; +class vkParticleStream; +class vkText; +class vkPad; + +#define GBUF_COLOR 0 +#define GBUF_BLUR 1 +#define GBUF_TEMP 2 +#define GBUF_DEPTH 3 +#define GBUF_GDI 4 +#define GBUF_COUNT 5 // Buffer count + +#define TEX_NOISE 0 +#define TEX_CLUT 1 +#define TEX_COUNT 2 + +#define RENDERPASS_UNKNOWN 0x0000 +#define RENDERPASS_MAINSCENE 0x0001 +#define RENDERPASS_ENVCAM 0x0002 +#define RENDERPASS_CUSTOMCAM 0x0003 +#define RENDERPASS_SHADOWMAP 0x0004 +#define RENDERPASS_PICKSCENE 0x0005 +#define RENDERPASS_SKETCHPAD 0x0006 +#define RENDERPASS_MAINOVERLAY 0x0007 +#define RENDERPASS_NORMAL_DEPTH 0x0008 +#define RENDERPASS_VC_SHADOWMAP 0x0009 +#define RENDERPASS_STAGESET 0x000A + +#define RESTORE ((LPDIRECT3DSURFACE9)(-1)) +#define CURRENT ((LPDIRECT3DSURFACE9)(-2)) + +#define RENDERTURN_ENVCAM 0 +#define RENDERTURN_CUSTOMCAM 1 +#define RENDERTURN_LAST 1 + +#define SMAP_MODE_FOCUS 1 +#define SMAP_MODE_SCENE 2 + +#define OBJTP_BUILDING 1000 + +// Secundary scene render flags +#define SCN_PLANETS 0x1 +#define SCN_VESSELS 0x2 +#define SCN_EXHAUST 0x4 +#define SCN_BEACONS 0x8 +#define SCN_PARTICLES 0x10 +#define SCN_BASESTRUCT 0x20 +#define SCN_ALLEXT 0x3F ///< All exterior features +#define SCN_VC 0x40 ///< Virtual cockpit +#define SCN_STAGE 0x1000 ///< Render a stage around the world. Cude texture needed. +#define SCN_NOCLEAR 0x2000 ///< Do not clear render target + +#define CAMERA(x) ((Scene::CAMREC*)x) + + + + +class Scene { + + friend class vkCelestialSphere; + + // Visual record =================================================================== + // + struct VOBJREC { // linked list of object visuals + vObject *vobj; // visual instance + int type; + float apprad; + }; + + +public: + + FVECTOR3 vPickRay; + bool bStageSet = false; + + struct FRUSTUM { + float znear; + float zfar; + }; + + // Custom camera parameters ======================================================== + // + struct CAMREC { + MATRIX3 mRotation; + VECTOR3 vPosition; + double dAperture; + SURFHANDLE hSurface; + OBJHANDLE hVessel; + DWORD dwFlags; + int iError; + bool bActive; + __gcRenderProc pRenderProc; + void* pUser; + }; + + std::list InteriorCams; + std::list Planets; + std::list Visuals; + std::set RootList; + std::set Vessels; + std::set eCamRenderList; + std::set CustomCams; + std::set::const_iterator camCurrent; + std::set::const_iterator vobjEnv, vobjIP; + std::list::const_iterator itIC; + + + // Camera frustum parameters ======================================================== + // + struct CAMERA { + float corner; // corner to center aperture [rad] + float aperture; // aperture [rad] + float aspect; // aspect ratio + float nearplane; // frustum nearplane distance + float farplane; // frustum farplane distance + float apsq; + float vh, vw, vhf, vwf; + + VECTOR3 pos; // Global camera position + VECTOR3 relpos; // Relative camera position (Used by Mesh Debugger) + VECTOR3 dir; // Camera direction + + FVECTOR3 x; // Camera axis vector + FVECTOR3 y; // Camera axis vector + FVECTOR3 z; // Camera axis vector + FVECTOR3 upos; // Camera position unit vector + + FMATRIX4 mView; // D3DX view matrix for current camera state + FMATRIX4 mProj; // D3DX projection matrix for current camera state + FMATRIX4 mProjView; // D3DX combined projection view matrix + FMATRIX4 mProjViewInf; // D3DX combined projection view matrix, far plane at infinity + OBJHANDLE hTarget; // Current camera target, Mesh Debugger Related + + OBJHANDLE hObj_proxy; // closest celestial body + vPlanet * vProxy; // closest celestial body (visual) + double alt_proxy; // camera distance to surface of hObj_proxy + + OBJHANDLE hNear; // closest celestial body + vPlanet * vNear; // closest celestial body (visual) + + OBJHANDLE hGravRef; // closest celestial body + vObject* vGravRef; // closest celestial body (visual) + + double alt_near; + double lng, lat, elev; + }; + + // Screen space sun visual parameters ================================================== + // + struct SUNVISPARAMS { + float brightness; + bool visible; + FVECTOR2 position; + FVECTOR4 color; + }; + + SHADOWMAP* smEX = nullptr; // Exterior shadow map + SHADOWMAP* smVC = nullptr; // Virtual Cockpit shadow map + SHADOWMAP* smSS = nullptr; // Shadow map for rendering in a stage-set + + static void vkTechInit(LPDIRECT3DDEVICE9 pDev, const char *folder); + + /** + * \brief Release global parameters + */ + static void GlobalExit(); + + Scene (oapi::vkClient *_gc, DWORD w, DWORD h); + ~Scene (); + + /** + * \brief Get a pointer to the client + */ + //inline const oapi::vkClient *GetClient() const { return gc; } + inline oapi::vkClient *GetClient() const { return gc; } + + + void clbkOnOptionChanged(int cat, int item); + void clbkScenarioChanged(OBJHANDLE hV, ScnChgEvent e); + void clbkInitialise(); + + /** + * \brief Update camera position, visuals, etc. + */ + void clbkUpdate(); + + /** + * \brief Render the whole main scene + */ + void clbkRenderMainScene(); + + /** + * \brief Create a visual for a new vessel if within visual range. + * \param hVessel vessel object handle + */ + void clbkNewVessel(OBJHANDLE hVessel); + + /** + * \brief Delete a vessel visual prior to destruction of the logical vessel. + * \param hVessel vessel object handle + */ + void clbkDeleteVessel(OBJHANDLE hVessel); + + + + const vkSun *GetSun() const { return &sunLight; } + const vkLight *GetLight(int index) const; + const vkLight *GetLights() const { return Lights; } + DWORD GetLightCount() const { return nLights; } + vkPad* GetPooledSketchpad(int id); + void RecallDefaultState(); + float GetDisplayScale() const { return fDisplayScale; } + void CreateSunGlare(); + + + DWORD GetRenderPass() const; + DWORD GetRenderFlags() const { return RenderFlags; } + void BeginPass(DWORD dwPass); + void PopPass(); + + const SHADOWMAP* GetSMapData(ShdPackage tp) const; + + inline DWORD GetStencilDepth() const { return stencilDepth; } + + /** + * \brief Get the ambient background colour + */ + inline DWORD GetBgColour() const { return bg_rgba; } + + /** + * \brief Get the viewport dimension (width) + */ + inline const DWORD ViewW() const { return viewW; } + + /** + * \brief Get the viewport dimension (height) + */ + inline const DWORD ViewH() const { return viewH; } + + bool UpdateCamVis(); + + + + /** + * \brief Returns screen space sun visual parameters for Lens Flare rendering. + */ + SUNVISPARAMS GetSunScreenVisualState(); + + /** + * \brief Gets sun diffuse colour (accounting for atmospheric shift) + */ + FVECTOR4 GetSunDiffColor(); + + /** + * \brief Render a secondary scene. (Env Maps, Shadow Maps, MFD Camera Views) + */ + void RenderStageSet(const LPDIRECT3DCUBETEXTURE9 pCT); + void RenderSecondaryScene(std::set &RndList, + std::set &AdditionalLightsList, DWORD flags = SCN_ALLEXT, + const LPDIRECT3DCUBETEXTURE9 pCT = nullptr, SHADOWMAP* sm = nullptr); + + int RenderShadowMap(SMapInput* smp, SHADOWMAP* out, std::list& Casters, bool bInternal = false); + int RenderVCShadowMap(FVECTOR3& cdir, FVECTOR3& ld, std::list& Casters); + bool RenderVCProbes(vVessel *vFocus); + + bool IntegrateIrradiance(vVessel *vV, ENVCAMREC* ec, bool bInterior); + bool RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DCUBETEXTURE9 pSrc); + bool RenderBlurredMap(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DTEXTURE9 pSrc); + void RenderMesh(DEVMESHHANDLE hMesh, const oapi::FMATRIX4 *pWorld); + void RenderStage(LPDIRECT3DCUBETEXTURE9 pCT); + + LPDIRECT3DSURFACE9 GetEnvDepthStencil() const { return pEnvDS; } + LPDIRECT3DSURFACE9 GetBuffer(int id) const { return psgBuffer[id]; } + LPDIRECT3DTEXTURE9 GetSunTexture() const { return pSunTex; } + LPDIRECT3DTEXTURE9 GetSunGlareAtm() const { return pSunGlareAtm; } + + LPDIRECT3DSURFACE9 GetDepthStencilMatch(LPDIRECT3DSURFACE9 pRef); + LPDIRECT3DSURFACE9 GetDepthStencil(DWORD size); + + /** + * \brief Render any shadows cast by vessels on planet surfaces + * \param hPlanet handle of planet to cast shadows on + * \param depth shadow darkness parameter (0=none, 1=black) + * \note Uses stencil buffering if available and requested. Otherwise shadows + * are pure black. + * \note Requests for any planet other than that closest to the camera + * are ignored. + */ + void RenderVesselShadows(OBJHANDLE hPlanet, float depth) const; + + + + void AddParticleStream (class vkParticleStream *_pstream); + void DelParticleStream (DWORD idx); + + void AddLocalLight(const LightEmitter *le, const vObject *vo); + void ClearLocalLights(); + + /** + * \brief Get object radius in pixels using oapiCameraGlobalPos() + * \param hObj object handle + */ + double GetObjectAppRad(OBJHANDLE hObj) const; + + /** + * \brief Get object radius in pixels using a custom camera location. + * \param hObj object handle + */ + double GetObjectAppRad2(OBJHANDLE hObj) const; + + // Picking Functions ============================================================================================================ + // + FVECTOR3 GetPickingRay(short x, short y); + vkPick PickScene(short xpos, short ypos, const PickProp* p); + TILEPICK PickSurface(short xpos, short ypos); + vkPick PickMesh(DEVMESHHANDLE hMesh, const FMATRIX4* pW, short xpos, short ypos); + + void ClearOmitFlags(); + bool IsRendering() const { return bRendering; } + + + // Custom Camera Interface ====================================================================================================== + // + CAMERAHANDLE SetupCustomCamera(CAMERAHANDLE hCamera, OBJHANDLE hVessel, MATRIX3 &mRot, VECTOR3 &pos, double fov, SURFHANDLE hSurf, DWORD flags); + int DeleteCustomCamera(CAMERAHANDLE hCamera); + void DeleteAllCustomCameras(); + void CustomCameraOnOff(CAMERAHANDLE hCamera, bool bOn); + void RenderCustomCameraView(CAMREC *cCur); + + + // Camera Matrix Access ========================================================================================================= + // + void GetAdjProjViewMatrix(FMATRIX4* mP, float znear, float zfar); + const FMATRIX4* GetProjectionViewMatrix() const { return (FMATRIX4*)&Camera.mProjView; } + const FMATRIX4* GetProjectionMatrix() const { return (FMATRIX4*)&Camera.mProj; } + const FMATRIX4* GetViewMatrix() const { return (FMATRIX4*)&Camera.mView; } + + + // Main Camera Interface ========================================================================================================= + // + void SetCameraAperture(float _ap, float _as); + void SetCameraFrustumLimits(double nearlimit, double farlimit); + float GetDepthResolution(float dist) const; + float CameraInSpace() const; + void ResetOrigin(VECTOR3 pos); + + // Acquire camera information from the Orbiter and initialize internal camera setup + bool UpdateCameraFromOrbiter(DWORD dwPass); + + // Manually initialize client's internal camera setup + bool SetupInternalCamera(FMATRIX4 *mView, VECTOR3 *pos, double apr, double asp); + void CameraOffOrigin90(FMATRIX4* mView, FVECTOR3 pos); + + // Pan Camera in a mesh debugger + bool CameraPan(VECTOR3 pan, double speed); + + // Check if a sphere located in pCnt (relative to cam) with a specified radius is visible in a camera + bool IsVisibleInCamera(const FVECTOR3 *pCnt, float radius); + bool IsProxyMesh(); + bool CameraDirection2Viewport(const VECTOR3 &dir, int &x, int &y); + double GetTanAp() const { return tan(Camera.aperture); } + float GetCameraAspect() const { return (float)Camera.aspect; } + float GetCameraFarPlane() const { return Camera.farplane; } + float GetCameraNearPlane() const { return Camera.nearplane; } + float GetCameraAperture() const { return (float)Camera.aperture; } + float GetCameraApertureCorner() const { return (float)Camera.corner; } + VECTOR3 GetCameraGPos() const { return Camera.pos; } + VECTOR3 GetCameraGDir() const { return Camera.dir; } + OBJHANDLE GetCameraProxyBody() const { return Camera.hObj_proxy; } + vPlanet * GetCameraProxyVisual() const { return Camera.vProxy; } + double GetCameraAltitude() const { return Camera.alt_proxy; } + OBJHANDLE GetCameraNearBody() const { return Camera.hNear; } + vPlanet * GetCameraNearVisual() const { return Camera.vNear; } + double GetCameraNearAltitude() const { return Camera.alt_near; } + double GetCameraElevation() const { return Camera.elev; } + void GetCameraLngLat(double *lng, double *lat) const; + bool WorldToScreenSpace(const VECTOR3& rdir, oapi::IVECTOR2* pt, FMATRIX4* pVP = NULL, float clip = 1.0f); + bool WorldToScreenSpace2(const VECTOR3& rdir, oapi::FVECTOR2* pt, FMATRIX4* pVP = NULL, float clip = 1.0f); + + DWORD GetFrameId() const { return dwFrameId; } + + const FVECTOR3 *GetCameraX() const { return &Camera.x; } + const FVECTOR3 *GetCameraY() const { return &Camera.y; } + const FVECTOR3 *GetCameraZ() const { return &Camera.z; } + + const CAMERA * GetCamera() const { return &Camera; } + + void PushCamera(); // Push current camera onto a stack + void PopCamera(); // Restore a camera from a stack + FMATRIX4 PushCameraFrustumLimits(float nearlimit, float farlimit); + FMATRIX4 PopCameraFrustumLimits(); + + + + // Visual Management ========================================================================================================= + // + void GetLVLH(vVessel *vV, FVECTOR3 *up, FVECTOR3 *nr, FVECTOR3 *cp); + class vObject * GetVisObject(OBJHANDLE hObj) const; + class vVessel * GetFocusVisual() const { return vFocus; } + void CheckVisual(OBJHANDLE hObj); + double GetFocusGroundAltitude() const; + double GetTargetGroundAltitude() const; + double GetTargetElevation() const; + std::set GetVessels(double max_dst, bool bActive = true); + + // Locate the visual for hObj in the list if present, or return + // NULL if not found + +protected: + + /** + * \brief Render a single marker at a given global position + * \param hDC device context + * \param gpos global position (ecliptic frame) + * \param label1 label above marker + * \param label2 label below marker + * \param mode marker shape + * \param scale marker size + */ + void RenderObjectMarker(oapi::Sketchpad *pSkp, const VECTOR3 &gpos, const std::string& label1, const std::string& label2, int mode, int scale); + + void RenderGlares(); + +private: + void ActivateLocalLights(vObject* vO, bool bInterior); + void ActivateAllLocalLights(bool bInterior); + void ComputeLocalLightsVisibility(); + DWORD GetActiveParticleEffectCount(); + float ComputeNearClipPlane(); + void VisualizeCubeMap(LPDIRECT3DCUBETEXTURE9 pCube, int mip); + void VisualizeShadowMap(SHADOWMAP *sm); + VOBJREC * FindVisual (OBJHANDLE hObj) const; + void RenderVesselMarker(vVessel *vV, vkPad *pSketch); + float GetLODLevel(SMapInput* smi); + void CombineSMaps(SMapInput* a, SMapInput* b, SMapInput* out); + + LPDIRECT3DTEXTURE9 RenderObjectsInShadow(SMapInput* smi, list& rList, vkPad *pSkp = nullptr); + + // Locate the visual for hObj in the list if present, or return + // NULL if not found + + void DelVisualRec (VOBJREC *pv); + void DeleteAllVisuals(); + // Delete entry pv from the list of visuals + + VOBJREC *AddVisualRec (OBJHANDLE hObj); + // Add an entry for object hObj in the list of visuals + + VECTOR3 SkyColour (); + // Sky background colour based on atmospheric parameters of closest planet + + void InitGDIResources(); + void ExitGDIResources(); + + void FreePooledSketchpads(); ///< Release pooled Sketchpad instances + + + + // Scene variables ================================================================ + // + + struct _cascfg { + float size; + float dist; + } cascfg[SHM_CASCADE_COUNT] = {}; + + oapi::vkClient* gc = {}; + LPDIRECT3DDEVICE9 pDevice = {}; // render device + DWORD viewW = {}; + DWORD viewH = {}; // render viewport size + DWORD stencilDepth = {}; // stencil buffer bit depth + vkCelestialSphere* m_celSphere = {}; // celestial sphere background + DWORD iVCheck = {}; // index of last object checked for visibility + bool bLocalLight = {}; // enable local light sources + bool surfLabelsActive = {}; // v.2 surface labels activated? + + OBJHANDLE hSun = {}; + + vkParticleStream **pstream = {}; // list of particle streams + DWORD nstream = {}; // number of streams + + DEVMESHHANDLE dmCubeMesh = {}; + DWORD bg_rgba = {}; // ambient background colour + + // GDI resources ==================================================================== + // + oapi::Font *label_font[4] = {}; + + std::list Shadowed; + std::list RenderList; + std::list ObjectsToShadowMap; + std::list Casters; + std::stack CameraStack; + std::stack PassStack; + std::stack FrustumStack; + + + CAMERA Camera = {}; + vkLight* Lights = {}; + vkSun sunLight = {}; + + VECTOR3 origin = {}; + VECTOR3 sky_color = {}; + double bglvl = {}; + + float fCascadeRatio = {}; + float fDisplayScale = {}; + float lmaxdst2 = {}; + DWORD nLights = {}; + DWORD dwTurn = {}; + DWORD dwFrameId = {}; + DWORD camIndex = {}; + DWORD RenderFlags = {}; + bool bRendering = {}; + + oapi::Font *pAxisFont = {}; + oapi::Font *pLabelFont = {}; + oapi::Font *pDebugFont = {}; + + SurfNative *pLblSrf = {}; + + class ImageProcessing* pLightBlur = {}; + class ImageProcessing* pBlur = {}; + class ImageProcessing* pBlur2D = {}; + class ImageProcessing* pGDIOverlay = {}; + class ImageProcessing* pIrradiance = {}; + class ImageProcessing* pVisDepth = {}; + class ImageProcessing* pCreateGlare = {}; + class ImageProcessing* pBakeLights = {}; + class ShaderClass* pLocalCompute = {}; + class ShaderClass* pRenderGlares = {}; + class ShaderClass* pRenderStage = {}; + + class vVessel *vFocus = {}; + double dVisualAppRad = {}; + + FVECTOR2 DepthSampleKernel[57] = {}; + + LPDIRECT3DTEXTURE9 pSunTex = {}; + LPDIRECT3DTEXTURE9 pLightGlare = {}; + LPDIRECT3DTEXTURE9 pSunGlare = {}; + LPDIRECT3DTEXTURE9 pSunGlareAtm = {}; + LPDIRECT3DTEXTURE9 pLocalResults = {}; + LPDIRECT3DSURFACE9 pLocalResultsSL = {}; + + // Blur Sampling Kernel ============================================================== + LPDIRECT3DCUBETEXTURE9 pBlrTemp[5] = {}; + LPDIRECT3DTEXTURE9 pBlrTemp2D[5] = {}; + LPDIRECT3DTEXTURE9 pIrradTemp = {}; + LPDIRECT3DTEXTURE9 ptRandom = {}; + + // Deferred Experiment =============================================================== + // + LPDIRECT3DSURFACE9 psgBuffer[GBUF_COUNT] = {}; + LPDIRECT3DTEXTURE9 ptgBuffer[GBUF_COUNT] = {}; + LPDIRECT3DSURFACE9 pOffscreenTarget = {}; + LPDIRECT3DTEXTURE9 pTextures[TEX_COUNT] = {}; + + LPDIRECT3DSURFACE9 pEnvDS = {}; + LPDIRECT3DSURFACE9 pDepthNormalDS = {}; + LPDIRECT3DSURFACE9 psShmDS[SHM_LOD_COUNT] = {}; + + LocalLightsCompute LLCBuf[MAX_SCENE_LIGHTS + 1] = {}; + + // Rendering Technique related parameters ============================================ + // + static ID3DXEffect *FX; + static D3DXHANDLE eLine; + static D3DXHANDLE eStar; + static D3DXHANDLE eWVP; + static D3DXHANDLE eColor; + static D3DXHANDLE eTex0; + +}; + +#endif // !__SCENE_H diff --git a/OVP/VulkanClient/Spherepatch.cpp b/OVP/VulkanClient/Spherepatch.cpp new file mode 100644 index 000000000..3ea47c8ec --- /dev/null +++ b/OVP/VulkanClient/Spherepatch.cpp @@ -0,0 +1,370 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// ============================================================== +// spherepatch.cpp +// Create meshes for spheres and sphere patches +// ============================================================== + +#include "Spherepatch.h" +#include "AABBUtil.h" +#include "TileMgr2.h" +#include "DirectXCollision.h" + +static float TEX2_MULTIPLIER = 4.0f; // microtexture multiplier + +// ============================================================== +// struct VBMESH + +VBMESH::VBMESH (class TileManager2Base *pmgr) + : pIB(NULL) + , pVB(NULL) + , idx(NULL) + , vtx(NULL) + , nv(0) + , nf(0) + , bBox(false) + , nv_cur(0) + , nf_cur(0) + , bsRad(0.0) +{ +} + +VBMESH::VBMESH () + : pIB(NULL) + , pVB(NULL) + , idx(NULL) + , vtx(NULL) + , nv(0) + , nf(0) + , bBox(false) + , nv_cur(0) + , nf_cur(0) + , bsRad(0.0) +{ +} + +VBMESH::~VBMESH () +{ + if (pVB) g_pVtxmgr_vb->Free(pVB); + if (pIB) g_pIdxmgr_ib->Free(pIB); + if (vtx) g_pMemgr_vtx->Free(vtx); + if (idx) g_pMemgr_w->Free(idx); + nv_cur = 0; + nf_cur = 0; +} + +void VBMESH::MapVertices(LPDIRECT3DDEVICE9 pDev, DWORD MemFlag) +{ + if (nv!=nv_cur && vtx) { + pVB = g_pVtxmgr_vb->New(nv); nv_cur = nv; + } + + if (nf!=nf_cur && idx) { + pIB = g_pIdxmgr_ib->New(nf); nf_cur = nf; + } + + VERTEX_2TEX *pVBuffer; + WORD *pIBuffer; + + if (vtx) { + + DirectX::BoundingSphere spr; + DirectX::BoundingSphere::CreateFromPoints(spr, nv, (const XMFLOAT3*)&vtx->x, sizeof(VERTEX_2TEX)); + + bsCnt = _F(spr.Center); + bsRad = spr.Radius; + + if (pVB) { + if (HROK(pVB->Lock(0, 0, (LPVOID*)&pVBuffer, D3DLOCK_DISCARD))) { + memcpy(pVBuffer, vtx, nv*sizeof(VERTEX_2TEX)); + pVB->Unlock(); + } + } else LogErr("Failed to create vertex buffer"); + } + + if (idx) { + if (pIB) { + if (HROK(pIB->Lock(0, 0, (LPVOID*)&pIBuffer, D3DLOCK_DISCARD))) { + memcpy(pIBuffer, idx, nf*sizeof(WORD)*3); + pIB->Unlock(); + } + } else LogErr("Failed to create index buffer"); + } +} + + +// ============================================================== +// CreateSphere() +// Create a spherical mesh of radius 1 and resolution defined by nrings +// Below is a list of #vertices and #indices against nrings: +// +// nrings nvtx nidx (nidx = 12 nrings^2) +// 4 38 192 +// 6 80 432 +// 8 138 768 +// 12 302 1728 +// 16 530 3072 +// 20 822 4800 +// 24 1178 6912 + +void CreateSphere (LPDIRECT3DDEVICE9 pDev, VBMESH &mesh, DWORD nrings, bool hemisphere, int which_half, int texres) +{ + // Allocate memory for the vertices and indices + DWORD nVtx = hemisphere ? nrings*(nrings+1)+2 : nrings*(2*nrings+1)+2; + DWORD nIdx = hemisphere ? 6*nrings*nrings : 12*nrings*nrings; + VERTEX_2TEX* Vtx = g_pMemgr_vtx->New(nVtx); + WORD* Idx = g_pMemgr_w->New(nIdx); + + // Counters + WORD x, y, nvtx = 0, nidx = 0; + VERTEX_2TEX *vtx = Vtx; + WORD *idx = Idx; + + // Angle deltas for constructing the sphere's vertices + FLOAT fDAng = (FLOAT)PI / nrings; + FLOAT fDAngY0 = fDAng; + DWORD x1 = (hemisphere ? nrings : nrings*2); + DWORD x2 = x1+1; + FLOAT du = 0.5f/(FLOAT)texres; + FLOAT a = (1.0f-2.0f*du)/(FLOAT)x1; + + // Make the middle of the sphere + for (y = 0; y < nrings; y++) { + FLOAT y0 = (FLOAT)cos(fDAngY0); + FLOAT r0 = (FLOAT)sin(fDAngY0); + FLOAT tv = fDAngY0/(FLOAT)PI; + + for (x = 0; x < x2; x++) { + FLOAT fDAngX0 = x*fDAng - (FLOAT)PI; // subtract Pi to wrap at +-180° + if (hemisphere && which_half) fDAngX0 += (FLOAT)PI; + + FVECTOR3 v = {r0*(FLOAT)cos(fDAngX0), y0, r0*(FLOAT)sin(fDAngX0)}; + FLOAT tu = a*(FLOAT)x + du; + //FLOAT tu = x/(FLOAT)x1; + + *vtx++ = VERTEX_2TEX (v, v, tu, tv, tu, tv); + nvtx++; + } + fDAngY0 += fDAng; + } + + for (y = 0; y < nrings-1; y++) { + for (x = 0; x < x1; x++) { + *idx++ = (WORD)( (y+0)*x2 + (x+0) ); + *idx++ = (WORD)( (y+0)*x2 + (x+1) ); + *idx++ = (WORD)( (y+1)*x2 + (x+0) ); + *idx++ = (WORD)( (y+0)*x2 + (x+1) ); + *idx++ = (WORD)( (y+1)*x2 + (x+1) ); + *idx++ = (WORD)( (y+1)*x2 + (x+0) ); + nidx += 6; + } + } + // Make top and bottom + FVECTOR3 pvy = {0, 1, 0}, nvy = {0,-1,0}; + WORD wNorthVtx = nvtx; + *vtx++ = VERTEX_2TEX (pvy, pvy, 0.5f, 0.0f, 0.5f, 0.0f); + nvtx++; + WORD wSouthVtx = nvtx; + *vtx++ = VERTEX_2TEX (nvy, nvy, 0.5f, 1.0f, 0.5f, 1.0f); + nvtx++; + + for (x = 0; x < x1; x++) { + WORD p1 = wSouthVtx; + WORD p2 = (WORD)( (y)*x2 + (x+0) ); + WORD p3 = (WORD)( (y)*x2 + (x+1) ); + + *idx++ = p1; + *idx++ = p3; + *idx++ = p2; + nidx += 3; + } + + for (x = 0; x < x1; x++) { + WORD p1 = wNorthVtx; + WORD p2 = (WORD)( (0)*x2 + (x+0) ); + WORD p3 = (WORD)( (0)*x2 + (x+1) ); + + *idx++ = p1; + *idx++ = p3; + *idx++ = p2; + nidx += 3; + } + + mesh.nv = nVtx; + mesh.nf = nIdx/3; + mesh.vtx = Vtx; + mesh.idx = Idx; + mesh.MapVertices(pDev); + + if (Vtx) g_pMemgr_vtx->Free(Vtx); + if (Idx) g_pMemgr_w->Free(Idx); + + Vtx = NULL; + Idx = NULL; + mesh.vtx = NULL; + mesh.idx = NULL; +} + +// ============================================================== + +void CreateSpherePatch (LPDIRECT3DDEVICE9 pDev, VBMESH &mesh, int nlng, int nlat, int ilat, int res, int bseg, + bool reduce, bool outside, bool store_vtx, bool shift_origin) +{ + + const float c1 = 1.0f, c2 = 0.0f; + int i, j, nVtx, nIdx, nseg, n, nofs0, nofs1; + double minlat, maxlat, lat, minlng, maxlng, lng; + double slat, clat, slng, clng; + WORD tmp; + VECTOR3 pos, tpos; + + minlat = PI*0.5 * (double)ilat/(double)nlat; + maxlat = PI*0.5 * (double)(ilat+1)/(double)nlat; + minlng = 0; + maxlng = PI*2.0/(double)nlng; + if (bseg < 0 || ilat == nlat-1) bseg = (nlat-ilat)*res; + + // generate nodes + nVtx = (bseg+1)*(res+1); + if (reduce) nVtx -= ((res+1)*res)/2; + VERTEX_2TEX *Vtx = g_pMemgr_vtx->New(nVtx); + + // create transformation for bounding box + // we define the local coordinates for the patch so that the x-axis points + // from (minlng,minlat) corner to (maxlng,minlat) corner (origin is halfway between) + // y-axis points from local origin to middle between (minlng,maxlat) and (maxlng,maxlat) + // bounding box is created in this system and then transformed back to planet coords. + double clat0 = cos(minlat), slat0 = sin(minlat); + double clng0 = cos(minlng), slng0 = sin(minlng); + double clat1 = cos(maxlat), slat1 = sin(maxlat); + double clng1 = cos(maxlng), slng1 = sin(maxlng); + VECTOR3 ex = {clat0*clng1 - clat0*clng0, 0, clat0*slng1 - clat0*slng0}; normalise(ex); + VECTOR3 ey = {0.5*(clng0+clng1)*(clat1-clat0), slat1-slat0, 0.5*(slng0+slng1)*(clat1-clat0)}; normalise(ey); + VECTOR3 ez = crossp (ey, ex); + MATRIX3 R = {ex.x, ex.y, ex.z, ey.x, ey.y, ey.z, ez.x, ez.y, ez.z}; + VECTOR3 pref = {0.5*(clat0*clng1 + clat0*clng0), slat0, 0.5*(clat0*slng1 + clat0*slng0)}; // origin + VECTOR3 tpmin, tpmax; + + float dx, dy; + if (shift_origin) { + dx = (float)clat0; + dy = (float)slat0; + } + + for (i = n = 0; i <= res; i++) { // loop over longitudinal strips + lat = minlat + (maxlat-minlat) * (double)i/(double)res; + slat = sin(lat), clat = cos(lat); + nseg = (reduce ? bseg-i : bseg); + for (j = 0; j <= nseg; j++) { + lng = (nseg ? minlng + (maxlng-minlng) * (double)j/(double)nseg : 0.0); + slng = sin(lng), clng = cos(lng); + pos = _V(clat*clng, slat, clat*slng); + tpos = mul (R, pos-pref); + if (!n) { + tpmin = tpos; + tpmax = tpos; + } else { + if (tpos.x < tpmin.x) tpmin.x = tpos.x; + else if (tpos.x > tpmax.x) tpmax.x = tpos.x; + if (tpos.y < tpmin.y) tpmin.y = tpos.y; + else if (tpos.y > tpmax.y) tpmax.y = tpos.y; + if (tpos.z < tpmin.z) tpmin.z = tpos.z; + else if (tpos.z > tpmax.z) tpmax.z = tpos.z; + } + + Vtx[n].x = Vtx[n].nx = float(pos.x); + Vtx[n].y = Vtx[n].ny = float(pos.y); + Vtx[n].z = Vtx[n].nz = float(pos.z); + if (shift_origin) + Vtx[n].x -= dx, Vtx[n].y -= dy; + + Vtx[n].tu0 = float(nseg ? (c1*j)/nseg+c2 : 0.5f); // overlap to avoid seams + Vtx[n].tv0 = float((c1*(res-i))/res+c2); + //Vtx[n].tu1 = (nseg ? Vtx[n].tu0 * TEX2_MULTIPLIER : 0.5f); + //Vtx[n].tv1 = Vtx[n].tv0 * TEX2_MULTIPLIER; + if (!outside) { + Vtx[n].nx = -Vtx[n].nx; + Vtx[n].ny = -Vtx[n].ny; + Vtx[n].nz = -Vtx[n].nz; + } + n++; + } + } + + // generate faces + nIdx = (reduce ? res * (2*bseg-res) : 2*res*bseg) * 3; + WORD *Idx = g_pMemgr_w->New(nIdx); + + for (i = n = nofs0 = 0; i < res; i++) { + nseg = (reduce ? bseg-i : bseg); + nofs1 = nofs0+nseg+1; + for (j = 0; j < nseg; j++) { + Idx[n++] = nofs0+j; + Idx[n++] = nofs1+j; + Idx[n++] = nofs0+j+1; + if (reduce && j == nseg-1) break; + Idx[n++] = nofs0+j+1; + Idx[n++] = nofs1+j; + Idx[n++] = nofs1+j+1; + } + nofs0 = nofs1; + } + if (!outside) + for (i = 0; i < nIdx/3; i += 3) + tmp = Idx[i+1], Idx[i+1] = Idx[i+2], Idx[i+2] = tmp; + + mesh.nv = nVtx; + mesh.nf = nIdx/3; + mesh.vtx = Vtx; + mesh.idx = Idx; + mesh.MapVertices(pDev); + + if (Vtx) g_pMemgr_vtx->Free(Vtx); + if (Idx) g_pMemgr_w->Free(Idx); + + Vtx = NULL; + Idx = NULL; + mesh.vtx = NULL; + mesh.idx = NULL; + + if (shift_origin) { + pref.x -= dx; + pref.y -= dy; + } + + // transform bounding box back to patch coordinates + mesh.Box[0] = _V(tmul (R, _V(tpmin.x, tpmin.y, tpmin.z)) + pref); + mesh.Box[1] = _V(tmul (R, _V(tpmax.x, tpmin.y, tpmin.z)) + pref); + mesh.Box[2] = _V(tmul (R, _V(tpmin.x, tpmax.y, tpmin.z)) + pref); + mesh.Box[3] = _V(tmul (R, _V(tpmax.x, tpmax.y, tpmin.z)) + pref); + mesh.Box[4] = _V(tmul (R, _V(tpmin.x, tpmin.y, tpmax.z)) + pref); + mesh.Box[5] = _V(tmul (R, _V(tpmax.x, tpmin.y, tpmax.z)) + pref); + mesh.Box[6] = _V(tmul (R, _V(tpmin.x, tpmax.y, tpmax.z)) + pref); + mesh.Box[7] = _V(tmul (R, _V(tpmax.x, tpmax.y, tpmax.z)) + pref); +} + + + +// ==================================================================== +// NOTE: This is used to delete a vertex buffers from a static VBMESH +// +void ClearVBMesh (VBMESH &mesh) +{ + if (mesh.pVB) g_pVtxmgr_vb->Free(mesh.pVB); + if (mesh.pIB) g_pIdxmgr_ib->Free(mesh.pIB); + if (mesh.vtx) g_pMemgr_vtx->Free(mesh.vtx); + if (mesh.idx) g_pMemgr_w->Free(mesh.idx); + mesh.nv = 0; + mesh.nf = 0; + mesh.nv_cur = 0; + mesh.nf_cur = 0; + mesh.pVB = nullptr; + mesh.pIB = nullptr; + mesh.vtx = nullptr; + mesh.idx = nullptr; +} + diff --git a/OVP/VulkanClient/Spherepatch.h b/OVP/VulkanClient/Spherepatch.h new file mode 100644 index 000000000..fdc438d0a --- /dev/null +++ b/OVP/VulkanClient/Spherepatch.h @@ -0,0 +1,45 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// ============================================================== +// spherepatch.h +// Create meshes for spheres and sphere patches +// ============================================================== + +#ifndef __SPHEREPATCH_H +#define __SPHEREPATCH_H + +#include "Client.h" +#include "Util.h" + +struct VBMESH { + + explicit VBMESH(class TileManager2Base *pMgr); + VBMESH(); + ~VBMESH(); + + void MapVertices (LPDIRECT3DDEVICE9 dev, DWORD MemFlag=0); // copy vertices from vtx to vb + + LPDIRECT3DVERTEXBUFFER9 pVB; // mesh vertex buffer + LPDIRECT3DINDEXBUFFER9 pIB; // mesh index buffer + + VERTEX_2TEX *vtx; // separate storage of vertices (NULL if not available) + WORD *idx; // list of indices + DWORD nv; // number of vertices + DWORD nf; // number of faces (number of indices/3) + DWORD nv_cur; + DWORD nf_cur; + VECTOR4 Box[8]; // bounding box vertices + FVECTOR3 bsCnt; // bounding sphere position + float bsRad; // bounding sphere radius + bool bBox; // true if bounding box data is valid +}; + +void CreateSphere(LPDIRECT3DDEVICE9 pDev, VBMESH &mesh, DWORD nrings, bool hemisphere, int which_half, int texres); +void CreateSpherePatch(LPDIRECT3DDEVICE9 pDev, VBMESH &mesh, int nlng, int nlat, int ilat, int res, int bseg = -1, bool reduce = true, bool outside = true, bool store_vtx = false, bool shift_origin = false); +void ClearVBMesh (VBMESH &mesh); + +#endif // !__SPHEREPATCH_H diff --git a/OVP/VulkanClient/SurfMgr.cpp b/OVP/VulkanClient/SurfMgr.cpp new file mode 100644 index 000000000..b2b649328 --- /dev/null +++ b/OVP/VulkanClient/SurfMgr.cpp @@ -0,0 +1,225 @@ +// ============================================================== +// SurfMgr.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007 - 2016 Martin Schweiger +// 2011 - 2016 Jarmo Nikkanen (vkClient modification) +// ============================================================== + +// ============================================================== +// class SurfaceManager (implementation) +// +// Planetary surface rendering management, including a simple +// LOD (level-of-detail) algorithm for surface patch resolution. +// ============================================================== + +#include "SurfMgr.h" +#include "VPlanet.h" +#include "Surface.h" +#include "DebugControls.h" + +using namespace oapi; + +D3DMATERIAL9 watermat = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{0,0,0,0},20.0f}; +D3DMATERIAL9 def_mat = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{0,0,0,1},0}; + +int nrender[15]; // temporary + +// ======================================================================= + +SurfaceManager::SurfaceManager (vkClient *gclient, const vPlanet *vplanet) +: TileManager(gclient, vplanet) +{ + + maxlvl = min (*(int*)gc->GetConfigParam (CFGPRM_SURFACEMAXLEVEL), // global setting + *(int*)oapiGetObjectParam (obj, OBJPRM_PLANET_SURFACEMAXLEVEL)); // planet-specific setting + + maxbaselvl = min(8, maxlvl); + + pcdir = _V(1,0,0); + lightfac = *(double*)gc->GetConfigParam (CFGPRM_SURFACELIGHTBRT); + spec_base = 0.95f; + atmc = oapiGetPlanetAtmConstants (obj); + + int maxidx = patchidx[maxbaselvl]; + tiledesc = new TILEDESC[maxidx]; + memset (tiledesc, 0, maxidx*sizeof(TILEDESC)); +} + +// ======================================================================= + +void SurfaceManager::LoadData() +{ + if (ntex!=0) return; + LoadPatchData (); + LoadTileData (); + LoadTextures (); + LoadSpecularMasks (); +} + + +// ======================================================================= + +void SurfaceManager::SetMicrotexture (const char *fname) +{ + TileManager::SetMicrotexture (fname); + spec_base = (microtex ? 1.05f : 0.95f); // increase specular intensity to compensate for "ripple" losses +} + +// ======================================================================= + +void SurfaceManager::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap, bool bfog) +{ + if (ntex==0) LoadData(); + + // modify colour of specular reflection component + if (bGlobalSpecular) { + extern D3DMATERIAL9 watermat; + SpecularColour (&watermat.Specular); + watermat.Power = (microtex ? 40.0f : 35.0f); + } + + TileManager::Render (dev, wmat, scale, level, viewap, bfog); +} + +// ============================================================== + +void SurfaceManager::RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWrld) +{ + // render complete sphere (used at low LOD levels) + HR(FX->SetTechnique(ePlanetTile)); + HR(FX->SetValue(eSun, gc->GetScene()->GetSun(), sizeof(vkSun))); + HR(FX->SetMatrix(eW, _DX(mWrld))); + HR(FX->SetValue(eWater, &watermat, sizeof(D3DMATERIAL9))); + HR(FX->SetValue(eMat, &def_mat, sizeof(D3DMATERIAL9))); + HR(FX->SetValue(eColor, &(FVECTOR4(cAmbient)), sizeof(FVECTOR4))); + HR(FX->SetFloat(eTime, float(fmod(oapiGetSimTime(),60.0)))); + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(1.0f, 0.0f, 1.0f, 0.0f)))); + HR(FX->SetFloat(eMix, 0.0f)); + + LPDIRECT3DDEVICE9 pDev = gc->GetDevice(); + pDev->SetVertexDeclaration(pPatchVertexDecl); + + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); + + for (int idx = 0; idx < npatch; idx++) { + + VBMESH &mesh = PATCH_TPL[level][idx]; // patch template + + bool purespec = ((tile[idx].flag & 3) == 2); + bool mixedspec = ((tile[idx].flag & 3) == 3); + + // step 1: render full patch, either completely diffuse or completely specular + if (purespec) { // completely specular + HR(FX->SetInt(eSpecularMode, 1)); + } + else if (mixedspec) { + HR(FX->SetInt(eSpecularMode, 2)); + } + else { + HR(FX->SetInt(eSpecularMode, 0)); + } + + LPDIRECT3DTEXTURE9 ltex = tile[idx].ltex; + if (ltex==NULL) ltex = gc->GetDefaultTexture()->GetTexture(); + + FX->SetTexture(eTex0, tile[idx].tex); + FX->SetTexture(eTex1, ltex); + FX->CommitChanges(); + + pDev->SetStreamSource(0, mesh.pVB, 0, sizeof(VERTEX_2TEX)); + pDev->SetIndices(mesh.pIB); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh.nv, 0, mesh.nf); + } + + HR(FX->EndPass()); + HR(FX->End()); +} + + +void SurfaceManager::InitRenderTile() +{ + HR(FX->SetTechnique(ePlanetTile)); + HR(FX->SetValue(eSun, gc->GetScene()->GetSun(), sizeof(vkSun))); + HR(FX->SetValue(eMat, &def_mat, sizeof(D3DMATERIAL9))); + HR(FX->SetValue(eWater, &watermat, sizeof(D3DMATERIAL9))); + HR(FX->SetValue(eColor, &(FVECTOR4(cAmbient)), sizeof(FVECTOR4))); + HR(FX->SetFloat(eTime, float(fmod(oapiGetSimTime(),60.0)))); + + LPDIRECT3DDEVICE9 pDev = gc->GetDevice(); + pDev->SetVertexDeclaration(pPatchVertexDecl); + + UINT numPasses = 0; + HR(FX->Begin(&numPasses, D3DXFX_DONOTSAVESTATE)); + HR(FX->BeginPass(0)); +} + +void SurfaceManager::EndRenderTile() +{ + HR(FX->EndPass()); + HR(FX->End()); +} + + +// ======================================================================= + +void SurfaceManager::RenderTile (int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, double sdist, + TILEDESC *tile, const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag) +{ + LPDIRECT3DDEVICE9 pDev = gc->GetDevice(); + + VBMESH &mesh = PATCH_TPL[lvl][ilat]; // patch template + + if (range.tumin == 0 && range.tumax == 1) { + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(1.0f, 0.0f, 1.0f, 0.0f)))); + } + else { + float tuscale = range.tumax-range.tumin, tuofs = range.tumin; + float tvscale = range.tvmax-range.tvmin, tvofs = range.tvmin; + HR(FX->SetVector(eTexOff, _DX(FVECTOR4(tuscale,tuofs,tvscale,tvofs)))); + } + + DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + + if (DebugControls::IsActive()) { + if (flags&DBG_FLAGS_TILES) { + float x = 0.6f; + switch(lvl) { + case 14: FX->SetVector(eColor, _DX(FVECTOR4(x, 0, 0, 0))); break; + case 13: FX->SetVector(eColor, _DX(FVECTOR4(0, x, 0, 0))); break; + case 12: FX->SetVector(eColor, _DX(FVECTOR4(0, 0, x, 0))); break; + case 11: FX->SetVector(eColor, _DX(FVECTOR4(x, x, 0, 0))); break; + case 10: FX->SetVector(eColor, _DX(FVECTOR4(x, 0, x, 0))); break; + case 9: FX->SetVector(eColor, _DX(FVECTOR4(0, x, x, 0))); break; + default: FX->SetVector(eColor, _DX(FVECTOR4(0.0f, 0, 0, 0))); break; + } + } + } + + bool purespec = ((flag & 3) == 2); + bool mixedspec = ((flag & 3) == 3); + + if (ltex==NULL) ltex = gc->GetDefaultTexture()->GetTexture(); + + HR(FX->SetMatrix(eW, _DX(mWorld))); + HR(FX->SetTexture(eTex0, tex)); // Base Texture + HR(FX->SetTexture(eTex1, ltex)); // Specular Mask and Night Lights + + if (microtex) { + HR(FX->SetTexture(eTex3, SURFACE(microtex)->GetTexture())); + HR(FX->SetFloat(eMix, 1.0f)); + } + else HR(FX->SetFloat(eMix, 0.0f)); + + if (mixedspec) FX->SetInt(eSpecularMode, 2); + else if (purespec) FX->SetInt(eSpecularMode, 1); + else FX->SetInt(eSpecularMode, 0); + + FX->CommitChanges(); + + pDev->SetStreamSource(0, mesh.pVB, 0, sizeof(VERTEX_2TEX)); + pDev->SetIndices(mesh.pIB); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh.nv, 0, mesh.nf); +} diff --git a/OVP/VulkanClient/SurfMgr.h b/OVP/VulkanClient/SurfMgr.h new file mode 100644 index 000000000..3fdfda09e --- /dev/null +++ b/OVP/VulkanClient/SurfMgr.h @@ -0,0 +1,37 @@ +// ============================================================== +// SurfMgr.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007-2016 Martin Schweiger +// ============================================================== + +#ifndef __SURFMGR_H +#define __SURFMGR_H + +#include "TileMgr.h" + +/** + * \brief Planetary surface rendering management. + * + * Planetary surface rendering management, including a simple + * LOD (level-of-detail) algorithm for surface patch resolution. + */ +class SurfaceManager: public TileManager { +public: + SurfaceManager(oapi::vkClient *gclient, const vPlanet *vplanet); + void SetMicrotexture(const char *fname); + void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap = 0.0, bool bfog = false); + void LoadData(); + +protected: + + void InitRenderTile(); + void EndRenderTile(); + void RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWorld); + + void RenderTile(int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, double sdist, + TILEDESC *tile, const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag); + +}; + +#endif // !__SURFMGR_H diff --git a/OVP/VulkanClient/Surface.cpp b/OVP/VulkanClient/Surface.cpp new file mode 100644 index 000000000..578b67e40 --- /dev/null +++ b/OVP/VulkanClient/Surface.cpp @@ -0,0 +1,1362 @@ +// =========================================================================================== +// vkSurface.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Licensed under LGPL v2 +// Copyright (C) 2011 - 2016 Jarmo Nikkanen +// =========================================================================================== + +#define STRICT + +#include "Surface.h" +#include "Client.h" +#include "Config.h" +#include "Catalog.h" +#include "Util.h" +#include "AABBUtil.h" +#include "Log.h" + +using namespace oapi; + +extern vkClient* g_client; + + +// =============================================================================================== +// +void NatCheckFlags(DWORD &flags) +{ + // Append dependend flags + if (flags & OAPISURFACE_RENDER3D) flags |= OAPISURFACE_RENDERTARGET; + if (flags & OAPISURFACE_SKETCHPAD) flags |= OAPISURFACE_RENDERTARGET; + + if (flags & OAPISURFACE_RENDERTARGET) { + if (flags & OAPISURFACE_GDI) + { + oapiWriteLog((char*)"OAPISURFACE_GDI is incomaptible with OAPISURFACE_RENDERTARGET and OAPISURFACE_SKETCHPAD"); + HALT(); + } + if (flags & OAPISURFACE_SYSMEM) + { + oapiWriteLog((char*)"OAPISURFACE_SYSMEM is incomaptible with OAPISURFACE_RENDERTARGET and OAPISURFACE_SKETCHPAD"); + HALT(); + } + } + + if (flags & OAPISURFACE_MIPMAPS) { + if ((flags & OAPISURFACE_TEXTURE) == 0) + { + oapiWriteLog((char*)"OAPISURFACE_MIPMAPS can be only assigned to a OAPISURFACE_TEXTURE"); + HALT(); + } + if (flags & OAPISURFACE_SYSMEM) + { + oapiWriteLog((char*)"OAPISURFACE_MIPMAPS is incomaptible with OAPISURFACE_SYSMEM"); + HALT(); + } + } +} + + +// =============================================================================================== +// Load a simple plain texture +// +LPDIRECT3DTEXTURE9 NatLoadTexture(const char* path, bool bNoMips) +{ + LPDIRECT3DTEXTURE9 pTex = NULL; + D3DXIMAGE_INFO info; + + if (D3DXGetImageInfoFromFileA(path, &info) == S_OK) { + + DWORD Mips = D3DFMT_FROM_FILE; + + if (Config->TextureMips == 2) Mips = 0; // Autogen all + if (Config->TextureMips == 1 && info.MipLevels == 1) Mips = 0; // Autogen missing + if (bNoMips) Mips = 1; + + if (S_OK == D3DXCreateTextureFromFileExA(g_client->GetDevice(), path, info.Width, info.Height, Mips, 0, D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pTex)) + { + LogBlu("TextureLoaded [%s] Mips=%u Format=%u (%u,%u)", RemovePath(path), pTex->GetLevelCount(), info.Format, info.Width, info.Height); + return pTex; + } + } + + return NULL; +} + + +// =============================================================================================== +// Load a simple plain texture with map type extension +// +LPDIRECT3DTEXTURE9 NatLoadSpecialTexture(const char* path, const char* ext, bool bNoMips) +{ + char name[MAX_PATH]; + NatCreateName(name, ARRAYSIZE(name), path, ext); + return NatLoadTexture(name, bNoMips); +} + + +// ====================================================================================== +// Main loading routine for all maps +// +void NatLoadMaps(SurfNative *pNat, const char* path) +{ + pNat->AddMap(MAP_HEAT, NatLoadSpecialTexture(path, "_heat")); + pNat->AddMap(MAP_NORMAL, NatLoadSpecialTexture(path, "_norm")); + pNat->AddMap(MAP_SPECULAR, NatLoadSpecialTexture(path, "_spec")); + pNat->AddMap(MAP_EMISSION, NatLoadSpecialTexture(path, "_emis")); + pNat->AddMap(MAP_ROUGHNESS, NatLoadSpecialTexture(path, "_rghn")); + pNat->AddMap(MAP_METALNESS, NatLoadSpecialTexture(path, "_metal")); + pNat->AddMap(MAP_REFLECTION, NatLoadSpecialTexture(path, "_refl")); + pNat->AddMap(MAP_TRANSLUCENCE, NatLoadSpecialTexture(path, "_transl")); + pNat->AddMap(MAP_TRANSMITTANCE, NatLoadSpecialTexture(path, "_transm")); + pNat->AddMap(MAP_AMBIENT, NatLoadSpecialTexture(path, "_bkao")); +} + + +// ====================================================================================== +// Main loading routine for a single map +// +void NatLoadMap(SurfNative* pNat, const char* path) +{ + if (NatIsTypeOf(path, "heat")) { pNat->AddMap(MAP_HEAT, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "norm")) { pNat->AddMap(MAP_NORMAL, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "spec")) { pNat->AddMap(MAP_SPECULAR, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "emis")) { pNat->AddMap(MAP_EMISSION, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "rghn")) { pNat->AddMap(MAP_ROUGHNESS, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "metal")) { pNat->AddMap(MAP_METALNESS, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "refl")) { pNat->AddMap(MAP_REFLECTION, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "transl")) { pNat->AddMap(MAP_TRANSLUCENCE, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "transm")) { pNat->AddMap(MAP_TRANSMITTANCE, NatLoadTexture(path)); return; } + if (NatIsTypeOf(path, "bkao")) { pNat->AddMap(MAP_AMBIENT, NatLoadTexture(path)); return; } +} + + +// ====================================================================================== +// Main loading routine +// +SURFHANDLE NatLoadSurface(const char* file, DWORD flags, bool bPath) +{ + LPDIRECT3DTEXTURE9 pTex = NULL; + SurfNative* pNat = NULL; + + NatCheckFlags(flags); + + char path[MAX_PATH]; + + if (bPath) strcpy_s(path, MAX_PATH, file); + else if (!g_client->TexturePath(file, path)) return NULL; + + DWORD pass = OAPISURFACE_TEXTURE | OAPISURFACE_SHARED; + + // Load regular texture with additional maps if exists + // + if ((flags & ~pass) == 0) + { + D3DXIMAGE_INFO info; + + if (D3DXGetImageInfoFromFileA(path, &info) == S_OK) + { + + if (info.ImageFileFormat == D3DXIFF_JPG) info.Format = D3DFMT_X8R8G8B8, flags |= OAPISURFACE_DIFFUSE_ONLY; + if (info.ImageFileFormat == D3DXIFF_PNG) info.Format = D3DFMT_X8R8G8B8, flags |= OAPISURFACE_DIFFUSE_ONLY; + if (info.ImageFileFormat == D3DXIFF_BMP) info.Format = D3DFMT_X8R8G8B8, flags |= OAPISURFACE_DIFFUSE_ONLY; + + DWORD Mips = D3DFMT_FROM_FILE; + if (Config->TextureMips == 2) Mips = 0; // Autogen all + if (Config->TextureMips == 1 && info.MipLevels == 1) Mips = 0; // Autogen missing + + if (S_OK == D3DXCreateTextureFromFileExA(g_client->GetDevice(), path, info.Width, info.Height, Mips, 0, info.Format, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pTex)) + { + pNat = new SurfNative(pTex, flags); + pNat->SetName(file); + pNat->SetPath(path); + LogBlu("TextureLoaded [%s] PLAIN Mips=%u Format=%u (%u,%u) Flags=0x%X, %s", file, pTex->GetLevelCount(), info.Format, info.Width, info.Height, flags, _PTR(pNat)); + + if ((flags & OAPISURFACE_DIFFUSE_ONLY) == 0) + { + NatLoadMaps(pNat, path); + } + } + else oapiWriteLogV("FAILED: NatLoadSurface(%d)", path); + } + else oapiWriteLogV("FAILED: NatLoadSurface(%d)", path); + + return SURFHANDLE(pNat); + } + + + // Load more complex surface + // + D3DXIMAGE_INFO info; + + if (S_OK == D3DXGetImageInfoFromFileA(path, &info)) + { + if (flags & OAPISURFACE_SKETCHPAD) flags |= OAPISURFACE_RENDERTARGET; + if (flags & OAPISURFACE_RENDERTARGET) flags |= OAPISURFACE_UNCOMPRESS; + + DWORD Mips = D3DX_FROM_FILE; + DWORD Usage = 0; + D3DFORMAT Format = info.Format; + D3DPOOL Pool = D3DPOOL_DEFAULT; + D3DMULTISAMPLE_TYPE Multi = D3DMULTISAMPLE_NONE; + bool bLock = false; + + // File Formats Not Supported + if (info.Format == D3DFMT_A4R4G4B4) Format = D3DFMT_A8R8G8B8; + if (info.Format == D3DFMT_X4R4G4B4) Format = D3DFMT_X8R8G8B8; + + // Predict the surface format + // + if (info.ImageFileFormat == D3DXIFF_JPG) Format = D3DFMT_X8R8G8B8; + if (info.ImageFileFormat == D3DXIFF_PNG) Format = D3DFMT_X8R8G8B8; + if (info.ImageFileFormat == D3DXIFF_BMP) Format = D3DFMT_X8R8G8B8; + + if (flags & OAPISURFACE_UNCOMPRESS) + { + D3DFORMAT Fmt = (flags & OAPISURFACE_ALPHA) ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8; + if (info.Format == D3DFMT_DXT1) Format = Fmt; + if (info.Format == D3DFMT_DXT5) Format = Fmt; + if (info.Format == D3DFMT_DXT3) Format = Fmt; + } + + // User defined format + // + D3DFORMAT Fmt = D3DFORMAT(NatConvertFormat_OAPI_to_DX(flags)); + if (Fmt != 0) Format = Fmt; + + if (flags & OAPISURFACE_RENDERTARGET) Usage = D3DUSAGE_RENDERTARGET; + if (flags & OAPISURFACE_GDI) Usage = D3DUSAGE_DYNAMIC; + if (flags & OAPISURFACE_SYSMEM) Pool = D3DPOOL_SYSTEMMEM; + if (flags & OAPISURFACE_NOMIPMAPS) Mips = 1; + if (flags & OAPISURFACE_MIPMAPS) Mips = 0; + + if (flags & OAPISURFACE_TEXTURE) + { + if (S_OK == D3DXCreateTextureFromFileExA(g_client->GetDevice(), path, info.Width, info.Height, Mips, + Usage, Format, Pool, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pTex)) + { + SurfNative* pSrf = new SurfNative(pTex, flags); + pSrf->SetName(file); + pSrf->SetPath(path); + LogBlu("TextureLoaded [%s] Mips=%u, Usage=%u, Format=%u (%u,%u) Flags=0x%X, %s", file, pTex->GetLevelCount(), Usage, Format, info.Width, info.Height, flags, _PTR(pSrf)); + return SURFHANDLE(pSrf); + } + return NULL; + } + + if (flags & OAPISURFACE_RENDERTARGET) + { + LPDIRECT3DSURFACE9 pSurf = NULL; + if (S_OK == g_client->GetDevice()->CreateRenderTarget(info.Width, info.Height, Format, Multi, 0, bLock, &pSurf, NULL)) + { + if (S_OK == D3DXLoadSurfaceFromFile(pSurf, NULL, NULL, path, NULL, D3DX_DEFAULT, 0, NULL)) + { + SurfNative* pSrf = new SurfNative(pSurf, flags); + pSrf->SetName(file); + pSrf->SetPath(path); + LogBlu("SurfaceLoaded [%s] RENDERTARGET Format=%u (%u,%u) Flags=0x%X %s", file, Format, info.Width, info.Height, flags, _PTR(pSrf)); + return SURFHANDLE(pSrf); + } + } + } + } + + return NULL; +} + + + +// =============================================================================================== +// +bool NatSaveSurface(const char* path, LPDIRECT3DRESOURCE9 pResource) +{ + LPDIRECT3DDEVICE9 pDev = g_client->GetDevice(); + + D3DXIMAGE_FILEFORMAT fmt = D3DXIMAGE_FILEFORMAT(0); + + if (contains(path, ".dds")) fmt = D3DXIFF_DDS; + if (contains(path, ".bmp")) fmt = D3DXIFF_BMP; + if (contains(path, ".jpg")) fmt = D3DXIFF_JPG; + if (contains(path, ".png")) fmt = D3DXIFF_PNG; + + + if (pResource->GetType() == D3DRTYPE_SURFACE) + { + LPDIRECT3DSURFACE9 pSurf = static_cast(pResource); + if (D3DXSaveSurfaceToFileA(path, fmt, pSurf, NULL, NULL) == S_OK) return true; + oapiWriteLog((char*)"NatSaveSurface(SURF):"); + NatDumpResource(pResource); + return false; + } + + + if (pResource->GetType() == D3DRTYPE_TEXTURE) + { + LPDIRECT3DTEXTURE9 pTex = static_cast(pResource); + D3DSURFACE_DESC desc; + pTex->GetLevelDesc(0, &desc); + + if (desc.Pool == D3DPOOL_SYSTEMMEM || desc.Usage & D3DUSAGE_DYNAMIC) { + if (D3DXSaveTextureToFileA(path, fmt, pTex, NULL) == S_OK) return true; + oapiWriteLog((char*)"NatSaveSurface(TEX):"); + NatDumpResource(pResource); + return false; + } + + if (desc.Usage & D3DUSAGE_RENDERTARGET) { + LPDIRECT3DTEXTURE9 pSys = NULL; + DWORD Mips = pTex->GetLevelCount(); + HR(D3DXCreateTexture(pDev, desc.Width, desc.Height, Mips, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pSys)); + for (DWORD i = 0; i < Mips; i++) { + LPDIRECT3DSURFACE9 pSrc, pTgt; + HR(pTex->GetSurfaceLevel(i, &pSrc)); + HR(pSys->GetSurfaceLevel(i, &pTgt)); + HR(pDev->GetRenderTargetData(pSrc, pTgt)); + pSrc->Release(); + pTgt->Release(); + } + if (D3DXSaveTextureToFile(path, fmt, pSys, NULL) == S_OK) { + pSys->Release(); + return true; + } + pSys->Release(); + } + + if (D3DXSaveTextureToFileA(path, fmt, pTex, NULL) == S_OK) return true; + } + + oapiWriteLog((char*)"NatSaveSurface():"); + NatDumpResource(pResource); + return false; +} + + + +// =============================================================================================== +// +SURFHANDLE NatCreateSurface(int width, int height, DWORD flags) +{ + DWORD Mips = 1; + DWORD Usage = 0; + D3DPOOL Pool = D3DPOOL_DEFAULT; + D3DMULTISAMPLE_TYPE Multi = D3DMULTISAMPLE_NONE; + LPDIRECT3DDEVICE9 pDev = g_client->GetDevice(); + + NatCheckFlags(flags); + + if (flags & OAPISURFACE_RENDERTARGET) Usage = D3DUSAGE_RENDERTARGET; + if (flags & OAPISURFACE_GDI) Usage = D3DUSAGE_DYNAMIC; + if (flags & OAPISURFACE_SYSMEM) Pool = D3DPOOL_SYSTEMMEM; + if (flags & OAPISURFACE_NOMIPMAPS) Mips = 1; + + if (flags & OAPISURFACE_MIPMAPS) + { + Mips = 0; + Usage |= D3DUSAGE_AUTOGENMIPMAP; + } + + if (flags & OAPISURFACE_ANTIALIAS) Multi = D3DMULTISAMPLE_8_SAMPLES; + + D3DFORMAT Format = D3DFORMAT(NatConvertFormat_OAPI_to_DX(flags)); + + if (Format == 0) + { + if (flags & OAPISURFACE_ALPHA) Format = D3DFMT_A8R8G8B8; + else Format = D3DFMT_X8R8G8B8; + } + + + if ((flags & OAPISURFACE_TEXTURE) || (flags & OAPISURFACE_SYSMEM) || (flags & OAPISURFACE_GDI)) + { + LPDIRECT3DTEXTURE9 pTex = NULL; + LPDIRECT3DSURFACE9 pDepth = NULL; + + if (S_OK == D3DXCreateTexture(pDev, width, height, Mips, Usage, Format, Pool, &pTex)) + { + if (flags & OAPISURFACE_RENDER3D) + { + HR(pDev->CreateDepthStencilSurface(width, height, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, true, &pDepth, NULL)); + } + return SURFHANDLE(new SurfNative(pTex, flags, pDepth)); + } + } + + + if (flags & OAPISURFACE_RENDERTARGET) + { + LPDIRECT3DSURFACE9 pSurf = NULL; + LPDIRECT3DSURFACE9 pDepth = NULL; + + if (S_OK == pDev->CreateRenderTarget(width, height, Format, Multi, 0, false, &pSurf, NULL)) + { + if (flags & OAPISURFACE_RENDER3D) + { + HR(pDev->CreateDepthStencilSurface(width, height, D3DFMT_D24X8, Multi, 0, true, &pDepth, NULL)); + } + return SURFHANDLE(new SurfNative(pSurf, flags, pDepth)); + } + } + assert(false); + return NULL; +} + + +// =============================================================================================== +// +SURFHANDLE NatGetMipSublevel(SURFHANDLE hSrf, int level) +{ + static const DWORD fl = OAPISURFACE_RENDERTARGET | OAPISURFACE_TEXTURE; + + if ((SURFACE(hSrf)->Flags & fl) == fl) + { + LPDIRECT3DSURFACE9 pSurf = NULL; + LPDIRECT3DTEXTURE9 pTex = SURFACE(hSrf)->GetTexture(); + if (pTex->GetSurfaceLevel(level, &pSurf) == S_OK) { + SurfNative* pNat = new SurfNative(pSurf, OAPISURFACE_RENDERTARGET, NULL); + return SURFHANDLE(pNat); + } + } + else { + LogErr("NatGetMipSublevel() Surface is not a rendertarget-texture. Handle = %s", _PTR(hSrf)); + } + return NULL; +} + + +// =============================================================================================== +// +SURFHANDLE NatCompressSurface(SURFHANDLE hSurface, DWORD flags) +{ + D3DSURFACE_DESC desc; + LPDIRECT3DSURFACE9 pDest = NULL; + LPDIRECT3DTEXTURE9 pTex = NULL; + LPDIRECT3DDEVICE9 pDev = g_client->GetDevice(); + LPDIRECT3DRESOURCE9 pResource = SURFACE(hSurface)->GetResource(); + + DWORD Mips = 1; + D3DFORMAT Fmt = D3DFMT_DXT1; + D3DPOOL Pool = D3DPOOL_DEFAULT; + + if (flags & OAPISURFACE_MIPMAPS) Mips = 0; + if ((flags & OAPISURFACE_PF_MASK) == OAPISURFACE_PF_DXT1) Fmt = D3DFMT_DXT1; + if ((flags & OAPISURFACE_PF_MASK) == OAPISURFACE_PF_DXT3) Fmt = D3DFMT_DXT3; + if ((flags & OAPISURFACE_PF_MASK) == OAPISURFACE_PF_DXT5) Fmt = D3DFMT_DXT5; + if (flags & OAPISURFACE_SYSMEM) Pool = D3DPOOL_SYSTEMMEM; + + if (pResource->GetType() == D3DRTYPE_SURFACE) + { + LPDIRECT3DSURFACE9 pSurf = static_cast(pResource); + HR(pSurf->GetDesc(&desc)); + HR(D3DXCreateTexture(pDev, desc.Width, desc.Height, Mips, 0, Fmt, Pool, &pTex)); + for (DWORD i = 0; i < pTex->GetLevelCount(); i++) { + HR(pTex->GetSurfaceLevel(i, &pDest)); + HR(D3DXLoadSurfaceFromSurface(pDest, NULL, NULL, pSurf, NULL, NULL, D3DX_FILTER_BOX, 0)); + pDest->Release(); + } + return new SurfNative(pTex, flags); + } + + if (pResource->GetType() == D3DRTYPE_TEXTURE) + { + LPDIRECT3DSURFACE9 pSurf = NULL; + LPDIRECT3DTEXTURE9 pInp = static_cast(pResource); + HR(pInp->GetLevelDesc(0, &desc)); + HR(D3DXCreateTexture(pDev, desc.Width, desc.Height, Mips, 0, Fmt, Pool, &pTex)); + HR(pInp->GetSurfaceLevel(0, &pSurf)); + for (DWORD i = 0; i < pTex->GetLevelCount(); i++) { + HR(pTex->GetSurfaceLevel(i, &pDest)); + HR(D3DXLoadSurfaceFromSurface(pDest, NULL, NULL, pSurf, NULL, NULL, D3DX_FILTER_BOX, 0)); + pDest->Release(); + } + pSurf->Release(); + return new SurfNative(pTex, flags); + } + + return NULL; +} + + +// =============================================================================================== +// +bool NatGenerateMipmaps(SURFHANDLE hSrf) +{ + LPDIRECT3DTEXTURE9 pTex = SURFACE(hSrf)->GetTexture(); + if (!pTex) return false; + + DWORD nMip = pTex->GetLevelCount(); + if (nMip <= 1) return false; + + LPDIRECT3DDEVICE9 pDev = g_client->GetDevice(); + LPDIRECT3DSURFACE9 pHigh = NULL; + LPDIRECT3DSURFACE9 pLow = NULL; + + HR(pTex->GetSurfaceLevel(0, &pHigh)); + + if (pHigh) { + for (DWORD i = 1; i < nMip; i++) { + if (pTex->GetSurfaceLevel(i, &pLow) == S_OK) { + HR(pDev->StretchRect(pHigh, NULL, pLow, NULL, D3DTEXF_LINEAR)); + pHigh->Release(); + pHigh = pLow; + } + } + pHigh->Release(); + return true; + } + return false; +} + + + + + + + +// ----------------------------------------------------------------------------------------------- +// +SurfNative::SurfNative(LPDIRECT3DRESOURCE9 pRes, DWORD flags, LPDIRECT3DSURFACE9 _pDepth) : + pResource(pRes), + pDX7(NULL), + pSkp(NULL), + pTemp(NULL), + pDepth(_pDepth), + hOrigin(this), + pTexSurf(NULL), + pGDICache(NULL), + pDevice(g_client->GetDevice()), + ColorKey(SURF_NO_CK), + Flags(flags), + type(D3DRTYPE_FORCE_DWORD), + Mipmaps(1), + RefCount(1), + ClientFlags(0) +{ + + assert(pRes != NULL); + assert(GetCurrentThread() == g_client->GetMainThread()); + + SurfaceCatalog.insert(this); + + memset(pMap, 0, sizeof(pMap)); + memset(&desc, 0, sizeof(desc)); + memset(&DC, 0, sizeof(DC)); + + strcpy_s(name, sizeof(name), "null"); + strcpy_s(path, sizeof(path), "null"); + + type = pResource->GetType(); + + if (type == D3DRTYPE_SURFACE) { + LPDIRECT3DSURFACE9 pSrf = static_cast(pResource); + pSrf->GetDesc(&desc); + } + else + if (type == D3DRTYPE_TEXTURE) { + LPDIRECT3DTEXTURE9 pTex = static_cast(pResource); + pTex->GetLevelDesc(0, &desc); + Mipmaps = pTex->GetLevelCount(); + } + else assert(false); +} + + +// ----------------------------------------------------------------------------------------------- +// +SurfNative::SurfNative(SurfNative* pOrigin) +{ + pResource = pOrigin->pResource; + pDX7 = NULL; + pSkp = NULL; + pTemp = NULL; + pDepth = pOrigin->pDepth; + hOrigin = pOrigin; + pTexSurf = pOrigin->pTexSurf; + pGDICache = NULL; + pDevice = g_client->GetDevice(); + ColorKey = pOrigin->ColorKey; + Flags = pOrigin->Flags; + type = pOrigin->type; + Mipmaps = pOrigin->Mipmaps; + RefCount = 1; + + for (int i = 0; i < MAP_MAX_COUNT; i++) pMap[i] = pOrigin->pMap[i]; + + strcpy_s(name, sizeof(name), pOrigin->name); + strcpy_s(path, sizeof(path), pOrigin->path); +} + + +// ----------------------------------------------------------------------------------------------- +// +SurfNative::~SurfNative() +{ + if (SurfaceCatalog.erase(this) != 1) assert(false); + + if (hOrigin == this) + { + for (int i = 0; i < MAP_MAX_COUNT; i++) SAFE_RELEASE(pMap[i]); + + if (!(Flags & OAPISURFACE_BACKBUFFER)) + { + SAFE_RELEASE(pResource); + SAFE_RELEASE(pDepth); + } + SAFE_RELEASE(pTexSurf); + } + + SAFE_RELEASE(pTemp); + SAFE_RELEASE(pDX7); + SAFE_RELEASE(pGDICache); + SAFE_DELETE(pSkp); +} + + +// ----------------------------------------------------------------------------------------------- +// +void SurfNative::AddMap(DWORD id, LPDIRECT3DTEXTURE9 _pMap) +{ + if (id >= MAP_MAX_COUNT) return; + SAFE_RELEASE(pMap[id]); + pMap[id] = _pMap; + Flags |= OAPISURFACE_MAPS; +} + + +// ----------------------------------------------------------------------------------------------- +// +LPDIRECT3DTEXTURE9 SurfNative::GetTexture() const +{ + if (type == D3DRTYPE_TEXTURE) return static_cast(pResource); + return NULL; +} + + +// ----------------------------------------------------------------------------------------------- +// +LPDIRECT3DSURFACE9 SurfNative::GetTempSurface() +{ + if (pTemp) return pTemp; + HR(pDevice->CreateRenderTarget(desc.Width, desc.Height, desc.Format, D3DMULTISAMPLE_NONE, 0, false, &pTemp, NULL)); + return pTemp; +} + + +// ----------------------------------------------------------------------------------------------- +// +LPDIRECT3DSURFACE9 SurfNative::GetSurface() +{ + if (type == D3DRTYPE_SURFACE) return static_cast(pResource); + + if (type == D3DRTYPE_TEXTURE) + { + LPDIRECT3DTEXTURE9 pTex = static_cast(pResource); + + if (!pTexSurf) + { + HR(pTex->GetSurfaceLevel(0, &pTexSurf)) + } + return pTexSurf; + } + return NULL; +} + + +// ----------------------------------------------------------------------------------------------- +// +void SurfNative::SetColorKey(DWORD ck) +{ + ColorKey = ck; +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::IsGDISurface() const +{ + if (desc.Pool == D3DPOOL_SYSTEMMEM) return true; + if (desc.Usage & D3DUSAGE_DYNAMIC) return true; + return false; +} + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::IsRenderTarget() const +{ + if (Flags & OAPISURFACE_BACKBUFFER) return true; + if (desc.Pool == D3DPOOL_DEFAULT && desc.Usage & D3DUSAGE_RENDERTARGET) return true; + return false; +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::Is3DRenderTarget() const +{ + return (pDepth != NULL) && ((Flags & OAPISURFACE_RENDER3D) != 0); +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::IsBackBuffer() const +{ + if (Flags & OAPISURFACE_BACKBUFFER) return true; + return false; +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::IsCompressed() const +{ + if (desc.Format == D3DFMT_DXT1) return true; + if (desc.Format == D3DFMT_DXT3) return true; + if (desc.Format == D3DFMT_DXT5) return true; + if (desc.Format == D3DFMT_DXT2) return true; + if (desc.Format == D3DFMT_DXT4) return true; + return false; +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::IsPowerOfTwo() const +{ + DWORD w = desc.Width, h = desc.Height; + for (int i = 0; i < 14; i++) if ((w & 1) == 0) w = w >> 1; else { if (w != 1) return false; else break; } + for (int i = 0; i < 14; i++) if ((h & 1) == 0) h = h >> 1; else { if (h != 1) return false; else break; } + return true; +} + + +// ----------------------------------------------------------------------------------------------- +// +void SurfNative::SetName(const char* n) +{ + strcpy_s(name, sizeof(name), n); + int i = -1; + while (name[++i] != 0) if (name[i] == '/') name[i] = '\\'; +} + + +// ----------------------------------------------------------------------------------------------- +// +void SurfNative::SetPath(const char* n) +{ + strcpy_s(path, sizeof(path), n); + int i = -1; + while (path[++i] != 0) if (path[i] == '/') path[i] = '\\'; +} + + +// ----------------------------------------------------------------------------------------------- +// +LPDIRECT3DTEXTURE9 SurfNative::GetGDICache(DWORD Flags) +{ + if (pGDICache) return pGDICache; + D3DPOOL Pool = (Flags & OAPISURFACE_SYSMEM) ? D3DPOOL_SYSTEMMEM : D3DPOOL_DEFAULT; + DWORD Usage = (Flags & OAPISURFACE_SYSMEM) ? 0 : D3DUSAGE_DYNAMIC; + HR(D3DXCreateTexture(pDevice, desc.Width, desc.Height, 1, Usage, D3DFMT_X8R8G8B8, Pool, (LPDIRECT3DTEXTURE9 *)&pGDICache)); + return pGDICache; +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::GetSpecs(gcCore::SurfaceSpecs* sp, int size) +{ + if (size == sizeof(gcCore::SurfaceSpecs)) + { + sp->Flags = Flags; + sp->Width = desc.Width; + sp->Height = desc.Height; + sp->Mips = Mipmaps; + return true; + } + return false; +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::GenerateMipMaps() +{ + if (type == D3DRTYPE_TEXTURE) + { + LPDIRECT3DTEXTURE9 pTex = static_cast(pResource); + + if (desc.Usage & D3DUSAGE_AUTOGENMIPMAP) { + pTex->GenerateMipSubLevels(); + return true; + } + else { + + DWORD nMip = pTex->GetLevelCount(); + if (nMip <= 1) return false; + + LPDIRECT3DSURFACE9 pHigh = NULL; + LPDIRECT3DSURFACE9 pLow = NULL; + + HR(pTex->GetSurfaceLevel(0, &pHigh)); + + if (pHigh) { + for (DWORD i = 1; i < nMip; i++) { + if (pTex->GetSurfaceLevel(i, &pLow) == S_OK) { + HR(pDevice->StretchRect(pHigh, NULL, pLow, NULL, D3DTEXF_LINEAR)); + pHigh->Release(); + pHigh = pLow; + } + } + pHigh->Release(); + return true; + } + return false; + + } + } + return false; +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::CreateDX7() +{ + if (pDX7) return true; + if (desc.Format == D3DFMT_X8R8G8B8) + { + if (S_OK == g_client->GetDevice()->CreateRenderTarget(desc.Width, desc.Height, D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, true, &pDX7, NULL)) + { + LogBreak("Surface[%s] Handle=%s (%u,%u) going in DX7 compatibility mode", name, _PTR(this), desc.Width, desc.Height); + return true; + } + } + return false; +} + + +// ----------------------------------------------------------------------------------------------- +// +void SurfNative::DX7Sync(bool bUp) +{ + if (bUp) { + HR(pDevice->StretchRect(GetSurface(), NULL, pDX7, NULL, D3DTEXF_POINT)); + } + else { + HR(pDevice->StretchRect(pDX7, NULL, GetSurface(), NULL, D3DTEXF_POINT)); + } +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::Fill(LPRECT rect, DWORD c) +{ + + LPRECT r; + RECT re; + + if (rect==NULL) { re.left=0, re.top=0, re.right=desc.Width, re.bottom=desc.Height; r=&re; } + else r = rect; + + if (desc.Pool==D3DPOOL_DEFAULT) + { + if (desc.Usage&D3DUSAGE_RENDERTARGET) + { + LPDIRECT3DSURFACE9 pSrf = GetSurface(); + if (pDevice->ColorFill(pSrf, r, c) == S_OK) return true; + } + } + + if (IsGDISurface()) + { + HDC hDC = SurfNative::GetDC(); + HBRUSH hBr = CreateSolidBrush(c); + FillRect(hDC, &re, hBr); + DeleteObject(hBr); + SurfNative::ReleaseDC(hDC); + return true; + } + + LogErr("ColorFill Failed"); + LogSpecs(); + HALT(); + return false; +} + + +// ----------------------------------------------------------------------------------------------- +// +HDC SurfNative::GetDC() +{ + if (!DC.hDC) + { + if (Flags & OAPISURFACE_CAPTURE) { + bool bReady = false; + LPDIRECT3DSURFACE9 pSrf = GetSurface(); + D3DLOCKED_RECT rect; + if (S_OK == pSrf->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT)) { + bReady = true; + pSrf->UnlockRect(); + } + if (bReady) { + if (pSrf->GetDC(&DC.hDC) == S_OK) + { + DC.pSrf = pSrf; + return DC.hDC; + } + } + return NULL; + } + + if (IsGDISurface()) { + LPDIRECT3DSURFACE9 pSrf = GetSurface(); + if (pSrf->GetDC(&DC.hDC) == S_OK) + { + DC.pSrf = pSrf; + return DC.hDC; + } + } + else if (IsRenderTarget()) + { + if (CreateDX7()) + { + DX7Sync(true); + if (pDX7->GetDC(&DC.hDC) == S_OK) + { + DC.pSrf = pDX7; + return DC.hDC; + } + } + } + } + else + { + LogErr("SurfNative: GetDC() Is Already Open"); + } + + LogErr("SurfNative: GetDC() Failed"); + LogSpecs(); + HALT(); + return NULL; +} + + + +// ----------------------------------------------------------------------------------------------- +// +void SurfNative::ReleaseDC(HDC _hDC) +{ + if (!_hDC) return; + + assert(_hDC == DC.hDC); + + HR(DC.pSrf->ReleaseDC(DC.hDC)); + + if (DC.pSrf == pDX7) + { + DX7Sync(false); + } + + DC.pSrf = NULL; + DC.hDC = NULL; +} + + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::Decompress() +{ + if (IsCompressed()) + { + LPDIRECT3DTEXTURE9 pDecomp = NULL; + LPDIRECT3DSURFACE9 pDeSrf = NULL; + D3DFORMAT Format = D3DFMT_FROM_FILE; + + if (desc.Format == D3DFMT_DXT1) Format = D3DFMT_X8R8G8B8; + if (desc.Format == D3DFMT_DXT5) Format = D3DFMT_A8R8G8B8; + if (desc.Format == D3DFMT_DXT3) Format = D3DFMT_A8R8G8B8; + + if (S_OK == D3DXCreateTextureFromFileExA(pDevice, path, desc.Width, desc.Height, Mipmaps, D3DUSAGE_RENDERTARGET, Format, + D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pDecomp)) + { + HR(pDecomp->GetSurfaceLevel(0, &pDeSrf)) + + SAFE_RELEASE(pTexSurf); + SAFE_RELEASE(pResource); + + pResource = pDecomp; + pTexSurf = pDeSrf; + + HR(pDecomp->GetLevelDesc(0, &desc)); + Mipmaps = pDecomp->GetLevelCount(); + type = pDecomp->GetType(); + Flags = OAPISURFACE_RENDERTARGET | OAPISURFACE_TEXTURE; + return true; + + } + + LogSpecs(); + return false; + } + + return true; +} + + +// ----------------------------------------------------------------------------------------------- +// +bool SurfNative::DeClone() +{ + + if (!IsClone()) return false; + else + { + LogWrn("DeCloning Surface [%s] Handle=%s", name, _PTR(this)); + + assert(pGDICache == NULL); + assert(pDX7 == NULL); + assert(pTemp == NULL); + assert(DC.hDC == NULL); + + D3DFORMAT Format = D3DFMT_FROM_FILE; + LPDIRECT3DTEXTURE9 pTex = NULL; + + // Decompress + if (desc.Format == D3DFMT_DXT1) Format = D3DFMT_X8R8G8B8; + if (desc.Format == D3DFMT_DXT5) Format = D3DFMT_A8R8G8B8; + if (desc.Format == D3DFMT_DXT3) Format = D3DFMT_A8R8G8B8; + + if (S_OK == D3DXCreateTextureFromFileExA(pDevice, path, desc.Width, desc.Height, Mipmaps, D3DUSAGE_RENDERTARGET, Format, + D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pTex)) + { + pResource = pTex; + hOrigin = this; + + HR((pTex)->GetSurfaceLevel(0, &pTexSurf)); + HR(pTex->GetLevelDesc(0, &desc)); + Mipmaps = pTex->GetLevelCount(); + type = pTex->GetType(); + Flags = OAPISURFACE_RENDERTARGET | OAPISURFACE_TEXTURE; + return true; + } + } + + LogErr("DeClone Failed"); + LogSpecs(); + return false; +} + +// ----------------------------------------------------------------------------------------------- +// +void SurfNative::Reload() +{ + SAFE_RELEASE(pTexSurf); + SAFE_RELEASE(pResource); + for (int i = 0; i < ARRAYSIZE(pMap); i++) SAFE_RELEASE(pMap[i]); + + if (Flags == OAPISURFACE_TEXTURE) + { + D3DXIMAGE_INFO info; + + if (D3DXGetImageInfoFromFileA(path, &info) == S_OK) + { + DWORD Mips = D3DFMT_FROM_FILE; + if (Config->TextureMips == 2) Mips = 0; // Autogen all + if (Config->TextureMips == 1 && info.MipLevels == 1) Mips = 0; // Autogen missing + + if (S_OK == D3DXCreateTextureFromFileExA(g_client->GetDevice(), path, info.Width, info.Height, Mips, 0, + D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, (LPDIRECT3DTEXTURE9 *)&pResource)) + { + AddMap(MAP_HEAT, NatLoadSpecialTexture(name, "_heat")); + AddMap(MAP_NORMAL, NatLoadSpecialTexture(name, "_norm")); + AddMap(MAP_SPECULAR, NatLoadSpecialTexture(name, "_spec")); + AddMap(MAP_EMISSION, NatLoadSpecialTexture(name, "_emis")); + AddMap(MAP_ROUGHNESS, NatLoadSpecialTexture(name, "_rghn")); + AddMap(MAP_METALNESS, NatLoadSpecialTexture(name, "_metal")); + AddMap(MAP_REFLECTION, NatLoadSpecialTexture(name, "_refl")); + AddMap(MAP_TRANSLUCENCE, NatLoadSpecialTexture(name, "_transl")); + AddMap(MAP_TRANSMITTANCE, NatLoadSpecialTexture(name, "_transm")); + } + } + } +} + + + +// ----------------------------------------------------------------------------------------------- +// +void SurfNative::LogSpecs() const +{ + LogErr("Surface name is [%s] OAPI_Handle=%s", name, _PTR(this)); + if (pTexSurf) LogErr("Has a Surface Interface"); + if (pTemp) LogErr("Has a In-surface temp layer"); + if (pDepth) LogErr("Has a DepthStencil surface"); + if (pDX7) LogErr("Surface is in DX7 compatibility mode"); + if (DC.hDC) LogErr("Has a HDC [%s]", _PTR(DC.hDC)); + LogErr("OAPI_Attribs: %s", NatOAPIFlags(Flags)); + NatDumpResource(pResource); +} + + + +// ----------------------------------------------------------------------------------------------- +// +DWORD SurfNative::GetTextureSizeInBytes(LPDIRECT3DTEXTURE9 pT) +{ + if (!pT) return 0; + D3DSURFACE_DESC d; pT->GetLevelDesc(0,&d); + DWORD size = GetFormatSizeInBytes(d.Format, d.Height * d.Width); + if (pT->GetLevelCount() > 1) size += ((size>>2) + (size>>4) + (size>>6)); + return size; +} + +// ----------------------------------------------------------------------------------------------- +// +DWORD SurfNative::GetFormatSizeInBytes(D3DFORMAT Format, DWORD pixels) +{ + if (Format == D3DFMT_DXT1) return pixels >> 1; + if (Format == D3DFMT_DXT3) return pixels; + if (Format == D3DFMT_DXT5) return pixels; + if (Format == D3DFMT_A8R8G8B8) return pixels * 4; + if (Format == D3DFMT_X8R8G8B8) return pixels * 4; + if (Format == D3DFMT_R5G6B5) return pixels * 2; + if (Format == D3DFMT_A4R4G4B4) return pixels *2; + if (Format == D3DFMT_R8G8B8) return pixels * 3; + if (Format == D3DFMT_L16) return pixels * 2; + if (Format == D3DFMT_R16F) return pixels * 2; + if (Format == D3DFMT_G16R16F) return pixels * 4; + if (Format == D3DFMT_R32F) return pixels * 4; + if (Format == D3DFMT_G32R32F) return pixels * 8; + if (Format == D3DFMT_L8) return pixels; + if (Format == D3DFMT_A8) return pixels; + if (Format == D3DFMT_A32B32G32R32F) return pixels * 16; + if (Format == D3DFMT_A16B16G16R16F) return pixels * 8; + return pixels; +} + + +// ----------------------------------------------------------------------------------------------- +// +DWORD SurfNative::GetSizeInBytes() +{ + if (type == D3DRTYPE_SURFACE) return GetFormatSizeInBytes(desc.Format, desc.Height * desc.Width); + if (type == D3DRTYPE_TEXTURE) + { + DWORD size = GetTextureSizeInBytes(static_cast(pResource)); + for (int i = 0; i < MAP_MAX_COUNT; i++) size += GetTextureSizeInBytes(pMap[i]); + return size; + } + return 0; +} + + +// ----------------------------------------------------------------------------------------------- +// +DWORD* SurfNative::GetClientFlags() +{ + return &ClientFlags; +} + + +// ----------------------------------------------------------------------------------------------- +// +vkPad * SurfNative::GetPooledSketchPad() +{ + if (!IsRenderTarget()) { + LogErr("Can't optain a Sketchpad to a non-render target surface %s", _PTR(this)); + assert(false); + return NULL; + } + if (!pSkp) pSkp = new vkPad(this, "SurfNative.Pooled"); + return pSkp; +} + + + +// ----------------------------------------------------------------------------------------------- +// +bool NatCreateName(char* out, int mlen, const char* fname, const char* id) +{ + char buffe[MAX_PATH]; + strcpy_s(buffe, MAX_PATH, fname); + char* p = strrchr(buffe, '.'); + if (p != NULL) { + *p = '\0'; + sprintf_s(out, mlen, "%s%s.%s", buffe, id, ++p); + } + return (p != NULL); +} + +// ----------------------------------------------------------------------------------------------- +// +bool NatIsTypeOf(const char* fname, const char* id) +{ + char buf[32]; + sprintf_s(buf, 32, "_%s.dds", id); + const char* p = strstr(fname, id); + return (p != NULL); +} + +// ----------------------------------------------------------------------------------------------- +// +DWORD NatConvertFormat_DX_to_OAPI(DWORD Format) +{ + DWORD Out = OAPISURFACE_NOALPHA; + if (Format == D3DFMT_X8R8G8B8) return Out | OAPISURFACE_PF_XRGB; + if (Format == D3DFMT_R5G6B5) return Out | OAPISURFACE_PF_RGB565; + if (Format == D3DFMT_L16) return Out | OAPISURFACE_PF_S16R; + if (Format == D3DFMT_R16F) return Out | OAPISURFACE_PF_F16R; + if (Format == D3DFMT_G16R16F) return Out | OAPISURFACE_PF_F16RG; + if (Format == D3DFMT_R32F) return Out | OAPISURFACE_PF_F32R; + if (Format == D3DFMT_G32R32F) return Out | OAPISURFACE_PF_F32RG; + if (Format == D3DFMT_DXT1) return Out | OAPISURFACE_PF_DXT1; + if (Format == D3DFMT_L8) return Out | OAPISURFACE_PF_GRAY; + + Out = OAPISURFACE_ALPHA; + if (Format == D3DFMT_A8) return Out | OAPISURFACE_PF_ALPHA; + if (Format == D3DFMT_A32B32G32R32F) return Out | OAPISURFACE_PF_F32RGBA; + if (Format == D3DFMT_A16B16G16R16F) return Out | OAPISURFACE_PF_F16RGBA; + if (Format == D3DFMT_A8R8G8B8) return Out | OAPISURFACE_PF_ARGB; + if (Format == D3DFMT_DXT3) return Out | OAPISURFACE_PF_DXT3; + if (Format == D3DFMT_DXT5) return Out | OAPISURFACE_PF_DXT5; + return 0; +} + + +// ----------------------------------------------------------------------------------------------- +// +DWORD NatConvertFormat_OAPI_to_DX(DWORD Format) +{ + Format &= OAPISURFACE_PF_MASK; + + if (Format == OAPISURFACE_PF_XRGB) return D3DFMT_X8R8G8B8; + if (Format == OAPISURFACE_PF_ARGB) return D3DFMT_A8R8G8B8; + if (Format == OAPISURFACE_PF_RGB565) return D3DFMT_R5G6B5; + if (Format == OAPISURFACE_PF_S16R) return D3DFMT_L16; + if (Format == OAPISURFACE_PF_F16R) return D3DFMT_R16F; + if (Format == OAPISURFACE_PF_F16RG) return D3DFMT_G16R16F; + if (Format == OAPISURFACE_PF_F32R) return D3DFMT_R32F; + if (Format == OAPISURFACE_PF_F32RG) return D3DFMT_G32R32F; + if (Format == OAPISURFACE_PF_DXT1) return D3DFMT_DXT1; + if (Format == OAPISURFACE_PF_F32RGBA) return D3DFMT_A32B32G32R32F; + if (Format == OAPISURFACE_PF_F16RGBA) return D3DFMT_A16B16G16R16F; + if (Format == OAPISURFACE_PF_ARGB) return D3DFMT_A8R8G8B8; + if (Format == OAPISURFACE_PF_DXT3) return D3DFMT_DXT3; + if (Format == OAPISURFACE_PF_DXT5) return D3DFMT_DXT5; + if (Format == OAPISURFACE_PF_GRAY) return D3DFMT_L8; + if (Format == OAPISURFACE_PF_ALPHA) return D3DFMT_A8; + return 0; +} + + +// ----------------------------------------------------------------------------------------------- +// +const char* NatUsage(DWORD Usage) +{ + static char buf[128]; + strcpy_s(buf, 128, ""); + if (Usage & D3DUSAGE_AUTOGENMIPMAP) strcat_s(buf, "AUTOGENMIPMAP "); + if (Usage & D3DUSAGE_RENDERTARGET) strcat_s(buf, "RENDERTARGET "); + if (Usage & D3DUSAGE_DYNAMIC) strcat_s(buf, "DYNAMIC "); + if (Usage == 0) strcat_s(buf, "DEFAULT "); + return buf; +} + + +// ----------------------------------------------------------------------------------------------- +// +const char* NatPool(D3DPOOL Pool) +{ + static char buf[64]; + if (Pool == D3DPOOL_DEFAULT) strcpy_s(buf, 64, "D3DPOOL_DEFAULT"); + if (Pool == D3DPOOL_SYSTEMMEM) strcpy_s(buf, 64, "D3DPOOL_SYSTEMMEM"); + if (Pool == D3DPOOL_MANAGED) strcpy_s(buf, 64, "D3DPOOL_MANAGED"); + return buf; +} + + +// ----------------------------------------------------------------------------------------------- +// +const char* NatOAPIFlags(DWORD AF) +{ + static char buf[512]; strcpy_s(buf, 512, ""); + + if (AF & OAPISURFACE_TEXTURE) strcat_s(buf, 512, "OAPISURFACE_TEXTURE, "); + if (AF & OAPISURFACE_RENDERTARGET) strcat_s(buf, 512, "OAPISURFACE_RENDERTARGET, "); + if (AF & OAPISURFACE_GDI) strcat_s(buf, 512, "OAPISURFACE_GDI, "); + if (AF & OAPISURFACE_SKETCHPAD) strcat_s(buf, 512, "OAPISURFACE_SKETCHPAD, "); + if (AF & OAPISURFACE_MIPMAPS) strcat_s(buf, 512, "OAPISURFACE_MIPMAPS, "); + if (AF & OAPISURFACE_NOMIPMAPS) strcat_s(buf, 512, "OAPISURFACE_NOMIPMAPS, "); + if (AF & OAPISURFACE_ALPHA) strcat_s(buf, 512, "OAPISURFACE_ALPHA, "); + if (AF & OAPISURFACE_NOALPHA) strcat_s(buf, 512, "OAPISURFACE_NOALPHA, "); + if (AF & OAPISURFACE_UNCOMPRESS) strcat_s(buf, 512, "OAPISURFACE_UNCOMPRESS, "); + if (AF & OAPISURFACE_SYSMEM) strcat_s(buf, 512, "OAPISURFACE_SYSMEM, "); + if (AF & OAPISURFACE_ANTIALIAS) strcat_s(buf, 512, "OAPISURFACE_ANTIALIAS, "); + if (AF & OAPISURFACE_RENDER3D) strcat_s(buf, 512, "OAPISURFACE_RENDER3D, "); + return buf; +} + + +// ----------------------------------------------------------------------------------------------- +// +const char* NatOAPIFormat(DWORD PF) +{ + static char buf[64]; + strcpy_s(buf, 64, "UNKNOWN"); + DWORD AF = PF & OAPISURFACE_PF_MASK; + + if (AF == OAPISURFACE_PF_XRGB) strcpy_s(buf, 64, "OAPISURFACE_PF_XRGB "); + if (AF == OAPISURFACE_PF_ARGB) strcpy_s(buf, 64, "OAPISURFACE_PF_ARGB "); + if (AF == OAPISURFACE_PF_RGB565)strcpy_s(buf, 64, "OAPISURFACE_PF_RGB565 "); + if (AF == OAPISURFACE_PF_S16R) strcpy_s(buf, 64, "OAPISURFACE_PF_S16R "); + if (AF == OAPISURFACE_PF_F32R) strcpy_s(buf, 64, "OAPISURFACE_PF_F32R "); + if (AF == OAPISURFACE_PF_F32RG) strcpy_s(buf, 64, "OAPISURFACE_PF_F32RG "); + if (AF == OAPISURFACE_PF_F32RGBA)strcpy_s(buf, 64, "OAPISURFACE_PF_F32RGBA "); + if (AF == OAPISURFACE_PF_F16R) strcpy_s(buf, 64, "OAPISURFACE_PF_F16R "); + if (AF == OAPISURFACE_PF_F16RG) strcpy_s(buf, 64, "OAPISURFACE_PF_F16RG "); + if (AF == OAPISURFACE_PF_F16RGBA)strcpy_s(buf, 64, "OAPISURFACE_PF_F16RGBA "); + if (AF == OAPISURFACE_PF_DXT1) strcpy_s(buf, 64, "OAPISURFACE_PF_DXT1 "); + if (AF == OAPISURFACE_PF_DXT3) strcpy_s(buf, 64, "OAPISURFACE_PF_DXT3 "); + if (AF == OAPISURFACE_PF_DXT5) strcpy_s(buf, 64, "OAPISURFACE_PF_DXT5 "); + if (AF == OAPISURFACE_PF_ALPHA) strcpy_s(buf, 64, "OAPISURFACE_PF_ALPHA "); + if (AF == OAPISURFACE_PF_GRAY) strcpy_s(buf, 64, "OAPISURFACE_PF_GRAY "); + return buf; +} + + +// ----------------------------------------------------------------------------------------------- +// +void NatDumpResource(LPDIRECT3DRESOURCE9 pResource) +{ + D3DSURFACE_DESC desc; memset(&desc, 0, sizeof(desc)); + + static const char* sType[] = { "Unknown", "Surface", "Volume", "Texture", "3DTexture", "CubeTexture" }; + + D3DRESOURCETYPE type = pResource->GetType(); + + if (type == D3DRTYPE_SURFACE) + { + LPDIRECT3DSURFACE9 pSurf = static_cast(pResource); + pSurf->GetDesc(&desc); + } + else if (type == D3DRTYPE_TEXTURE) + { + LPDIRECT3DTEXTURE9 pTex = static_cast(pResource); + pTex->GetLevelDesc(0, &desc); + oapiWriteLogV("DX9_DUMP: Mips = %d", pTex->GetLevelCount()); + } + + oapiWriteLogV("DX9_DUMP: Type = %s", sType[type]); + + if (type == D3DRTYPE_SURFACE || type == D3DRTYPE_TEXTURE) + { + DWORD f = NatConvertFormat_DX_to_OAPI(desc.Format); + + oapiWriteLogV("DX9_DUMP: Usage = %s", NatUsage(desc.Usage)); + oapiWriteLogV("DX9_DUMP: Pool = %s", NatPool(desc.Pool)); + oapiWriteLogV("DX9_DUMP: Format = %s (%u)", NatOAPIFormat(f), desc.Format); + oapiWriteLogV("DX9_DUMP: Multisample = %u", desc.MultiSampleType); + oapiWriteLogV("DX9_DUMP: Size = (%u, %u)", desc.Width, desc.Height); + } +} diff --git a/OVP/VulkanClient/Surface.h b/OVP/VulkanClient/Surface.h new file mode 100644 index 000000000..66338318d --- /dev/null +++ b/OVP/VulkanClient/Surface.h @@ -0,0 +1,166 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Licensed under LGPL v2 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __D3DSURFACE_H +#define __D3DSURFACE_H + +#include "OrbiterAPI.h" +#include "Client.h" +#include "Pad.h" +#include "GDIPad.h" +//#include "gcCore.h" +#include +#include "MathAPI.h" + +#define MAP_NORMAL 0 +#define MAP_SPECULAR 1 +#define MAP_EMISSION 2 +#define MAP_REFLECTION 3 +#define MAP_TRANSLUCENCE 4 +#define MAP_TRANSMITTANCE 5 +#define MAP_ROUGHNESS 6 +#define MAP_METALNESS 7 +#define MAP_HEAT 8 +#define MAP_AMBIENT 9 +#define MAP_MAX_COUNT 10 + +#define OAPISURFACE_MAPS 0x80000000 // Additional Texture Maps +#define OAPISURFACE_BACKBUFFER 0x40000000 // It's a backbuffer +#define OAPISURFACE_ORIGIN 0x20000000 // The origin from where the clones are being made, can't change (immutable) +#define OAPISURFACE_CAPTURE 0x10000000 // The origin from where the clones are being made, can't change (immutable) + +#define OAPISURF_SKP_GDI_WARN 0x00000001 + +LPDIRECT3DTEXTURE9 NatLoadTexture(const char* path, bool bNoMips = false); +LPDIRECT3DTEXTURE9 NatLoadSpecialTexture(const char* fname, const char* ext, bool bNoMips = false); +SURFHANDLE NatLoadSurface(const char* file, DWORD flags, bool bPath = false); +bool NatSaveSurface(const char* file, LPDIRECT3DRESOURCE9 pResource); +SURFHANDLE NatCreateSurface(int width, int height, DWORD flags); +SURFHANDLE NatGetMipSublevel(SURFHANDLE hSrf, int level); +bool NatGenerateMipmaps(SURFHANDLE hSrf); +SURFHANDLE NatCompressSurface(SURFHANDLE hSurface, DWORD flags); +bool NatCreateName(char* out, int mlen, const char* fname, const char* id); +DWORD NatConvertFormat_DX_to_OAPI(DWORD Format); +DWORD NatConvertFormat_OAPI_to_DX(DWORD Format); +const char* NatUsage(DWORD Usage); +const char* NatPool(D3DPOOL Pool); +const char* NatOAPIFlags(DWORD AF); +const char* NatOAPIFormat(DWORD PF); +void NatDumpResource(LPDIRECT3DRESOURCE9 pResource); +void NatLoadMaps(SurfNative* pNat, const char* file); +void NatLoadMap(SurfNative* pNat, const char* file); +bool NatIsTypeOf(const char*, const char*); + + +#define ERR_DC_NOT_AVAILABLE 0x1 +#define ERR_USED_NOT_DEFINED 0x2 + + +// Every SURFHANDLE in the client is a pointer into the SurfNative class + +class SurfNative +{ + friend class vkClient; + friend class vkPad; + friend class GDIPad; + + struct _HDC_LOCAL { + HDC hDC; + LPDIRECT3DSURFACE9 pSrf; + }; + +public: + + SurfNative(LPDIRECT3DRESOURCE9 pSrf, DWORD Flags, LPDIRECT3DSURFACE9 pDep = NULL); + SurfNative(SurfNative* hOrigin); + ~SurfNative(); + + void AddMap(DWORD id, LPDIRECT3DTEXTURE9 pMap); + const D3DSURFACE_DESC* GetDesc() const { return &desc; } + bool GenerateMipMaps(); + bool Decompress(); + LPDIRECT3DTEXTURE9 GetGDICache(DWORD Flags); + void IncRef() { RefCount++; } + bool DecRef() { RefCount--; return RefCount <= 0; } + bool DeClone(); + bool GetSpecs(gcCore::SurfaceSpecs* sp, int size); + + void Reload(); + + DWORD GetMipMaps() const { return Mipmaps; } + DWORD GetWidth() const { return desc.Width; } + DWORD GetHeight() const { return desc.Height; } + DWORD GetOAPIFlags() const { return Flags; } + DWORD GetType() const { return (DWORD)type; } + DWORD GetSizeInBytes(); + DWORD* GetClientFlags(); + + const char* GetName() const { return name; } + const char* GetPath() const { return path; } + void SetName(const char*); + void SetPath(const char*); + HDC GetDC(); + void ReleaseDC(HDC); + + bool IsGDISurface() const; + bool IsCompressed() const; + bool IsBackBuffer() const; + bool IsTexture() const { return (type == D3DRTYPE_TEXTURE); } + bool IsRenderTarget() const; + bool Is3DRenderTarget() const; + bool IsPowerOfTwo() const; + bool IsSystemMem() const { return (desc.Pool == D3DPOOL_SYSTEMMEM); } + bool IsAdvanced() const { return (Flags & OAPISURFACE_MAPS); } + bool IsColorKeyEnabled() const { return (ColorKey != SURF_NO_CK); } + bool IsClone() const { return hOrigin != this; } + + LPDIRECT3DSURFACE9 GetTempSurface(); + LPDIRECT3DRESOURCE9 GetResource() const { return pResource; } + LPDIRECT3DSURFACE9 GetDepthStencil() const { return pDepth; } + LPDIRECT3DSURFACE9 GetSurface(); + LPDIRECT3DTEXTURE9 GetTexture() const; + LPDIRECT3DTEXTURE9 GetMap(int type) const { return pMap[type]; } + LPDIRECT3DTEXTURE9 GetMap(int type, int type2) const { return (pMap[type] ? pMap[type] : pMap[type2]); } + vkPad* GetPooledSketchPad(); + void SetColorKey(DWORD ck); // Enable and set color key + DWORD GetColorKey() const { return ColorKey; } + + bool Fill(LPRECT r, DWORD color); + + DWORD GetTextureSizeInBytes(LPDIRECT3DTEXTURE9 pT); + DWORD GetFormatSizeInBytes(D3DFORMAT Format, DWORD pixels); + + void LogSpecs() const; + bool CreateDX7(); + void DX7Sync(bool bUp); + + + // ------------------------------------------------------------------------------- + + char name[128]; // Surface name + char path[MAX_PATH]; // Surface name with path + SURFHANDLE hOrigin; + D3DSURFACE_DESC desc; // Surface size and format description + D3DRESOURCETYPE type; // Resource type + LPDIRECT3DTEXTURE9 pGDICache; // Low level GDI cache for surface syncing + LPDIRECT3DSURFACE9 pTemp; // Cache for in-surface blitting + LPDIRECT3DSURFACE9 pDepth; // DepthStencil surface for 3D rendering + LPDIRECT3DSURFACE9 pTexSurf; // Texture "surface" level cache + LPDIRECT3DRESOURCE9 pResource; // Main resource + LPDIRECT3DSURFACE9 pDX7; + LPDIRECT3DTEXTURE9 pMap[MAP_MAX_COUNT]; // Additional texture maps _norm, _rghn, _spec, etc... + LPDIRECT3DDEVICE9 pDevice; + DWORD ColorKey; + DWORD Flags; // Surface Flags/Attribs + DWORD Mipmaps; // Mipmap count. 1 = no mipmaps + DWORD ClientFlags; + int RefCount; + vkPad* pSkp; // Pooled sketchpad interface cache + _HDC_LOCAL DC; +}; + + +#endif diff --git a/OVP/VulkanClient/Surfmgr2.cpp b/OVP/VulkanClient/Surfmgr2.cpp new file mode 100644 index 000000000..373940298 --- /dev/null +++ b/OVP/VulkanClient/Surfmgr2.cpp @@ -0,0 +1,1885 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// ============================================================== +// surfmgr2.cpp +// class SurfaceManager2 (implementation) +// +// Planetary surface rendering engine v2, including a simple +// LOD (level-of-detail) algorithm for surface patch resolution. +// ============================================================== + +#include "Surfmgr2.h" +#include "Tilemgr2.h" +#include "Cloudmgr2.h" +#include "Catalog.h" +#include "Config.h" +#include "vVessel.h" +#include "VectorHelpers.h" +#include "DebugControls.h" +#include "gcCore.h" +#include "Surface.h" + +// ======================================================================= +extern void FilterElevationGraphics(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, float *elev); + +#pragma pack(push, 4) +struct LightF +{ + float3 position[4]; /* position in world space */ + float3 direction[4]; /* direction in world space */ + float3 diffuse[4]; /* diffuse color of light */ + float3 attenuation[4]; /* Attenuation */ + float4 param[4]; /* range, falloff, theta, phi */ +}; +#pragma pack(pop) + + +// ======================================================================= +// Utility functions + +static void VtxInterpolate (VERTEX_2TEX &res, const VERTEX_2TEX &a, const VERTEX_2TEX &b, double w) +{ + float w1 = (float)w; + float w0 = 1.0f-w1; + res.x = a.x*w0 + b.x*w1; + res.y = a.y*w0 + b.y*w1; + res.z = a.z*w0 + b.z*w1; + res.nx = a.nx*w0 + b.nx*w1; + res.ny = a.ny*w0 + b.ny*w1; + res.nz = a.nz*w0 + b.nz*w1; +} + + +int compare_lights(const void * a, const void * b); + + +// ======================================================================= +// ======================================================================= + +SurfTile::SurfTile (TileManager2Base *_mgr, int _lvl, int _ilat, int _ilng) +: Tile (_mgr, _lvl, _ilat, _ilng) +{ + smgr = static_cast* > (_mgr); + node = 0; + elev = NULL; + ggelev = NULL; + elev_file = NULL; + ltex = NULL; + has_elevfile = false; + label = NULL; + imicrolvl = 14; // Water resolution level + MaxRep = mgr->Client()->GetFramework()->GetCaps()->MaxTextureRepeat; + if (Config->TileMipmaps == 1) bMipmaps = true; + + memset(&ehdr, 0, sizeof(ELEVFILEHEADER)); +} + +// ----------------------------------------------------------------------- + +SurfTile::~SurfTile () +{ + smgr->GetPlanet()->TileDeleted(this); + + if (elev) g_pMemgr_f->Free(elev); + if (elev_file) g_pMemgr_i->Free(elev_file); + if (tex && owntex) g_pTexmgr_tt->Free(tex); + if (ltex && owntex) g_pTexmgr_tt->Free(ltex); + + DeleteLabels(); +} + +// ----------------------------------------------------------------------- + +void SurfTile::PreLoad() +{ + char fname[128]; + char path[MAX_PATH]; + + owntex = true; + + assert(tex == nullptr); + assert(ltex == nullptr); + + LPDIRECT3DDEVICE9 pDev = mgr->Dev(); + LPDIRECT3DTEXTURE9 pSysSrf = nullptr; + + // Configure microtexture range for "Water texture" and "Cloud microtexture". + GetParentMicroTexRange(µrange); + GetParentOverlayRange(&overlayrange); + + // Load surface texture + + if (smgr->DoLoadIndividualFiles(0)) { // try loading from individual tile file + sprintf_s(path, MAX_PATH, "%s\\Surf\\%02d\\%06d\\%06d.dds", mgr->DataRootDir().c_str(), lvl + 4, ilat, ilng); + LoadTextureFile(path, &pSysSrf); + } + if (!pSysSrf && smgr->ZTreeManager(0)) { // try loading from compressed archive + BYTE *buf; + DWORD ndata = smgr->ZTreeManager(0)->ReadData(lvl+4, ilat, ilng, &buf); + if (ndata) { + LoadTextureFromMemory(buf, ndata, &pSysSrf); + smgr->ZTreeManager(0)->ReleaseData(buf); + } + } + + if (CreateTexture(pDev, pSysSrf, &tex) != true) { + if (GetParentSubTexRange(&texrange)) { + tex = getSurfParent()->Tex(); + owntex = false; + } + } + + SAFE_RELEASE(pSysSrf); + + + // Load mask texture + + if (tex && (mgr->Cprm().bSpecular || mgr->Cprm().bLights)) + { + if (smgr->DoLoadIndividualFiles(1)) { // try loading from individual tile file + sprintf_s(path, MAX_PATH, "%s\\Mask\\%02d\\%06d\\%06d.dds", mgr->DataRootDir().c_str(), lvl + 4, ilat, ilng); + LoadTextureFile(path, &pSysSrf); + } + if (!pSysSrf && smgr->ZTreeManager(1)) { // try loading from compressed archive + BYTE* buf; + DWORD ndata = smgr->ZTreeManager(1)->ReadData(lvl + 4, ilat, ilng, &buf); + if (ndata) { + LoadTextureFromMemory(buf, ndata, &pSysSrf); + smgr->ZTreeManager(1)->ReleaseData(buf); + } + } + + if (owntex) { + CreateTexture(pDev, pSysSrf, <ex); + } + else if (node && node->Parent()) { + ltex = getSurfParent()->ltex; + } + + SAFE_RELEASE(pSysSrf); + } +} + +// ----------------------------------------------------------------------- + +void SurfTile::Load () +{ + // Load elevation data + float *elev = ElevationData (); + + bool shift_origin = (lvl >= 4); + int res = mgr->GridRes(); + + if (lvl <= 0) + { + if (!lvl) { // create hemisphere mesh for western or eastern hemispheres + mesh = CreateMesh_hemisphere(res, elev, 0.0); + // } else { // create full sphere mesh + // // TODO + } + } else { + // create rectangular patch + mesh = CreateMesh_quadpatch (res, res, elev, 1.0, 0.0, &texrange, shift_origin, &vtxshift, mgr->GetPlanet()->prm.tilebb_excess); + } + + static const DWORD label_enable = MKR_ENABLE | MKR_LMARK; + DWORD mkrmode = *(DWORD*)smgr->Client()->GetConfigParam(CFGPRM_SURFMARKERFLAG); + if ((mkrmode & label_enable) == label_enable) { + CreateLabels(); + } +} + +// ----------------------------------------------------------------------- + +INT16 *SurfTile::ReadElevationFile (const char *name, int lvl, int ilat, int ilng) +{ + const int ndat = TILE_ELEVSTRIDE*TILE_ELEVSTRIDE; + INT16 *e = NULL; + + // Elevation resolution used for "rounding" due to INT16 elevation. + // Technically, should not apply to float based elevation but required due to rounding in physics. + double tgt_res = mgr->ElevRes(); + + char path[MAX_PATH]; + char fname[128]; + FILE *f; + int i; + + // Elevation data + if (smgr->DoLoadIndividualFiles(2)) { // try loading from individual tile file + sprintf_s(path, MAX_PATH, "%s\\Elev\\%02d\\%06d\\%06d.elv", mgr->DataRootDir().c_str(), lvl, ilat, ilng); + if (!fopen_s(&f, path, "rb")) { + e = g_pMemgr_i->New(ndat); + elev = g_pMemgr_f->New(ndat); + // read the elevation file header + fread (&ehdr, sizeof(ELEVFILEHEADER), 1, f); + if (ehdr.hdrsize != sizeof(ELEVFILEHEADER)) fseek (f, ehdr.hdrsize, SEEK_SET); + LogClr("Teal", "NewTile[%s]: Lvl=%d, Scale=%g, Offset=%g", name, lvl-4, ehdr.scale, ehdr.offset); + +#ifdef ORBITER2016 + ehdr.scale = 1.0; +#endif + switch (ehdr.dtype) { + case 0: // flat tile, defined by offset + for (i = 0; i < ndat; i++) e[i] = 0; + break; + case 8: { + UINT8 *tmp = g_pMemgr_u->New(ndat); + fread (tmp, sizeof(UINT8), ndat, f); + for (i = 0; i < ndat; i++) + e[i] = (INT16)tmp[i]; + g_pMemgr_u->Free(tmp); + tmp = NULL; + } + break; + case -16: + fread (e, sizeof(INT16), ndat, f); + break; + } + fclose (f); + } + } + if (!e && smgr->ZTreeManager(2)) { // try loading from compressed archive + BYTE *buf; + DWORD ndata = smgr->ZTreeManager(2)->ReadData(lvl, ilat, ilng, &buf); + if (ndata) { + BYTE *p = buf; + e = g_pMemgr_i->New(ndat); + elev = g_pMemgr_f->New(ndat); + memcpy(&ehdr, p, sizeof(ELEVFILEHEADER)); + LogClr("Teal", "NewTileA[%s]: Lvl=%d, Scale=%g, Offset=%g", name, lvl-4, ehdr.scale, ehdr.offset); + +#ifdef ORBITER2016 + ehdr.scale = 1.0; +#endif + p += ehdr.hdrsize; + switch (ehdr.dtype) { + case 0: + for (i = 0; i < ndat; i++) e[i] = 0; + break; + case 8: + for (i = 0; i < ndat; i++) + e[i] = (INT16)(*p++); + break; + case -16: + memcpy(e, p, ndat*sizeof(INT16)); + p += ndat*sizeof(INT16); + break; + } + smgr->ZTreeManager(2)->ReleaseData(buf); + } + } + + if (e) { + + if (ehdr.scale != tgt_res) { // rescale the data + double rescale = ehdr.scale / tgt_res; + for (i = 0; i < ndat; i++) + e[i] = (INT16)(e[i] * rescale); + } + if (ehdr.offset) { + INT16 sofs = (INT16)(ehdr.offset / tgt_res); + for (i = 0; i < ndat; i++) + e[i] += sofs; + } + + // Convert to float + for (i = 0; i < ndat; i++) elev[i] = float(e[i]) * float(tgt_res); + } + + // Elevation mod data + if (e) { + bool ok = false; + double rescale; + INT16 offset; + bool do_rescale, do_shift; + ELEVFILEHEADER hdr; + if (smgr->DoLoadIndividualFiles(3)) { // try loading from individual tile file + sprintf_s (path, MAX_PATH, "%s\\Elev_mod\\%02d\\%06d\\%06d.elv", mgr->DataRootDir().c_str(), lvl, ilat, ilng); + if (!fopen_s(&f, path, "rb")) { + fread (&hdr, sizeof(ELEVFILEHEADER), 1, f); + if (hdr.hdrsize != sizeof(ELEVFILEHEADER)) fseek (f, hdr.hdrsize, SEEK_SET); + LogClr("Teal", "NewElevMod[%s]: Lvl=%d, Scale=%g, Offset=%g", name, lvl - 4, hdr.scale, hdr.offset); + +#ifdef ORBITER2016 + hdr.scale = 1.0; +#endif + rescale = (do_rescale = (hdr.scale != tgt_res)) ? hdr.scale/tgt_res : 1.0; + offset = (do_shift = (hdr.offset != 0.0)) ? INT16(hdr.offset/tgt_res) : 0; + + switch (hdr.dtype) { + case 0: // overwrite the entire tile with a flat offset + for (i = 0; i < ndat; i++) e[i] = offset, elev[i] = float(hdr.offset); + break; + case 8: { + const UINT8 mask = UCHAR_MAX; + UINT8 *tmp = g_pMemgr_u->New(ndat); + fread (tmp, sizeof(UINT8), ndat, f); + for (i = 0; i < ndat; i++) { + if (tmp[i] != mask) { + e[i] = (INT16)(do_rescale ? (INT16)(tmp[i] * rescale) : (INT16)tmp[i]); + if (do_shift) e[i] += offset; + elev[i] = float(e[i]) * float(tgt_res); + } + } + g_pMemgr_u->Free(tmp); + tmp = NULL; + } + break; + case -16: { + const INT16 mask = SHRT_MAX; + INT16* tmp = g_pMemgr_i->New(ndat); + fread (tmp, sizeof(INT16), ndat, f); + for (i = 0; i < ndat; i++) { + if (tmp[i] != mask) { + e[i] = (do_rescale ? (INT16)(tmp[i] * rescale) : tmp[i]); + if (do_shift) e[i] += offset; + elev[i] = float(e[i]) * float(tgt_res); + } + } + g_pMemgr_i->Free(tmp); + tmp = NULL; + } + break; + } + fclose(f); + ok = true; + } + } + if (!ok && smgr->ZTreeManager(3)) { // try loading from compressed archive + BYTE *buf; + DWORD ndata = smgr->ZTreeManager(3)->ReadData(lvl, ilat, ilng, &buf); + if (ndata) { + BYTE *p = buf; + ELEVFILEHEADER *phdr = (ELEVFILEHEADER*)p; + LogClr("Teal", "NewElevModA[%s]: Lvl=%d, Scale=%g, Offset=%g", name, lvl - 4, phdr->scale, phdr->offset); + +#ifdef ORBITER2016 + phdr->scale = 1.0; +#endif + p += phdr->hdrsize; + rescale = (do_rescale = (phdr->scale != tgt_res)) ? phdr->scale/tgt_res : 1.0; + offset = (do_shift = (phdr->offset != 0.0)) ? INT16(phdr->offset/tgt_res) : 0; + + switch(phdr->dtype) { + case 0: + for (i = 0; i < ndat; i++) e[i] = offset, elev[i] = float(phdr->offset); + break; + case 8: { + const UINT8 mask = UCHAR_MAX; + for (i = 0; i < ndat; i++) { + if (p[i] != mask) { + e[i] = (INT16)(do_rescale ? p[i] * rescale : p[i]); + if (do_shift) e[i] += offset; + elev[i] = float(e[i]) * float(tgt_res); + } + } + } break; + case -16: { + const INT16 mask = SHRT_MAX; + INT16 *buf16 = (INT16*)p; + for (i = 0; i < ndat; i++) { + if (buf16[i] != mask) { + e[i] = (do_rescale ? (INT16)(buf16[i] * rescale) : buf16[i]); + if (do_shift) e[i] += offset; + elev[i] = float(e[i]) * float(tgt_res); + } + } + } break; + } + smgr->ZTreeManager(3)->ReleaseData(buf); + } + } + if (Config->bFlats) FilterElevationGraphics(mgr->GetPlanet()->Object(), lvl - 4, ilat, ilng, elev); + } + return e; +} + +// ----------------------------------------------------------------------- + +LPDIRECT3DTEXTURE9 SurfTile::SetOverlay(LPDIRECT3DTEXTURE9 pOverlay, bool bOwn) +{ + LPDIRECT3DTEXTURE9 pRet = NULL; + if (bOwn && ownoverlay && overlay) pRet = overlay; + + overlay = pOverlay; + ownoverlay = bOwn; + + for (int i = 0; i < 4; i++) { + auto x = node->Child(i); + if (x) if (x->Entry()) { + LPDIRECT3DTEXTURE9 pOld = x->Entry()->overlay; + if (pOld == NULL || pOld == pRet) { + x->Entry()->GetParentOverlayRange(&overlayrange); + x->Entry()->SetOverlay(pOverlay, false); + } + } + } + return pRet; +} + +// ----------------------------------------------------------------------- + +bool SurfTile::DeleteOverlay(LPDIRECT3DTEXTURE9 pOverlay) +{ + bool bReturn = false; + if (pOverlay == NULL) pOverlay = overlay; + + if (overlay) { + if (pOverlay == overlay) { + if (ownoverlay) { + overlay->Release(); + bReturn = true; + } + for (int i = 0; i < 4; i++) { + auto x = node->Child(i); + if (x) if (x->Entry()) x->Entry()->DeleteOverlay(pOverlay); + } + overlay = NULL; + ownoverlay = false; + } + } + return bReturn; +} + + +// ----------------------------------------------------------------------- + +float SurfTile::Interpolate(FMATRIX4 &in, float t, float u) +{ + return 0.0f; +} + +// ----------------------------------------------------------------------- + +bool SurfTile::InterpolateElevationGrid(const float *in, float *out) +{ + int q0 = 0, c = 129; + + if (!(ilat & 1)) q0 = TILE_ELEVSTRIDE * 128; + if ((ilng & 1)) q0 += 128; + + for (int i = 0; i <= c; i++) + { + int q1 = q0 + 1; + int q2 = q0 + TILE_ELEVSTRIDE; + int q3 = q1 + TILE_ELEVSTRIDE; + int x = (TILE_ELEVSTRIDE << 1) * i; + + for (int k = 0; k <= c; k++) + { + float f0 = in[k + q0]; float f1 = in[k + q1]; + float f2 = in[k + q2]; float f3 = in[k + q3]; + + out[x + 0] = (f0 + f1 + f2 + f3) * 0.25f; + if (k != c) out[x + 1] = (f1 + f3) * 0.5f; + + if (i != c) { + out[x + TILE_ELEVSTRIDE] = (f2 + f3) * 0.5f; + if (k != c) out[x + TILE_ELEVSTRIDE + 1] = f3; + } + x += 2; + } + q0 += TILE_ELEVSTRIDE; + } + return true; +} + + +// ----------------------------------------------------------------------- + +bool SurfTile::LoadElevationData () +{ + // Note: a tile's elevation data are retrieved from its great-grandparent tile. Each tile stores the elevation + // data for its area at 8x higher resolution than required by itself, so they can be used by the grandchildren. + // This reduces the number of elevation files, by storing more information in each. It also has a caching effect: + // Once a grandparent has loaded its elevation data on request of a grandchild, any other grandchildren's requests + // can be served directly without further disk I/O. + + if (elev) return true; // already present + + has_elevfile = false; + int mode = mgr->Cprm().elevMode; + if (!mode) return false; + + DWORD phy_lvl = mgr->GetPlanet()->GetPhysicsPatchRes(); + int ndat = TILE_ELEVSTRIDE*TILE_ELEVSTRIDE; + + elev_file = ReadElevationFile (mgr->CbodyName(), lvl + 4, ilat, ilng); + double tgt_res = mgr->ElevRes(); + + if (elev_file) has_elevfile = true; + else if (lvl > 0) { + + // Acquire elev header data from a parent + QuadTreeNode *parent = node->Parent(); + if (parent && parent->Entry()) memcpy(&ehdr, &parent->Entry()->ehdr, sizeof(ELEVFILEHEADER)); + + // construct elevation grid by interpolating ancestor data + ELEVHANDLE hElev = mgr->ElevMgr(); + if (!hElev) return false; + + // Cubic Interpolation + if (mode == 2) { + + int plvl = lvl-1; + int pilat = ilat >> 1; + int pilng = ilng >> 1; + INT16 *pelev_file = 0; + QuadTreeNode *parent = node->Parent(); + for (; plvl >= 0; plvl--) { // find ancestor with elevation data + if (parent && parent->Entry()->has_elevfile) { + pelev_file = parent->Entry()->elev_file; + break; + } + if (parent) parent = parent->Parent(); + pilat >>= 1; + pilng >>= 1; + } + + if (!pelev_file) return false; + + elev = g_pMemgr_f->New(ndat); + + // submit ancestor data to elevation manager for interpolation + mgr->GetClient()->ElevationGrid(hElev, ilat, ilng, lvl, pilat, pilng, plvl, pelev_file, elev); + + // Convert to float + for (int i = 0; i < ndat; i++) elev[i] *= float(tgt_res); + } + + // Experimental Linear Interpolation + else { + QuadTreeNode *parent = node->Parent(); + if (parent && parent->Entry()->elev) { + elev = g_pMemgr_f->New(ndat); + InterpolateElevationGrid(parent->Entry()->elev, elev); + } + } + } + + if (has_elevfile) LogClr("Teal", "TileCreatedFromFile: Level=%d, ilat=%d, ilng=%d", lvl, ilat, ilng); + else LogClr("Teal", "TileInterpolatedFromParent: Level=%d, ilat=%d, ilng=%d", lvl, ilat, ilng); + + return (elev != 0); +} + +// ----------------------------------------------------------------------- + +float *SurfTile::ElevationData () const +{ + if (!ggelev) { + int ancestor_dlvl = 3; + while (1 << (8-ancestor_dlvl) < mgr->GridRes()) ancestor_dlvl--; + if (lvl >= ancestor_dlvl) { // traverse quadtree back to great-grandparent + QuadTreeNode *ancestor = node; // start at my own node + int blockRes = TILE_FILERES; + while (blockRes > mgr->GridRes() && ancestor) { + ancestor = ancestor->Parent(); + blockRes >>= 1; + } + if (ancestor && ancestor->Entry()->LoadElevationData()) { + // compute pixel offset into great-grandparent tile set + int nblock = TILE_FILERES/blockRes; + int mask = nblock-1; + int ofs = ((mask - ilat & mask) * TILE_ELEVSTRIDE + (ilng & mask)) * blockRes; + ggelev = ancestor->Entry()->elev + ofs; + } + } else { + SurfTile *ggp = smgr->GlobalTile(lvl - ancestor_dlvl);// +3); + if (ggp && ggp->LoadElevationData ()) { + int blockRes = mgr->GridRes(); + int nblock = TILE_FILERES/blockRes; + int mask = nblock-1; + int ofs = ((mask - ilat & mask) * TILE_ELEVSTRIDE + (ilng & mask)) * blockRes; + ggelev = ggp->elev + ofs; + } + } + if (ggelev) ComputeElevationData(ggelev); + } + return ggelev; +} + +// ----------------------------------------------------------------------- + +void SurfTile::ComputeElevationData(const float *elev) const +{ + if (has_elevfile) return; + int i, j; + int res = mgr->GridRes(); + ehdr.emax = -1e30; + ehdr.emin = 1e30; + ehdr.emean = 0.0; + for (j = 0; j <= res; j++) { + for (i = 0; i <= res; i++) { + ehdr.emean += elev[i]; + ehdr.emax = max(ehdr.emax, (double)elev[i]); + ehdr.emin = min(ehdr.emin, (double)elev[i]); + } + elev += TILE_ELEVSTRIDE; + } + ehdr.emean /= double((res + 1)*(res + 1)); +} + +// ------------------------------------------------------------------------------ +// bGet(true) = Get the data specifically from this tile recardless of it's state +// +int SurfTile::GetElevation(double lng, double lat, double *elev, FVECTOR3 *nrm, SurfTile **cache, bool bFilter, bool bGet) const +{ + static int ndat = TILE_ELEVSTRIDE*TILE_ELEVSTRIDE; + if (cache) *cache = (SurfTile *)this; + + if (latbnd.maxlat) return -1; + if (lngbnd.maxlng) return -1; + + if (state == ForRender || bGet) + { + if (!ggelev) { *elev = 0.0; return 2; } + else { + double fRes = double(mgr->GridRes()); + + if (!bFilter) { + int i = int((lat - bnd.minlat) * fRes / (bnd.maxlat - bnd.minlat)) + 1; + int j = int((lng - bnd.minlng) * fRes / (bnd.maxlng - bnd.minlng)) + 1; + *elev = double(ggelev[j+i*TILE_ELEVSTRIDE]); + } + else { + + float x = float((lat - bnd.minlat) * fRes / (bnd.maxlat - bnd.minlat)) + 1.0f; // 0.5f + float y = float((lng - bnd.minlng) * fRes / (bnd.maxlng - bnd.minlng)) + 1.0f; // 0.5f + float fx = (x - floor(x)); + float fy = (y - floor(y)); + + int i0 = int(x) * TILE_ELEVSTRIDE; + int i1 = i0 + TILE_ELEVSTRIDE; + int j0 = int(y); + + assert(i0 > 0); + assert(j0 > 0); + assert((j0 + i1) < ndat); + + float q = lerp(ggelev[j0+i0], ggelev[j0+i1], fx); j0++; + float w = lerp(ggelev[j0+i0], ggelev[j0+i1], fx); + + *elev = double(lerp(q,w,fy)); + } + + return 1; + } + } + + if (state == Invisible) return 0; + + if (state == Active) { + int i = 0; + if (lng > (bnd.minlng + bnd.maxlng)*0.5) i++; + if (lat < (bnd.minlat + bnd.maxlat)*0.5) i += 2; + if (node->Child(i)) + return node->Child(i)->Entry()->GetElevation(lng, lat, elev, nrm, cache); + } + + if (state == Inactive) { + // Calling elevation without tree being fully initialized. + // Force data acquision using current level due to lack of render level. + return GetElevation(lng, lat, elev, nrm, cache, bFilter, true); + } + + return -3; +} + +// ----------------------------------------------------------------------- + +SurfTile *SurfTile::getTextureOwner() +{ + if (owntex) return this; + SurfTile *parent = getSurfParent(); + while (parent) { + if (parent->owntex) return parent; + else parent = parent->getSurfParent(); + } + return NULL; +} + +// ----------------------------------------------------------------------- + +float SurfTile::fixinput(double a, int x) +{ + switch(x) { + case 0: return float((1.0+floor(a*0.0625))*16.0); + case 1: return float((1.0+floor(a*0.125))*8.0); + case 2: return float((1.0+floor(a*0.5))*2.0); + } + return 0.0f; +} + +// ----------------------------------------------------------------------- + +double SurfTile::GetCameraDistance() +{ + VECTOR3 cnt = Centre() * (mgr->CbodySize() + GetMeanElev()); + cnt = mgr->prm.cpos + mul(mgr->prm.grot, cnt); + return length(cnt); +} + +// ----------------------------------------------------------------------- + +FVECTOR4 SurfTile::MicroTexRange(SurfTile *pT, int ml) const +{ + float rs = 1.0f / float( 1 << (lvl-pT->Level()) ); // Range subdivision + float xo = pT->MicroRep[ml].x * texrange.tumin; + float yo = pT->MicroRep[ml].y * texrange.tvmin; + xo -= floor(xo); // Micro texture offset for current tile + yo -= floor(yo); // Micro texture offset for current tile + return FVECTOR4(xo, yo, pT->MicroRep[ml].x * rs, pT->MicroRep[ml].y * rs); +} + +// ----------------------------------------------------------------------- +// Called during rendering even if this tile is not rendered but children are +// i.e. called for every RENDERED and ACTIVE tile +// +void SurfTile::StepIn () +{ + LPDIRECT3DDEVICE9 pDev = mgr->Dev(); + const vPlanet *vPlanet = mgr->GetPlanet(); + + if (vPlanet != mgr->GetScene()->GetCameraProxyVisual()) return; + + // Compute micro texture repeat counts ------------------------------------- + // + if (owntex && vPlanet->MicroCfg.bEnabled) { + double s = vPlanet->GetSize(); + for (int i = 0; i < ARRAYSIZE(vPlanet->MicroCfg.Level); ++i) { + double f = s / vPlanet->MicroCfg.Level[i].size; + MicroRep[i] = FVECTOR2(fixinput(width*f, i), fixinput(height*f, i)); + } + } +} + + +// ----------------------------------------------------------------------- + +void SurfTile::Render () +{ + Tile::Render(); + + if (!mesh) return; // DEBUG : TEMPORARY + + LPDIRECT3DDEVICE9 pDev = mgr->Dev(); + vPlanet *vPlanet = mgr->GetPlanet(); + const Scene *scene = mgr->GetScene(); + const vkClient *pClient = mgr->GetClient(); + + static const double rad0 = sqrt(2.0)*PI05; + double sdist, rad; + bool has_specular = false; + bool has_shadows = false; + bool has_lights = false; + bool has_microtex = false; + bool has_atmosphere = vPlanet->HasAtmosphere(); + bool has_ripples = vPlanet->HasRipples(); + bool bUseZBuf = mgr->IsUsingZBuf(); + + if (vPlanet->CameraAltitude()>20e3) has_ripples = false; + + double ca = 1.0 + saturate(vPlanet->CameraAltitude() / 150e3) * Config->OrbitalShadowMult; + + PlanetShader* pShader = mgr->GetShader(); + ShaderParams* sp = vPlanet->GetTerrainParams(); + FlowControlPS* fc = vPlanet->GetFlowControl(); + FlowControlVS* fcv = vPlanet->GetFlowControlVS(); + + bool render_lights = pShader->bNightlights; + bool render_shadows = (mgr->GetPlanet()->CloudMgr2() != NULL) && mgr->GetClient()->GetConfigParam(CFGPRM_CLOUDSHADOWS) && pShader->bCloudShd; + + if (ltex) { + sdist = acos(dotp(mgr->prm.sdir, cnt)); + rad = rad0 / (double)(2 << lvl); // tile radius + has_specular = (ltex != NULL) && sdist < (1.75 + rad); + has_lights = (render_lights && ltex && sdist > 1.35); + has_shadows = (render_shadows && sdist < (PI05 + rad)); + } + + has_specular &= pShader->bWater; + has_ripples &= pShader->bRipples & has_specular; + has_lights &= pShader->bNightlights; + has_atmosphere &= pShader->bAtmosphere; + + sp->vCloudOff = FVECTOR4(0.0f, 0.0f, 1.0f, 1.0f); + + FVECTOR3 bs_pos = oapiTransformCoord(&mesh->bsCnt, &mWorld); + + // ---------------------------------------------------------------------- + // Assign micro texture range information to shaders + // ---------------------------------------------------------------------- + + SurfTile *pT = getTextureOwner(); + + if (pT && vPlanet->MicroCfg.bEnabled && pShader->bMicrotex) + { + sp->vMSc[0] = MicroTexRange(pT, 0); + sp->vMSc[1] = MicroTexRange(pT, 1); + sp->vMSc[2] = MicroTexRange(pT, 2); + has_microtex = true; + } + + + + // ---------------------------------------------------------------------- + // Setup cloud shadows + // ---------------------------------------------------------------------- + + has_shadows = false; + + if (render_shadows) + { + LPDIRECT3DTEXTURE9 pCloud = NULL, pCloud2 = NULL; + + const TileManager2 *cmgr = vPlanet->CloudMgr2(); + int maxlvl = min(lvl,9); + double rot = mgr->prm.rprm->cloudrot; + double edglat = (bnd.minlat + bnd.maxlat)*0.5; // latitude of tile center + double edglng = wrap(rot + bnd.minlng); // surface tile minlng-edge position on cloud-layer + + for (int attempt=0;attempt<2;attempt++) { + + CloudTile *ctile = (CloudTile *)cmgr->SearchTile(edglng, edglat, maxlvl, true); + + if (ctile) { + + double icsize = double( 1 << ctile->Level() ) / PI; // inverse of cloud tile size in radians + + // Compute surface tile uv origin on a selected claud tile + // Note: edglng exists always within tile i.e. no wrap from PI to -PI + double u0 = (edglng - ctile->bnd.minlng) * icsize; + double v0 = (ctile->bnd.maxlat - bnd.maxlat) * icsize; // Note: Tile corner is lower-left, texture corner is upper-left + double u1 = u0 + (bnd.maxlng - bnd.minlng) * icsize; + double v1 = v0 + (bnd.maxlat - bnd.minlat) * icsize; + + // Feed uv-offset and uv-range to the shaders + sp->vCloudOff = FVECTOR4(float(u0), float(v0), float(u1-u0), float(v1-v0)); + sp->fAlpha = float(ca); + pCloud = ctile->Tex(); + + + // Texture uv range extends to another tile + if (u1 > 1.0) { + + double csize = PI / double(1<Level()); // cloud tile size in radians + double ctr = (ctile->bnd.minlng + ctile->bnd.maxlng) * 0.5; // cloud tile center + double lng = wrap(ctr + csize*sign(rot)); // center of the next tile + + // Request an other tile from the same level as the first one + CloudTile *ctile2 = (CloudTile *)cmgr->SearchTile(lng, edglat, ctile->Level(), true); + + if (ctile2) { + if (ctile2->Level() == ctile->Level()) { + // Rendering with dual texture + pCloud2 = ctile2->Tex(); + has_shadows = true; + break; + } + else { + // Failed to match a dual texture render requirements (due to no-valid tile available) + // Try again and request a lower level texture data for the first texture + maxlvl = ctile2->Level(); + if (attempt==1) LogErr("CloudShadows mapping failed"); + } + } + } + else { + // Just one texture in use + has_shadows = true; + break; + } + } + } + + pShader->SetTexture(pShader->tCloud, pCloud, IPF_CLAMP | IPF_ANISOTROPIC, Config->Anisotrophy); + pShader->SetTexture(pShader->tCloud2, pCloud2, IPF_CLAMP | IPF_ANISOTROPIC, Config->Anisotrophy); + } + + + fc->bCloudShd = has_shadows; + fc->bMicroTex = has_microtex; + fc->bLocals = false; + fc->bOverlay = false; + fc->bMask = false; + fc->bShadows = false; + fc->bInSpace = !vPlanet->CameraInAtmosphere(); + fc->bPlanetShadow = vPlanet->SphericalShadow(); + fcv->bElevOvrl = false; + + + // ---------------------------------------------------------------------- + // DevTools: Render with overlay image + // ---------------------------------------------------------------------- + + if (pShader->bDevtools && scene->GetRenderPass() == RENDERPASS_MAINSCENE) + { + FVECTOR4 texcoord; + const vPlanet::sOverlay* oLay = vPlanet->IntersectOverlay(bnd.vec, &texcoord); + + if (oLay) + { + bool bOlayEnable = false; for (auto x : oLay->pSurf) if (x) bOlayEnable = true; + + if (bOlayEnable) + { + // Global large-scale overlay + pShader->SetTexture("tOverlay", oLay->pSurf[0]); + pShader->SetTexture("tMskOverlay", oLay->pSurf[1]); + pShader->SetTexture("tElvOverlay", oLay->pSurf[2]); + + memcpy(&sp->vOverlayCtrl, &oLay->Blend, sizeof(FVECTOR4) * 4); + sp->vOverlayOff = texcoord; + + if (oLay->pSurf[0] || oLay->pSurf[1]) fc->bOverlay = true; + if (oLay->pSurf[2]) fcv->bElevOvrl = true; + } + } + else if (overlay) { + // Local tile specific overlay + pShader->SetTexture("tOverlay", overlay); + sp->vOverlayOff = GetTexRangeDX(&overlayrange); + fc->bOverlay = true; + } + } + + + // ---------------------------------------------------------------------- + // Setup Main Texture + // ---------------------------------------------------------------------- + + pShader->SetTexture(pShader->tDiff, tex, IPF_CLAMP | IPF_ANISOTROPIC, Config->Anisotrophy); + fc->bTexture = (tex != nullptr); + + // ---------------------------------------------------------------------- + // Night Lights and Water Specular + // ---------------------------------------------------------------------- + + if (pShader->bNightlights || pShader->bWater) + { + if ((has_specular || has_lights) && ltex) { + pShader->SetTexture(pShader->tMask, ltex, IPF_CLAMP | IPF_ANISOTROPIC, Config->Anisotrophy); + fc->bMask = true; + } + else pShader->SetTexture(pShader->tMask, NULL, IPF_CLAMP | IPF_ANISOTROPIC, Config->Anisotrophy); + } + + if (tex == nullptr || !vPlanet->HasTextures()) + { + fc->bTexture = false; + fc->bMask = false; + fc->bCloudShd = false; + fc->bMicroTex = false; + } + + sp->vTexOff = GetTexRangeDX(&texrange); + sp->vMicroOff = GetTexRangeDX(µrange); + sp->mWorld = mWorld; + sp->fTgtScale = tgtscale; + + if (has_lights) sp->fBeta = float(mgr->Cprm().lightfac); + else sp->fBeta = 0.0f; + + + + + // --------------------------------------------------------------------- + // Setup shadow maps + // --------------------------------------------------------------------- + + if (pShader->bShdMap) + { + const SHADOWMAP* shd = scene->GetSMapData(ShdPackage::Main); + + FVECTOR3 bc = FVECTOR3(bs_pos) - shd->pos; + + double alt = scene->GetCameraAltitude() - scene->GetTargetElevation(); + + if ((alt < 10e3) && (scene->GetCameraProxyVisual() == mgr->GetPlanet())) { + + if (shd->IsValid() && (Config->ShadowMapMode != 0) && (Config->TerrainShadowing == 2)) { + + float x = dotp(bc, shd->ld); + + if (sqrt(dotp(bc, bc) - x * x) < (shd->rad + mesh->bsRad)) { + float s = float(shd->size); + float sr = 2.0f * shd->rad / s; + sp->mLVP = shd->mLVP; + sp->vSHD = FVECTOR4(sr, 1.0f / s, 0.0f, 1.0f / shd->depth); + fc->bShadows = true; + } + } + } + + pShader->SetTexture(pShader->tShadowMap, shd->ptShmRT[0]); + } + + // --------------------------------------------------------------------- + // Setup local light sources + //--------------------------------------------------------------------- + + int cfg = vPlanet->GetShaderID(); + + LightF Locals; + BOOL Spots[4]; + + if (cfg != PLT_GIANT) + { + for (int i = 0; i < 4; i++) + { + Locals.attenuation[i] = FVECTOR3(1.0f, 1.0f, 1.0f); + Locals.diffuse[i] = FVECTOR3(0.0f, 0.0f, 0.0f); + Locals.direction[i] = FVECTOR3(1.0f, 0.0f, 0.0f); + Locals.param[i] = FVECTOR4(0.0f, 0.0f, 0.0f, 0.0f); + Locals.position[i] = FVECTOR3(0.0f, 0.0f, 0.0f); + Spots[i] = false; + } + + if (scene->GetRenderPass() == RENDERPASS_MAINSCENE) + { + const vkLight* pLights = scene->GetLights(); + int nSceneLights = min(scene->GetLightCount(), MAX_SCENE_LIGHTS); + + if (pLights && nSceneLights > 0 && pShader->bLocals) + { + int nMeshLights = 0; + + _LightList LightList[MAX_SCENE_LIGHTS]; + + // Find all local lights effecting this mesh ------------------------------------------ + // + for (int i = 0; i < nSceneLights; i++) { + float il = pLights[i].GetIlluminance(bs_pos, mesh->bsRad); + if (il > 0.005f) { + LightList[nMeshLights].illuminace = il; + LightList[nMeshLights++].idx = i; + } + } + + if (nMeshLights > 0) { + + // If any, Sort the list based on illuminance ------------------------------------------- + qsort(LightList, nMeshLights, sizeof(_LightList), compare_lights); + + nMeshLights = min(nMeshLights, 4); + + // Create a list of N most effective lights --------------------------------------------- + for (int i = 0; i < nMeshLights; i++) + { + auto pL = pLights[LightList[i].idx]; + Locals.attenuation[i] = pL.Attenuation; + Locals.diffuse[i] = FVECTOR4(pL.Diffuse).rgb; + Locals.direction[i] = pL.Direction; + Locals.param[i] = pL.Param; + Locals.position[i] = pL.Position; + Spots[i] = (pL.Type == 1); + } + + // Enable local lights and feed data to shader + fc->bLocals = true; + } + } + } + } + + + + // ------------------------------------------------------------------- + // render surface mesh + // + pShader->SetVSConstants(pShader->PrmVS, sp, sizeof(ShaderParams)); + pShader->SetPSConstants(pShader->Prm, sp, sizeof(ShaderParams)); + + + pShader->SetVSConstants(pShader->FlowVS, fcv, sizeof(FlowControlVS)); + pShader->SetPSConstants(pShader->Flow, fc, sizeof(FlowControlPS)); + + if (fc->bLocals && (cfg != PLT_GIANT)) + { + pShader->SetPSConstants(pShader->Lights, &Locals, sizeof(Locals)); + pShader->SetPSConstants(pShader->Spotlight, Spots, sizeof(Spots)); + } + + pShader->UpdateTextures(); + + pDev->SetStreamSource(0, mesh->pVB, 0, sizeof(VERTEX_2TEX)); + pDev->SetIndices(mesh->pIB); + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh->nv, 0, mesh->nf); + + // Render tile bounding box + // + /* + if (DebugControls::IsActive()) { + DWORD flags = *(DWORD*)mgr->GetClient()->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + if (flags&DBG_FLAGS_TILEBOXES) { + vkEffect::RenderTileBoundingBox(&mWorld, mesh->Box, &FVECTOR4(1,0,0,1)); + } + }*/ +} + +// ----------------------------------------------------------------------- + +void SurfTile::MatchEdges () +{ + if (edgeok) return; // done already + edgeok = true; + if (!mesh) return; // sanity check + + QuadTreeNode* lngnbr = smgr->FindNode(lvl, ilng + (ilng & 1 ? 1 : -1), ilat); + QuadTreeNode* latnbr = smgr->FindNode(lvl, ilng, ilat + (ilat & 1 ? 1 : -1)); + QuadTreeNode* dianbr = smgr->FindNode(lvl, ilng + (ilng & 1 ? 1 : -1), ilat + (ilat & 1 ? 1 : -1)); + + + if (lngnbr && !(lngnbr->Entry()->state & TILE_VALID)) lngnbr = 0; + if (latnbr && !(latnbr->Entry()->state & TILE_VALID)) latnbr = 0; + if (dianbr && !(dianbr->Entry()->state & TILE_VALID)) dianbr = 0; + + int new_lngnbr_lvl = (lngnbr ? lngnbr->Entry()->lvl : lvl); + int new_latnbr_lvl = (latnbr ? latnbr->Entry()->lvl : lvl); + int new_dianbr_lvl = (dianbr ? dianbr->Entry()->lvl : lvl); + + // fix lower-level neighbours first + bool nbr_updated = false; + if (new_lngnbr_lvl < lvl) { + lngnbr->Entry()->MatchEdges (); + nbr_updated = true; + } + if (new_latnbr_lvl < lvl) { + latnbr->Entry()->MatchEdges (); + nbr_updated = true; + } + if (!nbr_updated && new_dianbr_lvl < lvl) { + // now we need to check the diagonal neighbour, since this could be lower resolution + // and thus responsible for the corner node elevation + dianbr->Entry()->MatchEdges (); + } + + bool lngedge_changed = (new_lngnbr_lvl != lngnbr_lvl); + bool latedge_changed = (new_latnbr_lvl != latnbr_lvl); + bool diaedge_changed = (new_dianbr_lvl != dianbr_lvl); + + if (lngedge_changed || latedge_changed || diaedge_changed) { + if (new_dianbr_lvl < new_lngnbr_lvl && new_dianbr_lvl < new_latnbr_lvl) { + FixCorner (dianbr ? dianbr->Entry() : 0); + FixLatitudeBoundary (latnbr ? latnbr->Entry() : 0, true); + FixLongitudeBoundary (lngnbr ? lngnbr->Entry() : 0, true); + } else if (new_latnbr_lvl < new_lngnbr_lvl) { + FixLatitudeBoundary (latnbr ? latnbr->Entry() : 0); + FixLongitudeBoundary (lngnbr ? lngnbr->Entry() : 0, true); + } else { + FixLongitudeBoundary (lngnbr ? lngnbr->Entry() : 0); + FixLatitudeBoundary (latnbr ? latnbr->Entry() : 0, true); + } + mesh->MapVertices(mgr->Dev()); // copy the updated vertices to the vertex buffer + lngnbr_lvl = new_lngnbr_lvl; + latnbr_lvl = new_latnbr_lvl; + dianbr_lvl = new_dianbr_lvl; + } +} + +// ----------------------------------------------------------------------- + +void SurfTile::FixCorner (const SurfTile *nbr) +{ + if (!mesh) return; // sanity check + + int res = mgr->GridRes(); + int vtx_idx = (ilat & 1 ? 0 : (res+1)*res) + (ilng & 1 ? res : 0); + VERTEX_2TEX &vtx_store = mesh->vtx[mesh->nv + (ilat & 1 ? 0 : res)]; + + float *elev = ElevationData(); + if (!elev) return; + + if (nbr) { + float *nbr_elev = nbr->ElevationData(); + if (!nbr_elev) return; + + float corner_elev = elev[TILE_ELEVSTRIDE+1 + (ilat & 1 ? 0 : TILE_ELEVSTRIDE*res) + (ilng & 1 ? res : 0)]; + float nbr_corner_elev = nbr_elev[TILE_ELEVSTRIDE+1 + (ilat & 1 ? TILE_ELEVSTRIDE*res : 0) + (ilng & 1 ? 0 : res)]; + + double rad = mgr->CbodySize(); + double radfac = (rad+nbr_corner_elev)/(rad+corner_elev); + mesh->vtx[vtx_idx].x = (float)(vtx_store.x*radfac + vtxshift.x*(radfac-1.0)); + mesh->vtx[vtx_idx].y = (float)(vtx_store.y*radfac + vtxshift.y*(radfac-1.0)); + mesh->vtx[vtx_idx].z = (float)(vtx_store.z*radfac + vtxshift.z*(radfac-1.0)); + } +} + +// ----------------------------------------------------------------------- + +void SurfTile::FixLongitudeBoundary (const SurfTile *nbr, bool keep_corner) +{ + // Fix the left or right edge + int i, i0, i1, j, vtx_ofs, nbrlvl, dlvl; + int res = mgr->GridRes(); + VERTEX_2TEX *vtx_store; + + vtx_ofs = (ilng & 1 ? res : 0); // check if neighbour is at left or right edge + if (nbr && nbr->mesh && nbr->mesh->vtx) { + nbrlvl = min(nbr->lvl, lvl); // we don't need to worry about neigbour levels higher than ours + vtx_store = mesh->vtx + mesh->nv; + if (nbrlvl == lvl) { // put my own edge back + i0 = 0; + i1 = res; + if (keep_corner) + if (ilat & 1) i0++; else i1--; + for (i = i0; i <= i1; i++) + mesh->vtx[vtx_ofs+i*(res+1)] = vtx_store[i]; + } else { // interpolate to neighbour's left edge + dlvl = lvl-nbrlvl; + int nsub = 1 << dlvl; // number of tiles fitting alongside the lowres neighbour + if (nsub <= res) { // for larger tile level differences the interleaved sampling method doesn't work + float *elev = ElevationData(); + float *nbr_elev = nbr->ElevationData(); + if (elev && nbr_elev) { + elev += TILE_ELEVSTRIDE+1 + vtx_ofs; // add offset + int nsub = 1 << dlvl; // number of tiles fitting alongside the lowres neighbour + int vtx_skip = nsub*(res+1); // interleave factor for nodes attaching to neigbour nodes + int nbr_range = res/nsub; // number of neighbour vertices attaching to us - 1 + int subidxmask = (1<CbodySize(); + // match nodes to neighbour elevations + i0 = 0; + i1 = nbr_range; + if (keep_corner) + if (ilat & 1) i0++; else i1--; + for (i = i0; i <= i1; i++) { + double radfac = (rad+nbr_elev[i*TILE_ELEVSTRIDE])/(rad+elev[i*TILE_ELEVSTRIDE*nsub]); + mesh->vtx[vtx_ofs+i*vtx_skip].x = (float)(vtx_store[i*nsub].x*radfac + vtxshift.x*(radfac-1.0)); + mesh->vtx[vtx_ofs+i*vtx_skip].y = (float)(vtx_store[i*nsub].y*radfac + vtxshift.y*(radfac-1.0)); + mesh->vtx[vtx_ofs+i*vtx_skip].z = (float)(vtx_store[i*nsub].z*radfac + vtxshift.z*(radfac-1.0)); + } + // interpolate the nodes that fall between neighbour nodes + for (i = 0; i < nbr_range; i++) + for (j = 1; j < nsub; j++) + VtxInterpolate (mesh->vtx[vtx_ofs+j*(res+1)+i*vtx_skip], mesh->vtx[vtx_ofs+i*vtx_skip], mesh->vtx[vtx_ofs+(i+1)*vtx_skip], (double)j/double(nsub)); + } + } else { + // problems + } + } + } +} + +// ----------------------------------------------------------------------- + +void SurfTile::FixLatitudeBoundary (const SurfTile *nbr, bool keep_corner) +{ + // Fix the top or bottom edge + int i, i0, i1, j, nbrlvl, dlvl; + int res = mgr->GridRes(); + VERTEX_2TEX *vtx_store; + + int line = (ilat & 1 ? 0 : res); + int vtx_ofs = (ilat & 1 ? 0 : line*(res+1)); + if (nbr && nbr->mesh && nbr->mesh->vtx) { + nbrlvl = min(nbr->lvl, lvl); // we don't need to worry about neigbour levels higher than ours + vtx_store = mesh->vtx + mesh->nv + res + 1; + if (nbrlvl == lvl) { // put my own edge back + i0 = 0; + i1 = res; + if (keep_corner) + if (ilng & 1) i1--; else i0++; + for (i = i0; i <= i1; i++) + mesh->vtx[vtx_ofs+i] = vtx_store[i]; + } else { + dlvl = lvl-nbrlvl; + int nsub = 1 << dlvl; // number of tiles fitting alongside the lowres neighbour + if (nsub <= res) { // for larger tile level differences the interleaved sampling method doesn't work + float *elev = ElevationData(); + float *nbr_elev = nbr->ElevationData(); + if (elev && nbr_elev) { + elev += (line+1)*TILE_ELEVSTRIDE + 1; // add offset + int nsub = 1 << dlvl; // number of tiles fitting alongside the lowres neighbour + int vtx_skip = nsub; // interleave factor for nodes attaching to neigbour nodes + int nbr_range = res/nsub; // number of neighbour vertices attaching to us - 1 + int subidxmask = (1<CbodySize(); + // match nodes to neighbour elevations + i0 = 0; + i1 = nbr_range; + if (keep_corner) + if (ilng & 1) i1--; else i0++; + for (i = i0; i <= i1; i++) { + double radfac = (rad+nbr_elev[i])/(rad+elev[i*nsub]); + mesh->vtx[vtx_ofs+i*vtx_skip].x = (float)(vtx_store[i*nsub].x*radfac + vtxshift.x*(radfac-1.0)); + mesh->vtx[vtx_ofs+i*vtx_skip].y = (float)(vtx_store[i*nsub].y*radfac + vtxshift.y*(radfac-1.0)); + mesh->vtx[vtx_ofs+i*vtx_skip].z = (float)(vtx_store[i*nsub].z*radfac + vtxshift.z*(radfac-1.0)); + } + // interpolate the nodes that fall between neighbour nodes + for (i = 0; i < nbr_range; i++) + for (j = 1; j < nsub; j++) + VtxInterpolate (mesh->vtx[vtx_ofs+i*vtx_skip+j], mesh->vtx[vtx_ofs+i*vtx_skip], mesh->vtx[vtx_ofs+(i+1)*vtx_skip], (double)j/double(nsub)); + } + } else { + // problems + } + } + } +} + +// ----------------------------------------------------------------------- + +void SurfTile::CreateLabels() +{ + if (!label) { + label = TileLabel::Create(this); + } +} + +// ----------------------------------------------------------------------- + +inline void SurfTile::DeleteLabels() +{ + SAFE_DELETE(label); +} + +// ----------------------------------------------------------------------- + +void SurfTile::RenderLabels(vkPad *skp, oapi::Font **labelfont, int *fontidx) +{ + if (!label) return; + label->Render(skp, labelfont, fontidx); +} + + +// ======================================================================= +// ======================================================================= + +template<> +void TileManager2::Render (MATRIX4 &dwmat, bool use_zbuf, const vPlanet::RenderPrm &rprm) +{ + bUseZ = use_zbuf; + ElevModeLvl = 0; + + // set generic parameters + SetRenderPrm (dwmat, 0, use_zbuf, rprm); + + int i; + class Scene *scene = GetClient()->GetScene(); + + // adjust scaling parameters (can only be done if no z-buffering is in use) + if (!use_zbuf) { + double R = obj_size; + double D = prm.cdist*R; + double zmax = (D - R*R/D) * 1.5; + double zmin = max (2.0, min (zmax*1e-4, (D-R) * 0.8)); + + vp->GetScatterConst()->mVP = scene->PushCameraFrustumLimits(zmin, zmax); + } + + // build a transformation matrix for frustum testing + MATRIX4 Mproj = _MATRIX4(scene->GetProjectionMatrix()); + Mproj.m33 = 1.0; Mproj.m43 = -1.0; // adjust near plane to 1, far plane to infinity + MATRIX4 Mview = _MATRIX4(scene->GetViewMatrix()); + prm.dviewproj = mul(Mview, Mproj); + + // --------------------------------------------------------------------- + + static float spec_base = 0.7f; // 0.95f; + + bool has_ripples = vp->HasRipples(); + + // Choose a proper shader for a body + // + pShader = vp->GetShader(); + int cfg = vp->GetShaderID(); + + pShader->ClearTextures(); + pShader->Setup(pPatchVertexDecl, bUseZ, 0); + pShader->GetDevice()->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + + ShaderParams* sp = vp->GetTerrainParams(); + FlowControlPS* fc = vp->GetFlowControl(); + FlowControlVS* fcv = vp->GetFlowControlVS(); + ConstParams* cp = vp->GetScatterConst(); + + + pShader->SetVSConstants("Const", cp, sizeof(ConstParams)); + pShader->SetPSConstants("Const", cp, sizeof(ConstParams)); + + if (pShader->bAtmosphere && cfg != PLT_GIANT) + { + pShader->SetTexture("tLndRay", vp->GetScatterTable(RAY_LAND), IPF_LINEAR | IPF_CLAMP); + pShader->SetTexture("tLndMie", vp->GetScatterTable(MIE_LAND), IPF_LINEAR | IPF_CLAMP); + pShader->SetTexture("tLndAtn", vp->GetScatterTable(ATN_LAND), IPF_LINEAR | IPF_CLAMP); + pShader->SetTexture("tSun", vp->GetScatterTable(SUN_COLOR), IPF_LINEAR | IPF_CLAMP); + pShader->SetTexture("tNoise", GetClient()->GetNoiseTex(), IPF_LINEAR | IPF_WRAP); + + if (pShader->bWater) { + pShader->SetTexture("tAmbient", vp->GetScatterTable(SKY_AMBIENT), IPF_LINEAR | IPF_CLAMP); + } + } + + if (ElevMode == eElevMode::Spherical) { + // Force spherical rendering at shader level + fcv->bSpherical = true; + } + + if (!use_zbuf) fcv->bSpherical = true; + + // ------------------------------------------------------------------- + vVessel *vFocus = scene->GetFocusVisual(); + vPlanet *vProxy = scene->GetCameraProxyVisual(); + + + // Setup micro textures --------------------------------------------- + // + if (vp->MicroCfg.bEnabled && pShader->bMicrotex) + { + UINT Filter = IPF_ANISOTROPIC; + if (Config->MicroFilter == 0) Filter = IPF_POINT; + if (Config->MicroFilter == 1) Filter = IPF_LINEAR; + UINT micro_aniso = (1 << (max(1, Config->MicroFilter) - 1)); + pShader->SetTexture("tMicroA", vp->MicroCfg.Level[0].pTex, IPF_WRAP | Filter, micro_aniso); + pShader->SetTexture("tMicroB", vp->MicroCfg.Level[1].pTex, IPF_WRAP | Filter, micro_aniso); + pShader->SetTexture("tMicroC", vp->MicroCfg.Level[2].pTex, IPF_WRAP | Filter, micro_aniso); + + fc->bMicroNormals = vp->MicroCfg.bNormals; + } + + if (has_ripples && pShader->bRipples) { + fc->bRipples = true; + pShader->SetTexture("tOcean", hOcean, IPF_WRAP | IPF_ANISOTROPIC, 4); + } + + vp->InitEclipse(pShader); + + if (Config->NoPlanetAA) pShader->GetDevice()->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, 0); + + // ------------------------------------------------------------------ + // TODO: render full sphere for levels < 4 + + loader->WaitForMutex(); + + // update the tree + for (i = 0; i < 2; i++) + ProcessNode (tiletree+i); + + vp->tile_cache = NULL; + + // render the tree + for (i = 0; i < 2; i++) + RenderNode (tiletree+i); + + loader->ReleaseMutex(); + + if (Config->NoPlanetAA) pShader->GetDevice()->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, 1); + + // Backup the stats and clear counters + if (scene->GetRenderPass() == RENDERPASS_MAINSCENE) prevstat = elvstat; + elvstat.Elev = elvstat.Sphe = 0; + + /*if (scene->GetRenderPass() == RENDERPASS_MAINSCENE) { + if (GetHandle() == scene->GetCameraNearBody()) { + vkDebugLog("Body=%s, Elv=%d, Sph=%d", CbodyName(), prevstat.Elev, prevstat.Sphe); + if (ElevMode == eElevMode::Elevated) vkDebugLog("Elevated"); + if (ElevMode == eElevMode::Spherical) vkDebugLog("Spherical"); + if (ElevMode == eElevMode::ForcedElevated) vkDebugLog("ForcedElevated"); + } + }*/ + + // Pop previous frustum configuration, must initialize mVP + if (!use_zbuf) vp->GetScatterConst()->mVP = scene->PopCameraFrustumLimits(); +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::RenderLabels(vkPad *skp, oapi::Font **labelfont, int *fontidx) +{ + for (int i = 0; i < 2; ++i) { + RenderNodeLabels(tiletree + i, skp, labelfont, fontidx); + } +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::CreateLabels() +{ + loader->WaitForMutex(); + for (int i = 0; i < 2; ++i) { + SetSubtreeLabels(tiletree + i, true); + } + loader->ReleaseMutex(); +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::DeleteLabels() +{ + loader->WaitForMutex(); + for (int i = 0; i < 2; ++i) { + SetSubtreeLabels(tiletree + i, false); + } + loader->ReleaseMutex(); +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::SetSubtreeLabels(QuadTreeNode *node, bool activate) +{ + if (node->Entry()) { + if (activate) node->Entry()->CreateLabels(); + else node->Entry()->DeleteLabels(); + } + for (int i = 0; i < 4; ++i) { + if (node->Child(i)) { + SetSubtreeLabels(node->Child(i), activate); + } + } +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::LoadZTrees() +{ + treeMgr = new ZTreeMgr*[ntreeMgr = 5](); + if (cprm.tileLoadFlags & 0x0002) { + treeMgr[0] = ZTreeMgr::CreateFromFile(m_dataRootDir.c_str(), ZTreeMgr::LAYER_SURF); + treeMgr[1] = ZTreeMgr::CreateFromFile(m_dataRootDir.c_str(), ZTreeMgr::LAYER_MASK); + treeMgr[2] = ZTreeMgr::CreateFromFile(m_dataRootDir.c_str(), ZTreeMgr::LAYER_ELEV); + treeMgr[3] = ZTreeMgr::CreateFromFile(m_dataRootDir.c_str(), ZTreeMgr::LAYER_ELEVMOD); + treeMgr[4] = ZTreeMgr::CreateFromFile(m_dataRootDir.c_str(), ZTreeMgr::LAYER_LABEL); + } + else { + for (int i = 0; i < ntreeMgr; i++) + treeMgr[i] = 0; + } +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::InitHasIndividualFiles() +{ + hasIndividualFiles = new bool[ntreeMgr](); + if (cprm.tileLoadFlags & 0x0001) { + const char *name[] = { "Surf", "Mask", "Elev", "Elev_mod", "Label" }; + char path[MAX_PATH], dummy[MAX_PATH]; + for (int i = 0; i < ARRAYSIZE(name); ++i) { + sprintf_s(path, MAX_PATH, "%s\\%s", m_dataRootDir.c_str(), name[i]); + hasIndividualFiles[i] = FileExists(path); + } + } +} + +// ----------------------------------------------------------------------- + +template<> +Tile * TileManager2::SearchTile (double lng, double lat, int maxlvl, bool bOwntex) const +{ + if (lng<0) return SearchTileSub(&tiletree[0], lng, lat, maxlvl, bOwntex); + else return SearchTileSub(&tiletree[1], lng, lat, maxlvl, bOwntex); +} +// ----------------------------------------------------------------------- + +template<> +int TileManager2::GetElevation(double lng, double lat, double *elev, FVECTOR3 *nrm, SurfTile **cache) +{ + int rv = 0; + loader->WaitForMutex(); + if (lng<0) rv = tiletree[0].Entry()->GetElevation(lng, lat, elev, nrm, cache); + else rv = tiletree[1].Entry()->GetElevation(lng, lat, elev, nrm, cache); + loader->ReleaseMutex(); + return rv; +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::Unload(int lvl) +{ + tiletree[0].DelAbove(lvl); + tiletree[1].DelAbove(lvl); +} + +// ----------------------------------------------------------------------- + +template<> +void TileManager2::Pick(FVECTOR3 &vRay, TILEPICK *pPick) +{ + std::list tiles; + + pPick->d = 1e12f; + + // Give me a list of rendered tiles ---------------------------------------------- + // + QueryTiles(&tiletree[0], tiles); + QueryTiles(&tiletree[1], tiles); + + for (auto tile : tiles) tile->Pick(&(tile->mWorld), &vRay, *pPick); +} + +// ----------------------------------------------------------------------- + +template<> +SURFHANDLE TileManager2::SeekTileTexture(int iLng, int iLat, int level, int flags) +{ + bool bOk = false; + LPDIRECT3DTEXTURE9 pTex = NULL; + + if (flags & gcTileFlags::TEXTURE) + { + if (flags & gcTileFlags::CACHE) + { + char path[MAX_PATH]; + sprintf_s(path, MAX_PATH, "%s\\Surf\\%02d\\%06d\\%06d.dds", m_dataRootDir.c_str(), level + 4, iLat, iLng); + bOk = (D3DXCreateTextureFromFileEx(Dev(), path, 0, 0, 0, 0, D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, + D3DX_FILTER_NONE, D3DX_FILTER_BOX, 0, NULL, NULL, &pTex) == S_OK); + } + + if (flags & gcTileFlags::TREE) + { + if (!bOk) { + if (ZTreeManager(0)) { + BYTE *buf; + DWORD ndata = ZTreeManager(0)->ReadData(level + 4, iLat, iLng, &buf); + if (ndata) { + if (D3DXCreateTextureFromFileInMemoryEx(Dev(), buf, ndata, 0, 0, D3DX_FROM_FILE, 0, + D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_FILTER_NONE, D3DX_FILTER_BOX, 0, NULL, NULL, &pTex) == S_OK) bOk = true; + ZTreeManager(0)->ReleaseData(buf); + } + } + } + } + } + + if (flags & gcTileFlags::MASK) + { + if (flags & gcTileFlags::CACHE) + { + char path[MAX_PATH]; + sprintf_s(path, MAX_PATH, "%s\\Mask\\%02d\\%06d\\%06d.dds", m_dataRootDir.c_str(), level + 4, iLat, iLng); + bOk = (D3DXCreateTextureFromFileEx(Dev(), path, 0, 0, 0, 0, D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, + D3DX_FILTER_NONE, D3DX_FILTER_BOX, 0, NULL, NULL, &pTex) == S_OK); + } + + if (flags & gcTileFlags::TREE) + { + if (!bOk) { + if (ZTreeManager(1)) { + BYTE* buf; + DWORD ndata = ZTreeManager(1)->ReadData(level + 4, iLat, iLng, &buf); + if (ndata) { + if (D3DXCreateTextureFromFileInMemoryEx(Dev(), buf, ndata, 0, 0, D3DX_FROM_FILE, 0, + D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_FILTER_NONE, D3DX_FILTER_BOX, 0, NULL, NULL, &pTex) == S_OK) bOk = true; + ZTreeManager(1)->ReleaseData(buf); + } + } + } + } + } + + if (bOk && pTex) return new SurfNative(pTex, OAPISURFACE_TEXTURE); + return NULL; +} + +// ----------------------------------------------------------------------- + +template<> +bool TileManager2::HasTileData(int iLng, int iLat, int level, int flags) +{ + bool bOk = false; + + if (flags & gcTileFlags::TEXTURE) { + if (flags & gcTileFlags::CACHE) { + char path[MAX_PATH]; + sprintf_s(path, MAX_PATH, "%s\\Surf\\%02d\\%06d\\%06d.dds", m_dataRootDir.c_str(), level + 4, iLat, iLng); + bOk = FileExists(path); + + } + if (flags & gcTileFlags::TREE) if (!bOk && ZTreeManager(0)) if (ZTreeManager(0)->Idx(level + 4, iLat, iLng) != ((DWORD)-1)) bOk = true; + } + + if (flags & gcTileFlags::MASK) { + if (flags & gcTileFlags::CACHE) { + char path[MAX_PATH]; + sprintf_s(path, MAX_PATH, "%s\\Mask\\%02d\\%06d\\%06d.dds", m_dataRootDir.c_str(), level + 4, iLat, iLng); + bOk = FileExists(path); + } + if (flags & gcTileFlags::TREE) if (!bOk && ZTreeManager(1)) if (ZTreeManager(1)->Idx(level + 4, iLat, iLng) != ((DWORD)-1)) bOk = true; + } + + if (flags & gcTileFlags::ELEVATION) { + if (flags & gcTileFlags::CACHE) { + char path[MAX_PATH]; + sprintf_s(path, MAX_PATH, "%s\\Elev\\%02d\\%06d\\%06d.elv", m_dataRootDir.c_str(), level + 4, iLat, iLng); + bOk = FileExists(path); + } + if (flags & gcTileFlags::TREE) if (!bOk && ZTreeManager(2)) if (ZTreeManager(2)->Idx(level + 4, iLat, iLng) != ((DWORD)-1)) bOk = true; + } + + return bOk; +} + +// ----------------------------------------------------------------------- + +template<> +float* TileManager2::BrowseElevationData(int lvl, int ilat, int ilng, int flags, ELEVFILEHEADER* _hdr) +{ + ELEVFILEHEADER ehdr; + const int ndat = TILE_ELEVSTRIDE * TILE_ELEVSTRIDE; + float* elev = NULL; + char path[MAX_PATH]; + char fname[128]; + FILE* f; + int i; + + // Elevation data + if (flags & gcTileFlags::CACHE) { // try loading from individual tile file + sprintf_s(path, MAX_PATH, "%s\\Elev\\%02d\\%06d\\%06d.elv", m_dataRootDir.c_str(), lvl + 4, ilat, ilng); + if (!fopen_s(&f, path, "rb")) { + elev = new float[ndat]; + fread(&ehdr, sizeof(ELEVFILEHEADER), 1, f); + if (ehdr.hdrsize != sizeof(ELEVFILEHEADER)) fseek(f, ehdr.hdrsize, SEEK_SET); + + ehdr.scale = 1.0; + + switch (ehdr.dtype) { + case 0: // flat tile, defined by offset + for (i = 0; i < ndat; i++) elev[i] = 0.0f; + break; + case 8: { + UINT8* tmp = new UINT8[ndat]; + fread(tmp, sizeof(UINT8), ndat, f); + for (i = 0; i < ndat; i++) elev[i] = float(tmp[i]); + delete[]tmp; + break; + } + case -16: { + INT16* tmp = new INT16[ndat]; + fread(tmp, sizeof(INT16), ndat, f); + for (i = 0; i < ndat; i++) elev[i] = float(tmp[i]); + delete[]tmp; + break; + } + } + fclose(f); + } + } + if (!elev && (flags & gcTileFlags::TREE) && ZTreeManager(2)) { // try loading from compressed archive + BYTE* buf; + DWORD ndata = ZTreeManager(2)->ReadData(lvl + 4, ilat, ilng, &buf); + if (ndata) { + BYTE* p = buf; + elev = new float[ndat]; + memcpy(&ehdr, p, sizeof(ELEVFILEHEADER)); + p += ehdr.hdrsize; + INT16* pi = (INT16*)p; + switch (ehdr.dtype) { + case 0: + for (i = 0; i < ndat; i++) elev[i] = 0.0f; break; + case 8: + for (i = 0; i < ndat; i++) elev[i] = float(*p++); break; + case -16: + for (i = 0; i < ndat; i++) elev[i] = float(*pi++); break; + } + ZTreeManager(2)->ReleaseData(buf); + } + } + + if (elev) { + if (ehdr.scale != 1.0) for (i = 0; i < ndat; i++) elev[i] = trunc(elev[i] * float(ehdr.scale)); + if (ehdr.offset) for (i = 0; i < ndat; i++) elev[i] += trunc(float(ehdr.offset)); + } + + + // Elevation mod data + if (elev && (flags & gcTileFlags::MOD)) { + bool ok = false; + ELEVFILEHEADER hdr; + if (flags & gcTileFlags::CACHE) { // try loading from individual tile file + sprintf_s(fname, ARRAYSIZE(fname), "%s\\Elev_mod\\%02d\\%06d\\%06d.elv", CbodyName(), lvl + 4, ilat, ilng); + bool found = GetClient()->TexturePath(fname, path); + if (found && !fopen_s(&f, path, "rb")) { + + fread(&hdr, sizeof(ELEVFILEHEADER), 1, f); + if (hdr.hdrsize != sizeof(ELEVFILEHEADER)) fseek(f, hdr.hdrsize, SEEK_SET); + + switch (hdr.dtype) + { + case 0: // overwrite the entire tile with a flat offset + for (i = 0; i < ndat; i++) elev[i] = float(hdr.offset); + break; + + case 8: { + const UINT8 mask = UCHAR_MAX; + UINT8* tmp = new UINT8[ndat]; + fread(tmp, sizeof(UINT8), ndat, f); + for (i = 0; i < ndat; i++) + if (tmp[i] != mask) + elev[i] = float(trunc(float(tmp[i]) * hdr.scale) + trunc(hdr.offset)); + delete[]tmp; + break; + } + case -16: { + const INT16 mask = SHRT_MAX; + INT16* tmp = new INT16[ndat]; + fread(tmp, sizeof(INT16), ndat, f); + for (i = 0; i < ndat; i++) + if (tmp[i] != mask) + elev[i] = float(trunc(float(tmp[i]) * hdr.scale) + trunc(hdr.offset)); + delete[]tmp; + break; + } + } + fclose(f); + ok = true; + } + } + + if (!ok && (flags & gcTileFlags::TREE) && ZTreeManager(3)) { // try loading from compressed archive + BYTE* buf; + DWORD ndata = ZTreeManager(3)->ReadData(lvl + 4, ilat, ilng, &buf); + if (ndata) { + BYTE* p = buf; + ELEVFILEHEADER* phdr = (ELEVFILEHEADER*)p; + p += phdr->hdrsize; + + switch (phdr->dtype) + { + case 0: + for (i = 0; i < ndat; i++) elev[i] = float(phdr->offset); + break; + case 8: { + const UINT8 mask = UCHAR_MAX; + for (i = 0; i < ndat; i++) + if (p[i] != mask) + elev[i] = float(trunc(float(p[i]) * phdr->scale) + trunc(phdr->offset)); + break; + } + case -16: { + const INT16 mask = SHRT_MAX; + INT16* buf16 = (INT16*)p; + for (i = 0; i < ndat; i++) + if (buf16[i] != mask) + elev[i] = float(trunc(float(buf16[i]) * phdr->scale) + trunc(phdr->offset)); + break; + } + } + ZTreeManager(3)->ReleaseData(buf); + } + } + } + + if (_hdr) memcpy(_hdr, &ehdr, sizeof(ELEVFILEHEADER)); + + return elev; +} diff --git a/OVP/VulkanClient/Surfmgr2.h b/OVP/VulkanClient/Surfmgr2.h new file mode 100644 index 000000000..0eda39f42 --- /dev/null +++ b/OVP/VulkanClient/Surfmgr2.h @@ -0,0 +1,98 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +#ifndef __SURFMGR2_H +#define __SURFMGR2_H + +#include "Tilemgr2_imp.hpp" +#include "TileLabel.h" +#include "Pad.h" + +/** + * \brief Planetary surface rendering engine. + * + * Planetary surface rendering engine v2, including a simple + * LOD (level-of-detail) algorithm for surface patch resolution. + */ +class SurfTile: public Tile { + friend class TileManager2Base; + template friend class TileManager2; + friend class TileLabel; + + void MatchEdges (); + +public: + SurfTile (TileManager2Base *_mgr, int _lvl, int _ilat, int _ilng); + ~SurfTile (); + + inline QuadTreeNode *GetNode() const { return node; } + inline void SetNode (QuadTreeNode *_node) { node = _node; } + // Register the tile to a quad tree node + + int GetElevation(double lng, double lat, double *elev, FVECTOR3 *nrm=NULL, SurfTile **cache=NULL, bool bFilter=true, bool bGet=false) const; + + double GetCameraDistance(); + SurfTile *getTextureOwner(); + + LPDIRECT3DTEXTURE9 SetOverlay(LPDIRECT3DTEXTURE9 pOverlay, bool bOwn = true); + bool DeleteOverlay(LPDIRECT3DTEXTURE9 pOverlay = NULL); + + double GetMinElev() const { return ehdr.emin; } // virtual from Tile:: + double GetMaxElev() const { return ehdr.emax; } // virtual from Tile:: + double GetMeanElev() const { return ehdr.emean; } // virtual from Tile:: + +protected: + virtual Tile *getParent() const { return node && node->Parent() ? node->Parent()->Entry() : NULL; } + inline SurfTile *getSurfParent() const { return node && node->Parent() ? node->Parent()->Entry() : NULL; } + + // Return pointer to parent tile, if exists + + void Load (); + void PreLoad (); + INT16 *ReadElevationFile (const char *name, int lvl, int ilat, int ilng); + bool LoadElevationData (); + void Render (); + void StepIn (); + bool IsElevated() { return (ggelev!=NULL); } + + TileManager2 *smgr; // surface tile manager interface + QuadTreeNode *node; // my node in the quad tree, if I'm part of a tree + + void FixCorner (const SurfTile *nbr); + // Match corner elevation to neighbour + + void FixLongitudeBoundary (const SurfTile *nbr, bool keep_corner=false); + // Match longitude edge elevation to neighbour. If keep_corner==true, skip corner node + + void FixLatitudeBoundary (const SurfTile *nbr, bool keep_corner=false); + // Match latitude edge elevation to neighbour. If keep_corner==true, skip corner node + + // v2 Labels interface ----------------------------------------------- + void CreateLabels(); ///< create the label object from the label tile file, if available + void DeleteLabels(); ///< delete the TileLabel object if it exists + void RenderLabels(vkPad *skp, oapi::Font **labelfont, int *fontidx); + +private: + bool InterpolateElevationGrid(const float *pelev, float *elev); + float Interpolate(FMATRIX4 &in, float x, float y); + float *ElevationData () const; + void ComputeElevationData(const float *elev) const; + float fixinput(double, int); + FVECTOR4 MicroTexRange(SurfTile *pT, int lvl) const; + + PlanetShader* pShader; + mutable ELEVFILEHEADER ehdr;///< Let's store the complete header for later use + FVECTOR2 MicroRep[3]; + DWORD MaxRep; + LPDIRECT3DTEXTURE9 ltex; ///< landmask/nightlight texture, if applicable + INT16 *elev_file; ///< elevation data [m] + float *elev; ///< elevation data [m] (8x subsampled) + mutable float *ggelev; ///< pointer to my elevation data in the great-grandparent + + TileLabel *label; ///< surface labels associated with this tile +}; + +#endif // !__SURFMGR2_H diff --git a/OVP/VulkanClient/TextMgr.cpp b/OVP/VulkanClient/TextMgr.cpp new file mode 100644 index 000000000..ce5483080 --- /dev/null +++ b/OVP/VulkanClient/TextMgr.cpp @@ -0,0 +1,584 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + +#include +#include +#include +#include +#include "MathAPI.h" +#include "TextMgr.h" +#include "Log.h" +#include "Client.h" +#include "Surface.h" +#include "Util.h" +#include "Config.h" +#include "Pad.h" + +#if defined(_MSC_VER) && (_MSC_VER <= 1700 ) // Microsoft Visual Studio Version 2012 and lower +#define round(v) floor(v+0.5) +#endif + +// ---------------------------------------------------------------------------------------- +// +vkText::vkText(LPDIRECT3DDEVICE9 pDevice) : + red (1.0), + green (1.0), + blue (1.0), + alpha (1.0), + tex_w (), + tex_h (), + sharing (), + spacing (0.0f), + linespacing(), + max_len (), + rotation (), + scaling (1.0f), + charset (ANSI_CHARSET), + first (0), + halign (), + valign (), + pDev (pDevice), + pTex (NULL), + FontData (NULL), + wfont (NULL) +{ + ZeroMemory(&tm, sizeof(TEXTMETRIC)); + ZeroMemory(&lf, sizeof(LOGFONT)); +} + + +// ---------------------------------------------------------------------------------------- +// +vkText::~vkText() +{ + SAFE_DELETEA(FontData); + SAFE_RELEASE(pTex); + SAFE_RELEASE(wfont); +} + + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetCharSet(int set) +{ + charset=set; +} + + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetTextSpace(float space) +{ + spacing = space; +} + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetTextHAlign(int x) +{ + halign=x; +} + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetTextVAlign(int x) +{ + valign=x; +} + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetTextShare(int share) +{ + sharing = (tm.tmHeight*share)/100; +} + + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetLineSpace(int line) +{ + linespacing = tm.tmHeight + (tm.tmHeight*line)/100; +} + + +// ---------------------------------------------------------------------------------------- +// +int vkText::GetLineSpace() +{ + return linespacing; +} + + +// ---------------------------------------------------------------------------------------- +// +bool vkText::Init(HFONT hFont) +{ + if (hFont==NULL) { + LogErr("NULL Font in vkText::Init()"); + return false; + } + + // Receive font attributes + GetObject(hFont, sizeof(LOGFONT), &lf); + + tex_w = 2048; // Texture Width + tex_h = 32; + + // Allocate space for data + // + FontData = new vkFontData[256](); // zero-initialized + + LogAlw("[NEW FONT] (%31s), Size=%d, Weight=%d Pitch&Family=%x", lf.lfFaceName, lf.lfHeight, lf.lfWeight, lf.lfPitchAndFamily); + + bool bFirst = true; + +restart: + + if (tex_h>=2048) { + LogErr("^^ Font is too large for pre-rendering"); + return false; + } + + LPDIRECT3DTEXTURE9 pSrcTex = NULL; + LPDIRECT3DSURFACE9 pSurf = NULL; + + if (pDev->CreateTexture(tex_w, tex_h, 1, 0, D3DFMT_R5G6B5, D3DPOOL_SYSTEMMEM, &pSrcTex, NULL)!=S_OK) { + LogErr("vkText::CreateOffscreenPlainSurface Fail"); + return false; + } + + HR(pSrcTex->GetSurfaceLevel(0, &pSurf)); + + HDC hDC = NULL; + + if (pSurf->GetDC(&hDC)!=S_OK) { + LogErr("vkText::GetDC Fail"); + return false; + } + + HFONT hOld = (HFONT)SelectObject(hDC, hFont); + + if (hOld == NULL) { LogErr("SelectObject(hFont) FAIL"); return false; } + + if (bFirst) { + + // Get Text Metrics information + // + memset((void *)&tm, 0, sizeof(TEXTMETRIC)); + + if (GetTextMetrics(hDC, &tm)==false) { + LogErr("GetTextMetrics() FAIL"); + return false; + } + bFirst = false; + } + + // Draw Charters + // + + char text[] = "c"; + + int s = tm.tmMaxCharWidth; + int a = tm.tmAscent + 1; + int d = tm.tmDescent + 1; + int h = a+d; + int x = 5; + int y = 5 + h; + int c = first; // ANSI code of the First Charter + vkFontData *pData; + + SIZE fnts; + + SetTextAlign(hDC, TA_BASELINE | TA_LEFT); + SetTextColor(hDC, 0xFFFFFF); + SetBkColor(hDC, 0); + SetBkMode(hDC, TRANSPARENT); + + float tw = 1.0f / float(tex_w); + float th = 1.0f / float(tex_h); + + while ( c < 256 ) { + pData = Data(c); + + text[0] = c; + + TextOutA(hDC, x, y, text, 1); + GetTextExtentPoint32(hDC, text, 1, &fnts); + + pData->sp = float(fnts.cx); // Char spacing + pData->w = float(fnts.cx+3); // Char Width + pData->h = float(h); // Char Height + + pData->tx0 = float(x-1); + pData->tx1 = float(x-1 + fnts.cx+3); + + pData->ty0 = float(y - a); + pData->ty1 = float(y + d); + + pData->tx0 *= tw; + pData->tx1 *= tw; + pData->ty0 *= th; + pData->ty1 *= th; + + c++; // Next Charter + + x += (fnts.cx + 4); // --!!-- In order to increase spacing between charters increase this --!!-- + + if ((x+s) >= tex_w) { // Start a New Line + x = 5; + y+= (h+5); + } + + if ((y+h) >= tex_h) { + pSurf->ReleaseDC(hDC); + pSurf->Release(); + pSrcTex->Release(); + tex_h *= 2; + goto restart; + } + } + + SelectObject(hDC, hOld); + + pSurf->ReleaseDC(hDC); + pSurf->Release(); + + DeleteObject(hFont); + + HR(pDev->CreateTexture(tex_w, tex_h, 0, D3DUSAGE_AUTOGENMIPMAP, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &pTex, NULL)); + + LogAlw("Font Video Memory Usage = %u kb",tex_w*tex_h*2/1024); + + + if (pDev->UpdateTexture(pSrcTex, pTex)!=S_OK) { + LogErr("vkTextMgr: Surface Update Failed"); + return false; + } + + pTex->GenerateMipSubLevels(); + +#ifdef FNTDBG + char texname[256]; + sprintf_s(texname, 256, "_%s_%d_0x%X.dds", lf.lfFaceName, lf.lfHeight, DWORD(this)); + D3DXSaveSurfaceToFile(texname, D3DXIFF_DDS, pSurf, NULL, NULL); +#endif + pSrcTex->Release(); + + // Init WCHAR font + HR(D3DXCreateFont( + pDev, // D3D Device + lf.lfHeight, // Font height + lf.lfWidth, // Font width + lf.lfWeight, // Font Weight + 1, // MipLevels + lf.lfItalic, // Italic + lf.lfCharSet, // CharSet + lf.lfOutPrecision, // OutputPrecision + lf.lfQuality, // Quality + lf.lfPitchAndFamily, // PitchAndFamily + lf.lfFaceName, // pFacename, + &wfont // ppFont + )); + + SetLineSpace(0); + SetTextShare(0); + SetTextSpace(0); + + LogAlw("Font and Charter set creation succesfull"); + + return true; +} + +// ---------------------------------------------------------------------------------------- +// +vkFontData *vkText::Data (int c) { +#ifdef _DEBUG + if (c < first) { c = first; } // <= did *never* happen, but better save than sorry +#endif + return &FontData[c - first]; +} + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetColor(DWORD c) +{ + alpha = ((float)((c>>24)&0xFF)) / 255.0f; + red = ((float)((c>>16)&0xFF)) / 255.0f; + green = ((float)((c>>8)&0xFF)) / 255.0f; + blue = ((float)(c&0xFF)) / 255.0f; +} + + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetColor(float r, float g, float b, float a=1.0) +{ + red=r; green=g; blue=b; alpha=a; +} + +// ---------------------------------------------------------------------------------------- +// +void vkText::Reset() +{ + max_len = 0; +} + +// ---------------------------------------------------------------------------------------- +// +float vkText::Width() +{ + return max_len; +} + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetRotation(float deg) +{ + rotation = deg; +} + +// ---------------------------------------------------------------------------------------- +// +void vkText::SetScaling(float factor) +{ + scaling = factor; +} + +// ---------------------------------------------------------------------------------------- +// +int vkText::GetIndex(const char *pText, float pos, int x) +{ + float del = 1e6; + float len = 0.0f; + int i = 0; + int idx = 0; + + const BYTE *str = (const BYTE *)pText; + + while (i < x || x < 0) { + if (fabs(pos - len) < del) { + del = fabs(pos - len); + idx = i; + } else break; + if (str[i] == 0) break; + len += (Data(str[i])->sp + spacing); + i++; + } + + return idx; +} + +// ---------------------------------------------------------------------------------------- +// +float vkText::Length2(const char *_str, int l) +{ + float len = 0; + int i = 0; + + const BYTE *str = (const BYTE *)_str; // Negative index may occur without this + + while ((isp + spacing); + i++; + } + + len -= spacing; + if (len<0) len=0; + return len * scaling; +} + + +// ---------------------------------------------------------------------------------------- +// +float vkText::Length(BYTE c) +{ + return (Data(c)->sp + spacing) * scaling; +} + +// ---------------------------------------------------------------------------------------- +// +float vkText::PrintSkp(vkPad *pSkp, float xpos, float ypos, const char *_str, int len, bool bBox) +{ + + pSkp->SetFontTextureNative(pTex); + + if (halign == 1) xpos -= Length2(_str, len) * 0.5f; + if (halign == 2) xpos -= Length2(_str, len); + if (valign == 1) ypos -= tm.tmAscent; + if (valign == 2) ypos -= tm.tmHeight; + + const BYTE *str = (const BYTE *)_str; + + xpos = ceil(xpos); + ypos = ceil(ypos); + + float x_orig = xpos; + + float h = FontData[0].h; + + float bbox_l = xpos - 2; + float bbox_t = ypos + 1; + float bbox_b = ypos + h - 1; + float bbox_r = xpos + 2; + + unsigned char c = str[0]; + int idx = 1; + + while (c && (idx<=len || len<=0)) { + bbox_r += ceil(Data(c)->sp + spacing); + c = str[idx++]; + } + + FMATRIX4 rot, out, mBak; + bool bRestore = false; + + if (fabs(rotation)>1e-3 || fabs(scaling - 1.0f)>0.001f) { + FVECTOR2 center = FVECTOR2((bbox_l + bbox_r)*0.5f, bbox_t); + FVECTOR2 scale = FVECTOR2(scaling, scaling); + center.x = ceil(center.x); + center.y = ceil(center.y); + + D3DMAT_Transformation2D(&rot, ¢er, 0.0f, &scale, ¢er, -rotation*0.01745329f, NULL); + + memcpy(&mBak, pSkp->WorldMatrix(), sizeof(FMATRIX4)); + oapiMatrixMultiply(pSkp->WorldMatrix(), &rot, &mBak); + bRestore = true; + } + + if (bBox) { + pSkp->FillRect(int(bbox_l), int(bbox_t+2), int(bbox_r), int(bbox_b), pSkp->bkcolor); + } + + idx = 1; + c = str[0]; + + // Feed data directly into a drawing queue + // + if (pSkp->Topology(vkPad::Topo::TRIANGLE)) { + + SkpVtx *pVtx = pSkp->Vtx; + WORD *pIdx = pSkp->Idx; + WORD iI = pSkp->iI; + WORD vI = pSkp->vI; + + DWORD flags = SKPSW_FONT | SKPSW_CENTER | SKPSW_FRAGMENT; + DWORD color = pSkp->textcolor.dclr; + + while (c && (idx <= len || len <= 0)) { + + vkFontData *pData = Data(c); + + pIdx[iI++] = vI; + pIdx[iI++] = vI + 1; + pIdx[iI++] = vI + 2; + pIdx[iI++] = vI; + pIdx[iI++] = vI + 2; + pIdx[iI++] = vI + 3; + + float w = pData->w; + float xp = ceil(xpos); + SkpVtxFF(pVtx[vI++], xp, ypos, pData->tx0, pData->ty0); + SkpVtxFF(pVtx[vI++], xp, ypos + h, pData->tx0, pData->ty1); + SkpVtxFF(pVtx[vI++], xp + w, ypos + h, pData->tx1, pData->ty1); + SkpVtxFF(pVtx[vI++], xp + w, ypos, pData->tx1, pData->ty0); + + pVtx[vI - 1].fnc = flags; + pVtx[vI - 1].clr = color; + pVtx[vI - 2].fnc = flags; + pVtx[vI - 2].clr = color; + pVtx[vI - 3].fnc = flags; + pVtx[vI - 3].clr = color; + pVtx[vI - 4].fnc = flags; + pVtx[vI - 4].clr = color; + + xpos += (pData->sp + spacing); + + c = str[idx++]; + } + + pSkp->vI = vI; + pSkp->iI = iI; + } + + + if (bRestore) { + memcpy(pSkp->WorldMatrix(), &mBak, sizeof(FMATRIX4)); + } + + float l = xpos - x_orig; + if (l>max_len) max_len = l; + return l; +} + +// ---------------------------------------------------------------------------------------- +// +float vkText::PrintSkp (vkPad *pSkp, float xpos, float ypos, LPCWSTR str, int len, bool bBox) +{ + + if (len == -1) len = int(wcslen(str)); + + LONG x = LONG(round(xpos)), + y = LONG(round(ypos)); + RECT rect = { x, y, 0, 0 }; + + // Must Flush() pending graphics before using ID3DXFont interface + pSkp->Flush(); + + wfont->DrawTextW( + NULL, // pSprite + str, // pString + len, // Count + &rect, // pRect + DT_CALCRECT | DT_NOCLIP, // Format + pSkp->textcolor.dclr // Color + ); + + if (bBox) + { + pSkp->FillRect(rect.left-2, rect.top+1, rect.right+2, rect.bottom-1, pSkp->bkcolor); + } + + // Must Flush() pending graphics before using ID3DXFont interface + pSkp->Flush(); + + wfont->DrawTextW( + NULL, // pSprite + str, // pString + len, // Count + &rect, // pRect + DT_VCENTER | DT_LEFT | DT_NOCLIP, // Format + pSkp->textcolor.dclr // Color + ); + + return float(rect.right - rect.left); +} + +// ----------------------------------------------------------------------------------------------- +// +void vkText::vkTechInit(vkClient *_gc, LPDIRECT3DDEVICE9 pDev) +{ + Buffer = new char[512]; +} + +void vkText::GlobalExit() +{ + SAFE_DELETEA(Buffer); +} + +char * vkText::Buffer = 0; diff --git a/OVP/VulkanClient/TextMgr.h b/OVP/VulkanClient/TextMgr.h new file mode 100644 index 000000000..78b3674ba --- /dev/null +++ b/OVP/VulkanClient/TextMgr.h @@ -0,0 +1,127 @@ + +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2012-2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + +#include +#include + +#include +#include +#include +#include "MathAPI.h" + +#include "Client.h" +#include "AABBUtil.h" + + +class SurfNative; + +// ---------------------------------------------------------------------------------------- +// +struct vkFontData { + float w, h; // X,Y position of the charter baseline + float sp; + float tx0, ty0; + float tx1, ty1; +}; + + +// ---------------------------------------------------------------------------------------- +// +class vkText { + +public: + /** + * \brief Constructs a new text object + * \param pDevice direct 3D device instance pointer + */ + explicit vkText(LPDIRECT3DDEVICE9 pDevice); + + /** + * \brief Destroys the text object + */ + ~vkText(); + + static void vkTechInit(oapi::vkClient *gc, LPDIRECT3DDEVICE9 pDev); + + /** + * \brief Release global parameters + */ + static void GlobalExit(); + + void SetCharSet(int charset=ANSI_CHARSET); // Must be set before Init + + // Init Will Create Charters from "first" (32:space) to "last" (255 ???) + bool Init(HFONT hFont); + + LPDIRECT3DTEXTURE9 GetTexture() const { return pTex; } + + void SetLineSpace(int percent=10); + void SetTextSpace(float space = 0.0f); + void SetTextShare(int percent=0); // Percent of average width (default=0) + + void SetColor(DWORD c); // 0xAARRGGBB + void SetColor(float red, float green, float blue, float alpha); + void SetRotation(float deg); + void SetScaling(float factor); + + void Reset(); + float Width(); + int GetLineSpace(); + + float Length2(const char *str, int len = -1); + float Length(BYTE c); + int GetIndex(const char *pText, float pos, int len = -1); + + void SetTextHAlign(int x); // 0-left, 1=center, 2=right + void SetTextVAlign(int x); // 0-top, 1=base, 2=bottom + + float PrintSkp (class vkPad *pSkp, float x, float y, const char *str, int len = -1, bool bBox = false); + float PrintSkp (class vkPad *pSkp, float x, float y, LPCWSTR str, int len = -1, bool bBox = false); + + void GetvkTextMetrics(TEXTMETRIC *t) { memcpy(t, &tm, sizeof(TEXTMETRIC)); } + +private: + + float red, green, blue, alpha; + + int tex_w; + int tex_h; + int sharing; + float spacing; + int linespacing; + float max_len; // If several strings are printed. This is the wide of the widest one + float rotation; + float scaling; + int charset; + int first; ///< ANSI code of the first charter (FontData[0]) + int halign,valign; + + vkFontData *Data (int c); ///< Returns FontData reference of a character + + LPDIRECT3DDEVICE9 pDev; + LPDIRECT3DTEXTURE9 pTex; + vkFontData *FontData; ///< Array of font data information ( [c - first] ) + TEXTMETRIC tm; ///< Font attributes + LOGFONT lf; ///< Font attributes + ID3DXFont *wfont; ///< WCHAR font + + // Rendering pipeline configuration + // + static char * Buffer; +}; diff --git a/OVP/VulkanClient/Texture.cpp b/OVP/VulkanClient/Texture.cpp new file mode 100644 index 000000000..827a8a999 --- /dev/null +++ b/OVP/VulkanClient/Texture.cpp @@ -0,0 +1,194 @@ +// ============================================================== +// Texture.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006 -2016 Martin Schweiger +// ============================================================== + +// ============================================================== +// Texture loading and management routines for the D3D9 client. +// +// Methods for loading single (.dds) and multi-texture files (.tex) +// stored in DXT? format into DIRECTDRAWSURFACE7 instances. +// ============================================================== + +#include "windows.h" +#include "Texture.h" +#include "D3D9Surface.h" +#include "D3D9Catalog.h" +#include + +using namespace oapi; + + +#pragma pack(push, 1) +typedef struct _DDDESC2_x64 +{ + DWORD dwSize; // size of the DDSURFACEDESC structure + DWORD dwFlags; // determines what fields are valid + DWORD dwHeight; // height of surface to be created + DWORD dwWidth; // width of input surface + union + { + LONG lPitch; // distance to start of next line (return value only) + DWORD dwLinearSize; // Formless late-allocated optimized surface size + } DUMMYUNIONNAMEN(1); + union + { + DWORD dwBackBufferCount; // number of back buffers requested + DWORD dwDepth; // the depth if this is a volume texture + } DUMMYUNIONNAMEN(5); + union + { + DWORD dwMipMapCount; // number of mip-map levels requestde + // dwZBufferBitDepth removed, use ddpfPixelFormat one instead + DWORD dwRefreshRate; // refresh rate (used when display mode is described) + DWORD dwSrcVBHandle; // The source used in VB::Optimize + } DUMMYUNIONNAMEN(2); + DWORD dwAlphaBitDepth; // depth of alpha buffer requested + DWORD dwReserved; // reserved + DWORD lpSurface; // pointer to the associated surface memory + union + { + DDCOLORKEY ddckCKDestOverlay; // color key for destination overlay use + DWORD dwEmptyFaceColor; // Physical color for empty cubemap faces + } DUMMYUNIONNAMEN(3); + DDCOLORKEY ddckCKDestBlt; // color key for destination blt use + DDCOLORKEY ddckCKSrcOverlay; // color key for source overlay use + DDCOLORKEY ddckCKSrcBlt; // color key for source blt use + union + { + DDPIXELFORMAT ddpfPixelFormat; // pixel format description of the surface + DWORD dwFVF; // vertex format description of vertex buffers + } DUMMYUNIONNAMEN(4); + DDSCAPS2 ddsCaps; // direct draw surface capabilities + DWORD dwTextureStage; // stage in multitexture cascade +} DDSURFACEDESC2_x64; +#pragma pack(pop) + + + +// ============================================================== +// ============================================================== +// Class TextureManager +// ============================================================== +// ============================================================== + +TextureManager::TextureManager(D3D9Client *gclient) : + gc(gclient), + pDev(gclient->GetDevice()), + firstTex() +{ +} + +// ============================================================== + +TextureManager::~TextureManager () +{ + ClearRepository(); +} + + +HRESULT TextureManager::LoadTexture(const char *fname, SURFHANDLE *pSurf, int flags) +{ + + DWORD attrib = OAPISURFACE_TEXTURE; + if (flags & 0x1) attrib |= OAPISURFACE_SYSMEM; + if (flags & 0x2) attrib |= OAPISURFACE_UNCOMPRESS | OAPISURFACE_NOALPHA; + if (flags & 0x4) attrib |= OAPISURFACE_NOMIPMAPS; + + if ((flags&0x2) && ((flags&0x1)==0)) attrib |= OAPISURFACE_RENDERTARGET; // Uncompress means that it's going to do something bad, so, let's prepare for the worst. + + if ((*pSurf = NatLoadSurface(fname, attrib)) != NULL) return S_OK; + + return -1; +} + + + +// ======================================================================= +// Retrieve a texture. First scans the repository of loaded textures. +// If not found, loads the texture from file and adds it to the repository +// +bool TextureManager::GetTexture(const char *fname, SURFHANDLE *pd3dt, int flags) +{ + TexRec *texrec = ScanRepository(fname); + + if (texrec) { + // found in repository + *pd3dt = texrec->tex; + SURFACE(texrec->tex)->IncRef(); + return true; + } + else if (SUCCEEDED(LoadTexture(fname, pd3dt, flags))) { + // loaded from file + LogAlw("Texture %s (%s) added in repository", _PTR(*pd3dt), fname); + AddToRepository (fname, *pd3dt); + return true; + } + else { + LogWrn("Texture %s not found",fname); + // not found + return false; + } +} + +// ======================================================================= +// Return a matching texture entry from the repository, if found. +// Otherwise, return NULL. + +TextureManager::TexRec *TextureManager::ScanRepository (const char *fname) +{ + TexRec *texrec; + DWORD id = MakeTexId (fname); + for (texrec = firstTex; texrec; texrec = texrec->next) { + if (id == texrec->id) if (!strncmp (fname, texrec->fname, 64)) + return texrec; + } + return NULL; +} + +// ======================================================================= +// Return a true if the surface is in repository + +bool TextureManager::IsInRepository (SURFHANDLE p) +{ + TexRec *texrec; + for (texrec = firstTex; texrec; texrec = texrec->next) if (p == texrec->tex) return true; + return false; +} + +// ======================================================================= +// Add a new entry to the repository + +void TextureManager::AddToRepository (const char *fname, SURFHANDLE pdds) +{ + TexRec *texrec = new TexRec; + texrec->tex = pdds; + strncpy_s(texrec->fname, 63, fname, 64); + texrec->id = MakeTexId (fname); + texrec->next = firstTex; // add to beginning of list + firstTex = texrec; +} + +// ======================================================================= +// De-allocates the repository and release the DX7 textures + +void TextureManager::ClearRepository() +{ + while (firstTex) { + TexRec *tmp = firstTex; + firstTex = firstTex->next; + if (tmp->tex) delete SURFACE(tmp->tex); + delete tmp; + } +} + +// ======================================================================= + +DWORD TextureManager::MakeTexId (const char *fname) +{ + DWORD id = 0; + for (const char *c = fname; *c; c++) id += *c; + return id; +} diff --git a/OVP/VulkanClient/Texture.h b/OVP/VulkanClient/Texture.h new file mode 100644 index 000000000..5cc356f0f --- /dev/null +++ b/OVP/VulkanClient/Texture.h @@ -0,0 +1,82 @@ +// ============================================================== +// Texture.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// ============================================================== + +// ============================================================== +// Texture loading and management routines for the D3D9 client. +// +// Methods for loading single (.dds) and multi-texture files (.tex) +// stored in DXT? format into DIRECTDRAWSURFACE7 instances. +// ============================================================== + +#ifndef __TEXTURE_H +#define __TEXTURE_H + +#include "D3D9Client.h" +#include + +// ============================================================== +// Class TextureManager + +class TextureManager { +public: + + explicit TextureManager(oapi::D3D9Client *gclient); + ~TextureManager(); + + HRESULT LoadTexture(const char *fname, SURFHANDLE *ppdds, int flags); + + int LoadTextures(const char *fname, LPDIRECT3DTEXTURE9 *ppdds, DWORD flags, int count); + // Read a texture from file 'fname' into the DX7 surface + // pointed to by 'ppdds'. + + bool GetTexture(const char *fname, SURFHANDLE *ppdds,int flags); + // Retrieve a texture. First scans the repository of loaded textures. + // If not found, loads the texture from file and adds it to the repository + + bool IsInRepository (SURFHANDLE p); + +protected: + + DWORD MakeTexId(const char *fname); + // simple checksum of a string. Used for speeding up texture searches. + +private: + oapi::D3D9Client *gc; + LPDIRECT3DDEVICE9 pDev; + + // simple repository of loaded textures: linked list + struct TexRec { + SURFHANDLE tex; + char fname[64]; + DWORD id; + struct TexRec *next; + } *firstTex; + + //TexRec *pRepo; + + // Some repository management functions below. + // This could be made more sophisticated (defining a maximum size of + // the repository, deallocating unused textures as required, etc.) + // Would also require a reference counter and a size parameter in the + // TexRec structure. + + TexRec *ScanRepository (const char *fname); + // Return a matching texture entry from the repository, if found. + // Otherwise, return NULL. + + void AddToRepository (const char *fname, SURFHANDLE pdds); + // Add a new entry to the repository + + void ClearRepository (); + // De-allocates the repository and release the DX7 textures +}; + + +// ============================================================== +// Non-member utility functions + +#endif // !__TEXTURE_H \ No newline at end of file diff --git a/OVP/VulkanClient/TileLabel.cpp b/OVP/VulkanClient/TileLabel.cpp new file mode 100644 index 000000000..dab38d0d8 --- /dev/null +++ b/OVP/VulkanClient/TileLabel.cpp @@ -0,0 +1,461 @@ +// ============================================================== +// TileLabel.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2017 Martin Schweiger (martins/apogee) +// Peter Schneider (Kuddel) +// ============================================================== + +#include "TileLabel.h" +#include +#include +#include + +TileLabel *TileLabel::Create (const SurfTile *stile) +{ + TileLabel *label = new TileLabel(stile); + label->Read(); // read label list from tile file + + const int lvlshift = 5; + SurfTile *ancestor = NULL; + if (stile->lvl >= lvlshift) { + QuadTreeNode *nd4 = stile->node->Ancestor(lvlshift); + if (nd4) ancestor = nd4->Entry(); + } else if (stile->lvl >= lvlshift-3) { + ancestor = stile->smgr->GlobalTile(stile->lvl-lvlshift); + } + if (ancestor) label->ExtractAncestorData(ancestor); + + if (label->nlabel || label->nrenderlabel) { + return label; + } else { + delete label; + return NULL; + } +} + +TileLabel::TileLabel (const SurfTile *stile) + : tile(stile) + , nlabel(0), nrenderlabel(0) + , nbuf(0), nrenderbuf(0) + , label(NULL), renderlabel(NULL) +{ +} + +TileLabel::~TileLabel () +{ + if (nbuf) + { + for (DWORD i = 0; i < nlabel; ++i) { + delete label[i]; + } + delete []label; + label = NULL; + } + if (nrenderbuf) + { + delete []renderlabel; + renderlabel = NULL; + // delete the list, not the labels themselves, since they are just + // references to ancestor entries + } +} + + +// --------------------------------------------------------------------------- +// Wide string / label buffer helper +// --------------------------------------------------------------------------- + +static int _wbufferSize = 0; +static std::unique_ptr _wbuffer; // this should get destroyed @ shutdown + +static LPWSTR GetWBuffer (const std::string &name, int *_len, int _stopLen = -1) +{ + LPWSTR dst = NULL; + int len = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), _stopLen, NULL, 0); + if (len) { + // Grow buffer? + if (len > _wbufferSize) { + _wbuffer.reset(new WCHAR[len+16]); + _wbufferSize = len+16; + } + dst = _wbuffer.get(); + MultiByteToWideChar(CP_UTF8, 0, name.c_str(), _stopLen, dst, len); + } + *_len = len ? len - 1 : 0; + return dst; +} + +// --------------------------------------------------------------------------- + +static void appendName (LPSTR *buffer, int *len, const std::string &name) +{ + size_t _len = name.size(); + if (!_len) return; + + // Create new name buffer + size_t size = *len + _len + (*len ? 2 : 1); // either '\n' + '\0' or just '\0' + LPSTR dst = new CHAR[size]; + + // Previous content? + size_t offs = 0; + if (*len) { + strcpy_s(dst, size, *buffer); + dst[*len] = '\n'; + offs = *len + 1; + delete[] *buffer; + } + + // Copy new 'name' + strcpy_s(dst+ offs, size-offs, name.c_str()); + + *buffer = dst; + *len = int(size - 1); // length is WITHOUT terminating zero +} + +// --------------------------------------------------------------------------- + +void TileLabel::StoreLabel (TLABEL *l, const std::string &name) +{ + // Check if we've already a location.. + for (DWORD i = 0; i < nlabel; ++i) { + if (l->lat == label[i]->lat && l->lng == label[i]->lng && l->labeltype == label[i]->labeltype) { + appendName(&label[i]->label, &label[i]->len, name); + ++label[i]->nLines; + delete l; + return; + } + } + + // Grow buffer? + if (nlabel == nbuf) { + TLABEL **tmp = new TLABEL*[nbuf += 16]; + if (nlabel) { + memcpy(tmp, label, nlabel * sizeof(TLABEL*)); + delete[]label; + } + label = tmp; + } + + // new loation + appendName(&l->label, &l->len, name); + label[nlabel++] = l; +} + +// --------------------------------------------------------------------------- + +bool TileLabel::Read () +{ + char path[MAX_PATH], texpath[MAX_PATH]; + int lvl = tile->lvl; + int ilat = tile->ilat; + int ilng = tile->ilng; + double lat, lng; + char typestr; + std::string altstr, name; + + // --- Easter Egg ;) --- + //static bool done = false; + //if (!done && lvl==8 && ilat==53 && ilng==265 && !strcmp(tile->mgr->CbodyName(),"Earth")) { + // TLABEL *item = new TLABEL; + // item->lat = 52.12949 * RAD; + // item->lng = 6.90243 * RAD; + // item->alt = 40; + // item->labeltype = 'C'; + // item->nLines = 2; + // StoreLabel(item, "Kuddel\nwas here!"); + // done = true; + //} + + if (tile->smgr->DoLoadIndividualFiles(4)) { // try loading from individual tile file + sprintf_s(path, MAX_PATH, "%s\\Label\\%02d\\%06d\\%06d.lab", tile->mgr->CbodyName(), lvl+4, ilat, ilng); + tile->mgr->GetClient()->TexturePath(path, texpath); + + std::ifstream ifs(texpath); + while (ifs >> typestr >> lat >> lng >> altstr >> std::ws) { + std::getline(ifs, name, '\n'); + TLABEL *item = new TLABEL; + item->lat = lat * RAD; + item->lng = lng * RAD; + item->alt = toDoubleOrNaN(altstr); + item->labeltype = typestr; + StoreLabel(item, name); + } + } + if (!nlabel && tile->smgr->ZTreeManager(4)) { // try loading from compressed archive + BYTE *buf; + ZTreeMgr *mgr = tile->smgr->ZTreeManager(4); + DWORD ndata = mgr->ReadData(lvl+4, ilat, ilng, &buf); + if (ndata) { + std::istringstream iss((char*)buf); + while (/*iss.tellg() < ndata &&*/ iss >> typestr >> lat >> lng >> altstr >> std::ws) { + std::getline(iss, name, '\n'); + + TLABEL *item = new TLABEL; + item->lat = lat * RAD; + item->lng = lng * RAD; + item->alt = toDoubleOrNaN(altstr); + item->labeltype = typestr; + StoreLabel(item, name); + } + tile->smgr->ZTreeManager(4)->ReleaseData(buf); + } + } + return (nlabel > 0); +} + +bool TileLabel::ExtractAncestorData (const SurfTile *atile) +{ + if (!atile) { return false; } + + TileLabel *atl = atile->label; + if (atl && atl->nlabel) { + TLABEL **alabel = atl->label; + double lat, lng, latmin, latmax, lngmin, lngmax; + tile->Extents(&latmin, &latmax, &lngmin, &lngmax); + for (DWORD i = 0; i < atl->nlabel; i++) { + lat = alabel[i]->lat; + lng = alabel[i]->lng; + if (lat >= latmin && lat < latmax && lng >= lngmin && lng < lngmax) { + if (nrenderlabel == nrenderbuf) { // grow buffer + TLABEL **tmp = new TLABEL*[nrenderbuf+=16]; + if (nrenderlabel) { + memcpy(tmp, renderlabel, nrenderlabel*sizeof(TLABEL*)); + delete []renderlabel; + } + renderlabel = tmp; + } + renderlabel[nrenderlabel++] = alabel[i]; + if (!alabel[i]->pos.x && !alabel[i]->pos.y && !alabel[i]->pos.z) { + if (_isnan(alabel[i]->alt)) + alabel[i]->alt = Elevation(lat, lng, latmin, latmax, lngmin, lngmax, 1.0); + double rad = tile->mgr->CbodySize() + alabel[i]->alt; + oapiEquToLocal(tile->mgr->Cbody(), lng, lat, rad, &alabel[i]->pos); + } + } + } + } + return (nrenderlabel > 0); +} + +double TileLabel::Elevation (double lat, double lng, double latmin, double latmax, double lngmin, double lngmax, double elev_res) const +{ + float *elev = tile->ggelev; + if (!elev) return 0.0; + + int blockRes = tile->mgr->GridRes(); + float *elev_base = elev+TILE_ELEVSTRIDE+1; // strip padding + double latidx = (lat-latmin) * blockRes/(latmax-latmin); + double lngidx = (lng-lngmin) * blockRes/(lngmax-lngmin); + int lat0 = (int)latidx; + int lng0 = (int)lngidx; + elev = elev_base + lat0*TILE_ELEVSTRIDE + lng0; + double w_lat = latidx-lat0; + double w_lng = lngidx-lng0; + double e01 = elev[0]*(1.0-w_lng) + elev[1]*w_lng; + double e02 = elev[TILE_ELEVSTRIDE]*(1.0-w_lng) + elev[TILE_ELEVSTRIDE+1]*w_lng; + double e = e01*(1.0-w_lat) + e02*w_lat; + return e*elev_res; +} + +void TileLabel::Render (vkPad *skp, oapi::Font **labelfont, int *fontidx) +{ + if (!nrenderlabel) { return; }// nothing to render + + const int symscale[4] = {5, 6, 8, 10}; + DWORD i; + COLORREF col, pcol = 0; + int x, y, nl, scale, len, partLen; + const oapi::GraphicsClient::LABELTYPE *lspec; + VECTOR3 sp, dir; + const OBJHANDLE &hPlanet = tile->mgr->Cbody(); + Scene *pScene = tile->mgr->GetClient()->GetScene(); + + if (pScene->GetCameraProxyBody() != hPlanet) { return; } // do not render other body's labels + + VECTOR3 Ppl; + oapiGetGlobalPos(hPlanet, &Ppl); // planet global position + const VECTOR3 *Pcam = &pScene->GetCamera()->pos; // camera global position + MATRIX3 Rpl; + oapiGetRotationMatrix(hPlanet, &Rpl); // planet rotation matrix + VECTOR3 campos = tmul(Rpl, *Pcam - Ppl); // camera pos in planet frame + + Tick(); + + for (i = 0; i < nrenderlabel; ++i) { + VECTOR3 camlabelpos = campos-renderlabel[i]->pos; + if (dotp (renderlabel[i]->pos, camlabelpos) >= 0.0) { + double fontscale = 1e4/length(camlabelpos)*(13-min(tile->lvl,12)*1); + int idx = max(0, min(3, (int)fontscale)); + if (idx != *fontidx) { + skp->SetFont(labelfont[idx]); + *fontidx = idx; + } + scale = symscale[idx]; + sp = mul(Rpl, renderlabel[i]->pos) + Ppl - *Pcam; + dir = unit(sp); + if (pScene->CameraDirection2Viewport(dir, x, y)) { + + bool active = false; // default for label types not listed in the legend + char symbol = 0; // undefined + if (nl = tile->smgr->GetClient()->GetSurfaceMarkerLegend(hPlanet, &lspec)) { + for (int j = 0; j < nl; ++j) { + if (renderlabel[i]->labeltype == lspec[j].labelId) { + symbol = lspec[j].markerId; + col = lspec[j].col; + active = lspec[j].active; + break; + } + } + } + if (!active) continue; + + if (!symbol) { // default + symbol = 'S'; + col = RGB(255,255,255); + } + if (col != pcol) { + skp->SetTextColor(col); + pcol = col; + } + switch (symbol) { + case 'O': // circle + skp->Ellipse(x-scale, y-scale, x+scale+1, y+scale+1); + break; + case 'D': // Delta + skp->MoveTo(x, y-scale); + skp->LineTo(x-scale, y+scale+1); + skp->LineTo(x+scale+1, y+scale+1); + skp->LineTo(x, y-scale); + break; + case 'N': // Nabla + skp->MoveTo(x, y+scale+1); + skp->LineTo(x-scale, y-scale); + skp->LineTo(x+scale+1, y-scale); + skp->LineTo(x, y+scale+1); + break; + default: + skp->Rectangle(x-scale, y-scale, x+scale+1, y+scale+1); + break; + } + + DWORD H = skp->GetLineHeight(); + + partLen = LimitAndRotateLongLabelList(renderlabel[i], H, &y); + + LPWSTR wname = GetWBuffer(renderlabel[i]->label, &len, partLen+1); + skp->TextW(x + scale + 2, y - scale - 1, wname, len); + } + } + } +} + +// --------------------------------------------------------------------------- +// Long label list rotation +// --------------------------------------------------------------------------- + +DWORD TileLabel::rotStep = 0; + +//const std::vector &split (const std::wstring &s) +//{ +// static std::vector parts; +// std::wstringstream iss(s); +// std::wstring part; +// +// parts.clear(); +// while (std::getline(iss, part, L'\n')) { +// parts.push_back(part); +// } +// +// return parts; +//} + +//const std::wstring &join (const std::vector &v) +//{ +// static std::wstring s; +// s = v.front(); +// for (auto it = v.begin()+1; it != v.end(); ++it) { +// s += L'\n' + *it; +// } +// return s; +//} + +const std::vector &split (const std::string &s) +{ + static std::vector parts; + std::stringstream iss(s); + std::string part; + + parts.clear(); + while (std::getline(iss, part, '\n')) { + parts.push_back(part); + } + + return parts; +} + +const std::string &join (const std::vector &v) +{ + static std::string s; + s = v.front(); + for (auto it = v.begin()+1; it != v.end(); ++it) { + s += '\n' + *it; + } + return s; +} + +void TileLabel::Tick () +{ + double now = oapiGetSysTime(); + static double lastT = now; + + // Update current rotation step (to rotate long lists every 1.3 seconds) + if (now - lastT > 0.1) + { + lastT = now; + ++rotStep; + } +} + +int TileLabel::LimitAndRotateLongLabelList(TLABEL *l, DWORD H, int *y) +{ + int partLen = l->len; + + if (l->nLines > 3) + { + if ((BYTE)rotStep != l->rotStep) + { + ++H; // height+1 to let the modulo OPs fit + if (rotStep % H == 0) + { + l->rotStep = (BYTE)rotStep; + + // split + auto v = split(l->label); + // rotate + std::rotate(v.begin(), v.begin() + 1, v.end()); // rot left + //std::rotate(v.rbegin(), v.rbegin() + 1, v.rend()); // rot right + // re-join + std::string label = join(v); + strcpy_s(l->label, l->len + 1, label.c_str()); + + // Calculate/Update "render stop" length + int n = 0; + for (CHAR *c = l->label; *c; ++c) { + if (*c == '\n' && ++n == 4) { // render 4 lines + l->stopLen = int(c - l->label); + break; + } + } + } + else { // shift "up" a pixel + *y -= (rotStep % H); + } + + } // end-if (rotStep != l->rotStep) + + partLen = l->stopLen; + } + + return partLen; +} diff --git a/OVP/VulkanClient/TileLabel.h b/OVP/VulkanClient/TileLabel.h new file mode 100644 index 000000000..932cef2b7 --- /dev/null +++ b/OVP/VulkanClient/TileLabel.h @@ -0,0 +1,57 @@ +// ============================================================== +// TileLabel.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2017 Martin Schweiger (martins/apogee) +// Peter Schneider (Kuddel) +// ============================================================== + +#ifndef __TILELABEL_H +#define __TILELABEL_H + +#include "Surfmgr2.h" +#include "ZTreeMgr.h" +#include "Pad.h" + +class TileLabel { +public: + static TileLabel *Create (const SurfTile *stile); ///< TileLabel factory. Returns NULL if no labels available for this tile + + TileLabel (const SurfTile *stile); + ~TileLabel (); + + void Render (vkPad *skp, oapi::Font **labelfont, int *fontidx); + +protected: + bool Read (); + bool ExtractAncestorData (const SurfTile *atile); + double Elevation (double lat, double lng, double latmin, double latmax, double lngmin, double lngmax, double elev_res) const; + +private: + struct TLABEL { + TLABEL() : labeltype(0), len(0), stopLen(0), label(NULL), pos(), nLines(1), rotStep(0), lat(), lng(), alt() {} + ~TLABEL() { SAFE_DELETEA(label); } + double lat, lng, alt; ///< spheric coordinates of the label + VECTOR3 pos; ///< position of the label + char labeltype; ///< label type ID (what feature group it belongs to) + int len; ///< label length WITHOUT terminating zero! + LPSTR label; ///< the label (might contain multiple lines) + + int nLines; ///< number of lines (for labels with multiple names) + int stopLen; ///< end of the 4th line position for multi-line labels (rendered only 'til here) + BYTE rotStep; ///< rotation step state (for labels with more than 3 names) + }; + + void StoreLabel (TLABEL *l, const std::string &name); ///< store (new) label to label-storage (**label) + void Tick(); ///< updates current rotation step every 1.3 seconds + int LimitAndRotateLongLabelList (TLABEL *l, DWORD H, int *y); ///< get render-length-limit & rotate long-label list + + static DWORD rotStep; ///< current step-state (to rotate labels with more than 3 names) + const SurfTile *tile; ///< associated surface tile + DWORD nlabel, nbuf; ///< number of allocated labels and label buffer size + TLABEL **label; ///< the list read from file + DWORD nrenderlabel, nrenderbuf; ///< number of allocated render-labels and render-label buffer size + TLABEL **renderlabel; ///< the list to be rendered, extracted from an ancestor label list +}; + +#endif // !__TILELABEL_H diff --git a/OVP/VulkanClient/TileMgr.cpp b/OVP/VulkanClient/TileMgr.cpp new file mode 100644 index 000000000..5a64df18d --- /dev/null +++ b/OVP/VulkanClient/TileMgr.cpp @@ -0,0 +1,1291 @@ +// ============================================================== +// TileMgr.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006 - 2016 Martin Schweiger +// 2011 - 2016 Jarmo Nikkanen (vkClient modification) +// ============================================================== + +// ============================================================== +// class TileManager (implementation) +// +// Planetary surface rendering management, including a simple +// LOD (level-of-detail) algorithm for surface patch resolution. +// ============================================================== + +#include +#include "TileMgr.h" +#include "VPlanet.h" +#include "Config.h" +#include "Surface.h" +#include "Catalog.h" +#include "Client.h" +#include "OapiExtension.h" + +using namespace oapi; + +// Max supported patch resolution level +int SURF_MAX_PATCHLEVEL = 14; +const LONG_PTR NOTILE = (LONG_PTR)-1; // "no tile" flag + +//static float TEX2_MULTIPLIER = 4.0f; // microtexture multiplier + +struct IDXLIST { + DWORD idx; + LONG_PTR ofs; +}; + +// Some debugging parameters +int tmissing = 0; + +// ======================================================================= +// Local prototypes +void ReleaseTex(LPDIRECT3DTEXTURE9 pTex) +{ + pTex->Release(); +} + +int compare_idx (const void *el1, const void *el2); + +// ======================================================================= + + + + +TileManager::TileManager (vkClient *gclient, const vPlanet *vplanet) : vkEffect() +{ + vp = vplanet; + obj = vp->Object(); + char name[256]; + oapiGetObjectName (obj, name, 256); int len = lstrlen(name) + 2; + objname = new char[len]; + strcpy_s(objname, len, name); + ntex = 0; + nhitex = 0; + nmask = 0; + nhispec = 0; + maxlvl = maxbaselvl = 0; + microtex = NULL; + microlvl = 0.0; + tiledesc = NULL; + texbuf = NULL; + specbuf = NULL; + cAmbient = 0; + bNoTextures = false; + bPreloadTile = (Config->PlanetPreloadMode!=0); + if (bPreloadTile) LogAlw("PreLoad Highres textures"); +} + +// ======================================================================= + +TileManager::~TileManager () +{ + DWORD i; + + if (ntex && texbuf) { + for (i = 0; i < ntex; ++i) + ReleaseTex(texbuf[i]); + delete []texbuf; + texbuf = NULL; + } + if (nmask && specbuf) { + for (i = 0; i < nmask; ++i) + ReleaseTex(specbuf[i]); + delete []specbuf; + specbuf = NULL; + } + DELETE_SURFACE(microtex); + if (tiledesc) { + delete []tiledesc; + tiledesc = NULL; + } + if (objname) { + delete []objname; + objname = NULL; + } +} + +// ======================================================================= + +bool TileManager::LoadPatchData () +{ + // Read information about specular reflective patch masks + // from a binary data file + + FILE *binf; + BYTE minres, maxres, flag; + int i, idx, npatch; + nmask = 0; + char fname[128], path[MAX_PATH]; + strcpy_s(fname, ARRAYSIZE(fname), objname); + strcat_s(fname, ARRAYSIZE(fname), "_lmask.bin"); + + if (!(bGlobalSpecular || bGlobalLights) || !gc->TexturePath(fname, path) || fopen_s(&binf, path, "rb")) { + + for (i = 0; i < patchidx[maxbaselvl]; i++) + tiledesc[i].flag = 1; + return false; // no specular reflections, no city lights + + } else { + + WORD *tflag = 0; + LMASKFILEHEADER lmfh; + fread (&lmfh, sizeof (lmfh), 1, binf); + if (!strncmp (lmfh.id, "PLTA0100", 8)) { // v.1.00 format + minres = lmfh.minres; + maxres = lmfh.maxres; + npatch = lmfh.npatch; + tflag = new WORD[npatch]; + fread (tflag, sizeof(WORD), npatch, binf); + } else { // pre-v.1.00 format + fseek (binf, 0, SEEK_SET); + fread (&minres, 1, 1, binf); + fread (&maxres, 1, 1, binf); + npatch = patchidx[maxres] - patchidx[minres-1]; + tflag = new WORD[npatch]; + for (i = 0; i < npatch; i++) { + fread (&flag, 1, 1, binf); + tflag[i] = flag; + } + //LOGOUT1P("*** WARNING: Old-style texture contents file %s_lmask.bin", cbody->Name()); + } + fclose (binf); + + for (i = idx = 0; i < patchidx[maxbaselvl]; i++) { + if (i < patchidx[minres-1]) { + tiledesc[i].flag = 1; // no mask information -> assume opaque, no lights + } else { + flag = (BYTE)tflag[idx++]; + tiledesc[i].flag = flag; + if (((flag & 3) == 3) || (flag & 4)) + nmask++; + } + } + if (tflag) { + delete []tflag; + tflag = NULL; + } + + return true; + } +} + +// ======================================================================= + +bool TileManager::LoadTileData () +{ + // Read table of contents file for high-resolution planet tiles + + FILE *file; + + if (maxlvl <= 8) // no tile data required + return false; + + char fname[128], path[MAX_PATH]; + strcpy_s (fname, ARRAYSIZE(fname), objname); + strcat_s (fname, ARRAYSIZE(fname), "_tile.bin"); + + if (!gc->TexturePath (fname, path) || fopen_s (&file, path, "rb")) { + LogWrn("Surface Tile TOC not found for %s", fname); + return false; // TOC file not found + } + + LogAlw("Reading Tile Data for %s", fname); + + // read file header + char idstr[9] = " "; + fread (idstr, 1, 8, file); + if (!strncmp (idstr, "PLTS", 4)) { + tilever = 1; + } else { // no header: old-style file format + tilever = 0; + fseek (file, 0, SEEK_SET); + } + + DWORD n, i, j; + fread (&n, sizeof(DWORD), 1, file); + TILEFILESPEC *tfs = new TILEFILESPEC[n]; + fread (tfs, sizeof(TILEFILESPEC), n, file); + + if (bPreloadTile) { + if (tilever >= 1) { // convert texture offsets to indices + IDXLIST *idxlist = new IDXLIST[n]; + for (i = 0; i < n; i++) { + idxlist[i].idx = i; + idxlist[i].ofs = tfs[i].sidx; + } + qsort (idxlist, n, sizeof(IDXLIST), compare_idx); + for (i = 0; i < n && idxlist[i].ofs != NOTILE; i++) + tfs[idxlist[i].idx].sidx = i; + + for (i = 0; i < n; i++) { + idxlist[i].idx = i; + idxlist[i].ofs = tfs[i].midx; + } + qsort (idxlist, n, sizeof(IDXLIST), compare_idx); + for (i = 0; i < n && idxlist[i].ofs != NOTILE; i++) + tfs[idxlist[i].idx].midx = i; + + tilever = 0; + delete []idxlist; + idxlist = NULL; + } + } + + TILEDESC *tile8 = tiledesc + patchidx[7]; + for (i = 0; i < 364; i++) { // loop over level 8 tiles + TILEDESC &tile8i = tile8[i]; + for (j = 0; j < 4; j++) + if (tfs[i].subidx[j]) + AddSubtileData (tile8i, tfs, i, j, 9); + } + + fclose (file); + delete []tfs; + tfs = NULL; + return true; +} + +// ======================================================================= + +int compare_idx (const void *el1, const void *el2) +{ + const IDXLIST *idx1 = static_cast(el1); + const IDXLIST *idx2 = static_cast(el2); + return (idx1->ofs < idx2->ofs ? -1 : idx1->ofs > idx2->ofs ? 1 : 0); +} + +// ======================================================================= + +bool TileManager::AddSubtileData (TILEDESC &td, TILEFILESPEC *tfs, DWORD idx, DWORD sub, DWORD lvl) +{ + DWORD j, subidx = tfs[idx].subidx[sub]; + TILEFILESPEC &t = tfs[subidx]; + bool bSubtiles = false; + for (j = 0; j < 4; j++) + if (t.subidx[j]) { bSubtiles = true; break; } + if (t.flags || bSubtiles) { + if ((int)lvl <= maxlvl) { + td.subtile[sub] = tilebuf->AddTile(); + td.subtile[sub]->flag = t.flags; + td.subtile[sub]->tex = (LPDIRECT3DTEXTURE9)t.sidx; + if (bGlobalSpecular || bGlobalLights) { + if (t.midx != NOTILE) { + td.subtile[sub]->ltex = (LPDIRECT3DTEXTURE9)t.midx; + } + } else { + td.subtile[sub]->flag = 1; // remove specular flag + } + td.subtile[sub]->flag |= 0x80; // 'Not-loaded' flag + if (!tilever) + td.subtile[sub]->flag |= 0x40; // 'old-style index' flag + // recursively step down to higher resolutions + if (bSubtiles) { + for (j = 0; j < 4; j++) { + if (t.subidx[j]) AddSubtileData (*td.subtile[sub], tfs, subidx, j, lvl+1); + } + } + nhitex++; + if (t.midx != NOTILE) nhispec++; + } else td.subtile[sub] = NULL; + } + return true; +} + +// ======================================================================= + +void TileManager::LoadTextures (char *modstr) +{ + // pre-load level 1-8 textures + ntex = patchidx[maxbaselvl]; + texbuf = new LPDIRECT3DTEXTURE9[ntex]; + char fname[256]; + strcpy_s (fname, 256, objname); + if (modstr) strcat_s (fname, 256, modstr); + strcat_s (fname, 256, ".tex"); + + if (ntex = LoadPlanetTextures(fname, texbuf, 0, ntex)) { + while ((int)ntex < patchidx[maxbaselvl]) maxlvl = --maxbaselvl; + while ((int)ntex > patchidx[maxbaselvl]) ReleaseTex(texbuf[--ntex]); + // not enough textures loaded for requested resolution level + for (int i = 0; i < patchidx[maxbaselvl]; ++i) + tiledesc[i].tex = texbuf[i]; + } else { + delete []texbuf; + texbuf = NULL; + bNoTextures = true; + // no textures at all! + } + + // pre-load highres tile textures + if (bPreloadTile && nhitex) { + TILEDESC *tile8 = tiledesc + patchidx[7]; + PreloadTileTextures (tile8, nhitex, nhispec); + } +} + +// ======================================================================= + +void TileManager::PreloadTileTextures (TILEDESC *tile8, DWORD ntex, DWORD nmask) +{ + // Load tile surface and mask/light textures, and copy them into the tile tree + + char fname[256]; + DWORD i, j, nt = 0, nm = 0; + LPDIRECT3DTEXTURE9 *texbuf = NULL, *maskbuf = NULL; + + if (ntex) { // load surface textures + texbuf = new LPDIRECT3DTEXTURE9[ntex]; + strcpy_s (fname, 256, objname); + strcat_s (fname, 256, "_tile.tex"); + + gc->OutputLoadStatus(fname, 1); + + nt = LoadPlanetTextures(fname, texbuf, 0, ntex); + LogAlw("Number of textures loaded = %u",nt); + } + if (nmask) { // load mask/light textures + maskbuf = new LPDIRECT3DTEXTURE9[nmask]; + strcpy_s (fname, 256, objname); + strcat_s (fname, 256, "_tile_lmask.tex"); + + gc->OutputLoadStatus(fname, 1); + + nm = LoadPlanetTextures(fname, maskbuf, 0, nmask); + } + // copy textures into tile tree + for (i = 0; i < 364; i++) { + TILEDESC *tile8i = tile8+i; + for (j = 0; j < 4; j++) + if (tile8i->subtile[j]) + AddSubtileTextures (tile8i->subtile[j], texbuf, nt, maskbuf, nm); + } + // release unused textures + if (nt) { + for (i = 0; i < nt; i++) + if (texbuf[i]) + ReleaseTex(texbuf[i]); + delete []texbuf; + texbuf = NULL; + } + if (nm) { + for (i = 0; i < nm; i++) + if (maskbuf[i]) + ReleaseTex(maskbuf[i]); + delete []maskbuf; + maskbuf = NULL; + } +} + +// ======================================================================= + +void TileManager::AddSubtileTextures (TILEDESC *td, LPDIRECT3DTEXTURE9 *tbuf, DWORD nt, LPDIRECT3DTEXTURE9 *mbuf, DWORD nm) +{ + DWORD i; + + // -------------------------------------------------------------------------------------------------------------- + // jarmonik: 30-jul-2021 + // This is tricky. At first td->tex is an index to a texture array and later it becomes a pointer to a texture ?!! + // -------------------------------------------------------------------------------------------------------------- + + LONG_PTR tidx = (LONG_PTR)td->tex; // copy surface texture + if (tidx != NOTILE) { + if (tidx < LONG_PTR(nt)) { + td->tex = tbuf[tidx]; + tbuf[tidx] = NULL; + } else { // inconsistency + tmissing++; + td->tex = NULL; + } + } else td->tex = NULL; + + LONG_PTR midx = (LONG_PTR)td->ltex; // copy mask/light texture + if (midx != NOTILE) { + if (midx < LONG_PTR(nm)) { + td->ltex = mbuf[midx]; + mbuf[midx] = NULL; + } else { // inconsistency + tmissing++; + td->ltex = NULL; + } + } else td->ltex = NULL; + td->flag &= ~0x80; // remove "not loaded" flag + + for (i = 0; i < 4; i++) { + if (td->subtile[i]) AddSubtileTextures (td->subtile[i], tbuf, nt, mbuf, nm); + } +} + +// ======================================================================= + +void TileManager::LoadSpecularMasks () +{ + if (nmask) { + + int i; + DWORD n; + char fname[256]; + + strcpy_s (fname, 256, objname); + strcat_s (fname, 256, "_lmask.tex"); + + gc->OutputLoadStatus(fname, 1); + + specbuf = new LPDIRECT3DTEXTURE9[nmask]; + if (n = LoadPlanetTextures(fname, specbuf, 0, nmask)) { + if (n < nmask) { + //LOGOUT1P("Transparency texture mask file too short: %s_lmask.tex", cbody->Name()); + //LOGOUT("Disabling specular reflection for this planet"); + delete []specbuf; + specbuf = NULL; + nmask = 0; + for (i = 0; i < patchidx[maxbaselvl]; i++) + tiledesc[i].flag = 1; + } else { + for (i = n = 0; i < patchidx[maxbaselvl]; i++) { + if (((tiledesc[i].flag & 3) == 3) || (tiledesc[i].flag & 4)) { + if (n < nmask) tiledesc[i].ltex = specbuf[n++]; + else tiledesc[i].flag = 1; + } + if (!bGlobalLights) tiledesc[i].flag &= 0xFB; + if (!bGlobalSpecular) tiledesc[i].flag &= 0xFD, tiledesc[i].flag |= 1; + } + } + } else { + nmask = 0; + for (i = 0; i < patchidx[maxbaselvl]; i++) + tiledesc[i].flag = 1; + } + } +} + +// ============================================================== + +void TileManager::SetAmbientColor(DWORD c) +{ + cAmbient = c; +} + +// ============================================================== + +void TileManager::Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap, bool bfog) +{ + VECTOR3 gpos; + FMATRIX4 imat; + + FX->SetFloat(eDistScale, 1.0f/float(scale)); + + level = min (level, maxlvl); + + RenderParam.dev = dev; + D3DMAT_Copy (&RenderParam.wmat, &wmat); + D3DMAT_Copy (&RenderParam.wmat_tmp, &wmat); + oapiMatrixInverse(&imat, NULL, &wmat); + RenderParam.cdir = _V(imat.m41, imat.m42, imat.m43); // camera position in local coordinates (units of planet radii) + RenderParam.cpos = vp->PosFromCamera() * scale; + normalise (RenderParam.cdir); // camera direction + RenderParam.bfog = bfog; + + oapiGetRotationMatrix (obj, &RenderParam.grot); + RenderParam.grot *= scale; + oapiGetGlobalPos (obj, &gpos); + + RenderParam.bCockpit = (oapiCameraMode()==CAM_COCKPIT); + RenderParam.objsize = oapiGetSize (obj); + RenderParam.cdist = vp->CamDist() / vp->GetSize(); // camera distance in units of planet radius + RenderParam.viewap = (viewap ? viewap : acos (1.0/max (1.0, RenderParam.cdist))); + RenderParam.sdir = tmul (RenderParam.grot, -gpos); + RenderParam.horzdist = sqrt(RenderParam.cdist*RenderParam.cdist-1.0) * RenderParam.objsize; + normalise (RenderParam.sdir); // sun direction in planet frame + + // limit resolution for fast camera movements + double limitstep, cstep = acos (dotp (RenderParam.cdir, pcdir)); + int maxlevel = SURF_MAX_PATCHLEVEL; + static double limitstep0 = 5.12 * pow(2.0, -(double)SURF_MAX_PATCHLEVEL); + for (limitstep = limitstep0; cstep > limitstep && maxlevel > 5; limitstep *= 2.0) + maxlevel--; + level = min (level, maxlevel); + + RenderParam.tgtlvl = level; + + int startlvl = min (level, 8); + int hemisp, ilat, ilng, idx; + int nlat = NLAT[startlvl]; + int *nlng = NLNG[startlvl]; + int texofs = patchidx[startlvl-1]; + TILEDESC *td = tiledesc + texofs; + + TEXCRDRANGE range = {0,1,0,1}; + + if (level <= 4) { + int npatch = patchidx[level] - patchidx[level-1]; + RenderSimple(level, npatch, td, &RenderParam.wmat); + + } else { + + WaitForSingleObject (tilebuf->hQueueMutex, INFINITE); // make sure we can write to texture request queue + for (hemisp = idx = 0; hemisp < 2; hemisp++) { + if (hemisp) { // flip world transformation to southern hemisphere + oapiMatrixMultiply(&RenderParam.wmat, &Rsouth, &RenderParam.wmat); + D3DMAT_Copy (&RenderParam.wmat_tmp, &RenderParam.wmat); + RenderParam.grot.m12 = -RenderParam.grot.m12; + RenderParam.grot.m13 = -RenderParam.grot.m13; + RenderParam.grot.m22 = -RenderParam.grot.m22; + RenderParam.grot.m23 = -RenderParam.grot.m23; + RenderParam.grot.m32 = -RenderParam.grot.m32; + RenderParam.grot.m33 = -RenderParam.grot.m33; + } + + InitRenderTile(); + + for (ilat = nlat-1; ilat >= 0; ilat--) { + for (ilng = 0; ilng < nlng[ilat]; ilng++) { + ProcessTile (startlvl, hemisp, ilat, nlat, ilng, nlng[ilat], td+idx, + range, td[idx].tex, td[idx].ltex, td[idx].flag, + range, td[idx].tex, td[idx].ltex, td[idx].flag); + idx++; + } + } + + EndRenderTile(); + } + ReleaseMutex (tilebuf->hQueueMutex); + } + + pcdir = RenderParam.cdir; // store camera direction +} + +// ======================================================================= + +void TileManager::ProcessTile (int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, TILEDESC *tile, + const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag, + const TEXCRDRANGE &bkp_range, LPDIRECT3DTEXTURE9 bkp_tex, LPDIRECT3DTEXTURE9 bkp_ltex, DWORD bkp_flag) +{ + + // Check if patch is visible from camera position + static const double rad0 = sqrt(2.0)*PI05*0.5; + VECTOR3 cnt = TileCentre (hemisp, ilat, nlat, ilng, nlng); + double rad = rad0/(double)nlat; + double x = dotp (RenderParam.cdir, cnt); + double adist = acos(x) - rad; + + if (adist >= RenderParam.viewap) { + //if (RenderParam.bCockpit) tilebuf->DeleteSubTiles(tile); // remove tile descriptions below + return; + } + + // Set world transformation matrix for patch + SetWorldMatrix (ilng, nlng, ilat, nlat); + + float bsScale = D3DMAT_BSScaleFactor(&mWorld); + + // Check if patch bounding box intersects viewport + if (!IsTileInView(lvl, ilat, bsScale)) { + tilebuf->DeleteSubTiles (tile); // remove tile descriptions below + return; + } + + // Reduce resolution for distant or oblique patches + bool bStepDown = (lvl < RenderParam.tgtlvl); + bool bCoarseTex = false; + if (bStepDown && lvl >= 8 && adist > 0.0) { + double lat1, lat2, lng1, lng2, clat, clng, crad; + double adist_lng, adist_lat, adist2; + TileExtents (hemisp, ilat, nlat, ilng, nlng, lat1, lat2, lng1, lng2); + oapiLocalToEqu (obj, RenderParam.cdir, &clng, &clat, &crad); + if (clng < lng1-PI) clng += PI2; + else if (clng > lng2+PI) clng -= PI2; + if (clng < lng1) adist_lng = lng1-clng; + else if (clng > lng2) adist_lng = clng-lng2; + else adist_lng = 0.0; + if (clat < lat1) adist_lat = lat1-clat; + else if (clat > lat2) adist_lat = clat-lat2; + else adist_lat = 0.0; + adist2 = max (adist_lng, adist_lat); + + // reduce resolution further for tiles that are visible + // under a very oblique angle + double cosa = cos(adist2); + double a = sin(adist2); + double b = RenderParam.cdist-cosa; + double ctilt = b*cosa/sqrt(a*a*(1.0+2.0*b)+b*b); // tile visibility tilt angle cosine + if (adist2 > rad*(2.0*ctilt+0.3)) { + bStepDown = false; + if (adist2 > rad*(4.2*ctilt+0.3)) + bCoarseTex = true; + } + } + + // Recursion to next level: subdivide into 2x2 patch + if (bStepDown) { + int i, j, idx = 0; + float du = (range.tumax-range.tumin) * 0.5f; + float dv = (range.tvmax-range.tvmin) * 0.5f; + TEXCRDRANGE subrange; + static TEXCRDRANGE fullrange = {0,1,0,1}; + for (i = 1; i >= 0; i--) { + subrange.tvmax = (subrange.tvmin = range.tvmin + (1-i)*dv) + dv; + for (j = 0; j < 2; j++) { + subrange.tumax = (subrange.tumin = range.tumin + j*du) + du; + TILEDESC *subtile = tile->subtile[idx]; + bool isfull = true; + if (!subtile) { + tile->subtile[idx] = subtile = tilebuf->AddTile(); + isfull = false; + } else if (subtile->flag & 0x80) { // not yet loaded + if ((tile->flag & 0x80) == 0) // only load subtile texture if parent texture is present + tilebuf->LoadTileAsync (objname, subtile); + isfull = false; + } + if (isfull) + isfull = (subtile->tex != NULL); + if (isfull) + ProcessTile (lvl+1, hemisp, ilat*2+i, nlat*2, ilng*2+j, nlng*2, subtile, + fullrange, subtile->tex, subtile->ltex, subtile->flag, + subrange, tex, ltex, flag); + else + ProcessTile (lvl+1, hemisp, ilat*2+i, nlat*2, ilng*2+j, nlng*2, subtile, + subrange, tex, ltex, flag, + subrange, tex, ltex, flag); + idx++; + } + } + } + else { + + // check if the tile is visible in the viewport --------------------------------- + // + VBMESH *mesh = &PATCH_TPL[lvl][ilat]; + + float bsrad = mesh->bsRad * bsScale; + FVECTOR3 vBS = oapiTransformCoord(&mesh->bsCnt, &mWorld); + float dist = length(vBS); + if ((dist-bsrad)>RenderParam.horzdist) return; //Tile is behind the horizon + if (gc->GetScene()->IsVisibleInCamera(&vBS, bsrad)==false) return; + + // actually render the tile at this level --------------------------------------- + // + double sdist = acos (dotp (RenderParam.sdir, cnt)); + + if (bCoarseTex) { + //if (sdist > PI05+rad && bkp_flag & 2) bkp_flag &= 0xFD; + RenderTile (lvl, hemisp, ilat, nlat, ilng, nlng, sdist, tile, bkp_range, bkp_tex, bkp_ltex, bkp_flag); + } else { + //if (sdist > PI05+rad && flag & 2) flag &= 0xFD; + RenderTile (lvl, hemisp, ilat, nlat, ilng, nlng, sdist, tile, range, tex, ltex, flag); + } + } +} + +// ======================================================================= +// returns the direction of the tile centre from the planet centre in local +// planet coordinates + +VECTOR3 TileManager::TileCentre (int hemisp, int ilat, int nlat, int ilng, int nlng) +{ + double cntlat = PI*0.5 * ((double)ilat+0.5)/(double)nlat, slat = sin(cntlat), clat = cos(cntlat); + double cntlng = PI*2.0 * ((double)ilng+0.5)/(double)nlng + PI, slng = sin(cntlng), clng = cos(cntlng); + if (hemisp) return _V(clat*clng, -slat, -clat*slng); + else return _V(clat*clng, slat, clat*slng); +} + +// ======================================================================= + +void TileManager::TileExtents (int hemisp, int ilat, int nlat, int ilng, int nlng, double &lat1, double &lat2, double &lng1, double &lng2) const +{ + lat1 = PI05 * (double)ilat/(double)nlat; + lat2 = lat1 + PI05/(double)nlat; + lng1 = PI2 * (double)ilng/(double)nlng + PI; + lng2 = lng1 + PI2/nlng; + if (hemisp) { + double tmp = lat1; lat1 = -lat2; lat2 = -tmp; + tmp = lng1; lng1 = -lng2; lng2 = -tmp; + if (lng2 < 0) lng1 += PI2, lng2 += PI2; + } +} + +// ======================================================================= + +int TileManager::IsTileInView(int lvl, int ilat, float scale) +{ + VBMESH &mesh = PATCH_TPL[lvl][ilat]; + float rad = mesh.bsRad * scale; + FVECTOR3 vP = oapiTransformCoord(&mesh.bsCnt, &mWorld); + + //float dist = D3DXVec3Length(&vP); + //if ((dist-rad)>RenderParam.horzdist) return -1; //Tile is behind the horizon + + return gc->GetScene()->IsVisibleInCamera(&vP, rad); //Is the bounding sphere visible in a viewing volume ? +} + +// ======================================================================= + +void TileManager::SetWorldMatrix (int ilng, int nlng, int ilat, int nlat) +{ + // set up world transformation matrix + FMATRIX4 rtile, wtrans; + double lng = PI*2.0 * (double)ilng/(double)nlng + PI; // add pi so texture wraps at +-180° + D3DMAT_RotY (&rtile, lng); + + if (nlat > 8) { + // The reference point for these tiles has been shifted from the centre of the sphere + // to the lower left corner of the tile, to reduce offset distances which cause rounding + // errors in the single-precision world matrix. The offset calculations are done in + // double-precision before copying them into the world matrix. + double lat = PI05 * (double)ilat/(double)nlat; + double s = RenderParam.objsize; + double dx = s*cos(lng)*cos(lat); // the offsets between sphere centre and tile corner + double dy = s*sin(lat); + double dz = s*sin(lng)*cos(lat); + RenderParam.wmat_tmp.m41 = (float)(dx*RenderParam.grot.m11 + dy*RenderParam.grot.m12 + dz*RenderParam.grot.m13 + RenderParam.cpos.x); + RenderParam.wmat_tmp.m42 = (float)(dx*RenderParam.grot.m21 + dy*RenderParam.grot.m22 + dz*RenderParam.grot.m23 + RenderParam.cpos.y); + RenderParam.wmat_tmp.m43 = (float)(dx*RenderParam.grot.m31 + dy*RenderParam.grot.m32 + dz*RenderParam.grot.m33 + RenderParam.cpos.z); + oapiMatrixMultiply(&mWorld, &rtile, &RenderParam.wmat_tmp); + } else { + oapiMatrixMultiply(&mWorld, &rtile, &RenderParam.wmat); + } +} + +// ============================================================== + +bool TileManager::SpecularColour (D3DCOLORVALUE *col) +{ + if (!atmc) { + col->r = col->g = col->b = spec_base; + return false; + } else { + double fac = 0.7; // needs thought ... + double cosa = dotp (RenderParam.cdir, RenderParam.sdir); + double alpha = 0.5*acos(cosa); // sun reflection angle + double scale = sin(alpha)*fac; + col->r = (float)max(0.0, spec_base - scale*atmc->color0.x); + col->g = (float)max(0.0, spec_base - scale*atmc->color0.y); + col->b = (float)max(0.0, spec_base - scale*atmc->color0.z); + return true; + } +} + +// ============================================================== + +void TileManager::GlobalInit (vkClient *gclient) +{ + LogAlw("TileManager::GlobalInit()..."); + + LPDIRECT3DDEVICE9 dev = gclient->GetDevice(); + + bGlobalSpecular = *(bool*)gclient->GetConfigParam (CFGPRM_SURFACEREFLECT); + bGlobalRipple = bGlobalSpecular && *(bool*)gclient->GetConfigParam (CFGPRM_SURFACERIPPLE); + bGlobalLights = *(bool*)gclient->GetConfigParam (CFGPRM_SURFACELIGHTS); + + // Level 1 patch template + CreateSphere (dev, PATCH_TPL_1, 6, false, 0, 64); + + // Level 2 patch template + CreateSphere (dev, PATCH_TPL_2, 8, false, 0, 128); + + // Level 3 patch template + CreateSphere (dev, PATCH_TPL_3, 12, false, 0, 256); + + // Level 4 patch templates + CreateSphere (dev, PATCH_TPL_4[0], 16, true, 0, 256); + CreateSphere (dev, PATCH_TPL_4[1], 16, true, 1, 256); + + // Level 5 patch template + CreateSpherePatch (dev, PATCH_TPL_5, 4, 1, 0, 18); + + // Level 6 patch templates + CreateSpherePatch (dev, PATCH_TPL_6[0], 8, 2, 0, 10, 16); + CreateSpherePatch (dev, PATCH_TPL_6[1], 4, 2, 1, 12); + + // Level 7 patch templates + CreateSpherePatch (dev, PATCH_TPL_7[0], 16, 4, 0, 12, 12, false); + CreateSpherePatch (dev, PATCH_TPL_7[1], 16, 4, 1, 12, 12, false); + CreateSpherePatch (dev, PATCH_TPL_7[2], 12, 4, 2, 10, 16, true); + CreateSpherePatch (dev, PATCH_TPL_7[3], 6, 4, 3, 12, -1, true); + + // Level 8 patch templates + CreateSpherePatch (dev, PATCH_TPL_8[0], 32, 8, 0, 12, 15, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[1], 32, 8, 1, 12, 15, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[2], 30, 8, 2, 12, 16, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[3], 28, 8, 3, 12, 12, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[4], 24, 8, 4, 12, 12, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[5], 18, 8, 5, 12, 12, false, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[6], 12, 8, 6, 10, 16, true, true, true); + CreateSpherePatch (dev, PATCH_TPL_8[7], 6, 8, 7, 12, -1, true, true, true); + + + // Patch templates for level 9 and beyond + const int n = 8; + const int nlng8[8] = {32,32,30,28,24,18,12,6}; + const int res8[8] = {15,15,16,12,12,12,12,12}; + int mult = 2, idx, lvl, i, j; + for (lvl = 9; lvl <= SURF_MAX_PATCHLEVEL; lvl++) { + idx = 0; + for (i = 0; i < 8; i++) { + for (j = 0; j < mult; j++) { + if (idx < n*mult) + CreateSpherePatch (dev, PATCH_TPL[lvl][idx], nlng8[i]*mult, n*mult, idx, 12, res8[i], false, true, true, true); + else + CreateSpherePatch (dev, PATCH_TPL[lvl][idx], nlng8[i]*mult, n*mult, idx, 12, -1, true, true, true, true); + + idx++; + } + } + mult *= 2; + } + + // create the system-wide tile cache + tilebuf = new TileBuffer (gclient); + + // viewport size for clipping calculations + D3DVIEWPORT9 vp; + dev->GetViewport (&vp); + vpX0 = vp.X, vpX1 = vpX0 + vp.Width; + vpY0 = vp.Y, vpY1 = vpY0 + vp.Height; + + // rotation matrix for flipping patches onto southern hemisphere + D3DMAT_RotX (&Rsouth, PI); +} + +// ============================================================== + +void TileManager::GlobalExit () +{ + int i; + ClearVBMesh(PATCH_TPL_1); + ClearVBMesh(PATCH_TPL_2); + ClearVBMesh(PATCH_TPL_3); + for (i = 0; i < 2; i++) ClearVBMesh(PATCH_TPL_4[i]); + ClearVBMesh(PATCH_TPL_5); + for (i = 0; i < 2; i++) ClearVBMesh(PATCH_TPL_6[i]); + for (i = 0; i < 4; i++) ClearVBMesh(PATCH_TPL_7[i]); + for (i = 0; i < 8; i++) ClearVBMesh(PATCH_TPL_8[i]); + + const int n = 8; + int mult = 2, lvl; + for (lvl = 9; lvl <= SURF_MAX_PATCHLEVEL; lvl++) { + for (i = 0; i < n*mult; i++) ClearVBMesh(PATCH_TPL[lvl][i]); + mult *= 2; + } + + delete tilebuf; +} + +// ============================================================== + +void TileManager::SetMicrotexture (const char *fname) +{ + if (fname) microtex = gc->clbkLoadTexture(fname); + else microtex = NULL; +} + +// ============================================================== + +void TileManager::SetMicrolevel (double lvl) +{ + microlvl = lvl; +} + +// ============================================================== +// static member initialisation + +DWORD TileManager::vbMemCaps = 0; +int TileManager::patchidx[9] = {0, 1, 2, 3, 5, 13, 37, 137, 501}; +bool TileManager::bGlobalSpecular = false; +bool TileManager::bGlobalRipple = false; +bool TileManager::bGlobalLights = false; + +TileBuffer *TileManager::tilebuf = NULL; +FMATRIX4 TileManager::Rsouth; + +VBMESH TileManager::PATCH_TPL_1; +VBMESH TileManager::PATCH_TPL_2; +VBMESH TileManager::PATCH_TPL_3; +VBMESH TileManager::PATCH_TPL_4[2]; +VBMESH TileManager::PATCH_TPL_5; +VBMESH TileManager::PATCH_TPL_6[2]; +VBMESH TileManager::PATCH_TPL_7[4]; +VBMESH TileManager::PATCH_TPL_8[8]; +VBMESH TileManager::PATCH_TPL_9[16]; +VBMESH TileManager::PATCH_TPL_10[32]; +VBMESH TileManager::PATCH_TPL_11[64]; +VBMESH TileManager::PATCH_TPL_12[128]; +VBMESH TileManager::PATCH_TPL_13[256]; +VBMESH TileManager::PATCH_TPL_14[512]; +VBMESH *TileManager::PATCH_TPL[15] = { + 0, &PATCH_TPL_1, &PATCH_TPL_2, &PATCH_TPL_3, PATCH_TPL_4, &PATCH_TPL_5, + PATCH_TPL_6, PATCH_TPL_7, PATCH_TPL_8, PATCH_TPL_9, PATCH_TPL_10, + PATCH_TPL_11, PATCH_TPL_12, PATCH_TPL_13, PATCH_TPL_14 +}; + +int TileManager::NLAT[9] = {0,1,1,1,1,1,2,4,8}; +int TileManager::NLNG5[1] = {4}; +int TileManager::NLNG6[2] = {8,4}; +int TileManager::NLNG7[4] = {16,16,12,6}; +int TileManager::NLNG8[8] = {32,32,30,28,24,18,12,6}; +int *TileManager::NLNG[9] = {0,0,0,0,0,NLNG5,NLNG6,NLNG7,NLNG8}; + +DWORD TileManager::vpX0, TileManager::vpX1, TileManager::vpY0, TileManager::vpY1; + + +// ======================================================================= +// ======================================================================= +// Class TileBuffer: implementation + +TileBuffer::TileBuffer (const oapi::vkClient *gclient) + : gc(gclient) + , nbuf(0) + , nused(0) + , last(0) + , bLoadMip(true) +{ + DWORD id; + + // Initialize statics + nqueue = queue_in = queue_out = 0; + hQueueMutex = CreateMutex (0, FALSE, NULL); + hLoadThread = CreateThread (NULL, 2048, LoadTile_ThreadProc, this, 0, &id); +} + +// ======================================================================= + +TileBuffer::~TileBuffer() +{ + LogAlw("=============== Deleting %u Tile Buffers =================",nbuf); + + CloseHandle(hQueueMutex); hQueueMutex = NULL; + + TerminateLoadThread(); + + if (nbuf) { + for (DWORD i = 0; i < nbuf; i++) + if (buf[i]) { + if (!(buf[i]->flag & 0x80)) { // if loaded, release tile textures + if (buf[i]->tex) ReleaseTex(buf[i]->tex); + if (buf[i]->ltex) ReleaseTex(buf[i]->ltex); + } + delete buf[i]; + } + delete []buf; + buf = NULL; + } +} + +// ======================================================================= + +bool TileBuffer::ShutDown() +{ + if (hLoadThread) { + TerminateLoadThread(); + return true; + } + return false; +} + +// ======================================================================= + +void TileBuffer::HoldThread(bool bHold) +{ + bHoldThread = bHold; +} + +// ======================================================================= + +void TileBuffer::TerminateLoadThread() +{ + if (hLoadThread) { + // Signal thread to stop and wait for it to happen + SetEvent(hStopThread); + WaitForSingleObject(hLoadThread, INFINITE); + // Clean up for next run + ResetEvent(hStopThread); + CloseHandle(hLoadThread); + hLoadThread = NULL; + } +} + +// ======================================================================= + +TILEDESC *TileBuffer::AddTile () +{ + TILEDESC *td = new TILEDESC; + memset (td, 0, sizeof(TILEDESC)); + DWORD i, j; + + if (nused == nbuf) { + TILEDESC **tmp = new TILEDESC*[nbuf+16]; + if (nbuf) { + memcpy (tmp, buf, nbuf*sizeof(TILEDESC*)); + delete []buf; + buf = NULL; + } + memset (tmp+nbuf, 0, 16*sizeof(TILEDESC*)); + buf = tmp; + nbuf += 16; + last = nused; + } else { + for (i = 0; i < nbuf; i++) { + j = (i+last)%nbuf; + if (!buf[j]) { + last = j; + break; + } + } + if (i == nbuf) { + /* Problems! */; + } + } + buf[last] = td; + td->ofs = last; + nused++; + return td; +} + +// ======================================================================= + +void TileBuffer::DeleteSubTiles (TILEDESC *tile) +{ + for (DWORD i = 0; i < 4; i++) + if (tile->subtile[i]) { + if (DeleteTile (tile->subtile[i])) + tile->subtile[i] = 0; + } +} + +// ======================================================================= + +bool TileBuffer::DeleteTile (TILEDESC *tile) +{ + bool del = true; + for (DWORD i = 0; i < 4; i++) + if (tile->subtile[i]) { + if (DeleteTile (tile->subtile[i])) + tile->subtile[i] = 0; + else + del = false; + } + + if (tile->tex || !del) { + return false; // tile or subtile contains texture -> don't deallocate + } else { + buf[tile->ofs] = 0; // remove from list + delete tile; + nused--; + return true; + } +} + +// ======================================================================= + +bool TileBuffer::LoadTileAsync (const char *name, TILEDESC *tile) +{ + bool ok = true; + + if (nqueue == MAXQUEUE) + ok = false; // queue full + else { + for (int i = 0; i < nqueue; i++) { + int j = (i+queue_out) % MAXQUEUE; + if (loadqueue[j].td == tile) + { ok = false; break; }// request already present + } + } + + if (ok) { + QUEUEDESC *qd = loadqueue+queue_in; + qd->name = name; + qd->td = tile; + + nqueue++; + queue_in = (queue_in+1) % MAXQUEUE; + } + return ok; +} + +// ======================================================================= + +DWORD WINAPI TileBuffer::LoadTile_ThreadProc (void *data) +{ + static const LONG_PTR TILESIZE = 32896; // default texture size for old-style texture files + TileBuffer *tb = static_cast(data); + auto device = tb->gc->GetDevice(); + bool load; + static QUEUEDESC qd; + DWORD idle = 1000/Config->PlanetLoadFrequency; + + LogAlw("TileBuffer::LoadTile thread started"); + + bool bFirstRun = true; + while (bFirstRun || WAIT_OBJECT_0 != WaitForSingleObject(hStopThread, idle)) + { + bFirstRun = false; + + if (bHoldThread) continue; + + WaitForSingleObject(hQueueMutex, INFINITE); + if (load = (nqueue > 0)) { + memcpy (&qd, loadqueue+queue_out, sizeof(QUEUEDESC)); + } + ReleaseMutex (hQueueMutex); + + if (load) { + char fname[MAX_PATH]; + TILEDESC *td = qd.td; + LPDIRECT3DTEXTURE9 tex, mask = 0; + LONG_PTR tidx, midx; + LONG_PTR ofs; + + if ((td->flag & 0x80) == 0) + MessageBeep (-1); + + tidx = (LONG_PTR)td->tex; + if (tidx == NOTILE) + tex = NULL; // "no texture" flag + else { + ofs = (td->flag & 0x40) ? tidx * TILESIZE : tidx; + strcpy_s (fname, 256, qd.name); + strcat_s (fname, 256, "_tile.tex"); + + HRESULT hr = ReadDDSSurface (device, fname, ofs, &tex, false); + + if (hr != S_OK) { + tex = NULL; + LogErr("Failed to load a tile using ReadDDSSurface() offset=%u, name=%s, ErrorCode = %d",ofs,fname,hr); + } + } + // Load the specular mask and/or light texture + if (((td->flag & 3) == 3) || (td->flag & 4)) { + midx = (LONG_PTR)td->ltex; + if (midx == NOTILE) + mask = NULL; // "no mask" flag + else { + ofs = (td->flag & 0x40) ? midx * TILESIZE : midx; + strcpy_s (fname, 256, qd.name); + strcat_s (fname, 256, "_tile_lmask.tex"); + if (ReadDDSSurface (device, fname, ofs, &mask, false) != S_OK) mask = NULL; + } + } + // apply loaded components + WaitForSingleObject (hQueueMutex, INFINITE); + td->tex = tex; + td->ltex = mask; + td->flag &= 0x3F; // mark as loaded + nqueue--; + queue_out = (queue_out+1) % MAXQUEUE; + ReleaseMutex (hQueueMutex); + } + } + + LogAlw("TileBuffer::LoadTile thread terminated"); + return 0; +} + + +HRESULT TileBuffer::ReadDDSSurface (LPDIRECT3DDEVICE9 pDev, const char *fname, LONG_PTR ofs, LPDIRECT3DTEXTURE9* pTex, bool bManaged) +{ + _TRACE; + char cpath[256]; + + DDSURFACEDESC2 ddsd; + DWORD dwMagic; + + FILE *f = NULL; + + sprintf_s(cpath,256,"%s%s", OapiExtension::GetHightexDir(), fname); + + if (fopen_s(&f, cpath, "rb")) return -3; + + fseek(f, (long)ofs, SEEK_SET); + + // Read the magic number + if (!fread(&dwMagic, sizeof(DWORD), 1, f)) return -2; + + if (dwMagic != MAKEFOURCC('D','D','S',' ')) return -4; + + // Read the surface description + fread(&ddsd, sizeof(DDSURFACEDESC2), 1, f); + + D3DFORMAT Format; + + switch (ddsd.ddpfPixelFormat.dwFourCC) { + + case MAKEFOURCC ('D','X','T','1'): + Format = D3DFMT_DXT1; + break; + + case MAKEFOURCC ('D','X','T','3'): + Format = D3DFMT_DXT3; + break; + + case MAKEFOURCC ('D','X','T','5'): + Format = D3DFMT_DXT5; + break; + + default: + LogErr("INVALID TEXTURE FORMAT in ReadDDSSurface()"); + return -5; + } + + if (ddsd.dwHeight>4096 || ddsd.dwWidth>4096) LogErr("Attempting to load very large surface tile (%u,%u)", ddsd.dwWidth, ddsd.dwHeight); + + *pTex = NULL; + + D3DLOCKED_RECT rect; + + if (bManaged) { + if (pDev->CreateTexture(ddsd.dwWidth, ddsd.dwHeight, 1, 0, Format, D3DPOOL_MANAGED, pTex, NULL)!=S_OK) { + LogErr("Surface Tile Allocation Failed. w=%u, h=%u", ddsd.dwWidth, ddsd.dwHeight); + return -10; + } + if ((*pTex)==NULL) return -8; + if ((*pTex)->LockRect(0, &rect, NULL, 0)==S_OK) { + if (ddsd.dwFlags & DDSD_LINEARSIZE) { + fread(rect.pBits, ddsd.dwLinearSize, 1, f); + (*pTex)->UnlockRect(0); + fclose(f); + return S_OK; + } + } + } + else { + if (pDev->CreateTexture(ddsd.dwWidth, ddsd.dwHeight, 1, 0, Format, D3DPOOL_DEFAULT, pTex, NULL)!=S_OK) { + LogErr("Surface Tile Allocation Failed. w=%u, h=%u", ddsd.dwWidth, ddsd.dwHeight); + return -9; + } + LPDIRECT3DTEXTURE9 pSys = NULL; + HR(pDev->CreateTexture(ddsd.dwWidth, ddsd.dwHeight, 1, 0, Format, D3DPOOL_SYSTEMMEM, &pSys, NULL)); + + if (pSys==NULL || (*pTex)==NULL) return -8; + if (pSys->LockRect(0, &rect, NULL, 0)==S_OK) { + if (ddsd.dwFlags & DDSD_LINEARSIZE) { + fread(rect.pBits, ddsd.dwLinearSize, 1, f); + pSys->UnlockRect(0); + HR(pDev->UpdateTexture(pSys,(*pTex))); + pSys->Release(); + fclose(f); + return S_OK; + } + } + } + + fclose(f); + return -7; +} + + + + + + + +// ======================================================================= + +bool TileBuffer::bHoldThread = false; +int TileBuffer::nqueue = 0; +int TileBuffer::queue_in = 0; +int TileBuffer::queue_out = 0; +HANDLE TileBuffer::hQueueMutex = NULL; +HANDLE TileBuffer::hLoadThread = NULL; +HANDLE TileBuffer::hStopThread(CreateEvent(NULL, FALSE, FALSE, NULL)); +struct TileBuffer::QUEUEDESC TileBuffer::loadqueue[MAXQUEUE] = {0}; + diff --git a/OVP/VulkanClient/TileMgr.h b/OVP/VulkanClient/TileMgr.h new file mode 100644 index 000000000..64093d2e8 --- /dev/null +++ b/OVP/VulkanClient/TileMgr.h @@ -0,0 +1,280 @@ +// ============================================================== +// TileMgr.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// ============================================================== + +// ============================================================== +// class TileManager (interface) +// +// Planetary surface rendering management, including a simple +// LOD (level-of-detail) algorithm for surface patch resolution. +// ============================================================== + +#ifndef __TILEMGR_H +#define __TILEMGR_H + +#include "Effect.h" +#include "Util.h" +#include "Mesh.h" +#include "Spherepatch.h" + +#define MAXQUEUE 10 + +#pragma pack(push,1) + struct TILEFILESPEC { + LONG_PTR sidx; // index for surface texture (-1: not present) + LONG_PTR midx; // index for land-water mask texture (-1: not present) + DWORD eidx; // index for elevation data blocks (not used yet; always -1) + DWORD flags; // tile flags: bit 0: has diffuse component; bit 1: has specular component; bit 2: has city lights + DWORD subidx[4]; // subtile indices + }; + +struct LMASKFILEHEADER { // file header for contents file at level 1-8 + char id[8]; // ID+version string + DWORD hsize; // header size + DWORD flag; // bitflag content information + DWORD npatch; // number of patches + BYTE minres; // min. resolution level + BYTE maxres; // max. resolution level +}; +#pragma pack(pop) + +struct TILEDESC { + LPDIRECT3DTEXTURE9 tex; // diffuse surface texture + LPDIRECT3DTEXTURE9 ltex; // landmask texture, if applicable + DWORD flag; + struct TILEDESC *subtile[4]; // sub-tiles for the next resolution level + DWORD ofs; // refers back to the master list entry for the tile +}; + +typedef struct { + float tumin, tumax; + float tvmin, tvmax; +} TEXCRDRANGE; + +class vkConfig; +class vPlanet; + + +class TileManager : public vkEffect { + + friend class TileBuffer; + friend class CSphereManager; + +public: + /** + * \brief Constructs a new tile manager object + * \param gclient client instance pointer + * \param vPlanet planet instance pointer + */ + TileManager (oapi::vkClient *gclient, const vPlanet *vplanet); + + /** + * \brief Destroys the tile manager object + */ + virtual ~TileManager (); + + /** + * \brief Set up global parameters shared by all instances + * \param gclient client instance pointer + */ + static void GlobalInit(oapi::vkClient *gclient); + + /** + * \brief Release global parameters + */ + static void GlobalExit(); + + // One-time global initialisation/exit methods + + inline int GetMaxLevel () const { return maxlvl; } + + virtual void SetMicrotexture (const char *fname); + virtual void SetMicrolevel (double lvl); + + virtual void Render(LPDIRECT3DDEVICE9 dev, FMATRIX4 &wmat, double scale, int level, double viewap = 0.0, bool bfog = false); + + void SetAmbientColor(DWORD cAmbient); + +protected: + + void ProcessTile (int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, TILEDESC *tile, + const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag, + const TEXCRDRANGE &bkp_range, LPDIRECT3DTEXTURE9 bkp_tex, LPDIRECT3DTEXTURE9 bkp_ltex, DWORD bkp_flag); + + virtual void InitRenderTile() = 0; + virtual void EndRenderTile() = 0; + virtual void RenderSimple(int level, int npatch, TILEDESC *tile, FMATRIX4* mWorld) = 0; + + virtual void RenderTile(int lvl, int hemisp, int ilat, int nlat, int ilng, int nlng, double sdist, TILEDESC *tile, + const TEXCRDRANGE &range, LPDIRECT3DTEXTURE9 tex, LPDIRECT3DTEXTURE9 ltex, DWORD flag) = 0; + + bool LoadPatchData (); + // load binary definition file for LOD levels 1-8 + + bool LoadTileData (); + // load binary definition file for LOD levels > 8 + + bool AddSubtileData (TILEDESC &td, TILEFILESPEC *tfs, DWORD idx, DWORD sub, DWORD lvl); + // add a high-resolution subtile specification to the tree + + void LoadTextures (char *modstr = 0); + // load patch textures for all LOD levels + + void PreloadTileTextures (TILEDESC *tile8, DWORD ntex, DWORD nmask); + // Pre-load high-resolution tile textures for the planet (level >= 9) + + void AddSubtileTextures (TILEDESC *td, LPDIRECT3DTEXTURE9 *tbuf, DWORD nt, LPDIRECT3DTEXTURE9 *mbuf, DWORD nm); + // add a high-resolution subtile texture to the tree + + void LoadSpecularMasks (); + // load specular and night light textures + + VECTOR3 TileCentre (int hemisp, int ilat, int nlat, int ilng, int nlng); + // direction to tile centre from planet centre in planet frame + + void TileExtents (int hemisp, int ilat, int nlat, int ilg, int nlng, double &lat1, double &lat2, double &lng1, double &lng2) const; + + int IsTileInView(int lvl, int ilat, float scale); + // checks if a given tile is observable from camera position + + void SetWorldMatrix (int ilng, int nlng, int ilat, int nlat); + // set the world transformation for a particular tile + + bool SpecularColour (D3DCOLORVALUE *col); + // adjust specular reflection through atmosphere + + + FMATRIX4 mWorld; + DWORD cAmbient; + const vPlanet *vp; // the planet visual + OBJHANDLE obj; // the planet object + char *objname; // the name of the planet (for identifying texture files) + DWORD tilever; // file version for tile textures + int maxlvl; // max LOD level + int maxbaselvl; // max LOD level, capped at 8 + DWORD ntex; // total number of loaded textures for levels <= 8 + DWORD nhitex; // number of textures for levels > 8 + DWORD nhispec; // number of specular reflection masks (level > 8) + double hipatchrad; // angular aperture fraction at which to downgrade patch resolution + double lightfac; // city light intensity factor + double microlvl; // intensity of microtexture + DWORD nmask; // number of specular reflection masks/light maps (level <= 8) + VECTOR3 pcdir; // previous camera direction + static FMATRIX4 Rsouth; // rotation matrix for mapping tiles to southern hemisphere + float spec_base; // base intensity for specular reflections + const ATMCONST *atmc; // atmospheric parameters (used for specular colour modification) + bool bPreloadTile; // pre-load surface tile textures + bool bNoTextures; // Textures failed to load + TILEDESC *tiledesc; // tile descriptors for levels 1-8 + static TileBuffer *tilebuf; // subtile manager + + LPDIRECT3DTEXTURE9 *texbuf; // texture buffer for surface textures (level <= 8) + LPDIRECT3DTEXTURE9 *specbuf; // texture buffer for specular masks (level <= 8); + SURFHANDLE microtex; // microtexture overlay + + // object-independent configuration data + static bool bGlobalSpecular; // user wants specular reflections + static bool bGlobalRipple; // user wants specular microtextures + static bool bGlobalLights; // user wants planet city lights + + // tile patch templates + static VBMESH PATCH_TPL_1; + static VBMESH PATCH_TPL_2; + static VBMESH PATCH_TPL_3; + static VBMESH PATCH_TPL_4[2]; + static VBMESH PATCH_TPL_5; + static VBMESH PATCH_TPL_6[2]; + static VBMESH PATCH_TPL_7[4]; + static VBMESH PATCH_TPL_8[8]; + static VBMESH PATCH_TPL_9[16]; + static VBMESH PATCH_TPL_10[32]; + static VBMESH PATCH_TPL_11[64]; + static VBMESH PATCH_TPL_12[128]; + static VBMESH PATCH_TPL_13[256]; + static VBMESH PATCH_TPL_14[512]; + static VBMESH *PATCH_TPL[15]; + static int patchidx[9]; // texture offsets for different LOD levels + static int NLAT[9]; + static int NLNG5[1], NLNG6[2], NLNG7[4], NLNG8[8], *NLNG[9]; + static DWORD vpX0, vpX1, vpY0, vpY1; // viewport boundaries + + static DWORD vbMemCaps; // video/system memory flag for vertex buffers + + struct RENDERPARAM { + LPDIRECT3DDEVICE9 dev; // render device + FMATRIX4 wmat; // world matrix + FMATRIX4 wmat_tmp; // copy of world matrix used as work buffer + int tgtlvl; // target resolution level + MATRIX3 grot; // planet rotation matrix + VECTOR3 cpos; // planet offset vector (in global frame) + VECTOR3 sdir; // sun direction from planet centre (in planet frame) + VECTOR3 cdir; // camera direction from planet centre (in planet frame) + double cdist; // camera distance from planet centre (in units of planet radii) + double viewap; // aperture of surface cap visible from camera pos + double objsize; // planet radius + double horzdist; // distance to the horizon in meters + bool bfog; // distance fog flag + bool bCockpit; + } RenderParam; +}; + + +// ======================================================================= +// Class TileBuffer: Global resource; holds a collection of +// tile specifications across all planets + +class TileBuffer { +public: + explicit TileBuffer (const oapi::vkClient *gclient); + ~TileBuffer (); + TILEDESC *AddTile (); + void DeleteSubTiles (TILEDESC *tile); + + friend void ClearVertexBuffers (TILEDESC *td); + // Recursively remove subrange vertex buffers from a tile tree with + // root td. This is necessary when a new tile has been loaded, because + // this can change the subrange extents for child tiles. + + bool LoadTileAsync (const char *name, TILEDESC *tile); + // load the textures for a tile for planet 'name', given by descriptor + // 'tile', using a separate thread. + // Returns false if request can't be entered (queue full, or request + // already present) + + static bool ShutDown(); + static void HoldThread(bool bHold); + + static HANDLE hQueueMutex; // Tile loading queue access mutex + +private: + static HANDLE hLoadThread; // LoadTile ThreadProc handle + static HANDLE hStopThread; // Thread kill signal handle + + static void TerminateLoadThread(); // Terminates the LoadTile thread + + bool DeleteTile (TILEDESC *tile); + + static HRESULT ReadDDSSurface (LPDIRECT3DDEVICE9 pDev, const char *fname, LONG_PTR ofs, LPDIRECT3DTEXTURE9* pTex, bool bManaged); + static DWORD WINAPI LoadTile_ThreadProc (void*); + // the thread function loading tile textures on demand + + const oapi::vkClient *gc; // the client + bool bLoadMip; // load mipmaps for tiles if available + + static bool bHoldThread; + static int nqueue, queue_in, queue_out; + DWORD nbuf; // buffer size; + DWORD nused; // number of active entries + DWORD last; // index of last activated entry + TILEDESC **buf; // tile buffer + + static struct QUEUEDESC { + const char *name; + TILEDESC *td; + } loadqueue[MAXQUEUE]; +}; + +#endif // !__TILEMGR_H diff --git a/OVP/VulkanClient/Tilemgr2.cpp b/OVP/VulkanClient/Tilemgr2.cpp new file mode 100644 index 000000000..aa023ce60 --- /dev/null +++ b/OVP/VulkanClient/Tilemgr2.cpp @@ -0,0 +1,1218 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// ============================================================== +// tilemanager2.cpp +// Rendering of planetary surfaces using texture tiles at +// variable resolutions (new version). +// ============================================================== + +#include "Tilemgr2.h" +#include "Config.h" +#include "Catalog.h" +#include "Scene.h" +#include "OapiExtension.h" +#include "DirectXCollision.h" + +#include +#include +#include + +// ======================================================================= +// Externals +static TEXCRDRANGE2 fullrange = {0,1,0,1}; + +int SURF_MAX_PATCHLEVEL2 = 18; // move this somewhere else + + +bool FileExists(const char* path) +{ + bool exists; + struct _finddata_t fd; + intptr_t fh = _findfirst(path, &fd); + if (exists = (fh != -1)) + _findclose(fh); + return exists; +} + + +// ======================================================================= +// Class Tile + +Tile::Tile (TileManager2Base *_mgr, int _lvl, int _ilat, int _ilng) +: mgr(_mgr), lvl(_lvl), ilat(_ilat), ilng(_ilng), + lngnbr_lvl(_lvl), latnbr_lvl(_lvl), dianbr_lvl(_lvl), + texrange(fullrange), microrange(fullrange), overlayrange(fullrange), cnt(Centre()), + mesh(NULL), tex(NULL), overlay(NULL), + last_used(0.0), + state(Invalid), + edgeok(false), owntex (true), ownoverlay(false) +{ + double f = 1.0 / double(1<TilesLoaded++; + bMipmaps = false; +} + +// ----------------------------------------------------------------------- + +Tile::~Tile () +{ + vkStats.TilesAllocated--; + mgr->TilesLoaded--; + state = Invalid; + if (mesh) delete mesh; +} + + +// ------------------------------------------------------------------------ +// Pre Load routine for surface tiles +// ------------------------------------------------------------------------ + +bool Tile::LoadTextureFile(const char *fullpath, LPDIRECT3DTEXTURE9 *pPre) +{ + auto y = filesystem::status(fullpath); + if (filesystem::exists(y)) { + DWORD Mips = 1, Filter = D3DX_FILTER_NONE; + if (bMipmaps) Filter = D3DX_FILTER_BOX, Mips = 0; + if (D3DXCreateTextureFromFileEx(mgr->Dev(), fullpath, 0, 0, Mips, 0, D3DFMT_FROM_FILE, D3DPOOL_SYSTEMMEM, D3DX_DEFAULT, Filter, 0, NULL, NULL, pPre) == S_OK) { + return true; + } + } + + *pPre = NULL; + return false; +} + +bool Tile::LoadTextureFromMemory(void *data, DWORD ndata, LPDIRECT3DTEXTURE9 *pPre) +{ + DWORD Mips = 1, Filter = D3DX_FILTER_NONE; + if (bMipmaps) Filter = D3DX_FILTER_BOX, Mips = 0; + if (D3DXCreateTextureFromFileInMemoryEx(mgr->Dev(), data, ndata, 0, 0, Mips, 0, D3DFMT_FROM_FILE, D3DPOOL_SYSTEMMEM, D3DX_DEFAULT, Filter, 0, NULL, NULL, pPre) == S_OK) { + return true; + } + + *pPre = NULL; + return false; +} + + +// ------------------------------------------------------------------------ +// Create a texture from a pre-loaded data +// ------------------------------------------------------------------------ + +bool Tile::CreateTexture(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DTEXTURE9 pPre, LPDIRECT3DTEXTURE9 *pTex) +{ + D3DSURFACE_DESC desc; + if (pPre) { + pPre->GetLevelDesc(0, &desc); + *pTex = g_pTexmgr_tt->New(desc.Width, desc.Format); + HR(pDev->UpdateTexture(pPre, (*pTex))); + return true; + } + return false; +} + +// ----------------------------------------------------------------------- + +bool Tile::PreDelete () +{ + switch (state) { + case Loading: + return false; // locked + case InQueue: + mgr->loader->Unqueue (this); // remove from load queue + // fall through + default: + return true; + } +} + +// ----------------------------------------------------------------------- + +void Tile::Render() +{ + if (mgr->gc->IsControlPanelOpen()) + if (vkStats.TilesRendered.count(mgr->GetScene()->GetRenderPass())) + vkStats.TilesRendered[mgr->GetScene()->GetRenderPass()]++; +} + +// ----------------------------------------------------------------------- + +bool Tile::GetParentSubTexRange (TEXCRDRANGE2 *subrange) +{ + Tile *parent = getParent(); + if (!parent) return false; // haven't got a parent + + if (!(ilat&1)) { // left column + subrange->tvmin = parent->texrange.tvmin; + subrange->tvmax = (parent->texrange.tvmin+parent->texrange.tvmax)*0.5f; + } else { // right column + subrange->tvmin = (parent->texrange.tvmin+parent->texrange.tvmax)*0.5f; + subrange->tvmax = parent->texrange.tvmax; + } + if (!(ilng&1)) { // bottom row + subrange->tumin = parent->texrange.tumin; + subrange->tumax = (parent->texrange.tumin+parent->texrange.tumax)*0.5f; + } else { // top row + subrange->tumin = (parent->texrange.tumin+parent->texrange.tumax)*0.5f; + subrange->tumax = parent->texrange.tumax; + } + return true; +} + +// ----------------------------------------------------------------------- + +bool Tile::GetParentOverlayRange(TEXCRDRANGE2 *subrange) +{ + Tile *parent = getParent(); + if (!parent) return false; // haven't got a parent + + overlay = parent->overlay; + + if (!(ilat & 1)) { // left column + subrange->tvmin = parent->overlayrange.tvmin; + subrange->tvmax = (parent->overlayrange.tvmin + parent->overlayrange.tvmax)*0.5f; + } + else { // right column + subrange->tvmin = (parent->overlayrange.tvmin + parent->overlayrange.tvmax)*0.5f; + subrange->tvmax = parent->overlayrange.tvmax; + } + if (!(ilng & 1)) { // bottom row + subrange->tumin = parent->overlayrange.tumin; + subrange->tumax = (parent->overlayrange.tumin + parent->overlayrange.tumax)*0.5f; + } + else { // top row + subrange->tumin = (parent->overlayrange.tumin + parent->overlayrange.tumax)*0.5f; + subrange->tumax = parent->overlayrange.tumax; + } + return true; +} + +// ----------------------------------------------------------------------- + +bool Tile::GetParentMicroTexRange(TEXCRDRANGE2 *subrange) +{ + + int dlev = imicrolvl - Level(); + + + if (dlev >= 0) { + int f = (1 << dlev); + subrange->tumax = float(f); + subrange->tvmax = float(f); + return true; + } + + Tile *parent = getParent(); + if (!parent) return false; // haven't got a parent + + if (!(ilat & 1)) { // left column + subrange->tvmin = parent->microrange.tvmin; + subrange->tvmax = (parent->microrange.tvmin + parent->microrange.tvmax)*0.5f; + } + else { // right column + subrange->tvmin = (parent->microrange.tvmin + parent->microrange.tvmax)*0.5f; + subrange->tvmax = parent->microrange.tvmax; + } + if (!(ilng & 1)) { // bottom row + subrange->tumin = parent->microrange.tumin; + subrange->tumax = (parent->microrange.tumin + parent->microrange.tumax)*0.5f; + } + else { // top row + subrange->tumin = (parent->microrange.tumin + parent->microrange.tumax)*0.5f; + subrange->tumax = parent->microrange.tumax; + } + return true; +} + +// ----------------------------------------------------------------------- + +FVECTOR4 Tile::GetTexRangeDX (const TEXCRDRANGE2 *subrange) const +{ + return FVECTOR4(subrange->tumin, subrange->tvmin, subrange->tumax - subrange->tumin, subrange->tvmax - subrange->tvmin); +} + +// ----------------------------------------------------------------------- +// Check if tile bounding box intersects the viewing frustum +// given the transformation matrix transform + +bool Tile::InView (const MATRIX4 &transform) +{ + if (!lvl) return true; // no good check for this yet + if (!mesh) return true; // DEBUG : TEMPORARY + + bool bx1, bx2, by1, by2, bz1; + bx1 = bx2 = by1 = by2 = bz1 = false; + int v; + double hx, hy; + for (v = 0; v < 8; v++) { + VECTOR4 vt = mul (mesh->Box[v], transform); + hx = vt.x/vt.w, hy = vt.y/vt.w; + if (vt.z > 0.0) bz1 = true; + if (vt.w < 0.0) hx = -hx, hy = -hy; + if (hx > -1.0) bx1 = true; + if (hx < 1.0) bx2 = true; + if (hy > -1.0) by1 = true; + if (hy < 1.0) by2 = true; + if (bx1 && bx2 && by1 && by2 && bz1) return true; + } + return false; +} + +// ----------------------------------------------------------------------- + +float Tile::GetBoundingSphereRad() const +{ + if (mesh) return mesh->bsRad; + return 0.0f; +} + +// ----------------------------------------------------------------------- + +FVECTOR3 Tile::GetBoundingSpherePos() const +{ + if (mesh) return mesh->bsCnt; + return FVECTOR3(0,0,0); +} + +// ----------------------------------------------------------------------- + +bool Tile::Pick(const FMATRIX4* pW, const FVECTOR3 *vDir, TILEPICK &result) +{ + if (!mesh) { + LogErr("Tile::Pick() Failed: No Mesh Available"); + return false; + } + if (!mesh->idx || !mesh->vtx) { + LogErr("Tile::Pick() Failed: No Geometry Available"); + return false; + } + + FVECTOR3 bs = oapiTransformCoord(&mesh->bsCnt, pW); + + float dst = dotp(bs, *vDir); + float len2 = dotp(bs, bs); + + if (dst < -mesh->bsRad) return false; + if (sqrt(len2 - dst*dst) > mesh->bsRad) return false; + + FVECTOR3 _a, _b, _c, cp; + FMATRIX4 mWI; + float det; + + WORD *pIdc = mesh->idx; + VERTEX_2TEX *vtx = mesh->vtx; + + oapiMatrixInverse(&mWI, &det, pW); + + XMVECTOR pos = oapiTransformCoord(&(FVECTOR3(0, 0, 0)), &mWI).XM(); + XMVECTOR dir = oapiTransformNormal(vDir, &mWI).XM(); + + int idx = -1; + + for (DWORD i = 0; inf; i++) + { + WORD a = pIdc[i * 3 + 0]; + WORD b = pIdc[i * 3 + 1]; + WORD c = pIdc[i * 3 + 2]; + + _a = FVECTOR3(vtx[a].x, vtx[a].y, vtx[a].z); + _b = FVECTOR3(vtx[b].x, vtx[b].y, vtx[b].z); + _c = FVECTOR3(vtx[c].x, vtx[c].y, vtx[c].z); + + float dst; + + cp = crossp((_c - _b), (_a - _b)); + + if (dotp(cp, dir)<0) { + if (DirectX::TriangleTests::Intersects(pos, dir, _c.XM(), _b.XM(), _a.XM(), dst)) { + if (dst > 0.1f) { + if (dst < result.d) { + idx = i; + result.d = dst; + result.i = i; + result.pTile = this; + } + } + } + } + } + + if (idx >= 0) { + + int i = result.i; + + WORD a = pIdc[i * 3 + 0]; + WORD b = pIdc[i * 3 + 1]; + WORD c = pIdc[i * 3 + 2]; + + _a = FVECTOR3(vtx[a].x, vtx[a].y, vtx[a].z); + _b = FVECTOR3(vtx[b].x, vtx[b].y, vtx[b].z); + _c = FVECTOR3(vtx[c].x, vtx[c].y, vtx[c].z); + + cp = crossp((_c - _b), (_a - _b)); + cp = oapiTransformNormal(&cp, pW); + + result._n = unit(cp); + result._p = *vDir * result.d; + + return true; + } + + return false; +} + + + +// ----------------------------------------------------------------------- +// returns the direction of the tile centre from the planet centre in local +// planet coordinates + +VECTOR3 Tile::Centre () const +{ + int nlat = 1 << lvl; + int nlng = 2 << lvl; + double cntlat = PI05 - PI * ((double)ilat+0.5)/(double)nlat, slat = sin(cntlat), clat = cos(cntlat); + double cntlng = PI2 * ((double)ilng+0.5)/(double)nlng + PI, slng = sin(cntlng), clng = cos(cntlng); + return _V(clat*clng, slat, clat*slng); +} + + +// ----------------------------------------------------------------------- + +void Tile::Extents (double *_latmin, double *_latmax, double *_lngmin, double *_lngmax) const +{ + int nlat = 1 << lvl; + int nlng = 2 << lvl; + *_latmin = PI * (0.5 - (double)(ilat+1)/(double)nlat); + *_latmax = PI * (0.5 - (double)ilat/(double)nlat); + *_lngmin = PI2 * (double)(ilng-nlng/2)/(double)nlng; + *_lngmax = PI2 * (double)(ilng-nlng/2+1)/(double)nlng; +} + +// ----------------------------------------------------------------------- + + +bool Tile::IsPointInTile(double lng, double lat) +{ + if (lat > bnd.maxlat || lat < bnd.minlat) return false; + if (lng > bnd.maxlng || lng < bnd.minlng) return false; + return true; +} + +// ----------------------------------------------------------------------- + +VBMESH *Tile::CreateMesh_quadpatch (int grdlat, int grdlng, float *elev, double elev_scale, double globelev, + const TEXCRDRANGE2 *range, bool shift_origin, VECTOR3 *shift, double bb_excess) +{ +// const float TEX2_MULTIPLIER = 1.0f; // was: 4.0f was: 16.0f + const float c1 = 1.0f, c2 = 0.0f; // -1.0f/512.0f; // assumes 256x256 texture patches + int i, j, n, nofs0, nofs1; + int nlng = (lvl >= 0 ? 2 << lvl : 1); + int nlat = (lvl >= 0 ? 1 << lvl : 1); + bool north = (ilat < nlat/2); + + double lat, slat, clat, lng, slng, clng, eradius, dx, dy; + double minlat = PI * (double)(nlat/2-ilat-1)/(double)nlat; + double maxlat = PI * (double)(nlat/2-ilat)/(double)nlat; + double minlng = 0; + double maxlng = PI2/(double)nlng; + double radius = mgr->obj_size; + VECTOR3 pos, tpos, nml; + if (!range) range = &fullrange; + //float turange = range->tumax-range->tumin; + //float tvrange = range->tvmax-range->tvmin; + + int nvtx = (grdlat+1)*(grdlng+1); // patch mesh node grid + int nvtxbuf = nvtx + grdlat+1 + grdlng+1; // add buffer for storage of edges (for elevation matching) + VERTEX_2TEX *vtx = g_pMemgr_vtx->New(nvtxbuf); + + // create transformation for bounding box + // we define the local coordinates for the patch so that the x-axis points + // from (minlng,minlat) corner to (maxlng,minlat) corner (origin is halfway between) + // y-axis points from local origin to middle between (minlng,maxlat) and (maxlng,maxlat) + // bounding box is created in this system and then transformed back to planet coords. + double clat0 = cos(minlat), slat0 = sin(minlat); + double clng0 = cos(minlng), slng0 = sin(minlng); + double clat1 = cos(maxlat), slat1 = sin(maxlat); + double clng1 = cos(maxlng), slng1 = sin(maxlng); + VECTOR3 ex = {clat0*clng1 - clat0*clng0, 0, clat0*slng1 - clat0*slng0}; normalise (ex); + VECTOR3 ey = {0.5*(clng0+clng1)*(clat1-clat0), slat1-slat0, 0.5*(slng0+slng1)*(clat1-clat0)}; normalise (ey); + VECTOR3 ez = crossp (ey, ex); + MATRIX3 R = {ex.x, ex.y, ex.z, ey.x, ey.y, ey.z, ez.x, ez.y, ez.z}; + VECTOR3 pref = {radius*clat0*0.5*(clng1+clng0), radius*slat0, radius*clat0*0.5*(slng1+slng0)}; // origin + VECTOR3 tpmin, tpmax; + + float e = 0.0f; + + // patch translation vector + if (shift_origin) { + dx = (north ? clat0:clat1)*radius; + dy = (north ? slat0:slat1)*radius; + } else { + dx = dy = 0.0; + } + if (shift) { + shift->x = dx; + shift->y = dy; + shift->z = 0.0; + } + + // create the vertices + for (i = n = 0; i <= grdlat; i++) { + lat = minlat + (maxlat-minlat) * (double)i/(double)grdlat; + slat = sin(lat), clat = cos(lat); + for (j = 0; j <= grdlng; j++) { + lng = minlng + (maxlng-minlng) * (double)j/(double)grdlng; + slng = sin(lng), clng = cos(lng); + + eradius = radius + globelev; // radius including node elevation + if (elev) e = elev[(i+1)*TILE_ELEVSTRIDE + j+1] * float(elev_scale); + eradius += double(e); + + nml = _V(clat*clng, slat, clat*slng); + pos = nml*eradius; + tpos = mul (R, pos-pref); + if (!n) { + tpmin = tpos; + tpmax = tpos; + } else { + if (tpos.x < tpmin.x) tpmin.x = tpos.x; + else if (tpos.x > tpmax.x) tpmax.x = tpos.x; + if (tpos.y < tpmin.y) tpmin.y = tpos.y; + else if (tpos.y > tpmax.y) tpmax.y = tpos.y; + if (tpos.z < tpmin.z) tpmin.z = tpos.z; + else if (tpos.z > tpmax.z) tpmax.z = tpos.z; + } + vtx[n].x = float(pos.x - dx); vtx[n].nx = float(nml.x); + vtx[n].y = float(pos.y - dy); vtx[n].ny = float(nml.y); + vtx[n].z = float(pos.z); vtx[n].nz = float(nml.z); + vtx[n].e = float(e); + + vtx[n].tu0 = float((c1*j)/grdlng+c2); // overlap to avoid seams + vtx[n].tv0 = float(grdlat-i)/float(grdlat); + //vtx[n].tu1 = vtx[n].tu0 * TEX2_MULTIPLIER; + //vtx[n].tv1 = vtx[n].tv0 * TEX2_MULTIPLIER; + // map texture coordinates to subrange + //vtx[n].tu0 = vtx[n].tu0*turange + range->tumin; + //vtx[n].tv0 = vtx[n].tv0*tvrange + range->tvmin; + n++; + } + } + + // create the face indices + int nidx = 2*grdlat*grdlng * 3; + WORD *idx = g_pMemgr_w->New(nidx); + + if (elev) { // do adaptive orientation of cell diagonal + float *elev1, *elev2, *elev1n, *elev2n, err1, err2; + for (i = n = nofs0 = 0; i < grdlat; i++) { + nofs1 = nofs0+grdlng+1; + for (j = 0; j < grdlng; j++) { + elev1 = elev + TILE_ELEVSTRIDE+2+j+TILE_ELEVSTRIDE*i; + elev2 = elev1 + TILE_ELEVSTRIDE-1; + elev1n = elev1 - TILE_ELEVSTRIDE+1; + elev2n = elev2 + TILE_ELEVSTRIDE-1; + err1 = fabs(*elev1 * 2 - *elev2 - *elev1n) + fabs(*elev2 * 2 - *elev1 - *elev2n); + elev1 = elev + TILE_ELEVSTRIDE+1+j+TILE_ELEVSTRIDE*i; + elev2 = elev1 + TILE_ELEVSTRIDE+1; + elev1n = elev1 - TILE_ELEVSTRIDE-1; + elev2n = elev2 + TILE_ELEVSTRIDE+1; + err2 = fabs(*elev1 * 2 - *elev2 - *elev1n) + fabs(*elev2 * 2 - *elev1 - *elev2n); + if (err1 < err2) { + idx[n++] = nofs0+j; + idx[n++] = nofs1+j; + idx[n++] = nofs0+j+1; + idx[n++] = nofs0+j+1; + idx[n++] = nofs1+j; + idx[n++] = nofs1+j+1; + } else { + idx[n++] = nofs0+j; + idx[n++] = nofs1+j+1; + idx[n++] = nofs0+j+1; + idx[n++] = nofs1+j+1; + idx[n++] = nofs0+j; + idx[n++] = nofs1+j; + } + } + nofs0 = nofs1; + } + } else { // no elevation => no adaptation of cell diagonals necessary + for (i = n = nofs0 = 0; i < grdlat; i++) { + nofs1 = nofs0+grdlng+1; + for (j = 0; j < grdlng; j++) { + idx[n++] = nofs0+j; + idx[n++] = nofs1+j; + idx[n++] = nofs0+j+1; + idx[n++] = nofs1+j+1; + idx[n++] = nofs0+j+1; + idx[n++] = nofs1+j; + } + nofs0 = nofs1; + } + } + + // regenerate normals for terrain + if (elev) { +// const double shade_exaggerate = 1.0; // 1 = normal, <1 = more dramatic landscape shadows + double dy, dz, dydz, /*nz_x, ny_x,*/ nx1, ny1, nz1; + int en; + dy = radius * PI/(nlat*grdlat); // y-distance between vertices +// ny_x = shade_exaggerate*dy; + for (i = n = 0; i <= grdlat; i++) { + lat = minlat + (maxlat-minlat) * (double)i/(double)grdlat; + slat = sin(lat), clat = cos(lat); + dz = radius * PI2*cos(lat) / (nlng*grdlng); // z-distance between vertices on unit sphere + dydz = dy*dz; +// nz_x = shade_exaggerate*dz; + for (j = 0; j <= grdlng; j++) { + lng = minlng + (maxlng-minlng) * (double)j/(double)grdlng; + slng = sin(lng), clng = cos(lng); + en = (i+1)*TILE_ELEVSTRIDE + (j+1); + + // This version avoids the normalisation of the 4 intermediate face normals + // It's faster and doesn't seem to make much difference + VECTOR3 nml = { 2.0*dydz, dz*elev_scale*(elev[en - TILE_ELEVSTRIDE] - elev[en + TILE_ELEVSTRIDE]), dy*elev_scale*(elev[en - 1] - elev[en + 1]) }; + normalise (nml); + // rotate into place + nx1 = nml.x*clat - nml.y*slat; + ny1 = nml.x*slat + nml.y*clat; + nz1 = nml.z; + vtx[n].nx = (float)(nx1*clng - nz1*slng); + vtx[n].ny = (float)(ny1); + vtx[n].nz = (float)(nx1*slng + nz1*clng); + n++; + } + } + } + + // store the adaptable edges in the separate vertex area + for (i = 0, n = nvtx; i <= grdlat; i++) // store left or right edge + vtx[n++] = vtx[i*(grdlng+1) + ((ilng&1) ? grdlng:0)]; + for (i = 0; i <= grdlng; i++) // store top or bottom edge + vtx[n++] = vtx[i + ((ilat&1) ? 0 : (grdlng+1)*grdlat)]; + + // create the mesh + VBMESH *mesh = new VBMESH(mgr); + mesh->vtx = vtx; + mesh->nv = nvtx; + mesh->idx = idx; + mesh->nf = nidx/3; + + // set bounding box for visibility calculations + if (bb_excess) { + double bb_dx = tpmax.x-tpmin.x; + double bb_dy = tpmax.y-tpmin.y; + double bb_dz = tpmax.z-tpmin.z; + double scale = sqrt(bb_dx*bb_dx + bb_dy*bb_dy + bb_dz*bb_dz)/(2.0*sqrt(3.0))*bb_excess; + tpmin.x -= scale; + tpmax.x += scale; + tpmin.y -= scale; + tpmax.y += scale; + tpmin.z -= scale; + tpmax.z += scale; + } + pref.x -= dx; + pref.y -= dy; + + mesh->Box[0] = _V(tmul (R, _V(tpmin.x, tpmin.y, tpmin.z)) + pref); + mesh->Box[1] = _V(tmul (R, _V(tpmax.x, tpmin.y, tpmin.z)) + pref); + mesh->Box[2] = _V(tmul (R, _V(tpmin.x, tpmax.y, tpmin.z)) + pref); + mesh->Box[3] = _V(tmul (R, _V(tpmax.x, tpmax.y, tpmin.z)) + pref); + mesh->Box[4] = _V(tmul (R, _V(tpmin.x, tpmin.y, tpmax.z)) + pref); + mesh->Box[5] = _V(tmul (R, _V(tpmax.x, tpmin.y, tpmax.z)) + pref); + mesh->Box[6] = _V(tmul (R, _V(tpmin.x, tpmax.y, tpmax.z)) + pref); + mesh->Box[7] = _V(tmul (R, _V(tpmax.x, tpmax.y, tpmax.z)) + pref); + + mesh->MapVertices(TileManager2Base::pDev); + + return mesh; +} + +// ----------------------------------------------------------------------- + +VBMESH *Tile::CreateMesh_hemisphere (int grd, float *elev, double globelev) +{ + const int texres = 512; + double radius = mgr->obj_size + globelev; + double eradius, slat, clat, slng, clng; + VECTOR3 pos, nml; + + // Allocate memory for the vertices and indices + int nVtx = (grd-1)*(grd+1)+2; + int nIdx = 6*(grd*(grd-2)+grd); + VERTEX_2TEX *Vtx = g_pMemgr_vtx->New(nVtx); + WORD* Idx = g_pMemgr_w->New(nIdx); + + // Counters + WORD x, y, nvtx = 0, nidx = 0; + VERTEX_2TEX *vtx = Vtx; + WORD *idx = Idx; + + // Angle deltas for constructing the sphere's vertices + double fDAng = PI / grd; + double lng, lat = fDAng; + DWORD x1 = grd; + DWORD x2 = x1+1; + FLOAT du = 0.5f/(FLOAT)texres; + FLOAT a = (1.0f-2.0f*du)/(FLOAT)x1; + + // Make the middle of the sphere + for (y = 1; y < grd; y++) { + slat = sin(lat), clat = cos(lat); + FLOAT tv = (float)(lat/PI); + + for (x = 0; x < x2; x++) { + lng = x*fDAng - PI; // subtract Pi to wrap at +-180° + if (ilng) lng += PI; + slng = sin(lng), clng = cos(lng); + eradius = radius + globelev; // radius including node elevation + if (elev) eradius += (double)elev[(grd+1-y)*TILE_ELEVSTRIDE + x+1]; + nml = _V(slat*clng, clat, slat*slng); + pos = nml*eradius; + vtx->x = float(pos.x); vtx->nx = float(nml.x); + vtx->y = float(pos.y); vtx->ny = float(nml.y); + vtx->z = float(pos.z); vtx->nz = float(nml.z); + FLOAT tu = a*(FLOAT)x + du; + vtx->tu0 = tu; // vtx->tu1 = tu; + vtx->tv0 = tv; // vtx->tv1 = tv; + vtx++; + nvtx++; + } + lat += fDAng; + } + + for (y = 0; y < grd-2; y++) { + for (x = 0; x < x1; x++) { + *idx++ = (WORD)( (y+0)*x2 + (x+0) ); + *idx++ = (WORD)( (y+0)*x2 + (x+1) ); + *idx++ = (WORD)( (y+1)*x2 + (x+0) ); + *idx++ = (WORD)( (y+0)*x2 + (x+1) ); + *idx++ = (WORD)( (y+1)*x2 + (x+1) ); + *idx++ = (WORD)( (y+1)*x2 + (x+0) ); + nidx += 6; + } + } + // Make top and bottom + WORD wNorthVtx = nvtx; + eradius = radius + globelev; + if (elev) { + double mn = 0.0; + for (x = 0; x < x2; x++) mn += (double)elev[TILE_ELEVSTRIDE*(grd+1) + x+1]; + eradius += mn/x2; + } + nml = _V(0,1,0); + pos = nml*eradius; + vtx->x = float(pos.x); vtx->nx = float(nml.x); + vtx->y = float(pos.y); vtx->ny = float(nml.y); + vtx->z = float(pos.z); vtx->nz = float(nml.z); + vtx->tu0 = 0.5f; // vtx->tu1 = 0.5f; + vtx->tv0 = 0.0f; // vtx->tv1 = 0.0f; + vtx++; + nvtx++; + WORD wSouthVtx = nvtx; + + + eradius = radius + globelev; + if (elev) { + double mn = 0.0; + for (x = 0; x < x2; x++) mn += (double)elev[TILE_ELEVSTRIDE + x+1]; + eradius += mn/x2; + } + nml = _V(0,-1,0); + pos = nml*eradius; + vtx->x = float(pos.x); vtx->nx = float(nml.x); + vtx->y = float(pos.y); vtx->ny = float(nml.y); + vtx->z = float(pos.z); vtx->nz = float(nml.z); + vtx->tu0 = 0.5f; // vtx->tu1 = 0.5f; + vtx->tv0 = 1.0f; // vtx->tv1 = 1.0f; + vtx++; + nvtx++; + + for (x = 0; x < x1; x++) { + WORD p1 = wSouthVtx; + WORD p2 = (WORD)( (grd-2)*x2 + (x+0) ); + WORD p3 = (WORD)( (grd-2)*x2 + (x+1) ); + + *idx++ = p1; + *idx++ = p2; + *idx++ = p3; + nidx += 3; + } + + for (x = 0; x < x1; x++) { + WORD p1 = wNorthVtx; + WORD p2 = (WORD)( (0)*x2 + (x+0) ); + WORD p3 = (WORD)( (0)*x2 + (x+1) ); + + *idx++ = p1; + *idx++ = p3; + *idx++ = p2; + nidx += 3; + } + + // regenerate normals for terrain + if (elev) { + double dy, dz, dydz, nx1, ny1, nz1; + int en; + dy = radius * PI/grd; // y-distance between vertices + vtx = Vtx; + for (y = 1; y < grd; y++) { + lat = PI05-y*fDAng; + slat = sin(lat), clat = cos(lat); + dz = radius * PI*cos(lat) / grd; // z-distance between vertices on unit sphere + dydz = dy*dz; + for (x = 0; x < x2; x++) { + lng = x*fDAng; + if (!ilng) lng -= PI; + slng = sin(lng), clng = cos(lng); + en = (grd+1-y)*TILE_ELEVSTRIDE + x+1; + VECTOR3 nml = {2.0*dydz, dz*(elev[en-TILE_ELEVSTRIDE]-elev[en+TILE_ELEVSTRIDE]), dy*(elev[en-1]-elev[en+1])}; + normalise(nml); + // rotate into place + nx1 = nml.x*clat - nml.y*slat; + ny1 = nml.x*slat + nml.y*clat; + nz1 = nml.z; + vtx->nx = (float)(nx1*clng - nz1*slng); + vtx->ny = (float)(ny1); + vtx->nz = (float)(nx1*slng + nz1*clng); + vtx++; + } + } + } + + // create the mesh + VBMESH *mesh = new VBMESH(mgr); + mesh->vtx = Vtx; + mesh->nv = nVtx; + mesh->idx = Idx; + mesh->nf = nIdx/3; + mesh->MapVertices (TileManager2Base::pDev); + return mesh; +} + +// ======================================================================= +// ======================================================================= + +int TileLoader::nqueue = 0; +int TileLoader::queue_in = 0; +int TileLoader::queue_out = 0; +HANDLE TileLoader::hLoadMutex = 0; +struct TileLoader::QUEUEDESC TileLoader::queue[MAXQUEUE2] = {0}; + +TileLoader::TileLoader (const oapi::vkClient *gclient) + : gc(gclient) + , hStopThread(CreateEvent(NULL, FALSE, FALSE, NULL)) + , load_frequency(Config->PlanetLoadFrequency) +{ + DWORD id; + + // Initialize statics + nqueue = queue_in = queue_out = 0; + hLoadMutex = CreateMutex (0, FALSE, NULL); + hLoadThread = CreateThread (NULL, 32768, Load_ThreadProc, this, 0, &id); +} + +// ----------------------------------------------------------------------- + +TileLoader::~TileLoader () +{ + if (hLoadThread) LogErr("TileLoader() Not Yet ShutDown()"); + TerminateLoadThread(); + CloseHandle (hLoadMutex); + hLoadMutex = NULL; +} + +// ----------------------------------------------------------------------- + +bool TileLoader::ShutDown() +{ + if (hLoadThread) { + TerminateLoadThread(); + return true; + } + return false; +} + +// ----------------------------------------------------------------------- + +void TileLoader::TerminateLoadThread() +{ + if (hLoadThread) { + // Signal thread to stop and wait for it to happen + SetEvent(hStopThread); + WaitForSingleObject(hLoadThread, INFINITE); //4000); + // Clean up for next run + ResetEvent(hStopThread); + CloseHandle(hLoadThread); + hLoadThread = NULL; + } +} + +// ----------------------------------------------------------------------- + +bool TileLoader::LoadTileAsync (Tile *tile) +{ + // Note: the queue should probably be sorted according to tile level + + int i, j; + + // Check if request is already present + for (i = 0; i < nqueue; i++) { + j = (i+queue_out) % MAXQUEUE2; + if (queue[j].tile == tile) + return false; + } + + if (nqueue == MAXQUEUE2) { // queue full + int maxlvl, maxlvl_idx = 0; + for (i = 1, maxlvl = queue[0].tile->lvl; i < nqueue; i++) + if (queue[i].tile->lvl > maxlvl) + maxlvl = queue[i].tile->lvl, maxlvl_idx = i; + if (maxlvl > tile->lvl) { // replace the highest level queued tile (load from lowest to highest) + queue[maxlvl_idx].tile->state = Tile::Invalid; + queue[maxlvl_idx].tile = tile; + tile->state = Tile::InQueue; + } else { + tile->state = Tile::Invalid; + } + return false; + } + + // add tile to load queue + QUEUEDESC *qd = queue+queue_in; + qd->tile = tile; + tile->state = Tile::InQueue; + nqueue++; + queue_in = (queue_in+1) % MAXQUEUE2; + return true; +} + +// ----------------------------------------------------------------------- + +void TileLoader::Unqueue (TileManager2Base *mgr) +{ + bool locked = false; // whether a mutex semaphore was taken + + bool found; + do { + int i, j; + found = false; + for (i = 0; i < nqueue; ++i) { + j = (queue_out+i) % MAXQUEUE2; + if (queue[j].tile->mgr == mgr) { + if (!locked) { + WaitForMutex(); + locked = true; + } + if (i) { + queue[j].tile = queue[queue_out].tile; + } + queue_out = (queue_out+1) % MAXQUEUE2; + nqueue--; + found = true; + break; + } + } + } while (found); + + if (locked) { + ReleaseMutex(); // ...if mutex was taken + } +} + +// ----------------------------------------------------------------------- + +bool TileLoader::Unqueue (Tile *tile) +{ + if (tile->state != Tile::InQueue) return false; + + int i, j; + for (i = 0; i < nqueue; i++) { + j = (queue_out+i) % MAXQUEUE2; + if (queue[j].tile == tile) { + WaitForMutex (); + if (i) { + queue[j].tile = queue[queue_out].tile; + } + queue_out = (queue_out+1) % MAXQUEUE2; + nqueue--; + ReleaseMutex (); + return true; + } + } + return false; +} + +// ----------------------------------------------------------------------- +#ifdef UNDEF +DWORD WINAPI TileLoader::Load_ThreadProc (void *data) +{ + TileLoader *loader = (TileLoader*)data; + DWORD idle = 1000/loader->load_frequency; + Tile *tile; + bool load; + + while (WAIT_OBJECT_0 != WaitForSingleObject(loader->hStopThread, idle)) { + + WaitForMutex(); + + if (load = (nqueue > 0)) { + tile = queue[queue_out].tile; + _ASSERT(TILE_STATE_OK(tile)); // THIS IS TRIGGERED OFTEN + if (tile->state == Tile::InQueue) { + tile->state = Tile::Loading; // lock tile and its ancestor tree + } else { + load = false; + } + queue_out = (queue_out+1) % MAXQUEUE2; // remove from queue + nqueue--; + } + + ReleaseMutex(); + + if (load) { + tile->PreLoad(); // Preload data from harddrive to system memory without a Mutex + + WaitForMutex(); + tile->Load(); // Create the actual tile texture from a pre-loaded data + tile->state = Tile::Inactive; // unlock tile + ReleaseMutex(); + + } + else { + Sleep(1); + } + } + return 0; +} +#endif + +// ----------------------------------------------------------------------- + +DWORD WINAPI TileLoader::Load_ThreadProc (void *data) +{ + const int tile_packet_size = 8; // max number of tiles to process from queue + TileLoader *loader = (TileLoader*)data; + DWORD idle = 1000/loader->load_frequency; + Tile *tile[tile_packet_size]; + int nload, i; + + LogAlw("TileLoader::Load thread started"); + + bool bFirstRun = true; + while (bFirstRun || WAIT_OBJECT_0 != WaitForSingleObject(loader->hStopThread, idle)) + { + bFirstRun = false; + + WaitForMutex (); + for (nload = 0; nqueue > 0 && nload < tile_packet_size; nload++) { + tile[nload] = queue[queue_out].tile; + tile[nload]->state = Tile::Loading; // lock tile and its ancestor tree + queue_out = (queue_out+1) % MAXQUEUE2; // remove from queue + nqueue--; + } + ReleaseMutex (); + + if (nload) { + + for (i = 0; i < nload; i++) { + tile[i]->PreLoad(); // load/create the tile + tile[i]->Load(); + } + + WaitForMutex(); + for (i = 0; i < nload; i++) { + tile[i]->state = Tile::Inactive; // unlock tile + } + ReleaseMutex (); + } else { + Sleep (idle); + } + } + + LogAlw("TileLoader::Load thread terminated"); + return 0; +} + +// ======================================================================= +// ======================================================================= + +TileManager2Base::ConfigPrm TileManager2Base::cprm = { + 2, // elevInterpol + false, // bSpecular + false, // bLights + false, // bCloudShadow + 0.5 // lightfac +}; +oapi::vkClient* TileManager2Base::gc = NULL; +LPDIRECT3DDEVICE9 TileManager2Base::pDev = NULL; +TileLoader *TileManager2Base::loader = NULL; +double TileManager2Base::resolutionBias = 4.0; +double TileManager2Base::resolutionScale = 1.0; +bool TileManager2Base::bTileLoadThread = true; +HFONT TileManager2Base::hFont = NULL; +LPDIRECT3DTEXTURE9 TileManager2Base::hOcean = NULL; +LPDIRECT3DTEXTURE9 TileManager2Base::hCloudMicro = NULL; +LPDIRECT3DTEXTURE9 TileManager2Base::hCloudMicroNorm = NULL; + +// ----------------------------------------------------------------------- + +TileManager2Base::TileManager2Base (vPlanet *vplanet, int _maxres, int _gridres) +: vp(vplanet), gridRes(_gridres), ElevMode(eElevMode::DontCare), ElevModeLvl(0) +{ + // set persistent parameters + TilesLoaded = 0; + prm.maxlvl = max (0, _maxres-4); + obj = vp->Object(); + obj_size = oapiGetSize (obj); + oapiGetObjectName (obj, cbody_name, 256); + emgr = oapiElevationManager(obj); + elevRes = *(double*)oapiGetObjectParam (obj, OBJPRM_PLANET_ELEVRESOLUTION); + LogClr("Teal", "Planet ElevRes %s = %g", vplanet->GetName(), elevRes); + + char path[1024]; + gc->PlanetTexturePath(cbody_name, path); + m_dataRootDir = path; +} + +// ----------------------------------------------------------------------- + +TileManager2Base::~TileManager2Base () +{ + LogAlw("Deleting TileManagerBase %s ...", _PTR(this)); + if (loader) loader->Unqueue(this); +} + +// ----------------------------------------------------------------------- + +void TileManager2Base::GlobalInit (class oapi::vkClient *gclient) +{ + gc = gclient; + pDev = gc->GetDevice(); + + resolutionBias = 4.0 + Config->LODBias; + DWORD w, h; + gc->clbkGetViewportSize (&w, &h); + resolutionScale = 1400.0 / (double)h; + cprm.bSpecular = *(bool*)gclient->GetConfigParam (CFGPRM_SURFACEREFLECT); + cprm.bLights = *(bool*)gclient->GetConfigParam (CFGPRM_SURFACELIGHTS); + cprm.bCloudShadow = *(bool*)gclient->GetConfigParam (CFGPRM_CLOUDSHADOWS); + cprm.lightfac = *(double*)gclient->GetConfigParam (CFGPRM_SURFACELIGHTBRT); + cprm.elevMode = *(int*)gclient->GetConfigParam (CFGPRM_ELEVATIONMODE); + cprm.tileLoadFlags = Config->PlanetTileLoadFlags; + bTileLoadThread = *(bool*)gclient->GetConfigParam(CFGPRM_TILELOADTHREAD); + + loader = new TileLoader (gc); + + hFont = CreateFont(42, 0, 0, 0, 600, false, false, 0, 0, 0, 2, CLEARTYPE_QUALITY, 49, "Arial"); + + char name[MAX_PATH]; + + if (gc->TexturePath("D3D9Ocean.dds", name)) D3DXCreateTextureFromFileA(pDev, name, &hOcean); + if (gc->TexturePath("cloud1.dds", name)) D3DXCreateTextureFromFileA(pDev, name, &hCloudMicro); + if (gc->TexturePath("cloud1_norm.dds", name)) D3DXCreateTextureFromFileA(pDev, name, &hCloudMicroNorm); +} + +// ----------------------------------------------------------------------- + +bool TileManager2Base::ShutDown() +{ + return loader->ShutDown(); +} + +// ----------------------------------------------------------------------- + +void TileManager2Base::GlobalExit () +{ + DeleteObject(hFont); hFont = NULL; + SAFE_RELEASE(hOcean); + SAFE_RELEASE(hCloudMicro); + SAFE_RELEASE(hCloudMicroNorm); + delete loader; +} + +// ----------------------------------------------------------------------- + +void TileManager2Base::SetRenderPrm (MATRIX4 &dwmat, double prerot, bool use_zbuf, const vPlanet::RenderPrm &rprm) +{ + const double minalt = max(0.002,rprm.horizon_excess); + VECTOR3 obj_pos, cam_pos; + + prm.rprm = &rprm; + + // set up the parameter structure + oapiGetRotationMatrix(obj, &prm.grot); // planet's rotation matrix + oapiGetGlobalPos (obj, &obj_pos); // planet position + cam_pos = GetScene()->GetCameraGPos(); // camera position + + if (prerot) { + double srot = sin(prerot), crot = cos(prerot); + MATRIX4 RM4 = { crot,0,-srot,0, 0,1,0,0, srot,0,crot,0, 0,0,0,1 }; + prm.dwmat = mul(RM4,dwmat); + prm.grot = mul (prm.grot, _M(crot,0,srot, 0,1,0, -srot,0,crot)); + } else { + prm.dwmat = dwmat; + } + prm.dwmat_tmp = prm.dwmat; + prm.cpos = obj_pos-cam_pos; + prm.cdir = tmul (prm.grot, -prm.cpos); // camera's direction in planet frame + double cdist = length(prm.cdir); + prm.cdist = cdist / obj_size; // camera's distance in units of planet radius + normalise (prm.cdir); + prm.sdir = tmul (prm.grot, -obj_pos); // sun's direction in planet frame + normalise (prm.sdir); + // Add 5km threshold to allow slight camera movement with out causing surface tiles to unload + prm.viewap = acos (1.0/(max ((cdist+5e3) / obj_size, 1.0+minalt))); + prm.scale = 1.0; +} + +// ----------------------------------------------------------------------- + +MATRIX4 TileManager2Base::WorldMatrix(Tile *tile) +{ + int lvl = tile->lvl; + int ilng = tile->ilng; + int ilat = tile->ilat; + int nlng = 2 << lvl; + int nlat = 1 << lvl; + return WorldMatrix(ilng, nlng, ilat, nlat); +} + +// ----------------------------------------------------------------------- + +MATRIX4 TileManager2Base::WorldMatrix (int ilng, int nlng, int ilat, int nlat) +{ + if (nlat < 2) { // render full sphere + return prm.dwmat; + } + + double lat, lng = PI2 * (double)ilng/(double)nlng + PI; // add pi so texture wraps at +-180° + double slng = sin(lng), clng = cos(lng); + MATRIX4 lrot = {clng,0,slng,0, 0,1.0,0,0, -slng,0,clng,0, 0,0,0,1.0}; + + if (nlat <= 8) { // rotate patch into place + return mul(lrot,prm.dwmat); + } else { // shift, scale and rotate + bool north = (ilat < nlat/2); + if (north) lat = PI * (double)(nlat/2-ilat-1)/(double)nlat; + else lat = PI * (double)(nlat/2-ilat)/(double)nlat; + double s = obj_size; + double dx = s*cos(lng)*cos(lat); // the offsets between sphere centre and tile corner + double dy = s*sin(lat); + double dz = s*sin(lng)*cos(lat); + prm.dwmat_tmp.m41 = (dx*prm.grot.m11 + dy*prm.grot.m12 + dz*prm.grot.m13 + prm.cpos.x) * (float)prm.scale; + prm.dwmat_tmp.m42 = (dx*prm.grot.m21 + dy*prm.grot.m22 + dz*prm.grot.m23 + prm.cpos.y) * (float)prm.scale; + prm.dwmat_tmp.m43 = (dx*prm.grot.m31 + dy*prm.grot.m32 + dz*prm.grot.m33 + prm.cpos.z) * (float)prm.scale; + return mul(lrot,prm.dwmat_tmp); + } +} diff --git a/OVP/VulkanClient/Tilemgr2.h b/OVP/VulkanClient/Tilemgr2.h new file mode 100644 index 000000000..dc54d4f47 --- /dev/null +++ b/OVP/VulkanClient/Tilemgr2.h @@ -0,0 +1,498 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// ======================================================================= +// tilemgr2.h +// Rendering of planetary surfaces using texture tiles at +// variable resolutions (new version). +// ======================================================================= + +#ifndef __TILEMGR2_H +#define __TILEMGR2_H + +#include "Client.h" +#include "Util.h" +#include "VPlanet.h" +#include "Spherepatch.h" +#include "Pad.h" +#include "Qtree.h" +#include "ZTreeMgr.h" +#include +#include +#include + +#define NPOOLS 32 +#define MAXQUEUE2 20 + +#define TILE_VALID 0x0001 +#define TILE_ACTIVE 0x0002 + +#define TILE_FILERES 256 +#define TILE_ELEVSTRIDE (TILE_FILERES+3) + +#ifdef _DEBUG +// Debugging helper +#define TILE_STATE_OK(t) (t->state == Tile::Invalid \ + || t->state == Tile::InQueue \ + || t->state == Tile::Loading \ + || t->state == Tile::Inactive \ + || t->state == Tile::Active \ + || t->state == Tile::Invisible \ + || t->state == Tile::ForRender) +#endif // _DEBUG + +// ======================================================================= +// Type definitions + + +#pragma pack(push,1) + +struct ELEVFILEHEADER { // file header for patch elevation data file + char id[4]; // ID string + version ('E','L','E',1) + int hdrsize; // header size (76 expected) + int dtype; // data format (0=flat, no data block; 8=uint8; -8=int8; 16=uint16; -16=int16) + int xgrd, ygrd; // data grid size (259 x 259 expected) + int xpad, ypad; // horizontal, vertical padding width (1, 1 expected) + double scale; // data scaling factor (1.0 expected) + double offset; // data offset (elevation = raw value * scale + offset) + double latmin, latmax; // latitude range [rad] + double lngmin, lngmax; // longitude range [rad] + double emin, emax, emean; // min, max, mean elevation [m] +}; + +#pragma pack(pop) + + +typedef struct { + float tumin, tumax; + float tvmin, tvmax; +} TEXCRDRANGE2; + +typedef union { + VECTOR4 vec; + struct { + double minlng; ///< Left - + double maxlat; ///< Top + + double maxlng; ///< Right + + double minlat; ///< Bottom - + }; +} TILEBOUNDS; + +bool FileExists(const char* path); + +// ======================================================================= + +/** + * \brief Surface tile texture class. + * + * A Tile is the basic (visual) representation of a "planetary" surface + * (of a planet, moon or asteroid). + * There should be no simple tile objects, but derived classes. + */ +class Tile +{ + friend class TileManager2Base; + friend class TileLoader; + +public: + Tile (TileManager2Base *_mgr, int _lvl, int _ilat, int _ilng); + virtual ~Tile (); + + inline int State() const { return state; } + inline int Level() const { return lvl; } + inline void GetIndex(int *lng, int *lat) const { *lng = ilng, *lat = ilat; } + inline bool HasOwnTex() const { return owntex; } + inline bool HasOwnElev() const { return has_elevfile; } + inline void GetWorldMatrix(void *pOut) const { memcpy(pOut, &mWorld, sizeof(FMATRIX4)); } + + bool PreDelete(); + // Prepare tile for deletion. Return false if tile is locked + + bool InView (const MATRIX4 &transform); + // tile in view of camera, given by transformation matrix 'transform'? + + VECTOR3 Centre() const; + // Returns the direction of the tile centre from the planet centre in local planet coordinates + + void Extents (double *latmin, double *latmax, double *lngmin, double *lngmax) const; + // Return the latitude/longitude extents of the tile + + bool IsPointInTile(double lng, double lat); + + virtual void MatchEdges () {} + // Match edges with neighbour tiles + + float GetBoundingSphereRad() const; + FVECTOR3 GetBoundingSpherePos() const; + bool Pick(const FMATRIX4* pW, const FVECTOR3 *vDir, TILEPICK &result); + FVECTOR4 GetTexRangeDX (const TEXCRDRANGE2 *subrange) const; + inline const TEXCRDRANGE2 *GetTexRange () const { return &texrange; } + // Returns the tile's texture coordinate range + + bool GetParentMicroTexRange(TEXCRDRANGE2 *subrange); + bool GetParentSubTexRange(TEXCRDRANGE2 *subrange); + bool GetParentOverlayRange(TEXCRDRANGE2 *subrange); + + // Returns the texture range that allows to access the appropriate subregion of the + // parent's texture + + inline LPDIRECT3DTEXTURE9 Tex() { return tex; } + inline const LPDIRECT3DTEXTURE9 Tex() const { return tex; } + + enum TileState { + Invalid = 0x0000, // tile data have not been loaded/created yet + InQueue = 0x0004, // queued for asynchronous load + Loading = 0x0008, // in the process of being loaded + Inactive = TILE_VALID, // valid data, but not in active part of quadtree (cached) + Active = TILE_VALID | TILE_ACTIVE, // active, but not rendered itself (ancestor of rendered tiles) + Invisible = TILE_VALID | TILE_ACTIVE | 0x0004, // active, but outside field of view + ForRender = TILE_VALID | TILE_ACTIVE | 0x0008 // rendered tile + }; + +protected: + virtual Tile *getParent() const = 0; + + virtual void Render (); + virtual void StepIn () {} + + virtual bool IsElevated() { return false; } + + virtual double GetMinElev() const = 0; + virtual double GetMaxElev() const = 0; + virtual double GetMeanElev() const = 0; + + + /** + * \brief Preloads a surface tile data into a system memory from a tile loader thread + */ + virtual void PreLoad() = 0; + + /** + * \brief Construct a surface tile textures from a preloaded data /see virtual bool PreLoad() + */ + virtual void Load () = 0; + + bool CreateTexture(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DTEXTURE9 pPre, LPDIRECT3DTEXTURE9 *pTex); + bool LoadTextureFile(const char *path, LPDIRECT3DTEXTURE9 *pPre); + bool LoadTextureFromMemory(void *data, DWORD ndata, LPDIRECT3DTEXTURE9 *pPre); + + VBMESH *CreateMesh_quadpatch (int grdlat, int grdlng, float *elev=0, double elev_scale = 1.0, double globelev=0.0, + const TEXCRDRANGE2 *range=0, bool shift_origin=false, VECTOR3 *shift=0, double bb_excess=0.0); + // Creates a quadrilateral patch mesh + + VBMESH *CreateMesh_hemisphere (int grd, float *elev=0, double globelev=0.0); + // Creates a hemisphere mesh for eastern or western hemisphere at resolution level 4 + + TileManager2Base *mgr; // the manager this tile is associated with + int lvl; // tile resolution level + int ilat; // latitude index + int ilng; // longitude index + int imicrolvl; // Micro texture level + LPDIRECT3DTEXTURE9 tex; // diffuse surface texture + LPDIRECT3DTEXTURE9 overlay; + bool bMipmaps; // create mipmaps for the tile + bool owntex; // true: tile owns the texture, false: tile uses ancestor subtexture + bool ownoverlay; + bool has_elevfile; // true if the elevation data for this tile were read from file + TEXCRDRANGE2 texrange; // texture coordinate subrange (if using ancestor subtexture) + TEXCRDRANGE2 microrange; // texture coordinate subrange (if using ancestor subtexture) + TEXCRDRANGE2 overlayrange; + VBMESH *mesh; // vertex-buffered tile mesh + VECTOR3 cnt; // tile centre in local planet coords + VECTOR3 vtxshift; // tile frame shift of origin from planet centre + bool edgeok; // edges have been checked in this frame + TileState state; // tile load/active/render state flags + int lngnbr_lvl, latnbr_lvl, dianbr_lvl; // neighbour levels to which edges have been adapted + double last_used; // time when this tile was last used as a part of active tile chain + float width; // tile width [rad] (widest section i.e base) + float height; // tile height [rad] + float tgtscale; + +public: + FMATRIX4 mWorld; + MATRIX4 dmWorld; + TILEBOUNDS bnd; +}; + +// ======================================================================= + +/** + * \brief Planetary surface tile loader template. + * + * Planetary surface tile loader template class. + */ +class TileLoader { + template friend class TileManager2; + +public: + explicit TileLoader (const oapi::vkClient *gclient); + ~TileLoader (); + bool LoadTileAsync (Tile *tile); + bool ShutDown (); + + bool Unqueue (Tile *tile); + // remove a tile from the load queue (caller must own hLoadMutex) + + void Unqueue (TileManager2Base *mgr); + // removes all tiles of a manager from the load queue (caller must own hLoadMutex) + + inline static DWORD WaitForMutex() { return ::WaitForSingleObject (hLoadMutex, INFINITE); } + inline static BOOL ReleaseMutex() { return ::ReleaseMutex (hLoadMutex); } + +private: + void TerminateLoadThread(); // Terminates the Load thread + + static struct QUEUEDESC { + Tile *tile; + } queue[MAXQUEUE2]; + + const oapi::vkClient *gc; // the client + static int nqueue, queue_in, queue_out; + HANDLE hLoadThread; // Load ThreadProc handle + HANDLE hStopThread; // Thread kill signal handle + static HANDLE hLoadMutex; + static DWORD WINAPI Load_ThreadProc (void*); + int load_frequency; +}; + +// ======================================================================= + + +namespace eElevMode { + const int DontCare = 0x0; + const int Elevated = 0x1; + const int Spherical = 0x2; + const int ForcedElevated = 0x3; // This can be really bad. Use only with small bodies +}; + + +/** + * \brief Base class for tile management classes. + * + * Rendering of planetary surfaces using texture tiles at + * variable resolutions (new version). + */ +class TileManager2Base +{ + friend class Tile; + +public: + /** + * \brief Global configuration parameters + */ + static struct ConfigPrm { + int elevMode; ///< elevation mode (0=none, 1=linear, 2=cubic) + bool bSpecular; ///< render specular surface reflections? + bool bLights; ///< render planet night lights? + bool bCloudShadow; ///< render cloud shadows? + double lightfac; ///< city light brightness factor + DWORD tileLoadFlags; ///< 0x0001: load tiles from directory tree + ///< 0x0002: load tiles from compressed archive + } cprm; + + /** + * \brief Global rendering parameters + */ + struct RenderPrm { + const vPlanet::RenderPrm *rprm; ///< render parameters inherited from the vPlanet object + int maxlvl; ///< max tile level + MATRIX4 dwmat; ///< planet world matrix, double precision + MATRIX4 dwmat_tmp; ///< modifiable planet world matrix, double precision + MATRIX4 dviewproj; ///< view+projection matrix, double precision + MATRIX3 grot; ///< planet rotation matrix + VECTOR3 cpos; ///< planet offset vector (in global frame) + VECTOR3 cdir; ///< camera direction from planet centre (in planet frame) + VECTOR3 sdir; ///< sun direction in local planet coordinates + double cdist; ///< camera distance from planet centre (in units of planet radii) + double viewap; ///< aperture of surface cap visible from camera pos + double scale; ///< scale factor + } prm; + + struct RenderStats { + int Elev; ///< Number of elevated tiles rendered + int Sphe; ///< Number of spherical tiles rendered + } elvstat, prevstat; + + int ElevMode, ElevModeLvl; + int TilesLoaded; + + /** + * \brief Constructs a new tile manager object + * \param vplanet planet instance pointer + * \param _maxres maximum resolution + */ + TileManager2Base (vPlanet *vplanet, int _maxres, int _gridres); + + /** + * \brief Destroys the tile manager object + */ + ~TileManager2Base (); + + /** + * \brief Set up global parameters shared by all instances + * \param gclient client instance pointer + */ + static void GlobalInit (class oapi::vkClient *gclient); + + /** + * \brief Release global parameters + */ + static void GlobalExit (); + + /** + * \brief Shutdown on loader instance + */ + static bool ShutDown (); + + static LPDIRECT3DDEVICE9 Dev() { return pDev; } + static HFONT GetDebugFont() { return hFont; } + + template + QuadTreeNode *FindNode (QuadTreeNode root[2], int lvl, int ilat, int ilng); + // Returns the node at the specified position, or 0 if it doesn't exist + + template + void DebugDump(QuadTreeNode* node); + + template + void ProcessNode (QuadTreeNode *node); + + template + void RenderNode (QuadTreeNode *node); + + template + void QueryTiles(QuadTreeNode *node, std::list &tiles); + + // v2 Labels interface ----------------------------------------------- + template + void RenderNodeLabels(QuadTreeNode *node, vkPad *skp, oapi::Font **labelfont, int *fontidx); + + void SetRenderPrm (MATRIX4 &dwmat, double prerot, bool use_zbuf, const vPlanet::RenderPrm &rprm); + + inline class Scene * GetScene() const { return gc->GetScene(); } + inline oapi::vkClient *GetClient() const { return gc; } + inline oapi::vkClient* Client() const { return gc; } + inline vPlanet *GetPlanet() { return vp; } + inline vPlanet* GetPlanet() const { return vp; } + + inline PlanetShader* GetShader() { return pShader; } + inline bool IsUsingZBuf() const { return bUseZ; } + inline const OBJHANDLE &Cbody() const { return obj; } + inline const ConfigPrm &Cprm() const { return cprm; } + inline const char *CbodyName() const { return cbody_name; } + inline const double CbodySize() const { return obj_size; } + inline const ELEVHANDLE ElevMgr() const { return emgr; } + inline const int GridRes() const { return gridRes; } + inline const double ElevRes() const { return elevRes; } + inline OBJHANDLE GetHandle() const { return obj; } + + /// \brief Return the root directory containing the body's texture data (surface, elevation, mask, cloud tiles) + inline const std::string& DataRootDir() const { return m_dataRootDir; } + +protected: + MATRIX4 WorldMatrix (int ilng, int nlng, int ilat, int nlat); + MATRIX4 WorldMatrix(Tile *tile); + + template + QuadTreeNode *LoadChildNode (QuadTreeNode *node, int idx); + // loads one of the four subnodes of 'node', given by 'idx' + + double obj_size; // planet radius + static TileLoader *loader; + vPlanet *vp; // the planet visual + PlanetShader* pShader; + std::string m_dataRootDir; // the root directory (usually ending in the cbody's name) for all tile data (textures, elevations, etc.) + bool bUseZ; + +private: + OBJHANDLE obj; // the planet object + char cbody_name[256]; + ELEVHANDLE emgr; // elevation data query handle + int gridRes; // mesh grid resolution. must be multiple of 2. Default: 64 for surfaces, 32 for clouds + double elevRes; // target elevation resolution + + static oapi::vkClient* gc; + static LPDIRECT3DDEVICE9 pDev; + static HFONT hFont; + static double resolutionBias; + static double resolutionScale; + static bool bTileLoadThread; // load tiles on separate thread +public: + static LPDIRECT3DTEXTURE9 hOcean; + static LPDIRECT3DTEXTURE9 hCloudMicro; + static LPDIRECT3DTEXTURE9 hCloudMicroNorm; +}; + +// ======================================================================= + +template +class TileManager2: public TileManager2Base { + friend class Tile; + +public: + TileManager2 (vPlanet *vplanet, int _maxres, int _gridres); + ~TileManager2 (); + + void Render (MATRIX4 &dwmat, bool use_zbuf, const vPlanet::RenderPrm &rprm); + + int GetElevation(double lng, double lat, double *elev, FVECTOR3 *nrm, SurfTile **cache); + void Unload(int lvl); + + void Pick(FVECTOR3 &vRay, TILEPICK *pPick); + + // v2 Labels interface ----------------------------------------------- + void CreateLabels(); + void DeleteLabels(); + void RenderLabels(vkPad *skp, oapi::Font **labelfont, int *fontidx); + void SetSubtreeLabels(QuadTreeNode *node, bool activate); + + QuadTreeNode *FindNode (int lvl, int ilat, int ilng) + { return TileManager2Base::FindNode (tiletree, lvl, ilat, ilng); } + // Returns the node at the specified position, or 0 if it doesn't exist + + Tile *SearchTile (double lng, double lat, int maxlvl, bool bOwntex) const; + + inline TileType *GlobalTile (int lvl) const { return (lvl >= -3 && lvl < 0 ? globtile[lvl + 3] : NULL); } +// { return globtile[lvl]; } @todo: Is that ( above) OK for us? + + // Returns a low-res global tile + + int Coverage (double latmin, double latmax, double lngmin, double lngmax, int maxlvl, const Tile **tbuf, int nt) const; + // fills tbuf with a list of tiles up to maxlvl currently covering the area latmin,latmax,lngmin,lngmax + // nt is the length of tbuf. Return value is the number of tiles copied into tbuf. + // If the number of covering tiles is larger than nt, return value is -1. In that case, no tiles are copied. + // The tile pointers are only valid for the current render pass. They are not guaranteed to exist any more + // after the next call to Render. + + inline ZTreeMgr *ZTreeManager (int i) const { return (i tiletree[2]; // quadtree roots for western and eastern hemisphere + + ZTreeMgr **treeMgr; // access to tile layers in compressed archives + int ntreeMgr; + + bool *hasIndividualFiles; // whether to check for individual texture files (per Layer-type) flags + + void CheckCoverage (const QuadTreeNode *node, double latmin, double latmax, double lngmin, double lngmax, int maxlvl, const Tile **tbuf, int nt, int *nfound) const; + + void LoadZTrees(); + void InitHasIndividualFiles(); + Tile *SearchTileSub (const QuadTreeNode *node, double lng, double lat, int maxlvl, bool bOwntex) const; +}; + +#endif // !__TILEMGR2_H diff --git a/OVP/VulkanClient/Tilemgr2_imp.hpp b/OVP/VulkanClient/Tilemgr2_imp.hpp new file mode 100644 index 000000000..a574d2fcb --- /dev/null +++ b/OVP/VulkanClient/Tilemgr2_imp.hpp @@ -0,0 +1,428 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// ======================================================================= +// tilemgr2_imp.hpp +// Rendering of planetary surfaces using texture tiles at +// variable resolutions (new version). +// ======================================================================= + +#ifndef __TILEMGR2_IMP_HPP +#define __TILEMGR2_IMP_HPP + +#include "Tilemgr2.h" +#include "DebugControls.h" + +// ----------------------------------------------------------------------- + +template +QuadTreeNode *TileManager2Base::FindNode (QuadTreeNode root[2], int lvl, int ilng, int ilat) +{ + int i, sublat, sublng, subidx; + + // wrap at longitude += 180 + int nlng = 2 << lvl; + if (ilng < 0) ilng += nlng; + else if (ilng >= nlng) ilng -= nlng; + + // Find the level-0 root + QuadTreeNode *node = root + ((ilng >> lvl) & 1); + for (i = lvl-1; i >= 0; i--) { + if (node->Entry()->state == Tile::ForRender) return node; + if (node->Entry()->state == Tile::Invisible) return 0; // tile invisible + sublat = (ilat >> i) & 1; + sublng = (ilng >> i) & 1; + subidx = sublat*2+sublng; + if (node->Child(subidx) && (node->Child(subidx)->Entry()->state & TILE_ACTIVE)) { + node = node->Child(subidx); + } else { + break; + } + } + return node; +} + +// ----------------------------------------------------------------------- + +template +QuadTreeNode *TileManager2Base::LoadChildNode (QuadTreeNode *node, int idx) +{ + TileType *parent = node->Entry(); + int lvl = parent->lvl+1; + int ilat = parent->ilat*2 + idx/2; + int ilng = parent->ilng*2 + idx%2; + TileType *tile = new TileType (this, lvl, ilat, ilng); + QuadTreeNode *child = node->AddChild (idx, tile); + if (bTileLoadThread) + loader->LoadTileAsync (tile); + else { + tile->PreLoad(); + tile->Load(); + tile->state = Tile::Inactive; + } + return child; +} + +// ----------------------------------------------------------------------- + +template +void TileManager2Base::QueryTiles(QuadTreeNode *node, std::list &tiles) +{ +/*Tile *tile = node->Entry(); + for (int i = 0; i < 4; i++) { + if (node->Child(i)) { + if (node->Child(i)->Entry()) QueryTiles(node->Child(i), tiles); + } else tiles.push_back(tile); + } +*/ + Tile *tile = node->Entry(); + if (tile->state == Tile::ForRender) tiles.push_back(tile); + else if (tile->state == Tile::Active) { + for (int i = 0; i < 4; i++) { + if (node->Child(i)) { + if (node->Child(i)->Entry() && (node->Child(i)->Entry()->state & TILE_ACTIVE)) QueryTiles(node->Child(i), tiles); + } + } + } +} + +// ----------------------------------------------------------------------- + +template +void TileManager2Base::DebugDump(QuadTreeNode* node) +{ + Tile* tile = node->Entry(); + oapiWriteLogV("Tile[0x%X] OwnTex=%u, Mesh=%u, state=%u, lvl=%u", tile, UINT(tile->owntex), UINT(tile->mesh != 0), UINT(tile->state), tile->lvl); + for (int i = 0; i < 4; i++) { + auto c = node->Child(i); + if (c) DebugDump(c); + } +} + +// ----------------------------------------------------------------------- + +template +void TileManager2Base::ProcessNode (QuadTreeNode *node) +{ + static const double res_scale = 1.1; // resolution scale with distance + + const Scene *scene = GetScene(); + + Tile *tile = node->Entry(); + int lvl = tile->lvl; + int ilng = tile->ilng; + int ilat = tile->ilat; + int nlng = 2 << lvl; + int nlat = 1 << lvl; + bool bstepdown = true; + double bias = DebugControls::resbias; // 2 to 6, default 4 + if (ilat < nlat/6 || ilat >= nlat-nlat/6) { // lower resolution at the poles + bias -= 1.0; + if (ilat < nlat/12 || ilat >= nlat-nlat/12) + bias -= 1.0; + } + + bool bNoRelease = false; + + // Override TileDeletion for forced elevated rendering of asteroids/comets/small moons + if (ElevMode == eElevMode::ForcedElevated) bNoRelease = true; + + tile->dmWorld = WorldMatrix(ilng, nlng, ilat, nlat); + tile->mWorld = FMATRIX4(tile->dmWorld); + + if (bFreeze) { + for (int i = 0; i < 4; i++) { + auto child = node->Child(i); + if (child) ProcessNode(child); + } + return; + } + + tile->state = Tile::ForRender; + tile->edgeok = false; + + // check if patch is visible from camera position + VECTOR3 &cnt = tile->cnt; // tile centre in unit planet frame + static const double rad0 = sqrt(2.0)*PI05; + double rad = rad0/(double)nlat; + double alpha = acos (dotp (prm.cdir, cnt)); // angle between tile centre and camera from planet centre + double adist = alpha - rad; // angle between closest tile corner and camera + if (adist >= prm.viewap) { + if (lvl == 0) + bstepdown = false; // force render at lowest resolution + else { + if (scene->GetRenderPass() == RENDERPASS_MAINSCENE) node->DelChildren(); + tile->state = Tile::Invisible; + return; // no need to continue + } + } + + // Check if patch bounding box intersects viewport + MATRIX4 transform = mul (tile->dmWorld, prm.dviewproj); + if (!tile->InView (transform)) { + if (lvl == 0) + bstepdown = false; + else { + // Keep a tile allocated as long as the tile can be seen from a current camera position. + // We have multiple views and only the active (current) view is checked here. + tile->state = Tile::Invisible; + //return; Cannot return here, must check the tile tatget level and release childs if needed. + } + } + + int tgtres = -1; + + // Compute target resolution level based on tile distance + if (bstepdown) { + double tdist; + double erad = 1.0 + tile->GetMaxElev()/obj_size; // radius of unit sphere plus elevation + if (adist < 0.0) { // if we are above the tile, use altitude for distance measurement + tdist = prm.cdist - erad; + if (tdist < 0.0) tdist = 0.0; + } else { // use distance to closest tile edge + double h = erad*sin(adist); + double a = prm.cdist - erad*cos(adist); + double x = a*a + h*h; + tdist = (x > 0.0) ? sqrt(x) : 0.0; + } + + bias -= 2.0 * sqrt(max(0.0,adist) / prm.viewap); + int maxlvl = prm.maxlvl; + + double maxtiles = 1200.0; + if (Config->MaxTiles == 0) maxtiles = 600.0; + if (Config->MaxTiles == 2) maxtiles = 2400.0; + + // Dynamic tile count limiter, start reducing above 900 tiles + double tc = double(TilesLoaded - (maxtiles*0.75)) / 500; + double fc = tc < 0 ? 1.0 : 1.0 + tc * tc; + + // This doesn't work with narrow FOV, added max() to set low limit + double apr = tdist * fc * max(0.12, scene->GetTanAp()) * resolutionScale; + tgtres = (apr < 1e-6 ? maxlvl : max(0, min(maxlvl, (int)(bias - log(apr)*res_scale)))); + bstepdown = (lvl < tgtres); + tile->tgtscale = pow(2.0f, float(tgtres - lvl)); + } + + if (scene->GetRenderPass() == RENDERPASS_MAINSCENE) + { + if (!bstepdown) { + // Count the tile elevation stats + if (tile->IsElevated()) elvstat.Elev++; + else elvstat.Sphe++; + } + + if (!bstepdown) { + // Search elevated tiles from sub-trees + // This can severally impact in performance if used incorrectly + if ((ElevMode == eElevMode::ForcedElevated) && (tile->IsElevated() == false)) { + if (ElevModeLvl == 0) ElevModeLvl = lvl + 1; + bstepdown = ElevModeLvl >= lvl; + } + } + } + + // Recursion to next level: subdivide into 2x2 patch + if (bstepdown) + { + bool subcomplete = true; + int i, idx; + // check if all 4 subtiles are available already, and queue any missing for loading + for (idx = 0; idx < 4; idx++) { + QuadTreeNode* child = node->Child(idx); + if (!child) + child = LoadChildNode(node, idx); + else if (child->Entry()->state == Tile::Invalid) + loader->LoadTileAsync(child->Entry()); + Tile::TileState state = child->Entry()->state; + if (!(state & TILE_VALID)) + subcomplete = false; + } + if (subcomplete) { + tile->state = Tile::Active; + for (i = 0; i < 4; i++) + ProcessNode(node->Child(i)); + return; // otherwise render at current resolution until all subtiles are available + } + } + else { + bool bDelete = true; + for (int idx = 0; idx < 4; idx++) { + QuadTreeNode* child = node->Child(idx); + // Check if any of the child tiles is rendered resently + double used = child ? child->Entry()->last_used : 0.0; + if ((used < 1.0) || ((used + 1.0) > oapiGetSysTime())) { + // This one been used, or just created, keep the tiles in memory + bDelete = false; + } + } + // If not then delete all children + if (bDelete) node->DelChildren(); + } +} + +// ----------------------------------------------------------------------- + +template +void TileManager2Base::RenderNode (QuadTreeNode *node) +{ + TileType *tile = node->Entry(); + const Scene *scene = GetScene(); + int lvl = tile->lvl; + + if (bFreeze && bFreezeRenderAll && lvl >= 4) { + if (tile->state == Tile::Invisible) { + tile->StepIn(); + tile->Render(); + return; + } + } + + tile->last_used = oapiGetSysTime(); + + if (tile->state == Tile::ForRender) { + if (scene->GetRenderPass() == RENDERPASS_MAINSCENE) tile->MatchEdges (); + tile->StepIn (); + tile->Render (); + } else if (tile->state == Tile::Active) { + tile->StepIn (); + for (int i = 0; i < 4; i++) { + if (node->Child(i)) { + if (node->Child(i)->Entry() && (node->Child(i)->Entry()->state & TILE_ACTIVE)) { + RenderNode (node->Child (i)); // step down into subtree + } + } + } + } +} + +// ----------------------------------------------------------------------- + +template +void TileManager2Base::RenderNodeLabels(QuadTreeNode *node, vkPad *skp, oapi::Font **labelfont, int *fontidx) +{ + TileType *tile = node->Entry(); + if (tile->state == Tile::ForRender || tile->state == Tile::Active) { + tile->RenderLabels(skp, labelfont, fontidx); + + // step down to next quadtree level + if (tile->state == Tile::Active) { + for (int i = 0; i < 4; i++) + if (node->Child(i)) + if (node->Child(i)->Entry() && (node->Child(i)->Entry()->state & TILE_ACTIVE)) + RenderNodeLabels(node->Child(i), skp, labelfont, fontidx); + } + } +} + +// ======================================================================= +// ======================================================================= + +template +TileManager2::TileManager2 (vPlanet *vplanet, int _maxres, int _gridres) + : TileManager2Base (vplanet, _maxres, _gridres), + ntreeMgr(0) +{ + // Initialise the compressed packed tile archives + LoadZTrees(); + InitHasIndividualFiles(); + + // Load the low-res full-sphere tiles + for (int i = 0; i < 3; i++) + { + globtile[i] = new TileType(this, i - 3, 0, 0); + globtile[i]->PreLoad(); + globtile[i]->Load(); + } + + // Set the root tiles for level 0 + for (int i = 0; i < 2; i++) { + tiletree[i].SetEntry (new TileType (this, 0, 0, i)); + tiletree[i].Entry()->PreLoad(); + tiletree[i].Entry()->Load(); + } +} + +// ----------------------------------------------------------------------- + +template +TileManager2::~TileManager2 () +{ + //oapiWriteLogV("=== Tile Dump for %s ===", vp->GetName()); + //for (int i = 0; i < 2; i++) + // DebugDump(&tiletree[i]); + + for (int i = 0; i < 2; i++) + tiletree[i].DelChildren(); + for (int i = 0; i < 3; i++) + delete globtile[i]; + + if (ntreeMgr) { + for (int i = 0; i < ntreeMgr; i++) + if (treeMgr[i]) delete treeMgr[i]; + delete []treeMgr; + delete[]hasIndividualFiles; + } +} + +// ----------------------------------------------------------------------- + +template +void TileManager2::CheckCoverage (const QuadTreeNode *node, + double latmin, double latmax, double lngmin, double lngmax, + int maxlvl, const Tile **tbuf, int nt, int *nfound) const +{ + if (*nfound < 0) return; // error state already set + double t_latmin, t_latmax, t_lngmin, t_lngmax; + const Tile *t = node->Entry(); + t->Extents (&t_latmin, &t_latmax, &t_lngmin, &t_lngmax); + if (latmin >= t_latmax || latmax <= t_latmin || lngmin >= t_lngmax || lngmax <= t_lngmin) return; // no overlap + // note: need to check for longitude wrap here! + + if (t->Level() < maxlvl) { + bool has_children = false; + for (int i = 0; i < 4; i++) { + if (node->Child(i) && node->Child(i)->Entry() && node->Child(i)->Entry()->Tex()) { + CheckCoverage (node->Child(i), latmin, latmax, lngmin, lngmax, maxlvl, tbuf, nt, nfound); + has_children = true; + } + } + if (has_children) return; + } + + if (*nfound == nt) { // no space left + *nfound = -1; // set error state + } else { + tbuf[*nfound] = t; + *nfound = *nfound+1; + } +} + +// ----------------------------------------------------------------------- + +template +Tile *TileManager2::SearchTileSub (const QuadTreeNode *node, double lng, double lat, int maxlvl, bool bOwntex) const +{ + Tile *t = node->Entry(); + + if (!t) return NULL; + if (!t->HasOwnTex() && bOwntex) return NULL; + if ((t->State()&TILE_VALID)==0) return NULL; + if (t->Level()==maxlvl) return t; + + int i = 0; + if (lng > (t->bnd.minlng + t->bnd.maxlng)*0.5) i++; + if (lat < (t->bnd.minlat + t->bnd.maxlat)*0.5) i += 2; + + const QuadTreeNode *next = node->Child(i); + if (next) { + Tile *check = SearchTileSub(next, lng, lat, maxlvl, bOwntex); + if (check) return check; + } + return t; +} + +#endif // !__TILEMGR2_IMP_HPP diff --git a/OVP/VulkanClient/Util.cpp b/OVP/VulkanClient/Util.cpp new file mode 100644 index 000000000..924d72783 --- /dev/null +++ b/OVP/VulkanClient/Util.cpp @@ -0,0 +1,2097 @@ +// ============================================================== +// Utilities +// Part of the ORBITER VISUALISATION PROJECT (OVP) vk Client +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// 2012-2016 �mile "Bibi Uncle" Gr�goire +// ============================================================== + +#define STRICT + +#include "Util.h" +#include "AABBUtil.h" +#include "Client.h" +#include "VectorHelpers.h" +#include "Config.h" +#include "VPlanet.h" +#include "Mesh.h" +#include +#include +#include +#include +#include "DirectXCollision.h" + +extern vkClient* g_client; +extern unordered_map MeshMap; + +DWORD BuildDate() +{ + const char *months[] = { "???","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; + char month[8]; + unsigned int day = 0, year = 0; + assert(sscanf_s(__DATE__, "%s %u %u", month, 8, &day, &year) == 3); + DWORD m = 0; + for (DWORD i = 1; i <= 12; i++) if (strncmp(month, months[i], 3) == 0) { m = i; break; } + assert(m != 0); + return (year % 100) * 10000 + m * 100 + day; +} + +WORD crc16(const char *data, int length) +{ + DWORD crc = 0; + for (int i = 0; i < length; ++i) { + crc = crc ^ (DWORD(data[i]) << 8); + for (int j = 0; j < 8; j++) { + if (crc & 0x8000) crc = (crc << 1) ^ 0x1021; + else crc = (crc << 1); + crc &= 0xFFFF; + } + } + return WORD(crc & 0xFFFF); +} + +// =========================================================================================== +// Sun occlusion by planet hObj for a given global position gpos +// +float SunOcclusionByPlanet(OBJHANDLE hObj, VECTOR3 gpos) +{ + VECTOR3 gsun, gpln; + OBJHANDLE hSun = oapiGetObjectByIndex(0); + + oapiGetGlobalPos(hSun, &gsun); + oapiGetGlobalPos(hObj, &gpln); + + VECTOR3 rpos = gpln - gpos; + VECTOR3 spos = gsun - gpos; + double sd = length(spos); + double sz = oapiGetSize(hObj); + VECTOR3 usd = spos / sd; + VECTOR3 up = unit(rpos); + double r = length(rpos); + double ca = -dotp(up, usd); + double qr = sqrt(saturate(1.0 - ca * ca)) * r; + double dp = r * r - sz * sz; + double hd = dp > 1e4 ? sqrt(dp) : 1000.0; // Distance to horizon + double sr = oapiGetSize(hSun) * abs(hd) / sd; + // How much of the sun's "disc" is shadowed by planet (APPROXIMATION) + double svb = ca > 0.0 ? 1.0 : ilerp(sz - sr * 0.33, sz + sr, qr); + return svb; +} + +// Check if object 'body' is casting shadows on 'ref' --------------------- +// +bool IsCastingShadows(vObject* body, vObject* ref, double* sunsize_out) +{ + double sz = oapiGetSize(oapiGetGbodyByIndex(0)); + VECTOR3 bc = body->GlobalPos() - ref->GlobalPos(); + double x = dotp(bc, ref->SunDirection()); // Distance to projection plane + double s = abs(x) * sz / ref->SunDistance(); // Size of the sun at projection plane + double refrad = body->GetSize() + ref->GetSize() + s; + if (sunsize_out) *sunsize_out = s; + + if (x < 0) return false; // 'body' is behind 'ref' + if (sqrt(dotp(bc, bc) - x * x) < refrad) return true; + return false; +} + + +double Distance(vObject* a, vObject* b) +{ + return length(a->GlobalPos() - b->GlobalPos()); +} + + +float OcclusionFactor(float x, float sunrad, float plnrad) +{ + bool bReverse = sunrad > plnrad; + return OcclusionFactor(x, sunrad, plnrad, bReverse); +} + + +// ================================================================================================================================= +// Occlusion area of two circles, 1.0f = zero occlusion, 0.0f = full occlusion of smaller circle by bigger one +// if bReverse then occlusion of bigger by smaller one +// +float OcclusionFactor(float x, float r1, float r2, bool bReverse) +{ + if (x > (r1 + r2)) return 1.0f; + + float rmax = std::max(r1, r2); + float rmin = std::min(r1, r2); + + float a2 = rmin * rmin; + float b2 = rmax * rmax; + + if (x < (rmax - rmin)) { + if (bReverse) return 1.0f - a2 / b2; + return 0.0f; + } + + bool bInv = x < sqrt(b2 - a2); + + float s = (r1 + r2 + x) * 0.5f; + float A = sqrt(s * (s - r1) * (s - r2) * (s - x)); //Heron's area formula + float h = 2.0f * A / x; + + /*float x2 = x * x; + float bx = b2 - x2; + float d = -(a2 * a2) + 2.0f * a2 * (b2 + x2) - bx * bx; + if (d < 0.0f) d = 0.0f; + float h = sqrt(d) / (2.0*x); + */ + + float s1 = asin(saturate(h / rmin)); // Sector 1 + float s2 = asin(saturate(h / rmax)); // Sector 2 + + if (bInv) s1 = float(PI) - s1; + + s1 *= a2; + s2 *= b2; + + float h2 = h * h; + float t1 = h * sqrt(std::max(0.0f, a2 - h2)); // Triangle 1 + float t2 = h * sqrt(std::max(0.0f, b2 - h2)); // Triangle 2 + + if (bInv) t1 = -t1; + + float area = (s1 - t1) + (s2 - t2); + + + //LogAlw("x=%f, area=%f, h=%f, s1=%f, t1=%f, s2=%f, t2=%f", x, area, h, s1, t1, s2, t2); + + + return 1.0f - area / (float(PI) * (bReverse ? b2 : a2)); +} + + +#if _WIN64 +#define PTR_FMT_STRING "0x%llX" +#else // 32 bit +#define PTR_FMT_STRING "0x%lX" +#endif + +const char *_PTR(const void *p) +{ + static long i = 0; static char buf[8][32]; i++; + sprintf_s(buf[i & 0x7], 32, PTR_FMT_STRING, LONG_PTR(p)); + return buf[i & 0x7]; +} + +bool CopyBuffer(LPDIRECT3DRESOURCE9 _pDst, LPDIRECT3DRESOURCE9 _pSrc) +{ + + void *pSrcData = NULL; + void *pDstData = NULL; + + if (_pSrc->GetType()==D3DRTYPE_VERTEXBUFFER && _pDst->GetType()==D3DRTYPE_VERTEXBUFFER) { + + LPDIRECT3DVERTEXBUFFER9 pSrc = (LPDIRECT3DVERTEXBUFFER9)_pSrc; + LPDIRECT3DVERTEXBUFFER9 pDst = (LPDIRECT3DVERTEXBUFFER9)_pDst; + + D3DVERTEXBUFFER_DESC src_desc, dst_desc; + + HR(pSrc->GetDesc(&src_desc)); + HR(pDst->GetDesc(&dst_desc)); + + if (dst_desc.SizeLock(0, 0, &pSrcData, D3DLOCK_READONLY)); + HR(pDst->Lock(0, 0, &pDstData, 0)); + + memcpy(pDstData, pSrcData, src_desc.Size); + + HR(pSrc->Unlock()); + HR(pDst->Unlock()); + + return true; + } + + if (_pSrc->GetType()==D3DRTYPE_INDEXBUFFER && _pDst->GetType()==D3DRTYPE_INDEXBUFFER) { + + LPDIRECT3DINDEXBUFFER9 pSrc = (LPDIRECT3DINDEXBUFFER9)_pSrc; + LPDIRECT3DINDEXBUFFER9 pDst = (LPDIRECT3DINDEXBUFFER9)_pDst; + + D3DINDEXBUFFER_DESC src_desc, dst_desc; + + HR(pSrc->GetDesc(&src_desc)); + HR(pDst->GetDesc(&dst_desc)); + + if (dst_desc.SizeLock(0, 0, &pSrcData, D3DLOCK_READONLY)); + HR(pDst->Lock(0, 0, &pDstData, 0)); + + memcpy(pDstData, pSrcData, src_desc.Size); + + HR(pSrc->Unlock()); + HR(pDst->Unlock()); + + return true; + } + + return false; +} + +inline FVECTOR4 CV2VEC4(const D3DCOLORVALUE &in) +{ + return FVECTOR4(in.r, in.g, in.b, in.a); +} + +inline FVECTOR4 CV2VEC4(const D3DCOLORVALUE &in, float w) +{ + return FVECTOR4(in.r, in.g, in.b, w); +} + +inline FVECTOR3 CV2VEC3(const D3DCOLORVALUE &in) +{ + return FVECTOR3(in.r, in.g, in.b); +} + +inline D3DCOLORVALUE VECtoCV(const FVECTOR3 &in, float w) +{ + D3DCOLORVALUE c = { in.x, in.y, in.z, w }; + return c; +} + +inline D3DCOLORVALUE VECtoCV(const FVECTOR4 &in) +{ + D3DCOLORVALUE c = { in.x, in.y, in.z, in.w }; + return c; +} + +void UpdateMatExt(const D3DMATERIAL9 *pIn, vkMatExt *pOut) +{ + pOut->Ambient = CV2VEC3(pIn->Ambient); + pOut->Diffuse = CV2VEC4(pIn->Diffuse); + pOut->Emissive = CV2VEC3(pIn->Emissive); + pOut->Specular = CV2VEC4(pIn->Specular, pIn->Power); +} + +void GetMatExt(const vkMatExt *pIn, D3DMATERIAL9 *pOut) +{ + pOut->Ambient = VECtoCV(pIn->Ambient, 0); + pOut->Diffuse = VECtoCV(pIn->Diffuse); + pOut->Emissive = VECtoCV(pIn->Emissive, 0); + pOut->Specular = VECtoCV(pIn->Specular); + pOut->Specular.a = 0.0f; + pOut->Power = pIn->Specular.w; +} + +void CreateMatExt(const D3DMATERIAL9 *pIn, vkMatExt *pOut) +{ + pOut->Ambient = CV2VEC3(pIn->Ambient); + pOut->Diffuse = CV2VEC4(pIn->Diffuse); + pOut->Emissive = CV2VEC3(pIn->Emissive); + pOut->Specular = CV2VEC4(pIn->Specular, pIn->Power); + pOut->Reflect = FVECTOR3(0.0f, 0.0f, 0.0f); + pOut->Fresnel = FVECTOR3(1.0f, 0.0f, 1024.0f); + pOut->Emission2 = FVECTOR3(1.0f, 1.0f, 1.0f); + pOut->Roughness = FVECTOR2(1.0f, 1.0f); + pOut->SpecialFX = FVECTOR4(0.0f, 0.0f, 0.0f, 0.0f); + pOut->Metalness = 0.0f; + pOut->ModFlags = 0; +} + +void CreateDefaultMat(vkMatExt *pOut) +{ + pOut->Ambient = FVECTOR3(0.0f, 0.0f, 0.0f); + pOut->Diffuse = FVECTOR4(1.0f, 1.0f, 1.0f, 1.0f); + pOut->Emissive = FVECTOR3(0.0f, 0.0f, 0.0f); + pOut->Specular = FVECTOR4(0.2f, 0.2f, 0.2f, 50.0f); + pOut->Reflect = FVECTOR3(0.0f, 0.0f, 0.0f); + pOut->Fresnel = FVECTOR3(1.0f, 0.0f, 1024.0f); + pOut->Emission2 = FVECTOR3(1.0f, 1.0f, 1.0f); + pOut->Roughness = FVECTOR2(1.0f, 1.0f); + pOut->SpecialFX = FVECTOR4(0.0f, 0.0f, 0.0f, 0.0f); + pOut->Metalness = 0.0f; + pOut->ModFlags = 0; +} + + +void SurfaceLighting(vkSun *light, OBJHANDLE hP, OBJHANDLE hO, float ao) +{ + // hP=hPlanet, hS=hSun + VECTOR3 GO, GS, GP; + + FVECTOR3 _one(1,1,1); + + OBJHANDLE hS = oapiGetGbodyByIndex(0); // the central star + oapiGetGlobalPos (hO, &GO); // object position + oapiGetGlobalPos (hS, &GS); // sun position + oapiGetGlobalPos (hP, &GP); // planet position + + VECTOR3 S = GS-GO; // sun's position from base + VECTOR3 P = unit(GO-GP); + + float s = float(length(S)); // sun's distance + float rs = float(oapiGetSize(hS)) / s; + float h = float(dotp(S,P)) / s; // sun elevation + float d = 0.173f; // sun elevation for dispersion + float ae = 0.242f; // sun elevation for ambient + float aq = 0.342f; + + float amb0 = 0.0f; + float disp = 0.0f; + float amb = 0.0f; + + const ATMCONST *atm = (oapiGetObjectType(hP)==OBJTP_PLANET ? oapiGetPlanetAtmConstants (hP) : NULL); + + if (atm) { + amb0 = float(min (0.7, log1p(atm->rho0)*0.4)); + disp = float(max (0.02, min(0.9, log1p(atm->rho0)))); + } + + FVECTOR3 lcol; + FVECTOR3 r0 = _one - FVECTOR3(0.65f, 0.75f, 1.0f) * disp; + + if (atm) { // case 1: planet has atmosphere + lcol = (r0 + (_one-r0) * saturate(h/d)) * saturate((h+rs)/(2.0f*rs)); + amb = saturate((h+ae)/aq); + amb = saturate(max(amb0*amb-0.05f,ao)); + lcol *= 1.0f-amb*0.5f; // reduce direct light component to avoid overexposure + } + else { // case 2: planet has no atmosphere + lcol = r0 * saturate((h+rs)/(2.0f*rs)); + amb = ao; + lcol *= 1.0f-amb*0.5f; // reduce direct light component to avoid overexposure + } + + light->Color = FVECTOR3(lcol.x, lcol.y, lcol.z); + light->Ambient = FVECTOR3(amb, amb, amb); + light->Dir = S * (-1.0/s); +} +// =========================================== +// Remove unecessary spaces and tablations +// when reading a file. +// =========================================== +/* +char* _fgets(char* cbuf, int num, FILE* stream, bool keepOneSpace) +{ + + cbuf[0] = '\0'; + + char* temp = new char[num]; + + if(fgets(temp, num, stream) == NULL) { + delete []temp; + temp = NULL; + return NULL; + } + + bool equalSign = false; + bool firstLetter = false; + int cbufLine = 0; + + for(int i=0; i0) { + cbufLine--; + if (cbuf[cbufLine]==' ' || cbuf[cbufLine]=='\t') cbuf[cbufLine] = '\0'; + else break; + } + + delete[] temp; + + return cbuf; +} +*/ + +void strremchr(char *str, int idx) +{ + while (str[idx]!='\0') { + str[idx] = str[idx+1]; + idx++; + } +} + +// -------------------------------------------------------------- +// Improved version of fgets +// Copyright (C) 2012 Jarmo Nikkanen +// Return: +// -1 = eof +// 0 = invalid string +// 1 = success without '=' in string +// 2 = success with '=' in string +// +// param: +// 0x01 = Don't Remove spaces from both sides of '=' +// 0x02 = Don't convert '/' to '\' +// 0x04 = Convert to upper case +// 0x08 = Remove '=' if exists + +int fgets2(char *buf, int cmax, FILE *file, DWORD param) //bool bEquality, bool bSlash) +{ + bool bEql = false; + bool bEquality = (param&0x01)==0; + bool bSlash = (param&0x02)==0; + bool bEqlRem = (param&0x08)!=0; + bool bUpper = (param&0x04)!=0; + + if (fgets(buf, cmax, file)==NULL) return -1; + + int num = lstrlen(buf); + + if (num==(cmax-1)) LogErr("Insufficient buffer size in fgets2() size=%d, string=(%s)",cmax,buf); + + // Replace tabs with spaces and cut a comment parts and unwanted chars + // Check the existance of equality sign '=' + for (int i=0;i0) { + num--; + if (buf[num]==' ') buf[num]='\0'; + else break; + } + + // Remove spaces from the front of the line + while (buf[0]==' ') strremchr(buf,0); + + num = lstrlen(buf); + if (num==0) return 0; + + // Remove repeatitive spaces if exists. (double trible spaces and so on) + // At this point a space can not be the last char, therefore [i+1] is not a problem + for (int i=0;i(std::isspace)))); + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](auto c) { return !std::isspace(c); })); + return s; +} + +// trim from end +std::string &rtrim (std::string &s) +{ + //s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(std::find_if(s.rbegin(), s.rend(), [](auto c) { return !std::isspace(c); }).base(), s.end()); + return s; +} + +// trim from both ends +std::string &trim (std::string &s) { + return ltrim(rtrim(s)); +} + +// uppercase complete string +void toUpper (std::string &s) { + std::transform(s.begin(), s.end(), s.begin(), std::toupper); +} + +// lowercase complete string +//void toLower (std::string &s) { +// std::transform(s.begin(), s.end(), s.begin(), std::tolower); +//} + +// string to double (returns quiet_NaN if conversion failed) +double toDoubleOrNaN (const std::string &str) +{ + return (str[0] == 'N' || str[0] == 'n') // "NaN" or "nan"? + ? std::numeric_limits::quiet_NaN() + : atof(str.c_str()); +} + +// case insensitive compare +bool startsWith (const std::string &haystack, const std::string &needle) +{ + auto it = std::search( + haystack.cbegin(), haystack.cend(), + needle.cbegin(), needle.cend(), + [](char a, char b) { return std::toupper(a) == std::toupper(b); } + ); + return it != haystack.cend(); +} + +// case insensitive conatins +bool contains (const std::string &haystack, const std::string &needle) +{ + auto it = std::search( + haystack.cbegin(), haystack.cend(), needle.cbegin(), needle.cend(), + [](char a, char b) { return std::toupper(a) == std::toupper(b); } + ); + return it != haystack.cend(); +} + +// case insensitive find +size_t find_ci (const std::string &haystack, const std::string &needle) +{ + auto it = std::search( + haystack.cbegin(), haystack.cend(), needle.cbegin(), needle.cend(), + [](char a, char b) { return std::toupper(a) == std::toupper(b); } + ); + return it != haystack.cend() + ? static_cast(it - haystack.cbegin()) + : std::string::npos; +} + +// case insensitive rfind +size_t rfind_ci (const std::string &haystack, const std::string &needle) +{ + auto it = std::search( + haystack.rbegin(), haystack.rend(), needle.rbegin(), needle.rend(), + [](char a, char b) { return std::toupper(a) == std::toupper(b); } + ); + return it != haystack.rend() + ? static_cast(haystack.rend() - it) + : std::string::npos; +} + +// parse assignments like "foo=bar", "foo = bar" or even "foo= bar ; with comment" +std::pair &splitAssignment (const std::string &line, const char delim /* = '=' */) +{ + static std::pair ret; + + //const char delim = '='; + const char comment = ';'; + size_t delPos = line.find(delim), // delimiter position + cmtPos = line.find(comment);// comment pos... + + // ...convert to 'comment part length' if comment found + cmtPos -= cmtPos != std::string::npos ? delPos + 1 : 0; + + ret.first = line.substr(0, delPos); + trim(ret.first); + ret.second = line.substr(delPos + 1, cmtPos); + trim(ret.second); + + return ret; +} + +// replace all occurances of 's' in 'subj' by 't' +std::string::size_type replace_all (std::string &subj, const std::string &s, const std::string &t) +{ + std::string::size_type n = 0, c = 0; + while ((n = subj.find(s, n)) != std::string::npos) { + subj.replace(n, s.size(), t); + n += t.size(); + ++c; + } + return c; +} + +// ======================================================================= +// Some utility methods for D3D vectors and matrices +// ============================================================================ + +float D3DXVec3Angle(FVECTOR3 a, FVECTOR3 b) +{ + normalise(a); + normalise(b); + float x = dotp(a, b); + if (x<-1.0f) x=-1.0f; + if (x> 1.0f) x= 1.0f; + return acos(x); +} + +// ============================================================================ +// +FVECTOR3 Perpendicular(FVECTOR3 *a) +{ + float x = fabs(a->x); + float y = fabs(a->y); + float z = fabs(a->z); + float m = min(min(x, y), z); + if (m==x) return FVECTOR3(0, a->z, a->y); + if (m==y) return FVECTOR3(a->z, 0, -a->x); + else return FVECTOR3(a->y, -a->x, 0); +} + +// Cleate a billboarding matrix. X-axis of the vertex data will be pointing to the camera +// +void D3DMAT_CreateX_Billboard(const FVECTOR3 *toCam, const FVECTOR3 *pos, float size, FMATRIX4 *pOut) +{ + float hz = 1.0f/sqrt(toCam->x*toCam->x + toCam->z*toCam->z); + + pOut->m11 = toCam->x; + pOut->m12 = toCam->y; + pOut->m13 = toCam->z; + pOut->m31 = -toCam->z*hz; + pOut->m32 = 0.0f; + pOut->m33 = toCam->x*hz; + pOut->m21 = -pOut->m12*pOut->m33; + pOut->m22 = pOut->m33*pOut->m11 - pOut->m13*pOut->m31; + pOut->m23 = pOut->m31*pOut->m12; + pOut->m41 = pos->x; + pOut->m42 = pos->y; + pOut->m43 = pos->z; + pOut->m14 = pOut->m24 = pOut->m34 = pOut->m44 = 0.0f; + pOut->m11 *= size; pOut->m12 *= size; pOut->m13 *= size; + pOut->m21 *= size; pOut->m22 *= size; pOut->m23 *= size; + pOut->m31 *= size; pOut->m33 *= size; +} + + +// Cleate a billboarding matrix. X-axis of the vertex data will be pointing to the camera +// +void D3DMAT_CreateX_Billboard(const FVECTOR3 *toCam, const FVECTOR3 *pos, const FVECTOR3 *dir, float size, float stretch, FMATRIX4 *pOut) +{ + FVECTOR3 q = unit(crossp(*dir, *toCam)); + FVECTOR3 w = unit(crossp(q, *dir)); + + pOut->m11 = w.x * size; + pOut->m12 = w.y * size; + pOut->m13 = w.z * size; + + pOut->m21 = q.x * size; + pOut->m22 = q.y * size; + pOut->m23 = q.z * size; + + pOut->m31 = dir->x * stretch; + pOut->m32 = dir->y * stretch; + pOut->m33 = dir->z * stretch; + + pOut->m41 = pos->x; + pOut->m42 = pos->y; + pOut->m43 = pos->z; + + pOut->m14 = pOut->m24 = pOut->m34 = pOut->m44 = 0.0f; +} + +// ============================================================================ +// +void D3DMAT_ZeroMatrix(FMATRIX4 *mat) +{ + ZeroMemory(mat, sizeof (FMATRIX4)); +} + +// ============================================================================ +// Copy a FMATRIX4 + +void D3DMAT_Copy (FMATRIX4 *tgt, const FMATRIX4 *src) +{ + memcpy(tgt, src, sizeof (FMATRIX4)); +} + +// ============================================================================ +// +void D3DMAT_FromAxis(FMATRIX4 *mat, const FVECTOR3 *x, const FVECTOR3 *y, const FVECTOR3 *z) +{ + mat->m11 = x->x; + mat->m21 = x->y; + mat->m31 = x->z; + + mat->m12 = y->x; + mat->m22 = y->y; + mat->m32 = y->z; + + mat->m13 = z->x; + mat->m23 = z->y; + mat->m33 = z->z; +} + +// ============================================================================ +// +void D3DMAT_FromAxis(FMATRIX4 *mat, const VECTOR3 *x, const VECTOR3 *y, const VECTOR3 *z) +{ + mat->m11 = float(x->x); + mat->m21 = float(x->y); + mat->m31 = float(x->z); + + mat->m12 = float(y->x); + mat->m22 = float(y->y); + mat->m32 = float(y->z); + + mat->m13 = float(z->x); + mat->m23 = float(z->y); + mat->m33 = float(z->z); +} + +// ============================================================================ +// +void D3DMAT_FromAxisT(FMATRIX4 *mat, const FVECTOR3 *x, const FVECTOR3 *y, const FVECTOR3 *z) +{ + mat->m11 = x->x; + mat->m12 = x->y; + mat->m13 = x->z; + + mat->m21 = y->x; + mat->m22 = y->y; + mat->m23 = y->z; + + mat->m31 = z->x; + mat->m32 = z->y; + mat->m33 = z->z; +} + +// ============================================================================ +// +void D3DMAT_Scale(FMATRIX4* mat, float x, float y, float z) +{ + mat->m11 *= x; + mat->m12 *= x; + mat->m13 *= x; + + mat->m21 *= y; + mat->m22 *= y; + mat->m23 *= y; + + mat->m31 *= z; + mat->m32 *= z; + mat->m33 *= z; +} + +// ============================================================================ +// Copy a rotation matrix into a FMATRIX4 + +void D3DMAT_SetRotation (FMATRIX4 *mat, const MATRIX3 *rot) +{ + mat->m11 = (FLOAT)rot->m11; + mat->m12 = (FLOAT)rot->m12; + mat->m13 = (FLOAT)rot->m13; + mat->m21 = (FLOAT)rot->m21; + mat->m22 = (FLOAT)rot->m22; + mat->m23 = (FLOAT)rot->m23; + mat->m31 = (FLOAT)rot->m31; + mat->m32 = (FLOAT)rot->m32; + mat->m33 = (FLOAT)rot->m33; +} + +// ============================================================================ +// Copy the transpose of a matrix as rotation of a D3D transformation matrix + +void D3DMAT_SetInvRotation (FMATRIX4 *mat, const MATRIX3 *rot) +{ + mat->m11 = (FLOAT)rot->m11; + mat->m12 = (FLOAT)rot->m21; + mat->m13 = (FLOAT)rot->m31; + mat->m21 = (FLOAT)rot->m12; + mat->m22 = (FLOAT)rot->m22; + mat->m23 = (FLOAT)rot->m32; + mat->m31 = (FLOAT)rot->m13; + mat->m32 = (FLOAT)rot->m23; + mat->m33 = (FLOAT)rot->m33; +} + +// ============================================================================ +// Define a rotation matrix from a rotation axis & rotation angle + +void D3DMAT_RotationFromAxis (const FVECTOR3 &axis, float angle, FMATRIX4 *rot) +{ + // Calculate quaternion + angle *= 0.5f; + float w = cosf(angle), sina = sinf(angle); + float x = sina * axis.x; + float y = sina * axis.y; + float z = sina * axis.z; + + // Rotation matrix + float xx = x*x, yy = y*y, zz = z*z; + float xy = x*y, xz = x*z, yz = y*z; + float wx = w*x, wy = w*y, wz = w*z; + + rot->m11 = 1 - 2 * (yy+zz); + rot->m12 = 2 * (xy+wz); + rot->m13 = 2 * (xz-wy); + rot->m21 = 2 * (xy-wz); + rot->m22 = 1 - 2 * (xx+zz); + rot->m23 = 2 * (yz+wx); + rot->m31 = 2 * (xz+wy); + rot->m32 = 2 * (yz-wx); + rot->m33 = 1 - 2 * (xx+yy); + + rot->m14 = rot->m24 = rot->m34 = rot->m41 = rot->m42 = rot->m43 = 0.0f; + rot->m44 = 1.0f; +} + +// ============================================================================ +// Set up a as matrix for ANTICLOCKWISE rotation r around x/y/z-axis + +void D3DMAT_RotX (FMATRIX4 *mat, double r) +{ + double sinr = sin(r), cosr = cos(r); + ZeroMemory (mat, sizeof (FMATRIX4)); + mat->m22 = mat->m33 = (FLOAT)cosr; + mat->m23 = -(mat->m32 = (FLOAT)sinr); + mat->m11 = mat->m44 = 1.0f; +} + +// ============================================================================ +// +void D3DMAT_RotY (FMATRIX4 *mat, double r) +{ + double sinr = sin(r), cosr = cos(r); + ZeroMemory (mat, sizeof (FMATRIX4)); + mat->m11 = mat->m33 = (FLOAT)cosr; + mat->m31 = -(mat->m13 = (FLOAT)sinr); + mat->m22 = mat->m44 = 1.0f; +} + +// ============================================================================ +// +float D3DMAT_BSScaleFactor(const FMATRIX4 *mat) +{ + float lx = mat->m11*mat->m11 + mat->m12*mat->m12 + mat->m13*mat->m13; + float ly = mat->m21*mat->m21 + mat->m22*mat->m22 + mat->m23*mat->m23; + float lz = mat->m31*mat->m31 + mat->m32*mat->m32 + mat->m33*mat->m33; + return sqrt(max(max(lx,ly),lz)); +} + +// ============================================================================ +// Apply a translation vector toa D3D transformation matrix + +void D3DMAT_SetTranslation (FMATRIX4 *mat, const VECTOR3 *trans) +{ + mat->m41 = (FLOAT)trans->x; + mat->m42 = (FLOAT)trans->y; + mat->m43 = (FLOAT)trans->z; +} + +// ============================================================================ +// +void D3DMAT_SetTranslation(FMATRIX4 *mat, const FVECTOR3 *trans) +{ + mat->m41 = (FLOAT)trans->x; + mat->m42 = (FLOAT)trans->y; + mat->m43 = (FLOAT)trans->z; +} + +// ============================================================================ +// +void D3DMAT_Transform(FVECTOR4* o, const FVECTOR4* i, const FMATRIX4* m) +{ + o->Load(XMVector4Transform(i->XM(), m->XM())); +} + +// ============================================================================ +// +void D3DMAT_AffineTransformation2D(FMATRIX4* pOut, float Scl, const FVECTOR2* pRotCtr, float Rot, const FVECTOR2* pTransl) +{ + XMVECTOR Tr = FVECTOR2(0, 0).XM(); + XMVECTOR Rc = Tr; + XMVECTOR Sl = FVECTOR2(Scl).XM(); + + if (pRotCtr) Rc = pRotCtr->XM(); + if (pTransl) Tr = pTransl->XM(); + + XMMATRIX M = XMMatrixAffineTransformation2D(Sl, Rc, Rot, Tr); + pOut->Load(M); +} + +// ============================================================================ +// +void D3DMAT_Transformation2D(FMATRIX4* pOut, const FVECTOR2* pSclCtr, float SclRot, const FVECTOR2* pScl, + const FVECTOR2* pRotCtr, float Rot, const FVECTOR2* pTransl) +{ + XMVECTOR Tr = FVECTOR2(0, 0).XM(); + XMVECTOR Sc = Tr; + XMVECTOR Rc = Tr; + XMVECTOR Sl = FVECTOR2(1, 1).XM(); + + if (pSclCtr) Sc = pSclCtr->XM(); + if (pScl) Sl = pScl->XM(); + if (pRotCtr) Rc = pRotCtr->XM(); + if (pTransl) Tr = pTransl->XM(); + + XMMATRIX M = XMMatrixTransformation2D(Sc, SclRot, Sl, Rc, Rot, Tr); + pOut->Load(M); +} + +// ============================================================================ +// +void D3DMAT_OrthoOffCenterLH(FMATRIX4* o, float l, float r, float b, float t, float zn, float zf) +{ + o->Load(XMMatrixOrthographicOffCenterLH(l, r, b, t, zn, zf)); +} + +// ============================================================================ +// +void D3DMAT_OrthoOffCenterRH(FMATRIX4* o, float l, float r, float b, float t, float zn, float zf) +{ + o->Load(XMMatrixOrthographicOffCenterRH(l, r, b, t, zn, zf)); +} + +// ============================================================================ +// +void D3DMAT_LookAtRH(FMATRIX4* o, const FVECTOR3* pEye, const FVECTOR3* pAt, const FVECTOR3* pUp) +{ + o->Load(XMMatrixLookAtRH(pEye->XM(), pAt->XM(), pUp->XM())); +} + + +// ============================================================================ +// +LPDIRECT3DPIXELSHADER9 CompilePixelShader(LPDIRECT3DDEVICE9 pDev, const char *file, const char *function, const char *name, const char* options, LPD3DXCONSTANTTABLE *pConst) +{ + ID3DXBuffer* pErrors = NULL; + ID3DXBuffer* pCode = NULL; + LPDIRECT3DPIXELSHADER9 pShader = NULL; + DWORD flags = 0; + char *str = NULL; + char *tok = NULL; + + WORD crc = 0; + if (options) crc = crc16(options, strlen(options)); + + string path(file); + char filename[MAX_PATH]; + + string last = path.substr(path.find_last_of("\\/") + 1); + sprintf_s(filename, MAX_PATH, "Cache/vkShaders/%s_%s_%hX_%s.bin", name, function, crc, last.c_str()); + + if (Config->ShaderCacheUse) + { + // Browse Shader Cache -------------------- + // + HANDLE hCacheRead = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hCacheRead != INVALID_HANDLE_VALUE) { + FILETIME cacheWrite, mainWrite; + if (GetFileTime(hCacheRead, NULL, NULL, &cacheWrite)) + { + HANDLE hRead = CreateFile(file, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hRead != INVALID_HANDLE_VALUE) + { + if (GetFileTime(hRead, NULL, NULL, &mainWrite)) + { + if (CompareFileTime(&cacheWrite, &mainWrite) == 1) + { + DWORD size = GetFileSize(hCacheRead, NULL); + DWORD* buffer = new DWORD[(size >> 2) + 1]; + DWORD bytesRead; + if (ReadFile(hCacheRead, buffer, size, &bytesRead, NULL)) { + HR(pDev->CreatePixelShader(buffer, &pShader)); + HR(D3DXGetShaderConstantTable(buffer, pConst)); + } + delete[] buffer; + } + } + CloseHandle(hRead); + } + } + CloseHandle(hCacheRead); + + if (pShader) { + //LogOapi("Shader Created From Cache: %s", filename); + return pShader; + } + } + } + + + + // Invalid Cache data, Recompile the shader -------------------- + // + D3DXMACRO macro[16]; + memset(¯o, 0, 16*sizeof(D3DXMACRO)); + bool bDisassemble = false; + + LogAlw("Compiling a Shader [%s] function [%s] name [%s]...", file, function, name); + + if (options) { + int m = 0; + int l = lstrlen(options) + 1; + str = new char[l]; + strcpy_s(str, l, options); + tok = strtok(str,";, "); + while (tok!=NULL && m<16) { + if (strcmp(tok, "PARTIAL") == 0) flags |= D3DXSHADER_PARTIALPRECISION; + if (strcmp(tok, "DISASM") == 0) bDisassemble = true; + else macro[m++].Name = tok; + tok = strtok(NULL, ";, "); + LogAlw("Macro (%s)", tok); + } + } + + HR(D3DXCompileShaderFromFileA(file, macro, NULL, function, "ps_3_0", flags, &pCode, &pErrors, pConst)); + + if (pErrors) { + LogErr("Compiling a Shader [%s] function [%s] Failed:\n %s", file, function, (char*)pErrors->GetBufferPointer()); + MessageBoxA(0, (char*)pErrors->GetBufferPointer(), "Failed to compile a shader", 0); + FatalAppExitA(0, "Failed to compile shader code. Exiting..."); + } + + if (!pCode) { + LogErr("Failed to compile a shader [%s] [%s]", file, function); + SAFE_DELETEA(str); + return NULL; + } + + + // Save Shader into a Cache + // + if (Config->ShaderCacheUse) + { + HANDLE hCache = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hCache != INVALID_HANDLE_VALUE) { + DWORD bytesWritten; + if (!WriteFile(hCache, pCode->GetBufferPointer(), pCode->GetBufferSize(), &bytesWritten, NULL)) + { + LogErr("CreateShaderCache: WriteFile Error: 0x%X", GetLastError()); + } + CloseHandle(hCache); + } + else { + LogErr("CreateShaderCache: CreateFile Error: 0x%X", GetLastError()); + LogErr("Path=[%s]", filename); + } + } + + + if (bDisassemble && pCode) { + LPD3DXBUFFER pBuffer = NULL; + if (D3DXDisassembleShader((DWORD*)pCode->GetBufferPointer(), true, NULL, &pBuffer) == S_OK) { + FILE *fp = NULL; + char name[256]; + sprintf_s(name, 256, "%s_%s_asm.html", RemovePath(file), function); + if (!fopen_s(&fp, name, "w")) { + fwrite(pBuffer->GetBufferPointer(), 1, pBuffer->GetBufferSize(), fp); + fclose(fp); + } + pBuffer->Release(); + } + } + + HR(pDev->CreatePixelShader((DWORD*)pCode->GetBufferPointer(), &pShader)); + + SAFE_RELEASE(pCode); + SAFE_RELEASE(pErrors); + SAFE_DELETEA(str); + + return pShader; +} + +// ============================================================================ +// +LPDIRECT3DVERTEXSHADER9 CompileVertexShader(LPDIRECT3DDEVICE9 pDev, const char *file, const char *function, const char* name, const char *options, LPD3DXCONSTANTTABLE *pConst) +{ + ID3DXBuffer* pErrors = NULL; + ID3DXBuffer* pCode = NULL; + LPDIRECT3DVERTEXSHADER9 pShader = NULL; + DWORD flags = 0; + + WORD crc = 0; + if (options) crc = crc16(options, strlen(options)); + + string path(file); + char filename[MAX_PATH]; + + string last = path.substr(path.find_last_of("\\/") + 1); + sprintf_s(filename, MAX_PATH, "Cache/vkShaders/%s_%s_%hX_%s.bin", name, function, crc, last.c_str()); + + if (Config->ShaderCacheUse) + { + // Browse Shader Cache -------------------- + // + HANDLE hCacheRead = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hCacheRead != INVALID_HANDLE_VALUE) { + FILETIME cacheWrite, mainWrite; + if (GetFileTime(hCacheRead, NULL, NULL, &cacheWrite)) + { + HANDLE hRead = CreateFile(file, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hRead != INVALID_HANDLE_VALUE) + { + if (GetFileTime(hRead, NULL, NULL, &mainWrite)) + { + if (CompareFileTime(&cacheWrite, &mainWrite) == 1) + { + DWORD size = GetFileSize(hCacheRead, NULL); + DWORD* buffer = new DWORD[(size >> 2) + 1]; + DWORD bytesRead; + if (ReadFile(hCacheRead, buffer, size, &bytesRead, NULL)) { + HR(pDev->CreateVertexShader(buffer, &pShader)); + HR(D3DXGetShaderConstantTable(buffer, pConst)); + } + delete[] buffer; + } + } + CloseHandle(hRead); + } + } + CloseHandle(hCacheRead); + + if (pShader) { + //LogOapi("Shader Created From Cache: %s", filename); + return pShader; + } + } + } + + char *str = NULL; + char *tok = NULL; + + D3DXMACRO macro[32]; + memset(¯o, 0, 32*sizeof(D3DXMACRO)); + + if (options) { + int m = 0; + int l = lstrlen(options) + 1; + str = new char[l]; + strcpy_s(str, l, options); + tok = strtok(str,";, "); + while (tok!=NULL && m<16) { + macro[m++].Name = tok; + tok = strtok(NULL, ";, "); + } + } + + LogAlw("Compiling a Shader [%s] function [%s]...", file, function); + + HR(D3DXCompileShaderFromFileA(file, macro, NULL, function, "vs_3_0", flags, &pCode, &pErrors, pConst)); + + if (pErrors) { + LogErr("Compiling a Shader [%s] function [%s] Failed:\n %s", file, function, (char*)pErrors->GetBufferPointer()); + MessageBoxA(0, (char*)pErrors->GetBufferPointer(), "Failed to compile a shader", 0); + FatalAppExitA(0, "Failed to compile shader code. Exiting..."); + } + + if (!pCode) { + LogErr("Failed to compile a shader [%s] [%s]", file, function); + SAFE_DELETEA(str); + return NULL; + } + + + // Save Shader into a Cache + // + if (Config->ShaderCacheUse) + { + HANDLE hCache = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hCache != INVALID_HANDLE_VALUE) { + DWORD bytesWritten; + if (!WriteFile(hCache, pCode->GetBufferPointer(), pCode->GetBufferSize(), &bytesWritten, NULL)) + { + LogErr("CreateShaderCache: WriteFile Error: 0x%X", GetLastError()); + } + CloseHandle(hCache); + } + else { + LogErr("CreateShaderCache: CreateFile Error: 0x%X", GetLastError()); + LogErr("Path=[%s]", filename); + } + } + + HR(pDev->CreateVertexShader((DWORD*)pCode->GetBufferPointer(), &pShader)); + + SAFE_RELEASE(pCode); + SAFE_RELEASE(pErrors); + SAFE_DELETEA(str); + + return pShader; +} + +// ============================================================================ +// +const char *RemovePath(const char *in) +{ + int len = lstrlen(in); + const char *cptr = in; + for (int i=0;iGetLevelDesc(0, &desc); + DWORD mips = pIn[0]->GetLevelCount(); + + if (D3DXCreateVolumeTexture(pDevice, desc.Width, desc.Height, count, mips, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pTemp)==S_OK) { + + DWORD height = desc.Height; + + for (DWORD m=0; m < mips; m++) { + pTemp->GetLevelDesc(m, &vd); + if (pTemp->LockBox(m, &box, NULL, 0)==S_OK) { + char *pDst = (char*)box.pBits; + for (int i=0; i < count; i++) { + pIn[i]->GetLevelDesc(m, &desc); + if (pIn[i]->LockRect(m, &rect, NULL, D3DLOCK_READONLY)==S_OK) { + if ((box.RowPitch == rect.Pitch) && (box.SlicePitch == rect.Pitch*height)) { + memcpy(pDst, rect.pBits, box.SlicePitch); + pDst += box.SlicePitch; + pIn[i]->UnlockRect(m); + continue; + } + LogErr("CreateVolumeTexture: Pitch miss-match"); + pIn[i]->UnlockRect(m); + pTemp->UnlockBox(m); + return false; + } + else { + LogErr("CreateVolumeTexture: Failed to lock a rect"); + return false; + } + } + } + else { + LogErr("CreateVolumeTexture: Failed to lock a box"); + return false; + } + height>>=1; + pTemp->UnlockBox(m); + } + + if (D3DXCreateVolumeTexture(pDevice, desc.Width, desc.Height, count, mips, 0, desc.Format, D3DPOOL_DEFAULT, pOut)==S_OK) { + HR(pDevice->UpdateTexture(pTemp, (*pOut))); + (*pOut)->GenerateMipSubLevels(); + pTemp->Release(); + return true; + } + return false; + } + return false; +} + +// Light Emitter ============================================================================ +// +vkLight::vkLight(const LightEmitter *le, const class vObject *vo) : + cosp(0), tanp(0), cosu(0), + range(0), range2(0), + intensity(-1.0) +{ + UpdateLight(le, vo); +} + +// ============================================================================ +// +vkLight::vkLight() : + cosp(0), tanp(0), cosu(0), + range(0), range2(0), + intensity(-1.0), + le(NULL), cone(1.0f), GPUId(-1) +{ + +} + +// ============================================================================ +// +vkLight::~vkLight() +{ + +} + +// ============================================================================ +// +void vkLight::Reset() +{ + intensity = -1.0f; + cone = 1.0f; + GPUId = -1; +} + +// ============================================================================ +// +float vkLight::GetIlluminance(FVECTOR3 &_pos, float r) const +{ + if (intensity < 0) return -1.0f; + + FVECTOR3 pos = _pos - Position; + + float d = length(pos); + float d2 = d*d; + + if (d < r) return 1e6; // Light is inside the sphere + if (d > (r + range)) return -1.0f; // Light can't reach the sphere + + if ((Type == 1) && (cosp>0.1)) { + float x = dotp(pos, Direction); + if (x < -r) return -1.0f; // The sphere is a way behind the spotlight + if ((sqrt(d2 - x*x) - x*tanp) * cosp > r) return -1.0f; // Light cone doesn't intersect the sphere + } + + // The sphere is lit from outside + return intensity / (Attenuation.x + Attenuation.y*d + Attenuation.z*d2); +} + +// ============================================================================ +// +const LightEmitter *vkLight::GetEmitter() const +{ + return le; +} + +// ============================================================================ +// +void vkLight::UpdateLight(const LightEmitter *_le, const class vObject *vo) +{ + le = _le; + + // ----------------------------------------------------------------------------- + + Position = oapiTransformCoord(&(_F(le->GetPosition())), vo->MWorld()); + Dst2 = dotp(Position, Position); + + // ----------------------------------------------------------------------------- + + const double *att = ((PointLight*)le)->GetAttenuation(); + Attenuation = FVECTOR3((float)att[0], (float)att[1], (float)att[2]); + + // ----------------------------------------------------------------------------- + + tanp = 0.0f; + cosu = 1.0f; + cosp = 1.0f; + cone = 1.0f; + float P = 0.0f; + float U = 0.0f; + + if (le->GetType() == LightEmitter::LT_SPOT) { + P = float(((SpotLight*)le)->GetPenumbra()); + U = float(((SpotLight*)le)->GetUmbra()); + if (P > 3.05f) P = 3.05f; + if (U > 2.96f) U = 2.96f; + } + + // ----------------------------------------------------------------------------- + switch (le->GetType()) { + + case LightEmitter::LT_POINT: { + Type = 0; + } break; + + case LightEmitter::LT_SPOT: { + Type = 1; + cosp = cos(P * 0.5f); + cosu = cos(U * 0.5f); + tanp = tan(P * 0.5f); + Param[vkLFalloff] = 1.0f; + Param[vkLPhi] = cosp; + Param[vkLTheta] = 1.0f / (cosu - cosp); + } break; + + default: + LogErr("Invalid Light Emitter Type"); + break; + } + + // ----------------------------------------------------------------------------- + intensity = float(le->GetIntensity()); + const FVECTOR4 &col_d = le->GetDiffuseColour(); + Diffuse.r = (col_d.r*intensity); + Diffuse.g = (col_d.g*intensity); + Diffuse.b = (col_d.b*intensity); + Diffuse.a = (col_d.a*intensity); + + + float c = float(att[0]); + float b = float(att[1]); + float a = float(att[2]); + float limit = 0.01f; // Intensity limit for max range + float Q = intensity - c*limit; + float d = b*b*limit + 4.0f*a*Q; + + range = (sqrt(limit * d) - b*limit) / (2.0f*a*limit); + + //oapiWriteLogV("LightEmitter[0x%X] R=%f(m), P=%f(deg), U=%f(deg)", this, range, P*DEG, U*DEG); + range = min(range, float(((PointLight*)le)->GetRange())); + + range2 = range*range; + Param[vkLRange] = range; + + + // ----------------------------------------------------------------------------- + if (Type != 0) { + Direction = oapiTransformNormal(&_F(le->GetDirection()), vo->MWorld()); + float angle = acos(dotp(unit(Position), Direction)); + cone = ilerp(U * 0.5f, P * 0.5f, angle); + } +} + + + +// Planet Texture Loader ------------------------------------------------------------------------------ +// +#include +#pragma pack(push, 1) +typedef struct _DDDESC2_x64 +{ + DWORD dwSize; // size of the DDSURFACEDESC structure + DWORD dwFlags; // determines what fields are valid + DWORD dwHeight; // height of surface to be created + DWORD dwWidth; // width of input surface + union + { + LONG lPitch; // distance to start of next line (return value only) + DWORD dwLinearSize; // Formless late-allocated optimized surface size + } DUMMYUNIONNAMEN(1); + union + { + DWORD dwBackBufferCount; // number of back buffers requested + DWORD dwDepth; // the depth if this is a volume texture + } DUMMYUNIONNAMEN(5); + union + { + DWORD dwMipMapCount; // number of mip-map levels requestde + // dwZBufferBitDepth removed, use ddpfPixelFormat one instead + DWORD dwRefreshRate; // refresh rate (used when display mode is described) + DWORD dwSrcVBHandle; // The source used in VB::Optimize + } DUMMYUNIONNAMEN(2); + DWORD dwAlphaBitDepth; // depth of alpha buffer requested + DWORD dwReserved; // reserved + DWORD lpSurface; // pointer to the associated surface memory + union + { + DDCOLORKEY ddckCKDestOverlay; // color key for destination overlay use + DWORD dwEmptyFaceColor; // Physical color for empty cubemap faces + } DUMMYUNIONNAMEN(3); + DDCOLORKEY ddckCKDestBlt; // color key for destination blt use + DDCOLORKEY ddckCKSrcOverlay; // color key for source overlay use + DDCOLORKEY ddckCKSrcBlt; // color key for source blt use + union + { + DDPIXELFORMAT ddpfPixelFormat; // pixel format description of the surface + DWORD dwFVF; // vertex format description of vertex buffers + } DUMMYUNIONNAMEN(4); + DDSCAPS2 ddsCaps; // direct draw surface capabilities + DWORD dwTextureStage; // stage in multitexture cascade +} DDSURFACEDESC2_x64; +#pragma pack(pop) + +int LoadPlanetTextures(const char* fname, LPDIRECT3DTEXTURE9* ppdds, DWORD flags, int amount) +{ + _TRACE; + + char path[MAX_PATH]; + + if (g_client->TexturePath(fname, path)) { + + FILE* f; + + if (fopen_s(&f, path, "rb")) return 0; + + int ntex = 0; + char* buffer, * location; + fseek(f, 0, SEEK_END); + long size = ftell(f); + long BytesLeft = size; + buffer = new char[size + 1]; + rewind(f); + fread(buffer, 1, size, f); + fclose(f); + + location = buffer; + while (ntex < amount && BytesLeft > 0) + { + DWORD Magic = *(DWORD*)location; + if (Magic != MAKEFOURCC('D', 'D', 'S', ' ')) break; + + DDSURFACEDESC2* header = (DDSURFACEDESC2*)(location + sizeof(Magic)); + + if ((header->dwFlags & DDSD_LINEARSIZE) == 0 && (header->dwFlags & DDSD_PITCH) == 0) { + header->dwFlags |= DDSD_LINEARSIZE; + if (header->ddpfPixelFormat.dwFourCC == MAKEFOURCC('D', 'X', 'T', '5')) header->dwLinearSize = header->dwHeight * header->dwWidth; + else if (header->ddpfPixelFormat.dwFourCC == MAKEFOURCC('D', 'X', 'T', '3')) header->dwLinearSize = header->dwHeight * header->dwWidth; + else if (header->ddpfPixelFormat.dwFourCC == MAKEFOURCC('D', 'X', 'T', '1')) header->dwLinearSize = header->dwHeight * header->dwWidth / 2; + else header->dwLinearSize = header->dwHeight * header->dwWidth * header->ddpfPixelFormat.dwRGBBitCount / 8; + } + + long bytes = (header->dwFlags & DDSD_LINEARSIZE) ? header->dwLinearSize : (header->dwHeight * header->dwWidth * header->ddpfPixelFormat.dwRGBBitCount / 8); + + bytes += sizeof(Magic) + sizeof(DDSURFACEDESC2_x64); + + D3DXIMAGE_INFO Info; + LPDIRECT3DTEXTURE9 pTex = NULL; + + if (D3DXCreateTextureFromFileInMemoryEx(g_client->GetDevice(), location, bytes, 0, 0, 1, 0, D3DFMT_FROM_FILE, + D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, &Info, NULL, &pTex) == S_OK) { + ppdds[ntex] = pTex; + //LogAlw("Loaded a texture from %s, 0x%X (%u x %u)", fname, pTex, Info.Width, Info.Height); + } + else { + delete[] buffer; + LogErr("Failed to surface tile (%d tiles loaded for %s)", ntex, fname); + return ntex; + } + + location += bytes; + BytesLeft -= bytes; + ntex++; + } + delete[] buffer; + LogOk("Loaded %d textures for %s", ntex, fname); + return ntex; + } + LogWrn("File %s not found", fname); + return 0; +} + + + + + + +// ====================================================================================== +// SketchMesh Interface +// ====================================================================================== + +SketchMesh* GetSketchMesh(const MESHHANDLE hMesh) +{ + if (MeshMap.find(hMesh) == MeshMap.end()) + { + SketchMesh* pMesh = new SketchMesh(g_client->GetDevice()); + + if (pMesh->LoadMeshFromHandle(hMesh)) + { + MeshMap[hMesh] = pMesh; + return pMesh; + } + delete pMesh; + return NULL; + } + else return MeshMap[hMesh]; +} + + +SketchMesh::SketchMesh(LPDIRECT3DDEVICE9 _pDev) : + MaxVert(0), MaxIdx(0), + nGrp(0), nMtrl(0), nTex(0), + pDev(_pDev), + Tex(NULL), + Grp(NULL), + Mtrl(NULL), + pIB(NULL), + pVB(NULL) +{ +} + + +// =============================================================================================== +// +SketchMesh::~SketchMesh() +{ + SAFE_DELETEA(Mtrl); + SAFE_DELETEA(Tex); + SAFE_DELETEA(Grp); + SAFE_RELEASE(pVB); + SAFE_RELEASE(pIB); +} + + +// =============================================================================================== +// +bool SketchMesh::LoadMeshFromHandle(MESHHANDLE hMesh) +{ + pVB = NULL; + pIB = NULL; + Mtrl = NULL; + Tex = NULL; + Grp = NULL; + + MaxVert = MaxIdx = 0; + + nGrp = oapiMeshGroupCount(hMesh); + if (nGrp == 0) return false; + + Grp = new SKETCHGRP[nGrp]; + memset(Grp, 0, sizeof(SKETCHGRP) * nGrp); + + // ----------------------------------------------------------------------- + + nTex = oapiMeshTextureCount(hMesh) + 1; + Tex = new SURFHANDLE[nTex]; + Tex[0] = 0; // 'no texture' + for (DWORD i = 1; i < nTex; i++) Tex[i] = SURFACE(oapiGetTextureHandle(hMesh, i)); + + // ----------------------------------------------------------------------- + + nMtrl = oapiMeshMaterialCount(hMesh); + if (nMtrl) Mtrl = new FVECTOR4[nMtrl]; + for (DWORD i = 0; i < nMtrl; i++) { + MATERIAL* pMat = oapiMeshMaterial(hMesh, i); + if (pMat) { + Mtrl[i].r = pMat->diffuse.r; + Mtrl[i].g = pMat->diffuse.g; + Mtrl[i].b = pMat->diffuse.b; + Mtrl[i].a = pMat->diffuse.a; + } + } + + // ----------------------------------------------------------------------- + + for (DWORD i = 0; i < nGrp; i++) { + MESHGROUPEX* pEx = oapiMeshGroupEx(hMesh, i); + Grp[i].MtrlIdx = pEx->MtrlIdx; + Grp[i].TexIdx = pEx->TexIdx; + Grp[i].nVert = pEx->nVtx; + Grp[i].nIdx = pEx->nIdx; + Grp[i].VertOff = MaxVert; + Grp[i].IdxOff = MaxIdx; + MaxVert += pEx->nVtx; + MaxIdx += pEx->nIdx; + } + + if (MaxVert == 0 || MaxIdx == 0) return false; + + // ----------------------------------------------------------------------- + + if (Grp[0].MtrlIdx == SPEC_INHERIT) Grp[0].MtrlIdx = SPEC_DEFAULT; + if (Grp[0].TexIdx == SPEC_INHERIT) Grp[0].TexIdx = SPEC_DEFAULT; + + for (DWORD i = 0; i < nGrp; i++) { + + if (Grp[i].MtrlIdx == SPEC_INHERIT) Grp[i].MtrlIdx = Grp[i - 1].MtrlIdx; + + if (Grp[i].TexIdx == SPEC_DEFAULT) Grp[i].TexIdx = 0; + else if (Grp[i].TexIdx == SPEC_INHERIT) Grp[i].TexIdx = Grp[i - 1].TexIdx; + else Grp[i].TexIdx++; + } + + // ----------------------------------------------------------------------- + + HR(pDev->CreateVertexBuffer(MaxVert * sizeof(NTVERTEX), 0, 0, D3DPOOL_DEFAULT, &pVB, NULL)); + HR(pDev->CreateIndexBuffer(MaxIdx * sizeof(WORD), 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &pIB, NULL)); + + NTVERTEX* pVert = NULL; + WORD* pIndex = NULL; + + for (DWORD i = 0; i < nGrp; i++) { + MESHGROUPEX* pEx = oapiMeshGroupEx(hMesh, i); + HR(pIB->Lock(Grp[i].IdxOff * sizeof(WORD), Grp[i].nIdx * sizeof(WORD), (LPVOID*)&pIndex, 0)); + HR(pVB->Lock(Grp[i].VertOff * sizeof(NTVERTEX), Grp[i].nVert * sizeof(NTVERTEX), (LPVOID*)&pVert, 0)); + memcpy(pIndex, pEx->Idx, sizeof(WORD) * pEx->nIdx); + memcpy(pVert, pEx->Vtx, sizeof(NTVERTEX) * pEx->nVtx); + HR(pIB->Unlock()); + HR(pVB->Unlock()); + } + + return true; +} + + +// =============================================================================================== +// +void SketchMesh::Init() +{ + pDev->SetVertexDeclaration(pNTVertexDecl); + pDev->SetStreamSource(0, pVB, 0, sizeof(NTVERTEX)); + pDev->SetIndices(pIB); +} + + +// =============================================================================================== +// +void SketchMesh::RenderGroup(DWORD idx) +{ + if (!pVB) return; + pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, Grp[idx].VertOff, 0, Grp[idx].nVert, Grp[idx].IdxOff, Grp[idx].nIdx / 3); +} + + +// =============================================================================================== +// +SURFHANDLE SketchMesh::GetTexture(DWORD idx) +{ + assert(idx < nGrp); + if (Grp[idx].TexIdx) return Tex[Grp[idx].TexIdx]; + return NULL; +} + + +// =============================================================================================== +// +FVECTOR4 SketchMesh::GetMaterial(DWORD idx) +{ + assert(idx < nGrp); + if (Grp[idx].MtrlIdx != SPEC_DEFAULT && Mtrl) return Mtrl[Grp[idx].MtrlIdx]; + return F4_One; +} + + + + + +ShaderClass::ShaderClass(LPDIRECT3DDEVICE9 pDev, const char* file, const char* vs, const char* ps, const char *name, const char* options) : + pPS(), pVS(), pPSCB(NULL), pVSCB(NULL), pDev(pDev), fn(file), psn(ps), vsn(vs), sn(name) +{ + for (int i = 0; i < ARRAYSIZE(pTextures); i++) pTextures[i] = {0}; + pPS = CompilePixelShader(pDev, file, ps, name, options, &pPSCB); + pVS = CompileVertexShader(pDev, file, vs, name, options, &pVSCB); +} + + + +ShaderClass::~ShaderClass() +{ + SAFE_RELEASE(pPS); + SAFE_RELEASE(pVS); + SAFE_RELEASE(pPSCB); + SAFE_RELEASE(pVSCB); +} + + +void ShaderClass::ClearTextures() +{ + for (int idx = 0; idx < ARRAYSIZE(pTextures); idx++) + { + pTextures[idx].pAssigned = NULL; + pTextures[idx].pTex = NULL; + pTextures[idx].bSamplerSet = false; + } +} + +void ShaderClass::DetachTextures() +{ + ClearTextures(); + for (int i = 0; i < 16; i++) HR(pDev->SetTexture(i, NULL)); + + HR(pDev->SetTexture(D3DVERTEXTEXTURESAMPLER0, NULL)); + HR(pDev->SetTexture(D3DVERTEXTEXTURESAMPLER1, NULL)); + HR(pDev->SetTexture(D3DVERTEXTEXTURESAMPLER2, NULL)); + HR(pDev->SetTexture(D3DVERTEXTEXTURESAMPLER3, NULL)); +} + + +void ShaderClass::UpdateTextures() +{ + // Set textures and samplers ----------------------------------------------- + // + for (int idx = 0; idx < ARRAYSIZE(pTextures); idx++) + { + int sid = idx > 15 ? idx - 16 + D3DVERTEXTEXTURESAMPLER0 : idx; + + if (pTextures[idx].pTex == NULL) continue; + + // If sampler state is not set, then set it + // + if (!pTextures[idx].bSamplerSet) + { + pTextures[idx].bSamplerSet = true; + DWORD flags = pTextures[idx].Flags; + + if (flags & IPF_CLAMP_U) pDev->SetSamplerState(sid, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + else if (flags & IPF_MIRROR_U) pDev->SetSamplerState(sid, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR); + else pDev->SetSamplerState(sid, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); + + if (flags & IPF_CLAMP_V) pDev->SetSamplerState(sid, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + else if (flags & IPF_MIRROR_V) pDev->SetSamplerState(sid, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR); + else pDev->SetSamplerState(sid, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); + + if (flags & IPF_CLAMP_W) pDev->SetSamplerState(sid, D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP); + else if (flags & IPF_MIRROR_W) pDev->SetSamplerState(sid, D3DSAMP_ADDRESSW, D3DTADDRESS_MIRROR); + else pDev->SetSamplerState(sid, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP); + + DWORD filter = D3DTEXF_POINT; + + if (flags & IPF_LINEAR) filter = D3DTEXF_LINEAR; + if (flags & IPF_PYRAMIDAL) filter = D3DTEXF_PYRAMIDALQUAD; + if (flags & IPF_GAUSSIAN) filter = D3DTEXF_GAUSSIANQUAD; + if (flags & IPF_ANISOTROPIC) filter = D3DTEXF_ANISOTROPIC; + + HR(pDev->SetSamplerState(sid, D3DSAMP_SRGBTEXTURE, false)); + HR(pDev->SetSamplerState(sid, D3DSAMP_MAXANISOTROPY, pTextures[idx].AnisoLvl)); + HR(pDev->SetSamplerState(sid, D3DSAMP_MAGFILTER, filter)); + HR(pDev->SetSamplerState(sid, D3DSAMP_MINFILTER, filter)); + if (idx <= 15) { HR(pDev->SetSamplerState(sid, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR)); } + else { HR(pDev->SetSamplerState(sid, D3DSAMP_MIPFILTER, D3DTEXF_POINT)); } + } + + // If texture has changed then assign it + if (pTextures[idx].pTex != pTextures[idx].pAssigned) + { + pTextures[idx].pAssigned = pTextures[idx].pTex; + HR(pDev->SetTexture(sid, pTextures[idx].pTex)); + } + } +} + + +void ShaderClass::Setup(LPDIRECT3DVERTEXDECLARATION9 pDecl, bool bZ, int blend) +{ + D3DSURFACE_DESC desc; + LPDIRECT3DSURFACE9 pTgt = NULL; + + HR(pDev->GetRenderTarget(0, &pTgt)); + + if (pTgt) pTgt->GetDesc(&desc); + else { + LogErr("ShaderClass::Setup No render target is set"); + return; + } + + SAFE_RELEASE(pTgt); + + D3DVIEWPORT9 VP; + VP.X = 0; + VP.Y = 0; + VP.Width = desc.Width; + VP.Height = desc.Height; + VP.MinZ = 0.0f; + VP.MaxZ = 1.0f; + + HR(pDev->SetViewport(&VP)); + + HR(pDev->SetVertexShader(pVS)); + HR(pDev->SetPixelShader(pPS)); + + if (pDecl) HR(pDev->SetVertexDeclaration(pDecl)); + + HR(pDev->SetRenderState(D3DRS_POINTSPRITEENABLE, false)); + HR(pDev->SetRenderState(D3DRS_ALPHATESTENABLE, false)); + HR(pDev->SetRenderState(D3DRS_STENCILENABLE, false)); + HR(pDev->SetRenderState(D3DRS_COLORWRITEENABLE, 0xF)); + + HR(pDev->SetRenderState(D3DRS_ZENABLE, bZ)); + HR(pDev->SetRenderState(D3DRS_ZWRITEENABLE, bZ)); + + HR(pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, (blend != 0))); + + if (blend == 1) { + HR(pDev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD)); + HR(pDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA)); + HR(pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA)); + } + + if (blend == 2) { + HR(pDev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD)); + HR(pDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE)); + HR(pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA)); + } + + if (blend == 3) { + HR(pDev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MAX)); + HR(pDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE)); + HR(pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE)); + } + + if (blend == 4) { + HR(pDev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD)); + HR(pDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA)); + HR(pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE)); + } +} + + +HANDLE ShaderClass::GetPSHandle(const char* name) +{ + D3DXHANDLE hVar = pPSCB->GetConstantByName(NULL, name); + return HANDLE(hVar); +} + + +HANDLE ShaderClass::GetVSHandle(const char* name) +{ + D3DXHANDLE hVar = pVSCB->GetConstantByName(NULL, name); + return HANDLE(hVar); +} + + + +void ShaderClass::SetTexture(const char* name, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso) +{ + D3DXHANDLE hVar = pPSCB->GetConstantByName(NULL, name); + SetTexture((HANDLE)hVar, pTex, flags, aniso); +} + + +void ShaderClass::SetTextureVS(const char* name, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso) +{ + D3DXHANDLE hVar = pVSCB->GetConstantByName(NULL, name); + SetTextureVS((HANDLE)hVar, pTex, flags, aniso); +} + +void ShaderClass::SetPSConstants(const char* name, void* data, UINT bytes) +{ + D3DXHANDLE hVar = pPSCB->GetConstantByName(NULL, name); +#ifdef SHDCLSDBG + if (!hVar) { + LogErr("Shader::SetPSConstants() Invalid variable name [%s]. File[%s], Entrypoint[%s], Shader[%s]", name, fn.c_str(), psn.c_str(), sn.c_str()); + assert(false); + } +#endif + if (!hVar) return; + if (pPSCB->SetValue(pDev, hVar, data, bytes) != S_OK) { + LogErr("Shader::SetPSConstants() Failed. Variable[%s], File[%s], Entrypoint[%s]", name, fn.c_str(), psn.c_str()); + } +} + +void ShaderClass::SetVSConstants(const char* name, void* data, UINT bytes) +{ + D3DXHANDLE hVar = pVSCB->GetConstantByName(NULL, name); +#ifdef SHDCLSDBG + if (!hVar) { + LogErr("Shader::SetVSConstants() Invalid variable name [%s]. File[%s], Entrypoint[%s], Shader[%s]", name, fn.c_str(), psn.c_str(), sn.c_str()); + assert(false); + } +#endif + if (!hVar) return; + if (pVSCB->SetValue(pDev, hVar, data, bytes) != S_OK) { + LogErr("Shader::SetVSConstants() Failed. Variable[%s], File[%s], Entrypoint[%s]", name, fn.c_str(), vsn.c_str()); + } +} + + + +void ShaderClass::SetTexture(HANDLE hVar, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso) +{ +#ifdef SHDCLSDBG + if (!hVar) { + LogErr("Shader::SetTexture() Invalid handle. File[%s], Entrypoint[%s], Shader[%s]", fn.c_str(), psn.c_str(), sn.c_str()); + assert(false); + } +#endif + if (!hVar) return; + DWORD idx = pPSCB->GetSamplerIndex(D3DXHANDLE(hVar)); + + if (!pTex) { + pTextures[idx].pTex = NULL; + return; + } + + if (pTextures[idx].Flags != flags) pTextures[idx].bSamplerSet = false; + if (pTextures[idx].AnisoLvl != aniso) pTextures[idx].bSamplerSet = false; + + pTextures[idx].pTex = pTex; + pTextures[idx].Flags = flags; + pTextures[idx].AnisoLvl = aniso; +} + + +void ShaderClass::SetTextureVS(HANDLE hVar, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso) +{ +#ifdef SHDCLSDBG + if (!hVar) { + LogErr("Shader::SetTextureVS() Invalid handle. File[%s], Entrypoint[%s], Shader[%s]", fn.c_str(), psn.c_str(), sn.c_str()); + assert(false); + } +#endif + if (!hVar) return; + DWORD idx = pVSCB->GetSamplerIndex(D3DXHANDLE(hVar)) + 16; + assert(idx < 20); + + if (!pTex) { + pTextures[idx].pTex = NULL; + return; + } + + if (pTextures[idx].Flags != flags) pTextures[idx].bSamplerSet = false; + if (pTextures[idx].AnisoLvl != aniso) pTextures[idx].bSamplerSet = false; + + pTextures[idx].pTex = pTex; + pTextures[idx].Flags = flags | IPF_VERTEXTEX; + pTextures[idx].AnisoLvl = aniso; +} + + +void ShaderClass::SetPSConstants(HANDLE hVar, void* data, UINT bytes) +{ +#ifdef SHDCLSDBG + if (!hVar) { + LogErr("Shader::SetPSConstants() Invalid handle. File[%s], Entrypoint[%s], Shader[%s]", fn.c_str(), psn.c_str(), sn.c_str()); + assert(false); + } +#endif + if (!hVar) return; + if (pVSCB->SetValue(pDev, D3DXHANDLE(hVar), data, bytes) != S_OK) { + LogErr("Shader::SetPSConstants() Failed. File[%s], Entrypoint[%s]", fn.c_str(), vsn.c_str()); + } +} + + +void ShaderClass::SetVSConstants(HANDLE hVar, void* data, UINT bytes) +{ +#ifdef SHDCLSDBG + if (!hVar) { + LogErr("Shader::SetVSConstants() Invalid handle. File[%s], Entrypoint[%s], Shader[%s]", fn.c_str(), psn.c_str(), sn.c_str()); + assert(false); + } +#endif + if (!hVar) return; + if (pVSCB->SetValue(pDev, D3DXHANDLE(hVar), data, bytes) != S_OK) { + LogErr("Shader::SetVSConstants() Failed. File[%s], Entrypoint[%s]", fn.c_str(), vsn.c_str()); + } +} + diff --git a/OVP/VulkanClient/Util.h b/OVP/VulkanClient/Util.h new file mode 100644 index 000000000..9ed9c8877 --- /dev/null +++ b/OVP/VulkanClient/Util.h @@ -0,0 +1,712 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __D3DUTIL_H +#define __D3DUTIL_H + +#include "OrbiterAPI.h" +#include "Log.h" +#include "DrawAPI.h" +#include +#include +#include "MathAPI.h" +#include +#include "gcCore.h" + +#define float2 FVECTOR2 +#define float3 FVECTOR3 +#define float4 FVECTOR4 +#define float4x4 FMATRIX4 + +#ifdef _DEBUG +#ifndef _TRACE +#define _TRACE { LogTrace("[TRACE] %s Line:%d %s",__FILE__,__LINE__,__FUNCTION__); } +#endif +#else +#ifndef _TRACE +#define _TRACE +#endif +#endif + + + +/* +#ifndef HR +#define HR(x) x; +#endif +*/ + +#ifndef HR +#define HR(x) \ +{ \ + HRESULT hr = x; \ + if(FAILED(hr)) \ + { \ + LogErr("%s Line:%d Error:%d %s",__FILE__,__LINE__,hr,(#x)); \ + } \ +} +#endif + + +inline bool _HROK(HRESULT hr, const char *file, int line) +{ + if (hr==S_OK) return true; + else LogErr("%s Line:%d Error:%d", file, line, hr); + return false; +} + +const char *_PTR(const void *p); + + +#ifndef HROK +#define HROK(x) _HROK(x, __FILE__, __LINE__) +#endif + +#define PI 3.141592653589793238462643383279 + +#define SURFACE(x) ((class SurfNative *)x) + +// helper function to get address of a temporary +// The regular "easy" way no longer works on some compilers so lets use a hack to get a simple thing done. +// NB: use with caution +template +T* ptr(T&& x) { return &x; } + +// ------------------------------------------------------------------------------------ +// Vertex Declaration equal to NTVERTEX +// ------------------------------------------------------------------------------------ + +const D3DVERTEXELEMENT9 BAVertexDecl[] = { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0}, + {0, 24, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + {0, 40, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1}, + {0, 48, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 NTVertexDecl[] = { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0}, + {0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 MeshVertexDecl[] = { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0}, + {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0}, + {0, 36, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 PatchVertexDecl[] = { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0}, + {0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + {0, 32, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 PosTexDecl[] = { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 PosColorDecl[] = { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 PositionDecl[] = { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 SketchpadDecl[] = { + { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, + { 0, 12, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, + { 0, 28, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }, + { 0, 32, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1 }, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 Vector4Decl[] = { + {0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 GPUBlitDecl[] = { + {0, 0, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 HazeVertexDecl[] = { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, + {0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + D3DDECL_END() +}; + +const D3DVERTEXELEMENT9 LocalLightsDecl[] = { + {0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, //Primitive Index + {0, 4, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, //Position .xyz and cone .w + D3DDECL_END() +}; + +typedef struct +{ + float index; + FVECTOR3 pos; + float cone; +} LocalLightsCompute; + +typedef struct { + float x; ///< vertex x position + float y; ///< vertex y position + float z; ///< vertex z position + float tu; ///< vertex u texture coordinate + float tv; ///< vertex v texture coordinate +} SMVERTEX; + +typedef struct { + float x, y, z; + float nx, ny, nz; + float tx, ty, tz; + float u, v, w; +} NMVERTEX; + +typedef struct { + short tx,ty; + short sx,sy; +} GPUBLITVTX; + + +typedef struct { + FVECTOR3 pos; ///< beacon position + FVECTOR3 dir; ///< light direction + float size, angle, on, off; ///< beacon size and light cone angle + float bright, falloff; + DWORD color; ///< beacon color +} BAVERTEX; + +typedef struct _LightStruct { + int Type; ///< Type of light source + float Dst2; ///< Square distance between camera and the light emitter + FVECTOR4 Diffuse; ///< Color of light + FVECTOR3 Position; ///< position in world space + FVECTOR3 Direction; ///< direction in world space + FVECTOR3 Attenuation; ///< Attenuation + FVECTOR4 Param; ///< range, falloff, theta, phi +public : _LightStruct () : Type(0), + Dst2(0.0), + Diffuse(0ul), + Position(), Direction(1.0f, 0.0f, 0.0f), Attenuation(1.0f, 1.0f, 1.0f), + Param() + {} +} LightStruct; + + +class vkLight : public LightStruct +{ +public: + vkLight(); + vkLight(const LightEmitter *le, const class vObject *vo); + ~vkLight(); + + float GetIlluminance(FVECTOR3 &pos, float r) const; + void UpdateLight(const LightEmitter *le, const class vObject *vo); + void Reset(); + const LightEmitter *GetEmitter() const; + + float cone; + int GPUId; +private: + float cosp, tanp, cosu; + float range, range2; + float intensity; + const LightEmitter *le; +}; + + + +class SketchMesh +{ + +public: + + struct SKETCHGRP { // mesh group definition + DWORD VertOff; // Main mesh Vertex Offset + DWORD IdxOff; // Main mesh Index Offset + DWORD nIdx; // Index count + DWORD nVert; // Vertex count + DWORD MtrlIdx; // material index + DWORD TexIdx; // texture index 0=None + }; + + explicit SketchMesh(LPDIRECT3DDEVICE9 pDev); + ~SketchMesh(); + + void Init(); + bool LoadMeshFromHandle(MESHHANDLE hMesh); + void RenderGroup(DWORD idx); + SURFHANDLE GetTexture(DWORD idx); + FVECTOR4 GetMaterial(DWORD idx); + DWORD GroupCount() const { return nGrp; } + +private: + + LPDIRECT3DVERTEXBUFFER9 pVB; ///< (Local) Vertex buffer pointer + LPDIRECT3DINDEXBUFFER9 pIB; + + DWORD MaxVert; + DWORD MaxIdx; + + DWORD nGrp; // number of mesh groups + DWORD nMtrl; // number of mesh materials + DWORD nTex; // number of mesh textures + + LPDIRECT3DDEVICE9 pDev; + SURFHANDLE* Tex; // list of mesh textures + SKETCHGRP* Grp; // list of mesh groups + FVECTOR4* Mtrl; +}; + +#pragma pack(push, 4) + +typedef struct { + FVECTOR3 Dir; + FVECTOR3 Color; // Color and Intensity of received sunlight + FVECTOR3 Ambient; // Ambient light level (Base Objects Only, Vessels are using dynamic methods) + FVECTOR3 Transmission; // Visibility through atmosphere (1.0 = fully visible, 0.0 = obscured) + FVECTOR3 Incatter; // Amount of incattered light from haze +} vkSun; + + +#define vkMATEX_DIFFUSE 0x001 +#define vkMATEX_AMBIENT 0x002 +#define vkMATEX_SPECULAR 0x004 +#define vkMATEX_EMISSIVE 0x008 +#define vkMATEX_REFLECT 0x010 +#define vkMATEX_FRESNEL 0x040 +#define vkMATEX_ROUGHNESS 0x080 +#define vkMATEX_EMISSION2 0x100 +#define vkMATEX_METALNESS 0x200 +#define vkMATEX_SPECIALFX 0x400 + + +/** + * \brief Material structure used in vkMesh. ModFlags is not loaded to shaders + */ +typedef struct { + FVECTOR4 Diffuse; + FVECTOR4 Specular; ///< Specular color, power in alpha + FVECTOR3 Ambient; + FVECTOR3 Emissive; + FVECTOR3 Reflect; ///< Color multiplier and intensity (alpha) + FVECTOR3 Emission2; ///< + FVECTOR3 Fresnel; ///< Fresnel reflection + FVECTOR2 Roughness; ///< + float Metalness; + FVECTOR4 SpecialFX; + // ----------------------- + DWORD ModFlags; ///< Modification flags +} vkMatExt; + +#pragma pack(pop) + +typedef struct { + class vkMesh *pMesh; ///< Mesh handle + class vObject *vObj; ///< Visual handle + float dist; ///< Distance to a pick point + int group; ///< Mesh group that was picked + FVECTOR3 normal; ///< Normal vector in local vessel coordinates + FVECTOR3 pos; ///< Position in local vessel coordinates + int idx; ///< Index that was picked + float u, v; ///< Barycentric coordinates +} vkPick; + +typedef struct { + FVECTOR3 _p; // Position from camera + FVECTOR3 _n; // Normal + int i; // Face Index + float d; // Distance from camera + float u, v; + double lng, lat, elev; + class Tile * pTile; +} TILEPICK; + +#define vkLRange 0 +#define vkLFalloff 1 +#define vkLTheta 2 +#define vkLPhi 3 + +extern IDirect3DVertexDeclaration9 *pMeshVertexDecl; +extern IDirect3DVertexDeclaration9 *pHazeVertexDecl; +extern IDirect3DVertexDeclaration9 *pNTVertexDecl; +extern IDirect3DVertexDeclaration9 *pBAVertexDecl; +extern IDirect3DVertexDeclaration9 *pPosColorDecl; +extern IDirect3DVertexDeclaration9 *pPositionDecl; +extern IDirect3DVertexDeclaration9 *pVector4Decl; +extern IDirect3DVertexDeclaration9 *pPosTexDecl; +extern IDirect3DVertexDeclaration9 *pPatchVertexDecl; +extern IDirect3DVertexDeclaration9 *pSketchpadDecl; +extern IDirect3DVertexDeclaration9 *pLocalLightsDecl; + + + +class ShaderClass +{ + + +public: + ShaderClass(LPDIRECT3DDEVICE9 pDev, const char* file, const char* vs, const char* ps, const char* name, const char* options); + ~ShaderClass(); + void ClearTextures(); + void UpdateTextures(); + void DetachTextures(); + void Setup(LPDIRECT3DVERTEXDECLARATION9 pDecl, bool bZ, int blend); + HANDLE GetPSHandle(const char* name); + HANDLE GetVSHandle(const char* name); + + void SetTexture(const char* name, LPDIRECT3DBASETEXTURE9 pTex, UINT Flags = IPF_CLAMP | IPF_ANISOTROPIC, UINT AnisoLvl = 4); + void SetTextureVS(const char* name, LPDIRECT3DBASETEXTURE9 pTex, UINT flags = IPF_CLAMP | IPF_POINT, UINT AnisoLvl = 0); + void SetPSConstants(const char* name, void* data, UINT bytes); + void SetVSConstants(const char* name, void* data, UINT bytes); + + void SetTexture(HANDLE hVar, LPDIRECT3DBASETEXTURE9 pTex, UINT flags = IPF_CLAMP | IPF_POINT, UINT AnisoLvl = 0); + void SetTextureVS(HANDLE hVar, LPDIRECT3DBASETEXTURE9 pTex, UINT flags, UINT aniso); + void SetPSConstants(HANDLE hVar, void* data, UINT bytes); + void SetVSConstants(HANDLE hVar, void* data, UINT bytes); + LPDIRECT3DDEVICE9 GetDevice() { return pDev; } + +private: + + struct TexParams + { + LPDIRECT3DBASETEXTURE9 pTex; + LPDIRECT3DBASETEXTURE9 pAssigned; + UINT Flags; + UINT AnisoLvl; + bool bSamplerSet; + } pTextures[20]; + + LPD3DXCONSTANTTABLE pPSCB, pVSCB; + LPDIRECT3DPIXELSHADER9 pPS; + LPDIRECT3DVERTEXSHADER9 pVS; + LPDIRECT3DDEVICE9 pDev; + std::string fn, psn, vsn, sn; +}; + + + +inline void swap(double &a, double &b) +{ + double c = a; a = b; b = c; +} + +// Jump between western and eastern hemispheres +inline double wrap(double a) +{ + if (a<-PI) return a+PI2; + if (a>PI) return a-PI2; + return a; +} + +inline void LogSunLight(vkSun& s) +{ + LogAlw("Sunlight.Dir = [%f, %f, %f]", s.Dir.x, s.Dir.y, s.Dir.z); + LogAlw("Sunlight.Color = [%f, %f, %f]", s.Color.x, s.Color.y, s.Color.z); + LogAlw("Sunlight.Ambie = [%f, %f, %f]", s.Ambient.x, s.Ambient.y, s.Ambient.z); + LogAlw("Sunlight.Trans = [%f, %f, %f]", s.Transmission.x, s.Transmission.y, s.Transmission.z); + LogAlw("Sunlight.Incat = [%f, %f, %f]", s.Incatter.x, s.Incatter.y, s.Incatter.z); +} + + + +inline void D3DXCOLORSWAP(FVECTOR4 *x) +{ + float a = x->r; x->r = x->b; x->b = a; +} + +inline FVECTOR4 D3DCOLORMULT(const FVECTOR4*a, const FVECTOR4*b) +{ + FVECTOR4 c; + c.a = a->a * b->a; + c.r = a->r * b->r; + c.g = a->g * b->g; + c.b = a->b * b->b; + return c; +} + +inline FMATRIX4 _FMATRIX (const MATRIX4 &M) +{ + FMATRIX4 D; + D.m11 = (float)M.m11; D.m12 = (float)M.m12; D.m13 = (float)M.m13; D.m14 = (float)M.m14; + D.m21 = (float)M.m21; D.m22 = (float)M.m22; D.m23 = (float)M.m23; D.m24 = (float)M.m24; + D.m31 = (float)M.m31; D.m32 = (float)M.m32; D.m33 = (float)M.m33; D.m34 = (float)M.m34; + D.m41 = (float)M.m41; D.m42 = (float)M.m42; D.m43 = (float)M.m43; D.m44 = (float)M.m44; + return D; +} + +inline MATRIX4 _MATRIX4(const FMATRIX4* M) +{ + MATRIX4 D; + D.m11 = (double)M->m11; D.m12 = (double)M->m12; D.m13 = (double)M->m13; D.m14 = (double)M->m14; + D.m21 = (double)M->m21; D.m22 = (double)M->m22; D.m23 = (double)M->m23; D.m24 = (double)M->m24; + D.m31 = (double)M->m31; D.m32 = (double)M->m32; D.m33 = (double)M->m33; D.m34 = (double)M->m34; + D.m41 = (double)M->m41; D.m42 = (double)M->m42; D.m43 = (double)M->m43; D.m44 = (double)M->m44; + return D; +} + +inline void TransformVertex(NMVERTEX *pVrt, const FMATRIX4* pW) +{ + FVECTOR3 p = oapiTransformCoord(ptr(FVECTOR3(pVrt->x, pVrt->y, pVrt->z)), pW); + FVECTOR3 n = oapiTransformNormal(ptr(FVECTOR3(pVrt->nx, pVrt->ny, pVrt->nz)), pW); + FVECTOR3 t = oapiTransformNormal(ptr(FVECTOR3(pVrt->tx, pVrt->ty, pVrt->tz)), pW); + pVrt->x = p.x; pVrt->y = p.y; pVrt->z = p.z; + pVrt->nx = n.x; pVrt->ny = n.y; pVrt->nz = n.z; + pVrt->tx = t.x; pVrt->ty = t.y; pVrt->tz = t.z; +} + +int fgets2(char *buf, int cmax, FILE *file, DWORD param=0); + +float D3DXVec3Angle(FVECTOR3 a, FVECTOR3 b); +FVECTOR3 Perpendicular(FVECTOR3 *a); + +const char *RemovePath(const char *in); +SketchMesh * GetSketchMesh(const MESHHANDLE hMesh); + +bool CreateVolumeTexture(LPDIRECT3DDEVICE9 pDevice, int count, LPDIRECT3DTEXTURE9 *pIn, LPDIRECT3DVOLUMETEXTURE9 *pOut); + +void CreateMatExt(const D3DMATERIAL9 *pIn, vkMatExt *pOut); +void UpdateMatExt(const D3DMATERIAL9 *pIn, vkMatExt *pOut); +void CreateDefaultMat(vkMatExt *pOut); +void GetMatExt(const vkMatExt *pIn, D3DMATERIAL9 *pOut); +bool CopyBuffer(LPDIRECT3DRESOURCE9 _pDst, LPDIRECT3DRESOURCE9 _pSrc); +int LoadPlanetTextures(const char* fname, LPDIRECT3DTEXTURE9* ppdds, DWORD flags, int amount); +float SunOcclusionByPlanet(OBJHANDLE hObj, VECTOR3 gpos); +float OcclusionFactor(float x, float sunrad, float plnrad); +float OcclusionFactor(float x, float r1, float r2, bool bReverse); +double Distance(vObject *a, vObject* b); +bool IsCastingShadows(vObject* body, vObject* ref, double* sunsize_out); + +LPDIRECT3DPIXELSHADER9 CompilePixelShader(LPDIRECT3DDEVICE9 pDev, const char *file, const char *function, const char* name, const char *options, LPD3DXCONSTANTTABLE *pConst); +LPDIRECT3DVERTEXSHADER9 CompileVertexShader(LPDIRECT3DDEVICE9 pDev, const char *file, const char *function, const char* name, const char *options, LPD3DXCONSTANTTABLE *pConst); + +DWORD BuildDate(); + +// ------------------------------------------------------------------------------------ +// D3D vector and matrix operations +// ------------------------------------------------------------------------------------ + +float D3DMAT_BSScaleFactor(const FMATRIX4 *mat); +void D3DMAT_ZeroMatrix(FMATRIX4 *mat); +void D3DMAT_Copy (FMATRIX4 *tgt, const FMATRIX4 *src); +void D3DMAT_Scale(FMATRIX4* mat, float x, float y, float z); +void D3DMAT_SetRotation (FMATRIX4 *mat, const MATRIX3 *rot); +void D3DMAT_SetInvRotation (FMATRIX4 *mat, const MATRIX3 *rot); +void D3DMAT_RotationFromAxis (const FVECTOR3 &axis, float angle, FMATRIX4 *rot); +void D3DMAT_FromAxis(FMATRIX4 *out, const FVECTOR3 *x, const FVECTOR3 *y, const FVECTOR3 *z); +void D3DMAT_FromAxis(FMATRIX4 *out, const VECTOR3 *x, const VECTOR3 *y, const VECTOR3 *z); +void D3DMAT_FromAxisT(FMATRIX4 *out, const FVECTOR3 *x, const FVECTOR3 *y, const FVECTOR3 *z); +void D3DMAT_CreateX_Billboard(const FVECTOR3 *toCam, const FVECTOR3 *pos, float scale, FMATRIX4 *pOut); +void D3DMAT_CreateX_Billboard(const FVECTOR3 *toCam, const FVECTOR3 *pos, const FVECTOR3 *dir, float size, float stretch, FMATRIX4 *pOut); +void D3DMAT_Transformation2D(FMATRIX4* pOut, const FVECTOR2* pSclCtr, float SclRot, const FVECTOR2* pScl, const FVECTOR2* pRotCtr, float Rot, const FVECTOR2* pTransl); +void D3DMAT_AffineTransformation2D(FMATRIX4* pOut, float Scl, const FVECTOR2* pRotCtr, float Rot, const FVECTOR2* pTransl); + +void D3DMAT_OrthoOffCenterLH(FMATRIX4* o, float l, float r, float b, float t, float zn, float zf); +void D3DMAT_OrthoOffCenterRH(FMATRIX4* o, float l, float r, float b, float t, float zn, float zf); +void D3DMAT_LookAtRH(FMATRIX4* o, const FVECTOR3* pEye, const FVECTOR3* pAt, const FVECTOR3* pUp); + +// Set up a as matrix for ANTICLOCKWISE rotation r around x/y/z-axis +void D3DMAT_RotX (FMATRIX4 *mat, double r); +void D3DMAT_RotY (FMATRIX4 *mat, double r); + +void D3DMAT_SetTranslation(FMATRIX4 *mat, const VECTOR3 *trans); +void D3DMAT_SetTranslation(FMATRIX4 *mat, const FVECTOR3 *trans); + +void D3DMAT_Transform(FVECTOR4* o, const FVECTOR4* i, const FMATRIX4* m); + +// ------------------------------------------------------------------------------------ +// Vertex formats +// ------------------------------------------------------------------------------------ +struct VERTEX_XYZ { float x, y, z; }; // transformed vertex +struct VERTEX_XYZC { float x, y, z; DWORD col; }; // untransformed vertex with single colour component + +// untransformed lit vertex with texture coordinates +struct VERTEX_XYZ_TEX { + float x, y, z; + float tu, tv; +}; + +// untransformed unlit vertex with two sets of texture coordinates +struct VERTEX_2TEX { + float x, y, z, nx, ny, nz; + float tu0, tv0, e; + inline VERTEX_2TEX() : x(0.0f), y(0.0f), z(0.0f), nx(0.0f), ny(0.0f), nz(0.0f), + tu0(0.0f), tv0(0.0f), e(0.0f) {} + inline VERTEX_2TEX(const FVECTOR3& p, const FVECTOR3& n, float u0, float v0, float u1, float v1) + : x(p.x), y(p.y), z(p.z), nx(n.x), ny(n.y), nz(n.z), + tu0(u0), tv0(v0), e(0.0f) {} +}; + +// ----------------------------------------------------------------------------------- +// String helper +// ------------------------------------------------------------------------------------ + +// trim from start +std::string <rim (std::string &s); + +// trim from end +std::string &rtrim (std::string &s); + +// trim from both ends +std::string &trim (std::string &s); + +// uppercase complete string +void toUpper (std::string &s); + +// lowercase complete string +//void toLower (std::string &s); + +// string to double (returns quiet_NaN if conversion failed) +double toDoubleOrNaN (const std::string &str); + +// case insensitive compare +bool startsWith (const std::string &haystack, const std::string &needle); + +// case insensitive contains +bool contains (const std::string &haystack, const std::string &needle); + +// case insensitive find +size_t find_ci (const std::string &haystack, const std::string &needle); + +// case insensitive rfind +size_t rfind_ci (const std::string &haystack, const std::string &needle); + +// parse assignments like "foo=bar", "foo = bar" or even "foo= bar ; with comment" +std::pair &splitAssignment (const std::string &line, const char delim = '='); + +// replace all occurrences of 's' in 'subj' by 't' +std::string::size_type replace_all (std::string &subj, const std::string &s, const std::string &t); + +// ----------------------------------------------------------------------------------- +// Resource handling +// ------------------------------------------------------------------------------------ +/** + * \brief Kind of auto_handle. + * This simple wrapper acts like 'auto_ptr' but is for HANDLE. It is used to + * avoid any not-closed HANDLES leaks when the block scope is left (via thrown + * exception or return e.g.) + */ +struct AutoHandle +{ + HANDLE Handle; ///< The handle storage + + AutoHandle () { + Handle = NULL; + } + + ~AutoHandle () { + ForceClose(); + } + + /** + * \brief Check whether the handle is not valid + */ + bool IsInvalid () { + return Handle == INVALID_HANDLE_VALUE || Handle == NULL; + } + + /** + * \brief Close the handle + */ + void ForceClose() + { + if (!IsInvalid()) { + CloseHandle(Handle); + } + Handle = NULL; + } +}; + + +/** + * \brief Kind of auto_file. + * This simple wrapper acts like 'auto_ptr' but is for FILE pointer. It is used + * to avoid any not-closed FILES leaks when the block scope is left (via thrown + * exception or return e.g.) + */ +struct AutoFile +{ + FILE* pFile; ///< The file handle storage + + AutoFile () { + pFile = NULL; + } + + ~AutoFile () { + ForceClose(); + } + + /** + * \brief Check whether the file is not valid + */ + bool IsInvalid () { + return pFile == NULL; + } + + /** + * \brief Close the file + */ + void ForceClose() + { + if (!IsInvalid()) { + fclose(pFile); + } + pFile = NULL; + } +}; + + +// ----------------------------------------------------------------------------------- +// Conversion functions +// ------------------------------------------------------------------------------------ + +inline RECT _RECT(DWORD l, DWORD t, DWORD r, DWORD b) +{ + RECT rect = { long(l), long(t), long(r), long(b) }; + return rect; +} + + + + +inline FVECTOR3 _F(const XMFLOAT3& i) { return FVECTOR3(float(i.x), float(i.y), float(i.z)); } + + +inline const D3DXVECTOR3* _DX(const FVECTOR3& a) { return (D3DXVECTOR3*)&a; } +inline const D3DXVECTOR4* _DX(const FVECTOR4& a) { return (D3DXVECTOR4*)&a; } +inline const D3DXVECTOR3* _DX(const FVECTOR3* a) { return (D3DXVECTOR3*)a; } +inline const D3DXVECTOR4* _DX(const FVECTOR4* a) { return (D3DXVECTOR4*)a; } +inline const D3DXMATRIX* _DX(const FMATRIX4& a) { return (D3DXMATRIX*)&a; } +inline const D3DXMATRIX* _DX(const FMATRIX4* a) { return (D3DXMATRIX*)a; } + +// ------------------------------------------------------------------------------------ +// Miscellaneous helper functions +// ------------------------------------------------------------------------------------ + +#define DELETE_SURFACE(p) { if (p) { delete ((SurfNative*)p); p = NULL; } } +#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } +#define SAFE_DELETEA(p) { if(p) { delete []p; (p)=NULL; } } +#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } +#define CLEARARRAY(p) { memset(p, 0, sizeof(p)); } + +#endif // !__D3DUTIL_H diff --git a/OVP/VulkanClient/VBase.cpp b/OVP/VulkanClient/VBase.cpp new file mode 100644 index 000000000..54ee22528 --- /dev/null +++ b/OVP/VulkanClient/VBase.cpp @@ -0,0 +1,556 @@ +// ============================================================== +// VBase.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007 - 2016 Martin Schweiger +// 2011 - 2016 Jarmo Nikkanen (vkClient modification) +// ============================================================== + +// ============================================================== +// class vBase (implementation) +// +// A vBase is the visual representation of a surface base +// object (a "spaceport" on the surface of a planet or moon, +// usually with runways or landing pads where vessels can +// land and take off. +// ============================================================== + +#include "VBase.h" +#include "TileMgr.h" +#include "Client.h" +#include "Surface.h" +#include "BeaconArray.h" +#include "RunwayLights.h" +#include "AABBUtil.h" +#include "OrbiterAPI.h" +#include "DebugControls.h" +#include "Config.h" +#include "VPlanet.h" + +#pragma warning(push) +#pragma warning(disable : 4838) +#include +#pragma warning(pop) + +typedef struct { + float rad; + float width, length, height; + FVECTOR3 pos, min, max; +} MeshStats; + + +void CheckMeshStats(MESHHANDLE hMesh, MeshStats *stats) +{ + int nGrp = oapiMeshGroupCount(hMesh); + if (nGrp == 0) return; + + XMVECTOR mi = XMLoadFloat3(&(XMFLOAT3(1e12f, 1e12f, 1e12f))); + XMVECTOR mx = -mi; + + for (int i = 0; i < nGrp; i++) { + + MESHGROUPEX *grp = oapiMeshGroupEx(hMesh, i); + + for (DWORD v = 0; v < grp->nVtx; v++) { + XMVECTOR x = XMLoadFloat3((XMFLOAT3 *)&grp->Vtx[v].x); + mi = XMVectorMin(mi, x); + mx = XMVectorMax(mx, x); + } + } + + XMStoreFloat3((XMFLOAT3 *)&stats->min.x, mi); + XMStoreFloat3((XMFLOAT3 *)&stats->max.x, mx); + + stats->width = stats->max.x - stats->min.x; + stats->height = stats->max.y - stats->min.y; + stats->length = stats->max.z - stats->min.z; + stats->pos = (stats->max + stats->min) * 0.5f; + stats->rad = length(stats->max + stats->min) * 0.5f; +} + + + +vBase::vBase (OBJHANDLE _hObj, const Scene *scene, vPlanet *_vP): vObject (_hObj, scene) +{ + _TRACE; + DWORD i,j; + + vP = _vP; + hPlanet = oapiGetBasePlanet(hObj); + + if (!vP) vP = static_cast( scene->GetVisObject(hPlanet) ); + + structure_bs = NULL; + structure_as = NULL; + nstructure_bs = 0; + nstructure_as = 0; + tspec = NULL; + tilemesh = NULL; + numRunwayLights = 0; + numTaxiLights = 0; + runwayLights = NULL; + taxiLights = NULL; + csun_lights = RAD * Config->SunAngle; + + // ---------------------------------------------------------------------- + // Compute transformations from local base frame to planet frame and back + // + MATRIX3 plrot; VECTOR3 relpos; + oapiGetRotationMatrix(hPlanet, &plrot); + oapiGetRotationMatrix(hObj, &mGlobalRot); + oapiGetRelativePos(hObj, hPlanet, &relpos); + vLocalPos = tmul(plrot, relpos); + + swap(plrot.m12, plrot.m21); + swap(plrot.m13, plrot.m31); + swap(plrot.m23, plrot.m32); + + mGlobalRot = mul(plrot, mGlobalRot); + + oapiMatrixIdentity(&mGlobalRotDX); + D3DMAT_SetRotation(&mGlobalRotDX, &mGlobalRot); + //------------------------------------------------------------------------ + + // load surface tiles + DWORD _ntile = gc->GetBaseTileList (_hObj, &tspec); + ntile = 0; + for (i=0; i<_ntile; ++i) { + // Only count (render) tiles where bit0 is set! + if (tspec[i].texflag & 0x01) { + ++ntile; + } + } + + // Do not render tiles for planets having a new tile format + if (vP->tilever >= 2) ntile = 0; + + if (ntile) { + + MESHGROUPEX **grps = new MESHGROUPEX*[ntile]; + SURFHANDLE *texs = new SURFHANDLE[ntile]; + + for (i = 0, j = 0; i < _ntile; ++i) { + // Only render tiles where bit0 is set! + if (tspec[i].texflag & 0x01) { + DWORD ng = oapiMeshGroupCount(tspec[i].mesh); + if (ng!=1) LogErr("MeshGroup Count = %u",ng); + else { + texs[j] = tspec[i].tex; + grps[j] = oapiMeshGroupEx(tspec[i].mesh, 0); + } + ++j; + } + } + tilemesh = new vkMesh(ntile, (const MESHGROUPEX**)grps, texs); + delete []grps; + grps = NULL; + delete []texs; + texs = NULL; + } + + // load meshes for generic structures + MESHHANDLE *sbs, *sas; + DWORD nsbs, nsas; + gc->GetBaseStructures (_hObj, &sbs, &nsbs, &sas, &nsas); + + if (nstructure_bs = nsbs) { + structure_bs = new vkMesh*[nsbs]; + for (i = 0; i < nsbs; i++) structure_bs[i] = new vkMesh(sbs[i]); + } + + if (nstructure_as = nsas) { + structure_as = new vkMesh*[nsas]; + for (i = 0; i < nsas; i++) structure_as[i] = new vkMesh(sas[i]); + } + + lights = false; + Tchk = Tlghtchk = oapiGetSimTime()-1.0; + + UpdateBoundingBox(); + + char name[64]; + oapiGetObjectName(_hObj, name, 64); + LogAlw("New Base Visual(%s) %s hBase=%s, nsbs=%u, nsas=%u", _PTR(this), name, _PTR(_hObj), nsbs, nsas); + + CreateRunwayLights(); + CreateTaxiLights(); +} + + +// =========================================================================================== +// +VECTOR3 vBase::ToLocal(VECTOR3 pos, double *lng, double *lat) const +{ + double rad; + VECTOR3 vLoc = mul(mGlobalRot, pos) + vLocalPos; + if (lng && lat) oapiLocalToEqu(hPlanet, vLoc, lng, lat, &rad); + return vLoc; +} + + +// =========================================================================================== +// +VECTOR3 vBase::FromLocal(VECTOR3 pos) const +{ + return tmul(mGlobalRot, pos-vLocalPos); +} + +// =========================================================================================== +// +void vBase::FromLocal(VECTOR3 pos, FVECTOR3 *pTgt) const +{ + FVECTOR3 pv(float(pos.x-vLocalPos.x), float(pos.y-vLocalPos.y), float(pos.z-vLocalPos.z)); + *pTgt = oapiTransformNormal(&pv, &mGlobalRotDX); +} + +// =========================================================================================== +// +double vBase::GetElevation() const +{ + VECTOR3 bp; + oapiGetRelativePos(hObj, hPlanet, &bp); + return length(bp) - oapiGetSize(hPlanet); +} + + +// =========================================================================================== +// +void vBase::CreateRunwayLights() +{ + const char *file = oapiGetObjectFileName(hObj); + if (file) numRunwayLights = RunwayLights::CreateRunwayLights(this, scn, file, runwayLights); + else LogErr("Configuration file not found for object %s", _PTR(hObj)); +} + +// =========================================================================================== +// +void vBase::CreateTaxiLights() +{ + const char *file = oapiGetObjectFileName(hObj); + if (file) numTaxiLights = TaxiLights::CreateTaxiLights(hObj, scn, file, taxiLights); + else LogErr("Configuration file not found for object %s", _PTR(hObj)); +} + +// =========================================================================================== +// +vBase::~vBase () +{ + DWORD i; + + if (tilemesh) delete tilemesh; + + if (nstructure_bs) { + for (i = 0; i < nstructure_bs; i++) delete structure_bs[i]; + delete []structure_bs; + structure_bs = NULL; + } + if (nstructure_as) { + for (i = 0; i < nstructure_as; i++) delete structure_as[i]; + delete []structure_as; + structure_as = NULL; + } + + if (runwayLights) { + for(i=0; i<(DWORD)numRunwayLights; i++) + { + SAFE_DELETE(runwayLights[i]); + } + delete[] runwayLights; + runwayLights = NULL; + } + + if (taxiLights) { + for(i=0; i<(DWORD)numTaxiLights; i++) + { + SAFE_DELETE(taxiLights[i]); + } + delete[] taxiLights; + } + + if (DebugControls::IsActive()) { + DebugControls::RemoveVisual(this); + } +} + +// =========================================================================================== +// +DWORD vBase::GetMeshCount() +{ + if (tilemesh) return nstructure_bs + nstructure_as + 1; + else return nstructure_bs + nstructure_as; +} + +// =========================================================================================== +// +bool vBase::GetMinMaxDistance(float *zmin, float *zmax, float *dmin) +{ + if (bBSRecompute) UpdateBoundingBox(); + + FMATRIX4 mWorldView; + + Scene *scn = gc->GetScene(); + + FVECTOR4 Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); + + oapiMatrixMultiply(&mWorldView, &mWorld, scn->GetViewMatrix()); + + if (tilemesh) { + D9ComputeMinMaxDistance(gc->GetDevice(), tilemesh->GetAABB(), &mWorldView, &Field, zmin, zmax, dmin); + } + + if (nstructure_bs) { + for (DWORD i = 0; i < nstructure_bs; i++) { + D9ComputeMinMaxDistance(gc->GetDevice(), structure_bs[i]->GetAABB(), &mWorldView, &Field, zmin, zmax, dmin); + } + } + + if (nstructure_as) { + for (DWORD i = 0; i < nstructure_as; i++) { + D9ComputeMinMaxDistance(gc->GetDevice(), structure_as[i]->GetAABB(), &mWorldView, &Field, zmin, zmax, dmin); + } + } + + return true; +} + + +// =========================================================================================== +// +void vBase::UpdateBoundingBox() +{ + bBSRecompute = false; + + if (tilemesh || nstructure_bs || nstructure_as) D9InitAABB(&BBox); + else D9ZeroAABB(&BBox); + + + if (tilemesh) D9AddAABB(tilemesh->GetAABB(), NULL, &BBox); + + if (nstructure_bs) { + for (DWORD i = 0; i < nstructure_bs; i++) { + D9AddAABB(structure_bs[i]->GetAABB(), NULL, &BBox); + } + } + + if (nstructure_as) { + for (DWORD i = 0; i < nstructure_as; i++) { + D9AddAABB(structure_as[i]->GetAABB(), NULL, &BBox); + } + } + + D9UpdateAABB(&BBox); +} + + +// =========================================================================================== +// +bool vBase::Update (bool bMainScene) +{ + _TRACE; + if (!active) return false; + if (!vObject::Update(bMainScene)) return false; + + double simt = oapiGetSimTime(); + + if (fabs(simt-Tlghtchk)>0.1 || oapiGetPause()) { + VECTOR3 rpos = gpos - vP->GlobalPos(); + sunLight = vP->GetObjectAtmoParams(rpos); + Tlghtchk = simt; + } + + if (fabs(simt-Tchk)>1.0) { + VECTOR3 pos, sdir; + MATRIX3 rot; + oapiGetGlobalPos (hObj, &pos); normalise(pos); + oapiGetRotationMatrix (hObj, &rot); + sdir = tmul (rot, -pos); + double csun = sdir.y; + bool night = csun < csun_lights; + if (lights != night) { + DWORD i; + for (i = 0; i < nstructure_bs; i++) structure_bs[i]->SetTexMixture (1, night ? 1.0f:0.0f); + for (i = 0; i < nstructure_as; i++) structure_as[i]->SetTexMixture (1, night ? 1.0f:0.0f); + lights = night; + } + Tchk = simt; + } + return true; +} + + +// =========================================================================================== +// +bool vBase::RenderSurface(LPDIRECT3DDEVICE9 dev) +{ + // note: assumes z-buffer disabled + if (!active) return false; + if (!IsVisible()) return false; + + g_pCurrentVisual = this; + + // render tiles + if (tilemesh) { + g_uCurrentMesh = 0; // Used for debugging + tilemesh->SetSunLight(&sunLight); + tilemesh->RenderBaseTile(&mWorld); + ++g_uCurrentMesh; + } + + // render generic objects under shadows + if (nstructure_bs) { + for (DWORD i = 0; i < nstructure_bs; ++i) { + structure_bs[i]->SetSunLight(&sunLight); + structure_bs[i]->Render(&mWorld, nullptr, RENDER_BASEBS); + ++g_uCurrentMesh; + } + } + + return true; +} + + +// =========================================================================================== +// +bool vBase::RenderStructures(LPDIRECT3DDEVICE9 dev) +{ + if (!active) return false; + if (!IsVisible()) return false; + + g_pCurrentVisual = this; + g_uCurrentMesh = 0; // Used for debugging + + if (tilemesh) g_uCurrentMesh++; + g_uCurrentMesh += nstructure_bs; + + // render generic objects above shadows + for (DWORD i=0; iGetBoundingSpherePos(); + FVECTOR3 qw = oapiTransformCoord(&bs, &mWorld); + vkSun sp = vP->GetObjectAtmoParams(qw._V() + vP->CameraPos()); + structure_as[i]->SetSunLight(&sp); + structure_as[i]->Render(&mWorld, nullptr, RENDER_BASE); + ++g_uCurrentMesh; + } + return true; +} + + +// =========================================================================================== +// +void vBase::RenderRunwayLights(LPDIRECT3DDEVICE9 dev) +{ + if (!active) return; + if (!IsVisible()) return; + + g_pCurrentVisual = this; + + for(int i=0; iGetRenderPass() == RENDERPASS_MAINSCENE) runwayLights[i]->Update(vP); + runwayLights[i]->Render(dev, &mWorld, lights); + } + + for(int i=0; iRender(dev, &mWorld, lights); + } + + if (DebugControls::IsActive()) { + DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + if (flags&DBG_FLAGS_SELVISONLY && this!=DebugControls::GetVisual()) return; // Used for debugging + if (flags&DBG_FLAGS_BOXES) { + FMATRIX4 id; + vkEffect::RenderBoundingBox(&mWorld, &FMATRIX_Identity, &BBox.mn, &BBox.mx, &(FVECTOR4(1,0,1,0.75f))); + } + } +} + + +// =========================================================================================== +// +void vBase::RenderGroundShadow(LPDIRECT3DDEVICE9 dev, float alpha) +{ + if (!nstructure_as) return; // nothing to do + if (!active) return; + if (!IsVisible()) return; + if (Config->TerrainShadowing == 0) return; + + g_pCurrentVisual = this; + + VECTOR3 sd; + oapiGetGlobalPos(hObj, &sd); normalise(sd); + + MATRIX3 mRot; + oapiGetRotationMatrix(hObj, &mRot); + FVECTOR3 lsun = _F(tmul(mRot, sd)); + + if (lsun.y > -0.07f) return; + + float scale = (-lsun.y - 0.07f) * 25.0f; + scale = (1.0f - alpha) * saturate(scale); + + + // build shadow projection matrix + FMATRIX4 mProj; + + OBJHANDLE hPlanet = oapiGetBasePlanet(hObj); + double prad = oapiGetSize(hPlanet); + FVECTOR4 param = D9OffsetRange(prad, 30e3); + + for (DWORD i=0; iHasShadow()) { + + double a, b, el0, el1, el2; + double d = atan(1.0 / prad); + VECTOR3 va, vb, vc; + VECTOR3 q = _V(structure_as[i]->BBox.bs); + float rad = structure_as[i]->BBox.bs.w; + + if (rad<250.0f) ToLocal(q, &a, &b); + else ToLocal(_V(0,0,0), &a, &b); + + if (vP->GetElevation(a, b, &el0) <= 0) el0 = oapiSurfaceElevation(hPlanet, a, b); + if (vP->GetElevation(a + d, b, &el1) <= 0) el1 = oapiSurfaceElevation(hPlanet, a + d, b); + if (vP->GetElevation(a, b + d, &el2) <= 0) el2 = oapiSurfaceElevation(hPlanet, a, b + d); + + oapiEquToLocal(hPlanet, a, b, el0 + prad, &va); + oapiEquToLocal(hPlanet, a + d, b, el1 + prad, &vb); + oapiEquToLocal(hPlanet, a, b + d, el2 + prad, &vc); + + va = FromLocal(va); + vb = FromLocal(vb); + vc = FromLocal(vc); + + VECTOR3 n = -unit(crossp(vb - va, vc - va)); + + FVECTOR3 hn = _F(n); + + float zo = float(-dotp(va, n)); + float nd = dotp(hn, lsun); + hn /= nd; + float ofs = zo / nd; + + mProj.m11 = 1.0f - (float)(lsun.x*hn.x); + mProj.m12 = -(float)(lsun.y*hn.x); + mProj.m13 = -(float)(lsun.z*hn.x); + mProj.m14 = 0; + mProj.m21 = -(float)(lsun.x*hn.y); + mProj.m22 = 1.0f - (float)(lsun.y*hn.y); + mProj.m23 = -(float)(lsun.z*hn.y); + mProj.m24 = 0; + mProj.m31 = -(float)(lsun.x*hn.z); + mProj.m32 = -(float)(lsun.y*hn.z); + mProj.m33 = 1.0f - (float)(lsun.z*hn.z); + mProj.m34 = 0; + mProj.m41 = -(float)(lsun.x*ofs); + mProj.m42 = -(float)(lsun.y*ofs); + mProj.m43 = -(float)(lsun.z*ofs); + mProj.m44 = 1; + + FVECTOR4 nrml = FVECTOR4(float(n.x), float(n.y), float(n.z), zo); + + structure_as[i]->RenderShadowsEx(scale, &mProj, &mWorld, &nrml, ¶m); + } + } +} diff --git a/OVP/VulkanClient/VBase.h b/OVP/VulkanClient/VBase.h new file mode 100644 index 000000000..1b7496415 --- /dev/null +++ b/OVP/VulkanClient/VBase.h @@ -0,0 +1,101 @@ +// ============================================================== +// VBase.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2007-2016 Martin Schweiger +// ============================================================== + +#ifndef __VBASE_H +#define __VBASE_H + +#include "VObject.h" +#include "Mesh.h" + +class RunwayLights; +class TaxiLights; + + +// ============================================================== +// class vBase (interface) +// ============================================================== + +/** + * \brief Visual representation of a surface base. + * + * A vBase is the visual representation of a surface base object (a "spaceport" + * on the surface of a planet or moon, usually with runways or landing pads + * where vessels can land and take off. + */ +class vBase: public vObject { + friend class vPlanet; + +public: + vBase (OBJHANDLE _hObj, const Scene *scene, vPlanet *vP=NULL); + ~vBase(); + + virtual bool GetMinMaxDistance(float *zmin, float *zmax, float *dmin); + virtual void UpdateBoundingBox(); + virtual DWORD GetMeshCount(); + + bool Update (bool bMainScene); + + double GetElevation() const; + vPlanet *GetPlanet() const { return vP; } + + // Convert from a base centric system to time invariant geocentric system + // (i.e. vector does not change in time and points to the same fixed surface location) + // + VECTOR3 ToLocal(VECTOR3 pos, double *lng=NULL, double *lat=NULL) const; + + + // Convert from time invariant geocentric frame to base centric system (Inverse of the ToLocal) + // + VECTOR3 FromLocal(VECTOR3 pos) const; + void FromLocal(VECTOR3 pos, FVECTOR3 *pTgt) const; + + void RenderRunwayLights (LPDIRECT3DDEVICE9 dev); + bool RenderSurface (LPDIRECT3DDEVICE9 dev); + bool RenderStructures (LPDIRECT3DDEVICE9 dev); + void RenderGroundShadow (LPDIRECT3DDEVICE9 dev, float alpha); + + const SurftileSpec *GetTileDesc() const { return tspec; } + +private: + + void CreateRunwayLights(); + void CreateTaxiLights(); + + /** + * \brief Modify local lighting due to planet shadow or + * atmospheric dispersion. + * \param light pointer to D3DLIGHT7 structure receiving modified parameters + * \param nextcheck time interval until next lighting check [s] + * \return \e true if lighting modifications should be applied, \e false + * if global lighting conditions apply. + */ + //bool ModLighting (vkLight *light, double &nextcheck); + + double Tchk; // next update + double Tlghtchk; // next lighting update + double csun_lights; + DWORD ntile; // number of surface tiles + const SurftileSpec *tspec; // list of tile specs + vkMesh *tilemesh; + vkMesh **structure_bs; + vkMesh **structure_as; + DWORD nstructure_bs, nstructure_as; + bool lights; // use nighttextures for base objects + //bool bLocalLight; // true if lighting is modified + class vPlanet *vP; + VECTOR3 vLocalPos; + MATRIX3 mGlobalRot; + FMATRIX4 mGlobalRotDX; + + int numRunwayLights; + RunwayLights** runwayLights; + + int numTaxiLights; + TaxiLights** taxiLights; +}; + +#endif // !__VBASE_H diff --git a/OVP/VulkanClient/VObject.cpp b/OVP/VulkanClient/VObject.cpp new file mode 100644 index 000000000..a9a5faeef --- /dev/null +++ b/OVP/VulkanClient/VObject.cpp @@ -0,0 +1,470 @@ +// ============================================================== +// VObject.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2010-2016 Jarmo Nikkanen (vkClient related parts) +// ============================================================== + +// ============================================================== +// class vObject (implementation) +// +// A "vObject" is the visual representation of an Orbiter object +// (vessel, planet/moon/sun, surface base). vObjects usually have +// one or more meshes associated with it that define their visual +// appearance, but they can be arbitrarily complex (e.g. planets +// with clould layers, atmospheric haze, etc.) +// Visual objects don't persist as their "logical" counterparts, +// but are created and deleted as they pass in and out of the +// visual range of a camera. vObjects are therefore associated +// with a particular scene. In multi-scene environments, a single +// logical object may have multiple vObjects associated with it. +// ============================================================== + +#include "VObject.h" +#include "VVessel.h" +#include "VPlanet.h" +#include "VStar.h" +#include "VBase.h" +#include "Util.h" +#include "Mesh.h" +#include "Surface.h" +#include "Client.h" + +using namespace oapi; + +// Initialisation of static members + +vkClient* vObject::gc = NULL; +SurfNative* vObject::blobtex[3] = { NULL }; +vkMesh* vObject::hStockMesh[16] = { NULL }; + + +// =========================================================================================== +// +vObject::vObject(OBJHANDLE _hObj, const Scene *scene) + : VisObject (_hObj) + , active (true) + , bBSRecompute (true) + , bStencilShadow(true) + , bOmit (false) + , scn( (Scene *)scene) // should be const! + , sunapprad() + , sundst () + , cdist () + , ctgtdst () + , hPlanet (NULL) + , lng(0), lat(0) +{ + _TRACE; + oapiMatrixIdentity(&mWorld); + if (_hObj) size = oapiGetSize(_hObj); + else size = 0; + dmWorld = identity4(); + albedo = _V(1,1,1); + oapiGetObjectName(hObj, name, 64); + sunLight = *scene->GetSun(); + objtp = oapiGetObjectType(hObj); +} + + +// =========================================================================================== +// +vObject *vObject::Create(OBJHANDLE _hObj, const Scene *scene) +{ + _TRACE; + + int objtp = oapiGetObjectType(_hObj); + switch (objtp) { + case OBJTP_VESSEL: return new vVessel (_hObj, scene); + case OBJTP_PLANET: return new vPlanet (_hObj, scene); + case OBJTP_STAR: return new vStar (_hObj, scene); + case OBJTP_SURFBASE: return new vBase (_hObj, scene); + default: + { + LogErr("Unidentified Object Type %d in %s",oapiGetObjectType(_hObj), _PTR(_hObj)); + return new vObject (_hObj, scene); + } + } +} + + +// =========================================================================================== +// +void vObject::GlobalInit(vkClient *gclient) +{ + _TRACE; + static const char *fname[3] = {"Ball.dds","Ball2.dds","Ball3.dds"}; + gc = gclient; + for (int i=0;i<3;i++) blobtex[i] = SURFACE(gc->clbkLoadTexture(fname[i])); + + // Create Some Stock Meshes ---------------------------------------- + // + hStockMesh[vkSM_ARROW] = new vkMesh("D3D9Arrow"); + hStockMesh[vkSM_SPHERE] = new vkMesh("D3D9Sphere"); + hStockMesh[vkSM_BOX] = new vkMesh("D3D9Box"); + + hStockMesh[vkSM_SPHERE]->SetDualSided(0, true); + hStockMesh[vkSM_BOX]->SetDualSided(0, true); +} + + +// =========================================================================================== +// +void vObject::GlobalExit() +{ + _TRACE; + for (int i=0;i<3;i++) DELETE_SURFACE(blobtex[i]); + for (int i = 0; i < ARRAYSIZE(hStockMesh); i++) SAFE_DELETE(hStockMesh[i]); +} + + +// =========================================================================================== +// +void vObject::Activate(bool isactive) +{ + active = isactive; +} + + +// =========================================================================================== +// +DWORD vObject::GetMeshCount() +{ + return 0; +} + + +// =========================================================================================== +// +void vObject::ReOrigin(VECTOR3 global_pos) +{ + cpos = gpos - global_pos; + + cdist = length(cpos); + + dmWorld.m41 = cpos.x; + dmWorld.m42 = cpos.y; + dmWorld.m43 = cpos.z; + + D3DMAT_SetTranslation(&mWorld, &cpos); +} + + +// =========================================================================================== +// +bool vObject::Update(bool bMainScene) +{ + if (!active) return false; + + assert(bMainScene==true); + + VECTOR3 tpos, cgpo; + OBJHANDLE hTgt = oapiCameraTarget(); + + if (hObj) { + oapiGetRotationMatrix(hObj, &grot); + oapiGetGlobalPos(hObj, &gpos); + } + else { + double elev = oapiSurfaceElevation(hPlanet, lng, lat) + oapiGetSize(hPlanet); + oapiEquToGlobal(hPlanet, lng, lat, elev, &gpos); + //grot = identity(); + } + + oapiGetGlobalPos(hTgt, &tpos); + + cgpo = scn->GetCameraGPos(); + axis = mul(grot, _V(0, 1, 0)); + cpos = gpos - cgpo; + cdist = length(cpos); + + // Create double precision world matrix + // + dmWorld = _M(grot.m11, grot.m21, grot.m31, 0, + grot.m12, grot.m22, grot.m32, 0, + grot.m13, grot.m23, grot.m33, 0, + cpos.x, cpos.y, cpos.z, 1); + + // Create single precision world matrix + // + D3DMAT_SetInvRotation(&mWorld, &grot); + D3DMAT_SetTranslation(&mWorld, &cpos); + + + OBJHANDLE hSun = oapiGetGbodyByIndex(0); + oapiGetGlobalPos(hSun, &sundir); + + ctgtdst = length(tpos - gpos); + sundst = length(sundir - gpos); + sunapprad = oapiGetSize(hSun) / sundst; + + if (hSun != hObj) sundir = unit(sundir - gpos); + else sundir = unit(sundir - cgpo); + + CheckResolution(); + + + return true; +} + + +// =========================================================================================== +// +void vObject::UpdateBoundingBox() +{ + +} + +// =========================================================================================== +// +FVECTOR3 vObject::GetBoundingSpherePosDX() +{ + if (bBSRecompute) UpdateBoundingBox(); + FVECTOR3 pos = oapiTransformCoord(&_F(BBox.bs), &mWorld); + return pos; +} + +// =========================================================================================== +// +VECTOR3 vObject::GetBoundingSpherePos() +{ + FVECTOR3 pos = GetBoundingSpherePosDX(); + return _V((double)pos.x, (double)pos.y, (double)pos.z); +} + + +// =========================================================================================== +// +float vObject::GetBoundingSphereRadius() +{ + if (bBSRecompute) UpdateBoundingBox(); + return BBox.bs.w; +} + + +// =========================================================================================== +// +const char *vObject::GetName() const +{ + return name; +} + + +// =========================================================================================== +// +bool vObject::IsVisible() +{ + VECTOR3 pos = GetBoundingSpherePos(); + float rad = GetBoundingSphereRadius(); + float apr = scn->GetCameraAperture(); + double apprad = rad / cdist; + + if ((objtp == OBJTP_VESSEL) && apprad < 0.005*apr) return false; + if ((objtp == OBJTP_SURFBASE) && apprad < 0.02*apr) return false; + + return gc->GetScene()->IsVisibleInCamera(ptr(_F(pos)), rad); + + /* + if (bVis) { + double brad = oapiGetSize(gc->GetScene()->GetCameraProxyBody()); + double crad = cdist; + double alfa = acos(brad/crad); + double trad = length(pos+cpos); + double beta = acos(dotp(pos+cpos, cpos)/(crad*trad)); + if (betarad) return false; + }*/ +} + + +// =========================================================================================== +// This routine will render beacons +// +void vObject::RenderSpot(LPDIRECT3DDEVICE9 dev, const VECTOR3 *ofs, float size, const VECTOR3 &col, bool lighting, int shape) +{ + VECTOR3 pos(cpos); + + if (ofs) pos += mul (grot, *ofs); + VECTOR3 camp = scn->GetCameraGPos(); + + const double ambient = 0.2; + double cosa = dotp (unit(gpos), unit(gpos - camp)); + double intens = (lighting ? 0.5 * ((1.0-ambient)*cosa + 1.0+ambient) : 1.0); + + FMATRIX4 W; + FVECTOR3 vPos(float(pos.x), float(pos.y), float(pos.z)); + FVECTOR3 vCam = unit(vPos); + D3DMAT_CreateX_Billboard(&vCam, &vPos, size, &W); + + FVECTOR4 color((float)col.x, (float)col.y, (float)col.z, 1.0f); + + vkEffect::RenderSpot((float)intens, &color, (const FMATRIX4*)&W, blobtex[shape]); +} + + +// =========================================================================================== +// This routine is for rendering celestial body dots +// +void vObject::RenderDot(LPDIRECT3DDEVICE9 dev) +{ + if (hObj==NULL) return; + + VECTOR3 spos; + oapiGetGlobalPos(oapiGetGbodyByIndex(0), &spos); + oapiGetGlobalPos(hObj, &gpos); + cpos = gpos - scn->GetCameraGPos(); + cdist = length(cpos); + + double alt = max(1.0, cdist - size); + double apr = size * scn->ViewH()*0.5 / (alt * tan(scn->GetCameraAperture())); + + double ds = 10000.0 / cdist; + double s = 2.0; + if (apr<0.3) s = 1.0; + float scale = float(size * ds * s/apr); + + FMATRIX4 W; + FVECTOR3 vPos(float(cpos.x), float(cpos.y), float(cpos.z)); + vPos*=float(ds); + + FVECTOR3 vCam = unit(vPos); + D3DMAT_CreateX_Billboard(&vCam, &vPos, scale, &W); + + float ints = float(sqrt(1.0+dotp(unit(gpos-spos), unit(cpos)))) * 1.0f; + + if (ints>1.0f) ints=1.0f; + + FVECTOR4 color(float(albedo.x)*ints, float(albedo.y)*ints, float(albedo.z)*ints, 1.0f); + + vkEffect::RenderSpot(1.0f, &color, (const FMATRIX4*)&W, blobtex[0]); +} + + +// =========================================================================================== +// +void vObject::RenderVectors (LPDIRECT3DDEVICE9 dev, vkPad* pSkp) +{ + DWORD favmode = *(DWORD*)gc->GetConfigParam(CFGPRM_FRAMEAXISFLAG); + + if (favmode & FAV_ENABLE) // General AXIS rendering ON/OFF + { + if ((objtp == OBJTP_VESSEL && favmode & FAV_VESSEL) || + (objtp == OBJTP_PLANET && favmode & FAV_CELBODY) || + (objtp == OBJTP_SURFBASE && favmode & FAV_BASE)) + { + float alpha = *(float*)gc->GetConfigParam(CFGPRM_FRAMEAXISOPACITY); + + if (alpha > 1e-9) // skip all this when opacity is to small (ZEROish) + { + float scale = float(size) / 50.0f; + float sclset = *(float*)gc->GetConfigParam(CFGPRM_FRAMEAXISSCALE); + //scale *= 0.99f; // 1% "slimmer" to avoid z-fighting with force vector(s) + float ascale = float(size) * sclset * 0.5f; + + RenderAxisVector(pSkp, ptr(FVECTOR4(1, 0, 0, alpha)), _V(1, 0, 0), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1, 0, 0, alpha)), _V(1, 0, 0), ascale, scale, "+X"); + + RenderAxisVector(pSkp, ptr(FVECTOR4(0, 1, 0, alpha)), _V(0, 1, 0), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0, 1, 0, alpha)), _V(0, 1, 0), ascale, scale, "+Y"); + + RenderAxisVector(pSkp, ptr(FVECTOR4(0, 0, 1, alpha)), _V(0, 0, 1), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0, 0, 1, alpha)), _V(0, 0, 1), ascale, scale, "+Z"); + + if (favmode & FAV_NEGATIVE) { + RenderAxisVector(pSkp, ptr(FVECTOR4(1, 0, 0, alpha * 0.5f)), _V(-1, 0, 0), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1, 0, 0, alpha)), _V(-1, 0, 0), ascale, scale, "-X"); + + RenderAxisVector(pSkp, ptr(FVECTOR4(0, 1, 0, alpha * 0.5f)), _V(0, -1, 0), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0, 1, 0, alpha)), _V(0, -1, 0), ascale, scale, "-Y"); + + RenderAxisVector(pSkp, ptr(FVECTOR4(0, 0, 1, alpha * 0.5f)), _V(0, 0, -1), ascale, scale); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0, 0, 1, alpha)), _V(0, 0, -1), ascale, scale, "-Z"); + } + } + } + } +} + + +// =========================================================================================== +// +void vObject::RenderAxisVector(vkPad *pSkp, const FVECTOR4 *pColor, VECTOR3 vector, float lscale, float size, bool bLog) +{ + FMATRIX4 W; + + VECTOR3 dir = mul(grot, vector); + VECTOR3 camp = gc->GetScene()->GetCameraGPos(); + + VECTOR3 pos = gpos - camp; + VECTOR3 rot = crossp(pos, vector); + + VECTOR3 y = mul (grot, unit(vector)) * double(size); + VECTOR3 x = mul (grot, unit(rot)) * double(size); + VECTOR3 z = mul (grot, unit(crossp(vector, rot))) * double(size); + + oapiMatrixIdentity(&W); + + W.m11 = float(x.x); W.m12 = float(x.y); W.m13 = float(x.z); + W.m21 = float(y.x); W.m22 = float(y.y); W.m23 = float(y.z); + W.m31 = float(z.x); W.m32 = float(z.y); W.m33 = float(z.z); + + W.m41 = float(pos.x); + W.m42 = float(pos.y); + W.m43 = float(pos.z); + + float len = float(length(vector)); + + if (bLog) len = max(0.0f, 13.0f+log(len)) * lscale / size; + else len = len * lscale / size; + + hStockMesh[vkSM_ARROW]->RenderAxisVector(&W, pColor, len); +} + + +// =========================================================================================== +// +void vObject::RenderAxisLabel(vkPad *pSkp, const FVECTOR4 *clr, VECTOR3 vector, float lscale, float size, const char *label, bool bLog) +{ + FVECTOR3 homog, ws; + FMATRIX4 W; + + VECTOR3 dir = mul(grot, vector); + VECTOR3 camp = gc->GetScene()->GetCameraGPos(); + + VECTOR3 pos = gpos - camp; + VECTOR3 rot = crossp(pos, vector); + + VECTOR3 y = mul(grot, unit(vector)) * double(size); + VECTOR3 x = mul(grot, unit(rot)) * double(size); + VECTOR3 z = mul(grot, unit(crossp(vector, rot))) * double(size); + + oapiMatrixIdentity(&W); + + W.m11 = float(x.x); W.m12 = float(x.y); W.m13 = float(x.z); + W.m21 = float(y.x); W.m22 = float(y.y); W.m23 = float(y.z); + W.m31 = float(z.x); W.m32 = float(z.y); W.m33 = float(z.z); + + W.m41 = float(pos.x); + W.m42 = float(pos.y); + W.m43 = float(pos.z); + + float len = float(length(vector)); + + if (bLog) len = max(0.0f, 13.0f + log(len)) * lscale / size; + else len = len * lscale / size; + + ws = oapiTransformCoord(ptr(FVECTOR3(0, len, 0)), &W); + homog = oapiTransformCoord(&ws, scn->GetProjectionViewMatrix()); + + if (dotp(ws, *scn->GetCameraZ()) < 0) return; + + if (homog.x >= -1.0f && homog.x <= 1.0f && homog.y >= -1.0f && homog.y <= 1.0f) { + int xc = (int)(scn->ViewW()*0.5*(1.0f + homog.x)); + int yc = (int)(scn->ViewH()*0.5*(1.0f - homog.y)); + pSkp->SetTextColor(FVECTOR4(clr->b, clr->g, clr->r, clr->a).dword_abgr()); + pSkp->Text(xc + 10, yc, label, lstrlen(label)); + } +} + diff --git a/OVP/VulkanClient/VObject.h b/OVP/VulkanClient/VObject.h new file mode 100644 index 000000000..04ba890a3 --- /dev/null +++ b/OVP/VulkanClient/VObject.h @@ -0,0 +1,277 @@ +// ============================================================== +// VObject.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __VOBJECT_H +#define __VOBJECT_H + +#include "OrbiterAPI.h" +#include "GraphicsAPI.h" +#include "Scene.h" +#include "AABBUtil.h" +#include +#include "MathAPI.h" +#include + +extern class vkConfig *Config; + +class vkPad; + + +// ============================================================== +// class vObject (interface) +// ============================================================== + +/** + * \brief Visual object base class. + * + * A vObject is a render object representing a 'logical' + * Orbiter object (identified by its OBJHANDLE) in a scene. + * + * A "vObject" is the visual representation of an Orbiter object (vessel, + * planet/moon/sun, surface base). vObjects usually have one or more meshes + * associated with them that define their visual appearance, but they can be + * arbitrarily complex (e.g. planets with cloud layers, atmospheric haze, + * etc.) + * Visual objects don't persist like their "logical" counterparts, but are + * created and deleted as they pass in and out of the visual range of a + * camera. + * vObjects are therefore associated with a particular scene. In multi-scene + * environments, a single logical object may have multiple vObjects + * associated with it. + */ +class vObject: public oapi::VisObject { +public: + + /** + * \brief Constructs a new visual object for a scene + * \param _hObj object handle + * \param scene scene to which the visual is added + */ + vObject (OBJHANDLE _hObj, const Scene *scene); + + /** + * \brief Destroys the visual object + */ + virtual ~vObject () {} + + /** + * \brief Set up global parameters shared by all instances + * \param gclient client instance pointer + */ + static void GlobalInit (oapi::vkClient *gclient); + + /** + * \brief Release global parameters + */ + static void GlobalExit (); + + /** + * \brief Creates a specific object from its handle + * \param _hObj object handle + * \param scene scene to which the visual is added + * \note Depending on the object type to which _hObj refers, this method + * creates and returns an instance of the following classes: + * - vVessel (vessel object) + * - vPlanet (planet or moon object) + * - vBase (surface base object) + */ + static vObject *Create (OBJHANDLE _hObj, const Scene *scene); + + /** + * \brief Activate or deactivate the object + * \param isactive \e true to activate, \e false to deactivate + * \note This method is only relevant for objects that persist when out + * of visual range. They are activated when entering visual range, and + * deactivated when moving out of visual range. + * \note Deactivated objects should skip their update and render methods + * to improve performance. + * \sa IsActive + */ + virtual void Activate (bool isactive); + + /** + * \brief Returns activation state + * \return \e true for active, \e false for inactive objects. + * \sa Activate + */ + inline bool IsActive () const { return active; } + + inline const FMATRIX4 * MWorld() const { return &mWorld; } + + inline Scene * GetScene() const { return scn; } + inline oapi::vkClient * GetClient() const { return gc; } + inline LPDIRECT3DDEVICE9 GetDevice() const { return gc->GetDevice(); } + + /** + * \brief Returns the handle of the associated logical object + * \return object handle + */ + inline const OBJHANDLE Object() const { return hObj; } + inline const int Type() const { return objtp; } + + /** + * \brief Returns one of the visual's meshes, given by its index. + * \param idx mesh index (>= 0) + * \return Mesh handle + * \note Currently only vessel visuals return anything here. + */ + virtual vkMesh * GetMesh (UINT idx) { return NULL; } + virtual DWORD GetMeshVisMode (UINT idx) { return MESHVIS_ALWAYS; } + + virtual void PreInitObject() { } + + virtual bool GetMinMaxDistance(float *zmin, float *zmax, float *dmin) { return false; } + + virtual void UpdateBoundingBox(); + virtual bool IsVisible(); + virtual DWORD GetMeshCount(); + + FVECTOR3 GetBoundingSpherePosDX(); + VECTOR3 GetBoundingSpherePos(); + float GetBoundingSphereRadius(); + const char *GetName() const; + + /** + * \brief Returns distance from camera + * \return camera distance [m] + * \sa PosFromCamera + */ + inline double CamDist() const { return cdist; } + + /** + * \brief Returns object size + * \return Object size [m] + * \sa CamDist + */ + inline double GetSize() const { return size; } + + /** + * \brief Returns the apparent radius of the Sun + * \return Apparent radius of the Sun [sun_rad/distance] + */ + inline double SunApparentRad() const { return sunapprad; } + + /** + * \brief Returns object position relative to camera + * \return relative position vector [m] + * \note The returned distance vector is expressed in the ecliptic frame. + * \sa CamDist + */ + inline double CameraTgtDist() const { return ctgtdst; } + inline const VECTOR3 &PosFromCamera() const { return cpos; } + inline const VECTOR3 &GlobalPos() const { return gpos; } + + /** + * \brief Returns a unit vector pointing towards the sun + * \return A unit vector pointing towards the sun [m] + * \note The returned vector is expressed in the ecliptic frame. + */ + inline const VECTOR3 &SunDirection() const { return sundir; } + inline const double SunDistance() const { return sundst; } + inline bool Is(const string x) const { return string(name) == x; } + + /** + * \brief Per-frame object parameter updates + * \return \e true if update was performed, \e false if skipped. + * \default Copies global and camera-relative position and rotation + * parameters from the logical object. Updates the world matrix. + * Calls CheckResolution. + * \note This method allows the visual to update any parameters in each + * frame before the render call. + * \note Inactive objects skip this method. + * \sa Render, CheckResolution + */ + virtual bool Update (bool bMainScene); + virtual void ReOrigin(VECTOR3 global_pos); + + /** + * \brief Level-of-detail check + * \default None. + * \note Derived classes can overload this method to select the level of + * detail (e.g. mesh and texture resolution) with which the object is to + * be rendered. + * \note Typically, the render detail will be a function of apparent size, + * which depends on camera distance and camera aperture. + * \note Called by Update. + */ + virtual void CheckResolution () {} + + /** + * \brief Object render call + * \param dev Render device + * \return \e true if render operation was performed, \e false if skipped. + * \default None, returns \e false. + */ + virtual bool Render(LPDIRECT3DDEVICE9 dev) { return false; } + + /** + * \brief Render the vessel's active light beacons + * \param dev render device + * \default None. + */ + virtual void RenderBeacons (LPDIRECT3DDEVICE9 dev) {} + + /** + * \brief Render the vessel's grapple points when switched on (see oapiGetShowGrapplePoints) + * \param dev render device + * \default None. + */ + virtual void RenderGrapplePoints (LPDIRECT3DDEVICE9 dev) {} + + /** + * \brief Render the objects coordinate axes + * \param dev render device + * \param pSkp The 2-D drawing context + */ + virtual void RenderVectors (LPDIRECT3DDEVICE9 dev, vkPad* pSkp); + + + void RenderDot(LPDIRECT3DDEVICE9 dev); + + + + bool bStencilShadow; // Use Stencil shadow for this object + bool bOmit; // Omit this object from scene rendering + D9BBox BBox; + +protected: + + void RenderSpot(LPDIRECT3DDEVICE9 dev, const VECTOR3 *ofs, float size, const VECTOR3 &col, bool lighting, int shape); + void RenderAxisVector(vkPad *pSkp, const FVECTOR4 *pColor, VECTOR3 vector, float lscale, float size, bool bLog=false); + void RenderAxisLabel(vkPad *pSkp, const FVECTOR4 *clr, VECTOR3 vector, float lscale, float size, const char *label, bool bLog=false); + + + static oapi::vkClient *gc; // graphics client instance pointer + static SurfNative * blobtex[3]; // beacon textures + static vkMesh * hStockMesh[16]; + + vkSun sunLight; // Local copy of sun light. (Can be freely edited) + bool bBSRecompute; + + bool active; // visual is active (within camera range) + int objtp; + Scene *scn; // The scene to which the object belongs + VECTOR3 axis; // Rotation Axis, i.e. _V(0,1,0) in global frame + VECTOR3 cpos; // camera-relative object position + VECTOR3 sundir; // Sun direction (unit vector) + VECTOR3 albedo; + VECTOR3 gpos; // Global position + MATRIX3 grot; // Global rotation + MATRIX4 dmWorld; // world matrix in double precision + FMATRIX4 mWorld; // world matrix in single precision + double size; // object radius [m] + double cdist; // current camera distance + double sunapprad; // Apparent size of the sun + double sundst; // Distance to the sun [m] + double ctgtdst; // Distance form a camera target + double lng, lat; // Surface (lng, lat) in some special cases only (e.g. vBuilding) + OBJHANDLE hPlanet; // Planet handle in some special cases (e.g. vBase, vBuilding) + char name[64]; +}; + +#endif // !__VOBJECT_H diff --git a/OVP/VulkanClient/VPlanet.cpp b/OVP/VulkanClient/VPlanet.cpp new file mode 100644 index 000000000..b8c63d63d --- /dev/null +++ b/OVP/VulkanClient/VPlanet.cpp @@ -0,0 +1,1588 @@ +// ============================================================== +// VPlanet.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2010-2016 Jarmo Nikkanen +// ============================================================== + +// ============================================================== +// class vPlanet (implementation) +// +// A vPlanet is the visual representation of a "planetary" object +// (planet, moon, asteroid). +// Currently this only supports spherical objects, without +// variations in elevation. +// ============================================================== + +#define D3D_OVERLOADS + +#include +#include +#include + +#include "Client.h" +#include "Config.h" +#include "VPlanet.h" +#include "VBase.h" +#include "SurfMgr.h" +#include "surfmgr2.h" +#include "cloudmgr2.h" +#include "CloudMgr.h" +#include "HazeMgr.h" +#include "RingMgr.h" +#include "DebugControls.h" +#include "AtmoControls.h" +#include "VectorHelpers.h" +#include "OapiExtension.h" +#include "IProcess.h" +#include + +using namespace oapi; + +// ============================================================== + +static double farplane = 1e6; +static double max_surf_dist = 1e4; + +// Buffered MicroTex.cfg file read: +static std::map MicroCfgs; +typedef std::map::iterator MicroCfgsIterator; + +ImageProcessing* vPlanet::pIP; +LPDIRECT3DDEVICE9 vPlanet::pDev; +LPDIRECT3DTEXTURE9 vPlanet::ptEclipse; + +int vPlanet::Qc = 0; +int vPlanet::Wc = 0; +int vPlanet::Nc = 0; + +extern int SURF_MAX_PATCHLEVEL; +extern vkClient* g_client; +extern unordered_map MicroTextures; + +// ============================================================== +// Face's Terrain Flattening section +// ============================================================== + +struct FlatShape +{ + double Lat; + double Lng; + double Dim1; + double Dim2; + double Cos; + double Sin; + double Falloff; + int Height; + int Type; +}; + +struct TilePixelPosition +{ + int iLat; + int iLng; + int X; + int Y; + bool operator==(const TilePixelPosition& other) const + { + return iLat == other.iLat && iLng == other.iLng && X == other.X && Y == other.Y; + } +}; + +std::unordered_map>>>> g_ElevationFlatteningShapes; +std::unordered_map> g_ShapeStore; +std::unordered_map g_ShapesLoaded; + +std::vector EnumerateDirectory(std::string directory, std::string filter) +{ + std::vector result; + std::string search_path = directory + "\\" + filter; + WIN32_FIND_DATA fd; + HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd); + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) result.push_back(fd.cFileName); + } while (::FindNextFile(hFind, &fd)); + ::FindClose(hFind); + } + return result; +} + +void GetTilePixelPosition(int lvl, double lat, double lng, TilePixelPosition& pos) +{ + //Normalize input + if (lat < -90) lat = -90; + if (lat > 90) lat = 90; + while (lng < -180) + { + lng += 360; + } + while (lng > 180) + { + lng -= 360; + } + + //Calculate tile position and pixel position + double bands = pow((double)2, lvl); + double width = 180 / bands; + pos.iLat = (int)((-lat + 90) / width); + pos.iLng = (int)((lng + 180) / width); + double step = width / 256; + double latmin = 90 - width * (pos.iLat + 1); + double lngmin = -180 + width * pos.iLng; + pos.X = (int)((lng - lngmin) / width * 256); + pos.Y = (int)(-(lat - latmin) / width * 256 + 256); +} + +void ProcessPlanetFlats(OBJHANDLE hPlanet) +{ + char name[MAX_PATH]; + char fname[MAX_PATH]; + oapiGetObjectName(hPlanet, name, ARRAYSIZE(name) - 6); + sprintf_s(fname, ARRAYSIZE(fname), "%s\\Flat", name); + g_client->TexturePath(fname, name); + auto files = EnumerateDirectory(name, "*.flt"); + // Load all planet shapes + for (auto file : files) + { + auto radius = oapiGetSize(hPlanet); + sprintf_s(fname, ARRAYSIZE(fname), "%s\\%s", name, file.c_str()); + auto f = fopen(fname, "r"); + if (f != 0) + { + while (!feof(f)) + { + int height, dim1, dim2, falloff, read; + double lat, lng, phi; + if ((read = fscanf(f, "%s %d %lf %lf %d %d %lf %d", fname, &height, &lng, &lat, &dim1, &dim2, &phi, &falloff)) < 5) continue; // Skip incomplete lines + if (fname[0] == '/' && fname[1] == '/') continue; // Skip commented lines + _strlwr(fname); + if (read < 6) dim2 = dim1; // Fallback for one dimension only + if (read < 7) phi = 0; // Fallback for no angle given + if (read < 8) falloff = 0; // Fallback for no falloff given + auto decdeg = radius * cos(lat * RAD) * 2 * PI; + decdeg = 360 / decdeg; + if (strcmp(fname, "ellipse") == 0) + { + g_ShapeStore[hPlanet].push_back(new FlatShape{ lat ,lng , (double)dim1 * decdeg, (double)dim2 * decdeg, cos(-phi * RAD),sin(-phi * RAD), (double)falloff / 100, height, 1 }); + } + else if (strcmp(fname, "rect") == 0) + { + g_ShapeStore[hPlanet].push_back(new FlatShape{ lat ,lng , (double)dim1 * decdeg / 2, (double)dim2 * decdeg / 2, cos(-phi * RAD),sin(-phi * RAD), (double)falloff / 100, height, 4 }); + } + } + fclose(f); + } + } + // Build up planet map only if at least one shape was added + if (g_ShapeStore.find(hPlanet) != g_ShapeStore.end()) for (auto shape : g_ShapeStore[hPlanet]) + { + double offset = sqrt(shape->Dim1 * shape->Dim1 + shape->Dim2 * shape->Dim2); + double latmin = shape->Lat - offset; + double latmax = shape->Lat + offset; + double lngmin = shape->Lng - offset; + double lngmax = shape->Lng + offset; + TilePixelPosition leftUpper, rightUpper, leftLower, rightLower; + for (auto lvl = 13; lvl >= 0; lvl--) + { + int latBands = 1 << lvl; + int lngBands = latBands * 2; + + // Get tile pixel position of all corner points + GetTilePixelPosition(lvl, latmax, lngmin, leftUpper); + GetTilePixelPosition(lvl, latmax, lngmax, rightUpper); + GetTilePixelPosition(lvl, latmin, lngmin, leftLower); + // Since we are symmetrically, we can deduce the last corner directly + rightLower.iLat = leftLower.iLat; + rightLower.iLng = rightUpper.iLng; + rightLower.X = rightUpper.X; + rightLower.Y = leftLower.Y; + // Check for subpixel visibility + if (leftUpper == rightUpper || leftLower == rightLower || leftUpper == leftLower || rightUpper == rightLower) break; + // Shift if longitude edge is crossed + if (leftUpper.iLng > rightUpper.iLng) + { + leftUpper.iLng -= lngBands; + leftLower.iLng -= lngBands; + } + // Check for edge pixel to compensate overlapping in elevation tiles + if (leftUpper.X == 0) + { + leftUpper.iLng--; + leftLower.iLng--; + } + if (rightUpper.X == 255) + { + rightUpper.iLng++; + rightLower.iLng++; + } + if (leftUpper.Y == 0 && leftUpper.iLat > 0) + { + leftUpper.iLat--; + rightUpper.iLat--; + } + if (leftLower.Y == 255 && leftLower.iLat < latBands) + { + leftLower.iLat++; + rightLower.iLat++; + } + // Sweep over the tile set to put in shape paths + for (auto ilat = leftUpper.iLat; ilat <= leftLower.iLat; ilat++) + for (auto ilng = leftUpper.iLng; ilng <= rightUpper.iLng; ilng++) + { + // Compensate for longitude edge switch + g_ElevationFlatteningShapes[hPlanet][lvl][ilat][ilng < 0 ? lngBands + ilng : ilng].push_back(shape); + } + } + } + g_ShapesLoaded[hPlanet] = true; +} + +template +bool FilterElevation(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, double elev_res, Type* elev) +{ + if (!elev) return false; + if (g_ShapesLoaded.find(hPlanet) == g_ShapesLoaded.end()) ProcessPlanetFlats(hPlanet); + if (g_ElevationFlatteningShapes.find(hPlanet) == g_ElevationFlatteningShapes.end()) return false; // Nothing for planet + auto planetShapes = g_ElevationFlatteningShapes[hPlanet]; + if (planetShapes.find(lvl) == planetShapes.end()) return false; // Nothing at this level + auto levelShapes = planetShapes[lvl]; + if (levelShapes.find(ilat) == levelShapes.end()) return false; // Nothing at this latitude + auto latShapes = levelShapes[ilat]; + if (latShapes.find(ilng) == latShapes.end()) return false; // Nothing at this longitude + auto shapes = latShapes[ilng]; + double bands = pow((double)2, lvl); + double width = 180 / bands; + double step = width / 256; + double latmin = 90 - width * (ilat + 1) - step * 1.5; + double lngmin = -180 + width * ilng - step * 1.5;; + for (auto i = 0; i < TILE_ELEVSTRIDE * TILE_ELEVSTRIDE; i++) + { + for (auto shape : shapes) + { + double y = latmin + (i / 259) * step - shape->Lat; + double x = lngmin + (i % 259) * step - shape->Lng; + double x1 = (x * shape->Cos - y * shape->Sin) / shape->Dim1; + double y1 = (x * shape->Sin + y * shape->Cos) / shape->Dim2; + for (auto pot = 0; pot < shape->Type; pot++) + { + x1 *= x1; + y1 *= y1; + } + double dist = x1 + y1; + if (dist <= 1.0) + { + // Do the math in INT16 basis even for "float" output + INT16 elevation = INT16((double)shape->Height / elev_res); + if (shape->Falloff > 0) + { + for (auto pot = 0; pot < shape->Type; pot++) + { + dist = sqrt(dist); + } + if (dist >= 1 - shape->Falloff) + { + double alpha = (dist - 1 + shape->Falloff) / shape->Falloff; + elevation = INT16(elev[i] * alpha + (double)shape->Height / elev_res * (1 - alpha)); + } + } + // Convert to float or keep as an INT16 depending on case + elev[i] = Type(elevation); + } + } + } + return true; +} + + +bool FilterElevationPhysics(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, double elev_res, INT16* elev) +{ + if (!Config->bFlats) return false; + char name[64]; + oapiGetObjectName(hPlanet, name, 64); + auto result = FilterElevation(hPlanet, lvl, ilat, ilng, elev_res, elev); + if (result) + LogClr("Coral", "FilterElevation[Physics][%s]: Level=%d, ilat=%d, ilng=%d", name, lvl, ilat, ilng); + return result; +} + + +void FilterElevationGraphics(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, float* elev) +{ + if (!Config->bFlats) return; + char name[64]; + oapiGetObjectName(hPlanet, name, 64); + if (FilterElevation(hPlanet, lvl, ilat, ilng, 1.0, elev)) + LogClr("Coral", "FilterElevation[Graphics][%s]: Level=%d, ilat=%d, ilng=%d", name, lvl, ilat, ilng); +} + + + + + + + +// ============================================================== + +void vPlanet::GlobalExit() +{ + // Face's Cleanup ShapeStore ----------------------- + // + for (auto store : g_ShapeStore) + { + for (auto shape : store.second) + { + delete shape; + } + } + g_ShapeStore.clear(); + g_ElevationFlatteningShapes.clear(); + g_ShapesLoaded.clear(); + + SAFE_DELETE(pIP); + SAFE_RELEASE(ptEclipse); + + for (int i=0;i<8;i++) SAFE_DELETE(pRender[i]); +} + +// ============================================================== + +void vPlanet::GlobalInit(oapi::vkClient* gc) +{ + pDev = gc->GetDevice(); + + D3DXCreateTexture(pDev, 512, 1, 1, D3DUSAGE_DYNAMIC, D3DFMT_R32F, D3DPOOL_DEFAULT, &ptEclipse); + LoadMicroTextures(pDev); + GlobalInitAtmosphere(gc); +} + +// ============================================================== + +vPlanet::vPlanet (OBJHANDLE _hObj, const Scene *scene) : + vObject (_hObj, scene), + pSunColor(), pRaySkyView(), pMieSkyView(), pLandViewRay(), pLandViewMie(), pAmbientSky(), pLandViewAtn(), ShaderName("Auto\0") +{ + memset(&MicroCfg, 0, sizeof(MicroCfg)); + vRefPoint = _V(1,0,0); + atm_mode = 0; + iConfig = 0; + dist_scale = 1.0f; + threshold = 1e16; + dwSctFrame = 0; + max_centre_dist = 0.9*scene->GetCameraFarPlane(); + maxdist = max (max_centre_dist, max_surf_dist + size); + DWORD elev_mode = *(DWORD*)gc->GetConfigParam(CFGPRM_ELEVATIONMODE); + + minelev = *(double*)oapiGetObjectParam(_hObj, OBJPRM_PLANET_MINELEVATION); + maxelev = *(double*)oapiGetObjectParam(_hObj, OBJPRM_PLANET_MAXELEVATION); + + physics_patchres = *(DWORD*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_SURFACEMAXLEVEL); + physics_patchres = min (physics_patchres, *(DWORD*)gc->GetConfigParam (CFGPRM_SURFACEMAXLEVEL)); + + // Push the graphics resolution higher than the one used for physics + // to enable more accurate bilinear interpolation of the terrain. + max_patchres = physics_patchres + 4; + + tilever = *(int*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_TILEENGINE); + if (tilever < 2) { + surfmgr = new SurfaceManager (gc, this); + surfmgr2 = NULL; + } else { + LoadAtmoConfig(); + surfmgr = NULL; + int patchlvl = 1 << (4 + Config->MeshRes); + surfmgr2 = new TileManager2 (this, max_patchres, patchlvl); + prm.horizon_excess = *(double*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_HORIZONEXCESS); + prm.tilebb_excess = *(double*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_TILEBBEXCESS); + } + + prm.horizon_minrad = min (1.0 + minelev / size, 1.0 - 1e-4); + + prm.bAtm = oapiPlanetHasAtmosphere (_hObj); + if (prm.bAtm) { + const ATMCONST *atmc = oapiGetPlanetAtmConstants(_hObj); + prm.atm_hzalt = atmc->horizonalt; + prm.atm_href = log(atmc->rho0)*2e4 + 2e4; + prm.atm_amb0 = min (0.7, log1p(atmc->rho0)*0.35); + DWORD amb0 = *(DWORD*)gc->GetConfigParam (CFGPRM_AMBIENTLEVEL); + prm.amb0col = 0; + for (int i = 0; i < 4; i++) prm.amb0col |= amb0 << (i<<3); + } + tile_cache = NULL; + hazemgr = NULL; + hazemgr2 = NULL; + hashaze = *(bool*)gc->GetConfigParam (CFGPRM_ATMHAZE) && prm.bAtm; + bRipple = *(bool*)gc->GetConfigParam (CFGPRM_SURFACERIPPLE) && + *(bool*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_SURFACERIPPLE); + if (bRipple) { + if (surfmgr) surfmgr->SetMicrotexture ("waves.dds"); + } + + shadowalpha = (float)(*(double*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_SHADOWCOLOUR)); + + bVesselShadow = (shadowalpha < 0.98); + bObjectShadow = (shadowalpha < 0.98); + + clouddata = 0; + cloudmgr2 = 0; + prm.bCloud = (*(bool*)gc->GetConfigParam (CFGPRM_CLOUDS) && + *(bool*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_HASCLOUDS)); + if (prm.bCloud) { + int cloudtilever = *(int*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_CLOUDTILEENGINE); + prm.cloudalt = *(double*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_CLOUDALT); + prm.bCloudBrighten = *(bool*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_CLOUDOVERSATURATE); + prm.bCloudShadow = *(bool*)gc->GetConfigParam (CFGPRM_CLOUDSHADOWS); + prm.shadowalpha = 1.0 - *(float*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_CLOUDSHADOWCOL); + if (prm.shadowalpha < 0.01) + prm.bCloudShadow = false; + if (cloudtilever == 1) { // legacy cloud engine + clouddata = new CloudData; + clouddata->cloudmgr = new CloudManager (gc, this); + clouddata->cloudshadow = prm.bCloudShadow; + if (clouddata->cloudshadow) { + clouddata->shadowalpha = (float)prm.shadowalpha; + } + if (*(bool*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_CLOUDMICROTEX)) { + clouddata->cloudmgr->SetMicrotexture ("cloud1.dds"); + clouddata->microalt0 = *(double*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_CLOUDMICROALTMIN); + clouddata->microalt1 = *(double*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_CLOUDMICROALTMAX); + } + } else { // v2 cloud engine + DWORD maxlvl = (DWORD)*(int*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_CLOUDMAXLEVEL); + maxlvl = min (maxlvl, *(DWORD*)gc->GetConfigParam (CFGPRM_SURFACEMAXLEVEL)); + cloudmgr2 = new TileManager2 (this, maxlvl, 32); + } + } else { + prm.bCloudShadow = false; + } + + if (*(bool*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_HASRINGS)) { + double minrad = *(double*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_RINGMINRAD); + double maxrad = *(double*)oapiGetObjectParam (_hObj, OBJPRM_PLANET_RINGMAXRAD); + ringmgr = new RingManager (this, minrad, maxrad); + } else { + ringmgr = 0; + } + + memcpy (&fog, oapiGetObjectParam (_hObj, OBJPRM_PLANET_FOGPARAM), sizeof (FogParam)); + prm.bFogEnabled = (fog.dens_0 > 0); + + patchres = 0; + + nbase = oapiGetBaseCount (_hObj); + if (nbase) vbase = new vBase*[nbase]; + else vbase = NULL; + for (DWORD i = 0; i < nbase; i++) + vbase[i] = NULL; + + VESSEL *hVes = oapiGetFocusInterface(); + + if (hVes) { + if (Config->PreLBaseVis && _hObj==hVes->GetSurfaceRef()) { + LogAlw("PreLoading Base Visuals"); + for (DWORD i=0;iGetMaxLevel() == 0) { + char cbuf[256]; + oapiGetObjectName (hObj, cbuf, 256); + OBJHANDLE hMesh = oapiLoadMesh (cbuf); + if (hMesh) { + LogAlw("Loading mesh [%s] for planetary body '%s'", cbuf, name); + float fSize = float(size); + mesh = new vkMesh(hMesh, false, NULL, &fSize); + oapiDeleteMesh (hMesh); + } + } + + // Finish creation ------------------------------------------- + // + MicroCfg.bEnabled = ParseMicroTextures(); + + ParseConfig(oapiGetObjectFileName(hObj)); + + UpdateScatter(); + + char msg[256]; char path[MAX_PATH]; + // Check texture directory + GetClient()->PlanetTexturePath(GetName(), path); + auto x = filesystem::status(path); + bHasTextures = filesystem::is_directory(x); + + // Check *.tex file + string tf = string(GetName()) + ".tex"; + GetClient()->PlanetTexturePath(tf.c_str(), path); + auto y = filesystem::status(path); + bHasTextures |= filesystem::exists(y); + + if (!bHasTextures) { + VESSEL* vss = oapiGetFocusInterface(); + sprintf_s(msg, sizeof(msg), "[WARNING] Surface textures are missing for %s", GetName()); + if (vss && (vss->GetGravityRef() == hObj)) { + oapiWriteLog(msg); + MessageBox(GetClient()->GetWindow(), msg, "Warning", MB_OK); + } + else oapiWriteLog(msg); + } +} + + +// =========================================================================================== +// +vPlanet::~vPlanet () +{ + + if (nbase) { + for (DWORD i = 0; i < nbase; i++) + if (vbase[i]) delete vbase[i]; + delete []vbase; + vbase = NULL; + } + if (surfmgr) delete surfmgr; + else if (surfmgr2) delete surfmgr2; + if (cloudmgr2) delete cloudmgr2; + + for (auto x : overlays) for (auto y : x->pSurf) if (y) y->Release(); + + if (clouddata) { + delete clouddata->cloudmgr; + delete clouddata; + } + if (hazemgr) delete hazemgr; + if (hazemgr2) delete hazemgr2; + if (ringmgr) delete ringmgr; + if (mesh) delete mesh; + + SAFE_RELEASE(pSunColor); + SAFE_RELEASE(pRaySkyView); + SAFE_RELEASE(pMieSkyView); + SAFE_RELEASE(pLandViewRay); + SAFE_RELEASE(pLandViewMie); + SAFE_RELEASE(pAmbientSky); + SAFE_RELEASE(pLandViewAtn); +} + + +// =========================================================================================== +// +void vPlanet::Activate(bool isactive) +{ + vObject::Activate(isactive); + if (!isactive) { + if (surfmgr2) surfmgr2->Unload(1); + if (cloudmgr2) cloudmgr2->Unload(1); + } +} + + + +// =========================================================================================== +// +bool vPlanet::ParseConfig(const char* fname) +{ + std::string dummy; + std::ifstream fs(fname); + + if (fs.fail()) { + LogErr("Could not open a planet configuration file '%s'", fname); + return false; + } + + OBJHANDLE hPlanet = NULL; + std::string line; // One file line + + while (std::getline(fs, line)) + { + line = trim(line); + + // skip empty lines and comments + if (!line.length() || line[0] == ';') continue; + + // AlbedoRGB = + if (startsWith(line, "AlbedoRGB")) + { + std::istringstream iss(line); + iss >> dummy >> dummy + >> albedo.x + >> albedo.y + >> albedo.z; + } + } + return true; +} + +// ============================================================== + +bool vPlanet::CameraInAtmosphere() const +{ + double calt = CamDist() - size; + double halt = GetHorizonAlt(); + if (prm.bAtm==false) return false; + if (calt>halt) return false; + return true; +} + +// ============================================================== + +double vPlanet::GetHorizonAlt() const +{ + if (!prm.bAtm) return 0.0; + if (!surfmgr2) return prm.atm_hzalt; + return SPrm.visalt; +} + +// ============================================================== + +vBase* vPlanet::GetBaseByHandle(OBJHANDLE hBase) const +{ + if (vbase) for (DWORD i=0;iObject()==hBase) return vbase[i]; + return NULL; +} + +// ============================================================== + +VECTOR3 vPlanet::GetUnitSurfacePos(double lng, double lat) const +{ + double slat = sin(lat), clat = cos(lat); + double slng = sin(lng), clng = cos(lng); + return _V(clat*clng, slat, clat*slng); +} + +// ============================================================== + +VECTOR3 vPlanet::ToLocal(VECTOR3 &glob) const +{ + MATRIX3 grot; + oapiGetRotationMatrix(hObj, &grot); + return tmul(grot, glob); +} + +// ============================================================== + +VECTOR3 vPlanet::CameraPos() const +{ + return scn->GetCameraGPos() - gpos; +} + +// ============================================================== + +void vPlanet::GetLngLat(VECTOR3 &loc, double *lng, double *lat) const +{ + *lat = asin(loc.y); + *lng = atan2(loc.z, loc.x); +} + +// ============================================================== + +bool vPlanet::GetMinMaxDistance(float *zmin, float *zmax, float *dmin) +{ + if (mesh==NULL) return false; + if (bBSRecompute) UpdateBoundingBox(); + + FVECTOR3 pos = FVECTOR3(mWorld.m41, mWorld.m42, mWorld.m43); + float dst = length(pos); + + *dmin = dst - float(size); + *zmin = *dmin; + *zmax = dst + float(size); + + return true; +} + +// ============================================================== + +int vPlanet::GetElevation(double lng, double lat, double *elv, FVECTOR3 *nrm) const +{ + int rv = 0; + if (!surfmgr2) return -4; + if (tile_cache) rv = tile_cache->GetElevation(lng, lat, elv, nrm, NULL); // tile_cache is set to NULL when tiles are deleted + if (rv!=1) rv = surfmgr2->GetElevation(lng, lat, elv, nrm, &tile_cache); + return rv; +} + +// ============================================================== + +SurfTile * vPlanet::FindTile(double lng, double lat, int maxlvl) +{ + return static_cast(SurfMgr2()->SearchTile(lng, lat, maxlvl, false)); +} + +// ============================================================== + +void vPlanet::PickSurface(FVECTOR3 &vRay, TILEPICK *result) +{ + if (surfmgr2) { + surfmgr2->Pick(vRay, result); + if (result->pTile) { + VECTOR3 loc = _V(result->_p) - cpos; + loc = ToLocal(loc); + double rad; + oapiLocalToEqu(hObj, loc, &(result->lng), &(result->lat), &rad); + result->elev = rad - size; + } + } +} + +// ============================================================== + +void vPlanet::UpdateBoundingBox() +{ + bBSRecompute = false; +} + +// ============================================================== + +void vPlanet::ReOrigin(VECTOR3 global_pos) +{ + vObject::ReOrigin(global_pos); + if (vbase) for (int i = 0; i < nbase; i++) if (vbase[i]) vbase[i]->ReOrigin(global_pos); +} + +// ============================================================== + +bool vPlanet::Update (bool bMainScene) +{ + _TRACE; + if (!active) return false; + + vObject::Update(bMainScene); + + if (patchres==0) return true; + + int i, j; + float rad_scale = float(size); + bool rescale = false; + dist_scale = 1.0f; + + if (IsMesh()) rad_scale = 1.0f; // Mesh vertices are already scaled + + if (cdist > maxdist) { + rescale = true; + dist_scale = (FLOAT)(max_centre_dist/cdist); + prm.DistScale = dist_scale; + } + if (rescale) { + rad_scale *= dist_scale; + mWorld.m41 *= dist_scale; + mWorld.m42 *= dist_scale; + mWorld.m43 *= dist_scale; + } + + // scale up from template sphere radius 1 + mWorld.m11 *= rad_scale; mWorld.m12 *= rad_scale; mWorld.m13 *= rad_scale; + mWorld.m21 *= rad_scale; mWorld.m22 *= rad_scale; mWorld.m23 *= rad_scale; + mWorld.m31 *= rad_scale; mWorld.m32 *= rad_scale; mWorld.m33 *= rad_scale; + + // cloud layer world matrix + if (prm.bCloud) { + double cloudrad = size + prm.cloudalt; + prm.cloudrot = *(double*)oapiGetObjectParam (hObj, OBJPRM_PLANET_CLOUDROTATION); + prm.cloudrot = posangle(prm.cloudrot); + prm.cloudvis = (cdist < cloudrad ? 1:0); + if (cdist > cloudrad*(1.0-1.5e-4)) prm.cloudvis |= 2; + + if (clouddata) { + if (prm.cloudvis & 1) { + clouddata->viewap = acos (size/cloudrad); + if (size < cdist) clouddata->viewap += acos (size/cdist); + } else { + clouddata->viewap = 0; + } + + float cloudscale = (float)(cloudrad/size); + + // world matrix for cloud shadows on the surface + memcpy (&clouddata->mWorldC0, &mWorld, sizeof (D3DMATRIX)); + if (prm.cloudrot) { + static FMATRIX4 crot (1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); + crot.m11 = crot.m33 = (float)cos(prm.cloudrot); + crot.m13 = -(crot.m31 = (float)sin(prm.cloudrot)); + oapiMatrixMultiply (&clouddata->mWorldC0, &crot, &clouddata->mWorldC0); + } + + // world matrix for cloud layer + memcpy (&clouddata->mWorldC, &clouddata->mWorldC0, sizeof (D3DMATRIX)); + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) { + clouddata->mWorldC.data[i + j<<2] *= cloudscale; + } + + // set microtexture intensity + double alt = cdist-size; + double lvl = (clouddata->microalt1-alt)/(clouddata->microalt1-clouddata->microalt0); + clouddata->cloudmgr->SetMicrolevel (max (0.0, min (1.0, lvl))); + } + } + + // check all base visuals + if (nbase) { + VECTOR3 pos, cpos = scn->GetCameraGPos(); + double scale = (double)scn->ViewH()/scn->GetTanAp(); + for (DWORD i = 0; i < nbase; i++) { + OBJHANDLE hBase = oapiGetBaseByIndex (hObj, i); + oapiGetGlobalPos (hBase, &pos); + double rad = oapiGetSize (hBase); + double dst = dist (pos, cpos); + double apprad = rad*scale/dst; + + // ----------------------------------------------------------------- + // + if (bMainScene) { + if (vbase[i]) { // base visual exists + if (apprad < 1.0) { // out of visual range + delete vbase[i]; + vbase[i] = 0; + } + } else { // base visual doesn't exist + if (apprad > 2.0) { // within visual range + vbase[i] = new vBase (hBase, scn, this); + } + } + } + + // Toggle surface base on/off based on visual size ----------------- + // + if (vbase[i]) { + if (apprad < 1.0) vbase[i]->Activate(false); + else if (apprad > 2.0) vbase[i]->Activate(true); + vbase[i]->Update(bMainScene); + } + } + } + return true; +} + +// ============================================================== + +void vPlanet::CheckResolution() +{ + double alt = max (1.0, cdist-size); + double apr = size * scn->ViewH()*0.5 / (alt * scn->GetTanAp()); + // apparent planet radius in units of screen pixels + + DWORD new_patchres; + double ntx; + + if (apr < 2.5) { // render planet as 2x2 pixels + renderpix = true; + new_patchres = 0; + ntx = 0; + } else { + renderpix = false; + ntx = PI*2.0 * apr; + + static const double scal2 = 1.0/log(2.0); + const double shift = (surfmgr2 ? 6.0 : 5.0); // reduce level for tile mgr v2, because of increased patch size + new_patchres = min (max ((DWORD)(scal2*log(ntx)-shift),(DWORD)1), max_patchres); + } + if (new_patchres != patchres) { + if (hashaze) { + if (new_patchres < 1) { + if (hazemgr) { delete hazemgr; hazemgr = NULL; } + if (hazemgr2) { delete hazemgr2; hazemgr2 = NULL; } + } else { + if (tilever>1) { + if (!hazemgr2) hazemgr2 = new HazeManager2 (this); + } + else if (!hazemgr) hazemgr = new HazeManager (scn->GetClient(), this); + } + } + if (ringmgr) { + int ringres = (new_patchres <= 3 ? 0 : new_patchres <= 4 ? 1:2); + ringmgr->SetMeshRes (ringres); + } + patchres = new_patchres; + } +} + +// ============================================================== + +void vPlanet::RenderZRange (double *nplane, double *fplane) +{ + double d = dotp (scn->GetCameraGDir(), cpos); + *fplane = max (1e3, d+size*1.2); + *nplane = max (1e0, d-size*1.2); + *fplane = min (*fplane, *nplane*1e5); +} + +// ============================================================== + +bool vPlanet::Render(LPDIRECT3DDEVICE9 dev) +{ + _TRACE; + if (!active) return false; + + const SHADOWMAP *shd = scn->GetSMapData(ShdPackage::Main); + + if (DebugControls::IsActive()) { + // DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + DWORD displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); + vObject *vSel = DebugControls::GetVisual(); + if (vSel && displ>0) { + if (vSel->GetObjHandle()) { + if (oapiGetObjectType(vSel->GetObjHandle())==OBJTP_VESSEL) return false; + } + } + } + + + FlowControlPS* fc = GetFlowControl(); + FlowControlVS* fcv = GetFlowControlVS(); + + memset(fc, 0, sizeof(FlowControlPS)); + memset(fcv, 0, sizeof(FlowControlVS)); + + + g_pCurrentVisual = this; + + if (renderpix) { // render as 2x2 pixel block + RenderDot (dev); + } + else // render as sphere ------------------------ + { + + // Must update the latest view projection matrix + cp.mVP = *scn->GetProjectionViewMatrix(); + + // Setup shadow maps for surface base objects and mesh based bodies --------------- + // + vkEffect::UpdateEffectCamera(hObj); + vkEffect::FX->SetFloat(vkEffect::eDistScale, 1.0f / float(dist_scale)); + + HR(vkEffect::FX->SetBool(vkEffect::eEnvMapEnable, false)); + HR(vkEffect::FX->SetBool(vkEffect::eShadowToggle, false)); + + if (shd->IsValid() && (scn->GetRenderPass() == RENDERPASS_MAINSCENE) && (Config->TerrainShadowing == 2)) { + if (scn->GetCameraAltitude() < 10e3 || IsMesh()) { + float s = float(shd->size); + float is = 1.0f / s; + float qw = 1.0f / float(Config->ShadowMapSize); + HR(vkEffect::FX->SetMatrix(vkEffect::eLVP, _DX(shd->mLVP))); + HR(vkEffect::FX->SetVector(vkEffect::eSHD, _DX(FVECTOR4(s, is, qw, 0)))); + HR(vkEffect::FX->SetBool(vkEffect::eShadowToggle, true)); + + vkMesh::SetShadows(shd); + } + } + + DWORD amb = prm.amb0col; + float fogfactor; + + DWORD bg = scn->GetBgColour(); + prm.bFog = prm.bFogEnabled; + prm.bTint = prm.bFogEnabled; + prm.bAddBkg = ((bg & 0xFFFFFF) && (hObj != scn->GetCameraProxyBody())); + prm.FogDensity = 0.0f; + prm.SkyColor = FVECTOR4(bg); + prm.AmbColor = _F4(0,0,0,0); + prm.FogColor = _F4(0,0,0,0); + prm.TintColor = _F4(0,0,0,0); + prm.SunDir = _F(SunDirection()); + + SetupEclipse(); + + if (scn->GetRenderPass() == RENDERPASS_MAINSCENE) UpdateScatter(); + + if (ringmgr) { + ringmgr->Render(dev, mWorld, false); + dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + } + + if (hazemgr2) { + double apr = 180.0 * scn->GetCameraAperture() / (scn->GetCameraAspect() * PI); + hazemgr2->Render(mWorld, float(apr)); + } + + if (prm.bCloud && (prm.cloudvis & 1)) + RenderCloudLayer (dev, D3DCULL_NONE); // render clouds from below + + if (hazemgr) hazemgr->Render(dev, mWorld); // horizon ring + + + if (prm.bAtm) { + if (ModLighting (amb)) + prm.AmbColor = FVECTOR4(amb); + } + + if (prm.bFog) { // set up distance fog + double h = max (1.0, cdist-size); + + VECTOR3 fogcol = fog.col; + double h_ref = fog.alt_ref; // 3e3; + double fog_0 = fog.dens_0; // 5e-5; + double fog_ref = fog.dens_ref; // 3e-5; + double h_max = size*1.5; // At this altitude, fog effect drops to zero + + if (h < h_ref) { + // linear zone + fogfactor = (float)(h/h_ref * (fog_ref-fog_0) + fog_0); + } else { + // hyperbolic zone: fogfactor = a/(h+b) + c + // a, b and c are designed such that + // * fogfactor(h) is continuous at h = h_ref + // * d fogfactor / dh is continuous at h = h_ref + // * fogfactor(h_max) = 0 + double b = - (fog_ref*h_max + (fog_ref-fog_0)*(h_max-h_ref)) / (fog_ref + (fog_ref-fog_0)/h_ref * (h_max-h_ref)); + double a = fog_ref*(h_ref+b)*(h_max+b)/(h_max-h_ref); + double c = -a/(h_max+b); + fogfactor = (float)(a/(h+b)+c); + } + + if (fogfactor < 0.0) prm.bFog = false; + else { + // day/nighttime fog lighting + VECTOR3 ppos; + oapiGetGlobalPos (hObj, &ppos); + double cosa = dotp (unit(ppos), unit(cpos)); + double bright = 1.0 * max (0.0, min (1.0, cosa + 0.3)); + float rfog = (float)(bright*(min(1.0,fogcol.x)+0.0)); // "whiten" the fog colour + float gfog = (float)(bright*(min(1.0,fogcol.y)+0.0)); + float bfog = (float)(bright*(min(1.0,fogcol.z)+0.0)); + prm.FogDensity = fogfactor; + prm.FogColor = FVECTOR4(rfog, gfog, bfog, 1.0f); + } + } + + + if (mesh) { + mesh->SetSunLight(scn->GetSun()); + mesh->RenderFast(&mWorld, RENDER_ASTEROID); + } else { + RenderSphere (dev); + } + + if (nbase) RenderBaseStructures (dev); + + if (prm.bCloud && (prm.cloudvis & 2)) + RenderCloudLayer (dev, D3DCULL_CCW); // render clouds from above + + if (hazemgr) hazemgr->Render (dev, mWorld, true); // haze across planet disc + if (ringmgr) { + ringmgr->Render (dev, mWorld, true); + dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + } + + } + + // Shutdown shadows to prevent from causing problems + HR(vkEffect::FX->SetBool(vkEffect::eShadowToggle, false)); + + return true; +} + +// ============================================================== + +void vPlanet::RenderBeacons(LPDIRECT3DDEVICE9 dev) +{ + // Beacons rendered elsewhere before the cloud layer +} + +// ============================================================== + +void vPlanet::RenderVectors (LPDIRECT3DDEVICE9 dev, vkPad* pSkp) +{ + // Call base class' method for planets axes + vObject::RenderVectors(dev, pSkp); + + // If this planet is not a proxy body skip the rest + if (hObj != oapiCameraProxyGbody()) return; + + for (DWORD i = 0; i < nbase; i++) if (vbase[i]) { + vbase[i]->RenderVectors(dev, pSkp); + } +} + +// ============================================================== + +void vPlanet::ActivateLabels(bool activate) +{ + if (surfmgr2 && *(int*)oapiGetObjectParam(hObj, OBJPRM_PLANET_LABELENGINE) == 2) + { + if (activate) surfmgr2->CreateLabels(); + else surfmgr2->DeleteLabels(); + } +} + +// ============================================================== + +void vPlanet::RenderLabels(LPDIRECT3DDEVICE9 dev, vkPad *skp, oapi::Font **labelfont, int *fontidx) +{ + if (surfmgr2 && *(int*)oapiGetObjectParam(hObj, OBJPRM_PLANET_LABELENGINE) == 2) + { + surfmgr2->RenderLabels(skp, labelfont, fontidx); + } +} + +// ============================================================== + +void vPlanet::RenderSphere (LPDIRECT3DDEVICE9 dev) +{ + + bool bUseZBuf = true; + + double calt = abs(cdist - size); + + if (surfmgr2) { + + tile_cache = NULL; // Clear tile cache + + if (scn->GetRenderPass() == RENDERPASS_MAINSCENE) { + if (size < 500e3) { + // Comet, Asteroid, Small Moon + if (cdist >= 1000.0*size) { + surfmgr2->ElevMode = eElevMode::Spherical; + bUseZBuf = false; + } + else surfmgr2->ElevMode = eElevMode::ForcedElevated; + } + else { + // Planet or Major Moon + if (cdist >= 1.6*size) { + surfmgr2->ElevMode = eElevMode::Spherical; + bUseZBuf = false; + } + else { + TileManager2Base::RenderStats rs = surfmgr2->prevstat; + if (calt > 35e3) { + if (abs(cdist - threshold) > 1e3) { + threshold = cdist; + // If there are more than 3 spherical tiles render the body as spherical + if (rs.Sphe > 3) { + surfmgr2->ElevMode = eElevMode::Spherical; // Spherical body + bUseZBuf = cdist < (1.6*size); + } + else surfmgr2->ElevMode = eElevMode::Elevated; // Elevated body + } + } + else surfmgr2->ElevMode = eElevMode::Elevated; // Elevated body + } + } + } + + //if (string(GetName()) == string("Earth")) sprintf_s(oapiDebugString(), 256, "UseZ=%hhu, ElevMode=%d", bUseZBuf, surfmgr2->ElevMode); + + surfmgr2->Render(dmWorld, bUseZBuf, prm); + } + else { + float fogfactor; + vkEffect::FX->GetFloat(vkEffect::eFogDensity, &fogfactor); + dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + if (prm.bFog) vkEffect::FX->SetFloat(vkEffect::eFogDensity, fogfactor/dist_scale); + surfmgr->SetAmbientColor(prm.AmbColor.dword_abgr()); + surfmgr->Render (dev, mWorld, dist_scale, patchres, 0.0, prm.bFog); // surface + if (prm.bFog) vkEffect::FX->SetFloat(vkEffect::eFogDensity, fogfactor); + } + + if (nbase) { + RenderBaseSurfaces (dev); // base surfaces + RenderBaseShadows (dev, shadowalpha); // base shadows + } + if (prm.bCloudShadow) + RenderCloudShadows (dev); // cloud shadows + + if (bVesselShadow && hObj == oapiCameraProxyGbody()) + // cast shadows only on planet closest to camera + scn->RenderVesselShadows (hObj, shadowalpha); // vessel shadows +} + +// ============================================================== + +void vPlanet::RenderCloudLayer (LPDIRECT3DDEVICE9 dev, DWORD cullmode) +{ + if (cullmode != D3DCULL_CCW) dev->SetRenderState (D3DRS_CULLMODE, cullmode); + if (cloudmgr2) { + cloudmgr2->Render(dmWorld, false, prm); + } + else + clouddata->cloudmgr->Render (dev, clouddata->mWorldC, dist_scale, min(patchres,8), clouddata->viewap); // clouds + if (cullmode != D3DCULL_CCW) dev->SetRenderState (D3DRS_CULLMODE, D3DCULL_CCW); +} + +// ============================================================== + +void vPlanet::RenderCloudShadows (LPDIRECT3DDEVICE9 dev) +{ + if (cloudmgr2) { + // Nothing to do here + } + else if (clouddata) { // legacy method + float fogfactor; + vkEffect::FX->GetFloat(vkEffect::eFogDensity, &fogfactor); + if (prm.bFog) vkEffect::FX->SetFloat(vkEffect::eFogDensity, fogfactor/dist_scale); + clouddata->cloudmgr->RenderShadow(dev, clouddata->mWorldC0, dist_scale, min(patchres,8), clouddata->viewap, clouddata->shadowalpha); + if (prm.bFog) vkEffect::FX->SetFloat(vkEffect::eFogDensity, fogfactor); + } +} + +// ============================================================== + +void vPlanet::RenderBaseSurfaces(LPDIRECT3DDEVICE9 dev) +{ + // If this planet is not a proxy body skip the rest + if (hObj != oapiCameraProxyGbody()) return; + + if (scn->GetRenderFlags() & 0x20) { + for (DWORD i = 0; i < nbase; i++) if (vbase[i]) { + vbase[i]->RenderSurface(dev); + vbase[i]->RenderRunwayLights(dev); + } + } +} + +// ============================================================== + +void vPlanet::RenderBaseShadows(LPDIRECT3DDEVICE9 dev, float depth) +{ + // If this planet is not a proxy body skip the rest + if (hObj != oapiCameraProxyGbody()) return; + + if (scn->GetRenderFlags() & 0x20) { + if (bObjectShadow) { + for (DWORD i = 0; i < nbase; i++) if (vbase[i]) vbase[i]->RenderGroundShadow(dev, depth); + // reset device parameters + dev->SetRenderState(D3DRS_STENCILENABLE, FALSE); + } + } +} + +// ============================================================== + +void vPlanet::RenderBaseStructures (LPDIRECT3DDEVICE9 dev) +{ + // If this planet is not a proxy body skip the rest + if (hObj != oapiCameraProxyGbody()) return; + + if (scn->GetRenderFlags() & 0x20) { + for (DWORD i = 0; i < nbase; i++) if (vbase[i]) vbase[i]->RenderStructures(dev); + for (DWORD i = 0; i < nbase; i++) if (vbase[i]) vbase[i]->RenderBeacons(dev); + } +} + +// ============================================================== + +void vPlanet::SetupEclipse() +{ + if (scn->GetRenderPass() != RENDERPASS_MAINSCENE) { + Eclipse.bEnable = false; + return; + } + + // Check Eclipse conditions ------------------------------------------- + // + OBJHANDLE hPar = oapiGetGbodyParent(hObj); + OBJHANDLE hSun = oapiGetGbodyByIndex(0); + VECTOR3 refpoint = _V(0, 0, 0); + double sunsize = 0.0; + double plnsize = 0.0; + double apprad = 0.0; + + vObject* vE = nullptr; + + if (hPar != hSun) { + // Eclipse by Parent + vE = scn->GetVisObject(hPar); + if (IsCastingShadows(vE, this, &sunsize)) + { + refpoint = vE->GlobalPos() - GlobalPos(); + plnsize = vE->GetSize(); + } + } + else { + // Eclipse by child + int i = 0; + while (true) + { + OBJHANDLE hC = oapiGetGbodyChild(hObj, i); i++; + if (!hC) break; + auto vO = scn->GetVisObject(hC); + double sz; + // Find biggest shadow caster + if (vO && IsCastingShadows(vO, this, &sz)) { + double pz = vO->GetSize() / Distance(vO, this); + if (pz > apprad) { + vE = vO; + apprad = pz; + sunsize = sz; + refpoint = vO->GlobalPos() - GlobalPos(); + plnsize = vO->GetSize(); + } + } + } + } + + if (plnsize > 1.0 && ptEclipse && vE) + { + D3DLOCKED_RECT rect; + if (ptEclipse->LockRect(0, &rect, nullptr, D3DLOCK_DISCARD) == S_OK) { + for (int i = 0; i < 512; i++) { + float x = float(i) * float(plnsize + sunsize) / 512.0f; + float v = OcclusionFactor(x, float(sunsize), float(plnsize)); + ((float*)rect.pBits)[i] = saturate(v); + } + ptEclipse->UnlockRect(0); + } + else LogErr("Failed to Lock 'hEclipse'"); + + VECTOR3 toSun = SunDirection(); + Eclipse.bEnable = true; + Eclipse.vPos = FVECTOR3(refpoint - toSun * dotp(refpoint, toSun)); // Flatten + Eclipse.fScale = float(1.0 / (plnsize + sunsize)); + + float s = sunsize / 1e6f; + float p = plnsize / 1e6f; + + //vkDebugLog("Eclipse of %s by %s Sun(%1.1fGm) %s(%1.1fGm)", + // GetName(), vE->GetName(), s, vE->GetName(), p); + } + else { + Eclipse.bEnable = false; + } + + +} + +// ============================================================== + +void vPlanet::InitEclipse(ShaderClass* pShr) +{ + ShaderParams* sp = GetTerrainParams(); + FlowControlPS* fc = GetFlowControl(); + fc->bEclipse = Eclipse.bEnable; + sp->vEclipse = Eclipse.vPos; + sp->fEclipse = Eclipse.fScale; + pShr->SetTexture("tEclipse", ptEclipse, IPF_CLAMP | IPF_LINEAR, 0); +} + +// ============================================================== + +bool vPlanet::ModLighting (DWORD &ambient) +{ + // modify ambient light level inside atmospheres as a function of sun elevation + if (!prm.bAtm) return false; + if (cdist >= size+prm.atm_href) return false; + + double alpha = acos (dotp (unit(scn->GetCameraGPos()), -unit(cpos))); + // angular distance between sun and planet as seen from camera + + double sunelev = alpha - PI05; // elevation of sun above horizon (assuming camera on ground) + if (sunelev < -14.0*RAD) return false; // total darkness + + double rscale = (size-cdist)/prm.atm_href + 1.0; // effect altitude scale (1 on ground, 0 at reference alt) + double amb = prm.atm_amb0 * min (1.0, (sunelev+14.0*RAD)/(20.0*RAD)); // effect magnitude (dependent on sun elevation) + if (amb < 0.05) return false; + amb = max (0.0, amb-0.05); + + DWORD addamb = (DWORD)(amb*rscale*256.0); + DWORD newamb = *(DWORD*)gc->GetConfigParam (CFGPRM_AMBIENTLEVEL) + addamb; + ambient = 0; + for (int i = 0; i < 4; i++) + ambient |= min ((DWORD)255, newamb) << (i<<3); + return true; +} + +// ============================================================== +// Get a "semi" fixed surface reference point. Update if camera +// movement is greater that 2deg +// +VECTOR3 vPlanet::ReferencePoint() +{ + MATRIX3 mRot; + oapiGetRotationMatrix(hObj, &mRot); + VECTOR3 vLPos = unit(tmul(mRot, PosFromCamera())); + if (dotp(vLPos, vRefPoint)<0.9993) vRefPoint = vLPos; + return vRefPoint; +} + +// =========================================================================================== +// static +void vPlanet::ParseMicroTexturesFile() +{ + std::string filename(OapiExtension::GetConfigDir()); + filename += "MicroTex.cfg"; + + std::string line, // One file line + kwd, // Dummy 'keyword' variable + name; // Body name + int idx, found = 0; + _MicroCfg currCfg = { 0 }; + + std::ifstream fs(filename.c_str()); + while (std::getline(fs, line)) + { + // Empty or comment line? + if (!line.length() || line.find("//") == 0) { + continue; + } + + std::istringstream iss(line); + + // BODY (start of block)? + if (line.find("BODY") == 0) + { + iss >> kwd >> name; + found = 1; // we don't use 'found |= 1' on purpose here; It's a new block! + } + // NORMALS <0|1>? + else if (line.find("NORMALS") == 0) { + iss >> kwd >> currCfg.bNormals; + found |= 2; + } + // LEVEL ? + else if (line.find("LEVEL") == 0) { + iss >> kwd >> idx; + if (idx >= 0 && idx <= 2) { + iss >> currCfg.Level[idx].file >> currCfg.Level[idx].reso; + found |= (4 << idx); + } else { + LogErr("Error in MicroTex.cfg (LEVEL index out of bounds idx:=[0..2])!"); + } + } + + // Block complete? + if (found == (1|2|4|8|16)) { + MicroCfgs[name] = currCfg; + found = 0; + } + } + fs.close(); +} + +// =========================================================================================== +// +bool vPlanet::ParseMicroTextures() +{ + if (Config->MicroMode==0) return false; // Micro textures are disabled + if (surfmgr2==NULL) return false; // Only supported with tile format 2 + + // Find 'our' config + MicroCfgsIterator it = MicroCfgs.find(GetName()); + if (it != MicroCfgs.end()) { + MicroCfg = it->second; + int cnt = 0; // Check that all three textures exists + for (auto& x : MicroCfg.Level) if (x.pTex) cnt++; + return (cnt == 3); + } + return false; +} + + +// =========================================================================================== +// +vPlanet::sOverlay * vPlanet::IntersectOverlay(VECTOR4 q, FVECTOR4 *texcoord) const +{ + for (auto olay : overlays) + { + double ow = fabs(olay->lnglat.x - olay->lnglat.z); + double oh = fabs(olay->lnglat.y - olay->lnglat.w); + double ew = ow * 0.001; + double eh = oh * 0.001; + + if (q.x > (olay->lnglat.z - ew)) continue; + if (q.z < (olay->lnglat.x + ew)) continue; + if (q.y < (olay->lnglat.w + eh)) continue; + if (q.w > (olay->lnglat.y - eh)) continue; + + + double tw = fabs(q.x - q.z); + double th = fabs(q.y - q.w); + + if (ow > PI) ow = PI2 - ow; + if (tw > PI) tw = PI2 - tw; + + (*texcoord).x = float((q.x - olay->lnglat.x) / ow); + (*texcoord).y = float((olay->lnglat.y - q.y) / oh); + (*texcoord).z = float(tw / ow); + (*texcoord).w = float(th / oh); + + return olay; + } + return NULL; +} + + +// =========================================================================================== +// +vPlanet::sOverlay * vPlanet::AddOverlaySurface(VECTOR4 lnglat, gcCore::OlayType type, LPDIRECT3DTEXTURE9 pSrf, vPlanet::sOverlay *pOld, const FVECTOR4 *pB) +{ + if (type == gcCore::OlayType::RELEASE_ALL) { + overlays.remove(pOld); + return NULL; + } + + if (pOld) { + pOld->pSurf[int(type)] = pSrf; + pOld->lnglat = lnglat; + if (pB) pOld->Blend[int(type)] = FVECTOR4(pB->r, pB->g, pB->b, pB->a); + else pOld->Blend[int(type)] = FVECTOR4(1.0f, 1.0f, 1.0f, 1.0f); + return pOld; + } + + sOverlay *oLay = new sOverlay(); + memset(&oLay->pSurf, 0, sizeof(oLay->pSurf)); + oLay->pSurf[int(type)] = pSrf; + oLay->lnglat = lnglat; + if (pB) oLay->Blend[int(type)] = FVECTOR4(pB->r, pB->g, pB->b, pB->a); + else oLay->Blend[int(type)] = FVECTOR4(1.0f, 1.0f, 1.0f, 1.0f); + overlays.push_back(oLay); + return oLay; +} + + +// =========================================================================================== +// +void vPlanet::SetMicroTexture(LPDIRECT3DTEXTURE9 pSrc, int slot) +{ + LPDIRECT3DTEXTURE9 pTex = NULL; + D3DSURFACE_DESC desc; + pSrc->GetLevelDesc(0, &desc); + DWORD MipLevels = pSrc->GetLevelCount(); + HR(D3DXCreateTexture(GetDevice(), desc.Width, desc.Height, MipLevels, 0, desc.Format, D3DPOOL_DEFAULT, &pTex)); + HR(GetDevice()->UpdateTexture(pSrc, pTex)); + SAFE_RELEASE(MicroCfg.Level[slot].pTex); + MicroCfg.Level[slot].pTex = pTex; +} + +// =========================================================================================== +// static +void vPlanet::LoadMicroTextures(LPDIRECT3DDEVICE9 pDev) +{ + if (Config->MicroMode == 0) return; + + for (auto &body : MicroCfgs) + { + for (auto &x : body.second.Level) + { + char file_path[MAX_PATH]; + sprintf_s(file_path, MAX_PATH, "Textures/%s", x.file); + + // If texture is not loaded, load it + if (MicroTextures.find(x.file) == MicroTextures.end()) { + if (D3DXCreateTextureFromFileA(pDev, file_path, &x.pTex) == S_OK) { + LogAlw("Microtexture [%s] loaded", x.file); + MicroTextures[x.file] = x.pTex; + } + else { + LogErr("Failed to read microtexture [%s] for [%s]", file_path, body.first.c_str()); + MicroTextures[x.file] = NULL; + } + } + + x.pTex = MicroTextures[x.file]; + + if (x.pTex) { + D3DSURFACE_DESC desc; + x.pTex->GetLevelDesc(0, &desc); + x.px = double(desc.Width); + x.size = double(desc.Width) / x.reso; + } + } + } +} diff --git a/OVP/VulkanClient/VPlanet.h b/OVP/VulkanClient/VPlanet.h new file mode 100644 index 000000000..196da5937 --- /dev/null +++ b/OVP/VulkanClient/VPlanet.h @@ -0,0 +1,486 @@ +// ============================================================== +// VPlanet.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// Copyright (C) 2012-2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __VPLANET_H +#define __VPLANET_H + +#include "VObject.h" +#include "AtmoControls.h" +#include "Util.h" +#include "Mesh.h" +#include + +class vkMesh; +class SurfTile; +class CloudTile; + +bool FilterElevationPhysics(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, double elev_res, INT16* elev); + + +#define SUN_COLOR 0 +#define RAY_COLOR 1 +#define MIE_COLOR 2 +#define RAY_LAND 3 +#define MIE_LAND 4 +#define ATN_LAND 5 +#define SUN_GLARE 6 +#define SKY_AMBIENT 7 + +#define PLT_CONFIG -1 +#define PLT_EARTH 0 +#define PLT_MARS 1 +#define PLT_MOON 2 +#define PLT_CLOUDS 3 +#define PLT_GIANT 4 +#define PLT_G_CLOUDS 5 + +class PlanetShader : public ShaderClass +{ +public: + PlanetShader(LPDIRECT3DDEVICE9 pDev, const char* file, const char* vs, const char* ps, const char* name, const char* options); + ~PlanetShader(); + + bool bLocals; + bool bMicrotex; + bool bShdMap; + bool bDevtools; + bool bAtmosphere; + bool bWater; + bool bRipples; + bool bCloudShd; + bool bNightlights; + + HANDLE tCloud; + HANDLE tCloud2; + HANDLE tMask; + HANDLE tDiff; + HANDLE tShadowMap; + HANDLE PrmVS; + HANDLE Prm; + HANDLE FlowVS; + HANDLE Flow; + HANDLE Lights; + HANDLE Spotlight; +}; + + +#pragma pack(push, 4) + +// Bools for Scatter.hlsl +struct sFlow { + BOOL bRay; + BOOL bCamLit; + BOOL bCamInSpace; +}; + +struct ShaderParams +{ + float4x4 mWorld; // World Matrix + float4x4 mLVP; // Light-View-Projection + float4 vSHD; // Shadow Map Parameters + float4 vMSc[3]; // Micro Texture offset-scale + float4 vTexOff; // Texture offset-scale + float4 vCloudOff; // Cloud texture offset-scale + float4 vMicroOff; // Micro texture offset-scale + float4 vOverlayOff; // Overlay texture offset-scale + float4 vOverlayCtrl[4]; + float3 vEclipse; // Eclipse caster position (geocentric) + float fEclipse; // Eclipse data addressing scale factor. (to access tExlipse) + float fAlpha; + float fBeta; + float fTgtScale; +}; + +// Bools for NewPlanet.hlsl +struct FlowControlPS +{ + BOOL bInSpace; // Camera in space (not in atmosphere) + BOOL bBelowClouds; // Camera is below cloud layer + BOOL bOverlay; // Overlay on/off + BOOL bShadows; // Shadow Map on/off + BOOL bLocals; // Local Lights on/off + BOOL bMicroNormals; // Micro texture has normals + BOOL bCloudShd; // Cloud shadow textures valid and enabled + BOOL bMask; // Nightlights/water mask texture is enabled + BOOL bRipples; // Water riples texture is enabled + BOOL bMicroTex; // Micro textures exists and enabled + BOOL bPlanetShadow; // Use spherical approximation for shadow + BOOL bEclipse; // Eclipse is occuring + BOOL bTexture; // Surface texture exists +}; + +struct FlowControlVS +{ + BOOL bInSpace; // Camera in space (not in atmosphere) + BOOL bSpherical; // Ignore elevation, render as sphere + BOOL bElevOvrl; // ElevOverlay on/off +}; + + + +struct ConstParams +{ + float4x4 mVP; // View Projection Matrix + float3 CamPos; // Geocentric Camera position + float3 toCam; // Geocentric Camera direction (unit vector) + float3 toSun; // Geocentric Sun direction (unit vector) + float3 SunAz; // Atmo scatter ref.frame (unit vector) (toCam, ZeroAz, SunAz) + float3 ZeroAz; // Atmo scatter ref.frame (unit vector) + float3 Up; // Sun/Shadow Ref Frame (Unit Vector) (Up, toSun, ZeroAz) + float3 vTangent; // Reference frame for normal mapping (Unit Vector) + float3 vBiTangent; // Reference frame for normal mapping (Unit Vector) + float3 vPolarAxis; // North Pole (unit vector) + float3 cSun; // Sun Color and intensity + float3 RayWave; // .rgb Rayleigh Wave lenghts + float3 MieWave; // .rgb Mie Wave lenghts + float4 HG; // Henyey-Greenstein Phase function params + float2 iH; // Inverse scale height for ray(.r) and mie(.g) e.g. exp(-altitude * iH) + float2 rmO; // Ray and Mie out-scatter factors + float2 rmI; // Ray and Mie in-scatter factors + float3 cAmbient; // Ambient light color at sealevel + float3 cGlare; // Sun glare color + float PlanetRad; // Planet Radius + float PlanetRad2; // Planet Radius Squared + float AtmoAlt; // Atmospehere upper altitude limit + float AtmoRad; // Atmospehere outer radius + float AtmoRad2; // Atmospehere outer radius squared + float CloudAlt; // Cloud layer altitude for color and light calculations (not for phisical rendering) + float MinAlt; // Minimum terrain altitude + float MaxAlt; // Maximum terrain altitude + float iAltRng; // 1.0 / (MaxAlt - MinAlt); + float AngMin; + float AngRng; + float iAngRng; + float AngCtr; // Cos of View cone angle from planet center that's visible from camera location + float HrzDst; // Distance to horizon (500 m) minimum if camera below sea level + float CamAlt; // Camera Altitude + float CamElev; // Camera Elevation above surface + float CamRad; // Camera geo-distance + float CamRad2; // Camera geo-distance squared + float Expo; // "HDR" exposure factor (atmosphere only) + float Time; // Simulation time / 180 + float TrGamma; // Terrain "Gamma" correction setting + float TrExpo; // "HDR" exposure factor (terrain only) + float Ambient; // Global ambient light level + float Clouds; // Cloud layer intensity (if below), and Blue light inscatter scale factor (if camera Above clouds) + float TW_Terrain; // Twilight intensity + float TW_Dst; // Twilight distance behind terminator + float CosAlpha; // Cosine of camera horizon angle i.e. PlanetRad/CamRad + float SinAlpha; // Sine of ^^ + float CamSpace; // Camera in space scale factor 0.0 = surf, 1.0 = space + float Cr2; // Camera radius on shadow plane (dot(cp.toCam, cp.Up) * cp.CamRad)^2 + float ShdDst; + float SunVis; + float dCS; + float smi; + float ecc; + float trLS; + float wNrmStr; // Water normal strength + float wSpec; // Water smoothness + float wBrightness; + float wBoost; +}; + +#pragma pack(pop) + + + +// ============================================================== +// class vPlanet (interface) +// ============================================================== + +/** + * \brief Visual representation of a planetary object. + * + * A vPlanet is the visual representation of a "planetary" object (planet, moon, + * asteroid). + * Simple planets might only be implemented as spherical objects, without + * variations in elevation. + */ +class vPlanet: public vObject { + + friend class TileManager; + friend class SurfaceManager; + template friend class TileManager2; + friend class CloudManager; + friend class HazeManager; + friend class HazeManager2; + friend class RingManager; + friend class vBase; + +public: + + struct sOverlay { + LPDIRECT3DTEXTURE9 pSurf[4]; + FVECTOR4 Blend[4]; + VECTOR4 lnglat; + }; + + vPlanet (OBJHANDLE _hObj, const Scene *scene); + ~vPlanet (); + + bool ParseConfig(const char *fname); + virtual bool GetMinMaxDistance(float *zmin, float *zmax, float *dmin); + virtual void UpdateBoundingBox(); + virtual void ReOrigin(VECTOR3 global_pos); + + static void GlobalInit(oapi::vkClient* gc); + static void GlobalInitAtmosphere(oapi::vkClient* gc); + static void GlobalExit(); + + void TestComputations(Sketchpad *); + + void TileDeleted(SurfTile *tile) { if (tile == tile_cache) tile_cache = nullptr; } + void Activate(bool isactive); + bool HasTextures() { return bHasTextures; } + bool IsMesh() { return mesh != NULL; } + bool Update (bool bMainScene); + void CheckResolution (); + void RenderZRange (double *nplane, double *fplane); + bool Render(LPDIRECT3DDEVICE9 dev); + void RenderBeacons(LPDIRECT3DDEVICE9 dev); + void RenderVectors (LPDIRECT3DDEVICE9 dev, vkPad* pSkp); + bool CameraInAtmosphere() const; + double CameraAltitude() const { return cdist - size; } + double GetHorizonAlt() const; + double GetMinElevation() const { return minelev; }; + double GetMaxElevation() const { return maxelev; }; + VECTOR3 GetUnitSurfacePos(double lng, double lat) const; + VECTOR3 GetRotationAxis() const { return axis; } + VECTOR3 ToLocal(VECTOR3 &glob) const; + VECTOR3 CameraPos() const; + void GetLngLat(VECTOR3 &loc, double *lng, double *lat) const; + VECTOR3 ReferencePoint(); + void SetMicroTexture(LPDIRECT3DTEXTURE9 pSrc, int slot); + int GetElevation(double lng, double lat, double *elv, FVECTOR3 *nrm = NULL) const; + SurfTile * FindTile(double lng, double lat, int maxres); + void PickSurface(FVECTOR3 &vRay, TILEPICK *pPick); + DWORD GetPhysicsPatchRes() const { return physics_patchres; } + sOverlay * AddOverlaySurface(VECTOR4 lnglat, gcCore::OlayType type, LPDIRECT3DTEXTURE9 pSrf = NULL, sOverlay *pOld = NULL, const FVECTOR4* pB = NULL); + sOverlay * IntersectOverlay(VECTOR4 bounds, FVECTOR4* texcoord) const; + + + // Surface base interface ------------------------------------------------- + DWORD GetBaseCount() const { return nbase; } + vBase* GetBaseByIndex(DWORD index) const { return vbase[index]; } + vBase* GetBaseByHandle(OBJHANDLE hBase) const; + + + // Atmospheric physics --------------------------------------------------- + ScatterParams * GetAtmoParams(int mode=-1); + bool LoadAtmoConfig(); + void SaveAtmoConfig(); + void SaveStruct(FILEHANDLE hFile, ScatterParams* prm, int bO); + void LoadStruct(FILEHANDLE hFile, ScatterParams* prm, int bO); + char* Label(const char* x); + bool HasAtmosphere() const { return prm.bAtm; } + bool HasRipples() const { return bRipple; } + LPDIRECT3DTEXTURE9 GetScatterTable(int i); + ConstParams* GetScatterConst(); + PlanetShader* GetShader(int id = PLT_CONFIG); + int GetShaderID(); + ShaderParams* GetTerrainParams() { return &sp; } + FlowControlPS* GetFlowControl() { return &fcp; } + FlowControlVS* GetFlowControlVS() { return &fcv; } + void UpdateScatter(); + int GetAtmoMode() { return atm_mode; } + FVECTOR4 SunLightColor(VECTOR3 pos, double rf = 1.0); // For a point in anywhere + FVECTOR3 SunLightColor(float ang, float alt); // For a point in atmosphere + float SunAltitude(); + FVECTOR4 ComputeCameraView(FVECTOR3 vPos); + FVECTOR4 ComputeCameraView(float a, float r, float d); + FVECTOR4 ComputeCameraView(FVECTOR3 vPos, FVECTOR3 vNrm, FVECTOR3 vRay, float r, float t_factor = 1.0f); + FVECTOR2 Gauss7(float cos_dir, float r0, float dist, FVECTOR2 ih0); + FVECTOR2 Gauss7(float alt, float cos_dir, float R0, float R1, FVECTOR2 iH0); + FVECTOR2 Gauss4(float cos_dir, float r0, float dist, FVECTOR2 ih0); + FVECTOR4 AmbientApprox(FVECTOR3 vNrm, bool bRayleigh); + void IntegrateSegment(FVECTOR3 vOrig, FVECTOR3 vRay, float len, FVECTOR4* rl = NULL, FVECTOR4* mie = NULL, FVECTOR4* tot = NULL); + float RayLength(float cos_dir, float r0, float r1); + float RayLength(float cos_dir, float r0); + float RayPhase(float cw); + float MiePhase(float cw); + vkSun GetObjectAtmoParams(VECTOR3 relpos); + FVECTOR3 HDR(FVECTOR3 i); + FVECTOR3 LightFX(FVECTOR3 x); + float SunOcclusionByPlanet(); + bool SphericalShadow(); + void SetupEclipse(); + void InitEclipse(ShaderClass* pShader); + LPDIRECT3DTEXTURE9 GetEclipse() { return ptEclipse; } + + // v2 Labels interface ---------------------------------------------------- + void ActivateLabels(bool activate); + void RenderLabels(LPDIRECT3DDEVICE9 dev, vkPad *skp, oapi::Font **labelfont, int *fontidx); + + struct RenderPrm { //< misc. parameters for rendering the planet + // persistent options + bool bAtm; ///< planet has atmosphere + bool bCloud; ///< planet has a cloud layer + bool bCloudShadow; ///< planet renders cloud shadows on surface + bool bCloudBrighten; ///< oversaturate cloud brightness? + bool bFogEnabled; ///< does this planet support fog rendering? + double atm_href; ///< reference altitude for atmospheric effects [m] + double atm_amb0; ///< scale parameter for ambient level modification + double atm_hzalt; ///< Horizon rendering altitude + DWORD amb0col; ///< baseline ambient colour [rgba] + double cloudalt; ///< altitude of cloud layer, if present [m] + double shadowalpha; ///< opacity of shadows (0-1) + double horizon_excess; ///< extend horizon visibility radius + double tilebb_excess; ///< extend tile visibility bounding box + double horizon_minrad; ///< scale factor for lower edge of rendered horizon (account for negative elevations) (unit sphere radius) + + // frame-by-frame options + bool bAddBkg; ///< render additive to sky background (i.e. planet seen through atm.layer of another planet) + int cloudvis; ///< cloud visibility: bit0=from below, bit1=from above + double cloudrot; ///< cloud layer rotation state + bool bFog; ///< render distance fog in this frame? + bool bTint; ///< render atmospheric tint? + //bool bCloudFlatShadows; ///< render cloud shadows onto a sphere? + + // Shader Params + FVECTOR4 TintColor; + FVECTOR4 AmbColor; + FVECTOR4 FogColor; + FVECTOR4 SkyColor; + FVECTOR3 SunDir; + float FogDensity; + float DistScale; + } prm; + + struct _eclipse { + FVECTOR3 vPos; + float fScale; + bool bEnable; + } Eclipse; + + list overlays; + + // Access functions + TileManager2 *SurfMgr2() const { return surfmgr2; } + TileManager2 *CloudMgr2() const { return cloudmgr2; } + + static void ParseMicroTexturesFile(); ///< Parse MicroTex.cfg file (once) + +protected: + void RenderSphere (LPDIRECT3DDEVICE9 dev); + void RenderCloudLayer (LPDIRECT3DDEVICE9 dev, DWORD cullmode); + void RenderBaseSurfaces (LPDIRECT3DDEVICE9 dev); + void RenderBaseStructures (LPDIRECT3DDEVICE9 dev); + void RenderBaseShadows (LPDIRECT3DDEVICE9 dev, float depth); + void RenderCloudShadows (LPDIRECT3DDEVICE9 dev); + bool ModLighting (DWORD &ambient); + + bool ParseMicroTextures(); ///< Read micro-texture config for this planet + static void LoadMicroTextures(LPDIRECT3DDEVICE9 pDev); + +private: + + LPDIRECT3DTEXTURE9 pSunColor, pRaySkyView, pMieSkyView, pLandViewRay, pLandViewMie, pAmbientSky, pLandViewAtn; + + ConstParams cp; + ShaderParams sp; + FlowControlPS fcp; + FlowControlVS fcv; + + static ImageProcessing* pIP; + static PlanetShader* pRender[8]; + static LPDIRECT3DDEVICE9 pDev; + static LPDIRECT3DTEXTURE9 ptEclipse; + static int Qc, Wc, Nc; + + float dist_scale; // planet rescaling factor + double maxdist, // ??? + max_centre_dist; + + double minelev, maxelev; + double threshold; + float shadowalpha; // alpha value for surface shadows + double cloudrad; // cloud layer radius [m] + DWORD max_patchres; // max surface LOD level used for graphics + DWORD physics_patchres; // max surface LOD level used by physics + int patchres; // surface LOD level + int tilever; // Surface tile version + int iConfig; + int atm_mode; + bool renderpix; // render planet as pixel block (at large distance) + bool hashaze; // render atmospheric haze + DWORD nbase; // number of surface bases + vBase **vbase; // list of base visuals + SurfaceManager *surfmgr; // planet surface tile manager + TileManager2 *surfmgr2; // planet surface tile manager (v2) + TileManager2 *cloudmgr2; // planet cloud layer tile manager (v2) + mutable class SurfTile *tile_cache; + HazeManager *hazemgr; // horizon haze rendering + HazeManager2 *hazemgr2; // horizon haze rendering + RingManager *ringmgr; // ring manager + bool bHasTextures; + bool bRipple; // render specular ripples on water surfaces + bool bVesselShadow; // render vessel shadows on surface + bool bObjectShadow; // render object shadows on surface + bool bFog; // render distance fog? + FogParam fog; // distance fog render parameters + vkMesh *mesh; // mesh for non-spherical body + VECTOR3 vRefPoint; // Auxiliary reference point for normal mapped water + ScatterParams SPrm; // Parameters for atmospheric configuration dialog + ScatterParams OPrm; // Parameters for atmospheric configuration dialog + ScatterParams HPrm; // Parameters for atmospheric configuration dialog + ScatterParams CPrm; // Parameters for atmospheric configuration dialog + DWORD dwSctFrame; + + struct CloudData { // cloud render parameters (for legacy interface) + CloudManager *cloudmgr; // cloud tile manager + double cloudrad; // cloud layer radius [m] + double viewap; // visible radius + FMATRIX4 mWorldC; // cloud world matrix + FMATRIX4 mWorldC0; // cloud shadow world matrix + DWORD rendermode; // bit 0: render from below, bit 1: render from above + bool cloudshadow; // render cloud shadows on the surface + float shadowalpha; // alpha value for cloud shadows + double microalt0, // altitude limits for micro-textures + microalt1; + } *clouddata; + + char ShaderName[32]; + char AtmoConfigName[32]; +public: + + struct _MicroTC { + char file[32]; // Texture file name + double reso; // Resolution px/m + double size; // Texture size in meters; + double px; // Size in pixels + LPDIRECT3DTEXTURE9 pTex; + }; + + struct _MicroCfg { + _MicroTC Level[3]; + bool bNormals; // Normals enabled + bool bEnabled; // Micro textures enabled + } MicroCfg; + + struct SHDPrm { + float se; // Shadow entry + float sx; // Shadow exit + float ae; // Atmosphere entry + float ax; // Atmosphere exit + float cr; // Camera Radius in shadow frame + float hd; // Horizon distance + float h2; // Closest approach sqr-dist + float w2; + float ca; + }; + + struct TestPrm { + FVECTOR3 toCam, toSun, ZeroAz, Up, SunAz, CamPos; + float CosAlpha, SinAlpha, CamRad, CamRad2; + } TestPrm; + + SHDPrm ComputeShadow(FVECTOR3 vRay); +}; + +#endif // !__VPLANET_H diff --git a/OVP/VulkanClient/VPlanetAtmo.cpp b/OVP/VulkanClient/VPlanetAtmo.cpp new file mode 100644 index 000000000..f96ffc664 --- /dev/null +++ b/OVP/VulkanClient/VPlanetAtmo.cpp @@ -0,0 +1,1460 @@ +// ============================================================== +// VPlanet.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under LGPL v3 +// Copyright (C) 2006-2022 Martin Schweiger +// 2010-2022 Jarmo Nikkanen +// ============================================================== + +#define D3D_OVERLOADS + +#include +#include +#include + +#include "Client.h" +#include "Config.h" +#include "VPlanet.h" +#include "AtmoControls.h" +#include "VectorHelpers.h" +#include "IProcess.h" + +using namespace oapi; + +const float invalid_val = -1e12; + +// =========================================================================================== +// + +PlanetShader::PlanetShader(LPDIRECT3DDEVICE9 pDev, const char* file, const char* vs, const char* ps, const char* name, const char* options) + : ShaderClass(pDev, file, vs, ps, name, options) +{ + bLocals = false; + bMicrotex = false; + bShdMap = false; + bDevtools = false; + bAtmosphere = true; + bWater = false; + bRipples = false; + bCloudShd = false; + bNightlights = false; + + if (string(options).find("_LOCALLIGHTS") != string::npos) bLocals = true; + if (string(options).find("_MICROTEX") != string::npos) bMicrotex = true; + if (string(options).find("_SHDMAP") != string::npos) bShdMap = true; + if (string(options).find("_DEVTOOLS") != string::npos) bDevtools = true; + if (string(options).find("_NO_ATMOSPHERE") != string::npos) bAtmosphere = false; + if (string(options).find("_WATER") != string::npos) bWater = true; + if (string(options).find("_RIPPLES") != string::npos) bRipples = true; + if (string(options).find("_CLOUDSHD") != string::npos) bCloudShd = true; + if (string(options).find("_NIGHTLIGHTS") != string::npos) bNightlights = true; + + tCloud = GetPSHandle("tCloud"); + tCloud2 = GetPSHandle("tCloud2"); + tMask = GetPSHandle("tMask"); + tDiff = GetPSHandle("tDiff"); + tShadowMap = GetPSHandle("tShadowMap"); + PrmVS = GetVSHandle("Prm"); + Prm = GetPSHandle("Prm"); + FlowVS = GetVSHandle("FlowVS"); + Flow = GetPSHandle("Flow"); + Lights = GetPSHandle("Lights"); + Spotlight = GetPSHandle("Spotlight"); +}; + +PlanetShader::~PlanetShader() +{ + +} + + + + + + +PlanetShader* vPlanet::pRender[8] = {}; + + +// =========================================================================================== +// +void vPlanet::GlobalInitAtmosphere(oapi::vkClient* gc) +{ + pDev = gc->GetDevice(); + + if (Config->bAtmoQuality) pIP = new ImageProcessing(pDev, "Modules/vkShaders/Scatter.hlsl", "SunColor"); + else pIP = new ImageProcessing(pDev, "Modules/vkShaders/Scatter.hlsl", "SunColor", "_PERFORMANCE"); + + pIP->CompileShader("SkyView"); + pIP->CompileShader("LandView"); + pIP->CompileShader("RingView"); + pIP->CompileShader("AmbientSky"); + pIP->CompileShader("LandViewAtten"); + + if (!pIP->IsOK()) { + oapiWriteLog((char*)"InitializeScatteringEx() FAILED"); + return; + } + + // Get Texture size constants form shader file + // + //Wc = pIP->FindDefine("Wc"); + //Nc = pIP->FindDefine("Nc"); + //Qc = pIP->FindDefine("Qc"); + + if (Config->bAtmoQuality) { + Nc = 8; Wc = 128; Qc = 96; + } + else { + Nc = 6; Wc = 72; Qc = 64; + } + + bool bRiples = *(bool*)gc->GetConfigParam(CFGPRM_SURFACERIPPLE); + bool bShadows = *(bool*)gc->GetConfigParam(CFGPRM_CLOUDSHADOWS); + bool bClouds = *(bool*)gc->GetConfigParam(CFGPRM_CLOUDS); + bool bNightLights = *(bool*)gc->GetConfigParam(CFGPRM_SURFACELIGHTS); + bool bWater = *(bool*)gc->GetConfigParam(CFGPRM_SURFACEREFLECT); + bool bLocals = *(bool*)gc->GetConfigParam(CFGPRM_LOCALLIGHT); + bool bAtmosphere = *(bool*)gc->GetConfigParam(CFGPRM_ATMHAZE); + + // --------------------------------------------------- + // Compile planet shader with different configurations + // --------------------------------------------------- + + string blend = ""; + string flags = ""; + if (!Config->bAtmoQuality) flags += "_PERFORMANCE "; + + pRender[PLT_GIANT] = new PlanetShader(pDev, "Modules/vkShaders/NewPlanet.hlsl", "GiantVS", "GiantPS", "Giant", flags.c_str()); + pRender[PLT_G_CLOUDS] = new PlanetShader(pDev, "Modules/vkShaders/NewPlanet.hlsl", "CloudVS", "GiantCloudPS", "GiantCloud", flags.c_str()); + + if (Config->CloudMicro) flags += "_CLOUDMICRO "; + if (Config->bCloudNormals) flags += "_CLOUDNORMALS "; + + pRender[PLT_CLOUDS] = new PlanetShader(pDev, "Modules/vkShaders/NewPlanet.hlsl", "CloudVS", "CloudPS", "Clouds", flags.c_str()); + + + + flags = ""; + if (Config->BlendMode == 0) blend = "_SOFT "; + if (Config->BlendMode == 1) blend = "_MED "; + if (Config->BlendMode == 2) blend = "_HARD "; + + if (bLocals) flags += "_LOCALLIGHTS "; + if (Config->MicroMode) flags += "_MICROTEX "; + if (Config->ShadowMapMode) flags += "_SHDMAP "; + if (Config->EnableMeshDbg) flags += "_DEVTOOLS "; + if (!Config->bAtmoQuality) flags += "_PERFORMANCE "; + + pRender[PLT_MARS] = new PlanetShader(pDev, "Modules/vkShaders/NewPlanet.hlsl", "TerrainVS", "TerrainPS", "Mars", (flags + blend).c_str()); + + + flags += "_NO_ATMOSPHERE "; + + pRender[PLT_MOON] = new PlanetShader(pDev, "Modules/vkShaders/NewPlanet.hlsl", "TerrainVS", "TerrainPS", "Moon", (flags + blend).c_str()); + + + flags = ""; + blend = ""; + if (bWater) flags += "_WATER "; + if (bWater && bRiples) flags += "_RIPPLES "; + if (bShadows) flags += "_CLOUDSHD "; + if (bNightLights) flags += "_NIGHTLIGHTS "; + if (bLocals) flags += "_LOCALLIGHTS "; + + if (Config->ShadowMapMode) flags += "_SHDMAP "; + if (Config->EnableMeshDbg) flags += "_DEVTOOLS "; + if (!Config->bAtmoQuality) flags += "_PERFORMANCE "; + + pRender[PLT_EARTH] = new PlanetShader(pDev, "Modules/vkShaders/NewPlanet.hlsl", "TerrainVS", "TerrainPS", "Earth", (flags + blend).c_str()); +} + + +// =========================================================================================== +// +FVECTOR2 vPlanet::Gauss7(float alt, float cos_dir, float R0, float R1, FVECTOR2 iH0) +{ + float dist = RayLength(cos_dir, R0 + alt, R1); + return Gauss7(cos_dir, R0 + alt, dist, iH0); +} + + +// =========================================================================================== +// +FVECTOR2 vPlanet::Gauss7(float cos_dir, float r0, float dist, FVECTOR2 ih0) +{ + static const float n[] = { -0.949107, -0.741531, -0.405845, 0.0, 0.405845,0.741531, 0.949107 }; + static const float w[] = { 0.129485, 0.279705, 0.381830, 0.417959, 0.381830, 0.279705, 0.129485 }; + + float x = 2.0 * r0 * cos_dir; + float r2 = r0 * r0; + float a[7]; + + // Compute altitudes of sample points + for (int i = 0; i < 7; i++) { + float d0 = dist * (n[i] + 1.0f) * 0.5f; + a[i] = sqrt(r2 + d0 * d0 - d0 * x) - cp.PlanetRad; + } + + float ray = 0.0f; float mie = 0.0f; + for (int i = 0; i < 7; i++) { + ray += exp(-::clamp(a[i] * ih0.x, -20.0f, 20.0f)) * w[i]; + mie += exp(-::clamp(a[i] * ih0.y, -20.0f, 20.0f)) * w[i]; + } + return FVECTOR2(ray, mie) * 0.5f * dist; +} + + +// =========================================================================================== +// +FVECTOR2 vPlanet::Gauss4(float cos_dir, float r0, float dist, FVECTOR2 ih0) +{ + static const float n[] = { 0.06943f, 0.33001f, 0.66999f, 0.93057f }; + static const float w[] = { 0.34786f, 0.65215f, 0.65215f, 0.34786f }; + + float x = 2.0 * r0 * cos_dir; + float r2 = r0 * r0; + float a[4]; + + // Compute altitudes of sample points + for (int i = 0; i < 4; i++) { + float d0 = dist * n[i]; + a[i] = sqrt(r2 + d0 * d0 - d0 * x) - cp.PlanetRad; + } + + float ray = 0.0f; float mie = 0.0f; + for (int i = 0; i < 4; i++) { + ray += exp(-::clamp(a[i] * ih0.x, -20.0f, 20.0f)) * w[i]; + mie += exp(-::clamp(a[i] * ih0.y, -20.0f, 20.0f)) * w[i]; + } + return FVECTOR2(ray, mie) * 0.5f * dist; +} + + +// =========================================================================================== +// +float vPlanet::SunAltitude() +{ + float d = dotp(cp.toCam, cp.toSun); + float q = cp.CamRad * d; + + // Ray's closest approach to planet + float alt = sqrt(cp.CamRad2 - q * q) - cp.PlanetRad; + if (d < 0) return alt; + return cp.AtmoAlt; +} + + +// =========================================================================================== +// +float vPlanet::RayLength(float cos_dir, float r0, float r1) +{ + float y = r0 * cos_dir; + float z2 = r0 * r0 - y * y; + return sqrt(r1 * r1 - z2) + y; +} + + +// =========================================================================================== +// +float vPlanet::RayLength(float cos_dir, float r0) +{ + return RayLength(cos_dir, r0, cp.AtmoRad); +} + + +// =========================================================================================== +// Rayleigh phase function +// +float vPlanet::RayPhase(float cw) +{ + return 0.25f * (4.0f + cw * cw); +} + + +// =========================================================================================== +// Henyey-Greenstein Phase function +// +/*float vPlanet::MiePhase(float cw) +{ + float cw2 = cw * cw; + return cp.HG.x * (1.0f + cw2) * pow(abs(cp.HG.y - cp.HG.z * cw2 * cw), -1.5f) + cp.HG.w; +}*/ + +float vPlanet::MiePhase(float cw) +{ + return 8.0f * cp.HG.x / (1.0f - cp.HG.y * cw) + cp.HG.w; +} + + +// =========================================================================================== +// Compute optical transparency of atmosphere. color in .rgb and (ray+mie) optical depth in .a +// +FVECTOR4 vPlanet::ComputeCameraView(float angle, float rad, float distance) +{ + FVECTOR2 od = Gauss7(angle, rad, distance, cp.iH); // Ray/Mie Optical depth + FVECTOR2 rm = od * cp.rmO; + FVECTOR3 clr = cp.RayWave * rm.x + cp.MieWave * rm.y; + return FVECTOR4(exp(-clr), od.x + od.y); +} + + +// =========================================================================================== +// +FVECTOR4 vPlanet::ComputeCameraView(FVECTOR3 vPos, FVECTOR3 vNrm, FVECTOR3 vRay, float r, float t_factor) +{ + float d = 0.0f; + float a = dotp(vNrm, vRay); + if (!CameraInAtmosphere()) d = RayLength(a, r); + else d = abs(dotp(vPos - cp.CamPos, vRay)); + return ComputeCameraView(a, r, d); +} + + +// =========================================================================================== +// +FVECTOR4 vPlanet::ComputeCameraView(FVECTOR3 vPos) +{ + FVECTOR3 vRP = vPos - cp.CamPos; + float d = length(vRP); + float r = length(vPos); + FVECTOR3 vRay = vRP / d; + float a = dotp(vPos / r, vRay); + if (!CameraInAtmosphere()) d = RayLength(a, r); // Recompute distance to atm exit + return ComputeCameraView(a, r, d); +} + + +// =========================================================================================== +// Amount of light inscattering along the ray +// +void vPlanet::IntegrateSegment(FVECTOR3 vOrig, FVECTOR3 vRay, float len, FVECTOR4* ral, FVECTOR4* mie, FVECTOR4* tot) +{ + static const int NSEG = 6; + static const float iNSEG = 1.0f / float(NSEG); + + if (ral) *ral = 0.0f; + if (mie) *mie = 0.0f; + + for (int i = 0; i < NSEG; i++) + { + float dst = len * (iNSEG * (float(i) + 0.5f)); + FVECTOR3 pos = vOrig + vRay * dst; + FVECTOR3 n = unit(pos); + float rad = dotp(n, pos); + float alt = rad - cp.PlanetRad; + float ang = dotp(n, vRay); + + FVECTOR3 x = SunLightColor(-dotp(n, cp.toSun), alt) * ComputeCameraView(-ang, rad, len - dst).rgb; + + if (ral) { + float f = exp(-alt * cp.iH.x) * iNSEG; + ral->rgb += x * f; ral->a += f; + } + if (mie) { + float f = exp(-alt * cp.iH.y) * iNSEG; + mie->rgb += x * f; mie->a += f; + } + } + + + if (ral) ral->rgb *= cp.RayWave * cp.cSun * cp.rmI.x * len; // Multiply with wavelength and inscatter factors + if (mie) mie->rgb *= cp.MieWave * cp.cSun * cp.rmI.y * len; + if (ral) ral->a *= len; + if (mie) mie->a *= len; + if (tot && ral && mie) { + float dRS = dotp(vRay, cp.toSun); + tot->rgb = HDR(ral->rgb * RayPhase(dRS) + mie->rgb * MiePhase(dRS)); + } +} + +// =========================================================================================== +// +FVECTOR3 vPlanet::HDR(FVECTOR3 i) +{ + return -exp(-i * cp.Expo) + 1.0f; +} + +// =========================================================================================== +// +FVECTOR3 vPlanet::LightFX(FVECTOR3 x) +{ + return (x * 2.0f) / (x + 1.0f); +} + +// =========================================================================================== +// Sunlight color for pixel in atmosphere +// +FVECTOR3 vPlanet::SunLightColor(float angle, float alt) +{ + float r = alt + cp.PlanetRad; + float d = RayLength(angle, r); + FVECTOR2 rm = Gauss7(angle, r, d, cp.iH) * cp.rmO; + FVECTOR3 clr = cp.RayWave * rm.x + cp.MieWave * rm.y; + return exp(-clr); +} + +// =========================================================================================== +// +float vPlanet::SunOcclusionByPlanet() +{ + OBJHANDLE hSun = oapiGetObjectByIndex(0); + VECTOR3 up = unit(-cpos); + double r = dotp(up, -cpos); + double ca = dotp(up, SunDirection()); + double om = saturate(1.0 - ca * ca); + double qr = sqrt(om) * r; + double sd = SunDistance(); + double dp = r * r - size * size; + double hd = dp > 1e4 ? sqrt(dp) : 1000.0; // Distance to horizon + double sr = oapiGetSize(hSun) * abs(hd) / sd; + double svb = ca > 0.0 ? 1.0 : ilerp(size - sr * 0.33, size + sr, qr); // How much of the sun's "disc" is shadowed by planet + return svb; +} + + +// =========================================================================================== +// +FVECTOR4 vPlanet::SunLightColor(VECTOR3 relpos, double rf) +{ + static const float lim = 0.0f; + + OBJHANDLE hSun = oapiGetGbodyByIndex(0); + assert(hSun != hObj); + + if (!active) { + Update(true); + UpdateScatter(); + } + FVECTOR3 cSun = 1.0f; + + OBJHANDLE hParent = oapiGetGbodyParent(hObj); + if (oapiGetObjectType(hParent) == OBJTP_PLANET) { + cSun *= ::SunOcclusionByPlanet(hParent, relpos + gpos); + } + + VECTOR3 sunp; + oapiGetGlobalPos(hSun, &sunp); + + VECTOR3 crs = sunp - (relpos + gpos); // 'relpos' relative to sun + double sd = length(crs); // 'relpos' sun distance + VECTOR3 ucs = crs / sd; // unit sun direction + FVECTOR2 rm = 0.0f; + VECTOR3 up = unit(relpos); + double r = dotp(up, relpos); // length of 'relpos' + double ca = dotp(up, ucs); + double om = saturate(1.0 - ca * ca); + double qr = sqrt(om) * r; // distance of ray's closest approach to a planet + double ar = GetHorizonAlt() + size; + + bool bAtm = HasAtmosphere() && (surfmgr2 != NULL); + + if (!bAtm) { + if (ca > lim) return FVECTOR4(cSun, 1); //'relpos' is on a sun lit side of the planet + } + else { + if (qr > ar) return FVECTOR4(cSun, 1); // Ray doesn't intersect atmosphere + if (r > ar && ca > lim) return FVECTOR4(cSun, 1); // Ray doesn't intersect atmosphere + } + + double dp = r * r - size * size; + double hd = dp > 1e4 ? sqrt(dp) : 1000.0; // Distance to horizon + double sr = oapiGetSize(hSun) * abs(hd) / sd; + double svb = ca > lim ? 1.0 : ilerp(size - sr * 0.33, size + sr, qr); // How much of the sun's "disc" is shadowed by planet + + if (!bAtm || !surfmgr2) return FVECTOR4(cSun*svb, svb); + + if (svb < 1e-3) return FVECTOR4(0.0, 0.0, 0.0, svb); // Ray is obscured by planet + if (r > ar) rm = Gauss7(qr - size, 0.0f, cp.PlanetRad, cp.AtmoRad, cp.iH) * 2.0f; // Ray passes through atmosphere from space to space + else rm = Gauss7(r - size, -ca, cp.PlanetRad, cp.AtmoRad, cp.iH); // Sample point 'pos' lies with-in atmosphere + + rm = rm * (cp.rmO * rf); + cSun *= exp(-(cp.RayWave * rm.x + cp.MieWave * rm.y)) * svb; + return FVECTOR4(cSun, svb); +} + + +// =========================================================================================== +// +FVECTOR4 vPlanet::AmbientApprox(FVECTOR3 vNrm, bool bR) +{ + float dNS = -dotp(vNrm, cp.toSun); + float fA = 1.0f - hermite(ilerp(0.0f, cp.TW_Dst, dNS)); + float3 clr = (bR ? cp.RayWave : cp.cAmbient); + return float4(clr, fA); +} + + +// =========================================================================================== +// +vkSun vPlanet::GetObjectAtmoParams(VECTOR3 vRelPos) +{ + assert(string(name) != "Sun"); + + if (!active) { + Update(true); + UpdateScatter(); + } + + vkSun op; + op.Transmission = 1.0f; + op.Incatter = 0.0f; + + double r = length(vRelPos); + float a = 0.0f; + if (HasAtmosphere()) a = cp.AtmoAlt * 0.5f; + + FVECTOR3 cSun = 1.0f; + + OBJHANDLE hParent = oapiGetGbodyParent(hObj); + if (oapiGetObjectType(hParent) == OBJTP_PLANET) { + cSun *= ::SunOcclusionByPlanet(hParent, vRelPos + gpos); + } + + cSun *= SunLightColor(vRelPos).rgb * cp.cSun; + + DWORD ambient = *(DWORD*)gc->GetConfigParam(CFGPRM_AMBIENTLEVEL); + + if (((r - size) > a) || !HasAtmosphere() || !surfmgr2) // In space + { + op.Dir = -cp.toSun; + op.Color = (MaxRGB(cSun) > 1.0f ? cSun / MaxRGB(cSun) : cSun) * Config->GFXSunIntensity; + op.Ambient = float(ambient) * 0.0039f; + return op; + } + + VECTOR3 vNrm = unit(vRelPos); + VECTOR3 vRP = (vRelPos + cpos); // Camera relative position + double d = length(vRP); // Distance to camera + FVECTOR3 vRay = (vRP / d); // Unit viewing ray from cameta to obj_gpos + float dNR = dotp(vNrm, vRay); // cosine of (Normal/vRay) angle + float dRS = dotp(vRay, cp.toSun); + float dNS = dotp(vNrm, cp.toSun); + float alt = r - size; + + FVECTOR4 mc = AmbientApprox(vNrm, false); + mc.a *= CPrm.tw_bld; + + + FVECTOR3 cSunAmb = SunLightColor(-dNS, alt) * cp.cSun; + cSunAmb = MaxRGB(cSunAmb) > 1.0f ? cSunAmb / MaxRGB(cSunAmb) : cSunAmb; + + op.Dir = -cp.toSun; + op.Color = (MaxRGB(cSun) > 1.0f ? cSun / MaxRGB(cSun) : cSun) * Config->GFXSunIntensity; + op.Ambient = unit(mc.rgb + cSunAmb * 2.0f) * mc.a * mc.g; + op.Ambient *= exp(-alt * cp.iH.x) * cp.rmI.x * 6e5; + op.Ambient += float(ambient) * 0.0039f; + + if (d > 1000.0f) { + FVECTOR4 rl, mi; // Incatter Color + IntegrateSegment(vRelPos, -vRay, d, &rl, &mi); // Rayleigh and Mie inscatter + op.Transmission = ComputeCameraView(dNR, r, d).rgb; + op.Incatter = HDR(rl.rgb * RayPhase(dRS) + mi.rgb * MiePhase(dRS)); + } + + return op; +} + + +// =========================================================================================== +// +bool vPlanet::SphericalShadow() +{ + if (abs(cp.MinAlt) < 1.0f && abs(cp.MaxAlt) < 1.0f) return false; // Spherical body or no data available + if (((cp.MaxAlt - cp.MinAlt) / cp.PlanetRad) < 0.05f) return true; + return false; +} + + +// =========================================================================================== +// +void vPlanet::UpdateScatter() +{ + if (scn->GetFrameId() == dwSctFrame) return; + if (scn->GetRenderPass() != RENDERPASS_MAINSCENE) return; + if (mesh) return; + + dwSctFrame = scn->GetFrameId(); + + if (HasAtmosphere() && surfmgr2) + { + if (!pSunColor) D3DXCreateTexture(pDev, 4*Qc, Qc/2, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pSunColor); + if (!pRaySkyView) D3DXCreateTexture(pDev, Qc * 2, Qc, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pRaySkyView); + if (!pMieSkyView) D3DXCreateTexture(pDev, Qc * 2, Qc, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pMieSkyView); + if (!pLandViewRay) D3DXCreateTexture(pDev, Wc * Nc, Wc, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pLandViewRay); + if (!pLandViewMie) D3DXCreateTexture(pDev, Wc * Nc, Wc, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pLandViewMie); + if (!pLandViewAtn) D3DXCreateTexture(pDev, Wc * Nc, Wc, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pLandViewAtn); + if (!pAmbientSky) D3DXCreateTexture(pDev, Qc, Qc, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &pAmbientSky); + } + + VECTOR3 sundir, campos; OBJHANDLE hSun = oapiGetGbodyByIndex(0); + oapiGetGlobalPos(hSun, &sundir); + oapiGetGlobalPos(hObj, &gpos); + oapiCameraGlobalPos(&campos); + + sundir = unit(sundir - gpos); + FVECTOR3 cam = (campos - gpos); + + const ScatterParams* atmo = GetAtmoParams(); + + DWORD dAmbient = *(DWORD*)gc->GetConfigParam(CFGPRM_AMBIENTLEVEL); + float fAmbient = float(dAmbient) * 0.0039f; + + double pr = GetSize() + minelev; // Planet Min Radius + double cr = CamDist(); // Camera distance from a planet center + double ca = cr - pr; // Camera altitude + + float mdh = 6000.0f; // Minumum distance to horizon + float scr = 1.6e-6; + float scm = 1.0e-6; + + float ph = atmo->mphase * 200.0f; + float g = ph / sqrt(1.0f + ph * ph); + float hrz = sqrt(max(0.0, cr * cr - pr * pr)); + float qw = float(pr / cr); + + LPDIRECT3DSURFACE9 pTgt, pTgt2; + + // --------------------------------------------------------------------- + // Initialize camera centric tangent frame for normal mapped water + // + MATRIX3 mRot; + oapiGetRotationMatrix(hObj, &mRot); + + VECTOR3 vNrm = mul(mRot, ReferencePoint()); + VECTOR3 vRot = unit(mul(mRot, _V(0, 1, 0))); + VECTOR3 vTan = unit(crossp(vRot, vNrm)); + VECTOR3 vBiT = unit(crossp(vTan, vNrm)); + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.mVP, scn->GetProjectionViewMatrix(), sizeof(FMATRIX4)); + + cp.vPolarAxis = vRot; + cp.vTangent = vTan; // RefFrame for surface micro-tex and water + cp.vBiTangent = vBiT; // RefFrame for surface micro-tex and water + cp.cSun = FVECTOR3(1, 1, 1) * atmo->suni; + cp.PlanetRad = float(pr); + cp.PlanetRad2 = float(pr * pr); + cp.MinAlt = float(minelev); + cp.MaxAlt = float(maxelev); // Max Elevation above sea-level + cp.iAltRng = 1.0f / (cp.MaxAlt - cp.MinAlt); + cp.CamAlt = float(ca); + cp.CamElev = float(scn->GetCameraElevation()); + cp.CamRad = float(cr); + cp.CamRad2 = float(cr * cr); + cp.CamPos = cam; + cp.toCam = unit(cam); + cp.toSun = sundir; // Sun-aligned Atmo Scatter RefFrame + cp.dCS = dotp(cp.toCam, cp.toSun); + cp.ZeroAz = unit(crossp(cp.toCam, cp.toSun)); // Sun-aligned Atmo Scatter RefFrame + cp.SunAz = unit(crossp(cp.toCam, cp.ZeroAz)); // Sun-aligned Atmo Scatter RefFrame + cp.Up = unit(crossp(cp.ZeroAz, cp.toSun)); + cp.HrzDst = sqrt(max(mdh, cp.CamRad2 - cp.PlanetRad2)); + cp.Time = fmod(oapiGetSimTime(), 3600.0f); + cp.TrGamma = 1.0f / float(atmo->tgamma); + cp.TrExpo = float(atmo->trb); + cp.Ambient = fAmbient; + cp.CosAlpha = min(1.0f, cp.PlanetRad / cp.CamRad); + cp.SinAlpha = sqrt(1.0f - cp.CosAlpha * cp.CosAlpha); + cp.cAmbient = atmo->acolor; + float A = dotp(cp.toCam, cp.Up) * cp.CamRad; + cp.Cr2 = A * A; + float g2 = cp.CamRad2 - cp.PlanetRad2; + cp.ShdDst = g2 > 0 ? sqrt(g2) : 0.0f; + cp.SunVis = SunOcclusionByPlanet(); + cp.trLS = float(atmo->tr3D); + + if (HasAtmosphere() == false) return; + if (surfmgr2 == NULL) return; + + + // Skip the rest if no atmosphere exists + // ------------------------------------------------------------------------------------------------------------ + float visalt = atmo->visalt; + cp.AtmoAlt = visalt; + cp.AtmoRad = visalt + cp.PlanetRad; + cp.AtmoRad2 = cp.AtmoRad * cp.AtmoRad; + cp.CloudAlt = prm.bCloud ? float(atmo->aux2 * 1e3f) : 0.0f; + cp.CamSpace = sqrt(saturate(cp.CamAlt / visalt)); + cp.AngMin = -sqrt(max(1.0f, cp.CamRad2 - cp.PlanetRad2)) / cp.CamRad; + cp.AngRng = 1.0f - cp.AngMin; + cp.iAngRng = 1.0f / cp.AngRng; + cp.AngCtr = sqrt(max(0.0f, 1.0f - qw * qw)); + cp.RayWave = unit(pow(FVECTOR3(atmo->red, atmo->green, atmo->blue), -atmo->rpow)); + cp.MieWave = unit(pow(FVECTOR3(atmo->red, atmo->green, atmo->blue), -atmo->mpow)); + + cp.rmO.x = atmo->ray * atmo->rayrat * scr * 4.0f * PI; + cp.rmO.y = atmo->mie * atmo->mierat * scm * 4.0f * PI; + cp.rmI.x = atmo->ray * scr; + cp.rmI.y = atmo->mie * scm; + + cp.iH.x = 1.0f / float(atmo->rheight * 1000.0); + cp.iH.y = 1.0f / float(atmo->mheight * 1000.0); + cp.Expo = atmo->aux3; + //cp.HG = FVECTOR4(1.5f * (1.0f - g * g) / (2.0f + g * g), 1.0f + g * g, 2.0f * g, float(atmo->mphaseb)); + cp.HG = FVECTOR4(pow(1.0f - g * g, 0.75f), g, 0.0f, float(atmo->mphaseb)); + + cp.cGlare = atmo->zcolor; + cp.Clouds = float(atmo->hazei); + cp.TW_Terrain = float(atmo->tw_bri); + cp.TW_Dst = float(atmo->tw_dst); + + cp.wNrmStr = 1.0f / float(atmo->wnrml); + cp.wSpec = float(atmo->wspec); + cp.wBrightness = float(atmo->wtrans); + cp.wBoost = float(atmo->wboost); + + if (cp.CamAlt < prm.cloudalt) { + float SMi = cp.CloudAlt; + float SMa = min(100e3f, cp.HrzDst); // Semi-major axis + cp.ecc = sqrt((SMa * SMa - SMi * SMi) / (SMa * SMa)); // eccentricity + cp.smi = SMi; + } + else { + cp.ecc = 0.0f; + cp.smi = cp.HrzDst; + } + + + sFlow Flow; + Flow.bCamLit = !((cp.Cr2 < cp.PlanetRad2) && (dotp(cp.toCam, cp.toSun) < 0)); + Flow.bCamInSpace = !CameraInAtmosphere(); + + + // + // ---------------------------------------------------------------------------- + // + pIP->Activate("SunColor"); + pIP->SetStruct("Const", &cp, sizeof(ConstParams)); + + pSunColor->GetSurfaceLevel(0, &pTgt); + pIP->SetOutputNative(0, pTgt); + if (!pIP->Execute(true)) LogErr("pIP Execute Failed (SunColor)"); + SAFE_RELEASE(pTgt); + + + // + // ---------------------------------------------------------------------------- + // + if (CameraInAtmosphere()) pIP->Activate("SkyView"); + else pIP->Activate("RingView"); + + // Rayleigh calculations + Flow.bRay = true; + pIP->SetStruct("Flo", &Flow, sizeof(sFlow)); + pIP->SetStruct("Const", &cp, sizeof(ConstParams)); + pIP->SetTextureNative("tSun", pSunColor, IPF_CLAMP | IPF_LINEAR); + pRaySkyView->GetSurfaceLevel(0, &pTgt); + pIP->SetOutputNative(0, pTgt); + if (!pIP->Execute(true)) LogErr("pIP Execute Failed (SkyView)"); + SAFE_RELEASE(pTgt); + + // Mie calculations + Flow.bRay = false; + pIP->SetStruct("Flo", &Flow, sizeof(sFlow)); + pIP->SetStruct("Const", &cp, sizeof(ConstParams)); + pMieSkyView->GetSurfaceLevel(0, &pTgt); + pIP->SetOutputNative(0, pTgt); + if (!pIP->Execute(true)) LogErr("pIP Execute Failed (SkyView)"); + SAFE_RELEASE(pTgt); + + + // + // ---------------------------------------------------------------------------- + // + pIP->Activate("AmbientSky"); + pIP->SetStruct("Const", &cp, sizeof(ConstParams)); + pIP->SetTextureNative("tSkyRayColor", pRaySkyView, IPF_CLAMP | IPF_LINEAR); + pIP->SetTextureNative("tSkyMieColor", pMieSkyView, IPF_CLAMP | IPF_LINEAR); + + pAmbientSky->GetSurfaceLevel(0, &pTgt); + pIP->SetOutputNative(0, pTgt); + if (!pIP->Execute(true)) LogErr("pIP Execute Failed (AmbientSky)"); + SAFE_RELEASE(pTgt); + + + // + // ---------------------------------------------------------------------------- + // + pIP->Activate("LandViewAtten"); + pIP->SetStruct("Const", &cp, sizeof(ConstParams)); + + pLandViewAtn->GetSurfaceLevel(0, &pTgt); + pIP->SetOutputNative(0, pTgt); + if (!pIP->Execute(true)) LogErr("pIP Execute Failed (AmbientSky)"); + SAFE_RELEASE(pTgt); + + + // + // ---------------------------------------------------------------------------- + // + pIP->Activate("LandView"); + + // Rayleigh calculations + Flow.bRay = true; + pIP->SetStruct("Const", &cp, sizeof(ConstParams)); + pIP->SetStruct("Flo", &Flow, sizeof(sFlow)); + pIP->SetTextureNative("tSun", pSunColor, IPF_CLAMP | IPF_LINEAR); + pLandViewRay->GetSurfaceLevel(0, &pTgt); + pIP->SetOutputNative(0, pTgt); + if (!pIP->Execute(true)) LogErr("pIP Execute Failed (SkyView)"); + SAFE_RELEASE(pTgt); + + // Mie calculations + Flow.bRay = false; + pIP->SetStruct("Const", &cp, sizeof(ConstParams)); + pIP->SetStruct("Flo", &Flow, sizeof(sFlow)); + pLandViewMie->GetSurfaceLevel(0, &pTgt); + pIP->SetOutputNative(0, pTgt); + if (!pIP->Execute(true)) LogErr("pIP Execute Failed (SkyView)"); + SAFE_RELEASE(pTgt); +} + +// ============================================================== + +PlanetShader* vPlanet::GetShader(int id) +{ + if (id == PLT_CONFIG) return pRender[GetShaderID()]; + return pRender[id]; +} + +// ============================================================== + +int vPlanet::GetShaderID() +{ + if (strcmp(ShaderName, "Moon") == 0) return PLT_MOON; + if (strcmp(ShaderName, "Earth") == 0) return PLT_EARTH; + if (strcmp(ShaderName, "Mars") == 0) return PLT_MARS; + if (strcmp(ShaderName, "Giant") == 0) return PLT_GIANT; + if (strcmp(ShaderName, "Auto") == 0) + { + bool has_atmosphere = HasAtmosphere(); + if (has_atmosphere) { + bool has_ripples = HasRipples(); + bool render_shadows = CloudMgr2() != NULL; + if (has_ripples || render_shadows) return PLT_EARTH; + if (size > 6e6) return PLT_GIANT; + return PLT_MARS; + } + } + return PLT_MOON; +} + +// ============================================================== + +ConstParams* vPlanet::GetScatterConst() +{ + if (!active) return NULL; + if (surfmgr2) return &cp; + return NULL; +} + +// ============================================================== + +LPDIRECT3DTEXTURE9 vPlanet::GetScatterTable(int i) +{ + switch (i) + { + case SUN_COLOR: return pSunColor; + case RAY_COLOR: return pRaySkyView; + case MIE_COLOR: return pMieSkyView; + case RAY_LAND: return pLandViewRay; + case MIE_LAND: return pLandViewMie; + case ATN_LAND: return pLandViewAtn; + case SKY_AMBIENT: return pAmbientSky; + default: return NULL; + } + return NULL; +} + +// ============================================================== + +ScatterParams* vPlanet::GetAtmoParams(int mode) +{ + if (!prm.bAtm || prm.atm_hzalt == 0.0) { + atm_mode = 1; + HPrm.cfg_alt = OPrm.cfg_alt = SPrm.cfg_alt = 0.0; + HPrm.cfg_halt = OPrm.cfg_halt = SPrm.cfg_halt = 0.0; + return &SPrm; // Return surface setup if a planet doesn't have atmosphere + } + + double lorb = SPrm.orbalt; + double horb = SPrm.orbalt * 10.0; + double ca = CamDist() - size; + double alt = saturate(ca / lorb); + double halt = saturate((ca - lorb) / horb); + + CPrm.cfg_alt = HPrm.cfg_alt = OPrm.cfg_alt = SPrm.cfg_alt = alt; + CPrm.cfg_halt = HPrm.cfg_halt = OPrm.cfg_halt = SPrm.cfg_halt = halt; + + if (mode == 0) { + if (ca < abs(ca - lorb)) mode = 1; + else { + if (abs(ca - lorb) < abs(ca - horb)) mode = 2; + else mode = 3; + } + } + + atm_mode = mode; + + if (mode == 1) return &SPrm; // Surface configuration + if (mode == 2) return &OPrm; // Orbital configuration + if (mode == 3) return &HPrm; // High Orbital configuration + + if (alt < 0.9999) + { + // ---------------------------------------------------- + CPrm.mierat = lerp(SPrm.mierat, OPrm.mierat, alt); + CPrm.tr3D = lerp(SPrm.tr3D, OPrm.tr3D, alt); + CPrm.aux3 = lerp(SPrm.aux3, OPrm.aux3, alt); + CPrm.mheight = lerp(SPrm.mheight, OPrm.mheight, alt); + CPrm.rheight = lerp(SPrm.rheight, OPrm.rheight, alt); + CPrm.trb = lerp(SPrm.trb, OPrm.trb, alt); + CPrm.mie = lerp(SPrm.mie, OPrm.mie, alt); + CPrm.mphase = lerp(SPrm.mphase, OPrm.mphase, alt); + CPrm.mpow = lerp(SPrm.mpow, OPrm.mpow, alt); + CPrm.rayrat = lerp(SPrm.rayrat, OPrm.rayrat, alt); + CPrm.ray = lerp(SPrm.ray, OPrm.ray, alt); + CPrm.rpow = lerp(SPrm.rpow, OPrm.rpow, alt); + // ---------------------------------------------------- + CPrm.tgamma = lerp(SPrm.tgamma, OPrm.tgamma, alt); + CPrm.mphaseb = lerp(SPrm.mphaseb, OPrm.mphaseb, alt); + CPrm.tw_bld = lerp(SPrm.tw_bld, OPrm.tw_bld, alt); + // ---------------------------------------------------- + CPrm.tw_bri = lerp(SPrm.tw_bri, OPrm.tw_bri, alt); + CPrm.green = lerp(SPrm.green, OPrm.green, alt); + CPrm.tw_dst = lerp(SPrm.tw_dst, OPrm.tw_dst, alt); + // ---------------------------------------------------- + CPrm.wnrml = lerp(SPrm.wnrml, OPrm.wnrml, alt); + CPrm.wspec = lerp(SPrm.wspec, OPrm.wspec, alt); + CPrm.wtrans = lerp(SPrm.wtrans, OPrm.wtrans, alt); + CPrm.wboost = lerp(SPrm.wboost, OPrm.wboost, alt); + } + else { + alt = 1.0 - halt; + // ---------------------------------------------------- + CPrm.mierat = lerp(HPrm.mierat, OPrm.mierat, alt); + CPrm.tr3D = lerp(HPrm.tr3D, OPrm.tr3D, alt); + CPrm.aux3 = lerp(HPrm.aux3, OPrm.aux3, alt); + CPrm.mheight = lerp(HPrm.mheight, OPrm.mheight, alt); + CPrm.rheight = lerp(HPrm.rheight, OPrm.rheight, alt); + CPrm.trb = lerp(HPrm.trb, OPrm.trb, alt); + CPrm.mie = lerp(HPrm.mie, OPrm.mie, alt); + CPrm.mphase = lerp(HPrm.mphase, OPrm.mphase, alt); + CPrm.mpow = lerp(HPrm.mpow, OPrm.mpow, alt); + CPrm.rayrat = lerp(HPrm.rayrat, OPrm.rayrat, alt); + CPrm.ray = lerp(HPrm.ray, OPrm.ray, alt); + CPrm.rpow = lerp(HPrm.rpow, OPrm.rpow, alt); + // ---------------------------------------------------- + CPrm.tgamma = lerp(HPrm.tgamma, OPrm.tgamma, alt); + CPrm.mphaseb = lerp(HPrm.mphaseb, OPrm.mphaseb, alt); + CPrm.tw_bld = lerp(HPrm.tw_bld, OPrm.tw_bld, alt); + // ---------------------------------------------------- + CPrm.tw_bri = lerp(HPrm.tw_bri, OPrm.tw_bri, alt); + CPrm.green = lerp(HPrm.green, OPrm.green, alt); + CPrm.tw_dst = lerp(HPrm.tw_dst, OPrm.tw_dst, alt); + // ---------------------------------------------------- + CPrm.wnrml = lerp(HPrm.wnrml, OPrm.wnrml, alt); + CPrm.wspec = lerp(HPrm.wspec, OPrm.wspec, alt); + CPrm.wtrans = lerp(HPrm.wtrans, OPrm.wtrans, alt); + CPrm.wboost = lerp(HPrm.wboost, OPrm.wboost, alt); + } + + bool bBelow = cp.CamAlt < prm.cloudalt; + CPrm.aux2 = bBelow ? SPrm.aux2 : lerp(HPrm.aux2, OPrm.aux2, saturate(1.0 - halt)); + CPrm.hazei = bBelow ? SPrm.hazei : lerp(HPrm.hazei, OPrm.hazei, saturate(1.0 - halt)); + + CPrm.red = SPrm.red; + CPrm.blue = SPrm.blue; + CPrm.suni = SPrm.suni; + CPrm.orbalt = SPrm.orbalt; + CPrm.visalt = GetHorizonAlt(); + CPrm.acolor = SPrm.acolor; + CPrm.hcolor = SPrm.hcolor; + CPrm.zcolor = SPrm.zcolor; + + return &CPrm; +} + +// ============================================================== + +bool vPlanet::LoadAtmoConfig() +{ + char name[32]; + char path[256]; + + oapiGetObjectName(hObj, name, 32); + + auto it = Config->AtmoCfg.find(name); + if (it != Config->AtmoCfg.end()) { + sprintf_s(path, 256, "GC/%s", it->second.c_str()); + } + else { + sprintf_s(path, "GC/%s.atm.cfg", name); + Config->AtmoCfg[name] = string(name) + ".atm.cfg"; + } + + FILEHANDLE hFile = oapiOpenFile(path, FILE_IN_ZEROONFAIL, CONFIG); + if (!hFile) hFile = oapiOpenFile("GC/Mercury.atm.cfg", FILE_IN_ZEROONFAIL, CONFIG); + if (!hFile) LogErr("Failed to initialize configuration for [%s]", name); + + LogAlw("Loading Atmospheric Configuration file [%s] Handle=%s", path, _PTR(hFile)); + + if (oapiReadItem_string(hFile, (char*)"Shader", ShaderName) == false) strcpy_s(ShaderName, 32, "Auto"); + if (oapiReadItem_string(hFile, (char*)"ConfigName", AtmoConfigName) == false) strcpy_s(AtmoConfigName, 32, "Custom"); + + LoadStruct(hFile, &SPrm, 0); + LoadStruct(hFile, &OPrm, 1); + LoadStruct(hFile, &HPrm, 2); + + oapiCloseFile(hFile, FILE_IN_ZEROONFAIL); + + if (!oapiPlanetHasAtmosphere(hObj)) return false; + + return true; + +} + +// ============================================================== + +char* vPlanet::Label(const char* x) +{ + static char lbl[32]; + if (iConfig == 0) sprintf_s(lbl, 32, "Srf_%s", x); + if (iConfig == 1) sprintf_s(lbl, 32, "Low_%s", x); + if (iConfig == 2) sprintf_s(lbl, 32, "Hig_%s", x); + return lbl; +} + +// ============================================================== + +void vPlanet::SaveStruct(FILEHANDLE hFile, ScatterParams* prm, int iCnf) +{ + iConfig = iCnf; + oapiWriteItem_float(hFile, Label("RPwr"), prm->rpow); + oapiWriteItem_float(hFile, Label("MPwr"), prm->mpow); + // ----------------------------------------------------------------- + oapiWriteItem_float(hFile, Label("Expo"), prm->trb); + oapiWriteItem_float(hFile, Label("TGamma"), prm->tgamma); + oapiWriteItem_float(hFile, Label("Tr3D"), prm->tr3D); + // ----------------------------------------------------------------- + oapiWriteItem_float(hFile, Label("WNrml"), prm->wnrml); + oapiWriteItem_float(hFile, Label("WSpec"), prm->wspec); + oapiWriteItem_float(hFile, Label("WTrsn"), prm->wtrans); + oapiWriteItem_float(hFile, Label("WBoost"), prm->wboost); + // ----------------------------------------------------------------- + oapiWriteItem_float(hFile, Label("TWDst"), prm->tw_dst); + oapiWriteItem_float(hFile, Label("Green"), prm->green); + oapiWriteItem_float(hFile, Label("TWBri"), prm->tw_bri); + // ----------------------------------------------------------------- + oapiWriteItem_float(hFile, Label("RayO"), prm->ray); + oapiWriteItem_float(hFile, Label("RayI"), prm->rayrat); + oapiWriteItem_float(hFile, Label("TWBld"), prm->tw_bld); + oapiWriteItem_float(hFile, Label("RayH"), prm->rheight); + // ----------------------------------------------------------------- + oapiWriteItem_float(hFile, Label("MieO"), prm->mie); + oapiWriteItem_float(hFile, Label("MieP"), prm->mphase); + oapiWriteItem_float(hFile, Label("MieI"), prm->mierat); + oapiWriteItem_float(hFile, Label("MieH"), prm->mheight); + // ----------------------------------------------------------------- + oapiWriteItem_float(hFile, Label("Aux2"), prm->aux2); + oapiWriteItem_float(hFile, Label("Aux3"), prm->aux3); + oapiWriteItem_float(hFile, Label("Aux4"), prm->mphaseb); + oapiWriteItem_float(hFile, Label("Aux5"), prm->hazei); +} + +// ============================================================== + +void vPlanet::LoadStruct(FILEHANDLE hFile, ScatterParams* prm, int iCnf) +{ + VECTOR3 v3; + iConfig = iCnf; + oapiReadItem_float(hFile, (char*)"OrbitAlt", prm->orbalt); + oapiReadItem_float(hFile, (char*)"AtmoVisualAlt", prm->visalt); + oapiReadItem_float(hFile, (char*)"Red", prm->red); + oapiReadItem_float(hFile, (char*)"Blue", prm->blue); + oapiReadItem_float(hFile, (char*)"SunI", prm->suni); + oapiReadItem_vec(hFile, (char*)"zcolor", v3); prm->zcolor = v3; + oapiReadItem_vec(hFile, (char*)"hcolor", v3); prm->hcolor = v3; + oapiReadItem_vec(hFile, (char*)"acolor", v3); prm->acolor = v3; + + + // ----------------------------------------------------------------- + + oapiReadItem_float(hFile, Label("RPwr"), prm->rpow); + oapiReadItem_float(hFile, Label("MPwr"), prm->mpow); + // ----------------------------------------------------------------- + oapiReadItem_float(hFile, Label("Expo"), prm->trb); + oapiReadItem_float(hFile, Label("TGamma"), prm->tgamma); + if (!oapiReadItem_float(hFile, Label("Tr3D"), prm->tr3D)) prm->tr3D = 1.0; + // ----------------------------------------------------------------- + if (!oapiReadItem_float(hFile, Label("WNrml"), prm->wnrml)) prm->wnrml = 1.0; + if (!oapiReadItem_float(hFile, Label("WSpec"), prm->wspec)) prm->wspec = 0.8; + if (!oapiReadItem_float(hFile, Label("WTrsn"), prm->wtrans)) prm->wtrans = 0.1; + if (!oapiReadItem_float(hFile, Label("WBoost"), prm->wboost)) prm->wboost = 0.0; + // ----------------------------------------------------------------- + oapiReadItem_float(hFile, Label("TWDst"), prm->tw_dst); + oapiReadItem_float(hFile, Label("Green"), prm->green); + oapiReadItem_float(hFile, Label("TWBri"), prm->tw_bri); + // ----------------------------------------------------------------- + oapiReadItem_float(hFile, Label("RayO"), prm->ray); + oapiReadItem_float(hFile, Label("RayI"), prm->rayrat); + oapiReadItem_float(hFile, Label("TWBld"), prm->tw_bld); + oapiReadItem_float(hFile, Label("RayH"), prm->rheight); + // ----------------------------------------------------------------- + oapiReadItem_float(hFile, Label("MieO"), prm->mie); + oapiReadItem_float(hFile, Label("MieP"), prm->mphase); + oapiReadItem_float(hFile, Label("MieI"), prm->mierat); + oapiReadItem_float(hFile, Label("MieH"), prm->mheight); + // ----------------------------------------------------------------- + oapiReadItem_float(hFile, Label("Aux2"), prm->aux2); + oapiReadItem_float(hFile, Label("Aux3"), prm->aux3); + oapiReadItem_float(hFile, Label("Aux4"), prm->mphaseb); + oapiReadItem_float(hFile, Label("Aux5"), prm->hazei); +} + +// ============================================================== + +void vPlanet::SaveAtmoConfig() +{ + char name[64]; + char path[256]; + + oapiGetObjectName(hObj, name, 64); + + auto it = Config->AtmoCfg.find(name); + if (it != Config->AtmoCfg.end()) { + sprintf_s(path, 256, "GC/%s", it->second.c_str()); + } + else { + sprintf_s(path, "GC/%s.atm.cfg", name); + Config->AtmoCfg[name] = string(name) + ".atm.cfg"; + } + + FILEHANDLE hFile = oapiOpenFile(path, FILE_OUT, CONFIG); + + oapiWriteItem_string(hFile, (char*)";", (char*)"Shader(s) = [Earth, Mars, Moon, Giant, Auto]"); + oapiWriteItem_string(hFile, (char*)"Shader", ShaderName); + oapiWriteItem_string(hFile, (char*)"Planet", name); + oapiWriteItem_string(hFile, (char*)"ConfigName", AtmoConfigName); + oapiWriteItem_float(hFile, (char*)"OrbitAlt", SPrm.orbalt); + oapiWriteItem_float(hFile, (char*)"AtmoVisualAlt", SPrm.visalt); + oapiWriteItem_float(hFile, (char*)"Red", SPrm.red); + oapiWriteItem_float(hFile, (char*)"Blue", SPrm.blue); + oapiWriteItem_float(hFile, (char*)"SunI", SPrm.suni); + oapiWriteItem_vec(hFile, (char*)"zcolor", SPrm.zcolor._V()); + oapiWriteItem_vec(hFile, (char*)"hcolor", SPrm.hcolor._V()); + oapiWriteItem_vec(hFile, (char*)"acolor", SPrm.acolor._V()); + + SaveStruct(hFile, &SPrm, 0); + SaveStruct(hFile, &OPrm, 1); + SaveStruct(hFile, &HPrm, 2); + + oapiCloseFile(hFile, FILE_OUT); +} + + +// =========================================================================================== +// +vPlanet::SHDPrm vPlanet::ComputeShadow(FVECTOR3 vRay) +{ + // Compute Planet's shadow entry and exit points + vPlanet::SHDPrm sp; + + // Camera radius in "shadow" frame. + double A = dotp(TestPrm.Up, TestPrm.toCam * TestPrm.CamRad); + sp.cr = abs(A); + + // Projection of viewing ray on 'shadow' axes + double u = dotp(vRay, TestPrm.Up); + double t = dotp(vRay, TestPrm.ZeroAz); + double z = dotp(vRay, TestPrm.toSun); + + // Cosine 'a' + double a = u / sqrt(u * u + t * t); + + double k2 = A * A * a * a; + double h2 = A * A - k2; + double w2 = cp.PlanetRad2 - h2; + double w = sqrt(w2); + double k = sqrt(k2) * sign(a); + double v2 = 0; + double m = TestPrm.CamRad2 - cp.PlanetRad2; + + sp.w2 = w2; + sp.se = k - w; + sp.sx = sp.se + 2.0f * w; + + // Project distances 'es' and 'xs' back to 3D space + double f = 1.0f / sqrt(max(2.5e-5, 1.0 - z * z)); + sp.se *= f; + sp.sx *= f; + + // Compute atmosphere entry and exit points + // + a = -dotp(TestPrm.toCam, vRay); + k2 = TestPrm.CamRad2 * a * a; + h2 = TestPrm.CamRad2 - k2; + v2 = cp.AtmoRad2 - h2; + w = sqrt(v2); + k = sqrt(k2) * sign(a); + + + sp.hd = m > 0.0 ? sqrt(m) : 0.0; + sp.ae = (k - w); + sp.ax = sp.ae + 2.0f * w; + sp.ca = TestPrm.CamRad * a; + + // If the ray doesn't intersect atmosphere then set both distances to zero + if (v2 < 0) sp.ae = sp.ax = invalid_val; + + // If the ray doesn't intersect shadow then set both distances to atmo exit + if (w2 < 0) { + sp.se = invalid_val; + sp.sx = invalid_val; + } + else { + FVECTOR3 vEn = TestPrm.CamPos + vRay * sp.se; + FVECTOR3 vEx = TestPrm.CamPos + vRay * sp.sx; + if (dotp(vEn, TestPrm.toSun) > 0) sp.se = invalid_val; + if (dotp(vEx, TestPrm.toSun) > 0) sp.sx = invalid_val; + } + + return sp; +} + + +// =========================================================================================== +// +void vPlanet::TestComputations(Sketchpad* pSkp) +{ + static int status = 0; + float size = 0.02f; + VECTOR3 campos, rpos; + VESSEL* pV = oapiGetFocusInterface(); + OBJHANDLE hV = pV->GetHandle(); + OBJHANDLE hR = pV->GetGravityRef(); + if (hR != hObj) return; + + oapiGetGlobalPos(hR, &rpos); + oapiCameraGlobalPos(&campos); + campos -= rpos; + + FVECTOR3 vCam(campos); + + static FVECTOR3 vRef = 0; + static FVECTOR3 vPos = 0; + static FVECTOR3 vRay = 0; + static float beta = 0.0f; + + if (length(GetScene()->vPickRay) > 0.8f) { + vRef = campos; + beta = dotp(unit(vRef), GetScene()->vPickRay); + TestPrm.CamPos = cp.CamPos; + TestPrm.toSun = cp.toSun; + TestPrm.toCam = unit(vRef); + TestPrm.CamRad = length(vRef); + TestPrm.CamRad2 = TestPrm.CamRad * TestPrm.CamRad; + TestPrm.ZeroAz = unit(crossp(TestPrm.toCam, TestPrm.toSun)); + TestPrm.SunAz = unit(crossp(TestPrm.toCam, TestPrm.ZeroAz)); + TestPrm.Up = unit(crossp(TestPrm.ZeroAz, TestPrm.toSun)); + TestPrm.CosAlpha = min(1.0f, cp.PlanetRad / TestPrm.CamRad); + TestPrm.SinAlpha = sqrt(1.0f - TestPrm.CosAlpha * TestPrm.CosAlpha); + } + + + // Trace picking ray -------------------------------------------- + // + float Ref2 = dotp(vRef, vRef); + float Ref = sqrt(Ref2); + float ds = Ref * beta; + float he2 = Ref2 - ds * ds; + + if (length(GetScene()->vPickRay) > 0.8f) + { + if (he2 < cp.PlanetRad2 && beta < 0) { // Surface contact + float s = sqrt(cp.PlanetRad2 - he2); + vPos = vRef + GetScene()->vPickRay * (-ds - s); + status = 1; + } + else { + if (!CameraInAtmosphere()) { // Horizon ring contact + float Alpha = acos(TestPrm.CosAlpha); + float Beta = acos(-beta); + float Gamma = PI - Alpha - Beta; + float re = Ref * sin(Beta) / sin(Gamma); + float di = sqrt(re * re + Ref2 - 2.0f * re * Ref * TestPrm.CosAlpha); + vPos = vRef + GetScene()->vPickRay * di; + status = 2; + } + else { // Sky Dome contact + float ew = sqrt(cp.AtmoRad2 - he2) - ds; + vPos = vRef + GetScene()->vPickRay * ew; + status = 3; + } + } + GetScene()->vPickRay = 0; + vRay = unit(vPos - vRef); // From vRef to vPos + } + + float cd = length(vRef - vPos); + + SHDPrm sp = ComputeShadow(vRay); + + FVECTOR4 rd = ComputeCameraView(vPos); + + vkDebugLog("Optical Depth=%f RGB(%f, %f, %f)", rd.a, rd.r, rd.g, rd.b); + + float sl = dotp(cp.toSun, TestPrm.toCam); + float sa = dotp(vRay, TestPrm.toCam); + float rl = RayLength(-sa, TestPrm.CamRad); + float rf = sqrt(cp.AtmoRad2 - cp.PlanetRad2); + float ph = dotp(vRay, cp.toSun); + + FVECTOR3 cl = SunLightColor(sl, cp.CamAlt); + FVECTOR4 ral, mie; + IntegrateSegment(vPos, vRay, rl, &ral, &mie); + + FVECTOR3 is = (ral.rgb + mie.rgb);// *MiePhase(-ph)); + + vkDebugLog("Sunlight RGB(%f, %f, %f)", cl.x, cl.y, cl.z); + vkDebugLog("InScatter RGB(%f, %f, %f)", is.x, is.y, is.z); + vkDebugLog("RayLength = %f vs %f, sa = %f, sl = %f", rl, rf, sa, sl); + vkDebugLog("AtmoAlt = %f(km)", cp.AtmoAlt/1000.0f); + + double u = dotp(vPos, TestPrm.Up); + double t = dotp(vPos, TestPrm.ZeroAz); + bool bTgt = ((u * u + t * t) < cp.PlanetRad2 && dotp(vPos, TestPrm.toSun) < 0); + bool bSrc = (sp.cr < cp.PlanetRad&& dotp(vRef, TestPrm.toSun) < 0); + + float s0 = 0, s1 = 0, e0 = 0, e1 = 0, mp = 0, lf = 0; + + if (status == 1) { + s0 = sp.ae > 0 ? sp.ae : 0; + e0 = sp.se > 0 ? min(cd, sp.se) : cd; + } + else { + + if (bSrc) { // Camera in Shadow + s0 = max(sp.sx, sp.ae); + + float lf = max(0.0f, sp.ca) / max(1.0f, abs(sp.hd)); // Lerp Factor + float mp = lerp((sp.ax + s0) * 0.5f, sp.hd, saturate(lf)); + + e0 = mp; + s1 = max(sp.sx, mp); + e1 = sp.ax; + } + else { // Camera is Lit + s0 = max(0.0f, sp.ae); + + float lf = max(0.0f, sp.ca) / max(1.0f, abs(sp.hd)); // Lerp Factor + float mp = lerp((sp.ax + s0) * 0.5f, sp.hd, saturate(lf)); + + bool bA = (sp.se > sp.ax || sp.se < 0); + + e0 = bA ? mp : sp.se; + s1 = bA ? mp : max(sp.sx, sp.ae); + e1 = sp.ax; + } + } + + + int sz = 250; + + // Origin Point + pSkp->QuickPen(0xFF00FF00); + pSkp->QuickBrush(0xFF00FF00); + pSkp->SetWorldBillboard(vRef - vCam, size); + pSkp->Ellipse(-sz, -sz, sz, sz); + + // Test Point 'contact point' + sz = 170; + pSkp->QuickPen(0xFF0000FF); + pSkp->QuickBrush(0xFF0000FF); + pSkp->SetWorldBillboard(vRef - vCam + vRay * cd, size); + pSkp->Ellipse(-sz, -sz, sz, sz); + + sz = 250; + // Shadow Crossing + if (sp.se != invalid_val) { + pSkp->QuickPen(0xFFDD00DD); + pSkp->QuickBrush(0xFFDD00DD); + pSkp->SetWorldBillboard(vRef - vCam + vRay * sp.se, size); + pSkp->Ellipse(-sz, -sz, sz, sz); + } + if (sp.sx != invalid_val) { + pSkp->QuickPen(0xFFFF88FF); + pSkp->QuickBrush(0xFFFF88FF); + pSkp->SetWorldBillboard(vRef - vCam + vRay * sp.sx, size); + pSkp->Ellipse(-sz, -sz, sz, sz); + } + sz = 170; + if (sp.ae != invalid_val) { + // Atmosphere entry points + pSkp->QuickPen(0xFF008080); + pSkp->QuickBrush(0xFF008080); + pSkp->SetWorldBillboard(vRef - vCam + vRay * sp.ae, size); + pSkp->Ellipse(-sz, -sz, sz, sz); + } + if (sp.ax != invalid_val) { + pSkp->QuickPen(0xFF00F0F0); + pSkp->QuickBrush(0xFF00F0F0); + pSkp->SetWorldBillboard(vRef - vCam + vRay * sp.ax, size); + pSkp->Ellipse(-sz, -sz, sz, sz); + } + sz = 120; + if (sp.hd != invalid_val) { + pSkp->QuickPen(0xFFFFFFFF); + pSkp->QuickBrush(0xFFFFFFFF); + pSkp->SetWorldBillboard(vRef - vCam + vRay * mp, size); + pSkp->Ellipse(-sz, -sz, sz, sz); + } + + + if (bSrc) vkDebugLog("Camera in Shadow"); + if (bTgt) vkDebugLog("Target in Shadow"); + + + if (status == 1) vkDebugLog("SURFACE"); + if (status == 2) vkDebugLog("HORIZON"); + if (status == 3) vkDebugLog("SKYDOME"); + + vkDebugLog("Shadow First=%f, Second=%f", sp.se, sp.sx); + vkDebugLog("Atmosp First=%f, Second=%f", sp.ae, sp.ax); + vkDebugLog("Hd=%f, Ca=%f", sp.hd, sp.ca); + vkDebugLog("CameraInSpace=%f", cp.CamSpace); + + FMATRIX4 mI; oapiMatrixIdentity(&mI); + VECTOR3 V0, V1; + IVECTOR2 pt0, pt1; + + pSkp->SetViewMode(Sketchpad::ORTHO); + pSkp->SetWorldTransform(); + + FVECTOR3 vR = vRef - vCam; + + vkDebugLog("s0=%f, e0=%f, s1=%f, e1=%f, mp=%f, lf=%f", s0, e0, s1, e1, mp, lf); + + if (e0 > s0) { + vkDebugLog("Primary Integral"); + V0 = (vR + vRay * s0)._V(); + V1 = V0 + _V(vRay) * double(e0 - s0); + scn->WorldToScreenSpace(V0, &pt0); + scn->WorldToScreenSpace(V1, &pt1); + pSkp->QuickPen(0xFF90FF90); + pSkp->Line(pt0.x, pt0.y, pt1.x, pt1.y); + } + + if (e1 > s1) { + vkDebugLog("Secondary Integral"); + V0 = _V(vR + vRay * s1); + V1 = V0 + _V(vRay) * double(e1 - s1); + scn->WorldToScreenSpace(V0, &pt0); + scn->WorldToScreenSpace(V1, &pt1); + pSkp->QuickPen(0xFF9090FF); + pSkp->Line(pt0.x, pt0.y, pt1.x, pt1.y); + } + pSkp->SetViewMode(Sketchpad::USER); +} + + diff --git a/OVP/VulkanClient/VStar.cpp b/OVP/VulkanClient/VStar.cpp new file mode 100644 index 000000000..8f0fb95b6 --- /dev/null +++ b/OVP/VulkanClient/VStar.cpp @@ -0,0 +1,98 @@ +// ============================================================== +// VStar.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// ============================================================== + +// ============================================================== +// class vStar (interface) +// Renders the central star as a billboard mesh. +// ============================================================== + +#include "Mesh.h" +#include "VStar.h" +#include "Surface.h" +#include "Config.h" + +SURFHANDLE vStar::deftex = 0; + +vStar::vStar(OBJHANDLE _hObj, const Scene *scene): vObject (_hObj, scene) +{ + maxdist = 0.5*scene->GetCameraFarPlane(); +} + +vStar::~vStar () +{ +} + +void vStar::GlobalInit (oapi::vkClient *gc) +{ + deftex = SURFACE(gc->clbkLoadTexture("star.dds", 0)); +} + +void vStar::GlobalExit() +{ + DELETE_SURFACE(deftex); +} + +bool vStar::Update (bool bMainScene) +{ + _TRACE; + if (!active) return false; + + vObject::Update (bMainScene); + + return true; +} + +bool vStar::Render(LPDIRECT3DDEVICE9 dev) +{ + _TRACE; + + if (Config->bGlares) return true; + + double dist_scale; + float rad_scale = float(size); + float size_hack; // make star look bigger at distance + + VECTOR3 bdir (unit(cpos)); + double hz = std::hypot (bdir.x, bdir.z); + // double phi = atan2 (bdir.z, bdir.x); + // FLOAT sphi = (FLOAT)sin(phi), cphi = (FLOAT)cos(phi); + // FLOAT tx = (FLOAT)cpos.x, ty = (FLOAT)cpos.y, tz = (FLOAT)cpos.z; + mWorld.m11 = (FLOAT)bdir.x; //cphi; + mWorld.m12 = (FLOAT)bdir.y; //0; + mWorld.m13 = (FLOAT)bdir.z; //sphi; + mWorld.m31 = -(FLOAT)(bdir.z/hz); //-sphi; + mWorld.m32 = 0; + mWorld.m33 = (FLOAT)(bdir.x/hz); //cphi; + mWorld.m21 = -(mWorld.m12*mWorld.m33 - mWorld.m32*mWorld.m13); //0; + mWorld.m22 = -(mWorld.m13*mWorld.m31 - mWorld.m33*mWorld.m11); //1; + mWorld.m23 = -(mWorld.m11*mWorld.m32 - mWorld.m31*mWorld.m12); // 0; + + // artificially reduce size reduction with distance + // to make star appear larger + size_hack = float(1.0+pow(cdist,0.6)*1e-6); + + rad_scale *= max(size_hack, 7.5f); + + maxdist = 0.1*scn->GetCameraFarPlane(); + + if (cdist > maxdist) { + dist_scale = maxdist/cdist; + rad_scale *= float(dist_scale); + mWorld.m41 = float(cpos.x*dist_scale); + mWorld.m42 = float(cpos.y*dist_scale); + mWorld.m43 = float(cpos.z*dist_scale); + } + + // scale up sphere radius from 1 to planet radius + mWorld.m11 *= rad_scale; mWorld.m12 *= rad_scale; mWorld.m13 *= rad_scale; + mWorld.m21 *= rad_scale; mWorld.m22 *= rad_scale; mWorld.m23 *= rad_scale; + mWorld.m31 *= rad_scale; mWorld.m32 *= rad_scale; mWorld.m33 *= rad_scale; + + //vkEffect::RenderBillboard(&mWorld, scn->GetSunTexture(), 1.0f); + vkEffect::RenderBillboard(&mWorld, SURFACE(deftex)->GetTexture(), 1.0f); + return true; +} diff --git a/OVP/VulkanClient/VStar.h b/OVP/VulkanClient/VStar.h new file mode 100644 index 000000000..ad22293d6 --- /dev/null +++ b/OVP/VulkanClient/VStar.h @@ -0,0 +1,58 @@ +// ============================================================== +// VStar.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// ============================================================== + +#ifndef __VSTAR_H +#define __VSTAR_H + +#include "VObject.h" + +//class vkMesh; + + +// ============================================================== +// class vStar (interface) +// ============================================================== + +/** + * \brief Visual representation of the (one) central star. + * + * Renders the central star as a billboard mesh. + */ +class vStar: public vObject { +public: + /** + * \brief Constructs a new central star object for a scene + * \param _hObj object handle + * \param scene scene to which the visual is added + */ + vStar (OBJHANDLE _hObj, const Scene *scene); + + /** + * \brief Destroys the central star object + */ + ~vStar (); + + /** + * \brief Set up global parameters shared by all instances + * \param gclient client instance pointer + */ + static void GlobalInit (oapi::vkClient *gc); + + /** + * \brief Release global parameters + */ + static void GlobalExit (); + + bool Update (bool bMainScene); + bool Render (LPDIRECT3DDEVICE9 dev); + +private: + double maxdist; ///< max render distance + static SURFHANDLE deftex; ///< default texture +}; + +#endif // !__VSTAR_H diff --git a/OVP/VulkanClient/VVessel.cpp b/OVP/VulkanClient/VVessel.cpp new file mode 100644 index 000000000..963a754ff --- /dev/null +++ b/OVP/VulkanClient/VVessel.cpp @@ -0,0 +1,2453 @@ +// ============================================================== +// VVessel.cpp +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2010-2019 Jarmo Nikkanen (vkClient modification) +// ============================================================== + +#include +#include +#include "VVessel.h" +#include "VPlanet.h" +#include "MeshMgr.h" +#include "AABBUtil.h" +#include "Surface.h" +#include "Catalog.h" +#include "Config.h" +#include "OapiExtension.h" +#include "DebugControls.h" +#include "Util.h" +#include "MaterialMgr.h" +#include "IProcess.h" + +using namespace oapi; + +// ============================================================== +// Local prototypes + +void TransformPoint (VECTOR3 &p, const FMATRIX4 &T); +void TransformDirection (VECTOR3 &a, const FMATRIX4 &T, bool normalise); +const char *value_string (double val); + +// ============================================================== +// class vVessel (implementation) +// +// A vVessel is the visual representation of a vessel object. +// ============================================================== + +void LogComp(ANIMATIONCOMP *AC, int ident) +{ + char id[64]; + strcpy_s(id, 64, ""); + for (int i = 0; i < ident; i++) strcat_s(id, 64, " "); + oapiWriteLogV("%s COMP[%s] has %u children, Parent = %s", id, _PTR(AC), AC->nchildren, _PTR(AC->parent)); + for (UINT i = 0; i < AC->nchildren; i++) LogComp(AC->children[i], ident + 2); +} + + +vVessel::vVessel(OBJHANDLE _hObj, const Scene *scene): vObject (_hObj, scene) +{ + _TRACE; + + //@todo throw @ vObject or visObject??? + if (_hObj==NULL) throw std::invalid_argument("_hObj"); + + vessel = oapiGetVesselInterface(_hObj); + nmesh = 0; + sunLight = *scene->GetSun(); + tCheckLight = oapiGetSimTime()-1.0; + vClass = 0; + pMatMgr = new MatMgr(this, scene->GetClient()); + + if (strncmp(vessel->GetClassNameA(), "XR2Ravenstar", 12) == 0) vClass = VCLASS_XR2; + if (strncmp(vessel->GetClassNameA(), "SpaceShuttleUltra", 17) == 0) vClass = VCLASS_ULTRA; + if (strncmp(vessel->GetClassNameA(), "SSU_CentaurGPrime", 17) == 0) vClass = VCLASS_SSU_CENTAUR; + + bBSRecompute = true; + ExhaustLength = 0.0f; + LoadMeshes(); + + // Initialize static animations + // + UINT na = vessel->GetAnimPtr(&anim); + + for (UINT i = 0; i < na; i++) { + currentstate[i] = anim[i].defstate; + if (Config->bAbsAnims) for (UINT k = 0; k < anim[i].ncomp; ++k) StoreDefaultState(anim[i].comp[k]); + } + + // Initialize default eCams; + // + ecDefExt.flags = ENVCAM_OMIT_ATTC; + ecDefExt.type = EnvCamType::Exterior; + + /* + oapiWriteLogV("%s", vessel->GetClassNameA()); + oapiWriteLogV("nanim = %u", na); + for (UINT i = 0; i < na; i++) { + oapiWriteLogV("ANIM[%u] = %u comp(s)", i, anim[i].ncomp); + for (UINT k = 0; k < anim[i].ncomp; ++k) LogComp(anim[i].comp[k], 2); + }*/ + + UpdateAnimations(); + + for (int i = 0; i < 16; i++) BakedLightsControl[i] = FVECTOR3(0.0f, 0.0f, 0.0f); + VCAmbient = FVECTOR3(0.5f, 0.5f, 0.5f); + bMustRebake = true; +} + + +// ============================================================================================ +// +vVessel::~vVessel () +{ + SAFE_DELETE(pMatMgr); + LogAlw("Deleting Vessel Visual %s ...", _PTR(this)); + DisposeAnimations(); + DisposeMeshes(); + + for (auto& x: InteriorCams) SAFE_DELETE(x); + + LogAlw("Vessel visual deleted succesfully"); +} + + +// ============================================================================================ +// +void vVessel::GlobalInit(vkClient *gc) +{ + _TRACE; + auto pDevice = gc->GetDevice(); + defreentrytex = SURFACE(gc->clbkLoadTexture("Reentry.dds", 0)); + defexhausttex = SURFACE(gc->clbkLoadTexture("Exhaust.dds", 0)); + pRenderZone = new ShaderClass(pDevice, "Modules/vkShaders/Custom.hlsl", "QuadVS", "QuadPS", "RenderZones", ""); +} + + +// ============================================================================================ +// +void vVessel::GlobalExit () +{ + DELETE_SURFACE(defexhausttex); + DELETE_SURFACE(defreentrytex); + SAFE_DELETE(pRenderZone); +} + + +// ============================================================================================ +// +void vVessel::clbkEvent(DWORD evnt, DWORD_PTR _context) +{ + UINT context = (UINT)_context; + + switch (evnt) { + + case EVENT_VESSEL_INSMESH: + bBSRecompute = true; + InsertMesh(context); + break; + + case EVENT_VESSEL_DELMESH: + bBSRecompute = true; + DelMesh(context); + break; + + case EVENT_VESSEL_MESHVISMODE: + { + bBSRecompute = true; + if (context < nmesh) { + meshlist[context].vismode = vessel->GetMeshVisibilityMode(context); + } + } break; + + case EVENT_VESSEL_MESHOFS: + { + bBSRecompute = true; + DWORD idx = (DWORD)context; + if (idx < nmesh) { + VECTOR3 ofs; + vessel->GetMeshOffset (idx, ofs); + if (length(ofs)) { + if (meshlist[idx].trans==NULL) meshlist[idx].trans = new FMATRIX4; + oapiMatrixIdentity(meshlist[idx].trans); + D3DMAT_SetTranslation(meshlist[idx].trans, &ofs); + } + else { + SAFE_DELETE(meshlist[idx].trans); + } + } + } break; + + case EVENT_VESSEL_MODMESHGROUP: + ResetMesh(context); + break; + + case EVENT_VESSEL_RESETANIM: + ResetAnimations(); + break; + + case EVENT_VESSEL_CLEARANIM: + ResetAnimations(context); + break; + + case EVENT_VESSEL_DELANIM: + DelAnimation(context); + break; + + case EVENT_VESSEL_NEWANIM: + InitNewAnimation(context); + break; + } +} + + +// ============================================================================================ +// +DWORD vVessel::GetMeshCount() +{ + return nmesh; +} + + +// ============================================================================================ +// +vkMesh* vVessel::GetMesh (UINT idx) +{ + return (idx < nmesh ? meshlist[idx].mesh : NULL); +} + + +// ============================================================================================ +// +DWORD vVessel::GetMeshVisMode(UINT idx) +{ + return (idx < nmesh ? meshlist[idx].vismode : MESHVIS_ALWAYS); +} + + +// ============================================================================================ +// +bool vVessel::HasExtPass() +{ + for (DWORD i=0;iHasShadow()) return true; + return false; +} + + +// ============================================================================================ +// +void vVessel::PreInitObject() +{ + if (pMatMgr->LoadConfiguration()) { + for (DWORD i=0;iApplyConfiguration(meshlist[i].mesh); + pMatMgr->LoadCameraConfig(); + } + else LogErr("Failed to load a custom configuration for %s",vessel->GetClassNameA()); +} + + +// ============================================================================================ +// +bool vVessel::Update(bool bMainScene) +{ + _TRACE; + if (!active) return false; + vObject::Update(bMainScene); + + if (fabs(oapiGetSimTime()-tCheckLight)>0.03 || oapiGetPause()) ModLighting(); + + bBSRecompute = true; + return true; +} + + +// ============================================================================================ +// +void vVessel::LoadMeshes() +{ + _TRACE; + bBSRecompute = true; + if (nmesh) DisposeMeshes(); + + MESHHANDLE hMesh = NULL; + const vkMesh *mesh = NULL; + VECTOR3 ofs; + UINT idx; + + MeshManager *mmgr = gc->GetMeshMgr(); + + nmesh = vessel->GetMeshCount(); + meshlist = new MESHREC[nmesh+1]; + + memset(meshlist, 0, nmesh*sizeof(MESHREC)); + + LogAlw("Vessel(%s) %s has %u meshes", _PTR(vessel), vessel->GetClassNameA(), nmesh); + + for (idx=0;idxGetMeshTemplate(idx); + mesh = mmgr->GetMesh(hMesh); + + if (hMesh && mesh) { + // copy from preloaded template + meshlist[idx].mesh = new vkMesh(hMesh, *mesh); // Create new Instance from an existing mesh template + meshlist[idx].mesh->SetClass(vClass); + meshlist[idx].mesh->SetName(idx); + } + else { + // It's vital to use "CopyMeshFromTemplate" here for some reason + // No global template exists for this mesh. Loaded with oapiLoadMesh() + hMesh = vessel->CopyMeshFromTemplate(idx); + if (hMesh) { + // load on the fly and discard after copying + meshlist[idx].mesh = new vkMesh(hMesh); // Create new DX9 Mesh + meshlist[idx].mesh->SetClass(vClass); + meshlist[idx].mesh->SetName(idx); + oapiDeleteMesh(hMesh); + } + } + + if (meshlist[idx].mesh) { + meshlist[idx].vismode = vessel->GetMeshVisibilityMode(idx); + vessel->GetMeshOffset(idx, ofs); + LogAlw("Mesh(%s) Offset = (%g, %g, %g)", _PTR(hMesh), ofs.x, ofs.y, ofs.z); + if (length(ofs)) { + meshlist[idx].trans = new FMATRIX4; + oapiMatrixIdentity(meshlist[idx].trans); + D3DMAT_SetTranslation(meshlist[idx].trans, &ofs); + // currently only mesh translations are supported + } + } + else { + LogWrn("Vessel %s has a NULL mesh in index %u",vessel->GetClassNameA(),idx); + } + } + + UpdateBoundingBox(); + + LogOk("Loaded %u meshed for %s",nmesh,vessel->GetClassNameA()); +} + + +// ============================================================================================ +// +void vVessel::InsertMesh(UINT idx) +{ + _TRACE; + + VECTOR3 ofs=_V(0,0,0); + + UINT i; + FMATRIX4* pT = NULL; + + if (idx >= nmesh) { // append a new entry to the list + MESHREC *tmp = new MESHREC[idx+1]; + if (nmesh) { + memcpy (tmp, meshlist, nmesh*sizeof(MESHREC)); + delete []meshlist; + } + meshlist = tmp; + for (i = nmesh; i <= idx; i++) { // zero any intervening entries + meshlist[i].mesh = 0; + meshlist[i].trans = 0; + meshlist[i].vismode = 0; + } + nmesh = idx+1; + } + else if (meshlist[idx].mesh) { // replace existing entry + SAFE_DELETE(meshlist[idx].mesh); + SAFE_DELETE(meshlist[idx].trans); + } + + // now add the new mesh + MeshManager *mmgr = gc->GetMeshMgr(); + MESHHANDLE hMesh = vessel->GetMeshTemplate(idx); + const vkMesh *mesh = mmgr->GetMesh(hMesh); + + + if (hMesh && mesh) { + meshlist[idx].mesh = new vkMesh(hMesh, *mesh); // Create new Instance from an existing mesh template + meshlist[idx].mesh->SetClass(vClass); + meshlist[idx].mesh->SetName(idx); + } else if (hMesh = vessel->CopyMeshFromTemplate (idx)) { + meshlist[idx].mesh = new vkMesh(hMesh); // Create new DX9 Mesh + meshlist[idx].mesh->SetClass(vClass); + meshlist[idx].mesh->SetName(idx); + oapiDeleteMesh (hMesh); + } else { + meshlist[idx].mesh = 0; + } + + if (meshlist[idx].mesh) { + pMatMgr->ApplyConfiguration(meshlist[idx].mesh); + meshlist[idx].vismode = vessel->GetMeshVisibilityMode (idx); + vessel->GetMeshOffset (idx, ofs); + if (length(ofs)) { + meshlist[idx].trans = new FMATRIX4; + oapiMatrixIdentity (meshlist[idx].trans); + D3DMAT_SetTranslation (meshlist[idx].trans, &ofs); + // currently only mesh translations are supported + } else { + meshlist[idx].trans = 0; + } + } + + UpdateAnimations(idx); +} + + +// ============================================================================================ +// In response to VESSEL::MeshModified() +// +void vVessel::ResetMesh(UINT idx) +{ + VECTOR3 ofs = _V(0, 0, 0); + + if ((idx < nmesh) && meshlist[idx].mesh) { + + MESHHANDLE hMesh = vessel->GetMeshTemplate(idx); + + if (hMesh) { + meshlist[idx].mesh->ReLoadMeshFromHandle(hMesh); + meshlist[idx].mesh->ResetTransformations(); + } + else { + hMesh = vessel->CopyMeshFromTemplate(idx); + if (hMesh) { + meshlist[idx].mesh->ReLoadMeshFromHandle(hMesh); + meshlist[idx].mesh->ResetTransformations(); + oapiDeleteMesh(hMesh); + } + } + + pMatMgr->ApplyConfiguration(meshlist[idx].mesh); + + meshlist[idx].vismode = vessel->GetMeshVisibilityMode(idx); + vessel->GetMeshOffset(idx, ofs); + + if (length(ofs)) { + if (!meshlist[idx].trans) meshlist[idx].trans = new FMATRIX4; + oapiMatrixIdentity(meshlist[idx].trans); + D3DMAT_SetTranslation(meshlist[idx].trans, &ofs); + } + else { + SAFE_DELETE(meshlist[idx].trans); + } + } +} + + +// ============================================================================================ +// +void vVessel::DisposeMeshes() +{ + if (nmesh && meshlist) { + for (UINT i = 0; i < nmesh; i++) { + SAFE_DELETE(meshlist[i].mesh); + SAFE_DELETE(meshlist[i].trans); + } + } + if (meshlist) delete[] meshlist; + meshlist = 0; + nmesh = 0; +} + + +// ============================================================================================ +// +void vVessel::DelMesh(UINT idx) +{ + if (idx==0xFFFFFFFF) { + DisposeMeshes(); + return; + } + + if (idx >= nmesh) return; + if (!meshlist[idx].mesh) return; + + SAFE_DELETE(meshlist[idx].mesh); + SAFE_DELETE(meshlist[idx].trans); +} + + +// ============================================================================================ +// +void vVessel::InitNewAnimation (UINT idx) +{ + //vessel->GetAnimPtr(&anim) returns invalid data here. New idx is not yet included in anim[] +} + + +// ============================================================================================ +// +void vVessel::GrowAnimstateBuffer (UINT newSize) +{ + // Obsolete +} + + +// ============================================================================================ +// +void vVessel::DisposeAnimations () +{ + defstate.clear(); + applyanim.clear(); + currentstate.clear(); +} + + +// ============================================================================================ +// +void vVessel::ResetAnimations (UINT reset/*=1*/) +{ + bBSRecompute = true; + //Nothing to do here +} + + +// ============================================================================================ +// +void vVessel::DelAnimation (UINT idx) +{ + // Orbiter never reduces the animation buffer size. (i.e. anim[]) + // VESSEL::GetAnimPtr() returns highest existing animation ID + 1, not the actual animation count + vessel->GetAnimPtr(&anim); + currentstate.erase(idx); + if (Config->bAbsAnims) for (UINT k = 0; k < anim[idx].ncomp; ++k) DeleteDefaultState(anim[idx].comp[k]); +} + + +// ============================================================================================ +// +void vVessel::UpdateAnimations (int mshidx) +{ + + UINT na = vessel->GetAnimPtr(&anim); + + + // Check that all animations exists in local databases, if not then add it. + // New animations 'should' be in their default states (at)in this point. + // + for (UINT i = 0; i < na; ++i) { + + if (currentstate.count(i) == 0) currentstate[i] = anim[i].defstate; + + if (Config->bAbsAnims) { + for (UINT k = 0; k < anim[i].ncomp; ++k) { + ANIMATIONCOMP *AC = anim[i].comp[k]; + if (defstate.count(AC->trans) == 0) StoreDefaultState(AC); + } + } + } + + + if (Config->bAbsAnims) + { + + // -------------------------------------------- + // Apply Absolute Animations + // -------------------------------------------- + + // Restore default transformations + for (UINT i = 0; i < nmesh; ++i) if (meshlist[i].mesh) meshlist[i].mesh->ResetTransformations(); + + // Restore default animation states + for (UINT i = 0; i < na; ++i) { + currentstate[i] = anim[i].defstate; + for (UINT k = 0; k < anim[i].ncomp; ++k) { + if (anim[i].state != anim[i].defstate) + RestoreDefaultState(anim[i].comp[k]); + } + } + + for (UINT i = 0; i < na; ++i) { + if (!anim[i].ncomp) continue; + if (applyanim.count(i)) continue; + if (anim[i].state != anim[i].defstate) applyanim.insert(applyanim.end(), i); + } + + // Update animations --------------------------------------------- + for (auto i : applyanim) Animate(i, mshidx); + } + else + { + + // -------------------------------------------- + // Apply Incremental Animations + // -------------------------------------------- + + for (UINT i = 0; i < na; ++i) { + if (anim[i].state != currentstate[i]) { + Animate(i, mshidx); + currentstate[i] = anim[i].state; + } + } + } +} + +// ============================================================================================ +// +bool vVessel::GetSMapRenderData(SMI type, int idx, SMapInput *sm) +{ + FVECTOR3 cpos; float rad; + + if (type == SMI::Visual) { + *sm = { GetBoundingSpherePosDX(), FVECTOR3(-sundir), GetBoundingSphereRadius() }; + return true; + } + if (type == SMI::VC) { + bool bRet = GetVCPos(&cpos, NULL, &rad); + *sm = { cpos, FVECTOR3(-sundir), rad }; + return bRet; + } + if (type == SMI::Mesh) { + bool bRet = GetMeshPosition(idx, &cpos, nullptr, &rad); + *sm = { cpos, FVECTOR3(-sundir), rad }; + return bRet; + } + return false; +} + +// ============================================================================================ +// +bool vVessel::IsInsideShadows(const SMapInput* shd) +{ + FVECTOR3 bc = oapiTransformCoord(&_F(BBox.bs), &mWorld); + FVECTOR3 fbc = FVECTOR3(bc) - shd->pos; + float x = dotp(fbc, shd->ld); + if (sqrt(dotp(fbc, fbc) - x*x) < ((shd->rad * 1.01f) - BBox.bs.w)) return true; + return false; +} + + +// ============================================================================================ +// +bool vVessel::IntersectShadowVolume(const SMapInput* shd) +{ + FVECTOR3 bc = oapiTransformCoord(&_F(BBox.bs), &mWorld); + FVECTOR3 fbc = FVECTOR3(bc) - shd->pos; + float x = dotp(fbc, shd->ld); + if (sqrt(dotp(fbc, fbc) - x*x) > (shd->rad + BBox.bs.w)) return false; + return true; +} + + +// ============================================================================================ +// +bool vVessel::IntersectShadowTarget(const SMapInput* shd) +{ + FVECTOR3 bc = oapiTransformCoord(&_F(BBox.bs), &mWorld); + FVECTOR3 fbc = FVECTOR3(bc) - shd->pos; + if (length(fbc) < (shd->rad + BBox.bs.w)) return true; + return false; +} + + +// ============================================================================================ +// +void vVessel::GetMinMaxLightDist(const SMapInput* shd, float *mind, float *maxd) +{ + FVECTOR3 bc = oapiTransformCoord(&_F(BBox.bs), &mWorld); + FVECTOR3 fbc = FVECTOR3(bc) - shd->pos; + float x = dotp(fbc, shd->ld); + *mind = min(*mind, x - BBox.bs.w); + *maxd = max(*maxd, x + BBox.bs.w); +} + + +// ============================================================================================ +// +bool vVessel::GetVCPos(FVECTOR3* cpos, FVECTOR3* lpos, float* rad) +{ + for (int i = 0; i < nmesh; i++) + { + if (!meshlist[i].mesh) continue; + if (meshlist[i].mesh->MeshFlags & MESHFLAG_VC) + { + return GetMeshPosition(i, cpos, lpos, rad); + } + } + return false; +} + + +// ============================================================================================ +// +bool vVessel::GetMeshPosition(int idx, FVECTOR3* cpos, FVECTOR3* lpos, float* rad) +{ + if (!meshlist[idx].mesh) return false; + + FVECTOR3 pos = meshlist[idx].mesh->GetBoundingSpherePos(); + if (rad) *rad = meshlist[idx].mesh->GetBoundingSphereRadius(); + + if (meshlist[idx].trans) + { + pos = oapiTransformCoord(&pos, meshlist[idx].trans); + if (lpos) *lpos = pos; + if (cpos) pos = oapiTransformCoord(&pos, &mWorld); + } + else { + if (lpos) *lpos = pos; + if (cpos) pos = oapiTransformCoord(&pos, &mWorld); + } + if (cpos) *cpos = pos; + return true; +} + + +// ============================================================================================ +// +void vVessel::BakeLights(ImageProcessing *pBaker) +{ + OBJHANDLE hGRef = vessel->GetGravityRef(); + + MATRIX3 grot; VECTOR3 rpos; LVLH lvlh; + oapiGetRotationMatrix(hGRef, &grot); + vessel->GetRelativePos(hGRef, rpos); + + FMATRIX4 mW(mWorld); + FVECTOR3 polaraxis = mul(grot, _V(0, 1, 0)); + lvlh.Up = unit(rpos); + lvlh.East = unit(crossp(polaraxis, lvlh.Up)); + lvlh.North = unit(crossp(lvlh.Up, lvlh.East)); + lvlh.Up = mul(mW, FVECTOR4(lvlh.Up, 0)).xyz; + lvlh.East = mul(mW, FVECTOR4(lvlh.East, 0)).xyz; + lvlh.North = mul(mW, FVECTOR4(lvlh.North, 0)).xyz; + + auto maps = GetExteriorEnvMap(); + + if (maps) { + for (int i = 0; i < nmesh; i++) + { + if (!meshlist[i].mesh) continue; + if (meshlist[i].vismode & MESHVIS_VC) // TODO: Should check Shader type + { + auto vSun = mul(mW, FVECTOR4(sundir, 0)); + if (bMustRebake) meshlist[i].mesh->BakeLights(pBaker, BakedLightsControl); + if (Config->ExpVCLight == 0) meshlist[i].mesh->BakeAO(pBaker, vSun.xyz, lvlh, maps->pIrrad); + } + } + } + + bMustRebake = false; +} + + +// ============================================================================================ +// +void vVessel::ErrorOnce(Errors e) +{ + static bool bDisp[Errors::ITEMS] = { true }; + static DWORD dwDisp[Errors::ITEMS] = { 0 }; + + if (bDisp[e]) { + switch (e) { + case Errors::NoVC: + oapiWriteLogV("[ERROR:vk] Cannot identify Virtual Cockpit mesh [%s]", vessel->GetClassNameA()); + break; + default: + oapiWriteLogV("[ERROR:vk] Unknows error code [%s]", vessel->GetName()); + } + dwDisp[e]++; // Count the errors; + bDisp[e] = false; // Disable error from printing again + } +} + +// ============================================================================================ +// +bool vVessel::Render(LPDIRECT3DDEVICE9 dev) +{ + _TRACE; + if (!active) return false; + UpdateBoundingBox(); + bool bRet = Render(dev, false, nullptr); + if (oapiCameraInternal()==false) RenderReentry(dev); + return bRet; +} + +// ============================================================================================ +// +bool vVessel::Render(LPDIRECT3DDEVICE9 dev, bool internalpass, const SHADOWMAP* shd) +{ + bool bCockpit = (oapiCameraInternal() && (hObj == oapiGetFocusObject())); + bool bVC = (bCockpit && (oapiCockpitMode() == COCKPIT_VIRTUAL)); + + DWORD flags = 0; + flags |= bCockpit ? Render::D2 : 0; + flags |= bVC ? Render::VC : 0; + flags |= internalpass ? Render::IP : 0; + + return Render(dev, shd, flags); +} + +// ============================================================================================ +// +bool vVessel::Render(LPDIRECT3DDEVICE9 dev, const SHADOWMAP *shd, DWORD flg) +{ + _TRACE; + if (!active) return false; + + UINT i, mfd; + + g_pCurrentVisual = this; // Set current visual for mesh debugger + DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + DWORD displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); + + bool bCockpit = (flg & Render::D2) | (flg & Render::VC); + bool bVC = (flg & Render::VC); + bool bInternal = (flg & Render::IP); + + if (scn->GetRenderPass() == RENDERPASS_CUSTOMCAM) bCockpit = bVC = false; + // Always render exterior view for custom cams + + + static VCHUDSPEC hudspec_; + const VCHUDSPEC* hudspec = &hudspec_; + static bool gotHUDSpec(false); + const VCMFDSPEC* mfdspec[MAXMFD] = { NULL }; + + HR(vkEffect::FX->SetBool(vkEffect::eEnvMapEnable, false)); + + if (shd && shd->IsValid()) + { + float s = float(shd->size); + float sr = 2.0f * shd->rad / s; + FVECTOR4 sh = FVECTOR4(sr, 1.0f / s, float(oapiRand()), 1.0f / shd->depth); + FVECTOR4 px = FVECTOR4(shd->SubPx[0], shd->SubPx[1], shd->SubPx[2], 0.0f); + HR(vkEffect::FX->SetMatrix(vkEffect::eLVP, _DX(shd->mLVP))); // Cascade 0 for all + HR(vkEffect::FX->SetVector(vkEffect::eSHD, _DX(sh))); + HR(vkEffect::FX->SetVector(vkEffect::eSHDPx, _DX(px))); + HR(vkEffect::FX->SetBool(vkEffect::eShadowToggle, true)); + vkMesh::SetShadows(shd); + } + else { + vkMesh::SetShadows(NULL); + HR(vkEffect::FX->SetBool(vkEffect::eShadowToggle, false)); + } + + // Check VC MFD screen resolutions ------------------------------------------------ + // + if (bVC && bInternal) { + for (mfd = 0; mfd < MAXMFD; mfd++) gc->GetVCMFDSurface(mfd, &mfdspec[mfd]); + gotHUDSpec = !!gc->GetVCHUDSurface(&hudspec); + } + + + // Initialize MeshShader constants + // + MeshShader::ps_const.Cam_X = *scn->GetCameraX(); + MeshShader::ps_const.Cam_Y = *scn->GetCameraY(); + MeshShader::ps_const.Cam_Z = *scn->GetCameraZ(); + + + // Render Exterior and Interior (VC) meshes -------------------------------------------- + // + for (i=0;i1) vismode = MESHVIS_ALWAYS; + + if (scn->GetRenderPass() != RENDERPASS_VC_SHADOWMAP) + { + if (vismode == 0) continue; + if (bInternal == false) { + if (vismode == MESHVIS_VC) continue; // Added 3-jan-2011 to prevent VC interior double rendering during exterior and interior passes + if ((vismode & MESHVIS_EXTPASS) == 0 && bCockpit) continue; + } + if (bCockpit) { + if (bInternal && (vismode & MESHVIS_EXTPASS)) continue; + if (!(vismode & MESHVIS_COCKPIT)) { + if ((!bVC) || (!(vismode & MESHVIS_VC))) continue; + } + } + else { + if (!(vismode & MESHVIS_EXTERNAL)) continue; + } + } + + WORD Shader = pMesh->GetDefaultShader(); + DWORD mFlags = pMesh->MeshFlags; + + FMATRIX4 mWT; + FMATRIX4* pWT; + + if (meshlist[i].trans) pWT = oapiMatrixMultiply(&mWT, (const FMATRIX4*)meshlist[i].trans, &mWorld); + else pWT = &mWorld; + + if (bVC && bInternal && (pMesh->GetDefaultShader() == SHADER_LEGACY)) { + vkSun local = sunLight; + local.Color *= 0.5f; + pMesh->SetSunLight(&local); + } + else pMesh->SetSunLight(&sunLight); + + + if (bVC && bInternal) + { + for (mfd=0;mfdnmesh == i) { + pMesh->SetMFDScreenId(mfdspec[mfd]->ngroup, 1 + mfd); + } + } + if (gotHUDSpec) { + if (hudspec->nmesh == i) { + pMesh->SetMFDScreenId(hudspec->ngroup, 0x100); + } + } + } + + const FMATRIX4* pVP = scn->GetProjectionViewMatrix(); + const FMATRIX4* pLVP = shd ? (const FMATRIX4*)&shd->mLVP : nullptr; + + // Render vessel meshes -------------------------------------------------------------------------- + // + if (scn->GetRenderPass() == RENDERPASS_VC_SHADOWMAP) + { + if (mFlags & MESHFLAG_SHADOW_VC) + pMesh->RenderShadowMap(pWT, pLVP, 0, bVC); + } + else if (scn->GetRenderPass() == RENDERPASS_SHADOWMAP) + { + pMesh->RenderShadowMap(pWT, pLVP, 0, bVC); + } + else if (scn->GetRenderPass() == RENDERPASS_NORMAL_DEPTH) + { + pMesh->RenderShadowMap(pWT, pVP, 1); + } + else + { + auto ec = GetEnvCam(EnvCamType::Interior, 0); + bool bSL = false; + if (shd) bSL = (shd->tp == SHADOWMAP::sMapType::SingleLod); + + if (Shader != SHADER_LEGACY) + { + if (mFlags & MESHFLAG_VC) { + if (ec) { + float f = ec->da_force * 60.0f; + float b = ec->da_bounch * 2.0f; + float c = ec->da_curve * 3.0f; + if (scn->GetRenderPass() != RENDERPASS_MAINSCENE) + vkEffect::FX->SetValue(vkEffect::eVCIrrad, &FVECTOR4(b, b, b, 1.0f), sizeof(FVECTOR4)); + else + vkEffect::FX->SetValue(vkEffect::eVCIrrad, &FVECTOR4(f, f, f, c), sizeof(FVECTOR4)); + } + if (bSL) pMesh->Render(pWT, ec, RENDER_VESSEL); + else pMesh->Render(pWT, ec, RENDER_VC); + } + else pMesh->Render(pWT, GetExteriorEnvMap(), RENDER_VESSEL); + } + else { + if (bInternal) pMesh->Render(pWT, ec, RENDER_VC); + else pMesh->Render(pWT, GetExteriorEnvMap(), RENDER_VESSEL); + } + } + } + + // Shutdown shadows to prevent from causing problems + HR(vkEffect::FX->SetBool(vkEffect::eShadowToggle, false)); + + if (scn->GetRenderPass() == RENDERPASS_MAINSCENE) { + if (DebugControls::IsActive()) { + if (flags&DBG_FLAGS_SELVISONLY && this != DebugControls::GetVisual()) return true; + if (flags&DBG_FLAGS_BOXES && !bInternal) { + FMATRIX4 id; + vkEffect::RenderBoundingBox(&mWorld, &FMATRIX_Identity, &BBox.mn, &BBox.mx, ptr(FVECTOR4(1, 0, 0, 0.75f))); + } + + RenderLightCone(&mWorld); + + dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + if (flags & DBG_FLAGS_VCZONES) RenderClickZones(); + } + } + + HR(vkEffect::FX->SetBool(vkEffect::eEnvMapEnable, false)) + + return true; +} + + +// ============================================================================================ +// +void vVessel::RenderClickZones() +{ + std::list Zones; + oapiVCGetAreaClickZones(&Zones); + + auto hPS = pRenderZone->GetPSHandle("cb"); + auto hVS = pRenderZone->GetVSHandle("cb"); + + pRenderZone->Setup(nullptr, false, 1); + + struct { + FVECTOR3 pt[4]; + FVECTOR4 color; + FMATRIX4 mW; + FMATRIX4 mVP; + BOOL bSphere; + } cb; + + cb.mW = mWorld; + cb.mVP = scn->GetProjectionViewMatrix(); + + cb.bSphere = false; + for (auto& z : Zones) + { + if (z.mode == 2) { // Quadrilateral + for (int i = 0; i < 4; i++) cb.pt[i] = z.pt[i]; + cb.color = FVECTOR4(1.0f, 1.0f, 0.0f, 1.0f); + pRenderZone->SetPSConstants(hPS, &cb, sizeof(cb)); + pRenderZone->SetVSConstants(hVS, &cb, sizeof(cb)); + hStockMesh[vkSM_BOX]->RenderGroup(0); + } + } + + cb.bSphere = true; + for (auto& z : Zones) + { + if (z.mode == 1) { // Spherical + cb.pt[0] = z.cnt; + cb.pt[1] = z.rad; + cb.color = FVECTOR4(0.3f, 1.0f, 1.0f, 1.0f); + pRenderZone->SetPSConstants(hPS, &cb, sizeof(cb)); + pRenderZone->SetVSConstants(hVS, &cb, sizeof(cb)); + hStockMesh[vkSM_SPHERE]->RenderGroup(0); + } + } +} + + +// ============================================================================================ +// +void vVessel::RenderVectors (LPDIRECT3DDEVICE9 dev, vkPad *pSkp) +{ + const double threshold = 0;//0.25; // threshold for forces to be drawn + VECTOR3 vector; + float lscale = 1e-3f; + float alpha; + double len = 1e-9f; // avoids division by zero if len is not updated + + DWORD bfvmode = *(DWORD*)gc->GetConfigParam(CFGPRM_FORCEVECTORFLAG); + float sclset = *(float*)gc->GetConfigParam(CFGPRM_FORCEVECTORSCALE); + float scale = float(size) / 50.0f; + + // ------------------------------------- + // Render Body Force Vectors + + if (bfvmode & BFV_ENABLE) + { + char label[64]; + bool bLog; + + if (bfvmode & BFV_LOGSCALE) bLog = true; + else bLog = false; + + if (!bLog) { + if (bfvmode & BFV_DRAG) { vessel->GetDragVector(vector); if (length(vector)>len) len = length(vector); } + if (bfvmode & BFV_WEIGHT) { vessel->GetWeightVector(vector); if (length(vector)>len) len = length(vector); } + if (bfvmode & BFV_THRUST) { vessel->GetThrustVector(vector); if (length(vector)>len) len = length(vector); } + if (bfvmode & BFV_LIFT) { vessel->GetLiftVector(vector); if (length(vector)>len) len = length(vector); } + if (bfvmode & BFV_TOTAL) { vessel->GetForceVector(vector); if (length(vector)>len) len = length(vector); } + if (bfvmode & BFV_TORQUE) { vessel->GetTorqueVector(vector); if (length(vector)>len) len = length(vector); } + if (bfvmode & BFV_SIDEFORCE) { vessel->GetSideForceVector(vector); if (length(vector) > len) len = length(vector); } + + lscale = float(size * sclset / len); + } + else { + lscale = float(size * sclset / 50.0); + } + + alpha = *(float*)gc->GetConfigParam(CFGPRM_FORCEVECTOROPACITY); + + if (alpha > 1e-9) // skip all this when opacity is to small (ZEROish) + { + if (bfvmode & BFV_DRAG) { + vessel->GetDragVector(vector); + if (length(vector) > threshold) { + RenderAxisVector(pSkp, ptr(FVECTOR4(1,0,0,alpha)), vector, lscale, scale, bLog); + sprintf_s(label, 64, "D = %sN", value_string(length(vector))); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1,0,0,alpha)), vector, lscale, scale, label, bLog); + } + } + + if (bfvmode & BFV_WEIGHT) { + vessel->GetWeightVector(vector); + if (length(vector) > threshold) { + RenderAxisVector(pSkp, ptr(FVECTOR4(1,1,0,alpha)), vector, lscale, scale, bLog); + sprintf_s(label, 64, "G = %sN", value_string(length(vector))); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1,1,0,alpha)), vector, lscale, scale, label, bLog); + } + } + + if (bfvmode & BFV_THRUST) { + vessel->GetThrustVector(vector); + if (length(vector) > threshold) { + RenderAxisVector(pSkp, ptr(FVECTOR4(0,0,1,alpha)), vector, lscale, scale, bLog); + sprintf_s(label, 64, "T = %sN", value_string(length(vector))); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0,0,1,alpha)), vector, lscale, scale, label, bLog); + } + } + + if (bfvmode & BFV_LIFT) { + vessel->GetLiftVector(vector); + if (length(vector) > threshold) { + RenderAxisVector(pSkp, ptr(FVECTOR4(0,1,0,alpha)), vector, lscale, scale, bLog); + sprintf_s(label, 64, "L = %sN", value_string(length(vector))); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0,1,0,alpha)), vector, lscale, scale, label, bLog); + } + } + + if (bfvmode & BFV_TOTAL) { + vessel->GetForceVector(vector); + if (length(vector) > threshold) { + RenderAxisVector(pSkp, ptr(FVECTOR4(1,1,1,alpha)), vector, lscale, scale, bLog); + sprintf_s(label, 64, "F = %sN", value_string(length(vector))); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1,1,1,alpha)), vector, lscale, scale, label, bLog); + } + } + + if (bfvmode & BFV_TORQUE) { + vessel->GetTorqueVector(vector); + if (length(vector) > threshold) { + RenderAxisVector(pSkp, ptr(FVECTOR4(1,0,1,alpha)), vector, lscale, scale, bLog); + sprintf_s(label, 64, "M = %sNm", value_string(length(vector))); + RenderAxisLabel(pSkp, ptr(FVECTOR4(1,0,1,alpha)), vector, lscale, scale, label, bLog); + } + } + + if (bfvmode & BFV_SIDEFORCE) { + vessel->GetSideForceVector(vector); + if (length(vector) > threshold) { + RenderAxisVector(pSkp, ptr(FVECTOR4(0.0392f, 0.6235f, 0.4941f, alpha)), vector, lscale, scale, bLog); + sprintf_s(label, 64, "SF = %sN", value_string(length(vector))); + RenderAxisLabel(pSkp, ptr(FVECTOR4(0.0392f, 0.6235f, 0.4941f, alpha)), vector, lscale, scale, label, bLog); + } + } + } + } + + // ------------------------------------- + // Render Coordinate Axes + vObject::RenderVectors(dev, pSkp); +} + + +// ============================================================================================ +// +bool vVessel::RenderExhaust() +{ + ExhaustLength = 0.0f; + if (!active) return false; + + DWORD nexhaust = vessel->GetExhaustCount(); + if (!nexhaust) return true; // nothing to do + + EXHAUSTSPEC es; + MATRIX3 R; + vessel->GetRotationMatrix(R); + VECTOR3 cdir = tmul(R, cpos); + + for (DWORD i=0;iGetExhaustLevel(i)==0.0) continue; + vessel->GetExhaustSpec(i, &es); + vkEffect::RenderExhaust(&mWorld, cdir, &es, defexhausttex); + if (es.lsize>ExhaustLength) ExhaustLength = float(es.lsize); + } + return true; +} + + +// ============================================================================================ +// +void vVessel::RenderBeacons(LPDIRECT3DDEVICE9 dev) +{ + if (nmesh < 1) return; + DWORD idx = 0; + const BEACONLIGHTSPEC *bls = vessel->GetBeacon(idx); + if (!bls) return; // nothing to do + bool need_setup = true; + double simt = oapiGetSimTime(); + + for (;bls; bls = vessel->GetBeacon(++idx)) { + if (bls->active) { + if (bls->period && (fmod(simt+bls->tofs, bls->period) > bls->duration)) continue; + double size = bls->size; + if (cdist > 50.0) size *= pow (cdist/50.0, bls->falloff); + RenderSpot(dev, bls->pos, (float)size, *bls->col, false, bls->shape); + } + } +} + + +// ============================================================================================ +// +void vVessel::RenderGrapplePoints (LPDIRECT3DDEVICE9 dev) +{ + if (!oapiGetShowGrapplePoints()) return; // nothing to do + + DWORD i; + ATTACHMENTHANDLE hAtt; + VECTOR3 pos, dir, rot; + const float size = 0.25; + const float alpha = 0.5; + + // Flash calculations + static double lastTime = 0; + static bool isOn = true; + double simt = oapiGetSysTime(); + if (simt-lastTime > 0.5) // Flashing period (twice per second) + { + isOn = !isOn; + lastTime = simt; + } + if (!isOn) return; // nothing to do + + const OBJHANDLE hVessel = vessel->GetHandle(); + + // attachment points to parent + for (i = 0; i < vessel->AttachmentCount(true); ++i) + { + hAtt = vessel->GetAttachmentHandle(true, i); + vessel->GetAttachmentParams(hAtt, pos, dir, rot); + vkEffect::RenderArrow(hVessel, &pos, &dir, &rot, size, ptr(FVECTOR4(1.0f, 0.0f, 0.0f, alpha))); + } + + // attachment points to children + for (i = 0; i < vessel->AttachmentCount(false); ++i) + { + hAtt = vessel->GetAttachmentHandle(false, i); + vessel->GetAttachmentParams(hAtt, pos, dir, rot); + vkEffect::RenderArrow(hVessel, &pos, &dir, &rot, size, ptr(FVECTOR4(0.0f, 0.5f, 1.0f, alpha))); + } +} + + +// ============================================================================================ +// +void vVessel::RenderGroundShadow(LPDIRECT3DDEVICE9 dev, OBJHANDLE hPlanet, float alpha) +{ + if (!bStencilShadow && scn->GetRenderPass() == RENDERPASS_MAINSCENE) return; + if (Config->TerrainShadowing == 0) return; + + static const double eps = 1e-2; + static const double shadow_elev_limit = 0.07; + double d, alt, R; + VECTOR3 pp, sd, pvr; + oapiGetGlobalPos(hPlanet, &pp); // planet global pos + vessel->GetGlobalPos(sd); // vessel global pos + pvr = sd - pp; // planet-relative vessel position + d = length(pvr); // vessel-planet distance + R = oapiGetSize(hPlanet); // planet mean radius + R += vessel->GetSurfaceElevation(); + alt = d - R; // altitude above surface + if (alt*eps > vessel->GetSize()) return; // too high to cast a shadow + + normalise(sd); // shadow projection direction + + // calculate the intersection of the vessel's shadow with the planet surface + double fac1 = dotp(sd, pvr); + if (fac1 > 0.0) return; // shadow doesn't intersect planet surface + double csun = -fac1 / d; // sun elevation above horizon + if (csun < shadow_elev_limit) return; // sun too low to cast shadow + double arg = fac1*fac1 - (dotp(pvr, pvr) - R*R); + if (arg <= 0.0) return; // shadow doesn't intersect with planet surface + double a = -fac1 - sqrt(arg); + + MATRIX3 vR; + vessel->GetRotationMatrix(vR); + VECTOR3 sdv = tmul(vR, sd); // projection direction in vessel frame + VECTOR3 shp = sdv*a; // projection point + VECTOR3 hn, hnp = vessel->GetSurfaceNormal(); + vessel->HorizonInvRot(hnp, hn); + + // perform projections + //double nr0 = dotp(hn, shp); + float nr0 = float(-alt); + double nd = dotp(hn, sdv); + VECTOR3 sdvs = sdv / nd; + + FVECTOR4 nrml = FVECTOR4(float(hn.x), float(hn.y), float(hn.z), float(alt)); + + // build shadow projection matrix + FMATRIX4 mProj, mProjWorld, mProjWorldShift; + + mProj.m11 = 1.0f - (float)(sdvs.x*hn.x); + mProj.m12 = -(float)(sdvs.y*hn.x); + mProj.m13 = -(float)(sdvs.z*hn.x); + mProj.m14 = 0; + mProj.m21 = -(float)(sdvs.x*hn.y); + mProj.m22 = 1.0f - (float)(sdvs.y*hn.y); + mProj.m23 = -(float)(sdvs.z*hn.y); + mProj.m24 = 0; + mProj.m31 = -(float)(sdvs.x*hn.z); + mProj.m32 = -(float)(sdvs.y*hn.z); + mProj.m33 = 1.0f - (float)(sdvs.z*hn.z); + mProj.m34 = 0; + mProj.m41 = (float)(sdvs.x*nr0); + mProj.m42 = (float)(sdvs.y*nr0); + mProj.m43 = (float)(sdvs.z*nr0); + mProj.m44 = 1; + + oapiMatrixMultiply(&mProjWorld, &mProj, &mWorld); + + float scale = (csun - shadow_elev_limit) * 25.0f; + alpha = (1.0f - alpha) * saturate(scale); + + // project all vessel meshes. This should be replaced by a dedicated shadow mesh + + for (UINT i = 0; iHasShadow() == false) continue; + + vkMesh *mesh = meshlist[i].mesh; + + if (meshlist[i].trans) { + VECTOR3 of; + vessel->GetMeshOffset(i, of); + nrml.w += float(dotp(of, hn)); // Sift a local groung level + oapiMatrixMultiply(&mProjWorldShift, meshlist[i].trans, &mProjWorld); + mesh->RenderStencilShadows(alpha, &mWorld, &mProjWorldShift, false, &nrml); + } + else mesh->RenderStencilShadows(alpha, &mWorld, &mProjWorld, false, &nrml); + } +} + + +// ============================================================================================ +// Return true if it's time to move to a next vessel +// false, if more rendereing is required here. +// +bool vVessel::ProcessEnvMaps(LPDIRECT3DDEVICE9 pDev, DWORD cnt, DWORD flags) +{ + bool bReflective = false; + + if (meshlist) { + for (DWORD i = 0; i < nmesh; i++) { + if (meshlist[i].mesh) { + if (meshlist[i].mesh->IsReflective()) { + bReflective = true; + break; + } + } + } + } + + if (!bReflective) return true; // If none of the meshes are reflective then we are done + + // Render vessel specific map + bool bRet = RenderENVMap(pDev, &ecDefExt, cnt, flags); + + if (bRet) ecDefExt.bRendered = false; + + return bRet; +} + + +// ============================================================================================ +// Render Env Map, return 'true' if the map is completed +// +bool vVessel::RenderENVMap(LPDIRECT3DDEVICE9 pDev, ENVCAMREC* ec, DWORD cnt, DWORD flags) +{ + LPDIRECT3DSURFACE9 pEnvDS = GetScene()->GetEnvDepthStencil(); + + if (!pEnvDS) { + LogErr("EnvDepthStencil doesn't exists"); + return true; + } + + // Create a EnvMap if doesn't already exists -------------------------------------------------------------------- + // + if (ec->flags & ENVCAM_PLANE) + { + if (!ec->pPlane) + { + D3DSURFACE_DESC desc; + pEnvDS->GetDesc(&desc); + if (D3DXCreateTexture(pDev, desc.Width, desc.Height, 5, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &ec->pPlane) != S_OK) { + LogErr("Failed to create env-plane for visual %s", _PTR(this)); + return true; + } + } + } + else { + if (!ec->pCube) // If the maps exists check the type and assign + { + D3DSURFACE_DESC desc; + pEnvDS->GetDesc(&desc); + if (D3DXCreateCubeTexture(pDev, desc.Width, 5, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &ec->pCube) != S_OK) { + LogErr("Failed to create env-cubemap for visual %s", _PTR(this)); + return true; + } + } + } + + + // Create a Irradiance map if doesn't already exists -------------------------------------------------------------------- + // + if (!ec->pIrrad) + { + if (D3DXCreateTexture(pDev, 128, 64, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT, &ec->pIrrad) != S_OK) { + LogErr("Failed to create irradiance map for visual %s", _PTR(this)); + return true; + } + } + + + // Create blurred maps and irradiance --------------------------------------------------------------------- + // + if (ec->bRendered) { + if (ec->pCube) { + scn->RenderBlurredMap(pDev, ec->pCube); + scn->IntegrateIrradiance(this, ec, false); + } + // TODO: Blur 2D plane map + return true; + } + + + // Render EnvMaps --------------------------------------------------------------------------------------- + // + std::set RndList = scn->GetVessels(10e3, true); + std::set AddLightSrc; + + AddLightSrc.insert(this); + + if ((ec->flags & ENVCAM_FOCUS) == 0) RndList.erase(this); + + DWORD nAtc = vessel->AttachmentCount(false); + DWORD nDoc = vessel->DockCount(); + + if (ec->flags & ENVCAM_OMIT_ATTC) { + for (DWORD i = 0; i < nAtc; i++) { + ATTACHMENTHANDLE hAtc = vessel->GetAttachmentHandle(false, i); + if (hAtc) { + OBJHANDLE hAtcObj = vessel->GetAttachmentStatus(hAtc); + if (hAtcObj) { + vObject* vObj = gc->GetScene()->GetVisObject(hAtcObj); + if (vObj) RndList.erase((vVessel*)vObj); + } + } + } + } + else { + for (auto id : ec->omitAttc) { + ATTACHMENTHANDLE hAtc = vessel->GetAttachmentHandle(false, id); + if (hAtc) { + OBJHANDLE hAtcObj = vessel->GetAttachmentStatus(hAtc); + if (hAtcObj) { + vObject* vObj = gc->GetScene()->GetVisObject(hAtcObj); + if (vObj) RndList.erase((vVessel*)vObj); + } + } + } + } + + + // ----------------------------------------------------------------------------------------------- + // + VECTOR3 gpos; + vessel->Local2Global(_V(ec->lPos.x, ec->lPos.y, ec->lPos.z), gpos); + + if (ec->pCube) + { + // Prepare camera and scene for env map rendering + scn->PushCamera(); + scn->SetupInternalCamera(NULL, &gpos, 0.7853981634, 1.0); + scn->BeginPass(RENDERPASS_ENVCAM); + gc->PushRenderTarget(NULL, pEnvDS, RENDERPASS_ENVCAM); + + FMATRIX4 mEnv; + FVECTOR3 dir, up; + LPDIRECT3DSURFACE9 pSrf = NULL; + + for (DWORD i = 0; i < cnt; i++) { + + HR(ec->pCube->GetCubeMapSurface(D3DCUBEMAP_FACES(ec->iSide), 0, &pSrf)); + + gc->AlterRenderTarget(pSrf, pEnvDS); + + EnvMapDirection(ec->iSide, &dir, &up); + + FVECTOR3 cp = crossp(up, dir); + normalize(cp); + oapiMatrixIdentity(&mEnv); + D3DMAT_FromAxis(&mEnv, &cp, &up, &dir); + + scn->SetCameraFrustumLimits(0.25, 1e8); + scn->SetupInternalCamera(&mEnv, NULL, 0.7853981634, 1.0); + scn->RenderSecondaryScene(RndList, AddLightSrc, flags); + + SAFE_RELEASE(pSrf); + + ec->iSide++; + + if (ec->iSide >= 6) { + ec->bRendered = true; + ec->iSide = 0; + break; + } + } + + gc->PopRenderTargets(); + scn->PopPass(); + scn->PopCamera(); + + return false; + } + + if (ec->pPlane) + { + // TODO: + return true; + } + + return true; +} + + + +// ============================================================================================ +// Render interior Env Map, return 'true' if the map is completed +// +bool vVessel::RenderInteriorENVMap(LPDIRECT3DDEVICE9 pDev, ENVCAMREC* ec, SHADOWMAP *sm) +{ + if (!ec) return true; + + LPDIRECT3DSURFACE9 pEnvDS = GetScene()->GetDepthStencil(128); + + if (!pEnvDS) { + LogErr("RenderInteriorENVMap() DS doesn't exists"); + return true; + } + + D3DSURFACE_DESC desc; + pEnvDS->GetDesc(&desc); + + // Create a EnvMap if doesn't already exists -------------------------------------------------------------------- + // + if (!ec->pCube) { + if (D3DXCreateCubeTexture(pDev, desc.Width, 5, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &ec->pCube) != S_OK) { + LogErr("Failed to create env-cubemap for visual %s", _PTR(this)); + return true; + } + } + + + // Create a Irradiance map if doesn't already exists -------------------------------------------------------------------- + // + if (!ec->pIrrad) { + if (D3DXCreateTexture(pDev, 128, 64, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &ec->pIrrad) != S_OK) { + LogErr("Failed to create irradiance map for visual %s", _PTR(this)); + return true; + } + } + + + // Compute blur and irradiance -------------------------------------------------------------------------- + // + if (ec->bRendered) { + scn->RenderBlurredMap(pDev, ec->pCube); + if (Config->ExpVCLight) scn->IntegrateIrradiance(this, ec, true); + ec->bRendered = false; + return true; + } + + + + // Render EnvMaps --------------------------------------------------------------------------------------- + // + std::set RndList; + std::set AddLightSrc; // empty + + RndList.insert(this); + + if (ec->pCube) + { + auto eStage = GetExteriorEnvMap(); + + VECTOR3 gp; + vessel->Local2Global(ec->lPos._V(), gp); + + //FVECTOR3 rpos = TransformCoord(ec->lPos, FMATRIX4(mWorld)); + //FMATRIX4 mW, mWI; + //oapiMatrixIdentity(&mW); + //D3DMAT_SetTranslation(&mW, &rpos._V()); + //oapiMatrixInverse(&mWI, NULL, &mW); + + // Prepare camera and scene for env map rendering + scn->PushCamera(); + scn->BeginPass(RENDERPASS_ENVCAM); + scn->SetCameraFrustumLimits(0.1, 3e4); + + gc->PushRenderTarget(NULL, pEnvDS, RENDERPASS_ENVCAM); + + FMATRIX4 mEnv; + FVECTOR3 dir, up; + LPDIRECT3DSURFACE9 pSrf = NULL; + + for (DWORD i = 0; i < 6; i++) + { + HR(ec->pCube->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &pSrf)); + gc->AlterRenderTarget(pSrf, pEnvDS); + EnvMapDirection(i, &dir, &up); + FVECTOR3 cp = crossp(up, dir); + normalize(cp); + oapiMatrixIdentity(&mEnv); + D3DMAT_FromAxis(&mEnv, &cp, &up, &dir); + //oapiMatrixMultiply(&mEnv, &mWI, &mEnv); + + //scn->CameraOffOrigin90(&mEnv, rpos); + scn->SetupInternalCamera(&mEnv, &gp, 0.7853981634, 1.0); + scn->RenderStageSet(eStage->pCube); + + SAFE_RELEASE(pSrf); + } + + gc->PopRenderTargets(); + scn->PopPass(); + scn->PopCamera(); + + ec->bRendered = true; + } + + return false; +} + + +// ============================================================================================ +// Return exterior env-map for a vessel, seek from a root if doesn't exists. +// Root must always have a map +// +ENVCAMREC*vVessel::GetExteriorEnvMap() +{ + if (ecDefExt.pCube) return &ecDefExt; + else if (vRoot != this && vRoot) return vRoot->GetExteriorEnvMap(); + + assert(Config->EnvMapMode != 0); // Should always return valid map if feature is enabled + return nullptr; +} + + +// ============================================================================================ +// +ENVCAMREC* vVessel::CreateEnvCam(EnvCamType ec, int idx) +{ + if (ec == EnvCamType::Interior) + { + if (idx >= MAX_INTCAM) return nullptr; + auto x = new ENVCAMREC; + x->type = ec; + + if (idx < 0) { + for (auto& c : InteriorCams) if (!c) { c = x; return x; } // Use empty slot + idx = 0; // No empty slot + } + SAFE_DELETE(InteriorCams[idx]); + InteriorCams[idx] = x; + return x; + } + if (ec == EnvCamType::Exterior) { + return &ecDefExt; + } + return nullptr; +} + + +// ============================================================================================ +// +bool vVessel::HasOwnEnvCam(EnvCamType ec) +{ + if (ec == EnvCamType::Exterior && (ecDefExt.flags & ENVCAM_USER)) return true; + if (ec == EnvCamType::Interior) for (auto x : InteriorCams) if (x) return true; + if (ec == EnvCamType::Mesh) { + // TODO: + } + return false; +} + +// ============================================================================================ +// +ENVCAMREC* vVessel::GetEnvCam(EnvCamType ec, int idx) +{ + if (ec == EnvCamType::Exterior) return &ecDefExt; + if (ec == EnvCamType::Interior) { + if (idx >= MAX_INTCAM) return nullptr; + if (idx < 0) { + for (auto c : InteriorCams) if (c) return c; // Get any cam + } + else return InteriorCams[std::clamp(idx, 0, MAX_INTCAM - 1)]; + } + if (ec == EnvCamType::Mesh) { + // TODO: + } + return nullptr; +} + +// ============================================================================================ +// +bool vVessel::GetInteriorCams(std::list* pCams) +{ + pCams->clear(); + for (auto x : InteriorCams) if (x) pCams->push_back(x); + return !pCams->empty(); +} + + +// ============================================================================================ +// +bool vVessel::IsRoot() const +{ + return (vRoot == nullptr) | (vRoot == this); +} + +// ============================================================================================ +// +void vVessel::RenderLightCone(FMATRIX4* pWT) +{ + if (DebugControls::sEmitter == 0) return; + if (DebugControls::Emitters.count(DebugControls::sEmitter) == 0) return; + + DWORD ec = vessel->LightEmitterCount(); + const LightEmitter *se = DebugControls::Emitters[DebugControls::sEmitter]; + const LightEmitter *em = NULL; + + for (DWORD i = 0; i < ec; i++) if (vessel->GetLightEmitter(i) == se) { em = se; break; } + + if (!em) return; + + float P = 0.0f, U = 0.0f, R = 0.0f; + + if (em->GetType() == LightEmitter::LT_SPOT) { + P = float(((const SpotLight *)em)->GetPenumbra()); + U = float(((const SpotLight *)em)->GetUmbra()); + R = float(((const SpotLight *)em)->GetRange()); + } + if (em->GetType() == LightEmitter::LT_POINT) { + R = float(((const SpotLight *)em)->GetRange()); + return; + } + + VECTOR3 _P = em->GetPosition(); + VECTOR3 _D = em->GetDirection(); + + if (em->GetType() == LightEmitter::LT_SPOT) { + FVECTOR3 Main[2]; + Main[0] = _F(_P); + Main[1] = _F(_P + _D * double(R)); + WORD Idx[2] = { 0, 1 }; + vkEffect::RenderLines(Main, Idx, 2, 2, pWT, 0xFF00FF00); + + FVECTOR3 Circle[65]; + WORD CIdx[130]; + + VECTOR3 _X = crossp(_D, _V(0.4, 0.2, -0.6)); + VECTOR3 _Y = crossp(_D, _X); + + FVECTOR3 _x = _F(_X); + FVECTOR3 _y = _F(_Y); + FVECTOR3 _d = _F(_D); + + float q = tan(P*0.5f) * R; + float a = 0.0f; + for (int i = 0; i < 64; i++) { + Circle[i] = _x * (cos(a)*q) + _y * (sin(a)*q) + _d * R + Main[0]; + a += float(PI2 / 63.0); + CIdx[i * 2] = i; + CIdx[i * 2 + 1] = i + 1; + } + vkEffect::RenderLines(Circle, CIdx, 64, 126, pWT, 0xFF00FF00); + } +} + + +// ============================================================================================ +// +bool vVessel::ModLighting() +{ + tCheckLight = oapiGetSimTime(); + + // we only test the closest celestial body for shadowing + OBJHANDLE hP = vessel->GetSurfaceRef(); + if (hP==NULL) { LogErr("Vessel's surface reference is NULL"); return false; } + + vObject *vO = GetScene()->GetVisObject(hP); + + if (vO) { + if (vO->Type() == OBJTP_PLANET) { + vPlanet* vP = (vPlanet*)vO; + VECTOR3 rpos = gpos - vP->GlobalPos(); + sunLight = vP->GetObjectAtmoParams(rpos); + return true; + } + } + + DWORD ambient = *(DWORD*)gc->GetConfigParam(CFGPRM_AMBIENTLEVEL); + sunLight.Ambient = float(ambient) * 0.0039f; + sunLight.Transmission = 1.0f; + sunLight.Incatter = 0.0f; + sunLight.Color = 1.0f; // Config->GFXSunIntensity; + sunLight.Dir = FVECTOR3(-sundir); + + return true; +} + + +// ============================================================================================ +// Delete AC and all of it's children from local database +// +void vVessel::DeleteDefaultState(ANIMATIONCOMP *AC) +{ + defstate.erase(AC->trans); + for (UINT i = 0; i < AC->nchildren; ++i) DeleteDefaultState(AC->children[i]); +} + + +// ============================================================================================ +// Store AC and all of it's children to local database +// +void vVessel::StoreDefaultState(ANIMATIONCOMP *AC) +{ + // If Already exists then skip it + if (defstate.count(AC->trans)) return; + + auto trans = AC->trans; + _defstate def; + + switch (trans->Type()) { + case MGROUP_TRANSFORM::NULLTRANSFORM: + break; + case MGROUP_TRANSFORM::ROTATE: { + MGROUP_ROTATE *rot = (MGROUP_ROTATE*)trans; + def.ref = rot->ref; + def.vdata = unit(rot->axis); + def.fdata = rot->angle; + } break; + case MGROUP_TRANSFORM::TRANSLATE: { + MGROUP_TRANSLATE *lin = (MGROUP_TRANSLATE*)trans; + def.vdata = lin->shift; + } break; + case MGROUP_TRANSFORM::SCALE: { + MGROUP_SCALE *scl = (MGROUP_SCALE*)trans; + def.ref = scl->ref; + def.vdata = scl->scale; + } break; + } + + if (trans->mesh == LOCALVERTEXLIST) for (UINT j = 0; j < trans->ngrp; ++j) def.vtx.push_back(((VECTOR3 *)trans->grp)[j]); + + defstate[AC->trans] = def; + + for (UINT i = 0; i < AC->nchildren; ++i) StoreDefaultState(AC->children[i]); +} + + +// ============================================================================================ +// +void vVessel::RestoreDefaultState(ANIMATIONCOMP *AC) +{ + auto trans = AC->trans; + auto it = defstate.find(AC->trans); + + assert(it != defstate.end()); + + if (trans->mesh == LOCALVERTEXLIST) { + VECTOR3 *vtx = (VECTOR3*)trans->grp; + for (UINT i = 0; i < trans->ngrp; i++) vtx[i] = it->second.vtx[i]; + } + + switch (trans->Type()) { + case MGROUP_TRANSFORM::NULLTRANSFORM: + break; + case MGROUP_TRANSFORM::ROTATE: { + MGROUP_ROTATE *rot = (MGROUP_ROTATE*)trans; + rot->ref = it->second.ref; + rot->axis = it->second.vdata; + rot->angle = it->second.fdata; + } break; + case MGROUP_TRANSFORM::TRANSLATE: { + MGROUP_TRANSLATE *lin = (MGROUP_TRANSLATE*)trans; + lin->shift = it->second.vdata; + } break; + case MGROUP_TRANSFORM::SCALE: { + MGROUP_SCALE *scl = (MGROUP_SCALE*)trans; + scl->ref = it->second.ref; + scl->scale = it->second.vdata; + } break; + } + + for (UINT i = 0; i < AC->nchildren; ++i) RestoreDefaultState(AC->children[i]); +} + + +// ============================================================================================ +// +void vVessel::Animate(UINT an, UINT mshidx) +{ + double s0, s1, ds; + UINT i, ii; + FMATRIX4 T; + ANIMATION *A = anim+an; + + for (ii = 0; ii < A->ncomp; ii++) { + + i = (A->state > currentstate[an] ? ii : A->ncomp-ii-1); + ANIMATIONCOMP *AC = A->comp[i]; + + if ((mshidx != LOCALVERTEXLIST) && (mshidx != AC->trans->mesh)) continue; + + s0 = currentstate[an]; // current animation state in the visual + if (s0 < AC->state0) s0 = AC->state0; + else if (s0 > AC->state1) s0 = AC->state1; + s1 = A->state; // required animation state + if (s1 < AC->state0) s1 = AC->state0; + else if (s1 > AC->state1) s1 = AC->state1; + if ((ds = (s1-s0)) == 0) continue; // nothing to do for this component + ds /= (AC->state1 - AC->state0); // stretch to range 0..1 + + // Build transformation matrix + switch (AC->trans->Type()) + { + case MGROUP_TRANSFORM::NULLTRANSFORM: + { + oapiMatrixIdentity (&T); + AnimateComponent (AC, T); + } break; + + case MGROUP_TRANSFORM::ROTATE: + { + MGROUP_ROTATE *rot = (MGROUP_ROTATE*)AC->trans; + FVECTOR3 ax(float(rot->axis.x), float(rot->axis.y), float(rot->axis.z)); + D3DMAT_RotationFromAxis (ax, (float)ds*rot->angle, &T); + float dx = float(rot->ref.x), dy = float(rot->ref.y), dz = float(rot->ref.z); + T.m41 = dx - T.m11*dx - T.m21*dy - T.m31*dz; + T.m42 = dy - T.m12*dx - T.m22*dy - T.m32*dz; + T.m43 = dz - T.m13*dx - T.m23*dy - T.m33*dz; + AnimateComponent (AC, T); + } break; + + case MGROUP_TRANSFORM::TRANSLATE: + { + MGROUP_TRANSLATE *lin = (MGROUP_TRANSLATE*)AC->trans; + oapiMatrixIdentity (&T); + T.m41 = (float)(ds*lin->shift.x); + T.m42 = (float)(ds*lin->shift.y); + T.m43 = (float)(ds*lin->shift.z); + AnimateComponent (AC, T); + } break; + + case MGROUP_TRANSFORM::SCALE: + { + MGROUP_SCALE *scl = (MGROUP_SCALE*)AC->trans; + s0 = (s0-AC->state0)/(AC->state1-AC->state0); + s1 = (s1-AC->state0)/(AC->state1-AC->state0); + oapiMatrixIdentity (&T); + T.m11 = (float)((s1*(scl->scale.x-1)+1)/(s0*(scl->scale.x-1)+1)); + T.m22 = (float)((s1*(scl->scale.y-1)+1)/(s0*(scl->scale.y-1)+1)); + T.m33 = (float)((s1*(scl->scale.z-1)+1)/(s0*(scl->scale.z-1)+1)); + T.m41 = (float)scl->ref.x * (1.0f-T.m11); + T.m42 = (float)scl->ref.y * (1.0f-T.m22); + T.m43 = (float)scl->ref.z * (1.0f-T.m33); + AnimateComponent (AC, T); + } break; + } + } +} + + +// ============================================================================================ +// +void vVessel::AnimateComponent (ANIMATIONCOMP *comp, const FMATRIX4 &T) +{ + UINT i; + + bBSRecompute = true; + + MGROUP_TRANSFORM *trans = comp->trans; + + if (trans->mesh == LOCALVERTEXLIST) { // transform a list of individual vertices + VECTOR3 *vtx = (VECTOR3*)trans->grp; + for (i = 0; i < trans->ngrp; i++) TransformPoint(vtx[i], T); + } + else { // transform mesh groups + + if (trans->mesh >= nmesh) return; // mesh index out of range + vkMesh *mesh = meshlist[trans->mesh].mesh; + if (!mesh) return; + + if (trans->grp) { // animate individual mesh groups + for (i=0;ingrp;i++) mesh->TransformGroup(trans->grp[i], &T); + } + else { // animate complete mesh + mesh->Transform(&T); + } + } + + // recursively transform all child animations + for (i = 0; i < comp->nchildren; i++) { + + ANIMATIONCOMP *child = comp->children[i]; + AnimateComponent (child, T); + + switch (child->trans->Type()) { + + case MGROUP_TRANSFORM::NULLTRANSFORM: + break; + + case MGROUP_TRANSFORM::ROTATE: { + MGROUP_ROTATE *rot = (MGROUP_ROTATE*)child->trans; + TransformPoint (rot->ref, T); + TransformDirection (rot->axis, T, true); + } break; + + case MGROUP_TRANSFORM::TRANSLATE: { + MGROUP_TRANSLATE *lin = (MGROUP_TRANSLATE*)child->trans; + TransformDirection (lin->shift, T, false); + } break; + + case MGROUP_TRANSFORM::SCALE: { + MGROUP_SCALE *scl = (MGROUP_SCALE*)child->trans; + TransformPoint (scl->ref, T); + // we can't transform anisotropic scaling vector + } break; + } + } +} + + +// ============================================================================================ +// +void vVessel::SetVisualProperty(VisualProp prp, int idx, const type_info& t, const void* val) +{ + if (t == typeid(FVECTOR3)) + { + FVECTOR3* v = (FVECTOR3*)val; + + if (prp == VisualProp::BAKED_LIGHT) { + if (idx >= 0 && idx <= 15) { + if (BakedLightsControl[idx] != *v) { + BakedLightsControl[idx] = *v; + bMustRebake = true; + } + } + return; + } + + if (prp == VisualProp::AMBIENT) { + VCAmbient = *v; + return; + } + + if (prp == VisualProp::EXT_PROBE_POS) { + ecDefExt.lPos = *v; + ecDefExt.flags |= ENVCAM_USER; + return; + } + + if (prp == VisualProp::CREATE_VC_PROBE) { + auto ec = GetEnvCam(EnvCamType::Interior, idx); + if (!ec) ec = CreateEnvCam(EnvCamType::Interior, idx); + ec->lPos = *v; + oapiWriteLogV("-- [ CREATE_VC_PROBE ] --"); + return; + } + } + + if (t == typeid(float)) + { + float* v = (float*)val; + auto ec = GetEnvCam(EnvCamType::Interior, idx); + if (ec) { + if (prp == VisualProp::DA_CURVE) ec->da_curve = *v; + if (prp == VisualProp::DA_BOUNCH) ec->da_bounch = *v; + if (prp == VisualProp::DA_FORCE) ec->da_force = *v; + } + } + + oapiWriteLogV("Failed to set visual property prp=%d, type=[%s]", int(prp), t.name()); +} + + +// ============================================================================================ +// +bool vVessel::GetVisualProperty(VisualProp prp, int idx, const type_info& t, void* val) +{ + if (t == typeid(FVECTOR3)) + { + if (prp == VisualProp::BAKED_LIGHT) if (idx >= 0 && idx <= 15) { + *((FVECTOR3*)val) = BakedLightsControl[idx]; + return true; + } + + if (prp == VisualProp::AMBIENT) { + *((FVECTOR3*)val) = VCAmbient; + return true; + } + } + + if (t == typeid(float)) + { + auto ec = GetEnvCam(EnvCamType::Interior, idx); + if (ec) { + if (prp == VisualProp::DA_CURVE) *((float*)val) = ec->da_curve; + if (prp == VisualProp::DA_BOUNCH) *((float*)val) = ec->da_bounch; + if (prp == VisualProp::DA_FORCE) *((float*)val) = ec->da_force; + } + } + + oapiWriteLogV("Failed to get visual property prp=%d, type=[%s]", int(prp), t.name()); + return false; +} + + +// ============================================================================================ +// +void vVessel::RenderReentry(LPDIRECT3DDEVICE9 dev) +{ + + if (defreentrytex == NULL || nmesh < 1) return; + + double p = vessel->GetAtmDensity(); + double v = vessel->GetAirspeed(); + + float lim = 100000000.0; + float www = float(p*v*v*v); + float ints = max(0.0f,(www-lim)) / (5.0f*lim); + + if (ints>1.0f) ints = 1.0f; + if (ints<0.01f) return; + + VECTOR3 d; + vessel->GetShipAirspeedVector(d); + vessel->GlobalRot(d, d); + normalise(d); + + float x = float(dotp(d, unit(cpos))); + if (x<0) x=-x; x=pow(x,0.3f); + + float alpha_B = (x*0.40f + 0.60f) * ints; + float alpha_A = (1.0f - x*0.50f) * ints * 1.2f; + + float size = float(vessel->GetSize()) * 1.7f; + + FVECTOR3 vPosA(float(cpos.x), float(cpos.y), float(cpos.z)); + FVECTOR3 vDir(float(d.x), float(d.y), float(d.z)); + FVECTOR3 vPosB = vPosA + vDir * (size*0.25f); + + vkEffect::RenderReEntry(defreentrytex, &vPosA, &vPosB, &vDir, alpha_A, alpha_B, size); +} + + +// =========================================================================================== +// +bool vVessel::GetMinMaxDistance(float *zmin, float *zmax, float *dmin) +{ + if (bBSRecompute) UpdateBoundingBox(); + + FMATRIX4 mWorldView, mWorldViewTrans, mTF; + + WORD vismode = MESHVIS_EXTERNAL | MESHVIS_EXTPASS; + + Scene *scn = gc->GetScene(); + + FVECTOR4 Field = D9LinearFieldOfView(scn->GetProjectionMatrix()); + + oapiMatrixMultiply(&mWorldView, &mWorld, scn->GetViewMatrix()); + + for (DWORD i=0;iGetDevice(), meshlist[i].mesh->GetAABB(), &mWorldViewTrans, &Field, zmin, zmax, dmin); + } + else { + D9ComputeMinMaxDistance(gc->GetDevice(), meshlist[i].mesh->GetAABB(), &mWorldView, &Field, zmin, zmax, dmin); + } + } + } + + return true; +} + +// =========================================================================================== +// +void vVessel::UpdateBoundingBox() +{ + if (nmesh==0) return; + if (bBSRecompute==false) return; + + bBSRecompute = false; + bool bFirst = true; + + WORD vismode = MESHVIS_EXTERNAL | MESHVIS_EXTPASS; + + FMATRIX4 mTF; + + for (DWORD i=0;iGetTransform(); + FMATRIX4* pTF = &mTF; + + if (meshlist[i].trans) { + if (pMeshTF) oapiMatrixMultiply(pTF, meshlist[i].trans, pMeshTF); + else pTF = meshlist[i].trans; + } else { + if (pMeshTF) pTF = pMeshTF; + else pTF = NULL; + } + + D9AddAABB(meshlist[i].mesh->GetAABB(), pTF, &BBox, bFirst); + bFirst = false; + } + else { + meshlist[i].mesh->UpdateBoundingBox(); + } + } + + DWORD nexhaust = vessel->GetExhaustCount(); + + if (nexhaust) { + EXHAUSTSPEC es; + for (DWORD i=0;iGetExhaustLevel(i); + if (lvl==0.0) continue; + vessel->GetExhaustSpec(i, &es); + VECTOR3 e = (*es.lpos) - (*es.ldir) * (es.lofs + es.lsize*lvl); + FVECTOR3 ext(float(e.x), float(e.y), float(e.z)); + D9AddPointAABB(&BBox, &ext); + } + + for (DWORD i=0;iGetExhaustSpec(i, &es); + VECTOR3 r = (*es.lpos); + FVECTOR3 ref(float(r.x), float(r.y), float(r.z)); + D9AddPointAABB(&BBox, &ref); + } + } + + D9UpdateAABB(&BBox); +} + +// =========================================================================================== +// +vkPick vVessel::Pick(const FVECTOR3 *vDir, const PickProp *p) +{ + FMATRIX4 mWT; + FMATRIX4* pWT = NULL; + + DWORD flags = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDEBUGFLAGS); + DWORD displ = *(DWORD*)gc->GetConfigParam(CFGPRM_GETDISPLAYMODE); + + bool bCockpit = (oapiCameraInternal() && (hObj == oapiGetFocusObject())); + bool bVC = (bCockpit && (oapiCockpitMode() == COCKPIT_VIRTUAL)); + + vkPick result; + result.dist = 1e30f; + result.pMesh = NULL; + result.vObj = NULL; + result.group = -1; + result.idx = -1; + + if (!meshlist || nmesh==0) return result; + + for (DWORD i=0;iPick(&mWorld, meshlist[i].trans, vDir, p); + if (pick.pMesh) if (pick.dist= nmesh) return -1; + vkMesh *pMesh = meshlist[mi].mesh; + if (pMesh == NULL) return -2; + if (gi >= pMesh->GetGroupCount()) return -3; + + if (func == gcCore::MatrixId::MESH) memcpy_s(pMat, sizeof(FMATRIX4), ptr(pMesh->GetTransform(-1, false)), sizeof(FMATRIX4)); + if (func == gcCore::MatrixId::GROUP) memcpy_s(pMat, sizeof(FMATRIX4), ptr(pMesh->GetTransform(gi, false)), sizeof(FMATRIX4)); + + if (func == gcCore::MatrixId::OFFSET) { + if (meshlist[mi].trans) memcpy_s(pMat, sizeof(FMATRIX4), meshlist[mi].trans, sizeof(FMATRIX4)); + else { + FMATRIX4 Ident; + oapiMatrixIdentity(&Ident); + memcpy_s(pMat, sizeof(FMATRIX4), &Ident, sizeof(FMATRIX4)); + } + return 0; + } + + if (func == gcCore::MatrixId::COMBINED) { + FMATRIX4 MeshGrp = pMesh->GetTransform(gi, true); + if (meshlist[mi].trans) { + FMATRIX4 MeshGrpTrans; + oapiMatrixMultiply(&MeshGrpTrans, &MeshGrp, meshlist[mi].trans); + memcpy_s(pMat, sizeof(FMATRIX4), &MeshGrpTrans, sizeof(FMATRIX4)); + } + else memcpy_s(pMat, sizeof(FMATRIX4), &MeshGrp, sizeof(FMATRIX4)); + } + + return 0; +} + +// ============================================================================================ +// +int vVessel::SetMatrixTransform(gcCore::MatrixId func, DWORD mi, DWORD gi, const FMATRIX4 *pMat) +{ + if (mi >= nmesh) return -1; + vkMesh *pMesh = meshlist[mi].mesh; + if (pMesh == NULL) return -2; + if (gi >= pMesh->GetGroupCount()) return -3; + + if (func == gcCore::MatrixId::OFFSET) { + if (meshlist[mi].trans == NULL) meshlist[mi].trans = new FMATRIX4; + memcpy_s(meshlist[mi].trans, sizeof(FMATRIX4), pMat, sizeof(FMATRIX4)); + } + + if (func == gcCore::MatrixId::MESH) if (!pMesh->SetTransform(-1, (FMATRIX4*)pMat)) return -4; + if (func == gcCore::MatrixId::GROUP) if (!pMesh->SetTransform(gi, (FMATRIX4*)pMat)) return -5; + + return 0; +} + +// ============================================================================================ +// +void vVessel::ReloadTextures() +{ + for (UINT i = 0; i < nmesh; i++) if (meshlist[i].mesh) meshlist[i].mesh->ReloadTextures(); +} + + + + + +// =========================================================================================== +// + +SurfNative * vVessel::defreentrytex = 0; +SurfNative * vVessel::defexhausttex = 0; +ShaderClass* vVessel::pRenderZone = 0; + + +// ============================================================== +// Nonmember helper functions + +void TransformPoint (VECTOR3 &p, const FMATRIX4 &T) +{ + double x = p.x*T.m11 + p.y*T.m21 + p.z*T.m31 + T.m41; + double y = p.x*T.m12 + p.y*T.m22 + p.z*T.m32 + T.m42; + double z = p.x*T.m13 + p.y*T.m23 + p.z*T.m33 + T.m43; + double w = 1.0/(p.x*T.m14 + p.y*T.m24 + p.z*T.m34 + T.m44); + p.x = x*w; + p.y = y*w; + p.z = z*w; +} + +// =============================================================== +// +void TransformDirection (VECTOR3 &a, const FMATRIX4 &T, bool normalise) +{ + double x = a.x*T.m11 + a.y*T.m21 + a.z*T.m31; + double y = a.x*T.m12 + a.y*T.m22 + a.z*T.m32; + double z = a.x*T.m13 + a.y*T.m23 + a.z*T.m33; + a.x = x, a.y = y, a.z = z; + if (normalise) { + double len = 1.0/sqrt (x*x + y*y + z*z); + a.x *= len; + a.y *= len; + a.z *= len; + } +} + +// =========================================================================== +// Add ISO-prefix to a value +// +#define ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) + +static inline const int clip(int v, int vMin, int vMax) +{ + if (v < vMin) return vMin; + else if (v > vMax) return vMax; + else return v; +} + +inline const char *value_string (char *buf, size_t buf_size, double val) +{ + static const char unit_prefixes[] = { (char)181/*'µ'*/, 'm', '\0', 'k' , 'M' , 'G' , 'T' , 'P'}; + + int index = (int) (log10(val)) / 3; + val /= pow(10.0, index*3); + + // apply array index offset (+2) [-2...5] => [0...7] + index = clip(index+2, 0, ARRAY_ELEMS(unit_prefixes) -1); + sprintf_s(buf, buf_size, "%.3f %c", val, unit_prefixes[index]); + + return buf; +} + +const char *value_string (double val) +{ + static char buf[16]; + return value_string(buf, sizeof(buf), val); +} diff --git a/OVP/VulkanClient/VVessel.h b/OVP/VulkanClient/VVessel.h new file mode 100644 index 000000000..dbf072b96 --- /dev/null +++ b/OVP/VulkanClient/VVessel.h @@ -0,0 +1,265 @@ +// ============================================================== +// VVessel.h +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __VVESSEL_H +#define __VVESSEL_H + +#define MAX_INTCAM 6 + +#include "VObject.h" +#include "Mesh.h" +#include "gcCore.h" +#include +#include + +class oapi::vkClient; + +typedef struct { + float fdata; + VECTOR3 ref, vdata; + std::vector vtx; +} _defstate; + + + +// ============================================================== +// class vVessel (interface) +// ============================================================== + +/** + * \brief Visual representation of a vessel object. + * + * The vVessel instance is not persistent: It is created when the + * object moves into visual range of the camera, and is destroyed + * when it moves out of visual range. + * + * The vVessel contains a set of meshes and animations. At each + * time step, it synchronises the visual with the logical animation + * state, and renders the resulting meshes. + */ + +class vVessel: public vObject { +public: + friend class vkClient; + + enum Errors { NoVC, ITEMS }; // ITEMS must be last entry + + enum class SMI { VC, Visual, Mesh }; + + struct Render { + static const int VC = 0x1; // Camera in VC view + static const int D2 = 0x2; // Camera in 2D panel view + static const int IP = 0x4; // Internal pass + }; + + vVessel* vRoot = nullptr; + + /** + * \brief Creates a new vessel visual for a scene + * \param _hObj vessel object handle + * \param scene scene to which the visual is added + */ + vVessel (OBJHANDLE _hObj, const Scene *scene); + + ~vVessel (); + + /** + * \brief Set up global parameters shared by all instances + * \param gclient client instance pointer + */ + static void GlobalInit (oapi::vkClient *gc); + + /** + * \brief Release global parameters + */ + static void GlobalExit (); + + void clbkEvent(DWORD evnt, DWORD_PTR context); + + vkMesh* GetMesh (UINT idx); + DWORD GetMeshVisMode(UINT idx); + bool GetMinMaxDistance(float *zmin, float *zmax, float *dmin); + int GetMatrixTransform(gcCore::MatrixId matrix_id, DWORD mesh, DWORD group, FMATRIX4 *pMat); + int SetMatrixTransform(gcCore::MatrixId matrix_id, DWORD mesh, DWORD group, const FMATRIX4 *pMat); + bool GetVCPos(FVECTOR3* cpos, FVECTOR3* lpos, float* rad); + bool GetMeshPosition(int idx, FVECTOR3* cpos, FVECTOR3* lpos, float* rad); + void BakeLights(ImageProcessing* pBaker); + void ErrorOnce(Errors e); + void UpdateBoundingBox(); + + // Shadow Map Methods + bool IsInsideShadows(const SMapInput* shd); + bool IntersectShadowVolume(const SMapInput* shd); + bool IntersectShadowTarget(const SMapInput* shd); + void GetMinMaxLightDist(const SMapInput* shd, float* mind, float* maxd); + bool GetSMapRenderData(SMI type, int idx, SMapInput* sm); + + void ReloadTextures(); + + inline DWORD GetMeshCount(); + + void PreInitObject(); + + VESSEL *GetInterface() { return vessel; } + + /** + * \brief Per-frame object parameter updates + * \return \e true if update was performed, \e false if skipped. + * \action + * - Calls vObject::Update + * - Calls \ref UpdateAnimations + */ + bool Update (bool bMainScene); + + /** + * \brief Object render call + * \param dev render device + * \return \e true if render operation was performed (object active), + * \e false if skipped (object inactive) + * \action Calls Render(dev,false), i.e. performs the external render pass. + * \sa Render(LPDIRECT3DDEVICE9,bool) + */ + bool Render (LPDIRECT3DDEVICE9 dev); + + /** + * \brief Object render call + * \param dev render device + * \param internalpass flag for internal render pass + * \note This method renders either the external vessel meshes + * (internalpass=false) or internal meshes (internalpass=true), e.g. + * the virtual cockpit. + * \note The internal pass is only performed on the focus object, and only + * in cockpit camera mode. + * \sa Render(LPDIRECT3DDEVICE9) + */ + bool Render (LPDIRECT3DDEVICE9 dev, const SHADOWMAP *sm, DWORD flags); + bool Render(LPDIRECT3DDEVICE9 dev, bool internalpass, const SHADOWMAP* shd); + + bool RenderExhaust(); + + /** + * \brief Render the vessel's active light beacons + * \param dev render device + */ + void RenderLightCone (FMATRIX4* pWT); + void RenderBeacons (LPDIRECT3DDEVICE9 dev); + void RenderReentry (LPDIRECT3DDEVICE9 dev); + void RenderGrapplePoints (LPDIRECT3DDEVICE9 dev); + void RenderGroundShadow (LPDIRECT3DDEVICE9 dev, OBJHANDLE hPlanet, float depth); + void RenderVectors (LPDIRECT3DDEVICE9 dev, vkPad *pSkp); + void RenderClickZones(); + bool RenderENVMap (LPDIRECT3DDEVICE9 pDev, ENVCAMREC* ec, DWORD cnt = 2, DWORD flags = 0xFF); + bool RenderInteriorENVMap(LPDIRECT3DDEVICE9 pDev, ENVCAMREC* ec, SHADOWMAP* sm); + bool ProcessEnvMaps(LPDIRECT3DDEVICE9 pDev, DWORD cnt, DWORD flags); + + ENVCAMREC* GetExteriorEnvMap(); + ENVCAMREC* CreateEnvCam(EnvCamType ec, int idx = -1); + ENVCAMREC* GetEnvCam(EnvCamType ec, int idx = -1); + bool GetInteriorCams(std::list* pCams); + bool HasOwnEnvCam(EnvCamType ec); + + + bool IsRoot() const; + vVessel* GetRoot() const { return vRoot; } + + float GetExhaustLength() const { return ExhaustLength; } + + vkPick Pick(const FVECTOR3 *vDir, const PickProp* p); + + bool HasExtPass(); + bool HasShadow(); + bool const Playback() const { return vessel->Playback(); } + class MatMgr * GetMaterialManager() const { return pMatMgr; } + + /** + * \brief Update animations of the visual + * + * Synchronises the visual animation states with the logical + * animation states of the vessel object. + * \param mshidx mesh index + * \note If mshidx == (UINT)-1 (default), all meshes are updated. + */ + void UpdateAnimations(int mshidx = -1); + + void SetVisualProperty(VisualProp prp, int idx, const type_info& t, const void* val); + bool GetVisualProperty(VisualProp prp, int idx, const type_info& t, void* val); + + +protected: + + void LoadMeshes(); + void InsertMesh(UINT idx); + void DisposeMeshes(); + void DelMesh(UINT idx); + void ResetMesh(UINT idx); + void InitNewAnimation(UINT idx); + void DisposeAnimations(); + void DelAnimation(UINT idx); + void ResetAnimations(UINT reset = 1); + + /** + * \brief Grow \ref animstate buffer (if needed) + * + * Increases the size of \ref animstate buffer (if needed). + * This method also updates the \ref nanim member. + * \param newSize new size of buffer (return value of VESSEL::GetAnimPtr()) + * \return previous value of \ref nanim + */ + void GrowAnimstateBuffer (UINT newSize); + + /** + * \brief Modify local lighting due to planet shadow or + * atmospheric dispersion. + * \param light pointer to vkLight structure receiving modified parameters + * \return \e true if lighting modifications should be applied, \e false + * if global lighting conditions apply. + */ + bool ModLighting(); + + void Animate (UINT an, UINT mshidx); + void AnimateComponent (ANIMATIONCOMP *comp, const FMATRIX4 &T); + void RestoreDefaultState(ANIMATIONCOMP *AC); + void StoreDefaultState(ANIMATIONCOMP *AC); + void DeleteDefaultState(ANIMATIONCOMP *AC); + + +private: + + // Animation database containing 'default' states. + // + std::map defstate; + std::unordered_set applyanim; + std::map currentstate; + ENVCAMREC* InteriorCams[MAX_INTCAM] = { nullptr }; + + // Default eCam configurations + ENVCAMREC ecDefExt; + FVECTOR3 BakedLightsControl[16]; + FVECTOR3 VCAmbient; + bool bMustRebake; + + VESSEL *vessel; // access instance for the vessel + class MatMgr *pMatMgr; + + struct MESHREC { + vkMesh *mesh; // DX9 mesh representation + FMATRIX4 *trans; // mesh transformation matrix (rel. to vessel frame) + WORD vismode; + } *meshlist; // list of associated meshes + + UINT nmesh; // number of meshes + UINT vClass; + ANIMATION *anim; // list of animations (defined in the vessel object) + double tCheckLight; // time for next lighting check + float ExhaustLength; + + static class SurfNative *defreentrytex, *defexhausttex; + static class ShaderClass* pRenderZone; +}; + +#endif // !__VVESSEL_H diff --git a/OVP/VulkanClient/VectorHelpers.h b/OVP/VulkanClient/VectorHelpers.h new file mode 100644 index 000000000..4e2b8d55a --- /dev/null +++ b/OVP/VulkanClient/VectorHelpers.h @@ -0,0 +1,226 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2013-2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + + +#include "OrbiterAPI.h" +#include + + +#ifndef __VECTORHELPERS_H +#define __VECTORHELPERS_H + +#if defined(_MSC_VER) && (_MSC_VER >= 1900 ) // Microsoft Visual Studio Version 2015 and higher + // exp2() and log2() are defined in +#define _constexpr_ constexpr + +#else // older MSVC++ versions + +inline double exp2(double d) +{ + return exp(d*0.69314718055994530941723212145818); +} + +inline float exp2(float d) +{ + return exp(d*0.693147180559945f); +} + +inline double log2(double d) +{ + return log(d*1.4426950408889634073599246810019); +} +inline float log2(float d) +{ + return log(d*1.442695040888963f); +} + +inline double log1p(double d) +{ + return log(d+1.0); +} +// inline float log1p(float d) +// { +// return log(d+1.0f); +// } +#define _constexpr_ +#endif + +template inline _constexpr_ T sign (T val) +{ + return val < T(0) ? T(-1) : T(1); +} + + +template inline _constexpr_ T clamp(T x, T a, T b) +{ + return x > b ? b + : x < a ? a + : x; +} + +template inline _constexpr_ T ilerp(T a, T b, T x) +{ + return saturate((x - a) / (b - a)); +} + + + +// VECTOR3 Helpers ================================================================== +// +inline VECTOR3 &operator+= (VECTOR3 &v, double d) +{ + v.x+=d; v.y+=d; v.z+=d; + return v; +} + +inline VECTOR3 &operator-= (VECTOR3 &v, double d) +{ + v.x-=d; v.y-=d; v.z-=d; + return v; +} + +inline VECTOR3 operator+ (const VECTOR3 &v, double d) +{ + return _V(v.x+d, v.y+d, v.z+d); +} + +inline VECTOR3 operator- (const VECTOR3 &v, double d) +{ + return _V(v.x-d, v.y-d, v.z-d); +} + +inline VECTOR3 operator- (double d, const VECTOR3 &v) +{ + return _V(d-v.x, d-v.y, d-v.z); +} + +inline VECTOR3 exp2(const VECTOR3 &v) +{ + return _V(exp2(v.x), exp2(v.y), exp2(v.z)); +} + +inline VECTOR3 rcp(const VECTOR3 &v) +{ + return _V(1.0/v.x, 1.0/v.y, 1.0/v.z); +} + +inline VECTOR3 vmax(const VECTOR3 &v, const VECTOR3 &w) +{ + return _V(std::max(v.x, w.x), std::max(v.y, w.y), std::max(v.z, w.z)); +} + +inline VECTOR3 vmin(const VECTOR3 &v, const VECTOR3 &w) +{ + return _V(std::min(v.x, w.x), std::min(v.y, w.y), std::min(v.z, w.z)); +} + + +// VECTOR4 Helpers ================================================================== +// +// +inline VECTOR4 &operator+= (VECTOR4 &v, double d) +{ + v.x+=d; v.y+=d; v.z+=d; v.w+=d; + return v; +} + +inline VECTOR4 &operator-= (VECTOR4 &v, double d) +{ + v.x-=d; v.y-=d; v.z-=d; v.w-=d; + return v; +} + +inline VECTOR4 operator+ (const VECTOR4 &v, double d) +{ + return _V(v.x+d, v.y+d, v.z+d, v.w+d); +} + +inline VECTOR4 operator- (const VECTOR4 &v, double d) +{ + return _V(v.x-d, v.y-d, v.z-d, v.w-d); +} + +inline VECTOR4 operator- (double d, const VECTOR4 &v) +{ + return _V(d-v.x, d-v.y, d-v.z, d-v.w); +} + +inline VECTOR4 &operator*= (VECTOR4 &v, double d) +{ + v.x*=d; v.y*=d; v.z*=d; v.w*=d; + return v; +} + +inline VECTOR4 &operator/= (VECTOR4 &v, double d) +{ + d=1.0/d; v.x*=d; v.y*=d; v.z*=d; v.w*=d; + return v; +} + +inline VECTOR4 operator* (const VECTOR4 &v, double d) +{ + return _V(v.x*d, v.y*d, v.z*d, v.w*d); +} + +inline VECTOR4 operator/ (const VECTOR4 &v, double d) +{ + d=1.0/d; return _V(v.x*d, v.y*d, v.z*d, v.w*d); +} + +inline double dotp(const VECTOR4 &v, const VECTOR4 &w) +{ + return v.x*w.x + v.y*w.y + v.z*w.z + v.w*w.w; +} + +inline double length(const VECTOR4 &v) +{ + return sqrt(dotp(v,v)); +} + +inline VECTOR4 normalize(const VECTOR4 &v) +{ + return v*(1.0/sqrt(dotp(v,v))); +} + +inline VECTOR4 exp2(const VECTOR4 &v) +{ + return _V(exp2(v.x), exp2(v.y), exp2(v.z), exp2(v.w)); +} + +inline VECTOR4 vmax(const VECTOR4 &v, const VECTOR4 &w) +{ + return _V(std::max(v.x, w.x), std::max(v.y, w.y), std::max(v.z, w.z), std::max(v.w, w.w)); +} + +inline VECTOR4 vmin(const VECTOR4 &v, const VECTOR4 &w) +{ + return _V(std::min(v.x, w.x), std::min(v.y, w.y), std::min(v.z, w.z), std::min(v.w, w.w)); +} + +inline float MaxRGB(const FVECTOR3& v) +{ + return (std::max)(v.r, (std::max)(v.g, v.b)); +} + +inline float MaxRGB(const FVECTOR4& v) +{ + return (std::max)(v.r, (std::max)(v.g, v.b)); +} + +#endif + diff --git a/OVP/VulkanClient/VideoTab.cpp b/OVP/VulkanClient/VideoTab.cpp new file mode 100644 index 000000000..c5ba3aa16 --- /dev/null +++ b/OVP/VulkanClient/VideoTab.cpp @@ -0,0 +1,1136 @@ +// ============================================================== +// Class VideoTab (implementation) +// Manages the user selections in the "Video" tab of the Orbiter +// Launchpad dialog. +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2010-2016 Jarmo Nikkanen (vkClient implementation) +// ============================================================== + +#include "Client.h" +#include "VideoTab.h" +#include "resource.h" +#include "resource_video.h" +#include "VideoTab.h" +#include "AABBUtil.h" +#include "Config.h" +#include "Commctrl.h" +#include "Junction.h" +#include "OapiExtension.h" +#include +#include +#include + +using namespace oapi; + +const UINT IDC_SCENARIO_TREE = (oapiGetOrbiterVersion() >= 111105) ? 1090 : 1088; + +BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) +{ + if (GetDlgItem(hwnd, IDC_SCENARIO_TREE)) { + *(HWND*)lParam = hwnd; + return false; + } + return true; +} + + +// ============================================================== +// Constructor + +VideoTab::VideoTab(vkClient *gc, HINSTANCE _hInst, HINSTANCE _hOrbiterInst, HWND hVideoTab) +{ + gclient = gc; + hInst = _hInst; + hOrbiterInst = _hOrbiterInst; + hTab = hVideoTab; + aspect_idx = 0; + SelectedAdapterIdx = 0; +} + +VideoTab::~VideoTab() +{ + +} + +// ============================================================== +// Dialog message handler + +BOOL VideoTab::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + GraphicsClient::VIDEODATA *data = gclient->GetVideoData(); + + switch (uMsg) { + + case WM_INITDIALOG: + { + return TRUE; + } + + case WM_COMMAND: + + switch (LOWORD(wParam)) { + + case IDC_VID_DEVICE: + if (HIWORD(wParam)==CBN_SELCHANGE) { + DWORD idx = DWORD(SendDlgItemMessage(hWnd, IDC_VID_DEVICE, CB_GETCURSEL, 0, 0)); + SelectAdapter(idx); + return TRUE; + } + break; + + case IDC_VID_MODE: + if (HIWORD(wParam) == CBN_SELCHANGE) { + DWORD idx = DWORD(SendDlgItemMessage (hWnd, IDC_VID_MODE, CB_GETCURSEL, 0, 0)); + SelectMode(idx); + return TRUE; + } + break; + + case IDC_VID_BPP: + if (HIWORD(wParam) == CBN_SELCHANGE) { + SelectFullscreen(data->fullscreen); + return TRUE; + } + + + case IDC_VID_FULL: + if (HIWORD(wParam) == BN_CLICKED) { + SelectFullscreen(true); + data->fullscreen = true; + return TRUE; + } + break; + + case IDC_VID_WINDOW: + if (HIWORD(wParam) == BN_CLICKED) { + SelectFullscreen(false); + data->fullscreen = false; + return TRUE; + } + break; + + case IDC_VID_WIDTH: + if (HIWORD(wParam) == EN_CHANGE) { + SelectWidth (); + return TRUE; + } + break; + + case IDC_VID_HEIGHT: + if (HIWORD(wParam) == EN_CHANGE) { + SelectHeight (); + return TRUE; + } + break; + + case IDC_VID_STENCIL: + return TRUE; + break; + + case IDC_VID_ASPECT: + if (HIWORD(wParam) == BN_CLICKED) { + SelectWidth(); + return TRUE; + } + break; + + case IDC_VID_4X3: + case IDC_VID_16X10: + case IDC_VID_16X9: + if (HIWORD(wParam) == BN_CLICKED) { + aspect_idx = LOWORD(wParam) - IDC_VID_4X3; + SelectWidth(); + return TRUE; + } + break; + + case IDC_VID_INFO: + DialogBoxParamA(hInst, MAKEINTRESOURCE(IDD_vkSETUP), hTab, SetupDlgProcWrp, (LPARAM)this); + return TRUE; + } + break; + } + return FALSE; +} + +// ============================================================== +// Initialise the Launchpad "video" tab + +bool VideoTab::Initialise() +{ + D3DDISPLAYMODE mode, curMode; + D3DADAPTER_IDENTIFIER9 info; + + GraphicsClient::VIDEODATA *data = gclient->GetVideoData(); + + data->forceenum = false; + data->trystencil = false; + + SendDlgItemMessage(hTab, IDC_VID_DEVICE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessage(hTab, IDC_VID_MODE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessage(hTab, IDC_VID_BPP, CB_RESETCONTENT, 0, 0); + + ScanAtmoCfgs(); + + char cbuf[32]; + int nAdapter = g_pD3DObject->GetAdapterCount(); + + if (nAdapter == 0) { + LogErr("VideoTab::Initialize() No DirectX9 Adapters Found"); + FailedDeviceError(); + return false; + } + + if (data->deviceidx < 0 || (data->deviceidx)>=nAdapter) data->deviceidx = 0; + + for (int i=0;iGetAdapterIdentifier(i, 0, &info)); + LogAlw("Adapter %d: %s", i, info.Description); + SendDlgItemMessageA(hTab, IDC_VID_DEVICE, CB_ADDSTRING, 0, (LPARAM)info.Description); + } + + SendDlgItemMessage(hTab, IDC_VID_DEVICE, CB_SETCURSEL, data->deviceidx, 0); + + + HR(g_pD3DObject->GetAdapterDisplayMode(data->deviceidx, &curMode)); + + LogAlw("Current Mode W=%u, H=%u", curMode.Width, curMode.Height); + + UINT nModes = g_pD3DObject->GetAdapterModeCount(data->deviceidx, D3DFMT_X8R8G8B8); + + if (nModes == 0) { + LogErr("VideoTab::Initialize() No Display Modes Available"); + FailedDeviceError(); + } + + for (UINT k=0;kEnumAdapterModes(data->deviceidx, D3DFMT_X8R8G8B8, k, &mode)); + sprintf_s(cbuf,32,"%u x %u %uHz", mode.Width, mode.Height, mode.RefreshRate); + LogAlw("Index:%u %u x %u %uHz (%u)", k, mode.Width, mode.Height, mode.RefreshRate, mode.Format); + SendDlgItemMessageA(hTab, IDC_VID_MODE, CB_ADDSTRING, 0, (LPARAM)cbuf); + SendDlgItemMessageA(hTab, IDC_VID_MODE, CB_SETITEMDATA, k, (LPARAM)(mode.Height<<16 | mode.Width)); + } + + SendDlgItemMessageA(hTab, IDC_VID_BPP, CB_ADDSTRING, 0, (LPARAM)"True Full Screen (no alt-tab)"); + SendDlgItemMessageA(hTab, IDC_VID_BPP, CB_ADDSTRING, 0, (LPARAM)"Full Screen Window"); + SendDlgItemMessageA(hTab, IDC_VID_BPP, CB_ADDSTRING, 0, (LPARAM)"Window with Taskbar"); + SendDlgItemMessageA(hTab, IDC_VID_BPP, CB_SETCURSEL, data->style, 0); + + //SetWindowText(GetDlgItem(hTab, IDC_VID_STATIC5), "Resolution"); + SetWindowText(GetDlgItem(hTab, IDC_VID_STATIC6), "Full Screen Mode"); + + + SendDlgItemMessage(hTab, IDC_VID_MODE, CB_SETCURSEL, data->modeidx, 0); + SendDlgItemMessage(hTab, IDC_VID_VSYNC, BM_SETCHECK, data->novsync ? BST_CHECKED : BST_UNCHECKED, 0); + + SetWindowText(GetDlgItem(hTab, IDC_VID_WIDTH), std::to_string(data->winw).c_str()); + SetWindowText(GetDlgItem(hTab, IDC_VID_HEIGHT), std::to_string(data->winh).c_str()); + + aspect_idx = 0; + + if (data->winw == (4*data->winh)/3 || data->winh == (3*data->winw)/4) aspect_idx = 1; + else if (data->winw == (16*data->winh)/10 || data->winh == (10*data->winw)/16) aspect_idx = 2; + else if (data->winw == (16*data->winh)/9 || data->winh == (9*data->winw)/16) aspect_idx = 3; + + SendDlgItemMessage(hTab, IDC_VID_ASPECT, BM_SETCHECK, aspect_idx ? BST_CHECKED : BST_UNCHECKED, 0); + if (aspect_idx) aspect_idx--; + SendDlgItemMessage(hTab, IDC_VID_4X3+aspect_idx, BM_SETCHECK, BST_CHECKED, 0); + + SendDlgItemMessage(hTab, IDC_VID_STENCIL, BM_SETCHECK, data->trystencil, 0); // GDI Compatibility mode + SendDlgItemMessage(hTab, IDC_VID_ENUM, BM_SETCHECK, data->forceenum, 0); + SendDlgItemMessage(hTab, IDC_VID_PAGEFLIP, BM_SETCHECK, data->pageflip, 0); // Full scrren Window + + bool bRet = SelectAdapter(data->deviceidx); + + SelectFullscreen(data->fullscreen); + + ShowWindow (GetDlgItem (hTab, IDC_VID_INFO), SW_SHOW); + + SetWindowText(GetDlgItem(hTab, IDC_VID_INFO), "Advanced"); + + return bRet; +} + + +// ============================================================== +// +void VideoTab::SelectMode(DWORD index) +{ + GraphicsClient::VIDEODATA *data = gclient->GetVideoData(); + SendDlgItemMessage(hTab, IDC_VID_MODE, CB_GETITEMDATA, index, 0); + data->modeidx = index; +} + + +// ============================================================== +// Respond to user adapter selection +// +bool VideoTab::SelectAdapter(DWORD index) +{ + + SelectedAdapterIdx = index; + + GraphicsClient::VIDEODATA *data = gclient->GetVideoData(); + + if (g_pD3DObject == NULL) { + LogErr("VideoTab::SelectAdapter(%u) Direct3DCreate9 Failed", index); + return false; + } + else { + + char cbuf[32]; + D3DDISPLAYMODE mode, curMode; + + if (g_pD3DObject->GetAdapterCount()<=index) { + LogErr("Adapter Index out of range"); + return false; + } + + HR(g_pD3DObject->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &curMode)); + + SendDlgItemMessage(hTab, IDC_VID_MODE, CB_RESETCONTENT, 0, 0); + + DWORD nModes = g_pD3DObject->GetAdapterModeCount(index, D3DFMT_X8R8G8B8); + + if (nModes == 0) { + LogErr("VideoTab::SelectAdapter() No Display Modes Available"); + } + + for (DWORD k=0;kEnumAdapterModes(index, D3DFMT_X8R8G8B8, k, &mode)); + sprintf_s(cbuf,32,"%u x %u %uHz", mode.Width, mode.Height, mode.RefreshRate); + SendDlgItemMessageA(hTab, IDC_VID_MODE, CB_ADDSTRING, 0, (LPARAM)cbuf); + SendDlgItemMessageA(hTab, IDC_VID_MODE, CB_SETITEMDATA, k, (LPARAM)(mode.Height<<16 | mode.Width)); + } + + SendDlgItemMessage(hTab, IDC_VID_MODE, CB_SETCURSEL, data->modeidx, 0); + } + + return true; +} + + + +void VideoTab::SelectFullscreen(bool bFull) +{ + + SetWindowText(GetDlgItem(hTab, IDC_VID_ENUM), "(unused)"); + SetWindowText(GetDlgItem(hTab, IDC_VID_STENCIL), "Force window size"); + SetWindowText(GetDlgItem(hTab, IDC_VID_PAGEFLIP), "Multiple displays"); + + SendDlgItemMessage(hTab, IDC_VID_FULL, BM_SETCHECK, bFull ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(hTab, IDC_VID_WINDOW, BM_SETCHECK, bFull ? BST_UNCHECKED : BST_CHECKED, 0); + + EnableWindow(GetDlgItem(hTab, IDC_VID_ENUM), false); + EnableWindow(GetDlgItem(hTab, IDC_VID_STENCIL), true); + + if (bFull) { + EnableWindow(GetDlgItem(hTab, IDC_VID_ASPECT), false); + EnableWindow(GetDlgItem(hTab, IDC_VID_WIDTH), false); + EnableWindow(GetDlgItem(hTab, IDC_VID_HEIGHT), false); + EnableWindow(GetDlgItem(hTab, IDC_VID_4X3), false); + EnableWindow(GetDlgItem(hTab, IDC_VID_16X10), false); + EnableWindow(GetDlgItem(hTab, IDC_VID_16X9), false); + EnableWindow(GetDlgItem(hTab, IDC_VID_MODE), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_VSYNC), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_PAGEFLIP), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_BPP), true); + } + else { + EnableWindow(GetDlgItem(hTab, IDC_VID_ASPECT), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_WIDTH), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_HEIGHT), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_4X3), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_16X10), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_16X9), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_MODE), false); + EnableWindow(GetDlgItem(hTab, IDC_VID_VSYNC), true); + EnableWindow(GetDlgItem(hTab, IDC_VID_PAGEFLIP), false); + EnableWindow(GetDlgItem(hTab, IDC_VID_BPP), false); + } +} + + +static int aspect_wfac[3] = {4,16,16}; +static int aspect_hfac[3] = {3,10,9}; + + +void VideoTab::SelectWidth () +{ + if (SendDlgItemMessage (hTab, IDC_VID_ASPECT, BM_GETCHECK, 0, 0) == BST_CHECKED) { + char cbuf[32]; + int w, h, wfac = aspect_wfac[aspect_idx], hfac = aspect_hfac[aspect_idx]; + GetWindowText(GetDlgItem(hTab, IDC_VID_WIDTH), cbuf, 32); w = atoi(cbuf); + GetWindowText(GetDlgItem(hTab, IDC_VID_HEIGHT), cbuf, 32); h = atoi(cbuf); + if (w != (wfac*h)/hfac) { + h = (hfac*w)/wfac; + SetWindowText (GetDlgItem (hTab, IDC_VID_HEIGHT), std::to_string(h).c_str()); + } + } +} + +// ============================================================== +// Respond to user selection of render window height + +void VideoTab::SelectHeight () +{ + if (SendDlgItemMessage (hTab, IDC_VID_ASPECT, BM_GETCHECK, 0, 0) == BST_CHECKED) { + char cbuf[32]; + int w, h, wfac = aspect_wfac[aspect_idx], hfac = aspect_hfac[aspect_idx]; + GetWindowText(GetDlgItem(hTab, IDC_VID_WIDTH), cbuf, 32); w = atoi(cbuf); + GetWindowText(GetDlgItem(hTab, IDC_VID_HEIGHT), cbuf, 32); h = atoi(cbuf); + if (h != (hfac*w)/wfac) { + w = (wfac*h)/hfac; + SetWindowText (GetDlgItem (hTab, IDC_VID_WIDTH), std::to_string(w).c_str()); + } + } +} + +// ============================================================== +// copy dialog state back to parameter structure + +void VideoTab::UpdateConfigData() +{ + char cbuf[32]; + GraphicsClient::VIDEODATA *data = gclient->GetVideoData(); + + // device parameters + data->deviceidx = (int)SendDlgItemMessage (hTab, IDC_VID_DEVICE, CB_GETCURSEL, 0, 0); + data->modeidx = (int)SendDlgItemMessage (hTab, IDC_VID_MODE, CB_GETCURSEL, 0, 0); + data->style = SendDlgItemMessage (hTab, IDC_VID_BPP, CB_GETCURSEL, 0, 0); + data->fullscreen = (SendDlgItemMessage (hTab, IDC_VID_FULL, BM_GETCHECK, 0, 0) == BST_CHECKED); + data->novsync = (SendDlgItemMessage (hTab, IDC_VID_VSYNC, BM_GETCHECK, 0, 0) == BST_CHECKED); + data->pageflip = (SendDlgItemMessage (hTab, IDC_VID_PAGEFLIP, BM_GETCHECK, 0, 0) == BST_CHECKED); + data->trystencil = (SendDlgItemMessage (hTab, IDC_VID_STENCIL, BM_GETCHECK, 0, 0) == BST_CHECKED); + data->forceenum = (SendDlgItemMessage (hTab, IDC_VID_ENUM, BM_GETCHECK, 0, 0) == BST_CHECKED); + + GetWindowText(GetDlgItem(hTab, IDC_VID_WIDTH), cbuf, 32); data->winw = atoi(cbuf); + GetWindowText(GetDlgItem(hTab, IDC_VID_HEIGHT), cbuf, 32); data->winh = atoi(cbuf); + + + HWND hChild = NULL; + HWND hRoot = GetAncestor(hTab, GA_ROOT); + + EnumChildWindows(hRoot, EnumChildProc, (LPARAM)&hChild); + + if (hChild) { + + HWND hTree = GetDlgItem(hChild, IDC_SCENARIO_TREE); + + if (hTree==NULL) { + LogErr("FAILED to get a scenario tree control handle"); + return; + } + + HTREEITEM item = TreeView_GetSelection(hTree); + + if (item == NULL) { + LogErr("FAILED. Scenario not selected"); + return; + } + + using std::vector; + vector hNodes; + + while (item) { // [ego, parent, grandparent, ...] + hNodes.push_back( item ); + item = TreeView_GetParent(hTree, item); + } + + using std::string; + string path = OapiExtension::GetScenarioDir(); + path.erase( path.find_last_not_of( '\\' )+1 ); // trim trailing path-delimiter + + char buf[MAX_PATH]; + TVITEMA tvItem = {0}; + tvItem.mask = TVIF_TEXT | TVIF_HANDLE; + tvItem.pszText = buf; + tvItem.cchTextMax = ARRAYSIZE(buf); + + for (auto it = hNodes.crbegin(); it != hNodes.crend(); ++it) { + tvItem.hItem = *it; + TreeView_GetItem(hTree, &tvItem); + // Note: The returned text will not necessarily be stored in the + // original buffer passed by the application. + // It is possible that pszText will point to text in a + // new buffer rather than place it in the old buffer. + path += "\\"; path += tvItem.pszText; + } + path += ".scn"; + + gclient->SetScenarioName(path); + + LogAlw("Scenario = %s", path.c_str()); + } + else { + LogErr("FAILED to get a handle of a scenario dialog"); + } +} + + + + + +// *************************************************************************************************** +// Advanced setup Dialog +// *************************************************************************************************** + + +INT_PTR CALLBACK VideoTab::SetupDlgProcWrp(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static class VideoTab *VTab = NULL; + switch (uMsg) { + case WM_INITDIALOG: + VTab = (class VideoTab *)lParam; + VTab->InitSetupDialog(hWnd); + return true; + + case WM_COMMAND: + case WM_HSCROLL: + if (VTab) VTab->SetupDlgProc(hWnd, uMsg, wParam, lParam); + break; + } + return false; +} + + + +INT_PTR CALLBACK VideoTab::SetupDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + + if (uMsg==WM_HSCROLL) { + if (LOWORD(wParam)==TB_THUMBTRACK) { + char lbl[32]; + WORD pos = HIWORD(wParam); + if (HWND(lParam)==GetDlgItem(hWnd, IDC_CONVERGENCE)) { + sprintf_s(lbl,32,"%1.2fm",float(pos)*0.01); + SetWindowTextA(GetDlgItem(hWnd, IDC_CONV_DSP), lbl); + } + if (HWND(lParam)==GetDlgItem(hWnd, IDC_SEPARATION)) { + sprintf_s(lbl,32,"%1.0f%%",float(pos)); + SetWindowTextA(GetDlgItem(hWnd, IDC_SEPA_DSP), lbl); + } + } + return false; + } + + switch (LOWORD(wParam)) { + + case IDC_MESH_DEBUGGER: + MessageBoxA(hWnd,"You must restart launchpad for changes to take effect","Notification",MB_OK); + break; + + case IDC_SYMBOLIC: + CreateSymbolicLinks(); + break; + + case IDC_CREDITS: + LoadLibrary("riched20.dll"); + DialogBoxParamA(hInst, MAKEINTRESOURCEA(IDD_vkCREDITS), hWnd, CreditsDlgProcWrp, (LPARAM)this); + break; + + case IDC_SRFPRELOAD: + SendDlgItemMessageA(hWnd, IDC_DEMAND, BM_SETCHECK, BST_UNCHECKED, 0); + break; + + case IDC_DEMAND: + SendDlgItemMessageA(hWnd, IDC_SRFPRELOAD, BM_SETCHECK, BST_UNCHECKED, 0); + break; + + case IDOK: + case IDCANCEL: + SaveSetupState(hWnd); + EndDialog (hWnd, 0); + break; + } + + return false; +} + + + + + +void VideoTab::InitSetupDialog(HWND hWnd) +{ + + char cbuf[32]; + DWORD aamax = 0; + D3DCAPS9 caps; + + if (g_pD3DObject == NULL) { + LogErr("VideoTab::SelectAdapter(%u) Direct3DCreate9 Failed", SelectedAdapterIdx); + return; + } + + g_pD3DObject->GetDeviceCaps(SelectedAdapterIdx, D3DDEVTYPE_HAL, &caps); + + if (g_pD3DObject->CheckDeviceMultiSampleType(SelectedAdapterIdx, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, true, D3DMULTISAMPLE_2_SAMPLES, NULL)==S_OK) aamax=2; + if (g_pD3DObject->CheckDeviceMultiSampleType(SelectedAdapterIdx, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, true, D3DMULTISAMPLE_4_SAMPLES, NULL)==S_OK) aamax=4; + if (g_pD3DObject->CheckDeviceMultiSampleType(SelectedAdapterIdx, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, true, D3DMULTISAMPLE_8_SAMPLES, NULL)==S_OK) aamax=8; + + LogAlw("InitSetupDialog() Enum Device AA capability = %u",aamax); + + + // AA ----------------------------------------- + + SendDlgItemMessage(hWnd, IDC_AA, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_AA, CB_ADDSTRING, 0, (LPARAM)"None"); + if (aamax>=2) SendDlgItemMessageA(hWnd, IDC_AA, CB_ADDSTRING, 0, (LPARAM)"2x"); + if (aamax>=4) SendDlgItemMessageA(hWnd, IDC_AA, CB_ADDSTRING, 0, (LPARAM)"4x"); + if (aamax>=8) SendDlgItemMessageA(hWnd, IDC_AA, CB_ADDSTRING, 0, (LPARAM)"8x"); + + + // AF ----------------------------------------- + + SendDlgItemMessage(hWnd, IDC_AF, CB_RESETCONTENT, 0, 0); + if (caps.MaxAnisotropy>=2) SendDlgItemMessageA(hWnd, IDC_AF, CB_ADDSTRING, 0, (LPARAM)"2x"); + if (caps.MaxAnisotropy>=4) SendDlgItemMessageA(hWnd, IDC_AF, CB_ADDSTRING, 0, (LPARAM)"4x"); + if (caps.MaxAnisotropy>=8) SendDlgItemMessageA(hWnd, IDC_AF, CB_ADDSTRING, 0, (LPARAM)"8x"); + if (caps.MaxAnisotropy>=12) SendDlgItemMessageA(hWnd, IDC_AF, CB_ADDSTRING, 0, (LPARAM)"12x"); + if (caps.MaxAnisotropy>=16) SendDlgItemMessageA(hWnd, IDC_AF, CB_ADDSTRING, 0, (LPARAM)"16x"); + + + // DEBUG -------------------------------------- + + SendDlgItemMessage(hWnd, IDC_DEBUG, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_DEBUG, CB_ADDSTRING, 0, (LPARAM)"0"); + SendDlgItemMessageA(hWnd, IDC_DEBUG, CB_ADDSTRING, 0, (LPARAM)"1"); + SendDlgItemMessageA(hWnd, IDC_DEBUG, CB_ADDSTRING, 0, (LPARAM)"2"); + SendDlgItemMessageA(hWnd, IDC_DEBUG, CB_ADDSTRING, 0, (LPARAM)"3"); + SendDlgItemMessageA(hWnd, IDC_DEBUG, CB_ADDSTRING, 0, (LPARAM)"4"); + + // SKETCHPAD -------------------------------------- + + SendDlgItemMessage(hWnd, IDC_FONT, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_FONT, CB_ADDSTRING, 0, (LPARAM)"Crisp"); + SendDlgItemMessageA(hWnd, IDC_FONT, CB_ADDSTRING, 0, (LPARAM)"Antialiased"); + SendDlgItemMessageA(hWnd, IDC_FONT, CB_ADDSTRING, 0, (LPARAM)"Cleartype"); + + // ENVMAP MODE -------------------------------------- + + SendDlgItemMessage(hWnd, IDC_ENVMODE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_ENVMODE, CB_ADDSTRING, 0, (LPARAM)"Disable (Debug)"); + SendDlgItemMessageA(hWnd, IDC_ENVMODE, CB_ADDSTRING, 0, (LPARAM)"Planet Only"); + SendDlgItemMessageA(hWnd, IDC_ENVMODE, CB_ADDSTRING, 0, (LPARAM)"Full Scene"); + SendDlgItemMessage(hWnd, IDC_ENVMODE, CB_SETCURSEL, 0, 0); + + // CUSTOM CAMERA MODE -------------------------------------- + + SendDlgItemMessage(hWnd, IDC_CAMMODE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_CAMMODE, CB_ADDSTRING, 0, (LPARAM)"Disable"); + SendDlgItemMessageA(hWnd, IDC_CAMMODE, CB_ADDSTRING, 0, (LPARAM)"Enabled"); + SendDlgItemMessage(hWnd, IDC_ENVMODE, CB_SETCURSEL, 0, 0); + + // ENVMAP FACES -------------------------------------- + + SendDlgItemMessage(hWnd, IDC_ENVFACES, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_ENVFACES, CB_ADDSTRING, 0, (LPARAM)"Light"); + SendDlgItemMessageA(hWnd, IDC_ENVFACES, CB_ADDSTRING, 0, (LPARAM)"Medimum"); + SendDlgItemMessageA(hWnd, IDC_ENVFACES, CB_ADDSTRING, 0, (LPARAM)"Heavy"); + SendDlgItemMessage(hWnd, IDC_ENVFACES, CB_SETCURSEL, 0, 0); + + // TEXTURE MIPMAP POLICY -------------------------------------- + + SendDlgItemMessage(hWnd, IDC_TEXMIPS, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_TEXMIPS, CB_ADDSTRING, 0, (LPARAM)"Load as defined"); + SendDlgItemMessageA(hWnd, IDC_TEXMIPS, CB_ADDSTRING, 0, (LPARAM)"Autogen missing"); + SendDlgItemMessageA(hWnd, IDC_TEXMIPS, CB_ADDSTRING, 0, (LPARAM)"Autogen all"); + SendDlgItemMessage(hWnd, IDC_TEXMIPS, CB_SETCURSEL, 0, 0); + + // MICROTEX FILTER -------------------------------------------- + + SendDlgItemMessage(hWnd, IDC_MICROFILTER, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_MICROFILTER, CB_ADDSTRING, 0, (LPARAM)"Point (Fast/Good)"); + SendDlgItemMessageA(hWnd, IDC_MICROFILTER, CB_ADDSTRING, 0, (LPARAM)"Linear (Fast/Bad)"); + SendDlgItemMessageA(hWnd, IDC_MICROFILTER, CB_ADDSTRING, 0, (LPARAM)"Anisotropic 2x"); + SendDlgItemMessageA(hWnd, IDC_MICROFILTER, CB_ADDSTRING, 0, (LPARAM)"Anisotropic 4x (Better)"); + SendDlgItemMessageA(hWnd, IDC_MICROFILTER, CB_ADDSTRING, 0, (LPARAM)"Anisotropic 8x"); + SendDlgItemMessageA(hWnd, IDC_MICROFILTER, CB_ADDSTRING, 0, (LPARAM)"Anisotropic 16x (Slow/Best)"); + SendDlgItemMessage(hWnd, IDC_MICROFILTER, CB_SETCURSEL, 0, 0); + + // MICROTEX FILTER -------------------------------------------- + + SendDlgItemMessage(hWnd, IDC_MICROMODE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_MICROMODE, CB_ADDSTRING, 0, (LPARAM)"Disabled"); + SendDlgItemMessageA(hWnd, IDC_MICROMODE, CB_ADDSTRING, 0, (LPARAM)"Enabled"); + SendDlgItemMessage(hWnd, IDC_MICROMODE, CB_SETCURSEL, 0, 0); + + + // MICROTEX BLEND MODE ----------------------------------------- + + SendDlgItemMessage(hWnd, IDC_BLENDMODE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_BLENDMODE, CB_ADDSTRING, 0, (LPARAM)"Soft light"); + SendDlgItemMessageA(hWnd, IDC_BLENDMODE, CB_ADDSTRING, 0, (LPARAM)"Normal light"); + SendDlgItemMessageA(hWnd, IDC_BLENDMODE, CB_ADDSTRING, 0, (LPARAM)"Hard light"); + SendDlgItemMessage(hWnd, IDC_BLENDMODE, CB_SETCURSEL, 0, 0); + + // TILE MIPMAP POLICY ----------------------------------------- + + SendDlgItemMessage(hWnd, IDC_MIPMAPS, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_MIPMAPS, CB_ADDSTRING, 0, (LPARAM)"Disabled"); + SendDlgItemMessageA(hWnd, IDC_MIPMAPS, CB_ADDSTRING, 0, (LPARAM)"Enabled (slow2load)"); + SendDlgItemMessage(hWnd, IDC_MIPMAPS, CB_SETCURSEL, 0, 0); + + // ARCHIVE METHOD ------------------------------------------ + + SendDlgItemMessage(hWnd, IDC_ARCHIVE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_ARCHIVE, CB_ADDSTRING, 0, (LPARAM)"Cache only"); + SendDlgItemMessageA(hWnd, IDC_ARCHIVE, CB_ADDSTRING, 0, (LPARAM)"Archive only"); + SendDlgItemMessageA(hWnd, IDC_ARCHIVE, CB_ADDSTRING, 0, (LPARAM)"Cache & Archive"); + SendDlgItemMessage(hWnd, IDC_ARCHIVE, CB_SETCURSEL, 0, 0); + + // POSTPROCESSING METHOD ------------------------------------------ + + SendDlgItemMessage(hWnd, IDC_POSTPROCESS, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_POSTPROCESS, CB_ADDSTRING, 0, (LPARAM)"None"); + SendDlgItemMessageA(hWnd, IDC_POSTPROCESS, CB_ADDSTRING, 0, (LPARAM)"Light glow"); + SendDlgItemMessage(hWnd, IDC_POSTPROCESS, CB_SETCURSEL, 0, 0); + + // Local Lights ----------------------------------------- + + SendDlgItemMessage(hWnd, IDC_LIGHTCONFIG, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_LIGHTCONFIG, CB_ADDSTRING, 0, (LPARAM)"None"); + SendDlgItemMessageA(hWnd, IDC_LIGHTCONFIG, CB_ADDSTRING, 0, (LPARAM)"4x Partial"); + SendDlgItemMessageA(hWnd, IDC_LIGHTCONFIG, CB_ADDSTRING, 0, (LPARAM)"4x Full"); + SendDlgItemMessageA(hWnd, IDC_LIGHTCONFIG, CB_ADDSTRING, 0, (LPARAM)"8x Partial"); + SendDlgItemMessageA(hWnd, IDC_LIGHTCONFIG, CB_ADDSTRING, 0, (LPARAM)"8x Full"); + + // Shadows ----------------------------------------- + + SendDlgItemMessage(hWnd, IDC_SELFSHADOWS, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_SELFSHADOWS, CB_ADDSTRING, 0, (LPARAM)"None"); + SendDlgItemMessageA(hWnd, IDC_SELFSHADOWS, CB_ADDSTRING, 0, (LPARAM)"Focus + payload"); + SendDlgItemMessageA(hWnd, IDC_SELFSHADOWS, CB_ADDSTRING, 0, (LPARAM)"Near by objects"); + SendDlgItemMessageA(hWnd, IDC_SELFSHADOWS, CB_ADDSTRING, 0, (LPARAM)"All visible objects"); + + SendDlgItemMessage(hWnd, IDC_SHADOWFILTER, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_SHADOWFILTER, CB_ADDSTRING, 0, (LPARAM)"9 samples"); + SendDlgItemMessageA(hWnd, IDC_SHADOWFILTER, CB_ADDSTRING, 0, (LPARAM)"27 samples"); + SendDlgItemMessageA(hWnd, IDC_SHADOWFILTER, CB_ADDSTRING, 0, (LPARAM)"27s dither"); + //SendDlgItemMessageA(hWnd, IDC_SHADOWFILTER, CB_ADDSTRING, 0, (LPARAM)"40 samples"); + //SendDlgItemMessageA(hWnd, IDC_SHADOWFILTER, CB_ADDSTRING, 0, (LPARAM)"40s dither"); + + SendDlgItemMessage(hWnd, IDC_CASCOUNT, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_CASCOUNT, CB_ADDSTRING, 0, (LPARAM)"5m 1-cas low quality"); + SendDlgItemMessageA(hWnd, IDC_CASCOUNT, CB_ADDSTRING, 0, (LPARAM)"8m 2-cas hight quality"); + SendDlgItemMessageA(hWnd, IDC_CASCOUNT, CB_ADDSTRING, 0, (LPARAM)"24m 3-cas hight quality"); + + SendDlgItemMessage(hWnd, IDC_TERRAIN, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_TERRAIN, CB_ADDSTRING, 0, (LPARAM)"None"); + SendDlgItemMessageA(hWnd, IDC_TERRAIN, CB_ADDSTRING, 0, (LPARAM)"Stencil"); + SendDlgItemMessageA(hWnd, IDC_TERRAIN, CB_ADDSTRING, 0, (LPARAM)"Projected"); + + SendDlgItemMessage(hWnd, IDC_MESHRES, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_MESHRES, CB_ADDSTRING, 0, (LPARAM)"16"); + SendDlgItemMessageA(hWnd, IDC_MESHRES, CB_ADDSTRING, 0, (LPARAM)"32"); + + SendDlgItemMessage(hWnd, IDC_TILECOUNT, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_TILECOUNT, CB_ADDSTRING, 0, (LPARAM)"600"); + SendDlgItemMessageA(hWnd, IDC_TILECOUNT, CB_ADDSTRING, 0, (LPARAM)"1200"); + SendDlgItemMessageA(hWnd, IDC_TILECOUNT, CB_ADDSTRING, 0, (LPARAM)"2400"); + + // gcGUI ----------------------------------------- + if (Config->gcGUIMode == 1) Config->gcGUIMode = 0; + SendDlgItemMessage(hWnd, IDC_GUIMODE, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageA(hWnd, IDC_GUIMODE, CB_ADDSTRING, 0, (LPARAM)"Disabled"); + SendDlgItemMessageA(hWnd, IDC_GUIMODE, CB_ADDSTRING, 0, (LPARAM)"(unused)"); + SendDlgItemMessageA(hWnd, IDC_GUIMODE, CB_ADDSTRING, 0, (LPARAM)"Windowed"); + + // Earth AtmoConfig ------------------------------- + SendDlgItemMessage(hWnd, IDC_EARTHVISCFG, CB_RESETCONTENT, 0, 0); + for (auto x : AtmoCfgs["Earth"]) SendDlgItemMessageA(hWnd, IDC_EARTHVISCFG, CB_ADDSTRING, 0, (LPARAM)x.cfg.c_str()); + for (int i = 0; i < AtmoCfgs["Earth"].size(); i++) { + if (Config->AtmoCfg["Earth"] == AtmoCfgs["Earth"][i].file) { + SendDlgItemMessage(hWnd, IDC_EARTHVISCFG, CB_SETCURSEL, i, 0); + break; + } + } + + + + // Write values in controls ---------------- + + bool bFS = (SendDlgItemMessage(hTab, IDC_VID_BPP, CB_GETCURSEL, 0, 0)==0 && SendDlgItemMessage(hTab, IDC_VID_FULL, BM_GETCHECK, 0, 0)==BST_CHECKED); + bool bGB = (SendDlgItemMessage (hTab, IDC_VID_STENCIL, BM_GETCHECK, 0, 0)==BST_CHECKED); + + if (bFS || bGB) { + Config->SceneAntialias = 0; + EnableWindow(GetDlgItem(hWnd, IDC_AA), false); + } + else { + EnableWindow(GetDlgItem(hWnd, IDC_AA), true); + } + + + SendDlgItemMessage(hWnd, IDC_CONVERGENCE, TBM_SETRANGEMAX, 1, 100); + SendDlgItemMessage(hWnd, IDC_CONVERGENCE, TBM_SETRANGEMIN, 1, 5); + SendDlgItemMessage(hWnd, IDC_CONVERGENCE, TBM_SETTICFREQ, 5, 0); + + SendDlgItemMessage(hWnd, IDC_SEPARATION, TBM_SETRANGEMAX, 1, 100); + SendDlgItemMessage(hWnd, IDC_SEPARATION, TBM_SETRANGEMIN, 1, 10); + SendDlgItemMessage(hWnd, IDC_SEPARATION, TBM_SETTICFREQ, 5, 0); + + SendDlgItemMessage(hWnd, IDC_LODBIAS, TBM_SETRANGEMAX, 1, 10); + SendDlgItemMessage(hWnd, IDC_LODBIAS, TBM_SETRANGEMIN, 1, -10); + SendDlgItemMessage(hWnd, IDC_LODBIAS, TBM_SETTICFREQ, 1, 0); + + SendDlgItemMessage(hWnd, IDC_MICROBIAS, TBM_SETRANGEMAX, 1, 10); + SendDlgItemMessage(hWnd, IDC_MICROBIAS, TBM_SETRANGEMIN, 1, 0); + SendDlgItemMessage(hWnd, IDC_MICROBIAS, TBM_SETTICFREQ, 1, 0); + + + sprintf_s(cbuf,32,"%1.1fm",float(Config->Convergence)); + SetWindowTextA(GetDlgItem(hWnd, IDC_CONV_DSP), cbuf); + + sprintf_s(cbuf,32,"%1.0f%%",float(Config->Separation)); + SetWindowTextA(GetDlgItem(hWnd, IDC_SEPA_DSP), cbuf); + + SendDlgItemMessage(hWnd, IDC_CONVERGENCE, TBM_SETPOS, 1, int(Config->Convergence*100.0)); + SendDlgItemMessage(hWnd, IDC_SEPARATION, TBM_SETPOS, 1, int(Config->Separation)); + SendDlgItemMessage(hWnd, IDC_LODBIAS, TBM_SETPOS, 1, int(Config->LODBias*5.0)); + SendDlgItemMessage(hWnd, IDC_MICROBIAS, TBM_SETPOS, 1, int(Config->MicroBias)); + + SendDlgItemMessage(hWnd, IDC_TILECOUNT, CB_SETCURSEL, Config->MaxTiles, 0); + SendDlgItemMessage(hWnd, IDC_MESHRES, CB_SETCURSEL, Config->MeshRes, 0); + SendDlgItemMessage(hWnd, IDC_ARCHIVE, CB_SETCURSEL, Config->PlanetTileLoadFlags-1, 0); + SendDlgItemMessage(hWnd, IDC_BLENDMODE, CB_SETCURSEL, Config->BlendMode, 0); + SendDlgItemMessage(hWnd, IDC_MICROMODE, CB_SETCURSEL, Config->MicroMode, 0); + SendDlgItemMessage(hWnd, IDC_MICROFILTER, CB_SETCURSEL, Config->MicroFilter, 0); + SendDlgItemMessage(hWnd, IDC_TEXMIPS, CB_SETCURSEL, Config->TextureMips, 0); + SendDlgItemMessage(hWnd, IDC_ENVMODE, CB_SETCURSEL, Config->EnvMapMode, 0); + SendDlgItemMessage(hWnd, IDC_CAMMODE, CB_SETCURSEL, Config->CustomCamMode, 0); + SendDlgItemMessage(hWnd, IDC_ENVFACES, CB_SETCURSEL, Config->EnvMapFaces-1, 0); + SendDlgItemMessage(hWnd, IDC_FONT, CB_SETCURSEL, Config->SketchpadFont, 0); + SendDlgItemMessage(hWnd, IDC_DEBUG, CB_SETCURSEL, Config->DebugLvl, 0); + SendDlgItemMessage(hWnd, IDC_MIPMAPS, CB_SETCURSEL, Config->TileMipmaps, 0); + SendDlgItemMessage(hWnd, IDC_POSTPROCESS, CB_SETCURSEL, Config->PostProcess, 0); + SendDlgItemMessage(hWnd, IDC_LIGHTCONFIG, CB_SETCURSEL, Config->LightConfig, 0); + SendDlgItemMessage(hWnd, IDC_SELFSHADOWS, CB_SETCURSEL, Config->ShadowMapMode, 0); + SendDlgItemMessage(hWnd, IDC_SHADOWFILTER, CB_SETCURSEL, Config->ShadowFilter, 0); + SendDlgItemMessage(hWnd, IDC_TERRAIN, CB_SETCURSEL, Config->TerrainShadowing, 0); + SendDlgItemMessage(hWnd, IDC_GUIMODE, CB_SETCURSEL, Config->gcGUIMode, 0); + SendDlgItemMessage(hWnd, IDC_CASCOUNT, CB_SETCURSEL, Config->VCCascadeCount - 1, 0); + + SendDlgItemMessage(hWnd, IDC_DEMAND, BM_SETCHECK, Config->PlanetPreloadMode==0, 0); + SendDlgItemMessage(hWnd, IDC_SRFPRELOAD, BM_SETCHECK, Config->PlanetPreloadMode==1, 0); + SendDlgItemMessage(hWnd, IDC_GLASSSHADE, BM_SETCHECK, Config->EnableGlass==1, 0); + SendDlgItemMessage(hWnd, IDC_MESH_DEBUGGER, BM_SETCHECK, Config->EnableMeshDbg==1, 0); + SendDlgItemMessage(hWnd, IDC_CLOUDMICRO, BM_SETCHECK, Config->CloudMicro == 1, 0); + SendDlgItemMessage(hWnd, IDC_GDIOVERLAY, BM_SETCHECK, Config->GDIOverlay == 1, 0); + SendDlgItemMessage(hWnd, IDC_ABSANIM, BM_SETCHECK, Config->bAbsAnims == 1, 0); + SendDlgItemMessage(hWnd, IDC_CLOUDNORM, BM_SETCHECK, Config->bCloudNormals == 1, 0); + SendDlgItemMessage(hWnd, IDC_FLATS, BM_SETCHECK, Config->bFlats == 1, 0); + SendDlgItemMessage(hWnd, IDC_ESUNGLARE, BM_SETCHECK, Config->bGlares == 1, 0); + SendDlgItemMessage(hWnd, IDC_ELIGHTSGLARE, BM_SETCHECK, Config->bLocalGlares == 1, 0); + SendDlgItemMessage(hWnd, IDC_EIRRAD, BM_SETCHECK, Config->bIrradiance == 1, 0); + SendDlgItemMessage(hWnd, IDC_ESCACHE, BM_SETCHECK, Config->ShaderCacheUse == 1, 0); + SendDlgItemMessage(hWnd, IDC_EAQUALITY, BM_SETCHECK, Config->bAtmoQuality == 1, 0); + + + SendDlgItemMessage(hWnd, IDC_NORMALMAPS, BM_SETCHECK, Config->UseNormalMap==1, 0); + SendDlgItemMessage(hWnd, IDC_BASEVIS, BM_SETCHECK, Config->PreLBaseVis==1, 0); + SendDlgItemMessage(hWnd, IDC_NEARPLANE, BM_SETCHECK, Config->NearClipPlane==1, 0); + SendDlgItemMessage(hWnd, IDC_BREAK, BM_SETCHECK, Config->DebugBreak == 1, 0); + + sprintf_s(cbuf,32,"%d", Config->PlanetLoadFrequency); + SetWindowText(GetDlgItem(hWnd, IDC_HZ), cbuf); + + sprintf_s(cbuf,32,"%3.3f", Config->PlanetGlow); + SetWindowText(GetDlgItem(hWnd, IDC_PLANETGLOW), cbuf); + + DWORD af = min(caps.MaxAnisotropy, DWORD(Config->Anisotrophy)); + + switch(af) { + case 2: SendDlgItemMessage(hWnd, IDC_AF, CB_SETCURSEL, 0, 0); break; + default: + case 4: SendDlgItemMessage(hWnd, IDC_AF, CB_SETCURSEL, 1, 0); break; + case 8: SendDlgItemMessage(hWnd, IDC_AF, CB_SETCURSEL, 2, 0); break; + case 12: SendDlgItemMessage(hWnd, IDC_AF, CB_SETCURSEL, 3, 0); break; + case 16: SendDlgItemMessage(hWnd, IDC_AF, CB_SETCURSEL, 4, 0); break; + } + + DWORD aa = min(aamax, DWORD(Config->SceneAntialias)); + + switch(aa) { + case 0: SendDlgItemMessage(hWnd, IDC_AA, CB_SETCURSEL, 0, 0); break; + case 2: SendDlgItemMessage(hWnd, IDC_AA, CB_SETCURSEL, 1, 0); break; + default: + case 4: SendDlgItemMessage(hWnd, IDC_AA, CB_SETCURSEL, 2, 0); break; + case 8: SendDlgItemMessage(hWnd, IDC_AA, CB_SETCURSEL, 3, 0); break; + } +} + + + + +void VideoTab::SaveSetupState(HWND hWnd) +{ + char cbuf[32]; + // Combo boxes + Config->SketchpadFont = (int)SendDlgItemMessage (hWnd, IDC_FONT, CB_GETCURSEL, 0, 0); + Config->EnvMapMode = (int)SendDlgItemMessage (hWnd, IDC_ENVMODE, CB_GETCURSEL, 0, 0); + Config->CustomCamMode = (int)SendDlgItemMessage (hWnd, IDC_CAMMODE, CB_GETCURSEL, 0, 0); + Config->EnvMapFaces = (int)SendDlgItemMessage (hWnd, IDC_ENVFACES, CB_GETCURSEL, 0, 0) + 1; + Config->TextureMips = (int)SendDlgItemMessage (hWnd, IDC_TEXMIPS, CB_GETCURSEL, 0, 0); + Config->MicroMode = (int)SendDlgItemMessage (hWnd, IDC_MICROMODE, CB_GETCURSEL, 0, 0); + Config->MicroFilter = (int)SendDlgItemMessage (hWnd, IDC_MICROFILTER, CB_GETCURSEL, 0, 0); + Config->BlendMode = (int)SendDlgItemMessage (hWnd, IDC_BLENDMODE, CB_GETCURSEL, 0, 0); + Config->TileMipmaps = (int)SendDlgItemMessage (hWnd, IDC_MIPMAPS, CB_GETCURSEL, 0, 0); + Config->PostProcess = (int)SendDlgItemMessage (hWnd, IDC_POSTPROCESS, CB_GETCURSEL, 0, 0); + Config->PlanetTileLoadFlags = (int)SendDlgItemMessage (hWnd, IDC_ARCHIVE, CB_GETCURSEL, 0, 0) + 1; + Config->LightConfig = (int)SendDlgItemMessage(hWnd, IDC_LIGHTCONFIG, CB_GETCURSEL, 0, 0); + Config->ShadowMapMode = (int)SendDlgItemMessage(hWnd, IDC_SELFSHADOWS, CB_GETCURSEL, 0, 0); + Config->ShadowFilter = (int)SendDlgItemMessage(hWnd, IDC_SHADOWFILTER, CB_GETCURSEL, 0, 0); + Config->TerrainShadowing = (int)SendDlgItemMessage(hWnd, IDC_TERRAIN, CB_GETCURSEL, 0, 0); + Config->gcGUIMode = (int)SendDlgItemMessage(hWnd, IDC_GUIMODE, CB_GETCURSEL, 0, 0); + Config->VCCascadeCount = (int)SendDlgItemMessage(hWnd, IDC_CASCOUNT, CB_GETCURSEL, 0, 0) + 1; + Config->MeshRes = int(SendDlgItemMessage(hWnd, IDC_MESHRES, CB_GETCURSEL, 0, 0)); + Config->MaxTiles = int(SendDlgItemMessage(hWnd, IDC_TILECOUNT, CB_GETCURSEL, 0, 0)); + + + if (Config->gcGUIMode == 1) Config->gcGUIMode = 0; + + // Check boxes + Config->UseNormalMap = (int)SendDlgItemMessage (hWnd, IDC_NORMALMAPS, BM_GETCHECK, 0, 0); + Config->PreLBaseVis = (int)SendDlgItemMessage (hWnd, IDC_BASEVIS, BM_GETCHECK, 0, 0); + Config->NearClipPlane = (int)SendDlgItemMessage (hWnd, IDC_NEARPLANE, BM_GETCHECK, 0, 0); + Config->EnableGlass = (int)SendDlgItemMessage (hWnd, IDC_GLASSSHADE, BM_GETCHECK, 0, 0); + Config->EnableMeshDbg = (int)SendDlgItemMessage (hWnd, IDC_MESH_DEBUGGER, BM_GETCHECK, 0, 0); + Config->CloudMicro = (int)SendDlgItemMessage (hWnd, IDC_CLOUDMICRO, BM_GETCHECK, 0, 0); + Config->GDIOverlay = (int)SendDlgItemMessage (hWnd, IDC_GDIOVERLAY, BM_GETCHECK, 0, 0); + Config->bAbsAnims = (int)SendDlgItemMessage (hWnd, IDC_ABSANIM, BM_GETCHECK, 0, 0); + Config->bCloudNormals = (int)SendDlgItemMessage(hWnd, IDC_CLOUDNORM, BM_GETCHECK, 0, 0); + Config->bFlats = (int)SendDlgItemMessage(hWnd, IDC_FLATS, BM_GETCHECK, 0, 0); + Config->DebugBreak = (int)SendDlgItemMessage(hWnd, IDC_BREAK, BM_GETCHECK, 0, 0); + Config->bGlares = (int)SendDlgItemMessage(hWnd, IDC_ESUNGLARE, BM_GETCHECK, 0, 0); + Config->bLocalGlares = (int)SendDlgItemMessage(hWnd, IDC_ELIGHTSGLARE, BM_GETCHECK, 0, 0); + Config->bIrradiance = (int)SendDlgItemMessage(hWnd, IDC_EIRRAD, BM_GETCHECK, 0, 0); + Config->ShaderCacheUse= (int)SendDlgItemMessage(hWnd, IDC_ESCACHE, BM_GETCHECK, 0, 0); + Config->bAtmoQuality = (int)SendDlgItemMessage(hWnd, IDC_EAQUALITY, BM_GETCHECK, 0, 0); + + // Sliders + Config->Convergence = double(SendDlgItemMessage(hWnd, IDC_CONVERGENCE, TBM_GETPOS, 0, 0)) * 0.01; + Config->Separation = double(SendDlgItemMessage(hWnd, IDC_SEPARATION, TBM_GETPOS, 0, 0)); + Config->LODBias = 0.2 * double(SendDlgItemMessage(hWnd, IDC_LODBIAS, TBM_GETPOS, 0, 0)); + Config->MicroBias = int(SendDlgItemMessage(hWnd, IDC_MICROBIAS, TBM_GETPOS, 0, 0)); + + // Other things + GetWindowText(GetDlgItem(hWnd, IDC_HZ), cbuf, 32); + + Config->PlanetLoadFrequency = atoi(cbuf); + Config->PlanetPreloadMode = (int)SendDlgItemMessage (hWnd, IDC_SRFPRELOAD, BM_GETCHECK, 0, 0); + + GetWindowText(GetDlgItem(hWnd, IDC_PLANETGLOW), cbuf, 32); + Config->PlanetGlow = atof(cbuf); + + Config->DebugLvl = (int)SendDlgItemMessage (hWnd, IDC_DEBUG, CB_GETCURSEL, 0, 0); + + switch(SendDlgItemMessage (hWnd, IDC_AF, CB_GETCURSEL, 0, 0)) { + default: + case 0: Config->Anisotrophy = 2; break; + case 1: Config->Anisotrophy = 4; break; + case 2: Config->Anisotrophy = 8; break; + case 3: Config->Anisotrophy = 12; break; + case 4: Config->Anisotrophy = 16; break; + } + + switch(SendDlgItemMessage (hWnd, IDC_AA, CB_GETCURSEL, 0, 0)) { + default: + case 0: Config->SceneAntialias = 0; break; + case 1: Config->SceneAntialias = 2; break; + case 2: Config->SceneAntialias = 4; break; + case 3: Config->SceneAntialias = 8; break; + } + + int EASel = (int)SendDlgItemMessage(hWnd, IDC_EARTHVISCFG, CB_GETCURSEL, 0, 0); + if (!AtmoCfgs["Earth"][EASel].file.empty()) Config->AtmoCfg["Earth"] = AtmoCfgs["Earth"][EASel].file; + else Config->AtmoCfg["Earth"] = "Earth.atm.cfg"; +} + + + + + + +void VideoTab::CreateSymbolicLinks() +{ + // Ask user + // + int ret = MessageBox(NULL, "This function will create a symbolic links in /Modules/Server/ folder " + "as required by some addons like the spacecraft3.dll.\n\n" + "Do you want to proceed ?", "vkClient Configuration", MB_YESNO); + if (ret != IDYES) { + return; + } + + std::string result(""); + + // Config -> Modules/Server/Config + // + result += "Config: "; + if (junction::TargetDirectoryExists(OapiExtension::GetConfigDir())) + { + if (!junction::IsDirectoryJunction("Modules\\Server\\Config")) + { + if (!junction::CreateJunctionPoint(OapiExtension::GetConfigDir(), "Modules\\Server\\Config")) + { + result += (GetLastError() == ERROR_DIR_NOT_EMPTY) + ? "OK. A non-empty 'Config' directory already exists." + : "FAIL. Could not create link."; + } else { + result += "OK. Link created."; + } + } else { + result += "OK. Link exists."; + } + } else { + result += "FAIL. Target does not exist!"; + } + result += "\r\n"; + + // Sound -> Modules/Server/Sound + // + if (OapiExtension::RunsOrbiter2010()) + { + result += "Sound: "; + if (junction::TargetDirectoryExists("Sound")) + { + if (OapiExtension::RunsOrbiterSound40()) { + result += "OK. OrbiterSound (4.0) detected. No link necessary."; + } + else if (!junction::IsDirectoryJunction("Modules\\Server\\Sound")) + { + if (!junction::CreateJunctionPoint("Sound", "Modules\\Server\\Sound")) + { + result += (GetLastError() == ERROR_DIR_NOT_EMPTY) + ? "OK. A non-empty 'Sound' directory already exists." + : "FAIL. Could not create link."; + } + else { + result += "OK. Link created."; + } + } + else { + result += "OK. Link exists."; + } + } + else { + result += "OK. OrbiterSound not installed."; + } + result += "\r\n"; + } + + MessageBox(NULL, result.c_str(), "vkClient Configuration", MB_OK); +} + + + + + + +// *************************************************************************************************** +// Credist Dialog +// *************************************************************************************************** + +INT_PTR CALLBACK VideoTab::CreditsDlgProcWrp(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static class VideoTab *VTab = NULL; + switch (uMsg) { + case WM_INITDIALOG: + VTab = (class VideoTab *)lParam; + VTab->InitCreditsDialog(hWnd); + return true; + case WM_COMMAND: + if (VTab) VTab->CreditsDlgProc(hWnd, uMsg, wParam, lParam); + } + return false; +} + + + +INT_PTR CALLBACK VideoTab::CreditsDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + EndDialog (hWnd, 0); + break; + } + return false; +} + +void VideoTab::InitCreditsDialog(HWND hWnd) +{ + HANDLE hFile = CreateFile("Modules/vkShaders/Credits.rtf", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile==INVALID_HANDLE_VALUE) { + LogErr("Failed to open a file /Modules/vkShaders/Credits.rtf"); + return; + } + + DWORD size = GetFileSize(hFile, NULL); + char *credits = new char[size+1]; + memset(credits,0,size+1); + DWORD bytes; + + if (ReadFile(hFile, credits, size, &bytes, NULL)) { + SETTEXTEX text; + text.flags = ST_DEFAULT; + text.codepage = CP_ACP; + SendDlgItemMessageA(hWnd, IDC_CREDITSTEXT, EM_SETTEXTEX, (WPARAM)&text, (LPARAM)credits); + } + else LogErr("Failed to read a file \\Modules\\vkShaders\\Credits.rtf Error=%u",GetLastError()); + + delete []credits; + credits = NULL; + + CloseHandle(hFile); +} + +bool VideoTab::GetConfigName(const char* file, string& cfg, string& planet) +{ + string filename = "GC\\" + string(file); + FILEHANDLE hFile = oapiOpenFile(filename.c_str(), FILE_IN_ZEROONFAIL, CONFIG); + if (hFile) { + char ConfigName[32] = {}; char PlanetName[32] = {}; + bool bA = oapiReadItem_string(hFile, (char*)"ConfigName", ConfigName); + bool bB = oapiReadItem_string(hFile, (char*)"Planet", PlanetName); + oapiCloseFile(hFile, FILE_IN_ZEROONFAIL); + cfg = string(ConfigName); + planet = string(PlanetName); + return bA && bB; + } + return false; +} + +void VideoTab::ScanAtmoCfgs() +{ + _AtmoCfg cfg = { "Default", "Earth.atm.cfg"}; + AtmoCfgs["Earth"].push_back(cfg); + + WIN32_FIND_DATA FileInformation; + string name = string(OapiExtension::GetConfigDir()) + "GC\\*_atm.cfg"; + HANDLE hFile = FindFirstFileA(name.c_str(), &FileInformation); + + if (hFile != INVALID_HANDLE_VALUE) { + do { + if (FileInformation.cFileName[0] != '.') { + if (!(FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + string cfgname, planet; + if (GetConfigName(FileInformation.cFileName, cfgname, planet)) { + _AtmoCfg cfg = { cfgname, FileInformation.cFileName }; + AtmoCfgs[planet].push_back(cfg); + } + else oapiWriteLogV("File Not Found [%s]", FileInformation.cFileName); + } + } + } + while (FindNextFileA(hFile, &FileInformation) == TRUE); + FindClose(hFile); + } +} + + diff --git a/OVP/VulkanClient/VideoTab.h b/OVP/VulkanClient/VideoTab.h new file mode 100644 index 000000000..6b7887a4e --- /dev/null +++ b/OVP/VulkanClient/VideoTab.h @@ -0,0 +1,67 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2006-2016 Martin Schweiger +// 2012-2016 Jarmo Nikkanen +// ============================================================== + +#ifndef __VIDEOTAB_H +#define __VIDEOTAB_H +#include +#include + +// ============================================================== + +class VideoTab { + + struct _AtmoCfg { string cfg, file; }; +public: + VideoTab(oapi::vkClient *gc, HINSTANCE _hInst, HINSTANCE _hOrbiterInst, HWND hVideoTab); + ~VideoTab(); + + BOOL WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + // Video tab message handler + + void UpdateConfigData(); + // copy dialog state back to parameter structure + + bool Initialise(); + // Initialise dialog elements + +protected: + void SelectFullscreen(bool); + void SelectMode(DWORD index); + bool SelectAdapter(DWORD index); + // Update dialog after user device selection + + void SelectWidth(); + // Update dialog after window width selection + + void SelectHeight(); + // Update dialog after window height selection + +private: + static INT_PTR CALLBACK SetupDlgProcWrp(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + static INT_PTR CALLBACK CreditsDlgProcWrp(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + INT_PTR CALLBACK SetupDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + INT_PTR CALLBACK CreditsDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + void InitCreditsDialog(HWND hWnd); + void CreateSymbolicLinks(); + void InitSetupDialog(HWND hWnd); + void SaveSetupState(HWND hWnd); + void ScanAtmoCfgs(); + bool GetConfigName(const char* file, string& cfg, string& planet); + + oapi::vkClient *gclient; + HINSTANCE hOrbiterInst; // orbiter instance handle + HINSTANCE hInst; // module instance handle + HWND hTab; // window handle of the video tab + int aspect_idx; + DWORD SelectedAdapterIdx; + bool bHasMultiSample; + std::map> AtmoCfgs; +}; + +//}; + +#endif // !__VIDEOTAB_H diff --git a/OVP/VulkanClient/WindowMgr.cpp b/OVP/VulkanClient/WindowMgr.cpp new file mode 100644 index 000000000..06e4e795a --- /dev/null +++ b/OVP/VulkanClient/WindowMgr.cpp @@ -0,0 +1,1825 @@ + +// ================================================================================================================================= +// +// Copyright (C) 2019 Jarmo Nikkanen +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +// files (the "Software"), to use, copy, modify, merge, publish, distribute, interact with the Software and sublicense copies +// of the Software, subject to the following conditions: +// +// a) You do not sell, rent or auction the Software. +// b) You do not collect distribution fees. +// c) If the Software is distributed in an object code form, it must inform that the source code is available and how to obtain it. +// d) You do not remove or alter any copyright notices contained within the Software. +// e) This copyright notice must 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. +// ================================================================================================================================= + + +#include +#include "MathAPI.h" +#include "WindowMgr.h" +#include "windows.h" +#include "WindowsX.h" +#include "resource.h" +#include "OapiExtension.h" +#include "Config.h" +#include "Log.h" +#include "Client.h" + +#define APPNODE(x) ((Node *)x) + +extern vkClient *g_client; + +class WindowManager *g_pWM = NULL; + +list g_gcGUIAppList; + +// =============================================================================================== +// +inline bool PointInside(int x, int y, LPRECT r) +{ + if (x < r->left) return false; + if (x > r->right) return false; + if (y < r->top) return false; + if (y > r->bottom) return false; + return true; +} +// =============================================================================================== +// +inline DWORD _Colour(const FVECTOR4 *c) +{ + DWORD r = DWORD(c->r * 255.0f + 0.5f); + DWORD g = DWORD(c->g * 255.0f + 0.5f); + DWORD b = DWORD(c->b * 255.0f + 0.5f); + + if (r > 0xFF) r = 0xFF; + if (g > 0xFF) g = 0xFF; + if (b > 0xFF) b = 0xFF; + + return (b << 16) | (g << 8) | r; +} +// =============================================================================================== +// +inline FVECTOR4 _Colour(DWORD dwABGR) +{ + DWORD r = (dwABGR & 0xFF); dwABGR >>= 8; + DWORD g = (dwABGR & 0xFF); dwABGR >>= 8; + DWORD b = (dwABGR & 0xFF); dwABGR >>= 8; + FVECTOR4 c; + float q = 3.92156862e-3f; + c.r = float(r) * q; + c.g = float(g) * q; + c.b = float(b) * q; + c.a = 1.0f; + return c; +} +// =============================================================================================== +// +LRESULT CALLBACK SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (g_pWM) { + SideBar *pBar = g_pWM->GetSideBar(hWnd); + if (pBar) return pBar->SideBarWndProc(hWnd, uMsg, wParam, lParam); + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} +// =============================================================================================== +// +INT_PTR CALLBACK DummyDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_INITDIALOG: + return true; + } + return false; +} + + +// =============================================================================================== +// +INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_INITDIALOG: + return true; + } + return false; +} + + + + +// =============================================================================================== +// +void OpenTestClbk(void *context) +{ + /* + WindowManager *pWM = (WindowManager *)context; + HINSTANCE hInst = pWM->GetInstance(); + HWND hAppMainWindow = pWM->GetMainWindow(); + + + HWND hDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_MESHDEBUG), hAppMainWindow, DlgProc, 0); + HNODE hRootNode = pWM->RegisterApplication("vk Controls", NULL, 0, gcGUI::DS_LEFT); + + pWM->RegisterSubsection(hRootNode, "Mesh Debugger", hDlg); + + hDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_MATERIAL), hAppMainWindow, DlgProc, 0); + pWM->RegisterSubsection(hRootNode, "Material Config", hDlg); + + hDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SCENEDEBUG), hAppMainWindow, DlgProc, 0); + HNODE hSD = pWM->RegisterSubsection(hRootNode, "Scene Debugger", hDlg); + + hDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_MICROTEXTOOLS), hAppMainWindow, DlgProc, 0); + HNODE hTT = pWM->RegisterSubsection(hRootNode, "MicroTex Tools", hDlg); + + pWM->OpenNode(hSD, false); + pWM->OpenNode(hTT, false); + + pWM->DisplayWindow(hRootNode); + */ +} + + + + + + + + + + + + + + +// =============================================================================================== +// Node Implementation +// =============================================================================================== +// +Node::Node(SideBar *pSB, const char *label, HWND hDlg, DWORD color, Node *pP) : + pSB(pSB), pParent(pP), hBmp(NULL), hDlg(hDlg), pApp(NULL), bOpen(true), bClose(false) +{ + + memset(&bm, 0, sizeof(BITMAP)); + + WindowManager *pMgr = pSB->GetWM(); + + pSB->AddWindow(this); + + if (label) Label = _strdup(label); + else Label = NULL; + + HBITMAP hTit; + + if ((pParent == NULL) && (Config->gcGUIMode == 3) && hDlg) return; // No Title Bar + + if (pParent == NULL) hTit = pMgr->GetBitmap(gcGUI::BM_TITLE); + else { + hTit = pMgr->GetBitmap(gcGUI::BM_SUBTITLE); + pApp = pParent->pApp; + } + + FVECTOR4 clr = _Colour(color); + FVECTOR4 white = _Colour(0xFFFFFFFF); + + HDC hDC = pSB->GetDC(); + HDC hSrc = CreateCompatibleDC(hDC); + HDC hTgt = CreateCompatibleDC(hDC); + + + GetObject(hTit, sizeof(BITMAP), &bm); + + hBmp = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); + + pSB->ReleaseDC(hDC); + + SelectObject(hSrc, hTit); + SelectObject(hTgt, hBmp); + + // Recolorize the title bar + + for (int y = 0; y < bm.bmHeight; y++) { + for (int x = 0; x < bm.bmWidth; x++) { + COLORREF cr = GetPixel(hSrc, x, y); + FVECTOR4 c = _Colour(cr); + FVECTOR4 out = (clr * c.b) + (white * c.g); + SetPixel(hTgt, x, y, _Colour(&out)); + } + } + + DeleteDC(hSrc); + DeleteDC(hTgt); +} + +// =============================================================================================== +// +Node::~Node() +{ + if (hBmp) DeleteObject(hBmp); + if (Label) delete[] Label; +} + +// =============================================================================================== +// +void Node::SetApp(gcGUIApp *_pApp) +{ + pApp = _pApp; +} + +// =============================================================================================== +// +void Node::ReColorize(DWORD color) +{ + memset(&bm, 0, sizeof(BITMAP)); + + WindowManager *pMgr = pSB->GetWM(); + + HBITMAP hTit; + + if ((pParent == NULL) && (Config->gcGUIMode == 3) && hDlg) return; // No Title Bar + + if (pParent == NULL) hTit = pMgr->GetBitmap(gcGUI::BM_TITLE); + else hTit = pMgr->GetBitmap(gcGUI::BM_SUBTITLE); + + FVECTOR4 clr = _Colour(color); + FVECTOR4 white = _Colour(0xFFFFFFFF); + + HDC hDC = pSB->GetDC(); + HDC hSrc = CreateCompatibleDC(hDC); + HDC hTgt = CreateCompatibleDC(hDC); + + GetObject(hTit, sizeof(BITMAP), &bm); + + if (hBmp) DeleteObject(hBmp); + + hBmp = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); + + pSB->ReleaseDC(hDC); + + SelectObject(hSrc, hTit); + SelectObject(hTgt, hBmp); + + // Recolorize the title bar + + for (int y = 0; y < bm.bmHeight; y++) { + for (int x = 0; x < bm.bmWidth; x++) { + COLORREF cr = GetPixel(hSrc, x, y); + FVECTOR4 c = _Colour(cr); + FVECTOR4 out = (clr * c.b) + (white * c.g); + SetPixel(hTgt, x, y, _Colour(&out)); + } + } + + DeleteDC(hSrc); + DeleteDC(hTgt); +} + + +// =============================================================================================== +// +int Node::CellSize() +{ + int y = 0; + if (hBmp) y += bm.bmHeight; + if (bOpen && hDlg) { + RECT r; + GetWindowRect(hDlg, &r); + y += (r.bottom - r.top); + } + return y; +} + + +// =============================================================================================== +// +int Node::Paint(HDC hDC, int y) +{ + WindowManager *pMgr = pSB->GetWM(); + + int width = pSB->GetWidth(); + int x = 0; + int wof = 0, hof = 0; + DWORD ck = 0; + + if (pSB->GetStyle() == gcGUI::DS_FLOAT) x += 1; + + if (hBmp) { + if (pParent) { + SelectObject(hDC, pMgr->GetSubTitleFont()); + SetTextColor(hDC, pMgr->cfg.txt_sub_clr); + wof = pMgr->cfg.txt_sub_x; + hof = pMgr->cfg.txt_sub_y; + } + else { + SelectObject(hDC, pMgr->GetAppTitleFont()); + SetTextColor(hDC, pMgr->cfg.txt_main_clr); + wof = pMgr->cfg.txt_main_x; + hof = pMgr->cfg.txt_main_y; + } + } + + + // Draw Title Bars ----------------------------- + // + if (hBmp) { + + HDC hSr = CreateCompatibleDC(hDC); + int z = width - bm.bmHeight - 3; + + trect = { 0, y, width, y + bm.bmHeight }; + crect = { z, y, width, y + bm.bmHeight }; + + SelectObject(hSr, hBmp); + BitBlt(hDC, x, y, width - 10, bm.bmHeight, hSr, 0, 0, SRCCOPY); + BitBlt(hDC, width - 10 - x, y, 10, bm.bmHeight, hSr, bm.bmWidth - 10, 0, SRCCOPY); + TextOut(hDC, wof, y + hof, Label, lstrlen(Label)); + + DeleteDC(hSr); + + if (bOpen) PaintIcon(hDC, x, y, 0); + else PaintIcon(hDC, x, y, 1); + if (bClose) PaintIcon(hDC, z, y, 2); + + y += bm.bmHeight; + } + + pos = { x, y }; + + if (bOpen && hDlg) { + RECT r; + GetWindowRect(hDlg, &r); + y += (r.bottom - r.top); + } + + return y; +} + + +// =============================================================================================== +// +void Node::PaintIcon(HDC hDC, int x, int y, int id) +{ + WindowManager *pMgr = pSB->GetWM(); + DWORD yell = RGB(255, 255, 0); + DWORD mang = RGB(255, 0, 255); + + HBITMAP hIco = pMgr->GetBitmap(gcGUI::BM_ICONS); + + if (hIco) { + DWORD ck = 0, sx = 0; + BITMAP ic; + GetObject(hIco, sizeof(BITMAP), &ic); + + switch (id) { + case 0: sx = ic.bmHeight * 0; ck = yell; break; + case 1: sx = ic.bmHeight * 1; ck = mang; break; + case 2: sx = ic.bmHeight * 2; ck = yell; break; + case 3: sx = ic.bmHeight * 3; ck = mang; break; + } + + HDC hSr = CreateCompatibleDC(hDC); + SelectObject(hSr, hIco); + int yo = (bm.bmHeight - ic.bmHeight) / 2; + TransparentBlt(hDC, x, y + yo, ic.bmHeight, ic.bmHeight, hSr, sx, 0, ic.bmHeight, ic.bmHeight, ck); + DeleteDC(hSr); + } +} + + +// =============================================================================================== +// +int Node::Spacer(HDC hDC, int y) +{ + WindowManager *pMgr = pSB->GetWM(); + + if (pParent) if (pParent->bOpen == false) if (pParent->pSB == pSB) return y; + + int width = pSB->GetWidth(); + int h = CellSize(); + + SelectObject(hDC, (HBRUSH)GetStockObject(GRAY_BRUSH)); + SelectObject(hDC, (HPEN)GetStockObject(NULL_PEN)); + Rectangle(hDC, 0, y, width + 1, y + h + 1); + + return y + h; +} + + +// =============================================================================================== +// +void Node::Move() +{ + if (!hDlg) return; + SetWindowPos(hDlg, NULL, pos.x, pos.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); +} + + + + + + + + +// =============================================================================================== +// WindowManager Implementation +// =============================================================================================== +// +WindowManager::WindowManager(HWND hAppMainWindow, HINSTANCE _hInst, bool bWindowed) +{ + char path[256]; + char cbuf[256]; + + g_pWM = NULL; + hMainWnd = hAppMainWindow; + hInst = _hInst; + sbDrag = NULL; + sbDragSrc = NULL; + sbDest = NULL; + bWin = bWindowed; + + hIcons = hTitle = hSub = NULL; + + RECT rMain; + GetClientRect(hAppMainWindow, &rMain); + + AutoFile file; + + if (file.IsInvalid()) { + sprintf_s(path, 256, "%sgcGUI.cfg", OapiExtension::GetConfigDir()); + fopen_s(&file.pFile, path, "r"); + } + + if (!file.IsInvalid()) { + + int q, w; + bool bFound = false; + + while (fgets2(cbuf, 256, file.pFile, 0x0A) >= 0) + { + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "RESOLUTION", 10)) { + if (sscanf_s(cbuf, "RESOLUTION %d %d", &q, &w) != 2) LogErr("Invalid Line in (%s): %s", path, cbuf); + if (q < rMain.bottom && rMain.bottom < w) bFound = true; + continue; + } + if (bFound == false) continue; + + if (!strncmp(cbuf, "END", 3)) break; + + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "FONT_MAIN", 9)) { + if (sscanf_s(cbuf, "FONT_MAIN \"%[^\"]\" %d %d", cfg.fnt_main, 32, &cfg.txt_main_size, &cfg.txt_main_weight) != 3) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "FONT_SUB", 8)) { + if (sscanf_s(cbuf, "FONT_SUB \"%[^\"]\" %d %d", cfg.fnt_sub, 32, &cfg.txt_sub_size, &cfg.txt_sub_weight) != 3) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "MAIN_OFS", 8)) { + if (sscanf_s(cbuf, "MAIN_OFS %d %d", &cfg.txt_main_x, &cfg.txt_main_y) != 2) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "SUB_OFS", 7)) { + if (sscanf_s(cbuf, "SUB_OFS %d %d", &cfg.txt_sub_x, &cfg.txt_sub_y) != 2) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "MAIN_BMP", 8)) { + if (sscanf_s(cbuf, "MAIN_BMP %s", cfg.bmp_main, 32) != 1) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "SUB_BMP", 7)) { + if (sscanf_s(cbuf, "SUB_BMP %s", cfg.bmp_sub, 32) != 1) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "ICON_BMP", 8)) { + if (sscanf_s(cbuf, "ICON_BMP %s", cfg.bmp_icon, 32) != 1) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "MAIN_CLR", 8)) { + if (sscanf_s(cbuf, "MAIN_CLR %X", &cfg.txt_main_clr) != 1) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "SUB_CLR", 7)) { + if (sscanf_s(cbuf, "SUB_CLR %X", &cfg.txt_sub_clr) != 1) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + // -------------------------------------------------------------------------------------------- + if (!strncmp(cbuf, "SCROLL", 6)) { + if (sscanf_s(cbuf, "SCROLL %d", &cfg.scroll) != 1) LogErr("Invalid Line in (%s): %s", path, cbuf); + continue; + } + } + if (bFound == false) { + LogErr("Configuration not found (%s)", path); + return; + } + } + else { + LogErr("Configuration not found (%s)", path); + return; + } + + + + + // Create window class for sidebars + // + DWORD flags = 0; + if (Config->gcGUIMode == 1) flags |= CS_NOCLOSE; + + WNDCLASS wc; + memset(&wc, 0, sizeof(WNDCLASS)); + wc.style = flags | CS_OWNDC | CS_SAVEBITS; + wc.lpfnWndProc = SideBarWndProc; + wc.hInstance = hInst; +#pragma warning(disable:4302) + wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCE(IDC_ARROW)); + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.lpszClassName = "SideBarWnd"; + + RegisterClass(&wc); + + memset(&wc, 0, sizeof(WNDCLASS)); + wc.style = flags | CS_OWNDC | CS_SAVEBITS | CS_DROPSHADOW; + wc.lpfnWndProc = SideBarWndProc; + wc.hInstance = hInst; + wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCE(IDC_ARROW)); +#pragma warning(default:4302) + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.lpszClassName = "Floater"; + + RegisterClass(&wc); + + // ---------------------------------- + + hIcons = g_client->gcReadImageFromFile(cfg.bmp_icon); + hTitle = g_client->gcReadImageFromFile(cfg.bmp_main); + hSub = g_client->gcReadImageFromFile(cfg.bmp_sub); + + hAppFont = CreateFont(cfg.txt_main_size, 0, 0, 0, cfg.txt_main_weight, false, false, 0, 0, 0, 2, CLEARTYPE_QUALITY, 49, cfg.fnt_main); + hSubFont = CreateFont(cfg.txt_sub_size, 0, 0, 0, cfg.txt_sub_weight, false, false, 0, 0, 0, 2, CLEARTYPE_QUALITY, 49, cfg.fnt_sub); + + if (!hAppFont) LogErr("Font Not Found [%s]", cfg.fnt_main); + if (!hSubFont) LogErr("Font Not Found [%s]", cfg.fnt_sub); + + // ---------------------------------- + + + // Smaple dialog for a proper size and scaling + // + HWND hDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_MESHDEBUG), hAppMainWindow, DummyDlgProc, 0); + + RECT r; + GetWindowRect(hDlg, &r); + width = (r.right - r.left); + + DestroyWindow(hDlg); + + /* + if (Config->gcGUIMode == 2) { + Cmd = oapiRegisterCustomCmd("gcGUI Test", "gcGUI Test Program", OpenTestClbk, this); + } + else { + OpenTestClbk(this); + }*/ + + SetFocus(hAppMainWindow); + + // Must be last one + g_pWM = this; +} + + +// =============================================================================================== +// +WindowManager::~WindowManager() +{ + oapiUnregisterCustomCmd(Cmd); + + for(SideBar* sb : sbList) delete sb; + + UnregisterClass("SideBarWnd", hInst); + UnregisterClass("Floater", hInst); + + if (hTitle) DeleteObject(hTitle); + if (hSub) DeleteObject(hSub); + if (hIcons) DeleteObject(hIcons); + if (hAppFont) DeleteObject(hAppFont); + if (hSubFont) DeleteObject(hSubFont); +} + + +// =============================================================================================== +// +bool WindowManager::IsOK() const +{ + if (!hAppFont) return false; + if (!hSubFont) return false; + if (!hTitle) return false; + if (!hSub) return false; + if (!hIcons) return false; + return true; +} + + +// =============================================================================================== +// +HBITMAP WindowManager::GetBitmap(int id) const +{ + switch (id) { + case gcGUI::BM_TITLE: return hTitle; + case gcGUI::BM_SUBTITLE: return hSub; + case gcGUI::BM_ICONS: return hIcons; + } + return NULL; +} + + +// =============================================================================================== +// +SideBar * WindowManager::GetSideBar(HWND hWnd) +{ + if (sbList.size() == 0) return NULL; + for (SideBar * sb : sbList) if (sb->GetHWND() == hWnd) return sb; + return NULL; +} + + +// =============================================================================================== +// Virtual +// +HNODE WindowManager::RegisterApplication(gcGUIApp *pPtr, const char *label, HWND hDlg, DWORD docked, DWORD color) +{ + + g_gcGUIAppList.push_back(pPtr); + + SideBar *pSB = NULL; + + if (color == 0) color = 0xC0A020; + + // Always "Float" in this mode + if (Config->gcGUIMode >= 2) docked = gcGUI::DS_FLOAT; + + if (docked == gcGUI::DS_FLOAT) pSB = NewSideBar(NULL); + + if (Config->gcGUIMode == 3) { + HWND hWnd = pSB->GetHWND(); + SetWindowText(hWnd, label); + } + + Node *pAp = new Node(pSB, label, hDlg, color, NULL); + + pAp->SetApp(pPtr); + return HNODE(pAp); +} + + + +// =============================================================================================== +// Virtual +// +HNODE WindowManager::RegisterSubsection(HNODE hNode, const char *label, HWND hDlg, DWORD color) +{ + if (APPNODE(hNode)->pParent != NULL) { + LogErr("RegisterSubsection Failed. Parent cannot be an other subnode"); + return NULL; + } + + SideBar *pSB = APPNODE(hNode)->GetSideBar(); + if (color == 0) color = pSB->GetAutoColor(); + Node *pAp = new Node(pSB, label, hDlg, color, APPNODE(hNode)); + return HNODE(pAp); +} + + +// =============================================================================================== +// Virtual +// +void WindowManager::UpdateStatus(HNODE hNode, const char *label, HWND hDlg, DWORD color) +{ + Node *pAp = APPNODE(hNode); + pAp->hDlg = hDlg; + if (pAp->Label) delete[] pAp->Label; + if (label) pAp->Label = _strdup(label); + else pAp->Label = NULL; + if (color != 0) pAp->ReColorize(color); + SideBar *pSB = pAp->GetSideBar(); + pSB->RescaleWindow(); + pSB->Invalidate(); +} + + +// =============================================================================================== +// Virtual +// +void WindowManager::DisplayWindow(HNODE hNode, bool bShow) +{ + SideBar *pSB = APPNODE(hNode)->GetSideBar(); + + if (pSB) { + if (pSB->IsFloater()) { + if (bShow) { + pSB->ManageButtons(); + pSB->Sort(); + pSB->RescaleWindow(); + ShowWindow(pSB->GetHWND(), SW_SHOW); + } + else ShowWindow(pSB->GetHWND(), SW_HIDE); + } + } +} + + +// =============================================================================================== +// Virtual +// +HFONT WindowManager::GetFont(int id) +{ + return g_pWM->GetSubTitleFont(); +} + + +// =============================================================================================== +// Virtual +// +HWND WindowManager::GetDialog(HNODE hNode) +{ + return ((Node *)(hNode))->hDlg; +} + + +// =============================================================================================== +// Virtual +// +void WindowManager::UpdateSize(HWND hDlg) +{ + HNODE hNode = GetNode(hDlg); + if (hNode) UpdateStatus(hNode); +} + + +// =============================================================================================== +// Virtual +// +HNODE WindowManager::GetNode(HWND hDlg) +{ + for (SideBar* sb : sbList) + { + HNODE hNode = (HNODE)sb->FindNode(hDlg); + if (hNode) return hNode; + } + return NULL; +} + + +// =============================================================================================== +// Virtual +// +void WindowManager::OpenNode(HNODE hNode, bool bOpen) +{ + APPNODE(hNode)->bOpen = bOpen; +} + + +// =============================================================================================== +// Virtual +// +bool WindowManager::IsOpen(HNODE hNode) +{ + return APPNODE(hNode)->bOpen; +} + + +// =============================================================================================== +// Virtual +// +bool WindowManager::UnRegister(HNODE hNode) +{ + if (!DoesExist(APPNODE(hNode))) return false; + + if (APPNODE(hNode)->IsRoot()) return false; + + // Delete/Remove every child node + /* + for each (SideBar *sb in sbList) + { + list remlist; + for each (Node *pn in sb->wList)if (pn->pParent == hNode) remlist.push_back(pn); + for each (Node *pn in remlist) + { + sb->RemoveWindow(pn); + delete pn; + } + }*/ + + SideBar *pSB = APPNODE(hNode)->GetSideBar(); + pSB->RemoveWindow(APPNODE(hNode)); + delete APPNODE(hNode); + return true; +} + +// =============================================================================================== +// +bool WindowManager::DoesExist(Node *pn) +{ + for (SideBar *sb : sbList) if (sb->DoesExists(pn)) return true; + return false; +} + +// =============================================================================================== +// +void WindowManager::UpdateStatus(HNODE hNode) +{ + if (!hNode) return; + Node *pNode = (Node *)hNode; + SideBar *pSB = pNode->GetSideBar(); + if (pSB) { + pSB->RescaleWindow(); + pSB->Invalidate(); + } +} + + +// =============================================================================================== +// +void WindowManager::CloseWindow(Node *pAp) +{ + SideBar *pSB = pAp->GetSideBar(); + Node *pPar = pAp->pParent; + + if (pPar) { + pSB->RemoveWindow(pAp); + pSB->RescaleWindow(); + pSB->Invalidate(); + + if (pSB->IsEmpty() && pSB->IsFloater()) ReleaseSideBar(pSB); + + pSB = pPar->GetSideBar(); + pSB->AddWindow(pAp); + pSB->RescaleWindow(); + pSB->Sort(); + pSB->Invalidate(); + } + else { + if (Config->gcGUIMode == 2) { + for (DWORD i = 0; i < sbList.size(); i++) { + if (sbList[i] == pSB) { + sbList.erase(sbList.begin() + i); + delete pSB; + } + } + } + } +} + +// =============================================================================================== +// +void WindowManager::Animate() +{ + //if (Config->gcGUIMode == 1) for each (SideBar * sb in sbList) if (!sb->IsInactive()) sb->Animate(); +} + + +// =============================================================================================== +// +SideBar *WindowManager::NewSideBar(Node *pAN) +{ + for (SideBar* sb : sbList) if (sb->IsInactive() && sb->IsEmpty()) { + sb->SetState(gcGUI::DS_FLOAT); + return sb; + } + + SideBar *pSB = new SideBar(this, gcGUI::DS_FLOAT); + sbList.push_back(pSB); + return pSB; +} + + +// =============================================================================================== +// +void WindowManager::ReleaseSideBar(SideBar *pSB) +{ + pSB->SetState(gcGUI::INACTIVE); + ShowWindow(pSB->GetHWND(), SW_HIDE); +} + + +// =============================================================================================== +// +void WindowManager::SetOffset(int x, int y) +{ + ptOffset = { x, y }; +} + + +// =============================================================================================== +// +SideBar* WindowManager::StartDrag(Node *pAN, int x, int y) +{ + sbDragSrc = pAN->GetSideBar(); + SideBar *pSBOld = pAN->GetSideBar(); + SideBar *pSBNew = NewSideBar(pAN); + + if (pAN->pParent) { + pSBOld->RemoveWindow(pAN); + pSBNew->AddWindow(pAN); + } + else { + pSBOld->RemoveWindow(pAN); + pSBNew->AddWindow(pAN); + + vector tmp; + + for (Node * an : pSBOld->wList) if (an->pParent == pAN) tmp.push_back(an); + + for (Node * an : tmp) + { + pSBOld->RemoveWindow(an); // Cant remove directly from a list being browsed + pSBNew->AddWindow(an); + } + } + + sbDrag = pSBNew; + + x -= ptOffset.x; + y -= ptOffset.y; + + pSBNew->ResetWindow(x, y); + + InvalidateRect(pSBNew->GetHWND(), NULL, true); + InvalidateRect(pSBOld->GetHWND(), NULL, true); + + return pSBNew; +} + + +// =============================================================================================== +// +void WindowManager::BeginMove(Node *pAN, int x, int y) +{ + sbDrag = pAN->GetSideBar(); +} + + +// =============================================================================================== +// +void WindowManager::Drag(int x, int y) +{ + if (sbDrag) { + + if (sbDrag->GetTopNode() == NULL) return; + + HWND hBar = sbDrag->GetHWND(); + int w = sbDrag->GetWidth(); + int h = sbDrag->GetHeight(); + x -= ptOffset.x; + y -= ptOffset.y; + + RECT rect; + SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0); + int t = sbDrag->GetTopNode()->bm.bmHeight; + + if (x < rect.left) x = rect.left; + if (y < rect.top) y = rect.top; + if ((x + w) > rect.right) x = rect.right - w; + if ((y + t) > rect.bottom) y = rect.bottom - t; + + SetWindowPos(hBar, HWND_TOP, x, y, w, h, SWP_SHOWWINDOW); + } +} + + +// =============================================================================================== +// +void WindowManager::MouseMoved(int x, int y) +{ + RECT r; + if (hMainWnd) GetClientRect(hMainWnd, &r); + int w = r.right - r.left; + int h = r.bottom - r.top; + int q = (width * 3) / 2; +} + + +// =============================================================================================== +// +void WindowManager::EndDrag() +{ + if (sbDrag) InvalidateRect(sbDrag->GetHWND(), NULL, true); + sbDrag = NULL; + sbDragSrc = NULL; +} + + +// =============================================================================================== +// +SideBar *WindowManager::FindDestination() +{ + if (Config->gcGUIMode != 1) return NULL; + + int z = 0; + SideBar *pOld = sbDest; + sbDest = NULL; + for (SideBar* sb : sbList) { + if (sb->IsInactive()) continue; + if (sb != sbDrag) { + RECT out; + IntersectRect(&out, ptr(sb->GetRect()), ptr(sbDrag->GetRect())); + int a = (out.right - out.left) * (out.bottom - out.top); + if (a > z) { z = a; sbDest = sb; } + } + } + + if (sbDest != pOld && pOld != NULL) { + qInsert.pTgt = NULL; + qInsert.List.clear(); + if (pOld->GetStyle() == gcGUI::DS_FLOAT) pOld->RescaleWindow(); + pOld->Invalidate(); + } + + qInsert.pTgt = sbDest; + return sbDest; +} + + +// =============================================================================================== +// Orbiter Application Main Window Proc +// +bool WindowManager::MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static int xpos, ypos; + switch (uMsg) { + + case WM_MOUSELEAVE: + case WM_MBUTTONDOWN: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + return false; + + case WM_KEYDOWN: + { + return false; + } + + case WM_MOUSEWHEEL: + return false; + + case WM_MOUSEMOVE: + xpos = GET_X_LPARAM(lParam); + ypos = GET_Y_LPARAM(lParam); + MouseMoved(xpos, ypos); + break; + } + + return false; +} + + + + + + + + +// =============================================================================================== +// SideBar Implementation +// =============================================================================================== +// +SideBar::SideBar(class WindowManager *_pMgr, DWORD _state) +{ + pMgr = _pMgr; + + HWND hMainWnd = pMgr->GetMainWindow(); + hInst = pMgr->GetInstance(); + width = pMgr->GetWidth(); + state = _state; + + dnNode = NULL; + dnClose = NULL; + ypos = 60; + anim_state = 0; + rollpos = 0; + wndlen = 0; + cidx = 0; + bOpening = false; + bIsOpen = false; + bValidate = true; + bLock = false; + bFirstTime = true; + title_height = 0; + bWin = pMgr->IsWindowed(); + + RECT r; + GetClientRect(hMainWnd, &r); + height = (r.bottom - r.top) - ypos; + + DWORD exstyle = 0; + DWORD style = 0; + + if (Config->gcGUIMode == 1) { + if (state == gcGUI::DS_RIGHT) xref = r.right; + if (state == gcGUI::DS_LEFT) xref = -width; + if (state == gcGUI::DS_FLOAT) xref = width, height = width; + if (bWin) { + if (state == gcGUI::DS_FLOAT) style = WS_CLIPSIBLINGS; + else style = WS_CHILD | WS_CLIPSIBLINGS; + } else style = 0; + } + + if (Config->gcGUIMode == 2) { + state = gcGUI::DS_FLOAT; + xref = width; + } + + if (Config->gcGUIMode == 3) { + state = gcGUI::DS_FLOAT; + xref = width; + exstyle = 0; + if (bWin) style = WS_CAPTION | WS_SYSMENU; + else style = WS_CAPTION | WS_SYSMENU; + } + + if (state == gcGUI::DS_FLOAT) width += 2, height += 1; // Border + + if (state == gcGUI::DS_FLOAT) hBar = CreateWindowExA(exstyle, "Floater", "Float", style, xref, ypos, width, height, hMainWnd, NULL, hInst, 0); + else hBar = CreateWindowExA(exstyle, "SideBarWnd", "Dock", style, xref, ypos, width, height, hMainWnd, NULL, hInst, 0); + + SetWindowLong(hBar, GWL_STYLE, style); // Make it bordeless + + if (Config->gcGUIMode == 3) { + RECT w, c; + GetWindowRect(hBar, &w); + GetClientRect(hBar, &c); + title_height = (w.bottom - w.top) - c.bottom; + } + + if (Config->gcGUIMode == 1) ShowWindow(hBar, SW_SHOW); +} + + +// =============================================================================================== +// +SideBar::~SideBar() +{ + for (Node *v : wList) + { + if (v->hDlg) DestroyWindow(v->hDlg); + delete v; + } + wList.clear(); + DestroyWindow(hBar); +} + + +// =============================================================================================== +// +void SideBar::ToggleLock() +{ + bLock = !bLock; +} + + +// =============================================================================================== +// +void SideBar::ManageButtons() +{ + for (Node* nd : wList) + { + nd->bClose = false; + + bool bRootIncluded = false; + if (nd->pParent) if (nd->pParent->GetSideBar() == this) bRootIncluded = true; + if (nd->pParent == NULL) bRootIncluded = true; + + if (state == gcGUI::DS_FLOAT && !bRootIncluded) nd->bClose = true; + if (Config->gcGUIMode == 2) if (nd->pParent == NULL) nd->bClose = true; + } +} + + +// =============================================================================================== +// +void SideBar::Invalidate() +{ + ManageButtons(); + InvalidateRect(hBar, NULL, true); +} + + +// =============================================================================================== +// +void SideBar::ResetWindow(int x, int y) +{ + if (state == gcGUI::DS_FLOAT) { + height = ComputeLength() + title_height + 1; + } + SetWindowPos(hBar, NULL, x, y, width, height, SWP_NOZORDER | SWP_SHOWWINDOW); +} + + +// =============================================================================================== +// +RECT SideBar::GetRect() const +{ + RECT r = { 0, 0, 0, 0 }; + if (hBar) GetWindowRect(hBar, &r); + return r; +} + + +// =============================================================================================== +// +void SideBar::Open(bool bO) +{ + if (Config->gcGUIMode >= 2) return; + + if (bLock) { + bOpening = bIsOpen; + return; + } + if (pMgr->GetDragSource() == this) { + bOpening = true; + return; + } + if (state == gcGUI::DS_FLOAT) bOpening = true; + else bOpening = bO; +} + +// =============================================================================================== +// +DWORD SideBar::GetAutoColor() +{ + static DWORD color[] = { 0xf5d0ff, 0xfffebb, 0xbbfffc, 0xbaffc4, 0xffc0c0, 0xc6c5ff, 0 }; + DWORD c = color[cidx]; cidx++; + if (color[cidx] == 0) cidx = 0; + return c; +} + +// =============================================================================================== +// +void SideBar::Animate() +{ + if (bLock && bValidate) return; + if (bLock && bIsOpen) return; + if (state == gcGUI::DS_FLOAT) return; + + if (bOpening && anim_state >= 0.9999f) { + if (bValidate) { + bIsOpen = true; + InvalidateRect(hBar, NULL, TRUE); + bFirstTime = false; + } + bValidate = false; + return; + } + + if (!bOpening) bIsOpen = false; + + if (!bOpening && anim_state <= 0.0001f) { + bValidate = true; + return; + } + + if (bOpening) anim_state += float(oapiGetSysStep() * 2.5); + else anim_state -= float(oapiGetSysStep() * 2.5); + + anim_state = saturate(anim_state); + + float as = sin(anim_state * 1.5707f); + int x = xref; + + if (state == gcGUI::DS_RIGHT) x = xref + int(-as * float(width)); + if (state == gcGUI::DS_LEFT) x = xref + int(as * float(width)); + + + if (GetWindowLong(hBar, GWL_STYLE)&WS_CHILD) bFirstTime = true; + + MoveWindow(hBar, x, ypos, width, height, bFirstTime); +} + + +// =============================================================================================== +// +void SideBar::AddWindow(Node *pAp, bool bSetupOnly) +{ + bFirstTime = true; // Enable Full redraw + pAp->pSB = this; + if (pAp->hDlg) SetParent(pAp->hDlg, hBar); + if (!bSetupOnly) wList.push_back(pAp); +} + + +// =============================================================================================== +// +void SideBar::RemoveWindow(class Node *pAp) +{ + for (DWORD i = 0; i < wList.size(); i++) { + if (wList[i] == pAp) { + wList.erase(wList.begin() + i); + return; + } + } +} + + +// =============================================================================================== +// +bool SideBar::IsOpen() const +{ + if (state == gcGUI::DS_FLOAT) return true; + return bIsOpen; +} + + +// =============================================================================================== +// +void SideBar::RescaleWindow() +{ + if (state == gcGUI::DS_FLOAT) { + height = ComputeLength() + title_height + 1; + } + SetWindowPos(hBar, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW); +} + + +// =============================================================================================== +// +Node* SideBar::FindNode(HWND hDlg) +{ + for (Node* nd : wList) if (nd->hDlg == hDlg) return nd; + return NULL; +} + + +// =============================================================================================== +// +void SideBar::GetVisualList(vector &tmp) +{ + if (Config->gcGUIMode == 3) { + for (Node* ap : wList) if (ap->hDlg) tmp.push_back(ap); + } + else + { + for (Node* ap : wList) { + Node* pPar = ap->pParent; + if (pPar) { + if (pPar->pSB == this) { + if (pPar->bOpen) tmp.push_back(ap); // Visible Child + else continue; // Hidden Child + } + else tmp.push_back(ap); // Foreing Child + } + else tmp.push_back(ap); // Root Node + } + } +} + + +// =============================================================================================== +// +void SideBar::Sort() +{ + vector tmp; + for (Node* ap : wList) { + Node* pPar = ap->pParent; + if (pPar == NULL) { + tmp.push_back(ap); // Root Node + for (Node* ch : wList) if (ch->pParent == ap) tmp.push_back(ch); // Child + } + else if (pPar->GetSideBar()!=this) tmp.push_back(ap); // Foreing Child + } + wList = tmp; +} + + +// =============================================================================================== +// +bool SideBar::Insert(Node *pNode, Node *pAfter) +{ + if (!pAfter) { + wList.insert(wList.begin(), pNode); + AddWindow(pNode, true); + return true; + } + DWORD i = 0; + while (i < (wList.size() - 1)) { + if (wList[i] == pAfter) { + wList.insert(wList.begin() + i + 1, pNode); + AddWindow(pNode, true); + return true; + } + i++; + } + wList.push_back(pNode); + AddWindow(pNode, true); + return true; +} + + +// =============================================================================================== +// +Node *SideBar::GetTopNode() +{ + if (wList.size() == 0) return NULL; + return wList.front(); +} + + +// =============================================================================================== +// +bool SideBar::DoesExists(Node *pX) +{ + for (Node* ap : wList) if (ap == pX) return true; + return false; +} + + +// =============================================================================================== +// +Node *SideBar::FindClosest(vector &vis, Node *pRoot, int yval) +{ + int d = 1000000; + int y = rollpos; + Node *out = NULL; + if (!pRoot) if (abs(y - yval) < d) d = abs(y - yval); + for (Node* ap : vis) { + y += ap->CellSize(); + if (ap->pParent == pRoot || ap == pRoot || ap == vis.back()) { + if (abs(y - yval) < d) d = abs(y - yval), out = ap; + } + } + return out; +} + + +// =============================================================================================== +// +bool SideBar::TryInsert(SideBar *sbIn) +{ + vector drawList; + + GetVisualList(drawList); + + tInsert *pIns = pMgr->InsertList(); + + if (pIns->pTgt != this) return false; + + map &wIns = pIns->List; + map wPrev = wIns; + + wIns.clear(); + + int yp = sbIn->GetRect().top - GetRect().top; + int h = sbIn->ComputeLength(); + int y = rollpos; + + Node *pNode = sbIn->GetTopNode(); + if (!pNode) return false; + + Node *pParent = pNode->pParent; + if (!DoesExists(pParent)) pParent = NULL; + + Node *pPlace = FindClosest(drawList, pParent, yp); + for (Node* an : sbIn->wList) wIns[an] = pPlace; + + return wIns != wPrev; +} + + +// =============================================================================================== +// +bool SideBar::Apply() +{ + bool bRet = false; + + SideBar *pDG = pMgr->GetDraged(); + tInsert *pIns = pMgr->InsertList(); + + if ((pIns->pTgt == this) && (pIns->List.size()>0)) + { + for (auto &var : pIns->List) + { + Node *pAfter = var.second; + Node *pSrc = var.first; + if (!Insert(pSrc, pAfter)) break; + pDG->RemoveWindow(pSrc); + bRet = true; + } + + if (pDG->IsEmpty()) pMgr->ReleaseSideBar(pDG); + + pIns->List.clear(); + pIns->pTgt = NULL; + + Sort(); + Invalidate(); + } + + return bRet; +} + + +// =============================================================================================== +// +LRESULT SideBar::SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static int xpos, ypos; + static int xof, yof; + static bool bUpdate = false; + + HWND hMain = pMgr->GetMainWindow(); + + switch(uMsg) { + + case WM_KEYDOWN: + { + pMgr->MainWindowProc(hWnd, uMsg, wParam, lParam); + break; + } + + + case WM_MOUSEWHEEL: + { + if (GetStyle() == gcGUI::DS_FLOAT) break; + + int old = rollpos; + short d = GET_WHEEL_DELTA_WPARAM(wParam); + if (d>0) rollpos += pMgr->cfg.scroll; + else rollpos -= pMgr->cfg.scroll; + int q = height - wndlen; + if (q > 0) q = 0; + if (rollpos > 0) rollpos = 0; + if (rollpos < q) rollpos = q; + if (old != rollpos) Invalidate(); + break; + } + + case WM_LBUTTONDOWN: + { + xpos = GET_X_LPARAM(lParam); + ypos = GET_Y_LPARAM(lParam); + + for (Node* nd : wList) + { + Node *pPar = nd->pParent; + + if (pPar) { + if (pPar->GetSideBar() == this) { + if (pPar->bOpen == false) continue; + } + } + + if (PointInside(xpos, ypos, &(nd->trect))) { + + if (nd->bClose && PointInside(xpos, ypos, &(nd->crect))) { + dnClose = nd; + } + else { + xof = xpos - nd->trect.left; + yof = ypos - nd->trect.top; + dnNode = nd; + } + TRACKMOUSEEVENT te; te.cbSize = sizeof(TRACKMOUSEEVENT); te.dwFlags = TME_LEAVE; te.hwndTrack = hBar; + TrackMouseEvent(&te); + break; // break for + } + } + break; + } + + case WM_LBUTTONUP: + { + int xp = GET_X_LPARAM(lParam); + int yp = GET_Y_LPARAM(lParam); + + SideBar *pDG = pMgr->GetDraged(); + + if (pDG) { + SideBar *pTgt = pMgr->FindDestination(); + if (pTgt) pTgt->Apply(); + pMgr->EndDrag(); + ReleaseCapture(); + dnNode = NULL; + dnClose = NULL; + break; + } + + if (dnNode) { + if (dnNode->GetSideBar() == this) { + if (PointInside(xp, yp, &(dnNode->trect))) { + dnNode->bOpen = !dnNode->bOpen; + if (IsFloater()) RescaleWindow(); + Invalidate(); + DWORD msg = (dnNode->bOpen ? gcGUI::MSG_OPEN_NODE : gcGUI::MSG_CLOSE_NODE); + dnNode->pApp->clbkMessage(msg, dnNode, 0); + } + } + } + + if (dnClose) { + if (dnClose->GetSideBar() == this) { + if (PointInside(xp, yp, &(dnClose->crect))) { + if (dnClose->pApp->clbkMessage(gcGUI::MSG_CLOSE_APP, NULL, 0)) + { + pMgr->CloseWindow(dnClose); + } + } + } + } + + dnNode = NULL; + dnClose = NULL; + break; + } + + + case WM_MOUSELEAVE: + { + dnNode = NULL; + dnClose = NULL; + break; + } + + + case WM_MOUSEMOVE: + { + int dx = abs(GET_X_LPARAM(lParam) - xpos); + int dy = abs(GET_Y_LPARAM(lParam) - ypos); + int x = GET_X_LPARAM(lParam); + int y = GET_Y_LPARAM(lParam); + + if (Config->gcGUIMode < 3) { + + POINT scp = { x, y }; + ClientToScreen(hWnd, &scp); + + // Begin Moving a Window + // + if (dnNode && IsFloater() && (GetTopNode() == dnNode) && (dx > 1 || dy > 1)) + { + SetCapture(hBar); + pMgr->SetOffset(xof, yof); + pMgr->BeginMove(dnNode, scp.x, scp.y); + dnNode = NULL; + dnClose = NULL; + break; + } + + // Detach a window from a dock + // + if (Config->gcGUIMode == 1) { + if (dnNode && dx > 25) { + SetCapture(hBar); + pMgr->SetOffset(xof, yof); + SideBar* pTgt = pMgr->StartDrag(dnNode, scp.x, scp.y); + dnNode = NULL; + dnClose = NULL; + break; + } + } + + // Move a dragged window and try to insert content into a dock + // + if (pMgr->GetDraged()) { + pMgr->MouseMoved(scp.x, scp.y); + pMgr->Drag(scp.x, scp.y); + SideBar *pTgt = pMgr->FindDestination(); + SideBar *pDG = pMgr->GetDraged(); + if (pTgt) if (pTgt->IsOpen()) { + if (pTgt->TryInsert(pDG)) { + if (pTgt->GetStyle() == gcGUI::DS_FLOAT) pTgt->RescaleWindow(); + pTgt->Invalidate(); + pDG->Invalidate(); + } + } + break; + } + } + break; + } + + case WM_PAINT: + PaintWindow(); + break; + + case WM_ERASEBKGND: + return 1; + + default: + break; + } + + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + + +// =============================================================================================== +// +int SideBar::ComputeLength() +{ + int y = 0; + vector drawList; + GetVisualList(drawList); + + tInsert *pIns = pMgr->InsertList(); + map &wIns = pIns->List; + + bool bInsert = (pIns->pTgt == this) && (wIns.size() > 0); + + if (bInsert) for (auto &var : wIns) if (var.second == NULL) y += var.first->CellSize(); + + for (Node* ap : drawList) + { + y += ap->CellSize(); + if (bInsert) for (auto &var : wIns) if (var.second == ap) y += var.first->CellSize(); + } + + wndlen = y; + return y; +} + + +// =============================================================================================== +// +void SideBar::PaintWindow() +{ + vector drawList; + + GetVisualList(drawList); + + PAINTSTRUCT ps; + HDC hDC = BeginPaint(hBar, &ps); + + int y = rollpos; + SetBkMode(hDC, TRANSPARENT); + + tInsert *pIns = pMgr->InsertList(); + map &wIns = pIns->List; + + bool bInsert = (pIns->pTgt == this) && (wIns.size() > 0); + + if (bInsert) for (auto &var : wIns) + { + if (var.second == NULL) y = var.first->Spacer(hDC, y); + } + + for (Node* ap : drawList) + { + if (ap != drawList.front()) if (ap->pParent == NULL) { + RECT fr = { 0, y, width, y + 3 }; + FillRect(hDC, &fr, (HBRUSH)GetStockObject(BLACK_BRUSH)); + y += 3; + } + + y = ap->Paint(hDC, y); + + if (bInsert) for (auto &var : wIns) + { + if (var.second == ap) y = var.first->Spacer(hDC, y); + } + } + + // Window stack length/height + wndlen = y - rollpos; + + if (state == gcGUI::DS_FLOAT) { + SelectObject(hDC, (HBRUSH)GetStockObject(NULL_BRUSH)); + SelectObject(hDC, (HPEN)GetStockObject(BLACK_PEN)); + Rectangle(hDC, 0, 0, width, height); + } else { + if (y < height) { + SelectObject(hDC, (HBRUSH)GetStockObject(DKGRAY_BRUSH)); + SelectObject(hDC, (HPEN)GetStockObject(NULL_PEN)); + Rectangle(hDC, 0, y, width + 1, height + 1); + } + } + + EndPaint(hBar, &ps); + + // Move dialogs in place + for (Node* ap : wList) { + bool bFound = false; + for (Node* q : drawList) if (q == ap) { bFound = true; break; } + + if (bFound && ap->hDlg) { + if (ap->bOpen) ap->Move(); + else ShowWindow(ap->hDlg, SW_HIDE); + } + else if (ap->hDlg) ShowWindow(ap->hDlg, SW_HIDE); + } +} diff --git a/OVP/VulkanClient/WindowMgr.h b/OVP/VulkanClient/WindowMgr.h new file mode 100644 index 000000000..9f9f02f8c --- /dev/null +++ b/OVP/VulkanClient/WindowMgr.h @@ -0,0 +1,259 @@ + +// ================================================================================================================================= +// +// Copyright (C) 2019 Jarmo Nikkanen +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +// files (the "Software"), to use, copy, modify, merge, publish, distribute, interact with the Software and sublicense copies +// of the Software, subject to the following conditions: +// +// a) You do not sell, rent or auction the Software. +// b) You do not collect distribution fees. +// c) If the Software is distributed in an object code form, it must inform that the source code is available and how to obtain it. +// d) You do not remove or alter any copyright notices contained within the Software. +// e) This copyright notice must 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. +// ================================================================================================================================= + + +#ifndef __WNDMGR_H +#define __WNDMGR_H + +#include +#include "gcCore.h" +#include +#include +#include "gcGUI.h" + +using namespace std; + +extern class WindowManager *g_pWM; + +class SideBar; +class Node; +class WindowManager; + +typedef struct { + std::map List; + SideBar *pTgt; +} tInsert; + +LRESULT CALLBACK SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +class Node +{ +public: + + Node(SideBar *pSB, const char *label, HWND hDlg, DWORD color, Node *pParent = NULL); + ~Node(); + + void Move(); + int Paint(HDC hDC, int y); + int Spacer(HDC hDC, int y); + void PaintIcon(HDC hDC, int x, int y, int id); + int CellSize(); + SideBar * GetSideBar() const { return pSB; } + string Title() const { return Label; } + void ReColorize(DWORD color); + void SetApp(gcGUIApp *pApp); + bool IsRoot() const { return pParent == 0; } + + gcGUIApp * pApp; + SideBar * pSB; + Node * pParent; // Parent node for subsections or NULL for Application root node + HBITMAP hBmp; // Titlebar graphics + BITMAP bm; // Titlebar dimensions + HWND hDlg; // Dialog handle or NULL + POINT pos; // Top-left corner of dialog window + RECT trect; // Titlebar rect + RECT crect; // Close button rect + bool bOpen; // Open or collapsed + bool bClose; // Display close window icon + char * Label; // Titlebar label +}; + + + +class SideBar +{ +public: + + SideBar(class WindowManager *pMgr, DWORD flags); + ~SideBar(); + + LRESULT SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + bool IsEmpty() const { return wList.size() == 0; } + bool IsFloater() const { return state == gcGUI::DS_FLOAT; } + bool IsInactive() const { return state == gcGUI::INACTIVE; } + void SetState(int x) { state = x; } + void Open(bool bOpen); + void Invalidate(); + void ToggleLock(); + void ManageButtons(); + void Animate(); + HWND GetHWND() const { return hBar; } + void AddWindow(Node *pAp, bool bSetupOnly = false); + void RemoveWindow(class Node *pAp); + bool IsOpen() const; + void PaintWindow(); + HDC GetDC() const { return GetWindowDC(hBar); } + void ReleaseDC(HDC hdc) const { ::ReleaseDC(hBar, hdc); } + int ComputeLength(); + int GetWidth() const { return width; } + int GetHeight() const { return height; } + int GetStyle() const { return state; } + void RescaleWindow(); + void ResetWindow(int x, int y); + RECT GetRect() const; + bool Insert(Node *pNode, Node *pAfter); + bool DoesExists(Node *pX); + bool TryInsert(SideBar *anIn); + void GetVisualList(vector &out); + void Sort(); + bool Apply(); + Node * GetTopNode(); + Node * FindClosest(vector &vis, Node *pPar, int yval); + Node * FindNode(HWND hDlg); + DWORD GetAutoColor(); + + WindowManager *GetWM() const { return pMgr; } + + vector wList; + +private: + + class WindowManager *pMgr; + HWND hBar; + HINSTANCE hInst; + DWORD state; + float anim_state; + bool bOpening, bIsOpen, bValidate, bLock, bFirstTime, bWin; + int xref, ypos, width, height, wndlen, rollpos, title_height; + Node * dnNode; + Node * dnClose; + int cidx; +}; + + +struct Conf +{ + int txt_main_x; + int txt_main_y; + int txt_main_size; + int txt_main_weight; + + int txt_sub_x; + int txt_sub_y; + int txt_sub_size; + int txt_sub_weight; + + int scroll; + + DWORD txt_main_clr; + DWORD txt_sub_clr; + + char bmp_main[32]; + char bmp_sub[32]; + char bmp_icon[32]; + + char fnt_main[32]; + char fnt_sub[32]; +}; + + +class WindowManager : public gcGUIBase +{ +public: + + + WindowManager(HWND hAppMainWindow, HINSTANCE hInst, bool bWindowed); + ~WindowManager(); + + + // =============================================================================================== + // + bool MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + void Animate(); + int GetWidth() const { return width; } + HWND GetMainWindow() const { return hMainWnd; } + HINSTANCE GetInstance() const { return hInst; } + SideBar * GetSideBar(HWND hWnd); + SideBar * NewSideBar(Node *pAN); + void ReleaseSideBar(SideBar *pSB); + void CloseWindow(Node *pAp); + bool IsWindowed() const { return bWin; } + bool IsOK() const; + bool DoesExist(Node *pn); + void UpdateStatus(HNODE hNode); + + + // =============================================================================================== + // + HBITMAP GetBitmap(int id) const; + HFONT GetAppTitleFont() const { return hAppFont; } + HFONT GetSubTitleFont() const { return hSubFont; } + + + // =============================================================================================== + // + SideBar * StartDrag(Node *pAN, int x, int y); + void BeginMove(Node *pAN, int x, int y); + void MouseMoved(int x, int y); + void Drag(int x, int y); + void SetOffset(int x, int y); + void EndDrag(); + SideBar * GetDraged() const { return sbDrag; } + SideBar * GetDragSource() const { return sbDragSrc; } + SideBar * FindDestination(); + tInsert * InsertList() { return &qInsert; } + + + + // =============================================================================================== + // gcGUIBase virtual overrides + // =============================================================================================== + + HNODE RegisterApplication(gcGUIApp *pApp, const char *label, HWND hDlg, DWORD docked, DWORD color); + HNODE RegisterSubsection(HNODE hNode, const char *label, HWND hDlg, DWORD color); + void UpdateStatus(HNODE hNode, const char *label, HWND hDlg, DWORD color); + bool UnRegister(HNODE hNode); + bool IsOpen(HNODE hNode); + void OpenNode(HNODE hNode, bool bOpen = true); + void DisplayWindow(HNODE hNode, bool bShow = true); + HFONT GetFont(int id); + HNODE GetNode(HWND hDlg); + HWND GetDialog(HNODE hNode); + void UpdateSize(HWND hDlg); + + // =============================================================================================== + + Conf cfg; + +private: + + tInsert qInsert; + vector sbList; + SideBar * sbDrag; + SideBar * sbDragSrc; + SideBar * sbDest; + POINT ptOffset; + int width; + HWND hMainWnd; + HINSTANCE hInst; + HFONT hAppFont; + HFONT hSubFont; + HBITMAP hTitle; + HBITMAP hIcons; + HBITMAP hSub; + bool bWin; + DWORD Cmd; +}; + + + + +#endif diff --git a/OVP/VulkanClient/ZTreeMgr.cpp b/OVP/VulkanClient/ZTreeMgr.cpp new file mode 100644 index 000000000..51797842c --- /dev/null +++ b/OVP/VulkanClient/ZTreeMgr.cpp @@ -0,0 +1,218 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// vk Client module +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// -------------------------------------------------------------- +// ZTreeMgr.cpp +// Class ZTreeMgr (implementation) +// +// Manage compressed and packed tile trees for planetary surface +// and cloud layers. +// -------------------------------------------------------------- + +#include "ZTreeMgr.h" +#include "OrbiterAPI.h" + +// ======================================================================= +// File header for compressed tree files + +TreeFileHeader::TreeFileHeader () : + magic(MAKEFOURCC('T','X',1,0)), + size(sizeof(TreeFileHeader)), dataOfs(sizeof(TreeFileHeader)), + flags(0), nodeCount(0), dataLength(0) +{ + rootPos1 = rootPos2 = rootPos3 = rootPos4[0] = rootPos4[1] = (DWORD)-1; +} + +// ----------------------------------------------------------------------- + +size_t TreeFileHeader::fwrite (FILE *f) +{ + return ::fwrite(this, sizeof(TreeFileHeader), 1, f); +} + +// ----------------------------------------------------------------------- + +bool TreeFileHeader::fread (FILE *f) +{ + DWORD mg, sz; + + if (::fread(&mg, sizeof(DWORD), 1, f) != 1 || mg != magic) { return false; } + if (::fread(&sz, sizeof(DWORD), 1, f) != 1 || sz != size) { return false; } + ::fread(&flags, sizeof(DWORD), 1, f); + ::fread(&dataOfs, sizeof(DWORD), 1, f); + ::fread(&dataLength, sizeof(__int64), 1, f); + ::fread(&nodeCount, sizeof(DWORD), 1, f); + ::fread(&rootPos1, sizeof(DWORD), 1, f); + ::fread(&rootPos2, sizeof(DWORD), 1, f); + ::fread(&rootPos3, sizeof(DWORD), 1, f); + ::fread(rootPos4, sizeof(DWORD), 2, f); + return true; +} + +// ======================================================================= +// Tree table of contents + +TreeTOC::TreeTOC () : + ntree(0), ntreebuf(0), totlength(0), + tree(NULL) +{ +} + +// ----------------------------------------------------------------------- + +TreeTOC::~TreeTOC () +{ + if (ntreebuf) { + delete []tree; + tree = NULL; + } +} + +// ----------------------------------------------------------------------- + +size_t TreeTOC::fread (DWORD size, FILE *f) +{ + if (ntreebuf != size) { + TreeNode *tmp = new TreeNode[size]; + if (ntreebuf) { delete []tree; } + tree = tmp; + ntree = ntreebuf = size; + } + return ::fread(tree, sizeof(TreeNode), size, f); +} + +// ======================================================================= +// ZTreeMgr class: manage a single layer tree for a planet + +ZTreeMgr *ZTreeMgr::CreateFromFile (const char *PlanetPath, Layer _layer) +{ + ZTreeMgr *mgr = new ZTreeMgr(PlanetPath, _layer); + if (!mgr->TOC().size()) { + delete mgr; + mgr = NULL; + } + return mgr; +} + +// ----------------------------------------------------------------------- + +ZTreeMgr::ZTreeMgr (const char *PlanetPath, Layer _layer) : + layer(_layer), treef(NULL) +{ + int len = lstrlen(PlanetPath) + 1; + path = new char[len]; + strcpy_s(path, len, PlanetPath); + OpenArchive(); +} + +// ----------------------------------------------------------------------- + +ZTreeMgr::~ZTreeMgr () +{ + delete []path; + path = NULL; + if (treef) { fclose(treef); } +} + +// ----------------------------------------------------------------------- + +bool ZTreeMgr::OpenArchive () +{ + const char *name[6] = { "Surf", "Mask", "Elev", "Elev_mod", "Label", "Cloud" }; + char fname[MAX_PATH]; + sprintf_s (fname, MAX_PATH, "%s\\Archive\\%s.tree", path, name[layer]); + if (fopen_s(&treef, fname, "rb")) { + return false; + } + TreeFileHeader tfh; + if (!tfh.fread(treef)) { + fclose(treef); + treef = NULL; + return false; + } + rootPos1 = tfh.rootPos1; + rootPos2 = tfh.rootPos2; + rootPos3 = tfh.rootPos3; + for (int i = 0; i < 2; ++i) { + rootPos4[i] = tfh.rootPos4[i]; + } + dofs = (__int64)tfh.dataOfs; + + if (!toc.fread(tfh.nodeCount, treef)) { + fclose(treef); + treef = NULL; + return false; + } + toc.totlength = tfh.dataLength; + + return true; +} + +// ----------------------------------------------------------------------- + +DWORD ZTreeMgr::Idx (int lvl, int ilat, int ilng) +{ + if (lvl <= 4) { + return (lvl == 1 ? rootPos1 : lvl == 2 ? rootPos2 : lvl == 3 ? rootPos3 : rootPos4[ilng]); + } else { + int plvl = lvl-1; + int pilat = ilat/2; + int pilng = ilng/2; + DWORD pidx = Idx(plvl, pilat, pilng); + if (pidx == (DWORD)-1) { return pidx; } + int cidx = ((ilat&1) << 1) + (ilng&1); + return toc[pidx].child[cidx]; + } +} + +// ----------------------------------------------------------------------- + +DWORD ZTreeMgr::ReadData (DWORD idx, BYTE **outp) +{ + if (idx == (DWORD)-1) { return 0; } // sanity check + + DWORD esize = NodeSizeInflated(idx); + if (!esize) {// node doesn't have data, but has descendants with data + return 0; + } + + if (_fseeki64(treef, toc[idx].pos+dofs, SEEK_SET)) { + return 0; + } + + DWORD zsize = NodeSizeDeflated(idx); + BYTE *zbuf = new BYTE[zsize]; + fread(zbuf, 1, zsize, treef); + + BYTE *ebuf = new BYTE[esize]; + + DWORD ndata = Inflate(zbuf, zsize, ebuf, esize); + delete []zbuf; + zbuf = NULL; + + if (!ndata) { + delete []ebuf; + ebuf = NULL; + } + *outp = ebuf; + return ndata; +} + +// ----------------------------------------------------------------------- + +DWORD ZTreeMgr::Inflate (const BYTE *inp, DWORD ninp, BYTE *outp, DWORD noutp) +{ + return oapiInflate(inp, ninp, outp, noutp); +} + +// ----------------------------------------------------------------------- + +void ZTreeMgr::ReleaseData (BYTE *data) +{ + delete []data; + data = NULL; +} diff --git a/OVP/VulkanClient/ZTreeMgr.h b/OVP/VulkanClient/ZTreeMgr.h new file mode 100644 index 000000000..471ccb48e --- /dev/null +++ b/OVP/VulkanClient/ZTreeMgr.h @@ -0,0 +1,144 @@ +// ============================================================== +// ORBITER VISUALISATION PROJECT (OVP) +// vk Client module +// Copyright (C) 2006-2016 Martin Schweiger +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +// -------------------------------------------------------------- +// ZTreeMgr.h +// Class ZTreeMgr (interface) +// +// Manage compressed and packed tile trees for planetary surface +// and cloud layers. +// -------------------------------------------------------------- + +#ifndef __ZTREEMGR_H +#define __ZTREEMGR_H + +#include +#include + +/// \defgroup ztree Z-Tree management for tile archive access +/// @{ + +// ======================================================================= +/** + * \brief Tree node structure + */ +struct TreeNode { + __int64 pos; ///< file position of node data + DWORD size; ///< data block size [bytes] + DWORD child[4]; ///< array index positions of the children ((DWORD)-1=no child) + + TreeNode () : pos(0), size(0) { + for (int i = 0; i < ARRAYSIZE(child); ++i) child[i] = (DWORD)-1; + } +}; + + +// ======================================================================= +/** + * \brief File header for compressed tree files + */ +class TreeFileHeader { + friend class ZTreeMgr; + +public: + TreeFileHeader (); + + inline size_t fwrite (FILE *f); + bool fread (FILE *f); + +private: + DWORD magic; ///< file ID and version + DWORD size; ///< header size [bytes] + DWORD flags; ///< bit flags + DWORD dataOfs; ///< file offset of start of data block (header + TOC) + __int64 dataLength; ///< total length of compressed data block + DWORD nodeCount; ///< total number of tree nodes + DWORD rootPos1; ///< index of level-1 tile ((DWORD)-1 for not present) + DWORD rootPos2; ///< index of level-2 tile ((DWORD)-1 for not present) + DWORD rootPos3; ///< index of level-3 tile ((DWORD)-1 for not present) + DWORD rootPos4[2]; ///< index of the level-4 tiles (quadtree roots; (DWORD)-1 for not present) +}; + + +// ======================================================================= +/** + * \brief Tree table of contents + */ +class TreeTOC { + friend class ZTreeMgr; + +public: + TreeTOC (); + ~TreeTOC (); + + size_t fread (DWORD size, FILE *f); + DWORD size () const { return ntree; } + inline const TreeNode &operator[] (int idx) const { return tree[idx]; } + + inline DWORD NodeSizeDeflated (DWORD idx) const + { return (DWORD)((idx < ntree-1 ? tree[idx+1].pos : totlength) - tree[idx].pos); } + + inline DWORD NodeSizeInflated (DWORD idx) const { return tree[idx].size; } + +private: + TreeNode *tree; ///< array containing all tree node entries + DWORD ntree; ///< number of entries + DWORD ntreebuf; ///< array size + __int64 totlength; ///< total data size (deflated) +}; + + +// ======================================================================= +/** + * \brief ZTreeMgr class: manage a single layer tree for a planet + */ +class ZTreeMgr { +public: + enum Layer { LAYER_SURF, LAYER_MASK, LAYER_ELEV, LAYER_ELEVMOD, LAYER_LABEL, LAYER_CLOUD }; + static ZTreeMgr *CreateFromFile (const char *PlanetPath, Layer _layer); + + // Disable copy construct & copy assign + ZTreeMgr (ZTreeMgr const&) = delete; + ZTreeMgr& operator= (ZTreeMgr const&) = delete; + + ZTreeMgr (const char *PlanetPath, Layer _layer); + ~ZTreeMgr (); + + inline const TreeTOC &TOC () const { return toc; } + + DWORD Idx (int lvl, int ilat, int ilng); + // return the array index of an arbitrary tile ((DWORD)-1: not present) + + DWORD ReadData (DWORD idx, BYTE **outp); + + inline DWORD ReadData (int lvl, int ilat, int ilng, BYTE **outp) + { return ReadData(Idx(lvl, ilat, ilng), outp); } + + void ReleaseData (BYTE *data); + + inline DWORD NodeSizeDeflated (DWORD idx) const { return toc.NodeSizeDeflated(idx); } + inline DWORD NodeSizeInflated (DWORD idx) const { return toc.NodeSizeInflated(idx); } + +protected: + bool OpenArchive (); + inline DWORD Inflate (const BYTE *inp, DWORD ninp, BYTE *outp, DWORD noutp); + +private: + char *path; ///< file path of the tree-file + Layer layer; ///< layer type (enum) + FILE *treef; ///< file pointer to tree-file + TreeTOC toc; ///< tree table of contents + DWORD rootPos1; ///< index of level-1 tile ((DWORD)-1 for not present) + DWORD rootPos2; ///< index of level-2 tile ((DWORD)-1 for not present) + DWORD rootPos3; ///< index of level-3 tile ((DWORD)-1 for not present) + DWORD rootPos4[2]; ///< index of the level-4 tiles (quadtree roots; (DWORD)-1 for not present) + __int64 dofs; +}; + +/// @} + +#endif // !__ZTREEMGR_H diff --git a/OVP/VulkanClient/gcConst.cpp b/OVP/VulkanClient/gcConst.cpp new file mode 100644 index 000000000..fd2fb7e88 --- /dev/null +++ b/OVP/VulkanClient/gcConst.cpp @@ -0,0 +1,361 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2013-2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + + +#include +#include "MathAPI.h" +#include +#include "gcConst.h" +#include "Surface.h" +#include "Mesh.h" +#include "Client.h" + +extern class vkClient *g_client; + +// =============================================================================================== +// Custom SwapChain Interface +// =============================================================================================== +// +HSWAP gcConst::RegisterSwap(HWND hWnd, HSWAP hData, int AA) +{ + return gcCore::RegisterSwap(hWnd, hData, AA); +} + +// =============================================================================================== +// +void gcConst::FlipSwap(HSWAP hSwap) +{ + gcCore::FlipSwap(hSwap); +} + +// =============================================================================================== +// +SURFHANDLE gcConst::GetRenderTarget(HSWAP hSwap) +{ + return gcCore::GetRenderTarget(hSwap); +} + +// =============================================================================================== +// +void gcConst::ReleaseSwap(HSWAP hSwap) +{ + gcCore::ReleaseSwap(hSwap); +} + + + + +// =============================================================================================== +// Custom Camera Interface +// =============================================================================================== +// +CAMERAHANDLE gcConst::SetupCustomCamera(CAMERAHANDLE hCam, OBJHANDLE hVessel, VECTOR3 &pos, VECTOR3 &dir, VECTOR3 &up, double fov, SURFHANDLE hSurf, DWORD flags) +{ + return gcCore::SetupCustomCamera(hCam, hVessel, pos, dir, up, fov, hSurf, flags); +} + +// =============================================================================================== +// +void gcConst::CustomCameraOnOff(CAMERAHANDLE hCam, bool bOn) +{ + gcCore::CustomCameraOnOff(hCam, bOn); +} + +// =============================================================================================== +// +int gcConst::DeleteCustomCamera(CAMERAHANDLE hCam) +{ + return gcCore::DeleteCustomCamera(hCam); +} + + + + + + + +// =============================================================================================== +// SketchPad Interface +// =============================================================================================== + + +// =============================================================================================== +// +int gcConst::SketchpadVersion(Sketchpad* pSkp) +{ + return gcCore::SketchpadVersion(pSkp); +} + +// =============================================================================================== +// +oapi::Font* gcConst::CreateSketchpadFont(int height, char* face, int width, int weight, int gcFontStyle, float spacing) +{ + FontStyle sty = FontStyle::FONT_NORMAL; + if (gcFontStyle & gcFont::ITALIC) sty = (FontStyle)(sty | FontStyle::FONT_ITALIC); + if (gcFontStyle & gcFont::UNDERLINE) sty = (FontStyle)(sty | FontStyle::FONT_UNDERLINE); + if (gcFontStyle & gcFont::STRIKEOUT) sty = (FontStyle)(sty | FontStyle::FONT_STRIKEOUT); + if (gcFontStyle & gcFont::CRISP) sty = (FontStyle)(sty | FontStyle::FONT_CRISP); + if (gcFontStyle & gcFont::ANTIALIAS) sty = (FontStyle)(sty | FontStyle::FONT_ANTIALIAS); + return gcCore::CreateSketchpadFont(height, face, width, weight, sty, spacing); +} + + +// =============================================================================================== +// +HPOLY gcConst::CreatePoly(HPOLY hPoly, const FVECTOR2 *pt, int npt, DWORD flags) +{ + return gcCore::CreatePoly(hPoly, pt, npt, flags); +} + + +// =============================================================================================== +// +HPOLY gcConst::CreateTriangles(HPOLY hPoly, const gcCore::clrVtx *pt, int npt, DWORD flags) +{ + return gcCore::CreateTriangles(hPoly, pt, npt, flags); +} + + +// =============================================================================================== +// +void gcConst::DeletePoly(HPOLY hPoly) +{ + gcCore::DeletePoly(hPoly); +} + + +// =============================================================================================== +// +DWORD gcConst::GetTextLength(oapi::Font *hFont, const char *pText, int len) +{ + return gcCore::GetTextLength(hFont, pText, len); +} + + +// =============================================================================================== +// +DWORD gcConst::GetCharIndexByPosition(oapi::Font *hFont, const char *pText, int pos, int len) +{ + return gcCore::GetCharIndexByPosition(hFont, pText, pos, len); +} + + +// =============================================================================================== +// +bool gcConst::RegisterRenderProc(__gcRenderProc proc, DWORD flags, void *pParam) +{ + return g_client->RegisterRenderProc(proc, flags, pParam); +} + + + + +// =============================================================================================== +// Mesh interface functions +// =============================================================================================== +// +int gcConst::GetMatrix(int matrix_id, OBJHANDLE hVessel, DWORD mesh, DWORD group, FMATRIX4 *pMat) +{ + return gcCore::GetMatrix((gcCore::MatrixId)matrix_id, hVessel, mesh, group, pMat); +} + + +// =============================================================================================== +// +int gcConst::SetMatrix(int matrix_id, OBJHANDLE hVessel, DWORD mesh, DWORD group, const FMATRIX4 *pMat) +{ + return gcCore::SetMatrix((gcCore::MatrixId)matrix_id, hVessel, mesh, group, pMat); +} + + +// =============================================================================================== +// +int gcConst::MeshMaterial(DEVMESHHANDLE hMesh, DWORD idx, int prop, FVECTOR4* value, bool bSet) +{ + MatProp mat; + if (prop == MESHM_DIFFUSE) mat = MatProp::Diffuse; + if (prop == MESHM_AMBIENT) mat = MatProp::Ambient; + if (prop == MESHM_SPECULAR) mat = MatProp::Specular; + if (prop == MESHM_EMISSION) mat = MatProp::Light; + if (prop == MESHM_EMISSION2) mat = MatProp::Emission; + if (prop == MESHM_REFLECT) mat = MatProp::Reflect; + if (prop == MESHM_ROUGHNESS) mat = MatProp::Smooth; + if (prop == MESHM_FRESNEL) mat = MatProp::Fresnel; + if (prop == MESHM_METALNESS) mat = MatProp::Metal; + if (prop == MESHM_SPECIALFX) mat = MatProp::SpecialFX; + + if (bSet) return g_client->clbkSetMeshMaterialEx(hMesh, idx, mat, value); + return g_client->clbkMeshMaterialEx(hMesh, idx, mat, value); +} + + +// =============================================================================================== +// +DEVMESHHANDLE gcConst::GetDevMesh(MESHHANDLE hMesh) +{ + return g_client->GetDevMesh(hMesh); +} + +// =============================================================================================== +// +DEVMESHHANDLE gcConst::LoadDevMeshGlobal(const char* file_name, bool bUseCache) +{ + MESHHANDLE hMesh = oapiLoadMeshGlobal(file_name); + return g_client->GetDevMesh(hMesh); +} + +// =============================================================================================== +// +void gcConst::ReleaseDevMesh(DEVMESHHANDLE hMesh) +{ + delete (vkMesh*)(hMesh); +} + +// =============================================================================================== +// +void gcConst::RenderMesh(DEVMESHHANDLE hMesh, const oapi::FMATRIX4* pWorld) +{ + gcCore::RenderMesh(hMesh, pWorld); +} + +// =============================================================================================== +// +bool gcConst::PickMesh(PickMeshStruct* pm, DEVMESHHANDLE hMesh, const FMATRIX4* pWorld, short x, short y) +{ + return gcCore::PickMesh((gcCore::PickMeshStruct*)pm, hMesh, pWorld, x, y); +} + + + + + + + +// =============================================================================================== +// Custom Render Interface +// =============================================================================================== +// +// =============================================================================================== +// +SURFHANDLE gcConst::LoadSurface(const char* fname, DWORD flags) +{ + return g_client->clbkLoadSurface(fname, flags); +} + + +// =============================================================================================== +// +void gcConst::RenderLines(const FVECTOR3* pVtx, const WORD* pIdx, int nVtx, int nIdx, const FMATRIX4* pWorld, DWORD color) +{ + gcCore::RenderLines(pVtx, pIdx, nVtx, nIdx, pWorld, color); +} + + + + +// =============================================================================================== +// Some Helper Functions +// =============================================================================================== +// +// =============================================================================================== +// +gcConst::PickGround gcConst::ScanScreen(int scr_x, int scr_y) +{ + gcCore::PickGround pg = gcCore::ScanScreen(scr_x, scr_y); + gcConst::PickGround tg; + + tg.Bounds = pg.Bounds; + tg.lat = pg.lat; + tg.lng = pg.lng; + tg.emax = pg.emax; + tg.emin = pg.emin; + tg.msg = pg.msg; + tg.dist = pg.dist; + tg.elev = pg.elev; + tg.level = pg.level; + tg.hTile = pg.hTile; + tg.normal = pg.normal; + tg.pos = pg.pos; + return tg; +} + + +// =============================================================================================== +// +void gcConst::GetSystemSpecs(SystemSpecs* sp, int size) +{ + if (size == sizeof(SystemSpecs)) { + sp->DisplayMode = g_client->GetFramework()->GetDisplayMode(); + sp->MaxTexSize = g_client->GetHardwareCaps()->MaxTextureWidth; + sp->MaxTexRep = g_client->GetHardwareCaps()->MaxTextureRepeat; + sp->gcAPIVer = BuildDate(); + } +} + + +// =============================================================================================== +// +bool gcConst::RegisterGenericProc(__gcGenericProc proc, DWORD id, void* pParam) +{ + return g_client->RegisterGenericProc(proc, id, pParam); +} + + +// =============================================================================================== +// +HBITMAP gcConst::LoadBitmapFromFile(const char* fname) +{ + return g_client->gcReadImageFromFile(fname); +} + + +// =============================================================================================== +// +HWND gcConst::GetRenderWindow() +{ + return g_client->GetRenderWindow(); +} + + +// =============================================================================================== +// +int gcConst::GetElevation(HTILE hTile, double lng, double lat, double *out_elev) +{ + return gcCore2::GetElevation(hTile, lng, lat, out_elev); +} + +// =============================================================================================== +// +DWORD gcConst::Color(const COLOUR4* c) +{ + return FVECTOR4(*c).dword_abgr(); +} + +// =============================================================================================== +// +DWORD gcConst::Color(const oapi::FVECTOR4* c) +{ + return c->dword_abgr(); +} + +// =============================================================================================== +// +COLOUR4 gcConst::Colour4(DWORD dwABGR) +{ + return FVECTOR4(dwABGR); +} diff --git a/OVP/VulkanClient/gcConst.h b/OVP/VulkanClient/gcConst.h new file mode 100644 index 000000000..4a967d94c --- /dev/null +++ b/OVP/VulkanClient/gcConst.h @@ -0,0 +1,677 @@ +// =================================================== +// Copyright (C) 2012-2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + + +#include "OrbiterAPI.h" +#include "DrawAPI.h" +#include "gcCore.h" +#include + +#pragma once + +using namespace std; +using namespace oapi; + + +/// \defgroup MeshMaterialFlags Mesh material flags for gcMeshMaterial function +///@{ +#define MESHM_DIFFUSE 0x01 ///< Stock material +#define MESHM_AMBIENT 0x02 ///< Stock material +#define MESHM_SPECULAR 0x04 ///< Stock material +#define MESHM_EMISSION 0x08 ///< Stock material +#define MESHM_EMISSION2 0x10 ///< vk material +#define MESHM_REFLECT 0x20 ///< vk material +#define MESHM_ROUGHNESS 0x40 ///< vk material +#define MESHM_FRESNEL 0x80 ///< vk material +#define MESHM_METALNESS 0x100 ///< vk material +#define MESHM_SPECIALFX 0x200 ///< vk material +///@} + + +namespace gcMatrix +{ + static const int offset = 1; ///< Set/Get Mesh offset matrix, Also used by VESSEL::ShiftMesh() + static const int mesh = 2; ///< Set/Get Mesh animation matrix, Transforms all the groups in the mesh + static const int group = 3; ///< Set/Get Group animation matrix, Transforms a single group + static const int combined = 4; ///< Get combined Mesh*Group*Offset matrix. (Can't 'set' this) +}; + + +/** +* \brief Flags for +*/ +namespace gcFont +{ + static const int ITALIC = 0x1; + static const int UNDERLINE = 0x2; + static const int STRIKEOUT = 0x4; + static const int CRISP = 0x8; ///< Override app-default, No Antialiasing + static const int ANTIALIAS = 0x10; ///< Override app-default, Use Antialiashing +}; + + +/// \brief Handle to a surface manager's glogal overlay +typedef void * HOVERLAY; +/// \brief Handle to a native DirectX9 surface (Obsolete) +typedef void * HSURFNATIVE; +/// \brief Handle to a planet/surface manager +typedef void * HPLANETMGR; +/// \brief Handle to an instance buffer +typedef void * HINSTBUF; +/// \brief Handle to a surface tile (SurfTile) +typedef void * HTILE; +/// \brief Custom swapchain handle +typedef void * HSWAP; +/// \brief Custom camera handle +typedef void * CAMERAHANDLE; +/// \brief Sketchmesh handle +typedef void * SKETCHMESH; +/// \brief Poly object handle +typedef void * HPOLY; + + +/// \brief Render HUD and Planetarium callback function +typedef void(__cdecl *__gcRenderProc)(oapi::Sketchpad *pSkp, void *pParam); +typedef void(__cdecl *__gcGenericProc)(int iUser, void *pUser, void *pParam); + + +// =========================================================================== +/** +* \class gcCore +* \brief Core class for graphics services +*/ +// =========================================================================== + +class gcConst +{ + +public: + + typedef struct { + int Width; + int Height; + int Mips; + DWORD Flags; + } SurfaceSpecs; + + typedef struct { + WORD Size; ///< sizeof(ElevInfo) + WORD Format; + double Resolution; + } ElevInfo; + + typedef struct { + HTILE pTile; + int Lvl; + int iLng; + int iLat; + } TileCreated; + + typedef struct { + OBJHANDLE hVessel; ///< Handle to a vessel that was clicked + MESHHANDLE mesh; ///< Mesh index that was clicked + int group; ///< Mesh group index that was clicked + float dist; ///< Distance from a camera to a click point + FVECTOR3 normal; ///< Normal vector in local vessel coordinates + FVECTOR3 pos; ///< Position in local vessel coordinates + } PickData; + + typedef struct { + int iUser; ///< Unused variable for user's own purposes. + int grp_inst; ///< Mesh group index or instance number that was clicked + float dist; ///< Distance from a camera to a click point + FVECTOR3 normal; ///< Normal vector in local vessel coordinates + FVECTOR3 pos; ///< Position in local vessel coordinates + } PickMeshStruct; + + typedef struct { + double lng, lat; ///< Longitude and Latitude of the point being clicked + double elev; ///< Elevation of the point being clicked above mean radius + double dist; ///< Distance from a camera to a click point + DRECT Bounds; ///< Tile bounds (i.e. min/max * lng/lat) + FVECTOR3 normal; ///< Normal in ecliptic frame + FVECTOR3 pos; ///< Position from a camera in ecliptic frame + float emax; ///< Max elevation within the tile + float emin; ///< Min elevation within the tile + UINT msg; ///< Zero or (WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_LBUTTONUP, WM_RBUTTONUP) or (WM_MOUSEMOVE, WM_MOUSEWHEEL) + int level; ///< Tile level + int iLng, iLat; ///< Tile Index + HTILE hTile; ///< Tile handle being clicked. WARNING: Tile returned by this data entry can become invalid without notice. + } PickGround; + + typedef struct { + int MaxTexSize; ///< Maximum texture size in pixels + int DisplayMode; ///< 0 = True Fullscreen, 1 = Fullscreen Window, 2 = Windowed + int MaxTexRep; ///< Maximum texture repeat count + DWORD gcAPIVer; ///< gcAPI Build Date 0xYYYYMMDD + } SystemSpecs; + + typedef struct { + HTILE hTile; ///< WARNING: Tile returned by this data entry can become invalid without notice. + FMATRIX4 mWorld; + } RenderTileData; + + + // =========================================================================== + /// \name Custom swap-chain management functions + // =========================================================================== + //@{ + /** + * \brief Create a new custom swap-chain (i.e. Frontbufer/Backbuffer) for a user defined window. + * \param hWnd Handle to a window client rect + * \param hSwap Handle to an existing swap object to resize it. + * \param AA Level of requested anti-aliasing. Valid values are 0, 2, 4, 8 + * \return Handle to a Swap object or NULL in a case of an error + */ + virtual HSWAP RegisterSwap(HWND hWnd, HSWAP hSwap = NULL, int AA = 0); + + /** + * \brief Flip backbuffer to a front + * \param hSwap Handle to a swap object. + */ + virtual void FlipSwap(HSWAP hSwap); + + /** + * \brief Get a backbuffer surface + * \param hSwap Handle to a swap object. + * \return A Handle to a rendering surface (i.e. backbuffer) + */ + virtual SURFHANDLE GetRenderTarget(HSWAP hSwap); + virtual HSURFNATIVE obsolete_GetRenderTargetNative(HSWAP hSwap) { return NULL; } + + /** + * \brief Release a swap object after it's no longer needed. + * \param hSwap Handle to a swap object. + */ + virtual void ReleaseSwap(HSWAP hSwap); + //@} + + + + + // =========================================================================== + /// \name Custom Camera Interface + // =========================================================================== + //@{ + /** + * \brief Delete/Release a custom camera. + * \param hCam camera handle to delete. + * \return zero or an error code if the camera didn't work properly. + * \note Always delete all cameras bound to a render surface before releasing the rendering surface it-self. + */ + virtual int DeleteCustomCamera(CAMERAHANDLE hCam); + + /** + * \brief Toggle camera on and off + * \param hCam camera handle to toggle + * \param bOn true to turn on the camera. + * \note If multiple cameras are sharing the same rendering surface. Flickering will occur if more than one camera is turned on. + */ + virtual void CustomCameraOnOff(CAMERAHANDLE hCam, bool bOn); + + /** + * \brief Create a new custom camera that can be used to render views into a surfaces and textures + * \param hCam camera handle to modify an existing camera or, NULL + * \param hVessel handle to a vessel where the camera is attached to. + * \param vPos camara position in vessel's local coordinate system + * \param vDir camara direction in vessel's local coordinate system. [Unit Vector] + * \param vUp camara up vector. Must be perpendicular to vDir. [Unit Vector] + * \param dFow camera field of view in radians + * \param hSurf rendering surface. Must be created at least with OAPISURFACE_RENDER3D | OAPISURFACE_RENDERTARGET. Multiple cameras can share the same surface. + * \param dwFlags Flags to controls what is drawn and what is not. + * \return Camera handle, or NULL if an error occurred or if the custom camera interface is disabled. + * \note Camera count is unlimited. + * \note Only a cameras attached to currently active vessel are operational and recording. + * \note Having multiple cameras active at the same time doesn't impact in a frame rate, however, camera refresh rates are reduced. + */ + virtual CAMERAHANDLE SetupCustomCamera(CAMERAHANDLE hCam, OBJHANDLE hVessel, VECTOR3& vPos, VECTOR3& vDir, VECTOR3& vUp, double dFov, SURFHANDLE hSurf, DWORD dwFlags = 0xFF); + //@} + + + + + // =========================================================================== + /// \name Sketchpad related functions + // =========================================================================== + //@{ + /** + * \brief Get the sketchpad version + * \param pSkp handle to a sketchpad interface. + * \return Currently returns 2 or (1 in some very special cases). + */ + virtual int SketchpadVersion(Sketchpad *pSkp); + + /** + * \brief Get a Sketchpad for a native DirectX 9 surface + * \param hSrf handle to a surface + * \param hDep handle to optional depth stencil surface + * \return a pointer to a new sketchpad interface or NULL if an error occurs. + */ + virtual Sketchpad* obsolete_GetSketchpadNative(HSURFNATIVE hSrf, HSURFNATIVE hDep = NULL) { return NULL; } + + /** + * \brief Release a native sketchpad interface acquired by GetSketchpadNative() + * \param pSkp handle to a sketchpad interface to release. + */ + virtual void obsolete_ReleaseSketchpadNative(Sketchpad *pSkp) {} + + /** + * \brief Load a mesh from a harddrive to be used with Sketchpad2::SketchMesh + * \param name Name of the mesh file without ".msh" identifier. + * \sa gcDeleteSketchMesh + * \note SKETCHMESH handle isn't compatible with MESHHANDLE nor DEVMESHHANDLE. + */ + virtual SKETCHMESH obsolete_LoadSketchMesh(const char *name) { return NULL; } + + /** + * \brief Delete a mesh previously loaded with gcLoadSketchMesh + * \sa gcLoadSketchMesh + */ + virtual void obsolete_DeleteSketchMesh(SKETCHMESH hMesh) {} + + /** + * \brief Create or Update a polyline composed form piecewise straight segments. + * \param hPoly Handle to a polyline to be updated or NULL to create a new one. + * \param pt list of vertex points. + * \param npt number of points in the list. + * \param flags additional PolyFlags flags + * \sa gcDeletePoly, Sketchpad2::DrawPoly() + * \note Poly objects should be created during initialization not for every frame or update. Updating existing (pre created) poly object is pretty fast. + * \note During update number of points must be equal or smaller than during initial creation of poly object. + */ + virtual HPOLY CreatePoly(HPOLY hPoly, const oapi::FVECTOR2* pt, int npt, DWORD flags = 0); + + + /** + * \brief Create or Update a triangle object. + * \param hPoly Handle to a triangle to be updated or NULL to create a new one. + * \param pt list of vertex points. + * \param npt number of points in the list. + * \param flags additional flags (see below) + * \sa gcDeletePoly, Sketchpad2::DrawPoly() + * \note Poly objects should be created during initialization not for every frame or update. Updating existing (pre created) poly object is pretty fast. + * \note During update number of points must be equal or smaller than during initial creation of poly object. + * \note Flags: + * \note PF_TRIANGLES Each independent triangle is composed from three vertex points. ("npt" must be multiple of 3) + * \note PF_FAN Triangle fan. The first vertex is in a centre of the fan/circle and other lie at the edge. ("npt" must be "number of triangles" + 2) + * \note PF_STRIP Is build from quads. Where each quad requires two vertices. ("npt" must be "number of quads" * 2 + 2) + */ + virtual HPOLY CreateTriangles(HPOLY hPoly, const gcCore::clrVtx *pt, int npt, DWORD flags); + + + /** + * \brief Deletes a polyline created with gcCreatePolyPolyline() + * \param hPoly Handle to a polyline to be deleted + * \sa gcCreatePolyline + */ + virtual void DeletePoly(HPOLY hPoly); + + /** + * \brief Compute a length of a text string + * \param hFont a Pointer into a font + * \param pText a Pointer into a text string + * \param len a Length of the text string to process. -1 will scan to a NULL terminator. + */ + virtual DWORD GetTextLength(oapi::Font* hFont, const char* pText, int len = -1); + + /** + * \brief Find index of nearest "cap" between charters in specified location. (i.e. distance from start of the string in pixels) + * \param hFont a Pointer into a font + * \param pText a Pointer into a text line + * \param pos a Position in pixels from start of the string + * \param len a Length of the text line to process. -1 will process to a NULL terminator. + * \return index from 0 to number of charters. For just one char it can be either "0" or "1" depending which side is closer to "pos". + * \note This is used for finding a spot for a "cursor" when a text string is clicked with mouse. + */ + virtual DWORD GetCharIndexByPosition(oapi::Font* hFont, const char* pText, int pos, int len = -1); + + /** + * \brief This function will register a custom render callback function + * \param proc function to be called when render event occur + * \param id render event id + * \param pParam a pointer to user data (to a class for an example) + * \return false if an error occurred, true otherwise. + */ + virtual bool RegisterRenderProc(__gcRenderProc proc, DWORD id, void* pParam); + + /** + * \brief Create a Font + * \param height Font height + * \param face Name of the font + * \param width Width of the font (0 for default aspect ration) + * \param weight Font thickness (400 for default weight) + * \param style A combination of \see gcFont flags (0 for default) + * \param spacing A spacing between charters in a string (0.0f for default) + * \return A pointer to a created or pre-existing font or NULL in a case of an error. + */ + virtual oapi::Font *CreateSketchpadFont(int height, char *face, int width = 0, int weight = 400, int gcFontStyle = 0, float spacing = 0.0f); + //@} + + + + + + // =========================================================================== + /// \name Mesh interface functions + // =========================================================================== + //@{ + /** + * \brief This function will register a custom render callback function + * \param hMesh Handle to a devmesh containing the material + * \param idx Material index + * \param prop material property identifier (\ref MeshMaterialFlags) + * \param value a pointer to FVECTOR4 structure containing/receiving the data, or \e NULL to reset a default value or to unspecify a property. + * \param bSet \e true to set material value, \e false to get a material value + * \return -4 = Invalid handle \n -3 = Unknown property flag \n -2 = Property not specified cannot get it \n -1 = Index out of range \n 0 = Success + */ + virtual int MeshMaterial(DEVMESHHANDLE hMesh, DWORD idx, int prop, FVECTOR4 *value, bool bSet); + + /** + * \brief A Function to get a mesh transformation/animation matrix. + * \param matrix_id Id of the matrix to get. One of gcMatrix::xxx datatypes. + * \param hVessel Vessel object handle. + * \param mesh Mesh index + * \param group Group index + * \param pMat A pointer to FMATRIX4 struct for receiving the data. + * \return 0 = on Success, or error code. + */ + virtual int GetMatrix(int matrix_id, OBJHANDLE hVessel, DWORD mesh, DWORD group, oapi::FMATRIX4* pMat); + + + /** + * \brief A Function to set a mesh transformation/animation matrix. Do not use this function for animated parts/meshes. + * \param matrix_id Id of the matrix to set. One of gcMatrix::xxx datatypes. + * \param hVessel Vessel object handle. + * \param mesh Mesh index + * \param group Group index + * \param pMat A pointer to FMATRIX4 containing the data to set. + * \return 0 = on Success, or error code. + */ + virtual int SetMatrix(int matrix_id, OBJHANDLE hVessel, DWORD mesh, DWORD group, const oapi::FMATRIX4* pMat); + //@} + + + + + // =========================================================================== + /// \name Some Helper Functions + // =========================================================================== + //@{ + /** + * \brief Get some system information + * \param sp A pointer to SystemSpecs struct + * \param size sizeof(SystemSpecs) + */ + virtual void GetSystemSpecs(SystemSpecs* sp, int size); + + /** + * \brief Convert a floating point color to DWORD color value + * \param c A pointer to a color + * \return DWORD color in 0xAABBGGRR + * \note Alpha will range from 1 to 255. Zero is never returned because of backwards compatibility issues 0-alpha is mapped to 255 + */ + virtual DWORD Color(const COLOUR4 *c); + + /** + * \brief Convert a floating point color to DWORD color value + * \param c A pointer to a color + * \return DWORD color in 0xAABBGGRR + * \note Alpha will range from 1 to 255. Zero is never returned because of backwards compatibility issues 0-alpha is mapped to 255 + */ + virtual DWORD Color(const oapi::FVECTOR4 *c); + + /** + * \brief Convert a DWORD color to floating point COLOUR4 value + * \param dwABGR A color in 0xAABBGGRR + * \return COLOUR4 + * \note Alpha will range from 1 to 255. Zero is never used because of backwards compatibility issues 0-alpha is mapped to 255 + */ + virtual COLOUR4 Colour4(DWORD dwABGR); + + + /** + * \brief Get Surface Attributes (e.g. OAPISURFACE_TEXTURE) + * \param hSurf handle to a surface + * \param bCreation if true return creation time attributes, if false return current attributes + * \return Surface attributes + */ + virtual DWORD obsolete_GetSurfaceAttribs(SURFHANDLE hSurf, bool bCreation = false) { return 0; } + + /** + * \brief Convert an existing surface to an other type. + * \param hSurf handle to a surface + * \param attrib new attributes + */ + virtual void obsolete_ConvertSurface(SURFHANDLE hSurf, DWORD attrib) {} + + /** + * \brief Load a texture into a specific type of a surface + * \param fname name of a texture to be loaded. + * \param flags surface attributes (see: OAPISURFACE_x flags) + * \return surface handle or NULL in a case of an error + */ + virtual SURFHANDLE LoadSurface(const char *fname, DWORD flags); + + /** + * \brief Load a bitmap from file (*.bmp *.png *.jpg *.gif) + * \param fname name of the file to be loaded. + * \return Bitmap handle of NULL in a case of an error + */ + virtual HBITMAP LoadBitmapFromFile(const char* fname); + + /** + * \brief Get render window handle + * \return Render window handle + */ + virtual HWND GetRenderWindow(); + + /** + * \brief Register generic callback function + * \param proc function to be called when event occur + * \param id requested callback event id + * \param pParam a pointer to user data (to a class for an example) + * \return false if an error occurred, true otherwise. + */ + virtual bool RegisterGenericProc(__gcGenericProc proc, DWORD id, void* pParam); + //@} + + + + // =========================================================================== + /// \name Planetary surface interface + /// Graphics client maintains a tile database for a tiles used in rendering + /// This API can't access tile data outside visual range + // =========================================================================== + //@{ + virtual HPLANETMGR obsolete_GetPlanetManager(OBJHANDLE hPlanet) { return NULL; } + virtual SURFHANDLE obsolete_SetTileOverlay(HTILE hTile, const SURFHANDLE hOverlay) { return NULL; } + virtual HOVERLAY obsolete_AddGlobalOverlay(HPLANETMGR hMgr, VECTOR4 mmll, const SURFHANDLE hOverlay = NULL, HOVERLAY hOld = NULL) { return NULL; } + + /** + * \brief Find a tile from a specified coordinates. Limited to a highest allocated level found from memory. + * \param hMgr handle to a tile/planet manager + * \param lng longitude of the location. + * \param lng latitude of the location. + * \param maxlevel highest level to search, -1 = Current render level. + * \return NULL, or a tile handle at the current render resolution + * \note WARNING: Tile returned by this function can become invalid without notice. + */ + virtual PickGround obsolete_GetTileData(HPLANETMGR hMgr, double lng, double lat, int maxlevel = -1) + { + PickGround pg; memset(&pg, 0, sizeof(PickGround)); + return pg; + } + + virtual HTILE obsolete_GetTile(HPLANETMGR hMgr, double lng, double lat, int maxlevel = -1) { return NULL; } + + + /** + * \brief Find a tile from a specified coordinates. Limited to a highest allocated level found from memory. + * \param hMgr handle to a tile/planet manager + * \param iLng longitude index + * \param iLng latitude index + * \param level level of the tile + * \param flags what to search (see gcTileFlags) + * \return NULL, or a tile handle at the current render resolution + * \note WARNING: Tile returned by this function can become invalid without notice. + */ + virtual bool obsolete_HasTileData(HPLANETMGR hMgr, int iLng, int iLat, int level, int flags) { return false; } + virtual SURFHANDLE obsolete_SeekTileTexture(HPLANETMGR hMgr, int iLng, int iLat, int level, int flags = 3, void *reserved = NULL) { return NULL; } + virtual void * obsolete_SeekTileElevation(HPLANETMGR hMgr, int iLng, int iLat, int level, int flags, ElevInfo *pEI) { return NULL; } + + + /** + * \brief Find a tile from specified coordinates. + * \param scr_x screen space x-coordinate. + * \param scr_y screen space y-coordinate. + * \return PickGround data structure, all members are zero if ray doesn't intersect ground. + */ + virtual PickGround ScanScreen(int scr_x, int scr_y); + + /** + * \brief Seek surface elevation from within the tile + * \param hTile handle to a tile + * \param lng geocentric longitude + * \param lat geocentric latitude + * \param out_elev pointer to float receiving the elevation above mean radius. + * \return 1 = Nominal, 0 = Tile Invisible but valid, -1 = (lng,lat) out of bounds, -3 = Fail + */ + virtual int GetElevation(HTILE hTile, double lng, double lat, double *out_elev); + //@} + + + + + + + // =========================================================================== + /// \name Native Object Interface + // =========================================================================== + //@{ + + /** + * \brief Load a file into native DirectX. Valid formats are (*.dds, *.jpg, *.bmp, *.png) + * \param file filename. + * \param flags a combination of OAPISURFACE_ flags. + * \return NULL in a case of failure. + */ + virtual HSURFNATIVE obsolete_LoadSurfaceNative(const char *file, DWORD flags) { return NULL; } + + /** + * \brief Create a native DirectX 9 Surface + * \param width surface width in pixels + * \param height surface height in pixels + * \param flags a combination of OAPISURFACE_ flags. + * \return Surface handle or NULL in a case of a failure. + */ + virtual HSURFNATIVE obsolete_CreateSurfaceNative(int width, int height, DWORD flags) { return NULL; } + + /** + * \brief Get a handle to a specific mipmap sub-level + * \param hSrf Handle to a texture containing mipmaps + * \param level Level of the mipmap to acquire. (level >= 1) (0 = "hSrf" it self with surface interface) + * \return Surface handle or NULL in a case of a failure. Must be released with ReleaseSurface() after no longer accessed. + */ + virtual HSURFNATIVE obsolete_GetMipSublevel(HSURFNATIVE hSrf, int level) { return NULL; } + virtual void obsolete_ReleaseSurface(HSURFNATIVE hSrf) { } + virtual bool obsolete_GetSurfaceSpecs(HSURFNATIVE hSrf, SurfaceSpecs* pOut) { return false; } + + /** + * \brief Save a native DirectX surface to a file (*.dds, *.jpg, *.bmp, *.png) + * \param file filename. + * \param hSrf handle to a surface to same. + * \return false in a case of failure. + */ + virtual bool obsolete_SaveSurfaceNative(const char *file, HSURFNATIVE hSrf) { return false; } + + /** + * \brief Realtime Mipmap auto-generation from the top/main level. + * \param hSurface handle to a surface + * \return false if an error occurred, true otherwise. + * \note Surface must be created with (OAPISURFACE_TEXTURE | OAPISURFACE_RENDERTARGET | OAPISURFACE_MIPMAPS) + * \note Exact attribute requirements/conflicts are unknown. + */ + virtual bool obsolete_GenerateMipMaps(HSURFNATIVE hSurface) { return false; } + + /** + * \brief On the fly texture compression into a DXT format. Input remains unchanged. + * \param hSurface handle to a surface to compress + * \param flags combination of OAPISURFACE_PF_DXT1, OAPISURFACE_PF_DXT3, OAPISURFACE_PF_DXT5, OAPISURFACE_MIPMAPS, OAPISURFACE_SYSMEM + * \return Handle to a compressed texture, user must release this. + * \note Compression is slow, separate thread recommended for realtime compression. + */ + virtual HSURFNATIVE obsolete_CompressSurface(HSURFNATIVE hSurface, DWORD flags) { return NULL; } + + /** + * \brief Get device specific mesh from Orbiter mesh template + * \param hMesh handle to a mesh acquired from oapiLoadMeshGlobal() + * \param pBox a pointer to an array of 8 FVECTOR3s + */ + virtual DEVMESHHANDLE GetDevMesh(MESHHANDLE hMesh); + virtual DEVMESHHANDLE LoadDevMeshGlobal(const char *file_name, bool bUseCache = true); + virtual void ReleaseDevMesh(DEVMESHHANDLE hMesh); + + /** + * \brief Recover tile bounding box data + * \param hTile handle to a tile + * \param pBox a pointer to an array of 8 FVECTOR3s + */ + virtual void RenderMesh(DEVMESHHANDLE hMesh, const FMATRIX4 *pWorld); + virtual bool PickMesh(PickMeshStruct *pm, DEVMESHHANDLE hMesh, const FMATRIX4 *pWorld, short x, short y); + + /** + * \brief Render a list of independent lines 0-1, 2-3,... + * \param pVtx a pointer to a vertex array + * \param pIdx a pointer to index array + * \param nIdx number of lines to draw multiplied by 2, (ARRAYSIZE() of index array) + * \param pWorld pointer to World matrix relative to camera + * \param color color in 0xAABBGGRR + */ + virtual void RenderLines(const FVECTOR3 *pVtx, const WORD *pIdx, int nVtx, int nIdx, const FMATRIX4 *pWorld, DWORD color); + //@} + + + + /** + * \brief Alters objects position. Matrix must be initially valid. + * \param mat [in/out] Pointer to a matrix to change + * \param pos New position + */ + inline void SetTranslation(FMATRIX4 *mat, const VECTOR3 &pos) + { + mat->m41 = float(pos.x); mat->m42 = float(pos.y); mat->m43 = float(pos.z); + } + + inline void SetTranslation(FMATRIX4 *mat, const FVECTOR3 &pos) + { + mat->m41 = pos.x; mat->m42 = pos.y; mat->m43 = pos.z; + } + + /** + * \brief Creates a world transformation matrix + * \param mat [out] Pointer to a matrix + * \param pos Objects position relative to a camera in ecliptic frame + * \param x X-axis, major axis [unit vector] + * \param z Z-axis, minor axis [unit vector] + * \param scale a scale factor (default 1.0) + */ + inline void WorldMatrix(FMATRIX4 *mat, const VECTOR3 &pos, const VECTOR3 &x, const VECTOR3 &z, double scale = 1.0) + { + VECTOR3 y = crossp(x, z); + mat->m11 = float(x.x * scale); mat->m12 = float(x.y * scale); mat->m13 = float(x.z * scale); mat->m14 = 0.0f; + mat->m21 = float(y.x * scale); mat->m22 = float(y.y * scale); mat->m23 = float(y.z * scale); mat->m24 = 0.0f; + mat->m31 = float(z.x * scale); mat->m32 = float(z.y * scale); mat->m33 = float(z.z * scale); mat->m34 = 0.0f; + mat->m41 = float(pos.x); mat->m42 = float(pos.y); mat->m43 = float(pos.z); mat->m44 = 1.0f; + } + + inline void WorldMatrix(FMATRIX4 *mat, const FVECTOR3 &pos, const FVECTOR3 &x, const FVECTOR3 &z, float scale = 1.0f) + { + FVECTOR3 y = crossp(x, z); + mat->m11 = (x.x * scale); mat->m12 = (x.y * scale); mat->m13 = (x.z * scale); mat->m14 = 0.0f; + mat->m21 = (y.x * scale); mat->m22 = (y.y * scale); mat->m23 = (y.z * scale); mat->m24 = 0.0f; + mat->m31 = (z.x * scale); mat->m32 = (z.y * scale); mat->m33 = (z.z * scale); mat->m34 = 0.0f; + mat->m41 = (pos.x); mat->m42 = (pos.y); mat->m43 = (pos.z); mat->m44 = 1.0f; + } +}; diff --git a/OVP/VulkanClient/gcCore.cpp b/OVP/VulkanClient/gcCore.cpp new file mode 100644 index 000000000..286e5d104 --- /dev/null +++ b/OVP/VulkanClient/gcCore.cpp @@ -0,0 +1,787 @@ +// =================================================== +// Copyright (C) 2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + + +#include +#include "MathAPI.h" +#include "gcCore.h" +#include "Surface.h" +#include "Client.h" +#include "Scene.h" +#include "VVessel.h" +#include "VPlanet.h" +#include "Surfmgr2.h" +#include "IProcess.h" + +extern vkClient *g_client; +extern std::set g_fonts; + +class gcSwap +{ +public: + gcSwap() : pSwap(NULL), hSurf(NULL), pBack(NULL) { } + ~gcSwap() { Release(); } + void Release() { + DELETE_SURFACE(hSurf); + SAFE_RELEASE(pBack); + SAFE_RELEASE(pSwap); + } + LPDIRECT3DSWAPCHAIN9 pSwap; + LPDIRECT3DSURFACE9 pBack; + SURFHANDLE hSurf; +}; + + + +DLLCLBK void gcBindCoreMethod(void** ppFnc, const char* name) +{ + *ppFnc = NULL; +#define binder_start + if (strcmp(name,"RegisterSwap")==0) *ppFnc = &gcCore2::RegisterSwap; + if (strcmp(name,"FlipSwap")==0) *ppFnc = &gcCore2::FlipSwap; + if (strcmp(name,"GetRenderTarget")==0) *ppFnc = &gcCore2::GetRenderTarget; + if (strcmp(name,"ReleaseSwap")==0) *ppFnc = &gcCore2::ReleaseSwap; + if (strcmp(name,"DeleteCustomCamera")==0) *ppFnc = &gcCore2::DeleteCustomCamera; + if (strcmp(name,"CustomCameraOnOff")==0) *ppFnc = &gcCore2::CustomCameraOnOff; + if (strcmp(name,"CustomCameraOverlay")==0) *ppFnc = &gcCore2::CustomCameraOverlay; + if (strcmp(name,"SetupCustomCamera")==0) *ppFnc = &gcCore2::SetupCustomCamera; + if (strcmp(name,"SketchpadVersion")==0) *ppFnc = &gcCore2::SketchpadVersion; + if (strcmp(name,"CreatePoly")==0) *ppFnc = &gcCore2::CreatePoly; + if (strcmp(name,"CreateTriangles")==0) *ppFnc = &gcCore2::CreateTriangles; + if (strcmp(name,"DeletePoly")==0) *ppFnc = &gcCore2::DeletePoly; + if (strcmp(name,"GetTextLength")==0) *ppFnc = &gcCore2::GetTextLength; + if (strcmp(name,"GetCharIndexByPosition")==0) *ppFnc = &gcCore2::GetCharIndexByPosition; + if (strcmp(name,"RegisterRenderProc")==0) *ppFnc = &gcCore2::RegisterRenderProc; + if (strcmp(name,"CreateSketchpadFont")==0) *ppFnc = &gcCore2::CreateSketchpadFont; + if (strcmp(name,"GetMeshMaterial")==0) *ppFnc = &gcCore2::GetMeshMaterial; + if (strcmp(name,"SetMeshMaterial")==0) *ppFnc = &gcCore2::SetMeshMaterial; + if (strcmp(name,"GetMatrix")==0) *ppFnc = &gcCore2::GetMatrix; + if (strcmp(name,"SetMatrix")==0) *ppFnc = &gcCore2::SetMatrix; + if (strcmp(name,"GetDevMesh")==0) *ppFnc = &gcCore2::GetDevMesh; + if (strcmp(name,"LoadDevMeshGlobal")==0) *ppFnc = &gcCore2::LoadDevMeshGlobal; + if (strcmp(name,"ReleaseDevMesh")==0) *ppFnc = &gcCore2::ReleaseDevMesh; + if (strcmp(name,"RenderMesh")==0) *ppFnc = &gcCore2::RenderMesh; + if (strcmp(name,"PickMesh")==0) *ppFnc = &gcCore2::PickMesh; + if (strcmp(name,"RenderLines")==0) *ppFnc = &gcCore2::RenderLines; + if (strcmp(name,"GetSystemSpecs")==0) *ppFnc = &gcCore2::GetSystemSpecs; + if (strcmp(name,"GetSurfaceSpecs")==0) *ppFnc = &gcCore2::GetSurfaceSpecs; + if (strcmp(name,"LoadSurface")==0) *ppFnc = &gcCore2::LoadSurface; + if (strcmp(name,"SaveSurface")==0) *ppFnc = &gcCore2::SaveSurface; + if (strcmp(name,"GetMipSublevel")==0) *ppFnc = &gcCore2::GetMipSublevel; + if (strcmp(name,"GenerateMipmaps")==0) *ppFnc = &gcCore2::GenerateMipmaps; + if (strcmp(name,"CompressSurface")==0) *ppFnc = &gcCore2::CompressSurface; + if (strcmp(name,"LoadBitmapFromFile")==0) *ppFnc = &gcCore2::LoadBitmapFromFile; + if (strcmp(name,"GetRenderWindow")==0) *ppFnc = &gcCore2::GetRenderWindow; + if (strcmp(name,"RegisterGenericProc")==0) *ppFnc = &gcCore2::RegisterGenericProc; + if (strcmp(name,"StretchRectInScene")==0) *ppFnc = &gcCore2::StretchRectInScene; + if (strcmp(name,"ClearSurfaceInScene")==0) *ppFnc = &gcCore2::ClearSurfaceInScene; + if (strcmp(name,"ScanScreen")==0) *ppFnc = &gcCore2::ScanScreen; + if (strcmp(name,"LockSurface")==0) *ppFnc = &gcCore2::LockSurface; + if (strcmp(name,"ReleaseLock")==0) *ppFnc = &gcCore2::ReleaseLock; + if (strcmp(name,"GetPlanetManager")==0) *ppFnc = &gcCore2::GetPlanetManager; + if (strcmp(name,"SetTileOverlay")==0) *ppFnc = &gcCore2::SetTileOverlay; + if (strcmp(name,"AddGlobalOverlay")==0) *ppFnc = &gcCore2::AddGlobalOverlay; + if (strcmp(name,"GetTileData")==0) *ppFnc = &gcCore2::GetTileData; + if (strcmp(name,"GetTile")==0) *ppFnc = &gcCore2::GetTile; + if (strcmp(name,"HasTileData")==0) *ppFnc = &gcCore2::HasTileData; + if (strcmp(name,"SeekTileTexture")==0) *ppFnc = &gcCore2::SeekTileTexture; + if (strcmp(name,"SeekTileElevation")==0) *ppFnc = &gcCore2::SeekTileElevation; + if (strcmp(name,"GetElevation")==0) *ppFnc = &gcCore2::GetElevation; + if (strcmp(name,"CreateIPInterface")==0) *ppFnc = &gcCore2::CreateIPInterface; + if (strcmp(name,"ReleaseIPInterface")==0) *ppFnc = &gcCore2::ReleaseIPInterface; +#define binder_end + if (*ppFnc == NULL) oapiWriteLogV("ERROR:gcCoreAPI: Function [%s] failed to bind", name); +} + + +// =============================================================================================== +// Custom SwapChain Interface +// =============================================================================================== +// +HSWAP gcCore::RegisterSwap(HWND hWnd, HSWAP hData, int AA) +{ + gcSwap * pData = (gcSwap*)hData; + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 0; + pp.BackBufferHeight = 0; + pp.BackBufferFormat = D3DFMT_X8R8G8B8; + pp.BackBufferCount = 1; + pp.MultiSampleType = D3DMULTISAMPLE_NONE; + pp.MultiSampleQuality = 0; + pp.SwapEffect = D3DSWAPEFFECT_FLIP; + pp.hDeviceWindow = hWnd; + pp.Windowed = true; + pp.EnableAutoDepthStencil = false; + pp.AutoDepthStencilFormat = D3DFMT_D24S8; + pp.Flags = 0; + pp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; + pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + + LPDIRECT3DDEVICE9 pDev = g_client->GetDevice(); + LPDIRECT3DSWAPCHAIN9 pSwap = NULL; + + if (pDev->CreateAdditionalSwapChain(&pp, &pSwap) == S_OK) + { + if (!pData) pData = new gcSwap(); + else pData->Release(); + + LPDIRECT3DSURFACE9 pBack = NULL; + HR(pSwap->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBack)); + + SurfNative *pSrf = new SurfNative(pBack, OAPISURFACE_BACKBUFFER | OAPISURFACE_RENDER3D | OAPISURFACE_RENDERTARGET); + pSrf->SetName("SwapChainBackBuffer"); + + pData->hSurf = SURFHANDLE(pSrf); + pData->pSwap = pSwap; + pData->pBack = pBack; + + return HSWAP(pData); + } + else { + LogErr("Failed to create a swapchain. (Feature not supported in true-fullscreen mode)"); + return NULL; + } +} + + +// =============================================================================================== +// +void gcCore::FlipSwap(HSWAP hSwap) +{ + HR(((gcSwap*)hSwap)->pSwap->Present(0, 0, 0, 0, 0)); +} + + +// =============================================================================================== +// +SURFHANDLE gcCore::GetRenderTarget(HSWAP hSwap) +{ + return ((gcSwap*)hSwap)->hSurf; +} + +// =============================================================================================== +// +void gcCore::ReleaseSwap(HSWAP hSwap) +{ + if (hSwap) delete ((gcSwap*)hSwap); +} + + + + + + +// =============================================================================================== +// Custom Camera Interface +// =============================================================================================== +// +CAMERAHANDLE gcCore::SetupCustomCamera(CAMERAHANDLE hCam, OBJHANDLE hVessel, VECTOR3 &pos, VECTOR3 &dir, VECTOR3 &up, double fov, SURFHANDLE hSurf, DWORD flags) +{ + VECTOR3 x = crossp(up, dir); + MATRIX3 mTake; + mTake.m11 = x.x; mTake.m21 = x.y; mTake.m31 = x.z; + mTake.m12 = up.x; mTake.m22 = up.y; mTake.m32 = up.z; + mTake.m13 = dir.x; mTake.m23 = dir.y; mTake.m33 = dir.z; + Scene *pScene = g_client->GetScene(); + return pScene ? pScene->SetupCustomCamera(hCam, hVessel, mTake, pos, fov, hSurf, flags) : NULL; +} + + +// =============================================================================================== +// +void gcCore::CustomCameraOnOff(CAMERAHANDLE hCam, bool bOn) +{ + Scene *pScene = g_client->GetScene(); + if (pScene) { + pScene->CustomCameraOnOff(hCam, bOn); + } +} + + +// =============================================================================================== +// +void gcCore::CustomCameraOverlay(CAMERAHANDLE hCam, __gcRenderProc clbk, void *pUser) +{ + CAMERA(hCam)->pRenderProc = clbk; + CAMERA(hCam)->pUser = pUser; +} + + +// =============================================================================================== +// +int gcCore::DeleteCustomCamera(CAMERAHANDLE hCam) +{ + Scene *pScene = g_client->GetScene(); + return pScene ? pScene->DeleteCustomCamera(hCam) : 0; +} + + + + + + + +// =============================================================================================== +// SketchPad Interface +// =============================================================================================== + + +// =============================================================================================== +// +int gcCore::SketchpadVersion(Sketchpad* pSkp) +{ + return ((vkPad*)pSkp)->GetVersion(); +} + + +// =============================================================================================== +// +oapi::Font* gcCore::CreateSketchpadFont(int height, char* face, int width, int weight, FontStyle Style, float spacing) +{ + return g_client->clbkCreateFontEx(height, face, width, weight, Style, spacing); +} + + +// =============================================================================================== +// +HPOLY gcCore::CreatePoly(HPOLY hPoly, const FVECTOR2 *pt, int npt, DWORD flags) +{ + LPDIRECT3DDEVICE9 pDev = g_client->GetDevice(); + if (!hPoly) return new vkPolyLine(pDev, pt, npt, (flags&PF_CONNECT) != 0); + ((vkPolyLine *)hPoly)->Update(pt, npt, (flags&PF_CONNECT) != 0); + return hPoly; +} + + +// =============================================================================================== +// +HPOLY gcCore::CreateTriangles(HPOLY hPoly, const gcCore::clrVtx *pt, int npt, DWORD flags) +{ + LPDIRECT3DDEVICE9 pDev = g_client->GetDevice(); + if (!hPoly) return new vkTriangle(pDev, pt, npt, flags); + ((vkTriangle *)hPoly)->Update(pt, npt); + return hPoly; +} + + +// =============================================================================================== +// +void gcCore::DeletePoly(HPOLY hPoly) +{ + if (hPoly) { + ((vkPolyBase *)hPoly)->Release(); + delete ((vkPolyBase *)hPoly); + } +} + + +// =============================================================================================== +// +DWORD gcCore::GetTextLength(oapi::Font *hFont, const char *pText, int len) +{ + return DWORD((static_cast(hFont))->GetTextLength(pText, len)); +} + + +// =============================================================================================== +// +DWORD gcCore::GetCharIndexByPosition(oapi::Font *hFont, const char *pText, int pos, int len) +{ + return DWORD((static_cast(hFont))->GetIndexByPosition(pText, pos, len)); +} + + +// =============================================================================================== +// +bool gcCore::RegisterRenderProc(__gcRenderProc proc, DWORD flags, void *pParam) +{ + return g_client->RegisterRenderProc(proc, flags, pParam); +} + + + + +// =============================================================================================== +// Mesh interface functions +// =============================================================================================== +// +int gcCore::GetMatrix(MatrixId matrix_id, OBJHANDLE hVessel, DWORD mesh, DWORD group, FMATRIX4 *pMat) +{ + if (oapiGetObjectType(hVessel) != OBJTP_VESSEL) return -10; + Scene *pScn = g_client->GetScene(); + vVessel *pVes = (vVessel *)pScn->GetVisObject(hVessel); + if (pVes) return pVes->GetMatrixTransform(matrix_id, mesh, group, pMat); + return -11; +} + + +// =============================================================================================== +// +int gcCore::SetMatrix(MatrixId matrix_id, OBJHANDLE hVessel, DWORD mesh, DWORD group, const FMATRIX4 *pMat) +{ + if (oapiGetObjectType(hVessel) != OBJTP_VESSEL) return -10; + Scene *pScn = g_client->GetScene(); + vVessel *pVes = (vVessel *)pScn->GetVisObject(hVessel); + if (pVes) return pVes->SetMatrixTransform(matrix_id, mesh, group, pMat); + return -11; +} + + +// =============================================================================================== +// +int gcCore::GetMeshMaterial(DEVMESHHANDLE hMesh, DWORD idx, MatProp prop, FVECTOR4* value) +{ + return g_client->clbkMeshMaterialEx(hMesh, idx, prop, value); +} + + +// =============================================================================================== +// +int gcCore::SetMeshMaterial(DEVMESHHANDLE hMesh, DWORD idx, MatProp prop, const FVECTOR4* value) +{ + return g_client->clbkSetMeshMaterialEx(hMesh, idx, prop, value); +} + +// =============================================================================================== +// +DEVMESHHANDLE gcCore::GetDevMesh(MESHHANDLE hMesh) +{ + return g_client->GetDevMesh(hMesh); +} + + +// =============================================================================================== +// +DEVMESHHANDLE gcCore::LoadDevMeshGlobal(const char* file_name, bool bUseCache) +{ + MESHHANDLE hMesh = oapiLoadMeshGlobal(file_name); + return g_client->GetDevMesh(hMesh); +} + + +// =============================================================================================== +// +void gcCore::ReleaseDevMesh(DEVMESHHANDLE hMesh) +{ + delete (vkMesh*)(hMesh); +} + + +// =============================================================================================== +// +void gcCore::RenderMesh(DEVMESHHANDLE hMesh, const oapi::FMATRIX4* pWorld) +{ + Scene* pScene = g_client->GetScene(); + pScene->RenderMesh(hMesh, pWorld); +} + + +// =============================================================================================== +// +bool gcCore::PickMesh(PickMeshStruct* pm, DEVMESHHANDLE hMesh, const FMATRIX4* pWorld, short x, short y) +{ + Scene* pScene = g_client->GetScene(); + vkPick pk = pScene->PickMesh(hMesh, (const FMATRIX4*)pWorld, x, y); + if (pk.group >= 0) { + if (pk.dist < pm->dist) { + pm->pos = pk.pos; + pm->normal = pk.normal; + pm->grp_inst = pk.group; + pm->dist = pk.dist; + return true; + } + } + return false; +} + + + + + + + +// =============================================================================================== +// Custom Render Interface +// =============================================================================================== +// +// =============================================================================================== +// +SURFHANDLE gcCore::LoadSurface(const char* fname, DWORD flags) +{ + return g_client->clbkLoadSurface(fname, flags); +} + +// =============================================================================================== +// +bool gcCore::SaveSurface(const char* file, SURFHANDLE hSrf) +{ + return NatSaveSurface(file, SURFACE(hSrf)->GetResource()); +} + +// =============================================================================================== +// +SURFHANDLE gcCore::GetMipSublevel(SURFHANDLE hSrf, int level) +{ + return NatGetMipSublevel(hSrf, level); +} + +// =============================================================================================== +// +bool gcCore::GenerateMipmaps(SURFHANDLE hSurface) +{ + return NatGenerateMipmaps(hSurface); +} + +// =============================================================================================== +// +SURFHANDLE gcCore::CompressSurface(SURFHANDLE hSurface, DWORD flags) +{ + return NatCompressSurface(hSurface, flags); +} + + +// =============================================================================================== +// +void gcCore::RenderLines(const FVECTOR3* pVtx, const WORD* pIdx, int nVtx, int nIdx, const FMATRIX4* pWorld, DWORD color) +{ + vkEffect::RenderLines((const FVECTOR3*)pVtx, pIdx, nVtx, nIdx, (const FMATRIX4*)pWorld, color); +} + + +// =============================================================================================== +// +bool gcCore::StretchRectInScene(SURFHANDLE tgt, SURFHANDLE src, LPRECT tr, LPRECT sr) +{ + if (S_OK == g_client->BeginScene()) + { + LPDIRECT3DSURFACE9 pss = SURFACE(src)->GetSurface(); + LPDIRECT3DSURFACE9 pts = SURFACE(tgt)->GetSurface(); + HRESULT hr = g_client->GetDevice()->StretchRect(pss, sr, pts, tr, D3DTEXF_LINEAR); + g_client->EndScene(); + return hr == S_OK; + } + return false; +} + +// =============================================================================================== +// +bool gcCore::ClearSurfaceInScene(SURFHANDLE tgt, DWORD color, LPRECT tr) +{ + if (S_OK == g_client->BeginScene()) + { + LPDIRECT3DSURFACE9 pts = SURFACE(tgt)->GetSurface(); + HRESULT hr = g_client->GetDevice()->ColorFill(pts, tr, (DWORD)color); + g_client->EndScene(); + return hr == S_OK; + } + return false; +} + + + + + +// =============================================================================================== +// Some Helper Functions +// =============================================================================================== +// +// =============================================================================================== +// +gcCore::PickGround gcCore::ScanScreen(int scr_x, int scr_y) +{ + PickGround pg; memset(&pg, 0, sizeof(PickGround)); + + Scene* pScene = g_client->GetScene(); + TILEPICK tp = pScene->PickSurface(scr_x, scr_y); + SurfTile* pTile = static_cast(tp.pTile); + + if (pTile) { + + pTile->GetIndex(&pg.iLng, &pg.iLat); + + pg.Bounds.left = pTile->bnd.minlng; + pg.Bounds.right = pTile->bnd.maxlng; + pg.Bounds.top = pTile->bnd.maxlat; + pg.Bounds.bottom = pTile->bnd.minlat; + + pg.lat = tp.lat; + pg.lng = tp.lng; + + pg.emax = float(pTile->GetMaxElev()); + pg.emin = float(pTile->GetMinElev()); + + pg.msg = 0; + pg.dist = tp.d; + pg.elev = tp.elev; + pg.level = pTile->Level(); + pg.hTile = HTILE(pTile); + pg.normal = tp._n; + pg.pos = tp._p; + } + return pg; +} + + +// =============================================================================================== +// +void gcCore::GetSystemSpecs(SystemSpecs* sp, int size) +{ + if (size == sizeof(SystemSpecs)) { + sp->DisplayMode = g_client->GetFramework()->GetDisplayMode(); + sp->MaxTexSize = g_client->GetHardwareCaps()->MaxTextureWidth; + sp->MaxTexRep = g_client->GetHardwareCaps()->MaxTextureRepeat; + sp->gcAPIVer = BuildDate(); + } +} + +// =============================================================================================== +// +bool gcCore::GetSurfaceSpecs(SURFHANDLE hSrf, SurfaceSpecs* sp, int size) +{ + return SURFACE(hSrf)->GetSpecs(sp, size); +} + + +// =============================================================================================== +// +bool gcCore::RegisterGenericProc(__gcGenericProc proc, DWORD id, void* pParam) +{ + return g_client->RegisterGenericProc(proc, id, pParam); +} + + +// =============================================================================================== +// +HBITMAP gcCore::LoadBitmapFromFile(const char* fname) +{ + return g_client->gcReadImageFromFile(fname); +} + + +// =============================================================================================== +// +HWND gcCore::GetRenderWindow() +{ + return g_client->GetRenderWindow(); +} + + +// =============================================================================================== +// gcCore2 Interface --- Tile access interface functions +// =============================================================================================== +// + +HPLANETMGR gcCore2::GetPlanetManager(OBJHANDLE hPlanet) +{ + Scene *pScene = g_client->GetScene(); + vPlanet *vPl = (vPlanet *)pScene->GetVisObject(hPlanet); + return HPLANETMGR(vPl); +} + + +// =============================================================================================== +// +HTILE gcCore2::GetTile(HPLANETMGR vPl, double lng, double lat, int maxlevel) +{ + vPlanet *vP = static_cast(vPl); + return HTILE(vP->FindTile(lng, lat, maxlevel)); +} + + +// =============================================================================================== +// +gcCore::PickGround gcCore2::GetTileData(HPLANETMGR vPl, double lng, double lat, int maxlevel) +{ + PickGround pg; memset(&pg, 0, sizeof(PickGround)); + if (!vPl) return pg; + + vPlanet *vP = static_cast(vPl); + SurfTile *pTile = static_cast(vP->FindTile(lng, lat, maxlevel)); + + if (!pTile) { + oapiWriteLogV("gcCore::FindTile() Failed"); + return pg; + } + + pTile->GetIndex(&pg.iLng, &pg.iLat); + pTile->GetElevation(lng, lat, &pg.elev, &pg.normal, NULL, true, false); + + VECTOR3 pos = vP->GetUnitSurfacePos(lng, lat) * (vP->GetSize() + pg.elev); + MATRIX3 mRot; oapiGetRotationMatrix(vP->GetObjHandle(), &mRot); + + pos = mul(mRot, pos) + vP->PosFromCamera(); + + pg.Bounds.left = pTile->bnd.minlng; + pg.Bounds.right = pTile->bnd.maxlng; + pg.Bounds.top = pTile->bnd.maxlat; + pg.Bounds.bottom = pTile->bnd.minlat; + + pg.lat = lat; + pg.lng = lng; + + pg.emax = float(pTile->GetMaxElev()); + pg.emin = float(pTile->GetMinElev()); + + pg.msg = 0; + pg.dist = length(pos); + pg.level = pTile->Level(); + pg.hTile = HTILE(pTile); + pg.pos = FVECTOR3(pos); + + return pg; +} + +// =============================================================================================== +// +bool gcCore2::SeekTileElevation(HPLANETMGR hMgr, int iLng, int iLat, int level, int flags, ElevInfo *pInfo) +{ + ELEVFILEHEADER hdr; + if (!hMgr) return false; + if (((vPlanet*)(hMgr))->SurfMgr2()) { + float* pData = ((vPlanet*)(hMgr))->SurfMgr2()->BrowseElevationData(level, iLat, iLng, flags, &hdr); + if (!pData) return false; + pInfo->MaxElev = hdr.emax; + pInfo->MinElev = hdr.emin; + pInfo->MeanElev = hdr.emean; + pInfo->Resolution = hdr.scale; + pInfo->Offset = hdr.offset; + pInfo->pElevData = pData; + return true; + } + return false; +} + + +// =============================================================================================== +// +SURFHANDLE gcCore2::SeekTileTexture(HPLANETMGR hMgr, int iLng, int iLat, int level, int flags, void *reserved) +{ + if (!hMgr) return NULL; + if (((vPlanet *)(hMgr))->SurfMgr2()) { + return ((vPlanet *)(hMgr))->SurfMgr2()->SeekTileTexture(iLng, iLat, level, flags); + } + return NULL; +} + + +// =============================================================================================== +// +bool gcCore2::HasTileData(HPLANETMGR hMgr, int iLng, int iLat, int level, int flags) +{ + if (!hMgr) return false; + if (((vPlanet *)(hMgr))->SurfMgr2()) { + return ((vPlanet *)(hMgr))->SurfMgr2()->HasTileData(iLng, iLat, level, flags); + } + return false; +} + + +// =============================================================================================== +// +int gcCore2::GetElevation(HTILE hTile, double lng, double lat, double *out_elev) +{ + SurfTile *pTile = static_cast(hTile); + return pTile->GetElevation(lng, lat, out_elev, NULL, NULL, true, true); +} + + +// =============================================================================================== +// +SURFHANDLE gcCore2::SetTileOverlay(HTILE hTile, const SURFHANDLE hOverlay) +{ + //SurfTile *pTile = static_cast(hTile); + //LPDIRECT3DTEXTURE9 pTex = static_cast(hOverlay); + //return HSURFNATIVE(pTile->SetOverlay(pTex, true)); + return NULL; +} + + +// =============================================================================================== +// +HOVERLAY gcCore2::AddGlobalOverlay(HPLANETMGR hMgr, VECTOR4 mmll, OlayType type, const SURFHANDLE hOverlay, HOVERLAY hOld, const FVECTOR4* pBlend) +{ + if (!hMgr) return NULL; + vPlanet *vP = static_cast(hMgr); + vPlanet::sOverlay* oLay = static_cast(hOld); + if (hOverlay) { + LPDIRECT3DTEXTURE9 pTex = SURFACE(hOverlay)->GetTexture(); + return vP->AddOverlaySurface(mmll, type, pTex, oLay, pBlend); + } + return vP->AddOverlaySurface(mmll, type, NULL, oLay, pBlend); +} + +// =============================================================================================== +// +bool gcCore::LockSurface(SURFHANDLE hSrf, Lock* pOut, bool bWait) +{ + D3DLOCKED_RECT lock; + LPDIRECT3DRESOURCE9 pResource = SURFACE(hSrf)->GetResource(); + DWORD flags = 0; + if (!bWait) flags |= D3DLOCK_DONOTWAIT; + + if (pResource->GetType() == D3DRTYPE_SURFACE) { + LPDIRECT3DSURFACE9 pSurf = static_cast(hSrf); + if (HROK(pSurf->LockRect(&lock, NULL, flags))) { + pOut->pData = lock.pBits; + pOut->Pitch = lock.Pitch; + return true; + } + return false; + } + + if (pResource->GetType() == D3DRTYPE_TEXTURE) { + LPDIRECT3DTEXTURE9 pTex = static_cast(hSrf); + if (HROK(pTex->LockRect(0, &lock, NULL, flags))) { + pOut->pData = lock.pBits; + pOut->Pitch = lock.Pitch; + return true; + } + return false; + } + + return false; +} + + +// =============================================================================================== +// +void gcCore::ReleaseLock(SURFHANDLE hSrf) +{ + LPDIRECT3DRESOURCE9 pResource = SURFACE(hSrf)->GetResource(); + if (pResource->GetType() == D3DRTYPE_SURFACE) { + LPDIRECT3DSURFACE9 pSurf = static_cast(hSrf); + HR(pSurf->UnlockRect()); + } + if (pResource->GetType() == D3DRTYPE_TEXTURE) { + LPDIRECT3DTEXTURE9 pTex = static_cast(hSrf); + HR(pTex->UnlockRect(0)); + } +} + +// =============================================================================================== +// +gcIPInterface* gcCore2::CreateIPInterface(const char* file, const char* PSEntry, const char* VSEntry, const char* ppf) +{ + ImageProcessing* pIPI = new ImageProcessing(g_client->GetDevice(), file, PSEntry, ppf); + + if (pIPI->IsOK() == false) { + oapiWriteLogV("gcCore::CreateIPInterface() Failed ! File = [%s]", file); + return NULL; + } + return new gcIPInterface(pIPI); +} + +// =============================================================================================== +// +void gcCore2::ReleaseIPInterface(gcIPInterface* pIPI) +{ + if (!pIPI) return; + if (pIPI->pIPI) delete pIPI->pIPI; + delete pIPI; +} + diff --git a/OVP/VulkanClient/gcCore.h b/OVP/VulkanClient/gcCore.h new file mode 100644 index 000000000..21da437d7 --- /dev/null +++ b/OVP/VulkanClient/gcCore.h @@ -0,0 +1,875 @@ +// =================================================== +// Copyright (C) 2012-2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + + +#include "OrbiterAPI.h" +#include "DrawAPI.h" +#include + +#ifndef __GC_CORE +#define __GC_CORE + +#define INTERFACE_BUILDER +#define gc_interface static + +using namespace oapi; + +class gcCore; +class gcCore2; + +typedef void (__cdecl* __gcBindCoreMethod)(void** ppFnc, const char* name); + +static class gcCore2 *pCoreInterface = NULL; + +/** +* \file gcConst.h +* \brief Structures and definitions +*/ + + +/// \defgroup PixelFormats Common pixel formats for surfaces [ Orbiter 2021+ only ] +///@{ +#define OAPISURFACE_PF_MASK 0xFF0000 ///< PixelFormat Mask +#define OAPISURFACE_PF_XRGB 0x010000 ///< 32bit RGB no-alpha +#define OAPISURFACE_PF_ARGB 0x020000 ///< 32bit ARGB with-alpha 0xAARRGGBB +#define OAPISURFACE_PF_RGB565 0x030000 ///< 16bit RGB no-alpha +#define OAPISURFACE_PF_S16R 0x040000 ///< Signed integer 16-bit (1-channel) +#define OAPISURFACE_PF_F32R 0x050000 ///< Float 32-bit (1-channel) +#define OAPISURFACE_PF_F32RG 0x060000 ///< Float 64-bit (2-channel) +#define OAPISURFACE_PF_F32RGBA 0x070000 ///< Float 128-bit (4-channel) float4(r,g,b,a) +#define OAPISURFACE_PF_F16R 0x080000 ///< Float 16-bit (1-channel) +#define OAPISURFACE_PF_F16RG 0x090000 ///< Float 32-bit (2-channel) +#define OAPISURFACE_PF_F16RGBA 0x0A0000 ///< Float 64-bit (4-channel) float4(r,g,b,a) +#define OAPISURFACE_PF_DXT1 0x0B0000 ///< Compressed DXT1 format +#define OAPISURFACE_PF_DXT3 0x0C0000 ///< Compressed DXT3 format +#define OAPISURFACE_PF_DXT5 0x0D0000 ///< Compressed DXT5 format +#define OAPISURFACE_PF_ALPHA 0x0E0000 ///< Alpha only surface 8-bit +#define OAPISURFACE_PF_GRAY 0x0F0000 ///< Grayscale Image 8-bit +///@} + + +/// \defgroup RenderProc Specify a SketchPad render callback function +///@{ +#define RENDERPROC_DELETE 0x0000 ///< Unregister/Remove existing callback +#define RENDERPROC_HUD_1ST 0x0001 ///< Register a HUD callback to draw under Orbiter's main HUD +#define RENDERPROC_HUD_2ND 0x0002 ///< Register a HUD callback to draw over Orbiter's main HUD +#define RENDERPROC_PLANETARIUM 0x0003 ///< Register a HUD callback to draw into a planetarium view using perspective projection +#define RENDERPROC_EXTERIOR 0x0005 ///< Register a callback to draw into an exterior vessel view using perspective projection +///@} + + +/// \defgroup GenericProc Specify a generic callback function +///@{ +#define GENERICPROC_DELETE 0x0000 ///< Unregister/Remove existing callback +#define GENERICPROC_PICK_VESSEL 0x0001 ///< Called when user clicks a vessel with LMB, RMB +#define GENERICPROC_PICK_TERRAIN 0x0002 ///< Called when user clicks a terrain with LMB, RMB +#define GENERICPROC_HOVER_TERRAIN 0x0003 ///< Called when hovering over terrain with mouse (Performance heavy AVOID !!) +#define GENERICPROC_SHUTDOWN 0x0004 ///< Callback for resource/memory deallocation +#define GENERICPROC_RENDERTILE 0x0010 ///< Render Tile Callback +#define GENERICPROC_RENDER_EXTERIOR 0x0011 ///< Render Post Scene Exterior Only Callback +#define GENERICPROC_TILE_CREATED 0x0020 ///< +#define GENERICPROC_TILE_DELETED 0x0021 ///< +///@} + +#define IPF_WRAP 0x0000 +#define IPF_WRAP_U 0x0000 +#define IPF_WRAP_V 0x0000 +#define IPF_WRAP_W 0x0000 +#define IPF_CLAMP 0x0007 +#define IPF_CLAMP_U 0x0001 +#define IPF_CLAMP_V 0x0002 +#define IPF_CLAMP_W 0x0004 +#define IPF_MIRROR 0x0038 +#define IPF_MIRROR_U 0x0008 +#define IPF_MIRROR_V 0x0010 +#define IPF_MIRROR_W 0x0020 +#define IPF_POINT 0x0000 +#define IPF_LINEAR 0x0040 +#define IPF_PYRAMIDAL 0x0080 +#define IPF_GAUSSIAN 0x0100 +#define IPF_ANISOTROPIC 0x0200 +#define IPF_VERTEXTEX 0x0400 + +/// \defgroup dwFlags for gcSetupCustomCamera() API function +///@{ +#define CUSTOMCAM_DEFAULTS 0x00FF +///@} + +/// \defgroup Polyline Polyline object creation and update flags +///@{ +#define PF_CONNECT 0x01 ///< Connect line endpoints forming a loop +///@} + +/// \defgroup Triangle Triangle object creation and update flags +///@{ +#define PF_TRIANGLES 0 +#define PF_STRIP 1 +#define PF_FAN 2 +///@} + + +/// \brief Handle to a surface manager's global overlay +typedef void * HOVERLAY; +/// \brief Handle to a planet/surface manager +typedef void * HPLANETMGR; +/// \brief Handle to a surface tile (SurfTile) +typedef void * HTILE; +/// \brief Custom swapchain handle +typedef void * HSWAP; +/// \brief Custom camera handle +typedef void * CAMERAHANDLE; +/// \brief Poly object handle +typedef void * HPOLY; + + +namespace gcTileFlags +{ + static const int TEXTURE = 0x1; ///< Texture data + static const int MASK = 0x2; ///< Nightlights/Water mask + static const int ELEVATION = 0x4; ///< Elevation + static const int CLOUD = 0x8; ///< Texture data + static const int ELEV_MOD = 0x10; ///< Elevation + // ------------------------------------------------------------------ + static const int TREE = 0x1000; ///< Search/Use Tree Archive + static const int CACHE = 0x2000; ///< Search/Use Cache + static const int MOD = 0x4000; ///< Search/Use ElevMod Cache +}; + + + +/// \brief Render HUD and Planetarium callback function +typedef void(__cdecl *__gcRenderProc)(oapi::Sketchpad *pSkp, void *pParam); +typedef void(__cdecl *__gcGenericProc)(int iUser, void *pUser, void *pParam); + + + + +// =========================================================================== +/** +* \class gcIPInterface +* \brief Image Processing Interface, Feed data through user supplied shader. +*/ +// =========================================================================== + +class gcIPInterface +{ + +public: + + friend class gcCore2; + + enum ipitemplate { Rect, Octagon, Mesh }; + enum ipicull { None, CCW, CW }; + + gcIPInterface(class ImageProcessing* p) : pIPI(p) { } + + virtual ~gcIPInterface(); + + // ---------------------------------------------------------------------------------- + // Compile and Activate Additional Shaders, Each shader has it's own local set of variables. + // Variables are not shared between shaders + // ---------------------------------------------------------------------------------- + virtual bool CompileShader(const char* PSEntry); + virtual bool Activate(const char* Shader = NULL); + + // ---------------------------------------------------------------------------------- + // Use the 'Set' functions to assign a value into a shader constants ( e.g. uniform extern float4 myVector; ) + // If the variable "var" is defined but NOT used by the shader code, the variable "var" doesn't exists in + // a constant table and an error is printed when trying to assign a value to it. + // ---------------------------------------------------------------------------------- + virtual void SetFloat(const char* var, float val); + virtual void SetInt(const char* var, int val); + virtual void SetBool(const char* var, bool val); + // ---------------------------------------------------------------------------------- + virtual void SetFloat(const char* var, const void* val, int bytes); + virtual void SetInt(const char* var, const int* val, int bytes); + virtual void SetBool(const char* var, const bool* val, int bytes); + virtual void SetStruct(const char* var, const void* val, int bytes); + + // ---------------------------------------------------------------------------------- + // Use the 'SetVS' functions to assign a value into a vertex shader constants ( e.g. uniform extern float4 myVector; ) + // If the variable "var" is defined but NOT used by the shader code, the variable "var" doesn't exists in + // a constant table and an error is printed when trying to assign a value to it. + // ---------------------------------------------------------------------------------- + virtual void SetVSFloat(const char* var, float val); + virtual void SetVSInt(const char* var, int val); + virtual void SetVSBool(const char* var, bool val); + // ---------------------------------------------------------------------------------- + virtual void SetVSFloat(const char* var, const void* val, int bytes); + virtual void SetVSInt(const char* var, const int* val, int bytes); + virtual void SetVSBool(const char* var, const bool* val, int bytes); + virtual void SetVSStruct(const char* var, const void* val, int bytes); + + // ---------------------------------------------------------------------------------- + // SetTexture can be used to assign a taxture and a sampler state flags to a sampler + // In a shader code sampler is defined as (e.g. sampler mySamp; ) where "mySamp" is + // the variable passed to SetTexture function. It's then used in a shader code like + // tex2D(mySamp, float2(x,y)) + // ---------------------------------------------------------------------------------- + virtual void SetTexture(const char* var, SURFHANDLE hTex, DWORD flags); + + // ---------------------------------------------------------------------------------- + // SetOutput assigns a render target to the IP interface. "id" is an index of the render + // target with a maximum value of 3. It is possible to render in four different targets + // at the same time. Multisample AA is only supported with one render target. Unbound + // a render target by setting it to NULL. After a NULL render target all later targets + // are ignored. + // ---------------------------------------------------------------------------------- + virtual void SetOutput(int id, SURFHANDLE hSrf); + + // ---------------------------------------------------------------------------------- + // Set output rect is need to render into a specific subsection + // ---------------------------------------------------------------------------------- + virtual void SetOutputRegion(float w = 1.0f, float h = 1.0f, float x = 0.0f, float y = 0.0f); + + // ---------------------------------------------------------------------------------- + // Check that everything is Initialized OK and Execute the shader + // ---------------------------------------------------------------------------------- + virtual bool IsOK(); + virtual void SetMesh(const MESHHANDLE hMesh, const char* tex = NULL, ipicull = ipicull::None); + + virtual bool Execute(bool bInScene = false); + virtual bool Execute(DWORD blendop, bool bInScene = false, ipitemplate mde = Rect); + + // ---------------------------------------------------------------------------------- + virtual int FindDefine(const char* key); + +private: + + class ImageProcessing* pIPI; +}; + + + + + +// =========================================================================== +/** +* \class gcCore +* \brief Core class for graphics services +*/ +// =========================================================================== + + +INTERFACE_BUILDER class gcCore +{ +public: + + enum class Tile + { + TEXTURE = 0x0, ///< Texture data + MASK = 0x2, ///< Nightlights/Water mask + ELEVATION = 0x3, ///< Elevation + CLOUD = 0x4, ///< Texture data + ELEV_MOD = 0x5, ///< Elevation + // ------------------------------------------------------------------ + TREE = 0x10, ///< Search/Use Tree Archive + CACHE = 0x20, ///< Search/Use Cache + MOD = 0x40 ///< Search/Use ElevMod Cache + }; + + + enum class MatrixId + { + OFFSET = 1, ///< Set/Get Mesh offset matrix, Also used by VESSEL::ShiftMesh() + MESH = 2, ///< Set/Get Mesh animation matrix, Transforms all the groups in the mesh + GROUP = 3, ///< Set/Get Group animation matrix, Transforms a single group + COMBINED = 4 ///< Get combined Mesh*Group*Offset matrix. (Can't 'set' this) + }; + + enum class OlayType + { + RELEASE_ALL = -1, + SURFACE = 0, + MASK = 1, + ELEVATION = 2 + }; + + typedef struct { + void* pData; + DWORD Pitch; + } Lock; + + typedef struct { + double lng, lat; ///< Longitude and Latitude of the point being clicked + double elev; ///< Elevation of the point being clicked above mean radius + double dist; ///< Distance from a camera to a click point + DRECT Bounds; ///< Tile bounds (i.e. min/max * lng/lat) + FVECTOR3 normal; ///< Normal in ecliptic frame + FVECTOR3 pos; ///< Position from a camera in ecliptic frame + float emax; ///< Max elevation within the tile + float emin; ///< Min elevation within the tile + UINT msg; ///< Zero or (WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_LBUTTONUP, WM_RBUTTONUP) or (WM_MOUSEMOVE, WM_MOUSEWHEEL) + int level; ///< Tile level + int iLng, iLat; ///< Tile Index + HTILE hTile; ///< Tile handle being clicked. WARNING: Tile returned by this data entry can become invalid without notice. + } PickGround; + + + typedef struct { + WORD Size; ///< sizeof(ElevInfo) + WORD Format; + float* pElevData; + double Resolution; + double Offset; + double MinElev; + double MaxElev; + double MeanElev; + } ElevInfo; + + + typedef struct { + int Width; + int Height; + int Mips; + DWORD Flags; + } SurfaceSpecs; + + + typedef struct { + OBJHANDLE hVessel; ///< Handle to a vessel that was clicked + MESHHANDLE mesh; ///< Mesh index that was clicked + int group; ///< Mesh group index that was clicked + float dist; ///< Distance from a camera to a click point + FVECTOR3 normal; ///< Normal vector in local vessel coordinates + FVECTOR3 pos; ///< Position in local vessel coordinates + } PickData; + + + typedef struct { + int iUser; ///< Unused variable for user's own purposes. + int grp_inst; ///< Mesh group index or instance number that was clicked + float dist; ///< Distance from a camera to a click point + FVECTOR3 normal; ///< Normal vector in local vessel coordinates + FVECTOR3 pos; ///< Position in local vessel coordinates + } PickMeshStruct; + + + typedef struct { + int MaxTexSize; ///< Maximum texture size in pixels + int DisplayMode; ///< 0 = True Fullscreen, 1 = Fullscreen Window, 2 = Windowed + int MaxTexRep; ///< Maximum texture repeat count + DWORD gcAPIVer; ///< gcAPI Build Date 0xYYYYMMDD + } SystemSpecs; + + + typedef struct { + FVECTOR2 pos; + DWORD color; + } clrVtx; + + + // =========================================================================== + /// \name Custom swap-chain management functions + // =========================================================================== + //@{ + /** + * \brief Create a new custom swap-chain (i.e. Frontbufer/Backbuffer) for a user defined window. + * \param hWnd Handle to a window client rect + * \param hSwap Handle to an existing swap object to resize it. + * \param AA Level of requested anti-aliasing. Valid values are 0, 2, 4, 8 + * \return Handle to a Swap object or NULL in a case of an error + */ + gc_interface HSWAP RegisterSwap(HWND hWnd, HSWAP hSwap = NULL, int AA = 0); + + /** + * \brief Flip backbuffer to a front + * \param hSwap Handle to a swap object. + */ + gc_interface void FlipSwap(HSWAP hSwap); + + /** + * \brief Get a backbuffer surface + * \param hSwap Handle to a swap object. + * \return A Handle to a rendering surface (i.e. backbuffer) + */ + gc_interface SURFHANDLE GetRenderTarget(HSWAP hSwap); + + /** + * \brief Release a swap object after it's no longer needed. + * \param hSwap Handle to a swap object. + */ + gc_interface void ReleaseSwap(HSWAP hSwap); + //@} + + + + + // =========================================================================== + /// \name Custom Camera Interface + // =========================================================================== + //@{ + /** + * \brief Delete/Release a custom camera. + * \param hCam camera handle to delete. + * \return zero or an error code if the camara didn't work properly. + * \note Always delete all cameras bound to a render surface before releasing the rendering surface it-self. + */ + gc_interface int DeleteCustomCamera(CAMERAHANDLE hCam); + + /** + * \brief Toggle camera on and off + * \param hCam camera handle to toggle + * \param bOn true to turn on the camera. + * \note If multiple cameras are sharing the same rendering surface. Flickering will occur if more than one camera is turned on. + */ + gc_interface void CustomCameraOnOff(CAMERAHANDLE hCam, bool bOn); + + /** + * \brief Setup a custom camera overlay drawing callback + * \param hCam camera handle to toggle + * \param clbk pointer to a function to be called after each frame. + */ + gc_interface void CustomCameraOverlay(CAMERAHANDLE hCam, __gcRenderProc clbk, void* pUser); + + /** + * \brief Create a new custom camera that can be used to render views into a surfaces and textures + * \param hCam camera handle to modify an existing camera or, NULL + * \param hVessel handle to a vessel where the camera is attached to. + * \param vPos camera position in vessel's local coordinate system + * \param vDir camera direction in vessel's local coordinate system. [Unit Vector] + * \param vUp camera up vector. Must be perpendicular to vDir. [Unit Vector] + * \param dFow camera field of view in radians + * \param hSurf rendering surface. Must be created at least with OAPISURFACE_RENDER3D | OAPISURFACE_RENDERTARGET. Multiple cameras can share the same surface. + * \param dwFlags Flags to controls what is drawn and what is not. + * \return Camera handle, or NULL if an error occurred or if the custom camera interface is disabled. + * \note Camera count is unlimited. + * \note Only a cameras attached to currently active vessel are operational and recording. + * \note Having multiple cameras active at the same time doesn't impact in a frame rate, however, camera refresh rates are reduced. + */ + gc_interface CAMERAHANDLE SetupCustomCamera(CAMERAHANDLE hCam, OBJHANDLE hVessel, VECTOR3& vPos, VECTOR3& vDir, VECTOR3& vUp, double dFov, SURFHANDLE hSurf, DWORD dwFlags = 0xFF); + //@} + + + + + // =========================================================================== + /// \name Sketchpad related functions + // =========================================================================== + //@{ + /** + * \brief Get the sketchpad version + * \param pSkp handle to a sketchpad interface. + * \return Currently returns 2 or (1 in some very special cases). + */ + gc_interface int SketchpadVersion(Sketchpad *pSkp); + + + /** + * \brief Create or Update a polyline composed form piecewise straight segments. + * \param hPoly Handle to a polyline to be updated or NULL to create a new one. + * \param pt list of vertex points. + * \param npt number of points in the list. + * \param flags additional PolyFlags flags + * \sa gcDeletePoly, Sketchpad2::DrawPoly() + * \note Poly objects should be created during initialization not for every frame or update. Updating existing (pre created) poly object is pretty fast. + * \note During update number of points must be equal or smaller than during initial creation of poly object. + */ + gc_interface HPOLY CreatePoly(HPOLY hPoly, const oapi::FVECTOR2* pt, int npt, DWORD flags = 0); + + + /** + * \brief Create or Update a triangle object. + * \param hPoly Handle to a triangle to be updated or NULL to create a new one. + * \param pt list of vertex points. + * \param npt number of points in the list. + * \param flags additional flags (see below) + * \sa gcDeletePoly, Sketchpad2::DrawPoly() + * \note Poly objects should be created during initialization not for every frame or update. Updating existing (pre created) poly object is pretty fast. + * \note During update number of points must be equal or smaller than during initial creation of poly object. + * \note Flags: + * \note PF_TRIANGLES Each independent triangle is composed from three vertex points. ("npt" must be multiple of 3) + * \note PF_FAN Triangle fan. The first vertex is in the center of the fan/circle and other lie at the edge. ("npt" must be "number of triangles" + 2) + * \note PF_STRIP Is build from quads. Where each quad requires two vertices. ("npt" must be "number of quads" * 2 + 2) + */ + gc_interface HPOLY CreateTriangles(HPOLY hPoly, const clrVtx* pt, int npt, DWORD flags); + + + /** + * \brief Deletes a polyline created with gcCreatePolyPolyline() + * \param hPoly Handle to a polyline to be deleted + * \sa gcCreatePolyline + */ + gc_interface void DeletePoly(HPOLY hPoly); + + + /** + * \brief Compute a length of a text string + * \param hFont a Pointer into a font + * \param pText a Pointer into a text string + * \param len a Length of the text string to process. -1 will scan to a NULL terminator. + */ + gc_interface DWORD GetTextLength(oapi::Font* hFont, const char* pText, int len = -1); + + + /** + * \brief Find index of nearest "cap" between charters in specified location. (i.e. distance from start of the string in pixels) + * \param hFont a Pointer into a font + * \param pText a Pointer into a text line + * \param pos a Position in pixels from start of the string + * \param len a Length of the text line to process. -1 will process to a NULL terminator. + * \return index from 0 to number of charters. For just one char it can be either "0" or "1" depending which side is closer to "pos". + * \note This is used for finding a spot for a "cursor" when a text string is clicked with mouse. + */ + gc_interface DWORD GetCharIndexByPosition(oapi::Font* hFont, const char* pText, int pos, int len = -1); + + + /** + * \brief This function will register a custom render callback function + * \param proc function to be called when render event occur + * \param id render event id + * \param pParam a pointer to user data (to a class for an example) + * \return false if an error occurred, true otherwise. + */ + gc_interface bool RegisterRenderProc(__gcRenderProc proc, DWORD id, void* pParam); + + /** + * \brief Create a Font + * \param height Font height + * \param face Name of the font + * \param width Width of the font (0 for default aspect ration) + * \param weight Font thickness (400 for default weight) + * \param style A combination of \see gcFont flags (0 for default) + * \param spacing A spacing between charters in a string (0.0f for default) + * \return A pointer to a created or pre-existing font or NULL in a case of an error. + */ + gc_interface oapi::Font *CreateSketchpadFont(int height, char *face, int width = 0, int weight = 400, FontStyle Style = FontStyle::FONT_NORMAL, float spacing = 0.0f); + //@} + + + + + + // =========================================================================== + /// \name Mesh interface functions + // =========================================================================== + //@{ + /** + * \brief This function will register a custom render callback function + * \param hMesh Handle to a devmesh containing the material + * \param idx Material index + * \param prop material property identifier (\ref MeshMaterialFlags) + * \param value a pointer to COLOUR4 structure containing/receiving the data, or \e NULL to reset a default value or to unspecify a property. + * \param bSet \e true to set material value, \e false to get a material value + * \return -4 = Invalid handle \n -3 = Unknown property flag \n -2 = Property not specified cannot get it \n -1 = Index out of range \n 0 = Success + */ + gc_interface int GetMeshMaterial(DEVMESHHANDLE hMesh, DWORD idx, MatProp prop, FVECTOR4 *value); + + gc_interface int SetMeshMaterial(DEVMESHHANDLE hMesh, DWORD idx, MatProp prop, const FVECTOR4* value); + + /** + * \brief A Function to get a mesh transformation/animation matrix. + * \param matrix_id Id of the matrix to get. One of MatrixId::xxx datatypes. + * \param hVessel Vessel object handle. + * \param mesh Mesh index + * \param group Group index + * \param pMat A pointer to FMATRIX4 struct for receiving the data. + * \return 0 = on Success, or error code. + */ + gc_interface int GetMatrix(MatrixId mat, OBJHANDLE hVessel, DWORD mesh, DWORD group, oapi::FMATRIX4* pMat); + + + /** + * \brief A Function to set a mesh transformation/animation matrix. Do not use this function for animated parts/meshes. + * \param matrix_id Id of the matrix to set. One of MatrixId::xxx datatypes. + * \param hVessel Vessel object handle. + * \param mesh Mesh index + * \param group Group index + * \param pMat A pointer to FMATRIX4 containing the data to set. + * \return 0 = on Success, or error code. + */ + gc_interface int SetMatrix(MatrixId mat, OBJHANDLE hVessel, DWORD mesh, DWORD group, const oapi::FMATRIX4* pMat); + + + /** + * \brief Get device specific mesh from Orbiter mesh template + * \param hMesh handle to a mesh acquired from oapiLoadMeshGlobal() + * \param pBox a pointer to an array of 8 FVECTOR3s + */ + gc_interface DEVMESHHANDLE GetDevMesh(MESHHANDLE hMesh); + + gc_interface DEVMESHHANDLE LoadDevMeshGlobal(const char* file_name, bool bUseCache = true); + + gc_interface void ReleaseDevMesh(DEVMESHHANDLE hMesh); + + + /** + * \brief Recover tile bounding box data + * \param hTile handle to a tile + * \param pBox a pointer to an array of 8 FVECTOR3s + */ + gc_interface void RenderMesh(DEVMESHHANDLE hMesh, const FMATRIX4* pWorld); + + gc_interface bool PickMesh(PickMeshStruct* pm, DEVMESHHANDLE hMesh, const FMATRIX4* pWorld, short x, short y); + //@} + + + + + // =========================================================================== + /// \name Some Helper Functions + // =========================================================================== + //@{ + /** + * \brief Render a list of independent lines 0-1, 2-3,... + * \param pVtx a pointer to a vertex array + * \param pIdx a pointer to index array + * \param nIdx number of lines to draw multiplied by 2, (ARRAYSIZE() of index array) + * \param pWorld pointer to World matrix relative to camera + * \param color color in 0xAABBGGRR + */ + gc_interface void RenderLines(const FVECTOR3* pVtx, const WORD* pIdx, int nVtx, int nIdx, const FMATRIX4* pWorld, DWORD color); + + /** + * \brief Get some system information + * \param sp A pointer to SystemSpecs struct + * \param size sizeof(SystemSpecs) + */ + gc_interface void GetSystemSpecs(SystemSpecs* sp, int size); + + /** + * \brief Get some system information + * \param sp A pointer to SystemSpecs struct + * \param size sizeof(SystemSpecs) + */ + gc_interface bool GetSurfaceSpecs(SURFHANDLE hSrf, SurfaceSpecs *sp, int size); + + /** + * \brief Load a texture into a specific type of a surface + * \param fname name of a texture to be loaded. + * \param flags surface attributes (see: OAPISURFACE_x flags) + * \return surface handle or NULL in a case of an error + */ + gc_interface SURFHANDLE LoadSurface(const char *fname, DWORD flags); + + /** + * \brief Save a native DirectX surface to a file (*.dds, *.jpg, *.bmp, *.png) + * \param file filename. + * \param hSrf handle to a surface to same. + * \return false in a case of failure. + */ + gc_interface bool SaveSurface(const char* file, SURFHANDLE hSrf); + + /** + * \brief Get a handle to a specific mipmap sub-level + * \param hSrf Handle to a RenderTarget Texture containing mipmaps. + * \param level Level of the mipmap to acquire. (level >= 1) (0 = "hSrf" it self with surface interface) + * \return Surface handle to a render-target or NULL in a case of a failure. Must be released with after no longer accessed. + */ + gc_interface SURFHANDLE GetMipSublevel(SURFHANDLE hSrf, int level); + + /** + * \brief Realtime Mipmap auto-generation from the top/main level. + * \param hSurface handle to a surface + * \return false if an error occurred, true otherwise. + * \note Surface must be created with (OAPISURFACE_TEXTURE | OAPISURFACE_RENDERTARGET | OAPISURFACE_MIPMAPS) + * \note Exact attribute requirements/conflicts are unknown. + */ + gc_interface bool GenerateMipmaps(SURFHANDLE hSurface); + + /** + * \brief On the fly texture compression into a DXT format. Input remains uncanged. + * \param hSurface handle to a surface to compress + * \param flags combination of OAPISURFACE_PF_DXT1, OAPISURFACE_PF_DXT3, OAPISURFACE_PF_DXT5, OAPISURFACE_MIPMAPS, OAPISURFACE_SYSMEM + * \return Handle to a compressed texture, user must release this. + * \note Compression is slow, separate thread recommended for realtime compression. + */ + gc_interface SURFHANDLE CompressSurface(SURFHANDLE hSurface, DWORD flags); + + /** + * \brief Load a bitmap from file (*.bmp *.png *.jpg *.gif) + * \param fname name of the file to be loaded. + * \return Bitmap handle of NULL in a case of an error + */ + gc_interface HBITMAP LoadBitmapFromFile(const char *fname); + + /** + * \brief Get render window handle + * \return Render window handle + */ + gc_interface HWND GetRenderWindow(); + + /** + * \brief Register generic callback function + * \param proc function to be called when event occur + * \param id requested callback event id + * \param pParam a pointer to user data (to a class for an example) + * \return false if an error occurred, true otherwise. + */ + gc_interface bool RegisterGenericProc(__gcGenericProc proc, DWORD id, void *pParam); + + /** + * \brief Do not use these functions unless you know what's you doing. + */ + gc_interface bool StretchRectInScene(SURFHANDLE tgt, SURFHANDLE src, LPRECT tr = NULL, LPRECT sr = NULL); + + gc_interface bool ClearSurfaceInScene(SURFHANDLE tgt, DWORD color, LPRECT tr = NULL); + + + /** + * \brief Find a tile from specified coordinates. + * \param scr_x screen space x-coordinate. + * \param scr_y screen space y-coordinate. + * \return PickGround data structure, all members are zero if ray doesn't intersect ground. + */ + gc_interface PickGround ScanScreen(int scr_x, int scr_y); + + /** + * \brief Lock a surface created with OAPISURFACE_SYSMEM or OAPISURFACE_TEXTURE | OAPISURFACE_GDI flags. + * \param hSrf Handle to a surface/texture to lock + * \param pOut Data structure receiving pointer and row pitch/length + * \param bWait if "true" will wait other threads to release lock on resource if already locked. + * \return true if lock was gained, false otherwise + */ + gc_interface bool LockSurface(SURFHANDLE hSrf, Lock* pOut, bool bWait = false); + gc_interface void ReleaseLock(SURFHANDLE hSrf); + + /** + * \brief Alters objects position. Matrix must be initially valid. + * \param mat [in/out] Pointer to a matrix to change + * \param pos New position + */ + inline void SetTranslation(FMATRIX4* mat, const VECTOR3& pos) + { + mat->m41 = float(pos.x); mat->m42 = float(pos.y); mat->m43 = float(pos.z); + } + + inline void SetTranslation(FMATRIX4* mat, const FVECTOR3& pos) + { + mat->m41 = pos.x; mat->m42 = pos.y; mat->m43 = pos.z; + } + + /** + * \brief Creates a world transformation matrix + * \param mat [out] Pointer to a matrix + * \param pos Objects position relative to a camera in ecliptic frame + * \param x X-axis, direction [unit vector] + * \param z Z-axis, direction [unit vector] + * \param scale a scale factor (default 1.0) + */ + inline void WorldMatrix(FMATRIX4* mat, const VECTOR3& pos, const VECTOR3& x, const VECTOR3& z, double scale = 1.0) + { + VECTOR3 y = crossp(x, z); + mat->m11 = float(x.x * scale); mat->m12 = float(x.y * scale); mat->m13 = float(x.z * scale); mat->m14 = 0.0f; + mat->m21 = float(y.x * scale); mat->m22 = float(y.y * scale); mat->m23 = float(y.z * scale); mat->m24 = 0.0f; + mat->m31 = float(z.x * scale); mat->m32 = float(z.y * scale); mat->m33 = float(z.z * scale); mat->m34 = 0.0f; + mat->m41 = float(pos.x); mat->m42 = float(pos.y); mat->m43 = float(pos.z); mat->m44 = 1.0f; + } + + inline void WorldMatrix(FMATRIX4* mat, const FVECTOR3& pos, const FVECTOR3& x, const FVECTOR3& z, float scale = 1.0f) + { + FVECTOR3 y = crossp(x, z); + mat->m11 = (x.x * scale); mat->m12 = (x.y * scale); mat->m13 = (x.z * scale); mat->m14 = 0.0f; + mat->m21 = (y.x * scale); mat->m22 = (y.y * scale); mat->m23 = (y.z * scale); mat->m24 = 0.0f; + mat->m31 = (z.x * scale); mat->m32 = (z.y * scale); mat->m33 = (z.z * scale); mat->m34 = 0.0f; + mat->m41 = (pos.x); mat->m42 = (pos.y); mat->m43 = (pos.z); mat->m44 = 1.0f; + } + //@} + + + // =========================================================================== + // Function pointer table + // =========================================================================== + // +#define fnc_typedefs + // + // =========================================================================== +}; + + + +INTERFACE_BUILDER class gcCore2 : public gcCore +{ + +public: + + gcCore2(__gcBindCoreMethod pBindCoreMethod) + { +#define fnc_binder + } + + // =========================================================================== + /// \name Planetary surface interface + /// Graphics client maintains a tile database for a tiles used in rendering + /// This API can't access tile data outside visual range + // =========================================================================== + //@{ + gc_interface HPLANETMGR GetPlanetManager(OBJHANDLE hPlanet); + + gc_interface SURFHANDLE SetTileOverlay(HTILE hTile, const SURFHANDLE hOverlay); + + gc_interface HOVERLAY AddGlobalOverlay(HPLANETMGR hMgr, VECTOR4 mmll, OlayType type, const SURFHANDLE hOverlay = NULL, HOVERLAY hOld = NULL, const FVECTOR4 *pBlend = NULL); + + + + /** + * \brief Find a tile from a specified coordinates. Limited to a highest allocated level found from memory. + * \param hMgr handle to a tile/planet manager + * \param lng longitude of the location. + * \param lng latitude of the location. + * \param maxlevel highest level to search, -1 = Current render level. + * \return NULL, or a tile handle at the current render resolution + * \note WARNING: Tile returned by this function can become invalid without notice. + */ + gc_interface PickGround GetTileData(HPLANETMGR hMgr, double lng, double lat, int maxlevel = -1); + + gc_interface HTILE GetTile(HPLANETMGR hMgr, double lng, double lat, int maxlevel = -1); + + /** + * \brief Find a tile from a specified coordinates. Limited to a highest allocated level found from memory. + * \param hMgr handle to a tile/planet manager + * \param iLng longitude index + * \param iLng latitude index + * \param level level of the tile + * \param flags what to search (see gcTileFlags) + * \return NULL, or a tile handle at the current render resolution + * \note WARNING: Tile returned by this function can become invalid without notice. + */ + gc_interface bool HasTileData(HPLANETMGR hMgr, int iLng, int iLat, int level, int flags); + + gc_interface SURFHANDLE SeekTileTexture(HPLANETMGR hMgr, int iLng, int iLat, int level, int flags = 3, void *reserved = NULL); + + gc_interface bool SeekTileElevation(HPLANETMGR hMgr, int iLng, int iLat, int level, int flags, ElevInfo *pEI); + + + /** + * \brief Seek surface elevation from within the tile + * \param hTile handle to a tile + * \param lng geocentric longitude + * \param lat geocentric latitude + * \param out_elev pointer to float receiving the elevation above mean radius. + * \return 1 = Nominal, 0 = Tile Invisible but valid, -1 = (lng,lat) out of bounds, -3 = Fail + */ + gc_interface int GetElevation(HTILE hTile, double lng, double lat, double *out_elev); + + + /** + * \brief Create Image processing interface which allows user to process and create data via GPU + * \param file *.hlsl file name containing the shader + * \param PSEntry Entry point name for per pixel operations + * \param VSEntry Entry point name for vertex operations or NULL for a simple rectangular shape + * \param ppf a list of preprocessor directives e.g. "_MYSECTION;_DEBUG" used like #if defined(_MYSECTION) ..code.. #endif + * \return Pointer to IPInterface + */ + gc_interface gcIPInterface* CreateIPInterface(const char* file, const char* PSEntry, const char* VSEntry = NULL, const char* ppf = NULL); + gc_interface void ReleaseIPInterface(gcIPInterface* pIPI); + //@} +}; + + + +inline gcCore2* gcGetCoreInterface() +{ + if (pCoreInterface) return pCoreInterface; + HMODULE hModule = GetModuleHandle("VulkanClient.dll"); + if (hModule) { + __gcBindCoreMethod pBindCoreMethod = (__gcBindCoreMethod)GetProcAddress(hModule, "gcBindCoreMethod"); + if (pBindCoreMethod) return (pCoreInterface = new gcCore2(pBindCoreMethod)); + else oapiWriteLogV("gcGetCoreInterface() FAILED"); + } else oapiWriteLogV("gcGetCoreInterface() FAILED. VulkanClient Not Found"); + return NULL; +} + +#endif // !__GC_CORE diff --git a/OVP/VulkanClient/gcGUI.h b/OVP/VulkanClient/gcGUI.h new file mode 100644 index 000000000..ec6aca3ab --- /dev/null +++ b/OVP/VulkanClient/gcGUI.h @@ -0,0 +1,183 @@ +// =================================================== +// Copyright (C) 2021 Jarmo Nikkanen +// licensed under LGPL v2 +// =================================================== + +#include "OrbiterAPI.h" +#include "DrawAPI.h" +#include + +using namespace std; +using namespace oapi; + +#ifndef __GC_GUI +#define __GC_GUI + +namespace gcGUI +{ + // ----------------------------- + // Dialog status identifiers + // + static const int INACTIVE = 0; + static const int DS_FLOAT = 1; + static const int DS_LEFT = 2; + static const int DS_RIGHT = 3; + + // ----------------------------- + // Bitmap Identifiers + // + static const int BM_TITLE = 0; + static const int BM_SUBTITLE = 1; + static const int BM_ICONS = 2; + + // ----------------------------- + // Messages passed to gcGUIApp::clbkMessge + // + static const int MSG_OPEN_NODE = 1; + static const int MSG_CLOSE_NODE = 2; + static const int MSG_CLOSE_APP = 3; +}; + +typedef void * HNODE; + +class gcGUIBase +{ + friend class gcGUIApp; + +public: + + virtual HNODE RegisterApplication(gcGUIApp *pApp, const char *label, HWND hDlg, DWORD docked, DWORD color) = 0; + virtual HNODE RegisterSubsection(HNODE hNode, const char *label, HWND hDlg, DWORD color) = 0; + virtual void UpdateStatus(HNODE hNode, const char *label, HWND hDlg, DWORD color) = 0; + virtual bool IsOpen(HNODE hNode) = 0; + virtual void OpenNode(HNODE hNode, bool bOpen = true) = 0; + virtual void DisplayWindow(HNODE hNode, bool bShow = true) = 0; + virtual HFONT GetFont(int id) = 0; + virtual HNODE GetNode(HWND hDlg) = 0; + virtual HWND GetDialog(HNODE hNode) = 0; + virtual void UpdateSize(HWND hDlg) = 0; + virtual bool UnRegister(HNODE hNode) = 0; +}; + + + +// =========================================================================== +/** +* \class gcGUI +* \brief gcGUI Access and management functions +*/ +// =========================================================================== + +class gcGUIApp +{ + +public: + + gcGUIApp() : pApp(NULL) + { + + } + + + ~gcGUIApp() + { + // Can do nothing here, too late + } + + // ----------------------------------------------------- + + virtual void clbkShutdown() + { + + } + + virtual bool clbkMessage(DWORD uMsg, HNODE hNode, int data) + { + return false; + } + + // ----------------------------------------------------- + + inline bool Initialize() + { + typedef gcGUIBase * (__cdecl *__gcGetGUICore)(); + HMODULE hModule = GetModuleHandle("D3D9Client.dll"); + if (hModule) { + __gcGetGUICore pGetGUICore = (__gcGetGUICore)GetProcAddress(hModule, "gcGetGUICore"); + if (pGetGUICore) return ((pApp = pGetGUICore()) != NULL); + } + return false; + } + + HNODE RegisterApplication(const char *label, HWND hDlg, DWORD docked, DWORD color = 0) + { + assert(pApp); + return pApp->RegisterApplication(this, label, hDlg, docked, color); + } + + HNODE RegisterSubsection(HNODE hNode, const char *label, HWND hDlg, DWORD color = 0) + { + assert(pApp); + return pApp->RegisterSubsection(hNode, label, hDlg, color); + } + + void UpdateStatus(HNODE hNode, const char *label, HWND hDlg, DWORD color = 0) + { + assert(pApp); + return pApp->UpdateStatus(hNode, label, hDlg, color); + } + + bool IsOpen(HNODE hNode) + { + assert(pApp); + return pApp->IsOpen(hNode); + } + + void OpenNode(HNODE hNode, bool bOpen = true) + { + assert(pApp); + pApp->OpenNode(hNode, bOpen); + } + + void DisplayWindow(HNODE hNode, bool bShow = true) + { + assert(pApp); + pApp->DisplayWindow(hNode, bShow); + } + + HFONT GetFont(int id) + { + assert(pApp); + return pApp->GetFont(id); + } + + HNODE GetNode(HWND hDlg) + { + assert(pApp); + return pApp->GetNode(hDlg); + } + + HWND GetDialog(HNODE hNode) + { + assert(pApp); + return pApp->GetDialog(hNode); + } + + void UpdateSize(HWND hDlg) + { + assert(pApp); + pApp->UpdateSize(hDlg); + } + + bool UnRegister(HNODE hNode) + { + assert(pApp); + return pApp->UnRegister(hNode); + } + +private: + + gcGUIBase *pApp; +}; + +#endif \ No newline at end of file diff --git a/OVP/VulkanClient/res/Undo.bmp b/OVP/VulkanClient/res/Undo.bmp new file mode 100644 index 000000000..c47d96112 Binary files /dev/null and b/OVP/VulkanClient/res/Undo.bmp differ diff --git a/OVP/VulkanClient/resource.h b/OVP/VulkanClient/resource.h new file mode 100644 index 000000000..fd152917d --- /dev/null +++ b/OVP/VulkanClient/resource.h @@ -0,0 +1,276 @@ +#include +// Fix Microsoft Visual Studio Version 2012 resource compiler RC4011 warnings. +// Instead of #include +#if defined(RC_INVOKED) +#ifndef RICHEDIT_CLASS +#ifdef UNICODE +#define RICHEDIT_CLASS L"RichEdit20W" +#else +#define RICHEDIT_CLASS "RichEdit20A" +#endif // UNICODE +#endif // !RICHEDIT_CLASS +#endif // RC_INVOKED + + +#ifndef IDC_STATIC +#define IDC_STATIC (-1) +#endif + +#define IDS_INFO 1000 // never change these! +#define IDS_TYPE 1001 // " " " +#define IDD_vkCREDITS 101 +#define IDD_vkMESHDEBUG 102 +#define IDD_vkSCATTER 103 +#define IDD_vkSETUP 104 +#define IDD_DEBUGVIEW 105 +#define IDD_MESHDEBUG 106 +#define IDD_MATERIAL 107 +#define IDD_MICROTEXTOOLS 108 +#define IDD_SCENEDEBUG 109 +#define IDD_GRAPHICS 110 +#define IDB_BITMAP1 111 +#define IDC_AA 2002 +#define IDC_AF 2003 +#define IDC_ATM_LOAD 2034 +#define IDC_ATM_MODE 2036 +#define IDC_ATM_SAVE 2044 +#define IDC_BASEVIS 2045 +#define IDC_CONVERGENCE 2046 +#define IDC_CONV_DSP 2047 +#define IDC_CREDITS 2048 +#define IDC_CREDITSTEXT 2049 +#define IDC_DBG_ALPHA 2050 +#define IDC_DBG_AMBIENT 2051 +#define IDC_DBG_BLUE 2052 +#define IDC_DBG_BOXES 2053 +#define IDC_DBG_CAMERA 2054 +#define IDC_DBG_COPY 2055 +#define IDC_DBG_DISPLAY 2056 +#define IDC_DBG_DUAL 2057 +#define IDC_DBG_ENVMAP 2058 +#define IDC_DBG_FPSLIM 2059 +#define IDC_DBG_GREEN 2060 +#define IDC_DBG_GROUP 2061 +#define IDC_DBG_GRPDN 2062 +#define IDC_DBG_GRPO 2063 +#define IDC_DBG_GRPUP 2064 +#define IDC_DBG_HSG 2065 +#define IDC_DBG_HSM 2066 +#define IDC_DBG_LINK 2067 +#define IDC_DBG_MATADJ 2068 +#define IDC_DBG_MATGRP 2070 +#define IDC_DBG_MATPRP 2071 +#define IDC_DBG_MATSAVE 2072 +#define IDC_DBG_MESH 2073 +#define IDC_DBG_MESHNAME 2074 +#define IDC_DBG_MSHDN 2075 +#define IDC_DBG_MSHO 2076 +#define IDC_DBG_MSHUP 2077 +#define IDC_DBG_PASTE 2078 +#define IDC_DBG_PICK 2079 +#define IDC_DBG_RED 2080 +#define IDC_DBG_SPEED 2083 +#define IDC_DBG_SPEEDDSP 2084 +#define IDC_DBG_SPHERES 2085 +#define IDC_DBG_TEXTURE 2086 +#define IDC_DBG_VISO 2088 +#define IDC_DBG_VISUAL 2089 +#define IDC_DBG_WIRE 2090 +#define IDC_DEBUG 2091 +#define IDC_DEMAND 2092 +#define IDC_ENVFACES 2094 +#define IDC_ENVMODE 2095 +#define IDC_FONT 2096 +#define IDC_MIPMAPS 2097 +#define IDC_GLASSSHADE 2098 +#define IDC_HZ 2099 +#define IDC_LODBIAS 2100 +#define IDC_MESH_DEBUGGER 2101 +#define IDC_NEARPLANE 2102 +#define IDC_NORMALMAPS 2103 +#define IDC_PLANETGLOW 2104 +#define IDC_SEPARATION 2105 +#define IDC_SEPA_DSP 2106 +#define IDC_SRFPRELOAD 2107 +#define IDC_SYMBOLIC 2108 +#define IDC_ATM_COPYTO 2109 +#define IDC_CAMMODE 2110 +#define IDC_MESHRES 2111 +#define IDC_TEXMIPS 2112 +#define IDC_MICROMODE 2113 +#define IDC_MICROFILTER 2114 +#define IDC_DBG_MORE 2115 +#define IDC_DBG_SCENEDBG 2116 +#define IDC_BLENDMODE 2118 +#define IDC_DBG_OPEN 2119 +#define IDC_DBG_EXECUTE 2120 +#define IDC_DBG_ACTION 2121 +#define IDC_DBG_FILE 2122 +#define IDC_MICROBIAS 2123 +#define IDC_DBG_TARGET 2125 +#define IDC_DBG_VARA 2126 +#define IDC_DBG_VARB 2127 +#define IDC_DBG_VARC 2128 +#define IDC_DBG_NORM 2129 +#define IDC_DBG_FADE 2130 +#define IDC_DBG_SEAMS 2131 +#define IDC_DBG_TILEBB 2132 +#define IDC_DBG_RESBIAS 2136 +#define IDC_DBG_ENVSAVE 2139 +#define IDC_POSTPROCESS 2140 +#define IDC_ARCHIVE 2142 +#define IDC_DBG_GROUPSTAT 2143 +#define IDC_DBG_DEFINED 2145 +#define IDC_DBG_DATAWND 2154 +#define IDC_DBG_DATAVIEW 2155 +#define IDC_LIGHTCONFIG 2156 +#define IDC_DBG_KERNEL 2157 +#define IDC_SELFSHADOWS 2158 +#define IDC_SHADOWFILTER 2159 +#define IDC_TERRAIN 2160 +#define IDC_CLOUDMICRO 2161 +#define IDC_GDIOVERLAY 2162 +#define IDC_GUIMODE 2163 +#define IDC_ABSANIM 2164 +#define IDC_CLOUDNORM 2165 +#define IDC_FLATS 2166 +#define IDC_DBG_DEFSHADER 2167 +#define IDC_DBG_DATAVIEW2 2168 +#define IDC_GFX_INTENSITY 2170 +#define IDC_GFX_DISTANCE 2171 +#define IDC_GFX_GAMMA 2173 +#define IDC_GFX_VAL1 2174 +#define IDC_GFX_VAL2 2175 +#define IDC_GFX_VAL3 2176 +#define IDC_GFX_VAL4 2177 +#define IDC_GFX_INTENSITY_RESET 2178 +#define IDC_GFX_DISTANCE_RESET 2179 +#define IDC_GFX_GAMMA_RESET 2181 +#define IDC_DBG_EXTEND 2183 +#define IDC_DBG_RELOADSHD 2184 +#define IDC_GFX_SUNLIGHT 2185 +#define IDC_GFX_SUNLIGHT_RESET 2186 +#define IDC_GFX_IRRADIANCE 2187 +#define IDC_GFX_LOCALMAX 2188 +#define IDC_GFX_IRRADIANCE_RESET 2189 +#define IDC_GFX_LOCALMAX_RESET 2190 +#define IDC_GFX_VAL5 2191 +#define IDC_GFX_VAL6 2192 +#define IDC_GFX_VAL7 2193 +#define IDC_GFX_THRESHOLD 2194 +#define IDC_GFX_THRESHOLD_RESET 2195 +#define IDC_DBG_CONES 2196 +#define IDC_DBG_RELOADTEX 2197 +#define IDC_BREAK 2198 +#define IDC_ATM_DISPLAY 3012 +#define IDC_ATM_COPYLOW 3019 +#define IDC_ATM_COPYHIGH 3020 +#define IDC_GFX_GLARE 3025 +#define IDC_GFX_VAL8 3026 +#define IDC_GFX_GLARE_RESET 3027 +#define IDC_GFX_RECOMPILE 3028 +#define IDC_DBG_EXPTEX 3029 +#define IDC_ESUNGLARE 3030 +#define IDC_ELIGHTSGLARE 3031 +#define IDC_EIRRAD 3032 +#define IDC_EARTHVISCFG 3033 +#define IDC_ESCACHE 3034 +#define IDC_EAQUALITY 3035 +#define IDC_ATM_TRLIGHTSHAD 3036 +#define IDC_ATD_TRLIGHTSHAD 3037 +#define IDC_DBG_BKLID 3038 +#define IDC_DBG_BKLADJ 3039 +#define IDC_CASCOUNT 3040 +#define IDC_DBG_NOSHADOW 3041 +#define IDC_DBG_NORENDER 3042 +#define IDC_DBG_NOLIGHT 3043 +#define IDC_DBG_ADDITIVE 3044 +#define IDC_DBG_NOCOLOR 3045 +#define IDC_DBG_OIT 3046 +#define IDC_DBG_MATIDX 3047 +#define IDC_DBG_TEXIDX 3048 +#define IDC_DBG_MESHSAVE 3049 +#define IDC_DBG_NEXT 3050 +#define IDC_DBG_GRPLABEL 3051 +#define IDC_DBG_EXTVC 3052 +#define IDC_DBG_CLIPDIST 3053 +#define IDC_DBG_VISMODE 3054 +#define IDC_DBG_PICKCURRENT 3055 +#define IDC_DBG_DYNAMIC 3056 +#define IDC_DBG_ISVCMESH 3057 +#define IDC_DBG_VCSHADOW 3058 +#define IDC_DBG_AMBDIR 3059 +#define IDC_DBG_NOSUNAMB 3060 +#define IDC_DBG_NOPLNAMB 3061 +#define IDC_DBG_DATASRC 3062 +#define IDC_DBG_NODYNSUN 3063 +#define IDC_DBG_BKLGROUP 3064 +#define IDC_DBG_VCZONES 3065 +#define IDC_TILECOUNT 3036 +#define IDC_ATM_S1 4000 +#define IDC_ATM_S2 4001 +#define IDC_ATM_S3 4002 +#define IDC_ATM_S4 4003 +#define IDC_ATM_S5 4004 +#define IDC_ATM_S6 4005 +#define IDC_ATM_S7 4006 +#define IDC_ATM_S8 4007 +#define IDC_ATM_S9 4008 +#define IDC_ATM_S10 4009 +#define IDC_ATM_S11 4010 +#define IDC_ATM_S12 4011 +#define IDC_ATM_S13 4012 +#define IDC_ATM_S14 4013 +#define IDC_ATM_S15 4014 +#define IDC_ATM_S16 4015 +#define IDC_ATM_S17 4016 +#define IDC_ATM_S18 4017 +#define IDC_ATM_S19 4018 +#define IDC_ATM_S20 4019 +#define IDC_ATD_S1 4020 +#define IDC_ATD_S2 4021 +#define IDC_ATD_S3 4022 +#define IDC_ATD_S4 4023 +#define IDC_ATD_S5 4024 +#define IDC_ATD_S6 4025 +#define IDC_ATD_S7 4026 +#define IDC_ATD_S8 4027 +#define IDC_ATD_S9 4028 +#define IDC_ATD_S10 4029 +#define IDC_ATD_S11 4030 +#define IDC_ATD_S12 4031 +#define IDC_ATD_S13 4032 +#define IDC_ATD_S14 4033 +#define IDC_ATD_S15 4034 +#define IDC_ATD_S16 4035 +#define IDC_ATD_S17 4036 +#define IDC_ATD_S18 4037 +#define IDC_ATD_S19 4038 +#define IDC_ATD_S20 4039 +#define IDC_ATL_S1 4040 +#define IDC_ATL_S2 4041 +#define IDC_ATL_S3 4042 +#define IDC_ATL_S4 4043 +#define IDC_ATL_S5 4044 +#define IDC_ATL_S6 4045 +#define IDC_ATL_S7 4046 +#define IDC_ATL_S8 4047 +#define IDC_ATL_S9 4048 +#define IDC_ATL_S10 4049 +#define IDC_ATL_S11 4050 +#define IDC_ATL_S12 4051 +#define IDC_ATL_S13 4052 +#define IDC_ATL_S14 4053 +#define IDC_ATL_S15 4054 +#define IDC_ATL_S16 4055 +#define IDC_ATL_S17 4056 +#define IDC_ATL_S18 4057 +#define IDC_ATL_S19 4058 +#define IDC_ATL_S20 4059 +#define IDC_ATG1 4060 +#define IDC_ATG2 4061 +#define IDC_ATG3 4062 +#define IDC_ATG4 4063 +#define IDC_ATG5 4064 +#define IDC_ATG6 4065 +#define IDC_ATM_PAGE 4066 diff --git a/OVP/VulkanClient/shaders/BakedVC.fx b/OVP/VulkanClient/shaders/BakedVC.fx new file mode 100644 index 000000000..6c9b52ab3 --- /dev/null +++ b/OVP/VulkanClient/shaders/BakedVC.fx @@ -0,0 +1,238 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// licensed under LGPL v2 +// ============================================================== + + + + + +// ============================================================================ +// A Shader for a typical "Metalness" PBR workflow. +// ============================================================================ + +float4 BakedVC_PS(float4 sc : VPOS, PBRData frg) : COLOR +{ + float3 nrmT; + float3 nrmW; + float3 cEmis; + float4 cDiff; + float3 cBL; + float3 cBA; + float3 cBAO; + float fSmth, fMetal; + + // ====================================================================== + // Start fetching texture data + // ====================================================================== + + // Fetch a normal map + // + if (gCfg.Norm) nrmT = tex2D(Nrm0S, frg.tex0.xy).rgb; + + if (gTextured) cDiff = tex2D(WrapS, frg.tex0.xy); + else cDiff = 1; + + // Fetch Smoothness map (i.e. *_rghn.dds) + // + if (gCfg.Rghn) fSmth = tex2D(RghnS, frg.tex0.xy).g; + else fSmth = 1.0f; + + // Fetch Roughness map + // + if (gCfg.Metl) fMetal = tex2D(MetlS, frg.tex0.xy).g; + else fMetal = gMtrl.metalness; + + // Sample emission map. (Note: Emissive materials and textures need to go different stages, material is added to light) + // + if (gCfg.Emis) cEmis = tex2D(EmisS, frg.tex0.xy).rgb; + else cEmis = 0; + + // Load baked light emitters + // + if (gCfg.Baked) cBL = tex2D(BakedLightS, frg.tex0.xy).rgb; + else cBL = 0.0f; + + // Load baked ambient lights + // + if (gCfg.BakedAmb) cBA = tex2D(BakedSunS, frg.tex0.xy).rgb; + else cBA = 1.0f; + + // Load baked ambient occlusion (note: gCfg.BakedAO ahouldn't be enabled at the same time with gCfg.BakedAmb) + // + if (gCfg.BakedAO) cBAO = tex2D(BakedAOS, frg.tex0.xy).rgb; + else cBAO = 1.0f; + + + + // ---------------------------------------------------------------------- + // Now do other calculations while textures are being fetched + // ---------------------------------------------------------------------- + + float3 camW = normalize(frg.camW); + float3 cSun = gSun.Color; + + + // ====================================================================== + // Construct a proper world space normal + // ====================================================================== + + float3 tanW = frg.tanW.xyz; + float3 bitW = cross(tanW, frg.nrmW) * frg.tanW.w; + + if (gCfg.Norm) { + nrmT.rg = nrmT.rg * 2.0f - 1.0f; + nrmW = frg.nrmW*nrmT.z + tanW*nrmT.x + bitW*nrmT.y; + } + else nrmW = frg.nrmW; + + nrmW = normalize(nrmW); + + + + // ====================================================================== + // Typical compatibility requirements + // ====================================================================== + + cDiff.a = saturate(cDiff.a * gMtrlAlpha); + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); + + + // ====================================================================== + // Some Precomputations + // ====================================================================== + + float3 sunW = -gSun.Dir; + float3 cEnv = 0; + + float3 rflW = reflect(-camW, nrmW); + float3 hlvW = normalize(camW + sunW); + + // Dot Products + float uLN = dot(sunW, nrmW); + float uLC = dot(sunW, camW); + float dLN = saturate(uLN); + float dLH = saturate(dot(sunW, hlvW)); + float dCN = saturate(dot(camW, nrmW)); + float dHN = saturate(dot(hlvW, nrmW)); + + // Apply a proper curve to a texture data, modulate with material value and clamp + fSmth = pow(abs(fSmth), gMtrl.roughness.y) * gMtrl.roughness.x; + + // Apply fresnel and Fresnell cut-off to fSmth + fSmth = fSmth + ((1.0f - fSmth) * pow(abs(1.0f - dCN), 4.0f)) * pow(abs(fSmth), 0.5f); + + float fRgh = saturate(1.0f - fSmth); + float fRgh3 = fRgh*fRgh*fRgh; + + + // ====================================================================== + // Sample reflections and irradiance + // ====================================================================== + +#if defined(_ENVMAP) + if (gEnvMapEnable) + { + SampleEnvMap(cEnv, dCN, fRgh, fMetal, rflW, nrmW); + + // Use dynamic ambient light if baked doesn't exists + if (!gCfg.BakedAmb) { + float3 cP = Paraboloidal_LVLH(IrradS, nrmW).rgb; + cBA *= pow(abs(cP), gVCIrrad.w) * cBAO * gVCIrrad.rgb; + } + } +#else +#endif + + cBA *= (1.0f - fMetal); // No ambient for metals + + + // ====================================================================== + // Add vessel self-shadows + // ====================================================================== + +#if SHDMAP > 0 + if (gCockpit) { + cSun *= smoothstep(0, 0.72, ComputeShadowVC(frg.shdH, dLN, sc)); + } + else { + cSun *= smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); + } +#endif + + + + // ====================================================================== + // Main shader core MetalnessPS + // ====================================================================== + + float fD = GGX_NDF(dHN, lerp(0.01f, 1.0f, fRgh3)); + float fG = SchlickBeckmanGSF(dLN, dCN, fRgh); + float fR = DiffuseRetroReflectance(dLN, dCN, dLH, fRgh, fMetal); + + + // Base material color for reflections. Use cDiff for metals and very rough plastics, white for the rest. + // cDiff for rough plastics is to avoid washed-out(white) look of black and rough parts. + float3 cSpec = lerp(cDiff.rgb, float3(1, 1, 1), (1.0f - fMetal) * (1.0f - fRgh3)); + + // Fresnel power 2.5 for glossy, 5.0 for rough + float fFrs = pow(1.0f - dCN, fRgh*2.5 + 2.5f); + + // Fresnel cut-off below X of fSmth + fFrs *= saturate(0.3f - fRgh*fRgh) * 3.3f; + + // Assume that plastics absorve 50-90% of specular light + float fP = lerp(0.1f + (1.0f - fRgh)*0.4f, 1.0f, fMetal); + + // Fresnel color shift + float3 cF = cSpec + (1.0f - cSpec) * fFrs; + + // Specular Color + float3 cS = (fD * cF * fG * fP) * 0.25f; // (4.0f*dLN*dCN) removed to avoid division by zero, compensation in GSF + + + // How plastics reflect the environment + float R = 0.1f * fSmth; + float frP = R + (1.0f - R) * fFrs; + + float3 cE = (cEnv * cF * lerp(frP, 1.0f, fMetal)); + + // Attennuate diffuse color for Metals & Fresnel + float fA = (1.0f - fFrs) * (1.0f - fMetal); + + // Add a faint diffuse hue for rough metals. Rough metal doesn't look good if it's totally black + fA += fRgh * fMetal * 0.05f; + + // Light + float3 zL = Sq(cSun * fR * dLN) + Sq(cBL) + Sq(cBA) + Sq(gVCAmbient); + + // gVCAmbient is an application and debug controls controllable variable + float3 zD = cDiff.rgb * fA * LightFXSq(gMtrl.diffuse.rgb * zL + Sq(gMtrl.emissive.rgb)); + + // Combine specular terms + float3 zS = cS * (cSun * dLN); + + // Combine Diffuse, Specular and Environment + cDiff.rgb = zD + zS + cE; + + // Override material alpha to make reflections visible + cDiff.a = saturate(cDiff.a + cmax(zS + cE)); + + // Add emission texture to output, modulate with material + cDiff.rgb = max(cDiff.rgb, cEmis * gMtrl.emission2.rgb); + + +#if defined(_DEBUG) + cDiff = cDiff * (1.0f - gColor*0.5f) + gColor; +#endif + + + // Is post-processing shader enabled on not ? + // +#if defined(_LIGHTGLOW) + return cDiff; +#else + float3 h2 = cDiff.rgb*cDiff.rgb; + return float4(cDiff.rgb * pow(max(0, 1.0f + h2*h2), -0.25), cDiff.a); +#endif +} diff --git a/OVP/VulkanClient/shaders/BeaconArray.fx b/OVP/VulkanClient/shaders/BeaconArray.fx new file mode 100644 index 000000000..11ae7d833 --- /dev/null +++ b/OVP/VulkanClient/shaders/BeaconArray.fx @@ -0,0 +1,86 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +struct BAVERTEX { + float3 posL : POSITION0; + float3 dirL : NORMAL0; + float4 data : TEXCOORD0; + float2 dataB : TEXCOORD1; + float4 colr : COLOR0; +}; + + +struct BeaconVS +{ + float4 posH : POSITION0; + float4 colr : COLOR0; + float2 tex0 : TEXCOORD0; + float size : PSIZE; + float haze : COLOR1; +}; + + +BeaconVS BeaconArrayVS(BAVERTEX vrt) +{ + BeaconVS outVS = (BeaconVS)0; + + //float3 posX = mul(float4(vrt.posL, 1.0f), gW).xyz; + //float dist = length(posX); // distance to a beacon + //float3 offL = float3(0, dist*2e-3, 0); + //float3 posW = mul(float4(vrt.posL+offL, 1.0f), gW).xyz; + + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + float3 dirW = mul(float4(vrt.dirL, 0.0f), gW).xyz; + outVS.posH = mul(float4(posW, 1.0f), gVP); + + //posW = posW * gDistScale; + + float fog = exp(-abs(outVS.posH.z * gFogDensity * 20.0f)); // Haze effect scale factor + + float dist = length(posW); // distance to a beacon + + // Viewing angle dependency + float dota = -dot(normalize(posW), dirW); + float angl = saturate((dota - vrt.data[1])/(1.0f-vrt.data[1])); // beacon visibility from a current point 1.0 to 0.0 + float disp = pow(angl, vrt.dataB[1]); // apply a proper curve into a visibility + + // Distance attennuation + float att0 = vrt.dataB[0] / (1.0f+dist*2e-4); + float att1 = saturate(pow(att0, 2.0f)); + + if (gTimevrt.data[3]) disp = 0.0f; + + outVS.colr = float4(vrt.colr.rgb, min(1, vrt.colr.a * disp * att1 * 1.2f)); + outVS.size = (5.5f + vrt.data[0] * gPointScale / dist) * disp; + outVS.haze = clamp(fog*1.8f, 0.3f, 1.8f); + + return outVS; +} + +float4 BeaconArrayPS(BeaconVS frg) : COLOR +{ + float4 cTex = tex2D(ClampS, frg.tex0); + //return float4(frg.colr.rgb*saturate(pow(abs(cTex.a),0.3f)*gMix), pow(abs(cTex.a), frg.haze) * frg.colr.a); + return float4(frg.colr.rgb, pow(abs(cTex.a), frg.haze) * frg.colr.a); +} + + +technique BeaconArrayTech +{ + pass P0 + { + vertexShader = compile vs_3_0 BeaconArrayVS(); + pixelShader = compile ps_3_0 BeaconArrayPS(); + + PointSpriteEnable = true; + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + } +} \ No newline at end of file diff --git a/OVP/VulkanClient/shaders/CelSphere.hlsl b/OVP/VulkanClient/shaders/CelSphere.hlsl new file mode 100644 index 000000000..93d49528b --- /dev/null +++ b/OVP/VulkanClient/shaders/CelSphere.hlsl @@ -0,0 +1,61 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Licensed under LGPL v2 +// Copyright (C) 2021 Jarmo Nikkanen +// ============================================================== + +#define BOOL bool + +struct CelDataStruct +{ + float4x4 mWorld; + float4x4 mViewProj; + float fAlpha; + float fBeta; +}; + +// Booleans go to sepatate structure. (not mixing different datatypes in a structure) +// Also bool is 32-bits in HLSL therefore must use BOOL in C++ structure +struct CelDataFlow +{ + BOOL bAlpha; + BOOL bBeta; +}; + +struct TILEVERTEX // (VERTEX_2TEX) Vertex declaration used for surface tiles and cloud layer +{ + float3 posL : POSITION0; + float3 normalL : NORMAL0; + float2 tex0 : TEXCOORD0; + float elev : TEXCOORD1; +}; + +struct CelSphereVS +{ + float4 posH : POSITION0; + float2 tex0 : TEXCOORD0; +}; + +uniform extern CelDataStruct Const; +uniform extern CelDataFlow Flow; + +sampler2D tTexA; +sampler2D tTexB; + +CelSphereVS CelVS(TILEVERTEX vrt) +{ + // Zero output. + CelSphereVS outVS = (CelSphereVS)0; + float3 posW = mul(float4(vrt.posL, 1.0f), Const.mWorld).xyz; + outVS.posH = mul(float4(posW, 1.0f), Const.mViewProj); + outVS.tex0 = vrt.tex0; + return outVS; +} + +float4 CelPS(CelSphereVS frg) : COLOR +{ + float3 vColor = 0; + if (Flow.bAlpha) vColor += tex2D(tTexA, frg.tex0).rgb * Const.fAlpha; + if (Flow.bBeta) vColor += tex2D(tTexB, frg.tex0).rgb * Const.fBeta; + return float4(vColor, 1.0); +} diff --git a/OVP/VulkanClient/shaders/Common.hlsl b/OVP/VulkanClient/shaders/Common.hlsl new file mode 100644 index 000000000..ec2c81cbf --- /dev/null +++ b/OVP/VulkanClient/shaders/Common.hlsl @@ -0,0 +1,453 @@ + +#define KERNEL_RADIUS 2.0f + +sampler tShadowMap[3] : register(s13); + +// ============================================================================ +// +float4 Paraboloidal_LVLH(sampler s, float3 i) +{ + float z = dot(gCameraPos, i); + float2 p = float2(dot(gEast, i), dot(gNorth, i)) / (1.0f + abs(z)); + p *= float2(0.2273f, 0.4545f); + float4 A = tex2D(s, p + float2(0.25f, 0.5f)); + float4 B = tex2D(s, p + float2(0.75f, 0.5f)); + return lerp(A, B, smoothstep(-0.03, 0.03, z)); +} + +float3 Sq(float3 x) +{ + return x*x; +} + +float4 Sq(float4 x) +{ + return x*x; +} + +bool PointInRect(float2 pt) +{ + if (pt.x < 0) return false; + if (pt.x > 1) return false; + if (pt.y < 0) return false; + if (pt.y > 1) return false; + return true; +} + +// ========================================================================================================== +// Local light sources +// ========================================================================================================== + + +float3 Light_fx(float3 x) +{ + return saturate(x); //1.5 - exp2(-x.rgb)*1.5f; +} + +void LocalLights( + out float3 diff_out, + out float3 spec_out, + in float3 nrmW, + in float3 posW, + in float sp, + uniform int x, + uniform bool bSpec) +{ + + float3 posWN = normalize(-posW); + float3 p[4]; + float4 spe; + int i; + + // Relative positions + [unroll] for (i = 0; i < 4; i++) p[i] = posW - gLights[i + x].position; + + // Square distances + float4 sd; + [unroll] for (i = 0; i < 4; i++) sd[i] = dot(p[i], p[i]); + + // Normalize + sd = rsqrt(sd); + [unroll] for (i = 0; i < 4; i++) p[i] *= sd[i]; + + // Distances + float4 dst = rcp(sd); + + // Attennuation factors + float4 att; + [unroll] for (i = 0; i < 4; i++) att[i] = dot(gLights[i + x].attenuation.xyz, float3(1.0, dst[i], dst[i] * dst[i])); + + att = rcp(att); + + // Spotlight factors + float4 spt; + [unroll] for (i = 0; i < 4; i++) { + spt[i] = (dot(p[i], gLights[i + x].direction) - gLights[i + x].param[Phi]) * gLights[i + x].param[Theta]; + if (gLights[i + x].type == 0) spt[i] = 1.0f; + } + + spt = saturate(spt); + + // Diffuse light factors + float4 dif; + [unroll] for (i = 0; i < 4; i++) dif[i] = dot(-p[i], nrmW); + + dif = saturate(dif); + dif *= (att*spt); + + // Specular lights factors + if (bSpec) { + + [unroll] for (i = 0; i < 4; i++) spe[i] = dot(reflect(p[i], nrmW), posWN) * (dif[i] > 0); + + spe = pow(saturate(spe), sp); + spe *= (att*spt); + } + + diff_out = 0; + spec_out = 0; + + [unroll] for (i = 0; i < 4; i++) diff_out += gLights[i + x].diffuse.rgb * dif[i]; + + if (bSpec) { + [unroll] for (i = 0; i < 4; i++) spec_out += gLights[i + x].diffuse.rgb * spe[i]; + } +} + + +void LocalLightsBeckman( + out float3 diff_out, + out float3 spec_out, + in float3 nrmW, + in float3 posW, + in float fRgh, + uniform int x, + uniform bool bSpec) +{ + + float3 camW = normalize(-posW); + float3 p[4]; + float3 H[4]; + float4 spe; + float4 dHN; + int i; + + // Relative positions + [unroll] for (i = 0; i < 4; i++) p[i] = posW - gLights[i + x].position; + + // Square distances + float4 sd; + [unroll] for (i = 0; i < 4; i++) sd[i] = dot(p[i], p[i]); + + // Normalize + sd = rsqrt(sd); + [unroll] for (i = 0; i < 4; i++) p[i] *= sd[i]; + + // Distances + float4 dst = rcp(sd); + + if (bSpec) { + + // Halfway Vectors + float4 hd; + [unroll] for (i = 0; i < 4; i++) H[i] = (camW - p[i]); + [unroll] for (i = 0; i < 4; i++) hd[i] = dot(H[i], H[i]); + + hd = rsqrt(hd); + + [unroll] for (i = 0; i < 4; i++) H[i] *= hd[i]; + [unroll] for (i = 0; i < 4; i++) dHN[i] = dot(H[i], nrmW); + } + + + + // Attennuation factors + float4 att; + [unroll] for (i = 0; i < 4; i++) att[i] = dot(gLights[i + x].attenuation.xyz, float3(1.0, dst[i], dst[i] * dst[i])); + + att = rcp(att); + + // Spotlight factors + float4 spt; + [unroll] for (i = 0; i < 4; i++) { + spt[i] = (dot(p[i], gLights[i + x].direction) - gLights[i + x].param[Phi]) * gLights[i + x].param[Theta]; + if (gLights[i + x].type == 0) spt[i] = 1.0f; + } + + spt = saturate(spt); + + // Diffuse light factors + float4 dif; + [unroll] for (i = 0; i < 4; i++) dif[i] = dot(-p[i], nrmW); + + dif = saturate(dif); + + // Specular lights factors + + if (bSpec) { + + float r2 = fRgh*fRgh; + float4 d2 = dHN*dHN; + float4 w = rcp(3.14*r2*d2*d2); + float4 q = rcp(r2*d2); + + spe = (att*spt*dif) * w * exp((d2 - 1.0f) * q); + } + + dif *= (att*spt); + + diff_out = 0; + spec_out = 0; + + [unroll] for (i = 0; i < 4; i++) diff_out += Sq(gLights[i + x].diffuse.rgb * dif[i]); + + if (bSpec) { + [unroll] for (i = 0; i < 4; i++) spec_out += gLights[i + x].diffuse.rgb * spe[i]; + } +} + + + +void LocalLightsEx(out float3 cDiffLocal, out float3 cSpecLocal, in float3 nrmW, in float3 posW, in float sp, uniform bool ubBeckman) +{ + +#if LMODE !=0 + if (!gLightsEnabled) { + cDiffLocal = 0; + cSpecLocal = 0; + } +#endif + +#if LMODE == 1 + if (ubBeckman) LocalLightsBeckman(cDiffLocal, cSpecLocal, nrmW, posW, sp, 0, false); + else LocalLights(cDiffLocal, cSpecLocal, nrmW, posW, sp, 0, false); + +#elif LMODE == 2 + if (ubBeckman) LocalLightsBeckman(cDiffLocal, cSpecLocal, nrmW, posW, sp, 0, true); + else LocalLights(cDiffLocal, cSpecLocal, nrmW, posW, sp, 0, true); + +#elif LMODE == 3 + float3 dd, ss; + if (ubBeckman) { + LocalLightsBeckman(cDiffLocal, cSpecLocal, nrmW, posW, sp, 0, false); + LocalLightsBeckman(dd, ss, nrmW, posW, sp, 4, false); + } + else { + LocalLights(cDiffLocal, cSpecLocal, nrmW, posW, sp, 0, false); + LocalLights(dd, ss, nrmW, posW, sp, 4, false); + } + cDiffLocal += dd; + cSpecLocal += ss; + +#elif LMODE == 4 + float3 dd, ss; + if (ubBeckman) { + LocalLightsBeckman(cDiffLocal, cSpecLocal, nrmW, posW, sp, 0, true); + LocalLightsBeckman(dd, ss, nrmW, posW, sp, 4, true); + } + else { + LocalLights(cDiffLocal, cSpecLocal, nrmW, posW, sp, 0, true); + LocalLights(dd, ss, nrmW, posW, sp, 4, true); + } + cDiffLocal += dd; + cSpecLocal += ss; +#else + cDiffLocal = 0; + cSpecLocal = 0; +#endif +} + + + + + +// ========================================================================================================== +// Object Self Shadows +// ========================================================================================================== + +float SampleShadows(float2 sp, float pd, int sid) +{ + + float2 dxa = float2(gSHD[1], 0) * 0.75f; + float2 dya = float2(0, gSHD[1]) * 0.75f; + float2 dxb = dxa * 0.707f; + float2 dyb = dya * 0.707f; + float va = 0; + + if ((tex2D(tShadowMap[sid], sp - dxb - dyb).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp - dya).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp + dxb - dyb).r) > pd) va++; + + if ((tex2D(tShadowMap[sid], sp - dxa).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp + dxa).r) > pd) va++; + + if ((tex2D(tShadowMap[sid], sp - dxb + dyb).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp + dya).r) > pd) va++; + if ((tex2D(tShadowMap[sid], sp + dxb + dyb).r) > pd) va++; + + return va * 0.1111111f; +} + + +// --------------------------------------------------------------------------------------------------- +// +float SampleShadows2(float2 sp, float pd, int sid) +{ + + float val = 0; + float m = KERNEL_RADIUS * gSHD[1]; + + [unroll] for (int i = 0; i < KERNEL_SIZE; i++) { + if ((tex2D(tShadowMap[sid], sp + kernel[i].xy * m).r) > pd) val += kernel[i].z; + } + + return saturate(val * KERNEL_WEIGHT); +} + + +// --------------------------------------------------------------------------------------------------- +// +float SampleShadows3(float2 sp, float pd, float4 frame, int sid) +{ + + float val = 0; + frame *= KERNEL_RADIUS * gSHD[1]; + + [unroll] for (int i = 0; i < KERNEL_SIZE; i++) { + float2 ofs = frame.xy*kernel[i].x + frame.zw*kernel[i].y; + if (tex2D(tShadowMap[sid], sp + ofs).r > pd) val += kernel[i].z; + } + + return saturate(val * KERNEL_WEIGHT); +} + + +// --------------------------------------------------------------------------------------------------- +// +float SampleShadowsEx(float2 sp, float pd, float4 sc, int sid) +{ + +#if SHDMAP == 1 + return SampleShadows(sp, pd, sid); +#elif SHDMAP == 2 || SHDMAP == 4 + return SampleShadows2(sp, pd, sid); +#else + float si, co; + sc += (gSHD[2] * 2.0f); + sincos(sc.y + sc.x*149.0f, si, co); + return SampleShadows3(sp, pd, float4(si, co, co, -si), sid); +#endif +} + + + +// --------------------------------------------------------------------------------------------------- +// +float ComputeShadow(float4 shdH, float dLN, float4 sc) +{ + if (!gShadowsEnabled) return 1.0f; + + shdH.xyz /= shdH.w; + shdH.z = 1 - shdH.z; + float2 sp = shdH.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + + sp += gSHD[1] * 0.5f; + + if (sp.x < 0 || sp.y < 0) return 1.0f; // If a sample is outside border -> fully lit + if (sp.x > 1 || sp.y > 1) return 1.0f; + + float fShadow; + + float kr = gSHD[0] * KERNEL_RADIUS; + float dx = rsqrt(1.0 - dLN*dLN); + float ofs = 0.33f * kr / (dLN * dx); + float omx = min(gSHD[0] * 2.0f + max(0, ofs), 0.25); + + float pd = shdH.z + omx * gSHD[3]; + + if (pd < 0) pd = 0; + if (pd > 1) pd = 1; + + fShadow = SampleShadowsEx(sp, pd, sc, 0); + + return 1 - fShadow; +} + + +// --------------------------------------------------------------------------------------------------- +// +float ComputeShadowVC(float4 shdH, float dLN, float4 sc) +{ + if (!gShadowsEnabled) return 1.0f; + + shdH.xyz /= shdH.w; + shdH.z = 1 - shdH.z; + float2 sp = shdH.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + float2 c[3]; + int sid = 0; + +#if CASCOUNT >= 1 + c[0] = (sp + gSHDSubRect[0].xy) * gSHDSubRect[0].zw; + if (!PointInRect(c[0])) return 1.0f; // Sample outside of cascade '0' +#endif +#if CASCOUNT >= 2 + c[1] = (sp + gSHDSubRect[1].xy) * gSHDSubRect[1].zw; + if (PointInRect(c[1])) sid = 1; +#endif +#if CASCOUNT >= 3 + c[2] = (sp + gSHDSubRect[2].xy) * gSHDSubRect[2].zw; + if (PointInRect(c[2])) sid = 2; +#endif + + float kr = gSHDPx[sid]; + float omx = max(0.0015f, kr * sqrt(rcp(dLN * dLN) - 1.0f)); + float pd = shdH.z + min(omx, 0.02f) * gSHD[3]; + + if (pd < 0) pd = 0; + if (pd > 1) pd = 1; + + float fShadow[3]; + +#if CASCOUNT >= 1 + fShadow[0] = SampleShadows(c[0], pd, 0); +#endif +#if CASCOUNT >= 2 + fShadow[1] = SampleShadows(c[1], pd, 1); +#endif +#if CASCOUNT >= 3 + fShadow[2] = SampleShadows(c[2], pd, 2); +#endif + + return 1 - fShadow[sid]; +} + + +// --------------------------------------------------------------------------------------------------- +// +float3 VisualizeCascades(float4 shdH) +{ + if (!gShadowsEnabled) return float3(1, 1, 1); + + shdH.xyz /= shdH.w; + shdH.z = 1 - shdH.z; + float2 sp = shdH.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + + sp += gSHD[1] * 0.5f; // Shift 0.5 pixels aside + + float2 c0 = (sp + gSHDSubRect[0].xy) * gSHDSubRect[0].zw; + float2 c1 = (sp + gSHDSubRect[1].xy) * gSHDSubRect[1].zw; + float2 c2 = (sp + gSHDSubRect[2].xy) * gSHDSubRect[2].zw; + +#if CASCOUNT >= 3 + if (PointInRect(c2)) return float3(0, 0, 1); +#endif +#if CASCOUNT >= 2 + if (PointInRect(c1)) return float3(0, 1, 0); +#endif +#if CASCOUNT >= 1 + if (PointInRect(c0)) return float3(1, 0, 0); +#endif + + return float3(1, 1, 1); +} diff --git a/OVP/VulkanClient/shaders/Credits.rtf b/OVP/VulkanClient/shaders/Credits.rtf new file mode 100644 index 000000000..0c32e3d65 Binary files /dev/null and b/OVP/VulkanClient/shaders/Credits.rtf differ diff --git a/OVP/VulkanClient/shaders/Custom.hlsl b/OVP/VulkanClient/shaders/Custom.hlsl new file mode 100644 index 000000000..fd15cb10d --- /dev/null +++ b/OVP/VulkanClient/shaders/Custom.hlsl @@ -0,0 +1,105 @@ +// =================================================== +// Copyright (C) 2022 Jarmo Nikkanen +// licensed under MIT +// =================================================== + + +float ilerp(float a, float b, float x) +{ + return saturate((x - a) / (b - a)); +} + + +struct MESH_VERTEX { + float3 posL : POSITION0; + float3 nrmL : NORMAL0; + float3 tanL : TANGENT0; + float3 tex0 : TEXCOORD0; +}; + +struct MData { + float4 posH : POSITION0; + float3 camW : TEXCOORD0; +}; + + + +// ================================================================================================== +// Stage-Set for renderin auxiliary camera views +// ================================================================================================== + +sampler tTex; + +uniform extern struct { + float4x4 mVP; + float4x4 mW; +} cbPS; + +MData StageVS(MESH_VERTEX vrt) +{ + MData outVS = (MData)0; + float3 posW = mul(float4(vrt.posL, 1.0f), cbPS.mW).xyz; + outVS.posH = mul(float4(posW, 1.0f), cbPS.mVP); + outVS.camW = -posW; + return outVS; +} + +float4 StagePS(MData frg) : COLOR +{ + float3 cA = texCUBElod(tTex, float4(normalize(-frg.camW), 0.0)).rgb; + return float4(cA, 1); +} + + + +// ================================================================================================== +// Quadrilateral VC Click Zones +// ================================================================================================== + +struct { + float3 pt[4]; + float4 color; + float4x4 mW; + float4x4 mVP; + bool bSphere; +} cb; + +MData QuadVS(MESH_VERTEX vrt) +{ + MData outVS = (MData)0; + float3 pt; + float3 posL = vrt.posL; + + if (cb.bSphere) { + pt = cb.pt[0] + posL * cb.pt[1].x; + } + else { + posL = (posL + 1.0f) * 0.5f; + float3 h0 = normalize(cross(cb.pt[1] - cb.pt[0], cb.pt[2] - cb.pt[0])) * posL.z * 0.02f; + float3 p0 = lerp(cb.pt[0], cb.pt[1], posL.x); + float3 p1 = lerp(cb.pt[2], cb.pt[3], posL.x); + pt = lerp(p0, p1, posL.y) + h0; + } + + float3 nrmW = mul(float4(vrt.nrmL, 0.0f), cb.mW).xyz; + float3 posW = mul(float4(pt, 1.0f), cb.mW).xyz; + outVS.posH = mul(float4(posW, 1.0f), cb.mVP); + + if (cb.bSphere) outVS.camW = 1.0f - abs(dot(normalize(posW), nrmW)) * 0.9f; + else outVS.camW = posL; + + return outVS; +} + +float4 QuadPS(MData frg) : COLOR +{ + if (cb.bSphere) { + float b = frg.camW.z; + return float4(cb.color.rgb, cb.color.a * b); + } + else { + float a = 1 - frg.camW.z; + float b = a > 0.9 ? 0.2 : a; + return float4(cb.color.rgb, cb.color.a * b); + } +} diff --git a/OVP/VulkanClient/shaders/EnvMapBlur.hlsl b/OVP/VulkanClient/shaders/EnvMapBlur.hlsl new file mode 100644 index 000000000..20f791458 --- /dev/null +++ b/OVP/VulkanClient/shaders/EnvMapBlur.hlsl @@ -0,0 +1,47 @@ + +uniform extern float3 vDir; +uniform extern float3 vUp; +uniform extern float3 vCp; +uniform extern float fD; +uniform extern bool bDir; + +sampler tCube; +sampler tSrc; + +static float coeff[8] = { 0.13298076, 0.125794409, 0.106482669, 0.080656908, 0.054670025, 0.033159046, 0.017996989, 0.00874063 }; + +float4 PSBlur(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + x = x * 2.0f - 1.0f; + y = y * 2.0f - 1.0f; + + float3 vD; + + float3 dir = vDir - vUp*y + vCp*x; + + if (bDir) vD = cross(dir, vCp); + else vD = cross(dir, vUp); + + vD = normalize(vD) * fD; + + float3 color = texCUBE(tCube, dir).rgb; + float3 vX = 0; + float f = 0.75f; + float a = 0.5f; + + for (int i = 1; i < 16; i++) { + vX += vD; + color += f * texCUBE(tCube, dir + vX).rgb; + color += f * texCUBE(tCube, dir - vX).rgb; + a += f; + f *= 0.75f; + } + color /= (a*2.0f); + return float4(color, 1); +} + + +float4 PS2DBlur(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + return float4(0.5, 0.5, 0.5, 1.0); +} diff --git a/OVP/VulkanClient/shaders/GDIOverlay.hlsl b/OVP/VulkanClient/shaders/GDIOverlay.hlsl new file mode 100644 index 000000000..25457d5ae --- /dev/null +++ b/OVP/VulkanClient/shaders/GDIOverlay.hlsl @@ -0,0 +1,18 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2016 Jarmo Nikkanen +// ============================================================== + +uniform extern float4 vColorKey; +sampler tSrc; + +#define tol 0.02 + +float4 PSMain(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float3 vClr = tex2D(tSrc, float2(x, y)).rgb; + float3 c = abs(vClr - vColorKey.rgb); + if (c.r obscured + if (sp.x > 1 || sp.y > 1) return 0.0f; + + float2 vScale = 40.0f * cbPS.vSrc.zz; // Kernel scale factor (from unit kernel) + float fDepth = dot(frg.posW, cbPS.vDir) - 0.25f; // Depth to compare + float fRet = 0; + + [unroll] for (int i = 0; i < LocalKernelSize; i++) { + float2 s = sp + cbKernel[i].xy * vScale * 0.4f; + if (s.x < 0 || s.x > 1) { fRet += iLKS; continue; } + if (s.y < 0 || s.y > 1) { fRet += iLKS; continue; } + float d = tex2D(tDepth, s).a; + fRet += d > 0.1f && d < fDepth ? iLKS : 0; + } + + return 1.0f - fRet; +} + + + +// ==================================================================== +// Rendering of glares for the Sun and local lights +// ==================================================================== + +sampler2D tVis; // Pre-computed visibility factors +sampler2D tTex0; // Main Glare +sampler2D tTex1; // Atmospheric Glare + +uniform extern struct { + float4x4 mVP; + float4 Pos; + float4 Color; + float GPUId; + float Alpha; + float Blend; +} Const; + + +struct OutputVS +{ + float4 posH : POSITION0; + float3 uvi : TEXCOORD0; +}; + + +OutputVS GlareVS(float3 posL : POSITION0, float2 tex0 : TEXCOORD0) +{ + // Zero output. + OutputVS outVS = (OutputVS)0; + + float visibility = smoothstep(0.5f, 1.0f, tex2Dlod(tVis, float4(Const.GPUId, 0.5f, 0, 0)).r); + + posL.xy *= Const.Pos.zw * (0.01f + visibility); + posL.xy += Const.Pos.xy; + + outVS.posH = mul(float4(posL.xy - 0.5f, 0.0f, 1.0f), Const.mVP); + outVS.uvi = float3(tex0.xy, visibility); + + return outVS; +} + + +float4 GlarePS(OutputVS frg) : COLOR +{ + float t0 = max(0, tex2D(tTex0, frg.uvi.xy).r - 0.1f); // Texture intensity + //float t1 = max(0, tex2D(tTex1, frg.uvi.xy).r - 0.1f); // Texture intensity + //float t = lerp(t1, t0, Const.Blend); + float a = saturate(1.0f - exp(-frg.uvi.z * Const.Alpha * t0)); + return float4(HDRtoLDR(Const.Color.rgb * sqrt(t0 + 1.0f)), a); +} + + + + + + + + +// ==================================================================== +// Creation of "glare" textures +// ==================================================================== + + + +// ====================================================================== +// Render sun "Glare" (seen in space) +// +float4 CreateSunGlarePS(float u : TEXCOORD0, float v : TEXCOORD1) : COLOR +{ + u = u * 2.0 - 1.0; v = v * 2.0 - 1.0; + + float a = atan2(u, v); + float r = sqrt(u * u + v * v); + + float q = 0.5f + 0.3f * pow(sin(3.0f * a), 4.0f); + float w = 0.5f + 0.2f * pow(sin(30.0f * a), 2.0f) * pow(sin(41.0f * a), 2.0f); + + //float I = pow(max(0, 2.0f * (1 - r / q)), 12.0f); + //float K = pow(max(0, 2.0f * (1 - r / w)), 12.0f); + //float I = exp(max(0, 10.0f * (1 - r / q))) - 1.0f; + //float K = exp(max(0, 10.0f * (1 - r / w))) - 1.0f; + + float L = pow(max(0, (1 - r / q)), 6.0f) * 3.0f; // Low frequency spikes + float H = pow(max(0, (1 - r / w)), 6.0f) * 5.0f; // High frequency spikes + float C = ilerp(0.03, 0.01, r) * 7.0f; // Core + float S = ilerp(1.7f, 0.35f, r); // Skirt + + C *= C; + C += S * S * 0.40f; + + return float4(max(L + C, H + C), 0, 0, 1); +} + + + +// ====================================================================== +// Render sun "Glare" (seen in atmosphere) +// +float4 CreateSunGlareAtmPS(float u : TEXCOORD0, float v : TEXCOORD1) : COLOR +{ + u = u * 2.0 - 1.0; v = v * 2.0 - 1.0; + + float a = atan2(u, v); + float r = sqrt(u * u + v * v); + + float q = 0.5f + 1.0f * pow(sin(3.0f * a), 4.0f); + float w = 0.5f + 0.8f * pow(sin(30.0f * a), 2.0f) * pow(sin(41.0f * a), 2.0f); + + float I = pow(max(0, (1 - r / q)), 6.0f) * 4; + float K = pow(max(0, (1 - r / w)), 6.0f) * 8; + + float L = ilerp(0.05, 0.01, r) * 16.0f; + float T = max(0, max(I + L, K + L)) * 2.0f; + + return float4(T, 0, 0, 1); +} + + + +// ====================================================================== +// Render "Glare" for local light sources +// +float4 CreateLocalGlarePS(float u : TEXCOORD0, float v : TEXCOORD1) : COLOR +{ + u = u * 2.0 - 1.0; v = v * 2.0 - 1.0; + + float a = atan2(u, v); + float r = sqrt(u * u + v * v); + + float q = 0.5f + 0.5f * pow(sin(3.0f * a), 4.0f); + float w = 0.5f + 0.3f * pow(sin(30.0f * a), 2.0f) * pow(sin(41.0f * a), 2.0f); + + float L = pow(max(0, (1 - r / q)), 6.0f) * 4.0f; + float H = pow(max(0, (1 - r / w)), 6.0f) * 8.0f; + float C = ilerp(0.15, 0.10, r) * 4.0f; + + return float4(max(L + C, H + C), 0, 0, 1); +} + + + + +// ====================================================================== +// Render regular Sun texture [ NOT IN USE ] +// +float4 CreateSunTexPS(float u : TEXCOORD0, float v : TEXCOORD1) : COLOR +{ + u = u * 2.0 - 1.0; v = v * 2.0 - 1.0; + + float a = atan2(u, v); + float r = sqrt(u * u + v * v); + + float q = 0.5f + 1.0f * pow(sin(3.0f * a), 4.0f); + float w = 0.5f + 0.8f * pow(sin(30.0f * a), 2.0f) * pow(sin(41.0f * a), 2.0f); + + float I = pow(max(0, (1 - r / q)), 6.0f) * 1; + float K = pow(max(0, (1 - r / w)), 6.0f) * 3; + + float L = ilerp(0.08, 0.03, r) * 8.0f; + float T = max(0, max(I + L, K + L)); + + T = saturate(1.0f - exp(-T)); + return float4(1, 1, 1, T); +} + + diff --git a/OVP/VulkanClient/shaders/HorizonHaze.fx b/OVP/VulkanClient/shaders/HorizonHaze.fx new file mode 100644 index 000000000..ef45c04f4 --- /dev/null +++ b/OVP/VulkanClient/shaders/HorizonHaze.fx @@ -0,0 +1,45 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +HazeVS HazeTechVS(HZVERTEX vrt) +{ + // Zero output. + HazeVS outVS = (HazeVS)0; + + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.tex0 = vrt.tex0; + outVS.color = vrt.color; + return outVS; +} + + +// Horizon haze pixel-shader frg.tex0.y is the altitude. 0.0 = Horizon (ground level) 1.0 = top of atmosphere +// +float4 HazeTechPS(HazeVS frg) : COLOR +{ + return frg.color * tex2D(ClampS, frg.tex0); + + //return float4(frg.color.rgb, frg.color.a*frg.tex0.y*frg.tex0.y); + //return float4(frg.color.rgb*(frg.tex0.y+0.30), frg.color.a*frg.tex0.y*frg.tex0.y); +} + + +technique HazeTech +{ + pass P0 + { + vertexShader = compile vs_3_0 HazeTechVS(); + pixelShader = compile ps_3_0 HazeTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + } +} \ No newline at end of file diff --git a/OVP/VulkanClient/shaders/IPI.hlsl b/OVP/VulkanClient/shaders/IPI.hlsl new file mode 100644 index 000000000..76a57e698 --- /dev/null +++ b/OVP/VulkanClient/shaders/IPI.hlsl @@ -0,0 +1,39 @@ + +uniform extern float4x4 mVP; +uniform extern float4 vTgtSize; +uniform extern float4 vPos; + +struct OutputVS +{ + float4 posH : POSITION0; + float x : TEXCOORD0; + float y : TEXCOORD1; +}; + + +OutputVS VSMain(float3 posL : POSITION0, float2 tex0: TEXCOORD0) +{ + // Zero output. + OutputVS outVS = (OutputVS)0; + + posL.xy *= vPos.xy; + posL.xy += vPos.zw; + posL.xy *= vTgtSize.xy; + + outVS.posH = mul(float4(posL.xy-0.5f, 0.0f, 1.0f), mVP); + outVS.x = tex0.x; + outVS.y = tex0.y; + return outVS; +} + + + + +// Example of pixel shader + +sampler mySmp; + +float4 PSMain(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + return tex2D(mySmp, float2(x,y)); +} diff --git a/OVP/VulkanClient/shaders/IrradianceInteg.hlsl b/OVP/VulkanClient/shaders/IrradianceInteg.hlsl new file mode 100644 index 000000000..209dbd861 --- /dev/null +++ b/OVP/VulkanClient/shaders/IrradianceInteg.hlsl @@ -0,0 +1,76 @@ + +#define IKernelSize 120 + +uniform extern float4 Kernel[IKernelSize]; +uniform extern float3 vNr; // North +uniform extern float3 vUp; // Up +uniform extern float3 vCp; // Forward (East) +uniform extern float2 fD; +uniform extern float fIntensity; +uniform extern bool bUp; + +sampler tCube; +sampler tSrc; +sampler tRandom; + + +float3 Paraboloidal_to_World(float3 i) +{ + i.xy *= 1.1f; + float d = (1.0f - dot(i.xy, i.xy)) * 0.5f; + float3 p = normalize(float3(i.xy, d)); + return (vCp*p.x) + (vNr*p.y) + (vUp * p.z * i.z); +} + + +float4 PSInteg(float x : TEXCOORD0, float y : TEXCOORD1, float2 sc : VPOS) : COLOR +{ + float a = tex2D(tRandom, float2(x*3,y*1.5)).r * 6.283185307; + float2 qw = float2(x, y) * 2.0f - 1.0f; + float3 vz = Paraboloidal_to_World(float3(qw.xy, (bUp ? 1 : -1))); + float3 qx = normalize(cross(vz, vNr)); + float3 qy = normalize(cross(vz, qx)); + float3 vx = qx * sin(a) + qy * cos(a); + float3 vy = qx * cos(a) - qy * sin(a); + + float3 sum = 0; + + [unroll] for (int i = 0; i < IKernelSize; i++) { + float3 d = (vx*Kernel[i].x) + (vy*Kernel[i].y) + (vz*Kernel[i].z); + float3 k = texCUBElod(tCube, float4(d, 0)).rgb; + sum += k; // *Kernel[i].w; + } + + sum = sum * (1.0f / IKernelSize); + sum = sum * fIntensity; + + return float4(sum, 1.0f); +} + +// Exterior Irradiance blur +// +float4 PSPostBlur(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float3 color = 0; + float2 p = float2(x, y); + [unroll] for (int k = -2; k < 3; k++) { + [unroll] for (int i = -2; i < 3; i++) { + color += tex2D(tSrc, p + float2(i+0.5, k+0.5) * fD).rgb; + } + } + return float4(color / 25, 1); +} + +// Interior Irradiance blur +// +float4 PSPostBlurIntr(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float3 color = 0; + float2 p = float2(x, y); + [unroll] for (int k = -5; k < 6; k++) { + [unroll] for (int i = -5; i < 6; i++) { + color += tex2D(tSrc, p + float2(i + 0.5, k + 0.5) * fD).rgb; + } + } + return float4(color / 121, 1); +} diff --git a/OVP/VulkanClient/shaders/LensFlare.hlsl b/OVP/VulkanClient/shaders/LensFlare.hlsl new file mode 100644 index 000000000..7b2e2a143 --- /dev/null +++ b/OVP/VulkanClient/shaders/LensFlare.hlsl @@ -0,0 +1,200 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2016 SolarLiner (Nathan Graule) +// ============================================================== + +// This is the ogirinal, rewrited versiion for vk Client + +sampler2D tBack; +sampler2D tCLUT; + +struct SUNVISPARAMS +{ + float brightness; + bool visible; + float2 position; + float4 color; +}; + +uniform extern SUNVISPARAMS sunParams; +uniform extern bool bCockpitCamera; + +uniform extern float2 vResolution; + +uniform extern float fSize; +uniform extern float fBrightness; +#define EPSILON 1e-10 +#define CLUT_SIZE float2(256, 16) + +float2 GetDistOffset(float2 uv, float2 pxoffset, float dist) +{ + if(dist == 0.0) return pxoffset; + float2 tocenter = uv.xy; + float3 prep = normalize(float3(tocenter.y, -tocenter.x, 0.0)); + + float angle = length(tocenter.xy)*2.221*saturate(dist); + float3 oldoffset = float3(pxoffset,0.0); + + float3 rotated = oldoffset * cos(angle) /*+ cross(prep, oldoffset) * sin(angle) + prep * dot(prep, oldoffset) * (1.0-cos(angle))*/; + + return rotated.xy; +} + +float3 HUEtoRGB(in float H) +{ + float R = abs(H * 6 - 3) - 1; + float G = 2 - abs(H * 6 - 2); + float B = 2 - abs(H * 6 - 4); + return saturate(float3(R,G,B)); +} +float3 RGBtoHCV(in float3 RGB) +{ + // Based on work by Sam Hocevar and Emil Persson + float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0); + float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx); + float C = Q.x - min(Q.w, Q.y); + float H = abs((Q.w - Q.y) / (6 * C + EPSILON) + Q.z); + return float3(H, C, Q.x); +} +float3 RGBtoHSV(in float3 RGB) +{ + float3 HCV = RGBtoHCV(RGB); + float S = HCV.y / (HCV.z + EPSILON); + return float3(HCV.x, S, HCV.z); +} +float3 HSVtoRGB(in float3 HSV) +{ + float3 RGB = HUEtoRGB(HSV.x); + return ((RGB - 1) * HSV.y + 1) * HSV.z; +} + +float3 complementary(float3 c) +{ + float3 hsv = RGBtoHSV(c); + hsv.r += 0.5; + hsv.r = hsv.r > 1.0? hsv.r-1.0 : hsv.r; + + return HSVtoRGB(hsv); +} + +float3 CLUT(float3 color) +{ + float2 CLut_pSize = 1 / CLUT_SIZE; + + float3 c = saturate(color); + c.b *= 15; + float3 clut_uv = 0; + clut_uv.z = floor(c.b); + clut_uv.xy = c.rg*15*CLut_pSize+0.5*CLut_pSize; + clut_uv.x += clut_uv.z * CLut_pSize.y; + + return lerp( tex2D(tCLUT, clut_uv.xy).rgb, tex2D(tCLUT, clut_uv.xy + float2(CLut_pSize.y, 0)).rgb, c.b - clut_uv.z); +} + +// Renders the main light glare, along with added streaks +float glare(float2 uv, float2 pos, float size, float streakCount, float streakMix) +{ + float2 p = uv-pos; + + float angle = atan2(p.y, p.x); + float dist = length(p); + + float bright = size-pow(dist, 0.1)*size; + float streak = pow(sin(angle*streakCount*0.5), 2.0)*size; + + return max(0.0, bright*6.0 + streak*0.32*saturate(streakMix)); +} + +// Render circular blobs, colors dissociate and separate with distance of the light form the center +float flare (float2 uv, float2 pos, float dist, float size) +{ + return max(0.01 - pow(length(uv+dist*pos), 10.0*size), 0.0)*6.0; +} +float3 flare(float2 uv, float2 pos, float dist, float size, float barrel, float3 color) +{ + pos = GetDistOffset(uv, pos, barrel); + + float r = flare(uv, pos, dist-0.02, size); + float g = flare(uv, pos, dist , size); + float b = flare(uv, pos, dist+0.02, size); + + return max(0.0, color*float3(r,g,b)); +} + +// Renders an arc lined with the light source and the center of screen (dist only changes the side the ring is rendered) +float3 ring(float2 uv, float2 pos) +{ + float2 uvd = uv*length(uv); + float3 col = 0; + col.r = max(0.0, pow(abs(1.0-pow(length(uvd*0.90), 4.0)) * length(uvd), 3.5)); + col.g = max(0.0, pow(abs(1.0-pow(length(uvd*0.95), 4.0)) * length(uvd), 3.2)); + col.b = max(0.0, pow(abs(1.0-pow(length(uvd*1.00), 4.0)) * length(uvd), 3.0)); + + float s = max(0.0, 1.0/(1.0 + 32.0 * pow(length(uvd+pos), 4.0))); + + return col * s*4.0; +} + +float3 LensFlare_Exterior(float2 uv, float2 pos, float brightness, float size, float3 color) +{ + // Here you can customize your lens flare. + // The glare should only appear one and is the star at the position of the light. + // The flare is the round artifact that appear along the light-center axis. + // The orb is a collection of flares in a particular arrangement that kinda looks like a caustic. + // The ring is a wide arc. + // + // The "uv" and "pos" variables are mandatory in the drawing of the lens flare and should always be put first and in this order. + // Next argument is the distance of the artifact. -1.0 is at the light source position, 0.0 is at the center, and 1.0 at the opposite of the light source. + // Then comes the size. Please make it a multiple of "size" so that is responds to the global size (use a constant if you want the artifact not to scale). + // Finally is the color of the flare. use it like this: float3(red, green, blue). + float3 f = flare(uv, pos, 1, size, 1.0, float3(0.1, 1.0, 0.5)*color)*3.0; + + f += flare(uv,pos, -3, 3*size, 0.1, float3(0.4, 0.6, 1.0)*complementary(color)); + + f += flare(uv,pos, -0.5, size, 0.6, float3(0.8, 0.3, 0.9)*complementary(color))*1.5; + f += flare(uv,pos, -0.2, size*0.6, 1.0, float3(1.0, 0.6, 0.8)*color); + f += flare(uv,pos, 0.1, size*0.5, 0.8, float3(0.3, 0.6, 0.5)*color)*1.5; + f += flare(uv,pos, 0.6, size, 0.5, float3(0.6, 0.3, 0.2)*complementary(color))*2.0; + + float3 c = glare(uv,pos, pow(max(1.0, length(pos)), 2.0)*size, 8.0, 0.5)*color; + c += f*(1.0-pow(saturate(length(pos)*0.50), 2.0)); + + c += ring(uv, pos)*1.5*size*color; + + return c*brightness; +} + +float3 LensFlare_Cockpit(float2 uv, float2 pos, float brightness, float size, float3 color) +{ + float3 c = glare(uv, pos, size, 16.0, 0.2)*color; + + return c*brightness; +} + +float3 Screen(float3 a, float3 b) +{ + return 1 - (1-saturate(a))*(1-saturate(b)); +} + +float4 PSMain(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float3 orig = tex2D(tBack, float2(x, y)).rgb; + float3 flare = (float3)0; + + if (sunParams.visible) + { + float2 uv = float2(x,y) - 0.5; + uv.x *= vResolution.x / vResolution.y; // Aspect ratio correction + uv.y = -uv.y; + + flare = bCockpitCamera? + LensFlare_Cockpit (uv, sunParams.position, sunParams.brightness*sunParams.color.a, 0.4/(fSize), sunParams.color.rgb)*1.414 : + LensFlare_Exterior(uv, sunParams.position, sunParams.brightness*sunParams.color.a, 0.4/(fSize), sunParams.color.rgb)*1.414; + } + + //float3 color = pow(max(0.0, Screen(orig, flare)), 2.2); + float3 color = Screen(orig, pow(saturate(flare), 2.2)); + //color = CLUT(color); + return float4(color, 1.0); +} \ No newline at end of file diff --git a/OVP/VulkanClient/shaders/LightBlur.hlsl b/OVP/VulkanClient/shaders/LightBlur.hlsl new file mode 100644 index 000000000..7e8ff8c2d --- /dev/null +++ b/OVP/VulkanClient/shaders/LightBlur.hlsl @@ -0,0 +1,181 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2016 Jarmo Nikkanen +// 2016 SolarLiner (Nathan Graule) +// ============================================================== + +// Shader configurations ----------------------------------------------- +// +//#define fGlowIntensity 0.1 // Overal glow brightness multiplier +#define radius 20 // Radius of the glow +#define rate 0.7 // glow "linearity" [0.7 to 0.95] +#define fMinThreshold 1.1 // Glow starts to appear when back buffer intensity reaches this level +#define fMaxThreshold 2.5 // Glow reaches it's maximum intensity when backbuffer goes above this level + + +// Orher configurations ------------------------------------------------ +// +#define fSunIntensity 3.14 // Sunlight intensity +#define fInvSunIntensity (1.0/fSunIntensity) + +// --------------------------------------------------------------------- +// Client configuration parameters +// +#define BufferDivider 2 // Blur buffer size in pixels = ScreenSize / BufferDivider +#define PassCount 1 // Number of "bBlur" passes +#define BufferFormat 1 // Render buffer format, 2=RGB10A2, 1 = RGBA_16F, 0=DEFAULT (RGBX8) +// --------------------------------------------------------------------- + +uniform extern float fIntensity; +uniform extern float fDistance; +uniform extern float fThreshold; +uniform extern float fGamma; +uniform extern float2 vSB; +uniform extern float2 vBB; +uniform extern bool bDir; +uniform extern bool bBlur; +uniform extern bool bBlendIn; +uniform extern bool bSample; + +uniform extern int PassId; // NOTE: CANNOT be used to toggle code section on and off efficiently, any code effected by PassId must be minimized + +sampler tBack; +sampler tBlur; +sampler tCLUT; // 2D vkClut.dds texture +sampler tTone; // 4x4 mipmap of backbuffer + + +static const float3 cMult = { 3.0f, 1.0f, 5.0f }; + +float Desaturate (float3 color) +{ + return dot(color, float3(0.2, 0.7, 0.1) ); +} + + + +float3 HDRtoLDR(float3 hdr) +{ + float3 h2 = hdr*hdr; + return hdr * pow(max(0, 1.0f + h2*h2), -0.25); +} + + +float4 PSMain(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float2 vPos = float2(x,y); + + float2 vX = float2(vSB.x, 0); // Delta between two pixels in a "glow" buffer + float2 vY = float2(0, vSB.y); + + float2 sX = float2(vBB.x, 0); // Delta between two pixels in backbuffer + float2 sY = float2(0, vBB.y); + + float3 color = 0; + + + // Sample a backbuffer into a glow buffer -------------------------------- + // + if (bSample) { + float3 res = tex2D(tBack, vPos).rgb; + //res += tex2D(tBack, vPos + sX).rgb; + //res += tex2D(tBack, vPos + sY).rgb; + //res += tex2D(tBack, vPos + sX + sY).rgb; + //res *= 0.25f; + float s = Desaturate(res); + res *= smoothstep(fThreshold, fThreshold*1.5f, s) * 3.0f * rsqrt(1.0f + s*s); + return float4(abs(res), 1); + } + + + // Construct a glow gradient --------------------------------------------- + // + if (bBlur) { + + if (bDir) vX = vY; + + float2 pos = vPos; + float f = 1.0f; + float d = 1.0f; + + color += tex2D(tBlur, pos).rgb; + + for (int i = 1; i 3) return float4(0, 0, 0, 1); + if (a < -1) return saturate(float4(0, 0, 2 + a, 1.0)); + else if (a < 0) return saturate(float4(0, 1 + a, 1, 1.0)); + else if (a < 1) return saturate(float4(a, 1, 1 - a, 1.0)); + else if (a < 2) return saturate(float4(1, 2 - a, 0, 1.0)); + return saturate(float4(1, a - 2, a - 2, 1.0)); +} + + +// Visualize screen depth +// +float4 PSDepth(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float z = tex2D(tBack, float2(x,y)).a; + if (z <= 0) return float4(0, 0, 0, 1); + float q = 3 - log(1.0f + sqrt(max(0, z - 5.0))); + return float4(HeightToColor(q).rgb, 1); +} + +// Visualize screen space normals +// +float4 PSNormal(float tx : TEXCOORD0, float ty : TEXCOORD1) : COLOR +{ + float3 q = tex2D(tBack, float2(tx,ty)).xyz; + float2 xy = (q.xy + 1.0f) * 0.5f; + return float4(xy, abs(q.z), 1.0f); +} + diff --git a/OVP/VulkanClient/shaders/Mesh.fx b/OVP/VulkanClient/shaders/Mesh.fx new file mode 100644 index 000000000..ee9477438 --- /dev/null +++ b/OVP/VulkanClient/shaders/Mesh.fx @@ -0,0 +1,552 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012-2016 Jarmo Nikkanen +// ============================================================== + + +struct TileMeshVS +{ + float4 posH : POSITION0; + float3 CamW : TEXCOORD0; + float2 tex0 : TEXCOORD1; + float3 nrmW : TEXCOORD2; + float4 atten : COLOR0; // (Atmospheric haze) Attennuate incoming fragment color + float4 insca : COLOR1; // (Atmospheric haze) "Inscatter" Add to incoming fragment color +}; + +struct MeshVS +{ + float4 posH : POSITION0; + float3 CamW : TEXCOORD0; + float2 tex0 : TEXCOORD1; + float3 nrmW : TEXCOORD2; +}; + +struct TileMeshNMVS +{ + float4 posH : POSITION0; + float3 camW : TEXCOORD0; + float4 atten : TEXCOORD1; + float4 insca : TEXCOORD2; + float2 tex0 : TEXCOORD3; + float3 nrmT : TEXCOORD4; + float3 tanT : TEXCOORD5; + +}; + +MeshVS TinyMeshTechVS(MESH_VERTEX vrt) +{ + // Zero output. + MeshVS outVS = (MeshVS)0; + + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; // Apply world transformation matrix + outVS.posH = mul(float4(posW, 1.0f), gVP); + float3 nrmW = mul(float4(vrt.nrmL, 0.0f), gW).xyz; // Apply world transformation matri + outVS.nrmW = normalize(nrmW); + outVS.CamW = -posW; + outVS.tex0 = vrt.tex0.xy; + + return outVS; +} + + +float4 TinyMeshTechPS(MeshVS frg) : COLOR +{ + return float4(0,1,0,1); + + // Normalize input + float3 nrmW = normalize(frg.nrmW); + float3 CamW = normalize(frg.CamW); + float4 cSpec = gMtrl.specular; + float4 cTex = 1; + + if (gTextured) cTex = tex2D(WrapS, frg.tex0.xy); + + cTex.rgb = saturate(cTex.rgb + gNoColor.rgb); + + if (gFullyLit) return float4(cTex.rgb*saturate(gMtrl.diffuse.rgb + gMtrl.emissive.rgb), cTex.a); + + cTex.a *= gMtrlAlpha; + + // Sunlight calculations. Saturate with cSpec.a to gain an ability to disable specular light + float d = saturate(-dot(gSun.Dir, nrmW)); + float s = pow(saturate(dot(reflect(gSun.Dir, nrmW), CamW)), cSpec.a) * saturate(cSpec.a); + + if (d == 0) s = 0; + + float3 diff = gMtrl.diffuse.rgb * (d * saturate(gSun.Color)); // Compute total diffuse light + diff += (gMtrl.ambient.rgb*gSun.Ambient) + (gMtrl.emissive.rgb); + + float3 cTot = cSpec.rgb * (s * gSun.Color); // Compute total specular light + + cTex.rgb *= saturate(diff); // Lit the diffuse texture + +#if defined(_GLASS) + cTex.a = saturate(cTex.a + max(max(cTot.r, cTot.g), cTot.b)); // Re-compute output alpha for alpha blending stage +#endif + + cTex.rgb += cTot.rgb; // Apply reflections to output color + + return cTex; +} + + + +// ============================================================================ +// Planet Rings Technique +// ============================================================================ + +float4 RingTechPS(MeshVS frg) : COLOR +{ + float4 color = tex2D(RingS, frg.tex0); + + float3 pp = gCameraPos*gRadius[2] - frg.CamW*gDistScale; + + float da = dot(normalize(pp), gSun.Dir); + float r = sqrt(dot(pp,pp) * (1.0-da*da)); + + float sh = max(0.05, smoothstep(gRadius[0], gRadius[1], r)); + + if (da<0) sh = 1.0f; + + if ((dot(frg.nrmW, frg.CamW)*dot(frg.nrmW, gSun.Dir))>0) return float4(color.rgb*0.35f*sh, color.a); + return float4(color.rgb*sh, color.a); +} + +float4 RingTech2PS(MeshVS frg) : COLOR +{ + float3 pp = gCameraPos*gRadius[2] - frg.CamW*gDistScale; + float dpp = dot(pp,pp); + float len = sqrt(dpp); + + len = saturate(smoothstep(gTexOff.x, gTexOff.y, len)); + + float4 color = tex2D(RingS, float2(len, 0.5)); + color.a = color.r*0.75; + + float da = dot(normalize(pp), gSun.Dir); + float r = sqrt(dpp*(1.0-da*da)); + + float sh = max(0.05, smoothstep(gRadius[0], gRadius[1], r)); + + if (da<0) sh = 1.0f; + + color.rgb *= sh; + + if ((dot(frg.nrmW, frg.CamW)*dot(frg.nrmW, gSun.Dir))>0) return float4(color.rgb*0.35f, color.a); + return float4(color.rgb, color.a); +} + + +// ============================================================================ +// Base Tile Rendering Technique +// ============================================================================ + +TileMeshVS BaseTileVS(NTVERTEX vrt) +{ + // Null the output + TileMeshVS outVS = (TileMeshVS)0; + + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.nrmW = mul(float4(vrt.nrmL, 0.0f), gW).xyz; + outVS.tex0 = vrt.tex0; + outVS.CamW = -posW; + + // Atmospheric haze ------------------------------------------------------- + + AtmosphericHaze(outVS.atten, outVS.insca, outVS.posH.z, posW); + + float4 diffuse; + float ambi, nigh; + + LegacySunColor(diffuse, ambi, nigh, outVS.nrmW); + + outVS.insca *= (diffuse+ambi); + outVS.insca.a = nigh; + + return outVS; +} + + +float4 BaseTilePS(TileMeshVS frg) : COLOR +{ + // Normalize input + float3 nrmW = normalize(frg.nrmW); + float3 CamW = normalize(frg.CamW); + + float4 cTex = tex2D(ClampS, frg.tex0); + + float3 r = reflect(gSun.Dir, nrmW); + float s = pow(saturate(dot(r, CamW)), 20.0f) * (1.0f-cTex.a); + float d = saturate(dot(-gSun.Dir, nrmW)); + + if (d<=0) s = 0; + + float3 clr = cTex.rgb * saturate(d * gSun.Color + s * gSun.Color + gSun.Ambient); + + if (gNight) clr += tex2D(Tex1S, frg.tex0).rgb; + + return float4(clr.rgb*frg.atten.rgb+frg.insca.rgb, cTex.a); + //return float4(clr.rgb*frg.atten.rgb+frg.insca.rgb, cTex.a*(1-frg.insca.a)); // Make basetiles transparent during night +} + + +// ============================================================================ +// Vessel Axis vector technique +// ============================================================================ + +MeshVS AxisTechVS(MESH_VERTEX vrt) +{ + // Zero output. + MeshVS outVS = (MeshVS)0; + float stretch = vrt.tex0.x * gMix; + float3 posX = vrt.posL + float3(0.0, stretch, 0.0); + float3 posW = mul(float4(posX, 1.0f), gW).xyz; // Apply world transformation matrix + outVS.posH = mul(float4(posW, 1.0f), gVP); + float3 nrmW = mul(float4(vrt.nrmL, 0.0f), gW).xyz; // Apply world transformation matrix + + outVS.nrmW = normalize(nrmW); + outVS.CamW = -posW; + + return outVS; +} + + +float4 AxisTechPS(MeshVS frg) : COLOR +{ + float3 nrmW = normalize(frg.nrmW); + float d = saturate(dot(-gSun.Dir, nrmW)); + float3 clr = gColor.rgb * saturate(max(d,0) + 0.5); + return float4(clr, gColor.a); +} + +technique AxisTech +{ + pass P0 + { + vertexShader = compile vs_3_0 AxisTechVS(); + pixelShader = compile ps_3_0 AxisTechPS(); + + AlphaBlendEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = true; + ZWriteEnable = true; + } +} + + + +// ============================================================================ +// Mesh Shadow Technique +// ============================================================================ + +ShadowTexVS ShadowMeshTechVS(POSTEX vrt) +{ + // Zero output. + ShadowTexVS outVS = (ShadowTexVS)0; + float3 posW = mul(float4(vrt.posL.xyz, 1.0f), gW).xyz; + float alpha = dot(vrt.posL.xyz, gInScatter.xyz) + gInScatter.w; + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.tex0 = float3(vrt.tex0.xy, alpha); + outVS.dstW = outVS.posH.zw; + return outVS; +} + +ShadowTexVS ShadowMeshTechExVS(POSTEX vrt) +{ + // Zero output. + ShadowTexVS outVS = (ShadowTexVS)0; + float alpha = dot(vrt.posL.xyz, gColor.xyz) + gColor.w; + float3 posX = mul(float4(vrt.posL.xyz, 1.0f), gGrpT).xyz; + float3 posW = mul(float4(posX, 1.0f), gW).xyz; + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.tex0 = float3(vrt.tex0.xy, alpha); + outVS.dstW = outVS.posH.zw; + return outVS; +} + +float4 ShadowTechPS(ShadowTexVS frg) : COLOR +{ + if (frg.tex0.b < 0) clip(-1); + if (gOITEnable) { + float4 alpha = tex2D(WrapS, frg.tex0.xy); + if (alpha.a < 0.5f) clip(-1); + } + return float4(0.0f, 0.0f, 0.0f, gMix); +} + + +// ----------------------------------------------------------------------------------- +// Shadow Map rendering with plain geometry (without texture) +// +BShadowVS ShadowMapVS(SHADOW_VERTEX vrt) +{ + // Zero output. + BShadowVS outVS = (BShadowVS)0; + float3 posW = mul(float4(vrt.posL.xyz, 1.0f), gW).xyz; + outVS.posH = mul(float4(posW, 1.0f), gLVP); + outVS.dstW = outVS.posH.zw; + return outVS; +} + +float4 ShadowMapPS(BShadowVS frg) : COLOR +{ + return 1 - (frg.dstW.x / frg.dstW.y); +} + + +// ----------------------------------------------------------------------------------- +// Shadow Map rendering with texture alpha included +// +ShadowTexVS ShadowMapOIT_VS(POSTEX vrt) +{ + // Zero output. + ShadowTexVS outVS = (ShadowTexVS)0; + float3 posW = mul(float4(vrt.posL.xyz, 1.0f), gW).xyz; + outVS.posH = mul(float4(posW, 1.0f), gLVP); + outVS.tex0 = float3(vrt.tex0.xy, 0); + outVS.dstW = outVS.posH.zw; + return outVS; +} + +float4 ShadowMapOIT_PS(ShadowTexVS frg) : COLOR +{ + if (gOITEnable) { + float alpha = tex2D(WrapS, frg.tex0.xy).a; + if (alpha < 0.5f) return 1.0f; + } + return 1 - (frg.dstW.x / frg.dstW.y); +} + +// ----------------------------------------------------------------------------------- + +technique GeometryTech +{ + pass P0 + { + vertexShader = compile vs_3_0 ShadowMapVS(); + pixelShader = compile ps_3_0 ShadowMapPS(); + + AlphaBlendEnable = false; + ZEnable = true; + ZWriteEnable = true; + StencilEnable = false; + } + + pass P1 + { + vertexShader = compile vs_3_0 ShadowMapOIT_VS(); + pixelShader = compile ps_3_0 ShadowMapOIT_PS(); + + AlphaBlendEnable = false; + ZEnable = true; + ZWriteEnable = true; + StencilEnable = false; + } +} + +technique ShadowTech +{ + pass P0 + { + vertexShader = compile vs_3_0 ShadowMeshTechVS(); + pixelShader = compile ps_3_0 ShadowTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + + StencilEnable = true; + StencilRef = 1; + StencilMask = 1; + StencilFunc = NotEqual; + StencilPass = Replace; + } + + pass P1 + { + vertexShader = compile vs_3_0 ShadowMeshTechExVS(); + pixelShader = compile ps_3_0 ShadowTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + + StencilEnable = true; + StencilRef = 1; + StencilMask = 1; + StencilFunc = NotEqual; + StencilPass = Replace; + } +} + + + +// ============================================================================= +// Mesh Bounding Box Technique +// ============================================================================= + +BShadowVS BoundingBoxVS(float3 posL : POSITION0) +{ + // Zero output. + BShadowVS outVS = (BShadowVS)0; + float3 pos; + pos.x = gAttennuate.x * posL.x + gInScatter.x * (1-posL.x); + pos.y = gAttennuate.y * posL.y + gInScatter.y * (1-posL.y); + pos.z = gAttennuate.z * posL.z + gInScatter.z * (1-posL.z); + + float3 posX = mul(float4(pos, 1.0f), gGrpT).xyz; // Apply meshgroup specific transformation + float3 posW = mul(float4(posX, 1.0f), gW).xyz; // Apply world transformation matrix + outVS.posH = mul(float4(posW, 1.0f), gVP); + return outVS; +} + +BShadowVS BoundingSphereVS(float3 posL : POSITION0) +{ + // Zero output. + BShadowVS outVS = (BShadowVS)0; + float3 posW = mul(float4(posL, 1.0f), gW).xyz; // Apply world transformation matrix + outVS.posH = mul(float4(posW, 1.0f), gVP); + return outVS; +} + +float4 BoundingBoxPS(BShadowVS frg) : COLOR +{ + return gColor; +} + +technique TileBoxTech +{ + pass P0 + { + vertexShader = compile vs_3_0 BoundingSphereVS(); + pixelShader = compile ps_3_0 BoundingBoxPS(); + + AlphaBlendEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = true; + ZWriteEnable = true; + } +} + +technique BoundingBoxTech +{ + pass P0 + { + vertexShader = compile vs_3_0 BoundingBoxVS(); + pixelShader = compile ps_3_0 BoundingBoxPS(); + + AlphaBlendEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = true; + ZWriteEnable = true; + } +} + +technique BoundingSphereTech +{ + pass P0 + { + vertexShader = compile vs_3_0 BoundingSphereVS(); + pixelShader = compile ps_3_0 BoundingBoxPS(); + + AlphaBlendEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = true; + ZWriteEnable = true; + } +} + + +technique BaseTileTech +{ + /*pass P0 + { + vertexShader = compile VS_MOD BaseTileNMVS(); + pixelShader = compile PS_MOD BaseTileNMPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + CullMode = CCW; + }*/ + + pass P0 + { + vertexShader = compile vs_3_0 BaseTileVS(); + pixelShader = compile ps_3_0 BaseTilePS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + CullMode = CCW; + } +} + +technique RingTech +{ + pass P0 + { + vertexShader = compile vs_3_0 TinyMeshTechVS(); + pixelShader = compile ps_3_0 RingTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + ZEnable = false; + CullMode = NONE; + } +} + +technique RingTech2 +{ + pass P0 + { + vertexShader = compile vs_3_0 TinyMeshTechVS(); + pixelShader = compile ps_3_0 RingTech2PS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + ZEnable = false; + CullMode = NONE; + } +} + +technique SimplifiedTech +{ + pass P0 + { + vertexShader = compile vs_3_0 TinyMeshTechVS(); + pixelShader = compile ps_3_0 TinyMeshTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + ZEnable = true; + } +} diff --git a/OVP/VulkanClient/shaders/Metalness.fx b/OVP/VulkanClient/shaders/Metalness.fx new file mode 100644 index 000000000..82953c790 --- /dev/null +++ b/OVP/VulkanClient/shaders/Metalness.fx @@ -0,0 +1,408 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + +#define eps 0.001f +//#define _VISCASCADES + +// ============================================================================ +// Vertex shader for physics based rendering +// +PBRData MetalnessVS(MESH_VERTEX vrt) +{ + // Zero output. + PBRData outVS = (PBRData)0; + + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + float3 nrmW = mul(float4(vrt.nrmL, 0.0f), gW).xyz; + +#if SHDMAP > 0 + outVS.shdH = mul(float4(posW, 1.0f), gLVP); +#endif + + outVS.nrmW = nrmW; + outVS.tanW = float4(mul(float4(vrt.tanL, 0.0f), gW).xyz, vrt.tex0.z); + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.camW = -posW; + outVS.tex0 = vrt.tex0.xy; + + return outVS; +} + + +// ============================================================================ +// +float BeckmanNDF(float dHN, float rgh) +{ + float r2 = rgh*rgh; + float dHN2 = dHN*dHN; + float2 w = rcp(float2(3.14f * r2 * dHN2*dHN2, r2*dHN2)); + return w.x * exp((dHN2 - 1.0f) * w.y); +} + +// ============================================================================ +// +float GGX_NDF(float dHN, float rgh) +{ + float r2 = rgh*rgh; + float dHN2 = dHN*dHN; + float d = (r2 * dHN2) + (1.0f - dHN2); + return r2 / (3.14f * d * d); +} + + +// ============================================================================ +// +float SchlickBeckmanGSF(float dLN, float dCN, float rgh) // pre-devided by dLN * dCN +{ + float2 dots = clamp(float2(dLN, dCN), eps, 1.0f); // Avoid div-by-zero + float r2 = rgh * rgh; + float2 e; e.xy = (1.0f - r2); + float2 w = rcp((dots * e) + r2); + return w.x*w.y; +} + + +// ============================================================================ +// +float DiffuseRetroReflectance(float dLN, float dCN, float dLH, float rgh, float mtl) +{ + float2 q = (1.0f-float2(dLN, dCN)); q *= q*q; + float z = 0.5f + 1.6f * dLH*dLH * rgh; + return ((1.0f - q.x) + z*q.x) * ((1.0f - q.y) + z*q.y); +} + + +// ============================================================================ +// +float3 LightFX(float3 c) +{ + float q = cmax(c); + return c * rsqrt(2 + q*q) * 1.8f; +} + +// ============================================================================ +// +float3 LightFXSq(float3 c) +{ + c = sqrt(c); + float q = cmax(c); + return c * rsqrt(2 + q*q) * 1.8f; +} + + +// ============================================================================ +// +void SampleEnvMap(out float3 cE, float dCN, float fRgh, float fMetal, float3 rflW, float3 nrmW) +{ + // Sharpen reflection at low angles + fRgh = saturate(fRgh - 0.1f); + float fLOD = fRgh * lerp(dCN * 2.0f, (0.2f + dCN*0.8f) * 2.5f, fMetal); // Compute LOD level for blur effect + + fLOD *= 5.0f * rsqrt(1.0f + fLOD*fLOD); + + cE = texCUBElod(EnvMapAS, float4(rflW, fLOD)).rgb; +} + + +// ============================================================================ +// +void Transmittance(in out float4 cDiff, float uLN, float uLC, float2 uv, float3 cSun) +{ + float4 cTransm = float4(cDiff.rgb, 1.0f); + float3 cTransl = cDiff.rgb; + + if (gCfg.Transm) { + cTransm = tex2D(TransmS, uv); + cTransm.a *= 1024.0f; + } + + if (gCfg.Transl) cTransl = tex2D(TranslS, uv).rgb; + + float sunLightFromBehind = saturate(-uLN); + float sunSpotFromBehind = saturate(pow(saturate(-uLC), cTransm.a) * 3.0); // "3.0" Causes the transmittance (sun spot) effect to fall off at very shallow angles + + cDiff.rgb += (1.0f - cDiff.rgb) * cTransl.rgb * saturate(cSun * sunLightFromBehind); + cDiff.rgb += cTransm.rgb * (sunSpotFromBehind * cSun); +} + +#include "BakedVC.fx" + +// ============================================================================ +// A Shader for a typical "Metalness" PBR workflow. +// ============================================================================ + +float4 MetalnessPS(float4 sc : VPOS, PBRData frg) : COLOR +{ + float3 nrmT; + float3 nrmW; + float3 cEmis; + float4 cSpecularMap; + float4 cDiff; + float fHeat; + float fSmth, fMetal; + float3 cDiffLocal; + float3 cSpecLocal; + + // ====================================================================== + // Start fetching texture data + // ====================================================================== + + if (gTextured) cDiff = tex2D(WrapS, frg.tex0.xy); + else cDiff = 1; + +#if defined(_VISCASCADES) + cDiff.rgb *= VisualizeCascades(frg.shdH); + return cDiff; +#endif + + if (gOITEnable) if (cDiff.a < 0.5f) clip(-1); + + // Fetch a normal map + // + if (gCfg.Norm) nrmT = tex2D(Nrm0S, frg.tex0.xy).rgb; + + // Fetch Smoothness map (i.e. *_rghn.dds) + // + if (gCfg.Rghn) fSmth = tex2D(RghnS, frg.tex0.xy).g; + else fSmth = 1.0f; + + // Fetch Metalness map + // + if (gCfg.Metl) fMetal = tex2D(MetlS, frg.tex0.xy).g; + else fMetal = gMtrl.metalness; + + // Sample emission map. (Note: Emissive materials and textures need to go different stages, material is added to light) + // + if (gCfg.Emis) cEmis = tex2D(EmisS, frg.tex0.xy).rgb; + else cEmis = 0; + + // Sample specular map // Added + // // Added + if (gCfg.Spec) cSpecularMap = tex2D(SpecS, frg.tex0.xy).rgba; // Added + + // Fetch Heat map + // + if (gCfg.Heat) fHeat = saturate((tex2D(HeatS, frg.tex0.xy).g) - 1.0f + (gMtrl.specialfx.x * gMtrl.specialfx.x * gMtrl.specialfx.x)); + else fHeat = (gMtrl.specialfx.x * gMtrl.specialfx.x * gMtrl.specialfx.x); + + // ---------------------------------------------------------------------- + // Now do other calculations while textures are being fetched + // ---------------------------------------------------------------------- + + float3 camW = normalize(frg.camW); + float3 cSun = gSun.Color * lerp(float3(1.1, 1.1, 0.9), float3(1,1,1), saturate(gRadius[3]*2e-5)); + + + // ====================================================================== + // Construct a proper world space normal + // ====================================================================== + + float3 tanW = frg.tanW.xyz; + float3 bitW = cross(tanW, frg.nrmW) * frg.tanW.w; + + if (gCfg.Norm) { + nrmT.rg = nrmT.rg * 2.0f - 1.0f; + nrmW = frg.nrmW*nrmT.z + tanW*nrmT.x + bitW*nrmT.y; + } + else nrmW = frg.nrmW; + + nrmW = normalize(nrmW); + + + + // ====================================================================== + // Typical compatibility requirements + // ====================================================================== + + cDiff.a = saturate(cDiff.a * gMtrlAlpha); + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); + + + // ====================================================================== + // Some Precomputations + // ====================================================================== + + float3 sunW = -gSun.Dir; + float3 cEnv = 0; + + float3 rflW = reflect(-camW, nrmW); + float3 hlvW = normalize(camW + sunW); + + // Dot Products + float uLN = dot(sunW, nrmW); + float uLC = dot(sunW, camW); + float dLN = saturate(uLN); + float dLH = saturate(dot(sunW, hlvW)); + float dCN = saturate(dot(camW, nrmW)); + float dHN = saturate(dot(hlvW, nrmW)); + + // Apply a proper curve to a texture data, modulate with material value and clamp + fSmth = pow(abs(fSmth), gMtrl.roughness.y) * gMtrl.roughness.x; + + // Apply fresnel and Fresnell cut-off to fSmth + fSmth = fSmth + ((1.0f - fSmth) * pow(abs(1.0f - dCN), 4.0f)) * pow(abs(fSmth), 0.5f); + + float fRgh = saturate(1.0f - fSmth); + float fRgh3 = fRgh*fRgh*fRgh; + + + // ====================================================================== + // Compute Local Light Sources + // ====================================================================== + + LocalLightsEx(cDiffLocal, cSpecLocal, nrmW, -frg.camW, fRgh3, true); + + +#if defined(_ENVMAP) + + if (gEnvMapEnable) { + + // ====================================================================== + // Sample Env Map + SampleEnvMap(cEnv, dCN, fRgh, fMetal, rflW, nrmW); + } + +#if defined(_IRRADIANCE) + // ====================================================================== + // Sample Irradiance Map + float3 cAmbient = Paraboloidal_LVLH(IrradS, nrmW).rgb; + cAmbient *= cAmbient; + + //cAmbient = saturate(cAmbient * (1.0f + 15.0f * gNightTime)); + // Apply base ambient light + cAmbient = max(cAmbient, gSun.Ambient); +#endif +#endif + +#if !defined(_ENVMAP) || !defined(_IRRADIANCE) + // ====================================================================== + // Compute Earth glow + float angl = saturate((-dot(gCameraPos, nrmW) - gProxySize) * gInvProxySize); + float3 cAmbient = gAtmColor.rgb * max(0, angl * gGlowConst) + gSun.Ambient; +#endif + + + cAmbient *= (1.0f - fMetal); // No ambient for metals + + + // ====================================================================== + // Add vessel self-shadows + // ====================================================================== +#if SHDMAP > 0 + if (gCockpit) { + cSun *= smoothstep(0, 0.72, ComputeShadowVC(frg.shdH, dLN, sc)); + } + else { + cSun *= smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); + } +#endif + + + // ====================================================================== + // Main shader core MetalnessPS + // ====================================================================== + float fD = GGX_NDF(dHN, lerp(0.01f, 1.0f, fRgh3)); + float fG = SchlickBeckmanGSF(dLN, dCN, fRgh); + float fR = DiffuseRetroReflectance(dLN, dCN, dLH, fRgh, fMetal); + + float3 cS2 = 0; // Added + float3 cSpec2 = 0; // Added + + // Base material color for reflections. Use cDiff for metals and very rough plastics, white for the rest. + // cDiff for rough plastics is to avoid washed-out(white) look of black and rough parts. + float3 cSpec = lerp(cDiff.rgb, float3(1, 1, 1), (1.0f - fMetal) * (1.0f - fRgh3)); + + // Fresnel power 2.5 for glossy, 5.0 for rough + float fFrs = pow(1.0f - dCN, fRgh*2.5 + 2.5f); + + // Fresnel cut-off below X of fSmth + fFrs *= saturate(0.3f - fRgh*fRgh) * 3.3f; + + // Assume that plastics absorve 50-90% of specular light + float fP = lerp(0.1f + (1.0f - fRgh)*0.4f, 1.0f, fMetal); + + + // ====================================================================== // Start of Added section + // Add multilayer texture effect + // ====================================================================== + if (gCfg.Spec) { + cSpec2 = cSpecularMap.rgb; + cSpecularMap.a = 1.0f - cSpecularMap.a; // use this + // cSpecularMap.a = 0.0f; + float fD2 = GGX_NDF(dHN, lerp(0.01f, 1.0f, cSpecularMap.a)); // makes the sun glint larger at full smoothness so that it doesn't disappear + + // Specular Color + cS2 = (fD2 * cSpec2 * fP) * cSpecularMap.a ; // (4.0f*dLN*dCN) removed to avoid division by zero, compensation in GSF + // cS2 = (fD2 * cSpec2 * fG * fP) * 0.25f; // (4.0f*dLN*dCN) removed to avoid division by zero, compensation in GSF + } // end of Added section + + // Fresnel color shift + float3 cF = cSpec + (1.0f - cSpec) * fFrs; + + // Specular Color + float3 cS = (fD * cF * fG * fP) * 0.25f; // (4.0f*dLN*dCN) removed to avoid division by zero, compensation in GSF + + + // How plastics reflect the environment + float R = 0.1f * fSmth; + float frP = R + (1.0f - R) * fFrs; + + float3 cE = (cEnv * cF * lerp(frP, 1.0f, fMetal)); + + // Attennuate diffuse color for Metals & Fresnel + float fA = (1.0f - fFrs) * (1.0f - fMetal); + + // Add a faint diffuse hue for rough metals. Rough metal doesn't look good if it's totally black + fA += fRgh * fMetal * 0.05f; + + // Light terms + float3 zL = Sq(cSun * fR * dLN) + cDiffLocal + Sq(cAmbient); + + float3 zD = cDiff.rgb * fA * LightFXSq(gMtrl.diffuse.rgb * zL + Sq(gMtrl.emissive.rgb)); + + // Combine specular terms + // float3 zS = cS * (cSun * dLN) + cSpec * LightFX(cSpecLocal) * 0.5f; + float3 zS = cS * (cSun * dLN) + cSpec * LightFX(cSpecLocal) * 0.5f + cS2 * (cSun * dLN) + cSpec2 * LightFX(cSpecLocal) * 0.5f; // Modified + + cDiff.rgb = zD + zS + cE; + + // Override material alpha to make reflections visible + cDiff.a = saturate(cDiff.a + cmax(zS + cE)); + + + // ====================================================================== + // Add texture transmittance + // ====================================================================== + + if (gCfg.Transm || gCfg.Transl) Transmittance(cDiff, uLN, uLC, frg.tex0.xy, cSun); + + + + // Add emission texture to output, modulate with material + cDiff.rgb = max(cDiff.rgb, cEmis * gMtrl.emission2.rgb); + + // Add heat glow + //float3 cHeat = float3(pow(abs(fHeat), 0.5f), pow(abs(fHeat), 1.5f), pow(abs(fHeat), 8.0f)); + float3 cHeat = pow(abs(fHeat), float3(0.5f, 1.5f, 8.0f)); + cDiff.rgb = cDiff.rgb + cHeat; + +#if defined(_DEBUG) + cDiff = cDiff * (1.0f - gColor*0.5f) + gColor; +#endif + +#if defined(_LIGHTGLOW) + cDiff.rgb *= gSun.Transmission; + cDiff.rgb += gSun.Inscatter; + return cDiff; +#else + float3 h2 = cDiff.rgb * cDiff.rgb; + cDiff.rgb *= pow(max(0, 1.0f + h2 * h2), -0.25); + + cDiff.rgb *= gSun.Transmission; + cDiff.rgb += gSun.Inscatter; + + return cDiff; +#endif +} diff --git a/OVP/VulkanClient/shaders/NewMesh.hlsl b/OVP/VulkanClient/shaders/NewMesh.hlsl new file mode 100644 index 000000000..ae9c89de6 --- /dev/null +++ b/OVP/VulkanClient/shaders/NewMesh.hlsl @@ -0,0 +1,172 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2022 Jarmo Nikkanen +// ============================================================== + +#define BOOL bool + +// Constant Buffers -------------------------------------- +// +uniform extern struct VSConst { + float4x4 mVP; // View Projection Matrix + float4x4 mW; // World Matrix +} vs_const; + +uniform extern struct PSConst { + float3 Cam_X; + float3 Cam_Y; + float3 Cam_Z; +} ps_const; + +uniform extern struct PSBools { + BOOL bOIT; // Enable order independent transparency +} ps_bools; + + +sampler2D tDiff; + +// Vertex data input layouts ----------------------------- +// +struct MESH_VERTEX +{ + float3 posL : POSITION0; + float3 nrmL : NORMAL0; + float3 tanL : TANGENT0; + float3 tex0 : TEXCOORD0; // Handiness in .z +}; + +struct POSTEX +{ + float3 posL : POSITION0; + float2 tex0 : TEXCOORD0; +}; + +struct SHADOW_VERTEX +{ + float4 posL : POSITION0; +}; + + + +// Internal data feeds between VS and PS ------------------ +// +struct BShadowVS +{ + float4 posH : POSITION0; + float2 dstW : TEXCOORD0; +}; + +struct ShadowTexVS +{ + float4 posH : POSITION0; + float4 tex0 : TEXCOORD0; // distance in .zw +}; + +struct NormalTexVS +{ + float4 posH : POSITION0; + float3 posW : TEXCOORD0; + float3 nrmW : TEXCOORD1; + float2 tex0 : TEXCOORD2; +}; + +struct PBRData +{ + float4 posH : POSITION0; + float3 camW : TEXCOORD0; + float2 tex0 : TEXCOORD1; + float3 nrmW : TEXCOORD2; + float4 tanW : TEXCOORD3; // Handiness in .w +#if SHDMAP > 0 + float4 shdH : TEXCOORD4; +#endif +}; + +struct BasicData +{ + float4 posH : POSITION0; + float3 camW : TEXCOORD0; + float2 tex0 : TEXCOORD1; + float3 nrmW : TEXCOORD2; +#if SHDMAP > 0 + float4 shdH : TEXCOORD3; +#endif +}; + + + +// ----------------------------------------------------------------------------------- +// Shadow Map rendering with plain geometry (without texture) +// +BShadowVS ShdMapVS(SHADOW_VERTEX vrt) +{ + // Zero output. + BShadowVS outVS = (BShadowVS)0; + float3 posW = mul(float4(vrt.posL.xyz, 1.0f), vs_const.mW).xyz; + outVS.posH = mul(float4(posW, 1.0f), vs_const.mVP); + outVS.dstW = outVS.posH.zw; + return outVS; +} + +float4 ShdMapPS(BShadowVS frg) : COLOR +{ + return 1 - (frg.dstW.x / frg.dstW.y); +} + + + + +// ----------------------------------------------------------------------------------- +// Shadow Map rendering with texture alpha included +// +ShadowTexVS ShdMapOIT_VS(POSTEX vrt) +{ + // Zero output. + ShadowTexVS outVS = (ShadowTexVS)0; + float3 posW = mul(float4(vrt.posL.xyz, 1.0f), vs_const.mW).xyz; + outVS.posH = mul(float4(posW, 1.0f), vs_const.mVP); + outVS.tex0 = float4(vrt.tex0.xy, outVS.posH.zw); + return outVS; +} + +float4 ShdMapOIT_PS(ShadowTexVS frg) : COLOR +{ + if (ps_bools.bOIT) { + float alpha = tex2D(tDiff, frg.tex0.xy).a; + if (alpha < 0.75f) return 1.0f; + } + return 1 - (frg.tex0.z / frg.tex0.w); +} + + + + +// ----------------------------------------------------------------------------------- +// Render Normal and depth buffer +// +NormalTexVS NormalDepth_VS(MESH_VERTEX vrt) +{ + // Zero output. + NormalTexVS outVS = (NormalTexVS)0; + + outVS.posW = mul(float4(vrt.posL.xyz, 1.0f), vs_const.mW).xyz; + outVS.nrmW = mul(float4(vrt.nrmL, 0.0f), vs_const.mW).xyz; + outVS.posH = mul(float4(outVS.posW, 1.0f), vs_const.mVP); + outVS.tex0 = vrt.tex0.xy; + return outVS; +} + +float4 NormalDepth_PS(NormalTexVS frg) : COLOR +{ + if (ps_bools.bOIT) { + if (tex2D(tDiff, frg.tex0.xy).a < 0.75f) clip(-1); + } + //if (dot(frg.nrmW, ps_const.Cam_Z) > 0) clip(-1); + + float D = length(frg.posW); + float x = dot(frg.nrmW, ps_const.Cam_X); + float y = dot(frg.nrmW, ps_const.Cam_Y); + float z = sqrt(saturate(1.0 - (x * x + y * y))); + return float4(x, y, z, D); +} diff --git a/OVP/VulkanClient/shaders/NewPlanet.hlsl b/OVP/VulkanClient/shaders/NewPlanet.hlsl new file mode 100644 index 000000000..5b43b270c --- /dev/null +++ b/OVP/VulkanClient/shaders/NewPlanet.hlsl @@ -0,0 +1,967 @@ +// ============================================================================ +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// licensed under LGPL v2 +// Copyright (C) 2022 Jarmo Nikkanen +// ============================================================================ + +#include "Scatter.hlsl" + +struct _Light +{ + float3 position[4]; /* position in world space */ + float3 direction[4]; /* direction in world space */ + float3 diffuse[4]; /* diffuse color of light */ + float3 attenuation[4]; /* Attenuation */ + float4 param[4]; /* range, falloff, theta, phi */ +}; + +#define Range 0 +#define Falloff 1 +#define Theta 2 +#define Phi 3 + +#define ATMNOISE 0.25 +#define GLARE_SIZE 5 // Larger value -> smaller + +// ---------------------------------------------------------------------------- +// Vertex input layouts from Vertex buffers to vertex shader +// ---------------------------------------------------------------------------- + +struct TILEVERTEX // (VERTEX_2TEX) Vertex declaration used for surface tiles and cloud layer +{ + float3 posL : POSITION0; + float3 normalL : NORMAL0; + float2 tex0 : TEXCOORD0; + float elev : TEXCOORD1; +}; + +// ---------------------------------------------------------------------------- +// Vertex Shader to Pixel Shader datafeeds +// ---------------------------------------------------------------------------- + +struct TileVS +{ + float4 posH : POSITION0; + float2 texUV : TEXCOORD0; // Texture coordinate + float4 camW : TEXCOORD1; // Radius in .w + float3 nrmW : TEXCOORD2; +#if defined(_SHDMAP) + float4 shdH : TEXCOORD3; +#endif +}; + +struct CldVS +{ + float4 posH : POSITION0; + float2 texUV : TEXCOORD0; // Texture coordinate + float3 nrmW : TEXCOORD1; + float3 posW : TEXCOORD2; +}; + +struct HazeVS +{ + float4 posH : POSITION0; + float2 texUV : TEXCOORD0; + float3 posW : TEXCOORD1; + float alpha : COLOR0; +}; + + +// Note: "bool" is 32-bits in a shaders (max count 16) +// +struct FlowControlPS +{ + BOOL bInSpace; // Camera in space (not in atmosphere) + BOOL bBelowClouds; // Camera is below cloud layer + BOOL bOverlay; // Overlay on/off + BOOL bShadows; // Shadow Map on/off + BOOL bLocals; // Local Lights on/off + BOOL bMicroNormals; // Micro texture has normals + BOOL bCloudShd; // Cloud shadow textures valid and enabled + BOOL bMask; // Nightlights/water mask texture is enabled + BOOL bRipples; // Water riples texture is enabled + BOOL bMicroTex; // Micro textures exists and enabled + BOOL bPlanetShadow; // Use spherical approximation for shadow + BOOL bEclipse; // Eclipse is occuring + BOOL bTexture; // Surface texture exists +}; + +struct FlowControlVS +{ + BOOL bInSpace; // Camera in space (not in atmosphere) + BOOL bSpherical; // Ignore elevation, render as sphere + BOOL bElevOvrl; // ElevOverlay on/off +}; + +struct PerObjectParams +{ + float4x4 mWorld; // World Matrix + float4x4 mLVP; // Light-View-Projection + float4 vSHD; // Shadow Map Parameters + float4 vMSc[3]; // Micro Texture offset-scale + float4 vTexOff; // Texture offset-scale + float4 vCloudOff; // Cloud texture offset-scale + float4 vMicroOff; // Micro texture offset-scale + float4 vOverlayOff; // Overlay texture offset-scale + float4 vOverlayCtrl[4]; + float3 vEclipse; // Eclipse caster position (geocentric) + float fEclipse; // Eclipse data addressing scale factor. (to access tExlipse) + float fAlpha; + float fBeta; + float fTgtScale; +}; + +uniform extern PerObjectParams Prm; +uniform extern FlowControlPS Flow; +uniform extern FlowControlVS FlowVS; +uniform extern _Light Lights; // Note: DX9 doesn't tolerate structure arrays outside FX framework +uniform extern bool Spotlight[4]; + + +sampler tDiff; // Diffuse texture +sampler tMask; // Nightlights / Specular mask texture +sampler tCloud; // 1st Cloud shadow texture +sampler tCloud2; // 2nd Cloud shadow texture +sampler tCloudMicro; +sampler tCloudMicroNorm; +sampler tNoise; // +sampler tOcean; // Ocean Normal Map Texture +sampler tMicroA; +sampler tMicroB; +sampler tMicroC; +sampler tGlare; +sampler tShadowMap; +sampler tOverlay; +sampler tMskOverlay; +sampler tElvOverlay; +sampler tEclipse; + + + + +// --------------------------------------------------------------------------------------------------- +// +float SampleShadows(float2 sp, float pd) +{ + if (sp.x < 0 || sp.y < 0) return 0.0f; // If a sample is outside border -> fully lit + if (sp.x > 1 || sp.y > 1) return 0.0f; + + if (pd < 0) pd = 0; + if (pd > 2) pd = 2; + + float2 dx = float2(Prm.vSHD[1], 0) * 1.5f; + float2 dy = float2(0, Prm.vSHD[1]) * 1.5f; + float va = 0; + + sp -= dy; + if ((tex2D(tShadowMap, sp - dx).r) > pd) va++; + if ((tex2D(tShadowMap, sp).r) > pd) va++; + if ((tex2D(tShadowMap, sp + dx).r) > pd) va++; + sp += dy; + if ((tex2D(tShadowMap, sp - dx).r) > pd) va++; + if ((tex2D(tShadowMap, sp).r) > pd) va++; + if ((tex2D(tShadowMap, sp + dx).r) > pd) va++; + sp += dy; + if ((tex2D(tShadowMap, sp - dx).r) > pd) va++; + if ((tex2D(tShadowMap, sp).r) > pd) va++; + if ((tex2D(tShadowMap, sp + dx).r) > pd) va++; + + return va * 0.1111111f; +} + +// ------------------------------------------------------------------------------------------------------------- +// Local light sources +// +void LocalLights( + out float3 diff_out, + in float3 nrmW, + in float3 posW) +{ + diff_out = 0; + + if (!Flow.bLocals) return; + int i; + + // Relative positions + float3 p[4]; + [unroll] for (i = 0; i < 4; i++) p[i] = posW - Lights.position[i]; + + // Square distances + float4 sd; + [unroll] for (i = 0; i < 4; i++) sd[i] = dot(p[i], p[i]); + + // Normalize + sd = rsqrt(sd); + [unroll] for (i = 0; i < 4; i++) p[i] *= sd[i]; + + // Distances + float4 dst = rcp(sd); + + // Attennuation factors + float4 att; + [unroll] for (i = 0; i < 4; i++) att[i] = dot(Lights.attenuation[i].xyz, float3(1.0, dst[i], dst[i] * dst[i])); + + att = rcp(att); + + // Spotlight factors + float4 spt = 1; + + [unroll] for (i = 0; i < 4; i++) { + spt[i] = (dot(p[i], Lights.direction[i]) - Lights.param[i][Phi]) * Lights.param[i][Theta]; + if (!Spotlight[i]) spt[i] = 1.0f; + } + + spt = saturate(spt); + + // Diffuse light factors + float4 dif; + [unroll] for (i = 0; i < 4; i++) dif[i] = dot(-p[i], nrmW); + + dif = saturate(dif); + dif *= (att * spt); + + [unroll] for (i = 0; i < 4; i++) diff_out += Lights.diffuse[i].rgb * dif[i]; +} + + +// Render Eclipse ------------------------------------------------------------ +// +float GetEclipse(float3 vVrt) +{ + if (Flow.bEclipse) + { + float3 b = vVrt - Const.toSun * dot(vVrt, Const.toSun); // Flatten + float x = length(Prm.vEclipse - b) * Prm.fEclipse; + return tex1D(tEclipse, saturate(x)).r; + } + return 1.0; +} + + + +// ============================================================================ +// Render SkyDome and Horizon +// ============================================================================ + +HazeVS HorizonVS(float3 posL : POSITION0) +{ + // Zero output. + HazeVS outVS = (HazeVS)0; + + outVS.texUV = posL.xy*10.0; + + posL.xz *= lerp(Prm.vTexOff[0], Prm.vTexOff[1], posL.y); + posL.y = lerp(Prm.vTexOff[2], Prm.vTexOff[3], posL.y); + + outVS.posW = mul(float4(posL, 1.0f), Prm.mWorld).xyz; + outVS.posH = mul(float4(outVS.posW, 1.0f), Const.mVP); + + return outVS; +} + + +// SkyDome Shader, Renders the sky from with-in atmosphere +// +float4 HorizonPS(HazeVS frg) : COLOR +{ + float fNoise = (tex2Dlod(tNoise, float4(frg.texUV, 0, 0)).r - 0.5f) * 0.03; + + float3 uDir = normalize(frg.posW); + + SkyOut sky = GetSkyColor(uDir); + + float ph = dot(uDir, Const.toSun); + + float2 guv = float2(dot(uDir, Const.ZeroAz), dot(uDir, Const.Up)) * GLARE_SIZE + 0.5f; + float cGlr = tex2D(tGlare, guv).r * saturate(ph) * Const.SunVis; + + float3 color = HDR(sky.ray.rgb * RayPhase(ph) + (sky.mie.rgb + 0.0008f) * MiePhase(ph) * (0.75f + cGlr * Const.cGlare)); + + return float4(color + fNoise, sky.ray.a); +} + + +// Renders the horizon "ring" from space +// +float4 HorizonRingPS(HazeVS frg) : COLOR +{ + float3 uDir = normalize(frg.posW); + float3 uOrt = normalize(uDir - Const.toCam * dot(uDir, Const.toCam)); + float3 vVrt = Const.CamPos + frg.posW; + float d = dot(uDir, frg.posW); + float x = dot(uOrt, Const.SunAz) * 0.5 + 0.5; + float r = length(vVrt); + float q = (r - Const.PlanetRad) / Const.AtmoAlt; + + float2 uv = float2(x, q > 0 ? sqrt(q) : 0); + + float4 cRay = tex2D(tSkyRayColor, uv).rgba; + float3 cMie = tex2D(tSkyMieColor, uv).rgb; + + float ph = dot(uDir, Const.toSun); + + float3 color = HDR(cRay.rgb * RayPhase(ph) + cMie * MiePhase(ph)); + + color *= GetEclipse(vVrt); + + return float4(color, cRay.a); +} + + + +// ============================================================================ +// Planet Surface Renderer +// ============================================================================ + +#define AUX_DIST 0 // Vertex distance +#define AUX_NIGHT 1 // Night lights intensity +#define AUX_SLOPE 2 // Terrain slope factor 0.0=flat, 1.0=sloped +#define AUX_RAYDEPTH 3 // Optical depth of a ray + +TileVS TerrainVS(TILEVERTEX vrt) +{ + // Zero output. + TileVS outVS = (TileVS)0; + float4 vElev = 0; + float3 vNrmW; + + // Apply a world transformation matrix + float3 vPosW = mul(float4(vrt.posL, 1.0f), Prm.mWorld).xyz; + float3 vVrt = Const.CamPos + vPosW; + float3 vPlN = normalize(vVrt); + + if (FlowVS.bElevOvrl) + { + // ---------------------------------------------------------- + // Elevation Overlay + // + float2 vUVOvl = vrt.tex0.xy * Prm.vOverlayOff.zw + Prm.vOverlayOff.xy; + + // Sample Elevation Map + vElev = tex2Dlod(tElvOverlay, float4(vUVOvl, 0, 0)); + + // Construct world space normal + vNrmW = float3(vElev.xy, sqrt(saturate(1.0f - dot(vElev.xy, vElev.xy)))); + vNrmW = mul(float4(vNrmW, 0.0f), Prm.mWorld).xyz; + + // Reconstruct Elevation + vPosW += normalize(Const.CamPos + vPosW) * (vElev.z - vrt.elev) * vElev.w; + } + else { + vNrmW = mul(float4(vrt.normalL, 0.0f), Prm.mWorld).xyz; + } + + // Disrecard elevation and make the surface spherical + if (FlowVS.bSpherical) { + vPosW = (normalize(Const.CamPos + vPosW) * Const.PlanetRad) - Const.CamPos; + vNrmW = vPlN; + } + + outVS.posH = mul(float4(vPosW, 1.0f), Const.mVP); + +#if defined(_SHDMAP) + outVS.shdH = mul(float4(vPosW, 1.0f), Prm.mLVP); +#endif + + outVS.texUV.xy = vrt.tex0.xy; + outVS.camW = float4(-vPosW, dot(vVrt, vPlN)); + outVS.nrmW = vNrmW; + + return outVS; +} + + + +bool InRange(float2 a) +{ + return (a.x > 0.0f && a.x < 1.0f) && (a.y > 0.0f && a.y < 1.0f); +} + +float GGX_NDF(float dHN, float rgh) +{ + float r2 = rgh * rgh; + float dHN2 = dHN * dHN; + float d = (r2 * dHN2) + (1.0f - dHN2); + return r2 / (3.14f * d * d); +} + + +float4 TerrainPS(TileVS frg) : COLOR +{ + + float2 vUVSrf = frg.texUV.xy * Prm.vTexOff.zw + Prm.vTexOff.xy; + float2 vUVWtr = frg.texUV.xy * Prm.vMicroOff.zw + Prm.vMicroOff.xy; + float2 vUVCld = frg.texUV.xy * Prm.vCloudOff.zw + Prm.vCloudOff.xy; + + vUVWtr.x += Const.Time / 180.0f; + + float3 cNrm = float3(0.5, 0.5, 1.0); + float fChA = 0.0f, fChB = 0.0f; + +#if defined(_RIPPLES) + if (Flow.bTexture) cNrm = tex2D(tOcean, vUVWtr).xyz; +#endif + + // Fetch Main Textures + float4 cTex = float4(0.5, 0.5, 0.5, 1.0); + if (Flow.bTexture) cTex = tex2D(tDiff, vUVSrf); + + float4 cMsk = float4(0, 0, 0, 1); + if (Flow.bMask) cMsk = tex2D(tMask, vUVSrf); + +#if defined(_DEVTOOLS) + if (Flow.bOverlay) { + float2 vUVOvl = frg.texUV.xy * Prm.vOverlayOff.zw + Prm.vOverlayOff.xy; + if (InRange(vUVOvl)) { + float4 cOvl = tex2D(tOverlay, vUVOvl); + float4 cWtr = tex2D(tMskOverlay, vUVOvl); + cTex.rgb = lerp(cTex.rgb, cOvl.rgb, cOvl.a * Prm.vOverlayCtrl[0].rgb); + cMsk.rgb = lerp(cMsk.rgb, cWtr.rgb, cOvl.a * Prm.vOverlayCtrl[1].rgb); + cMsk.a = lerp(cMsk.a, cWtr.a, Prm.vOverlayCtrl[1].a); + } + } +#endif + +#if defined(_CLOUDSHD) + if (Flow.bCloudShd) { + fChA = tex2D(tCloud, vUVCld).a; + fChB = tex2D(tCloud2, vUVCld - float2(1, 0)).a; + } +#endif + + float fShadow = 1.0f; + +#if defined(_SHDMAP) + if (Flow.bShadows) { + frg.shdH.xyz /= frg.shdH.w; + frg.shdH.z = 1 - frg.shdH.z; + float2 sp = frg.shdH.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + float pd = frg.shdH.z + 0.05f * Prm.vSHD[3]; + fShadow = 1.0f - SampleShadows(sp, pd); + } +#endif + + float3 cFar, cMed, cLow; + +#if defined(_MICROTEX) + float2 UV = frg.texUV.xy; + // Create normals + if (Flow.bMicroTex) + { + if (Flow.bMicroNormals) { + // Normal in .ag luminance in .b + cFar = tex2D(tMicroC, UV * Prm.vMSc[2].zw + Prm.vMSc[2].xy).agb; // High altitude micro texture C + cMed = tex2D(tMicroB, UV * Prm.vMSc[1].zw + Prm.vMSc[1].xy).agb; // Medimum altitude micro texture B + cLow = tex2D(tMicroA, UV * Prm.vMSc[0].zw + Prm.vMSc[0].xy).agb; // Low altitude micro texture A + } + else { + // Color in .rgb no normals + cFar = tex2D(tMicroC, UV * Prm.vMSc[2].zw + Prm.vMSc[2].xy).rgb; // High altitude micro texture C + cMed = tex2D(tMicroB, UV * Prm.vMSc[1].zw + Prm.vMSc[1].xy).rgb; // Medimum altitude micro texture B + cLow = tex2D(tMicroA, UV * Prm.vMSc[0].zw + Prm.vMSc[0].xy).rgb; // Low altitude micro texture A + } + } +#endif + + float3 cRfl = 0; + float3 nvrW = normalize(frg.nrmW); // Per-pixel surface normal vector + float3 vRay = normalize(frg.camW.xyz); // Unit viewing ray + float3 vVrt = Const.CamPos - frg.camW.xyz; // Geo-centric pixel position + float3 vPlN = normalize(vVrt); // Planet mean normal + float3 hlvW = normalize(vRay + Const.toSun); + float dst = dot(vRay, frg.camW.xyz); // Pixel to camera distance + float rad = frg.camW.w; // Pixel geo-distance + float alt = rad - Const.PlanetRad; // Pixel altitude over mean radius + float fSrf = (1.0 - Const.CamSpace); // Camera colse to surface ? + float fMask = (1.0 - cMsk.a); // Specular Mask + float fSpe = 0; + float fAmpf = 1.0f; + float fDRS = dot(vRay, Const.toSun); + float fDPS = dot(vPlN, Const.toSun); // Mean normal dot sun + + +#if defined(_WATER) +#if defined(_RIPPLES) + + // Compute world space normal for water rendering + // + cNrm.xy = (cNrm.xy - 0.5f) * 2.0f; + cNrm.z *= Const.wNrmStr; + cNrm = normalize(cNrm); + + float3 wnrmW = (Const.vTangent * cNrm.r) + (Const.vBiTangent * cNrm.g) + (vPlN * cNrm.b); + wnrmW = lerp(nvrW, wnrmW, fMask); + float fDWS = dot(wnrmW, Const.toSun); // Water normal dot sun + + // Render with specular ripples and fresnel water ------------------------- + // + float fDCH = saturate(dot(vRay, hlvW)); + float fDCN = saturate(dot(vRay, wnrmW)); + float fDHN = dot(hlvW, wnrmW); + + float3 f = 1.0 - float3(fDCH, fDCN, fDWS); + float3 fFresnel4 = f * f * f; + float3 fF = (0.15f + fFresnel4 * 0.85f) * fMask * Const.wSpec; + + // Compute specular reflection intensity + fSpe = GGX_NDF(fDHN, 0.1f + saturate(fDWS) * 0.1f) * fF.y; + fSpe /= (4.0f * fDCH * max(fDWS, fDCN) + 1e-3); + + // Apply fresnel water only if close enough to a surface + // + if (!Flow.bInSpace) + { + cRfl = GetAmbient(reflect(-vRay, wnrmW)) * fF.y * fSrf; + // Attennuate diffuse texture for fresnel refl. + cTex.rgb *= saturate(1.0f - f.y * fSrf * fMask) * saturate(1.0f - f.z * fSrf * fMask); + } + + cTex.rgb = saturate(cTex.rgb + float3(0, 0.55, 1.0) * Const.wBrightness * fMask); + +#else + // Fallback to simple specular reflection + float fDHN = dot(hlvW, nvrW); + fSpe = pow(saturate(fDHN), 60.0f) * fMask * 5.0f; +#endif +#endif + + float3 nrmW = nvrW; // Micro normal defaults to vertex normal + + // Render with surface microtextures -------------------------------------- + // +#if defined(_MICROTEX) + + if (Flow.bMicroTex) + { + float step1 = smoothstep(15000, 3000, dst); + step1 *= (step1 * step1); + float3 cFnl = max(0, min(2, 1.333f * (cFar + cMed + cLow) - 1)); + + // Create normals + if (Flow.bMicroNormals) + { + cFnl = cFnl.bbb; + +#if defined(_SOFT) + float2 cMix = (cFar.rg + cMed.rg + cLow.rg) * 0.6666f; // SOFT BLEND +#endif +#if defined(_MED) + float2 cMix = (cFar.rg + 0.5f) * (cMed.rg + 0.5f) * (cLow.rg + 0.5f); // MEDIUM BLEND + fAmpf = 2.0f; +#endif +#if defined(_HARD) + float2 cMix = cFar.rg * cMed.rg * cLow.rg * 8.0f; // HARD BLEND + fAmpf = 4.0f; +#endif + + float3 cNrm = float3((cMix - 1.0f) * 2.0f, 0) * step1; + cNrm.z = cos(cNrm.x * cNrm.y * 1.57); + + // Approximate world space normal + nrmW = normalize((Const.vTangent * cNrm.x) + (Const.vBiTangent * cNrm.y) + (nvrW * cNrm.z)); + + // Bend the normal towards sun a bit + nrmW = normalize(nrmW + Const.toSun * 0.06f); + } + + // Apply luminance + cTex.rgb *= lerp(1.0f, cFnl, step1); + } +#endif + + + // Render Eclipse ------------------------------------------------------------ + // + float fECL = GetEclipse(vVrt); + + float3 cDiffLocal = 0; + +#if defined(_LOCALLIGHTS) + LocalLights(cDiffLocal, nrmW, -frg.camW.xyz); +#endif + +#if defined(_NO_ATMOSPHERE) + + float fDNS = saturate(dot(nvrW, Const.toSun)); + float fDCN = saturate(dot(nvrW, Const.toCam)); + float fLvl = 2.0f * fDNS / (fDNS + fDCN + 0.5f); + float fSHD = 1.0f; + + // Shadowing by planet + if (Flow.bPlanetShadow) { + float palt = sqrt(saturate(1.0f - fDPS * fDPS)) * rad - Const.PlanetRad; + fSHD = fDPS > 0 ? 1.0f : ilerp(Const.MinAlt, Const.MaxAlt, palt); + } + + // Amplify light and shadows + fLvl += dot(nvrW - vPlN, Const.toSun) * fLvl * Const.trLS; + + // Add opposition surge + fLvl += pow(saturate(fDRS), 4.0f) * 0.3f * fDNS; + +#if defined(_MICROTEX) + fLvl += dot(nrmW - nvrW, Const.toSun) * ilerp(0.0, 0.03, fLvl) * fAmpf; +#endif + + fLvl *= fSHD; // Apply planet shadow + fLvl *= fECL; // Apply eclipse + + float3 color = cTex.rgb * LightFX(max(fLvl, 0) * fShadow + cDiffLocal); + return float4(pow(saturate(color * Const.TrExpo), Const.TrGamma), 1.0f); // Gamma corrention +#else + + float fShd = 1.0f; + +#if defined(_CLOUDSHD) + // Do we render cloud shadows ? + if (Flow.bCloudShd) { + fShd = (vUVCld.x < 1.0 ? fChA : fChB); + fShd = saturate(1.0 - fShd * Prm.fAlpha); + } +#endif + + float3 cNgt = 0; + float3 cNgt2 = 0; + float fDNS = dot(nvrW, Const.toSun); // Vertex normal dot sun + +#if defined(_NIGHTLIGHTS) + + // Night lights ? + float fNgt = saturate(-fDPS * 4.0f + 0.05f) * Prm.fBeta; // Night lights intensity and 'on' time + + cMsk.b = (cMsk.b > 0.15f ? cMsk.b : 0.0f); // Blue dirt filter + + cNgt = cMsk.rgb * (1 - Const.CamSpace) * fNgt; // Nightlights surface texture illumination term + cNgt2 = cMsk.rgb * Const.CamSpace * 4.0f * fNgt; // Nightlights orbital visibility +#endif + + float fNoise = (tex2Dlod(tNoise, float4(frg.texUV.xy * 4.0f * Prm.fTgtScale, 0, 0)).r - 0.5f) * ATMNOISE; + + // Terrain with gamma correction and attennuation + cTex.rgb = pow(saturate(cTex.rgb), Const.TrGamma) * Const.TrExpo; + + // Evaluate ambient approximation + float4 cAmb = AmbientApprox(vPlN, false); + + LandOut sct = GetLandView(rad, vPlN); + + // Get the color of sunlight and set maximum intensity to 1.0 + float3 cSun = GetSunColor(fDPS, alt); + float3 cSF = cSun * Const.cSun; + float fMx = max(max(cSF.r, cSF.g), cSF.b); + cSF = fMx > 1.0 ? cSF / fMx : cSF; + + float fL = Const.trLS * 0.3f; + float fZ = clamp(dot(nvrW - vPlN, Const.toSun) * Const.trLS, -fL, fL); + float fX = 1.0f - pow(1.0f - saturate(fDPS), 2.0f); + + fZ = fZ > 0 ? fZ * 2.0f : fZ; + +#if defined(_MICROTEX) + float fG = dot(nrmW - nvrW, Const.toSun) * fAmpf; +#else + float fG = 0.0f; +#endif + + // Diffuse "lambertian" shading term + float fD = lerp(fX + (fG + fZ) * fX, fDPS * fDPS, fMask); + + // Water masking + float fM = 0.5f - fMask * 0.25f; + + // Ambient light for terrain + // Color Distance Altitude factor Particle Density + float3 cA = normalize(cAmb.rgb + cSF * 4.0f) * cAmb.a * cAmb.g * fM * exp(-alt * Const.iH.r) * Const.rmI.r * 6e5 * Const.TW_Terrain; + + fShd = saturate(fShd + (1.0f - fX)); + + // Bake light and shadow terms + float3 cL = cSF * fD * fShadow * fShd; + + // Lit the texture with various things + cTex.rgb *= cL * 2.0f + (cA + cDiffLocal + Const.cAmbient * Const.Ambient) * saturate(1.0f + fG + fZ) + cNgt; + + cTex.rgb = max(float3(0, 0, 0), cTex.rgb); + + // Add Reflection + cTex.rgb += cRfl * 0.75f; + + // Add Specular component + cTex.rgb += cSun * fSpe * smoothstep(-0.001f, 0.03f, fDPS); + + // Amplify cloud shadows for orbital views + float fOrbShd = 1.0f - (1.0f - fShd) * Const.CamSpace * 0.5f; + + // Add Haze and night lights + cTex.rgb *= sct.atn.rgb; + cTex.rgb += (sct.ray.rgb * RayPhase(-fDRS) + sct.mie.rgb * MiePhase(-fDRS)) * fOrbShd * (1.0f + fNoise); + + cTex.rgb *= fECL; // Apply eclipse + cTex.rgb += cNgt2; + + return float4(HDR(cTex.rgb), 1.0f); +#endif +} + + + + + + +// ============================================================================ +// Planet Cloud Renderer +// ============================================================================ + +CldVS CloudVS(TILEVERTEX vrt) +{ + // Zero output. + CldVS outVS = (CldVS)0; + + // Apply a world transformation matrix + float3 vPosW = mul(float4(vrt.posL, 1.0f), Prm.mWorld).xyz; + float3 vNrmW = mul(float4(vrt.normalL, 0.0f), Prm.mWorld).xyz; + + outVS.posH = mul(float4(vPosW, 1.0f), Const.mVP); + outVS.nrmW = vNrmW; + outVS.posW = vPosW; + outVS.texUV.xy = vrt.tex0.xy; // Note: vrt.tex0 is un-used (hardcoded in Tile::CreateMesh and varies per tile) + + return outVS; +} + + +// ============================================================================ +// +float4 CloudPS(CldVS frg) : COLOR +{ + float2 vUVTex = frg.texUV.xy; + float4 cTex = tex2D(tDiff, vUVTex); + float3 vRay; + float3 vPxl; + float dRC; + float fNrm = 1.0f; + + if (Flow.bBelowClouds) { + float rRef = Const.PlanetRad + Const.smi * 0.5f; // Reference altitude + float3 vRef = Const.toCam * rRef; + vRay = normalize(Const.toCam * (Const.CamRad - rRef) + frg.posW); // Viewing ray to the pixel + dRC = dot(vRay, Const.toCam); + float fEca2 = 1.0f - dRC * dRC; // Ray horizon angle^2 + float fD = Const.smi * rsqrt(1.0f - Const.ecc * Const.ecc * fEca2); // Distance to ellipse threshold + vPxl = vRef + vRay * fD; // Pretend the pixel being closer and lower + } + else { + vRay = normalize(frg.posW); // Viewing ray to the pixel + dRC = dot(vRay, Const.toCam); + vPxl = Const.CamPos + frg.posW; // Pixel's geocentric location + } + + float3 vPlN = normalize(vPxl); // Mean Normal at pixel's locatin + float3 vVrt = Const.CamPos + frg.posW.xyz; // Geo-centric pixel position + float3 nrm = vPlN; + + float dRS = dot(vRay, Const.toSun); + float dMNus = dot(vPlN, Const.toSun); + float dMN = saturate(dMNus); // Mean normal sun angle + float fPxR = dot(vPxl, vPlN); // Pixel geo distance + float fPxA = fPxR - Const.PlanetRad; // Pixel altitude + + if (!Flow.bBelowClouds) fPxA = Const.CloudAlt; + + + // ----------------------------------------------- + // Cloud layer rendering for Earth + // ----------------------------------------------- + +#if defined(_CLOUDMICRO) + float2 vUVMic = frg.texUV.xy * Prm.vMicroOff.zw + Prm.vMicroOff.xy; + float4 cMic = tex2D(tCloudMicro, vUVMic); +#endif + + +#if defined(_CLOUDNORMALS) +#if defined(_CLOUDMICRO) + + float4 cMicNorm = tex2D(tCloudMicroNorm, vUVMic); // Filename "cloud1_norm.dds" + + // Extract normal from transparency (height) data + // Filter width + float d = 2.0 / 512.0; + + float x1 = tex2D(tDiff, vUVTex + float2(-d, 0)).a; + float x2 = tex2D(tDiff, vUVTex + float2(+d, 0)).a; + nrm.x = (x1 * x1 - x2 * x2); + + float y1 = tex2D(tDiff, vUVTex + float2(0, -d)).a; + float y2 = tex2D(tDiff, vUVTex + float2(0, +d)).a; + nrm.y = (y1 * y1 - y2 * y2); + + // Blend in cloud normals only on moderately thick clouds, allowing the highest cloud tops to be smooth. + nrm.xy = (nrm.xy + saturate((cTex.a * 10.0f) - 3.0f) * saturate(((1.0f - cTex.a) * 10.0f) - 1.0f) * (cMicNorm.rg - 0.5f)); // new + + // Increase normals contrast based on sun-earth angle. + nrm.xyz = nrm.xyz * (1.0f + (0.5f * dMN)); + + nrm.z = sqrt(1.0f - saturate(nrm.x * nrm.x + nrm.y * nrm.y)); + + // Approximate world space normal from local tangent space + nrm = normalize((Const.vTangent * nrm.x) + (Const.vBiTangent * nrm.y) + (vPlN * nrm.z)); + + float dCS = dot(nrm, Const.toSun); // Cloud normal sun angle + + // Brighten the lighting model for clouds, based on sun-earth angle. Twice is better. + // Low sun angles = greater effect. No modulation leads to washed out normals at high sun angles. + dCS = saturate((1.0f - dMN) * (dCS * (1.0f - dCS)) + dCS); + dCS = saturate((1.0f - dMN) * (dCS * (1.0f - dCS)) + dCS); + + // With a high sun angle, don't let the dCS go below 0.2 to avoid unnaturally dark edges. + dCS = lerp(0.2f * dMN, 1.0f, dCS); + + // Effect of normal/sun angle to color + // Add some brightness (borrowing red channel from sunset attenuation) + // Adding it to the sun illumination factor, taking care to keep from saturating + fNrm = dCS +((1.0f - dCS) * 0.2f); +#endif +#endif + +#if defined(_CLOUDMICRO) + float f = cTex.a; + float g = lerp(1.0f, cMic.a, 1.0f - abs(dot(Const.vPolarAxis, vPlN))); + float h = (g + 4.0f) * 0.2f; + cTex.a = saturate(lerp(g, h, f) * f); +#endif + + // Render Eclipse ------------------------------------------------------------ + // + float fECL = GetEclipse(vVrt); + + if (Flow.bBelowClouds) + { + // Get sunlight color + float3 cSun = GetSunColor(dMNus, fPxA); + + // Get ambient information + float4 cMlt = AmbientApprox(vPlN); + + cSun *= saturate(dRS + 1.3f); + float fPh = pow(saturate(1.0f - dRC), 32.0f) * pow(saturate(dRS), 10.0f); // Boost near horizon and close the sun + cSun *= 1.0f + fPh * 8.0f; + + cSun *= Const.cSun * fNrm; + cSun *= Const.Clouds; + cSun += cMlt.rgb * cMlt.a * 0.2f; + + LandOut sct = GetLandView(fPxA + Const.PlanetRad, vPlN); + + cTex.rgb *= cSun; + cTex.rgb *= sct.atn.rgb; + cTex.rgb += sct.ray.rgb * 2.0f; + cTex.rgb *= fECL; + + return float4(HDR(cTex.rgb), saturate(cTex.a)); + } + else { + + // Get sunlight color + float3 cSun = GetSunColor(dMN, fPxA); + + // Get ambient information + float4 cAmb = AmbientApprox(dMNus); + float3 cMSC = Const.RayWave * Const.RayWave * Const.Clouds; // Multiscatter color + + cSun = sqrt(cMSC * cMSC + cSun * cSun * fNrm) * cAmb.a; + + LandOut sct = GetLandView(fPxA + Const.PlanetRad, vPlN); + + cTex.rgb *= cSun; + cTex.rgb *= sct.atn.rgb; + cTex.rgb += sct.ray.rgb; + cTex.rgb *= fECL; + + return float4(sqr(HDR(cTex.rgb * 4.0f)), cTex.a * cAmb.a * cAmb.a); + } +} + + + + + + + + +// ============================================================================ +// Gas Giant Renderer +// ============================================================================ + +TileVS GiantVS(TILEVERTEX vrt) +{ + // Zero output. + TileVS outVS = (TileVS)0; + + // Apply a world transformation matrix + float3 vPosW = mul(float4(vrt.posL, 1.0f), Prm.mWorld).xyz; + float3 vNrmW = mul(float4(vrt.normalL, 0.0f), Prm.mWorld).xyz; + + outVS.posH = mul(float4(vPosW, 1.0f), Const.mVP); + outVS.texUV.xy = vrt.tex0.xy; + outVS.camW = float4(-vPosW, 0); + outVS.nrmW = vNrmW; + + return outVS; +} + +// ============================================================================ +// +float4 GiantPS(TileVS frg) : COLOR +{ + + float2 vUVSrf = frg.texUV.xy * Prm.vTexOff.zw + Prm.vTexOff.xy; + + // Fetch Main Textures + float4 cTex = tex2D(tDiff, vUVSrf); + + float3 nrmW = normalize(frg.nrmW); // Per-pixel surface normal vector + float3 vRay = normalize(frg.camW.xyz); // Unit viewing ray + float3 vVrt = Const.CamPos - frg.camW.xyz; // Geo-centric pixel position + float3 vPlN = normalize(vVrt); // Planet mean normal + float fDPS = dot(vPlN, Const.toSun); + float3 cSun = saturate((fDPS + 0.1) * 5.0); + + + // Render Eclipse ------------------------------------------------------------ + // + cSun *= GetEclipse(vVrt); + + // Terrain with gamma correction and attennuation + cTex.rgb = pow(saturate(cTex.rgb), Const.TrGamma) * Const.TrExpo; + + float3 color = cTex.rgb * LightFX(cSun + float3(0.9, 0.9, 1.0) * Const.Ambient); + + return float4(HDR(color), 1.0f); +} + + +// ============================================================================ +// Gas giant cloud layer renderer +// ============================================================================ + +float4 GiantCloudPS(CldVS frg) : COLOR +{ + float4 cTex = tex2D(tDiff, frg.texUV.xy); + float3 vPlN = normalize(frg.nrmW); + float3 vRay = normalize(frg.posW); + float3 vVrt = Const.CamPos + frg.posW.xyz; // Geo-centric pixel position + float fDPS = dot(vPlN, Const.toSun); // Planet mean normal sun angle + + float3 cSun = saturate((fDPS + 0.1) * 5.0); + + // Render Eclipse ------------------------------------------------------------ + // + float fECL = GetEclipse(vVrt); + + cTex.rgb *= LightFX(cSun + float3(1.0, 1.0, 1.0) * Const.Ambient); + cTex.rgb = pow(saturate(cTex.rgb), Const.TrGamma) * Const.TrExpo; + cTex.rgb *= fECL; + + return float4(HDR(cTex.rgb), saturate(cTex.a)); +} diff --git a/OVP/VulkanClient/shaders/PBR.fx b/OVP/VulkanClient/shaders/PBR.fx new file mode 100644 index 000000000..febfc9f33 --- /dev/null +++ b/OVP/VulkanClient/shaders/PBR.fx @@ -0,0 +1,494 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2014 - 2018 Jarmo Nikkanen +// ============================================================== + + + + +struct PBRData +{ + float4 posH : POSITION0; + float3 camW : TEXCOORD0; + float2 tex0 : TEXCOORD1; + float3 nrmW : TEXCOORD2; + float4 tanW : TEXCOORD3; // Handiness in .w +#if SHDMAP > 0 + float4 shdH : TEXCOORD4; +#endif +}; + + + +// ======================================================================================================================== +// Vertex shader for physics based rendering +// +PBRData PBR_VS(MESH_VERTEX vrt) +{ + // Zero output. + PBRData outVS = (PBRData)0; + + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + float3 nrmW = mul(float4(vrt.nrmL, 0.0f), gW).xyz; + + outVS.nrmW = nrmW; + outVS.tanW = float4(mul(float4(vrt.tanL, 0.0f), gW).xyz, vrt.tex0.z); + outVS.posH = mul(float4(posW, 1.0f), gVP); + +#if SHDMAP > 0 + outVS.shdH = mul(float4(posW, 1.0f), gLVP); +#endif + + outVS.camW = -posW; + outVS.tex0 = vrt.tex0.xy; + + return outVS; +} + + + + + + +// ============================================================================ +// +float4 PBR_PS(float4 sc : VPOS, PBRData frg) : COLOR +{ + float3 nrmT; + float3 nrmW; + float3 cEmis; + float3 cRefl, cRefl2, cRefl3; + float3 cFrsl = 1; + float4 cDiff; + float4 cSpec; + float4 sMask = float4(1.0f, 1.0f, 1.0f, 1024.0f); + float fRghn; + float3 cDiffLocal; + float3 cSpecLocal; + + + // ---------------------------------------------------------------------- + // Start fetching texture data + // ---------------------------------------------------------------------- + + if (gTextured) cDiff = tex2D(WrapS, frg.tex0.xy); + else cDiff = 1; + + if (gOITEnable) if (cDiff.a < 0.5f) clip(-1); + + // Fetch a normal map + // + if (gCfg.Norm) nrmT = tex2D(Nrm0S, frg.tex0.xy).rgb; + + + // Sample specular map + if (gCfg.Spec) cSpec = tex2D(SpecS, frg.tex0.xy).rgba * sMask; + else cSpec = gMtrl.specular.rgba; + + + // Use _refl color for both + if (gCfg.Refl) cRefl = tex2D(ReflS, frg.tex0.xy).rgb; + else cRefl = gMtrl.reflect.rgb; + + + // Roughness map + if (gCfg.Rghn) fRghn = tex2D(RghnS, frg.tex0.xy).g; + else fRghn = gMtrl.roughness.r; + + + // Sample emission map. (Note: Emissive materials and textures need to go different stages, material is added to light) + if (gCfg.Emis) cEmis = tex2D(EmisS, frg.tex0.xy).rgb; + else cEmis = 0; + + + + // ---------------------------------------------------------------------- + // Now do other calculations while textures are being fetched + // ---------------------------------------------------------------------- + + float3 CamD = normalize(frg.camW); + float3 cSun = saturate(gSun.Color); + + + // Use alpha zero to mask off specular reflections + cSpec.rgb *= saturate(cSpec.a); + + // ---------------------------------------------------------------------- + // "Legacy/PBR" switch + // ---------------------------------------------------------------------- + + if (gPBRSw) { + cRefl2 = cRefl*cRefl; + cRefl3 = cRefl2*cRefl; + cSpec.rgb = cRefl2; + cSpec.a = exp2(fRghn * 12.0f); // Compute specular power + } + else { + cRefl3 = cRefl2 = cRefl; + } + + float fRefl = cmax(cRefl3); + + + // ---------------------------------------------------------------------- + // cSpec.pwr to fRghn Converter + // ---------------------------------------------------------------------- + + if (gRghnSw) { + fRghn = log2(cSpec.a+1.0f) * 0.1f; + } + + + + + // ---------------------------------------------------------------------- + // Construct a proper world space normal + // ---------------------------------------------------------------------- + + if (gCfg.Norm) { + float3 bitW = cross(frg.tanW.xyz, frg.nrmW) * frg.tanW.w; + nrmT.rg = nrmT.rg * 2.0f - 1.0f; + nrmW = frg.nrmW*nrmT.z + frg.tanW.xyz*nrmT.x + bitW*nrmT.y; + } + else nrmW = frg.nrmW; + + nrmW = normalize(nrmW); + + + + // ---------------------------------------------------------------------- + // Compute reflection vector and some required dot products + // ---------------------------------------------------------------------- + + float3 RflW = reflect(-CamD, nrmW); // Reflection vector + float dRS = saturate(-dot(RflW, gSun.Dir)); // Reflection/sun angle + float dLN = saturate(-dot(gSun.Dir, nrmW)); // Diffuse lighting term + float dLNx = saturate(dLN * 80.0f); // Specular, Fresnel shadowing term + + + // ---------------------------------------------------------------------- + // Add vessel self-shadows + // ---------------------------------------------------------------------- + +#if SHDMAP > 0 + if (!gCockpit) cSun *= smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); +#endif + + + // ---------------------------------------------------------------------- + // Compute a fresnel terms fFrsl, iFrsl, fFLbe + // ---------------------------------------------------------------------- + + float fFrsl = 0; // Fresnel angle co-efficiency factor + float iFrsl = 0; // Fresnel intensity + float fFLbe = 0; // Fresnel lobe + +#if defined(_GLASS) + + if (gFresnel) { + + float dCN = saturate(dot(CamD, nrmW)); + + // Compute a fresnel term + fFrsl = pow(1.0f - dCN, gMtrl.fresnel.x); + + // Compute a specular lobe for fresnel reflection + fFLbe = pow(dRS, gMtrl.fresnel.z) * dLNx * any(cRefl); + + // Modulate with material + cFrsl *= gMtrl.fresnel.y; + + // Compute intensity term. Fresnel is always on a top of a multi-layer material + // therefore it remains strong and attennuates other properties to maintain energy conservation using (1.0 - iFrsl) + iFrsl = cmax(cFrsl) * fFrsl; + } +#endif + + + + + // ---------------------------------------------------------------------- + // Compute a specular and diffuse lighting + // ---------------------------------------------------------------------- + + // Compute a specular lobe for base material + float fLobe = pow(dRS, cSpec.a) * dLNx; + + + // ---------------------------------------------------------------------- + // Compute Local Light Sources + // ---------------------------------------------------------------------- + + LocalLightsEx(cDiffLocal, cSpecLocal, nrmW, -frg.camW, cSpec.a, false); + + + // ---------------------------------------------------------------------- + // Compute Earth glow + // ---------------------------------------------------------------------- + + float angl = saturate((-dot(gCameraPos, nrmW) - gProxySize) * gInvProxySize); + cDiffLocal += gAtmColor.rgb * max(0, angl*gGlowConst); + + // Bake material props and lights together + float3 diffBaked = Light_fx(gMtrl.diffuse.rgb * (dLN * cSun + cDiffLocal) + gMtrl.emissive.rgb + gMtrl.ambient.rgb*gSun.Ambient); + +#if LMODE > 0 + cSun = Light_fx(cSun + cSpecLocal); // Add local light sources +#endif + + // Special alpha only texture in use, set the .rgb to 1.0f + // Used for panel background lighting in Delta Glider + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); + + // ------------------------------------------------------------------------ + cDiff.rgb *= diffBaked; // Lit the texture + cDiff.a *= gMtrlAlpha; // Modulate material alpha + + + // ------------------------------------------------------------------------ + // Compute total reflected sun light from a material + // + float3 cBase = cSpec.rgb * (1.0f - iFrsl) * fLobe; + +#if defined(_GLASS) + cBase += cFrsl.rgb * fFrsl * fFLbe; +#endif + + cSpec.rgb = cSun * saturate(cBase); + + + + + + + + // ---------------------------------------------------------------------- + // Compute a environment reflections + // ---------------------------------------------------------------------- + + float3 cEnv = 0; + +#if defined(_ENVMAP) + + if (gEnvMapEnable) { + +#if defined(_GLASS) + + if (gFresnel) { + + // Compute LOD level for fresnel reflection + float fLOD = max(0, (10.0f - log2(gMtrl.fresnel.z))); + + // Always mirror clear reflection for low angles + fLOD *= (1.0f - fFrsl); + + // Fresnel based environment reflections + cEnv = (cFrsl * fFrsl) * texCUBElod(EnvMapAS, float4(RflW, fLOD)).rgb; + } +#endif + + // Compute LOD level for blur effect + float fLOD = (1.0f - fRghn) * 8.0f; + + // Add a metallic reflections from a base material + cEnv += cRefl3 * (1.0f-iFrsl) * texCUBElod(EnvMapAS, float4(RflW, fLOD)).rgb; + } + +#endif + + + + + // ---------------------------------------------------------------------- + // Combine all results together + // ---------------------------------------------------------------------- + + // Compute total reflected light + float fTot = cmax(cEnv + cSpec.rgb); + + // Attennuate diffuse surface beneath + cDiff.rgb *= (1.0f - fTot); + +#if defined(_ENVMAP) + // Attennuate diffuse surface beneath + cDiff.rgb *= (1.0f - fRefl); + +#if defined(_GLASS) + // Further attennuate diffuse surface beneath + cDiff.rgb *= (1.0f - iFrsl*iFrsl); // note: (1-iFrsl) goes black too quick +#endif +#endif + + // Re-compute output alpha for alpha blending stage + cDiff.a = saturate(cDiff.a + fTot); + + // Add reflections to output + cDiff.rgb += cEnv; + + // Add specular to output + cDiff.rgb += cSpec.rgb; + + // Add emission texture to output, modulate with material + cDiff.rgb = max(cDiff.rgb, cEmis * gMtrl.emission2.rgb); + +#if defined(_DEBUG) + //if (gDebugHL) cDiff = cDiff*0.5f + gColor; + cDiff = cDiff * (1 - gColor*0.5f) + gColor; +#endif + + cDiff.rgb *= gSun.Transmission; + cDiff.rgb += gSun.Inscatter; + + return cDiff; +} + + + + + + + +// ============================================================================ +// Fast legacy Implementation no additional textures +// ============================================================================ + + +struct FASTData +{ + float4 posH : POSITION0; + float3 camW : TEXCOORD0; + float2 tex0 : TEXCOORD1; + float3 nrmW : TEXCOORD2; +#if SHDMAP > 0 + float4 shdH : TEXCOORD4; +#endif +}; + + +// ============================================================================ +// Vertex shader for physics based rendering +// +FASTData FAST_VS(MESH_VERTEX vrt) +{ + // Zero output. + FASTData outVS = (FASTData)0; + + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + float3 nrmW = mul(float4(vrt.nrmL, 0.0f), gW).xyz; + + outVS.nrmW = nrmW; + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.camW = -posW; + outVS.tex0 = vrt.tex0.xy; + +#if SHDMAP > 0 + outVS.shdH = mul(float4(posW, 1.0f), gLVP); +#endif + + return outVS; +} + + +// ============================================================================ +// +float4 FAST_PS(float4 sc : VPOS, FASTData frg) : COLOR +{ + + float3 cEmis; + float4 cDiff; + float3 cDiffLocal; + float3 cSpecLocal; + + // Start fetching texture data ------------------------------------------- + // + if (gTextured) cDiff = tex2D(WrapS, frg.tex0.xy); + else cDiff = 1; + + if (gOITEnable) if (cDiff.a < 0.5f) clip(-1); + + if (gFullyLit) { + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); + cDiff.rgb *= saturate(gMtrl.diffuse.rgb + gMtrl.emissive.rgb); + } + else { + + // Sample emission map. (Note: Emissive materials and textures need to go different stages, material is added to light) + if (gCfg.Emis) cEmis = tex2D(EmisS, frg.tex0.xy).rgb; + else cEmis = 0; + + float3 nrmW = normalize(frg.nrmW); + float4 cSpec = gMtrl.specular.rgba; + float3 cSun = saturate(gSun.Color); + float dLN = saturate(-dot(gSun.Dir, nrmW)); + + cDiff.rgb = saturate(cDiff.rgb + gNoColor.rgb); + + // ---------------------------------------------------------------------- + // Add vessel self-shadows + // ---------------------------------------------------------------------- + +#if SHDMAP > 0 + float fShadow = 1.0f; + if (!gCockpit) fShadow = smoothstep(0, 0.72, ComputeShadow(frg.shdH, dLN, sc)); + dLN *= fShadow; +#endif + + // ---------------------------------------------------------------------- + // Compute Local Light Sources + // ---------------------------------------------------------------------- + + LocalLightsEx(cDiffLocal, cSpecLocal, nrmW, -frg.camW, cSpec.a, false); + + + // ---------------------------------------------------------------------- + // Compute Earth glow + // ---------------------------------------------------------------------- + + float angl = saturate((-dot(gCameraPos, nrmW) - gProxySize) * gInvProxySize); + cDiffLocal += gAtmColor.rgb * max(0, angl*gGlowConst); + + cDiff.rgb *= saturate( (gMtrl.diffuse.rgb*(dLN * cSun + cDiffLocal)) + (gMtrl.ambient.rgb*gSun.Ambient) + gMtrl.emissive.rgb ); + + float3 CamD = normalize(frg.camW); + float3 HlfW = normalize(CamD - gSun.Dir); + float fSun = pow(saturate(dot(HlfW, nrmW)), gMtrl.specular.a); + +#if SHDMAP > 0 + fSun *= fShadow; +#endif + + + if (dLN == 0) fSun = 0; + +#if LMODE > 0 + float3 specLight = saturate((fSun * cSun) + cSpecLocal); +#else + float3 specLight = (fSun * cSun); +#endif + cDiff.rgb += (cSpec.rgb * specLight); + + cDiff.rgb += cEmis; + } + +#if defined(_DEBUG) + //if (gDebugHL) cDiff = cDiff*0.5f + gColor; + cDiff = cDiff * (1 - gColor*0.5f) + gColor; +#endif + + cDiff.a *= gMtrlAlpha; + + cDiff.rgb *= gSun.Transmission; + cDiff.rgb += gSun.Inscatter; + + return cDiff; +} + + + +// ======================================================================================================================== +// +float4 XRHUD_PS(FASTData frg) : COLOR +{ + return tex2D(WrapS, frg.tex0.xy); +} diff --git a/OVP/VulkanClient/shaders/Particle.fx b/OVP/VulkanClient/shaders/Particle.fx new file mode 100644 index 000000000..fb74b0c6a --- /dev/null +++ b/OVP/VulkanClient/shaders/Particle.fx @@ -0,0 +1,113 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +struct EPVERTEX { + float3 posL : POSITION0; + float2 tex0 : TEXCOORD0; +}; + +struct ParticleVS +{ + float4 posH : POSITION0; + float2 tex0 : TEXCOORD0; + float light : TEXCOORD1; +}; + +ParticleVS ParticleDiffuseVS(NTVERTEX vrt) +{ + ParticleVS outVS = (ParticleVS)0; + outVS.tex0 = vrt.tex0; + outVS.light = 1.0f; // saturate(dot(-gSun.Dir, vrt.nrmL) * 2.0f); + outVS.posH = mul(float4(vrt.posL, 1.0f), gVP); + return outVS; +} + +ParticleVS ParticleEmissiveVS(EPVERTEX vrt) +{ + ParticleVS outVS = (ParticleVS)0; + outVS.tex0 = vrt.tex0; + outVS.posH = mul(float4(vrt.posL, 1.0f), gVP); + return outVS; +} + + + +// ---------------------------------------------------------------------------- +// gMix is the particle opacity computed from time and halflife +// gColor is hardcoded to [1,1,1] in exhaust streams and [1, 0.7, 0.5] in reentry streams +// frg.light is a sun light intensity level illuminating a particles. Light color is [1,1,1] +// ---------------------------------------------------------------------------- + + +float4 ParticleDiffusePS(ParticleVS frg) : COLOR +{ + float4 color = tex2D(WrapS, frg.tex0); + return float4(color.rgb*frg.light, color.a*gMix); +} + +float4 ParticleEmissivePS(ParticleVS frg) : COLOR +{ + float4 color = tex2D(WrapS, frg.tex0); + return float4(color.rgb*gColor.rgb, color.a*gMix); +} + +float4 ParticleShadowPS(ParticleVS frg) : COLOR +{ + float4 color = tex2D(WrapS, frg.tex0); + return float4(0,0,0,color.a*gMix*2.0); +} + + + +technique ParticleDiffuseTech +{ + pass P0 + { + vertexShader = compile vs_3_0 ParticleDiffuseVS(); + pixelShader = compile ps_3_0 ParticleDiffusePS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = false; + } +} + + +technique ParticleEmissiveTech +{ + pass P0 + { + vertexShader = compile vs_3_0 ParticleEmissiveVS(); + pixelShader = compile ps_3_0 ParticleEmissivePS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = false; + } + + // ------------------------------------------------------------------------ + // Ground shadows are rendered only for DIFFUSE particles + // Shadow rendering is defined here because of identical vertex declarations [EPVERTEX] + // ------------------------------------------------------------------------ + pass P1 + { + vertexShader = compile vs_3_0 ParticleEmissiveVS(); + pixelShader = compile ps_3_0 ParticleShadowPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = false; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = false; + } +} \ No newline at end of file diff --git a/OVP/VulkanClient/shaders/Planet.fx b/OVP/VulkanClient/shaders/Planet.fx new file mode 100644 index 000000000..da11087f6 --- /dev/null +++ b/OVP/VulkanClient/shaders/Planet.fx @@ -0,0 +1,188 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +struct TileVS +{ + float4 posH : POSITION0; + float2 tex0 : TEXCOORD0; + float3 normalW : TEXCOORD1; + float3 toCamW : TEXCOORD2; // Vector to the camera + float3 posW : TEXCOORD3; // World space vertex position + float4 aux : TEXCOORD4; // Specular, Diffuse, Twilight, Night Texture Intensity, + float4 diffuse : TEXCOORD5; // Sun light + float4 atten : COLOR0; // Attennuate incoming fragment color + float4 insca : COLOR1; // "Inscatter" Add to incoming fragment color +}; + + + +TileVS PlanetTechVS(TILEVERTEX vrt) +{ + // Zero output. + TileVS outVS = (TileVS)0; + + // Apply a mesh group transformation matrix + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + float3 nrmW = normalize(mul(float4(vrt.normalL, 0.0f), gW).xyz); + + // Convert transformed vertex position into a "screen" space using a combined (World, View and Projection) Matrix + outVS.posH = mul(float4(posW, 1.0f), gVP); + + // A vector from the vertex to the camera + float3 tocam = normalize(-posW); + float3 sundir = gSun.Dir; + + float diff = saturate(dot(-sundir, nrmW)); + float dotr = max(dot(reflect(sundir, nrmW), tocam), 0.0f); + float spec = pow(diff,0.25f) * pow(dotr, gWater.specPower); + float nigh = 0.0f; + float ambi = 0.0f; + + outVS.tex0 = float2(vrt.tex0.x*gTexOff[0] + gTexOff[1], vrt.tex0.y*gTexOff[2] + gTexOff[3]); + outVS.toCamW = tocam; + outVS.normalW = nrmW; + outVS.posW = gCameraPos*gRadius[2] + posW*gDistScale; + + LegacySunColor(outVS.diffuse, ambi, nigh, nrmW); + + outVS.aux = float4(spec, diff, ambi, nigh); + + AtmosphericHaze(outVS.atten, outVS.insca, outVS.posH.z, posW); + + outVS.insca *= (outVS.diffuse+ambi); + + return outVS; +} + + + +float4 PlanetTechPS(TileVS frg) : COLOR +{ + + float4 diff = frg.aux.g*(gMat.diffuse*frg.diffuse) + (gMat.ambient*frg.aux.b); + float4 vSpe = frg.aux.r * (gWater.specular*frg.diffuse); + float4 vEff = tex2D(Planet1S, frg.tex0); + + if (gSpecMode==2) vSpe *= 1.0f - vEff.a; + if (gSpecMode==0) vSpe = 0; + + float3 cTex = tex2D(Planet0S, frg.tex0).rgb; + float3 color = diff.rgb * cTex.rgb + frg.aux.a*vEff.rgb + vSpe.rgb; + + return float4(color*frg.atten.rgb+gColor.rgb+frg.insca.rgb, 1.0f); +} + + + +float4 CloudTechPS(TileVS frg) : COLOR +{ + + float4 data = (gMat.ambient*frg.aux.b); + float4 color = tex2D(Planet0S, frg.tex0); + float alpha = color.a; + + if (dot(frg.normalW, frg.toCamW)<0) { // Render cloud layer from below + float4 diff = (min(1,frg.aux.g*2) * frg.diffuse) * gMat.diffuse + data; + return float4(color.rgb*diff.rgb, alpha); + } + + else { // Render cloud layer from above + float4 diff = (min(1,frg.aux.g*1.5) * frg.diffuse) * gMat.diffuse + data; + return float4(color.rgb*diff.rgb, alpha); + } +} + + + + + + + + + +// ----------------------------------------------------------------------------- +// Cloud Shadow Techs +// ----------------------------------------------------------------------------- + + +struct ShadowVS +{ + float4 posH : POSITION0; + float2 tex0 : TEXCOORD0; + float4 atten : TEXCOORD2; +}; + +ShadowVS CloudShadowTechVS(TILEVERTEX vrt) +{ + // Zero output. + ShadowVS outVS = (ShadowVS)0; + + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.tex0 = float2(vrt.tex0.x*gTexOff[0] + gTexOff[1], vrt.tex0.y*gTexOff[2] + gTexOff[3]); + + float4 none; + + AtmosphericHaze(outVS.atten, none, outVS.posH.z, posW); + + return outVS; +} + +float4 CloudShadowPS(ShadowVS frg) : COLOR +{ + return float4(0,0,0, tex2D(Planet0S, frg.tex0).a * frg.atten.b); +} + + + + + +// This is used for high resolution base tiles --------------------------------- +// +technique PlanetTech +{ + pass P0 + { + vertexShader = compile vs_3_0 PlanetTechVS(); + pixelShader = compile ps_3_0 PlanetTechPS(); + + AlphaBlendEnable = false; + ZEnable = false; + ZWriteEnable = false; + } +} + +technique PlanetCloudTech +{ + pass P0 + { + vertexShader = compile vs_3_0 PlanetTechVS(); + pixelShader = compile ps_3_0 CloudTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + } +} + +technique PlanetCloudShadowTech +{ + pass P0 + { + vertexShader = compile vs_3_0 CloudShadowTechVS(); + pixelShader = compile ps_3_0 CloudShadowPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + } +} \ No newline at end of file diff --git a/OVP/VulkanClient/shaders/PreBakeLights.hlsl b/OVP/VulkanClient/shaders/PreBakeLights.hlsl new file mode 100644 index 000000000..3430c5f95 --- /dev/null +++ b/OVP/VulkanClient/shaders/PreBakeLights.hlsl @@ -0,0 +1,78 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// licensed under MIT +// ============================================================== + +uniform extern float3 vParTexPos[6]; // Pre-computed paraboloidal map coords for prime directions +uniform extern float3 fControl[16]; +uniform extern int iCount; +uniform extern bool bEnabled[6]; +uniform extern float fShine; +uniform extern bool bShine; + +sampler tMap[16] : register(s0); // Lightmaps +sampler tAO[6] : register(s0); // AO Textures for prime directions +sampler tIrrad : register(s6); // Paraboloidal irradiance map + +float cmax(float3 a) +{ + return max(a.x, max(a.y, a.z)); +} + +// ============================================================================ +// +float3 LightFX(float3 c) +{ + float q = cmax(c); + return c * 1.2f * rsqrt(2 + q * q); +} + + +// ============================================================================ +// +float4 ParaboloidalSampler(sampler s, float3 p) +{ + float4 A = tex2D(s, p.xy + float2(0.25f, 0.5f)); + float4 B = tex2D(s, p.xy + float2(0.75f, 0.5f)); + return lerp(A, B, smoothstep(-0.03, 0.03, p.z)); +} + + + +// ============================================================================ +// Combine multiple (regular) baked lightmaps into a single map +// +float4 PSMain(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float3 color = 0; + [unroll] for (int i = 0; i < iCount; i++) color += tex2D(tMap[i], float2(x, y)).rgb * fControl[i]; + return float4(LightFX(color), 1.0f); +} + + + +// ============================================================================ +// Combine multiple baked (ambient) lightmaps into a single map +// Include sun and planet shine coming through windows +// +float4 PSSunAO(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float3 color = 0; + + // For a given pixel in texture (x,y) + [unroll] for (int i = 0; i < 6; i++) { // Browse through direction + if (bEnabled[i]) { + // Get ambient distribution inside VC for a given light direction 'i' + float3 ad = tex2D(tAO[i], float2(x, y)).rgb; + + float3 cShine = 0.0f; + // Get planet shine color for a given direction 'i' + if (bShine) cShine = ParaboloidalSampler(tIrrad, vParTexPos[i]).rgb; + // Compute sunlight and planet shine factors + float3 shineAO = ad * cShine * fShine; + float3 sunAO = ad * fControl[i]; + color += (sunAO + shineAO); + } + } + return float4(LightFX(color), 1.0f); +} diff --git a/OVP/VulkanClient/shaders/Scatter.hlsl b/OVP/VulkanClient/shaders/Scatter.hlsl new file mode 100644 index 000000000..76eca4f0e --- /dev/null +++ b/OVP/VulkanClient/shaders/Scatter.hlsl @@ -0,0 +1,771 @@ + +// ============================================================================ +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// licensed under MIT +// Copyright (C) 2022 Jarmo Nikkanen +// ============================================================================ + +#if defined(_PERFORMANCE) // DO NOT CHANGE THESE, MUST MATCH WITH C++ CODE +#define Nc 6 //Z-dimension count in 3D texture +#define Wc 72 //3D texture size (pixels) +#define Qc 64 //2D texture size (pixels) +#else +#define Nc 8 //Z-dimension count in 3D texture +#define Wc 128 //3D texture size (pixels) +#define Qc 96 //2D texture size (pixels) +#endif + + +#define NSEG 5 +#define iNSEG 1.0f / NSEG +#define MINANGLE -0.33f // Minumum angle +#define ANGRNG (0.25f - MINANGLE) +#define iANGRNG (1.0f / ANGRNG) +#define BOOL bool +#define LastLine (0.999999f - 1.0f / Qc) + + +// Per-Frame Params +// +struct AtmoParams +{ + float4x4 mVP; // View Projection Matrix + float3 CamPos; // Geocentric Camera position + float3 toCam; // Geocentric Camera direction (unit vector) + float3 toSun; // Geocentric Sun direction (unit vector) + float3 SunAz; // Atmo scatter ref.frame (unit vector) (toCam, ZeroAz, SunAz) + float3 ZeroAz; // Atmo scatter ref.frame (unit vector) + float3 Up; // Sun/Shadow Ref Frame (Unit Vector) (Up, toSun, ZeroAz) + float3 vTangent; // Reference frame for normal mapping (Unit Vector) + float3 vBiTangent; // Reference frame for normal mapping (Unit Vector) + float3 vPolarAxis; // North Pole (unit vector) + float3 cSun; // Sun Color and intensity + float3 RayWave; // .rgb Rayleigh Wave lenghts + float3 MieWave; // .rgb Mie Wave lenghts + float4 HG; // Henyey-Greenstein Phase function params + float2 iH; // Inverse scale height for ray(.r) and mie(.g) e.g. exp(-altitude * iH) + float2 rmO; // Ray and Mie out-scatter factors + float2 rmI; // Ray and Mie in-scatter factors + float3 cAmbient; // Ambient light color at sealevel + float3 cGlare; // Sun glare color + float PlanetRad; // Planet Radius + float PlanetRad2; // Planet Radius Squared + float AtmoAlt; // Atmospehere upper altitude limit + float AtmoRad; // Atmospehere outer radius + float AtmoRad2; // Atmospehere outer radius squared + float CloudAlt; // Cloud layer altitude for color and light calculations (not for phisical rendering) + float MinAlt; // Minimum terrain altitude + float MaxAlt; // Maximum terrain altitude + float iAltRng; // 1.0 / (MaxAlt - MinAlt); + float AngMin; + float AngRng; + float iAngRng; + float AngCtr; // Cos of View cone angle from planet center that's visible from camera location + float HrzDst; // Distance to horizon (500 m) minimum if camera below sea level + float CamAlt; // Camera Altitude + float CamElev; // Camera Elevation above surface + float CamRad; // Camera geo-distance + float CamRad2; // Camera geo-distance squared + float Expo; // "HDR" exposure factor (atmosphere only) + float Time; // Simulation time / 180 + float TrGamma; // Terrain "Gamma" correction setting + float TrExpo; // "HDR" exposure factor (terrain only) + float Ambient; // Global ambient light level + float Clouds; // Cloud layer intensity (if below), and Blue light inscatter scale factor (if camera Above clouds) + float TW_Terrain; // Twilight intensity + float TW_Dst; // Twilight distance behind terminator + float CosAlpha; // Cosine of camera horizon angle i.e. PlanetRad/CamRad + float SinAlpha; // Sine of ^^ + float CamSpace; // Camera in space scale factor 0.0 = surf, 1.0 = space + float Cr2; // Camera radius on shadow plane (dot(cp.toCam, cp.Up) * cp.CamRad)^2 + float ShdDst; + float SunVis; + float dCS; + float smi; + float ecc; + float trLS; + float wNrmStr; // Water normal strength + float wSpec; // Water smoothness + float wBrightness; + float wBoost; +}; + +struct sFlow { + BOOL bRay; // True for rayleigh render pass + BOOL bCamLit; // True if camera is lit by sunlight + BOOL bCamInSpace; // True if camera is in space (i.e. not in atmosphere) +}; + +uniform extern AtmoParams Const; +uniform extern sFlow Flo; + +sampler2D tSun; +sampler2D tCam; +sampler2D tLndRay; +sampler2D tLndMie; +sampler2D tLndAtn; +sampler2D tSunGlare; +sampler2D tAmbient; +sampler2D tSkyRayColor; +sampler2D tSkyMieColor; + +#if NSEG == 5 +static const float n[] = { 0.050, 0.25, 0.50, 0.75, 0.950 }; +static const float w[] = { 0.125, 0.25, 0.25, 0.25, 0.125 }; +#endif + +#if NSEG == 7 +static const float n[] = { 0.05, 0.167, 0.333, 0.500, 0.667, 0.833, 0.95 }; +static const float w[] = { 0.08, 0.167, 0.167, 0.167, 0.167, 0.167, 0.08 }; +#endif + +// Gauss7 points and weights +static const float4 n0 = float4(0.0714, 0.21428, 0.35714, 0.5 ); +static const float4 w0 = float4(0.1295, 0.27971, 0.38183, 0.41796); +static const float4 n1 = float4(0.64285, 0.78571, 0.92857, 0 ); +static const float4 w1 = float4(0.38183, 0.27971, 0.1295, 0 ); + +// Gauss4 points and weights +static const float4 n4 = float4( 0.06943, 0.33001, 0.66999, 0.93057 ); +static const float4 w4 = float4( 0.34786, 0.65215, 0.65215, 0.34786 ); + +float ilerp(float a, float b, float x) +{ + return saturate((x - a) / (b - a)); +} + +float3 sqr(float3 x) +{ + return x * x; +} + +float4 expc(float4 x) { return exp(clamp(x, -20, 20)); } +float3 expc(float3 x) { return exp(clamp(x, -20, 20)); } +float2 expc(float2 x) { return exp(clamp(x, -20, 20)); } +float expc(float x) { return exp(clamp(x, -20, 20)); } + + +float2 NrmToUV(float3 vNrm) +{ + float2 uv = float2(dot(Const.ZeroAz, vNrm), dot(Const.SunAz, vNrm)) / Const.AngCtr; + return (uv * 0.5 + 0.5); +} + + +float3 uvToLoc(float2 uv, float r) +{ + uv = (uv * 2.0 - 1.0); + float w = uv.x * uv.x + uv.y * uv.y; + if (w > 1.0f) uv *= rsqrt(w); + uv *= r; + float det = 1.0f - uv.x * uv.x - uv.y * uv.y; + float cos_ab = det > 0.0f ? sqrt(det) : 0.0f; + return float3(uv.xy, cos_ab); +} + + +float3 uvToNrm(float2 uv) +{ + float3 q = uvToLoc(uv, Const.AngCtr); + return normalize(Const.SunAz * q.y + Const.ZeroAz * q.x + Const.toCam * q.z); +} + + +float3 uvToDir(float2 uv) +{ + uv.y = uv.y * rsqrt(0.2f + uv.y * uv.y) * sqrt(1.2f); + + float x = uv.x * 2.0f - 1.0f; + float y = sqrt(1.0f - x * x); + float z = 1.0f - uv.y * Const.AngRng; + float k = sqrt(1.0f - z * z); + + return normalize(Const.SunAz * x * k + Const.ZeroAz * y * k + Const.toCam * z); +} + + +float2 DirToUV(float3 uDir) +{ + float y = dot(uDir, Const.toCam); + float3 uOrt = normalize(uDir - Const.toCam * y); + float x = dot(uOrt, Const.SunAz) * 0.5 + 0.5; + float2 uv = float2(x, 1.0f - (y - Const.AngMin) * Const.iAngRng); + uv.y = sqrt(0.2f) * uv.y * rsqrt(1.2f - uv.y * uv.y); + return uv; +} + + +float3 HDR(float3 x) +{ + return 1.0f - exp(-Const.Expo * x); +} + + +float3 LightFX(float3 x) +{ + //return x * rsqrt(1.0f + x*x) * 1.4f; + return 2.0 * x / (1.0f + x); +} + + +float RayLength(float cos_dir, float r0, float r1) +{ + float y = r0 * cos_dir; + float z2 = r0 * r0 - y * y; + return sqrt(r1 * r1 - z2) + y; +} + + +float RayLength(float cos_dir, float r0) +{ + return RayLength(cos_dir, r0, Const.AtmoRad); +} + + +// Compute UV and blend factor for smaple3D routine +// +float3 TransformUV(float3 uv, const float rc, const float prc) +{ + const float ipix = 1.0f / rc; + + uv = saturate(uv); + uv.z *= (rc - 1); + uv.x *= ipix; + uv.x += floor(uv.z) * ipix; + return uv; +} + + +// Sample a 3D texture composed from an array of 2D textures +// +float4 smaple3D(sampler2D tSamp, float3 uv, const float rc, const float pix) +{ + const float x = 1.0f / rc; + + float4 a = tex2D(tSamp, uv.xy).rgba; + float4 b = tex2D(tSamp, uv.xy + float2(x, 0)).rgba; + return lerp(a, b, frac(uv.z)); +} + + + + + + +// Optical depth integral in atmosphere for a given distance +// +float2 Gauss7(float cos_dir, float r0, float dist, float2 ih0) +{ + int i; + float x = 2.0 * r0 * cos_dir; + float r2 = r0 * r0; + + // Compute altitudes of sample points + float4 d0 = dist * n0; + float4 a0 = sqrt(r2 + d0 * (d0 - x)) - Const.PlanetRad; + float4 d1 = dist * n1; + float4 a1 = sqrt(r2 + d1 * (d1 - x)) - Const.PlanetRad; + + float2 sum = 0.0f; + for (i = 0; i < 4; i++) sum += expc(-a0[i] * ih0) * w0[i]; + for (i = 0; i < 3; i++) sum += expc(-a1[i] * ih0) * w1[i]; + return sum * dist * 0.5f; +} + + +// Optical depth integral in atmosphere for a given distance +// +float2 Gauss4(float cos_dir, float r0, float dist, float2 ih0) +{ + float4 d0 = dist * n4; + float4 a0 = sqrt(r0 * r0 + d0 * d0 - 2.0 * r0 * d0 * cos_dir) - Const.PlanetRad; + float4 ray = expc(-a0 * ih0.x); + float4 mie = expc(-a0 * ih0.y); + return float2(dot(ray, w4), dot(mie, w4)) * dist * 0.5f; +} + + +// Rayleigh phase function +// +float RayPhase(float cw) +{ + return 0.25f * (4.0f + cw * cw); +} + + +// Henyey-Greenstein Phase function +// +/*float MiePhase(float cw) +{ + float cw2 = cw * cw; + return Const.HG.x * (1.0f + cw2) * pow(abs(Const.HG.y - Const.HG.z * cw2*cw), -1.5f) + Const.HG.w; +}*/ + +float MiePhase(float cw) +{ + return 8.0f * Const.HG.x / (1.0f - Const.HG.y * cw) + Const.HG.w; +} + + +// Get a color of sunlight for a given altitude and normal-sun angle +// +float3 GetSunColor(float dir, float alt) +{ + float maxalt = max(Const.MaxAlt, Const.CloudAlt); + alt = ilerp(Const.MinAlt, maxalt, alt); + dir = saturate((dir - MINANGLE) * iANGRNG); + alt = sqrt(alt); + return tex2D(tSun, float2(dir, alt)).rgb; +} + + +float3 ComputeCameraView(float a, float r, float d) +{ + float2 rm = Gauss7(a, r, d, Const.iH) * Const.rmO; + float3 clr = Const.RayWave * rm.r + Const.MieWave * rm.g; + return exp(-clr); +} + + + +// Approximate multi-scatter effect to atmospheric color and light travel behind terminator +// +float4 AmbientApprox(float dNS, uniform const bool bR = true) +{ + float fA = 1.0f - smoothstep(0.0f, Const.TW_Dst, -dNS); + float3 clr = (bR ? Const.RayWave : Const.cAmbient); + return float4(clr, fA); +} + +float4 AmbientApprox(float3 vNrm, uniform const bool bR = true) +{ + float dNS = dot(vNrm, Const.toSun); + return AmbientApprox(dNS, bR); +} + + + +struct RayData { + float se; // Distance to 'Shadow entry' point from a camera + float sx; // Shadow exit + float ae; // Atmosphere entry + float ax; // Atmosphere exit + float hd; // Horizon distance from a camera + float ca; // Closest approach distance +}; + +struct IData { + float s0, s1, e0, e1; +}; + + +// Compute ray passage information +// vRay must point away from the camera +// +RayData ComputeRayStats(in float3 vRay, in uniform const bool bPreProcessData) +{ + RayData dat = (RayData)0; + + static const float invalid = -1e9; + + // Projection of viewing ray on 'shadow' axes + float u = dot(vRay, Const.Up); + float t = dot(vRay, Const.ZeroAz); + float z = dot(vRay, Const.toSun); + + // Shadow Entry and Exit points + // Cosine 'a' + float a = u * rsqrt(u * u + t * t); + + float k2 = Const.Cr2 * a * a; + float h2 = Const.Cr2 - k2; + float w2 = Const.PlanetRad2 - h2; + float2 b = sqrt(float2(w2, k2)); + float k = b.y * sign(a); + float v2 = 0; + float m = Const.CamRad2 - Const.PlanetRad2; + + dat.se = k - b.x; + dat.sx = dat.se + 2.0f * b.x; + + // Project distances back to 3D space + float q = rsqrt(max(2.5e-5, 1.0 - z * z)); + dat.se *= q; + dat.sx *= q; + + // Compute atmosphere entry and exit points + // + a = -dot(Const.toCam, vRay); + k2 = Const.CamRad2 * a * a; + h2 = Const.CamRad2 - k2; + v2 = Const.AtmoRad2 - h2; + float3 n = sqrt(float3(v2, k2, m)); + k = n.y * sign(a); + + dat.hd = m > 0.0 ? n.z : 0.0; + dat.ae = (k - n.x); + dat.ax = dat.ae + 2.0f * n.x; + dat.ca = Const.CamRad * a; + + // If the ray doesn't intersect atmosphere then set both distances to zero + if (v2 < 0) dat.ae = dat.ax = invalid; + + // If the ray doesn't intersect shadow then set both distances to atmo exit + if (w2 < 0) dat.se = dat.sx = invalid; + + if (bPreProcessData) + { + float3 vEn = Const.CamPos + vRay * dat.se; + float3 vEx = Const.CamPos + vRay * dat.sx; + + // If shadow entry/exit point is Lit then set it to atmo exit point + if (dot(vEn, Const.toSun) > 0) dat.se = invalid; + if (dot(vEx, Const.toSun) > 0) dat.sx = invalid; + } + + return dat; +} + + +IData PostProcessData(RayData sp) +{ + IData d; + if (!Flo.bCamLit) { // Camera in Shadow + d.s0 = max(sp.sx, sp.ae); + + float lf = max(0, sp.ca) / max(1.0f, abs(sp.hd)); // Lerp Factor + float mp = lerp((sp.ax + d.s0) * 0.5f, sp.hd, saturate(lf)); + + d.e0 = mp; + d.s1 = max(sp.sx, mp); + d.e1 = sp.ax; + } + else { // Camera is Lit + d.s0 = max(0, sp.ae); + + float lf = max(0, sp.ca) / max(1.0f, abs(sp.hd)); // Lerp Factor + float mp = lerp((sp.ax + d.s0) * 0.5f, sp.hd, saturate(lf)); + + bool bA = (sp.se > sp.ax || sp.se < 0); + + d.e0 = bA ? mp : sp.se; + d.s1 = bA ? mp : max(sp.sx, sp.ae); + d.e1 = sp.ax; + } + return d; +} + +// Compute attennuation from vPos in atmosphere to camera (or atm exit point) +// +float3 ComputeCameraView(float3 vPos, float3 vNrm, float3 vRay, float r) +{ + float d; + float a = dot(vNrm, vRay); + if (Flo.bCamInSpace) d = RayLength(a, r); + else d = dot(vPos - Const.CamPos, vRay); + float2 rm = Gauss7(a, r, d, Const.iH) * Const.rmO; + float3 clr = Const.RayWave * rm.r + Const.MieWave * rm.g; + return exp(-clr); +} + +// Integrate viewing ray for incatter color (.rgb) and optical depth (.a) +// vRay must point from camera to vOrig +// +float4 IntegrateSegmentMP(float3 vOrig, float3 vRay, float len, float iH) +{ + float4 ret = 0; + float3 vR = vRay * len; + for (int i = 0; i < NSEG; i++) + { + float3 pos = vOrig + vR * (iNSEG * (float(i) + 0.5f)); + float3 n = normalize(pos); + float rad = dot(n, pos); + float alt = rad - Const.PlanetRad; + float3 x = GetSunColor(dot(n, Const.toSun), alt); + x *= ComputeCameraView(pos, n, vRay, rad); + float f = exp(-alt * iH) * iNSEG; + ret.rgb += x * f; + ret.a += f; + } + return ret * len; +} + +float4 IntegrateSegmentNS(float3 vOrig, float3 vRay, float len, float iH) +{ + // TODO: Could try to accummulation of CamView seg. by seg. + + float4 ret = 0; + float3 vR = vRay * len; + for (int i = 0; i < NSEG; i++) + { + float3 pos = vOrig + vR * n[i]; + float3 n = normalize(pos); + float rad = dot(n, pos); + float alt = rad - Const.PlanetRad; + float3 x = GetSunColor(dot(n, Const.toSun), alt); + x *= ComputeCameraView(pos, n, vRay, rad); + float f = exp(-alt * iH) * w[i]; + ret.rgb += x * f; + ret.a += f; + } + return ret * len; +} + + + +// 2D lookup-table, for direct sunlight being filtered by atmosphere +// +float4 SunColor(float x : TEXCOORD0, float y : TEXCOORD1) : COLOR +{ + float maxalt = max(Const.MaxAlt, Const.CloudAlt); + float alt = lerp(Const.MinAlt, maxalt, y*y); + float ang = x * ANGRNG + MINANGLE; + float rad = alt + Const.PlanetRad; + float dist = RayLength(-ang, rad); + float2 rm = Gauss7(-ang, rad, dist, Const.iH) * Const.rmO; + float3 clr = Const.RayWave * rm.r + Const.MieWave * rm.g; + + return float4(exp(-clr), 1.0f); +} + + +struct SkyOut +{ + float4 ray; + float4 mie; +}; + + +// Get a precomputed rayleight and mie color values for a given direction from a camera +// +SkyOut GetSkyColor(float3 uDir) +{ + float2 uv = DirToUV(uDir); + + SkyOut o; + o.ray = tex2D(tSkyRayColor, uv).rgba; + o.mie = tex2D(tSkyMieColor, uv).rgba; + return o; +} + + + +float4 SkyView(float u : TEXCOORD0, float v : TEXCOORD1) : COLOR +{ + // Viewing ray + float3 vRay = uvToDir(float2(u,v)); + + float rmO = Flo.bRay ? Const.rmO.r : Const.rmO.g; + float rmI = Flo.bRay ? Const.rmI.r : Const.rmI.g; + float iH = Flo.bRay ? Const.iH.r : Const.iH.g; + + RayData sp = ComputeRayStats(vRay, true); + IData id = PostProcessData(sp); + + // First segment + float4 ret = 0; + if (id.e0 > id.s0) + ret += IntegrateSegmentNS(Const.CamPos + vRay * id.s0, vRay, id.e0 - id.s0, iH); + + // Second segment + if (id.e1 > id.s1) + ret += IntegrateSegmentNS(Const.CamPos + vRay * id.s1, vRay, id.e1 - id.s1, iH); + + + ret.rgb *= rmI; + ret.rgb *= Flo.bRay ? Const.RayWave : Const.MieWave; + ret.rgb *= Const.cSun; + + if (Flo.bRay) + { + float2 vDepth = Gauss7(dot(Const.toCam, -vRay), Const.CamRad, sp.ax, Const.iH); + float2 vOut = vDepth * Const.rmO; + float4 cMlt = AmbientApprox(Const.toCam); + cMlt.rgb *= exp(-Const.CamAlt * Const.iH.r); + cMlt.rgb *= cMlt.a; + cMlt.rgb *= exp(-(Const.RayWave * vOut.r + Const.MieWave * vOut.g)); + float alpha = ilerp(10e3, 150e3, vDepth.r); + return float4(ret.rgb + cMlt.rgb, alpha); + } + + return float4(ret.rgb, 1.0f); +} + + + +// 2D lookup-table for pre-computed sky color. (pre frame), varies with camera pos and sun pos +// +float4 RingView(float u : TEXCOORD0, float v : TEXCOORD1) : COLOR +{ + if (v >= LastLine) return float4(0, 0, 0, 0); + + v *= v; + float rmO = Flo.bRay ? Const.rmO.r : Const.rmO.g; + float rmI = Flo.bRay ? Const.rmI.r : Const.rmI.g; + float iH = Flo.bRay ? Const.iH.r : Const.iH.g; + + float x = -1.0f + u * 2.0f; + float y = sqrt(max(1e-6, 1.0f - x * x)); + float e = v * Const.AtmoAlt; + float re = Const.PlanetRad + e; + float z = re * Const.CosAlpha; + float j = re * Const.SinAlpha; + + // Sample position and viewing ray + float3 vPos = Const.SunAz * x * j + Const.ZeroAz * y * j + Const.toCam * z; + float3 vRay = normalize(vPos - Const.CamPos); + float cpd = abs(dot(vRay, Const.CamPos - vPos)); + + RayData sp = ComputeRayStats(vRay, true); + IData id = PostProcessData(sp); + + // First segment + float4 ret = 0; + if (id.e0 > id.s0) + ret += IntegrateSegmentNS(vPos - vRay * (cpd - id.s0), vRay, id.e0 - id.s0, iH); + + // Second segment + if (id.e1 > id.s1) + ret += IntegrateSegmentNS(vPos - vRay * (cpd - id.s1), vRay, id.e1 - id.s1, iH); + + ret.rgb *= rmI; + ret.rgb *= Flo.bRay ? Const.RayWave : Const.MieWave; + ret.rgb *= Const.cSun; + + if (Flo.bRay) + { + float3 vSrc = vPos - vRay * (cpd - sp.ax); + float3 vNrm = normalize(vSrc); + float2 vDepth = Gauss4(dot(vNrm, vRay), dot(vSrc, vNrm), sp.ax - sp.ae, Const.iH); + float2 vOut = vDepth * Const.rmO; + float4 cMlt = AmbientApprox(Const.toCam); + cMlt.rgb *= exp(-Const.CamAlt * Const.iH.r); + cMlt.rgb *= cMlt.a; + cMlt.rgb *= exp(-(Const.RayWave * vOut.r + Const.MieWave * vOut.g)); + float alpha = ilerp(10e3, 150e3, vDepth.r); + alpha = alpha > 0 ? sqrt(alpha) : 0; + return float4(ret.rgb + cMlt.rgb, alpha); + } + + return float4(ret.rgb, 1.0f); +} + + +// Get a precomputed total (combined) sky color for a given direction from a camera +// +float3 GetAmbient(float3 vRay) +{ + return tex2D(tAmbient, DirToUV(vRay)).rgb; +} + + +// 2D lookup-table for pre-computed total (combined) sky color. (pre frame), varies with sun/cam relation +// +float4 AmbientSky(float u : TEXCOORD0, float v : TEXCOORD1) : COLOR +{ + // Viewing ray + float2 uv = float2(u, v); + float3 uDir = uvToDir(uv); + float ph = dot(uDir, Const.toSun); + + float3 ray = tex2D(tSkyRayColor, uv).rgb; + float3 mie = tex2D(tSkyMieColor, uv).rgb; + float3 color = ray * RayPhase(ph) + mie * MiePhase(ph); + + return float4(color, 1.0f); +} + + + +struct LandOut +{ + float4 ray; + float4 mie; + float4 atn; +}; + + +// Get a precomputed rayleight and mie haze values for a given altitude and normal +// +LandOut GetLandView(float rad, float3 vNrm) +{ + float2 uv = NrmToUV(vNrm); + + float a = rad - Const.PlanetRad; + float z = saturate((a - Const.MinAlt) * Const.iAltRng); // inverse lerp + + float3 uvb = TransformUV(float3(uv, z), Nc, Wc); + + LandOut o; + o.ray = smaple3D(tLndRay, uvb, Nc, Wc); + o.mie = smaple3D(tLndMie, uvb, Nc, Wc); + o.atn = smaple3D(tLndAtn, uvb, Nc, Wc); + return o; +} + + +// Render 3D Lookup texture for land view +// +float4 LandView(float u : TEXCOORD0, float v : TEXCOORD1) : COLOR +{ + u *= Nc; + float a = floor(u) / Nc; + float alt = lerp(Const.MinAlt, Const.MaxAlt, a); + float r = alt + Const.PlanetRad; + + // Geo-centric Vertex location + float3 vNrm = uvToNrm(float2(frac(u), v)); + float3 vVrt = vNrm * r; + + // Viewing ray + float3 vCam = vVrt - Const.CamPos; + float3 vRay = normalize(vCam); // From camera to vertex + float cpd = abs(dot(vRay, vCam)); // Camera pixel distance + + RayData sp = ComputeRayStats(vRay, true); + + float s0 = sp.ae > 0 ? sp.ae : 0; + float e0 = sp.se > 0 ? min(cpd, sp.se) : cpd; + + //if (!Flo.bRay) dist = min(dist, 10e3); + + float rmI = Flo.bRay ? Const.rmI.r : Const.rmI.g; + float iH = Flo.bRay ? Const.iH.r : Const.iH.g; + + float4 ret = IntegrateSegmentNS(vVrt - vRay * (cpd - s0), vRay, e0 - s0, iH); + + ret.rgb *= Const.cSun; + ret.rgb *= rmI; + ret.rgb *= Flo.bRay ? Const.RayWave : Const.MieWave; + + return float4(ret.rgb, 0.0f); +} + + + +// Render 3D Lookup texture for land view attennuation +// +float4 LandViewAtten(float u : TEXCOORD0, float v : TEXCOORD1) : COLOR +{ + u *= Nc; + float a = floor(u) / Nc; + float alt = lerp(Const.MinAlt, Const.MaxAlt, a); + float r = alt + Const.PlanetRad; + + // Geo-centric Vertex location + float3 vNrm = uvToNrm(float2(frac(u), v)); + float3 vVrt = vNrm * r; + + // Viewing ray + float3 vCam = vVrt - Const.CamPos; + float3 vRay = normalize(vCam); // Towards vertex from camera + + float ang = dot(vNrm, vRay); + float len = RayLength(ang, r); + + float dist = min(len, dot(vRay, vCam)); + + float3 ret = ComputeCameraView(ang, r, dist); + + return float4(ret, 1.0); +} diff --git a/OVP/VulkanClient/shaders/SceneTech.fx b/OVP/VulkanClient/shaders/SceneTech.fx new file mode 100644 index 000000000..47a7543c5 --- /dev/null +++ b/OVP/VulkanClient/shaders/SceneTech.fx @@ -0,0 +1,138 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +// ---------------------------------------------------------------------------- +// vkClient generic scene rendering technique +// ---------------------------------------------------------------------------- + +uniform extern float4x4 gWVP; // Combined World, View and Projection matrix +uniform extern float4x4 gVP; // Combined World, View and Projection matrix +uniform extern float4 gColor; // Line Color +uniform extern texture gTex0; // Diffuse texture + +sampler Tex0S : register (s0) = sampler_state +{ + Texture = ; + MinFilter = Anisotropic; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = 8; + AddressU = WRAP; + AddressV = WRAP; +}; + + +struct LineOutputVS +{ + float4 posH : POSITION0; +}; + +// ---------------------------------------------------------------------------- +// Line Tech Vertex/Pixel shader implementation +// ---------------------------------------------------------------------------- + +LineOutputVS LineTechVS(float3 posL : POSITION0) +{ + // Zero output. + LineOutputVS outVS = (LineOutputVS)0; + outVS.posH = mul(float4(posL, 1.0f),gWVP); + return outVS; +} + +float4 LineTechPS() : COLOR +{ + return gColor; +} + +technique LineTech +{ + pass P0 + { + vertexShader = compile vs_2_0 LineTechVS(); + pixelShader = compile ps_2_0 LineTechPS(); + ZEnable = false; + AlphaBlendEnable = false; + } +} + + + +// ---------------------------------------------------------------------------- +// Star rendering technique +// ---------------------------------------------------------------------------- + +struct StarOutputVS +{ + float4 posH : POSITION0; + float4 col : COLOR0; +}; + +StarOutputVS StarTechVS(float3 posL : POSITION0, float4 col : COLOR0) +{ + // Zero output. + StarOutputVS outVS = (StarOutputVS)0; + outVS.posH = mul(float4(posL, 1.0f), gWVP); + outVS.col = col; + return outVS; +} + +float4 StarTechPS(float4 col : COLOR0) : COLOR +{ + return col; +} + +technique StarTech +{ + pass P0 + { + vertexShader = compile vs_2_0 StarTechVS(); + pixelShader = compile ps_2_0 StarTechPS(); + ZEnable = false; + AlphaBlendEnable = false; + ZWriteEnable = false; + } +} + + +struct LabelVS +{ + float4 posH : POSITION0; + float2 tex0 : TEXCOORD0; +}; + +LabelVS LabelTechVS(float3 posL : POSITION0, float2 tex0 : TEXCOORD0) +{ + // Zero output. + LabelVS outVS = (LabelVS)0; + outVS.posH = mul(float4(posL, 1.0f), gWVP); + outVS.tex0 = tex0; + return outVS; +} + +float4 LabelTechPS(LabelVS frg) : COLOR +{ + float4 col; + col = tex2D(Tex0S, frg.tex0); + col.rgb = gColor.rgb; + return col; +} + + +technique LabelTech +{ + pass P0 + { + vertexShader = compile vs_3_0 LabelTechVS(); + pixelShader = compile ps_3_0 LabelTechPS(); + ZEnable = false; + ZWriteEnable = false; + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + } +} + diff --git a/OVP/VulkanClient/shaders/Sketchpad.fx b/OVP/VulkanClient/shaders/Sketchpad.fx new file mode 100644 index 000000000..9cac7c46a --- /dev/null +++ b/OVP/VulkanClient/shaders/Sketchpad.fx @@ -0,0 +1,359 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2013-2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + + + +// ---------------------------------------------------------------------------- +// Sketchpad Implementation +// ---------------------------------------------------------------------------- + +uniform extern float4x4 gColorMatrix; +uniform extern float4x4 gVP; // Projection matrix +uniform extern float4x4 gW; // World matrix +uniform extern float4x4 gWVP; // World View Projection + +// Textures +uniform extern texture gFnt0; +uniform extern texture gTex0; // Diffuse texture +uniform extern texture gNoiseTex; + +// Colors +uniform extern float4 gPen; +uniform extern float4 gKey; +uniform extern float4 gMtrl; +uniform extern float4 gNoiseColor; +uniform extern float4 gGamma; + +uniform extern float3 gPos; // Clipper sphere direction [unit vector] +uniform extern float3 gPos2; // Clipper cone direction [unit vector] +uniform extern float4 gCov; // Clipper sphere coverage parameters +uniform extern float4 gSize; // Inverse Texture size in .xy [pixels] +uniform extern float4 gTarget; // Inverse Screen size in .xy [pixels], Screen Size in .zw [pixels] +uniform extern float3 gWidth; // Pen width in .x, and pattern scale in .y, pixel offset in .z +uniform extern float gFov; // atan( 2 * tan(fov/2) / H ) +uniform extern float gRandom; +uniform extern bool gDashEn; +uniform extern bool gTexEn; +uniform extern bool gFntEn; +uniform extern bool gKeyEn; +uniform extern bool gWide; // Unused +uniform extern bool gShade; +uniform extern bool gClipEn; +uniform extern bool gClearEn; // Unused +uniform extern bool gEffectsEn; + + +// ColorKey tolarance +#define tol 0.01f + +sampler TexS : register(s0) = sampler_state +{ + Texture = ; + MinFilter = LINEAR; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = 8; + AddressU = CLAMP; + AddressV = CLAMP; +}; + +sampler FntS : register(s1) = sampler_state +{ + Texture = ; + MinFilter = LINEAR; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = 8; + AddressU = CLAMP; + AddressV = CLAMP; +}; + +sampler NoiseS : register(s2) = sampler_state +{ + Texture = ; + MinFilter = LINEAR; + MagFilter = LINEAR; + MipFilter = LINEAR; + AddressU = WRAP; + AddressV = WRAP; +}; + + +struct InputVS +{ + float3 pos : POSITION0; // vertex x, y + float4 dir : TEXCOORD0; // Texture coord or inbound direction + float4 clr : COLOR0; // Color + float4 fnc : COLOR1; // Function switch +}; + +#define SSW 3 // Point side switch +#define TSW 2 // Fragment, Pen, Texture switch +#define CSW 1 // ColorKey, Font switch +#define LSW 0 // Length switch + +struct OutputVS +{ + float4 posH : POSITION0; + float4 sw : TEXCOORD0; + float4 tex : TEXCOORD1; + float len : TEXCOORD2; + float4 posW : TEXCOORD3; + float4 color : COLOR0; +}; + +struct SkpMshVS +{ + float4 posH : POSITION0; + float3 nrmW : TEXCOORD0; + float2 tex : TEXCOORD1; +}; + +struct NTVERTEX { // vkClient Mesh vertex layout + float3 posL : POSITION0; + float3 nrmL : NORMAL0; + float2 tex0 : TEXCOORD0; +}; + + +float cmax(float3 v) +{ + return max(v.x, max(v.y, v.z)); +} + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +OutputVS Sketch3DVS(InputVS v) +{ + // Zero output. + OutputVS outVS = (OutputVS)0; + + float3 posW = mul(float4(v.pos.xy, 0.0f, 1.0f), gW).xyz; + float3 prvW = mul(float4(v.dir.zw, 0.0f, 1.0f), gW).xyz; + float3 nxtW = mul(float4(v.dir.xy, 0.0f, 1.0f), gW).xyz; + + outVS.len = v.pos.z; + + float3 posN = normalize(posW); + float3 prvN = normalize(prvW); + float3 nxtN = normalize(nxtW); + float3 nxtS = normalize(cross(nxtN - posN, posN)); + float3 prvS = normalize(cross(posN - prvN, posN)); + float3 latN = normalize(nxtS + prvS) * (0.45*gWidth.x) * rsqrt(max(0.1, 0.5f + dot(nxtS, prvS)*0.5f)); + + if (v.fnc[LSW]>0.5f) outVS.len = min(1, length(posN-prvN)) / gFov; + else outVS.len = v.pos.z; + + float fSide = round(v.fnc[SSW] * 2.0 - 1.0); + float fPosD = dot(posN, posW); + + posW += latN * (fSide * fPosD * gFov); + + outVS.color.rgba = v.clr.bgra; + outVS.posW = float4(posW, fPosD); + outVS.sw = v.fnc; + outVS.posH = mul(float4(posW.xyz, 1.0f), gVP); + outVS.tex = float4(v.dir.xy * gSize.xy, v.dir.xy * gSize.zw); + + return outVS; +} + + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +OutputVS OrthoVS(InputVS v) +{ + // Zero output. + OutputVS outVS = (OutputVS)0; + + float4 posH = mul(float4(v.pos.xy, 0.0f, 1.0f), gWVP); + float4 prvH = mul(float4(v.dir.zw, 0.0f, 1.0f), gWVP); + + if (v.fnc[LSW]>0.5f) outVS.len = length((posH.xy - prvH.xy) * gTarget.zw * 0.5); + else outVS.len = v.pos.z; + + outVS.posW = 0; + + if (gWide) { + + float4 nxtH = mul(float4(v.dir.xy, 0.0f, 1.0f), gWVP); + float fSide = round(v.fnc[SSW] * 2.0 - 1.0); + float2 pixH = gTarget.xy * gWidth.z * abs(fSide); + + nxtH.xy -= pixH; + posH.xy -= pixH; + prvH.xy -= pixH; + + float2 nxtS = normalize(nxtH.xy - posH.xy); + float2 prvS = normalize(posH.xy - prvH.xy); + float2 latW = normalize(nxtS + prvS) * (0.45*gWidth.x) * rsqrt(max(0.1, 0.5f + dot(nxtS, prvS)*0.5f)); + + posH += float4(latW.y, -latW.x, 0, 0) * gTarget * fSide; + } + + outVS.color.rgba = v.clr.bgra; + outVS.sw = v.fnc; + outVS.posH = float4(posH.xyz, 1.0f); + + outVS.tex = float4(v.dir.xy * gSize.xy, v.dir.xy * gSize.zw); + + return outVS; +} + + + + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +float4 SketchpadPS(float4 sc : VPOS, OutputVS frg) : COLOR +{ + float4 t = 1; + float3 u = 1; + + if (gFntEn) u = tex2D(FntS, frg.tex.zw).rgb; + if (gTexEn) t = tex2D(TexS, frg.tex.xy); + + float f = max(u.r*0.7f, u.g); + + // Select Color source + float4 c = frg.color; + if (frg.sw[TSW] > 0.2f) c = gPen; + if (frg.sw[TSW] > 0.8f) c = t; + if (frg.sw[CSW] > 0.8f) c.a *= f; + + // Color keying + if (gTexEn && gKeyEn) { + float4 x = abs(c - gKey); + if ((x.r < tol) && (x.g < tol) && (x.b < tol)) { + if (frg.sw[CSW] > 0.2f && frg.sw[CSW] < 0.8) clip(-1); + } + } + + if (gDashEn) { + float q; + if (modf(frg.len*gWidth.y, q) > 0.5f) clip(-1); + } + + if (gClipEn) { + float3 posN = normalize(frg.posW.xyz); + if ((dot(gPos, posN) > gCov.x) && (frg.posW.w > gCov.y)) clip(-1); + if ((dot(gPos2, posN) > gCov.z) && (frg.posW.w > gCov.w)) clip(-1); + } + + if (gEffectsEn) { + + // Apply color matrix + c = mul(c, gColorMatrix); + + // Apply gamma correction + c.rgb = pow(max(c.rgb, 0), gGamma.rgb); + + // Color overboost correction beyond 0-1 range + c.rgb += saturate(cmax(c.rgb) - 1.0f); + + // Apply noise + float noise = (tex2D(NoiseS, sc.xy*(1.0f / 128.0f) + float2(gRandom, gRandom*7.0)).r * 2.0f) - 1.0f; + c.rgb += lerp(float3(1, 1, 1), c.rgb, gNoiseColor.a) * gNoiseColor.rgb * noise; + } + + return saturate(c); +} + + +technique SketchTech +{ + pass P0 + { + vertexShader = compile vs_3_0 OrthoVS(); + pixelShader = compile ps_3_0 SketchpadPS(); + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + CullMode = None; + } + + pass P1 + { + vertexShader = compile vs_3_0 Sketch3DVS(); + pixelShader = compile ps_3_0 SketchpadPS(); + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + CullMode = None; + } +} + + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +SkpMshVS SketchMeshVS(NTVERTEX v) +{ + // Zero output. + SkpMshVS outVS = (SkpMshVS)0; + float3 posW = mul(float4(v.posL, 1.0f), gW).xyz; + float3 nrmW = mul(float4(v.nrmL, 0.0f), gW).xyz; + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.tex = v.tex0; + outVS.nrmW = nrmW; + return outVS; + +} + + +float4 SketchMeshPS(SkpMshVS frg) : COLOR +{ + float4 cTex = 1; + float fS = 1; + if (gTexEn) cTex = tex2D(TexS, frg.tex); + if (gShade) fS = dot(normalize(frg.nrmW), float3(0, 0, -1)); + + cTex.rgba *= gPen.bgra; + cTex.rgba *= gMtrl.rgba; + cTex.rgb *= saturate(fS); + + return cTex; +} + + +technique SketchMesh +{ + pass P0 + { + vertexShader = compile vs_3_0 SketchMeshVS(); + pixelShader = compile ps_3_0 SketchMeshPS(); + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + } +} diff --git a/OVP/VulkanClient/shaders/Vessel.fx b/OVP/VulkanClient/shaders/Vessel.fx new file mode 100644 index 000000000..b23e7b03b --- /dev/null +++ b/OVP/VulkanClient/shaders/Vessel.fx @@ -0,0 +1,338 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// ============================================================== + + + +float3 cLuminosity = { 0.4, 0.7, 0.3 }; + + +inline float cmax(float3 color) +{ + return max(max(color.r, color.g), color.b); +} + +// Sun light brightness for diffuse and specular lighting +#include "LightBlur.hlsl" + +// Incluse Light and Shadow +#include "Common.hlsl" + +// Must be included here +#include "PBR.fx" + +// Must be included here +#include "Metalness.fx" + + +// ============================================================================ +// Vertex shader for physics based rendering +// +PBRData AdvancedVS(MESH_VERTEX vrt) +{ + // Zero output. + PBRData outVS = (PBRData)0; + + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + float3 nrmW = mul(float4(vrt.nrmL, 0.0f), gW).xyz; + +#if SHDMAP > 0 + outVS.shdH = mul(float4(posW, 1.0f), gLVP); +#endif + + outVS.nrmW = nrmW; + outVS.tanW = float4(mul(float4(vrt.tanL, 0.0f), gW).xyz, vrt.tex0.z); + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.camW = -posW; + outVS.tex0 = vrt.tex0.xy; + + return outVS; +} + + + +// ============================================================================ +// +float4 AdvancedPS(float4 sc : VPOS, PBRData frg) : COLOR +{ + float3 bitW; + float3 nrmT; + float3 cRefl; + float3 cEmis; + float4 cSpec; + float4 cTex; + + float3 cDiffLocal; + float3 cSpecLocal; + + if (gTextured) cTex = tex2D(WrapS, frg.tex0.xy); + else cTex = 1; + + if (gOITEnable) if (cTex.a < 0.5f) clip(-1); + + if (gCfg.Norm) nrmT = tex2D(Nrm0S, frg.tex0.xy).rgb; + + if (gCfg.Spec) cSpec = tex2D(SpecS, frg.tex0.xy); + else cSpec = gMtrl.specular; + + if (gCfg.Refl) cRefl = tex2D(ReflS, frg.tex0.xy).rgb; + else cRefl = gMtrl.reflect.rgb; + + // Sample emission map. (Note: Emissive materials and textures need to go different stages, material is added to light) + if (gCfg.Emis) cEmis = tex2D(EmisS, frg.tex0.xy).rgb; + else cEmis = 0; + + + float3 nrmW = frg.nrmW; + float3 tanW = frg.tanW.xyz; + float3 cSun = saturate(gSun.Color); + float3 CamD = normalize(frg.camW); + float3 Base = (gMtrl.ambient.rgb*gSun.Ambient) + (gMtrl.emissive.rgb); + + + // Compute World space normal ------------------------------------------- + // + if (gCfg.Norm) { + nrmT = nrmT * 2.0 - 1.0; + bitW = cross(tanW, nrmW) * frg.tanW.w; + nrmW = nrmW*nrmT.z + tanW*nrmT.x + bitW*nrmT.y; + } + + nrmW = normalize(nrmW); + + float3 TnrmW = -nrmW; + float3 RflW = reflect(-CamD, nrmW); + float dLN = saturate(-dot(gSun.Dir, nrmW)); + + if (gCfg.Spec) cSpec.a *= 255.0f; + + // Approximate roughness + float fRghn = log2(cSpec.a) * 0.1f; + + // Sunlight calculation + float fSun = pow(saturate(-dot(RflW, gSun.Dir)), cSpec.a) * saturate(cSpec.a); + + if (dLN == 0) fSun = 0; + + // Special alpha only texture in use + cTex.rgb = saturate(cTex.rgb + gNoColor.rgb); + + + // ---------------------------------------------------------------------- + // Add vessel self-shadows + // ---------------------------------------------------------------------- + +#if SHDMAP > 0 + if (!gCockpit) cSun.rgb *= ComputeShadow(frg.shdH, dLN, sc); +#endif + + + + // ---------------------------------------------------------------------- + // Compute Local Light Sources + // ---------------------------------------------------------------------- + + LocalLightsEx(cDiffLocal, cSpecLocal, nrmW, -frg.camW, cSpec.a, false); + + + // Lit the diffuse texture + cTex.rgb *= saturate(Base + gMtrl.diffuse.rgb * Light_fx(cDiffLocal + cSun * dLN)); + + // Lit the specular surface + cSpec.rgb *= saturate(cSpecLocal + fSun * cSun); + + + // Compute Transluciency effect -------------------------------------------------------------- + // + + if (gCfg.Transm || gCfg.Transl) { + + float4 cTransm = float4(cTex.rgb, 1.0f); + + if (gCfg.Transm) { + cTransm = tex2D(TransmS, frg.tex0.xy); + cTransm.a *= 1024.0f; + } + + float3 cTransl = cTex.rgb; + + if (gCfg.Transl) { + cTransl = tex2D(TranslS, frg.tex0.xy).rgb; + } + + float sunLightFromBehind = saturate(dot(gSun.Dir, nrmW)); + float sunSpotFromBehind = pow(saturate(dot(gSun.Dir, CamD)), cTransm.a); + sunSpotFromBehind *= saturate(sunLightFromBehind * 3.0f);// Causes the transmittance (sun spot) effect to fall off at very shallow angles + + cTransl.rgb *= saturate(cSun * sunLightFromBehind); + + cTex.rgb += (1 - cTex.rgb) * cTransl.rgb; + cTex.rgb += cTransm.rgb * (sunSpotFromBehind * cSun); + } + + + float fFrsl = 1.0f; + float fInt = 0.0f; + + // Compute reflectivity + float fRefl = cmax(cRefl); + + +#if defined(_ENVMAP) + + + // Compute environment map/fresnel effects -------------------------------- + // + if (gEnvMapEnable) { + + // Do we need fresnel code for this render pass ? + + if (gFresnel) { + + fFrsl = gMtrl.fresnel.y; + + // Get mirror reflection for fresnel + float3 cEnvFres = texCUBElod(EnvMapAS, float4(RflW, 0)).rgb; + + float dCN = saturate(dot(CamD, nrmW)); + + // Compute a fresnel term with compensations included + fFrsl *= pow(1.0f - dCN, gMtrl.fresnel.x) * (1.0 - fRefl) * any(cRefl); + + // Sunlight reflection for fresnel material + cSpec.rgb = saturate(cSpec.rgb + fSun * fFrsl * cSun); + + // Compute total reflected light with fresnel reflection + // and accummulate in cSpec + cSpec.rgb = saturate(cSpec.rgb + fFrsl * cEnvFres); + + // Compute intensity + fInt = saturate(dot(cSpec.rgb, cLuminosity)); + + // Attennuate diffuse surface + cTex.rgb *= (1.0f - fInt); + } + + // Compute LOD level for blur effect + float fLOD = (1.0f - fRghn) * 10.0f; + + float3 cEnv = texCUBElod(EnvMapAS, float4(RflW, fLOD)).rgb; + + // Compute total reflected light, accummulate in cSpec + cSpec.rgb += cRefl.rgb * cEnv; + } + +#endif + + // Attennuate diffuse surface + cTex.rgb *= (1.0f - fRefl); + + // Re-compute output alpha for alpha blending stage + // NOTE: Without fresnel fInt remains zero + cTex.a = saturate(cTex.a + fInt); + + // Add reflections to output + cTex.rgb += cSpec.rgb; + + // Add emissive textures to output + cTex.rgb += cEmis; + +#if defined(_DEBUG) + //if (gDebugHL) cTex = cTex*0.5f + gColor; + cTex = cTex * (1 - gColor*0.5f) + gColor; +#endif + + cTex.rgb *= gSun.Transmission; + cTex.rgb += gSun.Inscatter; + + return cTex; +} + + + + + +// ============================================================================ +// This is the default mesh rendering technique +// +technique VesselTech +{ + pass P0 + { + vertexShader = compile vs_3_0 PBR_VS(); + pixelShader = compile ps_3_0 PBR_PS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + } + + pass P1 + { + vertexShader = compile vs_3_0 AdvancedVS(); + pixelShader = compile ps_3_0 AdvancedPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + } + + pass P2 + { + vertexShader = compile vs_3_0 FAST_VS(); + pixelShader = compile ps_3_0 FAST_PS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + } + + pass P3 // XR2 HUD PASS + { + vertexShader = compile vs_3_0 FAST_VS(); + pixelShader = compile ps_3_0 XRHUD_PS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + } + + pass P4 + { + vertexShader = compile vs_3_0 MetalnessVS(); + pixelShader = compile ps_3_0 MetalnessPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + } + + pass P5 + { + vertexShader = compile vs_3_0 MetalnessVS(); + pixelShader = compile ps_3_0 BakedVC_PS(); + + AlphaBlendEnable = true; + BlendOp = Add; + ZEnable = true; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = true; + } +} diff --git a/OVP/VulkanClient/shaders/vkClient.fx b/OVP/VulkanClient/shaders/vkClient.fx new file mode 100644 index 000000000..fb68745b0 --- /dev/null +++ b/OVP/VulkanClient/shaders/vkClient.fx @@ -0,0 +1,789 @@ +// ============================================================== +// Part of the ORBITER VISUALISATION PROJECT (OVP) +// Dual licensed under GPL v3 and LGPL v3 +// Copyright (C) 2012 - 2016 Jarmo Nikkanen +// ============================================================== + +// ---------------------------------------------------------------------------- +// vkClient rendering techniques for Orbiter Spaceflight simulator +// ---------------------------------------------------------------------------- + + +#define NIGHT_CLOUDS 0.05f // range(0.0f-0.1f) Cloud ambient level at night +#define CLOUD_INTENSITY 1.8f // range(0.5f-2.0f) +#define NIGHT_LIGHTS 0.7f // range(0.2f-1.0f) + +struct Mat +{ + float4 diffuse; + float4 ambient; + float4 specular; + float4 emissive; + float specPower; +}; + +struct Mtrl +{ + float4 diffuse; + float4 specular; + float3 ambient; + float3 emissive; + float3 reflect; + float3 emission2; + float3 fresnel; + float2 roughness; + float metalness; + float4 specialfx; // x = Heat +}; + +struct Sun +{ + float3 Dir; + float3 Color; // Color and Intensity of received sunlight + float3 Ambient; // Ambient light level (Base Objects Only, Vessels are using dynamic methods) + float3 Transmission; // Visibility through atmosphere (1.0 = fully visible, 0.0 = obscured) + float3 Inscatter; // Amount of incattered light from haze +}; + +struct Light +{ + int type; /* Is is spotlight */ + float dst2; /* Camera-Light Emitter distance squared */ + float4 diffuse; /* diffuse color of light */ + float3 position; /* position in world space */ + float3 direction; /* direction in world space */ + float3 attenuation; /* Attenuation */ + float4 param; /* range, falloff, theta, phi */ +}; + +// Must match with counterpart in Effect.h + +struct Flow +{ + bool Emis; // Enable Emission Maps + bool Spec; // Enable Specular Maps + bool Refl; // Enable Reflection Maps + bool Transl; // Enable translucent effect + bool Transm; // Enable transmissive effect + bool Rghn; // Enable roughness map + bool Norm; // Enable normal map + bool Metl; // Enable metalness map + bool Heat; // Enable heat map + bool Baked; // Enable pre-baked local light map + bool BakedAO; // Enable pre-baked AO map + bool BakedAmb; // Enable pre-baked Ambient light map +}; + + +#define Range 0 +#define Falloff 1 +#define Theta 2 +#define Phi 3 + + +#define SH_SIZE 0 +#define SH_INVSIZE 1 + +uniform extern float3 kernel[KERNEL_SIZE]; + +// ------------------------------------------------------------------------- +uniform extern float4x4 gW; // World matrix +uniform extern float4x4 gLVP; // Light view projection +uniform extern float4x4 gVP; // Combined View and Projection matrix +uniform extern float4x4 gGrpT; // Mesh group transformation matrix +uniform extern float4 gAttennuate; // (Mesh Constant Fog) Attennuation of fragment color +uniform extern float4 gInScatter; // (Mesh Constant Fog) In scattering light +uniform extern float4 gColor; // General purpose color parameter +uniform extern float4 gFogColor; // Distance fog color in "Legacy" implementation +uniform extern float4 gAtmColor; // Earth glow color +uniform extern float4 gTexOff; // Texture offsets used by surface manager +uniform extern float4 gRadius; // PlanetRad, AtmOuterLimit, CameraRad, CameraAlt +uniform extern float4 gSHD; // ShadowMap data +uniform extern float4 gSHDPx; // Shadow resolution [Pixels / meter] for each cascade +uniform extern float4 gSHDSubRect[3]; // Shadow cascade sub-rects +uniform extern float4 gVCIrrad; // Virtual Cockpit ambient lighting control +uniform extern float3 gCameraPos; // Planet relative camera position, Unit vector +uniform extern float3 gNorth; +uniform extern float3 gEast; +uniform extern float3 gVCAmbient; // Ambient level inside virtual cockpit +uniform extern float3 gNoColor; // No Color option. +uniform extern Sun gSun; // Sun light direction +uniform extern Mat gMat; // Material input structure TODO: Remove all reference to this. Use gMtrl +uniform extern Mat gWater; // Water material input structure +uniform extern Mtrl gMtrl; // Material input structure +uniform extern Light gLights[MAX_LIGHTS]; +uniform extern bool gLightsEnabled; +uniform extern bool gModAlpha; // Configuration input +uniform extern bool gFullyLit; // Always fully lit bypass lighting calculations +uniform extern bool gTextured; // Enable Diffuse Texturing +uniform extern bool gFresnel; // Enable fresnel material +uniform extern bool gPBRSw; // Legacy / PBR Switch +uniform extern bool gRghnSw; // Roughness converter switch +uniform extern bool gNight; // Nighttime/Daytime +uniform extern bool gShadowsEnabled; // Enable shadow maps +uniform extern bool gEnvMapEnable; // Enable Environment mapping +uniform extern bool gInSpace; // True if a mesh is located in space +uniform extern bool gBaseBuilding; +uniform extern bool gOITEnable; +uniform extern bool gCockpit; +uniform extern int gSpecMode; +uniform extern int gHazeMode; +uniform extern float gProxySize; // Cosine of the angular size of the Proxy Gbody. (one half) +uniform extern float gInvProxySize; // = 1.0 / (1.0f-gProxySize) +uniform extern float gPointScale; +uniform extern float gDistScale; +uniform extern float gFogDensity; +uniform extern float gTime; +uniform extern float gMix; // General purpose parameter (multible uses) +uniform extern float gMtrlAlpha; +uniform extern float gGlowConst; +uniform extern float gNightTime; // 1 for nighttime, 0 for daytime +uniform extern Flow gCfg; + +// Textures ----------------------------------------------------------------- + +uniform extern texture gTex0; // Diffuse texture +uniform extern texture gTex1; // Nightlights +uniform extern texture gTex3; // Normal Map / Cloud Microtexture +uniform extern texture gSpecMap; // Specular Map +uniform extern texture gRghnMap; // Roughness Map +uniform extern texture gEmisMap; // Emission Map +uniform extern texture gEnvMapA; // Environment Map (Mirror clear) +uniform extern texture gEnvMapB; // Environment Map (Mipmapped with different levels of blur) +uniform extern texture gReflMap; // Reflectivity Map +uniform extern texture gMetlMap; // Metalness Map +uniform extern texture gHeatMap; // Heat Map +uniform extern texture gTranslMap; // Translucence Map +uniform extern texture gTransmMap; // Transmittance Map +uniform extern texture gIrradianceMap; // Irradiance Map +uniform extern texture gAmbientMap; // Baked Ambient occlusion map +uniform extern texture gCombinedMap; // Combined baked light map +uniform extern texture gCombinedSunMap; // Combined baked light map + +// Legacy Atmosphere -------------------------------------------------------- + +uniform extern float gGlobalAmb; // Global Ambient Level +uniform extern float gSunAppRad; // Sun apparent size (Radius / Distance) +uniform extern float gDispersion; +uniform extern float gAmbient0; + + +// ---------------------------------------------------------------------------- +// Vertex layouts +// ---------------------------------------------------------------------------- + +struct MESH_VERTEX { // vkClient Mesh vertex layout + float3 posL : POSITION0; + float3 nrmL : NORMAL0; + float3 tanL : TANGENT0; + float3 tex0 : TEXCOORD0; +}; + +struct NTVERTEX { // Orbiter Mesh vertex layout + float3 posL : POSITION0; + float3 nrmL : NORMAL0; + float2 tex0 : TEXCOORD0; +}; + +struct TILEVERTEX { // Vertex declaration used for surface tiles and cloud layer + float3 posL : POSITION0; + float3 normalL : NORMAL0; + float2 tex0 : TEXCOORD0; + float elev : TEXCOORD1; +}; + +struct HZVERTEX { + float3 posL : POSITION0; + float4 color : COLOR0; + float2 tex0 : TEXCOORD0; +}; + +struct POSTEX { + float3 posL : POSITION0; + float2 tex0 : TEXCOORD0; +}; + +struct SHADOW_VERTEX { + float4 posL : POSITION0; +}; + + +// ---------------------------------------------------------------------------- +// Vertex shader outputs +// ---------------------------------------------------------------------------- + +struct SimpleVS +{ + float4 posH : POSITION0; + float2 tex0 : TEXCOORD0; + float3 nrmW : TEXCOORD1; + float3 toCamW : TEXCOORD2; +}; + +struct HazeVS +{ + float4 posH : POSITION0; + float4 color : TEXCOORD0; + float2 tex0 : TEXCOORD1; +}; + +struct BShadowVS +{ + float4 posH : POSITION0; + float2 dstW : TEXCOORD0; + float alpha : TEXCOORD1; +}; + +struct ShadowTexVS +{ + float4 posH : POSITION0; + float2 dstW : TEXCOORD0; + float3 tex0 : TEXCOORD1; +}; + +// ---------------------------------------------------------------------------- +// Texture Sampler implementations +// ---------------------------------------------------------------------------- + +sampler IrradS = sampler_state // Irradiance map sampler +{ + Texture = ; + MinFilter = LINEAR; + MagFilter = LINEAR; + MipFilter = LINEAR; + AddressU = CLAMP; + AddressV = CLAMP; +}; + +sampler WrapS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler ClampS = sampler_state // Base tile sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = CLAMP; + AddressV = CLAMP; +}; + +sampler SpecS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler EmisS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler BakedLightS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler BakedSunS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler BakedAOS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + + +sampler ReflS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler MetlS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler HeatS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler RghnS = sampler_state // Primary Mesh texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler TranslS = sampler_state // Translucence texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; +sampler TransmS = sampler_state // Transmittance texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler Tex1S = sampler_state // Secundary mesh texture sampler (i.e. night texture) +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler Nrm0S = sampler_state // Normal Map Sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler MFDSamp = sampler_state // Virtual Cockpit MFD screen sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + AddressU = CLAMP; + AddressV = CLAMP; +}; + +sampler Panel0S = sampler_state // Sampler for mesh based panels, Panel MFDs. Must be compatible with Non-power of two conditional due to MFD screens. +{ + Texture = ; + MinFilter = POINT; + MagFilter = LINEAR; + MipFilter = NONE; + AddressU = CLAMP; + AddressV = CLAMP; +}; + +sampler SimpleS = sampler_state // Sampler used for SimpleTech. (Star, VC HUD) +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + MipMapLODBias = 0; + AddressU = CLAMP; // Modified for RC29 to fix the line issue in top-right corner + AddressV = CLAMP; +}; + +sampler ExhaustS = sampler_state +{ + Texture = ; + MinFilter = LINEAR; + MagFilter = LINEAR; + MipFilter = NONE; + MaxAnisotropy = ANISOTROPY_MACRO; + AddressU = CLAMP; + AddressV = CLAMP; +}; + +sampler RingS = sampler_state // Planetary rings sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + AddressU = WRAP; + AddressV = WRAP; +}; + +sampler EnvMapAS = sampler_state +{ + Texture = ; + MinFilter = LINEAR; + MagFilter = LINEAR; + MipFilter = LINEAR; + AddressU = CLAMP; + AddressV = CLAMP; + AddressW = CLAMP; +}; + +sampler EnvMapBS = sampler_state +{ + Texture = ; + MinFilter = LINEAR; + MagFilter = LINEAR; + MipFilter = LINEAR; + AddressU = CLAMP; + AddressV = CLAMP; + AddressW = CLAMP; +}; + + +// Planet surface samplers ---------------------------------------------------- + +sampler Planet0S = sampler_state // Planet/Cloud diffuse texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + AddressU = CLAMP; + AddressV = CLAMP; +}; + +sampler Planet1S = sampler_state // Planet nightlights/specular mask sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + AddressU = CLAMP; + AddressV = CLAMP; +}; + +sampler Planet3S = sampler_state // Planet/Cloud micro texture sampler +{ + Texture = ; + MinFilter = ANISOTROPIC; + MagFilter = LINEAR; + MipFilter = LINEAR; + MaxAnisotropy = ANISOTROPY_MACRO; + AddressU = WRAP; + AddressV = WRAP; +}; + + + +// ---------------------------------------------------------------------------- +// Atmospheric Haze implementation +// +// att = attennuation, ins = inscatter, depth = pixel depth [0 to 1], +// posW = camera centric world space position of the vertex +// ---------------------------------------------------------------------------- + +void AtmosphericHaze(out float4 att, out float4 ins, in float depth, in float3 posW) +{ + if (gHazeMode==0) { + att = 1; + ins = 0; + return; + } + else if (gHazeMode==1) { + att = gAttennuate; + ins = gInScatter; + return; + } + else if (gHazeMode==2) { + float fogFact = 1.0f / exp(max(0,depth) * gFogDensity); + att = fogFact; + ins = half4((1.0f-fogFact) * gFogColor.rgb, 0.0f); + return; + } +} + + +// ---------------------------------------------------------------------------- +// Legacy sun color on planet surface. Used for planet surface, base tiles and +// buildings. See SurfaceLighting() in vkUtil.cpp +// ---------------------------------------------------------------------------- + +void LegacySunColor(out float4 diff, out float ambi, out float nigh, in float3 normalW) +{ + float h = dot(-gSun.Dir, normalW); + float s = saturate((h+gSunAppRad)/(2.0f*gSunAppRad)); + float3 r0 = 1.0 - float3(0.65, 0.75, 1.0) * gDispersion; + + if (gDispersion!=0) { // case 1: planet has atmosphere + float3 di = (r0 + (1.0-r0) * saturate(h*5.780)) * s; + float ni = (h+0.242)*2.924; + float am = saturate(max(gAmbient0*saturate(ni)-0.05, gGlobalAmb)); + + diff = float4(di*(1.0-am*0.5),1); + ambi = am; + nigh = saturate(-ni-0.2); + } + else { // case 2: planet has no atmosphere + diff = float4(r0*s, 1); + ambi = gGlobalAmb; + nigh = 0; + } +} + + + +// ---------------------------------------------------------------------------- +// Vertex shader implementations +// ---------------------------------------------------------------------------- + + +SimpleVS BasicVS(NTVERTEX vrt) +{ + SimpleVS outVS = (SimpleVS)0; + float3 posW = mul(float4(vrt.posL, 1.0f), gW).xyz; + outVS.posH = mul(float4(posW, 1.0f), gVP); + outVS.nrmW = mul(float4(vrt.nrmL, 0.0f), gW).xyz; + outVS.toCamW = -posW; + outVS.tex0 = vrt.tex0; + return outVS; +} + + + +// ---------------------------------------------------------------------------- +// PixelShader Implementations +// ---------------------------------------------------------------------------- + +float4 SimpleTechPS(SimpleVS frg) : COLOR +{ + float4 c = tex2D(SimpleS, frg.tex0); + return float4(c.rgb, c.a * gMix); +} + +float4 PanelTechPS(SimpleVS frg) : COLOR +{ + float4 cTex = tex2D(SimpleS, frg.tex0); + return float4(cTex.rgb, cTex.a*gMix); +} + +float4 PanelTechBPS(SimpleVS frg) : COLOR +{ + float4 cTex = tex2D(Panel0S, frg.tex0); + return float4(cTex.rgb, cTex.a*gMix); +} + +float4 ExhaustTechPS(SimpleVS frg) : COLOR +{ + float4 c = tex2D(ExhaustS, frg.tex0); + return float4(c.rgb, c.a*gMix); +} + +float4 SpotTechPS(SimpleVS frg) : COLOR +{ + return (tex2D(SimpleS, frg.tex0) * gColor) * gMix; +} + +#include "Particle.fx" +#include "Mesh.fx" +#include "Vessel.fx" +#include "HorizonHaze.fx" +#include "Planet.fx" +#include "BeaconArray.fx" + + +BShadowVS ArrowTechVS(float3 posL : POSITION0) +{ + // Zero output. + BShadowVS outVS = (BShadowVS)0; + float3 posW = mul(float4(posL, 1.0f), gW).xyz; // Apply world transformation matrix + outVS.posH = mul(float4(posW, 1.0f), gVP); // Apply view projection matrix + return outVS; +} + +float4 ArrowTechPS(BShadowVS frg) : COLOR +{ + return gColor; +} + + +// This is used for rendering grapple points ---------------------------------- +// +technique ArrowTech +{ + pass P0 + { + vertexShader = compile vs_3_0 ArrowTechVS(); + pixelShader = compile ps_3_0 ArrowTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = false; + ZEnable = true; + } +} + + +// This is used for many simple renderings ------------------------------------ +// +technique SimpleTech +{ + pass P0 + { + vertexShader = compile vs_3_0 BasicVS(); + pixelShader = compile ps_3_0 SimpleTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + } +} + +// This is used for 2DPanel and Glass cockpit --------------------------------- +// +technique PanelTech +{ + pass P0 + { + vertexShader = compile vs_3_0 BasicVS(); + pixelShader = compile ps_3_0 PanelTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + } +} + +technique PanelTechB +{ + pass P0 + { + vertexShader = compile vs_3_0 BasicVS(); + pixelShader = compile ps_3_0 PanelTechBPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZEnable = false; + ZWriteEnable = false; + } +} + + +// Thil will render exhaust textures ------------------------------------------ +// +technique ExhaustTech +{ + pass P0 + { + vertexShader = compile vs_3_0 BasicVS(); + pixelShader = compile ps_3_0 ExhaustTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = false; + ZEnable = true; + } +} + +// This is used for rendering beacons ----------------------------------------- +// +technique SpotTech +{ + pass P0 + { + vertexShader = compile vs_3_0 BasicVS(); + pixelShader = compile ps_3_0 SpotTechPS(); + + AlphaBlendEnable = true; + BlendOp = Add; + SrcBlend = SrcAlpha; + DestBlend = InvSrcAlpha; + ZWriteEnable = false; + ZEnable = true; + } +} diff --git a/Orbitersdk/include/DrawAPI.h b/Orbitersdk/include/DrawAPI.h index e20d81651..8a71ead4d 100644 --- a/Orbitersdk/include/DrawAPI.h +++ b/Orbitersdk/include/DrawAPI.h @@ -21,827 +21,17 @@ #include "OrbiterAPI.h" #include -#include +#include "MathAPI.h" #if defined(_MSC_VER) && (_MSC_VER < 1920 ) // Microsoft Visual Studio Version 2017 and lower #include #endif - -#ifdef D3D9CLIENT_EXPORTS -#include "d3dx9.h" -#endif - /// \brief Poly object handle typedef void* HPOLY; namespace oapi { - /** - * \brief Integer-valued 2-D vector type. - * \note This structure is designed to be compatible with the Windows POINT type. - */ - union IVECTOR2 { - long data[2]; ///< vector data array - struct { - long x; ///< vector x coordinate - long y; ///< vector y coordinate - }; - }; - - - /** - * \brief 32-bit floating point 2D vector type. - * \note This structure is compatible with the D3DXVECTOR2 type. - */ - typedef struct FVECTOR2 - { - FVECTOR2() - { - x = y = 0.0f; - } - - FVECTOR2(float q) - { - x = y = q; - } - - FVECTOR2(float _x, float _y) - { - x = _x; - y = _y; - } - - FVECTOR2(double _x, double _y) - { - x = float(_x); - y = float(_y); - } - - FVECTOR2(long _x, long _y) - { - x = float(_x); - y = float(_y); - } - - FVECTOR2(DWORD _x, DWORD _y) - { - x = float(_x); - y = float(_y); - } - - FVECTOR2(int _x, int _y) - { - x = float(_x); - y = float(_y); - } - - FVECTOR2(const POINT& p) - { - x = float(p.x); - y = float(p.y); - } - - FVECTOR2(const POINT* p) - { - x = float(p->x); - y = float(p->y); - } - - FVECTOR2(const IVECTOR2& p) - { - x = float(p.x); - y = float(p.y); - } - - inline FVECTOR2 operator* (float f) const - { - return FVECTOR2(x * f, y * f); - } - - inline FVECTOR2 operator* (FVECTOR2 f) const - { - return FVECTOR2(x * f.x, y * f.y); - } - - inline FVECTOR2& operator*= (FVECTOR2& f) - { - x *= f.x; y *= f.y; - return *this; - } - - inline FVECTOR2& operator+= (FVECTOR2& f) - { - x += f.x; y += f.y; - return *this; - } - - inline FVECTOR2& operator-= (FVECTOR2& f) - { - x -= f.x; y -= f.y; - return *this; - } - - inline FVECTOR2& operator/= (FVECTOR2& f) - { - x /= f.x; y /= f.y; - return *this; - } - - inline FVECTOR2 operator/ (float f) const - { - f = 1.0f / f; - return FVECTOR2(x * f, y * f); - } - - inline FVECTOR2 operator+ (float f) const - { - return FVECTOR2(x + f, y + f); - } - - inline FVECTOR2 operator+ (FVECTOR2& f) const - { - return FVECTOR2(x + f.x, y + f.y); - } - - inline FVECTOR2 operator- (float f) const - { - return FVECTOR2(x - f, y - f); - } - - inline FVECTOR2 operator+ (const FVECTOR2& f) const - { - return FVECTOR2(x + f.x, y + f.y); - } - - inline FVECTOR2 operator- (const FVECTOR2& f) const - { - return FVECTOR2(x - f.x, y - f.y); - } - - float x, y; - } FVECTOR2; - - - - /** - * \brief 32-bit floating point 3D vector type. - * \note This structure is compatible with the D3DXVECTOR3 type. - */ - typedef union FVECTOR3 - { - FVECTOR3() - { - x = y = z = 0.0f; - } - - FVECTOR3(float q) - { - x = y = z = q; - } - - FVECTOR3(float _x, float _y, float _z) - { - x = _x; - y = _y; - z = _z; - } - - FVECTOR3(const VECTOR3& v) - { - x = float(v.x); - y = float(v.y); - z = float(v.z); - } - -#ifdef D3D9CLIENT_EXPORTS - FVECTOR3(const D3DXVECTOR3 &v) - { - x = float(v.x); - y = float(v.y); - z = float(v.z); - } - FVECTOR3(const D3DXCOLOR& v) - { - x = float(v.r); - y = float(v.g); - z = float(v.b); - } -#endif - float MaxRGB() const - { - return (std::max)(r, (std::max)(g, b)); - } - - float MinRGB() const - { - return (std::min)(r, (std::min)(g, b)); - } - - float sql() const - { - return x * x + y * y + z * z; - } - - inline VECTOR3 _V() const - { - VECTOR3 v = { x,y,z }; - return v; - } - - inline FVECTOR3& operator*= (float f) - { - x *= f; y *= f; z *= f; - return *this; - } - - inline FVECTOR3& operator*= (const FVECTOR3 &f) - { - x *= f.x; y *= f.y; z *= f.z; - return *this; - } - - inline FVECTOR3& operator/= (float f) - { - // return *this *= (1.0f / f); // nicer? - f = 1.0f / f; - x *= f; y *= f; z *= f; - return *this; - } - - inline FVECTOR3& operator+= (float f) - { - x += f; y += f; z += f; - return *this; - } - - inline FVECTOR3& operator+= (const FVECTOR3& f) - { - x += f.x; y += f.y; z += f.z; - return *this; - } - - inline FVECTOR3& operator-= (float f) - { - x -= f; y -= f; z -= f; - return *this; - } - - inline FVECTOR3& operator-= (const FVECTOR3 &f) - { - x -= f.x; y -= f.y; z -= f.z; - return *this; - } - - inline FVECTOR3 operator* (float f) const - { - return FVECTOR3(x * f, y * f, z * f); - } - - inline FVECTOR3 operator* (const FVECTOR3 &f) const - { - return FVECTOR3(x * f.x, y * f.y, z * f.z); - } - - inline FVECTOR3 operator/ (float f) const - { - f = 1.0f / f; - return FVECTOR3(x * f, y * f, z * f); - } - - inline FVECTOR3 operator/ (const FVECTOR3 &f) const - { - return FVECTOR3(x / f.x, y / f.y, z / f.z); - } - - inline FVECTOR3 operator+ (float f) const - { - return FVECTOR3(x + f, y + f, z + f); - } - - inline FVECTOR3 operator- (float f) const - { - return FVECTOR3(x - f, y - f, z - f); - } - - inline FVECTOR3 operator+ (const FVECTOR3& f) const - { - return FVECTOR3(x + f.x, y + f.y, z + f.z); - } - - inline FVECTOR3 operator- (const FVECTOR3& f) const - { - return FVECTOR3(x - f.x, y - f.y, z - f.z); - } - - inline FVECTOR3 operator-() const - { - return FVECTOR3(-x, -y, -z); - } - -#ifdef D3D9CLIENT_EXPORTS - inline operator D3DXVECTOR3() const - { - return D3DXVECTOR3(x, y, z); - } - inline operator D3DXCOLOR() const - { - return D3DXCOLOR(x, y, z, 1); - } -#endif - struct { float x, y, z; }; - struct { float r, g, b; }; - FVECTOR2 xy; - } FVECTOR3; - - - /** - * \brief 32-bit floating point 4D vector type. - * \note This structure is compatible with the D3DXVECTOR4 type. - */ - typedef union __declspec(align(16)) FVECTOR4 - { - DWORD dword_abgr() const - { - DWORD dr = DWORD((std::max)(0.0f, r) * 255.0f + 0.5f); - DWORD dg = DWORD((std::max)(0.0f, g) * 255.0f + 0.5f); - DWORD db = DWORD((std::max)(0.0f, b) * 255.0f + 0.5f); - DWORD da = DWORD((std::max)(0.0f, a) * 255.0f + 0.5f); - if (dr > 0xFF) dr = 0xFF; - if (dg > 0xFF) dg = 0xFF; - if (db > 0xFF) db = 0xFF; - if (da > 0xFF) da = 0xFF; - return (da << 24) | (db << 16) | (dg << 8) | dr; - } - - DWORD dword_argb() const - { - DWORD dr = DWORD((std::max)(0.0f, r) * 255.0f + 0.5f); - DWORD dg = DWORD((std::max)(0.0f, g) * 255.0f + 0.5f); - DWORD db = DWORD((std::max)(0.0f, b) * 255.0f + 0.5f); - DWORD da = DWORD((std::max)(0.0f, a) * 255.0f + 0.5f); - if (dr > 0xFF) dr = 0xFF; - if (dg > 0xFF) dg = 0xFF; - if (db > 0xFF) db = 0xFF; - if (da > 0xFF) da = 0xFF; - return (da << 24) | (dr << 16) | (dg << 8) | db; - } - - COLOUR4 Colour4() const - { - COLOUR4 clr = { r,g,b,a }; - return clr; - } - - float MaxRGB() const - { - return (std::max)(r, (std::max)(g, b)); - } - - FVECTOR4() - { - r = g = b = a = 0.0f; - } - - FVECTOR4(float q) - { - x = y = z = w = q; - } - - FVECTOR4(const COLOUR4& c) - { - r = c.r; - g = c.g; - b = c.b; - a = c.a; - } - - FVECTOR4(DWORD abgr) - { - DWORD dr = (abgr & 0xFF); abgr >>= 8; - DWORD dg = (abgr & 0xFF); abgr >>= 8; - DWORD db = (abgr & 0xFF); abgr >>= 8; - DWORD da = (abgr & 0xFF); - //if (da == 0) da = 255; - float q = 3.92156862e-3f; - r = float(dr) * q; - g = float(dg) * q; - b = float(db) * q; - a = float(da) * q; - } - - FVECTOR4(const VECTOR4& v) - { - x = float(v.x); - y = float(v.y); - z = float(v.z); - w = float(v.w); - } - - FVECTOR4(const VECTOR3& v, float _w) - { - x = float(v.x); - y = float(v.y); - z = float(v.z); - w = _w; - } - - FVECTOR4(const FVECTOR3& v, float _w) - { - rgb = v; - w = _w; - } - - FVECTOR4(float _x, float _y, float _z, float _w) - { - x = float(_x); - y = float(_y); - z = float(_z); - w = float(_w); - } - - FVECTOR4(int _x, int _y, int _z, int _w) - { - x = float(_x); - y = float(_y); - z = float(_z); - w = float(_w); - } - - FVECTOR4(double _x, double _y, double _z, double _w) - { - x = float(_x); - y = float(_y); - z = float(_z); - w = float(_w); - } - -#ifdef D3D9CLIENT_EXPORTS - FVECTOR4(const D3DXVECTOR4& v) - { - x = float(v.x); - y = float(v.y); - z = float(v.z); - w = float(v.w); - } - FVECTOR4(const D3DXCOLOR& v) - { - x = float(v.r); - y = float(v.g); - z = float(v.b); - w = float(v.a); - } -#endif - - - inline FVECTOR4 operator* (float f) const - { - return FVECTOR4(x * f, y * f, z * f, w * f); - } - - inline FVECTOR4& operator*= (float f) - { - x *= f; y *= f; z *= f; w *= f; - return *this; - } - - inline FVECTOR4& operator/= (float f) - { - // return *this *= (1.0f / f); // nicer? - f = 1.0f / f; - x *= f; y *= f; z *= f; w *= f; - return *this; - } - - inline FVECTOR4& operator+= (float f) - { - x += f; y += f; z += f; w += f; - return *this; - } - - inline FVECTOR4& operator-= (float f) - { - x -= f; y -= f; z -= f; w -= f; - return *this; - } - - inline FVECTOR4 operator/ (float f) const - { - f = 1.0f / f; - return FVECTOR4(x * f, y * f, z * f, w * f); - } - - inline FVECTOR4 operator+ (float f) const - { - return FVECTOR4(x + f, y + f, z + f, w + f); - } - - inline FVECTOR4 operator- (float f) const - { - return FVECTOR4(x - f, y - f, z - f, w - f); - } - - inline FVECTOR4 operator+ (const FVECTOR4& f) const - { - return FVECTOR4(x + f.x, y + f.y, z + f.z, w + f.w); - } - - inline FVECTOR4 operator- (const FVECTOR4& f) const - { - return FVECTOR4(x - f.x, y - f.y, z - f.z, w - f.w); - } - - inline FVECTOR4 operator-() const - { - return FVECTOR4(-x, -y, -z, -w); - } - -#ifdef D3D9CLIENT_EXPORTS - inline operator D3DXVECTOR4() const - { - return D3DXVECTOR4(x, y, z, w); - } -#endif - __m128 xm; - float data[4]; - struct { float x, y, z, w; }; - struct { float r, g, b, a; }; - FVECTOR3 xyz; // , w; }; - FVECTOR3 rgb; // , a; }; - } FVECTOR4; - - - typedef union DRECT - { - DRECT() - { - left = right = top = bottom = 0.0; - } - - DRECT(double l, double t, double r, double b) - { - left = l; top = t; right = r; bottom = b; - } - - DRECT(float l, float t, float r, float b) - { - left = double(l); top = double(t); right = double(r); bottom = double(b); - } - - DRECT(const DRECT& x) - { - left = x.left; - top = x.top; - right = x.right; - bottom = x.bottom; - } - - VECTOR4 vec; - - struct { - double left, top, right, bottom; - }; - - } DRECT; - - /** - * \brief Float-valued 4x4 matrix. - * \note This structure is compatible with the D3DXMATRIX. - */ - typedef union __declspec(align(16)) FMATRIX4 - { - FMATRIX4() { - m11 = m12 = m13, m14 = m21 = m22 = m23 = m24 = m31 = m32 = m33 = m34 = m41 = m42 = m43 = m44 = 0; - } - - FMATRIX4(float m11, float m12, float m13, float m14, - float m21, float m22, float m23, float m24, - float m31, float m32, float m33, float m34, - float m41, float m42, float m43, float m44) : - m11(m11), m12(m12), m13(m13), m14(m14), - m21(m21), m22(m22), m23(m23), m24(m24), - m31(m31), m32(m32), m33(m33), m34(m34), - m41(m41), m42(m42), m43(m43), m44(m44) - { - } - - FMATRIX4(const float* pSrc) { - for (int i = 0; i < 16; i++) data[i] = pSrc[i]; - } - -#ifdef D3D9CLIENT_EXPORTS - FMATRIX4(const D3DXMATRIX& m) - { - memcpy_s(data, sizeof(FMATRIX4), &m, sizeof(m)); - } - FMATRIX4(const LPD3DXMATRIX m) - { - memcpy_s(data, sizeof(FMATRIX4), m, sizeof(FMATRIX4)); - } - inline operator LPD3DXMATRIX() - { - return (LPD3DXMATRIX)this; - } -#endif - - void Zero() - { - for (int i = 0; i < 16; i++) data[i] = 0.0; - } - - void Ident() - { - for (int i = 0; i < 16; i++) data[i] = 0.0; - m11 = m22 = m33 = m44 = 1.0f; - } - - void _swap(float& a, float& b) { float c = a; a = b; b = c; } - - void Transpose() - { - _swap(m12, m21); _swap(m13, m31); - _swap(m14, m41); _swap(m23, m32); - _swap(m24, m42); _swap(m34, m43); - } - - float data[16]; - struct { FVECTOR4 _x, _y, _z, _p; }; - struct { float m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44; }; - } FMATRIX4; - - - /** - * \brief Vector Matrix multiplication - */ - inline FVECTOR4 mul(const FVECTOR4& V, const FMATRIX4& M) - { - float x = V.x * M.m11 + V.y * M.m21 + V.z * M.m31 + V.w * M.m41; - float y = V.x * M.m12 + V.y * M.m22 + V.z * M.m32 + V.w * M.m42; - float z = V.x * M.m13 + V.y * M.m23 + V.z * M.m33 + V.w * M.m43; - float w = V.x * M.m14 + V.y * M.m24 + V.z * M.m34 + V.w * M.m44; - return FVECTOR4(x, y, z, w); - } - - - inline FVECTOR4 tmul(const FVECTOR4& V, const FMATRIX4& M) - { - float x = V.x * M.m11 + V.y * M.m12 + V.z * M.m13 + V.w * M.m14; - float y = V.x * M.m21 + V.y * M.m22 + V.z * M.m23 + V.w * M.m24; - float z = V.x * M.m31 + V.y * M.m32 + V.z * M.m33 + V.w * M.m34; - float w = V.x * M.m41 + V.y * M.m42 + V.z * M.m43 + V.w * M.m44; - return FVECTOR4(x, y, z, w); - } - - - /** - * \brief Transform a position by matrix - */ - inline FVECTOR3 TransformCoord(const FVECTOR3& V, const FMATRIX4& M) - { - float x = V.x * M.m11 + V.y * M.m21 + V.z * M.m31 + M.m41; - float y = V.x * M.m12 + V.y * M.m22 + V.z * M.m32 + M.m42; - float z = V.x * M.m13 + V.y * M.m23 + V.z * M.m33 + M.m43; - float w = V.x * M.m14 + V.y * M.m24 + V.z * M.m34 + M.m44; - w = 1.0f / w; - return FVECTOR3(x * w, y * w, z * w); - } - - - /** - * \brief Transform a normal or direction by matrix - */ - inline FVECTOR3 TransformNormal(const FVECTOR3& V, const FMATRIX4& M) - { - float x = V.x * M.m11 + V.y * M.m21 + V.z * M.m31; - float y = V.x * M.m12 + V.y * M.m22 + V.z * M.m32; - float z = V.x * M.m13 + V.y * M.m23 + V.z * M.m33; - return FVECTOR3(x, y, z); - } - - - inline FVECTOR2 unit(const FVECTOR2& v) - { - float f = 1.0f / ::sqrt(v.x * v.x + v.y * v.y); - return FVECTOR2(v.x * f, v.y * f); - } - - inline FVECTOR3 unit(const FVECTOR3& v) - { - float d = v.x * v.x + v.y * v.y + v.z * v.z; - return d > 0 ? FVECTOR3(v.x, v.y, v.z) / ::sqrt(d) : 0.0f; - } - - inline FVECTOR3 normalize(const FVECTOR3& v) - { - float d = v.x * v.x + v.y * v.y + v.z * v.z; - return d > 0 ? FVECTOR3(v.x, v.y, v.z) / ::sqrt(d) : 0.0f; - } - - inline float dot(const FVECTOR2& v, const FVECTOR2& w) - { - return v.x * w.x + v.y * w.y; - } - - inline float dot(const FVECTOR3& v, const FVECTOR3& w) - { - return v.x * w.x + v.y * w.y + v.z * w.z; - } - - inline float dot(const FVECTOR4& v, const FVECTOR4& w) - { - return v.x * w.x + v.y * w.y + v.z * w.z + v.w * w.w; - } - - inline float length(const FVECTOR2& v) - { - return ::sqrt(v.x * v.x + v.y * v.y); - } - - inline float length(const FVECTOR3& v) - { - return ::sqrt(v.x * v.x + v.y * v.y + v.z * v.z); - } - - inline FVECTOR3 cross(const FVECTOR3& a, const FVECTOR3& b) - { - return FVECTOR3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); - } - - inline float saturate(float x) - { - //sadly std::clamp produces garbage assembly on both gcc and MSVC - //this version makes MSVC produce good code - x = (x < 0.0f) ? 0.0f : x; - x = (x > 1.0f) ? 1.0f : x; - return x; - } - - inline FVECTOR3 saturate(const FVECTOR3& v) - { - return FVECTOR3(saturate(v.x), saturate(v.y), saturate(v.z)); - } - - inline FVECTOR4 saturate(const FVECTOR4& v) - { - return FVECTOR4(saturate(v.x), saturate(v.y), saturate(v.z), saturate(v.w)); - } - - inline FVECTOR2 lerp(const FVECTOR2& a, const FVECTOR2& b, float x) - { - return a + (b - a) * x; - } - - inline FVECTOR3 lerp(const FVECTOR3& a, const FVECTOR3& b, float x) - { - return a + (b - a) * x; - } - - inline FVECTOR4 lerp(const FVECTOR4& a, const FVECTOR4& b, float x) - { - return a + (b - a) * x; - } - - inline FVECTOR3 pow(const FVECTOR3& x, float y) - { - return FVECTOR3(::pow(x.x, y), ::pow(x.y, y), ::pow(x.z, y)); - } - - inline FVECTOR4 pow(const FVECTOR4& x, float y) - { - return FVECTOR4(::pow(x.x, y), ::pow(x.y, y), ::pow(x.z, y), ::pow(x.w, y)); - } - - inline FVECTOR3 pow(const FVECTOR3& x, const FVECTOR3 &y) - { - return FVECTOR3(::pow(x.x, y.x), ::pow(x.y, y.y), ::pow(x.z, y.z)); - } - - inline FVECTOR4 pow(const FVECTOR4& x, const FVECTOR4 &y) - { - return FVECTOR4(::pow(x.x, y.x), ::pow(x.y, y.y), ::pow(x.z, y.z), ::pow(x.w, y.w)); - } - - inline FVECTOR3 exp(const FVECTOR3& x) - { - return FVECTOR3(::exp(x.x), ::exp(x.y), ::exp(x.z)); - } - - inline FVECTOR4 exp(const FVECTOR4& x) - { - return FVECTOR4(::exp(x.x), ::exp(x.y), ::exp(x.z), ::exp(x.w)); - } - - inline FVECTOR3 sqrt(const FVECTOR3& x) - { - return FVECTOR3(::sqrt(x.x), ::sqrt(x.y), ::sqrt(x.z)); - } - - inline FVECTOR4 sqrt(const FVECTOR4& x) - { - return FVECTOR4(::sqrt(x.x), ::sqrt(x.y), ::sqrt(x.z), ::sqrt(x.w)); - } - - // ====================================================================== // class oapi::DrawingTool @@ -1005,10 +195,12 @@ class OAPIFUNC Sketchpad RENDER_ALL = 0x04 ///< Render all meshgroups }; - + /** + * \brief Source layout structure for GUI element drawing + */ typedef struct { - RECT intr; - RECT outr; + RECT intr; ///< Interrior rect + RECT outr; ///< Outerrior rect } skpRegion; /** @@ -1528,7 +720,7 @@ class OAPIFUNC Sketchpad * \param scale Text scale factor (0.0f to 1.0f) * \param angle Rotation angle in radians. * \sa Text() - * \note Rotation and scaling can result a blurry text if used with a small fonts. Rotation of ±PI/2 or PI should work fine. + * \note Rotation and scaling can result a blurry text if used with a small fonts. Rotation of ±PI/2 or PI should work fine. * \note Rotation spefified during font creation is ignored in this function. */ virtual void TextEx(float x, float y, const char* str, float scale = 1.0f, float angle = 0.0f) { assert(false); } @@ -1588,7 +780,7 @@ class OAPIFUNC Sketchpad * \note SKP3_PRM_NOISE, Get Noise configuration. Noise color in (.rgb) and (.a) controls blending between input color (.rgb) and target color. * \sa SetRenderParam */ - virtual FVECTOR4 GetRenderParam(RenderParam param) { assert(false); return FVECTOR4(0, 0, 0, 0); } + virtual FVECTOR4 GetRenderParam(RenderParam param) { assert(false); return FVECTOR4(0.0f, 0.0f, 0.0f, 0.0f); } /** * \brief [DX9] Set a render configuration paramater or "effect". diff --git a/Orbitersdk/include/GraphicsAPI.h b/Orbitersdk/include/GraphicsAPI.h index ba7d4fbac..2c09b9bf8 100644 --- a/Orbitersdk/include/GraphicsAPI.h +++ b/Orbitersdk/include/GraphicsAPI.h @@ -459,12 +459,20 @@ class OAPIFUNC GraphicsClient: public Module { */ virtual void clbkOptionChanged(DWORD cat, DWORD item) {} + /** + * \brief React to vessel creation, deletion, docking, attaching events + * \param hVesselA object handle of first vessel + * \param hVesselB object handle of second vessel + * \param type Event type + */ + virtual void clbkScenarioChanged(OBJHANDLE hVessel, ScnChgEvent type) {} + /** * \brief Print multiple debug strings onto a screen, will be cleared when printed on screen. * \param str Text string to print */ virtual void clbkDebugString(const char *str) {} - + /** * \brief Texture request * @@ -516,6 +524,13 @@ class OAPIFUNC GraphicsClient: public Module { virtual SURFHANDLE clbkLoadSurface (const char *fname, DWORD attrib, bool bPath = false) { return NULL; } + /** + */ + virtual SURFHANDLE clbkLoadMaps(const char* diff, const char* maps, bool bPath, SURFHANDLE hOld = NULL, bool bAll = true) + { + return clbkLoadTexture(diff); + } + /** * \brief Save the contents of a surface to a formatted image file or to the clipboard * \param surf surface handle (0 for primary render surface) @@ -563,7 +578,7 @@ class OAPIFUNC GraphicsClient: public Module { * \default None, returns 2 ("client does not support operation"). */ virtual int clbkSetMeshMaterial (DEVMESHHANDLE hMesh, DWORD matidx, const MATERIAL *mat) { return 2; } - virtual int clbkSetMeshMaterialEx(DEVMESHHANDLE hMesh, DWORD matidx, MatProp prp, const oapi::FVECTOR4* in) { return 2; } + virtual int clbkSetMeshMaterialEx(DEVMESHHANDLE hMesh, DWORD matidx, MatProp prp, const FVECTOR4* in) { return 2; } /** * \brief Retrieve the properties of one of the mesh materials. @@ -574,7 +589,7 @@ class OAPIFUNC GraphicsClient: public Module { * \default None, returns 2 ("client does not support operation"). */ virtual int clbkMeshMaterial (DEVMESHHANDLE hMesh, DWORD matidx, MATERIAL *mat) { return 2; } - virtual int clbkMeshMaterialEx(DEVMESHHANDLE hMesh, DWORD matidx, MatProp prp, oapi::FVECTOR4* out) { return 2; } + virtual int clbkMeshMaterialEx(DEVMESHHANDLE hMesh, DWORD matidx, MatProp prp, FVECTOR4* out) { return 2; } /** * \brief Set custom properties for a device-specific mesh. @@ -590,7 +605,7 @@ class OAPIFUNC GraphicsClient: public Module { * if value<>0 modulate (mix) material alpha values with texture alpha maps. * \default None, returns \e false. */ - virtual bool clbkSetMeshProperty (DEVMESHHANDLE hMesh, DWORD property, DWORD value) { return false; } + virtual bool clbkSetMeshProperty(DEVMESHHANDLE hMesh, DWORD property, DWORD value) { return false; } // ================================================================== /// \name Visual object interface @@ -654,6 +669,7 @@ class OAPIFUNC GraphicsClient: public Module { * \sa RegisterVisObject, UnregisterVisObject, visevent */ virtual int clbkVisEvent (OBJHANDLE hObj, VISHANDLE vis, DWORD msg, DWORD_PTR context); + virtual void clbkSetVisualProperty(VISHANDLE vis, VisualProp prp, int idx, const type_info& t, const void* val) { } /** * \brief Return a mesh handle for a visual, defined by its index @@ -2056,4 +2072,4 @@ OAPIFUNC bool oapiRegisterGraphicsClient (oapi::GraphicsClient *gc); OAPIFUNC bool oapiUnregisterGraphicsClient (oapi::GraphicsClient *gc); -#endif // !__GRAPHICSAPI_H \ No newline at end of file +#endif // !__GRAPHICSAPI_H diff --git a/Orbitersdk/include/MathAPI.h b/Orbitersdk/include/MathAPI.h new file mode 100644 index 000000000..aca04b55a --- /dev/null +++ b/Orbitersdk/include/MathAPI.h @@ -0,0 +1,699 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2013-2016 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + +#ifndef __MATHAPI_H +#define __MATHAPI_H + +#include +#include "OrbiterAPI.h" + + +#if defined(_MSC_VER) && (_MSC_VER < 1920 ) // Microsoft Visual Studio Version 2017 and lower +#include +#endif + +#if defined (vkCLIENT_EXPORTS) || (Orbiter_EXPORTS) +#define __XM +#include "DirectXMath.h" +using namespace DirectX; +#endif + +namespace oapi +{ + /** + * \brief Integer-valued 2-D vector type. + * \note This structure is designed to be compatible with the Windows POINT type. + */ + union IVECTOR2 { + IVECTOR2() { x = y = 0; } + IVECTOR2(long x, long y) : x(x), y(y) + { + } + long data[2]; ///< vector data array + struct { + long x; ///< vector x coordinate + long y; ///< vector y coordinate + }; + }; + + + /** + * \brief 32-bit floating point 2D vector type. + * \note This structure is compatible with the D3DXVECTOR2 type. + */ + typedef union FVECTOR2 + { + FVECTOR2() + { + x = y = 0.0f; + } + + FVECTOR2(float q) + { + x = y = q; + } + + FVECTOR2(float _x, float _y) + { + x = _x; + y = _y; + } + + FVECTOR2(long _x, long _y) + { + x = float(_x); + y = float(_y); + } + + FVECTOR2(DWORD _x, DWORD _y) + { + x = float(_x); + y = float(_y); + } + + FVECTOR2(int _x, int _y) + { + x = float(_x); + y = float(_y); + } + + FVECTOR2(const IVECTOR2* p) + { + x = float(p->x); + y = float(p->y); + } + + FVECTOR2(const IVECTOR2& p) + { + x = float(p.x); + y = float(p.y); + } + +#ifdef __XM + FVECTOR2(const XMVECTOR m) + { + XMStoreFloat2((XMFLOAT2*)this, m); + } + + XMVECTOR XM() const + { + return XMLoadFloat2((XMFLOAT2*)this); + } + + void Load(const XMVECTOR m) + { + XMStoreFloat2((XMFLOAT2*)this, m); + } +#endif + inline float& operator[](int i) + { + return data[i]; + } + + inline bool operator== (const FVECTOR2& f) const + { + return x == f.x && y == f.y; + } + + inline bool operator!= (const FVECTOR2& f) const + { + return x != f.x || y != f.y; + } + + float data[2]; + struct { + float x, y; + }; + } FVECTOR2; + + + + /** + * \brief 32-bit floating point 3D vector type. + * \note This structure is compatible with the D3DXVECTOR3 type. + */ + typedef union FVECTOR3 + { + FVECTOR3() + { + x = y = z = 0.0f; + } + + FVECTOR3(float q) + { + x = y = z = q; + } + + FVECTOR3(float _x, float _y, float _z) + { + x = _x; + y = _y; + z = _z; + } + + FVECTOR3(const VECTOR3& v) + { + x = float(v.x); + y = float(v.y); + z = float(v.z); + } + +#ifdef __XM + FVECTOR3(const XMVECTOR v) + { + XMStoreFloat3((XMFLOAT3*)this, v); + } + + XMVECTOR XM() const + { + return XMLoadFloat3((XMFLOAT3*)this); + } + + void Load(const XMVECTOR v) + { + XMStoreFloat3((XMFLOAT3*)this, v); + } +#endif + + inline VECTOR3 _V() const { VECTOR3 v = { x,y,z }; return v; } + + inline float& operator[](int i) + { + return data[i]; + } + + inline bool operator== (const FVECTOR3& f) const + { + return x == f.x && y == f.y && z == f.z; + } + + inline bool operator!= (const FVECTOR3& f) const + { + return x != f.x || y != f.y || z != f.z; + } + + float data[3]; + struct { float x, y, z; }; + struct { float r, g, b; }; + FVECTOR2 xy; + } FVECTOR3; + + + /** + * \brief 32-bit floating point 4D vector type. + * \note This structure is compatible with the D3DXVECTOR4 type. + */ + typedef union FVECTOR4 + { + DWORD dword_abgr() const + { + DWORD dr = DWORD((std::max)(0.0f, r) * 255.0f + 0.5f); + DWORD dg = DWORD((std::max)(0.0f, g) * 255.0f + 0.5f); + DWORD db = DWORD((std::max)(0.0f, b) * 255.0f + 0.5f); + DWORD da = DWORD((std::max)(0.0f, a) * 255.0f + 0.5f); + if (dr > 0xFF) dr = 0xFF; + if (dg > 0xFF) dg = 0xFF; + if (db > 0xFF) db = 0xFF; + if (da > 0xFF) da = 0xFF; + return (da << 24) | (db << 16) | (dg << 8) | dr; + } + + DWORD dword_argb() const + { + DWORD dr = DWORD((std::max)(0.0f, r) * 255.0f + 0.5f); + DWORD dg = DWORD((std::max)(0.0f, g) * 255.0f + 0.5f); + DWORD db = DWORD((std::max)(0.0f, b) * 255.0f + 0.5f); + DWORD da = DWORD((std::max)(0.0f, a) * 255.0f + 0.5f); + if (dr > 0xFF) dr = 0xFF; + if (dg > 0xFF) dg = 0xFF; + if (db > 0xFF) db = 0xFF; + if (da > 0xFF) da = 0xFF; + return (da << 24) | (dr << 16) | (dg << 8) | db; + } + + FVECTOR4() + { + r = g = b = a = 0.0f; + } + + FVECTOR4(float q) + { + x = y = z = w = q; + } + + FVECTOR4(const COLOUR4& c) + { + r = c.r; + g = c.g; + b = c.b; + a = c.a; + } + + FVECTOR4(DWORD abgr) + { + DWORD dr = (abgr & 0xFF); abgr >>= 8; + DWORD dg = (abgr & 0xFF); abgr >>= 8; + DWORD db = (abgr & 0xFF); abgr >>= 8; + DWORD da = (abgr & 0xFF); + float q = 3.92156862e-3f; + r = float(dr) * q; + g = float(dg) * q; + b = float(db) * q; + a = float(da) * q; + } + +#ifdef __XM + FVECTOR4(const XMVECTOR v) + { + XMStoreFloat4((XMFLOAT4*)this, v); + } + + XMVECTOR XM() const + { + return XMLoadFloat4((XMFLOAT4*)this); + } + + void Load(const XMVECTOR v) + { + XMStoreFloat4((XMFLOAT4*)this, v); + } +#endif + + FVECTOR4(const VECTOR4& v) + { + x = float(v.x); + y = float(v.y); + z = float(v.z); + w = float(v.w); + } + + FVECTOR4(const VECTOR3& v, float _w) + { + x = float(v.x); + y = float(v.y); + z = float(v.z); + w = _w; + } + + FVECTOR4(const FVECTOR3& v, float _w) + { + rgb = v; + w = _w; + } + + FVECTOR4(float _x, float _y, float _z, float _w) + { + x = float(_x); + y = float(_y); + z = float(_z); + w = float(_w); + } + + operator const COLOUR4() const + { + COLOUR4 clr = { r,g,b,a }; + return clr; + } + + inline float& operator[](int i) + { + return data[i]; + } + + inline bool operator== (const FVECTOR4& f) const + { + return x == f.x && y == f.y && z == f.z && w == f.w; + } + + inline bool operator!= (const FVECTOR4& f) const + { + return x != f.x || y != f.y || z != f.z || w != f.w; + } + + float data[4]; + struct { float x, y, z, w; }; + struct { float r, g, b, a; }; + FVECTOR3 xyz; // , w; }; + FVECTOR3 rgb; // , a; }; + } FVECTOR4; + + + typedef union DRECT + { + DRECT() + { + left = right = top = bottom = 0.0; + } + + DRECT(double l, double t, double r, double b) + { + left = l; top = t; right = r; bottom = b; + } + + DRECT(float l, float t, float r, float b) + { + left = double(l); top = double(t); right = double(r); bottom = double(b); + } + + DRECT(const DRECT& x) + { + left = x.left; + top = x.top; + right = x.right; + bottom = x.bottom; + } + + VECTOR4 vec; + + struct { + double left, top, right, bottom; + }; + + } DRECT; + + + /** + * \brief Float-valued 4x4 matrix. + * \note This structure is compatible with the D3DXMATRIX. + */ + typedef union FMATRIX4 + { + + FMATRIX4(int i = 1) { if (i == 0) Zero(); else Ident(); } + + + FMATRIX4(float f) { + m11 = m12 = m13, m14 = m21 = m22 = m23 = m24 = m31 = m32 = m33 = m34 = m41 = m42 = m43 = m44 = f; + } + + + FMATRIX4(float m11, float m12, float m13, float m14, + float m21, float m22, float m23, float m24, + float m31, float m32, float m33, float m34, + float m41, float m42, float m43, float m44) : + m11(m11), m12(m12), m13(m13), m14(m14), + m21(m21), m22(m22), m23(m23), m24(m24), + m31(m31), m32(m32), m33(m33), m34(m34), + m41(m41), m42(m42), m43(m43), m44(m44) + { + } + + FMATRIX4(const float* pSrc) { + for (int i = 0; i < 16; i++) data[i] = pSrc[i]; + } + + FMATRIX4(const MATRIX4& pSrc) { + for (int i = 0; i < 16; i++) data[i] = float(pSrc.data[i]); + } + + FMATRIX4(const FMATRIX4& pSrc) { + for (int i = 0; i < 16; i++) data[i] = float(pSrc.data[i]); + } + + FMATRIX4(const FMATRIX4* pSrc) { + for (int i = 0; i < 16; i++) data[i] = float(pSrc->data[i]); + } + +#ifdef __XM + FMATRIX4(const XMMATRIX m) + { + XMStoreFloat4x4((XMFLOAT4X4*)this, m); + } + + XMMATRIX XM() const + { + return XMLoadFloat4x4((XMFLOAT4X4*)this); + } + + void Load(const XMMATRIX m) + { + XMStoreFloat4x4((XMFLOAT4X4*)this, m); + } +#endif + + void Zero() + { + m21 = m31 = m41 = m12 = m32 = m42 = 0.0f; + m13 = m23 = m43 = m14 = m24 = m34 = 0.0f; + m11 = m22 = m33 = m44 = 0.0f; + } + + void Ident() + { + m21 = m31 = m41 = m12 = m32 = m42 = 0.0f; + m13 = m23 = m43 = m14 = m24 = m34 = 0.0f; + m11 = m22 = m33 = m44 = 1.0f; + } + + void _swap(float& a, float& b) { float c = a; a = b; b = c; } + + void Transpose() + { + _swap(m12, m21); _swap(m13, m31); + _swap(m14, m41); _swap(m23, m32); + _swap(m24, m42); _swap(m34, m43); + } + + float data[16]; + struct { FVECTOR4 _x, _y, _z, _p; }; + struct { float m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44; }; + } FMATRIX4; +} + + + // ============================================================================================================== + // Type casting + // + + using namespace oapi; + + inline VECTOR3 _V(const FVECTOR3& i) { return { double(i.x), double(i.y), double(i.z) }; } + inline VECTOR3 _V(const VECTOR4 & i) { return { double(i.x), double(i.y), double(i.z) }; } + inline VECTOR3 _V(const FVECTOR4 & i) { return { double(i.x), double(i.y), double(i.z) }; } + + inline VECTOR4 _V4(const FVECTOR4& i) { return { i.x, i.y, i.z, i.w }; } + inline VECTOR4 _V4(const VECTOR3& i, double w = 0.0) { return { i.x, i.y, i.z, w }; } + inline VECTOR4 _V4(const FVECTOR3& i, float w = 0.0f) { return { i.x, i.y, i.z, w }; } + + inline FVECTOR3 _F(const VECTOR3& i) { return FVECTOR3(float(i.x), float(i.y), float(i.z)); } + inline FVECTOR3 _F(const VECTOR4& i) { return FVECTOR3(float(i.x), float(i.y), float(i.z)); } + inline FVECTOR3 _F(const FVECTOR4& i) { return FVECTOR3(float(i.x), float(i.y), float(i.z)); } + inline FVECTOR3 _F(const VECTOR3* i) { return FVECTOR3(float(i->x), float(i->y), float(i->z)); } + + inline FVECTOR4 _F4(const VECTOR4& i) { return FVECTOR4(float(i.x), float(i.y), float(i.z), float(i.w)); } + inline FVECTOR4 _F4(const VECTOR3& i, double w = 0.0) { return FVECTOR4(float(i.x), float(i.y), float(i.z), float(w)); } + inline FVECTOR4 _F4(const FVECTOR3& i, float w = 0.0f) { return FVECTOR4(float(i.x), float(i.y), float(i.z), float(w)); } + + inline VECTOR4 _V4(double x, double y, double z, double w) { return { x, y, z, w }; } + inline VECTOR4 _V4(int x, int y, int z, int w) { return { double(x), double(y), double(z), double(w) }; } + inline FVECTOR4 _F4(float x, float y, float z, float w) { return FVECTOR4(x, y, z, w); } + inline FVECTOR4 _F4(int x, int y, int z, int w) { return FVECTOR4(float(x), float(y), float(z), float(w)); } + inline FVECTOR3 _F(float x, float y, float z) { return FVECTOR3(x, y, z); } + inline FVECTOR3 _F(int x, int y, int z) { return FVECTOR3(float(x), float(y), float(z)); } + inline FVECTOR2 _F2(float x, float y) { return FVECTOR2(x, y); } + + // ============================================================================================================== + // FVECTOR2 operators + // + inline FVECTOR2& operator*= (FVECTOR2&v, float f) { v.x *= f; v.y *= f; return v; } + inline FVECTOR2& operator/= (FVECTOR2&v, float f) { v.x /= f; v.y /= f; return v; } + inline FVECTOR2& operator+= (FVECTOR2&v, float f) { v.x += f; v.y += f; return v; } + inline FVECTOR2& operator-= (FVECTOR2&v, float f) { v.x -= f; v.y -= f; return v; } + + inline FVECTOR2& operator*= (FVECTOR2&v, const FVECTOR2& f) { v.x *= f.x; v.y *= f.y; return v; } + inline FVECTOR2& operator/= (FVECTOR2&v, const FVECTOR2& f) { v.x /= f.x; v.y /= f.y; return v; } + inline FVECTOR2& operator+= (FVECTOR2&v, const FVECTOR2& f) { v.x += f.x; v.y += f.y; return v; } + inline FVECTOR2& operator-= (FVECTOR2&v, const FVECTOR2& f) { v.x -= f.x; v.y -= f.y; return v; } + + inline FVECTOR2 operator* (const FVECTOR2&v, float f) { return _F2(v.x * f, v.y * f); } + inline FVECTOR2 operator/ (const FVECTOR2&v, float f) { return _F2(v.x / f, v.y / f); } + inline FVECTOR2 operator+ (const FVECTOR2&v, float f) { return _F2(v.x + f, v.y + f); } + inline FVECTOR2 operator- (const FVECTOR2&v, float f) { return _F2(v.x - f, v.y - f); } + + inline FVECTOR2 operator* (const FVECTOR2&v, const FVECTOR2& f) { return _F2(v.x * f.x, v.y * f.y); } + inline FVECTOR2 operator/ (const FVECTOR2&v, const FVECTOR2& f) { return _F2(v.x / f.x, v.y / f.y); } + inline FVECTOR2 operator+ (const FVECTOR2&v, const FVECTOR2& f) { return _F2(v.x + f.x, v.y + f.y); } + inline FVECTOR2 operator- (const FVECTOR2&v, const FVECTOR2& f) { return _F2(v.x - f.x, v.y - f.y); } + + inline FVECTOR2 operator- (const FVECTOR2&v) { return _F2(-v.x, -v.y); } + + + // ============================================================================================================== + // FVECTOR3 operators + // + inline FVECTOR3& operator*= (FVECTOR3&v, float f) { v.x *= f; v.y *= f; v.z *= f; return v; } + inline FVECTOR3& operator/= (FVECTOR3&v, float f) { v.x /= f; v.y /= f; v.z /= f; return v; } + inline FVECTOR3& operator+= (FVECTOR3&v, float f) { v.x += f; v.y += f; v.z += f; return v; } + inline FVECTOR3& operator-= (FVECTOR3&v, float f) { v.x -= f; v.y -= f; v.z -= f; return v; } + + inline FVECTOR3& operator*= (FVECTOR3&v, const FVECTOR3& f) { v.x *= f.x; v.y *= f.y; v.z *= f.z; return v; } + inline FVECTOR3& operator/= (FVECTOR3&v, const FVECTOR3& f) { v.x /= f.x; v.y /= f.y; v.z /= f.z; return v; } + inline FVECTOR3& operator+= (FVECTOR3&v, const FVECTOR3& f) { v.x += f.x; v.y += f.y; v.z += f.z; return v; } + inline FVECTOR3& operator-= (FVECTOR3&v, const FVECTOR3& f) { v.x -= f.x; v.y -= f.y; v.z -= f.z; return v; } + + inline FVECTOR3 operator* (const FVECTOR3&v, float f) { return _F(v.x * f, v.y * f, v.z * f); } + inline FVECTOR3 operator/ (const FVECTOR3&v, float f) { return _F(v.x / f, v.y / f, v.z / f); } + inline FVECTOR3 operator+ (const FVECTOR3&v, float f) { return _F(v.x + f, v.y + f, v.z + f); } + inline FVECTOR3 operator- (const FVECTOR3&v, float f) { return _F(v.x - f, v.y - f, v.z - f); } + + inline FVECTOR3 operator* (const FVECTOR3&v, const FVECTOR3& f) { return _F(v.x * f.x, v.y * f.y, v.z * f.z); } + inline FVECTOR3 operator/ (const FVECTOR3&v, const FVECTOR3& f) { return _F(v.x / f.x, v.y / f.y, v.z / f.z); } + inline FVECTOR3 operator+ (const FVECTOR3&v, const FVECTOR3& f) { return _F(v.x + f.x, v.y + f.y, v.z + f.z); } + inline FVECTOR3 operator- (const FVECTOR3&v, const FVECTOR3& f) { return _F(v.x - f.x, v.y - f.y, v.z - f.z); } + + inline FVECTOR3 operator- (const FVECTOR3&v) { return _F(-v.x, -v.y, -v.z); } + + + + // ============================================================================================================== + // FVECTOR4 operators + // + inline FVECTOR4& operator*= (FVECTOR4&v, float f) { v.x *= f; v.y *= f; v.z *= f; v.w *= f; return v; } + inline FVECTOR4& operator/= (FVECTOR4&v, float f) { v.x /= f; v.y /= f; v.z /= f; v.w /= f; return v; } + inline FVECTOR4& operator+= (FVECTOR4&v, float f) { v.x += f; v.y += f; v.z += f; v.w += f; return v; } + inline FVECTOR4& operator-= (FVECTOR4&v, float f) { v.x -= f; v.y -= f; v.z -= f; v.w -= f; return v; } + + inline FVECTOR4& operator*= (FVECTOR4&v, const FVECTOR4& f) { v.x *= f.x; v.y *= f.y; v.z *= f.z; v.w *= f.w; return v; } + inline FVECTOR4& operator/= (FVECTOR4&v, const FVECTOR4& f) { v.x /= f.x; v.y /= f.y; v.z /= f.z; v.w /= f.w; return v; } + inline FVECTOR4& operator+= (FVECTOR4&v, const FVECTOR4& f) { v.x += f.x; v.y += f.y; v.z += f.z; v.w += f.w; return v; } + inline FVECTOR4& operator-= (FVECTOR4&v, const FVECTOR4& f) { v.x -= f.x; v.y -= f.y; v.z -= f.z; v.w -= f.w; return v; } + + inline FVECTOR4 operator* (const FVECTOR4&v, float f) { return _F4(v.x * f, v.y * f, v.z * f, v.w * f); } + inline FVECTOR4 operator/ (const FVECTOR4&v, float f) { return _F4(v.x / f, v.y / f, v.z / f, v.w / f); } + inline FVECTOR4 operator+ (const FVECTOR4&v, float f) { return _F4(v.x + f, v.y + f, v.z + f, v.w + f); } + inline FVECTOR4 operator- (const FVECTOR4&v, float f) { return _F4(v.x - f, v.y - f, v.z - f, v.w - f); } + + inline FVECTOR4 operator* (const FVECTOR4&v, const FVECTOR4& f) { return _F4(v.x * f.x, v.y * f.y, v.z * f.z, v.w * f.w); } + inline FVECTOR4 operator/ (const FVECTOR4&v, const FVECTOR4& f) { return _F4(v.x / f.x, v.y / f.y, v.z / f.z, v.w / f.w); } + inline FVECTOR4 operator+ (const FVECTOR4&v, const FVECTOR4& f) { return _F4(v.x + f.x, v.y + f.y, v.z + f.z, v.w + f.w); } + inline FVECTOR4 operator- (const FVECTOR4&v, const FVECTOR4& f) { return _F4(v.x - f.x, v.y - f.y, v.z - f.z, v.w - f.w); } + + inline FVECTOR4 operator- (const FVECTOR4&v) { return _F4(-v.x, -v.y, -v.z, -v.w); } + +//namespace oapi +//{ + /** + * \brief Vector Matrix multiplication + */ + inline FVECTOR4 mul(const FVECTOR4& V, const FMATRIX4& M) + { + float x = V.x * M.m11 + V.y * M.m21 + V.z * M.m31 + V.w * M.m41; + float y = V.x * M.m12 + V.y * M.m22 + V.z * M.m32 + V.w * M.m42; + float z = V.x * M.m13 + V.y * M.m23 + V.z * M.m33 + V.w * M.m43; + float w = V.x * M.m14 + V.y * M.m24 + V.z * M.m34 + V.w * M.m44; + return FVECTOR4(x, y, z, w); + } + + inline FVECTOR4 mul(const FMATRIX4& M, const FVECTOR4& V) + { + float x = V.x * M.m11 + V.y * M.m12 + V.z * M.m13 + V.w * M.m14; + float y = V.x * M.m21 + V.y * M.m22 + V.z * M.m23 + V.w * M.m24; + float z = V.x * M.m31 + V.y * M.m32 + V.z * M.m33 + V.w * M.m34; + float w = V.x * M.m41 + V.y * M.m42 + V.z * M.m43 + V.w * M.m44; + return FVECTOR4(x, y, z, w); + } + + inline FMATRIX4 transp(const FMATRIX4& M) + { + return FMATRIX4(M.m11, M.m21, M.m31, M.m41, + M.m12, M.m22, M.m32, M.m42, + M.m13, M.m23, M.m33, M.m43, + M.m14, M.m24, M.m34, M.m44 + ); + } + + inline FVECTOR3 crossp(const FVECTOR3& a, const FVECTOR3& b) + { + return FVECTOR3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); + } + + inline float saturate(float x) + { + //sadly std::clamp produces garbage assembly on both gcc and MSVC + //this version makes MSVC produce good code + x = (x < 0.0f) ? 0.0f : x; + x = (x > 1.0f) ? 1.0f : x; + return x; + } + + inline FVECTOR2 rcp(const FVECTOR2& v) { return _F2(1.0f / v.x, 1.0f / v.y); } + inline FVECTOR3 rcp(const FVECTOR3& v) { return _F(1.0f / v.x, 1.0f / v.y, 1.0f / v.z); } + inline FVECTOR4 rcp(const FVECTOR4& v) { return _F4(1.0f / v.x, 1.0f / v.y, 1.0f / v.z, 1.0f/ v.w); } + + inline float dotp(const FVECTOR2& v, const FVECTOR2& w) { return v.x * w.x + v.y * w.y; } + inline float dotp(const FVECTOR3& v, const FVECTOR3& w) { return v.x * w.x + v.y * w.y + v.z * w.z; } + inline float dotp(const FVECTOR4& v, const FVECTOR4& w) { return v.x * w.x + v.y * w.y + v.z * w.z + v.w * w.w; } + + inline FVECTOR2 abs(const FVECTOR2& v) { return _F2(fabs(v.x), fabs(v.y)); } + inline FVECTOR3 abs(const FVECTOR3& v) { return _F(fabs(v.x), fabs(v.y), fabs(v.z)); } + inline FVECTOR4 abs(const FVECTOR4& v) { return _F4(fabs(v.x), fabs(v.y), fabs(v.z), fabs(v.w)); } + + inline FVECTOR2 saturate(const FVECTOR2& v) { return _F2(saturate(v.x), saturate(v.y)); } + inline FVECTOR3 saturate(const FVECTOR3& v) { return _F(saturate(v.x), saturate(v.y), saturate(v.z)); } + inline FVECTOR4 saturate(const FVECTOR4& v) { return _F4(saturate(v.x), saturate(v.y), saturate(v.z), saturate(v.w)); } + + inline FVECTOR2 pow(const FVECTOR2& v, float p) { return _F2(pow(v.x, p), pow(v.y, p)); } + inline FVECTOR3 pow(const FVECTOR3& v, float p) { return _F(pow(v.x, p), pow(v.y, p), pow(v.z, p)); } + inline FVECTOR4 pow(const FVECTOR4& v, float p) { return _F4(pow(v.x, p), pow(v.y, p), pow(v.z, p), pow(v.w, p)); } + + inline FVECTOR2 pow(const FVECTOR2& v, const FVECTOR2& w) { return _F2(pow(v.x, w.x), pow(v.y, w.y)); } + inline FVECTOR3 pow(const FVECTOR3& v, const FVECTOR3& w) { return _F(pow(v.x, w.x), pow(v.y, w.y), pow(v.z, w.z)); } + inline FVECTOR4 pow(const FVECTOR4& v, const FVECTOR4& w) { return _F4(pow(v.x, w.x), pow(v.y, w.y), pow(v.z, w.z), pow(v.w, w.w)); } + + inline FVECTOR2 sqrt(const FVECTOR2& v) { return _F2(sqrt(v.x), sqrt(v.y)); } + inline FVECTOR3 sqrt(const FVECTOR3& v) { return _F(sqrt(v.x), sqrt(v.y), sqrt(v.z)); } + inline FVECTOR4 sqrt(const FVECTOR4& v) { return _F4(sqrt(v.x), sqrt(v.y), sqrt(v.z), sqrt(v.w)); } + + inline FVECTOR2 exp(const FVECTOR2& v) { return _F2(exp(v.x), exp(v.y)); } + inline FVECTOR3 exp(const FVECTOR3& v) { return _F(exp(v.x), exp(v.y), exp(v.z)); } + inline FVECTOR4 exp(const FVECTOR4& v) { return _F4(exp(v.x), exp(v.y), exp(v.z), exp(v.w)); } + + inline FVECTOR2 min(const FVECTOR2& v, const FVECTOR2& w) { return _F2(std::min(v.x, w.x), std::min(v.y, w.y)); } + inline FVECTOR3 min(const FVECTOR3& v, const FVECTOR3& w) { return _F(std::min(v.x, w.x), std::min(v.y, w.y), std::min(v.z, w.z)); } + inline FVECTOR4 min(const FVECTOR4& v, const FVECTOR4& w) { return _F4(std::min(v.x, w.x), std::min(v.y, w.y), std::min(v.z, w.z), std::min(v.w, w.w)); } + + inline FVECTOR2 max(const FVECTOR2& v, const FVECTOR2& w) { return _F2(std::max(v.x, w.x), std::max(v.y, w.y)); } + inline FVECTOR3 max(const FVECTOR3& v, const FVECTOR3& w) { return _F(std::max(v.x, w.x), std::max(v.y, w.y), std::max(v.z, w.z)); } + inline FVECTOR4 max(const FVECTOR4& v, const FVECTOR4& w) { return _F4(std::max(v.x, w.x), std::max(v.y, w.y), std::max(v.z, w.z), std::max(v.w, w.w)); } + + template inline constexpr float ilen(const T& v) { return 1.0f / sqrt(dotp(v, v)); } + template inline constexpr T unit(const T& v) { return v * ilen(v); } + template inline constexpr void normalize(T& v) { v *= ilen(v); } + template inline constexpr void normalise(T& v) { v *= ilen(v); } + template inline constexpr float length(const T& v) { return sqrt(dotp(v,v)); } + template inline constexpr T lerp(const T& a, const T& b, float x) { return a + (b - a) * x; } + template inline constexpr T lerp(const T& a, const T& b, double x) { return a + (b - a) * x; } + template inline constexpr T sqr(T a) { return a * a; } + template inline constexpr T hermite(T a) { return a * a * (T(3) - T(2) * a); } + template inline constexpr T herp(T a, T b, float x) { return lerp(a, b, (float)hermite(x)); } + template inline constexpr T herp(T a, T b, double x) { return lerp(a, b, (double)hermite(x)); } + +//} //namespace + + +static const FMATRIX4 FMATRIX_Identity = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f +}; + +static const oapi::FVECTOR4 F4_Zero = { 0.0f, 0.0f, 0.0f, 0.0f }; +static const oapi::FVECTOR4 F4_One = { 1.0f, 1.0f, 1.0f, 1.0f }; + +#endif diff --git a/Orbitersdk/include/OrbiterAPI.h b/Orbitersdk/include/OrbiterAPI.h index d2f24c2bd..6713cd883 100644 --- a/Orbitersdk/include/OrbiterAPI.h +++ b/Orbitersdk/include/OrbiterAPI.h @@ -28,6 +28,7 @@ #include #include #include +#include #if defined(_MSC_VER) && (_MSC_VER < 1920 ) // Microsoft Visual Studio Version 2017 and lower #include @@ -128,6 +129,9 @@ namespace oapi { class Pen; class Brush; union FVECTOR4; + union FVECTOR3; + union FVECTOR2; + union FMATRIX4; } // ====================================================================== @@ -330,6 +334,18 @@ typedef struct { float TexMixEx[MAXTEX]; ///< texture mix values } MESHGROUPEX; + +typedef struct { + union { + VECTOR3 pt[4]; + struct { + VECTOR3 cnt; + float rad; + }; + }; + int id, mode; +} VCClickZone; + /** * \ingroup defines * \defgroup surfacecaps Surface and texture attributes @@ -353,6 +369,7 @@ typedef struct { #define OAPISURFACE_RENDER3D 0x0400 ///< Create a surface that can act as a target for rendering a 3D scene #define OAPISURFACE_ANTIALIAS 0x0800 ///< Create a surface with anti-aliasing the level will depend on launchpad settings. #define OAPISURFACE_SHARED 0x1000 ///< Create a shared resource +#define OAPISURFACE_DIFFUSE_ONLY 0x2000 ///< Load only a regular diffuse texture and ignore additional maps if exists //@} /** @@ -475,10 +492,22 @@ namespace oapi { IMAGE_PNG = 1, IMAGE_JPG = 2, IMAGE_TIF = 3, - IMAGE_DDS = 4 ///< D3D9+ only + IMAGE_DDS = 4 ///< vk+ only }; } +enum class ScnChgEvent { + Invalid = 0, ///< Unspecified event + Added = 1, ///< Vessel dynamically added in scenario + Deleted = 2, ///< Vessel dynamically deleted from scenario + Docked = 3, ///< Vessels docked + UnDocked = 4, ///< Vessels undocked + Attached = 5, ///< Vessels attached + Detached = 6, ///< Vessels detached + VisualConfig = 7, ///< Vessel's rendering configuration has changed + VisualCreated = 8, ///< Vessel visual created + VisualDeleted = 9 ///< Vessel visual deleted +}; /** * \brief Kepler orbital elements @@ -2052,6 +2081,9 @@ typedef union { //@} #define MESHPROPERTY_MODULATEMATALPHA 1 +#define MESHPROPERTY_FLAGS 2 +#define MESHFLAG_VC 0x2 ///< This mesh is a virtual cockpit +#define MESHFLAG_SHADOW_VC 0x4 ///< This mesh casts shadows in virual cockpit // =========================================================================== /// \ingroup defines @@ -4576,6 +4608,7 @@ OAPIFUNC DWORD oapiGetMeshFlags (MESHHANDLE hMesh); * \note See 3DModel document for details of the mesh format. */ OAPIFUNC DWORD oapiMeshGroupCount (MESHHANDLE hMesh); +OAPIFUNC void oapiMeshGroupLabel(MESHHANDLE hMesh, DWORD grp, char* label, DWORD bufsize); /** * \brief Returns a pointer to the group specification of a mesh group. @@ -4847,12 +4880,14 @@ OAPIFUNC int oapiSetMaterialEx(DEVMESHHANDLE hMesh, DWORD matidx, MatProp prp, c * \return \e true if the property tag was recognised and the request could be executed, \e false otherwise. * \note Currently only a single mesh property is recognised, but this may be * extended in future versions: - * - \c MESHPROPERTY_MODULATEMATALPHA \n \n + * - \c MESHPROPERTY_MODULATEMATALPHA + * - \c MESHPROPERTY_FLAGS\n \n * if value==0 (default) disable material alpha information in textured mesh groups (only use texture alpha channel).\n * if value<>0 modulate (mix) material alpha values with texture alpha maps. * \sa oapiSetMeshProperty(DEVMESHHANDLE,DWORD,DWORD) */ OAPIFUNC bool oapiSetMeshProperty (MESHHANDLE hMesh, DWORD property, DWORD value); +OAPIFUNC bool oapiGetMeshProperty (MESHHANDLE hMesh, DWORD property, DWORD *value); /** * \brief Set custom properties for a device-specific mesh. @@ -4862,7 +4897,8 @@ OAPIFUNC bool oapiSetMeshProperty (MESHHANDLE hMesh, DWORD property, DWORD value * \return \e true if the property tag was recognised and the request could be executed, \e false otherwise. * \note Currently only a single mesh property is recognised, but this may be * extended in future versions: - * - \c MESHPROPERTY_MODULATEMATALPHA \n \n + * - \c MESHPROPERTY_MODULATEMATALPHA + * - \c MESHPROPERTY_FLAGS\n \n * if value==0 (default) disable material alpha information in textured mesh groups (only use texture alpha channel).\n * if value<>0 modulate (mix) material alpha values with texture alpha maps. * \sa oapiSetMeshProperty(MESHHANDLE,DWORD,DWORD) @@ -5545,6 +5581,17 @@ OAPIFUNC SURFHANDLE oapiLoadSurfaceEx(const char* fname, DWORD attrib, bool bPat */ OAPIFUNC bool oapiSaveSurface(const char* fname, SURFHANDLE hSrf, oapi::ImageFileFormat fmt, float quality = 0.7f); + /** + * \brief Load a diffuse/albedo texture and additional texture maps from a separate sources + * \param diff difuse/albedo texture file name. If NULL, hOld must be specified + * \param maps base name for texture maps, if NULL only diffuse is loaded. + * \param bPath if 'true' then 'fname' must contain absolute path to a file. If 'false' a normal Orbiter texture search path is used. + * \param hOld handle to an existing diffuse texture to receive additional maps, or NULL. + * \param bAll if true, auto load all existing additional maps. If false, load and add a simgle map. + * \return Surface handle for the loaded texture, or NULL in a case of an error. + * \note A surface must always have a base diffuse texture. + */ +OAPIFUNC SURFHANDLE oapiLoadAdditionalTextureMaps(const char* diff, const char* maps = NULL, bool bPath = false, SURFHANDLE hOld = NULL, bool bAll = true); /** * \brief Create a surface from a bitmap. Bitmap surfaces are typically used for blitting @@ -5878,6 +5925,8 @@ OAPIFUNC void oapiVCSetAreaClickmode_Spherical (int id, const VECTOR3 &cnt */ OAPIFUNC void oapiVCSetAreaClickmode_Quadrilateral (int id, const VECTOR3 &p1, const VECTOR3 &p2, const VECTOR3 &p3, const VECTOR3 &p4); +OAPIFUNC void oapiVCGetAreaClickZones(std::list* p_List); + /** * \brief Defines the neighbouring virtual cockpit camera positions in relation to the current * position. The user can switch to neighbour positions with Ctrl-Arrow keys. @@ -6267,6 +6316,7 @@ OAPIFUNC void oapiWriteLine (FILEHANDLE file, char *line); * \sa oapiWriteLogV */ OAPIFUNC void oapiWriteLog (char *line); +OAPIFUNC void oapiWriteLogVerbose(char* line); /** * \brief Writes a formatted string with variable number of arguments to orbiter.log. @@ -6752,6 +6802,15 @@ OAPIFUNC void oapiTriggerPanelRedrawArea (int panel_id, int area_id); OAPIFUNC void oapiTriggerRedrawArea (int panel_id, int vc_id, int area_id); //@} + +OAPIFUNC void oapiMatrixIdentity(oapi::FMATRIX4 *x); +OAPIFUNC oapi::FMATRIX4 mul(const oapi::FMATRIX4* a, const oapi::FMATRIX4* b); +OAPIFUNC oapi::FMATRIX4* oapiMatrixMultiply(oapi::FMATRIX4* o, const oapi::FMATRIX4* a, const oapi::FMATRIX4* b); +OAPIFUNC oapi::FMATRIX4* oapiMatrixInverse(oapi::FMATRIX4* o, float* d, const oapi::FMATRIX4* a); +OAPIFUNC oapi::FMATRIX4* oapiMatrixRotationAxis(oapi::FMATRIX4* o, oapi::FVECTOR3* pAxis, float rad); +OAPIFUNC oapi::FVECTOR3 oapiTransformCoord(const oapi::FVECTOR3* V, const oapi::FMATRIX4* M); +OAPIFUNC oapi::FVECTOR3 oapiTransformNormal(const oapi::FVECTOR3* V, const oapi::FMATRIX4* M); + //@} -- End of Orbiter API interface methods -- @@ -7043,10 +7102,7 @@ OAPIFUNC void oapiTriggerRedrawArea (int panel_id, int vc_id, int area_id); * \param z z-component * \return vector defined as (x,y,z) */ -inline VECTOR3 _V(double x, double y, double z) -{ - VECTOR3 vec = {x,y,z}; return vec; -} +inline VECTOR3 _V(double x, double y, double z) { return { x, y, z }; } /** * \ingroup vec @@ -7588,4 +7644,4 @@ void calldummy () { dummy(); } DLLCLBK char *ModuleDate () { return (char*)__DATE__; } #endif -#endif // !__ORBITERAPI_H \ No newline at end of file +#endif // !__ORBITERAPI_H diff --git a/Orbitersdk/include/VesselAPI.h b/Orbitersdk/include/VesselAPI.h index 521209e82..8a3964117 100644 --- a/Orbitersdk/include/VesselAPI.h +++ b/Orbitersdk/include/VesselAPI.h @@ -26,6 +26,8 @@ #define FRAME_ECL 0 #define FRAME_EQU 1 +#include + class Vessel; // Orbiter internal vessel class class SuperVessel; // Orbiter internal supervessel class @@ -40,6 +42,16 @@ typedef struct { double mu_lng; ///< longitudinal friction coefficient (only used for first 3 points) } TOUCHDOWNVTX; +enum class VisualProp { + BAKED_LIGHT, ///< baked light level + AMBIENT, ///< ambient light level + EXT_PROBE_POS, ///< Exterior probe position + CREATE_VC_PROBE, ///< Virtual cockpit probe position + DA_CURVE, + DA_BOUNCH, + DA_FORCE +}; + // ====================================================================== /** * \brief Base class for objects of vessel type (spacecraft and similar) @@ -3833,6 +3845,15 @@ class OAPIFUNC VESSEL { */ void SetMeshVisibilityMode (UINT idx, WORD mode) const; + /** + * \brief Set vessel visual propery + * \param vis visual object handle + * \param prp property id + * \param idx index of property + * \param val value to be set + */ + void SetVisualProperty(VISHANDLE vis, VisualProp prp, int idx, const type_info &t, const void *val); + /** * \brief Affine transformation of a mesh group. * \param vis vessel visual handle @@ -4663,6 +4684,12 @@ class OAPIFUNC VESSEL { */ ATTACHMENTHANDLE GetAttachmentHandle (bool toparent, DWORD i) const; + /** + * \brief Return the handle of a root object in a attachment hierarchy. (i.e. vessel that has not parent) + * \return Vessel handle. Returns vessel's own handle "this" if no parent exists. + */ + OBJHANDLE GetAttachmentRoot() const; + /** * \brief Attach a child vessel to an attachment point. * \param child handle of child vessel to be attached. diff --git a/Src/Module/LuaScript/LuaInterpreter/CMakeLists.txt b/Src/Module/LuaScript/LuaInterpreter/CMakeLists.txt index 8bdcb269a..ba21aa254 100644 --- a/Src/Module/LuaScript/LuaInterpreter/CMakeLists.txt +++ b/Src/Module/LuaScript/LuaInterpreter/CMakeLists.txt @@ -29,7 +29,7 @@ add_dependencies(LuaInterpreter ) if(ORBITER_BUILD_D3D9CLIENT) - add_dependencies(LuaInterpreter D3D9Client D3D9Client_Interface) + add_dependencies(LuaInterpreter VulkanClient D3D9Client_Interface) endif() if(ORBITER_BUILD_XRSOUND) diff --git a/Src/Orbiter/CMakeLists.txt b/Src/Orbiter/CMakeLists.txt index 597b1e4e5..7f2380cbc 100644 --- a/Src/Orbiter/CMakeLists.txt +++ b/Src/Orbiter/CMakeLists.txt @@ -105,6 +105,7 @@ set(common_src MFDAPI.cpp ModuleAPI.cpp OrbiterAPI.cpp + MathAPI.cpp # Graphics utils D3d7util.cpp D3dmath.cpp @@ -125,6 +126,7 @@ set(Orbiter_includes ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/Orbitersdk/include ${CMAKE_SOURCE_DIR}/OVP + ${EXTERN_DIR}/dxmath/Inc ${CMAKE_CURRENT_BINARY_DIR} ${HTMLHELP_INCLUDE_DIR} ) diff --git a/Src/Orbiter/CelSphereAPI.cpp b/Src/Orbiter/CelSphereAPI.cpp index c5b42c45e..29cccdd4a 100644 --- a/Src/Orbiter/CelSphereAPI.cpp +++ b/Src/Orbiter/CelSphereAPI.cpp @@ -707,4 +707,4 @@ const MESHHANDLE oapi::CelestialSphere::GridLabelMesh() m_meshGridLabel = (MESHHANDLE)mesh; } return m_meshGridLabel; -} \ No newline at end of file +} diff --git a/Src/Orbiter/MathAPI.cpp b/Src/Orbiter/MathAPI.cpp new file mode 100644 index 000000000..e97e1ccd9 --- /dev/null +++ b/Src/Orbiter/MathAPI.cpp @@ -0,0 +1,76 @@ +// ================================================================================================================================= +// The MIT Lisence: +// +// Copyright (C) 2025 Jarmo Nikkanen +// +// 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. +// ================================================================================================================================= + + +#define STRICT 1 +#define OAPI_IMPLEMENTATION + +#include "MathAPI.h" +/* +using namespace oapi; + +DLLEXPORT void oapiMatrixIdentity(FMATRIX4 *x) +{ + x->m21 = x->m31 = x->m41 = x->m12 = x->m32 = x->m42 = 0.0f; + x->m13 = x->m23 = x->m43 = x->m14 = x->m24 = x->m34 = 0.0f; + x->m11 = x->m22 = x->m33 = x->m44 = 1.0f; +} + +DLLEXPORT FMATRIX4 mul(const FMATRIX4* a, const FMATRIX4* b) +{ + return FMATRIX4(XMMatrixMultiply(a->XM(), b->XM())); +} + +DLLEXPORT FMATRIX4* oapiMatrixMultiply(FMATRIX4* o, const FMATRIX4* a, const FMATRIX4* b) +{ + o->Load(XMMatrixMultiply(a->XM(), b->XM())); + return o; +} + +DLLEXPORT FMATRIX4* oapiMatrixInverse(FMATRIX4* o, float* d, const FMATRIX4* a) +{ + XMVECTOR det; + o->Load(XMMatrixInverse(&det, a->XM())); + if (d != nullptr) *d = det.m128_f32[0]; + return o; +} + +DLLEXPORT FMATRIX4* oapiMatrixRotationAxis(FMATRIX4* o, FVECTOR3* pAxis, float rad) +{ + o->Load(XMMatrixRotationAxis(pAxis->XM(), rad)); + return o; +} + +DLLEXPORT FVECTOR3 oapiTransformCoord(const FVECTOR3& V, const FMATRIX4& M) +{ + float x = V.x * M.m11 + V.y * M.m21 + V.z * M.m31 + M.m41; + float y = V.x * M.m12 + V.y * M.m22 + V.z * M.m32 + M.m42; + float z = V.x * M.m13 + V.y * M.m23 + V.z * M.m33 + M.m43; + float w = V.x * M.m14 + V.y * M.m24 + V.z * M.m34 + M.m44; + w = 1.0f / w; + return FVECTOR3(x * w, y * w, z * w); +} + + +DLLEXPORT FVECTOR3 oapiTransformNormal(const FVECTOR3& V, const FMATRIX4& M) +{ + float x = V.x * M.m11 + V.y * M.m21 + V.z * M.m31; + float y = V.x * M.m12 + V.y * M.m22 + V.z * M.m32; + float z = V.x * M.m13 + V.y * M.m23 + V.z * M.m33; + return FVECTOR3(x, y, z); +}*/ diff --git a/Src/Orbiter/Mesh.cpp b/Src/Orbiter/Mesh.cpp index 8a4ef6f6b..4be627ba0 100644 --- a/Src/Orbiter/Mesh.cpp +++ b/Src/Orbiter/Mesh.cpp @@ -57,6 +57,7 @@ Mesh::Mesh () { name = NULL; nGrp = nMtrl = nTex = 0; + Flags = 0; GrpVis = 0; GrpSetup = false; bModulateMatAlpha = false; @@ -66,6 +67,7 @@ Mesh::Mesh (NTVERTEX *vtx, DWORD nvtx, WORD *idx, DWORD nidx, DWORD matidx, DWOR { name = NULL; nGrp = nMtrl = nTex = 0; + Flags = 0; GrpVis = 0; GrpSetup = false; AddGroup (vtx, nvtx, idx, nidx, matidx, texidx); @@ -121,6 +123,7 @@ void Mesh::Set (const Mesh &mesh) } SetName(mesh.GetName()); bModulateMatAlpha = mesh.bModulateMatAlpha; + Flags = mesh.Flags; } Mesh::~Mesh () @@ -176,6 +179,16 @@ void Mesh::SetupGroup (DWORD grp) GrpRad[grp] = (FLOAT)sqrt (d2max); } +void Mesh::AddLabel(DWORD grp, const char* label) +{ + GrpLabels[grp] = std::string(label); +} + +std::string Mesh::GetLabel(DWORD grp) +{ + return GrpLabels.find(grp) != GrpLabels.end() ? GrpLabels[grp] : std::string(""); +} + int Mesh::AddGroup (NTVERTEX *vtx, DWORD nvtx, WORD *idx, DWORD nidx, DWORD mtrl_idx, DWORD tex_idx, WORD zbias, DWORD flag, bool deepcopy) { @@ -834,6 +847,10 @@ istream &operator>> (istream &is, Mesh &mesh) break; } else if (!_strnicmp (cbuf, "STATICMESH", 10)) { staticmesh = true; + } else if (!_strnicmp(cbuf, "MESHFLAGS", 9)) { + DWORD mf = 0; + if (sscanf(cbuf + 9, "%lx", &mf) != 1) return is; + mesh.Flags = mf | 0x1; // Flag entry is defined } } @@ -853,7 +870,9 @@ istream &operator>> (istream &is, Mesh &mesh) for (;;) { if (!is.getline (cbuf, 256)) { term = true; break; } - if (!_strnicmp (cbuf, "MATERIAL", 8)) { // read material index + if (cbuf[0] == ';') { + // Do nothing + } else if (!_strnicmp (cbuf, "MATERIAL", 8)) { // read material index sscanf (cbuf+8, "%d", &mtrl_idx); mtrl_idx--; } else if (!_strnicmp (cbuf, "TEXTURE", 7)) { // read texture index @@ -873,7 +892,9 @@ istream &operator>> (istream &is, Mesh &mesh) } else if (!_strnicmp (cbuf, "FLIP", 4)) { flipidx = true; } else if (!_strnicmp (cbuf, "LABEL", 5)) { - // ignore group labels here + char label[64]; + sscanf(cbuf + 5, "%63s", label); + mesh.AddLabel(g, label); } else if (!_strnicmp (cbuf, "STATIC", 6)) { flag |= 0x04; } else if (!_strnicmp (cbuf, "DYNAMIC", 7)) { diff --git a/Src/Orbiter/Mesh.h b/Src/Orbiter/Mesh.h index 45f6a92f8..f27420ff0 100644 --- a/Src/Orbiter/Mesh.h +++ b/Src/Orbiter/Mesh.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "OrbiterAPI.h" typedef char Str256[256]; @@ -104,6 +106,12 @@ class Mesh { inline DWORD GetGroupUsrFlag (DWORD grp) const { return (grp < nGrp ? Grp[grp].UsrFlag : 0); } // return the user-defined flag for group grp + void AddLabel(DWORD grp, const char* label); + // Add label to a mesh group + + std::string GetLabel(DWORD grp); + // return group label + int AddGroup (NTVERTEX *vtx, DWORD nvtx, WORD *idx, DWORD nidx, DWORD mtrl_idx = SPEC_INHERIT, DWORD tex_idx = SPEC_INHERIT, WORD zbias = 0, DWORD flag = 0, bool deepcopy = false); @@ -203,6 +211,7 @@ class Mesh { static void GlobalEnableSpecular (bool enable); void EnableMatAlpha (bool enable); + bool EnableMatAlpha() { return bModulateMatAlpha; } friend std::istream &operator>> (std::istream &is, Mesh &mesh); // read mesh from file @@ -215,6 +224,7 @@ class Mesh { // Release textures acquired by the mesh private: + std::map GrpLabels; DWORD nGrp; // number of groups GroupSpec *Grp; // list of group specs diff --git a/Src/Orbiter/Orbiter.cpp b/Src/Orbiter/Orbiter.cpp index 966aab155..e3df8fb90 100644 --- a/Src/Orbiter/Orbiter.cpp +++ b/Src/Orbiter/Orbiter.cpp @@ -1230,8 +1230,11 @@ void Orbiter::InsertVessel (Vessel *vessel) for (auto it = m_Plugin.begin(); it != m_Plugin.end(); it++) it->pModule->clbkNewVessel((OBJHANDLE)vessel); - if (gclient) + if (gclient) { gclient->clbkNewVessel((OBJHANDLE)vessel); + gclient->clbkScenarioChanged((OBJHANDLE)vessel, ScnChgEvent::Added); + } + if (pDlgMgr) pDlgMgr->BroadcastMessage (MSG_CREATEVESSEL, vessel); //if (gclient) gclient->clbkDialogBroadcast (MSG_CREATEVESSEL, vessel); @@ -1284,8 +1287,11 @@ bool Orbiter::KillVessels () for (auto it = m_Plugin.begin(); it != m_Plugin.end(); it++) it->pModule->clbkDeleteVessel((OBJHANDLE)vessel); - if (gclient) + if (gclient) { gclient->clbkDeleteVessel((OBJHANDLE)vessel); + gclient->clbkScenarioChanged((OBJHANDLE)vessel, ScnChgEvent::Deleted); + } + // broadcast vessel destruction to all vessels g_psys->BroadcastVessel (MSG_KILLVESSEL, vessel); // broadcast vessel destruction to all MFDs diff --git a/Src/Orbiter/OrbiterAPI.cpp b/Src/Orbiter/OrbiterAPI.cpp index 78464c29e..9749a4d99 100644 --- a/Src/Orbiter/OrbiterAPI.cpp +++ b/Src/Orbiter/OrbiterAPI.cpp @@ -1452,6 +1452,13 @@ DLLEXPORT bool oapiSaveSurface(const char* fname, SURFHANDLE hSrf, oapi::ImageFi return false; } +DLLEXPORT SURFHANDLE oapiLoadAdditionalTextureMaps(const char* diff, const char* maps, bool bPath, SURFHANDLE hOld, bool bAll) +{ + oapi::GraphicsClient* gc = g_pOrbiter->GetGraphicsClient(); + if (gc) return gc->clbkLoadMaps(diff, maps, bPath, hOld, bAll); + else return NULL; +} + DLLEXPORT void oapiReleaseTexture (SURFHANDLE hTex) { oapi::GraphicsClient *gc = g_pOrbiter->GetGraphicsClient(); @@ -1496,6 +1503,12 @@ DLLEXPORT DWORD oapiGetMeshFlags (MESHHANDLE hMesh) return ((Mesh*)hMesh)->GetFlags(); } +DLLEXPORT void oapiMeshGroupLabel(MESHHANDLE hMesh, DWORD grp, char* label, DWORD bufsize) +{ + Mesh* pMesh = (Mesh*)hMesh; + strncpy(label, pMesh->GetLabel(grp).c_str(), bufsize); +} + DLLEXPORT MESHGROUP *oapiMeshGroup (MESHHANDLE hMesh, DWORD idx) { if (!hMesh) { @@ -1601,6 +1614,23 @@ DLLEXPORT bool oapiSetMeshProperty (MESHHANDLE hMesh, DWORD property, DWORD valu case MESHPROPERTY_MODULATEMATALPHA: mesh->EnableMatAlpha (value != 0); return true; + case MESHPROPERTY_FLAGS: + mesh->SetFlags(value); + return true; + } + return false; +} + +DLLEXPORT bool oapiGetMeshProperty(MESHHANDLE hMesh, DWORD property, DWORD *value) +{ + Mesh* mesh = (Mesh*)hMesh; + switch (property) { + case MESHPROPERTY_MODULATEMATALPHA: + *value = mesh->EnableMatAlpha() ? 1 : 0; + return true; + case MESHPROPERTY_FLAGS: + *value = mesh->GetFlags(); + return true; } return false; } @@ -1876,6 +1906,11 @@ DLLEXPORT void oapiVCSetAreaClickmode_Quadrilateral (int id, const VECTOR3 &p1, g_pane->SetVCAreaClickmode_Quadrilateral (id, Vector(p1.x, p1.y, p1.z), Vector(p2.x,p2.y,p2.z), Vector(p3.x,p3.y,p3.z), Vector(p4.x,p4.y,p4.z)); } +DLLEXPORT void oapiVCGetAreaClickZones(std::list* p_List) +{ + g_pane->GetVCAreaClickZones(p_List); +} + DLLEXPORT oapi::Sketchpad *oapiGetSketchpad (SURFHANDLE surf) { oapi::GraphicsClient *gc = g_pOrbiter->GetGraphicsClient(); @@ -2310,6 +2345,11 @@ DLLEXPORT void oapiWriteLog (char *line) LOGOUT (line); } +DLLEXPORT void oapiWriteLogVerbose(char* line) +{ + LogOutFine(line); +} + DLLEXPORT void oapiExitOrbiter(int code) { exit(code); @@ -2627,3 +2667,65 @@ DLLEXPORT void sscan_state (char *str, AnimState &s) s.action = (AnimState::Action)(a+1); s.pos = p; } + +#include "MathAPI.h" + +using namespace oapi; + +DLLEXPORT void oapiMatrixIdentity(FMATRIX4 *x) +{ + x->m21 = x->m31 = x->m41 = x->m12 = x->m32 = x->m42 = 0.0f; + x->m13 = x->m23 = x->m43 = x->m14 = x->m24 = x->m34 = 0.0f; + x->m11 = x->m22 = x->m33 = x->m44 = 1.0f; +} + +DLLEXPORT FMATRIX4 mul(const FMATRIX4* a, const FMATRIX4* b) +{ + return FMATRIX4(XMMatrixMultiply(a->XM(), b->XM())); +} + +DLLEXPORT FMATRIX4* oapiMatrixMultiply(FMATRIX4* o, const FMATRIX4* a, const FMATRIX4* b) +{ + o->Load(XMMatrixMultiply(a->XM(), b->XM())); + return o; +} + +DLLEXPORT FMATRIX4* oapiMatrixInverse(FMATRIX4* o, float* d, const FMATRIX4* a) +{ + XMVECTOR det; + o->Load(XMMatrixInverse(&det, a->XM())); + if (d != nullptr) *d = det.m128_f32[0]; + return o; +} + +DLLEXPORT FMATRIX4* oapiMatrixRotationAxis(FMATRIX4* o, FVECTOR3* pAxis, float rad) +{ + o->Load(XMMatrixRotationAxis(pAxis->XM(), rad)); + return o; +} + +/** +* \brief Transform a position by matrix +*/ +DLLEXPORT FVECTOR3 oapiTransformCoord(const FVECTOR3* V, const FMATRIX4* M) +{ + float x = V->x * M->m11 + V->y * M->m21 + V->z * M->m31 + M->m41; + float y = V->x * M->m12 + V->y * M->m22 + V->z * M->m32 + M->m42; + float z = V->x * M->m13 + V->y * M->m23 + V->z * M->m33 + M->m43; + float w = V->x * M->m14 + V->y * M->m24 + V->z * M->m34 + M->m44; + w = 1.0f / w; + return FVECTOR3(x * w, y * w, z * w); +} + + +/** +* \brief Transform a normal or direction by matrix +*/ +DLLEXPORT FVECTOR3 oapiTransformNormal(const FVECTOR3* V, const FMATRIX4* M) +{ + float x = V->x * M->m11 + V->y * M->m21 + V->z * M->m31; + float y = V->x * M->m12 + V->y * M->m22 + V->z * M->m32; + float z = V->x * M->m13 + V->y * M->m23 + V->z * M->m33; + return FVECTOR3(x, y, z); +} + diff --git a/Src/Orbiter/Pane.cpp b/Src/Orbiter/Pane.cpp index 1208ddc84..43fea4a04 100644 --- a/Src/Orbiter/Pane.cpp +++ b/Src/Orbiter/Pane.cpp @@ -1171,6 +1171,11 @@ void Pane::SetVCAreaClickmode_Quadrilateral (int aid, const Vector &p1, const Ve } } +void Pane::GetVCAreaClickZones(std::list* p_List) +{ + if (vcockpit) vcockpit->GetClickZones(p_List); +} + Instrument::Spec Pane::GetVCMFDSpec () { Instrument::Spec spec; diff --git a/Src/Orbiter/Pane.h b/Src/Orbiter/Pane.h index 6bc1ded42..a024e8ef9 100644 --- a/Src/Orbiter/Pane.h +++ b/Src/Orbiter/Pane.h @@ -234,6 +234,7 @@ class Pane { void SetVCAreaClickmode_Quadrilateral (int id, const Vector &p1, const Vector &p2, const Vector &p3, const Vector &p4); void TriggerVCRedrawArea (int vcid, int area_id); void TriggerRedrawArea (int pid, int vcid, int area_id); + void GetVCAreaClickZones(std::list* p_List); void SetPanel2DBlink (VECTOR3 v[4]); diff --git a/Src/Orbiter/VCockpit.cpp b/Src/Orbiter/VCockpit.cpp index c6fe3b49a..c565967e1 100644 --- a/Src/Orbiter/VCockpit.cpp +++ b/Src/Orbiter/VCockpit.cpp @@ -346,6 +346,29 @@ bool VirtualCockpit::SetClickZone_Quadrilateral (int i, return true; } +void VirtualCockpit::GetClickZones(std::list* p_List) +{ + if (!p_List) return; + for (int i = 0; i < narea; i++) { + auto& a = area[i]; + VCClickZone A; + A.id = a->id; + A.mode = a->cmode; + if (a->cmode == Area::ClickMode::CMODE_QUAD) for (int k = 0; k < 4; k++) { + A.pt[k].x = a->p[k].x; + A.pt[k].y = a->p[k].y; + A.pt[k].z = a->p[k].z; + } + if (a->cmode == Area::ClickMode::CMODE_SPHERICAL) { + A.cnt.x = a->cnt.x; + A.cnt.y = a->cnt.y; + A.cnt.z = a->cnt.z; + A.rad = a->rad; + } + p_List->push_back(A); + } +} + bool VirtualCockpit::ProcessMouse (UINT event, DWORD state, int x, int y) { mstate = 0; diff --git a/Src/Orbiter/VCockpit.h b/Src/Orbiter/VCockpit.h index 8c70eea4a..561a4a2b1 100644 --- a/Src/Orbiter/VCockpit.h +++ b/Src/Orbiter/VCockpit.h @@ -62,6 +62,7 @@ class VirtualCockpit { bool SetClickZone_Spherical (int i, const Vector &cnt, double rad); bool SetClickZone_Quadrilateral (int i, const Vector &p1, const Vector &p2, const Vector &p3, const Vector &p4); + void GetClickZones(std::list* p_List); bool ProcessMouse (UINT event, DWORD state, int x, int y); void GetMouseState (int &idx, int &state, Vector &xs) const; @@ -78,6 +79,7 @@ class VirtualCockpit { virtual void OptionChanged(DWORD cat, DWORD item); private: + inline int AreaIndex (int aid) const { for (int i = 0; i < narea; i++) if (area[i]->id == aid) return i; diff --git a/Src/Orbiter/Vecmat.h b/Src/Orbiter/Vecmat.h index 1472fe438..d42a77f33 100644 --- a/Src/Orbiter/Vecmat.h +++ b/Src/Orbiter/Vecmat.h @@ -1,6 +1,8 @@ // Copyright (c) Martin Schweiger // Licensed under the MIT License +#define OAPI_IMPLEMENTATION + #ifndef __VECMAT_H #define __VECMAT_H @@ -167,6 +169,7 @@ class Vector { }; }; + // ======================================================================= // class Matrix diff --git a/Src/Orbiter/Vessel.cpp b/Src/Orbiter/Vessel.cpp index 60fc9d2bc..19a7240b5 100644 --- a/Src/Orbiter/Vessel.cpp +++ b/Src/Orbiter/Vessel.cpp @@ -2793,6 +2793,11 @@ int Vessel::Dock (Vessel *target, DWORD mydid, DWORD tgtdid, DWORD mode) g_psys->DockVessels (this, target, mydid, tgtdid); RegisterDocking (mydid, target, tgtdid); target->RegisterDocking (tgtdid, this, mydid); + +#ifndef INLINEGRAPHICS + auto gc = g_pOrbiter->GetGraphicsClient(); + if (gc) gc->clbkScenarioChanged((OBJHANDLE)this, ScnChgEvent::Docked); +#endif return 0; } @@ -2819,6 +2824,11 @@ bool Vessel::Undock (UINT did, const Vessel *exclude, double vsep) for (n = n0; n < n1; ++n) buf += ' ' + std::to_string(n); FRecorder_SaveEvent("UNDOCK", buf.substr(1).data()); } + +#ifndef INLINEGRAPHICS + auto gc = g_pOrbiter->GetGraphicsClient(); + if (gc) gc->clbkScenarioChanged((OBJHANDLE)this, ScnChgEvent::UnDocked); +#endif return undocked; } @@ -3064,6 +3074,11 @@ bool Vessel::AttachChild (Vessel *child, AttachmentSpec *as, AttachmentSpec *asc if (allow_loose) strcat (cbuf, " LOOSE"); FRecorder_SaveEvent ("ATTACH", cbuf); } + +#ifndef INLINEGRAPHICS + auto gc = g_pOrbiter->GetGraphicsClient(); + if (gc) gc->clbkScenarioChanged((OBJHANDLE)this, ScnChgEvent::Attached); +#endif return true; } @@ -3074,6 +3089,11 @@ bool Vessel::AttachToParent (Vessel *parent, AttachmentSpec *asp, AttachmentSpec as->mate = parent; as->mate_attach = asp; InitAttachmentToParent (as, allow_loose); + +#ifndef INLINEGRAPHICS + auto gc = g_pOrbiter->GetGraphicsClient(); + if (gc) gc->clbkScenarioChanged((OBJHANDLE)this, ScnChgEvent::Attached); +#endif return true; } @@ -3088,6 +3108,11 @@ bool Vessel::DetachChild (AttachmentSpec *asp, double v) sprintf (cbuf, "%d, %0.3f", pidx, v); FRecorder_SaveEvent ("DETACH", cbuf); } + +#ifndef INLINEGRAPHICS + auto gc = g_pOrbiter->GetGraphicsClient(); + if (gc) gc->clbkScenarioChanged((OBJHANDLE)this, ScnChgEvent::Detached); +#endif return true; } @@ -3115,6 +3140,10 @@ bool Vessel::DetachFromParent (double v) if (bFRplayback) FRecorder_CheckEnd(); //bFRplayback = false; +#ifndef INLINEGRAPHICS + auto gc = g_pOrbiter->GetGraphicsClient(); + if (gc) gc->clbkScenarioChanged((OBJHANDLE)this, ScnChgEvent::Detached); +#endif return true; } @@ -3210,6 +3239,31 @@ void Vessel::ShiftAttachments (const Vector &ofs) // ============================================================== +Vessel* Vessel::GetAttachmentRoot() +{ + Vessel* mate = nullptr; + int cnt = 0; + for (DWORD i = 0; i < npattach; i++) { + if (pattach[i]) { + if (pattach[i]->toparent == false) { + oapiWriteLog("[ERROR] Parent attachment with no toparent flag"); // Sanity check + DebugBreak(); + } + if (pattach[i]->mate) { mate = pattach[i]->mate; cnt++; } + } + } + if (cnt > 1) { + oapiWriteLog("[ERROR] Vessel has multiple parent attachments"); // Sanity check + DebugBreak(); + } + + if (cnt == 1) return mate->GetAttachmentRoot(); + + return this; +} + +// ============================================================== + void Vessel::AddBeacon (BEACONLIGHTSPEC *bs) { BEACONLIGHTSPEC **tmp = new BEACONLIGHTSPEC*[nbeacon+1]; TRACENEW @@ -7488,6 +7542,11 @@ ATTACHMENTHANDLE VESSEL::GetAttachmentHandle (bool toparent, DWORD i) const } } +OBJHANDLE VESSEL::GetAttachmentRoot() const +{ + return vessel->GetAttachmentRoot(); +} + void VESSEL::AddBeacon (BEACONLIGHTSPEC *bs) { vessel->AddBeacon (bs); @@ -7715,6 +7774,12 @@ void VESSEL::SetMeshVisibilityMode (UINT idx, WORD mode) const vessel->SetMeshVisibilityMode (idx, mode); } +void VESSEL::SetVisualProperty(VISHANDLE vis, VisualProp prp, int idx, const type_info& t, const void* val) +{ + oapi::GraphicsClient* gc = g_pOrbiter->GetGraphicsClient(); + gc->clbkSetVisualProperty(vis, prp, idx, t, val); +} + void VESSEL::SetMeshVisibleInternal (UINT idx, bool visible) const { vessel->SetMeshVisibilityMode (idx, visible ? MESHVIS_ALWAYS : MESHVIS_EXTERNAL); diff --git a/Src/Orbiter/Vessel.h b/Src/Orbiter/Vessel.h index f8189a394..b07bb1d03 100644 --- a/Src/Orbiter/Vessel.h +++ b/Src/Orbiter/Vessel.h @@ -1079,6 +1079,9 @@ class Vessel: public VesselBase { void ShiftAttachments (const Vector &ofs); // move all attachment points by offset ofs + Vessel* Vessel::GetAttachmentRoot(); + // Get the root vessel. (i.e. vessel with no parent attachments) + // ======================================================================== // navigation radio interface diff --git a/Src/Vessel/Atlantis/Atlantis/Atlantis.cpp b/Src/Vessel/Atlantis/Atlantis/Atlantis.cpp index 46a94a8cb..b12d4461a 100644 --- a/Src/Vessel/Atlantis/Atlantis/Atlantis.cpp +++ b/Src/Vessel/Atlantis/Atlantis/Atlantis.cpp @@ -27,6 +27,7 @@ using std::min; using std::max; +using namespace oapi; #ifdef _DEBUG // D. Beachy: for BoundsChecker debugging @@ -1781,6 +1782,8 @@ void Atlantis::clbkVisualCreated (VISHANDLE _vis, int refcount) if (refcount > 1) return; // we don't support more than one visual per object vis = _vis; + SetVisualProperty(vis, VisualProp::CREATE_VC_PROBE, 0, typeid(FVECTOR3), &FVECTOR3(0.0f, 2.6f, 14.0f)); + // make sure the RMS attachment point is in sync with the animation state of the visual SetAttachmentParams (rms_attach, arm_tip[0], arm_tip[1]-arm_tip[0], arm_tip[2]-arm_tip[0]); } diff --git a/Src/Vessel/DeltaGlider/DeltaGlider.cpp b/Src/Vessel/DeltaGlider/DeltaGlider.cpp index 8d5e710f8..2e091560b 100644 --- a/Src/Vessel/DeltaGlider/DeltaGlider.cpp +++ b/Src/Vessel/DeltaGlider/DeltaGlider.cpp @@ -158,6 +158,8 @@ DeltaGlider::DeltaGlider (OBJHANDLE hObj, int fmodel) { int i; + oapiWriteLogVerbose("[DG] Constructor"); + modelidx = (fmodel ? 1 : 0); // Subsystem definitions @@ -213,6 +215,7 @@ DeltaGlider::DeltaGlider (OBJHANDLE hObj, int fmodel) // -------------------------------------------------------------- DeltaGlider::~DeltaGlider () { + oapiWriteLogVerbose("[DG] Destructor"); DWORD i; if (insignia_tex) oapiDestroySurface(insignia_tex); @@ -875,6 +878,7 @@ void DeltaGlider::InitVCMesh() void DeltaGlider::clbkSetClassCaps (FILEHANDLE cfg) { // *************** physical parameters ********************** + oapiWriteLogVerbose("[DG] clbkSetClassCaps"); bool b; int i; @@ -1110,6 +1114,7 @@ void DeltaGlider::clbkSetClassCaps (FILEHANDLE cfg) // -------------------------------------------------------------- void DeltaGlider::clbkLoadStateEx (FILEHANDLE scn, void *vs) { + oapiWriteLogVerbose("[DG] clbkLoadStateEx"); char *line; while (oapiReadScenario_nextline (scn, line)) { @@ -1150,6 +1155,7 @@ void DeltaGlider::clbkLoadStateEx (FILEHANDLE scn, void *vs) // -------------------------------------------------------------- void DeltaGlider::clbkSaveState (FILEHANDLE scn) { + oapiWriteLogVerbose("[DG] clbkSaveState"); char cbuf[256]; int i; @@ -1186,6 +1192,7 @@ void DeltaGlider::clbkSaveState (FILEHANDLE scn) // -------------------------------------------------------------- void DeltaGlider::clbkPostCreation () { + oapiWriteLogVerbose("[DG] clbkPostCreation"); ComponentVessel::clbkPostCreation (); SetEmptyMass (); @@ -1218,6 +1225,7 @@ void DeltaGlider::clbkPostCreation () // -------------------------------------------------------------- void DeltaGlider::clbkVisualCreated (VISHANDLE vis, int refcount) { + oapiWriteLogVerbose("[DG] clbkVisualCreated"); visual = vis; exmesh = GetDevMesh (vis, 0); vcmesh = GetDevMesh (vis, 1); @@ -1255,6 +1263,7 @@ void DeltaGlider::clbkVisualCreated (VISHANDLE vis, int refcount) // -------------------------------------------------------------- void DeltaGlider::clbkVisualDestroyed (VISHANDLE vis, int refcount) { + oapiWriteLogVerbose("[DG] clbkVisualDestroyed"); visual = NULL; exmesh = NULL; vcmesh = NULL; @@ -1352,6 +1361,7 @@ bool DeltaGlider::clbkLoadGenericCockpit () bool DeltaGlider::clbkLoadPanel2D (int id, PANELHANDLE hPanel, DWORD viewW, DWORD viewH) { + oapiWriteLogVerbose("[DG] clbkLoadPanel2D"); // set up subsystem panel elements ComponentVessel::clbkLoadPanel2D (id, hPanel, viewW, viewH); @@ -1444,6 +1454,8 @@ bool DeltaGlider::clbkPanelRedrawEvent (int id, int event, SURFHANDLE surf, void // -------------------------------------------------------------- bool DeltaGlider::clbkLoadVC (int id) { + oapiWriteLogVerbose("[DG] clbkLoadVC"); + static VCMFDSPEC mfds_left = {1, GRP_LMFD_DISPLAY_VC}; static VCMFDSPEC mfds_right = {1, GRP_RMFD_DISPLAY_VC}; static VCHUDSPEC huds = {1, GRP_HUDDISP_VC, {0,1.462,7.09}, 0.15}; diff --git a/Src/Vessel/DeltaGlider/Meshes/deltaglider_ns.msh b/Src/Vessel/DeltaGlider/Meshes/deltaglider_ns.msh index 92c56686b..2a1ce9b6b 100644 --- a/Src/Vessel/DeltaGlider/Meshes/deltaglider_ns.msh +++ b/Src/Vessel/DeltaGlider/Meshes/deltaglider_ns.msh @@ -26391,6 +26391,7 @@ GEOM 20 28 ; HUD_glass 116 16 18 0 MATERIAL 25 TEXTURE 0 +FLAG 11 GEOM 78 62 ; cockpitglass 110 -0.431152 1.201853 7.235322 -0.780780 0.469222 0.412569 -0.550900 1.232871 6.973425 -0.806691 0.484650 0.338177 diff --git a/Src/Vessel/DeltaGlider/Meshes/deltaglider_vc.msh b/Src/Vessel/DeltaGlider/Meshes/deltaglider_vc.msh index f7fed26e6..74b8a5ee8 100644 --- a/Src/Vessel/DeltaGlider/Meshes/deltaglider_vc.msh +++ b/Src/Vessel/DeltaGlider/Meshes/deltaglider_vc.msh @@ -19927,7 +19927,7 @@ GEOM 6 4 4 2 3 MATERIAL 24 TEXTURE 0 -FLAG 1 +FLAG 11 GEOM 120 108 ; cockpitglass 128 1.217259 1.510586 4.404531 -0.591781 -0.777958 -0.211134 1.242981 1.457264 4.528909 -0.591780 -0.777958 -0.211135 diff --git a/VulkanTransition.txt b/VulkanTransition.txt new file mode 100644 index 000000000..d90250af9 --- /dev/null +++ b/VulkanTransition.txt @@ -0,0 +1,5 @@ +Vulkan transition and API cleanup notes: + +- 32-bit FVECTOR math routines removed from DrawAPI.h, now included in MathAPI.h. +- Parameter order of "FVECTOR4 mul(FMATRIX4, FVECTOR4), mul(FVECTOR4, FMATRIX4)" may have changed, "tmul" discontinued +- FVECTOR based "dot" and "cross" renamed as "dotp" and "crossp" for consistency. \ No newline at end of file diff --git a/cmake/FindVulkan.cmake b/cmake/FindVulkan.cmake new file mode 100644 index 000000000..2e0046e82 --- /dev/null +++ b/cmake/FindVulkan.cmake @@ -0,0 +1,11 @@ +find_path(VULKAN_DIR + Include/vulkan/vulkan.hpp + HINTS "C:/VulkanSDK/1.2.189.2" + PATHS ENV VK_SDK_PATH +) + +if(${VULKAN_DIR} STREQUAL "VULKAN_DIR-NOTFOUND") + set(VULKAN_FOUND FALSE) +else() + set(VULKAN_FOUND TRUE) +endif()