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)
This commit is contained in:
parent
46c08b0516
commit
06e88cbf17
@ -3,7 +3,6 @@
|
||||
"scenes": {
|
||||
"start": "StartMenu",
|
||||
"matrix": { "reverseInfinite": true },
|
||||
"meshes": { "interleaved": false },
|
||||
"lights": { "enabled": true,
|
||||
"lightmaps": true,
|
||||
"max": 32,
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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<MyContent> 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>(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<MyContent> canvasArray =
|
||||
BinPack2D::UniformCanvasArrayBuilder<MyContent>(128,128,2).Build();
|
||||
|
||||
// A place to store content that didnt fit into the canvas array.
|
||||
BinPack2D::ContentAccumulator<MyContent> remainder;
|
||||
|
||||
// try to pack content into the bins.
|
||||
bool success = canvasArray.Place( inputContent, remainder );
|
||||
|
||||
// A place to store packed content.
|
||||
BinPack2D::ContentAccumulator<MyContent> outputContent;
|
||||
|
||||
// Read all placed content.
|
||||
canvasArray.CollectContent( outputContent );
|
||||
|
||||
// parse output.
|
||||
typedef BinPack2D::Content<MyContent>::Vector::iterator binpack2d_iterator;
|
||||
printf("PLACED:\n");
|
||||
for( binpack2d_iterator itor = outputContent.Get().begin(); itor != outputContent.Get().end(); itor++ ) {
|
||||
|
||||
const BinPack2D::Content<MyContent> &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<MyContent> &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<vector>
|
||||
#include<map>
|
||||
#include<list>
|
||||
#include<algorithm>
|
||||
#include<math.h>
|
||||
#include<sstream>
|
||||
|
||||
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<Coord> Vector;
|
||||
typedef std::list<Coord> 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<typename _T> class Content {
|
||||
|
||||
public:
|
||||
|
||||
typedef std::vector<Content<_T> > 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<typename _T> class Canvas {
|
||||
|
||||
Coord::List topLefts;
|
||||
typename Content<_T>::Vector contentVector;
|
||||
|
||||
bool needToSort;
|
||||
|
||||
public:
|
||||
|
||||
typedef Canvas<_T> CanvasT;
|
||||
typedef typename std::vector<CanvasT> 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 <typename _T> 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 <typename _T> 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<typename _T> 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 ***/
|
||||
623
dep/include/stb/stb_rect_pack.h
Normal file
623
dep/include/stb/stb_rect_pack.h
Normal file
@ -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 <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#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.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
@ -8,7 +8,6 @@ namespace uf {
|
||||
pod::Vector2f st{};
|
||||
pod::Vector3f normal{};
|
||||
pod::Vector3f tangent{};
|
||||
pod::Vector2us id{};
|
||||
|
||||
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> 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<uf::renderer::AttributeDescriptor> 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<uf::renderer::AttributeDescriptor> 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{};
|
||||
|
||||
|
||||
@ -1,12 +1,4 @@
|
||||
namespace pod {
|
||||
template<typename T, typename U = uint32_t>
|
||||
struct UF_API Meshlet_T {
|
||||
uf::stl::vector<T> vertices;
|
||||
uf::stl::vector<U> indices;
|
||||
|
||||
pod::Primitive primitive;
|
||||
};
|
||||
|
||||
struct UF_API Material {
|
||||
pod::Vector4f colorBase = { 0, 0, 0, 0 };
|
||||
pod::Vector4f colorEmissive = { 0, 0, 0, 0 };
|
||||
|
||||
@ -4,12 +4,10 @@
|
||||
#include <uf/utils/mesh/mesh.h>
|
||||
#include <uf/engine/graph/graph.h>
|
||||
|
||||
#define UF_GRAPH_MESH_FORMAT uf::graph::mesh::Base, uint32_t
|
||||
|
||||
namespace impl {
|
||||
const float sourceToMeters = 0.07f;
|
||||
|
||||
typedef uf::Meshlet_T<UF_GRAPH_MESH_FORMAT> Meshlet;
|
||||
typedef uf::Meshlet_T<uf::graph::mesh::Skinned, uint32_t> Meshlet;
|
||||
|
||||
template<typename T>
|
||||
T str2vec( uf::stl::string string ) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "./allocator.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
// 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<CharT, Traits, Allocator>;
|
||||
using string = uf::stl::basic_string<char>;
|
||||
|
||||
template<
|
||||
class CharT,
|
||||
class Traits = std::char_traits<CharT>
|
||||
>
|
||||
using basic_string_view = std::basic_string_view<CharT, Traits>;
|
||||
using string_view = uf::stl::basic_string_view<char>;
|
||||
|
||||
template<
|
||||
class CharT,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
|
||||
@ -23,9 +23,16 @@ namespace uf {
|
||||
|
||||
template<typename Key, typename T>
|
||||
uf::stl::vector<Key> keys( const uf::stl::unordered_map<Key, T>& map ) {
|
||||
uf::stl::vector<Key> keys;
|
||||
uf::stl::vector<Key> keys; keys.reserve( map.size() );
|
||||
for ( auto pair : map ) keys.emplace_back( pair.first );
|
||||
return keys;
|
||||
}
|
||||
|
||||
template<typename Key, typename T>
|
||||
uf::stl::vector<T> values( const uf::stl::unordered_map<Key, T>& map ) {
|
||||
uf::stl::vector<T> values; values.reserve( map.size() );
|
||||
for ( auto pair : map ) values.emplace_back( pair.second );
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,7 +65,6 @@ namespace uf {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename U = uint32_t>
|
||||
uf::stl::vector<uf::Meshlet_T<T,U>> 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<T> vertices = meshlet.vertices;
|
||||
uf::stl::vector<U> indices = meshlet.indices;
|
||||
|
||||
uf::shapes::clip<T,U>( 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<T,U>( grid, meshlet.vertices, meshlet.indices, meshlet.primitive );
|
||||
}
|
||||
if ( cleanup ) {
|
||||
uf::meshgrid::cleanup( grid );
|
||||
}
|
||||
#endif
|
||||
|
||||
for ( auto& meshlet : meshlets ) uf::meshgrid::partition<T,U>( 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<T,U>( 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;
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <uf/utils/math/matrix.h>
|
||||
#include <uf/utils/math/quant.h>
|
||||
#include <uf/utils/math/shapes.h>
|
||||
#include <uf/utils/string/hash.h>
|
||||
|
||||
#include <functional>
|
||||
#include <uf/utils/memory/unordered_map.h>
|
||||
@ -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<uint8_t> 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<uf::stl::string, uf::Mesh::AttributeView> attributes;
|
||||
uf::stl::unordered_map<uint32_t, uf::Mesh::AttributeView> attributes;
|
||||
|
||||
bool has( const uf::stl::string& name ) const {
|
||||
return attributes.count( name ) > 0;
|
||||
}
|
||||
const AttributeView& operator[]( const uf::stl::string& name ) const {
|
||||
if ( auto it = attributes.find(name); it != attributes.end() ) return it->second;
|
||||
UF_EXCEPTION("invalid view: {}", name);
|
||||
}
|
||||
|
||||
// to-do: resolve dependency order hell
|
||||
// these probably won't be directly called anyways?
|
||||
#if 0
|
||||
size_t fetchIndex( size_t index ) {
|
||||
return uf::mesh::fetchIndex( index );
|
||||
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<uf::Mesh::View> views_t;
|
||||
|
||||
@ -251,7 +220,7 @@ namespace uf {
|
||||
uf::stl::vector<uf::Mesh::View> 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<typename U> inline void _insertI( uf::Mesh::Input& input, U index, size_t i = 0 ) { return _insertI( input, (const void*) &index, i ); }
|
||||
template<typename U> inline void _insertIs( uf::Mesh::Input& input, const uf::stl::vector<U>& is, size_t i = 0 ) { return _insertIs( input, (const void*) is.data(), is.size(), i ); }
|
||||
public:
|
||||
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<typename T> inline void compile( const uf::stl::vector<T>& meshlets, uf::stl::vector<pod::Primitive>& primitives );
|
||||
template<typename K, typename V> inline void compile( const uf::stl::unordered_map<K, V>& meshlets, uf::stl::vector<pod::Primitive>& primitives );
|
||||
template<typename T> inline uf::stl::vector<pod::Primitive> compile( const uf::stl::vector<T>& meshlets );
|
||||
template<typename K, typename V> inline uf::stl::vector<pod::Primitive> compile( const uf::stl::unordered_map<K, V>& 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<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
|
||||
uf::Mesh::View makeView( size_t commandIndex, const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
|
||||
uf::stl::vector<uf::Mesh::View> makeViews( const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
|
||||
@ -388,18 +352,14 @@ namespace uf {
|
||||
template<typename U> inline void insertIndirects( const uf::stl::vector<U>& indirects, size_t i = 0 ) { return _insertIs( indirect, (const void*) indirects.data(), indirects.size(), i ); }
|
||||
|
||||
template<typename T, typename U = uf::renderer::index_t>
|
||||
void bind( bool interleave = uf::Mesh::defaultInterleaved, size_t indices = 1 ) {
|
||||
void bind( size_t indices = 1 ) {
|
||||
bindVertex<T>();
|
||||
bindIndex<U>( indices );
|
||||
_bind( interleave );
|
||||
_bind();
|
||||
}
|
||||
|
||||
template<typename From, typename To>
|
||||
void convert() {
|
||||
if ( this->isInterleaved() ) {
|
||||
UF_MSG_DEBUG("Downcasting/upcasting requested yet mesh is interleaved, ignoring...");
|
||||
return;
|
||||
}
|
||||
auto fromEnum = uf::renderer::typeToEnum<From>();
|
||||
auto toEnum = uf::renderer::typeToEnum<To>();
|
||||
if ( toEnum == fromEnum ) return;
|
||||
@ -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<typename T = pod::Vertex_3F, typename U = uf::renderer::index_t>
|
||||
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<typename T>
|
||||
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<typename T>
|
||||
T& getVertexAttribute( const uf::Mesh::View& view, const uf::Mesh::AttributeView& attributeView, size_t index ) {
|
||||
UF_ASSERT( uf::renderer::typeToEnum<typename T::type_t>() == attributeView.type() && T::size == attributeView.components() );
|
||||
return *(T*) attributeView.data( view.vertex.first + index );
|
||||
}
|
||||
//
|
||||
template<typename U> inline U& getIndex( void* pointer, size_t index ) {
|
||||
return ((U*) pointer)[index];
|
||||
}
|
||||
template<typename U> inline U& getIndex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, size_t index ) {
|
||||
return uf::mesh::getIndex<U>( indices.data(view.index.first), index );
|
||||
}
|
||||
template<typename U> inline U& getIndex( const uf::Mesh::View& view, size_t index ) {
|
||||
return uf::mesh::getIndex<U>( view, view["indices"_hash], index );
|
||||
}
|
||||
template<typename U> inline U& getIndex( const uf::Mesh::View& view, const uf::stl::string& indices, size_t index ) {
|
||||
return uf::mesh::getIndex<U>( view, view[indices], index );
|
||||
}
|
||||
|
||||
void UF_API setIndex( void* pointer, size_t stride, size_t index, size_t value );
|
||||
|
||||
//
|
||||
template<typename T, typename U> void compile( uf::Mesh& mesh, const uf::stl::vector<uf::Meshlet_T<T, U>>& meshlets, uf::stl::vector<pod::Primitive>& primitives );
|
||||
|
||||
template<typename K, typename V> inline uf::stl::vector<pod::Primitive> compile( uf::Mesh& mesh, const uf::stl::unordered_map<K, V>& meshlets );
|
||||
template<typename T> inline uf::stl::vector<pod::Primitive> compile( uf::Mesh& mesh, const uf::stl::vector<T>& meshlets );
|
||||
template<typename K, typename V> inline uf::stl::vector<pod::Primitive> compile( uf::Mesh& mesh, const uf::stl::unordered_map<K, V>& 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<void*>(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<typename T> uf::stl::vector<pod::Primitive> uf::Mesh::compile( const uf::stl::vector<T>& meshlets ) {
|
||||
uf::stl::vector<pod::Primitive> primitives;
|
||||
uf::mesh::compile( *this, meshlets, primitives );
|
||||
return primitives;
|
||||
}
|
||||
template<typename K, typename V> uf::stl::vector<pod::Primitive> uf::Mesh::compile( const uf::stl::unordered_map<K, V>& meshlets ) {
|
||||
uf::stl::vector<pod::Primitive> primitives;
|
||||
uf::mesh::compile( *this, uf::stl::values( meshlets ), primitives );
|
||||
return primitives;
|
||||
}
|
||||
|
||||
template<typename T> void uf::Mesh::compile( const uf::stl::vector<T>& meshlets, uf::stl::vector<pod::Primitive>& primitives ) {
|
||||
return uf::mesh::compile( *this, meshlets, primitives );
|
||||
}
|
||||
template<typename K, typename V> void uf::Mesh::compile( const uf::stl::unordered_map<K, V>& meshlets, uf::stl::vector<pod::Primitive>& primitives ) {
|
||||
return uf::mesh::compile( *this, uf::stl::values( meshlets ), primitives );
|
||||
}
|
||||
//
|
||||
template<typename T> uf::stl::vector<pod::Primitive> uf::mesh::compile( uf::Mesh& mesh, const uf::stl::vector<T>& meshlets ) {
|
||||
uf::stl::vector<pod::Primitive> primitives;
|
||||
uf::mesh::compile( mesh, meshlets, primitives );
|
||||
return primitives;
|
||||
}
|
||||
template<typename K, typename V> uf::stl::vector<pod::Primitive> uf::mesh::compile( uf::Mesh& mesh, const uf::stl::unordered_map<K, V>& meshlets ) {
|
||||
uf::stl::vector<pod::Primitive> primitives;
|
||||
uf::mesh::compile( mesh, uf::stl::values( meshlets ), primitives );
|
||||
return primitives;
|
||||
}
|
||||
template<typename K, typename V> void uf::mesh::compile( uf::Mesh& mesh, const uf::stl::unordered_map<K, V>& meshlets, uf::stl::vector<pod::Primitive>& primitives ) {
|
||||
return uf::mesh::compile( mesh, uf::stl::values( meshlets ), primitives );
|
||||
}
|
||||
//
|
||||
template<typename T, typename U> void uf::mesh::compile( uf::Mesh& mesh, const uf::stl::vector<uf::Meshlet_T<T, U>>& meshlets, uf::stl::vector<pod::Primitive>& primitives ) {
|
||||
mesh.bindIndirect<pod::DrawCommand>();
|
||||
mesh.bind<T, U>();
|
||||
|
||||
size_t indexID = 0;
|
||||
size_t vertexID = 0;
|
||||
size_t instanceID = 0;
|
||||
|
||||
uf::stl::vector<pod::DrawCommand> 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();
|
||||
}
|
||||
@ -10,11 +10,20 @@
|
||||
namespace uf {
|
||||
namespace string {
|
||||
template<typename T>
|
||||
uf::stl::string sha256( const T& input ) {
|
||||
// uf::stl::vector<unsigned char> 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<uint32_t>(*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<uint32_t>(c)) * 16777619u;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace literals {
|
||||
constexpr uint32_t operator""_hash(const char* str, size_t) { return uf::string::fnv1a(str); }
|
||||
}
|
||||
}
|
||||
|
||||
using namespace uf::literals;
|
||||
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@ -567,8 +567,8 @@ void ext::GuiBehavior::tick( uf::Object& self ) {
|
||||
pod::Vector2f max = { -std::numeric_limits<float>::max(), -std::numeric_limits<float>::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{};
|
||||
|
||||
@ -310,9 +310,9 @@ uf::stl::vector<pod::OBB> 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 );
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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<bool>() )
|
||||
{
|
||||
uf::stl::vector<uf::stl::string> attributesKept = ext::json::vector<uf::stl::string>(graph.metadata["decode"]["attributes"]);
|
||||
if ( !mesh.isInterleaved() ) {
|
||||
uf::stl::vector<size_t> remove; remove.reserve(mesh.vertex.attributes.size());
|
||||
uf::stl::vector<size_t> 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<uf::stl::string>({ "position", "uv", "st" });
|
||||
#else
|
||||
graph.metadata["decode"]["attributes"] = uf::stl::vector<uf::stl::string>({ "position", "color", "uv", "st", "tangent", "joints", "weights", "normal", "id" });
|
||||
graph.metadata["decode"]["attributes"] = uf::stl::vector<uf::stl::string>({ "position", "color", "uv", "st", "normal", "tangent", "joints", "weights" });
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -179,47 +179,12 @@ namespace {
|
||||
}
|
||||
|
||||
uf::Serializer json;
|
||||
#if 0
|
||||
uf::Mesh mesh = mesh;
|
||||
// remove extraneous buffers
|
||||
if ( !mesh.isInterleaved() ) {
|
||||
uf::stl::vector<size_t> 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<bool>() ) {
|
||||
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();\
|
||||
|
||||
@ -288,108 +288,71 @@ namespace {
|
||||
uint32_t jointID;
|
||||
};
|
||||
|
||||
if ( mesh.isInterleaved( mesh.vertex ) ) {
|
||||
uf::stl::string compShaderFilename = graphMetadataJson["shaders"]["skinning"]["compute"].as<uf::stl::string>("/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<uf::stl::string>("/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<uf::stl::string>("/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<size_t, uf::stl::vector<pod::Range>> 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;
|
||||
|
||||
|
||||
@ -21,6 +21,8 @@
|
||||
#include <uf/ext/gltf/gltf.h>
|
||||
|
||||
namespace {
|
||||
typedef uf::Meshlet_T<uf::graph::mesh::Skinned, uint32_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<uf::stl::string>("");
|
||||
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<bool>(false) || graph.metadata["exporter"]["optimize"].as<uf::stl::string>("") == "tagged" ) {
|
||||
if ( graph.metadata["exporter"]["optimize"].as<uf::stl::string>("") == "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>() ) {
|
||||
size_t d = meshgrid.metadata["size"].as<size_t>();
|
||||
@ -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<bool>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ if ( graph.metadata["sanitizer"]["tangents"].as<bool>(false) ) {
|
||||
sanitizer.tangents.should = true;
|
||||
}
|
||||
|
||||
uf::stl::vector<uf::Meshlet_T<UF_GRAPH_MESH_FORMAT>> 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<bool>(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<pod::DrawCommand>();
|
||||
mesh.bind<UF_GRAPH_MESH_FORMAT>(false); // default to de-interleaved regardless of requirement (makes things easier)
|
||||
|
||||
uf::stl::vector<pod::DrawCommand> 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();
|
||||
}
|
||||
mesh.compile( meshlets, primitives );
|
||||
@ -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<uint32_t>();
|
||||
mesh.bind(mesh, mesh.isInterleaved());
|
||||
mesh.bind(mesh);
|
||||
}
|
||||
|
||||
// write indices to buffer
|
||||
@ -183,11 +179,6 @@ uf::stl::vector<float> ext::meshopt::computeLODs( size_t count, size_t maxLODs,
|
||||
|
||||
uf::stl::vector<pod::LODMetadata> ext::meshopt::generateLODs( uf::Mesh& mesh, const uf::stl::vector<float>& lodFactors, bool verbose ) {
|
||||
uf::stl::vector<pod::LODMetadata> 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<pod::LODMetadata> 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<pod::LODMetadata> 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;
|
||||
|
||||
|
||||
@ -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> 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() ) {\
|
||||
|
||||
@ -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<uf::graph::id_t,2>& id = *((pod::Vector<uf::graph::id_t,2>*) (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];
|
||||
|
||||
@ -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<pod::DrawCommand>();
|
||||
mesh.bind<UF_GRAPH_MESH_FORMAT>(false);
|
||||
|
||||
size_t indexID = 0, vertexID = 0, masterInstanceID = 0;
|
||||
uf::stl::vector<pod::DrawCommand> 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
|
||||
|
||||
@ -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<uint16_t, uint32_t> 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<int8_t>(0, srcVert.m_BoneWeights.bone[0]) : 0;
|
||||
vert.joints.y = srcVert.m_BoneWeights.numbones > 1 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[1]) : 0;
|
||||
vert.joints.z = srcVert.m_BoneWeights.numbones > 2 ? std::max<int8_t>(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;
|
||||
}
|
||||
@ -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> 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<VkDeviceSize> 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 {
|
||||
|
||||
@ -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<bool>() && graph.metadata["exporter"]["unwrap"].as<bool>() ) {
|
||||
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<uint8_t*>(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<pod::Vector2f>( 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<uint8_t*>(dstAttribute.pointer) + dstAttribute.stride * (dstVertexFirst + j);
|
||||
const uint8_t* srcPtr = static_cast<const uint8_t*>(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<uint8_t*>(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();
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
#include <uf/utils/image/atlas.h>
|
||||
#include <binpack2d/binpack2d.hpp>
|
||||
#include <iostream>
|
||||
#include <bit>
|
||||
|
||||
#define STB_RECT_PACK_IMPLEMENTATION
|
||||
#include <stb/stb_rect_pack.h>
|
||||
|
||||
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<pod::Atlas::hash_t> internalAtlas;
|
||||
BinPack2D::ContentAccumulator<pod::Atlas::hash_t> queue, stored, remainder;
|
||||
pod::Vector2ui size = {};
|
||||
pod::Vector3ui largest = {};
|
||||
size_t index = 0;
|
||||
uf::stl::vector<stbrp_rect> rects;
|
||||
uf::stl::vector<pod::Atlas::hash_t> 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<pod::Atlas::hash_t>(hash, BinPack2D::Coord(), BinPack2D::Size(dim.x, dim.y), false );
|
||||
size += dim;
|
||||
|
||||
stbrp_rect rect;
|
||||
rect.id = static_cast<int>(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<stbrp_node> 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<pod::Atlas::hash_t>(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<pod::Image>& images, float padding ) {
|
||||
|
||||
@ -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 ) {
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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<uint8_t> 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<uint32_t> 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<uint8_t*>(srcInput.pointer) + index * srcInput.stride, srcInput.descriptor.size );
|
||||
dstInput.pointer = static_cast<uint8_t*>(dstInput.pointer) + dstInput.stride;
|
||||
uint8_t* srcAddr = static_cast<uint8_t*>(srcInput.pointer) + index * srcInput.stride;
|
||||
uint8_t* dstAddr = static_cast<uint8_t*>(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<uf::stl::string>& 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<uf::stl::string>& 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<uf::stl::stri
|
||||
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 };
|
||||
view.attributes[uf::string::fnv1a(attr.descriptor.name)] = { attr };
|
||||
}
|
||||
} else {
|
||||
for ( auto& attr : vertex.attributes ) view.attributes[attr.descriptor.name] = { attr };
|
||||
for ( auto& attr : vertex.attributes ) view.attributes[uf::string::fnv1a(attr.descriptor.name)] = { attr };
|
||||
}
|
||||
|
||||
if ( !index.attributes.empty() ) {
|
||||
view.attributes["index"] = { index.attributes[lod] };
|
||||
view.attributes["index"_hash] = { index.attributes[lod] };
|
||||
}
|
||||
|
||||
return view;
|
||||
@ -469,47 +377,35 @@ void uf::Mesh::_destroy( uf::Mesh::Input& input ) {
|
||||
}
|
||||
input.attributes.clear();
|
||||
}
|
||||
void uf::Mesh::_bind( bool interleave ) {
|
||||
void uf::Mesh::_bind() {
|
||||
int32_t buffer = 0;
|
||||
#define PARSE_INPUT(INPUT, INTERLEAVED){\
|
||||
INPUT.interleaved = (INTERLEAVED ? buffer : -1);\
|
||||
for ( auto i = 0; i < INPUT.attributes.size(); ++i ) {\
|
||||
INPUT.attributes[i].buffer = !INTERLEAVED ? buffer++ : buffer;\
|
||||
INPUT.attributes[i].pointer = NULL;\
|
||||
}\
|
||||
if ( !INPUT.attributes.empty() && INTERLEAVED ) ++buffer;\
|
||||
}
|
||||
|
||||
PARSE_INPUT(vertex, interleave)
|
||||
PARSE_INPUT(index, false)
|
||||
PARSE_INPUT(instance, interleave)
|
||||
PARSE_INPUT(indirect, false)
|
||||
|
||||
auto parse_input = [&](uf::Mesh::Input& input) {
|
||||
for ( auto& attribute : input.attributes ) {
|
||||
attribute.buffer = buffer++;
|
||||
attribute.pointer = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
parse_input(vertex);
|
||||
parse_input(index);
|
||||
parse_input(instance);
|
||||
parse_input(indirect);
|
||||
|
||||
buffers.resize( buffer );
|
||||
updateDescriptor();
|
||||
|
||||
#undef PARSE_INPUT
|
||||
}
|
||||
void uf::Mesh::_updateDescriptor( uf::Mesh::Input& input ) {
|
||||
input.size = 0;
|
||||
for ( auto& attribute : input.attributes ) {
|
||||
const bool interleaved = isInterleaved(input.interleaved);
|
||||
auto& buffer = buffers[interleaved ? input.interleaved : attribute.buffer];
|
||||
auto& buffer = buffers[attribute.buffer];
|
||||
attribute.length = buffer.size();
|
||||
attribute.pointer = buffer.data() + attribute.offset;
|
||||
|
||||
if ( &input == &index || &input == &indirect ) {
|
||||
input.size = attribute.descriptor.size;
|
||||
} else {
|
||||
input.size += attribute.descriptor.size;
|
||||
}
|
||||
if ( &input == &index || &input == &indirect ) input.size = attribute.descriptor.size;
|
||||
else input.size += attribute.descriptor.size;
|
||||
|
||||
if ( interleaved ) {
|
||||
attribute.pointer = static_cast<uint8_t*>(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<uint8_t*>(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<uint8_t*>(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<uf::rendere
|
||||
}
|
||||
}
|
||||
void uf::Mesh::_reserveVs( uf::Mesh::Input& input, size_t count ) {
|
||||
if ( isInterleaved(input.interleaved) ) {
|
||||
buffers[input.interleaved].reserve( count * input.size );
|
||||
for ( auto& attribute : input.attributes ) {
|
||||
attribute.length = buffers[input.interleaved].size();
|
||||
attribute.pointer = (uint8_t*) (buffers[input.interleaved].data());
|
||||
}
|
||||
} else for ( auto& attribute : input.attributes ) {
|
||||
for ( auto& attribute : input.attributes ) {
|
||||
buffers[attribute.buffer].reserve( count * attribute.descriptor.size );
|
||||
attribute.length = buffers[attribute.buffer].size();
|
||||
attribute.pointer = (uint8_t*) (buffers[attribute.buffer].data());
|
||||
}
|
||||
}
|
||||
void uf::Mesh::_resizeVs( uf::Mesh::Input& input, size_t count ) {
|
||||
if ( isInterleaved(input.interleaved) ) {
|
||||
buffers[input.interleaved].resize( count * input.size );
|
||||
for ( auto& attribute : input.attributes ) {
|
||||
attribute.length = buffers[input.interleaved].size();
|
||||
attribute.pointer = (uint8_t*) (buffers[input.interleaved].data());
|
||||
}
|
||||
} else for ( auto& attribute : input.attributes ) {
|
||||
for ( auto& attribute : input.attributes ) {
|
||||
buffers[attribute.buffer].resize( count * attribute.descriptor.size );
|
||||
attribute.length = buffers[attribute.buffer].size();
|
||||
attribute.pointer = (uint8_t*) (buffers[attribute.buffer].data());
|
||||
@ -694,26 +499,35 @@ void uf::Mesh::_resizeVs( uf::Mesh::Input& input, size_t count ) {
|
||||
void uf::Mesh::_insertV( uf::Mesh::Input& input, const void* data ) {
|
||||
_reserveVs( input, ++input.count );
|
||||
const uint8_t* pointer = (const uint8_t*) data;
|
||||
if ( isInterleaved(input.interleaved) ) {
|
||||
buffers[input.interleaved].insert( buffers[input.interleaved].end(), pointer, pointer + input.size );
|
||||
} else for ( auto& attribute : input.attributes ) {
|
||||
buffers[attribute.buffer].insert( buffers[attribute.buffer].end(), pointer + attribute.descriptor.offset, pointer + attribute.descriptor.offset + attribute.descriptor.size );
|
||||
|
||||
for ( auto& attribute : input.attributes ) {
|
||||
buffers[attribute.buffer].insert(
|
||||
buffers[attribute.buffer].end(),
|
||||
pointer + attribute.descriptor.offset,
|
||||
pointer + attribute.descriptor.offset + attribute.descriptor.size
|
||||
);
|
||||
}
|
||||
}
|
||||
void uf::Mesh::_insertVs( uf::Mesh::Input& input, const void* data, size_t size ) {
|
||||
#if 0
|
||||
const uint8_t* pointer = (const uint8_t*) data;
|
||||
for ( auto i = 0; i < size; ++i ) insertV( pointer + i * input.size );
|
||||
#else
|
||||
_reserveVs( input, input.count += size );
|
||||
const uint8_t* pointer = (const uint8_t*) data;
|
||||
if ( isInterleaved(input.interleaved) ) {
|
||||
buffers[input.interleaved].insert( buffers[input.interleaved].end(), pointer, pointer + size * input.size );
|
||||
} else for ( const uint8_t* p = pointer; p < pointer + size * input.size; p += input.size ) {
|
||||
for ( auto& attribute : input.attributes )
|
||||
buffers[attribute.buffer].insert( buffers[attribute.buffer].end(), p + attribute.descriptor.offset, p + attribute.descriptor.offset + attribute.descriptor.size );
|
||||
}
|
||||
#endif
|
||||
size_t count = input.count;
|
||||
input.count += size;
|
||||
|
||||
_resizeVs( input, input.count );
|
||||
|
||||
const uint8_t* pointer = static_cast<const uint8_t*>(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<uint8_t>(value); break;
|
||||
case 2: ((uint16_t*) pointer)[index] = static_cast<uint16_t>(value); break;
|
||||
case 4: ((uint32_t*) pointer)[index] = static_cast<uint32_t>(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 ) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user