automatic lod generator

This commit is contained in:
Mikulas Florek 2021-11-09 21:47:51 +01:00
parent db57e8138e
commit d6f46d338d
5 changed files with 158 additions and 67 deletions

View file

@ -570,7 +570,6 @@ if has_plugin("renderer") then
"../external/meshoptimizer/clusterizer.cpp",
"../external/meshoptimizer/overdrawanalyzer.cpp",
"../external/meshoptimizer/overdrawoptimizer.cpp",
"../external/meshoptimizer/simplifier.cpp",
"../external/meshoptimizer/spatialorder.cpp",
"../external/meshoptimizer/stripifier.cpp",
"../external/meshoptimizer/vcacheanalyzer.cpp",

View file

@ -619,13 +619,16 @@ template <typename T> T checkArg(lua_State* L, int index)
}
template <typename T>
void getOptionalField(lua_State* L, int idx, const char* field_name, T* out)
bool getOptionalField(lua_State* L, int idx, const char* field_name, T* out)
{
bool res = false;
if (LuaWrapper::getField(L, idx, field_name) != LUA_TNIL && isType<T>(L, -1))
{
*out = toType<T>(L, -1);
res = true;
}
lua_pop(L, 1);
return res;
}
template <typename T>

View file

@ -698,7 +698,7 @@ void FBXImporter::postprocessMeshes(const ImportConfig& cfg, const char* path)
}
const int vertex_size = getVertexSize(*geom, import_mesh.is_skinned, cfg);
import_mesh.vertex_data.reserve(import_geom.unique_vertex_count);
import_mesh.vertex_data.reserve(import_geom.unique_vertex_count * vertex_size);
Array<Skin> skinning(m_allocator);
if (import_mesh.is_skinned) fillSkinInfo(skinning, import_mesh);
@ -758,6 +758,24 @@ void FBXImporter::postprocessMeshes(const ImportConfig& cfg, const char* path)
}
}
for (u32 i = 0; i < 3; ++i) {
if ((cfg.autolod_mask & (1 << i)) == 0) continue;
if (import_mesh.lod != 0) continue;
import_mesh.autolod_indices[i].create(m_allocator);
import_mesh.autolod_indices[i]->resize(import_mesh.indices.size());
const size_t lod_index_count = meshopt_simplify(import_mesh.autolod_indices[i]->begin()
, import_mesh.indices.begin()
, import_mesh.indices.size()
, (const float*)import_mesh.vertex_data.data()
, u32(import_mesh.vertex_data.size() / vertex_size)
, vertex_size
, size_t(import_mesh.indices.size() * cfg.autolod_coefs[i])
, 9001.f // TODO
);
import_mesh.autolod_indices[i]->resize((u32)lod_index_count);
}
import_mesh.aabb = aabb;
import_mesh.origin_radius_squared = origin_radius_squared;
import_mesh.center_radius_squared = 0;
@ -1877,6 +1895,9 @@ void FBXImporter::writeGeometry(int mesh_idx, const ImportConfig& cfg)
write(import_mesh.aabb);
}
static bool hasAutoLOD(const FBXImporter::ImportConfig& cfg, u32 idx) {
return cfg.autolod_mask & (1 << idx);
}
void FBXImporter::writeGeometry(const ImportConfig& cfg)
{
@ -1884,32 +1905,60 @@ void FBXImporter::writeGeometry(const ImportConfig& cfg)
float origin_radius_squared = 0;
float center_radius_squared = 0;
OutputMemoryStream vertices_blob(m_allocator);
for (const ImportMesh& import_mesh : m_meshes)
{
if (!import_mesh.import) continue;
bool are_indices_16_bit = areIndices16Bit(import_mesh, cfg);
if (are_indices_16_bit)
for (u32 lod = 0; lod < 4; ++lod) {
for (const ImportMesh& import_mesh : m_meshes)
{
int index_size = sizeof(u16);
write(index_size);
write(import_mesh.indices.size());
for (int i : import_mesh.indices)
{
ASSERT(i <= (1 << 16));
u16 index = (u16)i;
write(index);
if (!import_mesh.import) continue;
const bool are_indices_16_bit = areIndices16Bit(import_mesh, cfg);
origin_radius_squared = maximum(origin_radius_squared, import_mesh.origin_radius_squared);
center_radius_squared = maximum(center_radius_squared, import_mesh.center_radius_squared);
aabb.merge(import_mesh.aabb);
if (import_mesh.lod == lod && !hasAutoLOD(cfg, lod - 1)) {
if (are_indices_16_bit)
{
const i32 index_size = sizeof(u16);
write(index_size);
write(import_mesh.indices.size());
for (int i : import_mesh.indices)
{
ASSERT(i <= (1 << 16));
u16 index = (u16)i;
write(index);
}
}
else
{
int index_size = sizeof(import_mesh.indices[0]);
write(index_size);
write(import_mesh.indices.size());
write(&import_mesh.indices[0], sizeof(import_mesh.indices[0]) * import_mesh.indices.size());
}
}
else if (lod > 0 && import_mesh.lod == 0 && hasAutoLOD(cfg, lod - 1)) {
const auto& lod_indices = *import_mesh.autolod_indices[lod - 1].get();
if (are_indices_16_bit) {
const i32 index_size = sizeof(u16);
write(index_size);
write(lod_indices.size());
for (u32 i : lod_indices)
{
ASSERT(i <= (1 << 16));
u16 index = (u16)i;
write(index);
}
}
else
{
i32 index_size = sizeof(lod_indices[0]);
write(index_size);
write(lod_indices.size());
write(lod_indices.begin(), import_mesh.autolod_indices[lod - 1]->byte_size());
}
}
}
else
{
int index_size = sizeof(import_mesh.indices[0]);
write(index_size);
write(import_mesh.indices.size());
write(&import_mesh.indices[0], sizeof(import_mesh.indices[0]) * import_mesh.indices.size());
}
aabb.merge(import_mesh.aabb);
origin_radius_squared = maximum(origin_radius_squared, import_mesh.origin_radius_squared);
center_radius_squared = maximum(center_radius_squared, import_mesh.center_radius_squared);
}
if (cfg.create_impostor) {
@ -1921,11 +1970,15 @@ void FBXImporter::writeGeometry(const ImportConfig& cfg)
write(indices, sizeof(indices));
}
for (const ImportMesh& import_mesh : m_meshes)
{
if (!import_mesh.import) continue;
write((i32)import_mesh.vertex_data.size());
write(import_mesh.vertex_data.data(), import_mesh.vertex_data.size());
for (u32 lod = 0; lod < 4; ++lod) {
for (const ImportMesh& import_mesh : m_meshes) {
if (!import_mesh.import) continue;
if (import_mesh.lod == lod && !hasAutoLOD(cfg, lod - 1) || lod > 0 && import_mesh.lod == 0 && hasAutoLOD(cfg, lod - 1)) {
write((i32)import_mesh.vertex_data.size());
write(import_mesh.vertex_data.data(), import_mesh.vertex_data.size());
}
}
}
if (cfg.create_impostor) {
writeImpostorVertices(aabb);
@ -1978,8 +2031,12 @@ void FBXImporter::writeMeshes(const char* src, int mesh_idx, const ImportConfig&
mesh_count = 1;
}
else {
for (ImportMesh& mesh : m_meshes)
if (mesh.import) ++mesh_count;
for (ImportMesh& mesh : m_meshes) {
if (mesh.import && (mesh.lod == 0 || !hasAutoLOD(cfg, mesh.lod - 1))) ++mesh_count;
for (u32 i = 0; i < 3; ++i) {
if (mesh.lod == 0 && hasAutoLOD(cfg, i) && mesh_idx < 0) ++mesh_count;
}
}
if (cfg.create_impostor) ++mesh_count;
}
write(mesh_count);
@ -2009,7 +2066,7 @@ void FBXImporter::writeMeshes(const char* src, int mesh_idx, const ImportConfig&
write(gpu::AttributeType::U8);
write((u8)4);
}
if (hasTangents(*mesh.getGeometry())) {
if (hasTangents(*geom)) {
write(Mesh::AttributeSemantic::TANGENT);
write(gpu::AttributeType::I8);
write((u8)4);
@ -2049,8 +2106,11 @@ void FBXImporter::writeMeshes(const char* src, int mesh_idx, const ImportConfig&
writeMesh(m_meshes[mesh_idx]);
}
else {
for (ImportMesh& import_mesh : m_meshes) {
if (import_mesh.import) writeMesh(import_mesh);
for (u32 lod = 0; lod < 4; ++lod) {
for (ImportMesh& import_mesh : m_meshes) {
if (import_mesh.import && import_mesh.lod == lod && !hasAutoLOD(cfg, lod - 1)) writeMesh(import_mesh);
else if (lod > 0 && import_mesh.lod == 0 && import_mesh.import && hasAutoLOD(cfg, lod - 1)) writeMesh(import_mesh);
}
}
}
@ -2102,30 +2162,35 @@ void FBXImporter::writeLODs(const ImportConfig& cfg)
{
i32 lod_count = 1;
i32 last_mesh_idx = -1;
i32 lods[8] = {};
i32 lods[4] = {};
for (auto& mesh : m_meshes) {
if (!mesh.import) continue;
++last_mesh_idx;
if (mesh.lod >= lengthOf(cfg.lods_distances)) continue;
lod_count = mesh.lod + 1;
lods[mesh.lod] = last_mesh_idx;
}
for (u32 i = 1; i < Lumix::lengthOf(lods); ++i) {
if (lods[i] < lods[i - 1]) lods[i] = lods[i - 1];
if (mesh.lod == 0 || !hasAutoLOD(cfg, mesh.lod - 1)) {
lod_count = maximum(lod_count, mesh.lod + 1);
++lods[mesh.lod];
}
for (u32 i = 0; i < 3; ++i) {
if (mesh.lod == 0 && hasAutoLOD(cfg, i)) {
lod_count = maximum(lod_count, i + 2);
++lods[i + 1];
}
}
}
if (cfg.create_impostor) {
lods[lod_count] = last_mesh_idx + 1;
lods[lod_count] = 1;
++lod_count;
}
write((const char*)&lod_count, sizeof(lod_count));
u32 to_mesh = 0;
for (int i = 0; i < lod_count; ++i) {
i32 to_mesh = lods[i];
write((const char*)&to_mesh, sizeof(to_mesh));
to_mesh += lods[i];
const i32 tmp = to_mesh - 1;
write((const char*)&tmp, sizeof(tmp));
float factor = cfg.lods_distances[i] < 0 ? FLT_MAX : cfg.lods_distances[i] * cfg.lods_distances[i];
write((const char*)&factor, sizeof(factor));
}
@ -2314,19 +2379,12 @@ void FBXImporter::writeModel(const char* src, const ImportConfig& cfg)
PROFILE_FUNCTION();
postprocessMeshes(cfg, src);
auto cmpMeshes = [](const void* a, const void* b) -> int {
auto a_mesh = static_cast<const ImportMesh*>(a);
auto b_mesh = static_cast<const ImportMesh*>(b);
return a_mesh->lod - b_mesh->lod;
};
bool import_any_mesh = false;
for (const ImportMesh& m : m_meshes) {
if (m.import) import_any_mesh = true;
}
if (!import_any_mesh && m_animations.empty()) return;
if (!m_meshes.empty()) qsort(&m_meshes[0], m_meshes.size(), sizeof(m_meshes[0]), cmpMeshes);
out_file.clear();
writeModelHeader();
writeMeshes(src, -1, cfg);

View file

@ -37,6 +37,8 @@ struct FBXImporter
bool bake_vertex_ao = false;
Physics physics = Physics::NONE;
float lods_distances[4] = {-10, -100, -1000, -10000};
float autolod_coefs[3] = { 0.5f, 0.25f, 0.125f };
u8 autolod_mask = 0;
float bounding_scale = 1.f;
};
@ -134,7 +136,8 @@ struct FBXImporter
u32 lod = 0;
int submesh = -1;
OutputMemoryStream vertex_data;
Array<int> indices;
Array<u32> indices;
Local<Array<u32>> autolod_indices[3];
AABB aabb;
float origin_radius_squared;
float center_radius_squared;
@ -161,6 +164,7 @@ struct FBXImporter
ofbx::IScene* getOFBXScene() { return scene; }
private:
void createAutoLODs(const ImportConfig& cfg, ImportMesh& import_mesh);
bool findTexture(const char* src_dir, const char* ext, FBXImporter::ImportTexture& tex) const;
const ImportGeometry& getImportGeometry(const ofbx::Geometry* geom) const;
const ImportMesh* getAnyMeshFromBone(const ofbx::Object* node, int bone_idx) const;

View file

@ -1874,6 +1874,8 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
bool force_skin = false;
bool import_vertex_colors = false;
bool bake_vertex_ao = false;
u8 autolod_mask = 0;
float autolod_coefs[3] = { 0.5f, 0.25f, 0.125f };
float lods_distances[4] = { -1, -1, -1, -1 };
FBXImporter::ImportConfig::Origin origin = FBXImporter::ImportConfig::Origin::SOURCE;
FBXImporter::ImportConfig::Physics physics = FBXImporter::ImportConfig::Physics::NONE;
@ -1926,6 +1928,10 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "create_impostor", &meta.create_impostor);
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "import_vertex_colors", &meta.import_vertex_colors);
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "bake_vertex_ao", &meta.bake_vertex_ao);
if (LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "autolod0", &meta.autolod_coefs[0])) meta.autolod_mask |= 1;
if (LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "autolod1", &meta.autolod_coefs[1])) meta.autolod_mask |= 2;
if (LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "autolod2", &meta.autolod_coefs[2])) meta.autolod_mask |= 4;
if (LuaWrapper::getField(L, LUA_GLOBALSINDEX, "position_error") != LUA_TNIL) logWarning(path, ": `position_error` deprecated");
if (LuaWrapper::getField(L, LUA_GLOBALSINDEX, "rotation_error") != LUA_TNIL) logWarning(path, ": `rotation_error` deprecated");
@ -2007,6 +2013,8 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
const char* filepath = getResourceFilePath(src.c_str());
FBXImporter::ImportConfig cfg;
const Meta meta = getMeta(Path(filepath));
cfg.autolod_mask = meta.autolod_mask;
memcpy(cfg.autolod_coefs, meta.autolod_coefs, sizeof(meta.autolod_coefs));
cfg.mikktspace_tangents = meta.use_mikktspace;
cfg.mesh_scale = meta.scale;
cfg.bounding_scale = meta.culling_scale;
@ -2478,16 +2486,31 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
for(u32 i = 0; i < lengthOf(m_meta.lods_distances); ++i) {
bool infinite = m_meta.lods_distances[i] <= 0;
if(ImGui::Checkbox(StaticString<32>("Infinite LOD ", i), &infinite)) {
m_meta.lods_distances[i] *= -1;
if (ImGui::TreeNode(&m_meta.lods_distances[i], "LOD %d", i)) {
if(ImGui::Checkbox(StaticString<32>("Infinite"), &infinite)) {
m_meta.lods_distances[i] *= -1;
}
if (!infinite && m_meta.lods_distances[i] > 0) {
ImGui::SameLine();
ImGui::SetNextItemWidth(-1);
ImGui::DragFloat(StaticString<32>("##lod", i), &m_meta.lods_distances[i]);
}
if (i > 0) {
bool autolod = m_meta.autolod_mask & (1 << (i - 1));
ImGuiEx::Label("Auto LOD");
if (ImGui::Checkbox("##autolod", &autolod)) {
m_meta.autolod_mask &= ~(1 << (i - 1));
if (autolod) m_meta.autolod_mask |= 1 << (i - 1);
}
if (autolod) {
ImGuiEx::Label("Auto LOD coef");
ImGui::DragFloat("##autolod_coef", &m_meta.autolod_coefs[i - 1]);
}
}
ImGui::TreePop();
}
if (infinite) break;
if (m_meta.lods_distances[i] > 0) {
ImGui::SameLine();
ImGui::SetNextItemWidth(-1);
ImGui::DragFloat(StaticString<32>("##lod", i), &m_meta.lods_distances[i]);
}
}
if (ImGui::Button(ICON_FA_CHECK "Apply")) {
@ -2499,13 +2522,17 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
.cat("\nphysics = \"").cat(toString(m_meta.physics)).cat("\"")
.cat("\nscale = ").cat(m_meta.scale)
.cat("\nculling_scale = ").cat(m_meta.culling_scale)
.cat("\nsplit = ").cat(m_meta.split ? "true\n" : "false\n")
.cat("\nimport_vertex_colors = ").cat(m_meta.import_vertex_colors ? "true\n" : "false\n")
.cat("\nbake_vertex_ao = ").cat(m_meta.bake_vertex_ao ? "true\n" : "false\n");
.cat("\nsplit = ").cat(m_meta.split ? "true" : "false")
.cat("\nimport_vertex_colors = ").cat(m_meta.import_vertex_colors ? "true" : "false")
.cat("\nbake_vertex_ao = ").cat(m_meta.bake_vertex_ao ? "true" : "false");
if (m_meta.autolod_mask & 1) src.cat("\nautolod0 = ").cat(m_meta.autolod_coefs[0]);
if (m_meta.autolod_mask & 2) src.cat("\nautolod1 = ").cat(m_meta.autolod_coefs[1]);
if (m_meta.autolod_mask & 4) src.cat("\nautolod2 = ").cat(m_meta.autolod_coefs[2]);
for (u32 i = 0; i < lengthOf(m_meta.lods_distances); ++i) {
if (m_meta.lods_distances[i] > 0) {
src.cat("lod").cat(i).cat("_distance").cat(" = ").cat(m_meta.lods_distances[i]).cat("\n");
src.cat("\nlod").cat(i).cat("_distance").cat(" = ").cat(m_meta.lods_distances[i]);
}
}