mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-02 01:20:25 +08:00
Tango: fixed saveOnDevice and shareSketchfab actions when visualizing optimized mesh of a just opened database
This commit is contained in:
@@ -61,7 +61,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#define LOW_RES_PIX 2
|
#define LOW_RES_PIX 2
|
||||||
//#define DEBUG_RENDERING_PERFORMANCE;
|
//#define DEBUG_RENDERING_PERFORMANCE;
|
||||||
|
|
||||||
const int g_exportedMeshId = -100;
|
const int g_optMeshId = -100;
|
||||||
|
|
||||||
static JavaVM *jvm;
|
static JavaVM *jvm;
|
||||||
static jobject RTABMapActivity = 0;
|
static jobject RTABMapActivity = 0;
|
||||||
@@ -187,7 +187,7 @@ RTABMapApp::RTABMapApp() :
|
|||||||
processGPUMemoryUsedBytes(0),
|
processGPUMemoryUsedBytes(0),
|
||||||
visualizingMesh_(false),
|
visualizingMesh_(false),
|
||||||
exportedMeshUpdated_(false),
|
exportedMeshUpdated_(false),
|
||||||
exportedMesh_(new pcl::TextureMesh),
|
optMesh_(new pcl::TextureMesh),
|
||||||
mapToOdom_(rtabmap::Transform::getIdentity())
|
mapToOdom_(rtabmap::Transform::getIdentity())
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -289,140 +289,44 @@ int RTABMapApp::openDatabase(const std::string & databasePath, bool databaseInMe
|
|||||||
rtabmap_ = 0;
|
rtabmap_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->registerToEventsManager();
|
||||||
|
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
// Open visualization while we load (if there is an optimized mesh saved in database)
|
// Open visualization while we load (if there is an optimized mesh saved in database)
|
||||||
exportedMesh_.reset(new pcl::TextureMesh);
|
optMesh_.reset(new pcl::TextureMesh);
|
||||||
exportedTexture_ = cv::Mat();
|
optTexture_ = cv::Mat();
|
||||||
cv::Mat cloudMat;
|
cv::Mat cloudMat;
|
||||||
std::vector<std::vector<std::vector<unsigned int> > > polygons;
|
std::vector<std::vector<std::vector<unsigned int> > > polygons;
|
||||||
|
std::vector<std::vector<Eigen::Vector2f> > texCoords;
|
||||||
cv::Mat textures;
|
cv::Mat textures;
|
||||||
std::map<int, rtabmap::Transform> optPoses;
|
std::map<int, rtabmap::Transform> optPoses;
|
||||||
if(!databaseSource.empty())
|
if(!databaseSource.empty())
|
||||||
{
|
{
|
||||||
UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized mesh..."));
|
UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized cloud/mesh..."));
|
||||||
rtabmap::DBDriver * driver = rtabmap::DBDriver::create();
|
rtabmap::DBDriver * driver = rtabmap::DBDriver::create();
|
||||||
if(driver->openConnection(databaseSource))
|
if(driver->openConnection(databaseSource))
|
||||||
{
|
{
|
||||||
cloudMat = driver->loadOptimizedMesh(&optPoses, &polygons, &exportedMesh_->tex_coordinates, &textures);
|
cloudMat = driver->loadOptimizedMesh(&optPoses, &polygons, &texCoords, &textures);
|
||||||
if(!cloudMat.empty())
|
if(!cloudMat.empty())
|
||||||
{
|
{
|
||||||
LOGI("Open: Found optimized mesh! Visualizing it.");
|
LOGI("Open: Found optimized mesh! Visualizing it.");
|
||||||
if(cloudMat.channels() <= 3)
|
optMesh_ = rtabmap::util3d::assembleTextureMesh(cloudMat, polygons, texCoords, textures, true);
|
||||||
|
optTexture_ = textures;
|
||||||
|
if(!optTexture_.empty())
|
||||||
{
|
{
|
||||||
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = rtabmap::util3d::laserScanToPointCloud(cloudMat);
|
LOGI("Open: Texture mesh: %dx%d.", optTexture_.cols, optTexture_.rows);
|
||||||
pcl::toPCLPointCloud2(*cloud, exportedMesh_->cloud);
|
status=3;
|
||||||
}
|
}
|
||||||
else if(cloudMat.channels() == 4)
|
else if(optMesh_->tex_polygons.size())
|
||||||
{
|
{
|
||||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudRGB(cloudMat);
|
LOGI("Open: Polygon mesh");
|
||||||
pcl::toPCLPointCloud2(*cloud, exportedMesh_->cloud);
|
status=2;
|
||||||
}
|
}
|
||||||
else if(cloudMat.channels() == 6)
|
else if(!optMesh_->cloud.data.empty())
|
||||||
{
|
{
|
||||||
pcl::PointCloud<pcl::PointNormal>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudNormal(cloudMat);
|
LOGI("Open: Point cloud");
|
||||||
pcl::toPCLPointCloud2(*cloud, exportedMesh_->cloud);
|
status=1;
|
||||||
}
|
|
||||||
else if(cloudMat.channels() == 7)
|
|
||||||
{
|
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudRGBNormal(cloudMat);
|
|
||||||
pcl::toPCLPointCloud2(*cloud, exportedMesh_->cloud);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(exportedMesh_->cloud.data.size())
|
|
||||||
{
|
|
||||||
status = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(exportedMesh_->cloud.data.size() && polygons.size())
|
|
||||||
{
|
|
||||||
status = 2;
|
|
||||||
exportedMesh_->tex_polygons.resize(polygons.size());
|
|
||||||
for(unsigned int t=0; t<polygons.size(); ++t)
|
|
||||||
{
|
|
||||||
exportedMesh_->tex_polygons[t].resize(polygons[t].size());
|
|
||||||
for(unsigned int p=0; p<polygons[t].size(); ++p)
|
|
||||||
{
|
|
||||||
exportedMesh_->tex_polygons[t][p].vertices = polygons[t][p];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!exportedMesh_->tex_coordinates.empty())
|
|
||||||
{
|
|
||||||
status = 3;
|
|
||||||
UASSERT(!textures.empty() && textures.cols % textures.rows == 0 && textures.cols/textures.rows == (int)exportedMesh_->tex_coordinates.size());
|
|
||||||
if(textures.cols/textures.rows == 1)
|
|
||||||
{
|
|
||||||
exportedTexture_ = textures;
|
|
||||||
}
|
|
||||||
else if(textures.cols/textures.rows > 1)
|
|
||||||
{
|
|
||||||
// Visualization doesn't support more than one material, so concatenate to 1
|
|
||||||
std::vector<bool> materialsKept;
|
|
||||||
float scale = 0.0f;
|
|
||||||
cv::Size imageSize(textures.rows, textures.rows);
|
|
||||||
int imageType = CV_8UC3;
|
|
||||||
rtabmap::util3d::concatenateTextureMaterials(*exportedMesh_, imageSize, textures.rows, 1, scale, &materialsKept);
|
|
||||||
if(scale && exportedMesh_->tex_materials.size() == 1)
|
|
||||||
{
|
|
||||||
int cols = float(textures.rows)/(scale*imageSize.width);
|
|
||||||
int rows = float(textures.rows)/(scale*imageSize.height);
|
|
||||||
|
|
||||||
exportedTexture_ = cv::Mat(textures.rows, textures.rows, imageType, cv::Scalar::all(255));
|
|
||||||
|
|
||||||
// make a blank texture
|
|
||||||
cv::Size resizedImageSize(int(imageSize.width*scale), int(imageSize.height*scale));
|
|
||||||
int oi=0;
|
|
||||||
for(int i=0; i<(int)materialsKept.size(); ++i)
|
|
||||||
{
|
|
||||||
if(materialsKept.at(i))
|
|
||||||
{
|
|
||||||
int u = oi%cols * resizedImageSize.width;
|
|
||||||
int v = ((oi/cols) % rows ) * resizedImageSize.height;
|
|
||||||
UASSERT(u < textures.rows-resizedImageSize.width);
|
|
||||||
UASSERT(v < textures.rows-resizedImageSize.height);
|
|
||||||
|
|
||||||
cv::Mat resizedImage;
|
|
||||||
cv::resize(textures(cv::Range::all(), cv::Range(i*textures.rows, (i+1)*textures.rows)), resizedImage, resizedImageSize, 0.0f, 0.0f, cv::INTER_AREA);
|
|
||||||
|
|
||||||
UASSERT(resizedImage.type() == exportedTexture_.type());
|
|
||||||
resizedImage.copyTo(exportedTexture_(cv::Rect(u, v, resizedImage.cols, resizedImage.rows)));
|
|
||||||
|
|
||||||
++oi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exportedMesh_->tex_materials.resize (exportedMesh_->tex_coordinates.size () + 1);
|
|
||||||
for(unsigned int i = 0 ; i <= exportedMesh_->tex_coordinates.size() ; ++i)
|
|
||||||
{
|
|
||||||
pcl::TexMaterial mesh_material;
|
|
||||||
mesh_material.tex_Ka.r = 0.2f;
|
|
||||||
mesh_material.tex_Ka.g = 0.2f;
|
|
||||||
mesh_material.tex_Ka.b = 0.2f;
|
|
||||||
|
|
||||||
mesh_material.tex_Kd.r = 0.8f;
|
|
||||||
mesh_material.tex_Kd.g = 0.8f;
|
|
||||||
mesh_material.tex_Kd.b = 0.8f;
|
|
||||||
|
|
||||||
mesh_material.tex_Ks.r = 1.0f;
|
|
||||||
mesh_material.tex_Ks.g = 1.0f;
|
|
||||||
mesh_material.tex_Ks.b = 1.0f;
|
|
||||||
|
|
||||||
mesh_material.tex_d = 1.0f;
|
|
||||||
mesh_material.tex_Ns = 75.0f;
|
|
||||||
mesh_material.tex_illum = 2;
|
|
||||||
|
|
||||||
std::stringstream tex_name;
|
|
||||||
tex_name << "material_" << i;
|
|
||||||
tex_name >> mesh_material.tex_name;
|
|
||||||
|
|
||||||
mesh_material.tex_file = uFormat("%d", i);
|
|
||||||
|
|
||||||
exportedMesh_->tex_materials[i] = mesh_material;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -435,11 +339,24 @@ int RTABMapApp::openDatabase(const std::string & databasePath, bool databaseInMe
|
|||||||
|
|
||||||
if(status > 0)
|
if(status > 0)
|
||||||
{
|
{
|
||||||
|
if(status==1)
|
||||||
|
{
|
||||||
|
UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized cloud...done!"));
|
||||||
|
}
|
||||||
|
else if(status==2)
|
||||||
|
{
|
||||||
|
UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized mesh...done!"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized texture mesh...done!"));
|
||||||
|
}
|
||||||
boost::mutex::scoped_lock lockRender(renderingMutex_);
|
boost::mutex::scoped_lock lockRender(renderingMutex_);
|
||||||
visualizingMesh_ = true;
|
visualizingMesh_ = true;
|
||||||
exportedMeshUpdated_ = true;
|
exportedMeshUpdated_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading database..."));
|
||||||
LOGI("Erasing database \"%s\"...", databasePath.c_str());
|
LOGI("Erasing database \"%s\"...", databasePath.c_str());
|
||||||
UFile::erase(databasePath);
|
UFile::erase(databasePath);
|
||||||
if(!databaseSource.empty())
|
if(!databaseSource.empty())
|
||||||
@@ -448,8 +365,6 @@ int RTABMapApp::openDatabase(const std::string & databasePath, bool databaseInMe
|
|||||||
UFile::copy(databaseSource, databasePath);
|
UFile::copy(databaseSource, databasePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->registerToEventsManager();
|
|
||||||
|
|
||||||
//Rtabmap
|
//Rtabmap
|
||||||
mapToOdom_.setIdentity();
|
mapToOdom_.setIdentity();
|
||||||
rtabmap_ = new rtabmap::Rtabmap();
|
rtabmap_ = new rtabmap::Rtabmap();
|
||||||
@@ -1119,32 +1034,37 @@ int RTABMapApp::Render()
|
|||||||
main_scene_.clear();
|
main_scene_.clear();
|
||||||
exportedMeshUpdated_ = false;
|
exportedMeshUpdated_ = false;
|
||||||
}
|
}
|
||||||
if(!main_scene_.hasCloud(g_exportedMeshId))
|
if(!main_scene_.hasCloud(g_optMeshId))
|
||||||
{
|
{
|
||||||
LOGI("Adding optimized mesh to opengl...");
|
LOGI("Adding optimized mesh to opengl (%d points, %d polygons, %d tex_coords, materials=%d texture=%dx%d)...",
|
||||||
if(exportedMesh_->tex_polygons.size() && exportedMesh_->tex_polygons[0].size())
|
optMesh_->cloud.point_step==0?0:(int)optMesh_->cloud.data.size()/optMesh_->cloud.point_step,
|
||||||
|
optMesh_->tex_polygons.size()!=1?0:(int)optMesh_->tex_polygons[0].size(),
|
||||||
|
optMesh_->tex_coordinates.size()!=1?0:(int)optMesh_->tex_coordinates[0].size(),
|
||||||
|
(int)optMesh_->tex_materials.size(),
|
||||||
|
optTexture_.cols, optTexture_.rows);
|
||||||
|
if(optMesh_->tex_polygons.size() && optMesh_->tex_polygons[0].size())
|
||||||
{
|
{
|
||||||
Mesh mesh;
|
Mesh mesh;
|
||||||
mesh.gains[0] = mesh.gains[1] = mesh.gains[2] = 1.0;
|
mesh.gains[0] = mesh.gains[1] = mesh.gains[2] = 1.0;
|
||||||
mesh.cloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
|
mesh.cloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
|
||||||
mesh.normals.reset(new pcl::PointCloud<pcl::Normal>);
|
mesh.normals.reset(new pcl::PointCloud<pcl::Normal>);
|
||||||
pcl::fromPCLPointCloud2(exportedMesh_->cloud, *mesh.cloud);
|
pcl::fromPCLPointCloud2(optMesh_->cloud, *mesh.cloud);
|
||||||
pcl::fromPCLPointCloud2(exportedMesh_->cloud, *mesh.normals);
|
pcl::fromPCLPointCloud2(optMesh_->cloud, *mesh.normals);
|
||||||
mesh.polygons = exportedMesh_->tex_polygons[0];
|
mesh.polygons = optMesh_->tex_polygons[0];
|
||||||
if(exportedMesh_->tex_coordinates.size())
|
if(optMesh_->tex_coordinates.size())
|
||||||
{
|
{
|
||||||
mesh.texCoords = exportedMesh_->tex_coordinates[0];
|
mesh.texCoords = optMesh_->tex_coordinates[0];
|
||||||
mesh.texture = exportedTexture_;
|
mesh.texture = optTexture_;
|
||||||
}
|
}
|
||||||
|
|
||||||
main_scene_.addMesh(g_exportedMeshId, mesh, opengl_world_T_rtabmap_world, true);
|
main_scene_.addMesh(g_optMeshId, mesh, opengl_world_T_rtabmap_world, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pcl::IndicesPtr indices(new std::vector<int>); // null
|
pcl::IndicesPtr indices(new std::vector<int>); // null
|
||||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
|
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
|
||||||
pcl::fromPCLPointCloud2(exportedMesh_->cloud, *cloud);
|
pcl::fromPCLPointCloud2(optMesh_->cloud, *cloud);
|
||||||
main_scene_.addCloud(g_exportedMeshId, cloud, indices, opengl_world_T_rtabmap_world);
|
main_scene_.addCloud(g_optMeshId, cloud, indices, opengl_world_T_rtabmap_world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1152,7 +1072,7 @@ int RTABMapApp::Render()
|
|||||||
bool isMeshRendering = main_scene_.isMeshRendering();
|
bool isMeshRendering = main_scene_.isMeshRendering();
|
||||||
bool isTextureRendering = main_scene_.isMeshTexturing();
|
bool isTextureRendering = main_scene_.isMeshTexturing();
|
||||||
|
|
||||||
main_scene_.setMeshRendering(main_scene_.hasMesh(g_exportedMeshId), main_scene_.hasTexture(g_exportedMeshId));
|
main_scene_.setMeshRendering(main_scene_.hasMesh(g_optMeshId), main_scene_.hasTexture(g_optMeshId));
|
||||||
|
|
||||||
fpsTime.restart();
|
fpsTime.restart();
|
||||||
lastDrawnCloudsCount_ = main_scene_.Render();
|
lastDrawnCloudsCount_ = main_scene_.Render();
|
||||||
@@ -1166,11 +1086,11 @@ int RTABMapApp::Render()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(main_scene_.hasCloud(g_exportedMeshId))
|
if(main_scene_.hasCloud(g_optMeshId))
|
||||||
{
|
{
|
||||||
main_scene_.clear();
|
main_scene_.clear();
|
||||||
exportedMesh_.reset(new pcl::TextureMesh);
|
optMesh_.reset(new pcl::TextureMesh);
|
||||||
exportedTexture_ = cv::Mat();
|
optTexture_ = cv::Mat();
|
||||||
}
|
}
|
||||||
|
|
||||||
// should be before clearSceneOnNextRender_ in case database is reset
|
// should be before clearSceneOnNextRender_ in case database is reset
|
||||||
@@ -2108,7 +2028,6 @@ void RTABMapApp::cancelProcessing()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool RTABMapApp::exportMesh(
|
bool RTABMapApp::exportMesh(
|
||||||
const std::string & filePath,
|
|
||||||
float cloudVoxelSize,
|
float cloudVoxelSize,
|
||||||
bool regenerateCloud,
|
bool regenerateCloud,
|
||||||
bool meshing,
|
bool meshing,
|
||||||
@@ -2399,7 +2318,7 @@ bool RTABMapApp::exportMesh(
|
|||||||
|
|
||||||
if(textureSize>0)
|
if(textureSize>0)
|
||||||
{
|
{
|
||||||
LOGI("Texturing...");
|
LOGI("Texturing... cameraPoses=%d, cameraDepths=%d", (int)cameraPoses.size(), (int)cameraDepths.size());
|
||||||
textureMesh = rtabmap::util3d::createTextureMesh(
|
textureMesh = rtabmap::util3d::createTextureMesh(
|
||||||
mesh,
|
mesh,
|
||||||
cameraPoses,
|
cameraPoses,
|
||||||
@@ -2630,41 +2549,6 @@ bool RTABMapApp::exportMesh(
|
|||||||
vertexToPixels,
|
vertexToPixels,
|
||||||
true, 10.0f, true ,true, 0, 0, 0, false,
|
true, 10.0f, true ,true, 0, 0, 0, false,
|
||||||
&progressionStatus_);
|
&progressionStatus_);
|
||||||
|
|
||||||
if(progressionStatus_.isCanceled())
|
|
||||||
{
|
|
||||||
if(blockRendering)
|
|
||||||
{
|
|
||||||
renderingMutex_.unlock();
|
|
||||||
}
|
|
||||||
exporting_ = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD("Saving texture(s) (%d)", globalTextures.empty()?0:globalTextures.cols/globalTextures.rows);
|
|
||||||
std::string baseName = uSplit(UFile::getName(filePath), '.').front();
|
|
||||||
std::string textureDirectory = UDirectory::getDir(filePath);
|
|
||||||
UASSERT(globalTextures.empty() || globalTextures.cols % globalTextures.rows == 0);
|
|
||||||
UASSERT((int)textureMesh->tex_materials.size() == globalTextures.cols/globalTextures.rows);
|
|
||||||
for(unsigned int i=0; i<textureMesh->tex_materials.size(); ++i)
|
|
||||||
{
|
|
||||||
std::string baseNameNum = baseName;
|
|
||||||
if(textureMesh->tex_materials.size()>1)
|
|
||||||
{
|
|
||||||
baseNameNum+=uNumber2Str(i);
|
|
||||||
}
|
|
||||||
std::string fullPath = textureDirectory+UDirectory::separator()+baseNameNum+".jpg";
|
|
||||||
textureMesh->tex_materials[i].tex_file = baseNameNum+".jpg";
|
|
||||||
LOGI("Saving texture to %s.", fullPath.c_str());
|
|
||||||
if(!cv::imwrite(fullPath, globalTextures(cv::Range::all(), cv::Range(i*globalTextures.rows, (i+1)*globalTextures.rows))))
|
|
||||||
{
|
|
||||||
LOGI("Failed saving %s!", fullPath.c_str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGI("Saved %s.", fullPath.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(progressionStatus_.isCanceled())
|
if(progressionStatus_.isCanceled())
|
||||||
{
|
{
|
||||||
@@ -2685,125 +2569,49 @@ bool RTABMapApp::exportMesh(
|
|||||||
UASSERT((int)polygonMesh->polygons.size() == totalPolygons);
|
UASSERT((int)polygonMesh->polygons.size() == totalPolygons);
|
||||||
if(polygonMesh->polygons.size())
|
if(polygonMesh->polygons.size())
|
||||||
{
|
{
|
||||||
LOGI("Saving ply (%d vertices, %d polygons) to %s.", (int)polygonMesh->cloud.data.size()/polygonMesh->cloud.point_step, totalPolygons, filePath.c_str());
|
// save in database
|
||||||
success = pcl::io::savePLYFile(filePath, *polygonMesh) == 0;
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
|
||||||
if(success)
|
pcl::fromPCLPointCloud2(polygonMesh->cloud, *cloud);
|
||||||
|
cv::Mat cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*cloud)); // for database
|
||||||
|
std::vector<std::vector<std::vector<unsigned int> > > polygons(1);
|
||||||
|
polygons[0].resize(polygonMesh->polygons.size());
|
||||||
|
for(unsigned int p=0; p<polygonMesh->polygons.size(); ++p)
|
||||||
{
|
{
|
||||||
UINFO("Saved ply to %s!", filePath.c_str());
|
polygons[0][p] = polygonMesh->polygons[p].vertices;
|
||||||
exportedMesh_.reset(new pcl::TextureMesh);
|
|
||||||
exportedMesh_->cloud = polygonMesh->cloud;
|
|
||||||
exportedMesh_->tex_polygons.push_back(polygonMesh->polygons);
|
|
||||||
|
|
||||||
// save in database
|
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
|
|
||||||
pcl::fromPCLPointCloud2(polygonMesh->cloud, *cloud);
|
|
||||||
cv::Mat cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*cloud)); // for database
|
|
||||||
std::vector<std::vector<std::vector<unsigned int> > > polygons(exportedMesh_->tex_polygons.size());
|
|
||||||
for(unsigned int t=0; t<exportedMesh_->tex_polygons.size(); ++t)
|
|
||||||
{
|
|
||||||
polygons[t].resize(exportedMesh_->tex_polygons[t].size());
|
|
||||||
for(unsigned int p=0; p<exportedMesh_->tex_polygons[t].size(); ++p)
|
|
||||||
{
|
|
||||||
polygons[t][p] = exportedMesh_->tex_polygons[t][p].vertices;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boost::mutex::scoped_lock lock(rtabmapMutex_);
|
|
||||||
rtabmap_->getMemory()->saveOptimizedMesh(cloudMat, poses, polygons);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UERROR("Failed saving ply to %s!", filePath.c_str());
|
|
||||||
}
|
}
|
||||||
|
boost::mutex::scoped_lock lock(rtabmapMutex_);
|
||||||
|
rtabmap_->getMemory()->saveOptimizedMesh(cloudMat, poses, polygons);
|
||||||
|
success = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(textureMesh->tex_materials.size())
|
else if(textureMesh->tex_materials.size())
|
||||||
{
|
{
|
||||||
// With Sketchfab, the OBJ models are rotated 90 degrees on x axis, so rotate -90 to have model in right position
|
|
||||||
pcl::PointCloud<pcl::PointNormal>::Ptr cloud(new pcl::PointCloud<pcl::PointNormal>);
|
pcl::PointCloud<pcl::PointNormal>::Ptr cloud(new pcl::PointCloud<pcl::PointNormal>);
|
||||||
pcl::fromPCLPointCloud2(textureMesh->cloud, *cloud);
|
pcl::fromPCLPointCloud2(textureMesh->cloud, *cloud);
|
||||||
cv::Mat cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*cloud)); // for database
|
cv::Mat cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*cloud)); // for database
|
||||||
pcl::PCLPointCloud2 tmp = textureMesh->cloud;
|
|
||||||
cloud = rtabmap::util3d::transformPointCloud(cloud, rtabmap::Transform(1,0,0,0, 0,0,1,0, 0,-1,0,0));
|
// save in database
|
||||||
pcl::toPCLPointCloud2(*cloud, textureMesh->cloud);
|
std::vector<std::vector<std::vector<unsigned int> > > polygons(textureMesh->tex_polygons.size());
|
||||||
LOGI("Saving obj (%d vertices, %d polygons) to %s.", (int)textureMesh->cloud.data.size()/textureMesh->cloud.point_step, totalPolygons, filePath.c_str());
|
for(unsigned int t=0; t<textureMesh->tex_polygons.size(); ++t)
|
||||||
success = pcl::io::saveOBJFile(filePath, *textureMesh) == 0;
|
|
||||||
textureMesh->cloud = tmp;
|
|
||||||
if(success)
|
|
||||||
{
|
{
|
||||||
LOGI("Saved obj to %s!", filePath.c_str());
|
polygons[t].resize(textureMesh->tex_polygons[t].size());
|
||||||
exportedMesh_ = textureMesh;
|
for(unsigned int p=0; p<textureMesh->tex_polygons[t].size(); ++p)
|
||||||
|
|
||||||
// save in database
|
|
||||||
{
|
{
|
||||||
std::vector<std::vector<std::vector<unsigned int> > > polygons(exportedMesh_->tex_polygons.size());
|
polygons[t][p] = textureMesh->tex_polygons[t][p].vertices;
|
||||||
for(unsigned int t=0; t<exportedMesh_->tex_polygons.size(); ++t)
|
|
||||||
{
|
|
||||||
polygons[t].resize(exportedMesh_->tex_polygons[t].size());
|
|
||||||
for(unsigned int p=0; p<exportedMesh_->tex_polygons[t].size(); ++p)
|
|
||||||
{
|
|
||||||
polygons[t][p] = exportedMesh_->tex_polygons[t][p].vertices;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boost::mutex::scoped_lock lock(rtabmapMutex_);
|
|
||||||
rtabmap_->getMemory()->saveOptimizedMesh(cloudMat, poses, polygons, exportedMesh_->tex_coordinates, globalTextures);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(globalTextures.cols/globalTextures.rows == 1)
|
|
||||||
{
|
|
||||||
exportedTexture_ = globalTextures;
|
|
||||||
}
|
|
||||||
else if(globalTextures.cols/globalTextures.rows > 1)
|
|
||||||
{
|
|
||||||
// Visualization doesn't support more than one material, so concatenate to 1
|
|
||||||
std::vector<bool> materialsKept;
|
|
||||||
float scale = 0.0f;
|
|
||||||
cv::Size imageSize(globalTextures.rows, globalTextures.rows);
|
|
||||||
int imageType = CV_8UC3;
|
|
||||||
rtabmap::util3d::concatenateTextureMaterials(*exportedMesh_, imageSize, textureSize, 1, scale, &materialsKept);
|
|
||||||
if(scale && exportedMesh_->tex_materials.size() == 1)
|
|
||||||
{
|
|
||||||
int cols = float(textureSize)/(scale*imageSize.width);
|
|
||||||
int rows = float(textureSize)/(scale*imageSize.height);
|
|
||||||
|
|
||||||
exportedTexture_ = cv::Mat(textureSize, textureSize, imageType, cv::Scalar::all(255));
|
|
||||||
|
|
||||||
// make a blank texture
|
|
||||||
cv::Size resizedImageSize(int(imageSize.width*scale), int(imageSize.height*scale));
|
|
||||||
int oi=0;
|
|
||||||
for(int i=0; i<(int)materialsKept.size(); ++i)
|
|
||||||
{
|
|
||||||
if(materialsKept.at(i))
|
|
||||||
{
|
|
||||||
int u = oi%cols * resizedImageSize.width;
|
|
||||||
int v = ((oi/cols) % rows ) * resizedImageSize.height;
|
|
||||||
UASSERT(u < textureSize-resizedImageSize.width);
|
|
||||||
UASSERT(v < textureSize-resizedImageSize.height);
|
|
||||||
|
|
||||||
cv::Mat resizedImage;
|
|
||||||
cv::resize(globalTextures(cv::Range::all(), cv::Range(i*globalTextures.rows, (i+1)*globalTextures.rows)), resizedImage, resizedImageSize, 0.0f, 0.0f, cv::INTER_AREA);
|
|
||||||
|
|
||||||
UASSERT(resizedImage.type() == exportedTexture_.type());
|
|
||||||
resizedImage.copyTo(exportedTexture_(cv::Rect(u, v, resizedImage.cols, resizedImage.rows)));
|
|
||||||
|
|
||||||
++oi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
boost::mutex::scoped_lock lock(rtabmapMutex_);
|
||||||
{
|
rtabmap_->getMemory()->saveOptimizedMesh(cloudMat, poses, polygons, textureMesh->tex_coordinates, globalTextures);
|
||||||
UERROR("Failed saving obj to %s!", filePath.c_str());
|
success = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UERROR("Failed exporting obj to %s! There are no textures!", filePath.c_str());
|
UERROR("Failed exporting texture mesh! There are no textures!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UERROR("Failed exporting to %s! There are no polygons!", filePath.c_str());
|
UERROR("Failed exporting mesh! There are no polygons!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // Point cloud
|
else // Point cloud
|
||||||
@@ -2911,29 +2719,12 @@ bool RTABMapApp::exportMesh(
|
|||||||
mergedClouds = rtabmap::util3d::voxelize(mergedClouds, cloudVoxelSize);
|
mergedClouds = rtabmap::util3d::voxelize(mergedClouds, cloudVoxelSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
pcl::PolygonMesh mesh;
|
// save in database
|
||||||
pcl::toPCLPointCloud2(*mergedClouds, mesh.cloud);
|
|
||||||
|
|
||||||
LOGI("Saving ply (%d points) to %s.", (int)mergedClouds->size(), filePath.c_str());
|
|
||||||
success = pcl::io::savePLYFileBinary(filePath, mesh) == 0;
|
|
||||||
if(success)
|
|
||||||
{
|
{
|
||||||
LOGI("Saved ply to %s!", filePath.c_str());
|
cv::Mat cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*mergedClouds)); // for database
|
||||||
|
boost::mutex::scoped_lock lock(rtabmapMutex_);
|
||||||
// save in database
|
rtabmap_->getMemory()->saveOptimizedMesh(cloudMat, poses);
|
||||||
{
|
success = true;
|
||||||
cv::Mat cloudMat = rtabmap::compressData2(rtabmap::util3d::laserScanFromPointCloud(*mergedClouds)); // for database
|
|
||||||
boost::mutex::scoped_lock lock(rtabmapMutex_);
|
|
||||||
rtabmap_->getMemory()->saveOptimizedMesh(cloudMat, poses);
|
|
||||||
}
|
|
||||||
|
|
||||||
mergedClouds->clear();
|
|
||||||
exportedMesh_.reset(new pcl::TextureMesh);
|
|
||||||
exportedMesh_->cloud = mesh.cloud;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UERROR("Failed saving ply to %s!", filePath.c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2964,23 +2755,148 @@ bool RTABMapApp::exportMesh(
|
|||||||
bool RTABMapApp::postExportation(bool visualize)
|
bool RTABMapApp::postExportation(bool visualize)
|
||||||
{
|
{
|
||||||
LOGI("postExportation(visualize=%d)", visualize?1:0);
|
LOGI("postExportation(visualize=%d)", visualize?1:0);
|
||||||
if(visualize && exportedMesh_->cloud.data.size())
|
optMesh_.reset(new pcl::TextureMesh);
|
||||||
|
optTexture_ = cv::Mat();
|
||||||
|
exportedMeshUpdated_ = false;
|
||||||
|
visualizingMesh_ = false;
|
||||||
|
|
||||||
|
if(visualize)
|
||||||
{
|
{
|
||||||
boost::mutex::scoped_lock lock(renderingMutex_);
|
cv::Mat cloudMat;
|
||||||
visualizingMesh_ = true;
|
std::vector<std::vector<std::vector<unsigned int> > > polygons;
|
||||||
exportedMeshUpdated_ = true;
|
std::vector<std::vector<Eigen::Vector2f> > texCoords;
|
||||||
}
|
cv::Mat textures;
|
||||||
else
|
std::map<int, rtabmap::Transform> optPoses;
|
||||||
{
|
if(rtabmap_ && rtabmap_->getMemory())
|
||||||
exportedMesh_.reset(new pcl::TextureMesh);
|
{
|
||||||
exportedTexture_ = cv::Mat();
|
cloudMat = rtabmap_->getMemory()->loadOptimizedMesh(&optPoses, &polygons, &texCoords, &textures);
|
||||||
exportedMeshUpdated_ = false;
|
if(!cloudMat.empty())
|
||||||
visualizingMesh_ = false;
|
{
|
||||||
|
LOGI("postExportation: Found optimized mesh! Visualizing it.");
|
||||||
|
optMesh_ = rtabmap::util3d::assembleTextureMesh(cloudMat, polygons, texCoords, textures, true);
|
||||||
|
optTexture_ = textures;
|
||||||
|
|
||||||
|
boost::mutex::scoped_lock lock(renderingMutex_);
|
||||||
|
visualizingMesh_ = true;
|
||||||
|
exportedMeshUpdated_ = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGI("postExportation: No optimized mesh found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return visualizingMesh_;
|
return visualizingMesh_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RTABMapApp::writeExportedMesh(const std::string & directory, const std::string & name)
|
||||||
|
{
|
||||||
|
LOGI("writeExportedMesh: dir=%s name=%s", directory.c_str(), name.c_str());
|
||||||
|
exporting_ = true;
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
pcl::PolygonMesh::Ptr polygonMesh(new pcl::PolygonMesh);
|
||||||
|
pcl::TextureMesh::Ptr textureMesh(new pcl::TextureMesh);
|
||||||
|
cv::Mat cloudMat;
|
||||||
|
std::vector<std::vector<std::vector<unsigned int> > > polygons;
|
||||||
|
std::vector<std::vector<Eigen::Vector2f> > texCoords;
|
||||||
|
cv::Mat textures;
|
||||||
|
std::map<int, rtabmap::Transform> optPoses;
|
||||||
|
if(rtabmap_ && rtabmap_->getMemory())
|
||||||
|
{
|
||||||
|
cloudMat = rtabmap_->getMemory()->loadOptimizedMesh(&optPoses, &polygons, &texCoords, &textures);
|
||||||
|
if(!cloudMat.empty())
|
||||||
|
{
|
||||||
|
LOGI("writeExportedMesh: Found optimized mesh!");
|
||||||
|
if(textures.empty())
|
||||||
|
{
|
||||||
|
polygonMesh = rtabmap::util3d::assemblePolygonMesh(cloudMat, polygons.size() == 1?polygons[0]:std::vector<std::vector<unsigned int> >());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
textureMesh = rtabmap::util3d::assembleTextureMesh(cloudMat, polygons, texCoords, textures, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGI("writeExportedMesh: No optimized mesh found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(polygonMesh->cloud.data.size())
|
||||||
|
{
|
||||||
|
// Point cloud PLY
|
||||||
|
std::string filePath = directory + UDirectory::separator() + name + ".ply";
|
||||||
|
LOGI("Saving ply (%d vertices, %d polygons) to %s.", (int)polygonMesh->cloud.data.size()/polygonMesh->cloud.point_step, (int)polygonMesh->polygons.size(), filePath.c_str());
|
||||||
|
success = pcl::io::savePLYFileBinary(filePath, *polygonMesh) == 0;
|
||||||
|
if(success)
|
||||||
|
{
|
||||||
|
LOGI("Saved ply to %s!", filePath.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UERROR("Failed saving ply to %s!", filePath.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(textureMesh->cloud.data.size())
|
||||||
|
{
|
||||||
|
// TextureMesh OBJ
|
||||||
|
LOGD("Saving texture(s) (%d)", textures.empty()?0:textures.cols/textures.rows);
|
||||||
|
UASSERT(textures.empty() || textures.cols % textures.rows == 0);
|
||||||
|
UASSERT((int)textureMesh->tex_materials.size() == textures.cols/textures.rows);
|
||||||
|
for(unsigned int i=0; i<textureMesh->tex_materials.size(); ++i)
|
||||||
|
{
|
||||||
|
std::string baseNameNum = name;
|
||||||
|
if(textureMesh->tex_materials.size()>1)
|
||||||
|
{
|
||||||
|
baseNameNum+=uNumber2Str(i);
|
||||||
|
}
|
||||||
|
std::string fullPath = directory+UDirectory::separator()+baseNameNum+".jpg";
|
||||||
|
textureMesh->tex_materials[i].tex_file = baseNameNum+".jpg";
|
||||||
|
LOGI("Saving texture to %s.", fullPath.c_str());
|
||||||
|
success = cv::imwrite(fullPath, textures(cv::Range::all(), cv::Range(i*textures.rows, (i+1)*textures.rows)));
|
||||||
|
if(!success)
|
||||||
|
{
|
||||||
|
LOGI("Failed saving %s!", fullPath.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGI("Saved %s.", fullPath.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(success)
|
||||||
|
{
|
||||||
|
// With Sketchfab, the OBJ models are rotated 90 degrees on x axis, so rotate -90 to have model in right position
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr cloud(new pcl::PointCloud<pcl::PointNormal>);
|
||||||
|
pcl::fromPCLPointCloud2(textureMesh->cloud, *cloud);
|
||||||
|
cloud = rtabmap::util3d::transformPointCloud(cloud, rtabmap::Transform(1,0,0,0, 0,0,1,0, 0,-1,0,0));
|
||||||
|
pcl::toPCLPointCloud2(*cloud, textureMesh->cloud);
|
||||||
|
std::string filePath = directory + UDirectory::separator() + name + ".obj";
|
||||||
|
int totalPolygons = 0;
|
||||||
|
for(unsigned int i=0;i<textureMesh->tex_polygons.size(); ++i)
|
||||||
|
{
|
||||||
|
totalPolygons += textureMesh->tex_polygons[i].size();
|
||||||
|
}
|
||||||
|
LOGI("Saving obj (%d vertices, %d polygons) to %s.", (int)textureMesh->cloud.data.size()/textureMesh->cloud.point_step, totalPolygons, filePath.c_str());
|
||||||
|
success = pcl::io::saveOBJFile(filePath, *textureMesh) == 0;
|
||||||
|
|
||||||
|
if(success)
|
||||||
|
{
|
||||||
|
LOGI("Saved obj to %s!", filePath.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UERROR("Failed saving obj to %s!", filePath.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exporting_ = false;
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
int RTABMapApp::postProcessing(int approach)
|
int RTABMapApp::postProcessing(int approach)
|
||||||
{
|
{
|
||||||
postProcessing_ = true;
|
postProcessing_ = true;
|
||||||
|
|||||||
@@ -151,7 +151,6 @@ class RTABMapApp : public UEventsHandler {
|
|||||||
void save(const std::string & databasePath);
|
void save(const std::string & databasePath);
|
||||||
void cancelProcessing();
|
void cancelProcessing();
|
||||||
bool exportMesh(
|
bool exportMesh(
|
||||||
const std::string & filePath,
|
|
||||||
float cloudVoxelSize,
|
float cloudVoxelSize,
|
||||||
bool regenerateCloud,
|
bool regenerateCloud,
|
||||||
bool meshing,
|
bool meshing,
|
||||||
@@ -169,6 +168,7 @@ class RTABMapApp : public UEventsHandler {
|
|||||||
int optimizedMinTextureClusterSize,
|
int optimizedMinTextureClusterSize,
|
||||||
bool blockRendering);
|
bool blockRendering);
|
||||||
bool postExportation(bool visualize);
|
bool postExportation(bool visualize);
|
||||||
|
bool writeExportedMesh(const std::string & directory, const std::string & name);
|
||||||
int postProcessing(int approach);
|
int postProcessing(int approach);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -232,9 +232,8 @@ class RTABMapApp : public UEventsHandler {
|
|||||||
|
|
||||||
bool visualizingMesh_;
|
bool visualizingMesh_;
|
||||||
bool exportedMeshUpdated_;
|
bool exportedMeshUpdated_;
|
||||||
pcl::TextureMesh::Ptr exportedMesh_;
|
pcl::TextureMesh::Ptr optMesh_;
|
||||||
cv::Mat exportedTexture_;
|
cv::Mat optTexture_;
|
||||||
std::map<int, rtabmap::Transform> exportedPoses_;
|
|
||||||
|
|
||||||
// main_scene_ includes all drawable object for visualizing Tango device's
|
// main_scene_ includes all drawable object for visualizing Tango device's
|
||||||
// movement and point cloud.
|
// movement and point cloud.
|
||||||
|
|||||||
@@ -359,7 +359,6 @@ Java_com_introlab_rtabmap_RTABMapLib_cancelProcessing(
|
|||||||
JNIEXPORT bool JNICALL
|
JNIEXPORT bool JNICALL
|
||||||
Java_com_introlab_rtabmap_RTABMapLib_exportMesh(
|
Java_com_introlab_rtabmap_RTABMapLib_exportMesh(
|
||||||
JNIEnv* env, jobject,
|
JNIEnv* env, jobject,
|
||||||
jstring filePath,
|
|
||||||
float cloudVoxelSize,
|
float cloudVoxelSize,
|
||||||
bool regenerateCloud,
|
bool regenerateCloud,
|
||||||
bool meshing,
|
bool meshing,
|
||||||
@@ -377,10 +376,7 @@ Java_com_introlab_rtabmap_RTABMapLib_exportMesh(
|
|||||||
int optimizedMinTextureClusterSize,
|
int optimizedMinTextureClusterSize,
|
||||||
bool blockRendering)
|
bool blockRendering)
|
||||||
{
|
{
|
||||||
std::string filePathC;
|
|
||||||
GetJStringContent(env,filePath,filePathC);
|
|
||||||
return app.exportMesh(
|
return app.exportMesh(
|
||||||
filePathC,
|
|
||||||
cloudVoxelSize,
|
cloudVoxelSize,
|
||||||
regenerateCloud,
|
regenerateCloud,
|
||||||
meshing,
|
meshing,
|
||||||
@@ -406,6 +402,18 @@ Java_com_introlab_rtabmap_RTABMapLib_postExportation(
|
|||||||
return app.postExportation(visualize);
|
return app.postExportation(visualize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT bool JNICALL
|
||||||
|
Java_com_introlab_rtabmap_RTABMapLib_writeExportedMesh(
|
||||||
|
JNIEnv* env, jobject, jstring directory, jstring name)
|
||||||
|
{
|
||||||
|
std::string directoryC;
|
||||||
|
GetJStringContent(env,directory,directoryC);
|
||||||
|
std::string nameC;
|
||||||
|
GetJStringContent(env,name,nameC);
|
||||||
|
return app.writeExportedMesh(directoryC, nameC);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
JNIEXPORT int JNICALL
|
JNIEXPORT int JNICALL
|
||||||
Java_com_introlab_rtabmap_RTABMapLib_postProcessing(
|
Java_com_introlab_rtabmap_RTABMapLib_postProcessing(
|
||||||
JNIEnv* env, jobject, int approach)
|
JNIEnv* env, jobject, int approach)
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
public static final String RTABMAP_AUTH_TOKEN_KEY = "com.introlab.rtabmap.AUTH_TOKEN";
|
public static final String RTABMAP_AUTH_TOKEN_KEY = "com.introlab.rtabmap.AUTH_TOKEN";
|
||||||
public static final String RTABMAP_FILENAME_KEY = "com.introlab.rtabmap.FILENAME";
|
public static final String RTABMAP_FILENAME_KEY = "com.introlab.rtabmap.FILENAME";
|
||||||
public static final String RTABMAP_OPENED_DB_PATH_KEY = "com.introlab.rtabmap.OPENED_DB_PATH";
|
public static final String RTABMAP_OPENED_DB_PATH_KEY = "com.introlab.rtabmap.OPENED_DB_PATH";
|
||||||
public static final String RTABMAP_EXPORTED_OBJ_KEY = "com.introlab.rtabmap.EXPORTED_OBJ";
|
|
||||||
public static final String RTABMAP_WORKING_DIR_KEY = "com.introlab.rtabmap.WORKING_DIR";
|
public static final String RTABMAP_WORKING_DIR_KEY = "com.introlab.rtabmap.WORKING_DIR";
|
||||||
public static final int SKETCHFAB_ACTIVITY_CODE = 999;
|
public static final int SKETCHFAB_ACTIVITY_CODE = 999;
|
||||||
private String mAuthToken;
|
private String mAuthToken;
|
||||||
@@ -198,7 +197,6 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
|
|
||||||
private int mTotalLoopClosures = 0;
|
private int mTotalLoopClosures = 0;
|
||||||
private boolean mMapIsEmpty = false;
|
private boolean mMapIsEmpty = false;
|
||||||
private boolean mExportedOBJ = false;
|
|
||||||
int mMapNodes = 0;
|
int mMapNodes = 0;
|
||||||
|
|
||||||
private Toast mToast = null;
|
private Toast mToast = null;
|
||||||
@@ -818,11 +816,11 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
{
|
{
|
||||||
if(mItemStatusVisibility != null && mItemDebugVisibility != null)
|
if(mItemStatusVisibility != null && mItemDebugVisibility != null)
|
||||||
{
|
{
|
||||||
if(mItemStatusVisibility.isChecked() && mItemDebugVisibility.isChecked())
|
if((mItemStatusVisibility.isChecked() || mState == State.STATE_VISUALIZING_WHILE_LOADING) && mItemDebugVisibility.isChecked())
|
||||||
{
|
{
|
||||||
mRenderer.updateTexts(mStatusTexts);
|
mRenderer.updateTexts(mStatusTexts);
|
||||||
}
|
}
|
||||||
else if(mItemStatusVisibility.isChecked())
|
else if((mItemStatusVisibility.isChecked() || mState == State.STATE_VISUALIZING_WHILE_LOADING))
|
||||||
{
|
{
|
||||||
mRenderer.updateTexts(Arrays.copyOfRange(mStatusTexts, 0, 3));
|
mRenderer.updateTexts(Arrays.copyOfRange(mStatusTexts, 0, 3));
|
||||||
}
|
}
|
||||||
@@ -1040,6 +1038,46 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
{
|
{
|
||||||
if(!DISABLE_LOG) Log.i(TAG, String.format("rtabmapInitEventsUI() status=%d msg=%s", status, msg));
|
if(!DISABLE_LOG) Log.i(TAG, String.format("rtabmapInitEventsUI() status=%d msg=%s", status, msg));
|
||||||
|
|
||||||
|
int optimizedMeshDetected = 0;
|
||||||
|
|
||||||
|
if(msg.equals("Loading optimized cloud...done!"))
|
||||||
|
{
|
||||||
|
optimizedMeshDetected = 1;
|
||||||
|
}
|
||||||
|
else if(msg.equals("Loading optimized mesh...done!"))
|
||||||
|
{
|
||||||
|
optimizedMeshDetected = 2;
|
||||||
|
}
|
||||||
|
else if(msg.equals("Loading optimized texture mesh...done!"))
|
||||||
|
{
|
||||||
|
optimizedMeshDetected = 3;
|
||||||
|
}
|
||||||
|
if(optimizedMeshDetected > 0)
|
||||||
|
{
|
||||||
|
resetNoTouchTimer();
|
||||||
|
mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
|
||||||
|
if(optimizedMeshDetected==1)
|
||||||
|
{
|
||||||
|
mItemRenderingPointCloud.setChecked(true);
|
||||||
|
}
|
||||||
|
else if(optimizedMeshDetected==2)
|
||||||
|
{
|
||||||
|
mItemRenderingMesh.setChecked(true);
|
||||||
|
}
|
||||||
|
else // isOBJ
|
||||||
|
{
|
||||||
|
mItemRenderingTextureMesh.setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState(State.STATE_VISUALIZING_WHILE_LOADING);
|
||||||
|
if(mButtonFirst.isChecked())
|
||||||
|
{
|
||||||
|
setCamera(1);
|
||||||
|
}
|
||||||
|
mToast.makeText(getActivity(), String.format("Optimized mesh detected in the database, it is shown while the database is loading..."), mToast.LENGTH_LONG).show();
|
||||||
|
mProgressDialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
if(mButtonPause!=null)
|
if(mButtonPause!=null)
|
||||||
{
|
{
|
||||||
if(mButtonPause.isChecked())
|
if(mButtonPause.isChecked())
|
||||||
@@ -1339,7 +1377,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
mDateOnPause = new Date();
|
mDateOnPause = new Date();
|
||||||
|
|
||||||
long memoryFree = getFreeMemory();
|
long memoryFree = getFreeMemory();
|
||||||
if(!mOnPause && !mItemLocalizationMode.isChecked() && !mItemDataRecorderMode.isChecked() && memoryFree >= 100)
|
if(!mOnPause && !mItemLocalizationMode.isChecked() && !mItemDataRecorderMode.isChecked() && memoryFree >= 100 && mMapNodes>2)
|
||||||
{
|
{
|
||||||
// Do standard post processing?
|
// Do standard post processing?
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
@@ -1882,34 +1920,13 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
|
|
||||||
mExportProgressDialog.show();
|
mExportProgressDialog.show();
|
||||||
updateState(State.STATE_PROCESSING);
|
updateState(State.STATE_PROCESSING);
|
||||||
final String tmpPath = mWorkingDirectory + RTABMAP_TMP_DIR + "/" + RTABMAP_TMP_FILENAME + extension;
|
|
||||||
|
|
||||||
File tmpDir = new File(mWorkingDirectory + RTABMAP_TMP_DIR);
|
|
||||||
tmpDir.mkdirs();
|
|
||||||
String[] fileNames = Util.loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR, false);
|
|
||||||
if(!DISABLE_LOG) Log.i(TAG, String.format("Deleting %d files in \"%s\"", fileNames.length, mWorkingDirectory + RTABMAP_TMP_DIR));
|
|
||||||
for(int i=0; i<fileNames.length; ++i)
|
|
||||||
{
|
|
||||||
File f = new File(mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames[i]);
|
|
||||||
if(f.delete())
|
|
||||||
{
|
|
||||||
if(!DISABLE_LOG) Log.i(TAG, String.format("Deleted \"%s\"", f.getPath()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(!DISABLE_LOG) Log.i(TAG, String.format("Failed deleting \"%s\"", f.getPath()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File exportDir = new File(mWorkingDirectory + RTABMAP_EXPORT_DIR);
|
|
||||||
exportDir.mkdirs();
|
|
||||||
|
|
||||||
Thread exportThread = new Thread(new Runnable() {
|
Thread exportThread = new Thread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
final long startTime = System.currentTimeMillis()/1000;
|
final long startTime = System.currentTimeMillis()/1000;
|
||||||
|
|
||||||
final boolean success = RTABMapLib.exportMesh(
|
final boolean success = RTABMapLib.exportMesh(
|
||||||
tmpPath,
|
|
||||||
cloudVoxelSize,
|
cloudVoxelSize,
|
||||||
regenerateCloud,
|
regenerateCloud,
|
||||||
meshing,
|
meshing,
|
||||||
@@ -1946,7 +1963,6 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
.setMessage(Html.fromHtml("Do you want visualize the result before saving to file or sharing to <a href=\"https://sketchfab.com/about\">Sketchfab</a>?"))
|
.setMessage(Html.fromHtml("Do you want visualize the result before saving to file or sharing to <a href=\"https://sketchfab.com/about\">Sketchfab</a>?"))
|
||||||
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
mExportedOBJ = isOBJ;
|
|
||||||
resetNoTouchTimer();
|
resetNoTouchTimer();
|
||||||
mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
|
mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
|
||||||
if(!meshing)
|
if(!meshing)
|
||||||
@@ -1976,7 +1992,6 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
})
|
})
|
||||||
.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
mExportedOBJ = isOBJ;
|
|
||||||
updateState(State.STATE_IDLE);
|
updateState(State.STATE_IDLE);
|
||||||
RTABMapLib.postExportation(false);
|
RTABMapLib.postExportation(false);
|
||||||
|
|
||||||
@@ -2091,8 +2106,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
private void saveOnDevice()
|
private void saveOnDevice()
|
||||||
{
|
{
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
final String extension = mExportedOBJ?".obj":".ply";
|
builder.setTitle("Model Name:");
|
||||||
builder.setTitle(String.format("File Name (*%s):", extension));
|
|
||||||
final EditText input = new EditText(this);
|
final EditText input = new EditText(this);
|
||||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||||
builder.setView(input);
|
builder.setView(input);
|
||||||
@@ -2126,7 +2140,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
if(!fileName.isEmpty())
|
if(!fileName.isEmpty())
|
||||||
{
|
{
|
||||||
File newFile = new File(mWorkingDirectory + RTABMAP_EXPORT_DIR + fileName + (mExportedOBJ?".zip":".ply"));
|
File newFile = new File(mWorkingDirectory + RTABMAP_EXPORT_DIR + fileName + ".zip");
|
||||||
if(newFile.exists())
|
if(newFile.exists())
|
||||||
{
|
{
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
@@ -2134,7 +2148,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
.setMessage("Do you want to overwrite the existing file?")
|
.setMessage("Do you want to overwrite the existing file?")
|
||||||
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
writeExportedFiles(fileName, mExportedOBJ);
|
writeExportedFiles(fileName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
||||||
@@ -2146,7 +2160,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
writeExportedFiles(fileName, mExportedOBJ);
|
writeExportedFiles(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2156,86 +2170,110 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
alertToShow.show();
|
alertToShow.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeExportedFiles(String fileName, boolean isOBJ)
|
private void writeExportedFiles(final String fileName)
|
||||||
{
|
{
|
||||||
String pathHuman;
|
Log.i(TAG, String.format("Write exported mesh to \"%s\"", fileName));
|
||||||
boolean success = true;
|
|
||||||
if(mExportedOBJ)
|
mProgressDialog.setTitle("Saving to sd-card");
|
||||||
{
|
mProgressDialog.setMessage(String.format("Compressing the files..."));
|
||||||
final String zipOutput = mWorkingDirectory+RTABMAP_EXPORT_DIR+fileName+".zip";
|
mProgressDialog.show();
|
||||||
pathHuman = mWorkingDirectoryHuman + RTABMAP_EXPORT_DIR + fileName + ".zip";
|
|
||||||
|
Thread workingThread = new Thread(new Runnable() {
|
||||||
String[] fileNames = Util.loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR, false);
|
public void run() {
|
||||||
if(fileNames.length > 0)
|
boolean success = false;
|
||||||
{
|
|
||||||
String[] filesToZip = new String[fileNames.length];
|
File tmpDir = new File(mWorkingDirectory + RTABMAP_TMP_DIR);
|
||||||
|
tmpDir.mkdirs();
|
||||||
|
String[] fileNames = Util.loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR, false);
|
||||||
|
if(!DISABLE_LOG) Log.i(TAG, String.format("Deleting %d files in \"%s\"", fileNames.length, mWorkingDirectory + RTABMAP_TMP_DIR));
|
||||||
for(int i=0; i<fileNames.length; ++i)
|
for(int i=0; i<fileNames.length; ++i)
|
||||||
{
|
{
|
||||||
filesToZip[i] = mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames[i];
|
File f = new File(mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames[i]);
|
||||||
|
if(f.delete())
|
||||||
|
{
|
||||||
|
if(!DISABLE_LOG) Log.i(TAG, String.format("Deleted \"%s\"", f.getPath()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!DISABLE_LOG) Log.i(TAG, String.format("Failed deleting \"%s\"", f.getPath()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
File exportDir = new File(mWorkingDirectory + RTABMAP_EXPORT_DIR);
|
||||||
File toZIPFile = new File(zipOutput);
|
exportDir.mkdirs();
|
||||||
toZIPFile.delete();
|
|
||||||
|
|
||||||
try
|
final String pathHuman = mWorkingDirectoryHuman + RTABMAP_EXPORT_DIR + fileName + ".zip";
|
||||||
|
if(RTABMapLib.writeExportedMesh(mWorkingDirectory + RTABMAP_TMP_DIR, RTABMAP_TMP_FILENAME))
|
||||||
{
|
{
|
||||||
Util.zip(filesToZip, zipOutput);
|
final String zipOutput = mWorkingDirectory+RTABMAP_EXPORT_DIR+fileName+".zip";
|
||||||
mToast.makeText(getActivity(), String.format("Mesh \"%s\" successfully exported!", pathHuman), mToast.LENGTH_LONG).show();
|
|
||||||
|
fileNames = Util.loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR, false);
|
||||||
|
if(fileNames.length > 0)
|
||||||
|
{
|
||||||
|
String[] filesToZip = new String[fileNames.length];
|
||||||
|
for(int i=0; i<fileNames.length; ++i)
|
||||||
|
{
|
||||||
|
filesToZip[i] = mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
File toZIPFile = new File(zipOutput);
|
||||||
|
toZIPFile.delete();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Util.zip(filesToZip, zipOutput);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
catch(IOException e)
|
||||||
|
{
|
||||||
|
final String msg = e.getMessage();
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! Error=%s", pathHuman, msg), mToast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(IOException e)
|
|
||||||
|
if(success)
|
||||||
{
|
{
|
||||||
mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! Error=%s", pathHuman, e.getMessage()), mToast.LENGTH_LONG).show();
|
runOnUiThread(new Runnable() {
|
||||||
success = false;
|
public void run() {
|
||||||
|
mProgressDialog.dismiss();
|
||||||
|
mToast.makeText(getActivity(), String.format("Mesh \"%s\" successfully exported!", pathHuman), mToast.LENGTH_LONG).show();
|
||||||
|
Intent intent = new Intent(getActivity(), RTABMapActivity.class);
|
||||||
|
// use System.currentTimeMillis() to have a unique ID for the pending intent
|
||||||
|
PendingIntent pIntent = PendingIntent.getActivity(getActivity(), (int) System.currentTimeMillis(), intent, 0);
|
||||||
|
|
||||||
|
// build notification
|
||||||
|
// the addAction re-use the same intent to keep the example short
|
||||||
|
Notification n = new Notification.Builder(getActivity())
|
||||||
|
.setContentTitle(getString(R.string.app_name))
|
||||||
|
.setContentText(pathHuman + " exported!")
|
||||||
|
.setSmallIcon(R.drawable.ic_launcher)
|
||||||
|
.setContentIntent(pIntent)
|
||||||
|
.setAutoCancel(true).build();
|
||||||
|
|
||||||
|
|
||||||
|
NotificationManager notificationManager =
|
||||||
|
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
|
notificationManager.notify(0, n);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mProgressDialog.dismiss();
|
||||||
|
mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! No files found in tmp directory!? Last export may have failed or have been canceled.", pathHuman), mToast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
});
|
||||||
{
|
workingThread.start();
|
||||||
mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! No files found in tmp directory!? Last export may have failed or have been canceled.", pathHuman), mToast.LENGTH_LONG).show();
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
final String path = mWorkingDirectory + RTABMAP_EXPORT_DIR+ fileName + ".ply";
|
|
||||||
pathHuman = mWorkingDirectoryHuman + RTABMAP_EXPORT_DIR + fileName + ".ply";
|
|
||||||
|
|
||||||
File toPLYFile = new File(path);
|
|
||||||
toPLYFile.delete();
|
|
||||||
File fromPLYFile = new File(mWorkingDirectory + RTABMAP_TMP_DIR + "/" + RTABMAP_TMP_FILENAME + ".ply");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
copy(fromPLYFile,toPLYFile);
|
|
||||||
mToast.makeText(getActivity(), String.format("Mesh/point cloud \"%s\" successfully exported!", pathHuman), mToast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
mToast.makeText(getActivity(), String.format("Exporting mesh/point cloud \"%s\" failed! Error=%s", pathHuman, e.getMessage()), mToast.LENGTH_LONG).show();
|
|
||||||
success=false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(success)
|
|
||||||
{
|
|
||||||
Intent intent = new Intent(getActivity(), RTABMapActivity.class);
|
|
||||||
// use System.currentTimeMillis() to have a unique ID for the pending intent
|
|
||||||
PendingIntent pIntent = PendingIntent.getActivity(getActivity(), (int) System.currentTimeMillis(), intent, 0);
|
|
||||||
|
|
||||||
// build notification
|
|
||||||
// the addAction re-use the same intent to keep the example short
|
|
||||||
Notification n = new Notification.Builder(getActivity())
|
|
||||||
.setContentTitle(getString(R.string.app_name))
|
|
||||||
.setContentText(pathHuman + " exported!")
|
|
||||||
.setSmallIcon(R.drawable.ic_launcher)
|
|
||||||
.setContentIntent(pIntent)
|
|
||||||
.setAutoCancel(true).build();
|
|
||||||
|
|
||||||
|
|
||||||
NotificationManager notificationManager =
|
|
||||||
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
|
||||||
|
|
||||||
notificationManager.notify(0, n);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openDatabase(final String fileName, final boolean optimize)
|
private void openDatabase(final String fileName, final boolean optimize)
|
||||||
@@ -2255,48 +2293,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
|
|
||||||
Thread openThread = new Thread(new Runnable() {
|
Thread openThread = new Thread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
SQLiteDatabase db = null;
|
|
||||||
try {
|
|
||||||
db = SQLiteDatabase.openDatabase(mOpenedDatabasePath, null, SQLiteDatabase.OPEN_READONLY);
|
|
||||||
|
|
||||||
// get version
|
|
||||||
Cursor c1 = db.rawQuery("SELECT version FROM Admin", null);
|
|
||||||
if(c1.moveToFirst()) {
|
|
||||||
String version = c1.getString(c1.getColumnIndex("version"));
|
|
||||||
Log.i(TAG, "Version="+version);
|
|
||||||
if(Util.versionCompare(version, "0.13.0") >= 0) {
|
|
||||||
Cursor c2 = db.rawQuery("SELECT version FROM Admin WHERE opt_cloud is not null", null);
|
|
||||||
if(c2.moveToFirst()) {
|
|
||||||
Log.i(TAG, "Found optimized mesh");
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
mProgressDialog.dismiss();
|
|
||||||
updateState(State.STATE_VISUALIZING_WHILE_LOADING);
|
|
||||||
mToast.makeText(getActivity(), String.format("Optimized mesh detected in the database. It will be shown while the database is loading..."), mToast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.i(TAG, "Not found optimized mesh");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.e(TAG, "Failed getting version from database");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, e.getMessage());
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if(db != null && db.isOpen()) {
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
|
final String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
|
||||||
final int status = RTABMapLib.openDatabase2(mOpenedDatabasePath, tmpDatabase, databaseInMemory, optimize);
|
final int status = RTABMapLib.openDatabase2(mOpenedDatabasePath, tmpDatabase, databaseInMemory, optimize);
|
||||||
|
|
||||||
@@ -2354,25 +2351,15 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
{
|
{
|
||||||
mProgressDialog.dismiss();
|
mProgressDialog.dismiss();
|
||||||
resetNoTouchTimer();
|
resetNoTouchTimer();
|
||||||
mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
|
|
||||||
if(status==1)
|
|
||||||
{
|
|
||||||
mItemRenderingPointCloud.setChecked(true);
|
|
||||||
}
|
|
||||||
else if(status==2)
|
|
||||||
{
|
|
||||||
mItemRenderingMesh.setChecked(true);
|
|
||||||
}
|
|
||||||
else // isOBJ
|
|
||||||
{
|
|
||||||
mItemRenderingTextureMesh.setChecked(true);
|
|
||||||
}
|
|
||||||
updateState(State.STATE_VISUALIZING);
|
updateState(State.STATE_VISUALIZING);
|
||||||
mToast.makeText(getActivity(), String.format("Database loaded!"), mToast.LENGTH_LONG).show();
|
mToast.makeText(getActivity(), String.format("Database loaded!"), mToast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
else if(!mItemTrajectoryMode.isChecked())
|
else if(!mItemTrajectoryMode.isChecked())
|
||||||
{
|
{
|
||||||
setCamera(1);
|
if(mButtonFirst.isChecked())
|
||||||
|
{
|
||||||
|
setCamera(1);
|
||||||
|
}
|
||||||
// creating meshes...
|
// creating meshes...
|
||||||
updateState(State.STATE_IDLE);
|
updateState(State.STATE_IDLE);
|
||||||
mProgressDialog.setTitle("Loading");
|
mProgressDialog.setTitle("Loading");
|
||||||
@@ -2406,7 +2393,6 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
|||||||
Intent intent = new Intent(getActivity(), SketchfabActivity.class);
|
Intent intent = new Intent(getActivity(), SketchfabActivity.class);
|
||||||
|
|
||||||
intent.putExtra(RTABMAP_AUTH_TOKEN_KEY, mAuthToken);
|
intent.putExtra(RTABMAP_AUTH_TOKEN_KEY, mAuthToken);
|
||||||
intent.putExtra(RTABMAP_EXPORTED_OBJ_KEY, mExportedOBJ);
|
|
||||||
intent.putExtra(RTABMAP_WORKING_DIR_KEY, mWorkingDirectory);
|
intent.putExtra(RTABMAP_WORKING_DIR_KEY, mWorkingDirectory);
|
||||||
|
|
||||||
if(mOpenedDatabasePath.isEmpty())
|
if(mOpenedDatabasePath.isEmpty())
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ public class RTABMapLib
|
|||||||
public static native void save(String outputDatabasePath);
|
public static native void save(String outputDatabasePath);
|
||||||
public static native void cancelProcessing();
|
public static native void cancelProcessing();
|
||||||
public static native boolean exportMesh(
|
public static native boolean exportMesh(
|
||||||
String filePath,
|
|
||||||
float cloudVoxelSize,
|
float cloudVoxelSize,
|
||||||
boolean regenerateCloud,
|
boolean regenerateCloud,
|
||||||
boolean meshing,
|
boolean meshing,
|
||||||
@@ -113,6 +112,7 @@ public class RTABMapLib
|
|||||||
float optimizedMaxTextureDistance,
|
float optimizedMaxTextureDistance,
|
||||||
int optimizedMinTextureClusterSize,
|
int optimizedMinTextureClusterSize,
|
||||||
boolean blockRendering);
|
boolean blockRendering);
|
||||||
|
public static native boolean writeExportedMesh(String directory, String name);
|
||||||
public static native boolean postExportation(boolean visualize);
|
public static native boolean postExportation(boolean visualize);
|
||||||
public static native int postProcessing(int approach);
|
public static native int postProcessing(int approach);
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ public class Renderer implements GLSurfaceView.Renderer {
|
|||||||
public void run() {
|
public void run() {
|
||||||
if(!RTABMapActivity.DISABLE_LOG) Log.i("RTABMapActivity", "Renderer: dismiss dialog, value received=" + String.valueOf(value));
|
if(!RTABMapActivity.DISABLE_LOG) Log.i("RTABMapActivity", "Renderer: dismiss dialog, value received=" + String.valueOf(value));
|
||||||
mProgressDialog.dismiss();
|
mProgressDialog.dismiss();
|
||||||
|
mActivity.resetNoTouchTimer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ public class SketchfabActivity extends Activity implements OnClickListener {
|
|||||||
private Dialog mAuthDialog;
|
private Dialog mAuthDialog;
|
||||||
|
|
||||||
private String mAuthToken;
|
private String mAuthToken;
|
||||||
private boolean mExportedOBJ;
|
|
||||||
private String mWorkingDirectory;
|
private String mWorkingDirectory;
|
||||||
|
|
||||||
EditText mFilename;
|
EditText mFilename;
|
||||||
@@ -70,7 +69,6 @@ public class SketchfabActivity extends Activity implements OnClickListener {
|
|||||||
mProgressDialog.setCanceledOnTouchOutside(false);
|
mProgressDialog.setCanceledOnTouchOutside(false);
|
||||||
|
|
||||||
mAuthToken = getIntent().getExtras().getString(RTABMapActivity.RTABMAP_AUTH_TOKEN_KEY);
|
mAuthToken = getIntent().getExtras().getString(RTABMapActivity.RTABMAP_AUTH_TOKEN_KEY);
|
||||||
mExportedOBJ = getIntent().getExtras().getBoolean(RTABMapActivity.RTABMAP_EXPORTED_OBJ_KEY);
|
|
||||||
mFilename.setText(getIntent().getExtras().getString(RTABMapActivity.RTABMAP_FILENAME_KEY));
|
mFilename.setText(getIntent().getExtras().getString(RTABMapActivity.RTABMAP_FILENAME_KEY));
|
||||||
mWorkingDirectory = getIntent().getExtras().getString(RTABMapActivity.RTABMAP_WORKING_DIR_KEY);
|
mWorkingDirectory = getIntent().getExtras().getString(RTABMapActivity.RTABMAP_WORKING_DIR_KEY);
|
||||||
|
|
||||||
@@ -129,50 +127,7 @@ public class SketchfabActivity extends Activity implements OnClickListener {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
final String extension = mExportedOBJ?".obj":".ply";
|
authorizeAndPublish(mFilename.getText().toString());
|
||||||
|
|
||||||
String[] files = new String[0];
|
|
||||||
// verify if we have all files
|
|
||||||
if(extension.compareTo(".obj") == 0)
|
|
||||||
{
|
|
||||||
String[] fileNames = Util.loadFileList(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR, false);
|
|
||||||
if(fileNames.length > 0)
|
|
||||||
{
|
|
||||||
files = new String[fileNames.length];
|
|
||||||
for(int i=0; i<fileNames.length; ++i)
|
|
||||||
{
|
|
||||||
files[i] = mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR + "/" + fileNames[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Toast.makeText(getActivity(), String.format("Missing OBJ files!"), Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(extension.compareTo(".ply") == 0)
|
|
||||||
{
|
|
||||||
File plyFile = new File(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR + "/" + RTABMapActivity.RTABMAP_TMP_FILENAME + extension);
|
|
||||||
if(plyFile.exists())
|
|
||||||
{
|
|
||||||
files = new String[1];
|
|
||||||
files[0] = plyFile.getAbsolutePath();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Toast.makeText(getActivity(), String.format("Missing PLY file!"), Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Toast.makeText(getActivity(), String.format("Unknown file extension \"%s\"!", extension), Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(files.length > 0)
|
|
||||||
{
|
|
||||||
final String[] filesToZip = files;
|
|
||||||
authorizeAndPublish(filesToZip, mFilename.getText().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isNetworkAvailable() {
|
private boolean isNetworkAvailable() {
|
||||||
@@ -182,7 +137,7 @@ public class SketchfabActivity extends Activity implements OnClickListener {
|
|||||||
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
|
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authorizeAndPublish(final String[] filesToZip, final String fileName)
|
private void authorizeAndPublish(final String fileName)
|
||||||
{
|
{
|
||||||
if(!isNetworkAvailable())
|
if(!isNetworkAvailable())
|
||||||
{
|
{
|
||||||
@@ -192,7 +147,7 @@ public class SketchfabActivity extends Activity implements OnClickListener {
|
|||||||
.setMessage("Network is not available. Make sure you have internet before continuing.")
|
.setMessage("Network is not available. Make sure you have internet before continuing.")
|
||||||
.setPositiveButton("Try Again", new DialogInterface.OnClickListener() {
|
.setPositiveButton("Try Again", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
authorizeAndPublish(filesToZip, fileName);
|
authorizeAndPublish(fileName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNeutralButton("Abort", new DialogInterface.OnClickListener() {
|
.setNeutralButton("Abort", new DialogInterface.OnClickListener() {
|
||||||
@@ -239,7 +194,7 @@ public class SketchfabActivity extends Activity implements OnClickListener {
|
|||||||
|
|
||||||
mAuthDialog.dismiss();
|
mAuthDialog.dismiss();
|
||||||
|
|
||||||
zipAndPublish(filesToZip, fileName);
|
zipAndPublish(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -250,14 +205,12 @@ public class SketchfabActivity extends Activity implements OnClickListener {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
zipAndPublish(filesToZip, fileName);
|
zipAndPublish(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void zipAndPublish(final String[] filesToZip, final String fileName)
|
private void zipAndPublish(final String fileName)
|
||||||
{
|
{
|
||||||
final String zipOutput = mWorkingDirectory+fileName+".zip";
|
|
||||||
|
|
||||||
mProgressDialog.setTitle("Upload to Sketchfab");
|
mProgressDialog.setTitle("Upload to Sketchfab");
|
||||||
mProgressDialog.setMessage(String.format("Compressing the files..."));
|
mProgressDialog.setMessage(String.format("Compressing the files..."));
|
||||||
mProgressDialog.show();
|
mProgressDialog.show();
|
||||||
@@ -265,56 +218,110 @@ public class SketchfabActivity extends Activity implements OnClickListener {
|
|||||||
Thread workingThread = new Thread(new Runnable() {
|
Thread workingThread = new Thread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try{
|
try{
|
||||||
Util.zip(filesToZip, zipOutput);
|
|
||||||
runOnUiThread(new Runnable() {
|
File tmpDir = new File(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR);
|
||||||
public void run() {
|
tmpDir.mkdirs();
|
||||||
mProgressDialog.dismiss();
|
String[] fileNames = Util.loadFileList(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR, false);
|
||||||
|
if(!RTABMapActivity.DISABLE_LOG) Log.i(RTABMapActivity.TAG, String.format("Deleting %d files in \"%s\"", fileNames.length, mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR));
|
||||||
File f = new File(zipOutput);
|
for(int i=0; i<fileNames.length; ++i)
|
||||||
|
{
|
||||||
// Continue?
|
File f = new File(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR + "/" + fileNames[i]);
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
if(f.delete())
|
||||||
builder.setTitle("File(s) compressed and ready to upload!");
|
{
|
||||||
|
if(!RTABMapActivity.DISABLE_LOG) Log.i(RTABMapActivity.TAG, String.format("Deleted \"%s\"", f.getPath()));
|
||||||
final int fileSizeMB = (int)f.length()/(1024 * 1024);
|
}
|
||||||
final int fileSizeKB = (int)f.length()/(1024);
|
else
|
||||||
if(fileSizeMB == 0)
|
{
|
||||||
|
if(!RTABMapActivity.DISABLE_LOG) Log.i(RTABMapActivity.TAG, String.format("Failed deleting \"%s\"", f.getPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File exportDir = new File(mWorkingDirectory + RTABMapActivity.RTABMAP_EXPORT_DIR);
|
||||||
|
exportDir.mkdirs();
|
||||||
|
|
||||||
|
if(RTABMapLib.writeExportedMesh(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR, RTABMapActivity.RTABMAP_TMP_FILENAME))
|
||||||
|
{
|
||||||
|
String[] files = new String[0];
|
||||||
|
// verify if we have all files
|
||||||
|
|
||||||
|
fileNames = Util.loadFileList(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR, false);
|
||||||
|
if(fileNames.length > 0)
|
||||||
|
{
|
||||||
|
files = new String[fileNames.length];
|
||||||
|
for(int i=0; i<fileNames.length; ++i)
|
||||||
{
|
{
|
||||||
Log.i(RTABMapActivity.TAG, String.format("Zipped files = %d KB", fileSizeKB));
|
files[i] = mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR + "/" + fileNames[i];
|
||||||
builder.setMessage(String.format("Total size to upload = %d KB. Do you want to continue?\n\n", fileSizeKB));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.i(RTABMapActivity.TAG, String.format("Zipped files = %d MB", fileSizeMB));
|
|
||||||
builder.setMessage(String.format("Total size to upload = %d MB. %sDo you want to continue?\n\n"
|
|
||||||
+ "Tip: To reduce the model size, you can also look at the Settings->Exporting options.", fileSizeMB,
|
|
||||||
fileSizeMB>=50?"Note that for size over 50 MB, a Sketchfab PRO account is required, otherwise the upload may fail. ":""));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!RTABMapActivity.DISABLE_LOG) Log.i(RTABMapActivity.TAG, "Missing files!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(files.length > 0)
|
||||||
|
{
|
||||||
|
final String[] filesToZip = files;
|
||||||
|
final String zipOutput = mWorkingDirectory+fileName+".zip";
|
||||||
|
Util.zip(filesToZip, zipOutput);
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mProgressDialog.dismiss();
|
||||||
|
|
||||||
|
File f = new File(zipOutput);
|
||||||
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
// Continue?
|
||||||
mProgressDialog.setTitle("Upload to Sketchfab");
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
builder.setTitle("File(s) compressed and ready to upload!");
|
||||||
|
|
||||||
|
final int fileSizeMB = (int)f.length()/(1024 * 1024);
|
||||||
|
final int fileSizeKB = (int)f.length()/(1024);
|
||||||
if(fileSizeMB == 0)
|
if(fileSizeMB == 0)
|
||||||
{
|
{
|
||||||
mProgressDialog.setMessage(String.format("Uploading model \"%s\" (%d KB) to Sketchfab...", fileName, fileSizeKB));
|
Log.i(RTABMapActivity.TAG, String.format("Zipped files = %d KB", fileSizeKB));
|
||||||
|
builder.setMessage(String.format("Total size to upload = %d KB. Do you want to continue?\n\n", fileSizeKB));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mProgressDialog.setMessage(String.format("Uploading model \"%s\" (%d MB) to Sketchfab...", fileName, fileSizeMB));
|
Log.i(RTABMapActivity.TAG, String.format("Zipped files = %d MB", fileSizeMB));
|
||||||
|
builder.setMessage(String.format("Total size to upload = %d MB. %sDo you want to continue?\n\n"
|
||||||
|
+ "Tip: To reduce the model size, you can also look at the Settings->Exporting options.", fileSizeMB,
|
||||||
|
fileSizeMB>=50?"Note that for size over 50 MB, a Sketchfab PRO account is required, otherwise the upload may fail. ":""));
|
||||||
}
|
}
|
||||||
mProgressDialog.show();
|
|
||||||
new uploadToSketchfabTask().execute(zipOutput, fileName);
|
|
||||||
|
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
mProgressDialog.setTitle("Upload to Sketchfab");
|
||||||
|
if(fileSizeMB == 0)
|
||||||
|
{
|
||||||
|
mProgressDialog.setMessage(String.format("Uploading model \"%s\" (%d KB) to Sketchfab...", fileName, fileSizeKB));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mProgressDialog.setMessage(String.format("Uploading model \"%s\" (%d MB) to Sketchfab...", fileName, fileSizeMB));
|
||||||
|
}
|
||||||
|
mProgressDialog.show();
|
||||||
|
new uploadToSketchfabTask().execute(zipOutput, fileName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
// do nothing...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
// do nothing...
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.show();
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mProgressDialog.dismiss();
|
||||||
|
Toast.makeText(getActivity(), String.format("Failed writing files!"), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(IOException ex) {
|
catch(IOException ex) {
|
||||||
Log.e(RTABMapActivity.TAG, "Failed to zip", ex);
|
Log.e(RTABMapActivity.TAG, "Failed to zip", ex);
|
||||||
|
|||||||
@@ -43,11 +43,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <opencv2/core/core.hpp>
|
#include <opencv2/core/core.hpp>
|
||||||
#include <opencv2/features2d/features2d.hpp>
|
#include <opencv2/features2d/features2d.hpp>
|
||||||
|
|
||||||
namespace pcl
|
|
||||||
{
|
|
||||||
class TextureMesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace rtabmap {
|
namespace rtabmap {
|
||||||
|
|
||||||
class Signature;
|
class Signature;
|
||||||
|
|||||||
@@ -174,6 +174,16 @@ pcl::TextureMesh::Ptr RTABMAP_EXP concatenateTextureMeshes(
|
|||||||
void RTABMAP_EXP concatenateTextureMaterials(
|
void RTABMAP_EXP concatenateTextureMaterials(
|
||||||
pcl::TextureMesh & mesh, const cv::Size & imageSize, int textureSize, int maxTextures, float & scale, std::vector<bool> * materialsKept=0);
|
pcl::TextureMesh & mesh, const cv::Size & imageSize, int textureSize, int maxTextures, float & scale, std::vector<bool> * materialsKept=0);
|
||||||
|
|
||||||
|
pcl::TextureMesh::Ptr RTABMAP_EXP assembleTextureMesh(
|
||||||
|
const cv::Mat & cloudMat,
|
||||||
|
const std::vector<std::vector<std::vector<unsigned int> > > & polygons,
|
||||||
|
const std::vector<std::vector<Eigen::Vector2f> > & texCoords,
|
||||||
|
cv::Mat & textures,
|
||||||
|
bool mergeTextures = false);
|
||||||
|
|
||||||
|
pcl::PolygonMesh::Ptr RTABMAP_EXP assemblePolygonMesh(
|
||||||
|
const cv::Mat & cloudMat,
|
||||||
|
const std::vector<std::vector<unsigned int> > & polygons);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge all textures in the mesh into "textureCount" textures of size "textureSize".
|
* Merge all textures in the mesh into "textureCount" textures of size "textureSize".
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
#include "rtabmap/core/util3d_surface.h"
|
#include "rtabmap/core/util3d_surface.h"
|
||||||
#include "rtabmap/core/util3d_filtering.h"
|
#include "rtabmap/core/util3d_filtering.h"
|
||||||
|
#include "rtabmap/core/util3d.h"
|
||||||
#include "rtabmap/core/util2d.h"
|
#include "rtabmap/core/util2d.h"
|
||||||
#include "rtabmap/core/Memory.h"
|
#include "rtabmap/core/Memory.h"
|
||||||
#include "rtabmap/core/DBDriver.h"
|
#include "rtabmap/core/DBDriver.h"
|
||||||
@@ -1190,6 +1191,163 @@ void concatenateTextureMaterials(pcl::TextureMesh & mesh, const cv::Size & image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pcl::TextureMesh::Ptr assembleTextureMesh(
|
||||||
|
const cv::Mat & cloudMat,
|
||||||
|
const std::vector<std::vector<std::vector<unsigned int> > > & polygons,
|
||||||
|
const std::vector<std::vector<Eigen::Vector2f> > & texCoords,
|
||||||
|
cv::Mat & textures,
|
||||||
|
bool mergeTextures)
|
||||||
|
{
|
||||||
|
pcl::TextureMesh::Ptr textureMesh(new pcl::TextureMesh);
|
||||||
|
|
||||||
|
if(cloudMat.channels() <= 3)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = rtabmap::util3d::laserScanToPointCloud(cloudMat);
|
||||||
|
pcl::toPCLPointCloud2(*cloud, textureMesh->cloud);
|
||||||
|
}
|
||||||
|
else if(cloudMat.channels() == 4)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudRGB(cloudMat);
|
||||||
|
pcl::toPCLPointCloud2(*cloud, textureMesh->cloud);
|
||||||
|
}
|
||||||
|
else if(cloudMat.channels() == 6)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudNormal(cloudMat);
|
||||||
|
pcl::toPCLPointCloud2(*cloud, textureMesh->cloud);
|
||||||
|
}
|
||||||
|
else if(cloudMat.channels() == 7)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudRGBNormal(cloudMat);
|
||||||
|
pcl::toPCLPointCloud2(*cloud, textureMesh->cloud);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(textureMesh->cloud.data.size() && polygons.size())
|
||||||
|
{
|
||||||
|
textureMesh->tex_polygons.resize(polygons.size());
|
||||||
|
for(unsigned int t=0; t<polygons.size(); ++t)
|
||||||
|
{
|
||||||
|
textureMesh->tex_polygons[t].resize(polygons[t].size());
|
||||||
|
for(unsigned int p=0; p<polygons[t].size(); ++p)
|
||||||
|
{
|
||||||
|
textureMesh->tex_polygons[t][p].vertices = polygons[t][p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!texCoords.empty() && !textures.empty())
|
||||||
|
{
|
||||||
|
textureMesh->tex_coordinates = texCoords;
|
||||||
|
|
||||||
|
textureMesh->tex_materials.resize (textureMesh->tex_coordinates.size());
|
||||||
|
for(unsigned int i = 0 ; i < textureMesh->tex_coordinates.size() ; ++i)
|
||||||
|
{
|
||||||
|
pcl::TexMaterial mesh_material;
|
||||||
|
mesh_material.tex_Ka.r = 0.2f;
|
||||||
|
mesh_material.tex_Ka.g = 0.2f;
|
||||||
|
mesh_material.tex_Ka.b = 0.2f;
|
||||||
|
|
||||||
|
mesh_material.tex_Kd.r = 0.8f;
|
||||||
|
mesh_material.tex_Kd.g = 0.8f;
|
||||||
|
mesh_material.tex_Kd.b = 0.8f;
|
||||||
|
|
||||||
|
mesh_material.tex_Ks.r = 1.0f;
|
||||||
|
mesh_material.tex_Ks.g = 1.0f;
|
||||||
|
mesh_material.tex_Ks.b = 1.0f;
|
||||||
|
|
||||||
|
mesh_material.tex_d = 1.0f;
|
||||||
|
mesh_material.tex_Ns = 75.0f;
|
||||||
|
mesh_material.tex_illum = 2;
|
||||||
|
|
||||||
|
std::stringstream tex_name;
|
||||||
|
tex_name << "material_" << i;
|
||||||
|
tex_name >> mesh_material.tex_name;
|
||||||
|
|
||||||
|
mesh_material.tex_file = uFormat("%d", i);
|
||||||
|
|
||||||
|
textureMesh->tex_materials[i] = mesh_material;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mergeTextures && textures.cols/textures.rows > 1)
|
||||||
|
{
|
||||||
|
UASSERT(textures.cols % textures.rows == 0 && textures.cols/textures.rows == (int)textureMesh->tex_coordinates.size());
|
||||||
|
std::vector<bool> materialsKept;
|
||||||
|
float scale = 0.0f;
|
||||||
|
cv::Size imageSize(textures.rows, textures.rows);
|
||||||
|
int imageType = textures.type();
|
||||||
|
rtabmap::util3d::concatenateTextureMaterials(*textureMesh, imageSize, textures.rows, 1, scale, &materialsKept);
|
||||||
|
if(scale && textureMesh->tex_materials.size() == 1)
|
||||||
|
{
|
||||||
|
int cols = float(textures.rows)/(scale*imageSize.width);
|
||||||
|
int rows = float(textures.rows)/(scale*imageSize.height);
|
||||||
|
|
||||||
|
cv::Mat mergedTextures = cv::Mat(textures.rows, textures.rows, imageType, cv::Scalar::all(255));
|
||||||
|
|
||||||
|
// make a blank texture
|
||||||
|
cv::Size resizedImageSize(int(imageSize.width*scale), int(imageSize.height*scale));
|
||||||
|
int oi=0;
|
||||||
|
for(int i=0; i<(int)materialsKept.size(); ++i)
|
||||||
|
{
|
||||||
|
if(materialsKept.at(i))
|
||||||
|
{
|
||||||
|
int u = oi%cols * resizedImageSize.width;
|
||||||
|
int v = ((oi/cols) % rows ) * resizedImageSize.height;
|
||||||
|
UASSERT(u < textures.rows-resizedImageSize.width);
|
||||||
|
UASSERT(v < textures.rows-resizedImageSize.height);
|
||||||
|
|
||||||
|
cv::Mat resizedImage;
|
||||||
|
cv::resize(textures(cv::Range::all(), cv::Range(i*textures.rows, (i+1)*textures.rows)), resizedImage, resizedImageSize, 0.0f, 0.0f, cv::INTER_AREA);
|
||||||
|
|
||||||
|
UASSERT(resizedImage.type() == mergedTextures.type());
|
||||||
|
resizedImage.copyTo(mergedTextures(cv::Rect(u, v, resizedImage.cols, resizedImage.rows)));
|
||||||
|
|
||||||
|
++oi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textures = mergedTextures;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return textureMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
pcl::PolygonMesh::Ptr assemblePolygonMesh(
|
||||||
|
const cv::Mat & cloudMat,
|
||||||
|
const std::vector<std::vector<unsigned int> > & polygons)
|
||||||
|
{
|
||||||
|
pcl::PolygonMesh::Ptr polygonMesh(new pcl::PolygonMesh);
|
||||||
|
|
||||||
|
if(cloudMat.channels() <= 3)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = rtabmap::util3d::laserScanToPointCloud(cloudMat);
|
||||||
|
pcl::toPCLPointCloud2(*cloud, polygonMesh->cloud);
|
||||||
|
}
|
||||||
|
else if(cloudMat.channels() == 4)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudRGB(cloudMat);
|
||||||
|
pcl::toPCLPointCloud2(*cloud, polygonMesh->cloud);
|
||||||
|
}
|
||||||
|
else if(cloudMat.channels() == 6)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudNormal(cloudMat);
|
||||||
|
pcl::toPCLPointCloud2(*cloud, polygonMesh->cloud);
|
||||||
|
}
|
||||||
|
else if(cloudMat.channels() == 7)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudRGBNormal(cloudMat);
|
||||||
|
pcl::toPCLPointCloud2(*cloud, polygonMesh->cloud);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(polygonMesh->cloud.data.size() && polygons.size())
|
||||||
|
{
|
||||||
|
polygonMesh->polygons.resize(polygons.size());
|
||||||
|
for(unsigned int p=0; p<polygons.size(); ++p)
|
||||||
|
{
|
||||||
|
polygonMesh->polygons[p].vertices = polygons[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return polygonMesh;
|
||||||
|
}
|
||||||
|
|
||||||
double sqr(uchar v)
|
double sqr(uchar v)
|
||||||
{
|
{
|
||||||
return double(v)*double(v);
|
return double(v)*double(v);
|
||||||
|
|||||||
Reference in New Issue
Block a user