6 #include "assimp/Importer.hpp"
7 #include "assimp/IOSystem.hpp"
8 #include "assimp/IOStream.hpp"
9 #include "assimp/scene.h"
10 #include "assimp/postprocess.h"
27 const vec3f& a_vertex,
28 const vec3f& a_normal,
74 : name(
"unknown_material")
75 , shader(std::nullopt)
90 const std::string& texture_name,
95 textures.emplace_back(texture_path, texture_type);
104 for(
const auto& part:
parts)
106 aabb.
extend(part.calc_aabb());
121 constexpr
unsigned int assimp_flags
122 = aiProcess_CalcTangentSpace
123 | aiProcess_Triangulate
124 | aiProcess_SortByPType
126 | aiProcess_GenUVCoords
127 | aiProcess_TransformUVCoords
128 | aiProcess_OptimizeMeshes
129 | aiProcess_RemoveRedundantMaterials
130 | aiProcess_PreTransformVertices
131 | aiProcess_ImproveCacheLocality
132 | aiProcess_FindDegenerates
133 | aiProcess_JoinIdenticalVertices
134 | aiProcess_ValidateDataStructure
135 | aiProcess_GenSmoothNormals
136 | aiProcess_FindInvalidData
141 get_texture_wrapping_mode(
const int mode)
147 case aiTextureMapMode_Decal:
throw "Unsupported texture wrapping mode: decal";
149 default:
throw "Unhandled texture wrapping mode";
155 con(
const aiColor3D c)
157 return Rgb {
c.r,
c.g,
c.b};
162 add_materials(Mesh* ret,
const aiScene* scene)
166 unsigned int material_id = 0;
167 material_id < scene->mNumMaterials;
171 const aiMaterial* mat = scene->mMaterials[material_id];
175 if(mat->GetTextureCount(aiTextureType_DIFFUSE) <= 0)
182 mat->GetTexture(aiTextureType_DIFFUSE, 0, &texture);
184 if(path.has_value() ==
false)
188 "invalid diffuse texture for material {0}: {0}",
193 material.textures.emplace_back
195 path.value_or(io::FilePath{
"~/image-plain/blue"}),
201 mat->Get(AI_MATKEY_NAME, ai_name);
202 material.name = ai_name.C_Str();
204 const bool got_shininess = aiReturn_SUCCESS == mat->Get(AI_MATKEY_SHININESS, material.shininess);
205 const bool got_alpha = aiReturn_SUCCESS == mat->Get(AI_MATKEY_OPACITY, material.alpha);
209 material.shininess = 0.0f;
214 material.alpha = 1.0f;
217 aiColor3D ai_ambient;
218 aiColor3D ai_diffuse;
219 aiColor3D ai_specular;
220 mat->Get(AI_MATKEY_COLOR_AMBIENT, ai_ambient);
221 mat->Get(AI_MATKEY_COLOR_DIFFUSE, ai_diffuse);
222 mat->Get(AI_MATKEY_COLOR_SPECULAR, ai_specular);
223 material.ambient =
con(ai_ambient);
224 material.diffuse =
con(ai_diffuse);
225 material.specular =
con(ai_specular);
229 mat->Get(AI_MATKEY_MAPPINGMODE_U(aiTextureType_DIFFUSE, 0), u);
230 mat->Get(AI_MATKEY_MAPPINGMODE_V(aiTextureType_DIFFUSE, 0), v);
231 material.wrap_s = get_texture_wrapping_mode(u);
232 material.wrap_t = get_texture_wrapping_mode(v);
235 material.shader = std::nullopt;
236 ret->materials.push_back(material);
242 add_faces(MeshPart* part,
const aiMesh* mesh)
244 for(
unsigned int face_id = 0; face_id < mesh->mNumFaces; face_id += 1)
246 const aiFace& face = mesh->mFaces[face_id];
247 if(face.mNumIndices<3)
251 ASSERTX(face.mNumIndices == 3, face.mNumIndices);
252 part->faces.emplace_back
263 add_points(MeshPart* part,
const aiMesh* mesh)
265 for(
unsigned int index = 0; index < mesh->mNumVertices; index += 1)
267 const aiVector3D&
vertex = mesh->mVertices[index];
268 const aiVector3D&
normal = mesh->mNormals[index];
271 if(mesh->HasTextureCoords(0))
273 const aiVector3D
uv = mesh->mTextureCoords[0][index];
277 part->points.push_back
291 convert_mesh(
const aiMesh* mesh)
295 part.material = mesh->mMaterialIndex;
296 add_points(&part, mesh);
297 add_faces(&part, mesh);
304 convert_scene(
const aiScene* scene,
const std::string& file_name)
312 if(scene->HasMeshes())
314 add_materials(&ret, scene);
316 for(
unsigned int meshid = 0; meshid < scene->mNumMeshes; meshid += 1)
318 const aiMesh* mesh = scene->mMeshes[meshid];
319 const MeshPart part = convert_mesh(mesh);
320 if(part.faces.empty())
322 const auto& name = mesh->mName;
323 const char*
const the_name = name.C_Str() !=
nullptr
327 LOG_WARN(
"No faces in part {0} for {1}", the_name, file_name);
331 ret.parts.push_back(part);
342 load_from_string(
const std::string& nff,
const std::string& format)
344 Assimp::Importer importer;
346 const aiScene* scene = importer.ReadFileFromMemory
355 throw std::string {importer.GetErrorString()};
357 return convert_scene(scene,
"<nff_source>");
361 constexpr
char const*
const file_format_nff =
"nff";
362 constexpr
char const*
const file_format_obj =
"obj";
366 decorate_mesh_materials
369 const io::FilePath& json_path,
370 const files::mesh::Mesh& json
373 std::map<std::string, Material*> mesh_materials;
374 for(
auto& material: mesh->materials)
376 mesh_materials[material.name] = &material;
379 for(
const auto& material: json.materials)
381 auto found = mesh_materials.find(material.name);
382 if(found == mesh_materials.end())
386 "Unable to find {0} in mesh {1} valid names are: {2}",
394 auto* other = found->second;
395 for(
const auto& src_texture: material.textures)
401 if(path.has_value() ==
false)
405 "Invalid path {0} in mesh {1}",
422 decorate_mesh_materials_ignore_ambient(Mesh* mesh)
424 for(
auto& material: mesh->materials)
426 material.ambient = material.diffuse;
432 fix_extension(io::FilePath* path,
const files::mesh::Folder& folder)
434 const auto ext = path->get_extension();
435 for(
auto c: folder.change_extensions)
439 const auto new_path = path->set_extension_copy(
c.new_ext);
448 fix_filename(io::FilePath* path,
const files::mesh::Folder& folder)
450 const auto [dir, file] = path->split_directories_and_file();
451 for(
auto c: folder.change_filenames)
453 if(file ==
c.old_file)
455 const auto new_path = dir.get_file(
c.new_file);
468 const io::FilePath& json_path
474 files::mesh::Mesh json_file;
478 LOG_ERROR(
"Failed to load {}: {}", json_path, loaded.get_error().display);
482 const auto& json = loaded.get_value();
490 if (json_file.diffuse_and_ambient_are_same)
492 decorate_mesh_materials_ignore_ambient(mesh);
495 if (!json_file.materials.empty())
497 decorate_mesh_materials(mesh, json_path, json_file);
505 const auto json_dir = json_path.get_directory();
506 const auto folder_path = json_dir.get_file(
"folder.json");
508 files::mesh::Folder folder;
514 else if (loaded ==
false)
516 LOG_ERROR(
"Failed to load {}: {}", json_path, loaded.get_error().display);
521 const auto& json = loaded.get_value();
529 for (
auto& p : mesh->parts)
531 for (
auto& v :
p.points)
533 v.vertex =
v.vertex * folder.scale;
537 if (folder.texture_override.empty())
541 auto dir = io::DirPath{ folder.texture_override };
542 if (dir.is_relative()) { dir =
io::join(json_dir, dir); }
544 for (
auto& m : mesh->materials)
546 for (
auto& t :
m.textures)
548 const auto new_file = dir.get_file(
t.path.get_file_with_extension());
550 fix_extension(&
t.path, folder);
551 fix_filename(&
t.path, folder);
566 size_t Read(
void* target_buffer,
size_t size,
size_t count)
override
568 char* target =
static_cast<char*
>(target_buffer);
569 size_t objects_read = 0;
570 for(
size_t object_index=0; object_index<count; object_index+=1)
584 size_t Write(
const void* ,
size_t ,
size_t )
override
589 aiReturn
Seek(
size_t offset, aiOrigin origin)
override
593 case aiOrigin_SET:
read_index = offset;
return aiReturn_SUCCESS;
594 case aiOrigin_CUR:
read_index += offset;
return aiReturn_SUCCESS;
595 case aiOrigin_END:
DIE(
"end seek with unsigned int?");
return aiReturn_FAILURE;
596 default:
DIE(
"unknown seek");
return aiReturn_FAILURE;
600 [[nodiscard]]
size_t Tell()
const override
625 bool Exists(
const char* file)
const override
628 return content !=
nullptr;
636 Assimp::IOStream*
Open(
const char* file,
const char* mode_cstr)
override
638 const std::string
mode = mode_cstr;
642 if(content ==
nullptr)
647 data->content = content;
651 void Close(Assimp::IOStream* file)
override
666 Assimp::Importer importer;
671 const aiScene* scene = importer.ReadFile(path.
path, assimp_flags);
674 res.
error = importer.GetErrorString();
688 res.
error =
"No parts(faces) in mesh";
706 std::ostringstream ss;
708 <<
"shader " << texture <<
"\n"
709 <<
"s 0 0 0 " << size
711 return load_from_string(ss.str(), file_format_nff);
719 const float x = width / 2;
720 const float y = height / 2;
721 const float z = depth / 2;
722 std::ostringstream ss;
724 <<
"v " << -
x <<
" " <<
" " << -
y <<
" " << -
z <<
"\n"
725 <<
"v " << -
x <<
" " <<
" " << -
y <<
" " <<
z <<
"\n"
726 <<
"v " << -
x <<
" " <<
" " <<
y <<
" " << -
z <<
"\n"
727 <<
"v " << -
x <<
" " <<
" " <<
y <<
" " <<
z <<
"\n"
728 <<
"v " <<
x <<
" " <<
" " << -
y <<
" " << -
z <<
"\n"
729 <<
"v " <<
x <<
" " <<
" " << -
y <<
" " <<
z <<
"\n"
730 <<
"v " <<
x <<
" " <<
" " <<
y <<
" " << -
z <<
"\n"
731 <<
"v " <<
x <<
" " <<
" " <<
y <<
" " <<
z <<
"\n"
738 <<
"f 3/1 7/2 5/3 1/4" <<
"\n"
739 <<
"f 6/1 8/2 4/3 2/4" <<
"\n"
740 <<
"f 2/1 4/2 3/3 1/4" <<
"\n"
741 <<
"f 7/1 8/2 6/3 5/4" <<
"\n"
742 <<
"f 4/1 8/2 7/3 3/4" <<
"\n"
743 <<
"f 5/1 6/2 2/3 1/4" <<
"\n"
746 auto box = load_from_string(ss.str(), file_format_obj);
#define DEFINE_ENUM_VALUE(TYPE, NAME, STRING)
constexpr ParseResult error
no error occurred
Mesh create_box(float width, float height, float depth)
LoadedMeshOrError load_mesh(io::FileSystem *fs, const io::FilePath &path)
Mesh create_sphere(float size, const std::string &texture)
Mesh create_cube(float size)
constexpr Rgbi con(unsigned char r, unsigned char g, unsigned char b)
std::vector< K > get_keys(const std::map< K, V > &m)
JsonResult read_json_file(FileSystem *fs, const FilePath &file_name)
DirPath join(const DirPath &lhs, const DirPath &rhs)
Logger * get_global_logger()
constexpr ShaderAttribute vertex
constexpr ShaderAttribute normal
constexpr StringMerger english_or
int c_sizet_to_int(size_t t)
int c_unsigned_int_to_int(unsigned int i)
static Aabb create_empty()
void extend(const vec3f &vec)
const Error & get_error() const
std::string merge(const std::vector< std::string > &strings) const
MaterialTexture(const io::FilePath &p, EnumValue t)
std::vector< MaterialTexture > textures
void set_texture(const std::string &texture_name, const io::FilePath &texture_path)
MeshFace(int a_a, int a_b, int a_c)
std::vector< MeshPoint > points
MeshPoint(const vec3f &a_vertex, const vec3f &a_normal, const vec2f &a_uv)
std::vector< MeshPart > parts
size_t Read(void *target_buffer, size_t size, size_t count) override
std::shared_ptr< MemoryChunk > content
size_t Tell() const override
size_t FileSize() const override
aiReturn Seek(size_t offset, aiOrigin origin) override
size_t Write(const void *, size_t, size_t) override
FilesystemForAssimp(io::FileSystem *fs)
Assimp::IOStream * Open(const char *file, const char *mode_cstr) override
bool Exists(const char *file) const override
void Close(Assimp::IOStream *file) override
char getOsSeparator() const override
io::FileSystem * file_system
bool DeleteFile(const std::string &) override
FilePath set_extension_copy(const std::string &ext) const
std::string path
contains either .
std::string get_extension() const
static std::optional< FilePath > from_script(const std::string &path)
apply only minor changes, return null on invalid
static std::optional< FilePath > from_dirty_source(const std::string &path)
do everything possible to convert from dirty path to valid path
std::shared_ptr< MemoryChunk > read_file(const FilePath &path)