All checks were successful
Ubuntu Cross to Win64 / Cross Compile with ming64 (1.4.0, ubuntu-latest) (push) Successful in 2m13s
291 lines
13 KiB
C++
291 lines
13 KiB
C++
#include <meshoptimizer.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4530)
|
|
#endif
|
|
|
|
#define TINYOBJLOADER_IMPLEMENTATION
|
|
#include <tiny_obj_loader.h>
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#include "meshlet_generator.hpp"
|
|
#include <runtime/mem_arena.h>
|
|
|
|
meshlet_generator::meshlet_generator()
|
|
: m_meshes(nullptr), m_num_meshes(0u), m_meshlets(nullptr), m_num_meshlets(0u) {
|
|
}
|
|
|
|
meshlet_generator::~meshlet_generator() {
|
|
Release();
|
|
}
|
|
|
|
void meshlet_generator::Release() {
|
|
for (uint32_t i = 0; i < m_num_meshes; ++i) {
|
|
delete m_meshes[i].vertices;
|
|
delete m_meshes[i].indices;
|
|
}
|
|
delete m_meshes;
|
|
m_meshes = nullptr;
|
|
m_num_meshes = 0;
|
|
|
|
for (uint32_t i = 0; i < m_num_meshlets; ++i) {
|
|
delete m_meshlets[i].indices;
|
|
delete m_meshlets[i].vertices;
|
|
}
|
|
delete m_meshlets;
|
|
m_meshlets = nullptr;
|
|
m_num_meshlets = 0;
|
|
}
|
|
|
|
rt_result meshlet_generator::LoadObj(const char *path) {
|
|
tinyobj::ObjReaderConfig config;
|
|
config.mtl_search_path = "./";
|
|
config.triangulate = true;
|
|
tinyobj::ObjReader reader;
|
|
|
|
if (!reader.ParseFromFile(path, config)) {
|
|
if (!reader.Error().empty()) {
|
|
rtReportError("OBJ", "Error: %s", reader.Error().c_str());
|
|
}
|
|
return RT_UNKNOWN_ERROR;
|
|
}
|
|
|
|
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
|
|
|
|
auto &attrib = reader.GetAttrib();
|
|
auto &shapes = reader.GetShapes();
|
|
// auto &materials = reader.GetMaterials();
|
|
|
|
mesh_data *meshes = new mesh_data[shapes.size()];
|
|
RT_VERIFY(meshes);
|
|
|
|
for (size_t shape_idx = 0; shape_idx < shapes.size(); ++shape_idx) {
|
|
|
|
rt_temp_arena rewind = rtBeginTempArena(temp.arena);
|
|
|
|
size_t num_faces = shapes[shape_idx].mesh.num_face_vertices.size();
|
|
vertex *unindexed_vertices = RT_ARENA_PUSH_ARRAY(temp.arena, vertex, num_faces * 3);
|
|
|
|
size_t index_offset = 0;
|
|
for (size_t face_idx = 0; face_idx < shapes[shape_idx].mesh.num_face_vertices.size();
|
|
++face_idx) {
|
|
size_t fv = shapes[shape_idx].mesh.num_face_vertices[face_idx];
|
|
for (size_t vert_idx = 0; vert_idx < fv; ++vert_idx) {
|
|
tinyobj::index_t idx = shapes[shape_idx].mesh.indices[index_offset + vert_idx];
|
|
tinyobj::real_t vx = attrib.vertices[3 * idx.vertex_index];
|
|
tinyobj::real_t vy = attrib.vertices[3 * idx.vertex_index + 1];
|
|
tinyobj::real_t vz = attrib.vertices[3 * idx.vertex_index + 2];
|
|
|
|
unindexed_vertices[index_offset + vert_idx].vx = vx;
|
|
unindexed_vertices[index_offset + vert_idx].vy = vy;
|
|
unindexed_vertices[index_offset + vert_idx].vz = vz;
|
|
}
|
|
|
|
index_offset += fv;
|
|
}
|
|
|
|
// Generate a non-redundant index buffer
|
|
size_t num_indices = num_faces * 3;
|
|
unsigned int *remap = RT_ARENA_PUSH_ARRAY(temp.arena, unsigned int, num_indices);
|
|
size_t num_vertices = meshopt_generateVertexRemap(remap,
|
|
nullptr,
|
|
num_indices,
|
|
unindexed_vertices,
|
|
num_faces * 3,
|
|
sizeof(vertex));
|
|
|
|
meshes[shape_idx].num_indices = (uint32_t)num_indices;
|
|
meshes[shape_idx].indices = new uint32_t[num_indices];
|
|
meshes[shape_idx].num_vertices = (uint32_t)num_vertices;
|
|
meshes[shape_idx].vertices = new vertex[num_vertices];
|
|
|
|
meshopt_remapIndexBuffer(meshes[shape_idx].indices, nullptr, num_indices, remap);
|
|
meshopt_remapVertexBuffer(meshes[shape_idx].vertices,
|
|
unindexed_vertices,
|
|
num_faces * 3,
|
|
sizeof(vertex),
|
|
remap);
|
|
|
|
rtEndTempArena(rewind);
|
|
}
|
|
|
|
m_num_meshes = (uint32_t)shapes.size();
|
|
m_meshes = meshes;
|
|
|
|
return RT_SUCCESS;
|
|
}
|
|
|
|
rt_result meshlet_generator::RunFlat(uint32_t mesh_idx) {
|
|
const float cone_weight = 0.5f;
|
|
|
|
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
|
|
|
|
size_t max_meshlets = meshopt_buildMeshletsBound(m_meshes[mesh_idx].num_indices,
|
|
MESHLET_VERTICES,
|
|
MESHLET_TRIANGLES);
|
|
meshopt_Meshlet *meshlets = RT_ARENA_PUSH_ARRAY(temp.arena, meshopt_Meshlet, max_meshlets);
|
|
unsigned int *meshlet_vertices =
|
|
RT_ARENA_PUSH_ARRAY(temp.arena, unsigned int, max_meshlets *MESHLET_VERTICES);
|
|
unsigned char *meshlet_triangles =
|
|
RT_ARENA_PUSH_ARRAY(temp.arena, unsigned char, max_meshlets *MESHLET_INDICES);
|
|
|
|
size_t meshlet_count = meshopt_buildMeshlets(meshlets,
|
|
meshlet_vertices,
|
|
meshlet_triangles,
|
|
m_meshes[mesh_idx].indices,
|
|
m_meshes[mesh_idx].num_indices,
|
|
&m_meshes[mesh_idx].vertices[0].vx,
|
|
m_meshes[mesh_idx].num_vertices,
|
|
sizeof(vertex),
|
|
MESHLET_VERTICES,
|
|
MESHLET_TRIANGLES,
|
|
cone_weight);
|
|
|
|
m_meshlets = new meshlet[meshlet_count];
|
|
m_num_meshlets = (uint32_t)meshlet_count;
|
|
|
|
for (size_t i = 0; i < meshlet_count; ++i) {
|
|
meshopt_optimizeMeshlet(&meshlet_vertices[meshlets[i].vertex_offset],
|
|
&meshlet_triangles[meshlets[i].triangle_offset],
|
|
meshlets[i].triangle_count,
|
|
meshlets[i].vertex_count);
|
|
|
|
meshopt_Bounds bounds =
|
|
meshopt_computeMeshletBounds(&meshlet_vertices[meshlets[i].vertex_offset],
|
|
&meshlet_triangles[meshlets[i].triangle_offset],
|
|
meshlets[i].triangle_count,
|
|
&m_meshes[mesh_idx].vertices[0].vx,
|
|
m_meshes[mesh_idx].num_vertices,
|
|
sizeof(vertex));
|
|
|
|
m_meshlets[i].vertices = new vertex[MESHLET_VERTICES];
|
|
m_meshlets[i].indices = new uint8_t[MESHLET_INDICES];
|
|
m_meshlets[i].num_vertices = meshlets[i].vertex_count;
|
|
m_meshlets[i].num_indices = meshlets[i].triangle_count * 3;
|
|
memcpy(m_meshlets[i].center, bounds.center, sizeof(bounds.center));
|
|
m_meshlets[i].radius = bounds.radius;
|
|
memcpy(m_meshlets[i].cone_axis, bounds.cone_axis, sizeof(bounds.cone_axis));
|
|
m_meshlets[i].cone_cutoff = bounds.cone_cutoff;
|
|
memcpy(m_meshlets[i].cone_apex, bounds.cone_apex, sizeof(bounds.cone_apex));
|
|
|
|
for (unsigned int vert_idx = 0; vert_idx < meshlets[i].vertex_count; ++vert_idx) {
|
|
unsigned int vert = meshlet_vertices[meshlets[i].vertex_offset + vert_idx];
|
|
m_meshlets[i].vertices[vert_idx] = m_meshes[mesh_idx].vertices[vert];
|
|
}
|
|
|
|
memcpy(m_meshlets[i].indices,
|
|
meshlet_triangles + meshlets[i].triangle_offset,
|
|
meshlets[i].triangle_count * 3);
|
|
m_meshlets[i].num_children = 0u;
|
|
}
|
|
|
|
return RT_SUCCESS;
|
|
}
|
|
|
|
rt_result meshlet_generator::RunHierarchical(uint32_t mesh_idx, uint32_t *out_root) {
|
|
const float cone_weight = 0.25f;
|
|
|
|
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
|
|
|
|
size_t max_meshlets = meshopt_buildMeshletsBound(m_meshes[mesh_idx].num_indices,
|
|
MESHLET_VERTICES,
|
|
MESHLET_TRIANGLES);
|
|
meshopt_Meshlet *meshlets = RT_ARENA_PUSH_ARRAY(temp.arena, meshopt_Meshlet, max_meshlets);
|
|
unsigned int *meshlet_vertices =
|
|
RT_ARENA_PUSH_ARRAY(temp.arena, unsigned int, max_meshlets *MESHLET_VERTICES);
|
|
unsigned char *meshlet_triangles =
|
|
RT_ARENA_PUSH_ARRAY(temp.arena, unsigned char, max_meshlets *MESHLET_INDICES);
|
|
meshopt_Bounds *meshlet_bounds = RT_ARENA_PUSH_ARRAY(temp.arena, meshopt_Bounds, max_meshlets);
|
|
|
|
uint32_t meshlet_count = (uint32_t)meshopt_buildMeshlets(meshlets,
|
|
meshlet_vertices,
|
|
meshlet_triangles,
|
|
m_meshes[mesh_idx].indices,
|
|
m_meshes[mesh_idx].num_indices,
|
|
&m_meshes[mesh_idx].vertices[0].vx,
|
|
m_meshes[mesh_idx].num_vertices,
|
|
sizeof(vertex),
|
|
MESHLET_VERTICES,
|
|
MESHLET_TRIANGLES,
|
|
cone_weight);
|
|
|
|
for (size_t i = 0; i < meshlet_count; ++i) {
|
|
meshopt_optimizeMeshlet(&meshlet_vertices[meshlets[i].vertex_offset],
|
|
&meshlet_triangles[meshlets[i].triangle_offset],
|
|
meshlets[i].triangle_count,
|
|
meshlets[i].vertex_count);
|
|
|
|
meshlet_bounds[i] =
|
|
meshopt_computeMeshletBounds(&meshlet_vertices[meshlets[i].vertex_offset],
|
|
&meshlet_triangles[meshlets[i].triangle_offset],
|
|
meshlets[i].triangle_count,
|
|
&m_meshes[mesh_idx].vertices[0].vx,
|
|
m_meshes[mesh_idx].num_vertices,
|
|
sizeof(vertex));
|
|
}
|
|
|
|
// We now have a flat list of meshlets -> the highest lod ones
|
|
// We now combine (up to 8) meshlets into one to generate the next hierarchy level
|
|
// Repeat until we only have 1 meshlet left
|
|
std::vector<uint32_t> unprocessed;
|
|
unprocessed.reserve(meshlet_count);
|
|
for (uint32_t i = 0; i < meshlet_count; ++i) {
|
|
unprocessed.push_back(i);
|
|
}
|
|
std::vector<uint32_t> next_level;
|
|
next_level.reserve((unprocessed.size() + 7) / 8);
|
|
while (!unprocessed.empty()) {
|
|
uint32_t first = unprocessed.back();
|
|
unprocessed.pop_back();
|
|
|
|
// Find the 7 closest center points
|
|
uint32_t closest[7];
|
|
float distances[7];
|
|
unsigned int closest_count = 0u;
|
|
|
|
float first_center[3];
|
|
memcpy(first_center, meshlet_bounds[first].center, sizeof(float) * 3);
|
|
|
|
for (uint32_t i = 0; i < unprocessed.size(); ++i) {
|
|
float center[3];
|
|
memcpy(center, meshlet_bounds[unprocessed[i]].center, sizeof(float) * 3);
|
|
|
|
float dist = sqrtf((center[0] - first_center[0]) * (center[0] - first_center[0]) +
|
|
(center[1] - first_center[1]) * (center[1] - first_center[1]) +
|
|
(center[2] - first_center[2]) * (center[2] - first_center[2]));
|
|
|
|
if (closest_count == RT_ARRAY_COUNT(closest)) {
|
|
// Check if we are closer than one of the other candidates
|
|
for (unsigned int j = 0; j < closest_count; ++j) {
|
|
uint32_t highest_idx = UINT_MAX;
|
|
float highest_dist = dist;
|
|
if (dist < distances[j]) {
|
|
if (distances[j] > highest_dist) {
|
|
highest_dist = distances[j];
|
|
highest_idx = j;
|
|
}
|
|
}
|
|
if (highest_idx < RT_ARRAY_COUNT(closest)) {
|
|
const uint32_t replaced = highest_idx;
|
|
distances[j] = dist;
|
|
closest[j] = i;
|
|
unprocessed.push_back(replaced);
|
|
}
|
|
}
|
|
} else {
|
|
closest[closest_count] = i;
|
|
distances[closest_count] = dist;
|
|
closest_count++;
|
|
unprocessed.erase(unprocessed.begin() + i);
|
|
}
|
|
}
|
|
|
|
// Combine into a new meshlet
|
|
// vertex *vertices = new vertex[MESHLET_VERTICES * 8];
|
|
}
|
|
return RT_SUCCESS;
|
|
}
|