fixed fbx import with center option enabled when importing models with multiple meshes

This commit is contained in:
Mikulas Florek 2023-09-23 13:26:42 +02:00
parent b3c264e4f4
commit 556273bf83
4 changed files with 88 additions and 68 deletions

View file

@ -476,12 +476,34 @@ LUMIX_FORCE_INLINE static u32 getPackedVec3(ofbx::Vec3 vec, const Matrix& mtx, F
return packF4u(v);
}
void FBXImporter::centerMesh(ImportMesh& mesh, bool bottom, const ImportConfig& cfg, const Matrix& mtx) const {
static int getVertexSize(const ofbx::Mesh& mesh, bool is_skinned, const FBXImporter::ImportConfig& cfg)
{
static const int POSITION_SIZE = sizeof(float) * 3;
static const int NORMAL_SIZE = sizeof(u8) * 4;
static const int TANGENT_SIZE = sizeof(u8) * 4;
static const int UV_SIZE = sizeof(float) * 2;
static const int COLOR_SIZE = sizeof(u8) * 4;
static const int AO_SIZE = sizeof(u8) * 4;
static const int BONE_INDICES_WEIGHTS_SIZE = sizeof(float) * 4 + sizeof(u16) * 4;
int size = POSITION_SIZE + NORMAL_SIZE;
const ofbx::GeometryData& geom = mesh.getGeometryData();
if (geom.getUVs().values) size += UV_SIZE;
if (cfg.bake_vertex_ao) size += AO_SIZE;
if (geom.getColors().values && cfg.import_vertex_colors) size += cfg.vertex_color_is_ao ? AO_SIZE : COLOR_SIZE;
if (hasTangents(mesh)) size += TANGENT_SIZE;
if (is_skinned) size += BONE_INDICES_WEIGHTS_SIZE;
return size;
}
static AABB computeMeshAABB(const FBXImporter::ImportMesh& mesh, const FBXImporter::ImportConfig& cfg) {
const int vertex_size = getVertexSize(*mesh.fbx, mesh.is_skinned, cfg);
const u32 vertex_count = u32(mesh.vertex_data.size() / vertex_size);
if (vertex_count <= 0) return;
if (vertex_count <= 0) return { Vec3(0), Vec3(0) };
u8* ptr = mesh.vertex_data.getMutableData();
const u8* ptr = mesh.vertex_data.data();
Vec3 min(FLT_MAX);
Vec3 max(-FLT_MAX);
@ -489,28 +511,26 @@ void FBXImporter::centerMesh(ImportMesh& mesh, bool bottom, const ImportConfig&
for (u32 i = 0; i < vertex_count; ++i) {
Vec3 v;
memcpy(&v, ptr + vertex_size * i, sizeof(v));
min.x = minimum(min.x, v.x);
min.y = minimum(min.y, v.y);
min.z = minimum(min.z, v.z);
max.x = maximum(max.x, v.x);
max.y = maximum(max.y, v.y);
max.z = maximum(max.z, v.z);
min = minimum(min, v);
max = maximum(max, v);
}
Vec3 center;
center.x = float(min.x + max.x) * 0.5f;
center.y = float(min.y + max.y) * 0.5f;
center.z = float(min.z + max.z) * 0.5f;
mesh.origin = center;
if (bottom) center.y = (float)min.y;
return { min, max };
}
static void offsetMesh(FBXImporter::ImportMesh& mesh, const FBXImporter::ImportConfig& cfg, const Vec3& offset) {
const int vertex_size = getVertexSize(*mesh.fbx, mesh.is_skinned, cfg);
const u32 vertex_count = u32(mesh.vertex_data.size() / vertex_size);
if (vertex_count <= 0) return;
u8* ptr = mesh.vertex_data.getMutableData();
mesh.origin = offset;
for (u32 i = 0; i < vertex_count; ++i) {
Vec3 v;
memcpy(&v, ptr + vertex_size * i, sizeof(v));
v -= center;
v -= offset;
memcpy(ptr + vertex_size * i, &v, sizeof(v));
}
}
@ -835,31 +855,49 @@ void FBXImporter::postprocessMeshes(const ImportConfig& cfg, const Path& path)
}
remap(unindexed_triangles, import_mesh, vertex_layout.size, cfg);
if (cfg.origin != ImportConfig::Origin::SOURCE) {
const bool bottom = cfg.origin == FBXImporter::ImportConfig::Origin::BOTTOM;
centerMesh(import_mesh, bottom, cfg, transform_matrix);
}
if (cfg.origin == ImportConfig::Origin::CENTER_EACH_MESH) {
const AABB aabb = computeMeshAABB(import_mesh, cfg);
Vec3 offset = (aabb.max + aabb.min) * 0.5f;
offsetMesh(import_mesh, cfg, offset);
}
computeBoundingShapes(import_mesh, vertex_layout.size);
}
});
// TODO autolods are not correct
for (u32 i = 0; i < cfg.lod_count; ++i) {
if ((cfg.autolod_mask & (1 << i)) == 0) continue;
if (import_mesh.lod != 0) continue;
AABB merged_aabb(Vec3(FLT_MAX), Vec3(-FLT_MAX));
for (const ImportMesh& m : m_meshes) {
merged_aabb.merge(m.aabb);
}
jobs::forEach(m_meshes.size(), 1, [&](i32 mesh_idx, i32){
ImportMesh& import_mesh = m_meshes[mesh_idx];
const i32 vertex_size = getVertexSize(*import_mesh.fbx, import_mesh.is_skinned, cfg);
if (cfg.origin != ImportConfig::Origin::SOURCE && cfg.origin != ImportConfig::Origin::CENTER_EACH_MESH) {
const bool bottom = cfg.origin == FBXImporter::ImportConfig::Origin::BOTTOM;
Vec3 offset = (merged_aabb.max + merged_aabb.min) * 0.5f;
if (bottom) offset.y = merged_aabb.min.y;
offsetMesh(import_mesh, cfg, offset);
computeBoundingShapes(import_mesh, vertex_size);
}
for (u32 i = 0; i < cfg.lod_count; ++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_simplifySloppy(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() / packed_vertex_size)
, packed_vertex_size
, size_t(import_mesh.indices.size() * cfg.autolod_coefs[i])
);
import_mesh.autolod_indices[i]->resize((u32)lod_index_count);
}
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_simplifySloppy(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])
);
import_mesh.autolod_indices[i]->resize((u32)lod_index_count);
}
});
@ -1848,28 +1886,6 @@ bool FBXImporter::writeAnimations(const Path& src, const ImportConfig& cfg)
return written_count > 0;
}
int FBXImporter::getVertexSize(const ofbx::Mesh& mesh, bool is_skinned, const ImportConfig& cfg) const
{
static const int POSITION_SIZE = sizeof(float) * 3;
static const int NORMAL_SIZE = sizeof(u8) * 4;
static const int TANGENT_SIZE = sizeof(u8) * 4;
static const int UV_SIZE = sizeof(float) * 2;
static const int COLOR_SIZE = sizeof(u8) * 4;
static const int AO_SIZE = sizeof(u8) * 4;
static const int BONE_INDICES_WEIGHTS_SIZE = sizeof(float) * 4 + sizeof(u16) * 4;
int size = POSITION_SIZE + NORMAL_SIZE;
const ofbx::GeometryData& geom = mesh.getGeometryData();
if (geom.getUVs().values) size += UV_SIZE;
if (cfg.bake_vertex_ao) size += AO_SIZE;
if (geom.getColors().values && cfg.import_vertex_colors) size += cfg.vertex_color_is_ao ? AO_SIZE : COLOR_SIZE;
if (hasTangents(mesh)) size += TANGENT_SIZE;
if (is_skinned) size += BONE_INDICES_WEIGHTS_SIZE;
return size;
}
void FBXImporter::fillSkinInfo(Array<Skin>& skinning, const ImportMesh& import_mesh) const {
const ofbx::Mesh* mesh = import_mesh.fbx;

View file

@ -29,9 +29,11 @@ struct FBXImporter {
};
enum class Origin : i32 {
SOURCE,
CENTER,
BOTTOM
SOURCE, // keep vertex data as is
CENTER, // center all meshes as a group
BOTTOM, // same as center, but don't change Y coordinate
CENTER_EACH_MESH // center each mesh in fbx separately, when exporting each mesh as a subresources
};
enum class Physics {
@ -174,8 +176,6 @@ private:
template <typename T> void write(const T& obj) { m_out_file.write(&obj, sizeof(obj)); }
void write(const void* ptr, size_t size) { m_out_file.write(ptr, size); }
void writeString(const char* str);
void centerMesh(ImportMesh& mesh, bool bottom, const ImportConfig& cfg, const Matrix& matrix) const;
int getVertexSize(const ofbx::Mesh& mesh, bool is_skinned, const ImportConfig& cfg) const;
void fillSkinInfo(Array<Skin>& skinning, const ImportMesh& mesh) const;
Vec3 fixOrientation(const Vec3& v) const;
Quat fixOrientation(const Quat& v) const;

View file

@ -25,6 +25,10 @@ struct ModelMeta {
case FBXImporter::ImportConfig::Origin::SOURCE: return "Keep";
case FBXImporter::ImportConfig::Origin::BOTTOM: return "Bottom";
case FBXImporter::ImportConfig::Origin::CENTER: return "Center";
case FBXImporter::ImportConfig::Origin::CENTER_EACH_MESH:
ASSERT(false); // this should not be exposed in UI / meta files so there should be no reason to convert to string
return "Center each mesh";
}
ASSERT(false);
return "none";

View file

@ -2568,7 +2568,7 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin {
bool any_written = false;
if (meta.split) {
cfg.origin = FBXImporter::ImportConfig::Origin::CENTER;
cfg.origin = FBXImporter::ImportConfig::Origin::CENTER_EACH_MESH;
any_written = importer.writeSubmodels(filepath, cfg) || any_written;
any_written = importer.writePrefab(filepath, cfg) || any_written;
}