14일에 발견했던 Multiplay 상에서의 Climb 기능 문제를 오늘에서야 해결했습니다.

 

그것도 너무나도 당연한 것을 확인하지 않고 말입니다.

 

문제 현상은 Climb 시 Client의 Character는 움직이지 않는 것이었습니다.

 

확인한 원인은 Character에 Attach 되어있는 MovementComponent가 가진 변수 MovementMode가 Interaction 시 값이 변했다가 몇 tick만에 원상복귀 되기 때문입니다.

 

처음에는 구현 방식을 살펴보고, 그 다음에는 통신 문제. 마지막으로 내부 구현상의 문제로 예상하고 살펴보았으나 별 다른 효과를 얻지 못했습니다.

 

그러던 중, 23일에 RPC 함수로 한번 묶어볼까 싶은 아이디어를 떠올렸습니다.

 

그리고 오늘, 구현 결과 기능이 작동하였습니다.

 

생각해보면 너무나도 당연한 것이었습니다.

 

Multiplay 상에서 Character가 움직이는 방식은 현재 두가지입니다.

 

하나는 유저가 조작 권한을 가지고 있는 Controller가 Character에 Possess 하여 직접 움직이는 것.

 

다른 하나는 다른 Client가 조작하는 Character의 트리거가 RPC를 통해 Replicated 되어 그 유저의 조작에 따라 동일하게 움직이는 것.

 

즉, Character는 각 Client에게 별도로 선언이 되어 있다는 것입니다.

 

이는 MovementComponent 또한 Client별로 하나씩 따로 존재한다는 것을 의미합니다.

 

때문에 정상적으로 움직이려면 모든 Client에서 동일한 Character의 움직임이 공유되어야 하고, 이는 RPC를 이용해야 한다는 것을 의미합니다.

 

MovementComponent라는 낯선 Component의 사용. 그리고 RPC 구조와 목적의 망각이 2주 가까운 시간의 낭비를 야기했습니다.

 

그리고 기능은 작동하지만, 완벽한 것은 아닙니다.

 

Client에서 Climb 할 때, Trigger 조작이 처음 1회 때 씹힙니다.

 

무슨 말인가 하면, Trigger 값이 처음 1회 때에는 변하지 않고, 그 뒤로는 정상적으로 들어갑니다.

 

결과적으로 기능이 동작은 하지만, Trigger를 로그로 찍어보면 값이 반대로 움직이고 있다는 것입니다.

 

오랫동안 고민한 문제가 어이 없게 풀리면서 긴장도 풀리고, 컨디션도 급격히 안좋아졌습니다.

 

이것 저것 시도를 해보았으나 유의미한 결과를 얻지 못하였고, 이 문제 해결은 수요일로 보류하겠습니다.

 

이전 3일 정도 적었던 이슈들은 평소에 이슈를 관리하던 GitKraken Board롤 옮겼습니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.05.28 개발일지  (0) 2020.05.28
20.05.27 개발일지  (0) 2020.05.27
20.05.23 개발일지  (2) 2020.05.23
20.05.21 개발일지  (0) 2020.05.21
20.05.20 개발일지  (0) 2020.05.20

EnableInput can only be specified on a PlayerController for itself 에러 발생.

한번 사라졌다가 rebuild 한 후 다시 발생.

시도해볼 해결 방안 링크를 첨부한다.

https://stackoverflow.com/questions/59857336/how-to-fix-enableinput-can-only-be-specified-on-a-pawn-for-its-controller-erro

 

How to fix "EnableInput can only be specified on a Pawn for its Controller" error in output log

I'm creating a weapon system for my third person shooter project and I'm having some problems getting input to the gun. With the way I have set up my classes, I have ACharacter parented player cla...

stackoverflow.com

Client에서 Interaction시 MovementMode가 변경 되었다가 다시 원상복귀 됨.

MovementComponent 내에 MovementMode가 호출되는 함수를 탐색해 보았다.

MovementMode
  DefaultLandMovementMode
    SetDefaultMovementMode
      Character::PostInitializeComponent
        - Controller is False, so SetDefautlMovementMode called
  GroundMovementMode
    SetGroundMovementMode
    ApplyNetworkMovementMode
  SetMovementMode
    SetDefaultMovementMode
    SetGroundMovementMode

OnMovementModeChanged
  SetMovementMode
  Character::OnMovementModeChanged

그 결과 Character의 PostInitializeComponent에서 Controller가 없을 경우, MovementComponent::SetDefaultMovementMode가 호출이 된다.

이 함수 내부에는 GameMode를 MOVE_Walk로 초기화 하는 구문이 있다.

 

이것이 원인이라면 Character를 찾지 못했다는 warning과 문제가 하나로 합쳐진다.

하지만 Interaction시 한번 수정되었다가 몇 tick 이후 원상복귀 된다는 점에서 이 부분이 문제라 하기에는 애매하다.

 

그래서 MovementComponent의 OnMovementModeChanged를 탐색해보았다. 

로그에서 Override 한 OnMovementModeChanged 함수의 로그가 찍혀있었기 때문이다.

하지만 이렇다 할 원인을 찾지 못했다.

 

현재 여기까지 탐색을 함.

다음 시도는 "Character에서 MovementComponent가 가지고 있는 MovementMode의 RPC Function 구현"입니다.

 

"ProcessRemoteFunction: No owning connection for actor THCharacterBP_Sample_C_0" Warning 발생

Controller와 Character가 연결되지 않아서 발생했다.

단순 네트워크 문제라는 것도 있고, Possess를 하라는 글도 있다.

이에 대해 그나마 가장 자세한 문서를 첨부한다.

https://dawnarc.com/2017/09/ue4no-owning-connection-for-actor-xxx.-function-xxx-will-not-be-processed/

 

[UE4]No owning connection for actor XXX. Function XXX will not be processed

keywords:UE4, Dedicated Server, Replication Issue Client execute a server function failed: LogNet: Warning: UIpNetDriver::ProcesRemoteFunction: No owning connection for actor TopDownCharacter_C_0. Function ServerMoveToDest will not be processed. Cause: B

dawnarc.com

우선 Possess를 따로 어떻게 해줘야 하는지를 잘 모르겠다.

그 부분을 먼저 고민해야 할 것 같다.

 

만약 Listen Server라서 발생하는 문제 중 하나라면 Climb 관련 기능들을 모두 Dedicate Server 구현 이후로 미뤄야 한다.

하지만 다른 Animation에서는 이런 문제가 발생하지 않는 것으로 보아 이런 가능성은 매우 낮다고 생각한다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.05.27 개발일지  (0) 2020.05.27
20.05.25 개발일지  (0) 2020.05.25
20.05.21 개발일지  (0) 2020.05.21
20.05.20 개발일지  (0) 2020.05.20
20.05.18 개발일지  (0) 2020.05.18

DirectX 9, 10의 D3DX Library에, 11에서는 XNA Math에서 3차원 그래픽에 필요한 것들을 지원한다.

코드상에서는 xnamath.h를 include 하면 사용이 가능하다고 설명되어 있다.

하지만 현재 예시 DirectX12 코드에서는 관련 기능들이 DiretXMath.h에 선언이 되어 있다.

명확하게 이전 되었다는 내용을 찾지는 못했지만, xnamath.h와 DirectXMath.h의 document를 보면 설명이 동일하다.

때문에 여기서는 DirectXMath.h를 기준으로 설명하겠다.

또한 관련해서 답이 충분히 될법한 링크 또한 첨부한다.

https://docs.microsoft.com/en-us/windows/win32/dxmath/pg-xnamath-migration#header-changes

 

Code Migration from the XNA Math Library - Win32 apps

Code Migration from the XNA Math Library In this article --> This overview describes the changes required to migrate existing code using the XNA Math library to the DirectXMath library. The DirectXMath library uses a new set of headers. Replace the xnamath

docs.microsoft.com

 

이 Library는 벡터 연산에 매우 유용하다.

이를 설명하기 위해서는 먼저 Library의 연산 과정을 조금 언급해야 한다.

DirectXMath Library는 Windows와 XBox 360에서 사용가능한 특별한 하드웨어 레지스터들을 활용한다.

Windows에서는 SSE2(Streaming SIMD Extensions) 명령집합을 사용하는데,
SIMD의 명령덜은 128비트 너비의 SIMD(Single Instruction Multiple Data) 레지스터들을 이용해
한 명령에서 32비트 float나 int 4개를 동시에 처리가 가능하다.

 

DirectXMath에서 핵심 Vector 형식은 SIMD 하드웨어 레지스터들에 대응되는 XMVECTOR이다.

#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
typedef __m128 XMVECTOR;
#elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
typedef float32x4_t XMVECTOR;
#else
typedef __vector4 XMVECTOR;
#endif

128bit 크기로, 하나의 SIMD 명령으로 처리되는 4개의 32bit float 값들로 이루어져있다.

저기서 _XM_SSE_INTRINSICS_는 SSE나 SSE2를, _XM_ARM_NEON_INTRINSICS_는 Windows RT.

즉, 모바일 플랫폼을 나타낸다.

_XM_NO_INTRINSICS_는 Windows 환경이 아닌 곳에서 DirectXMath가 따로 사용되는 경우를 일컫는다.

 

여기서 __m128은 특별한 SIMD 형식이다.

이 형식의 Vector들은 계산 시 반드시 SIMD의 장점을 취하게 된다.

게다가 2, 3차원 Vector일 경우, 사용하지 않는 element를 0으로 설정해 동일하게 SIMD의 장점을 취한다.

 

다만 여기에는 몇가지 규칙이 있는데, 플랫폼마다 규칙이 다르다.

특히 Windows 23bit와 64bit, XBox 360의 규칙들이 서로 다르다.

이런 차이로부터 독립적이기 위해, XMVECTOR 대신 CXXMVECTOR 형식과 FXMVECTOR형식을 사용한다.

// 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_ ) && !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 x64 vector call; by reference otherwise
#if ( defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || (_XM_VECTORCALL_ && !defined(_M_IX86) ) ) && !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) || _XM_VECTORCALL_ ) && !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;

 

32bit와 64bit의 차이는 복사 전달과 참조전달이다.

FXMVECTOR, CXMVECTOR. 더 나아가 GXMVECTOR, HXMVECTOR의 차이는 다음과 같다.

매개변수로 XMVECTOR을 전달 할 때,

  1. 1, 2, 3번째 XMVECTOR는 FXMVECTOR로 전달해야 한다.
  2. 4번째 XMVECTOR는 GXMVECTOR로 전달해야 한다.
  3. 5, 6번째 XMVECTOR는 HXMVECTOR로 전달해야 한다.
  4. 7번째 이후로는 CXMVECTOR로 전달해야 한다.

단, 생성자에서는 __vectorcall의 제한으로 인해, 규칙이 더 간단해진다.

  1. 처음 3개의 XMVECTOR는 FXMVECTOR로 전달한다.
  2. 그 이후에는 모두 CXMVECTOR로 전달한다.

함수에 전달 할 때 XMVECTOR가 연속적이지 않아도 상관 없다.

XMVECTOR들만 순번을 매겨 위 형식을 적용한다.

이와 관련된 문서를 첨부한다.

https://docs.microsoft.com/en-us/windows/win32/dxmath/pg-xnamath-internals

 

Library Internals - Win32 apps

Library Internals In this article --> This topic describes the internal design of the DirectXMath library. Calling Conventions To enhance portability and optimize data layout, you need to use the appropriate calling conventions for each platform supported

docs.microsoft.com

 

XMVECTOR는 16Byte 경계에 정렬되어야 하는데, Local/Global Value에서는 자동으로 정렬된다.

Field의 경우, XMVECTOR 대신 XMFLOAT2, XMFLOAT3, XMFLOAT4를 사용하는 것이 바람직하다.

// 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;

    XM_CONSTEXPR XMFLOAT2(float _x, float _y) : x(_x), y(_y) {}
    explicit XMFLOAT2(_In_reads_(2) const float *pArray) : x(pArray[0]), y(pArray[1]) {}
};
// 3D Vector; 32 bit floating point componentsstruct 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;

    XM_CONSTEXPR XMFLOAT3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
    explicit XMFLOAT3(_In_reads_(3) const float *pArray) : x(pArray[0]), y(pArray[1]), z(pArray[2]) {}
};
// 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;

    XM_CONSTEXPR XMFLOAT4(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) {}
    explicit XMFLOAT4(_In_reads_(4) const float *pArray) : x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {}
};

하지만 이 형식들을 계산에 직접 사용하면 SIMD의 장점을 취하지 못한다.

SIMD를 활용하려면 이 형식의 Instance를 XMVECTOR 형식으로 변환해야 한다.

이를 위해 DirectXMath에서는 다양한 적재 함수들을 제공한다.

XMVECTOR    XM_CALLCONV     XMLoadInt2(_In_reads_(2) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadInt2A(_In_reads_(2) const uint32_t* PSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat2(_In_ const XMFLOAT2* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat2A(_In_ const XMFLOAT2A* pSource);
XMVECTOR    XM_CALLCONV     XMLoadSInt2(_In_ const XMINT2* pSource);
XMVECTOR    XM_CALLCONV     XMLoadUInt2(_In_ const XMUINT2* pSource);

XMVECTOR    XM_CALLCONV     XMLoadInt3(_In_reads_(3) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadInt3A(_In_reads_(3) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat3(_In_ const XMFLOAT3* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat3A(_In_ const XMFLOAT3A* pSource);
XMVECTOR    XM_CALLCONV     XMLoadSInt3(_In_ const XMINT3* pSource);
XMVECTOR    XM_CALLCONV     XMLoadUInt3(_In_ const XMUINT3* pSource);

XMVECTOR    XM_CALLCONV     XMLoadInt4(_In_reads_(4) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadInt4A(_In_reads_(4) const uint32_t* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat4(_In_ const XMFLOAT4* pSource);
XMVECTOR    XM_CALLCONV     XMLoadFloat4A(_In_ const XMFLOAT4A* pSource);
XMVECTOR    XM_CALLCONV     XMLoadSInt4(_In_ const XMINT4* pSource);
XMVECTOR    XM_CALLCONV     XMLoadUInt4(_In_ const XMUINT4* pSource);

또한, 반대로 XMVECTOR Instance를 XMFLOAT* 형식으로 변환하는 저장 함수도 제공한다.

void        XM_CALLCONV     XMStoreInt2(_Out_writes_(2) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreInt2A(_Out_writes_(2) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat2(_Out_ XMFLOAT2* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat2A(_Out_ XMFLOAT2A* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreSInt2(_Out_ XMINT2* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreUInt2(_Out_ XMUINT2* pDestination, _In_ FXMVECTOR V);

void        XM_CALLCONV     XMStoreInt3(_Out_writes_(3) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreInt3A(_Out_writes_(3) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat3(_Out_ XMFLOAT3* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat3A(_Out_ XMFLOAT3A* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreSInt3(_Out_ XMINT3* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreUInt3(_Out_ XMUINT3* pDestination, _In_ FXMVECTOR V);

void        XM_CALLCONV     XMStoreInt4(_Out_writes_(4) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreInt4A(_Out_writes_(4) uint32_t* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat4(_Out_ XMFLOAT4* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreFloat4A(_Out_ XMFLOAT4A* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreSInt4(_Out_ XMINT4* pDestination, _In_ FXMVECTOR V);
void        XM_CALLCONV     XMStoreUInt4(_Out_ XMUINT4* pDestination, _In_ FXMVECTOR V);

또한 XMVECTOR Object의 내용을 설정하는 용도의 함수들을 정의하고 있다.

//(0, 0, 0, 0)
XMVECTOR    XM_CALLCONV     XMVectorZero();

// (1, 1, 1, 1)
XMVECTOR    XM_CALLCONV     XMVectorSplatOne();

//(x, y, z, w)
XMVECTOR    XM_CALLCONV     XMVectorSet(float x, float y, float z, float w);

//(value, value, value, value)
XMVECTOR    XM_CALLCONV     XMVectorReplicate(float Value);

//(Vx, Vx, Vx, Vx)
XMVECTOR    XM_CALLCONV     XMVectorSplatX(FXMVECTOR V);

//(Vy, Vy, Vy, Vy)
XMVECTOR    XM_CALLCONV     XMVectorSplatY(FXMVECTOR V);

//(Vz, Vz, Vz, Vz)
XMVECTOR    XM_CALLCONV     XMVectorSplatZ(FXMVECTOR V);

//(Vw, Vw, Vw, Vw)
XMVECTOR    XM_CALLCONV     XMVectorSplatW(FXMVECTOR V);

이 외에 XMVECTOR Instance의 한 성분만 읽거나, 변경하는 조회, 설정 함수들도 제공한다.

float       XM_CALLCONV     XMVectorGetX(FXMVECTOR V);
float       XM_CALLCONV     XMVectorGetY(FXMVECTOR V);
float       XM_CALLCONV     XMVectorGetZ(FXMVECTOR V);
float       XM_CALLCONV     XMVectorGetW(FXMVECTOR V);

XMVECTOR    XM_CALLCONV     XMVectorSetX(FXMVECTOR V, float x);
XMVECTOR    XM_CALLCONV     XMVectorSetY(FXMVECTOR V, float y);
XMVECTOR    XM_CALLCONV     XMVectorSetZ(FXMVECTOR V, float z);
XMVECTOR    XM_CALLCONV     XMVectorSetW(FXMVECTOR V, float w);

 

Const XMVECTOR Instance에는 반드시 XMVECTORF32 형식을 사용해야 한다.

간단히 말해, 초기화 구문을 사용하고자 할 때에는 항상 XMVECTORF32를 사용해야 한다는 것이다.

XMVECTORF32는 16Byte 경계로 정렬된 Structure로, XMVECTOR로의 변호나 연산자를 지원한다.

// Conversion types for constants
__declspec(align(16)) struct XMVECTORF32
{
    union
    {
        float f[4];
        XMVECTOR v;
    };

    inline operator XMVECTOR() const { return v; }
    inline operator const float*() const { return f; }
#if !defined(_XM_NO_INTRINSICS_) && defined(_XM_SSE_INTRINSICS_)
    inline operator __m128i() const { return _mm_castps_si128(v); }
    inline operator __m128d() const { return _mm_castps_pd(v); }
#endif
};

또한 XMVECTORU32를 이용해 정수 자료를 담은 상수 XMVECTOR를 생성하는 것도 가능하다.

__declspec(align(16)) struct XMVECTORU32
{
    union
    {
        uint32_t u[4];
        XMVECTOR v;
    };

    inline operator XMVECTOR() const { return v; }
#if !defined(_XM_NO_INTRINSICS_) && defined(_XM_SSE_INTRINSICS_)
    inline operator __m128i() const { return _mm_castps_si128(v); }
    inline operator __m128d() const { return _mm_castps_pd(v); }
#endif
};
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 } } };

XMVECTOR에는 Vector의 덧셈, 뺄셈, 스칼라 곱셈을 위한 Operator Overloading이 지원된다.

보통은 직관적이라 활성화 하지만, 일부 응용 프로그램은 성능상의 이유로 이를 비활성화 한다.

Operator Overloading을 비활성화 하고 싶으면 macro 상수 XM_NO_OPERATOR_OVERLOADS를 정의해야 한다.

// Vector operators

#ifndef _XM_NO_XMVECTOR_OVERLOADS_
XMVECTOR    XM_CALLCONV     operator+ (FXMVECTOR V);
XMVECTOR    XM_CALLCONV     operator- (FXMVECTOR V);

XMVECTOR&   XM_CALLCONV     operator+= (XMVECTOR& V1, FXMVECTOR V2);
XMVECTOR&   XM_CALLCONV     operator-= (XMVECTOR& V1, FXMVECTOR V2);
XMVECTOR&   XM_CALLCONV     operator*= (XMVECTOR& V1, FXMVECTOR V2);
XMVECTOR&   XM_CALLCONV     operator/= (XMVECTOR& V1, FXMVECTOR V2);

XMVECTOR&   operator*= (XMVECTOR& V, float S);
XMVECTOR&   operator/= (XMVECTOR& V, float S);

XMVECTOR    XM_CALLCONV     operator+ (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     operator- (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     operator* (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     operator/ (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     operator* (FXMVECTOR V, float S);
XMVECTOR    XM_CALLCONV     operator* (float S, FXMVECTOR V);
XMVECTOR    XM_CALLCONV     operator/ (FXMVECTOR V, float S);
#endif /* !_XM_NO_XMVECTOR_OVERLOADS_ */

수학과 관련된 Library답게, DirectXMath Library에서도 다양한 공식에서 사용되는 상수의 근사값을 지원한다.

XM_CONST float XM_PI        = 3.141592654f;
XM_CONST float XM_2PI       = 6.283185307f;
XM_CONST float XM_1DIVPI    = 0.318309886f;
XM_CONST float XM_1DIV2PI   = 0.159154943f;
XM_CONST float XM_PIDIV2    = 1.570796327f;
XM_CONST float XM_PIDIV4    = 0.785398163f;

또한 radian과 degree를 변환하는 함수들도 제공한다.

// Unit conversion

inline XM_CONSTEXPR float XMConvertToRadians(float fDegrees) { return fDegrees * (XM_PI / 180.0f); }
inline XM_CONSTEXPR float XMConvertToDegrees(float fRadians) { return fRadians * (180.0f / XM_PI); }

그리고 최솟값, 최댓값을 위한 매크로 함수들도 정의한다.

#if defined(__XNAMATH_H__) && defined(XMMin)
#undef XMMin
#undef XMMax
#endif

template<class T> inline T XMMin(T a, T b) { return (a < b) ? a : b; }
template<class T> inline T XMMax(T a, T b) { return (a > b) ? a : b; }

 

XMVECTOR가 Operator Overloading으로만 연산을 하는 것은 아니다.

XMVECTOR 연산에 필요한 여러 함수들을 따로 지원하기도 한다.

/****************************************************************************
 *
 * 3D vector operations
 *
 ****************************************************************************/

bool        XM_CALLCONV     XMVector3Equal(FXMVECTOR V1, FXMVECTOR V2);
uint32_t    XM_CALLCONV     XMVector3EqualR(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3EqualInt(FXMVECTOR V1, FXMVECTOR V2);
uint32_t    XM_CALLCONV     XMVector3EqualIntR(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3NearEqual(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR Epsilon);
bool        XM_CALLCONV     XMVector3NotEqual(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3NotEqualInt(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3Greater(FXMVECTOR V1, FXMVECTOR V2);
uint32_t    XM_CALLCONV     XMVector3GreaterR(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3GreaterOrEqual(FXMVECTOR V1, FXMVECTOR V2);
uint32_t    XM_CALLCONV     XMVector3GreaterOrEqualR(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3Less(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3LessOrEqual(FXMVECTOR V1, FXMVECTOR V2);
bool        XM_CALLCONV     XMVector3InBounds(FXMVECTOR V, FXMVECTOR Bounds);

bool        XM_CALLCONV     XMVector3IsNaN(FXMVECTOR V);
bool        XM_CALLCONV     XMVector3IsInfinite(FXMVECTOR V);

XMVECTOR    XM_CALLCONV     XMVector3Dot(FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     XMVector3Cross(FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     XMVector3LengthSq(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3ReciprocalLengthEst(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3ReciprocalLength(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3LengthEst(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3Length(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3NormalizeEst(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3Normalize(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3ClampLength(FXMVECTOR V, float LengthMin, float LengthMax);
XMVECTOR    XM_CALLCONV     XMVector3ClampLengthV(FXMVECTOR V, FXMVECTOR LengthMin, FXMVECTOR LengthMax);
XMVECTOR    XM_CALLCONV     XMVector3Reflect(FXMVECTOR Incident, FXMVECTOR Normal);
XMVECTOR    XM_CALLCONV     XMVector3Refract(FXMVECTOR Incident, FXMVECTOR Normal, float RefractionIndex);
XMVECTOR    XM_CALLCONV     XMVector3RefractV(FXMVECTOR Incident, FXMVECTOR Normal, FXMVECTOR RefractionIndex);
XMVECTOR    XM_CALLCONV     XMVector3Orthogonal(FXMVECTOR V);
XMVECTOR    XM_CALLCONV     XMVector3AngleBetweenNormalsEst(FXMVECTOR N1, FXMVECTOR N2);
XMVECTOR    XM_CALLCONV     XMVector3AngleBetweenNormals(FXMVECTOR N1, FXMVECTOR N2);
XMVECTOR    XM_CALLCONV     XMVector3AngleBetweenVectors(FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR    XM_CALLCONV     XMVector3LinePointDistance(FXMVECTOR LinePoint1, FXMVECTOR LinePoint2, FXMVECTOR Point);
void        XM_CALLCONV     XMVector3ComponentsFromNormal(_Out_ XMVECTOR* pParallel, _Out_ XMVECTOR* pPerpendicular, _In_ FXMVECTOR V, _In_ FXMVECTOR Normal);
XMVECTOR    XM_CALLCONV     XMVector3Rotate(FXMVECTOR V, FXMVECTOR RotationQuaternion);
XMVECTOR    XM_CALLCONV     XMVector3InverseRotate(FXMVECTOR V, FXMVECTOR RotationQuaternion);
XMVECTOR    XM_CALLCONV     XMVector3Transform(FXMVECTOR V, FXMMATRIX M);
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);
XMVECTOR    XM_CALLCONV     XMVector3TransformCoord(FXMVECTOR V, FXMMATRIX M);
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);
XMVECTOR    XM_CALLCONV     XMVector3TransformNormal(FXMVECTOR V, FXMMATRIX M);
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);
XMVECTOR    XM_CALLCONV     XMVector3Project(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ,
                                             FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World);
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);
XMVECTOR    XM_CALLCONV     XMVector3Unproject(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ,
                                               FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World);
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);

-----------------------------

본 블로그는 [DirectX11을 이용한 3D 게임 프로그래밍 입문]을 기반으로 공부를 하며,
DirectX12와 다른 부분을 수정해가며 정리를 하는 글입니다.

 

한글 자료가 필요하시다면, 부족하게나마 도움이 되었으면 합니다.

만약 더 자세한 내용이 필요하시다면, 공식 레퍼런스를 보시는 것을 추천합니다.

https://docs.microsoft.com/en-us/windows/win32/dxmath/ovw-xnamath-reference

 

DirectXMath programming reference - Win32 apps

DirectXMath programming reference In this article --> This section contains reference material for the DirectXMath Library. In this section DirectXMath DirectXMath programming guide -->

docs.microsoft.com

 

+ Recent posts