#pragma once
#include "../common_types.h"
#include "../vector_types.h"
#include <array>
#include <cstring>
#include <memory>
#include <span>
#include <type_traits>
#include <vector>
namespace whiteout::interfaces {
class WorkerPool;
}
namespace whiteout::utils {
enum class AttributeClass : u32 {
Position = 0,
Normal = 1,
Tangent = 2,
UV = 3,
Color = 4,
BlendIndices = 5,
BlendWeights = 6,
Binormal = 7,
Count,
};
enum class AttributeEncoding : u32 {
Float32, Float16, SNorm8, SNorm16, UNorm8, UNorm16, UInt8, UInt16, UInt32, Int8, Int16, Int32, };
using AttributeType = AttributeEncoding;
struct VertexBuffer {
struct Attribute {
AttributeClass attr_class;
AttributeEncoding encoding;
size_t component_count; size_t offset;
};
std::vector<u8> data; std::vector<Attribute> layout; u32 vertex_stride = 0;
size_t vertexCount() const;
size_t vertexSize() const;
size_t UVsNum() const;
bool hasVertexColors() const;
std::vector<Vector3f> getPositions() const;
std::vector<Vector3f> getNormals() const;
std::vector<Vector4f> getTangents() const;
std::vector<Vector2f> getUVs(size_t which) const;
std::vector<Vector2f> getUVs(size_t which, f32 uvMultiply, f32 uvOffset) const;
std::vector<Vector4f> getColors() const;
std::vector<std::array<u32, 4>> getBoneIndices() const;
std::vector<std::array<f32, 4>> getBoneWeights() const;
};
template <typename T, typename Enable = void>
struct vertex_traits;
template <>
struct vertex_traits<f32> {
static const size_t component_count = 1;
static const bool is_integer = false;
static f32 get_float(f32 v, size_t) {
return v;
}
};
template <>
struct vertex_traits<f64> {
static const size_t component_count = 1;
static const bool is_integer = false;
static f32 get_float(f64 v, size_t) {
return static_cast<f32>(v);
}
};
template <>
struct vertex_traits<f16> {
static const size_t component_count = 1;
static const bool is_integer = false;
static f32 get_float(f16 v, size_t) {
return v.to_float();
}
};
template <>
struct vertex_traits<u8> {
static const size_t component_count = 1;
static const bool is_integer = true;
static u32 get_uint(u8 v, size_t) {
return static_cast<u32>(v);
}
};
template <>
struct vertex_traits<u16> {
static const size_t component_count = 1;
static const bool is_integer = true;
static u32 get_uint(u16 v, size_t) {
return static_cast<u32>(v);
}
};
template <>
struct vertex_traits<u32> {
static const size_t component_count = 1;
static const bool is_integer = true;
static u32 get_uint(u32 v, size_t) {
return v;
}
};
template <>
struct vertex_traits<i8> {
static const size_t component_count = 1;
static const bool is_integer = true;
static u32 get_uint(i8 v, size_t) {
u32 r;
std::memcpy(&r, &v, sizeof(v));
i32 ext = static_cast<i32>(v);
std::memcpy(&r, &ext, sizeof(r));
return r;
}
};
template <>
struct vertex_traits<i16> {
static const size_t component_count = 1;
static const bool is_integer = true;
static u32 get_uint(i16 v, size_t) {
u32 r;
i32 ext = static_cast<i32>(v);
std::memcpy(&r, &ext, sizeof(r));
return r;
}
};
template <>
struct vertex_traits<i32> {
static const size_t component_count = 1;
static const bool is_integer = true;
static u32 get_uint(i32 v, size_t) {
u32 r;
std::memcpy(&r, &v, sizeof(r));
return r;
}
};
template <typename SInt>
struct vertex_traits<snorm<SInt>> {
static const size_t component_count = 1;
static const bool is_integer = false;
static f32 get_float(snorm<SInt> v, size_t) {
return static_cast<f32>(v);
}
};
template <typename UInt>
struct vertex_traits<unorm<UInt>> {
static const size_t component_count = 1;
static const bool is_integer = false;
static f32 get_float(unorm<UInt> v, size_t) {
return static_cast<f32>(v);
}
};
template <typename T>
struct vertex_traits<Vector2<T>, typename std::enable_if<!vertex_traits<T>::is_integer>::type> {
static const size_t component_count = 2;
static const bool is_integer = false;
static f32 get_float(const Vector2<T>& v, size_t i) {
return vertex_traits<T>::get_float(v.data[i], 0);
}
};
template <typename T>
struct vertex_traits<Vector2<T>, typename std::enable_if<vertex_traits<T>::is_integer>::type> {
static const size_t component_count = 2;
static const bool is_integer = true;
static u32 get_uint(const Vector2<T>& v, size_t i) {
return vertex_traits<T>::get_uint(v.data[i], 0);
}
};
template <typename T>
struct vertex_traits<Vector3<T>, typename std::enable_if<!vertex_traits<T>::is_integer>::type> {
static const size_t component_count = 3;
static const bool is_integer = false;
static f32 get_float(const Vector3<T>& v, size_t i) {
return vertex_traits<T>::get_float(v.data[i], 0);
}
};
template <typename T>
struct vertex_traits<Vector3<T>, typename std::enable_if<vertex_traits<T>::is_integer>::type> {
static const size_t component_count = 3;
static const bool is_integer = true;
static u32 get_uint(const Vector3<T>& v, size_t i) {
return vertex_traits<T>::get_uint(v.data[i], 0);
}
};
template <typename T>
struct vertex_traits<Vector4<T>, typename std::enable_if<!vertex_traits<T>::is_integer>::type> {
static const size_t component_count = 4;
static const bool is_integer = false;
static f32 get_float(const Vector4<T>& v, size_t i) {
return vertex_traits<T>::get_float(v.data[i], 0);
}
};
template <typename T>
struct vertex_traits<Vector4<T>, typename std::enable_if<vertex_traits<T>::is_integer>::type> {
static const size_t component_count = 4;
static const bool is_integer = true;
static u32 get_uint(const Vector4<T>& v, size_t i) {
return vertex_traits<T>::get_uint(v.data[i], 0);
}
};
template <>
struct vertex_traits<Quaternion> {
static const size_t component_count = 4;
static const bool is_integer = false;
static f32 get_float(const Quaternion& v, size_t i) {
return v.data[i];
}
};
template <typename T, size_t N>
struct vertex_traits<std::array<T, N>,
typename std::enable_if<!vertex_traits<T>::is_integer>::type> {
static const size_t component_count = N;
static const bool is_integer = false;
static f32 get_float(const std::array<T, N>& v, size_t i) {
return vertex_traits<T>::get_float(v[i], 0);
}
};
template <typename T, size_t N>
struct vertex_traits<std::array<T, N>,
typename std::enable_if<vertex_traits<T>::is_integer>::type> {
static const size_t component_count = N;
static const bool is_integer = true;
static u32 get_uint(const std::array<T, N>& v, size_t i) {
return vertex_traits<T>::get_uint(v[i], 0);
}
};
class VertexBufferBuilder {
public:
VertexBufferBuilder();
~VertexBufferBuilder();
VertexBufferBuilder(VertexBufferBuilder&&) noexcept;
VertexBufferBuilder& operator=(VertexBufferBuilder&&) noexcept;
VertexBufferBuilder(const VertexBufferBuilder&) = delete;
VertexBufferBuilder& operator=(const VertexBufferBuilder&) = delete;
template <typename T>
VertexBufferBuilder& declareAttribute(const std::vector<T>& src_data, AttributeClass attr_class,
AttributeEncoding encoding, size_t align = 0) {
return declareAttributeDispatch(
src_data, attr_class, encoding, align,
std::integral_constant<bool, vertex_traits<T>::is_integer>{});
}
VertexBufferBuilder& declareFloatAttribute(std::span<const f32> data, size_t components,
AttributeClass attr_class,
AttributeEncoding encoding, size_t align = 0) {
return declareAttributeFloat(data.data(), data.size() / components, components, attr_class,
encoding, align);
}
VertexBufferBuilder& declareIntAttribute(std::span<const u32> data, size_t components,
AttributeClass attr_class, AttributeEncoding encoding,
size_t align = 0) {
return declareAttributeUint(data.data(), data.size() / components, components, attr_class,
encoding, align);
}
VertexBuffer build(interfaces::WorkerPool* pool = nullptr) const;
private:
struct Impl;
std::unique_ptr<Impl> impl_;
VertexBufferBuilder& declareAttributeFloat(const f32* data, size_t vertex_count,
size_t components, AttributeClass attr_class,
AttributeEncoding encoding, size_t align);
VertexBufferBuilder& declareAttributeUint(const u32* data, size_t vertex_count,
size_t components, AttributeClass attr_class,
AttributeEncoding encoding, size_t align);
VertexBufferBuilder& declareAttributeFloatDirect(const f32* data, size_t vertex_count,
size_t components, AttributeClass attr_class,
AttributeEncoding encoding, size_t align);
template <typename T>
VertexBufferBuilder& declareAttributeDispatch(const std::vector<T>& src_data,
AttributeClass attr_class,
AttributeEncoding encoding, size_t align,
std::false_type ) {
typedef vertex_traits<T> Traits;
const size_t N = Traits::component_count;
const size_t vert_count = src_data.size();
std::vector<f32> flat(vert_count * N);
for (size_t v = 0; v < vert_count; ++v)
for (size_t i = 0; i < N; ++i)
flat[v * N + i] = Traits::get_float(src_data[v], i);
return declareAttributeFloat(flat.data(), vert_count, N, attr_class, encoding, align);
}
template <typename T>
VertexBufferBuilder& declareAttributeDispatch(const std::vector<T>& src_data,
AttributeClass attr_class,
AttributeEncoding encoding, size_t align,
std::true_type ) {
typedef vertex_traits<T> Traits;
const size_t N = Traits::component_count;
const size_t vert_count = src_data.size();
std::vector<u32> flat(vert_count * N);
for (size_t v = 0; v < vert_count; ++v)
for (size_t i = 0; i < N; ++i)
flat[v * N + i] = Traits::get_uint(src_data[v], i);
return declareAttributeUint(flat.data(), vert_count, N, attr_class, encoding, align);
}
};
template <>
inline VertexBufferBuilder& VertexBufferBuilder::declareAttributeDispatch<Vector2f>(
const std::vector<Vector2f>& src_data, AttributeClass attr_class, AttributeEncoding encoding,
size_t align, std::false_type) {
return declareAttributeFloatDirect(reinterpret_cast<const f32*>(src_data.data()),
src_data.size(), 2, attr_class, encoding, align);
}
template <>
inline VertexBufferBuilder& VertexBufferBuilder::declareAttributeDispatch<Vector3f>(
const std::vector<Vector3f>& src_data, AttributeClass attr_class, AttributeEncoding encoding,
size_t align, std::false_type) {
return declareAttributeFloatDirect(reinterpret_cast<const f32*>(src_data.data()),
src_data.size(), 3, attr_class, encoding, align);
}
template <>
inline VertexBufferBuilder& VertexBufferBuilder::declareAttributeDispatch<Vector4f>(
const std::vector<Vector4f>& src_data, AttributeClass attr_class, AttributeEncoding encoding,
size_t align, std::false_type) {
return declareAttributeFloatDirect(reinterpret_cast<const f32*>(src_data.data()),
src_data.size(), 4, attr_class, encoding, align);
}
template <>
inline VertexBufferBuilder& VertexBufferBuilder::declareAttributeDispatch<Quaternion>(
const std::vector<Quaternion>& src_data, AttributeClass attr_class, AttributeEncoding encoding,
size_t align, std::false_type) {
return declareAttributeFloatDirect(reinterpret_cast<const f32*>(src_data.data()),
src_data.size(), 4, attr_class, encoding, align);
}
template <>
inline VertexBufferBuilder& VertexBufferBuilder::declareAttributeDispatch<f32>(
const std::vector<f32>& src_data, AttributeClass attr_class, AttributeEncoding encoding,
size_t align, std::false_type) {
return declareAttributeFloatDirect(src_data.data(), src_data.size(), 1, attr_class, encoding,
align);
}
}