Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate NormalMaps and HeightMaps #1092

Merged
merged 28 commits into from
Sep 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
22e59c4
[software] Texturing: compute NormalMap (WIP)
Thomas-Zorroche Jul 23, 2021
524d3b2
[software] fix GeoVec type error and add debug
Thomas-Zorroche Jul 26, 2021
adc8443
[software] GenerateNormalMap: remap uv between [0,1]
Thomas-Zorroche Jul 29, 2021
75678d7
[software] Texturing: add normalsParams struct for normalMaps
Thomas-Zorroche Jul 29, 2021
f9fb592
[software] change normal maps suffix (1001 + atlasId)
Thomas-Zorroche Jul 29, 2021
c782569
[software] remap pixel color between 0 and 1 for exr
Thomas-Zorroche Aug 5, 2021
59a68f1
[software] fix rebase conflicts
Thomas-Zorroche Aug 6, 2021
8d3a026
[git] gitignore: ignore nfs temp files
Thomas-Zorroche Aug 9, 2021
3b806ea
[Texturing] height map usage
Thomas-Zorroche Aug 9, 2021
1770632
[Texturing] saveAs with different mesh file types
Thomas-Zorroche Aug 10, 2021
4559acb
[software] replace all saveToObj by save with different filetype
Thomas-Zorroche Aug 13, 2021
d1e9773
[convert] use assimp to convert mesh instead of geogram
Thomas-Zorroche Aug 13, 2021
f6956c0
[texturing] fix filename export
Thomas-Zorroche Aug 16, 2021
bb837c3
[texturing] change bumpMappingParams struct
Thomas-Zorroche Aug 16, 2021
96b5c7a
[texturing] flip uv when exporting mesh
Thomas-Zorroche Aug 17, 2021
ac087cb
[pipeline] receive two file type parameters for bump mapping
Thomas-Zorroche Aug 17, 2021
633cc5b
[Texturing] add flags for flipUV (only for .gltf)
Thomas-Zorroche Aug 18, 2021
3956c1a
[Texturing] add EBumpMappingType enum (Normal, Height)
Thomas-Zorroche Aug 18, 2021
6bdc933
[pipeline] add outputMeshFileType parameter for meshMasking
Thomas-Zorroche Aug 18, 2021
51aab84
[pipeline] rename loading functions
Thomas-Zorroche Aug 20, 2021
053ee8a
[Texturing] FlipUV: add comment from github issue
Thomas-Zorroche Aug 23, 2021
58546b8
[Texturing] add assimp flag for generating normals when export to gltf
Thomas-Zorroche Aug 23, 2021
9bb1574
[Mesh] add debug: vertices count for export / import
Thomas-Zorroche Aug 24, 2021
29a16fd
[Mesh] change Assimp flags for importer
Thomas-Zorroche Aug 26, 2021
470ffe9
[pipeline] change default format to obj
Thomas-Zorroche Aug 27, 2021
ec13480
[software] convertMesh: remove output type arguments
fabiencastan Sep 2, 2021
b69839d
[mvsData] use forward declaration to avoid new dependencies
fabiencastan Sep 2, 2021
8232794
[mesh] minor code style
fabiencastan Sep 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# temporary files
*~
.nfs*
.*.swp # vim
*.flc # xemacs
.DS_Store # MacOS
Expand Down
22 changes: 11 additions & 11 deletions src/aliceVision/fuseCut/DelaunayGraphCut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3271,7 +3271,7 @@ void DelaunayGraphCut::voteFullEmptyScore(const StaticVector<int>& cams, const s
if(false)
{
std::unique_ptr<mesh::Mesh> meshf(createTetrahedralMesh(false, 0.9f, [](const fuseCut::GC_cellInfo& c) { return c.emptinessScore; }));
meshf->saveToObj(folderName + "tetrahedralMesh_beforeForceTEdge_emptiness.obj");
meshf->save(folderName + "tetrahedralMesh_beforeForceTEdge_emptiness");
}

if(forceTEdge)
Expand Down Expand Up @@ -3789,47 +3789,47 @@ void DelaunayGraphCut::exportDebugMesh(const std::string& filename, const Point3
}

const std::string tempDirPath = boost::filesystem::temp_directory_path().generic_string();
mesh->saveToObj(tempDirPath + "/" + filename + ".obj");
meshf->saveToObj(tempDirPath + "/" + filename + "Filtered.obj");
mesh->save(tempDirPath + "/" + filename);
meshf->save(tempDirPath + "/" + filename);
}

void DelaunayGraphCut::exportFullScoreMeshs(const std::string& outputFolder, const std::string& name) const
{
const std::string nameExt = (name.empty() ? "" : "_" + name) + ".obj";
const std::string nameExt = (name.empty() ? "" : "_" + name);
{
std::unique_ptr<mesh::Mesh> meshEmptiness(
createTetrahedralMesh(false, 0.999f, [](const GC_cellInfo& c) { return c.emptinessScore; }));
meshEmptiness->saveToObj(outputFolder + "/mesh_emptiness" + nameExt);
meshEmptiness->save(outputFolder + "/mesh_emptiness" + nameExt);
}
{
std::unique_ptr<mesh::Mesh> meshFullness(
createTetrahedralMesh(false, 0.999f, [](const GC_cellInfo& c) { return c.fullnessScore; }));
meshFullness->saveToObj(outputFolder + "/mesh_fullness" + nameExt);
meshFullness->save(outputFolder + "/mesh_fullness" + nameExt);
}
{
std::unique_ptr<mesh::Mesh> meshSWeight(
createTetrahedralMesh(false, 0.999f, [](const GC_cellInfo& c) { return c.cellSWeight; }));
meshSWeight->saveToObj(outputFolder + "/mesh_sWeight" + nameExt);
meshSWeight->save(outputFolder + "/mesh_sWeight" + nameExt);
}
{
std::unique_ptr<mesh::Mesh> meshTWeight(
createTetrahedralMesh(false, 0.999f, [](const GC_cellInfo& c) { return c.cellTWeight; }));
meshTWeight->saveToObj(outputFolder + "/mesh_tWeight" + nameExt);
meshTWeight->save(outputFolder + "/mesh_tWeight" + nameExt);
}
{
std::unique_ptr<mesh::Mesh> meshOn(
createTetrahedralMesh(false, 0.999f, [](const GC_cellInfo& c) { return c.on; }));
meshOn->saveToObj(outputFolder + "/mesh_on" + nameExt);
meshOn->save(outputFolder + "/mesh_on" + nameExt);
}
{
std::unique_ptr<mesh::Mesh> mesh(createTetrahedralMesh(
false, 0.99f, [](const fuseCut::GC_cellInfo& c) { return c.fullnessScore - c.emptinessScore; }));
mesh->saveToObj(outputFolder + "/mesh_fullness-emptiness" + nameExt);
mesh->save(outputFolder + "/mesh_fullness-emptiness" + nameExt);
}
{
std::unique_ptr<mesh::Mesh> mesh(createTetrahedralMesh(
false, 0.99f, [](const fuseCut::GC_cellInfo& c) { return c.cellSWeight - c.cellTWeight; }));
mesh->saveToObj(outputFolder + "/mesh_s-t" + nameExt);
mesh->save(outputFolder + "/mesh_s-t" + nameExt);
}
}

Expand Down
170 changes: 142 additions & 28 deletions src/aliceVision/mesh/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <geogram/points/kd_tree.h>

#include <boost/filesystem.hpp>
#include <boost/algorithm/string/case_conv.hpp>

#include <assimp/Importer.hpp>
#include <assimp/Exporter.hpp>
Expand All @@ -36,9 +37,56 @@ Mesh::~Mesh()
{
}

void Mesh::saveToObj(const std::string& filename)

std::string EFileType_enumToString(const EFileType meshFileType)
{
ALICEVISION_LOG_INFO("Writing obj and mtl file.");
switch(meshFileType)
{
case EFileType::OBJ:
return "obj";
case EFileType::FBX:
return "fbx";
case EFileType::STL:
return "stl";
case EFileType::GLTF:
return "gltf";
}
throw std::out_of_range("Unrecognized EMeshFileType");
}

EFileType EFileType_stringToEnum(const std::string& meshFileType)
{
std::string m = meshFileType;
boost::to_lower(m);

if(m == "obj")
return EFileType::OBJ;
if(m == "fbx")
return EFileType::FBX;
if(m == "stl")
return EFileType::STL;
if(m == "gltf")
return EFileType::GLTF;
throw std::out_of_range("Invalid mesh file type " + meshFileType);
}

std::ostream& operator<<(std::ostream& os, EFileType meshFileType)
{
return os << EFileType_enumToString(meshFileType);
}
std::istream& operator>>(std::istream& in, EFileType& meshFileType)
{
std::string token;
in >> token;
meshFileType = EFileType_stringToEnum(token);
return in;
}

void Mesh::save(const std::string& filepath, EFileType fileType)
{
const std::string fileTypeStr = EFileType_enumToString(fileType);

ALICEVISION_LOG_INFO("Save " << fileTypeStr << " mesh file");

aiScene scene;

Expand Down Expand Up @@ -85,15 +133,36 @@ void Mesh::saveToObj(const std::string& filename)
}
}

std::string formatId = fileTypeStr;
unsigned int pPreprocessing = 0u;
// If gltf, use gltf 2.0
if (fileType == EFileType::GLTF)
{
formatId = "gltf2";
// gen normals in order to have correct shading in Qt 3D Scene
// but cause problems with assimp importer
pPreprocessing |= aiProcess_GenNormals;
}
// If obj, do not use material
else if (fileType == EFileType::OBJ)
{
formatId = "objnomtl";
}

Assimp::Exporter exporter;
exporter.Export(&scene, "objnomtl", filename);
exporter.Export(&scene, formatId, filepath, pPreprocessing);

ALICEVISION_LOG_INFO("Save mesh to " << fileTypeStr << " done.");

ALICEVISION_LOG_INFO("Save mesh to obj done.");
ALICEVISION_LOG_DEBUG("Vertices: " << pts.size());
ALICEVISION_LOG_DEBUG("Triangles: " << tris.size());
ALICEVISION_LOG_DEBUG("UVs: " << uvCoords.size());
ALICEVISION_LOG_DEBUG("Normals: " << normals.size());
}

bool Mesh::loadFromBin(const std::string& binFileName)
bool Mesh::loadFromBin(const std::string& binFilepath)
{
FILE* f = fopen(binFileName.c_str(), "rb");
FILE* f = fopen(binFilepath.c_str(), "rb");

if(f == nullptr)
return false;
Expand All @@ -114,12 +183,12 @@ bool Mesh::loadFromBin(const std::string& binFileName)
return true;
}

void Mesh::saveToBin(const std::string& binFileName)
void Mesh::saveToBin(const std::string& binFilepath)
{
long t = std::clock();
ALICEVISION_LOG_DEBUG("Save mesh to bin.");
// printf("open\n");
FILE* f = fopen(binFileName.c_str(), "wb");
FILE* f = fopen(binFilepath.c_str(), "wb");

int npts = pts.size();
// printf("write npts %i\n",npts);
Expand Down Expand Up @@ -885,26 +954,26 @@ void Mesh::getDepthMap(StaticVector<float>& depthMap, StaticVector<StaticVector<
} // for pix.x
}

void Mesh::getVisibleTrianglesIndexes(StaticVector<int>& out_visTri, const std::string& depthMapFileName, const std::string& trisMapFileName,
void Mesh::getVisibleTrianglesIndexes(StaticVector<int>& out_visTri, const std::string& depthMapFilepath, const std::string& trisMapFilepath,
const mvsUtils::MultiViewParams& mp, int rc, int w, int h)
{
StaticVector<float> depthMap;
loadArrayFromFile<float>(depthMap, depthMapFileName);
loadArrayFromFile<float>(depthMap, depthMapFilepath);
StaticVector<StaticVector<int>> trisMap;
loadArrayOfArraysFromFile<int>(trisMap, trisMapFileName);
loadArrayOfArraysFromFile<int>(trisMap, trisMapFilepath);

getVisibleTrianglesIndexes(out_visTri, trisMap, depthMap, mp, rc, w, h);
}

void Mesh::getVisibleTrianglesIndexes(StaticVector<int>& out_visTri, const std::string& tmpDir, const mvsUtils::MultiViewParams& mp, int rc, int w, int h)
{
std::string depthMapFileName = tmpDir + "depthMap" + std::to_string(mp.getViewId(rc)) + ".bin";
std::string trisMapFileName = tmpDir + "trisMap" + std::to_string(mp.getViewId(rc)) + ".bin";
std::string depthMapFilepath = tmpDir + "depthMap" + std::to_string(mp.getViewId(rc)) + ".bin";
std::string trisMapFilepath = tmpDir + "trisMap" + std::to_string(mp.getViewId(rc)) + ".bin";

StaticVector<float> depthMap;
loadArrayFromFile<float>(depthMap, depthMapFileName);
loadArrayFromFile<float>(depthMap, depthMapFilepath);
StaticVector<StaticVector<int>> trisMap;
loadArrayOfArraysFromFile<int>(trisMap, trisMapFileName);
loadArrayOfArraysFromFile<int>(trisMap, trisMapFilepath);

getVisibleTrianglesIndexes(out_visTri, trisMap, depthMap, mp, rc, w, h);
}
Expand Down Expand Up @@ -1855,9 +1924,9 @@ void Mesh::computeTrisCams(StaticVector<StaticVector<int>>& trisCams, const mvsU
long t1 = mvsUtils::initEstimate();
for(int rc = 0; rc < mp.ncams; ++rc)
{
std::string visTrisFileName = tmpDir + "visTris" + std::to_string(mp.getViewId(rc)) + ".bin";
std::string visTrisFilepath = tmpDir + "visTris" + std::to_string(mp.getViewId(rc)) + ".bin";
StaticVector<int> visTris;
loadArrayFromFile<int>(visTris, visTrisFileName);
loadArrayFromFile<int>(visTris, visTrisFilepath);
if(!visTris.empty())
{
for(int i = 0; i < visTris.size(); ++i)
Expand All @@ -1883,9 +1952,9 @@ void Mesh::computeTrisCams(StaticVector<StaticVector<int>>& trisCams, const mvsU
t1 = mvsUtils::initEstimate();
for(int rc = 0; rc < mp.ncams; ++rc)
{
std::string visTrisFileName = tmpDir + "visTris" + std::to_string(mp.getViewId(rc)) + ".bin";
std::string visTrisFilepath = tmpDir + "visTris" + std::to_string(mp.getViewId(rc)) + ".bin";
StaticVector<int> visTris;
loadArrayFromFile<int>(visTris, visTrisFileName);
loadArrayFromFile<int>(visTris, visTrisFilepath);
if(!visTris.empty())
{
for(int i = 0; i < visTris.size(); ++i)
Expand Down Expand Up @@ -2279,7 +2348,7 @@ void Mesh::getLargestConnectedComponentTrisIds(StaticVector<int>& out) const
}
}

void Mesh::loadFromObjAscii(const std::string& objAsciiFileName)
void Mesh::load(const std::string& filepath)
{
Assimp::Importer importer;

Expand All @@ -2294,16 +2363,58 @@ void Mesh::loadFromObjAscii(const std::string& objAsciiFileName)
normals.clear();
pointsVisibilities.clear();

if(!boost::filesystem::exists(objAsciiFileName))
{
ALICEVISION_THROW_ERROR("Mesh::loadFromObjAscii: no such file: " << objAsciiFileName);
}

unsigned int pFlags = aiProcessPreset_TargetRealtime_MaxQuality & (~aiProcess_SplitLargeMeshes);
const aiScene * scene = importer.ReadFile(objAsciiFileName, pFlags);
if(!boost::filesystem::exists(filepath))
{
ALICEVISION_THROW_ERROR("Mesh::load: no such file: " << filepath);
}

// see https://github.com/assimp/assimp/blob/master/include/assimp/postprocess.h#L85
const unsigned int pFlags =
// If this flag is not specified, no vertices are referenced by more than one face
aiProcess_JoinIdenticalVertices |
// if a face contain more than 3 vertices, split it in triangles
aiProcess_Triangulate |
// Removes the node graph and pre-transforms all vertices with the local transformation matrices of their nodes.
//aiProcess_PreTransformVertices |
// This is intended to get rid of some common exporter errors
//// aiProcess_FindInvalidData |
// Face normals are shared between all points of a single face,
// so a single point can have multiple normals, which forces the library to duplicate vertices in some cases.
aiProcess_DropNormals |
// This makes sure that all indices are valid
//aiProcess_ValidateDataStructure |
aiProcess_RemoveComponent |
// This step searches all meshes for degenerate primitives and converts them to proper lines or points.
// A face is 'degenerate' if one or more of its points are identical.
aiProcess_FindDegenerates |
// aiProcess_SortByPType needed for aiProcess_FindDegenerates.
// This step splits meshes with more than one primitive type in homogeneous sub-meshes
// (point and line in different meshes, so we can remove them with AI_CONFIG_PP_SBP_REMOVE).
aiProcess_SortByPType |
0;
importer.SetPropertyInteger(
AI_CONFIG_PP_RVC_FLAGS,
aiComponent_NORMALS |
aiComponent_TANGENTS_AND_BITANGENTS
// We do not remove texture coords as we need it for texturing,
// but it causes vertices duplicates with assimp. This is problematic for mesh post-processing
// but we should face this problem only for mesh coming from retopology,
// in which case we will only do texturing.
//aiComponent_TEXCOORDS
);

// aiProcess_FindDegenerates will convert degenerate triangles.
// As we don't want lines and points, we set the AI_CONFIG_PP_SBP_REMOVE.
importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);

// aiProcess_FindDegenerates will also remove very small triangles with a surface area smaller than 10^-6.
// As we don't want this extra-behavior, we set the property AI_CONFIG_PP_FD_CHECKAREA to false.
importer.SetPropertyBool(AI_CONFIG_PP_FD_CHECKAREA, false);

const aiScene* scene = importer.ReadFile(filepath, pFlags);
if (!scene)
{
ALICEVISION_THROW_ERROR("Failed loading mesh from file: " << objAsciiFileName);
ALICEVISION_THROW_ERROR("Failed loading mesh from file: " << filepath);
}

std::list<aiNode *> nodes;
Expand Down Expand Up @@ -2416,6 +2527,9 @@ void Mesh::loadFromObjAscii(const std::string& objAsciiFileName)
nodes.push_back(node->mChildren[idChild]);
}
}
ALICEVISION_LOG_DEBUG("Vertices: " << pts.size());
ALICEVISION_LOG_DEBUG("Triangles: " << tris.size());
ALICEVISION_LOG_DEBUG("UVs: " << uvCoords.size());
}

bool Mesh::getEdgeNeighTrisInterval(Pixel& itr, Pixel& edge, StaticVector<Voxel>& edgesXStat,
Expand Down
26 changes: 21 additions & 5 deletions src/aliceVision/mesh/Mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ ALICEVISION_BITMASK(EVisibilityRemappingMethod);
EVisibilityRemappingMethod EVisibilityRemappingMethod_stringToEnum(const std::string& method);
std::string EVisibilityRemappingMethod_enumToString(EVisibilityRemappingMethod method);

/**
* @brief File type available for exporting mesh
*/
enum class EFileType
{
OBJ = 0,
FBX,
GLTF,
STL
};

EFileType EFileType_stringToEnum(const std::string& filetype);
std::string EFileType_enumToString(const EFileType filetype);
std::istream& operator>>(std::istream& in, EFileType& meshFileType);
std::ostream& operator<<(std::ostream& os, EFileType meshFileType);


class Mesh
{
Expand Down Expand Up @@ -148,11 +164,11 @@ class Mesh
Mesh();
~Mesh();

void saveToObj(const std::string& filename);
void save(const std::string& filepath, EFileType filetype = EFileType::OBJ);

bool loadFromBin(const std::string& binFileName);
void saveToBin(const std::string& binFileName);
void loadFromObjAscii(const std::string& objAsciiFileName);
bool loadFromBin(const std::string& binFilepath);
void saveToBin(const std::string& binFilepath);
void load(const std::string& filepath);

void addMesh(const Mesh& mesh);

Expand All @@ -177,7 +193,7 @@ class Mesh
void getPtsNeighPtsOrdered(StaticVector<StaticVector<int>>& out_ptsNeighTris) const;

void getVisibleTrianglesIndexes(StaticVector<int>& out_visTri, const std::string& tmpDir, const mvsUtils::MultiViewParams& mp, int rc, int w, int h);
void getVisibleTrianglesIndexes(StaticVector<int>& out_visTri, const std::string& depthMapFileName, const std::string& trisMapFileName,
void getVisibleTrianglesIndexes(StaticVector<int>& out_visTri, const std::string& depthMapFilepath, const std::string& trisMapFilepath,
const mvsUtils::MultiViewParams& mp, int rc, int w, int h);
void getVisibleTrianglesIndexes(StaticVector<int>& out_visTri, StaticVector<StaticVector<int>>& trisMap,
StaticVector<float>& depthMap, const mvsUtils::MultiViewParams& mp, int rc, int w,
Expand Down
2 changes: 1 addition & 1 deletion src/aliceVision/mesh/MeshEnergyOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ bool MeshEnergyOpt::optimizeSmooth(float lambda, int niter, StaticVectorBool& pt
ALICEVISION_LOG_INFO("Optimizing mesh smooth: iteration " << i);
updateGradientParallel(lambda, LU, RD, ptsCanMove);
//if(saveDebug)
// saveToObj(folder + "mesh_smoothed_" + std::to_string(i) + ".obj");
// save(folder + "mesh_smoothed_" + std::to_string(i));
}

return true;
Expand Down
Loading