revamped meshopt-based mesh optimizations, fixed mesh slicer/gridder, added LOD system (i think it works), physics tweaks (to-do: make meshMesh viable)
This commit is contained in:
parent
fc36c77167
commit
84f2b63a8f
@ -66,9 +66,9 @@
|
||||
"vulkan": {
|
||||
"version": 1.3,
|
||||
"validation": {
|
||||
"enabled": true,
|
||||
"messages": true,
|
||||
"checkpoints": true,
|
||||
"enabled": false,
|
||||
"messages": false,
|
||||
"checkpoints": false,
|
||||
"filters": [
|
||||
// "0xe5d1743c" // VUID-vkCmdDispatch-None-02699 (problem when using VXGI) (seems to be fixed?)
|
||||
// ,"0x6714bd0c" // VUID-vkCmdDispatch-format-07753 (for some dumb shit) (seems to be fixed?)
|
||||
@ -118,7 +118,7 @@
|
||||
"vsync": true, // vsync on vulkan side rather than engine-side
|
||||
"hdr": true,
|
||||
"vxgi": false,
|
||||
"culling": false,
|
||||
"culling": true,
|
||||
"bloom": false,
|
||||
"dof": false,
|
||||
"rt": false,
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
"import": "/model.json",
|
||||
"assets": [
|
||||
// "/burger/burger.glb"
|
||||
// "/burger/burger_simpler.glb"
|
||||
// "/burger/burger/graph.json"
|
||||
// "/burger/burger_simpler.glb"
|
||||
"/burger/burger_simpler/graph.json"
|
||||
],
|
||||
"behaviors": [],
|
||||
@ -38,7 +38,7 @@
|
||||
"exporter": {
|
||||
"enabled": true,
|
||||
"unwrap": false,
|
||||
"optimize": false
|
||||
"optimize": "tagged"
|
||||
},
|
||||
"baking": {
|
||||
"enabled": false
|
||||
@ -48,6 +48,11 @@
|
||||
},
|
||||
"lighting": {
|
||||
"lightmap": false
|
||||
},
|
||||
"tags": {
|
||||
"/^.*/": {
|
||||
"optimize meshlets": { "simplify": 0.125, "print": true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"metadata": {
|
||||
"holdable": true,
|
||||
"physics": {
|
||||
"mass": 0,
|
||||
"mass": 100,
|
||||
"inertia": false,
|
||||
"type": "bounding box"
|
||||
// "type": "mesh"
|
||||
|
||||
@ -12,13 +12,13 @@
|
||||
"worldspawn": {
|
||||
"physics": { "type": "mesh", "static": true, "mass": 0 },
|
||||
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
|
||||
"optimize meshlets": { "simplify": 0.125, "print": false },
|
||||
"optimize mesh": { "simplify": 0, "lods": true, "print": true },
|
||||
"unwrap mesh": true
|
||||
},
|
||||
"worldspawn_skybox": {
|
||||
"physics": { "type": "mesh", "static": true, "mass": 0 },
|
||||
"physics": { "type": "aabb", "static": true, "mass": 0 },
|
||||
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
|
||||
"optimize meshlets": { "simplify": 0.125, "print": false },
|
||||
"optimize mesh": { "simplify": 0, "lods": true, "print": true },
|
||||
"unwrap mesh": true
|
||||
},
|
||||
"info_player_spawn": { "action": "attach", "filename": "./player.json", "transform": { "orientation": [ 0, 1, 0, 0 ] } },
|
||||
@ -42,8 +42,8 @@
|
||||
"/^prop_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } },
|
||||
"/^prop_static/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ },
|
||||
"/^prop_dynamic/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ },
|
||||
"/^func_physbox/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
"/^prop_physics/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
"/^func_physbox/": { "action": "load", "payload": { "import": "/prop.json" }, "optimize mesh": { "simplify": 0, "lods": true, "print": true } },
|
||||
"/^prop_physics/": { "action": "load", "payload": { "import": "/prop.json" }, "optimize mesh": { "simplify": 0, "lods": true, "print": true } },
|
||||
|
||||
"/^tools\\/toolsnodraw/": { "material": { "base": [ 1.0, 1.0, 1.0, 0.0 ] } }
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
"import": "./base_sourceengine.json",
|
||||
"assets": [
|
||||
// { "filename": "./models/mds_mcdonalds.glb" }
|
||||
{ "filename": "./models/mds_mcdonalds/graph.json" },
|
||||
{ "filename": "/burger.json", "delay": 1 }
|
||||
{ "filename": "./models/mds_mcdonalds/graph.json" }
|
||||
// ,{ "filename": "/burger.json", "delay": 1 }
|
||||
],
|
||||
"metadata": {
|
||||
"graph": {
|
||||
@ -15,10 +15,10 @@
|
||||
"func_door_rotating_5568": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
"func_door_rotating_5584": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
|
||||
"prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
|
||||
// "prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
|
||||
|
||||
// regex matches
|
||||
// "/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
"/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
|
||||
"/^tools\\/toolsnodraw/": { "material": {
|
||||
"base": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||
|
||||
@ -99,6 +99,15 @@ struct Bounds {
|
||||
float padding2;
|
||||
};
|
||||
|
||||
struct LOD {
|
||||
uint indices;
|
||||
uint indexID;
|
||||
};
|
||||
|
||||
struct LODMetadata {
|
||||
LOD levels[4];
|
||||
};
|
||||
|
||||
struct Instance {
|
||||
uint materialID;
|
||||
uint primitiveID;
|
||||
|
||||
@ -57,7 +57,7 @@ AF4 SpdLoadSourceImage(ASU2 p, AU1 slice) {
|
||||
}
|
||||
|
||||
AF4 SpdLoad(ASU2 p, AU1 slice) {
|
||||
uint loadMip = min(6u, MIPS - 1);
|
||||
uint loadMip = min(6u - 1, MIPS - 1);
|
||||
float d = imageLoad(outImage[loadMip], p).r;
|
||||
return AF4(d, d, d, d);
|
||||
}
|
||||
|
||||
@ -11,16 +11,18 @@ layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
#define QUERY_MIPMAPS 1
|
||||
#define DEPTH_BIAS 0.00005
|
||||
#define FRUSTUM_CULLING 1
|
||||
#define OCCLUSION_CULLING 0 // currently whack
|
||||
#define OCCLUSION_CULLING 1 // currently whack
|
||||
#define LODS 1
|
||||
#define MAX_LODS 4
|
||||
|
||||
#include "../../common/macros.h"
|
||||
#include "../../common/structs.h"
|
||||
|
||||
float mipLevels( vec2 size ) {
|
||||
return floor(log2(max(size.x, size.y)));
|
||||
return ceil(log2(max(size.x, size.y)));
|
||||
}
|
||||
float mipLevels( ivec2 size ) {
|
||||
return floor(log2(max(size.x, size.y)));
|
||||
return ceil(log2(max(size.x, size.y)));
|
||||
}
|
||||
|
||||
vec4 aabbToSphere( Bounds bounds ) {
|
||||
@ -61,19 +63,23 @@ layout (binding = 0) uniform Camera {
|
||||
Viewport viewport[PASSES];
|
||||
} camera;
|
||||
|
||||
layout (std140, binding = 1) buffer DrawCommands {
|
||||
layout (std430, binding = 1) buffer DrawCommands {
|
||||
DrawCommand drawCommands[];
|
||||
};
|
||||
|
||||
layout (std140, binding = 2) buffer Instances {
|
||||
layout (std430, binding = 2) buffer Instances {
|
||||
Instance instances[];
|
||||
};
|
||||
|
||||
layout (std140, binding = 3) buffer Objects {
|
||||
layout (std430, binding = 3) buffer LODs {
|
||||
LODMetadata lodMetadata[];
|
||||
};
|
||||
|
||||
layout (std430, binding = 4) buffer Objects {
|
||||
Object objects[];
|
||||
};
|
||||
|
||||
layout (binding = 4) uniform sampler2D samplerDepth;
|
||||
layout (binding = 5) uniform sampler2D samplerDepth;
|
||||
|
||||
shared vec4 sharedPlanes[PASSES][6];
|
||||
|
||||
@ -152,7 +158,7 @@ void main() {
|
||||
float width = (aabb.z - aabb.x) * pyramidSize.x;
|
||||
float height = (aabb.w - aabb.y) * pyramidSize.y;
|
||||
|
||||
float level = floor(log2(max(width, height)));
|
||||
float level = mipLevels(vec2(width, height));
|
||||
level = max(0.0, level);
|
||||
|
||||
float d1 = textureLod(samplerDepth, vec2(aabb.x, aabb.y), level).x;
|
||||
@ -174,6 +180,27 @@ void main() {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if LODS
|
||||
if ( isVisible ) {
|
||||
vec3 viewCenter = (camera.viewport[0].view * vec4(worldCenter, 1.0)).xyz;
|
||||
|
||||
float P11 = camera.viewport[0].projection[1][1];
|
||||
|
||||
float screenRadius = (worldRadius * P11) / max(abs(viewCenter.z), 0.001);
|
||||
|
||||
uint lodLevel = 0;
|
||||
if ( screenRadius < 0.5 ) lodLevel = 1;
|
||||
if ( screenRadius < 0.2 ) lodLevel = 2;
|
||||
if ( screenRadius < 0.05 ) lodLevel = 3;
|
||||
lodLevel = min(lodLevel, MAX_LODS - 1);
|
||||
|
||||
LOD lod = lodMetadata[drawCommand.instanceID].levels[lodLevel];
|
||||
|
||||
if ( lod.indices > 0 ) {
|
||||
drawCommands[gID].indices = lod.indices;
|
||||
drawCommands[gID].indexID = lod.indexID;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
drawCommands[gID].instances = isVisible ? 1 : 0;
|
||||
}
|
||||
@ -106,6 +106,7 @@ namespace pod {
|
||||
uf::renderer::Buffer drawCommands;
|
||||
uf::renderer::Buffer instance;
|
||||
uf::renderer::Buffer instanceAddresses;
|
||||
uf::renderer::Buffer lodMetadata;
|
||||
uf::renderer::Buffer joint;
|
||||
uf::renderer::Buffer object;
|
||||
uf::renderer::Buffer material;
|
||||
|
||||
@ -11,6 +11,9 @@ namespace ext {
|
||||
namespace meshopt {
|
||||
bool UF_API optimize( uf::Mesh&, float simplify = 1.0f, size_t = SIZE_MAX, bool verbose = false );
|
||||
|
||||
uf::stl::vector<float> computeLODs( size_t count, size_t maxLODs = 4, size_t minIndices = 32 );
|
||||
uf::stl::vector<pod::LODMetadata> UF_API generateLODs( uf::Mesh&, const uf::stl::vector<float>&, bool verbose = false );
|
||||
|
||||
template<typename T, typename U = uint32_t>
|
||||
bool optimize( uf::Meshlet_T<T,U>& meshlet, float simplify = 1.0f, size_t o = SIZE_MAX, bool verbose = false ) {
|
||||
#if UF_USE_MESHOPT
|
||||
@ -39,7 +42,7 @@ namespace ext {
|
||||
float targetError = 1e-2f / simplify;
|
||||
|
||||
float realError = 0.0f;
|
||||
size_t realIndices = meshopt_simplify(&indicesSimplified[0], &indices[0], indicesCount, &meshlet.vertices[0].position.x, verticesCount, sizeof(T), targetError, realError);
|
||||
size_t realIndices = meshopt_simplify(&indicesSimplified[0], &indices[0], indicesCount, &meshlet.vertices[0].position.x, verticesCount, sizeof(T), indicesCount * simplify, targetError);
|
||||
// size_t realIndices = meshopt_simplifySloppy(&indicesSimplified[0], &indices[0], indicesCount, &meshlet.vertices[0].position.x, verticesCount, sizeof(T), targetIndices);
|
||||
|
||||
if ( verbose ) UF_MSG_DEBUG("[Simplified] indices: {} -> {} | error: {} -> {}", indicesCount, realIndices, targetError, realError);
|
||||
|
||||
@ -6,9 +6,6 @@
|
||||
namespace ext {
|
||||
namespace xatlas {
|
||||
size_t UF_API unwrap( pod::Graph& );
|
||||
|
||||
size_t UF_API unwrapLazy( pod::Graph& );
|
||||
size_t UF_API unwrapExperimental( pod::Graph& );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -57,7 +57,8 @@ namespace pod {
|
||||
}
|
||||
};
|
||||
|
||||
typedef uf::stl::unordered_set<pair_t, PairHash, PairEq> pairs_t;
|
||||
// typedef uf::stl::unordered_set<pair_t, PairHash, PairEq> pairs_t;
|
||||
typedef uf::stl::vector<pair_t> pairs_t;
|
||||
|
||||
struct Node {
|
||||
BVH::index_t left = 0;
|
||||
@ -232,13 +233,13 @@ namespace pod {
|
||||
bool blockContactSolver = true; // use BlockNxN solvers (where N = number of contacts for a manifold)
|
||||
bool psgContactSolver = true; // use PSG contact solver
|
||||
bool useGjk = false; // currently don't have a way to broadphase mesh => narrowphase tri via GJK
|
||||
bool fixedStep = false; // run physics simulation with a fixed delta time (with accumulation), rather than rely on actual engine deltatime
|
||||
uint32_t substeps = 4; // number of substeps per frame tick
|
||||
bool fixedStep = true; // run physics simulation with a fixed delta time (with accumulation), rather than rely on actual engine deltatime
|
||||
uint32_t substeps = 1; // number of substeps per frame tick
|
||||
uint32_t reserveCount = 32; // amount of elements to reserve for vectors used in this system, to-do: have it tie to a memory pool allocator
|
||||
|
||||
// increasing these make things lag for reasons I can imagine why
|
||||
uint32_t broadphaseBvhCapacity = 2; // number of bodies per leaf node
|
||||
uint32_t meshBvhCapacity = 2; // number of triangles per leaf node
|
||||
uint32_t broadphaseBvhCapacity = 1; // number of bodies per leaf node
|
||||
uint32_t meshBvhCapacity = 1; // number of triangles per leaf node
|
||||
|
||||
// additionally flattens a BVH for linear iteration, rather than a recursive / stack-based traversal
|
||||
bool flattenBvhBodies = true;
|
||||
|
||||
@ -73,6 +73,14 @@ namespace pod {
|
||||
alignas(4) uint32_t vertices = 0; // stores vertex count, should be unused
|
||||
};
|
||||
|
||||
// stores index offsets for LODs
|
||||
struct UF_API LODMetadata {
|
||||
struct Level {
|
||||
alignas(4) uint32_t indices = 0;
|
||||
alignas(4) uint32_t indexID = 0;
|
||||
} levels[4];
|
||||
};
|
||||
|
||||
// stores information about how to transform a draw call
|
||||
// to-do: clean up this mess
|
||||
struct UF_API Instance {
|
||||
@ -133,6 +141,7 @@ namespace pod {
|
||||
struct Primitive {
|
||||
pod::DrawCommand drawCommand;
|
||||
pod::Instance instance;
|
||||
pod::LODMetadata lod;
|
||||
};
|
||||
}
|
||||
|
||||
@ -265,9 +274,9 @@ namespace uf {
|
||||
void eraseAttribute( uf::Mesh::Input&, const uf::Mesh::Attribute& );
|
||||
void eraseAttribute( uf::Mesh::Input&, size_t );
|
||||
|
||||
uf::Mesh::Input remapInput( const uf::Mesh::Input&, size_t i = 0 ) const;
|
||||
uf::Mesh::Input remapVertexInput( size_t i = 0 ) const;
|
||||
uf::Mesh::Input remapIndexInput( size_t i = 0 ) const;
|
||||
uf::Mesh::Input remapInput( const uf::Mesh::Input&, size_t = 0, size_t = 0 ) const;
|
||||
uf::Mesh::Input remapVertexInput( size_t i = 0, size_t = 0 ) const;
|
||||
uf::Mesh::Input remapIndexInput( size_t i = 0, size_t = 0 ) const;
|
||||
|
||||
void print( bool = true ) const;
|
||||
|
||||
@ -276,9 +285,9 @@ namespace uf {
|
||||
std::string printInstances( bool = true ) const;
|
||||
std::string printIndirects( bool = true ) const;
|
||||
|
||||
uf::Mesh::View makeView( const uf::stl::vector<uf::stl::string>& wanted = {} ) const;
|
||||
uf::Mesh::View makeView( size_t commandIndex, const uf::stl::vector<uf::stl::string>& wanted = {} ) const;
|
||||
uf::stl::vector<uf::Mesh::View> makeViews( const uf::stl::vector<uf::stl::string>& wanted = {} ) const;
|
||||
uf::Mesh::View makeView( const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
|
||||
uf::Mesh::View makeView( size_t commandIndex, const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
|
||||
uf::stl::vector<uf::Mesh::View> makeViews( const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
|
||||
|
||||
inline bool hasVertex( const uf::stl::vector<ext::RENDERER::AttributeDescriptor>& descriptors ) const { return _hasV( vertex, descriptors ); }
|
||||
inline bool hasVertex( const uf::Mesh& mesh ) const { return _hasV( vertex, mesh.vertex ); }
|
||||
|
||||
@ -185,10 +185,20 @@ namespace {
|
||||
return drawCommand;
|
||||
}
|
||||
|
||||
pod::LODMetadata decodeLODMetadata( ext::json::Value& json, pod::Graph& graph ) {
|
||||
pod::LODMetadata lodMetadata;
|
||||
ext::json::forEach( json, [&]( size_t i, ext::json::Value& value ){
|
||||
lodMetadata.levels[i].indices = value["indices"].as( lodMetadata.levels[i].indices );
|
||||
lodMetadata.levels[i].indexID = value["indexID"].as( lodMetadata.levels[i].indexID );
|
||||
});
|
||||
return lodMetadata;
|
||||
}
|
||||
|
||||
pod::Primitive decodePrimitive( ext::json::Value& json, pod::Graph& graph ) {
|
||||
pod::Primitive prim;
|
||||
prim.instance = decodeInstance( json["instance"], graph );
|
||||
prim.drawCommand = decodeDrawCommand( json["drawCommand"], graph );
|
||||
prim.lod = decodeLODMetadata( json["lod"], graph );
|
||||
return prim;
|
||||
}
|
||||
|
||||
|
||||
@ -135,10 +135,21 @@ namespace {
|
||||
json["vertices"] = drawCommand.vertices;
|
||||
return json;
|
||||
}
|
||||
uf::Serializer encode( const pod::LODMetadata& lodMetadata, const EncodingSettings& settings, const pod::Graph& graph ) {
|
||||
uf::Serializer json;
|
||||
ext::json::reserve( json, 4 );
|
||||
for ( size_t i = 0; i < 4; ++i ) {
|
||||
auto& value = json.emplace_back();
|
||||
value["indices"] = lodMetadata.levels[i].indices;
|
||||
value["indexID"] = lodMetadata.levels[i].indexID;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
uf::Serializer encode( const pod::Primitive& primitive, const EncodingSettings& settings, const pod::Graph& graph ) {
|
||||
uf::Serializer json;
|
||||
json["drawCommand"] = encode( primitive.drawCommand, settings, graph );
|
||||
json["instance"] = encode( primitive.instance, settings, graph );
|
||||
json["lod"] = encode( primitive.lod, settings, graph );
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
@ -477,6 +477,7 @@ namespace {
|
||||
shader.aliasBuffer( "camera", storage.buffers.camera );
|
||||
shader.aliasBuffer( "indirect", *indirect );
|
||||
shader.aliasBuffer( "instance", storage.buffers.instance );
|
||||
shader.aliasBuffer( "lodMetadata", storage.buffers.lodMetadata );
|
||||
shader.aliasBuffer( "object", storage.buffers.object );
|
||||
|
||||
shader.textures.clear();
|
||||
@ -1361,6 +1362,7 @@ void uf::graph::initialize( pod::Graph::Storage& storage, size_t initialElements
|
||||
storage.buffers.drawCommands.initialize( (const void*) nullptr, sizeof(pod::DrawCommand) * initialElements, uf::renderer::enums::Buffer::STORAGE );
|
||||
storage.buffers.instance.initialize( (const void*) nullptr, sizeof(pod::Instance) * initialElements, uf::renderer::enums::Buffer::STORAGE );
|
||||
storage.buffers.instanceAddresses.initialize( (const void*) nullptr, sizeof(pod::Instance::Addresses) * initialElements, uf::renderer::enums::Buffer::STORAGE );
|
||||
storage.buffers.lodMetadata.initialize( (const void*) nullptr, sizeof(pod::LODMetadata) * initialElements, uf::renderer::enums::Buffer::STORAGE );
|
||||
storage.buffers.joint.initialize( (const void*) nullptr, sizeof(pod::Matrix4f) * initialElements, uf::renderer::enums::Buffer::STORAGE );
|
||||
storage.buffers.object.initialize( (const void*) nullptr, sizeof(pod::Instance::Object) * initialElements, uf::renderer::enums::Buffer::STORAGE );
|
||||
storage.buffers.material.initialize( (const void*) nullptr, sizeof(pod::Material) * initialElements, uf::renderer::enums::Buffer::STORAGE );
|
||||
@ -1397,6 +1399,7 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
|
||||
|
||||
static thread_local uf::stl::vector<pod::Instance> instances;
|
||||
static thread_local uf::stl::vector<pod::Instance::Addresses> instanceAddresses;
|
||||
static thread_local uf::stl::vector<pod::LODMetadata> lodMetadata;
|
||||
static thread_local uf::stl::vector<pod::Matrix4f> joints;
|
||||
static thread_local uf::stl::vector<pod::Instance::Object> objects;
|
||||
static thread_local uf::stl::vector<pod::Material> materials;
|
||||
@ -1430,10 +1433,15 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
|
||||
rebuild = storage.buffers.object.update( (const void*) objects.data(), objects.size() * sizeof(pod::Instance::Object) ) || rebuild;
|
||||
|
||||
if ( ::newGraphAdded ) {
|
||||
drawCommands.clear();
|
||||
instances.clear();
|
||||
lodMetadata.clear();
|
||||
|
||||
for ( auto& key : storage.primitives.keys ) {
|
||||
for ( auto& primitive : storage.primitives[key] ) {
|
||||
drawCommands.emplace_back( primitive.drawCommand );
|
||||
instances.emplace_back( primitive.instance );
|
||||
lodMetadata.emplace_back( primitive.lod );
|
||||
}
|
||||
}
|
||||
|
||||
@ -1448,14 +1456,10 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
|
||||
materials.clear();
|
||||
for ( auto& key : storage.materials.keys ) materials.emplace_back( storage.materials.map[key] );
|
||||
|
||||
drawCommands.clear();
|
||||
for ( auto& key : storage.primitives.keys ) {
|
||||
for ( auto& primitive : storage.primitives[key] ) drawCommands.emplace_back( primitive.drawCommand );
|
||||
}
|
||||
|
||||
rebuild = storage.buffers.instance.update( (const void*) instances.data(), instances.size() * sizeof(pod::Instance) ) || rebuild;
|
||||
rebuild = storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) ) || rebuild;
|
||||
rebuild = storage.buffers.drawCommands.update( (const void*) drawCommands.data(), drawCommands.size() * sizeof(pod::DrawCommand) ) || rebuild;
|
||||
rebuild = storage.buffers.lodMetadata.update( (const void*) lodMetadata.data(), lodMetadata.size() * sizeof(pod::LODMetadata) ) || rebuild;
|
||||
rebuild = storage.buffers.material.update( (const void*) materials.data(), materials.size() * sizeof(pod::Material) ) || rebuild;
|
||||
rebuild = storage.buffers.texture.update( (const void*) textures.data(), textures.size() * sizeof(pod::Texture) ) || rebuild;
|
||||
|
||||
|
||||
@ -282,6 +282,7 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
|
||||
struct {
|
||||
bool should = false;
|
||||
bool print = false;
|
||||
bool lods = false;
|
||||
size_t level = SIZE_MAX;
|
||||
float simplify = 1.0f;
|
||||
} meshopt;
|
||||
@ -309,6 +310,7 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -522,6 +524,7 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
|
||||
size_t level = SIZE_MAX;
|
||||
float simplify = 1.0f;
|
||||
bool print = false;
|
||||
bool lods = false;
|
||||
|
||||
if ( graph.metadata["exporter"]["optimize"].as<uf::stl::string>("") == "tagged" ) {
|
||||
bool should = false;
|
||||
@ -536,6 +539,7 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
|
||||
level = value["optimize mesh"]["level"].as(level);
|
||||
simplify = value["optimize mesh"]["simplify"].as(simplify);
|
||||
print = value["optimize mesh"]["print"].as(print);
|
||||
lods = value["optimize mesh"]["lods"].as(lods);
|
||||
}
|
||||
});
|
||||
|
||||
@ -547,6 +551,18 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
|
||||
if ( !ext::meshopt::optimize( mesh, simplify, level, print ) ) {
|
||||
UF_MSG_ERROR("Mesh optimization failed: {}", keyName );
|
||||
}
|
||||
if ( lods ) {
|
||||
auto factors = ext::meshopt::computeLODs( mesh.index.count );
|
||||
auto lodMetadata = ext::meshopt::generateLODs( mesh, factors, print );
|
||||
if ( lodMetadata.empty() ) {
|
||||
UF_MSG_ERROR("LOD generation failed: {}", keyName );
|
||||
} else {
|
||||
UF_MSG_DEBUG("Generated {} LODs: {}", factors.size() - 1, keyName);
|
||||
auto& primitives = storage.primitives[keyName];
|
||||
UF_ASSERT( primitives.size() == lodMetadata.size() );
|
||||
for ( auto i = 0; i < primitives.size(); ++i ) primitives[i].lod = lodMetadata[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UF_MSG_DEBUG( "Optimized mesh" );
|
||||
|
||||
@ -356,7 +356,7 @@ if ( meshgrid.grid.divisions.x > 1 || meshgrid.grid.divisions.y > 1 || meshgrid.
|
||||
auto partitioned = uf::meshgrid::partition( meshgrid.grid, meshlets, meshgrid.eps, meshgrid.clip, meshgrid.cleanup );
|
||||
if ( meshgrid.print ) UF_MSG_DEBUG( "Draw commands: {}: {} -> {} | Partitions: {} -> {}", m.name, meshlets.size(), partitioned.size(),
|
||||
(meshgrid.grid.divisions.x * meshgrid.grid.divisions.y * meshgrid.grid.divisions.z), meshgrid.grid.nodes.size()
|
||||
);
|
||||
);
|
||||
meshlets = std::move( partitioned );
|
||||
}
|
||||
|
||||
@ -367,6 +367,15 @@ if ( meshopt.should ) {
|
||||
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
|
||||
|
||||
@ -4,175 +4,224 @@
|
||||
|
||||
bool ext::meshopt::optimize( uf::Mesh& mesh, float simplify, size_t o, bool verbose ) {
|
||||
if ( mesh.isInterleaved() ) {
|
||||
UF_MSG_ERROR("optimization of interleaved meshes is currently not supported. Consider optimizing on meshlets.");
|
||||
UF_MSG_ERROR("Optimization of interleaved meshes is currently not supported. Consider optimizing on meshlets.");
|
||||
return false;
|
||||
}
|
||||
mesh.updateDescriptor();
|
||||
|
||||
struct Remap {
|
||||
uf::Mesh::Attribute attribute;
|
||||
uf::stl::vector<uint8_t> buffer;
|
||||
};
|
||||
|
||||
uf::stl::vector<Remap> attributes;
|
||||
uf::stl::vector<meshopt_Stream> streams;
|
||||
for ( auto& attribute : mesh.vertex.attributes ) {
|
||||
auto& p = attributes.emplace_back();
|
||||
p.attribute = attribute;
|
||||
|
||||
auto& stream = streams.emplace_back();
|
||||
stream.data = p.attribute.pointer;
|
||||
stream.size = p.attribute.descriptor.size;
|
||||
stream.stride = p.attribute.stride;
|
||||
const auto& views = mesh.buffer_views;
|
||||
if ( views.empty() ) {
|
||||
UF_MSG_ERROR("No buffer views found. Cannot optimize per-submesh.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasIndices = mesh.index.count > 0;
|
||||
size_t indicesCount = hasIndices ? mesh.index.count : mesh.vertex.count;
|
||||
uf::stl::vector<uint32_t> remap(indicesCount);
|
||||
uf::stl::vector<uint32_t> optIndices;
|
||||
pod::DrawCommand* drawCommands = mesh.indirect.count > 0 ? (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data() : nullptr;
|
||||
|
||||
uint32_t* sourceIndicesPointer = NULL;
|
||||
uf::stl::vector<uint32_t> sourceIndices(indicesCount);
|
||||
if ( hasIndices ) {
|
||||
uint8_t* pointer = (uint8_t*) mesh.getBuffer(mesh.index).data();
|
||||
for ( auto index = 0; index < indicesCount; ++index ) {
|
||||
switch ( mesh.index.size ) {
|
||||
case 1: sourceIndices[index] = (( uint8_t*) pointer)[index]; break;
|
||||
case 2: sourceIndices[index] = ((uint16_t*) pointer)[index]; break;
|
||||
case 4: sourceIndices[index] = ((uint32_t*) pointer)[index]; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for ( size_t viewIdx = 0; viewIdx < views.size(); ++viewIdx ) {
|
||||
const auto& view = views[viewIdx];
|
||||
auto& indicesView = view["index"];
|
||||
auto& positionsView = view["position"];
|
||||
|
||||
size_t verticesCount = meshopt_generateVertexRemapMulti( &remap[0], sourceIndicesPointer, indicesCount, indicesCount, &streams[0], streams.size() );
|
||||
if ( !indicesView.valid() || !positionsView.valid() ) continue;
|
||||
|
||||
// generate new indices, as they're going to be specific to a region of vertices due to drawcommand shittery
|
||||
uf::stl::vector<uint32_t> indices(indicesCount);
|
||||
meshopt_remapIndexBuffer(&indices[0], sourceIndicesPointer, indicesCount, &remap[0]);
|
||||
size_t indicesCount = view.index.count;
|
||||
size_t vertexCount = view.vertex.count;
|
||||
|
||||
//
|
||||
for ( auto& p : attributes ) {
|
||||
auto& buffer = p.buffer;
|
||||
buffer.resize(verticesCount * p.attribute.descriptor.size);
|
||||
|
||||
meshopt_remapVertexBuffer(&buffer[0], p.attribute.pointer, indicesCount, p.attribute.descriptor.size, &remap[0]);
|
||||
}
|
||||
//
|
||||
meshopt_optimizeVertexCache(&indices[0], &indices[0], indicesCount, verticesCount);
|
||||
//
|
||||
meshopt_optimizeVertexFetchRemap(&remap[0], &indices[0], indicesCount, verticesCount);
|
||||
//
|
||||
for ( auto& p : attributes ) {
|
||||
auto& buffer = p.buffer;
|
||||
p.attribute.pointer = &buffer[0];
|
||||
|
||||
meshopt_remapVertexBuffer(p.attribute.pointer, p.attribute.pointer, verticesCount, p.attribute.descriptor.size, &remap[0]);
|
||||
}
|
||||
// almost always causes ID discontinuities
|
||||
if ( 0.0f < simplify && simplify < 1.0f ) {
|
||||
uf::stl::vector<uint32_t> indicesSimplified(indicesCount);
|
||||
|
||||
uf::Mesh::Attribute positionAttribute;
|
||||
for ( auto& p : attributes ) if ( p.attribute.descriptor.name == "position" ) positionAttribute = p.attribute;
|
||||
|
||||
size_t targetIndices = indicesCount * simplify;
|
||||
float targetError = 1e-2f / simplify;
|
||||
|
||||
float realError = 0.0f;
|
||||
size_t realIndices = meshopt_simplify(&indicesSimplified[0], &indices[0], indicesCount, (float*) positionAttribute.pointer, verticesCount, positionAttribute.stride, targetError, realError);
|
||||
// size_t realIndices = meshopt_simplifySloppy(&indicesSimplified[0], &indices[0], indicesCount, (float*) positionAttribute.pointer, verticesCount, positionAttribute.stride, targetIndices);
|
||||
|
||||
if ( verbose ) UF_MSG_DEBUG("[Simplified] indices: {} -> {} | error: {} -> {}", indicesCount, realIndices, targetError, realError);
|
||||
|
||||
indicesCount = realIndices;
|
||||
indices.swap( indicesSimplified );
|
||||
}
|
||||
// done
|
||||
if ( mesh.indirect.count ) {
|
||||
bool discontinuityDetected = false;
|
||||
size_t lastID = 0;
|
||||
struct Remap {
|
||||
pod::DrawCommand* drawCommand;
|
||||
struct {
|
||||
size_t start = SIZE_MAX;
|
||||
size_t end = 0;
|
||||
} index, vertex;
|
||||
};
|
||||
|
||||
pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data();
|
||||
uf::stl::vector<Remap> remappedDrawCommands( mesh.indirect.count );
|
||||
|
||||
uf::Mesh::Attribute idAttribute;
|
||||
for ( auto& p : attributes ) if ( p.attribute.descriptor.name == "id" ) idAttribute = p.attribute;
|
||||
for ( size_t index = 0; index < indicesCount; ++index ) {
|
||||
size_t vertex = indices[index];
|
||||
pod::Vector<uint16_t,2>& id = *(pod::Vector<uint16_t,2>*) ( static_cast<uint8_t*>(idAttribute.pointer) + idAttribute.stride * vertex );
|
||||
|
||||
auto& d = remappedDrawCommands[id.x];
|
||||
d.drawCommand = &drawCommands[id.x];
|
||||
d.vertex.start = std::min( d.vertex.start, vertex );
|
||||
d.vertex.end = std::max( d.vertex.end, vertex );
|
||||
|
||||
d.index.start = std::min( d.index.start, index );
|
||||
d.index.end = std::max( d.index.end, index );
|
||||
|
||||
if ( lastID == id.x ) {
|
||||
|
||||
} else if ( lastID + 1 == id.x ) {
|
||||
lastID = id.x;
|
||||
} else {
|
||||
if ( verbose ) UF_MSG_DEBUG("Discontinuity detected: {} | {} | {} | {}", index, vertex, lastID, id.x);
|
||||
discontinuityDetected = true;
|
||||
lastID = id.x;
|
||||
uf::stl::vector<uint32_t> submeshIndices(indicesCount);
|
||||
for ( size_t i = 0; i < indicesCount; ++i ) {
|
||||
size_t global_i = view.index.first + i;
|
||||
switch ( indicesView.attribute.descriptor.size ) {
|
||||
case 1: submeshIndices[i] = indicesView.get<uint8_t>(global_i)[0]; break;
|
||||
case 2: submeshIndices[i] = indicesView.get<uint16_t>(global_i)[0]; break;
|
||||
case 4: submeshIndices[i] = indicesView.get<uint32_t>(global_i)[0]; break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( discontinuityDetected ) {
|
||||
UF_MSG_ERROR("Discontinuity detected, bailing...");
|
||||
return false;
|
||||
}
|
||||
meshopt_optimizeVertexCache(&submeshIndices[0], &submeshIndices[0], indicesCount, mesh.vertex.count);
|
||||
|
||||
for ( auto& d : remappedDrawCommands ) {
|
||||
d.drawCommand->indices = d.index.end - d.index.start + 1;
|
||||
d.drawCommand->indexID = d.index.start;
|
||||
d.drawCommand->vertexID = d.vertex.start;
|
||||
d.drawCommand->vertices = d.vertex.end - d.vertex.start + 1;
|
||||
}
|
||||
meshopt_optimizeOverdraw(
|
||||
&submeshIndices[0],
|
||||
&submeshIndices[0],
|
||||
indicesCount,
|
||||
(const float*) positionsView.data(),
|
||||
mesh.vertex.count,
|
||||
positionsView.stride(),
|
||||
1.05f
|
||||
);
|
||||
|
||||
for ( size_t index = 0; index < indicesCount; ++index ) {
|
||||
auto& vertex = indices[index];
|
||||
pod::Vector<uint16_t,2>& id = *(pod::Vector<uint16_t,2>*) ( static_cast<uint8_t*>(idAttribute.pointer) + idAttribute.stride * vertex );
|
||||
if ( 0.0f < simplify && simplify < 1.0f ) {
|
||||
uf::stl::vector<uint32_t> indicesSimplified(indicesCount);
|
||||
|
||||
auto& d = remappedDrawCommands[id.x];
|
||||
vertex -= d.vertex.start;
|
||||
}
|
||||
}
|
||||
float targetError = 0.1; // 1e-2f / simplify;
|
||||
float realError = 0.0f;
|
||||
|
||||
mesh.index.count = indicesCount;
|
||||
mesh.vertex.count = verticesCount;
|
||||
size_t realIndices = meshopt_simplify(
|
||||
&indicesSimplified[0],
|
||||
&submeshIndices[0],
|
||||
indicesCount,
|
||||
(const float*) positionsView.data(),
|
||||
mesh.vertex.count,
|
||||
positionsView.stride(),
|
||||
indicesCount * simplify,
|
||||
targetError
|
||||
//,0, &realError
|
||||
);
|
||||
|
||||
// vertices
|
||||
for ( size_t i = 0; i < attributes.size(); ++i ) {
|
||||
auto& attribute = mesh.vertex.attributes[i];
|
||||
auto& remapped = attributes[i];
|
||||
|
||||
mesh.buffers[attribute.buffer].swap( remapped.buffer );
|
||||
attribute.pointer = mesh.buffers[attribute.buffer].data();
|
||||
}
|
||||
|
||||
// indices
|
||||
{
|
||||
mesh.resizeIndices( mesh.index.count );
|
||||
uint8_t* pointer = (uint8_t*) mesh.getBuffer(mesh.index).data();
|
||||
for ( auto index = 0; index < indicesCount; ++index ) {
|
||||
switch ( mesh.index.size ) {
|
||||
case 1: (( uint8_t*) pointer)[index] = indices[index]; break;
|
||||
case 2: ((uint16_t*) pointer)[index] = indices[index]; break;
|
||||
case 4: ((uint32_t*) pointer)[index] = indices[index]; break;
|
||||
if ( verbose ) {
|
||||
UF_MSG_DEBUG("[View {} Simplified] indices: {} -> {} | error: {} -> {}", viewIdx, indicesCount, realIndices, targetError, realError);
|
||||
}
|
||||
|
||||
indicesCount = realIndices;
|
||||
submeshIndices.swap(indicesSimplified);
|
||||
submeshIndices.resize(indicesCount);
|
||||
}
|
||||
|
||||
size_t newIndexStart = optIndices.size();
|
||||
optIndices.insert(optIndices.end(), submeshIndices.begin(), submeshIndices.end());
|
||||
|
||||
if ( drawCommands ) {
|
||||
drawCommands[view.indirectIndex].indexID = newIndexStart;
|
||||
drawCommands[view.indirectIndex].indices = indicesCount;
|
||||
}
|
||||
}
|
||||
|
||||
mesh.index.count = optIndices.size();
|
||||
mesh.resizeIndices( mesh.index.count );
|
||||
|
||||
uint8_t* dstPointer = (uint8_t*) mesh.getBuffer(mesh.index).data();
|
||||
for ( size_t i = 0; i < optIndices.size(); ++i ) {
|
||||
switch ( mesh.index.size ) {
|
||||
case 1: (( uint8_t*) dstPointer)[i] = (uint8_t) optIndices[i]; break;
|
||||
case 2: ((uint16_t*) dstPointer)[i] = (uint16_t) optIndices[i]; break;
|
||||
case 4: ((uint32_t*) dstPointer)[i] = (uint32_t) optIndices[i]; break;
|
||||
}
|
||||
}
|
||||
|
||||
mesh.updateDescriptor();
|
||||
return true;
|
||||
}
|
||||
|
||||
uf::stl::vector<float> ext::meshopt::computeLODs( size_t count, size_t maxLODs, size_t minIndices ) {
|
||||
uf::stl::vector<float> factors;
|
||||
factors.reserve(maxLODs);
|
||||
factors.emplace_back(1.0f);
|
||||
|
||||
float factor = 1.0f;
|
||||
for (size_t i = 1; i < maxLODs; ++i) {
|
||||
factor *= 0.5f;
|
||||
if ( count * factor < minIndices ) break;
|
||||
factors.emplace_back(factor);
|
||||
}
|
||||
|
||||
return factors;
|
||||
}
|
||||
|
||||
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;
|
||||
if ( views.empty() ) return lodMetadata;
|
||||
|
||||
size_t numLODs = std::min(lodFactors.size(), (size_t)4);
|
||||
lodMetadata.resize(mesh.indirect.count);
|
||||
|
||||
uf::stl::vector<uf::stl::vector<uint32_t>> lodBlocks(numLODs);
|
||||
pod::DrawCommand* drawCommands = mesh.indirect.count > 0 ? (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data() : nullptr;
|
||||
|
||||
for ( size_t viewIdx = 0; viewIdx < views.size(); ++viewIdx ) {
|
||||
const auto& view = views[viewIdx];
|
||||
uint32_t cmdIdx = view.indirectIndex;
|
||||
|
||||
auto& indicesView = view["index"];
|
||||
auto& positionsView = view["position"];
|
||||
|
||||
size_t baseIndicesCount = view.index.count;
|
||||
uf::stl::vector<uint32_t> baseIndices(baseIndicesCount);
|
||||
|
||||
for ( size_t i = 0; i < baseIndicesCount; ++i ) {
|
||||
size_t global_i = view.index.first + i;
|
||||
switch ( indicesView.attribute.descriptor.size ) {
|
||||
case 1: baseIndices[i] = indicesView.get<uint8_t>(global_i)[0]; break;
|
||||
case 2: baseIndices[i] = indicesView.get<uint16_t>(global_i)[0]; break;
|
||||
case 4: baseIndices[i] = indicesView.get<uint32_t>(global_i)[0]; break;
|
||||
}
|
||||
}
|
||||
|
||||
meshopt_optimizeVertexCache(&baseIndices[0], &baseIndices[0], baseIndicesCount, mesh.vertex.count);
|
||||
|
||||
for ( size_t lodIdx = 0; lodIdx < numLODs; ++lodIdx ) {
|
||||
float simplify = lodFactors[lodIdx];
|
||||
uf::stl::vector<uint32_t> lodIndices = baseIndices;
|
||||
size_t currentIndicesCount = baseIndicesCount;
|
||||
|
||||
if ( simplify < 1.0f ) {
|
||||
float targetError = 0.1; // 1e-2f / simplify;
|
||||
float realError = 0.0f;
|
||||
currentIndicesCount = meshopt_simplify(
|
||||
&lodIndices[0], &baseIndices[0], baseIndicesCount,
|
||||
(const float*)positionsView.data(0), mesh.vertex.count, positionsView.stride(),
|
||||
baseIndicesCount * simplify, targetError
|
||||
//, 0, &realError
|
||||
);
|
||||
|
||||
if ( baseIndicesCount == currentIndicesCount ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( verbose ) {
|
||||
UF_MSG_DEBUG("[View {} Simplified LOD {}] indices: {} -> {} | error: {} -> {}", viewIdx, lodIdx, baseIndicesCount, currentIndicesCount, targetError, realError);
|
||||
}
|
||||
|
||||
|
||||
lodIndices.resize(currentIndicesCount);
|
||||
}
|
||||
|
||||
lodMetadata[cmdIdx].levels[lodIdx].indexID = lodBlocks[lodIdx].size();
|
||||
lodMetadata[cmdIdx].levels[lodIdx].indices = currentIndicesCount;
|
||||
|
||||
lodBlocks[lodIdx].insert(lodBlocks[lodIdx].end(), lodIndices.begin(), lodIndices.end());
|
||||
}
|
||||
}
|
||||
|
||||
uf::stl::vector<uint32_t> unifiedIndices;
|
||||
size_t currentGlobalOffset = 0;
|
||||
|
||||
for ( size_t lodIdx = 0; lodIdx < numLODs; ++lodIdx ) {
|
||||
for ( size_t viewIdx = 0; viewIdx < views.size(); ++viewIdx ) {
|
||||
uint32_t cmdIdx = views[viewIdx].indirectIndex;
|
||||
lodMetadata[cmdIdx].levels[lodIdx].indexID += currentGlobalOffset;
|
||||
|
||||
if ( lodIdx == 0 && drawCommands ) {
|
||||
drawCommands[cmdIdx].indexID = lodMetadata[cmdIdx].levels[0].indexID;
|
||||
drawCommands[cmdIdx].indices = lodMetadata[cmdIdx].levels[0].indices;
|
||||
}
|
||||
}
|
||||
|
||||
unifiedIndices.insert(unifiedIndices.end(), lodBlocks[lodIdx].begin(), lodBlocks[lodIdx].end());
|
||||
currentGlobalOffset = unifiedIndices.size();
|
||||
}
|
||||
|
||||
mesh.index.count = unifiedIndices.size();
|
||||
mesh.resizeIndices( mesh.index.count );
|
||||
uint8_t* dstPointer = (uint8_t*) mesh.getBuffer(mesh.index).data();
|
||||
for ( size_t i = 0; i < unifiedIndices.size(); ++i ) {
|
||||
switch ( mesh.index.size ) {
|
||||
case 1: (( uint8_t*) dstPointer)[i] = (uint8_t) unifiedIndices[i]; break;
|
||||
case 2: ((uint16_t*) dstPointer)[i] = (uint16_t) unifiedIndices[i]; break;
|
||||
case 4: ((uint32_t*) dstPointer)[i] = (uint32_t) unifiedIndices[i]; break;
|
||||
}
|
||||
}
|
||||
|
||||
mesh.updateDescriptor();
|
||||
|
||||
return lodMetadata;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -2,13 +2,9 @@
|
||||
#if UF_USE_XATLAS
|
||||
#include <xatlas/xatlas.h>
|
||||
|
||||
#define UF_XATLAS_UNWRAP_MULTITHREAD 1
|
||||
#define UF_XATLAS_UNWRAP_SERIAL 1 // really big scenes will gorge on memory
|
||||
#define UF_XATLAS_UNWRAP_MULTITHREAD 0 // prone to crashing
|
||||
|
||||
size_t ext::xatlas::unwrap( pod::Graph& graph ) {
|
||||
return graph.metadata["exporter"]["unwrap lazy"].as<bool>(false) ? unwrapLazy( graph ) : unwrapExperimental( graph );
|
||||
}
|
||||
size_t ext::xatlas::unwrapExperimental( pod::Graph& graph ) {
|
||||
struct Entry {
|
||||
size_t index = 0;
|
||||
size_t commandID = 0;
|
||||
@ -60,81 +56,50 @@ size_t ext::xatlas::unwrapExperimental( pod::Graph& graph ) {
|
||||
if ( !should ) continue;
|
||||
|
||||
source = mesh;
|
||||
source.updateDescriptor();
|
||||
source.updateDescriptor();
|
||||
|
||||
uf::Mesh::Input vertexInput = mesh.vertex;
|
||||
for ( size_t viewIdx = 0; viewIdx < source.buffer_views.size(); ++viewIdx ) {
|
||||
const auto& view = source.buffer_views[viewIdx];
|
||||
|
||||
uf::Mesh::Attribute positionAttribute;
|
||||
uf::Mesh::Attribute uvAttribute;
|
||||
uf::Mesh::Attribute stAttribute;
|
||||
size_t atlasID = 0;
|
||||
if ( view.indirectIndex != -1 ) {
|
||||
pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data();
|
||||
atlasID = drawCommands[view.indirectIndex].auxID;
|
||||
}
|
||||
|
||||
for ( auto& attribute : mesh.vertex.attributes ) {
|
||||
if ( attribute.descriptor.name == "position" ) positionAttribute = attribute;
|
||||
else if ( attribute.descriptor.name == "uv" ) uvAttribute = attribute;
|
||||
else if ( attribute.descriptor.name == "st" ) stAttribute = attribute;
|
||||
}
|
||||
UF_ASSERT( positionAttribute.descriptor.name == "position" && uvAttribute.descriptor.name == "uv" && stAttribute.descriptor.name == "st" );
|
||||
auto& atlas = atlases[atlasID];
|
||||
auto& entry = atlas.entries.emplace_back();
|
||||
entry.index = index;
|
||||
entry.commandID = viewIdx;
|
||||
|
||||
if ( mesh.index.count ) {
|
||||
uf::Mesh::Input indexInput = mesh.index;
|
||||
uf::Mesh::Attribute indexAttribute = mesh.index.attributes.front();
|
||||
auto& decl = entry.decl;
|
||||
auto posView = view["position"];
|
||||
auto uvView = view["uv"];
|
||||
auto idxView = view["index"];
|
||||
|
||||
::xatlas::IndexFormat indexType = ::xatlas::IndexFormat::UInt32;
|
||||
switch ( mesh.index.size ) {
|
||||
case sizeof(uint16_t): indexType = ::xatlas::IndexFormat::UInt16; break;
|
||||
case sizeof(uint32_t): indexType = ::xatlas::IndexFormat::UInt32; break;
|
||||
default: UF_EXCEPTION("unsupported index type"); break;
|
||||
}
|
||||
UF_ASSERT( posView.valid() && uvView.valid() );
|
||||
|
||||
if ( mesh.indirect.count ) {
|
||||
auto& primitives = /*graph.storage*/storage.primitives[name];
|
||||
pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data();
|
||||
decl.vertexCount = view.vertex.count;
|
||||
decl.vertexPositionData = posView.data(view.vertex.first);
|
||||
decl.vertexPositionStride = posView.stride();
|
||||
decl.vertexUvData = uvView.data(view.vertex.first);
|
||||
decl.vertexUvStride = uvView.stride();
|
||||
|
||||
for ( auto i = 0; i < mesh.indirect.count; ++i ) {
|
||||
size_t atlasID = drawCommands[i].auxID;
|
||||
if ( idxView.valid() ) {
|
||||
decl.indexCount = view.index.count;
|
||||
// Pass view.index.first to offset the index pointer!
|
||||
decl.indexData = idxView.data(view.index.first);
|
||||
|
||||
vertexInput = mesh.remapVertexInput( i );
|
||||
indexInput = mesh.remapIndexInput( i );
|
||||
|
||||
auto& atlas = atlases[atlasID];
|
||||
auto& entry = atlas.entries.emplace_back();
|
||||
entry.index = index;
|
||||
entry.commandID = i;
|
||||
|
||||
auto& decl = entry.decl;
|
||||
|
||||
decl.vertexPositionData = static_cast<uint8_t*>(positionAttribute.pointer) + positionAttribute.stride * vertexInput.first;
|
||||
decl.vertexPositionStride = positionAttribute.stride;
|
||||
|
||||
decl.vertexUvData = static_cast<uint8_t*>(uvAttribute.pointer) + uvAttribute.stride * vertexInput.first;
|
||||
decl.vertexUvStride = uvAttribute.stride;
|
||||
|
||||
decl.vertexCount = vertexInput.count;
|
||||
|
||||
decl.indexCount = indexInput.count;
|
||||
decl.indexData = static_cast<uint8_t*>(indexAttribute.pointer) + indexAttribute.stride * indexInput.first;
|
||||
decl.indexFormat = indexType;
|
||||
}
|
||||
} else {
|
||||
size_t atlasID = 0;
|
||||
auto& atlas = atlases[atlasID];
|
||||
auto& entry = atlas.entries.emplace_back();
|
||||
entry.index = index;
|
||||
|
||||
auto& decl = entry.decl;
|
||||
decl.vertexPositionData = static_cast<uint8_t*>(positionAttribute.pointer) + positionAttribute.stride * vertexInput.first;
|
||||
decl.vertexPositionStride = positionAttribute.stride;
|
||||
|
||||
decl.vertexUvData = static_cast<uint8_t*>(uvAttribute.pointer) + uvAttribute.stride * vertexInput.first;
|
||||
decl.vertexUvStride = uvAttribute.stride;
|
||||
|
||||
decl.vertexCount = vertexInput.count;
|
||||
|
||||
decl.indexCount = indexInput.count;
|
||||
decl.indexData = static_cast<uint8_t*>(indexAttribute.pointer) + indexAttribute.stride * indexInput.first;
|
||||
decl.indexFormat = indexType;
|
||||
}
|
||||
} else UF_EXCEPTION("to-do: not require indices for meshes");
|
||||
switch ( idxView.attribute.descriptor.size ) {
|
||||
case 1: UF_EXCEPTION("xatlas does not support 8-bit indices"); break;
|
||||
case 2: decl.indexFormat = ::xatlas::IndexFormat::UInt16; break;
|
||||
case 4: decl.indexFormat = ::xatlas::IndexFormat::UInt32; break;
|
||||
default: UF_EXCEPTION("unsupported index type"); break;
|
||||
}
|
||||
} else {
|
||||
decl.indexCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::xatlas::ChartOptions chartOptions{};
|
||||
@ -168,7 +133,6 @@ size_t ext::xatlas::unwrapExperimental( pod::Graph& graph ) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// pack
|
||||
#if UF_XATLAS_UNWRAP_MULTITHREAD
|
||||
auto tasks = uf::thread::schedule(true);
|
||||
@ -272,108 +236,70 @@ size_t ext::xatlas::unwrapExperimental( pod::Graph& graph ) {
|
||||
|
||||
// update vertices
|
||||
for ( auto& pair : atlases ) {
|
||||
auto& atlas = pair.second;
|
||||
auto& atlas = pair.second;
|
||||
|
||||
size_t vertexIDOffset = 0;
|
||||
for ( auto i = 0; i < atlas.pointer->meshCount; i++ ) {
|
||||
auto& xmesh = atlas.pointer->meshes[i];
|
||||
auto& entry = atlas.entries[i];
|
||||
auto& name = graph.meshes[entry.index];
|
||||
auto& mesh = /*graph.storage*/storage.meshes[name];
|
||||
auto& source = sources[entry.index];
|
||||
for ( auto i = 0; i < atlas.pointer->meshCount; i++ ) {
|
||||
auto& xmesh = atlas.pointer->meshes[i];
|
||||
auto& entry = atlas.entries[i];
|
||||
|
||||
if ( source.vertex.count == 0 ) continue;
|
||||
auto& name = graph.meshes[entry.index];
|
||||
auto& mesh = storage.meshes[name];
|
||||
auto& source = sources[entry.index];
|
||||
|
||||
// draw commands
|
||||
if ( mesh.indirect.count ) {
|
||||
auto srcInput = source.remapVertexInput( entry.commandID );
|
||||
auto dstInput = mesh.remapVertexInput( entry.commandID );
|
||||
if ( source.vertex.count == 0 ) continue;
|
||||
|
||||
auto& primitives = /*graph.storage*/storage.primitives[name];
|
||||
pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data();
|
||||
// Grab the read-only view from our unmodified source mesh
|
||||
const auto& srcView = source.buffer_views[entry.commandID];
|
||||
|
||||
auto& drawCommand = drawCommands[entry.commandID];
|
||||
auto& primitive = primitives[entry.commandID];
|
||||
// We dynamically calculate the destination offsets using the updated draw commands
|
||||
// Or if it's direct, it's just 0.
|
||||
size_t dstVertexFirst = 0;
|
||||
size_t dstIndexFirst = 0;
|
||||
if ( mesh.indirect.count > 0 ) {
|
||||
pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data();
|
||||
dstVertexFirst = drawCommands[entry.commandID].vertexID;
|
||||
dstIndexFirst = drawCommands[entry.commandID].indexID;
|
||||
}
|
||||
|
||||
for ( auto j = 0; j < xmesh.vertexCount; ++j ) {
|
||||
auto& vertex = xmesh.vertexArray[j];
|
||||
auto ref = vertex.xref;
|
||||
|
||||
for ( auto _ = 0; _ < srcInput.attributes.size(); ++_ ) {
|
||||
auto srcAttribute = srcInput.attributes[_];
|
||||
auto dstAttribute = dstInput.attributes[_];
|
||||
// 1. Copy over the vertices based on the xref mapping
|
||||
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
|
||||
|
||||
memcpy(
|
||||
static_cast<uint8_t*>(dstAttribute.pointer) + dstAttribute.stride * (j + dstInput.first),
|
||||
static_cast<uint8_t*>(srcAttribute.pointer) + srcAttribute.stride * (ref + srcInput.first),
|
||||
srcAttribute.stride
|
||||
);
|
||||
}
|
||||
}
|
||||
for ( auto attrIdx = 0; attrIdx < mesh.vertex.attributes.size(); ++attrIdx ) {
|
||||
auto srcAttribute = srcView.vertex.attributes[attrIdx];
|
||||
auto dstAttribute = mesh.vertex.attributes[attrIdx];
|
||||
|
||||
for ( auto j = 0; j < xmesh.vertexCount; ++j ) {
|
||||
auto& vertex = xmesh.vertexArray[j];
|
||||
auto ref = vertex.xref;
|
||||
|
||||
for ( auto _ = 0; _ < srcInput.attributes.size(); ++_ ) {
|
||||
auto dstAttribute = dstInput.attributes[_];
|
||||
if ( dstAttribute.descriptor.name != "st" ) continue;
|
||||
pod::Vector2f& st = *(pod::Vector2f*) ( static_cast<uint8_t*>(dstAttribute.pointer) + dstAttribute.stride * (j + dstInput.first) );
|
||||
st = pod::Vector2f{ vertex.uv[0] / atlas.pointer->width, vertex.uv[1] / atlas.pointer->height };;
|
||||
}
|
||||
}
|
||||
uint8_t* dstPtr = static_cast<uint8_t*>(dstAttribute.pointer) + dstAttribute.stride * (dstVertexFirst + j);
|
||||
|
||||
// indices
|
||||
if ( mesh.index.count ) {
|
||||
uf::Mesh::Input indexInput = mesh.remapIndexInput( entry.commandID );
|
||||
uf::Mesh::Attribute indexAttribute = indexInput.attributes.front();
|
||||
uint8_t* pointer = (uint8_t*) static_cast<uint8_t*>(indexAttribute.pointer) + indexAttribute.stride * indexInput.first;
|
||||
for ( auto index = 0; index < xmesh.indexCount; ++index ) {
|
||||
switch ( mesh.index.size ) {
|
||||
case 1: (( uint8_t*) pointer)[index] = xmesh.indexArray[index]; break;
|
||||
case 2: ((uint16_t*) pointer)[index] = xmesh.indexArray[index]; break;
|
||||
case 4: ((uint32_t*) pointer)[index] = xmesh.indexArray[index]; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uf::Mesh::Attribute stAttribute;
|
||||
for ( auto& attribute : mesh.vertex.attributes ) if ( attribute.descriptor.name == "st" ) stAttribute = attribute;
|
||||
UF_ASSERT( stAttribute.descriptor.name == "st" );
|
||||
if ( dstAttribute.descriptor.name == "st" ) {
|
||||
// Write new lightmap STs!
|
||||
pod::Vector2f& st = *(pod::Vector2f*)dstPtr;
|
||||
st = pod::Vector2f{ vertex.uv[0] / atlas.pointer->width, vertex.uv[1] / atlas.pointer->height };
|
||||
} else {
|
||||
// Copy original vertex data
|
||||
const uint8_t* srcPtr = static_cast<const uint8_t*>(srcAttribute.pointer) + srcAttribute.stride * (srcView.vertex.first + ref);
|
||||
memcpy(dstPtr, srcPtr, srcAttribute.descriptor.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vertices
|
||||
for ( auto j = 0; j < xmesh.vertexCount; ++j ) {
|
||||
auto& vertex = xmesh.vertexArray[j];
|
||||
auto ref = vertex.xref;
|
||||
// 2. Write new indices
|
||||
if ( mesh.index.count ) {
|
||||
uf::Mesh::Attribute indexAttribute = mesh.index.attributes.front();
|
||||
uint8_t* dstIndexPtr = static_cast<uint8_t*>(indexAttribute.pointer) + indexAttribute.stride * dstIndexFirst;
|
||||
|
||||
for ( auto k = 0; k < mesh.vertex.attributes.size(); ++k ) {
|
||||
auto srcAttribute = source.vertex.attributes[k];
|
||||
auto dstAttribute = mesh.vertex.attributes[k];
|
||||
|
||||
if ( dstAttribute.descriptor.name == "st" ) {
|
||||
pod::Vector2f& st = *(pod::Vector2f*) ( ((uint8_t*) dstAttribute.pointer) + dstAttribute.stride * (mesh.vertex.first + j));
|
||||
st = pod::Vector2f{ vertex.uv[0] / atlas.pointer->width, vertex.uv[1] / atlas.pointer->height };
|
||||
} else {
|
||||
memcpy( static_cast<uint8_t*>(dstAttribute.pointer) + dstAttribute.stride * j, static_cast<uint8_t*>(srcAttribute.pointer) + srcAttribute.stride * ref, srcAttribute.stride );
|
||||
}
|
||||
}
|
||||
}
|
||||
// indices
|
||||
if ( mesh.index.count ) {
|
||||
// uint8_t* pointer = (uint8_t*) mesh.buffers[mesh.isInterleaved(mesh.index.interleaved) ? mesh.index.interleaved : mesh.index.attributes.front().buffer].data();
|
||||
uint8_t* pointer = (uint8_t*) mesh.getBuffer(mesh.index).data();
|
||||
for ( auto index = 0; index < xmesh.indexCount; ++index ) {
|
||||
switch ( mesh.index.size ) {
|
||||
case 1: (( uint8_t*) pointer)[index] = xmesh.indexArray[index]; break;
|
||||
case 2: ((uint16_t*) pointer)[index] = xmesh.indexArray[index]; break;
|
||||
case 4: ((uint32_t*) pointer)[index] = xmesh.indexArray[index]; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mesh.updateDescriptor();
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
mesh.updateDescriptor();
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
size_t atlasCount = 0;
|
||||
@ -384,276 +310,4 @@ size_t ext::xatlas::unwrapExperimental( pod::Graph& graph ) {
|
||||
}
|
||||
return atlasCount;
|
||||
}
|
||||
|
||||
size_t ext::xatlas::unwrapLazy( pod::Graph& graph ) {
|
||||
struct Entry {
|
||||
size_t index = 0;
|
||||
size_t commandID = 0;
|
||||
::xatlas::MeshDecl decl;
|
||||
};
|
||||
struct Atlas {
|
||||
::xatlas::Atlas* pointer = NULL;
|
||||
uf::stl::vector<Entry> entries;
|
||||
size_t vertexOffset = 0;
|
||||
};
|
||||
|
||||
uf::stl::unordered_map<size_t, Atlas> atlases;
|
||||
atlases.reserve(graph.meshes.size());
|
||||
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent<pod::Graph::Storage>();
|
||||
|
||||
// copy source meshes
|
||||
// create mesh decls for passing to xatlas
|
||||
for ( auto index = 0; index < graph.meshes.size(); ++index ) {
|
||||
auto& name = graph.meshes[index];
|
||||
auto& mesh = /*graph.storage*/storage.meshes[name];
|
||||
|
||||
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;
|
||||
} else {
|
||||
ext::json::forEach( graph.metadata["tags"], [&]( const uf::stl::string& key, ext::json::Value& value ) {
|
||||
if ( uf::string::isRegex( key ) ) {
|
||||
if ( !uf::string::matched( name, key ) ) return;
|
||||
} else if ( name != key ) return;
|
||||
|
||||
if ( ext::json::isNull( value["unwrap mesh"] ) ) return;
|
||||
if ( !value["unwrap mesh"].as<bool>(false) ) return;
|
||||
should = true;
|
||||
});
|
||||
}
|
||||
if ( !should ) continue;
|
||||
|
||||
|
||||
uf::Mesh::Input vertexInput = mesh.vertex;
|
||||
|
||||
uf::Mesh::Attribute positionAttribute;
|
||||
uf::Mesh::Attribute uvAttribute;
|
||||
uf::Mesh::Attribute stAttribute;
|
||||
|
||||
for ( auto& attribute : mesh.vertex.attributes ) {
|
||||
if ( attribute.descriptor.name == "position" ) positionAttribute = attribute;
|
||||
else if ( attribute.descriptor.name == "uv" ) uvAttribute = attribute;
|
||||
else if ( attribute.descriptor.name == "st" ) stAttribute = attribute;
|
||||
}
|
||||
UF_ASSERT( positionAttribute.descriptor.name == "position" && uvAttribute.descriptor.name == "uv" && stAttribute.descriptor.name == "st" );
|
||||
|
||||
if ( mesh.index.count ) {
|
||||
uf::Mesh::Input indexInput = mesh.index;
|
||||
uf::Mesh::Attribute indexAttribute = mesh.index.attributes.front();
|
||||
|
||||
::xatlas::IndexFormat indexType = ::xatlas::IndexFormat::UInt32;
|
||||
switch ( mesh.index.size ) {
|
||||
case sizeof(uint16_t): indexType = ::xatlas::IndexFormat::UInt16; break;
|
||||
case sizeof(uint32_t): indexType = ::xatlas::IndexFormat::UInt32; break;
|
||||
default: UF_EXCEPTION("unsupported index type"); break;
|
||||
}
|
||||
|
||||
if ( mesh.indirect.count ) {
|
||||
auto& primitives = /*graph.storage*/storage.primitives[name];
|
||||
pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer(mesh.indirect).data();
|
||||
|
||||
for ( auto i = 0; i < mesh.indirect.count; ++i ) {
|
||||
size_t atlasID = drawCommands[i].auxID;
|
||||
|
||||
vertexInput = mesh.remapVertexInput( i );
|
||||
indexInput = mesh.remapIndexInput( i );
|
||||
|
||||
auto& atlas = atlases[atlasID];
|
||||
auto& entry = atlas.entries.emplace_back();
|
||||
entry.index = index;
|
||||
entry.commandID = i;
|
||||
|
||||
auto& decl = entry.decl;
|
||||
|
||||
decl.vertexPositionData = static_cast<uint8_t*>(positionAttribute.pointer) + positionAttribute.stride * vertexInput.first;
|
||||
decl.vertexPositionStride = positionAttribute.stride;
|
||||
|
||||
decl.vertexUvData = static_cast<uint8_t*>(uvAttribute.pointer) + uvAttribute.stride * vertexInput.first;
|
||||
decl.vertexUvStride = uvAttribute.stride;
|
||||
|
||||
decl.vertexCount = vertexInput.count;
|
||||
|
||||
decl.indexCount = indexInput.count;
|
||||
decl.indexData = static_cast<uint8_t*>(indexAttribute.pointer) + indexAttribute.stride * indexInput.first;
|
||||
decl.indexFormat = indexType;
|
||||
}
|
||||
} else {
|
||||
size_t atlasID = 0;
|
||||
auto& atlas = atlases[atlasID];
|
||||
auto& entry = atlas.entries.emplace_back();
|
||||
entry.index = index;
|
||||
|
||||
auto& decl = entry.decl;
|
||||
decl.vertexPositionData = static_cast<uint8_t*>(positionAttribute.pointer) + positionAttribute.stride * vertexInput.first;
|
||||
decl.vertexPositionStride = positionAttribute.stride;
|
||||
|
||||
decl.vertexUvData = static_cast<uint8_t*>(uvAttribute.pointer) + uvAttribute.stride * vertexInput.first;
|
||||
decl.vertexUvStride = uvAttribute.stride;
|
||||
|
||||
decl.vertexCount = vertexInput.count;
|
||||
|
||||
decl.indexCount = indexInput.count;
|
||||
decl.indexData = static_cast<uint8_t*>(indexAttribute.pointer) + indexAttribute.stride * indexInput.first;
|
||||
decl.indexFormat = indexType;
|
||||
}
|
||||
} else UF_EXCEPTION("to-do: not require indices for meshes");
|
||||
}
|
||||
|
||||
::xatlas::ChartOptions chartOptions{};
|
||||
chartOptions.useInputMeshUvs = graph.metadata["baking"]["settings"]["useInputMeshUvs"].as(chartOptions.useInputMeshUvs);
|
||||
chartOptions.maxIterations = graph.metadata["baking"]["settings"]["maxIterations"].as(chartOptions.maxIterations);
|
||||
|
||||
::xatlas::PackOptions packOptions{};
|
||||
packOptions.maxChartSize = graph.metadata["baking"]["settings"]["maxChartSize"].as(packOptions.maxChartSize);
|
||||
packOptions.padding = graph.metadata["baking"]["settings"]["padding"].as(packOptions.padding);
|
||||
packOptions.texelsPerUnit = graph.metadata["baking"]["settings"]["texelsPerUnit"].as(packOptions.texelsPerUnit);
|
||||
packOptions.bilinear = graph.metadata["baking"]["settings"]["bilinear"].as(packOptions.bilinear);
|
||||
packOptions.blockAlign = graph.metadata["baking"]["settings"]["blockAlign"].as(packOptions.blockAlign);
|
||||
packOptions.bruteForce = graph.metadata["baking"]["settings"]["bruteForce"].as(packOptions.bruteForce);
|
||||
packOptions.createImage = graph.metadata["baking"]["settings"]["createImage"].as(packOptions.createImage);
|
||||
packOptions.rotateChartsToAxis = graph.metadata["baking"]["settings"]["rotateChartsToAxis"].as(packOptions.rotateChartsToAxis);
|
||||
packOptions.rotateCharts = graph.metadata["baking"]["settings"]["rotateCharts"].as(packOptions.rotateCharts);
|
||||
packOptions.resolution = graph.metadata["baking"]["resolution"].as(packOptions.resolution);
|
||||
|
||||
// pack
|
||||
#if UF_XATLAS_UNWRAP_SERIAL
|
||||
size_t atlasCount = 0;
|
||||
for ( auto& pair : atlases ) {
|
||||
auto& atlas = pair.second;
|
||||
if ( !atlas.pointer ) atlas.pointer = ::xatlas::Create();
|
||||
|
||||
for ( auto& entry : atlas.entries ) {
|
||||
::xatlas::AddMeshError error = ::xatlas::AddMesh(atlas.pointer, entry.decl, atlas.entries.size());
|
||||
if (error != ::xatlas::AddMeshError::Success) {
|
||||
::xatlas::Destroy(atlas.pointer);
|
||||
UF_EXCEPTION("{}", ::xatlas::StringForEnum(error));
|
||||
}
|
||||
}
|
||||
|
||||
::xatlas::Generate(atlas.pointer, chartOptions, packOptions);
|
||||
|
||||
for ( auto i = 0; i < atlas.pointer->meshCount; i++ ) {
|
||||
auto& xmesh = atlas.pointer->meshes[i];
|
||||
auto& entry = atlas.entries[i];
|
||||
auto& name = graph.meshes[entry.index];
|
||||
auto& mesh = /*graph.storage*/storage.meshes[name];
|
||||
|
||||
// draw commands
|
||||
if ( mesh.indirect.count ) {
|
||||
auto vertexInput = mesh.remapVertexInput( entry.commandID );
|
||||
for ( auto j = 0; j < xmesh.vertexCount; ++j ) {
|
||||
auto& vertex = xmesh.vertexArray[j];
|
||||
auto ref = vertex.xref;
|
||||
|
||||
for ( auto k = 0; k < vertexInput.attributes.size(); ++k ) {
|
||||
auto dstAttribute = vertexInput.attributes[k];
|
||||
if ( dstAttribute.descriptor.name != "st" ) continue;
|
||||
pod::Vector2f& st = *(pod::Vector2f*) ( static_cast<uint8_t*>(dstAttribute.pointer) + dstAttribute.stride * (ref + vertexInput.first) );
|
||||
st = pod::Vector2f{ vertex.uv[0] / atlas.pointer->width, vertex.uv[1] / atlas.pointer->height };;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ( auto j = 0; j < xmesh.vertexCount; ++j ) {
|
||||
auto& vertex = xmesh.vertexArray[j];
|
||||
auto ref = vertex.xref;
|
||||
|
||||
for ( auto k = 0; k < mesh.vertex.attributes.size(); ++k ) {
|
||||
auto dstAttribute = mesh.vertex.attributes[k];
|
||||
if ( dstAttribute.descriptor.name != "st" ) continue;
|
||||
pod::Vector2f& st = *(pod::Vector2f*) ( static_cast<uint8_t*>(dstAttribute.pointer) + dstAttribute.stride * (ref + mesh.vertex.first) );
|
||||
st = pod::Vector2f{ vertex.uv[0] / atlas.pointer->width, vertex.uv[1] / atlas.pointer->height };;
|
||||
}
|
||||
}
|
||||
}
|
||||
mesh.updateDescriptor();
|
||||
}
|
||||
|
||||
::xatlas::Destroy(atlas.pointer);
|
||||
++atlasCount;
|
||||
}
|
||||
#else
|
||||
// add mesh decls to mesh atlases
|
||||
// done after the fact since we'll know the total amount of meshes added
|
||||
for ( auto& pair : atlases ) {
|
||||
auto& atlas = pair.second;
|
||||
if ( !atlas.pointer ) atlas.pointer = ::xatlas::Create();
|
||||
|
||||
for ( auto& entry : atlas.entries ) {
|
||||
::xatlas::AddMeshError error = ::xatlas::AddMesh(atlas.pointer, entry.decl, atlas.entries.size());
|
||||
if (error != ::xatlas::AddMeshError::Success) {
|
||||
::xatlas::Destroy(atlas.pointer);
|
||||
UF_EXCEPTION("{}", ::xatlas::StringForEnum(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UF_XATLAS_UNWRAP_MULTITHREAD
|
||||
auto tasks = uf::thread::schedule(true);
|
||||
#else
|
||||
auto tasks = uf::thread::schedule(false);
|
||||
#endif
|
||||
for ( auto& pair : atlases ) {
|
||||
tasks.queue([&]{
|
||||
auto& atlas = pair.second;
|
||||
::xatlas::Generate(atlas.pointer, chartOptions, packOptions);
|
||||
});
|
||||
}
|
||||
uf::thread::execute( tasks );
|
||||
|
||||
// update vertices
|
||||
for ( auto& pair : atlases ) {
|
||||
auto& atlas = pair.second;
|
||||
|
||||
for ( auto i = 0; i < atlas.pointer->meshCount; i++ ) {
|
||||
auto& xmesh = atlas.pointer->meshes[i];
|
||||
auto& entry = atlas.entries[i];
|
||||
auto& name = graph.meshes[entry.index];
|
||||
auto& mesh = /*graph.storage*/storage.meshes[name];
|
||||
|
||||
// draw commands
|
||||
if ( mesh.indirect.count ) {
|
||||
auto vertexInput = mesh.remapVertexInput( entry.commandID );
|
||||
for ( auto j = 0; j < xmesh.vertexCount; ++j ) {
|
||||
auto& vertex = xmesh.vertexArray[j];
|
||||
auto ref = vertex.xref;
|
||||
|
||||
for ( auto k = 0; k < vertexInput.attributes.size(); ++k ) {
|
||||
auto dstAttribute = vertexInput.attributes[k];
|
||||
if ( dstAttribute.descriptor.name != "st" ) continue;
|
||||
pod::Vector2f& st = *(pod::Vector2f*) ( static_cast<uint8_t*>(dstAttribute.pointer) + dstAttribute.stride * (ref + vertexInput.first) );
|
||||
st = pod::Vector2f{ vertex.uv[0] / atlas.pointer->width, vertex.uv[1] / atlas.pointer->height };;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ( auto j = 0; j < xmesh.vertexCount; ++j ) {
|
||||
auto& vertex = xmesh.vertexArray[j];
|
||||
auto ref = vertex.xref;
|
||||
|
||||
for ( auto k = 0; k < mesh.vertex.attributes.size(); ++k ) {
|
||||
auto dstAttribute = mesh.vertex.attributes[k];
|
||||
if ( dstAttribute.descriptor.name != "st" ) continue;
|
||||
pod::Vector2f& st = *(pod::Vector2f*) ( static_cast<uint8_t*>(dstAttribute.pointer) + dstAttribute.stride * (ref + mesh.vertex.first) );
|
||||
st = pod::Vector2f{ vertex.uv[0] / atlas.pointer->width, vertex.uv[1] / atlas.pointer->height };;
|
||||
}
|
||||
}
|
||||
}
|
||||
mesh.updateDescriptor();
|
||||
}
|
||||
}
|
||||
// cleanup
|
||||
size_t atlasCount = 0;
|
||||
for ( auto& pair : atlases ) {
|
||||
auto& atlas = pair.second;
|
||||
::xatlas::Destroy(atlas.pointer);
|
||||
++atlasCount;
|
||||
}
|
||||
#endif
|
||||
return atlasCount;
|
||||
}
|
||||
#endif
|
||||
@ -6,6 +6,11 @@ namespace {
|
||||
|
||||
void queryFlatOverlaps( const pod::BVH& bvh, pod::BVH::pairs_t& outPairs );
|
||||
void queryFlatOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, pod::BVH::pairs_t& outPairs );
|
||||
|
||||
void postprocessPairs( pod::BVH::pairs_t& pairs ) {
|
||||
std::sort(pairs.begin(), pairs.end());
|
||||
pairs.erase(std::unique(pairs.begin(), pairs.end()), pairs.end());
|
||||
}
|
||||
}
|
||||
|
||||
// BVH
|
||||
@ -466,7 +471,7 @@ namespace {
|
||||
if ( bodyA == bodyB ) continue;
|
||||
if ( bodyA > bodyB ) std::swap( bodyA, bodyB );
|
||||
|
||||
pairs.emplace(bodyA, bodyB);
|
||||
pairs.emplace_back(bodyA, bodyB);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -496,17 +501,26 @@ namespace {
|
||||
if ( bodyA == bodyB ) continue;
|
||||
if ( bodyA > bodyB ) std::swap( bodyA, bodyB );
|
||||
|
||||
pairs.emplace(bodyA, bodyB);
|
||||
pairs.emplace_back(bodyA, bodyB);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( nodeA.getCount() == 0 ) {
|
||||
if ( nodeA.getCount() == 0 && nodeB.getCount() == 0 ) {
|
||||
if ( ::aabbSurfaceArea(bvhA.bounds[nodeAID]) > ::aabbSurfaceArea(bvhB.bounds[nodeBID]) ) {
|
||||
::traverseNodePair( bvhA, nodeA.left, bvhB, nodeBID, pairs );
|
||||
::traverseNodePair( bvhA, nodeA.right, bvhB, nodeBID, pairs );
|
||||
} else {
|
||||
::traverseNodePair( bvhA, nodeAID, bvhB, nodeB.left, pairs );
|
||||
::traverseNodePair( bvhA, nodeAID, bvhB, nodeB.right, pairs );
|
||||
}
|
||||
}
|
||||
else if ( nodeA.getCount() == 0 ) {
|
||||
::traverseNodePair( bvhA, nodeA.left, bvhB, nodeBID, pairs );
|
||||
::traverseNodePair( bvhA, nodeA.right, bvhB, nodeBID, pairs );
|
||||
}
|
||||
if ( nodeB.getCount() == 0 ) {
|
||||
else if ( nodeB.getCount() == 0 ) {
|
||||
::traverseNodePair( bvhA, nodeAID, bvhB, nodeB.left, pairs );
|
||||
::traverseNodePair( bvhA, nodeAID, bvhB, nodeB.right, pairs );
|
||||
}
|
||||
@ -524,7 +538,7 @@ namespace {
|
||||
if ( bodyA == bodyB ) continue;
|
||||
if ( bodyA > bodyB ) std::swap( bodyA, bodyB );
|
||||
|
||||
pairs.emplace(bodyA, bodyB);
|
||||
pairs.emplace_back(bodyA, bodyB);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -539,6 +553,8 @@ namespace {
|
||||
if ( bvh.nodes.empty() ) return;
|
||||
outPairs.reserve(uf::physics::impl::settings.reserveCount);
|
||||
::traverseBVH( bvh, 0, outPairs );
|
||||
|
||||
::postprocessPairs( outPairs );
|
||||
}
|
||||
|
||||
void queryOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, pod::BVH::pairs_t& outPairs ) {
|
||||
@ -547,6 +563,8 @@ namespace {
|
||||
if ( bvhA.nodes.empty() || bvhB.nodes.empty() ) return;
|
||||
outPairs.reserve(uf::physics::impl::settings.reserveCount);
|
||||
::traverseNodePair(bvhA, 0, bvhB, 0, outPairs);
|
||||
|
||||
::postprocessPairs( outPairs );
|
||||
}
|
||||
}
|
||||
|
||||
@ -681,13 +699,15 @@ namespace {
|
||||
if ( indexA == indexB ) continue;
|
||||
if ( indexA > indexB ) std::swap(indexA, indexB);
|
||||
|
||||
outPairs.emplace( indexA, indexB );
|
||||
outPairs.emplace_back( indexA, indexB );
|
||||
}
|
||||
}
|
||||
}
|
||||
++b;
|
||||
}
|
||||
}
|
||||
|
||||
::postprocessPairs( outPairs );
|
||||
}
|
||||
void queryFlatOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, pod::BVH::pairs_t& outPairs ) {
|
||||
auto& nodesA = bvhA.flattened;
|
||||
@ -701,34 +721,56 @@ namespace {
|
||||
if ( nodesA.empty() || nodesB.empty() ) return;
|
||||
outPairs.reserve(uf::physics::impl::settings.reserveCount);
|
||||
|
||||
for ( pod::BVH::index_t a = 0; a < nodesA.size(); ++a ) {
|
||||
const auto& nodeA = nodesA[a];
|
||||
if ( nodeA.getCount() <= 0 || nodeA.isAsleep() ) continue;
|
||||
static thread_local uf::stl::vector<std::pair<pod::BVH::index_t, pod::BVH::index_t>> stack;
|
||||
stack.clear();
|
||||
stack.emplace_back(0, 0);
|
||||
|
||||
const auto& bA = boundsA[a];
|
||||
while ( !stack.empty() ) {
|
||||
auto [a, b] = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
pod::BVH::index_t b = 0;
|
||||
while ( b < nodesB.size() ) {
|
||||
const auto& nodeB = nodesB[b];
|
||||
const auto& nodeA = bvhA.flattened[a];
|
||||
const auto& nodeB = bvhB.flattened[b];
|
||||
|
||||
if ( nodeB.isAsleep() || !::aabbOverlap(bA, boundsB[b]) ) {
|
||||
b = nodeB.skipIndex;
|
||||
continue;
|
||||
}
|
||||
if ( nodeA.isAsleep() && nodeB.isAsleep() ) continue;
|
||||
if ( !::aabbOverlap( bvhA.flatBounds[a], bvhB.flatBounds[b] ) ) continue;
|
||||
|
||||
if ( nodeB.getCount() > 0 ) {
|
||||
for ( pod::BVH::index_t ia = 0; ia < nodeA.getCount(); ++ia ) {
|
||||
for ( pod::BVH::index_t ib = 0; ib < nodeB.getCount(); ++ib ) {
|
||||
auto indexA = indicesA[nodeA.start + ia];
|
||||
auto indexB = indicesB[nodeB.start + ib];
|
||||
bool isLeafA = (nodeA.getCount() > 0);
|
||||
bool isLeafB = (nodeB.getCount() > 0);
|
||||
|
||||
outPairs.emplace(indexA, indexB);
|
||||
}
|
||||
if ( isLeafA && isLeafB ) {
|
||||
for ( pod::BVH::index_t ia = 0; ia < nodeA.getCount(); ++ia ) {
|
||||
for ( pod::BVH::index_t ib = 0; ib < nodeB.getCount(); ++ib ) {
|
||||
auto indexA = bvhA.indices[nodeA.start + ia];
|
||||
auto indexB = bvhB.indices[nodeB.start + ib];
|
||||
|
||||
// if ( indexA > indexB ) std::swap( indexA, indexB );
|
||||
outPairs.emplace_back(indexA, indexB);
|
||||
}
|
||||
}
|
||||
++b;
|
||||
}
|
||||
else if ( isLeafA ) {
|
||||
pod::BVH::index_t rightB = bvhB.flattened[b + 1].skipIndex;
|
||||
stack.emplace_back(a, b + 1);
|
||||
stack.emplace_back(a, rightB);
|
||||
}
|
||||
else if ( isLeafB ) {
|
||||
pod::BVH::index_t rightA = bvhA.flattened[a + 1].skipIndex;
|
||||
stack.emplace_back(a + 1, b);
|
||||
stack.emplace_back(rightA, b);
|
||||
}
|
||||
else {
|
||||
pod::BVH::index_t rightA = bvhA.flattened[a + 1].skipIndex;
|
||||
pod::BVH::index_t rightB = bvhB.flattened[b + 1].skipIndex;
|
||||
|
||||
stack.emplace_back(a + 1, b + 1);
|
||||
stack.emplace_back(a + 1, rightB);
|
||||
stack.emplace_back(rightA, b + 1);
|
||||
stack.emplace_back(rightA, rightB);
|
||||
}
|
||||
}
|
||||
|
||||
::postprocessPairs( outPairs );
|
||||
}
|
||||
|
||||
void queryFlatBVH( const pod::BVH& bvh, const pod::AABB& bounds, uf::stl::vector<pod::BVH::index_t>& outIndices ) {
|
||||
@ -836,10 +878,15 @@ namespace {
|
||||
|
||||
pod::BVH::index_t root = unionizer.find(i);
|
||||
|
||||
auto [ it, inserted ] = rootToIsland.try_emplace( root, (pod::BVH::index_t) islands.size());
|
||||
if ( inserted ) islands.emplace_back();
|
||||
|
||||
/*
|
||||
if ( rootToIsland.find(root) == rootToIsland.end() ) {
|
||||
rootToIsland[root] = (pod::BVH::index_t) islands.size();
|
||||
islands.emplace_back();
|
||||
}
|
||||
*/
|
||||
|
||||
pod::BVH::index_t islandID = rootToIsland[root];
|
||||
islands[islandID].indices.emplace_back( i );
|
||||
@ -857,7 +904,8 @@ namespace {
|
||||
pod::BVH::index_t root = unionizer.find(a);
|
||||
if ( rootToIsland.find(root) != rootToIsland.end() ) {
|
||||
pod::BVH::index_t islandID = rootToIsland[root];
|
||||
islands[islandID].pairs.emplace(a, b);
|
||||
|
||||
islands[islandID].pairs.emplace_back(a, b);
|
||||
|
||||
if ( bodies[a]->activity.awake || bodies[b]->activity.awake ) {
|
||||
::wakeBody( *bodies[dynamicIndex] );
|
||||
|
||||
@ -147,8 +147,10 @@ namespace {
|
||||
}
|
||||
|
||||
void mergeContacts( pod::Manifold& manifold ) {
|
||||
uf::stl::vector<pod::Contact> result;
|
||||
static thread_local uf::stl::vector<pod::Contact> result;
|
||||
result.clear();
|
||||
result.reserve(4);
|
||||
|
||||
for ( auto& c : manifold.points ) {
|
||||
bool merged = false;
|
||||
for ( auto& r : result ) {
|
||||
@ -163,8 +165,6 @@ namespace {
|
||||
if ( !merged ) result.emplace_back( c );
|
||||
}
|
||||
|
||||
// UF_MSG_DEBUG("Merged {} => {} contacts", manifold.points.size(), result.size());
|
||||
|
||||
manifold.points = result;
|
||||
}
|
||||
|
||||
|
||||
@ -126,17 +126,26 @@ namespace {
|
||||
// compute overlaps between one BVH and another BVH
|
||||
static thread_local pod::BVH::pairs_t pairs;
|
||||
pairs.clear();
|
||||
|
||||
|
||||
//UF_TIMER_MULTITRACE_START("Colliding {} ({} indices) <=> {} ({} indices)", a.object->getName(), bvhA.indices.size(), b.object->getName(), bvhB.indices.size());
|
||||
//UF_TIMER_MULTITRACE("Querying overlaps...");
|
||||
::queryOverlaps( bvhA, bvhB, pairs );
|
||||
//UF_TIMER_MULTITRACE("Queried overlaps.");
|
||||
|
||||
bool hit = false;
|
||||
// do collision per triangle
|
||||
//UF_TIMER_MULTITRACE("Colliding triangles (pairs={})...", pairs.size());
|
||||
for (auto [idA, idB] : pairs ) {
|
||||
auto tA = ::fetchTriangle( meshA, idA, a ); // transform triangles to world space
|
||||
auto tB = ::fetchTriangle( meshB, idB, b );
|
||||
|
||||
if ( !::triangleTriangle( tA, tB, manifold, eps ) ) continue;
|
||||
bool collides = ::triangleTriangle( tA, tB, manifold, eps );
|
||||
if ( !collides ) continue;
|
||||
hit = true;
|
||||
}
|
||||
//UF_TIMER_MULTITRACE("Collided triangles.");
|
||||
//UF_TIMER_MULTITRACE_END("Collided mesh.");
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,56 @@ namespace {
|
||||
std::min<uint32_t>(std::max<int>(0, (int)rel.z), divs.z - 1)
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
// to-do: just use the physics copy of AABB
|
||||
inline bool intersectTriangleAABB(const pod::Vector3f& v0, const pod::Vector3f& v1, const pod::Vector3f& v2, const pod::Vector3f& aabbMin, const pod::Vector3f& aabbMax) {
|
||||
pod::Vector3f boxCenter = (aabbMin + aabbMax) * 0.5f;
|
||||
pod::Vector3f boxExtents = (aabbMax - aabbMin) * 0.5f;
|
||||
|
||||
pod::Vector3f tv0 = v0 - boxCenter;
|
||||
pod::Vector3f tv1 = v1 - boxCenter;
|
||||
pod::Vector3f tv2 = v2 - boxCenter;
|
||||
|
||||
pod::Vector3f e0 = tv1 - tv0;
|
||||
pod::Vector3f e1 = tv2 - tv1;
|
||||
pod::Vector3f e2 = tv0 - tv2;
|
||||
|
||||
pod::Vector3f triMin = uf::vector::min(uf::vector::min(tv0, tv1), tv2);
|
||||
pod::Vector3f triMax = uf::vector::max(uf::vector::max(tv0, tv1), tv2);
|
||||
if (triMin.x > boxExtents.x || triMax.x < -boxExtents.x) return false;
|
||||
if (triMin.y > boxExtents.y || triMax.y < -boxExtents.y) return false;
|
||||
if (triMin.z > boxExtents.z || triMax.z < -boxExtents.z) return false;
|
||||
|
||||
pod::Vector3f normal = uf::vector::cross(e0, e1);
|
||||
float planeD = uf::vector::dot(normal, tv0);
|
||||
float r = boxExtents.x * std::abs(normal.x) + boxExtents.y * std::abs(normal.y) + boxExtents.z * std::abs(normal.z);
|
||||
if (std::abs(planeD) > r) return false;
|
||||
|
||||
float p0, p1, p2, rad;
|
||||
auto testAxis = [&](const pod::Vector3f& axis) {
|
||||
p0 = uf::vector::dot(tv0, axis);
|
||||
p1 = uf::vector::dot(tv1, axis);
|
||||
p2 = uf::vector::dot(tv2, axis);
|
||||
rad = boxExtents.x * std::abs(axis.x) + boxExtents.y * std::abs(axis.y) + boxExtents.z * std::abs(axis.z);
|
||||
return std::min({p0, p1, p2}) > rad || std::max({p0, p1, p2}) < -rad;
|
||||
};
|
||||
|
||||
if (testAxis({0, -e0.z, e0.y})) return false;
|
||||
if (testAxis({0, -e1.z, e1.y})) return false;
|
||||
if (testAxis({0, -e2.z, e2.y})) return false;
|
||||
|
||||
if (testAxis({e0.z, 0, -e0.x})) return false;
|
||||
if (testAxis({e1.z, 0, -e1.x})) return false;
|
||||
if (testAxis({e2.z, 0, -e2.x})) return false;
|
||||
|
||||
if (testAxis({-e0.y, e0.x, 0})) return false;
|
||||
if (testAxis({-e1.y, e1.x, 0})) return false;
|
||||
if (testAxis({-e2.y, e2.x, 0})) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uf::meshgrid::print( const uf::meshgrid::Grid& grid ) {
|
||||
@ -54,7 +104,6 @@ void uf::meshgrid::calculate( uf::meshgrid::Grid& grid, const pod::Vector3ui& di
|
||||
return calculate( grid, padding );
|
||||
}
|
||||
void uf::meshgrid::calculate( uf::meshgrid::Grid& grid, float padding ){
|
||||
// Pad bounding box
|
||||
grid.extents.min -= pod::Vector3f{ padding, padding, padding };
|
||||
grid.extents.max += pod::Vector3f{ padding, padding, padding };
|
||||
|
||||
@ -97,7 +146,6 @@ void uf::meshgrid::partition(uf::meshgrid::Grid& grid,
|
||||
|
||||
for (size_t i = 0; i < iCount; i += 3) {
|
||||
Triangle tri{};
|
||||
// Read indices
|
||||
auto readIndex = [&](size_t offs) -> uint32_t {
|
||||
switch (iStride) {
|
||||
case 1: return *(const uint8_t*)(idxBase + iStride * (iFirst + offs));
|
||||
@ -109,24 +157,26 @@ void uf::meshgrid::partition(uf::meshgrid::Grid& grid,
|
||||
tri.indices[1] = readIndex(i+1);
|
||||
tri.indices[2] = readIndex(i+2);
|
||||
|
||||
// Read vertices
|
||||
for (int v = 0; v < 3; v++) {
|
||||
tri.verts[v] = *(const pod::Vector3f*)(vtxBase + pStride * (pFirst + tri.indices[v]));
|
||||
}
|
||||
|
||||
// Compute bounding cell range
|
||||
pod::Vector3f triMin = uf::vector::min(uf::vector::min(tri.verts[0], tri.verts[1]), tri.verts[2]);
|
||||
pod::Vector3f triMax = uf::vector::max(uf::vector::max(tri.verts[0], tri.verts[1]), tri.verts[2]);
|
||||
|
||||
pod::Vector3ui minCell = ::cellCoords(triMin, grid.extents.min, grid.extents.piece, grid.divisions);
|
||||
pod::Vector3ui maxCell = ::cellCoords(triMax, grid.extents.min, grid.extents.piece, grid.divisions);
|
||||
|
||||
// Assign triangle to all overlapping cells
|
||||
for (uint32_t z = minCell.z; z <= maxCell.z; z++) {
|
||||
for (uint32_t y = minCell.y; y <= maxCell.y; y++) {
|
||||
for (uint32_t x = minCell.x; x <= maxCell.x; x++) {
|
||||
pod::Vector3ui cid{x,y,z};
|
||||
auto& node = grid.nodes[cid];
|
||||
|
||||
if ( !intersectTriangleAABB(tri.verts[0], tri.verts[1], tri.verts[2], node.extents.min, node.extents.max) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& meshlet = node.meshlets[primitive.instance.primitiveID];
|
||||
meshlet.primitive = primitive;
|
||||
|
||||
|
||||
@ -363,7 +363,7 @@ std::string uf::Mesh::printIndirects( bool full ) const {
|
||||
return str.str();
|
||||
}
|
||||
|
||||
uf::Mesh::View uf::Mesh::makeView( const uf::stl::vector<uf::stl::string>& wanted ) const {
|
||||
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;
|
||||
@ -378,15 +378,15 @@ uf::Mesh::View uf::Mesh::makeView( const uf::stl::vector<uf::stl::string>& wante
|
||||
}
|
||||
|
||||
if ( !index.attributes.empty() ) {
|
||||
view.attributes["index"] = { index.attributes.front() };
|
||||
view.attributes["index"] = { index.attributes[lod] };
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
uf::Mesh::View uf::Mesh::makeView( size_t i, const uf::stl::vector<uf::stl::string>& wanted ) const {
|
||||
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;
|
||||
view.vertex = remapVertexInput(i);
|
||||
view.index = remapIndexInput(i);
|
||||
view.vertex = remapVertexInput(i, lod);
|
||||
view.index = remapIndexInput(i, lod);
|
||||
view.indirectIndex = i;
|
||||
|
||||
if ( wanted.size() ) {
|
||||
@ -399,47 +399,47 @@ uf::Mesh::View uf::Mesh::makeView( size_t i, const uf::stl::vector<uf::stl::stri
|
||||
}
|
||||
|
||||
if ( !index.attributes.empty() ) {
|
||||
view.attributes["index"] = { index.attributes.front() };
|
||||
view.attributes["index"] = { index.attributes[lod] };
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
uf::stl::vector<uf::Mesh::View> uf::Mesh::makeViews( const uf::stl::vector<uf::stl::string>& wanted ) const {
|
||||
uf::stl::vector<uf::Mesh::View> uf::Mesh::makeViews( const uf::stl::vector<uf::stl::string>& wanted, size_t lod ) const {
|
||||
uf::stl::vector<uf::Mesh::View> views;
|
||||
if ( indirect.count > 0 ) {
|
||||
for ( auto i = 0; i < indirect.count; i++ ) views.emplace_back(makeView(i, wanted));
|
||||
for ( auto i = 0; i < indirect.count; i++ ) views.emplace_back(makeView(i, wanted, lod));
|
||||
} else {
|
||||
views.emplace_back( makeView(wanted) );
|
||||
views.emplace_back( makeView(wanted, lod) );
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
uf::Mesh::Input uf::Mesh::remapInput( const uf::Mesh::Input& input, size_t i ) const {
|
||||
uf::Mesh::Input uf::Mesh::remapInput( const uf::Mesh::Input& input, size_t i, size_t lod ) const {
|
||||
uf::Mesh::Input res = input;
|
||||
UF_ASSERT( &input == &vertex || &input == &index );
|
||||
UF_ASSERT( i < indirect.count );
|
||||
|
||||
const auto& drawCommand = ((const pod::DrawCommand*) getBuffer(indirect).data())[i];
|
||||
const auto& drawCommand = ((const pod::DrawCommand*) getBuffer(indirect, lod).data())[i];
|
||||
res.first = &input == &vertex ? drawCommand.vertexID : drawCommand.indexID;
|
||||
res.count = &input == &vertex ? drawCommand.vertices : drawCommand.indices;
|
||||
|
||||
return res;
|
||||
}
|
||||
uf::Mesh::Input uf::Mesh::remapVertexInput( size_t i ) const {
|
||||
uf::Mesh::Input uf::Mesh::remapVertexInput( size_t i, size_t lod ) const {
|
||||
uf::Mesh::Input res = vertex;
|
||||
UF_ASSERT( i < indirect.count );
|
||||
|
||||
const auto& drawCommand = ((const pod::DrawCommand*) getBuffer(indirect).data())[i];
|
||||
const auto& drawCommand = ((const pod::DrawCommand*) getBuffer(indirect, lod).data())[i];
|
||||
res.first = drawCommand.vertexID;
|
||||
res.count = drawCommand.vertices;
|
||||
|
||||
return res;
|
||||
}
|
||||
uf::Mesh::Input uf::Mesh::remapIndexInput( size_t i ) const {
|
||||
uf::Mesh::Input uf::Mesh::remapIndexInput( size_t i, size_t lod ) const {
|
||||
uf::Mesh::Input res = index;
|
||||
UF_ASSERT( i < indirect.count );
|
||||
|
||||
const auto& drawCommand = ((const pod::DrawCommand*) getBuffer(indirect).data())[i];
|
||||
const auto& drawCommand = ((const pod::DrawCommand*) getBuffer(indirect, lod).data())[i];
|
||||
res.first = drawCommand.indexID;
|
||||
res.count = drawCommand.indices;
|
||||
|
||||
@ -482,7 +482,13 @@ void uf::Mesh::_updateDescriptor( uf::Mesh::Input& input ) {
|
||||
auto& buffer = buffers[interleaved ? input.interleaved : attribute.buffer];
|
||||
attribute.length = buffer.size();
|
||||
attribute.pointer = buffer.data() + attribute.offset;
|
||||
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;
|
||||
}
|
||||
@ -588,8 +594,8 @@ void uf::Mesh::_insertIs( uf::Mesh::Input& dstInput, const uf::Mesh& mesh, const
|
||||
// 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 );
|
||||
auto& dst = getBuffer( dstInput );
|
||||
auto& src = mesh.getBuffer( srcInput, i );
|
||||
auto& dst = getBuffer( dstInput, i );
|
||||
|
||||
dst.insert( dst.end(), src.begin(), src.end() );
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user