@@ -760,6 +760,118 @@ void encodeIndexSequence(const std::vector<unsigned int>& data, size_t vertex_co
760760 (double (result.size () * 4 ) / 1e9 ) / (end - middle));
761761}
762762
763+ void encodeMeshlets (const Mesh& mesh, size_t max_vertices, size_t max_triangles, bool reorder = true )
764+ {
765+ size_t max_meshlets = meshopt_buildMeshletsBound (mesh.indices .size (), max_vertices, max_triangles);
766+ std::vector<meshopt_Meshlet> meshlets (max_meshlets);
767+ std::vector<unsigned int > meshlet_vertices (mesh.indices .size ());
768+ std::vector<unsigned char > meshlet_triangles (mesh.indices .size ());
769+
770+ meshlets.resize (meshopt_buildMeshlets (&meshlets[0 ], &meshlet_vertices[0 ], &meshlet_triangles[0 ], &mesh.indices [0 ], mesh.indices .size (), &mesh.vertices [0 ].px , mesh.vertices .size (), sizeof (Vertex), max_vertices, max_triangles, 0 .f ));
771+
772+ if (meshlets.size ())
773+ {
774+ const meshopt_Meshlet& last = meshlets.back ();
775+
776+ // this is an example of how to trim the vertex/triangle arrays when copying data out to GPU storage
777+ meshlet_vertices.resize (last.vertex_offset + last.vertex_count );
778+ meshlet_triangles.resize (last.triangle_offset + last.triangle_count * 3 );
779+
780+ // TODO: over-allocate meshlet_vertices to multiple of 3 to make meshopt_optimizeVertexFetch below work without assertions
781+ meshlet_vertices.resize ((meshlet_vertices.size () + 2 ) / 3 * 3 );
782+ }
783+
784+ std::vector<unsigned char > cbuf (meshopt_encodeMeshletBound (max_vertices, max_triangles));
785+
786+ // optimize each meshlet for locality; this is important for performance, and critical for good compression
787+ for (size_t i = 0 ; i < meshlets.size (); ++i)
788+ meshopt_optimizeMeshlet (&meshlet_vertices[meshlets[i].vertex_offset ], &meshlet_triangles[meshlets[i].triangle_offset ], meshlets[i].triangle_count , meshlets[i].vertex_count );
789+
790+ // optimize the order of vertex references within each meshlet and globally; this is valuable for access locality and critical for compression of vertex references
791+ // note that this reorders the vertex buffer too, so if a traditional index buffer is required it would need to be reconstructed from the meshlet data for optimal locality
792+ std::vector<Vertex> vertices = mesh.vertices ;
793+ if (reorder)
794+ meshopt_optimizeVertexFetch (&vertices[0 ], &meshlet_vertices[0 ], meshlet_vertices.size (), &mesh.vertices [0 ], mesh.vertices .size (), sizeof (Vertex));
795+
796+ size_t mbst = 0 ;
797+
798+ std::vector<unsigned char > packed;
799+
800+ for (size_t i = 0 ; i < meshlets.size (); ++i)
801+ {
802+ const meshopt_Meshlet& meshlet = meshlets[i];
803+
804+ size_t mbs = meshopt_encodeMeshlet (&cbuf[0 ], cbuf.size (), &meshlet_vertices[meshlet.vertex_offset ], meshlet.vertex_count , &meshlet_triangles[meshlet.triangle_offset ], meshlet.triangle_count );
805+ assert (mbs > 0 );
806+
807+ packed.push_back ((unsigned char )meshlet.vertex_count );
808+ packed.push_back ((unsigned char )meshlet.triangle_count );
809+ packed.push_back ((unsigned char )(mbs & 0xff ));
810+ packed.push_back ((unsigned char )((mbs >> 8 ) & 0xff ));
811+ packed.insert (packed.end (), &cbuf[0 ], &cbuf[mbs]);
812+
813+ unsigned int rv[256 ];
814+ unsigned int rt[256 ];
815+ int rc = meshopt_decodeMeshlet (rv, meshlet.vertex_count , rt, meshlet.triangle_count , &cbuf[0 ], mbs);
816+ assert (rc == 0 );
817+
818+ for (size_t j = 0 ; j < meshlet.vertex_count ; ++j)
819+ assert (rv[j] == meshlet_vertices[meshlet.vertex_offset + j]);
820+
821+ for (size_t j = 0 ; j < meshlet.triangle_count ; ++j)
822+ {
823+ unsigned int a = meshlet_triangles[meshlet.triangle_offset + j * 3 + 0 ];
824+ unsigned int b = meshlet_triangles[meshlet.triangle_offset + j * 3 + 1 ];
825+ unsigned int c = meshlet_triangles[meshlet.triangle_offset + j * 3 + 2 ];
826+
827+ unsigned int abc = (a << 0 ) | (b << 8 ) | (c << 16 );
828+ unsigned int bca = (b << 0 ) | (c << 8 ) | (a << 16 );
829+ unsigned int cba = (c << 0 ) | (a << 8 ) | (b << 16 );
830+
831+ assert (rt[j] == abc || rt[j] == bca || rt[j] == cba);
832+ }
833+
834+ mbst += mbs;
835+ }
836+
837+ size_t mbc = compress (packed);
838+
839+ printf (" MeshletCodec (%d/%d): %d meshlets, %d bytes/meshlet; %d bytes, %.1f bits/triangle\n " ,
840+ int (max_vertices), int (max_triangles),
841+ int (meshlets.size ()),
842+ int (mbst / meshlets.size ()),
843+ int (mbst), double (mbst * 8 ) / double (mesh.indices .size () / 3 ));
844+ printf (" MeshletCodec (%d/%d, packed): %d bytes/meshlet, %.1f bits/triangle; post-deflate: %d bytes/meshlet, %.1f bits/triangle)\n " ,
845+ int (max_vertices), int (max_triangles),
846+ int (packed.size () / meshlets.size ()), double (packed.size () * 8 ) / double (mesh.indices .size () / 3 ),
847+ int (mbc / meshlets.size ()), double (mbc * 8 ) / double (mesh.indices .size () / 3 ));
848+
849+ #if !TRACE
850+ double mbtime = 0 ;
851+
852+ for (int i = 0 ; i < 10 ; ++i)
853+ {
854+ unsigned int rv[256 ];
855+ unsigned int rt[256 ];
856+ double t0 = timestamp ();
857+ unsigned char * p = &packed[0 ];
858+ for (size_t j = 0 ; j < meshlets.size (); ++j)
859+ {
860+ size_t size = p[2 ] | (p[3 ] << 8 );
861+ meshopt_decodeMeshlet (rv, p[0 ], rt, p[1 ], p + 4 , size);
862+ p += 4 + size;
863+ }
864+ double t1 = timestamp ();
865+
866+ mbtime = (mbtime == 0 || t1 - t0 < mbtime) ? (t1 - t0) : mbtime;
867+ }
868+
869+ printf (" MeshletCodec (%d/%d, packed): decode time %.3f msec, %.3fB tri/sec, %.1f ns/meshlet\n " ,
870+ int (max_vertices), int (max_triangles),
871+ mbtime * 1000 , double (mesh.indices .size () / 3 ) / 1e9 / mbtime, mbtime * 1e9 / double (meshlets.size ()));
872+ #endif
873+ }
874+
763875template <typename PV>
764876void packVertex (const Mesh& mesh, const char * pvn)
765877{
@@ -1425,6 +1537,8 @@ void process(const char* path)
14251537 encodeVertex<PackedVertex>(copy, " " );
14261538 encodeVertex<PackedVertexOct>(copy, " O" );
14271539
1540+ encodeMeshlets (mesh, 64 , 96 );
1541+
14281542 simplify (mesh);
14291543 simplify (mesh, 0 .1f , meshopt_SimplifyPrune);
14301544 simplifyAttr (mesh);
@@ -1453,7 +1567,9 @@ void processDev(const char* path)
14531567 if (!loadMesh (mesh, path))
14541568 return ;
14551569
1456- simplifyUpdate (mesh, 0 .1f , meshopt_SimplifyPrune | meshopt_SimplifyPermissive);
1570+ encodeMeshlets (mesh, 32 , 48 );
1571+ encodeMeshlets (mesh, 64 , 64 );
1572+ encodeMeshlets (mesh, 64 , 96 );
14571573}
14581574
14591575void processNanite (const char * path)
0 commit comments