engine/engine/inc/uf/utils/mesh/mesh.h

715 lines
31 KiB
C++

#pragma once
#include <uf/utils/math/vector.h>
#include <uf/utils/math/matrix.h>
#include <uf/utils/math/quant.h>
#include <uf/utils/math/shapes.h>
#include <functional>
#include <uf/utils/memory/unordered_map.h>
#if UF_USE_VULKAN
#include <uf/ext/vulkan/enums.h>
#define RENDERER vulkan
#elif UF_USE_OPENGL
#include <uf/ext/opengl/enums.h>
#define RENDERER opengl
#endif
#if UF_USE_VULKAN
namespace uf {
namespace renderer = ext::vulkan;
}
#elif UF_USE_OPENGL
namespace uf {
namespace renderer = ext::opengl;
}
#endif
namespace ext {
namespace RENDERER {
#if UF_ENV_DREAMCAST && !UF_USE_OPENGL_GLDC
typedef uint16_t index_t;
#else
typedef uint32_t index_t;
#endif
struct UF_API AttributeDescriptor {
// essential for vertex input
size_t offset = 0;
size_t size = 0;
uf::renderer::enums::Format::type_t format = uf::renderer::enums::Format::UNDEFINED;
// not as essential
uf::stl::string name = "";
uf::renderer::enums::Type::type_t type = 0;
size_t components = 0;
bool operator==( const AttributeDescriptor& right ) const { return name == right.name;
/*
offset == right.offset &&
size == right.size &&
format == right.format &&
name == right.name &&
type == right.type &&
components == right.components;
*/
}
bool operator!=( const AttributeDescriptor& right ) const { return !(*this == right); };
};
}
}
namespace pod {
// stores information for a draw call
// used for GPU-driven indirection
// to-do: probably repurpose auxID and materialIDs
struct UF_API DrawCommand {
alignas(4) uint32_t indices = 0; // triangle count
alignas(4) uint32_t instances = 0; // instance count
alignas(4) uint32_t indexID = 0; // starting triangle position
alignas(4) uint32_t vertexID = 0; // starting vertex position
alignas(4) uint32_t instanceID = 0; // starting instance position
// extra data for padding
alignas(4) uint32_t auxID = 0; // used for storing which grid this belongs to when slicing, otherwise unused
alignas(4) uint32_t materialID = 0; // unused
alignas(4) uint32_t vertices = 0; // stores vertex count, should be unused
};
// stores index offsets for LODs
struct UF_API LODMetadata {
struct Level {
alignas(4) uint32_t indices = 0;
alignas(4) uint32_t vertexID = 0;
alignas(4) uint32_t indexID = 0;
alignas(4) uint32_t vertices = 0;
} levels[4];
};
// stores information about how to transform a draw call
// to-do: clean up this mess
struct UF_API Instance {
alignas(4) uint32_t materialID = 0; // index for material information
alignas(4) uint32_t primitiveID = 0; // index to reference the primitive(?)
alignas(4) uint32_t meshID = 0; // unused
alignas(4) uint32_t objectID = 0; // index for the object buffer
alignas(4) int32_t jointID = -1; // offset for skins(?)
alignas(4) int32_t lightmapID = -1; // index for lightmap to use
alignas(4) uint32_t imageID = 0; // unused?
alignas(4) uint32_t auxID = 0; // also the lightmap ID?
// AABB for this primitive
// should be for the specific draw call itself, rather than the mesh(let) entirely
struct Bounds {
pod::Vector3f min = { std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max() };
alignas(4) float padding1 = 0;
pod::Vector3f max = { -std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(), -std::numeric_limits<float>::max() };
alignas(4) float padding2 = 0;
} bounds;
// stores "pointers" on the GPU side for buffer locations, used for RT / recalculating barycentrics
struct UF_API Addresses {
alignas(8) uint64_t vertex{};
alignas(8) uint64_t index{};
alignas(8) uint64_t indirect{};
alignas(4) uint32_t drawID{};
alignas(4) uint32_t padding0{};
alignas(8) uint64_t position{};
alignas(8) uint64_t uv{};
alignas(8) uint64_t color{};
alignas(8) uint64_t st{};
alignas(8) uint64_t normal{};
alignas(8) uint64_t tangent{};
alignas(8) uint64_t joints{};
alignas(8) uint64_t weights{};
alignas(8) uint64_t id{};
alignas(8) uint64_t padding1{};
};
struct UF_API Object {
pod::Matrix4f model;
pod::Matrix4f previous;
pod::Vector4f color = { 1, 1, 1, 1 };
};
// Addresses addresses = {};
};
struct Primitive {
pod::DrawCommand drawCommand;
pod::Instance instance;
pod::LODMetadata lod;
};
}
namespace uf {
struct UF_API Mesh {
public:
static bool defaultInterleaved;
typedef uf::stl::vector<uint8_t> buffer_t;
struct Attribute {
uf::renderer::AttributeDescriptor descriptor;
int32_t buffer = -1;
size_t offset = 0;
size_t stride = 0;
size_t length = 0;
// void* pointer = NULL;
uint8_t* pointer = NULL;
};
struct Input {
uf::stl::vector<Attribute> attributes;
size_t count = 0; // how many elements is the input using
size_t first = 0; // base index to start from
size_t size = 0; // size of one element in the input's buffer
size_t offset = 0; // bytes to offset from within the associated buffer
int32_t interleaved = -1; // index to interleaved buffer if in bounds
} vertex, index, instance, indirect;
struct AttributeView {
Attribute attribute;
const void* data(size_t first = 0) const {
if ( !valid() ) return NULL;
return static_cast<const uint8_t*>(attribute.pointer) + attribute.stride * first;
}
template<typename T>
const T* get(size_t first = 0) const {
return reinterpret_cast<const T*>(data(first));
}
bool valid() const { return attribute.pointer != NULL; }
size_t stride() const { return attribute.stride; }
size_t components() const { return attribute.descriptor.components; }
uf::renderer::enums::Type::type_t type() const { return attribute.descriptor.type; }
};
struct View {
uf::Mesh::Input vertex;
uf::Mesh::Input index;
int32_t indirectIndex = -1;
uf::stl::unordered_map<uf::stl::string, uf::Mesh::AttributeView> attributes;
bool has( const uf::stl::string& name ) const {
return attributes.count( name ) > 0;
}
const AttributeView& operator[]( const uf::stl::string& name ) const {
if ( auto it = attributes.find(name); it != attributes.end() ) return it->second;
UF_EXCEPTION("invalid view: {}", name);
}
// to-do: resolve dependency order hell
// these probably won't be directly called anyways?
#if 0
size_t fetchIndex( size_t index ) {
return uf::mesh::fetchIndex( index );
}
size_t fetchIndex( const uf::Mesh::AttributeView& indices, size_t index ) {
return uf::mesh::fetchIndex( indices, index );
}
size_t fetchIndex( const uf::stl::string& indices, size_t index ) {
return uf::mesh::fetchIndex( indices, index );
}
pod::Vector3f fetchVertex( size_t index ) {
return uf::mesh::fetchVertex( index );
}
pod::Vector3f fetchVertex( const uf::Mesh::AttributeView& positions, size_t index ) {
return uf::mesh::fetchVertex( positions, index );
}
pod::Vector3f fetchVertex( const uf::stl::string& positions, size_t index ) {
return uf::mesh::fetchVertex( positions, index );
}
pod::TriangleWithNormal fetchTriangle( size_t triID ) {
return uf::mesh::fetchTriangle( *this, triID );
}
pod::TriangleWithNormal fetchTriangle( const uf::Mesh::AttributeView& indices, const uf::Mesh::AttributeView& positions, size_t triID ) {
return uf::mesh::fetchTriangle( *this, indices, positions, triID );
}
pod::TriangleWithNormal fetchTriangle( const uf::stl::string& indices, const uf::stl::string& positions, size_t triID ) {
auto& view = *this;
return uf::mesh::fetchTriangle( view, view[indices], view[positions], triID );
}
#endif
};
typedef uf::stl::vector<uf::Mesh::View> views_t;
uf::stl::vector<buffer_t> buffers;
// crunge, but it's better this way for streaming in mesh data
uf::stl::vector<uf::stl::string> buffer_paths;
// mega cringe, but i'd like to have a way to cache it
uf::stl::vector<uf::Mesh::View> buffer_views;
protected:
void _destroy( uf::Mesh::Input& input );
void _bind( bool interleaved = uf::Mesh::defaultInterleaved );
void _updateDescriptor( uf::Mesh::Input& input );
void _updateViews();
uf::Mesh::Attribute _remapAttribute( const uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute, size_t i = 0 ) const;
bool _hasV( const uf::Mesh::Input& input, const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) const;
bool _hasV( const uf::Mesh::Input& input, const uf::Mesh::Input& src ) const;
void _bindV( uf::Mesh::Input& input, const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors );
void _resizeVs( uf::Mesh::Input& input, size_t count );
void _reserveVs( uf::Mesh::Input& input, size_t count );
void _insertV( uf::Mesh::Input& input, const void* data );
void _insertVs( uf::Mesh::Input& input, const void* data, size_t size );
void _insertVs( uf::Mesh::Input& input, const uf::Mesh& mesh, const uf::Mesh::Input& srcInput );
template<typename T> inline bool _hasV( const uf::Mesh::Input& input ) const { return _hasV( input, T::descriptor ); }
template<typename T> inline void _bindV( uf::Mesh::Input& input ) { return _bindV( input, T::descriptor ); }
template<typename T> inline void _insertV( uf::Mesh::Input& input, const T& vertex ) { return _insertV( input, (const void*) &vertex ); }
template<typename T> inline void _insertVs( uf::Mesh::Input& input, const uf::stl::vector<T>& vs ) { return _insertVs( input, (const void*) vs.data(), vs.size() ); }
void _bindI( uf::Mesh::Input& input, size_t size, uf::renderer::enums::Type::type_t type, size_t count = 1 );
void _reserveIs( uf::Mesh::Input& input, size_t count, size_t i = 0 );
void _resizeIs( uf::Mesh::Input& input, size_t count, size_t i = 0 );
void _insertI( uf::Mesh::Input& input, const void* data, size_t i );
void _insertIs( uf::Mesh::Input& input, const void* data, size_t size, size_t i );
void _insertIs( uf::Mesh::Input& input, const uf::Mesh& mesh, const uf::Mesh::Input& srcInput );
template<typename U> inline void _bindI( uf::Mesh::Input& input, size_t indices = 1 ) { return _bindI( input, sizeof(U), uf::renderer::typeToEnum<U>(), indices ); }
template<typename U> inline void _insertI( uf::Mesh::Input& input, U index, size_t i = 0 ) { return _insertI( input, (const void*) &index, i ); }
template<typename U> inline void _insertIs( uf::Mesh::Input& input, const uf::stl::vector<U>& is, size_t i = 0 ) { return _insertIs( input, (const void*) is.data(), is.size(), i ); }
public:
void initialize();
void destroy();
uf::Mesh convert() const;
uf::Mesh copy() const;
uf::Mesh copy(bool) const;
uf::Mesh interleave() const;
uf::Mesh deinterleave() const;
uf::Mesh expand();
uf::Mesh expand(bool);
void updateDescriptor();
void bind( const uf::Mesh& );
void bind( const uf::Mesh&, bool );
void insert( const uf::Mesh& );
void generateIndices();
void generateIndirect();
bool isInterleaved() const;
bool isInterleaved( const uf::Mesh::Input& ) const;
bool isInterleaved( size_t ) const;
buffer_t& getBuffer( const uf::Mesh::Input&, size_t = 0 );
buffer_t& getBuffer( const uf::Mesh::Input&, const uf::Mesh::Attribute& );
const buffer_t& getBuffer( const uf::Mesh::Input&, size_t = 0 ) const;
const buffer_t& getBuffer( const uf::Mesh::Input&, const uf::Mesh::Attribute& ) const;
void clearAttribute( uf::Mesh::Input&, const uf::Mesh::Attribute& );
void clearAttribute( uf::Mesh::Input&, size_t );
void clear();
uf::Mesh::Input remapInput( const uf::Mesh::Input&, size_t = 0, size_t = 0 ) const;
uf::Mesh::Input remapVertexInput( size_t i = 0, size_t = 0 ) const;
uf::Mesh::Input remapIndexInput( size_t i = 0, size_t = 0 ) const;
void print( bool = true ) const;
std::string printVertices( bool = true ) const;
std::string printIndices( bool = true ) const;
std::string printInstances( bool = true ) const;
std::string printIndirects( bool = true ) const;
uf::Mesh::View makeView( const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
uf::Mesh::View makeView( size_t commandIndex, const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
uf::stl::vector<uf::Mesh::View> makeViews( const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
inline bool hasVertex( const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) const { return _hasV( vertex, descriptors ); }
inline bool hasVertex( const uf::Mesh& mesh ) const { return _hasV( vertex, mesh.vertex ); }
inline void bindVertex( const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) { return _bindV( vertex, descriptors ); }
inline void resizeVertices( size_t count ) { return _resizeVs( vertex, count ); }
inline void reserveVertices( size_t count ) { return _reserveVs( vertex, count ); }
inline void insertVertex( const void* data ) { return _insertV( vertex, data ); }
inline void insertVertices( const void* data, size_t size ) { return _insertVs( vertex, data, size ); }
inline void insertVertices( const uf::Mesh& mesh ) { return _insertVs( vertex, mesh, mesh.vertex ); }
inline void updateVertexDescriptor() { return _updateDescriptor( vertex ); }
inline uf::Mesh::Attribute remapVertexAttribute( const uf::Mesh::Attribute& attribute, size_t i = 0 ) const { return _remapAttribute( vertex, attribute, i ); }
template<typename T> inline bool hasVertex() const { return _hasV( vertex, T::descriptor ); }
template<typename T> inline void bindVertex() { return _bindV( vertex, T::descriptor ); }
template<typename T> inline void insertVertex( const T& v ) { return _insertV( vertex, (const void*) &v ); }
template<typename T> inline void insertVertices( const uf::stl::vector<T>& vertices ) { return _insertVs( vertex, (const void*) vertices.data(), vertices.size() ); }
inline void bindIndex( size_t size, uf::renderer::enums::Type::type_t type, size_t count = 1 ) { return _bindI( index, size, type, count ); }
inline void reserveIndices( size_t count, size_t i = 0 ) { return _reserveIs( index, count, i ); }
inline void resizeIndices( size_t count, size_t i = 0 ) { return _resizeIs( index, count, i ); }
inline void insertIndex( const void* data, size_t i = 0 ) { return _insertI( index, data, i ); }
inline void insertIndices( const void* data, size_t size, size_t i = 0 ) { return _insertIs( index, data, size, i ); }
inline void insertIndices( const uf::Mesh& mesh ) { return _insertIs( index, mesh, mesh.index ); }
inline void updateIndexDescriptor() { return _updateDescriptor( index ); }
inline uf::Mesh::Attribute remapIndexAttribute( const uf::Mesh::Attribute& attribute, size_t i = 0 ) const { return _remapAttribute( index, attribute, i ); }
template<typename U> inline void bindIndex( size_t count = 1 ) { return _bindI( index, sizeof(U), uf::renderer::typeToEnum<U>(), count ); }
template<typename U> inline void insertIndex( U I, size_t i = 0 ) { return _insertI( index, (const void*) &I, i ); }
template<typename U> inline void insertIndices( const uf::stl::vector<U>& indices, size_t i = 0 ) { return _insertIs( index, (const void*) indices.data(), indices.size(), i ); }
inline bool hasInstance( const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) const { return _hasV( instance, descriptors ); }
inline bool hasInstance( const uf::Mesh& mesh ) const { return _hasV( instance, mesh.instance ); }
inline void bindInstance( const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) { return _bindV( instance, descriptors ); }
inline void resizeInstances( size_t count ) { return _resizeVs( instance, count ); }
inline void reserveInstances( size_t count ) { return _reserveVs( instance, count ); }
inline void insertInstance( const void* data ) { return _insertV( instance, data ); }
inline void insertInstances( const void* data, size_t size ) { return _insertVs( instance, data, size ); }
inline void insertInstances( const uf::Mesh& mesh ) { return _insertVs( instance, mesh, mesh.instance ); }
inline void updateInstanceDescriptor() { return _updateDescriptor( instance ); }
template<typename T> inline bool hasInstance() const { return _hasV( instance, T::descriptor ); }
template<typename T> inline void bindInstance() { return _bindV( instance, T::descriptor ); }
template<typename T> inline void insertInstance( const T& v ) { return _insertV( instance, (const void*) &v ); }
template<typename T> inline void insertInstances( const uf::stl::vector<T>& instances ) { return _insertVs( instance, (const void*) instances.data(), instances.size() ); }
inline void bindIndirect( size_t size, uf::renderer::enums::Type::type_t type, size_t count = 1 ) { return _bindI( indirect, size, type, count ); }
inline void reserveIndirects( size_t count, size_t i = 0 ) { return _reserveIs( indirect, count, i ); }
inline void resizeIndirects( size_t count, size_t i = 0 ) { return _resizeIs( indirect, count, i ); }
inline void insertIndirect( const void* data, size_t i = 0 ) { return _insertI( indirect, data, i ); }
inline void insertIndirects( const void* data, size_t size, size_t i = 0 ) { return _insertIs( indirect, data, size, i ); }
inline void insertIndirects( const uf::Mesh& mesh ) { return _insertIs( indirect, mesh, mesh.indirect ); }
inline void updateIndirectDescriptor() { return _updateDescriptor( indirect ); }
template<typename U> inline void bindIndirect( size_t i = 1 ) { return _bindI( indirect, sizeof(U), uf::renderer::typeToEnum<U>(), i ); }
template<typename U> inline void insertIndirect( U v, size_t i = 0 ) { return _insertI( indirect, (const void*) &v, i ); }
template<typename U> inline void insertIndirects( const uf::stl::vector<U>& indirects, size_t i = 0 ) { return _insertIs( indirect, (const void*) indirects.data(), indirects.size(), i ); }
template<typename T, typename U = uf::renderer::index_t>
void bind( bool interleave = uf::Mesh::defaultInterleaved, size_t indices = 1 ) {
bindVertex<T>();
bindIndex<U>( indices );
_bind( interleave );
}
template<typename From, typename To>
void convert() {
if ( this->isInterleaved() ) {
UF_MSG_DEBUG("Downcasting/upcasting requested yet mesh is interleaved, ignoring...");
return;
}
auto fromEnum = uf::renderer::typeToEnum<From>();
auto toEnum = uf::renderer::typeToEnum<To>();
if ( toEnum == fromEnum ) return;
for ( auto& attribute : this->vertex.attributes ) {
if ( attribute.descriptor.type == toEnum ) continue;
if ( attribute.descriptor.type != fromEnum ) continue;
size_t elements = this->vertex.count * attribute.descriptor.components;
size_t bytes = elements * sizeof(To);
auto& srcBuffer = this->buffers[attribute.buffer];
if ( srcBuffer.empty() ) continue;
uf::stl::vector<uint8_t> dstBuffer( bytes );
From* srcPtr = (From*) (srcBuffer.data());
To* dstPtr = (To*) (dstBuffer.data());
if ( toEnum == uf::renderer::enums::Type::USHORT ) {
for ( size_t i = 0; i < elements; ++i ) dstPtr[i] = (To) uf::quant::quantize_f32u16(srcPtr[i]);
} else if ( fromEnum == uf::renderer::enums::Type::USHORT ) {
for ( size_t i = 0; i < elements; ++i ) dstPtr[i] = (To) uf::quant::dequantize_u16f32(srcPtr[i]);
} else {
for ( size_t i = 0; i < elements; ++i ) dstPtr[i] = (To) srcPtr[i];
}
srcBuffer.swap( dstBuffer );
attribute.pointer = (uint8_t*) ( srcBuffer.data() );
attribute.descriptor.type = toEnum;
attribute.descriptor.size = sizeof(To) * attribute.descriptor.components;
attribute.length = sizeof(To) * elements;
if ( toEnum == uf::renderer::enums::Type::FLOAT ) {
switch ( attribute.descriptor.components ) {
case 1: attribute.descriptor.format = uf::renderer::enums::Format::R32_SFLOAT; break;
case 2: attribute.descriptor.format = uf::renderer::enums::Format::R32G32_SFLOAT; break;
case 3: attribute.descriptor.format = uf::renderer::enums::Format::R32G32B32_SFLOAT; break;
case 4: attribute.descriptor.format = uf::renderer::enums::Format::R32G32B32A32_SFLOAT; break;
}
} else if ( toEnum == uf::renderer::enums::Type::FLOAT16 ) {
switch ( attribute.descriptor.components ) {
case 1: attribute.descriptor.format = uf::renderer::enums::Format::R16_SFLOAT; break;
case 2: attribute.descriptor.format = uf::renderer::enums::Format::R16G16_SFLOAT; break;
case 3: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16_SFLOAT; break;
case 4: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16A16_SFLOAT; break;
}
} else if ( toEnum == uf::renderer::enums::Type::USHORT ) {
switch ( attribute.descriptor.components ) {
case 1: attribute.descriptor.format = uf::renderer::enums::Format::R16_UINT; break;
case 2: attribute.descriptor.format = uf::renderer::enums::Format::R16G16_UINT; break;
case 3: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16_UINT; break;
case 4: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16A16_UINT; break;
}
}
}
}
};
}
namespace ext {
namespace RENDERER {
struct UF_API GraphicDescriptor {
typedef size_t hash_t;
uf::stl::string renderMode = "";
uf::stl::string pipeline = "";
hash_t material = {};
uint32_t renderTarget = 0;
uint32_t subpass = 0;
uint32_t aux = 0;
struct {
size_t width = 1;
size_t height = 1;
size_t depth = 1;
uint32_t point = 0;
} bind;
struct {
uf::Mesh::Input vertex, index, instance, indirect;
size_t bufferOffset = 0;
} inputs;
uf::renderer::enums::PrimitiveTopology::type_t topology = uf::renderer::enums::PrimitiveTopology::TRIANGLE_LIST;
uf::renderer::enums::PolygonMode::type_t fill = uf::renderer::enums::PolygonMode::FILL;
uf::renderer::enums::CullMode::type_t cullMode = uf::renderer::enums::CullMode::BACK;
uf::renderer::enums::Face::type_t frontFace = uf::renderer::enums::Face::CW;
float lineWidth = 1.0f;
struct {
bool test = true;
bool write = true;
uf::renderer::enums::Compare::type_t operation = uf::renderer::enums::Compare::GREATER_OR_EQUAL;
struct {
bool enable = false;
float constant = 0;
float slope = 0;
float clamp = 0;
} bias;
} depth;
// to-do: port the rest
struct {
bool enabled = true;
#if UF_USE_VULKAN
VkBlendFactor srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
VkBlendFactor dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
VkBlendOp colorBlendOp = VK_BLEND_OP_ADD;
VkBlendFactor srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
VkBlendFactor dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
VkBlendOp alphaBlendOp = VK_BLEND_OP_ADD;
VkColorComponentFlags colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
#endif
} blend;
bool invalidated = false;
hash_t hash() const;
void parse( ext::json::Value& );
bool operator==( const GraphicDescriptor& right ) const { return this->hash() == right.hash(); }
bool operator!=( const GraphicDescriptor& right ) const { return this->hash() != right.hash(); }
};
}
}
namespace std {
template <>
struct hash<uf::renderer::GraphicDescriptor> {
size_t operator()(const uf::renderer::GraphicDescriptor& descriptor) const { return descriptor.hash(); }
};
}
#undef UF_RENDERER
#define UF_VERTEX_DESCRIPTION( TYPE, FORMAT, ATTRIBUTE ) {\
.offset = offsetof(TYPE, ATTRIBUTE),\
.size = sizeof(decltype(TYPE::ATTRIBUTE)),\
.format = uf::renderer::enums::Format::FORMAT,\
.name = #ATTRIBUTE,\
.type = uf::renderer::typeToEnum<decltype(TYPE::ATTRIBUTE)::type_t>(),\
.components = decltype(TYPE::ATTRIBUTE)::size,\
},
#define UF_VERTEX_DESCRIPTOR( TYPE, ... )\
uf::stl::vector<uf::renderer::AttributeDescriptor> TYPE::descriptor = { __VA_ARGS__ };
#define UF_VERTEX_INTERPOLATE( TYPE, ... )\
TYPE UF_API TYPE::interpolate( const TYPE& p1, const TYPE& p2, float t ) __VA_ARGS__
namespace pod {
struct /*UF_API*/ Vertex_3F2F3F4F {
pod::Vector3f position;
pod::Vector2f uv;
pod::Vector3f normal;
pod::Vector4f color;
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Vertex_3F2F3F4F interpolate( const Vertex_3F2F3F4F& p1, const Vertex_3F2F3F4F& p2, float t );
};
struct /*UF_API*/ Vertex_3F2F3F32B {
pod::Vector3f position;
pod::Vector2f uv;
pod::Vector3f normal;
pod::Vector4t<uint8_t> color;
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Vertex_3F2F3F32B interpolate( const Vertex_3F2F3F32B& p1, const Vertex_3F2F3F32B& p2, float t );
};
struct /*UF_API*/ Vertex_3F3F3F {
pod::Vector3f position;
pod::Vector3f uv;
pod::Vector3f normal;
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Vertex_3F3F3F interpolate( const Vertex_3F3F3F& p1, const Vertex_3F3F3F& p2, float t );
};
struct /*UF_API*/ Vertex_3F2F3F1UI {
pod::Vector3f position;
pod::Vector2f uv;
pod::Vector3f normal;
pod::Vector1ui id;
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Vertex_3F2F3F1UI interpolate( const Vertex_3F2F3F1UI& p1, const Vertex_3F2F3F1UI& p2, float t );
};
struct /*UF_API*/ Vertex_3F2F3F {
pod::Vector3f position;
pod::Vector2f uv;
pod::Vector3f normal;
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Vertex_3F2F3F interpolate( const Vertex_3F2F3F& p1, const Vertex_3F2F3F& p2, float t );
};
struct /*UF_API*/ Vertex_3F2F {
pod::Vector3f position;
pod::Vector2f uv;
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Vertex_3F2F interpolate( const Vertex_3F2F& p1, const Vertex_3F2F& p2, float t );
};
struct /*UF_API*/ Vertex_2F2F {
pod::Vector2f position;
pod::Vector2f uv;
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Vertex_2F2F interpolate( const Vertex_2F2F& p1, const Vertex_2F2F& p2, float t );
};
struct /*UF_API*/ Vertex_3F {
pod::Vector3f position;
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Vertex_3F interpolate( const Vertex_3F& p1, const Vertex_3F& p2, float t );
};
}
namespace uf {
template<typename T = pod::Vertex_3F, typename U = uf::renderer::index_t>
struct UF_API Mesh_T {
typedef T vertex_t;
typedef U index_t;
uf::stl::vector<vertex_t> vertices;
uf::stl::vector<index_t> indices;
uf::stl::vector<pod::Primitive> primitives;
};
template<typename T = pod::Vertex_3F, typename U = uf::renderer::index_t>
struct UF_API Meshlet_T {
typedef T vertex_t;
typedef U index_t;
uf::stl::vector<vertex_t> vertices;
uf::stl::vector<index_t> indices;
pod::Primitive primitive;
};
namespace mesh {
size_t UF_API fetchIndex( const void* pointer, size_t stride, size_t index );
pod::Vector3f UF_API fetchVertex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& positions, size_t index );
pod::Triangle UF_API fetchTriangle( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, const uf::Mesh::AttributeView& positions, size_t triID );
pod::TriangleWithNormal UF_API fetchTriangle( const uf::Mesh& mesh, size_t triID );
static inline size_t fetchIndex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, size_t index ) {
return uf::mesh::fetchIndex( indices.data(view.index.first), indices.stride(), index );
}
// for clean code, these would be preferable
// but they incur additional lookups every triangle fetch, and I doubt the optimizer will optimize that away, so explicitly passing attribute views is preferable
static inline size_t fetchIndex( const uf::Mesh::View& view, size_t index ) {
return uf::mesh::fetchIndex( view, view["indices"], index );
}
static inline size_t fetchIndex( const uf::Mesh::View& view, const uf::stl::string& indices, size_t index ) {
return uf::mesh::fetchIndex( view, view[indices], index );
}
static inline pod::Vector3f fetchVertex( const uf::Mesh::View& view, size_t index ) {
return uf::mesh::fetchVertex( view, view["positions"], index );
}
static inline pod::Vector3f fetchVertex( const uf::Mesh::View& view, const uf::stl::string& positions, size_t index ) {
return uf::mesh::fetchVertex( view, view[positions], index );
}
static inline pod::Triangle fetchTriangle( const uf::Mesh::View& view, const uf::stl::string& indices, const uf::stl::string& positions, size_t triID ) {
return uf::mesh::fetchTriangle( view, view[indices], view[positions], triID );
}
static inline pod::Triangle fetchTriangle( const uf::Mesh::View& view, size_t triID ) {
return uf::mesh::fetchTriangle( view, view["index"], view["position"], triID );
}
template<typename T>
T fetchVertexAttribute( const uf::Mesh::View& view, const uf::Mesh::AttributeView& attributeView, size_t index ) {
#define CAST_VERTEX(type) {\
const type* vertices = (type*) attributeView.data(view.vertex.first + index);\
for ( auto i = 0; i < T::size; ++i ) res[i] = vertices[i];\
return res;\
}
#define DEQUANTIZE_VERTEX(type) {\
const type* vertices = (type*) attributeView.data(view.vertex.first + index);\
for ( auto i = 0; i < T::size; ++i ) res[i] = uf::quant::dequantize(vertices[i]);\
return res;\
}
// direct copy
if ( uf::renderer::typeToEnum<typename T::type_t>() == attributeView.type() && T::size == attributeView.components() ) {
return uf::vector::copy<typename T::type_t, T::size>( (typename T::type_t*) attributeView.data( view.vertex.first + index ) );
}
// implicit copy
T res;
switch ( attributeView.type() ) {
// dequantize
case uf::renderer::enums::Type::USHORT:
case uf::renderer::enums::Type::SHORT: {
DEQUANTIZE_VERTEX(uint16_t);
} break;
case uf::renderer::enums::Type::FLOAT: {
CAST_VERTEX(float);
} break;
#if UF_USE_FLOAT16
case uf::renderer::enums::Type::HALF: {
CAST_VERTEX(std::float16_t);
} break;
#endif
#if UF_USE_BFLOAT16
case uf::renderer::enums::Type::BFLOAT: {
CAST_VERTEX(std::bfloat16_t);
} break;
#endif
default: UF_EXCEPTION("unsupported attribute type: {}", attributeView.attribute.descriptor.type); break;
}
}
}
}