CelticCraft/Plugins/VoxelFree/Source/Voxel/Public/VoxelMaterial.h

773 lines
No EOL
16 KiB
C++

// Copyright 2020 Phyronnaz
#pragma once
#include "CoreMinimal.h"
#include "VoxelMinimal.h"
#include "VoxelUtilities/VoxelMathUtilities.h"
#include "VoxelMaterial.generated.h"
namespace EVoxelMaterialConfigFlag
{
enum Type : uint32
{
EnableA = 0x01,
EnableR = 0x02,
EnableG = 0x04,
// = 0x08, Forgot to use that one :(
EnableB = 0x10,
EnableUV0 = 0x20,
EnableUV1 = 0x40,
EnableUV2 = 0x80,
EnableUV3 = 0x100
};
}
constexpr uint32 GVoxelMaterialConfigFlag =
EVoxelMaterialConfigFlag::EnableA * VOXEL_MATERIAL_ENABLE_A +
EVoxelMaterialConfigFlag::EnableR * VOXEL_MATERIAL_ENABLE_R +
EVoxelMaterialConfigFlag::EnableG * VOXEL_MATERIAL_ENABLE_G +
EVoxelMaterialConfigFlag::EnableB * VOXEL_MATERIAL_ENABLE_B +
EVoxelMaterialConfigFlag::EnableUV0 * VOXEL_MATERIAL_ENABLE_UV0 +
EVoxelMaterialConfigFlag::EnableUV1 * VOXEL_MATERIAL_ENABLE_UV1 +
EVoxelMaterialConfigFlag::EnableUV2 * VOXEL_MATERIAL_ENABLE_UV2 +
EVoxelMaterialConfigFlag::EnableUV3 * VOXEL_MATERIAL_ENABLE_UV3;
UENUM(BlueprintType, DisplayName = "Voxel Material Mask", meta = (Bitflags))
enum class EVoxelMaterialMask_BP : uint8
{
R,
G,
B,
A,
U0,
V0,
U1,
V1,
U2,
V2,
U3,
V3,
};
namespace EVoxelMaterialMask
{
enum Type : uint32
{
R = 1 << 0,
G = 1 << 1,
B = 1 << 2,
A = 1 << 3,
U0 = 1 << 4,
V0 = 1 << 5,
U1 = 1 << 6,
V1 = 1 << 7,
U2 = 1 << 8,
V2 = 1 << 9,
U3 = 1 << 10,
V3 = 1 << 11,
None = 0,
All = R | G | B| A | U0 | U1 | U2 | U3 | V0 | V1 | V2 | V3,
RGB = R | G | B,
RGBA = R | G | B | A,
SingleIndex = A,
MultiIndex_Blend0 = R,
MultiIndex_Blend1 = G,
MultiIndex_Blend2 = B,
MultiIndex_Wetness = A,
MultiIndex_Index0 = U0,
MultiIndex_Index1 = V0,
MultiIndex_Index2 = U1,
MultiIndex_Index3 = V1,
MultiIndex_NoWetness = MultiIndex_Blend0 |
MultiIndex_Blend1 |
MultiIndex_Blend2 |
MultiIndex_Index0 |
MultiIndex_Index1 |
MultiIndex_Index2 |
MultiIndex_Index3,
MultiIndex = MultiIndex_NoWetness | MultiIndex_Wetness,
UV0 = U0 | V0,
UV1 = U1 | V1,
UV2 = U2 | V2,
UV3 = U3 | V3,
UV = UV0 | UV1 | UV2 | UV3
};
}
// Base class without all the #if ENABLE_SOMETHING
template<typename T>
struct TVoxelMaterialImpl
{
FORCEINLINE TVoxelMaterialImpl()
{
}
FORCEINLINE explicit TVoxelMaterialImpl(EForceInit)
{
#if VOXEL_MATERIAL_DEFAULT_IS_WHITE
SetR(255);
SetG(255);
SetB(255);
SetA(255);
#else
SetR(0);
SetG(0);
SetB(0);
SetA(0);
#endif
SetU0(0);
SetU1(0);
SetU2(0);
SetU3(0);
SetV0(0);
SetV1(0);
SetV2(0);
SetV3(0);
}
FORCEINLINE static T Default()
{
return T(ForceInit);
}
public:
#define DEFINE_GETTER_SETTER(Name) \
FORCEINLINE void Set##Name(uint8 New##Name) { static_cast<T&>(*this).Impl_Set##Name(New##Name); } \
FORCEINLINE void Set##Name(int32 New##Name) { static_cast<T&>(*this).Impl_Set##Name(FVoxelUtilities::CastToUINT8(New##Name)); } \
FORCEINLINE void Set##Name##_AsFloat(float New##Name) { static_cast<T&>(*this).Impl_Set##Name(FVoxelUtilities::FloatToUINT8(New##Name)); } \
template<typename X> \
FORCEINLINE void Set##Name(X New##Name) \
{ \
static_assert(!TIsSame<X, float>::Value, PREPROCESSOR_TO_STRING(Set##Name) ": need to use " PREPROCESSOR_TO_STRING(Set##Name##_AsFloat)); \
static_assert(TIsSame<X, float>::Value, PREPROCESSOR_TO_STRING(Set##Name) ": need to cast to float, uint8 or int32"); \
} \
FORCEINLINE uint8 Get##Name() const { return static_cast<const T&>(*this).Impl_Get##Name(); } \
FORCEINLINE float Get##Name##_AsFloat() const { return FVoxelUtilities::UINT8ToFloat(static_cast<const T&>(*this).Impl_Get##Name()); }
DEFINE_GETTER_SETTER(R)
DEFINE_GETTER_SETTER(G)
DEFINE_GETTER_SETTER(B)
DEFINE_GETTER_SETTER(A)
DEFINE_GETTER_SETTER(U0)
DEFINE_GETTER_SETTER(U1)
DEFINE_GETTER_SETTER(U2)
DEFINE_GETTER_SETTER(U3)
DEFINE_GETTER_SETTER(V0)
DEFINE_GETTER_SETTER(V1)
DEFINE_GETTER_SETTER(V2)
DEFINE_GETTER_SETTER(V3)
#undef DEFINE_GETTER_SETTER
public:
#define DEFINE_UV_GETTER_SETTER(Name, Type, Suffix) \
FORCEINLINE Type Get##Name##Suffix(int32 Tex) const \
{ \
switch (Tex) \
{ \
case 0: return Get##Name##0##Suffix(); \
case 1: return Get##Name##1##Suffix(); \
case 2: return Get##Name##2##Suffix(); \
case 3: return Get##Name##3##Suffix(); \
default: return 0; \
} \
} \
template<typename X> \
FORCEINLINE void Set##Name##Suffix(int32 Tex, X Value) \
{ \
switch (Tex) \
{ \
case 0: Set##Name##0##Suffix(Value); break; \
case 1: Set##Name##1##Suffix(Value); break; \
case 2: Set##Name##2##Suffix(Value); break; \
case 3: Set##Name##3##Suffix(Value); break; \
default: break; \
} \
}
DEFINE_UV_GETTER_SETTER(U, uint8,)
DEFINE_UV_GETTER_SETTER(V, uint8,)
DEFINE_UV_GETTER_SETTER(U, float, _AsFloat)
DEFINE_UV_GETTER_SETTER(V, float, _AsFloat)
#undef DEFINE_UV_GETTER_SETTER
public:
#define DEFINE_FORWARD(Name, Forward) \
FORCEINLINE void Set##Name(uint8 New##Name) { Set##Forward(New##Name); } \
FORCEINLINE void Set##Name(int32 New##Name) { Set##Forward(New##Name); } \
FORCEINLINE void Set##Name##_AsFloat(float New##Name) { Set##Forward##_AsFloat(New##Name); } \
template<typename X> \
FORCEINLINE void Set##Name(X New##Name) \
{ \
static_assert(!TIsSame<X, float>::Value, PREPROCESSOR_TO_STRING(Set##Name) ": need to use " PREPROCESSOR_TO_STRING(Set##Name##_AsFloat)); \
static_assert(TIsSame<X, float>::Value, PREPROCESSOR_TO_STRING(Set##Name) ": need to cast to float, uint8 or int32"); \
} \
FORCEINLINE uint8 Get##Name() const { return Get##Forward(); } \
FORCEINLINE float Get##Name##_AsFloat() const { return Get##Forward##_AsFloat(); }
DEFINE_FORWARD(SingleIndex, A)
DEFINE_FORWARD(MultiIndex_Blend0, R)
DEFINE_FORWARD(MultiIndex_Blend1, G)
DEFINE_FORWARD(MultiIndex_Blend2, B)
DEFINE_FORWARD(MultiIndex_Wetness, A)
DEFINE_FORWARD(MultiIndex_Index0, U0)
DEFINE_FORWARD(MultiIndex_Index1, V0)
DEFINE_FORWARD(MultiIndex_Index2, U1)
DEFINE_FORWARD(MultiIndex_Index3, V1)
#undef DEFINE_FORWARD
public:
FORCEINLINE uint32 GetPackedColor() const
{
return
(uint32(GetR()) << 0) |
(uint32(GetG()) << 8) |
(uint32(GetB()) << 16) |
(uint32(GetA()) << 24);
}
public:
FORCEINLINE void SetColor(const FColor& Color)
{
SetR(Color.R);
SetG(Color.G);
SetB(Color.B);
SetA(Color.A);
}
FORCEINLINE FColor GetColor() const
{
return FColor(
GetR(),
GetG(),
GetB(),
GetA());
}
FORCEINLINE FVector4 GetColorVector() const
{
return FVector4(
GetR_AsFloat(),
GetG_AsFloat(),
GetB_AsFloat(),
GetA_AsFloat());
}
FORCEINLINE void SetColor(const FLinearColor& Color)
{
SetR_AsFloat(Color.R);
SetG_AsFloat(Color.G);
SetB_AsFloat(Color.B);
SetA_AsFloat(Color.A);
}
FORCEINLINE FLinearColor GetLinearColor() const
{
return FLinearColor(
GetR_AsFloat(),
GetG_AsFloat(),
GetB_AsFloat(),
GetA_AsFloat());
}
public:
FORCEINLINE void SetUV_AsFloat(int32 Tex, const FVector2D& UV)
{
SetU_AsFloat(Tex, UV.X);
SetV_AsFloat(Tex, UV.Y);
}
FORCEINLINE FVector2D GetUV_AsFloat(int32 Tex) const
{
return FVector2D(GetU_AsFloat(Tex), GetV_AsFloat(Tex));
}
public:
FORCEINLINE static T CreateFromColor(const FLinearColor& Color)
{
T Material(ForceInit);
Material.SetColor(Color);
return Material;
}
FORCEINLINE static T CreateFromColor(const FColor& Color)
{
T Material(ForceInit);
Material.SetColor(Color);
return Material;
}
public:
FORCEINLINE void CopyFrom(const T& Other, uint32 Mask)
{
if (Mask & EVoxelMaterialMask::R) SetR(Other.GetR());
if (Mask & EVoxelMaterialMask::G) SetG(Other.GetG());
if (Mask & EVoxelMaterialMask::B) SetB(Other.GetB());
if (Mask & EVoxelMaterialMask::A) SetA(Other.GetA());
if (Mask & EVoxelMaterialMask::U0) SetU0(Other.GetU0());
if (Mask & EVoxelMaterialMask::U1) SetU1(Other.GetU1());
if (Mask & EVoxelMaterialMask::U2) SetU2(Other.GetU2());
if (Mask & EVoxelMaterialMask::U3) SetU3(Other.GetU3());
if (Mask & EVoxelMaterialMask::V0) SetV0(Other.GetV0());
if (Mask & EVoxelMaterialMask::V1) SetV1(Other.GetV1());
if (Mask & EVoxelMaterialMask::V2) SetV2(Other.GetV2());
if (Mask & EVoxelMaterialMask::V3) SetV3(Other.GetV3());
}
public:
FORCEINLINE bool operator==(const T& Other) const
{
return
GetR() == Other.GetR() &&
GetG() == Other.GetG() &&
GetB() == Other.GetB() &&
GetA() == Other.GetA() &&
GetU0() == Other.GetU0() &&
GetU1() == Other.GetU1() &&
GetU2() == Other.GetU2() &&
GetV3() == Other.GetU3() &&
GetV0() == Other.GetV0() &&
GetV1() == Other.GetV1() &&
GetV2() == Other.GetV2() &&
GetV3() == Other.GetV3();
}
FORCEINLINE bool operator!=(const T& Other) const
{
return
GetR() != Other.GetR() ||
GetG() != Other.GetG() ||
GetB() != Other.GetB() ||
GetA() != Other.GetA() ||
GetU0() != Other.GetU0() ||
GetU1() != Other.GetU1() ||
GetU2() != Other.GetU2() ||
GetV3() != Other.GetU3() ||
GetV0() != Other.GetV0() ||
GetV1() != Other.GetV1() ||
GetV2() != Other.GetV2() ||
GetV3() != Other.GetV3();
}
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
template<typename T>
struct TVoxelMaterialStorage
{
FORCEINLINE static TVoxelMaterialStorage<T> SerializeWithCustomConfig(FArchive& Ar, uint32 ConfigFlags)
{
check(Ar.IsLoading());
T R = 0;
T G = 0;
T B = 0;
T A = 0;
T U0 = 0;
T V0 = 0;
T U1 = 0;
T V1 = 0;
T U2 = 0;
T V2 = 0;
T U3 = 0;
T V3 = 0;
if (ConfigFlags & EVoxelMaterialConfigFlag::EnableR)
{
Ar << R;
}
if (ConfigFlags & EVoxelMaterialConfigFlag::EnableG)
{
Ar << G;
}
if (ConfigFlags & EVoxelMaterialConfigFlag::EnableB)
{
Ar << B;
}
if (ConfigFlags & EVoxelMaterialConfigFlag::EnableA)
{
Ar << A;
}
if (ConfigFlags & EVoxelMaterialConfigFlag::EnableUV0)
{
Ar << U0;
Ar << V0;
}
if (ConfigFlags & EVoxelMaterialConfigFlag::EnableUV1)
{
Ar << U1;
Ar << V1;
}
if (ConfigFlags & EVoxelMaterialConfigFlag::EnableUV2)
{
Ar << U2;
Ar << V2;
}
if (ConfigFlags & EVoxelMaterialConfigFlag::EnableUV3)
{
Ar << U3;
Ar << V3;
}
TVoxelMaterialStorage<T> Storage;
Storage.Impl_SetR(R);
Storage.Impl_SetG(G);
Storage.Impl_SetB(B);
Storage.Impl_SetA(A);
Storage.Impl_SetU0(U0);
Storage.Impl_SetV0(V0);
Storage.Impl_SetU1(U1);
Storage.Impl_SetV1(V1);
Storage.Impl_SetU2(U2);
Storage.Impl_SetV2(V2);
Storage.Impl_SetU3(U3);
Storage.Impl_SetV3(V3);
return Storage;
}
friend FORCEINLINE FArchive& operator<<(FArchive& Ar, TVoxelMaterialStorage<T>& Storage)
{
#if VOXEL_MATERIAL_ENABLE_R
Ar << Storage.R;
#endif
#if VOXEL_MATERIAL_ENABLE_G
Ar << Storage.G;
#endif
#if VOXEL_MATERIAL_ENABLE_B
Ar << Storage.B;
#endif
#if VOXEL_MATERIAL_ENABLE_A
Ar << Storage.A;
#endif
#if VOXEL_MATERIAL_ENABLE_UV0
Ar << Storage.U0;
Ar << Storage.V0;
#endif
#if VOXEL_MATERIAL_ENABLE_UV1
Ar << Storage.U1;
Ar << Storage.V1;
#endif
#if VOXEL_MATERIAL_ENABLE_UV2
Ar << Storage.U2;
Ar << Storage.V2;
#endif
#if VOXEL_MATERIAL_ENABLE_UV3
Ar << Storage.U3;
Ar << Storage.V3;
#endif
return Ar;
}
public:
static constexpr int32 NumChannels =
VOXEL_MATERIAL_ENABLE_R +
VOXEL_MATERIAL_ENABLE_G +
VOXEL_MATERIAL_ENABLE_B +
VOXEL_MATERIAL_ENABLE_A +
2 * VOXEL_MATERIAL_ENABLE_UV0 +
2 * VOXEL_MATERIAL_ENABLE_UV1 +
2 * VOXEL_MATERIAL_ENABLE_UV2 +
2 * VOXEL_MATERIAL_ENABLE_UV3;
static constexpr uint32 ChannelsMask =
EVoxelMaterialMask::R * VOXEL_MATERIAL_ENABLE_R +
EVoxelMaterialMask::G * VOXEL_MATERIAL_ENABLE_G +
EVoxelMaterialMask::B * VOXEL_MATERIAL_ENABLE_B +
EVoxelMaterialMask::A * VOXEL_MATERIAL_ENABLE_A +
EVoxelMaterialMask::UV0 * VOXEL_MATERIAL_ENABLE_UV0 +
EVoxelMaterialMask::UV1 * VOXEL_MATERIAL_ENABLE_UV1 +
EVoxelMaterialMask::UV2 * VOXEL_MATERIAL_ENABLE_UV2 +
EVoxelMaterialMask::UV3 * VOXEL_MATERIAL_ENABLE_UV3;
FORCEINLINE T& GetRaw(int32 Channel)
{
checkVoxelSlow(0 <= Channel && Channel < NumChannels);
return *(reinterpret_cast<T*>(this) + Channel);
}
FORCEINLINE T GetRaw(int32 Channel) const
{
checkVoxelSlow(0 <= Channel && Channel < NumChannels);
return *(reinterpret_cast<const T*>(this) + Channel);
}
FORCEINLINE T& operator[](int32 Channel)
{
return GetRaw(Channel);
}
FORCEINLINE T operator[](int32 Channel) const
{
return GetRaw(Channel);
}
public:
FORCEINLINE T Impl_GetR() const
{
#if VOXEL_MATERIAL_ENABLE_R
return R;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetG() const
{
#if VOXEL_MATERIAL_ENABLE_G
return G;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetB() const
{
#if VOXEL_MATERIAL_ENABLE_B
return B;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetA() const
{
#if VOXEL_MATERIAL_ENABLE_A
return A;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetU0() const
{
#if VOXEL_MATERIAL_ENABLE_UV0
return U0;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetU1() const
{
#if VOXEL_MATERIAL_ENABLE_UV1
return U1;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetU2() const
{
#if VOXEL_MATERIAL_ENABLE_UV2
return U2;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetU3() const
{
#if VOXEL_MATERIAL_ENABLE_UV3
return U3;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetV0() const
{
#if VOXEL_MATERIAL_ENABLE_UV0
return V0;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetV1() const
{
#if VOXEL_MATERIAL_ENABLE_UV1
return V1;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetV2() const
{
#if VOXEL_MATERIAL_ENABLE_UV2
return V2;
#else
return 0;
#endif
}
FORCEINLINE T Impl_GetV3() const
{
#if VOXEL_MATERIAL_ENABLE_UV3
return V3;
#else
return 0;
#endif
}
public:
FORCEINLINE void Impl_SetR(T Value)
{
#if VOXEL_MATERIAL_ENABLE_R
R = Value;
#endif
}
FORCEINLINE void Impl_SetG(T Value)
{
#if VOXEL_MATERIAL_ENABLE_G
G = Value;
#endif
}
FORCEINLINE void Impl_SetB(T Value)
{
#if VOXEL_MATERIAL_ENABLE_B
B = Value;
#endif
}
FORCEINLINE void Impl_SetA(T Value)
{
#if VOXEL_MATERIAL_ENABLE_A
A = Value;
#endif
}
FORCEINLINE void Impl_SetU0(T Value)
{
#if VOXEL_MATERIAL_ENABLE_UV0
U0 = Value;
#endif
}
FORCEINLINE void Impl_SetU1(T Value)
{
#if VOXEL_MATERIAL_ENABLE_UV1
U1 = Value;
#endif
}
FORCEINLINE void Impl_SetU2(T Value)
{
#if VOXEL_MATERIAL_ENABLE_UV2
U2 = Value;
#endif
}
FORCEINLINE void Impl_SetU3(T Value)
{
#if VOXEL_MATERIAL_ENABLE_UV3
U3 = Value;
#endif
}
FORCEINLINE void Impl_SetV0(T Value)
{
#if VOXEL_MATERIAL_ENABLE_UV0
V0 = Value;
#endif
}
FORCEINLINE void Impl_SetV1(T Value)
{
#if VOXEL_MATERIAL_ENABLE_UV1
V1 = Value;
#endif
}
FORCEINLINE void Impl_SetV2(T Value)
{
#if VOXEL_MATERIAL_ENABLE_UV2
V2 = Value;
#endif
}
FORCEINLINE void Impl_SetV3(T Value)
{
#if VOXEL_MATERIAL_ENABLE_UV3
V3 = Value;
#endif
}
private:
#if VOXEL_MATERIAL_ENABLE_R
T R;
#endif
#if VOXEL_MATERIAL_ENABLE_G
T G;
#endif
#if VOXEL_MATERIAL_ENABLE_B
T B;
#endif
#if VOXEL_MATERIAL_ENABLE_A
T A;
#endif
#if VOXEL_MATERIAL_ENABLE_UV0
T U0;
T V0;
#endif
#if VOXEL_MATERIAL_ENABLE_UV1
T U1;
T V1;
#endif
#if VOXEL_MATERIAL_ENABLE_UV2
T U2;
T V2;
#endif
#if VOXEL_MATERIAL_ENABLE_UV3
T U3;
T V3;
#endif
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// TODO Make not compatible with BP and have a serialization-safe BP version of it
USTRUCT(BlueprintType)
struct VOXEL_API FVoxelMaterial
#if CPP // Hide the template from UHT
: public TVoxelMaterialImpl<FVoxelMaterial>
, public TVoxelMaterialStorage<uint8>
#endif
{
GENERATED_BODY()
public:
FORCEINLINE FVoxelMaterial()
{
}
FORCEINLINE FVoxelMaterial(EForceInit)
: TVoxelMaterialImpl<FVoxelMaterial>(ForceInit)
{
}
FORCEINLINE FVoxelMaterial(const TVoxelMaterialStorage<uint8>& Storage)
: TVoxelMaterialStorage<uint8>(Storage)
{
}
FORCEINLINE bool Serialize(FArchive& Ar)
{
Ar << *this;
return true;
}
};
static_assert(FVoxelMaterial::NumChannels <= sizeof(FVoxelMaterial), "");
template <>
struct TTypeTraits<FVoxelMaterial> : TTypeTraitsBase<FVoxelMaterial>
{
enum
{
IsBytewiseComparable = true
};
};
template<>
struct TStructOpsTypeTraits<FVoxelMaterial> : TStructOpsTypeTraitsBase2<FVoxelMaterial>
{
enum
{
WithZeroConstructor = true,
WithNoInitConstructor = true,
WithNoDestructor = true,
WithSerializer = true,
WithIdenticalViaEquality = true
};
};