From 06e88cbf17f028a9ebf7af446459697b7e307acd Mon Sep 17 00:00:00 2001 From: ecker Date: Thu, 4 Jun 2026 19:56:45 -0500 Subject: [PATCH] cleanup (strictly de-interleaved meshes, cleaned up atlas packing and xatlas unwrapping, dropped mesh attribute ID since it's not necessary with draw command/instance indirectin) --- bin/data/config.json | 1 - bin/data/shaders/common/structs.h | 4 - bin/data/shaders/graph/base/vert.h | 5 +- dep/include/binpack2d/binpack2d.hpp | 595 ----------------- dep/include/stb/stb_rect_pack.h | 623 ++++++++++++++++++ engine/inc/uf/engine/graph/mesh.inl | 6 - engine/inc/uf/engine/graph/pod.inl | 8 - engine/inc/uf/ext/valve/common.h | 4 +- engine/inc/uf/utils/image/image.h | 16 +- engine/inc/uf/utils/memory/string.h | 8 + engine/inc/uf/utils/memory/unordered_map.h | 9 +- engine/inc/uf/utils/mesh/grid.h | 70 +- engine/inc/uf/utils/mesh/mesh.h | 273 +++++--- engine/inc/uf/utils/string/hash.h | 21 +- engine/src/engine/ext/ext.cpp | 1 - engine/src/engine/ext/gui/behavior.cpp | 4 +- engine/src/engine/graph/animation.cpp | 6 +- engine/src/engine/graph/convert.cpp | 4 +- engine/src/engine/graph/decode.cpp | 30 +- engine/src/engine/graph/encode.cpp | 35 - engine/src/engine/graph/graph.cpp | 213 +++--- engine/src/ext/gltf/gltf.cpp | 43 +- engine/src/ext/gltf/processPrimitives.inl | 71 +- engine/src/ext/meshopt/meshopt.cpp | 15 +- engine/src/ext/opengl/graphic.cpp | 15 +- engine/src/ext/opengl/opengl.cpp | 8 +- engine/src/ext/valve/bsp.cpp | 36 +- engine/src/ext/valve/mdl.cpp | 83 ++- engine/src/ext/vulkan/graphic.cpp | 58 +- engine/src/ext/xatlas/xatlas.cpp | 34 +- engine/src/utils/image/atlas.cpp | 93 ++- .../src/utils/math/physics/broadphase/bvh.cpp | 8 +- engine/src/utils/math/physics/common.cpp | 4 +- .../utils/math/physics/narrowphase/epa.cpp | 4 +- .../utils/math/physics/narrowphase/gjk.cpp | 2 +- engine/src/utils/mesh/mesh.cpp | 418 ++++-------- 36 files changed, 1221 insertions(+), 1607 deletions(-) delete mode 100644 dep/include/binpack2d/binpack2d.hpp create mode 100644 dep/include/stb/stb_rect_pack.h diff --git a/bin/data/config.json b/bin/data/config.json index e8b18e7a..921cb069 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -3,7 +3,6 @@ "scenes": { "start": "StartMenu", "matrix": { "reverseInfinite": true }, - "meshes": { "interleaved": false }, "lights": { "enabled": true, "lightmaps": true, "max": 32, diff --git a/bin/data/shaders/common/structs.h b/bin/data/shaders/common/structs.h index 143aedd4..c66d8c42 100644 --- a/bin/data/shaders/common/structs.h +++ b/bin/data/shaders/common/structs.h @@ -144,9 +144,6 @@ struct InstanceAddresses { uint64_t joints; uint64_t weights; - - uint64_t id; - uint64_t padding1; }; struct Object { @@ -301,7 +298,6 @@ struct Vertex { vec2 st; vec3 normal; vec3 tangent; - uint id; uvec2 joints; vec4 weights; }; diff --git a/bin/data/shaders/graph/base/vert.h b/bin/data/shaders/graph/base/vert.h index 78a3eabf..c6004d5a 100644 --- a/bin/data/shaders/graph/base/vert.h +++ b/bin/data/shaders/graph/base/vert.h @@ -11,10 +11,9 @@ layout (location = 2) in vec4 inColor; layout (location = 3) in vec2 inSt; layout (location = 4) in vec3 inNormal; layout (location = 5) in vec4 inTangent; -layout (location = 6) in uvec2 inId; #if SKINNED - layout (location = 7) in uvec4 inJoints; - layout (location = 8) in vec4 inWeights; + layout (location = 6) in uvec4 inJoints; + layout (location = 7) in vec4 inWeights; #endif layout( push_constant ) uniform PushBlock { diff --git a/dep/include/binpack2d/binpack2d.hpp b/dep/include/binpack2d/binpack2d.hpp deleted file mode 100644 index c79c05a3..00000000 --- a/dep/include/binpack2d/binpack2d.hpp +++ /dev/null @@ -1,595 +0,0 @@ -/** - * BinPack2D is a 2 dimensional, multi-bin, bin-packer. ( Texture Atlas Array! ) - * It supports an arbitrary number of bins, at arbitrary sizes. - * rectangles can be added one at a time, chunks at a time, or all at once. - * rectangles that dont fit are reported back. - * Data can be associated to rectangles before processing via a template, and recalled after processing. - * - * There is no documentation, See ExampleProgram() below for a taste. - * - * Instead of tracking 'free rectangles' like other solutions I've found online, - * this algorithm tracks free 'top lefts', keeps them sorted by closest to origin, and puts new rectangles into - * the first free top left that doesnt collide. Consuming a top left creates 2 new top lefts (x+w,y) and (x,y+h). - * If a rectangle doesnt fit into a bin, before condisering the next bin, the current bin is re-tried with the rectangle rotated. - * This SOMTIMES helps... but not always.. i might disable this in future !? - * - * This Header was origonally part of my rh_texture_packer program. - * A program I wrote to take advantage of my nexus-7's GL_EXT_texture_array extension. - * I wanted to be able to render out whole scenes with a single glDraw* - * blah blah blah... - */ - - -/** ***** EXAMPLE CODE ************************************** - - // Your data - whatever you want to associate with 'rectangle' - class MyContent { - public: - std::string str; - MyContent() : str("default string") {} - MyContent(const std::string &str) : str(str) {} - }; - - int ExampleProgram() { - - srandom(0x69); - - // Create some 'content' to work on. - BinPack2D::ContentAccumulator inputContent; - - for(int i=0;i<20;i++) { - - // random size for this content - int width = ((random() % 32)+1) * ((random() % 10)+1); - int height = ((random() % 32)+1) * ((random() % 10)+1); - - // whatever data you want to associate with this content - std::stringstream ss; - ss << "box " << i; - MyContent mycontent( ss.str().c_str() ); - - // Add it - inputContent += BinPack2D::Content(mycontent, BinPack2D::Coord(), BinPack2D::Size(width, height), false ); - } - - // Sort the input content by size... usually packs better. - inputContent.Sort(); - - // Create some bins! ( 2 bins, 128x128 in this example ) - BinPack2D::CanvasArray canvasArray = - BinPack2D::UniformCanvasArrayBuilder(128,128,2).Build(); - - // A place to store content that didnt fit into the canvas array. - BinPack2D::ContentAccumulator remainder; - - // try to pack content into the bins. - bool success = canvasArray.Place( inputContent, remainder ); - - // A place to store packed content. - BinPack2D::ContentAccumulator outputContent; - - // Read all placed content. - canvasArray.CollectContent( outputContent ); - - // parse output. - typedef BinPack2D::Content::Vector::iterator binpack2d_iterator; - printf("PLACED:\n"); - for( binpack2d_iterator itor = outputContent.Get().begin(); itor != outputContent.Get().end(); itor++ ) { - - const BinPack2D::Content &content = *itor; - - // retreive your data. - const MyContent &myContent = content.content; - - printf("\t%9s of size %3dx%3d at position %3d,%3d,%2d rotated=%s\n", - myContent.str.c_str(), - content.size.w, - content.size.h, - content.coord.x, - content.coord.y, - content.coord.z, - (content.rotated ? "yes":" no")); - } - - printf("NOT PLACED:\n"); - for( binpack2d_iterator itor = remainder.Get().begin(); itor != remainder.Get().end(); itor++ ) { - - const BinPack2D::Content &content = *itor; - - const MyContent &myContent = content.content; - - printf("\t%9s of size %3dx%3d\n", - myContent.str.c_str(), - content.size.w, - content.size.h); - } - - exit(0); -} -*/ - - - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace BinPack2D { - -class Size { - -public: - - /*const*/ int w; - /*const*/ int h; - - Size(int w, int h) - : w(w), - h(h) - {} - - bool operator < ( const Size &that ) const { - - if(this->w != that.w) return this->w < that.w; - if(this->h != that.h) return this->h < that.h; - return false; - } -}; - -class Coord { - -public: - - typedef std::vector Vector; - typedef std::list List; - - /*const*/ int x; - /*const*/ int y; - /*const*/ int z; - - Coord() - : x(0), - y(0), - z(0) - {} - - Coord(int x, int y) - : x(x), - y(y), - z(0) - {} - - Coord(int x, int y, int z) - : x(x), - y(y), - z(z) - {} - - bool operator < ( const Coord &that ) const { - - if(this->x != that.x) return this->x < that.x; - if(this->y != that.y) return this->y < that.y; - if(this->z != that.z) return this->z < that.z; - return false; - } -}; - -template class Content { - -public: - - typedef std::vector > Vector; - - /*const*/ bool rotated; - /*const*/ Coord coord; - /*const*/ Size size; - /*const*/ _T content; - - Content( const Content<_T> &src ) - : rotated(src.rotated), - coord(src.coord), - size(src.size), - content(src.content) - {} - - Content( const _T &content, const Coord &coord, const Size &size, bool rotated ) - : - content(content), - coord(coord), - size(size), - rotated(rotated) - {} - - void Rotate() { - - rotated = !rotated; - size = Size( size.h, size.w ); - } - - bool intersects(const Content<_T> &that) const { - - if(this->coord.x >= (that.coord.x + that.size.w)) - return false; - - if(this->coord.y >= (that.coord.y + that.size.h)) - return false; - - if(that.coord.x >= (this->coord.x + this->size.w)) - return false; - - if(that.coord.y >= (this->coord.y + this->size.h)) - return false; - - return true; - } -}; - -template class Canvas { - - Coord::List topLefts; - typename Content<_T>::Vector contentVector; - - bool needToSort; - -public: - - typedef Canvas<_T> CanvasT; - typedef typename std::vector Vector; - - static bool Place( Vector &canvasVector, const typename Content<_T>::Vector &contentVector, typename Content<_T>::Vector &remainder ) { - - typename Content<_T>::Vector todo = contentVector; - - for( typename Vector::iterator itor = canvasVector.begin(); itor != canvasVector.end(); itor++ ) { - - Canvas <_T> &canvas = *itor; - - remainder.clear(); - canvas.Place(todo, remainder); - todo = remainder; - } - - if(remainder.size()==0) - return true; - - return false; - } - - static bool Place( Vector &canvasVector, const typename Content<_T>::Vector &contentVector ) { - - typename Content<_T>::Vector remainder; - - return Place( canvasVector, contentVector, remainder ); - } - - static bool Place( Vector &canvasVector, const Content<_T> &content ) { - - typename Content<_T>::Vector contentVector(1, content); - - return Place( canvasVector, contentVector ); - } - - const int w; - const int h; - - Canvas(int w, int h) - : needToSort(false), - w(w), - h(h) - { - topLefts.push_back( Coord(0,0) ); - } - - bool HasContent() const { - - return ( contentVector.size() > 0) ; - } - - const typename Content<_T>::Vector &GetContents( ) const { - - return contentVector; - } - - bool operator < ( const Canvas &that ) const { - - if(this->w != that.w) return this->w < that.w; - if(this->h != that.h) return this->h < that.h; - return false; - } - - bool Place(const typename Content<_T>::Vector &contentVector, typename Content<_T>::Vector &remainder) { - - bool placedAll = true; - - for( typename Content<_T>::Vector::const_iterator itor = contentVector.begin(); itor != contentVector.end(); itor++ ) { - - const Content<_T> & content = *itor; - - if( Place( content ) == false ) { - - placedAll = false; - remainder.push_back( content ); - } - } - - return placedAll; - } - - bool Place(Content<_T> content) { - - Sort(); - - for( Coord::List::iterator itor = topLefts.begin(); itor != topLefts.end(); itor++ ) { - - content.coord = *itor; - - if( Fits( content ) ) { - - Use( content ); - topLefts.erase( itor ); - return true; - } - } - - // EXPERIMENTAL - TRY ROTATED? - content.Rotate(); - for( Coord::List::iterator itor = topLefts.begin(); itor != topLefts.end(); itor++ ) { - - content.coord = *itor; - - if( Fits( content ) ) { - - Use( content ); - topLefts.erase( itor ); - return true; - } - } - //////////////////////////////// - - - return false; - } - -private: - - bool Fits( const Content<_T> &content ) const { - - if( (content.coord.x + content.size.w) > w ) - return false; - - if( (content.coord.y + content.size.h) > h ) - return false; - - for( typename Content<_T>::Vector::const_iterator itor = contentVector.begin(); itor != contentVector.end(); itor++ ) - if( content.intersects( *itor ) ) - return false; - - return true; - } - - bool Use(const Content<_T> &content) { - - const Size &size = content.size; - const Coord &coord = content.coord; - - topLefts.push_front ( Coord( coord.x + size.w, coord.y ) ); - topLefts.push_back ( Coord( coord.x , coord.y + size.h ) ); - - contentVector.push_back( content ); - - needToSort = true; - - return true; - } - -private: - - struct TopToBottomLeftToRightSort { - - bool operator()(const Coord &a, const Coord &b) const { - - return ( a.x * a.x + a.y * a.y ) < ( b.x * b.x + b.y * b.y ); - } - }; - -public: - - void Sort() { - - if(!needToSort) - return; - - topLefts.sort(TopToBottomLeftToRightSort()); - - needToSort = false; - } -}; - -template class ContentAccumulator { - - typename Content<_T>::Vector contentVector; - - -public: - - ContentAccumulator() - {} - - const typename Content<_T>::Vector &Get() const { - - return contentVector; - } - - typename Content<_T>::Vector &Get() { - - return contentVector; - } - - ContentAccumulator<_T>& operator += ( const Content<_T> & content ) { - - contentVector.push_back( content ); - - - return *this; - } - - ContentAccumulator<_T>& operator += ( const typename Content<_T>::Vector & content ) { - - contentVector.insert( contentVector.end(), content.begin(), content.end() ); - - - return *this; - } - - ContentAccumulator<_T> operator + ( const Content<_T> & content ) { - - ContentAccumulator<_T> temp = *this; - - temp += content; - - return temp; - } - - ContentAccumulator<_T> operator + ( const typename Content<_T>::Vector & content ) { - - ContentAccumulator<_T> temp = *this; - - temp += content; - - return temp; - } - - - -private: - - struct GreatestWidthThenGreatestHeightSort { - - bool operator()(const Content<_T> &a, const Content<_T> &b) const { - - const Size &sa = a.size; - const Size &sb = b.size; - -// return( sa.w * sa.h > sb.w * sb.h ); - - if(sa.w != sb.w) - return sa.w > sb.w; - return sa.h > sb.h; - } - }; - - struct MakeHorizontal { - - Content<_T> operator()( const Content<_T> &elem) { - - if(elem.size.h > elem.size.w) - { - Content<_T> r = elem; - - r.size.w = elem.size.h; - r.size.h = elem.size.w; - r.rotated = !elem.rotated; - - return r; - } - - return elem; - } - }; - -public: - - void Sort() { - -// if(allow_rotation) -// std::transform(contentVector.begin(), contentVector.end(), contentVector.begin(), MakeHorizontal()); - - std::sort( contentVector.begin(), contentVector.end(), GreatestWidthThenGreatestHeightSort() ); - } -}; - -template class UniformCanvasArrayBuilder { - - int w; - int h; - int d; - -public: - - UniformCanvasArrayBuilder( int w, int h, int d ) - : w(w), - h(h), - d(d) - {} - - typename Canvas<_T>::Vector Build() { - - return typename Canvas<_T>::Vector(d, Canvas<_T>(w, h) ); - } -}; - -template class CanvasArray { - - typename Canvas<_T>::Vector canvasArray; - -public: - CanvasArray() { - - } - CanvasArray( const typename Canvas<_T>::Vector &canvasArray ) - : canvasArray( canvasArray ) - {} - - bool Place(const typename Content<_T>::Vector &contentVector, typename Content<_T>::Vector &remainder) { - - return Canvas<_T>::Place( canvasArray, contentVector, remainder ); - } - - bool Place(const ContentAccumulator<_T> &content, ContentAccumulator<_T> &remainder) { - - return Place( content.Get(), remainder.Get() ); - } - - bool Place(const typename Content<_T>::Vector &contentVector) { - - return Canvas<_T>::Place( canvasArray, contentVector ); - } - - bool Place(const ContentAccumulator<_T> &content) { - - return Place( content.Get() ); - } - - bool CollectContent( typename Content<_T>::Vector &contentVector ) const { - - int z = 0; - - for( typename Canvas<_T>::Vector::const_iterator itor = canvasArray.begin(); itor != canvasArray.end(); itor++ ) { - - const typename Content<_T>::Vector &contents = itor->GetContents(); - - for( typename Content<_T>::Vector::const_iterator itor = contents.begin(); itor != contents.end(); itor++ ) { - - Content<_T> content = *itor; - - content.coord.z = z; - - contentVector.push_back( content ); - } - z++; - } - return true; - } - - bool CollectContent( ContentAccumulator<_T> &content) const { - - return CollectContent( content.Get() ); - } -}; - -} /*** BinPack2D ***/ \ No newline at end of file diff --git a/dep/include/stb/stb_rect_pack.h b/dep/include/stb/stb_rect_pack.h new file mode 100644 index 00000000..76331b16 --- /dev/null +++ b/dep/include/stb/stb_rect_pack.h @@ -0,0 +1,623 @@ +// stb_rect_pack.h - v1.01 - public domain - rectangle packing +// Sean Barrett 2014 +// +// Useful for e.g. packing rectangular textures into an atlas. +// Does not do rotation. +// +// Before #including, +// +// #define STB_RECT_PACK_IMPLEMENTATION +// +// in the file that you want to have the implementation. +// +// Not necessarily the awesomest packing method, but better than +// the totally naive one in stb_truetype (which is primarily what +// this is meant to replace). +// +// Has only had a few tests run, may have issues. +// +// More docs to come. +// +// No memory allocations; uses qsort() and assert() from stdlib. +// Can override those by defining STBRP_SORT and STBRP_ASSERT. +// +// This library currently uses the Skyline Bottom-Left algorithm. +// +// Please note: better rectangle packers are welcome! Please +// implement them to the same API, but with a different init +// function. +// +// Credits +// +// Library +// Sean Barrett +// Minor features +// Martins Mozeiko +// github:IntellectualKitty +// +// Bugfixes / warning fixes +// Jeremy Jaussaud +// Fabian Giesen +// +// Version history: +// +// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section +// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles +// 0.99 (2019-02-07) warning fixes +// 0.11 (2017-03-03) return packing success/fail result +// 0.10 (2016-10-25) remove cast-away-const to avoid warnings +// 0.09 (2016-08-27) fix compiler warnings +// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) +// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) +// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort +// 0.05: added STBRP_ASSERT to allow replacing assert +// 0.04: fixed minor bug in STBRP_LARGE_RECTS support +// 0.01: initial release +// +// LICENSE +// +// See end of file for license information. + +////////////////////////////////////////////////////////////////////////////// +// +// INCLUDE SECTION +// + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +typedef int stbrp_coord; + +#define STBRP__MAXVAL 0x7fffffff +// Mostly for internal use, but this is the maximum supported coordinate value. + +STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +// Assign packed locations to rectangles. The rectangles are of type +// 'stbrp_rect' defined below, stored in the array 'rects', and there +// are 'num_rects' many of them. +// +// Rectangles which are successfully packed have the 'was_packed' flag +// set to a non-zero value and 'x' and 'y' store the minimum location +// on each axis (i.e. bottom-left in cartesian coordinates, top-left +// if you imagine y increasing downwards). Rectangles which do not fit +// have the 'was_packed' flag set to 0. +// +// You should not try to access the 'rects' array from another thread +// while this function is running, as the function temporarily reorders +// the array while it executes. +// +// To pack into another rectangle, you need to call stbrp_init_target +// again. To continue packing into the same rectangle, you can call +// this function again. Calling this multiple times with multiple rect +// arrays will probably produce worse packing results than calling it +// a single time with the full rectangle array, but the option is +// available. +// +// The function returns 1 if all of the rectangles were successfully +// packed and 0 otherwise. + +struct stbrp_rect +{ + // reserved for your use: + int id; + + // input: + stbrp_coord w, h; + + // output: + stbrp_coord x, y; + int was_packed; // non-zero if valid packing + +}; // 16 bytes, nominally + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +// Initialize a rectangle packer to: +// pack a rectangle that is 'width' by 'height' in dimensions +// using temporary storage provided by the array 'nodes', which is 'num_nodes' long +// +// You must call this function every time you start packing into a new target. +// +// There is no "shutdown" function. The 'nodes' memory must stay valid for +// the following stbrp_pack_rects() call (or calls), but can be freed after +// the call (or calls) finish. +// +// Note: to guarantee best results, either: +// 1. make sure 'num_nodes' >= 'width' +// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' +// +// If you don't do either of the above things, widths will be quantized to multiples +// of small integers to guarantee the algorithm doesn't run out of temporary storage. +// +// If you do #2, then the non-quantized algorithm will be used, but the algorithm +// may run out of temporary storage and be unable to pack some rectangles. + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +// Optionally call this function after init but before doing any packing to +// change the handling of the out-of-temp-memory scenario, described above. +// If you call init again, this will be reset to the default (false). + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +// Optionally select which packing heuristic the library should use. Different +// heuristics will produce better/worse results for different data sets. +// If you call init again, this will be reset to the default. + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// the details of the following structures don't matter to you, but they must +// be visible so you can handle the memory allocations for them + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' +}; + +#ifdef __cplusplus +} +#endif + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION SECTION +// + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#ifndef STBRP_SORT +#include +#define STBRP_SORT qsort +#endif + +#ifndef STBRP_ASSERT +#include +#define STBRP_ASSERT assert +#endif + +#ifdef _MSC_VER +#define STBRP__NOTUSED(v) (void)(v) +#define STBRP__CDECL __cdecl +#else +#define STBRP__NOTUSED(v) (void)sizeof(v) +#define STBRP__CDECL +#endif + +enum +{ + STBRP__INIT_skyline = 1 +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + // if it's ok to run out of memory, then don't bother aligning them; + // this gives better packing, but may fail due to OOM (even though + // the rectangles easily fit). @TODO a smarter approach would be to only + // quantize once we've hit OOM, then we could get rid of this parameter. + context->align = 1; + else { + // if it's not ok to run out of memory, then quantize the widths + // so that num_nodes is always enough nodes. + // + // I.e. num_nodes * align >= width + // align >= width / num_nodes + // align = ceil(width/num_nodes) + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; + context->extra[1].y = (1<<30); + context->extra[1].next = NULL; +} + +// find minimum y position if it starts at x1 +static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +{ + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + + STBRP__NOTUSED(c); + + STBRP_ASSERT(first->x <= x0); + + #if 0 + // skip in case we're past the node + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + // raise min_y higher. + // we've accounted for all waste up to min_y, + // but we'll now add more waste for everything we've visted + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + // the first time through, visited_width might be reduced + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + // add waste area + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + // align to multiple of c->align + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + // if it can't possibly fit, bail immediately + if (width > c->width || height > c->height) { + fr.prev_link = NULL; + fr.x = fr.y = 0; + return fr; + } + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL + // bottom left + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + // best-fit + if (y + height <= c->height) { + // can only use it if it first vertically + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + // if doing best-fit (BF), we also have to try aligning right edge to each node position + // + // e.g, if fitting + // + // ____________________ + // |____________________| + // + // into + // + // | | + // | ____________| + // |____________| + // + // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + // + // This makes BF take about 2x the time + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + // find first node that's admissible + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + // find the left position that matches this + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height <= c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + STBRP_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + // find best position according to heuristic + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + // bail if: + // 1. it failed + // 2. the best node doesn't fit (we don't always check this) + // 3. we're out of memory + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + // on success, create new node + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + // insert the new node into the right starting point, and + // let 'cur' point to the remaining nodes needing to be + // stiched back in + + cur = *res.prev_link; + if (cur->x < res.x) { + // preserve the existing one, so start testing with the next one + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + // from here, traverse cur and free the nodes, until we get to one + // that shouldn't be freed + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + // move the current node to the free list + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + // stitch the list back in + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + int count=0; + cur = context->active_head; + while (cur) { + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int STBRP__CDECL rect_height_compare(const void *a, const void *b) +{ + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int STBRP__CDECL rect_original_order(const void *a, const void *b) +{ + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i, all_rects_packed = 1; + + // we use the 'was_packed' field internally to allow sorting/unsorting + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + } + + // sort according to heuristic + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + if (rects[i].w == 0 || rects[i].h == 0) { + rects[i].x = rects[i].y = 0; // empty rect needs no space + } else { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + } + + // unsort + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); + + // set was_packed flags and all_rects_packed status + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); + if (!rects[i].was_packed) + all_rects_packed = 0; + } + + // return the all_rects_packed status + return all_rects_packed; +} +#endif + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/engine/inc/uf/engine/graph/mesh.inl b/engine/inc/uf/engine/graph/mesh.inl index 23cd3ecd..d4a994d0 100644 --- a/engine/inc/uf/engine/graph/mesh.inl +++ b/engine/inc/uf/engine/graph/mesh.inl @@ -8,7 +8,6 @@ namespace uf { pod::Vector2f st{}; pod::Vector3f normal{}; pod::Vector3f tangent{}; - pod::Vector2us id{}; static UF_API uf::stl::vector descriptor; static UF_API Base interpolate( const Base& p1, const Base& p2, float t ); @@ -20,7 +19,6 @@ namespace uf { pod::Vector2f st{}; pod::Vector3f normal{}; pod::Vector3f tangent{}; - pod::Vector2us id{}; pod::Vector4us joints{}; pod::Vector4f weights{}; @@ -35,7 +33,6 @@ namespace uf { pod::Vector2f16 st{}; pod::Vector3f16 normal{}; pod::Vector3f16 tangent{}; - pod::Vector2us id{}; static UF_API uf::stl::vector descriptor; static UF_API Base_16f interpolate( const Base_16f& p1, const Base_16f& p2, float t ); @@ -47,7 +44,6 @@ namespace uf { pod::Vector2f16 st{}; pod::Vector3f16 normal{}; pod::Vector3f16 tangent{}; - pod::Vector2us id{}; pod::Vector4us joints{}; pod::Vector3f16 weights{}; @@ -62,7 +58,6 @@ namespace uf { pod::Vector2us st{}; pod::Vector3us normal{}; pod::Vector3us tangent{}; - pod::Vector2us id{}; static UF_API uf::stl::vector descriptor; static UF_API Base_u16q interpolate( const Base_u16q& p1, const Base_u16q& p2, float t ); @@ -74,7 +69,6 @@ namespace uf { pod::Vector2us st{}; pod::Vector3us normal{}; pod::Vector3us tangent{}; - pod::Vector2us id{}; pod::Vector4us joints{}; pod::Vector3us weights{}; diff --git a/engine/inc/uf/engine/graph/pod.inl b/engine/inc/uf/engine/graph/pod.inl index 68a169ee..520590d8 100644 --- a/engine/inc/uf/engine/graph/pod.inl +++ b/engine/inc/uf/engine/graph/pod.inl @@ -1,12 +1,4 @@ namespace pod { - template - struct UF_API Meshlet_T { - uf::stl::vector vertices; - uf::stl::vector indices; - - pod::Primitive primitive; - }; - struct UF_API Material { pod::Vector4f colorBase = { 0, 0, 0, 0 }; pod::Vector4f colorEmissive = { 0, 0, 0, 0 }; diff --git a/engine/inc/uf/ext/valve/common.h b/engine/inc/uf/ext/valve/common.h index 3d895925..7cf3234d 100644 --- a/engine/inc/uf/ext/valve/common.h +++ b/engine/inc/uf/ext/valve/common.h @@ -4,12 +4,10 @@ #include #include -#define UF_GRAPH_MESH_FORMAT uf::graph::mesh::Base, uint32_t - namespace impl { const float sourceToMeters = 0.07f; - typedef uf::Meshlet_T Meshlet; + typedef uf::Meshlet_T Meshlet; template T str2vec( uf::stl::string string ) { diff --git a/engine/inc/uf/utils/image/image.h b/engine/inc/uf/utils/image/image.h index f2a16a23..b389f0e6 100644 --- a/engine/inc/uf/utils/image/image.h +++ b/engine/inc/uf/utils/image/image.h @@ -49,20 +49,12 @@ namespace uf { // C-tor Image() = default; Image( const pod::Image& image ) : pod::Image(image) {} - /* - Image(); - explicit Image(const pod::Vector2ui& size); - Image(container_t&& move, const pod::Vector2ui& size); - Image(const container_t& copy, const pod::Vector2ui& size); - Image(const Image& copy); - Image(Image&& move) noexcept; - ~Image() = default; + Image( const Image& ) = default; + Image& operator=( const Image& ) = default; - // Assignment - Image& operator=(const Image& copy); - Image& operator=(Image&& move) noexcept; - */ + Image( Image&& ) noexcept = default; + Image& operator=( Image&& ) noexcept = default; bool open( const uf::stl::string& filename, bool = true ); // from file void open( const std::istream& stream ); // from stream diff --git a/engine/inc/uf/utils/memory/string.h b/engine/inc/uf/utils/memory/string.h index b6ea8389..e6a36a6b 100644 --- a/engine/inc/uf/utils/memory/string.h +++ b/engine/inc/uf/utils/memory/string.h @@ -4,6 +4,7 @@ #include "./allocator.h" #include +#include // strings with custom allocators really do not play nice with existing libraries namespace uf { @@ -16,6 +17,13 @@ namespace uf { using basic_string = std::basic_string; using string = uf::stl::basic_string; + template< + class CharT, + class Traits = std::char_traits + > + using basic_string_view = std::basic_string_view; + using string_view = uf::stl::basic_string_view; + template< class CharT, class Traits = std::char_traits, diff --git a/engine/inc/uf/utils/memory/unordered_map.h b/engine/inc/uf/utils/memory/unordered_map.h index 1bb9f7f3..b0245492 100644 --- a/engine/inc/uf/utils/memory/unordered_map.h +++ b/engine/inc/uf/utils/memory/unordered_map.h @@ -23,9 +23,16 @@ namespace uf { template uf::stl::vector keys( const uf::stl::unordered_map& map ) { - uf::stl::vector keys; + uf::stl::vector keys; keys.reserve( map.size() ); for ( auto pair : map ) keys.emplace_back( pair.first ); return keys; } + + template + uf::stl::vector values( const uf::stl::unordered_map& map ) { + uf::stl::vector values; values.reserve( map.size() ); + for ( auto pair : map ) values.emplace_back( pair.second ); + return values; + } } } \ No newline at end of file diff --git a/engine/inc/uf/utils/mesh/grid.h b/engine/inc/uf/utils/mesh/grid.h index 8c6e4b82..dbcc3397 100644 --- a/engine/inc/uf/utils/mesh/grid.h +++ b/engine/inc/uf/utils/mesh/grid.h @@ -65,7 +65,6 @@ namespace uf { ); } - template uf::stl::vector> UF_API partition( uf::meshgrid::Grid& grid, @@ -84,61 +83,16 @@ namespace uf { uf::meshgrid::calculate( grid, eps ); - // it's better to naively clip the mesh multiple times rather than calculate the triangles needed to clip - #if 0 - if ( clip ) { - for ( auto& pair : grid.nodes ) { - ++atlasID; - for ( auto& meshlet : meshlets ) { - auto& node = pair.second; - - uf::stl::vector vertices = meshlet.vertices; - uf::stl::vector indices = meshlet.indices; - - uf::shapes::clip( vertices, indices, pod::AABB{ node.extents.min, node.extents.max } ); - - if ( vertices.empty() || indices.empty() ) continue; - - size_t primitiveID = partitioned.size(); - auto& slice = partitioned.emplace_back(); - - slice.vertices = std::move( vertices ); - slice.indices = std::move( indices ); - - for ( auto& vertex : slice.vertices ) { - vertex.id.x = primitiveID; - vertex.id.y = meshlet.primitive.instance.meshID; - } - - slice.primitive.instance = meshlet.primitive.instance; - slice.primitive.instance.materialID = meshlet.primitive.instance.materialID; - slice.primitive.instance.primitiveID = primitiveID; - slice.primitive.instance.meshID = meshlet.primitive.instance.meshID; - slice.primitive.instance.objectID = 0; - slice.primitive.instance.auxID = atlasID; - slice.primitive.instance.bounds.min = node.extents.min; - slice.primitive.instance.bounds.max = node.extents.max; - - slice.primitive.drawCommand.indices = slice.indices.size(); - slice.primitive.drawCommand.instances = 1; - slice.primitive.drawCommand.indexID = 0; - slice.primitive.drawCommand.vertexID = 0; - slice.primitive.drawCommand.instanceID = 0; - slice.primitive.drawCommand.auxID = atlasID; // meshlet.primitive.instance.meshID; - slice.primitive.drawCommand.vertices = slice.vertices.size(); - } - } - - return partitioned; + for ( auto& meshlet : meshlets ) { + uf::meshgrid::partition( grid, meshlet.vertices, meshlet.indices, meshlet.primitive ); + } + if ( cleanup ) { + uf::meshgrid::cleanup( grid ); } - #endif - for ( auto& meshlet : meshlets ) uf::meshgrid::partition( grid, meshlet.vertices, meshlet.indices, meshlet.primitive ); - if ( cleanup ) uf::meshgrid::cleanup( grid ); - - for ( auto& pair : grid.nodes ) { auto& node = pair.second; + for ( auto& [ _, node ] : grid.nodes ) { ++atlasID; - for ( auto& pair2 : node.meshlets ) { auto& mlet = pair2.second; + for ( auto& [ __, mlet ] : node.meshlets ) { if ( mlet.indices.empty() ) continue; auto& meshlet = meshlets[mlet.primitive.instance.primitiveID]; @@ -148,21 +102,11 @@ namespace uf { slice.vertices.reserve( mlet.indices.size() ); slice.indices.reserve( mlet.indices.size() ); - for ( auto idx : mlet.indices ) { - auto& vertex = slice.vertices.emplace_back( meshlet.vertices[idx] ); - auto& index = slice.indices.emplace_back( slice.indices.size() ); - - vertex.id.x = primitiveID; - vertex.id.y = meshlet.primitive.instance.meshID; - } - - #if 1 if ( clip ) { node.effectiveExtents.min = node.extents.min; node.effectiveExtents.max = node.extents.max; uf::shapes::clip( slice.vertices, slice.indices, pod::AABB{ node.extents.min, node.extents.max } ); } - #endif slice.primitive.instance = meshlet.primitive.instance; slice.primitive.instance.materialID = meshlet.primitive.instance.materialID; diff --git a/engine/inc/uf/utils/mesh/mesh.h b/engine/inc/uf/utils/mesh/mesh.h index 42a42365..127e31cd 100644 --- a/engine/inc/uf/utils/mesh/mesh.h +++ b/engine/inc/uf/utils/mesh/mesh.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -78,8 +79,8 @@ namespace pod { 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 vertexID = 0; alignas(4) uint32_t vertices = 0; } levels[4]; }; @@ -126,9 +127,6 @@ namespace pod { alignas(8) uint64_t joints{}; alignas(8) uint64_t weights{}; - - alignas(8) uint64_t id{}; - alignas(8) uint64_t padding1{}; }; struct UF_API Object { @@ -151,7 +149,6 @@ namespace pod { namespace uf { struct UF_API Mesh { public: - static bool defaultInterleaved; typedef uf::stl::vector buffer_t; struct Attribute { uf::renderer::AttributeDescriptor descriptor; @@ -169,7 +166,6 @@ namespace uf { 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 { @@ -196,50 +192,23 @@ namespace uf { uf::Mesh::Input index; int32_t indirectIndex = -1; - uf::stl::unordered_map attributes; + uf::stl::unordered_map 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 ); + bool has( uint32_t hash ) const { + return attributes.count( hash ) > 0; } - size_t fetchIndex( const uf::Mesh::AttributeView& indices, size_t index ) { - return uf::mesh::fetchIndex( indices, index ); + const AttributeView& operator[]( uint32_t hash ) const { + if ( auto it = attributes.find( hash ); it != attributes.end() ) return it->second; + UF_EXCEPTION("invalid view hash: {}", hash); } - size_t fetchIndex( const uf::stl::string& indices, size_t index ) { - return uf::mesh::fetchIndex( indices, index ); + // support legacy code + bool has( const uf::stl::string_view name ) const { + return has( uf::string::fnv1a( name ) ); } - - pod::Vector3f fetchVertex( size_t index ) { - return uf::mesh::fetchVertex( index ); + const AttributeView& operator[]( const uf::stl::string_view name ) const { + return operator[]( uf::string::fnv1a( name ) ); } - 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 views_t; @@ -251,7 +220,7 @@ namespace uf { uf::stl::vector buffer_views; protected: void _destroy( uf::Mesh::Input& input ); - void _bind( bool interleaved = uf::Mesh::defaultInterleaved ); + void _bind(); 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; @@ -281,30 +250,32 @@ namespace uf { template inline void _insertI( uf::Mesh::Input& input, U index, size_t i = 0 ) { return _insertI( input, (const void*) &index, i ); } template inline void _insertIs( uf::Mesh::Input& input, const uf::stl::vector& is, size_t i = 0 ) { return _insertIs( input, (const void*) is.data(), is.size(), i ); } public: + Mesh() = default; + Mesh( const Mesh& m ) { copy( m ); } + Mesh& operator=( const Mesh& m ) { return copy( m ); } + Mesh( Mesh&& ) noexcept = default; + Mesh& operator=( Mesh&& ) noexcept = default; + void initialize(); void destroy(); - uf::Mesh convert() const; + uf::Mesh& copy( const uf::Mesh& ); 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; + // API hell + template inline void compile( const uf::stl::vector& meshlets, uf::stl::vector& primitives ); + template inline void compile( const uf::stl::unordered_map& meshlets, uf::stl::vector& primitives ); + template inline uf::stl::vector compile( const uf::stl::vector& meshlets ); + template inline uf::stl::vector compile( const uf::stl::unordered_map& meshlets ); buffer_t& getBuffer( const uf::Mesh::Input&, size_t = 0 ); buffer_t& getBuffer( const uf::Mesh::Input&, const uf::Mesh::Attribute& ); @@ -320,13 +291,6 @@ namespace uf { 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& wanted = {}, size_t index = 0 ) const; uf::Mesh::View makeView( size_t commandIndex, const uf::stl::vector& wanted = {}, size_t index = 0 ) const; uf::stl::vector makeViews( const uf::stl::vector& wanted = {}, size_t index = 0 ) const; @@ -388,18 +352,14 @@ namespace uf { template inline void insertIndirects( const uf::stl::vector& indirects, size_t i = 0 ) { return _insertIs( indirect, (const void*) indirects.data(), indirects.size(), i ); } template - void bind( bool interleave = uf::Mesh::defaultInterleaved, size_t indices = 1 ) { + void bind( size_t indices = 1 ) { bindVertex(); bindIndex( indices ); - _bind( interleave ); + _bind(); } template void convert() { - if ( this->isInterleaved() ) { - UF_MSG_DEBUG("Downcasting/upcasting requested yet mesh is interleaved, ignoring..."); - return; - } auto fromEnum = uf::renderer::typeToEnum(); auto toEnum = uf::renderer::typeToEnum(); if ( toEnum == fromEnum ) return; @@ -509,13 +469,13 @@ namespace ext { 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; + 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; @@ -619,6 +579,7 @@ namespace pod { } namespace uf { + // ??? template struct UF_API Mesh_T { typedef T vertex_t; @@ -645,30 +606,6 @@ namespace uf { 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 T fetchVertexAttribute( const uf::Mesh::View& view, const uf::Mesh::AttributeView& attributeView, size_t index ) { #define CAST_VERTEX(type) {\ @@ -711,5 +648,143 @@ namespace uf { default: UF_EXCEPTION("unsupported attribute type: {}", attributeView.attribute.descriptor.type); break; } } + + template + T& getVertexAttribute( const uf::Mesh::View& view, const uf::Mesh::AttributeView& attributeView, size_t index ) { + UF_ASSERT( uf::renderer::typeToEnum() == attributeView.type() && T::size == attributeView.components() ); + return *(T*) attributeView.data( view.vertex.first + index ); + } + // + template inline U& getIndex( void* pointer, size_t index ) { + return ((U*) pointer)[index]; + } + template inline U& getIndex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, size_t index ) { + return uf::mesh::getIndex( indices.data(view.index.first), index ); + } + template inline U& getIndex( const uf::Mesh::View& view, size_t index ) { + return uf::mesh::getIndex( view, view["indices"_hash], index ); + } + template inline U& getIndex( const uf::Mesh::View& view, const uf::stl::string& indices, size_t index ) { + return uf::mesh::getIndex( view, view[indices], index ); + } + + void UF_API setIndex( void* pointer, size_t stride, size_t index, size_t value ); + + // + template void compile( uf::Mesh& mesh, const uf::stl::vector>& meshlets, uf::stl::vector& primitives ); + + template inline uf::stl::vector compile( uf::Mesh& mesh, const uf::stl::unordered_map& meshlets ); + template inline uf::stl::vector compile( uf::Mesh& mesh, const uf::stl::vector& meshlets ); + template inline uf::stl::vector compile( uf::Mesh& mesh, const uf::stl::unordered_map& meshlets ); + + // + 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 ); + } + static inline size_t fetchIndex( const uf::Mesh::View& view, size_t index ) { + return uf::mesh::fetchIndex( view, view["indices"_hash], 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"_hash], 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"_hash], view["position"_hash], triID ); + } + + static inline void setIndex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, size_t index, size_t value ) { + return uf::mesh::setIndex( const_cast(indices.data(view.index.first)), indices.stride(), index, value ); + } + static inline void setIndex( const uf::Mesh::View& view, size_t index, size_t value ) { + return uf::mesh::setIndex( view, view["indices"_hash], index, value ); + } } +} + +template uf::stl::vector uf::Mesh::compile( const uf::stl::vector& meshlets ) { + uf::stl::vector primitives; + uf::mesh::compile( *this, meshlets, primitives ); + return primitives; +} +template uf::stl::vector uf::Mesh::compile( const uf::stl::unordered_map& meshlets ) { + uf::stl::vector primitives; + uf::mesh::compile( *this, uf::stl::values( meshlets ), primitives ); + return primitives; +} + +template void uf::Mesh::compile( const uf::stl::vector& meshlets, uf::stl::vector& primitives ) { + return uf::mesh::compile( *this, meshlets, primitives ); +} +template void uf::Mesh::compile( const uf::stl::unordered_map& meshlets, uf::stl::vector& primitives ) { + return uf::mesh::compile( *this, uf::stl::values( meshlets ), primitives ); +} +// +template uf::stl::vector uf::mesh::compile( uf::Mesh& mesh, const uf::stl::vector& meshlets ) { + uf::stl::vector primitives; + uf::mesh::compile( mesh, meshlets, primitives ); + return primitives; +} +template uf::stl::vector uf::mesh::compile( uf::Mesh& mesh, const uf::stl::unordered_map& meshlets ) { + uf::stl::vector primitives; + uf::mesh::compile( mesh, uf::stl::values( meshlets ), primitives ); + return primitives; +} +template void uf::mesh::compile( uf::Mesh& mesh, const uf::stl::unordered_map& meshlets, uf::stl::vector& primitives ) { + return uf::mesh::compile( mesh, uf::stl::values( meshlets ), primitives ); +} +// +template void uf::mesh::compile( uf::Mesh& mesh, const uf::stl::vector>& meshlets, uf::stl::vector& primitives ) { + mesh.bindIndirect(); + mesh.bind(); + + size_t indexID = 0; + size_t vertexID = 0; + size_t instanceID = 0; + + uf::stl::vector drawCommands; + drawCommands.reserve( meshlets.size() ); + primitives.reserve( primitives.size() + meshlets.size() ); + + for ( auto& meshlet : meshlets ) { + if ( meshlet.indices.empty() ) continue; + auto& primitive = primitives.emplace_back(meshlet.primitive); + // write draw command + primitive.drawCommand = { + .indices = meshlet.indices.size(), + .instances = MAX(1, primitive.drawCommand.instances), + .indexID = indexID, + .vertexID = vertexID, + .instanceID = instanceID, + .auxID = 0, + .materialID = 0, + .vertices = meshlet.vertices.size(), + }; + // write LOD0 + primitive.lod.levels[0] = { + .indices = meshlet.indices.size(), + .indexID = indexID, + .vertexID = vertexID, + .vertices = meshlet.vertices.size(), + }; + // sync draw command with primitive + drawCommands.emplace_back(primitive.drawCommand); + // increase IDs + indexID += primitive.drawCommand.indices; + vertexID += primitive.drawCommand.vertices; + instanceID += primitive.drawCommand.instances; + // insert + mesh.insertVertices( meshlet.vertices ); + mesh.insertIndices( meshlet.indices ); + } + + mesh.insertIndirects(drawCommands); + mesh.updateDescriptor(); } \ No newline at end of file diff --git a/engine/inc/uf/utils/string/hash.h b/engine/inc/uf/utils/string/hash.h index d34e3acb..1f9a892a 100644 --- a/engine/inc/uf/utils/string/hash.h +++ b/engine/inc/uf/utils/string/hash.h @@ -10,11 +10,20 @@ namespace uf { namespace string { template - uf::stl::string sha256( const T& input ) { - // uf::stl::vector hash(picosha2::k_digest_size); - // picosha2::hash256(input.begin(), input.end(), hash); - // return picosha2::bytes_to_hex_string(hash.begin(), hash.end()); - return picosha2::hash256_hex_string(input); + uf::stl::string sha256( const T& input ) { return picosha2::hash256_hex_string(input); } + + constexpr uint32_t fnv1a(const char* str, uint32_t hash = 2166136261u) { return *str ? fnv1a(str + 1, (hash ^ static_cast(*str)) * 16777619u) : hash; } + + static inline uint32_t fnv1a( const uf::stl::string& str ) { return fnv1a(str.c_str()); } + static inline uint32_t fnv1a( const uf::stl::string_view str ) { + uint32_t hash = 2166136261u; + for ( char c : str ) hash = (hash ^ static_cast(c)) * 16777619u; + return hash; } } -} \ No newline at end of file + namespace literals { + constexpr uint32_t operator""_hash(const char* str, size_t) { return uf::string::fnv1a(str); } + } +} + +using namespace uf::literals; \ No newline at end of file diff --git a/engine/src/engine/ext/ext.cpp b/engine/src/engine/ext/ext.cpp index 1e50188d..a984c148 100644 --- a/engine/src/engine/ext/ext.cpp +++ b/engine/src/engine/ext/ext.cpp @@ -114,7 +114,6 @@ void UF_API uf::load( ext::json::Value& json ) { // Scene settings { auto& configEngineSceneJson = json["engine"]["scenes"]; - uf::Mesh::defaultInterleaved = configEngineSceneJson["meshes"]["interleaved"].as( uf::Mesh::defaultInterleaved ); uf::matrix::reverseInfiniteProjection = configEngineSceneJson["matrix"]["reverseInfinite"].as( uf::matrix::reverseInfiniteProjection ); } diff --git a/engine/src/engine/ext/gui/behavior.cpp b/engine/src/engine/ext/gui/behavior.cpp index 677e8028..1af1c208 100644 --- a/engine/src/engine/ext/gui/behavior.cpp +++ b/engine/src/engine/ext/gui/behavior.cpp @@ -567,8 +567,8 @@ void ext::GuiBehavior::tick( uf::Object& self ) { pod::Vector2f max = { -std::numeric_limits::max(), -std::numeric_limits::max() }; for ( const auto& view : mesh.buffer_views ) { - auto posView = view["position"]; - auto offView = view.has("offset") ? view["offset"] : uf::Mesh::AttributeView{}; + auto posView = view["position"_hash]; + auto offView = view.has("offset"_hash) ? view["offset"_hash] : uf::Mesh::AttributeView{}; for ( auto i = 0; i < view.vertex.count; ++i ) { auto pos = uf::mesh::fetchVertex( view, posView, i ); auto off = offView.valid() ? uf::mesh::fetchVertex( view, offView, i ) : pod::Vector3f{}; diff --git a/engine/src/engine/graph/animation.cpp b/engine/src/engine/graph/animation.cpp index a8ed441d..d7f339ba 100644 --- a/engine/src/engine/graph/animation.cpp +++ b/engine/src/engine/graph/animation.cpp @@ -310,9 +310,9 @@ uf::stl::vector uf::graph::obbFromSkin( const pod::Graph& graph, const // iterate through mesh to fetch attributes for ( const auto& view : mesh.buffer_views ) { - auto posView = view["position"]; - auto jointsView = view["joints"]; - auto weightView = view["weights"]; + auto posView = view["position"_hash]; + auto jointsView = view["joints"_hash]; + auto weightView = view["weights"_hash]; for ( auto i = 0; i < view.vertex.count; ++i ) { auto pos = uf::mesh::fetchVertex( view, posView, i ); diff --git a/engine/src/engine/graph/convert.cpp b/engine/src/engine/graph/convert.cpp index f4549364..c138ba36 100644 --- a/engine/src/engine/graph/convert.cpp +++ b/engine/src/engine/graph/convert.cpp @@ -87,7 +87,7 @@ namespace { if ( mesh.indirect.count > 0 ) { auto& attribute = mesh.indirect.attributes.front(); - auto& buffer = mesh.buffers[mesh.isInterleaved(mesh.indirect.interleaved) ? mesh.indirect.interleaved : attribute.buffer]; + auto& buffer = mesh.buffers[attribute.buffer]; if ( !buffer.empty() ) { sourceDrawCommands = (pod::DrawCommand*) buffer.data(); } @@ -296,7 +296,7 @@ void uf::graph::postprocess( pod::Graph& graph ) { UF_ASSERT( primitives.size() == mesh.indirect.count ); auto& attribute = mesh.indirect.attributes.front(); - auto& buffer = mesh.buffers[mesh.isInterleaved(mesh.indirect.interleaved) ? mesh.indirect.interleaved : attribute.buffer]; + auto& buffer = mesh.buffers[attribute.buffer]; pod::DrawCommand* drawCommands = (pod::DrawCommand*) buffer.data(); for ( auto drawID = 0; drawID < primitives.size(); ++drawID ) { primitives[drawID].drawCommand = drawCommands[drawID]; diff --git a/engine/src/engine/graph/decode.cpp b/engine/src/engine/graph/decode.cpp index 68656dce..3c23e806 100644 --- a/engine/src/engine/graph/decode.cpp +++ b/engine/src/engine/graph/decode.cpp @@ -232,7 +232,6 @@ namespace { mesh.N.first = input["first"].as( mesh.N.first );\ mesh.N.size = input["size"].as( mesh.N.size );\ mesh.N.offset = input["offset"].as( mesh.N.offset );\ - mesh.N.interleaved = input["interleaved"].as( mesh.N.interleaved );\ ext::json::forEach( input["attributes"], [&]( ext::json::Value& value ){\ auto& attribute = mesh.N.attributes.emplace_back();\ attribute.descriptor.offset = value["descriptor"]["offset"].as(attribute.descriptor.offset);\ @@ -266,7 +265,6 @@ namespace { mesh.buffers.emplace_back(); mesh.buffer_paths.emplace_back(directory + "/" + filename); } else { - // to-do: make it work for interleaved meshes mesh.buffers.emplace_back(uf::io::readAsBuffer( directory + "/" + filename )); } #endif @@ -289,22 +287,18 @@ namespace { // if ( graph.metadata["renderer"]["separate"].as() ) { uf::stl::vector attributesKept = ext::json::vector(graph.metadata["decode"]["attributes"]); - if ( !mesh.isInterleaved() ) { - uf::stl::vector remove; remove.reserve(mesh.vertex.attributes.size()); + uf::stl::vector remove; remove.reserve(mesh.vertex.attributes.size()); - for ( size_t i = 0; i < mesh.vertex.attributes.size(); ++i ) { - auto& attribute = mesh.vertex.attributes[i]; - if ( std::find( attributesKept.begin(), attributesKept.end(), attribute.descriptor.name ) != attributesKept.end() ) continue; - remove.insert(remove.begin(), i); - UF_MSG_DEBUG("Removing mesh attribute: {}", attribute.descriptor.name); - } - for ( auto& i : remove ) { - mesh.buffers[mesh.vertex.attributes[i].buffer].clear(); - mesh.buffers[mesh.vertex.attributes[i].buffer].shrink_to_fit(); - mesh.vertex.attributes.erase(mesh.vertex.attributes.begin() + i); - } - } else { - UF_MSG_DEBUG("Attribute removal requested yet mesh is interleaved, ignoring..."); + for ( size_t i = 0; i < mesh.vertex.attributes.size(); ++i ) { + auto& attribute = mesh.vertex.attributes[i]; + if ( std::find( attributesKept.begin(), attributesKept.end(), attribute.descriptor.name ) != attributesKept.end() ) continue; + remove.insert(remove.begin(), i); + UF_MSG_DEBUG("Removing mesh attribute: {}", attribute.descriptor.name); + } + for ( auto& i : remove ) { + mesh.buffers[mesh.vertex.attributes[i].buffer].clear(); + mesh.buffers[mesh.vertex.attributes[i].buffer].shrink_to_fit(); + mesh.vertex.attributes.erase(mesh.vertex.attributes.begin() + i); } } #endif @@ -395,7 +389,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const #if UF_USE_OPENGL graph.metadata["decode"]["attributes"] = uf::stl::vector({ "position", "uv", "st" }); #else - graph.metadata["decode"]["attributes"] = uf::stl::vector({ "position", "color", "uv", "st", "tangent", "joints", "weights", "normal", "id" }); + graph.metadata["decode"]["attributes"] = uf::stl::vector({ "position", "color", "uv", "st", "normal", "tangent", "joints", "weights" }); #endif } diff --git a/engine/src/engine/graph/encode.cpp b/engine/src/engine/graph/encode.cpp index c5f77633..070a3216 100644 --- a/engine/src/engine/graph/encode.cpp +++ b/engine/src/engine/graph/encode.cpp @@ -179,47 +179,12 @@ namespace { } uf::Serializer json; - #if 0 - uf::Mesh mesh = mesh; - // remove extraneous buffers - if ( !mesh.isInterleaved() ) { - uf::stl::vector remove; remove.reserve(mesh.vertex.attributes.size()); - - for ( size_t i = 0; i < mesh.vertex.attributes.size(); ++i ) { - auto& attribute = mesh.vertex.attributes[i]; - if ( attribute.descriptor.name == "position" ) continue; - if ( attribute.descriptor.name == "color" ) continue; - if ( attribute.descriptor.name == "uv" ) continue; - if ( attribute.descriptor.name == "st" ) continue; - - if ( graph.metadata["renderer"]["skinned"].as() ) { - if ( attribute.descriptor.name == "tangent" ) continue; - if ( attribute.descriptor.name == "joints" ) continue; - if ( attribute.descriptor.name == "weights" ) continue; - } - #if !UF_USE_OPENGL - if ( attribute.descriptor.name == "normal" ) continue; - #endif - - remove.insert(remove.begin(), i); - } - for ( auto& i : remove ) { - mesh.buffers[mesh.vertex.attributes[i].buffer].clear(); - mesh.buffers[mesh.vertex.attributes[i].buffer].shrink_to_fit(); - mesh.vertex.attributes.erase(mesh.vertex.attributes.begin() + i); - } - } else { - UF_MSG_DEBUG("Attribute removal requested yet mesh is not interleaved, ignoring..."); - } - #endif - #define SERIALIZE_MESH(N) {\ auto& input = json["inputs"][#N];\ input["count"] = mesh.N.count;\ input["first"] = mesh.N.first;\ input["size"] = mesh.N.size;\ input["offset"] = mesh.N.offset;\ - input["interleaved"] = mesh.N.interleaved;\ ext::json::reserve( input["attributes"], mesh.N.attributes.size() );\ for ( auto& attribute : mesh.N.attributes ) {\ auto& a = input["attributes"].emplace_back();\ diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index 67317a48..eb61a764 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -288,108 +288,71 @@ namespace { uint32_t jointID; }; - if ( mesh.isInterleaved( mesh.vertex ) ) { - uf::stl::string compShaderFilename = graphMetadataJson["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.interleaved.comp.spv"); { - compShaderFilename = entity.resolveURI( compShaderFilename, root ); - } - graphic.material.metadata.autoInitializeUniformBuffers = false; - graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, "skinning"); - graphic.material.metadata.autoInitializeUniformBuffers = true; - - graphic.descriptor.bind.width = mesh.vertex.count; - graphic.descriptor.bind.height = 1; - graphic.descriptor.bind.depth = 1; - - // compute shader - auto& shader = graphic.material.getShader("compute", "skinning"); - - // bind buffers - struct { - uint32_t jointID; - } uniforms = { - .jointID = 0 - }; - - auto& vertexSourceData = mesh.buffers[mesh.vertex.interleaved]; - size_t vertexSourceDataIndex = graphic.initializeBuffer( (const void*) vertexSourceData.data(), vertexSourceData.size(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR ); - - auto& vertexIn = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved); - auto& vertexOut = graphic.buffers.at(vertexSourceDataIndex); - graphic.metadata.buffers["vertexSkinned"] = vertexSourceDataIndex; - - shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); - - ::resetBuffers( shader ); - shader.aliasBuffer( storage.buffers.joint ); - shader.aliasBuffer( vertexIn ); - shader.aliasBuffer( vertexOut ); - } else { - uf::stl::string compShaderFilename = graphMetadataJson["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.deinterleaved.comp.spv"); { - compShaderFilename = entity.resolveURI( compShaderFilename, root ); - } - - // graphic.material.metadata.autoInitializeUniformBuffers = false; - graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, "skinning"); - // graphic.material.metadata.autoInitializeUniformBuffers = true; - - graphic.descriptor.bind.width = mesh.vertex.count; - graphic.descriptor.bind.height = 1; - graphic.descriptor.bind.depth = 1; - - uf::Mesh::Attribute vertexPos; - uf::Mesh::Attribute vertexJoints; - uf::Mesh::Attribute vertexWeights; - - size_t vertexPosIndex = 0; - size_t vertexJointsIndex = 0; - size_t vertexWeightsIndex = 0; - - for ( size_t i = 0; i < graphic.descriptor.inputs.vertex.attributes.size(); ++i ) { - auto& attribute = graphic.descriptor.inputs.vertex.attributes[i]; - - if ( attribute.buffer < 0 ) continue; - if ( attribute.descriptor.name == "position" ) { - vertexPos = attribute; - vertexPosIndex = graphic.metadata.buffers["vertex[position]"]; - } - else if ( attribute.descriptor.name == "joints" ) { - vertexJoints = attribute; - vertexJointsIndex = graphic.metadata.buffers["vertex[joints]"]; - } - else if ( attribute.descriptor.name == "weights" ) { - vertexWeights = attribute; - vertexWeightsIndex = graphic.metadata.buffers["vertex[weights]"]; - } - } - - auto& vertexSourceData = mesh.buffers[vertexPos.buffer]; - size_t vertexSourceDataIndex = graphic.initializeBuffer( (const void*) vertexSourceData.data(), vertexSourceData.size(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR ); - - auto& vertexPositionBuffer = graphic.buffers.at(vertexPosIndex); - auto& vertexJointsBuffer = graphic.buffers.at(vertexJointsIndex); - auto& vertexWeightsBuffer = graphic.buffers.at(vertexWeightsIndex); - - auto& vertexOutPosition = graphic.buffers.at(vertexSourceDataIndex); - graphic.metadata.buffers["vertexSkinned"] = vertexSourceDataIndex; - - auto& shader = graphic.material.getShader("compute", "skinning"); - - struct { - uint32_t jointID; - } uniforms = { - .jointID = 0 - }; - - shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); - - // bind buffers - ::resetBuffers( shader ); - shader.aliasBuffer( storage.buffers.joint ); - shader.aliasBuffer( vertexPositionBuffer ); - shader.aliasBuffer( vertexJointsBuffer ); - shader.aliasBuffer( vertexWeightsBuffer ); - shader.aliasBuffer( vertexOutPosition ); + uf::stl::string compShaderFilename = graphMetadataJson["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.deinterleaved.comp.spv"); { + compShaderFilename = entity.resolveURI( compShaderFilename, root ); } + + // graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, "skinning"); + // graphic.material.metadata.autoInitializeUniformBuffers = true; + + graphic.descriptor.bind.width = mesh.vertex.count; + graphic.descriptor.bind.height = 1; + graphic.descriptor.bind.depth = 1; + + uf::Mesh::Attribute vertexPos; + uf::Mesh::Attribute vertexJoints; + uf::Mesh::Attribute vertexWeights; + + size_t vertexPosIndex = 0; + size_t vertexJointsIndex = 0; + size_t vertexWeightsIndex = 0; + + for ( size_t i = 0; i < graphic.descriptor.inputs.vertex.attributes.size(); ++i ) { + auto& attribute = graphic.descriptor.inputs.vertex.attributes[i]; + + if ( attribute.buffer < 0 ) continue; + if ( attribute.descriptor.name == "position" ) { + vertexPos = attribute; + vertexPosIndex = graphic.metadata.buffers["vertex[position]"]; + } + else if ( attribute.descriptor.name == "joints" ) { + vertexJoints = attribute; + vertexJointsIndex = graphic.metadata.buffers["vertex[joints]"]; + } + else if ( attribute.descriptor.name == "weights" ) { + vertexWeights = attribute; + vertexWeightsIndex = graphic.metadata.buffers["vertex[weights]"]; + } + } + + auto& vertexSourceData = mesh.buffers[vertexPos.buffer]; + size_t vertexSourceDataIndex = graphic.initializeBuffer( (const void*) vertexSourceData.data(), vertexSourceData.size(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR ); + + auto& vertexPositionBuffer = graphic.buffers.at(vertexPosIndex); + auto& vertexJointsBuffer = graphic.buffers.at(vertexJointsIndex); + auto& vertexWeightsBuffer = graphic.buffers.at(vertexWeightsIndex); + + auto& vertexOutPosition = graphic.buffers.at(vertexSourceDataIndex); + graphic.metadata.buffers["vertexSkinned"] = vertexSourceDataIndex; + + auto& shader = graphic.material.getShader("compute", "skinning"); + + struct { + uint32_t jointID; + } uniforms = { + .jointID = 0 + }; + + shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); + + // bind buffers + ::resetBuffers( shader ); + shader.aliasBuffer( storage.buffers.joint ); + shader.aliasBuffer( vertexPositionBuffer ); + shader.aliasBuffer( vertexJointsBuffer ); + shader.aliasBuffer( vertexWeightsBuffer ); + shader.aliasBuffer( vertexOutPosition ); } graphic.generateBottomAccelerationStructures(); @@ -532,32 +495,24 @@ namespace { auto& instanceAddresses = addresses[drawID]; // THIS IS WRONG (to-do: actually use instanceIDs) if ( mesh.vertex.count ) { - if ( mesh.isInterleaved( mesh.vertex ) ) { - instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress(); - } else { - for ( auto& attribute : graphic.descriptor.inputs.vertex.attributes ) { - if ( attribute.buffer < 0 ) continue; - if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress(); - else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress(); - else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress(); - else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress(); - else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress(); - else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress(); - else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress(); - else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress(); - else if ( attribute.descriptor.name == "id" ) instanceAddresses.id = graphic.buffers.at(attribute.buffer).getAddress(); - } + for ( auto& attribute : graphic.descriptor.inputs.vertex.attributes ) { + if ( attribute.buffer < 0 ) continue; + if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress(); } } if ( mesh.index.count ) { - if ( mesh.isInterleaved( mesh.index ) ) instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.interleaved).getAddress(); - else instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.attributes.front().buffer).getAddress(); + instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.attributes.front().buffer).getAddress(); } if ( mesh.indirect.count ) { - if ( mesh.isInterleaved( mesh.indirect ) ) instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.interleaved).getAddress(); - else instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.attributes.front().buffer).getAddress(); - + instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.attributes.front().buffer).getAddress(); instanceAddresses.drawID = drawID; } } @@ -577,7 +532,6 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Base, UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base, R32G32_SFLOAT, st) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base, R32G32B32_SFLOAT, normal) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base, R32G32B32_SFLOAT, tangent) - UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base, R16G16_UINT, id) ); // it'd be super sugoi if I could somehow macro this annoyance UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base, { @@ -588,7 +542,6 @@ UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base, { uf::vector::lerp( p1.st, p2.st, t ), uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ), uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ), - t < 0.5 ? p1.id : p2.id, }; }) @@ -599,7 +552,6 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned, UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32_SFLOAT, st) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32B32_SFLOAT, normal) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32B32_SFLOAT, tangent) - UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R16G16_UINT, id) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R16G16B16A16_UINT, joints) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32B32A32_SFLOAT, weights) ); @@ -611,7 +563,6 @@ UF_VERTEX_INTERPOLATE(uf::graph::mesh::Skinned, { uf::vector::lerp( p1.st, p2.st, t ), uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ), uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ), - t < 0.5 ? p1.id : p2.id, t < 0.5 ? p1.joints : p2.joints, uf::vector::lerp( p1.weights, p2.weights, t ), }; @@ -625,7 +576,6 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Base_16f, UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_16f, R16G16_SFLOAT, st) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_16f, R16G16B16_SFLOAT, normal) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_16f, R16G16B16_SFLOAT, tangent) - UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_16f, R16G16_UINT, id) ); UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base_16f, { return t < 0.5 ? p1 : p2; @@ -638,7 +588,6 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned_16f, UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16_SFLOAT, st) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16B16_SFLOAT, normal) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16B16_SFLOAT, tangent) - UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16_UINT, id) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16B16A16_UINT, joints) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16B16A16_SFLOAT, weights) ); @@ -654,7 +603,6 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Base_u16q, UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_u16q, R16G16_UINT, st) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_u16q, R16G16B16_UINT, normal) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_u16q, R16G16B16_UINT, tangent) - UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_u16q, R16G16_UINT, id) ); UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base_u16q, { return t < 0.5 ? p1 : p2; @@ -667,7 +615,6 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned_u16q, UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16_UINT, st) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16B16_UINT, normal) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16B16_UINT, tangent) - UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16_UINT, id) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16B16A16_UINT, joints) UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16B16A16_UINT, weights) ); @@ -1371,7 +1318,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) if ( mesh.indirect.count && mesh.indirect.count <= primitives.size() ) { auto& attribute = mesh.indirect.attributes.front(); - auto& buffer = mesh.buffers[mesh.isInterleaved(mesh.indirect.interleaved) ? mesh.indirect.interleaved : attribute.buffer]; + auto& buffer = mesh.buffers[attribute.buffer]; pod::DrawCommand* drawCommands = (pod::DrawCommand*) buffer.data(); auto& drawCommand = drawCommands[drawID]; drawCommand.instanceID = instanceID; @@ -1860,7 +1807,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { if ( radius > 0 && mesh.indirect.count && mesh.indirect.count <= primitives.size() ) { // deduce draw command (indirect) buffer to write to auto& attribute = mesh.indirect.attributes.front(); - auto& buffer = mesh.buffers[mesh.isInterleaved(mesh.indirect.interleaved) ? mesh.indirect.interleaved : attribute.buffer]; + auto& buffer = mesh.buffers[attribute.buffer]; pod::DrawCommand* drawCommands = (pod::DrawCommand*) buffer.data(); // queues uf::stl::unordered_map> ranges; @@ -1951,8 +1898,8 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { // reset from LOD0 //primitives[drawID].drawCommand.instances = 1; - primitives[drawID].drawCommand.indexID = primitives[drawID].lod.levels[0].indexID; primitives[drawID].drawCommand.indices = primitives[drawID].lod.levels[0].indices; + primitives[drawID].drawCommand.indexID = primitives[drawID].lod.levels[0].indexID; primitives[drawID].drawCommand.vertexID = primitives[drawID].lod.levels[0].vertexID; primitives[drawID].drawCommand.vertices = primitives[drawID].lod.levels[0].vertices; @@ -2019,8 +1966,8 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { int8_t lodLevel = queuedLODs[drawID]; // reset from LOD0 //primitives[drawID].drawCommand.instances = 1; - primitives[drawID].drawCommand.indexID = primitives[drawID].lod.levels[0].indexID; primitives[drawID].drawCommand.indices = primitives[drawID].lod.levels[0].indices; + primitives[drawID].drawCommand.indexID = primitives[drawID].lod.levels[0].indexID; primitives[drawID].drawCommand.vertexID = primitives[drawID].lod.levels[0].vertexID; primitives[drawID].drawCommand.vertices = primitives[drawID].lod.levels[0].vertices; diff --git a/engine/src/ext/gltf/gltf.cpp b/engine/src/ext/gltf/gltf.cpp index ad17a857..278c4e1c 100644 --- a/engine/src/ext/gltf/gltf.cpp +++ b/engine/src/ext/gltf/gltf.cpp @@ -21,6 +21,8 @@ #include namespace { + typedef uf::Meshlet_T Meshlet; + decltype(auto) getWrapMode(int32_t wrapMode) { switch (wrapMode) { case 10497: return uf::renderer::enums::AddressMode::REPEAT; @@ -139,13 +141,6 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const return; } -#if 0 - if ( !uf::Mesh::defaultInterleaved ) { - uf::Mesh::defaultInterleaved = true; - UF_MSG_INFO("loading gltf file, defaulting to de-interleaved meshes (makes things easier)"); - } -#endif - uf::stl::string key = graph.metadata["key"].as(""); if ( key != "" ) { key += ":"; @@ -323,27 +318,6 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const meshgrid.metadata = value["grid"]; }); - #if 0 && UF_USE_MESHOPT - // cleanup if blender's exporter is poopy - if ( graph.metadata["exporter"]["optimize"].as(false) || graph.metadata["exporter"]["optimize"].as("") == "tagged" ) { - if ( graph.metadata["exporter"]["optimize"].as("") == "tagged" ) { - ext::json::forEach( graph.metadata["tags"], [&]( const uf::stl::string& key, ext::json::Value& value ) { - if ( ext::json::isNull( value["optimize meshlets"] ) ) return; - if ( uf::string::isRegex( key ) ) { - if ( !uf::string::matched( keyName, key ) ) return; - } else if ( keyName != key ) return; - meshopt.should = true; - if ( ext::json::isObject( value["optimize meshlets"] ) ) { - meshopt.level = value["optimize meshlets"]["level"].as(meshopt.level); - meshopt.simplify = value["optimize meshlets"]["simplify"].as(meshopt.simplify); - meshopt.print = value["optimize meshlets"]["print"].as(meshopt.print); - meshopt.lods = value["optimize meshlets"]["lods"].as(meshopt.lods); - } - }); - } - } - #endif - if ( ext::json::isObject( meshgrid.metadata ) ) { if ( meshgrid.metadata["size"].is() ) { size_t d = meshgrid.metadata["size"].as(); @@ -358,19 +332,8 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const meshgrid.cleanup = meshgrid.metadata["cleanup"].as(meshgrid.cleanup); } - if ( graph.metadata["renderer"]["skinned"].as(true) ) { - #define UF_GRAPH_MESH_FORMAT uf::graph::mesh::Skinned, uint32_t - #define UF_GRAPH_PROCESS_PRIMITIVES_FULL 1 - #define UF_GRAPH_GRID 1 + { #include "processPrimitives.inl" - #undef UF_GRAPH_PROCESS_PRIMITIVES_FULL - #undef UF_GRAPH_MESH_FORMAT - } else { - #define UF_GRAPH_MESH_FORMAT uf::graph::mesh::Base, uint32_t - #define UF_GRAPH_PROCESS_PRIMITIVES_FULL 0 - #include "processPrimitives.inl" - #undef UF_GRAPH_PROCESS_PRIMITIVES_FULL - #undef UF_GRAPH_MESH_FORMAT } } } diff --git a/engine/src/ext/gltf/processPrimitives.inl b/engine/src/ext/gltf/processPrimitives.inl index f581e1dd..fbbb12ab 100644 --- a/engine/src/ext/gltf/processPrimitives.inl +++ b/engine/src/ext/gltf/processPrimitives.inl @@ -16,7 +16,7 @@ if ( graph.metadata["sanitizer"]["tangents"].as(false) ) { sanitizer.tangents.should = true; } -uf::stl::vector> meshlets; +uf::stl::vector<::Meshlet> meshlets; for ( auto& p : m.primitives ) { size_t primitiveID = meshlets.size(); @@ -41,10 +41,8 @@ for ( auto& p : m.primitives ) { {"COLOR_0", {}}, {"NORMAL", {}}, {"TANGENT", {}}, -#if UF_GRAPH_PROCESS_PRIMITIVES_FULL {"JOINTS_0", {}}, {"WEIGHTS_0", {}}, -#endif }; for ( auto& kv : attributes ) { @@ -124,10 +122,8 @@ for ( auto& p : m.primitives ) { ITERATE_ATTRIBUTE("COLOR_0", color, 255.0f); ITERATE_ATTRIBUTE("NORMAL", normal, 1); ITERATE_ATTRIBUTE("TANGENT", tangent, 1); - #if UF_GRAPH_PROCESS_PRIMITIVES_FULL ITERATE_ATTRIBUTE("JOINTS_0", joints, 1); ITERATE_ATTRIBUTE("WEIGHTS_0", weights, 1); - #endif #undef ITERATE_ATTRIBUTE @@ -136,16 +132,11 @@ for ( auto& p : m.primitives ) { if ( graph.metadata["renderer"]["invert"].as(true) ){ vertex.position.x = -vertex.position.x; vertex.normal.x = -vertex.normal.x; - #if UF_GRAPH_PROCESS_PRIMITIVES_FULL vertex.tangent.x = -vertex.tangent.x; - #endif meshlet.primitive.instance.bounds.min = uf::vector::min( meshlet.primitive.instance.bounds.min, vertex.position ); meshlet.primitive.instance.bounds.max = uf::vector::max( meshlet.primitive.instance.bounds.max, vertex.position ); } - - vertex.id.x = primitiveID; - vertex.id.y = meshID; } if ( p.indices > -1 ) { @@ -340,62 +331,4 @@ if ( meshgrid.grid.divisions.x > 1 || meshgrid.grid.divisions.y > 1 || meshgrid. meshlets = std::move( partitioned ); } -// optimize each meshlet if requested -#if 0 && UF_USE_MESHOPT // should probably instead do it on the entire mesh? -if ( meshopt.should ) { - for ( auto& meshlet : meshlets ) { - if ( !ext::meshopt::optimize( meshlet, meshopt.simplify, meshopt.level, meshopt.print ) ) { - UF_MSG_ERROR("Mesh optimization failed: {}", keyName ); - } - /* - if ( meshopt.lods ) { - auto factors = ext::meshopt::computeLODs( meshlet.indices.size() ); - auto lodMetadata = ext::meshopt::generateLODs( meshlet, factors, meshopt.print ); - if ( lodMetadata.empty() ) { - UF_MSG_ERROR("LOD generation failed: {}", keyName ); - } - } - */ - } -} -#endif - -{ - size_t indexID = 0; - size_t vertexID = 0; - - mesh.bindIndirect(); - mesh.bind(false); // default to de-interleaved regardless of requirement (makes things easier) - - uf::stl::vector drawCommands; - drawCommands.reserve( meshlets.size() ); - primitives.reserve( meshlets.size() ); - - for ( auto& meshlet : meshlets ) { - meshlet.primitive.drawCommand.instances = 1; - meshlet.primitive.drawCommand.instanceID = ++masterInstanceID; // this doesn't matter...... - meshlet.primitive.drawCommand.indexID = indexID; - meshlet.primitive.drawCommand.indices = meshlet.indices.size(); - meshlet.primitive.drawCommand.vertexID = vertexID; - meshlet.primitive.drawCommand.vertices = meshlet.vertices.size(); - - // copy to LOD metadata - meshlet.primitive.lod.levels[0].indexID = indexID; - meshlet.primitive.lod.levels[0].indices = meshlet.indices.size(); - meshlet.primitive.lod.levels[0].vertexID = vertexID; - meshlet.primitive.lod.levels[0].vertices = meshlet.vertices.size(); - - drawCommands.emplace_back(meshlet.primitive.drawCommand); - - primitives.emplace_back( meshlet.primitive ); - - indexID += meshlet.indices.size(); - vertexID += meshlet.vertices.size(); - - mesh.insertVertices(meshlet.vertices); - mesh.insertIndices(meshlet.indices); - } - - mesh.insertIndirects(drawCommands); - mesh.updateDescriptor(); -} \ No newline at end of file +mesh.compile( meshlets, primitives ); \ No newline at end of file diff --git a/engine/src/ext/meshopt/meshopt.cpp b/engine/src/ext/meshopt/meshopt.cpp index 1663e64e..e5a695d0 100644 --- a/engine/src/ext/meshopt/meshopt.cpp +++ b/engine/src/ext/meshopt/meshopt.cpp @@ -26,10 +26,6 @@ bool ext::meshopt::optimize( uf::Mesh& mesh, float simplify, size_t o, bool verb if ( o == 0 ) { return false; // true, since theres no error technically? } - if ( mesh.isInterleaved() ) { - UF_MSG_ERROR("Optimization of interleaved meshes is currently not supported."); - return false; - } mesh.updateDescriptor(); const auto& views = mesh.buffer_views; @@ -145,7 +141,7 @@ bool ext::meshopt::optimize( uf::Mesh& mesh, float simplify, size_t o, bool verb // apply index buffer (if missing) if ( mesh.index.attributes.empty() ) { mesh.bindIndex(); - mesh.bind(mesh, mesh.isInterleaved()); + mesh.bind(mesh); } // write indices to buffer @@ -183,11 +179,6 @@ uf::stl::vector ext::meshopt::computeLODs( size_t count, size_t maxLODs, uf::stl::vector ext::meshopt::generateLODs( uf::Mesh& mesh, const uf::stl::vector& lodFactors, bool verbose ) { uf::stl::vector lodMetadata; - - if ( mesh.isInterleaved() ) { - UF_MSG_ERROR("Cannot generate LODs on interleaved meshes."); - return lodMetadata; - } mesh.updateDescriptor(); const auto& views = mesh.buffer_views; @@ -209,8 +200,8 @@ uf::stl::vector ext::meshopt::generateLODs( uf::Mesh& mesh, co for ( size_t viewIdx = 0; viewIdx < views.size(); ++viewIdx ) { uint32_t cmdIdx = views[viewIdx].indirectIndex; auto& cmd = drawCommands[cmdIdx]; - lodMetadata[cmdIdx].levels[0].indexID = cmd.indexID; lodMetadata[cmdIdx].levels[0].indices = cmd.indices; + lodMetadata[cmdIdx].levels[0].indexID = cmd.indexID; lodMetadata[cmdIdx].levels[0].vertexID = cmd.vertexID; lodMetadata[cmdIdx].levels[0].vertices = cmd.vertices; } @@ -278,8 +269,8 @@ uf::stl::vector ext::meshopt::generateLODs( uf::Mesh& mesh, co uint32_t lodVertexOffset = outVertices[0].size() / mesh.vertex.attributes[0].stride; uint32_t lodIndexOffset = outIndices.size(); - lodMetadata[cmdIdx].levels[lodIdx].indexID = lodIndexOffset; lodMetadata[cmdIdx].levels[lodIdx].indices = currentIndicesCount; + lodMetadata[cmdIdx].levels[lodIdx].indexID = lodIndexOffset; lodMetadata[cmdIdx].levels[lodIdx].vertexID = lodVertexOffset; lodMetadata[cmdIdx].levels[lodIdx].vertices = uniqueVertices; diff --git a/engine/src/ext/opengl/graphic.cpp b/engine/src/ext/opengl/graphic.cpp index a1a69c60..ad56facc 100644 --- a/engine/src/ext/opengl/graphic.cpp +++ b/engine/src/ext/opengl/graphic.cpp @@ -244,13 +244,7 @@ void ext::opengl::Graphic::initializeMesh( uf::Mesh& mesh, bool buffer ) { descriptor.inputs.bufferOffset = buffers.size(); // buffers.empty() ? 0 : buffers.size() - 1; #define PARSE_INPUT_INITIALIZE(NAME, USAGE){\ - if ( mesh.isInterleaved( mesh.NAME.interleaved ) ) {\ - auto& buffer = mesh.buffers[mesh.NAME.interleaved];\ - if ( !buffer.empty() ) {\ - descriptor.inputs.NAME.interleaved = initializeBuffer( (const void*) buffer.data(), buffer.size(), USAGE, alias );\ - this->metadata.buffers[#NAME] = descriptor.inputs.NAME.interleaved;\ - } else mesh.NAME.interleaved = -1;\ - } else for ( size_t i = 0; i < descriptor.inputs.NAME.attributes.size(); ++i ) {\ + for ( size_t i = 0; i < descriptor.inputs.NAME.attributes.size(); ++i ) {\ auto& attribute = descriptor.inputs.NAME.attributes[i];\ auto& buffer = mesh.buffers[attribute.buffer];\ if ( !buffer.empty() ) {\ @@ -294,12 +288,7 @@ bool ext::opengl::Graphic::updateMesh( uf::Mesh& mesh ) { uf::stl::vector queue; #define PARSE_INPUT_UPDATE(NAME, USAGE){\ - if ( mesh.isInterleaved( mesh.NAME.interleaved ) ) {\ - auto& buffer = mesh.buffers[mesh.NAME.interleaved];\ - if ( !buffer.empty() ) {\ - rebuild |= updateBuffer( (const void*) buffer.data(), buffer.size(), this->metadata.buffers[#NAME] );\ - } else mesh.NAME.interleaved = -1;\ - } else for ( size_t i = 0; i < descriptor.inputs.NAME.attributes.size(); ++i ) {\ + for ( size_t i = 0; i < descriptor.inputs.NAME.attributes.size(); ++i ) {\ auto& attribute = descriptor.inputs.NAME.attributes[i];\ auto& buffer = mesh.buffers[attribute.buffer];\ if ( !buffer.empty() ) {\ diff --git a/engine/src/ext/opengl/opengl.cpp b/engine/src/ext/opengl/opengl.cpp index 3a4c923f..69a05a26 100644 --- a/engine/src/ext/opengl/opengl.cpp +++ b/engine/src/ext/opengl/opengl.cpp @@ -263,16 +263,14 @@ void ext::opengl::initialize() { uf::renderer::AttributeDescriptor vertexAttributePosition, vertexAttributeUv, - vertexAttributeNormal, - vertexAttributeId; + vertexAttributeNormal; for ( auto& attribute : graphic.descriptor.attributes.vertex.descriptor ) { if ( attribute.name == "position" ) vertexAttributePosition = attribute; else if ( attribute.name == "normal" ) vertexAttributeNormal = attribute; else if ( attribute.name == "uv" ) vertexAttributeUv = attribute; - else if ( attribute.name == "id" ) vertexAttributeId = attribute; } - if ( vertexAttributePosition.name == "" || vertexAttributeUv.name == "" || vertexAttributeId.name == "" ) return; + if ( vertexAttributePosition.name == "" || vertexAttributeUv.name == "" ) return; bool hasNormals = vertexAttributeNormal.name != ""; // GPU-buffer based command dispatching @@ -293,12 +291,12 @@ void ext::opengl::initialize() { uint8_t* vertexDst = vertexDstPointer + (currentIndex * vertexStride); const pod::Vector3f& position = *((pod::Vector3f*) (vertexSrc + vertexAttributePosition.offset)); - const pod::Vector& id = *((pod::Vector*) (vertexSrc + vertexAttributeId.offset)); const pod::Vector2f& uv = *((pod::Vector2f*) (vertexSrc + vertexAttributeUv.offset)); pod::Vector3f& positionDst = *((pod::Vector3f*) (vertexDst + vertexAttributePosition.offset)); pod::Vector2f& uvDst = *((pod::Vector2f*) (vertexDst + vertexAttributeUv.offset)); + // to-do: update this auto& model = instances[id.x]; auto& material = materials[id.y]; auto& texture = textures[material.indexAlbedo]; diff --git a/engine/src/ext/valve/bsp.cpp b/engine/src/ext/valve/bsp.cpp index 0642fc1f..feb181a1 100644 --- a/engine/src/ext/valve/bsp.cpp +++ b/engine/src/ext/valve/bsp.cpp @@ -659,41 +659,7 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co auto& primitives = storage.primitives[meshName]; storage.instanceAddresses[meshName] = {}; - mesh.bindIndirect(); - mesh.bind(false); - - size_t indexID = 0, vertexID = 0, masterInstanceID = 0; - uf::stl::vector drawCommands; - - for ( auto& [materialID, meshlet] : meshlets) { - if ( meshlet.indices.empty() ) continue; - - meshlet.primitive.drawCommand.instances = 1; - meshlet.primitive.drawCommand.instanceID = ++masterInstanceID; - meshlet.primitive.drawCommand.indexID = indexID; - meshlet.primitive.drawCommand.indices = meshlet.indices.size(); - meshlet.primitive.drawCommand.vertexID = vertexID; - meshlet.primitive.drawCommand.vertices = meshlet.vertices.size(); - - meshlet.primitive.lod.levels[0] = { - indexID, - meshlet.indices.size(), - vertexID, - meshlet.vertices.size() - }; - - drawCommands.emplace_back(meshlet.primitive.drawCommand); - primitives.emplace_back(meshlet.primitive); - - indexID += meshlet.indices.size(); - vertexID += meshlet.vertices.size(); - - mesh.insertVertices(meshlet.vertices); - mesh.insertIndices(meshlet.indices); - } - - mesh.insertIndirects(drawCommands); - mesh.updateDescriptor(); + mesh.compile( meshlets, primitives ); } // read entities diff --git a/engine/src/ext/valve/mdl.cpp b/engine/src/ext/valve/mdl.cpp index b200036b..eca03d20 100644 --- a/engine/src/ext/valve/mdl.cpp +++ b/engine/src/ext/valve/mdl.cpp @@ -221,33 +221,7 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) { auto& meshlet = meshlets.emplace_back(); uf::stl::unordered_map vertRemap; - uf::stl::string matName = "missing_texture"; - if ( meshID < materials.size() ) { - matName = materials[meshID]; - } - - // does not exist, register - if ( storage.textures.map.count(matName) == 0 ) { - size_t imageID = graph.images.size(); - auto imgKeyName = graph.images.emplace_back(matName); - auto& image = storage.images[imgKeyName]; - - size_t textureID = graph.textures.size(); - auto texKeyName = graph.textures.emplace_back(matName); - storage.textures[texKeyName].index = imageID; - storage.texture2Ds[texKeyName]; - - size_t materialID = graph.materials.size(); - auto matKeyName = graph.materials.emplace_back(matName); - auto& material = storage.materials[matKeyName]; - material.indexAlbedo = textureID; - material.colorBase = {1.0f, 1.0f, 1.0f, 1.0f}; - } - - // meshlet.primitive.instance.materialID = ...; - const impl::vtxStripGroup_t* stripGroups = (const impl::vtxStripGroup_t*)((uint8_t*)&mesh + mesh.stripGroupHeaderOffset); - for ( int sg = 0; sg < mesh.numStripGroups; ++sg ) { const impl::vtxStripGroup_t& stripGroup = stripGroups[sg]; @@ -269,16 +243,71 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) { vert.normal = uf::vector::normalize( impl::convertPos( srcVert.m_vecNormal ) ); vert.uv = srcVert.m_vecTexCoord; vert.color = {1.0f, 1.0f, 1.0f, 1.0f}; + vert.joints.x = srcVert.m_BoneWeights.numbones > 0 ? std::max(0, srcVert.m_BoneWeights.bone[0]) : 0; + vert.joints.y = srcVert.m_BoneWeights.numbones > 1 ? std::max(0, srcVert.m_BoneWeights.bone[1]) : 0; + vert.joints.z = srcVert.m_BoneWeights.numbones > 2 ? std::max(0, srcVert.m_BoneWeights.bone[2]) : 0; + vert.joints.w = 0; - // to-do: bounds calculation + vert.weights.x = srcVert.m_BoneWeights.numbones > 0 ? srcVert.m_BoneWeights.weight[0] : 1.0f; + vert.weights.y = srcVert.m_BoneWeights.numbones > 1 ? srcVert.m_BoneWeights.weight[1] : 0.0f; + vert.weights.z = srcVert.m_BoneWeights.numbones > 2 ? srcVert.m_BoneWeights.weight[2] : 0.0f; + vert.weights.w = 0.0f; + + // Bounds calculation + auto& bounds = meshlet.primitive.instance.bounds; + if ( vertRemap.size() == 1 ) { + bounds.min = bounds.max = vert.position; + } else { + bounds.min = uf::vector::min( bounds.min, vert.position ); + bounds.max = uf::vector::max( bounds.max, vert.position ); + } } meshlet.indices.push_back(vertRemap[originalVvdID]); } } + + size_t materialID = 0; + uf::stl::string matName = "missing_texture"; + if ( meshID < materials.size() ) matName = materials[meshID]; + if ( storage.materials.map.count(matName) > 0 ) { + // to-do: add an indexOf + for ( ; materialID < graph.materials.size(); ++materialID ) { + if ( graph.materials[materialID] == matName ) break; + } + } else { + // does not exist, register + size_t imageID = graph.images.size(); + auto imgKeyName = graph.images.emplace_back(matName); + auto& image = storage.images[imgKeyName]; + + size_t textureID = graph.textures.size(); + auto texKeyName = graph.textures.emplace_back(matName); + storage.textures[texKeyName].index = imageID; + storage.texture2Ds[texKeyName]; + + materialID = graph.materials.size(); + auto matKeyName = graph.materials.emplace_back(matName); + auto& material = storage.materials[matKeyName]; + material.indexAlbedo = textureID; + material.colorBase = {1.0f, 1.0f, 1.0f, 1.0f}; + } + meshlet.primitive.instance.materialID = materialID; } } } + if ( !meshlets.empty() ) { + auto meshName = filename; + graph.meshes.emplace_back(meshName); + graph.primitives.emplace_back(meshName); + + auto& mesh = storage.meshes[meshName]; + auto& primitives = storage.primitives[meshName]; + storage.instanceAddresses[meshName] = {}; + + mesh.compile( meshlets, primitives ); + } + return true; } \ No newline at end of file diff --git a/engine/src/ext/vulkan/graphic.cpp b/engine/src/ext/vulkan/graphic.cpp index c82ba466..6e2faefd 100644 --- a/engine/src/ext/vulkan/graphic.cpp +++ b/engine/src/ext/vulkan/graphic.cpp @@ -359,22 +359,7 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes size_t vertexBindID = 0; size_t vertexLocationID = 0; if ( !descriptor.inputs.vertex.attributes.empty() ) { - if ( 0 <= descriptor.inputs.vertex.interleaved ) { - inputBindingDescriptions.emplace_back(ext::vulkan::initializers::vertexInputBindingDescription( - vertexBindID, // descriptor.inputs.vertex.interleaved, - descriptor.inputs.vertex.size, - VK_VERTEX_INPUT_RATE_VERTEX - )); - for ( auto& attribute : descriptor.inputs.vertex.attributes ) { - attributeDescriptions.emplace_back(ext::vulkan::initializers::vertexInputAttributeDescription( - vertexBindID, - vertexLocationID++, - attribute.descriptor.format, - attribute.descriptor.offset - )); - } - ++vertexBindID; - } else for ( auto& attribute : descriptor.inputs.vertex.attributes ) { + for ( auto& attribute : descriptor.inputs.vertex.attributes ) { inputBindingDescriptions.emplace_back(ext::vulkan::initializers::vertexInputBindingDescription( vertexBindID, // attribute.buffer, attribute.descriptor.size, @@ -1211,13 +1196,7 @@ void ext::vulkan::Graphic::initializeMesh( uf::Mesh& mesh, bool buffer ) { // VkBufferUsageFlags baseUsage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR; #define PARSE_INPUT_INITIALIZE(NAME, USAGE){\ - if ( mesh.isInterleaved( mesh.NAME.interleaved ) ) {\ - auto& buffer = mesh.buffers[mesh.NAME.interleaved];\ - if ( !buffer.empty() ) {\ - descriptor.inputs.NAME.interleaved = initializeBuffer( (const void*) buffer.data(), buffer.size(), USAGE | baseUsage );\ - this->metadata.buffers[#NAME] = descriptor.inputs.NAME.interleaved;\ - } else mesh.NAME.interleaved = -1;\ - } else for ( size_t i = 0; i < descriptor.inputs.NAME.attributes.size(); ++i ) {\ + for ( size_t i = 0; i < descriptor.inputs.NAME.attributes.size(); ++i ) {\ auto& attribute = descriptor.inputs.NAME.attributes[i];\ auto& buffer = mesh.buffers[attribute.buffer];\ if ( !buffer.empty() ) {\ @@ -1258,12 +1237,7 @@ bool ext::vulkan::Graphic::updateMesh( uf::Mesh& mesh ) { uf::stl::vector queue; #define PARSE_INPUT_UPDATE(NAME, USAGE){\ - if ( mesh.isInterleaved( mesh.NAME.interleaved ) ) {\ - auto& buffer = mesh.buffers[mesh.NAME.interleaved];\ - if ( !buffer.empty() ) {\ - rebuild = updateBuffer( (const void*) buffer.data(), buffer.size(), this->metadata.buffers[#NAME] ) || rebuild;\ - } else mesh.NAME.interleaved = -1;\ - } else for ( size_t i = 0; i < descriptor.inputs.NAME.attributes.size(); ++i ) {\ + for ( size_t i = 0; i < descriptor.inputs.NAME.attributes.size(); ++i ) {\ auto& attribute = descriptor.inputs.NAME.attributes[i];\ auto& buffer = mesh.buffers[attribute.buffer];\ if ( !buffer.empty() ) {\ @@ -1333,7 +1307,7 @@ void ext::vulkan::Graphic::generateBottomAccelerationStructures() { for ( auto& attribute : mesh.vertex.attributes ) if ( attribute.descriptor.name == "position" ) vertexAttribute = attribute; UF_ASSERT( vertexAttribute.descriptor.name == "position" ); - size_t vertexBufferIndex = (0 <= mesh.vertex.interleaved ? mesh.vertex.interleaved : vertexAttribute.buffer) + mesh.bufferOffset; + size_t vertexBufferIndex = (vertexAttribute.buffer) + mesh.bufferOffset; if ( this->metadata.buffers.count("vertexSkinned") > 0 ) vertexBufferIndex = this->metadata.buffers["vertexSkinned"]; vertexBufferAddress = this->buffers[vertexBufferIndex].getAddress(); } @@ -1341,7 +1315,7 @@ void ext::vulkan::Graphic::generateBottomAccelerationStructures() { if ( mesh.index.count ) { indexAttribute = mesh.index.attributes.front(); - size_t indexBufferIndex = (0 <= mesh.index.interleaved ? mesh.index.interleaved : indexAttribute.buffer) + mesh.bufferOffset; + size_t indexBufferIndex = (indexAttribute.buffer) + mesh.bufferOffset; indexBufferAddress = this->buffers[indexBufferIndex].getAddress(); } @@ -2046,24 +2020,14 @@ void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, const GraphicD uf::stl::vector offset; } vertexInstance; - if ( 0 <= descriptor.inputs.vertex.interleaved && !descriptor.inputs.vertex.attributes.empty() ) { - vertexInstance.buffer.emplace_back( buffers.at(descriptor.inputs.vertex.interleaved).buffer ); - vertexInstance.offset.emplace_back( descriptor.inputs.vertex.offset ); - } else { - for ( auto& attribute : descriptor.inputs.vertex.attributes ) { - vertexInstance.buffer.emplace_back( attribute.buffer < 0 ? VK_NULL_HANDLE : buffers.at(attribute.buffer).buffer ); - vertexInstance.offset.emplace_back( attribute.offset ); - } + for ( auto& attribute : descriptor.inputs.vertex.attributes ) { + vertexInstance.buffer.emplace_back( attribute.buffer < 0 ? VK_NULL_HANDLE : buffers.at(attribute.buffer).buffer ); + vertexInstance.offset.emplace_back( attribute.offset ); } - if ( 0 <= descriptor.inputs.instance.interleaved && !descriptor.inputs.instance.attributes.empty() ) { - vertexInstance.buffer.emplace_back( buffers.at(descriptor.inputs.instance.interleaved).buffer ); - vertexInstance.offset.emplace_back( descriptor.inputs.instance.offset ); - } else { - for ( auto& attribute : descriptor.inputs.instance.attributes ) { - vertexInstance.buffer.emplace_back( attribute.buffer < 0 ? VK_NULL_HANDLE : buffers.at(attribute.buffer).buffer ); - vertexInstance.offset.emplace_back( attribute.offset ); - } + for ( auto& attribute : descriptor.inputs.instance.attributes ) { + vertexInstance.buffer.emplace_back( attribute.buffer < 0 ? VK_NULL_HANDLE : buffers.at(attribute.buffer).buffer ); + vertexInstance.offset.emplace_back( attribute.offset ); } struct { diff --git a/engine/src/ext/xatlas/xatlas.cpp b/engine/src/ext/xatlas/xatlas.cpp index 8a44b143..e835d8d2 100644 --- a/engine/src/ext/xatlas/xatlas.cpp +++ b/engine/src/ext/xatlas/xatlas.cpp @@ -33,10 +33,6 @@ size_t ext::xatlas::unwrap( pod::Graph& graph ) { auto& mesh = /*graph.storage*/storage.meshes[name]; auto& source = sources[index]; - if ( mesh.isInterleaved() ) { - UF_EXCEPTION("unwrapping interleaved mesh is not supported"); - } - bool should = false; if ( graph.metadata["exporter"]["unwrap"].is() && graph.metadata["exporter"]["unwrap"].as() ) { should = true; @@ -247,6 +243,7 @@ size_t ext::xatlas::unwrap( pod::Graph& graph ) { if ( source.vertex.count == 0 ) continue; const auto& srcView = source.buffer_views[entry.commandID]; + const auto& dstView = mesh.buffer_views[entry.commandID]; size_t dstVertexFirst = 0; size_t dstIndexFirst = 0; @@ -256,22 +253,31 @@ size_t ext::xatlas::unwrap( pod::Graph& graph ) { dstIndexFirst = drawCommands[entry.commandID].indexID; } + auto stView = dstView["st"]; + int stAttributeIndex = -1; + for ( auto attrIdx = 0; attrIdx < mesh.vertex.attributes.size(); ++attrIdx ) { + if ( mesh.vertex.attributes[attrIdx].descriptor.name == "st" ) { + stAttributeIndex = attrIdx; + break; + } + } + for ( auto j = 0; j < xmesh.vertexCount; ++j ) { auto& vertex = xmesh.vertexArray[j]; - uint32_t ref = vertex.xref; // original vertex index relative to the sub-mesh + uint32_t ref = vertex.xref; // original vertex index for ( auto attrIdx = 0; attrIdx < mesh.vertex.attributes.size(); ++attrIdx ) { auto srcAttribute = srcView.vertex.attributes[attrIdx]; auto dstAttribute = mesh.vertex.attributes[attrIdx]; - uint8_t* dstPtr = static_cast(dstAttribute.pointer) + dstAttribute.stride * (dstVertexFirst + j); - - if ( dstAttribute.descriptor.name == "st" ) { - pod::Vector2f& st = *(pod::Vector2f*)dstPtr; - st = pod::Vector2f{ vertex.uv[0] / atlas.pointer->width, vertex.uv[1] / atlas.pointer->height }; + if ( attrIdx == stAttributeIndex ) { + auto& st = uf::mesh::getVertexAttribute( dstView, stView, dstVertexFirst + j ); + st.x = vertex.uv[0] / atlas.pointer->width; + st.y = vertex.uv[1] / atlas.pointer->height; } else { + uint8_t* dstPtr = static_cast(dstAttribute.pointer) + dstAttribute.stride * (dstVertexFirst + j); const uint8_t* srcPtr = static_cast(srcAttribute.pointer) + srcAttribute.stride * (srcView.vertex.first + ref); - memcpy(dstPtr, srcPtr, srcAttribute.descriptor.size); + std::memcpy(dstPtr, srcPtr, srcAttribute.descriptor.size); } } } @@ -281,11 +287,7 @@ size_t ext::xatlas::unwrap( pod::Graph& graph ) { uint8_t* dstIndexPtr = static_cast(indexAttribute.pointer) + indexAttribute.stride * dstIndexFirst; for ( auto idx = 0; idx < xmesh.indexCount; ++idx ) { - switch ( mesh.index.size ) { - case 1: (( uint8_t*) dstIndexPtr)[idx] = (uint8_t) xmesh.indexArray[idx]; break; - case 2: ((uint16_t*) dstIndexPtr)[idx] = (uint16_t) xmesh.indexArray[idx]; break; - case 4: ((uint32_t*) dstIndexPtr)[idx] = (uint32_t) xmesh.indexArray[idx]; break; - } + uf::mesh::setIndex(dstIndexPtr, mesh.index.size, idx, xmesh.indexArray[idx]); } } mesh.updateDescriptor(); diff --git a/engine/src/utils/image/atlas.cpp b/engine/src/utils/image/atlas.cpp index 879badd3..6f1de4b2 100644 --- a/engine/src/utils/image/atlas.cpp +++ b/engine/src/utils/image/atlas.cpp @@ -1,8 +1,10 @@ #include -#include #include #include +#define STB_RECT_PACK_IMPLEMENTATION +#include + pod::Atlas::hash_t uf::atlas::add( pod::Atlas& atlas, const pod::Image& image, const pod::Atlas::hash_t& hash ) { size_t index = atlas.tiles.size(); if ( atlas.tiles.count( hash ) > 0 ) return hash; @@ -18,58 +20,81 @@ pod::Atlas::hash_t uf::atlas::add( pod::Atlas& atlas, const pod::Image& image ) void uf::atlas::generate( pod::Atlas& atlas, float padding ) { if ( atlas.tiles.empty() ) return; - BinPack2D::CanvasArray internalAtlas; - BinPack2D::ContentAccumulator queue, stored, remainder; - pod::Vector2ui size = {}; - pod::Vector3ui largest = {}; - size_t index = 0; + uf::stl::vector rects; + uf::stl::vector hashes; + rects.reserve(atlas.tiles.size()); + hashes.reserve(atlas.tiles.size()); + size_t area = 0; size_t channels = 1; + for ( auto& [ hash, tile ] : atlas.tiles ) { auto& dim = tile.image.size; channels = std::max( channels, tile.image.channels ); - queue += BinPack2D::Content(hash, BinPack2D::Coord(), BinPack2D::Size(dim.x, dim.y), false ); - size += dim; + + stbrp_rect rect; + rect.id = static_cast(rects.size()); + rect.w = dim.x; + rect.h = dim.y; + rects.push_back(rect); + hashes.push_back(hash); + area += dim.x * dim.y; - if ( area >= largest.z ) { - largest.x = dim.x; - largest.y = dim.y; + } + + size_t side = std::sqrt( area ) * std::max(1.0f, padding); + pod::Vector2ui size = { std::bit_ceil(side), std::bit_ceil(side) }; + + bool all_packed = false; + uf::stl::vector nodes; + + while ( !all_packed ) { + nodes.resize(size.x); + stbrp_context context; + stbrp_init_target(&context, size.x, size.y, nodes.data(), nodes.size()); + + all_packed = stbrp_pack_rects(&context, rects.data(), rects.size()); + + if ( !all_packed ) { + size.x *= 2; + size.y *= 2; } } - size_t tries = 16; - do { - size_t side = std::sqrt( area ) * padding; - size = { std::bit_ceil(side), std::bit_ceil(side) }; // to-do: non-C++20 method - queue.Sort(); - internalAtlas = BinPack2D::UniformCanvasArrayBuilder(size.x, size.y, 1).Build(); - bool success = internalAtlas.Place( queue, remainder ); - if ( success && remainder.Get().empty() ) break; - // increase padding - padding += 0.10f; - } while ( --tries ); - internalAtlas.CollectContent( stored ); uf::image::load( atlas.image, NULL, size, 8, channels ); auto& dstBuffer = atlas.image.pixels; - for ( size_t i = 0; i < size.x * size.y * channels; ++i ) dstBuffer[i] = 0; - for ( auto& it : stored.Get() ) { - auto& tile = atlas.tiles[it.content]; - tile.coord = { it.coord.x, it.coord.y }; - tile.size = { it.size.w, it.size.h }; + + memset(dstBuffer.data(), 0, size.x * size.y * channels * sizeof(decltype(dstBuffer[0]))); + + for ( size_t i = 0; i < rects.size(); ++i ) { + const auto& rect = rects[i]; + auto hash = hashes[rect.id]; + auto& tile = atlas.tiles[hash]; + + tile.coord = { rect.x, rect.y }; + tile.size = { rect.w, rect.h }; auto& image = tile.image; auto& srcBuffer = image.pixels; auto srcChannels = image.channels; + size_t rowSizeSrc = tile.size.x * srcChannels; + size_t rowSizeDst = tile.size.x * channels; + for ( size_t y = 0; y < tile.size.y; ++y ) { - for ( size_t x = 0; x < tile.size.x; ++x ) { - size_t src = (y * tile.size.x * srcChannels) + (x * srcChannels); - size_t dst = ((y + tile.coord.y) * size.x * channels) + ((x + tile.coord.x) * channels); - for ( size_t i = 0; i < srcChannels; ++i ) { - dstBuffer[dst+i] = srcBuffer[src+i]; + size_t srcIndex = y * tile.size.x * srcChannels; + size_t dstIndex = ((y + tile.coord.y) * size.x * channels) + (tile.coord.x * channels); + + if ( srcChannels == channels ) { + memcpy(&dstBuffer[dstIndex], &srcBuffer[srcIndex], rowSizeSrc * sizeof(decltype(dstBuffer[0]))); + } else { + for ( size_t x = 0; x < tile.size.x; ++x ) { + for ( size_t c = 0; c < srcChannels; ++c ) { + dstBuffer[dstIndex + (x * channels) + c] = srcBuffer[srcIndex + (x * srcChannels) + c]; + } + } } } - } } } void uf::atlas::generate( pod::Atlas& atlas, const uf::stl::vector& images, float padding ) { diff --git a/engine/src/utils/math/physics/broadphase/bvh.cpp b/engine/src/utils/math/physics/broadphase/bvh.cpp index 82c4e830..397dc2a0 100644 --- a/engine/src/utils/math/physics/broadphase/bvh.cpp +++ b/engine/src/utils/math/physics/broadphase/bvh.cpp @@ -223,8 +223,8 @@ void impl::buildMeshBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::index_t // populate initial indices and bounds for ( auto& view : views ) { - auto& indices = view["index"]; - auto& positions = view["position"]; + auto& indices = view["index"_hash]; + auto& positions = view["position"_hash]; auto tris = view.index.count / 3; for ( auto triIndexID = 0; triIndexID < tris; ++triIndexID ) { @@ -441,8 +441,8 @@ void impl::refitBVH( pod::BVH& bvh, const uf::Mesh& mesh ) { // populate initial indices and bounds for ( auto& view : views ) { - auto& indices = view["index"]; - auto& positions = view["position"]; + auto& indices = view["index"_hash]; + auto& positions = view["position"_hash]; auto tris = view.index.count / 3; for ( auto triIndexID = 0; triIndexID < tris; ++triIndexID ) { diff --git a/engine/src/utils/math/physics/common.cpp b/engine/src/utils/math/physics/common.cpp index be2132cc..0239e2a6 100644 --- a/engine/src/utils/math/physics/common.cpp +++ b/engine/src/utils/math/physics/common.cpp @@ -624,7 +624,7 @@ pod::AABB impl::computeConvexHullAABB( const uf::Mesh::View& view, const uf::Mes return bounds; } pod::AABB impl::computeConvexHullAABB( const uf::Mesh::View& view, pod::AABB bounds ) { - return impl::computeConvexHullAABB( view, view["position"], bounds ); + return impl::computeConvexHullAABB( view, view["position"_hash], bounds ); } // combines two AABBs pod::AABB impl::mergeAabb( const pod::AABB& a, const pod::AABB& b ) { @@ -743,7 +743,7 @@ pod::AABB impl::computeAABB( const pod::PhysicsBody& body ) { return impl::transformAabbToWorld( body.collider.mesh.bvh->bounds[0], transform ); const auto& meshData = *body.collider.mesh.mesh; pod::AABB bounds = { { FLT_MAX, FLT_MAX, FLT_MAX }, { -FLT_MAX, -FLT_MAX, -FLT_MAX } }; - for ( const auto& view : meshData.buffer_views ) impl::computeConvexHullAABB( view, view["position"], bounds ); + for ( const auto& view : meshData.buffer_views ) impl::computeConvexHullAABB( view, view["position"_hash], bounds ); return impl::transformAabbToWorld( bounds, transform ); } default: { diff --git a/engine/src/utils/math/physics/narrowphase/epa.cpp b/engine/src/utils/math/physics/narrowphase/epa.cpp index 024a6632..be53e2ec 100644 --- a/engine/src/utils/math/physics/narrowphase/epa.cpp +++ b/engine/src/utils/math/physics/narrowphase/epa.cpp @@ -151,8 +151,8 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di if ( 0 <= selectedViewIdx && selectedViewIdx != viewIdx ) continue; const auto& view = mesh.buffer_views[viewIdx]; - auto& indices = view["index"]; - auto& positions = view["position"]; + auto& indices = view["index"_hash]; + auto& positions = view["position"_hash]; for ( size_t i = 0; i < view.index.count / 3; ++i ) { pod::Triangle tri = uf::mesh::fetchTriangle( view, indices, positions, i ); pod::Vector3f normal = impl::triangleNormal( tri ); diff --git a/engine/src/utils/math/physics/narrowphase/gjk.cpp b/engine/src/utils/math/physics/narrowphase/gjk.cpp index dfca66c0..d60b2418 100644 --- a/engine/src/utils/math/physics/narrowphase/gjk.cpp +++ b/engine/src/utils/math/physics/narrowphase/gjk.cpp @@ -66,7 +66,7 @@ pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f& for ( auto viewIdx = 0; viewIdx < mesh.buffer_views.size(); ++viewIdx ) { if ( 0 <= selectedViewIdx && selectedViewIdx != viewIdx ) continue; // cringe, but saves code duplication (could just alter the bounds above) const auto& view = mesh.buffer_views[viewIdx]; - auto& positions = view["position"]; + auto& positions = view["position"_hash]; for ( size_t i = 0; i < view.vertex.count; ++i ) { pod::Vector3f v = uf::mesh::fetchVertex( view, positions, i ); float dist = uf::vector::dot( v, localDir ); diff --git a/engine/src/utils/mesh/mesh.cpp b/engine/src/utils/mesh/mesh.cpp index 96221038..6425f642 100644 --- a/engine/src/utils/mesh/mesh.cpp +++ b/engine/src/utils/mesh/mesh.cpp @@ -103,12 +103,6 @@ UF_VERTEX_INTERPOLATE(pod::Vertex_3F, { }; }) -#if UF_USE_OPENGL - bool uf::Mesh::defaultInterleaved = true; -#else - bool uf::Mesh::defaultInterleaved = false; -#endif - void uf::Mesh::initialize() {} void uf::Mesh::destroy() { _destroy(vertex); @@ -118,20 +112,24 @@ void uf::Mesh::destroy() { buffers.clear(); } -uf::Mesh uf::Mesh::copy( bool interleaved ) const { +uf::Mesh uf::Mesh::copy() const { uf::Mesh res; - res.bind( *this, interleaved ); + res.bind( *this ); res.insert(*this); res.updateDescriptor(); return res; } -uf::Mesh uf::Mesh::copy() const { return copy( isInterleaved() ); } -// implicitly convert to opposite interleaving -uf::Mesh uf::Mesh::convert() const { return copy( !isInterleaved() ); } -uf::Mesh uf::Mesh::interleave() const { return copy(true); } -uf::Mesh uf::Mesh::deinterleave() const { return copy(false); } +uf::Mesh& uf::Mesh::copy( const uf::Mesh& src ) { + if ( src.buffers.empty() ) return *this; + + bind( src ); + insert( src ); + updateDescriptor(); + + return *this; +} void uf::Mesh::updateDescriptor() { _updateDescriptor(vertex); _updateDescriptor(index); @@ -139,14 +137,13 @@ void uf::Mesh::updateDescriptor() { _updateDescriptor(indirect); _updateViews(); } -void uf::Mesh::bind( const uf::Mesh& mesh ) { return bind( mesh, isInterleaved() ); } -void uf::Mesh::bind( const uf::Mesh& mesh, bool interleaved ) { +void uf::Mesh::bind( const uf::Mesh& mesh ) { vertex.attributes = mesh.vertex.attributes; index.attributes = mesh.index.attributes; instance.attributes = mesh.instance.attributes; indirect.attributes = mesh.indirect.attributes; - _bind( interleaved ); + _bind(); } void uf::Mesh::insert( const uf::Mesh& mesh ) { if ( vertex.attributes.empty() && index.attributes.empty() && instance.attributes.empty() && indirect.attributes.empty() ) bind( mesh ); @@ -170,7 +167,7 @@ void uf::Mesh::generateIndices() { _destroy( index ); } _bindI( index, size, type ); - _bind( isInterleaved( vertex.interleaved ) ); + _bind(); switch ( size ) { case 1: { uf::stl::vector indices( vertex.count ); std::iota( indices.begin(), indices.end(), 0 ); insertIndices( indices ); } break; @@ -178,14 +175,12 @@ void uf::Mesh::generateIndices() { case 4: { uf::stl::vector indices( vertex.count ); std::iota( indices.begin(), indices.end(), 0 ); insertIndices( indices ); } break; } } -uf::Mesh uf::Mesh::expand() { return expand( isInterleaved() ); } -uf::Mesh uf::Mesh::expand( bool interleaved ) { - uf::Mesh res = copy( interleaved ); +uf::Mesh uf::Mesh::expand( ) { + uf::Mesh res = copy(); res.resizeVertices( index.count ); res.vertex.count = index.count; - auto& srcIndex = index.attributes.front(); auto& dstIndex = res.index.attributes.front(); @@ -206,8 +201,10 @@ uf::Mesh uf::Mesh::expand( bool interleaved ) { auto& srcInput = vertex.attributes[_]; auto& dstInput = res.vertex.attributes[_]; - memcpy( dstInput.pointer, static_cast(srcInput.pointer) + index * srcInput.stride, srcInput.descriptor.size ); - dstInput.pointer = static_cast(dstInput.pointer) + dstInput.stride; + uint8_t* srcAddr = static_cast(srcInput.pointer) + index * srcInput.stride; + uint8_t* dstAddr = static_cast(dstInput.pointer) + idx * dstInput.stride; + + memcpy( dstAddr, srcAddr, srcInput.descriptor.size ); } } @@ -232,8 +229,6 @@ void uf::Mesh::clearAttribute( uf::Mesh::Input& input, const uf::Mesh::Attribute for ( size_t i = 0; i < input.attributes.size(); ++i ) if ( input.attributes[i].descriptor == attribute.descriptor ) return clearAttribute( input, i ); } void uf::Mesh::clearAttribute( uf::Mesh::Input& input, size_t i ) { - UF_ASSERT( !isInterleaved( input ) ); // can't be assed to de-interleave, erase, and then interleave again - auto attribute = input.attributes[i]; buffers[attribute.buffer].clear(); } @@ -272,127 +267,40 @@ void uf::Mesh::generateIndirect() { _bind(); insertIndirects( commands ); } -bool uf::Mesh::isInterleaved() const { return isInterleaved( vertex.interleaved ); } -bool uf::Mesh::isInterleaved( const uf::Mesh::Input& input ) const { return isInterleaved( input.interleaved ); } -bool uf::Mesh::isInterleaved( size_t i ) const { return 0 <= i && i < buffers.size(); } uf::Mesh::buffer_t& uf::Mesh::getBuffer( const uf::Mesh::Input& input, size_t i ) { return getBuffer( input, input.attributes[i] ); } uf::Mesh::buffer_t& uf::Mesh::getBuffer( const uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute ) { - return buffers[isInterleaved(input.interleaved) ? input.interleaved : attribute.buffer]; + return buffers[attribute.buffer]; } const uf::Mesh::buffer_t& uf::Mesh::getBuffer( const uf::Mesh::Input& input, size_t i ) const { return getBuffer( input, input.attributes[i] ); } const uf::Mesh::buffer_t& uf::Mesh::getBuffer( const uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute ) const { - return buffers[isInterleaved(input.interleaved) ? input.interleaved : attribute.buffer]; -} - -#define PRINT_HEADER(input) "Count: " << input.count << " | First: " << input.first << " | Size: " << input.size << " | Offset: " << input.offset << " | " << (isInterleaved(input.interleaved) ? "interleaved" : "deinterleaved") << "\n" - -void uf::Mesh::print( bool full ) const { - std::cout << "Buffers: " << buffers.size() << "\n" << printVertices(full) << printIndices(full) << printInstances(full) << printIndirects() << std::endl; -} - -std::string uf::Mesh::printVertices( bool full ) const { - std::stringstream str; - str << "Vertices: " << PRINT_HEADER( vertex ); - if ( full ) for ( auto i = 0; i < vertex.count; ++i ) { - for ( auto& attribute : vertex.attributes ) { - str << "[" << i << "][" << attribute.descriptor.name << "]: ( "; - uint8_t* e = (uint8_t*) attribute.pointer + i * attribute.stride; - switch ( attribute.descriptor.type ) { - case uf::renderer::enums::Type::UINT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((uint32_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::INT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((int32_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::USHORT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((uint16_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::SHORT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((int16_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::UBYTE: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((uint8_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::BYTE: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((int8_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::FLOAT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << ((float*) e)[j] << " "; break; - #if UF_USE_FLOAT16 - case uf::renderer::enums::Type::HALF: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << ((std::float16_t*) e)[j] << " "; break; - #endif - #if UF_USE_BFLOAT16 - case uf::renderer::enums::Type::BFLOAT16: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << ((std::bfloat16_t*) e)[j] << " "; break; - #endif - default: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << ((float*) e)[j] << " "; break; - } - str << ")\n"; - } - } - return str.str(); -} -std::string uf::Mesh::printIndices( bool full ) const { - std::stringstream str; - str << "Indices: " << PRINT_HEADER( index ); - if ( full ) for ( auto i = 0; i < index.count; ++i ) { - auto& buffer = getBuffer( index ); - switch ( index.size ) { - case 1: str << "[" << i << "]: " << *(( uint8_t*) &buffer[i * index.size]) << "\n"; break; - case 2: str << "[" << i << "]: " << *((uint16_t*) &buffer[i * index.size]) << "\n"; break; - case 4: str << "[" << i << "]: " << *((uint32_t*) &buffer[i * index.size]) << "\n"; break; - } - } - return str.str(); -} -std::string uf::Mesh::printInstances( bool full ) const { - std::stringstream str; - str << "Instances: " << PRINT_HEADER( instance ); - if ( full ) for ( auto i = 0; i < instance.count; ++i ) { - for ( auto& attribute : vertex.attributes ) { - str << "[" << i << "][" << attribute.descriptor.name << "]: ( "; - uint8_t* e = (uint8_t*) attribute.pointer + i * attribute.stride; - switch ( attribute.descriptor.type ) { - case uf::renderer::enums::Type::UINT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((uint32_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::INT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((int32_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::USHORT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((uint16_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::SHORT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((int16_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::UBYTE: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((uint8_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::BYTE: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << (int) ((int8_t*) e)[j] << " "; break; - case uf::renderer::enums::Type::FLOAT: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << ((float*) e)[j] << " "; break; - #if UF_USE_FLOAT16 - case uf::renderer::enums::Type::HALF: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << ((std::float16_t*) e)[j] << " "; break; - #endif - #if UF_USE_BFLOAT16 - case uf::renderer::enums::Type::BFLOAT16: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << ((std::bfloat16_t*) e)[j] << " "; break; - #endif - default: for ( auto j = 0; j < attribute.descriptor.components; ++j ) str << ((float*) e)[j] << " "; break; - } - str << ")\n"; - } - } - return str.str(); -} -std::string uf::Mesh::printIndirects( bool full ) const { - std::stringstream str; - str << "Indirect: " << PRINT_HEADER( indirect ) << "{ indices, instances, indexID, vertexID, instanceID, auxID, materialID, vertices }\n"; - if ( full ) for ( auto i = 0; i < indirect.count; ++i ) { - auto& buffer = getBuffer( indirect ); - auto& drawCommand = *(const pod::DrawCommand*) (&buffer[i * indirect.size]); - str << "[" << i << "]: {" << drawCommand.indices << ", " << drawCommand.instances << ", " << drawCommand.indexID << ", " << drawCommand.vertexID << ", " << drawCommand.instanceID << ", " << drawCommand.auxID << ", " << drawCommand.materialID << ", " << drawCommand.vertices << "}\n"; - } - return str.str(); + return buffers[attribute.buffer]; } uf::Mesh::View uf::Mesh::makeView( const uf::stl::vector& wanted, size_t lod ) const { uf::Mesh::View view; - view.vertex = vertex; - view.index = index; + view.vertex = vertex; + view.index = index; - if ( wanted.size() ) { - for ( auto& attr : vertex.attributes ) { - if ( std::find(wanted.begin(), wanted.end(), attr.descriptor.name ) == wanted.end() ) continue; - view.attributes[attr.descriptor.name] = { attr }; - } - } else { - for ( auto& attr : vertex.attributes ) view.attributes[attr.descriptor.name] = { attr }; - } + if ( wanted.size() ) { + for ( auto& attr : vertex.attributes ) { + if ( std::find(wanted.begin(), wanted.end(), attr.descriptor.name ) == wanted.end() ) continue; + view.attributes[uf::string::fnv1a(attr.descriptor.name)] = { attr }; + } + } else { + for ( auto& attr : vertex.attributes ) { + view.attributes[uf::string::fnv1a(attr.descriptor.name)] = { attr }; + } + } - if ( !index.attributes.empty() ) { - view.attributes["index"] = { index.attributes[lod] }; - } + if ( !index.attributes.empty() ) { + view.attributes["index"_hash] = { index.attributes[lod] }; + } - return view; + return view; } uf::Mesh::View uf::Mesh::makeView( size_t i, const uf::stl::vector& wanted, size_t lod ) const { uf::Mesh::View view; @@ -403,14 +311,14 @@ uf::Mesh::View uf::Mesh::makeView( size_t i, const uf::stl::vector(attribute.pointer) + attribute.descriptor.offset; - } - } - for ( auto& attribute : input.attributes ) { - attribute.stride = isInterleaved(input.interleaved) ? input.size : attribute.descriptor.size; + attribute.stride = attribute.descriptor.size; } } void uf::Mesh::_updateViews() { @@ -535,114 +431,35 @@ uf::Mesh::Attribute uf::Mesh::_remapAttribute( const uf::Mesh::Input& input, con void uf::Mesh::_insertVs( uf::Mesh::Input& dstInput, const uf::Mesh& mesh, const uf::Mesh::Input& srcInput ) { _reserveVs( dstInput, dstInput.count += srcInput.count ); - // both meshes are interleaved, just copy directly - if ( isInterleaved(dstInput.interleaved) && isInterleaved(srcInput.interleaved) ) { - if ( !_hasV( dstInput, srcInput ) ) return; - auto& src = mesh.buffers[srcInput.interleaved]; - auto& dst = buffers[dstInput.interleaved]; - dst.insert( dst.end(), src.begin(), src.end() ); - // both meshes are de-interleaved, just copy directly - } else if ( !isInterleaved(dstInput.interleaved) && !isInterleaved(srcInput.interleaved) ) { - if ( _hasV( dstInput, srcInput ) ) { - for ( auto i = 0; i < dstInput.attributes.size(); ++i ) { - auto& srcAttribute = srcInput.attributes[i]; - auto& dstAttribute = dstInput.attributes[i]; + if ( _hasV( dstInput, srcInput ) ) { + for ( auto i = 0; i < dstInput.attributes.size(); ++i ) { + auto& src = mesh.buffers[srcInput.attributes[i].buffer]; + auto& dst = buffers[dstInput.attributes[i].buffer]; + dst.insert( dst.end(), src.begin(), src.end() ); + } + } else { + for ( auto& dstAttribute : dstInput.attributes ) { + for ( auto& srcAttribute : srcInput.attributes ) { + if ( srcAttribute.descriptor != dstAttribute.descriptor ) continue; + auto& src = mesh.buffers[srcAttribute.buffer]; auto& dst = buffers[dstAttribute.buffer]; dst.insert( dst.end(), src.begin(), src.end() ); - } - } else { - for ( auto& dstAttribute : dstInput.attributes ) { - for ( auto& srcAttribute : srcInput.attributes ) { - if ( srcAttribute.descriptor != dstAttribute.descriptor ) continue; - - auto& src = mesh.buffers[srcAttribute.buffer]; - auto& dst = buffers[dstAttribute.buffer]; - dst.insert( dst.end(), src.begin(), src.end() ); - - break; - } + break; } } - // not easy to convert, will implement later - } else if ( isInterleaved(dstInput.interleaved) && !isInterleaved(srcInput.interleaved) ) { - // UF_EXCEPTION("to be implemented: deinterleaved -> interleaved"); - uf::Mesh::Input _srcInput = srcInput; - auto& dst = buffers.at(dstInput.interleaved); - size_t _ = 0; - while ( _++ < _srcInput.count ) { - for ( auto& srcAttribute : _srcInput.attributes ) { - dst.insert( dst.end(), (uint8_t*) srcAttribute.pointer, (uint8_t*) srcAttribute.pointer + srcAttribute.descriptor.size ); - srcAttribute.pointer = static_cast(srcAttribute.pointer) + srcAttribute.descriptor.size; - } - } - } else if ( !isInterleaved(dstInput.interleaved) && isInterleaved(srcInput.interleaved) ) { - // UF_EXCEPTION("to be implemented: interleaved -> deinterleaved"); - uf::Mesh::Input _srcInput = srcInput; - const uint8_t* src = (const uint8_t*) mesh.buffers.at(srcInput.interleaved).data(); - size_t _ = 0; - while ( _++ < _srcInput.count ) { - for ( size_t i = 0; i < dstInput.attributes.size(); ++i ) { - auto& srcAttribute = _srcInput.attributes.at(i); - auto& dstAttribute = dstInput.attributes.at(i); - - auto& dst = buffers.at(dstAttribute.buffer); - dst.insert( dst.end(), src, src + srcAttribute.descriptor.size ); - src += srcAttribute.descriptor.size; - } - } - } else { - UF_EXCEPTION("to be implemented: ??"); } + _updateDescriptor( dstInput ); } void uf::Mesh::_insertIs( uf::Mesh::Input& dstInput, const uf::Mesh& mesh, const uf::Mesh::Input& srcInput ) { -// if ( !_hasI( source ) ) return; _reserveIs( dstInput, dstInput.count += srcInput.count ); - // both meshes are interleaved, just copy directly - if ( isInterleaved(dstInput.interleaved) && isInterleaved(srcInput.interleaved) ) { - auto& src = mesh.getBuffer( srcInput ); - auto& dst = getBuffer( dstInput ); + for ( auto i = 0; i < dstInput.attributes.size(); ++i ) { + auto& src = mesh.getBuffer( srcInput, i ); + auto& dst = getBuffer( dstInput, i ); dst.insert( dst.end(), src.begin(), src.end() ); - // both meshes are de-interleaved, just copy directly - } else if ( !isInterleaved(dstInput.interleaved) && !isInterleaved(srcInput.interleaved) ) { - for ( auto i = 0; i < dstInput.attributes.size(); ++i ) { - auto& src = mesh.getBuffer( srcInput, i ); - auto& dst = getBuffer( dstInput, i ); - - dst.insert( dst.end(), src.begin(), src.end() ); - } - // not easy to convert, will implement later - } else if ( isInterleaved(dstInput.interleaved) && !isInterleaved(srcInput.interleaved) ) { - // UF_EXCEPTION("to be implemented: deinterleaved -> interleaved"); - uf::Mesh::Input _srcInput = srcInput; - auto& dst = getBuffer( dstInput ); - size_t _ = 0; - while ( _++ < _srcInput.count ) { - for ( auto& srcAttribute : _srcInput.attributes ) { - dst.insert( dst.end(), (uint8_t*) srcAttribute.pointer, (uint8_t*) srcAttribute.pointer + srcAttribute.descriptor.size ); - srcAttribute.pointer = static_cast(srcAttribute.pointer) + srcAttribute.descriptor.size; - } - } - } else if ( !isInterleaved(dstInput.interleaved) && isInterleaved(srcInput.interleaved) ) { - // UF_EXCEPTION("to be implemented: interleaved -> deinterleaved"); - uf::Mesh::Input _srcInput = srcInput; - const uint8_t* src = (const uint8_t*) mesh.getBuffer( srcInput ).data(); - size_t _ = 0; - while ( _++ < _srcInput.count ) { - for ( size_t i = 0; i < dstInput.attributes.size(); ++i ) { - auto& srcAttribute = _srcInput.attributes[i]; - auto& dstAttribute = dstInput.attributes[i]; - - auto& dst = buffers.at(dstAttribute.buffer); - dst.insert( dst.end(), src, src + srcAttribute.descriptor.size ); - src += srcAttribute.descriptor.size; - } - } - } else { - UF_EXCEPTION("to be implemented: ??"); } _updateDescriptor( dstInput ); } @@ -666,26 +483,14 @@ void uf::Mesh::_bindV( uf::Mesh::Input& input, const uf::stl::vector(data); + for ( auto& attribute : input.attributes ) { + uint8_t* dstBase = buffers[attribute.buffer].data() + (count * attribute.descriptor.size); + + size_t srcOffset = attribute.descriptor.offset; + size_t attrSize = attribute.descriptor.size; + + for ( size_t i = 0; i < size; ++i ) { + const uint8_t* srcAddr = pointer + (i * input.size) + srcOffset; + uint8_t* dstAddr = dstBase + (i * attrSize); + + memcpy( dstAddr, srcAddr, attrSize ); + } + } } // Indices void uf::Mesh::_bindI( uf::Mesh::Input& input, size_t size, ext::RENDERER::enums::Type::type_t type, size_t count ) { @@ -743,34 +557,28 @@ void uf::Mesh::_insertI( uf::Mesh::Input& input, const void* data, size_t i ) { auto& attribute = input.attributes[i]; _reserveIs( input, ++input.count ); const uint8_t* pointer = (const uint8_t*) data; -#if 1 + buffers[attribute.buffer].insert( buffers[attribute.buffer].end(), pointer, pointer + attribute.descriptor.size ); -#else - if ( isInterleaved(input.interleaved) ) { - buffers[input.interleaved].insert( buffers[input.interleaved].end(), pointer, pointer + attribute.descriptor.size ); - } else { - buffers[attribute.buffer].insert( buffers[attribute.buffer].end(), pointer, pointer + attribute.descriptor.size ); - } -#endif } void uf::Mesh::_insertIs( uf::Mesh::Input& input, const void* data, size_t size, size_t i ) { auto& attribute = input.attributes[i]; _reserveIs( input, input.count += size ); const uint8_t* pointer = (const uint8_t*) data; -#if 1 for ( const uint8_t* p = pointer; p < pointer + size * attribute.descriptor.size; p += attribute.descriptor.size ) buffers[attribute.buffer].insert( buffers[attribute.buffer].end(), p + attribute.descriptor.offset, p + attribute.descriptor.offset + attribute.descriptor.size ); -#else - if ( isInterleaved(input.interleaved) ) { - buffers[input.interleaved].insert( buffers[input.interleaved].end(), pointer, pointer + size * attribute.descriptor.size ); - } else for ( const uint8_t* p = pointer; p < pointer + size * attribute.descriptor.size; p += attribute.descriptor.size ) { - buffers[attribute.buffer].insert( buffers[attribute.buffer].end(), p + attribute.descriptor.offset, p + attribute.descriptor.offset + attribute.descriptor.size ); - } -#endif } //// - +void uf::mesh::setIndex( void* pointer, size_t stride, size_t index, size_t value ) { + switch ( stride ) { + case 1: ((uint8_t*) pointer)[index] = static_cast(value); break; + case 2: ((uint16_t*) pointer)[index] = static_cast(value); break; + case 4: ((uint32_t*) pointer)[index] = static_cast(value); break; + default: { + UF_EXCEPTION("invalid stride type: {}", stride); + } break; + } +} size_t uf::mesh::fetchIndex( const void* pointer, size_t stride, size_t index ) { #define CAST_INDEX(T) case sizeof(T): return ((T*) pointer)[index]; switch ( stride ) {