224 changed files with 130146 additions and 0 deletions
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
# Add applications |
||||
ADD_SUBDIRECTORY(InterfaceCOLMAP) |
||||
ADD_SUBDIRECTORY(InterfaceMetashape) |
||||
ADD_SUBDIRECTORY(InterfaceMVSNet) |
||||
ADD_SUBDIRECTORY(InterfacePolycam) |
||||
ADD_SUBDIRECTORY(DensifyPointCloud) |
||||
ADD_SUBDIRECTORY(ReconstructMesh) |
||||
ADD_SUBDIRECTORY(RefineMesh) |
||||
ADD_SUBDIRECTORY(TextureMesh) |
||||
ADD_SUBDIRECTORY(TransformScene) |
||||
ADD_SUBDIRECTORY(Viewer) |
||||
if(OpenMVS_ENABLE_TESTS) |
||||
ADD_SUBDIRECTORY(Tests) |
||||
endif() |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(DensifyPointCloud "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS DensifyPointCloud |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,433 @@
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* DensifyPointCloud.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
#include <boost/program_options.hpp> |
||||
|
||||
using namespace MVS; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("DensifyPointCloud") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace OPT { |
||||
String strInputFileName; |
||||
String strPointCloudFileName; |
||||
String strOutputFileName; |
||||
String strViewNeighborsFileName; |
||||
String strOutputViewNeighborsFileName; |
||||
String strMeshFileName; |
||||
String strExportROIFileName; |
||||
String strImportROIFileName; |
||||
String strDenseConfigFileName; |
||||
String strExportDepthMapsName; |
||||
String strMaskPath; |
||||
float fMaxSubsceneArea; |
||||
float fSampleMesh; |
||||
float fBorderROI; |
||||
bool bCrop2ROI; |
||||
int nEstimateROI; |
||||
int nTowerMode; |
||||
int nFusionMode; |
||||
float fEstimateScale; |
||||
int thFilterPointCloud; |
||||
int nExportNumViews; |
||||
int nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
String strConfigFileName; |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "produce this help message") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
#ifdef _USE_CUDA |
||||
("cuda-device", boost::program_options::value(&CUDA::desiredDeviceID)->default_value(-1), "CUDA device number to be used for depth-map estimation (-2 - CPU processing, -1 - best GPU, >=0 - device index)") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
#ifdef _USE_CUDA |
||||
const unsigned nNumViewsDefault(8); |
||||
const unsigned numIters(4); |
||||
#else |
||||
const unsigned nNumViewsDefault(5); |
||||
const unsigned numIters(3); |
||||
#endif |
||||
unsigned nResolutionLevel; |
||||
unsigned nMaxResolution; |
||||
unsigned nMinResolution; |
||||
unsigned nNumViews; |
||||
unsigned nMinViewsFuse; |
||||
unsigned nSubResolutionLevels; |
||||
unsigned nEstimationIters; |
||||
unsigned nEstimationGeometricIters; |
||||
unsigned nEstimateColors; |
||||
unsigned nEstimateNormals; |
||||
unsigned nOptimize; |
||||
int nIgnoreMaskLabel; |
||||
bool bRemoveDmaps; |
||||
boost::program_options::options_description config("Densify options"); |
||||
config.add_options() |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list") |
||||
("pointcloud-file,p", boost::program_options::value<std::string>(&OPT::strPointCloudFileName), "sparse point-cloud with views file name to densify (overwrite existing point-cloud)") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the dense point-cloud (optional)") |
||||
("view-neighbors-file", boost::program_options::value<std::string>(&OPT::strViewNeighborsFileName), "input filename containing the list of views and their neighbors (optional)") |
||||
("output-view-neighbors-file", boost::program_options::value<std::string>(&OPT::strOutputViewNeighborsFileName), "output filename containing the generated list of views and their neighbors") |
||||
("resolution-level", boost::program_options::value(&nResolutionLevel)->default_value(1), "how many times to scale down the images before point cloud computation") |
||||
("max-resolution", boost::program_options::value(&nMaxResolution)->default_value(2560), "do not scale images higher than this resolution") |
||||
("min-resolution", boost::program_options::value(&nMinResolution)->default_value(640), "do not scale images lower than this resolution") |
||||
("sub-resolution-levels", boost::program_options::value(&nSubResolutionLevels)->default_value(2), "number of patch-match sub-resolution iterations (0 - disabled)") |
||||
("number-views", boost::program_options::value(&nNumViews)->default_value(nNumViewsDefault), "number of views used for depth-map estimation (0 - all neighbor views available)") |
||||
("number-views-fuse", boost::program_options::value(&nMinViewsFuse)->default_value(3), "minimum number of images that agrees with an estimate during fusion in order to consider it inlier (<2 - only merge depth-maps)") |
||||
("ignore-mask-label", boost::program_options::value(&nIgnoreMaskLabel)->default_value(-1), "label value to ignore in the image mask, stored in the MVS scene or next to each image with '.mask.png' extension (<0 - disabled)") |
||||
("mask-path", boost::program_options::value<std::string>(&OPT::strMaskPath), "path to folder containing mask images with '.mask.png' extension") |
||||
("iters", boost::program_options::value(&nEstimationIters)->default_value(numIters), "number of patch-match iterations") |
||||
("geometric-iters", boost::program_options::value(&nEstimationGeometricIters)->default_value(2), "number of geometric consistent patch-match iterations (0 - disabled)") |
||||
("estimate-colors", boost::program_options::value(&nEstimateColors)->default_value(2), "estimate the colors for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)") |
||||
("estimate-normals", boost::program_options::value(&nEstimateNormals)->default_value(2), "estimate the normals for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)") |
||||
("estimate-scale", boost::program_options::value(&OPT::fEstimateScale)->default_value(0.f), "estimate the point-scale for the dense point-cloud (scale multiplier, 0 - disabled)") |
||||
("sub-scene-area", boost::program_options::value(&OPT::fMaxSubsceneArea)->default_value(0.f), "split the scene in sub-scenes such that each sub-scene surface does not exceed the given maximum sampling area (0 - disabled)") |
||||
("sample-mesh", boost::program_options::value(&OPT::fSampleMesh)->default_value(0.f), "uniformly samples points on a mesh (0 - disabled, <0 - number of points, >0 - sample density per square unit)") |
||||
("fusion-mode", boost::program_options::value(&OPT::nFusionMode)->default_value(0), "depth-maps fusion mode (-2 - fuse disparity-maps, -1 - export disparity-maps only, 0 - depth-maps & fusion, 1 - export depth-maps only)") |
||||
("postprocess-dmaps", boost::program_options::value(&nOptimize)->default_value(7), "flags used to filter the depth-maps after estimation (0 - disabled, 1 - remove-speckles, 2 - fill-gaps, 4 - adjust-filter)") |
||||
("filter-point-cloud", boost::program_options::value(&OPT::thFilterPointCloud)->default_value(0), "filter dense point-cloud based on visibility (0 - disabled)") |
||||
("export-number-views", boost::program_options::value(&OPT::nExportNumViews)->default_value(0), "export points with >= number of views (0 - disabled, <0 - save MVS project too)") |
||||
("roi-border", boost::program_options::value(&OPT::fBorderROI)->default_value(0), "add a border to the region-of-interest when cropping the scene (0 - disabled, >0 - percentage, <0 - absolute)") |
||||
("estimate-roi", boost::program_options::value(&OPT::nEstimateROI)->default_value(2), "estimate and set region-of-interest (0 - disabled, 1 - enabled, 2 - adaptive)") |
||||
("crop-to-roi", boost::program_options::value(&OPT::bCrop2ROI)->default_value(true), "crop scene using the region-of-interest") |
||||
("remove-dmaps", boost::program_options::value(&bRemoveDmaps)->default_value(false), "remove depth-maps after fusion") |
||||
("tower-mode", boost::program_options::value(&OPT::nTowerMode)->default_value(4), "add a cylinder of points in the center of ROI; scene assume to be Z-up oriented (0 - disabled, 1 - replace, 2 - append, 3 - select neighbors, 4 - select neighbors & append, <0 - force tower mode)") |
||||
; |
||||
|
||||
// hidden options, allowed both on command line and
|
||||
// in config file, but will not be shown to the user
|
||||
boost::program_options::options_description hidden("Hidden options"); |
||||
hidden.add_options() |
||||
("mesh-file", boost::program_options::value<std::string>(&OPT::strMeshFileName), "mesh file name used for image pair overlap estimation") |
||||
("export-roi-file", boost::program_options::value<std::string>(&OPT::strExportROIFileName), "ROI file name to be exported form the scene") |
||||
("import-roi-file", boost::program_options::value<std::string>(&OPT::strImportROIFileName), "ROI file name to be imported into the scene") |
||||
("dense-config-file", boost::program_options::value<std::string>(&OPT::strDenseConfigFileName), "optional configuration file for the densifier (overwritten by the command line options)") |
||||
("export-depth-maps-name", boost::program_options::value<std::string>(&OPT::strExportDepthMapsName), "render given mesh and save the depth-map for every image to this file name base (empty - disabled)") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config).add(hidden); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config).add(hidden); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
// initialize the log file
|
||||
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str()); |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidPath(OPT::strInputFileName); |
||||
if (OPT::vm.count("help") || OPT::strInputFileName.empty()) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config); |
||||
GET_LOG() << visible; |
||||
} |
||||
if (OPT::strInputFileName.empty()) |
||||
return false; |
||||
|
||||
// initialize optional options
|
||||
Util::ensureValidPath(OPT::strPointCloudFileName); |
||||
Util::ensureValidPath(OPT::strOutputFileName); |
||||
Util::ensureValidPath(OPT::strViewNeighborsFileName); |
||||
Util::ensureValidPath(OPT::strOutputViewNeighborsFileName); |
||||
Util::ensureValidPath(OPT::strMeshFileName); |
||||
Util::ensureValidPath(OPT::strExportROIFileName); |
||||
Util::ensureValidPath(OPT::strImportROIFileName); |
||||
if (OPT::strOutputFileName.empty()) |
||||
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + _T("_dense.mvs"); |
||||
|
||||
// init dense options
|
||||
if (!OPT::strDenseConfigFileName.empty()) |
||||
OPT::strDenseConfigFileName = MAKE_PATH_SAFE(OPT::strDenseConfigFileName); |
||||
OPTDENSE::init(); |
||||
const bool bValidConfig(OPTDENSE::oConfig.Load(OPT::strDenseConfigFileName)); |
||||
OPTDENSE::update(); |
||||
OPTDENSE::nResolutionLevel = nResolutionLevel; |
||||
OPTDENSE::nMaxResolution = nMaxResolution; |
||||
OPTDENSE::nMinResolution = nMinResolution; |
||||
OPTDENSE::nSubResolutionLevels = nSubResolutionLevels; |
||||
OPTDENSE::nNumViews = nNumViews; |
||||
OPTDENSE::nMinViewsFuse = nMinViewsFuse; |
||||
OPTDENSE::nEstimationIters = nEstimationIters; |
||||
OPTDENSE::nEstimationGeometricIters = nEstimationGeometricIters; |
||||
OPTDENSE::nEstimateColors = nEstimateColors; |
||||
OPTDENSE::nEstimateNormals = nEstimateNormals; |
||||
OPTDENSE::nOptimize = nOptimize; |
||||
OPTDENSE::nIgnoreMaskLabel = nIgnoreMaskLabel; |
||||
OPTDENSE::bRemoveDmaps = bRemoveDmaps; |
||||
if (!bValidConfig && !OPT::strDenseConfigFileName.empty()) |
||||
OPTDENSE::oConfig.Save(OPT::strDenseConfigFileName); |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
Scene scene(OPT::nMaxThreads); |
||||
if (OPT::fSampleMesh != 0) { |
||||
// sample input mesh and export the obtained point-cloud
|
||||
if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName), true) || scene.mesh.IsEmpty()) |
||||
return EXIT_FAILURE; |
||||
TD_TIMER_START(); |
||||
PointCloud pointcloud; |
||||
if (OPT::fSampleMesh > 0) |
||||
scene.mesh.SamplePoints(OPT::fSampleMesh, 0, pointcloud); |
||||
else |
||||
scene.mesh.SamplePoints(ROUND2INT<unsigned>(-OPT::fSampleMesh), pointcloud); |
||||
VERBOSE("Sample mesh completed: %u points (%s)", pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str()); |
||||
pointcloud.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T(".ply")); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
// load and estimate a dense point-cloud
|
||||
const Scene::SCENE_TYPE sceneType(scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName))); |
||||
if (sceneType == Scene::SCENE_NA) |
||||
return EXIT_FAILURE; |
||||
if (!OPT::strPointCloudFileName.empty() && !scene.pointcloud.Load(MAKE_PATH_SAFE(OPT::strPointCloudFileName))) { |
||||
VERBOSE("error: cannot load point-cloud file"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
if (!OPT::strMaskPath.empty()) { |
||||
Util::ensureValidFolderPath(OPT::strMaskPath); |
||||
for (Image& image : scene.images) { |
||||
if (!image.maskName.empty()) { |
||||
VERBOSE("error: Image %s has non-empty maskName %s", image.name.c_str(), image.maskName.c_str()); |
||||
return EXIT_FAILURE; |
||||
} |
||||
image.maskName = OPT::strMaskPath + Util::getFileName(image.name) + ".mask.png"; |
||||
if (!File::access(image.maskName)) { |
||||
VERBOSE("error: Mask image %s not found", image.maskName.c_str()); |
||||
return EXIT_FAILURE; |
||||
} |
||||
} |
||||
} |
||||
if (!OPT::strImportROIFileName.empty()) { |
||||
std::ifstream fs(MAKE_PATH_SAFE(OPT::strImportROIFileName)); |
||||
if (!fs) |
||||
return EXIT_FAILURE; |
||||
fs >> scene.obb; |
||||
scene.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
if (!scene.IsBounded()) |
||||
scene.EstimateROI(OPT::nEstimateROI, 1.1f); |
||||
if (!OPT::strExportROIFileName.empty() && scene.IsBounded()) { |
||||
std::ofstream fs(MAKE_PATH_SAFE(OPT::strExportROIFileName)); |
||||
if (!fs) |
||||
return EXIT_FAILURE; |
||||
fs << scene.obb; |
||||
return EXIT_SUCCESS; |
||||
} |
||||
if (OPT::nTowerMode!=0) |
||||
scene.InitTowerScene(OPT::nTowerMode); |
||||
if (!OPT::strMeshFileName.empty()) |
||||
scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName)); |
||||
if (!OPT::strViewNeighborsFileName.empty()) |
||||
scene.LoadViewNeighbors(MAKE_PATH_SAFE(OPT::strViewNeighborsFileName)); |
||||
if (!OPT::strOutputViewNeighborsFileName.empty()) { |
||||
if (!scene.ImagesHaveNeighbors()) { |
||||
VERBOSE("error: neighbor views not computed yet"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
scene.SaveViewNeighbors(MAKE_PATH_SAFE(OPT::strOutputViewNeighborsFileName)); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
if (!OPT::strExportDepthMapsName.empty() && !scene.mesh.IsEmpty()) { |
||||
// project mesh onto each image and save the resulted depth-maps
|
||||
TD_TIMER_START(); |
||||
if (!scene.ExportMeshToDepthMaps(MAKE_PATH_SAFE(OPT::strExportDepthMapsName))) |
||||
return EXIT_FAILURE; |
||||
VERBOSE("Mesh projection completed: %u depth-maps (%s)", scene.images.size(), TD_TIMER_GET_FMT().c_str()); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
if (OPT::fMaxSubsceneArea > 0) { |
||||
// split the scene in sub-scenes by maximum sampling area
|
||||
Scene::ImagesChunkArr chunks; |
||||
scene.Split(chunks, OPT::fMaxSubsceneArea); |
||||
scene.ExportChunks(chunks, GET_PATH_FULL(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
if (OPT::thFilterPointCloud < 0) { |
||||
// filter point-cloud based on camera-point visibility intersections
|
||||
scene.PointCloudFilter(OPT::thFilterPointCloud); |
||||
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T("_filtered")); |
||||
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
scene.pointcloud.Save(baseFileName+_T(".ply")); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
if (OPT::nExportNumViews && scene.pointcloud.IsValid()) { |
||||
// export point-cloud containing only points with N+ views
|
||||
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+ |
||||
String::FormatString(_T("_%dviews"), ABS(OPT::nExportNumViews))); |
||||
if (OPT::nExportNumViews > 0) { |
||||
// export point-cloud containing only points with N+ views
|
||||
scene.pointcloud.SaveNViews(baseFileName+_T(".ply"), (IIndex)OPT::nExportNumViews); |
||||
} else { |
||||
// save scene and export point-cloud containing only points with N+ views
|
||||
scene.pointcloud.RemoveMinViews((IIndex)-OPT::nExportNumViews); |
||||
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
scene.pointcloud.Save(baseFileName+_T(".ply")); |
||||
} |
||||
return EXIT_SUCCESS; |
||||
} |
||||
if (OPT::fEstimateScale > 0 && !scene.pointcloud.IsEmpty() && !scene.images.empty()) { |
||||
// simply export existing point-cloud with scale
|
||||
if (scene.pointcloud.normals.empty()) { |
||||
if (!scene.pointcloud.IsValid()) { |
||||
VERBOSE("error: can not estimate normals as the point-cloud is not valid"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
EstimatePointNormals(scene.images, scene.pointcloud); |
||||
} |
||||
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))); |
||||
scene.pointcloud.SaveWithScale(baseFileName+_T("_scale.ply"), scene.images, OPT::fEstimateScale); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
PointCloud sparsePointCloud; |
||||
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS || sceneType == Scene::SCENE_INTERFACE) { |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
if (VERBOSITY_LEVEL > 1 && !scene.pointcloud.IsEmpty()) |
||||
scene.pointcloud.PrintStatistics(scene.images.data(), &scene.obb); |
||||
#endif |
||||
if ((ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS) |
||||
sparsePointCloud = scene.pointcloud; |
||||
TD_TIMER_START(); |
||||
if (!scene.DenseReconstruction(OPT::nFusionMode, OPT::bCrop2ROI, OPT::fBorderROI)) { |
||||
if (ABS(OPT::nFusionMode) != 1) |
||||
return EXIT_FAILURE; |
||||
VERBOSE("Depth-maps estimated (%s)", TD_TIMER_GET_FMT().c_str()); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
VERBOSE("Densifying point-cloud completed: %u points (%s)", scene.pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str()); |
||||
} |
||||
|
||||
// save the final point-cloud
|
||||
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))); |
||||
scene.pointcloud.Save(baseFileName+_T(".ply"), (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS); |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
if (VERBOSITY_LEVEL > 2) |
||||
scene.ExportCamerasMLP(baseFileName+_T(".mlp"), baseFileName+_T(".ply")); |
||||
#endif |
||||
if ((ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS) |
||||
scene.pointcloud.Swap(sparsePointCloud); |
||||
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(InterfaceCOLMAP "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS InterfaceCOLMAP |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
|
||||
// its contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
|
||||
|
||||
#ifndef COLMAP_SRC_UTIL_ENDIAN_H_ |
||||
#define COLMAP_SRC_UTIL_ENDIAN_H_ |
||||
|
||||
#include <algorithm> |
||||
#include <vector> |
||||
#include <iostream> |
||||
|
||||
namespace colmap { |
||||
|
||||
// Reverse the order of each byte.
|
||||
template <typename T> |
||||
T ReverseBytes(const T& data); |
||||
|
||||
// Check the order in which bytes are stored in computer memory.
|
||||
bool IsLittleEndian(); |
||||
bool IsBigEndian(); |
||||
|
||||
// Convert data between endianness and the native format. Note that, for float
|
||||
// and double types, these functions are only valid if the format is IEEE-754.
|
||||
// This is the case for pretty much most processors.
|
||||
template <typename T> |
||||
T LittleEndianToNative(const T x); |
||||
template <typename T> |
||||
T BigEndianToNative(const T x); |
||||
template <typename T> |
||||
T NativeToLittleEndian(const T x); |
||||
template <typename T> |
||||
T NativeToBigEndian(const T x); |
||||
|
||||
// Read data in little endian format for cross-platform support.
|
||||
template <typename T> |
||||
T ReadBinaryLittleEndian(std::istream* stream); |
||||
template <typename T> |
||||
void ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data); |
||||
|
||||
// Write data in little endian format for cross-platform support.
|
||||
template <typename T> |
||||
void WriteBinaryLittleEndian(std::ostream* stream, const T& data); |
||||
template <typename T> |
||||
void WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data); |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T> |
||||
T ReverseBytes(const T& data) { |
||||
T data_reversed = data; |
||||
std::reverse(reinterpret_cast<char*>(&data_reversed), |
||||
reinterpret_cast<char*>(&data_reversed) + sizeof(T)); |
||||
return data_reversed; |
||||
} |
||||
|
||||
inline bool IsLittleEndian() { |
||||
#ifdef BOOST_BIG_ENDIAN |
||||
return false; |
||||
#else |
||||
return true; |
||||
#endif |
||||
} |
||||
|
||||
inline bool IsBigEndian() { |
||||
#ifdef BOOST_BIG_ENDIAN |
||||
return true; |
||||
#else |
||||
return false; |
||||
#endif |
||||
} |
||||
|
||||
template <typename T> |
||||
T LittleEndianToNative(const T x) { |
||||
if (IsLittleEndian()) { |
||||
return x; |
||||
} else { |
||||
return ReverseBytes(x); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
T BigEndianToNative(const T x) { |
||||
if (IsBigEndian()) { |
||||
return x; |
||||
} else { |
||||
return ReverseBytes(x); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
T NativeToLittleEndian(const T x) { |
||||
if (IsLittleEndian()) { |
||||
return x; |
||||
} else { |
||||
return ReverseBytes(x); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
T NativeToBigEndian(const T x) { |
||||
if (IsBigEndian()) { |
||||
return x; |
||||
} else { |
||||
return ReverseBytes(x); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
T ReadBinaryLittleEndian(std::istream* stream) { |
||||
T data_little_endian; |
||||
stream->read(reinterpret_cast<char*>(&data_little_endian), sizeof(T)); |
||||
return LittleEndianToNative(data_little_endian); |
||||
} |
||||
|
||||
template <typename T> |
||||
void ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data) { |
||||
for (size_t i = 0; i < data->size(); ++i) { |
||||
(*data)[i] = ReadBinaryLittleEndian<T>(stream); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
void WriteBinaryLittleEndian(std::ostream* stream, const T& data) { |
||||
const T data_little_endian = NativeToLittleEndian(data); |
||||
stream->write(reinterpret_cast<const char*>(&data_little_endian), sizeof(T)); |
||||
} |
||||
|
||||
template <typename T> |
||||
void WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data) { |
||||
for (const auto& elem : data) { |
||||
WriteBinaryLittleEndian<T>(stream, elem); |
||||
} |
||||
} |
||||
|
||||
} // namespace colmap
|
||||
|
||||
#endif // COLMAP_SRC_UTIL_ENDIAN_H_
|
||||
|
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(InterfaceCOLMAP "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS InterfaceCOLMAP |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
|
||||
// its contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
|
||||
|
||||
#ifndef COLMAP_SRC_UTIL_ENDIAN_H_ |
||||
#define COLMAP_SRC_UTIL_ENDIAN_H_ |
||||
|
||||
#include <algorithm> |
||||
#include <vector> |
||||
#include <iostream> |
||||
|
||||
namespace colmap { |
||||
|
||||
// Reverse the order of each byte.
|
||||
template <typename T> |
||||
T ReverseBytes(const T& data); |
||||
|
||||
// Check the order in which bytes are stored in computer memory.
|
||||
bool IsLittleEndian(); |
||||
bool IsBigEndian(); |
||||
|
||||
// Convert data between endianness and the native format. Note that, for float
|
||||
// and double types, these functions are only valid if the format is IEEE-754.
|
||||
// This is the case for pretty much most processors.
|
||||
template <typename T> |
||||
T LittleEndianToNative(const T x); |
||||
template <typename T> |
||||
T BigEndianToNative(const T x); |
||||
template <typename T> |
||||
T NativeToLittleEndian(const T x); |
||||
template <typename T> |
||||
T NativeToBigEndian(const T x); |
||||
|
||||
// Read data in little endian format for cross-platform support.
|
||||
template <typename T> |
||||
T ReadBinaryLittleEndian(std::istream* stream); |
||||
template <typename T> |
||||
void ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data); |
||||
|
||||
// Write data in little endian format for cross-platform support.
|
||||
template <typename T> |
||||
void WriteBinaryLittleEndian(std::ostream* stream, const T& data); |
||||
template <typename T> |
||||
void WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data); |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T> |
||||
T ReverseBytes(const T& data) { |
||||
T data_reversed = data; |
||||
std::reverse(reinterpret_cast<char*>(&data_reversed), |
||||
reinterpret_cast<char*>(&data_reversed) + sizeof(T)); |
||||
return data_reversed; |
||||
} |
||||
|
||||
inline bool IsLittleEndian() { |
||||
#ifdef BOOST_BIG_ENDIAN |
||||
return false; |
||||
#else |
||||
return true; |
||||
#endif |
||||
} |
||||
|
||||
inline bool IsBigEndian() { |
||||
#ifdef BOOST_BIG_ENDIAN |
||||
return true; |
||||
#else |
||||
return false; |
||||
#endif |
||||
} |
||||
|
||||
template <typename T> |
||||
T LittleEndianToNative(const T x) { |
||||
if (IsLittleEndian()) { |
||||
return x; |
||||
} else { |
||||
return ReverseBytes(x); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
T BigEndianToNative(const T x) { |
||||
if (IsBigEndian()) { |
||||
return x; |
||||
} else { |
||||
return ReverseBytes(x); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
T NativeToLittleEndian(const T x) { |
||||
if (IsLittleEndian()) { |
||||
return x; |
||||
} else { |
||||
return ReverseBytes(x); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
T NativeToBigEndian(const T x) { |
||||
if (IsBigEndian()) { |
||||
return x; |
||||
} else { |
||||
return ReverseBytes(x); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
T ReadBinaryLittleEndian(std::istream* stream) { |
||||
T data_little_endian; |
||||
stream->read(reinterpret_cast<char*>(&data_little_endian), sizeof(T)); |
||||
return LittleEndianToNative(data_little_endian); |
||||
} |
||||
|
||||
template <typename T> |
||||
void ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data) { |
||||
for (size_t i = 0; i < data->size(); ++i) { |
||||
(*data)[i] = ReadBinaryLittleEndian<T>(stream); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
void WriteBinaryLittleEndian(std::ostream* stream, const T& data) { |
||||
const T data_little_endian = NativeToLittleEndian(data); |
||||
stream->write(reinterpret_cast<const char*>(&data_little_endian), sizeof(T)); |
||||
} |
||||
|
||||
template <typename T> |
||||
void WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data) { |
||||
for (const auto& elem : data) { |
||||
WriteBinaryLittleEndian<T>(stream, elem); |
||||
} |
||||
} |
||||
|
||||
} // namespace colmap
|
||||
|
||||
#endif // COLMAP_SRC_UTIL_ENDIAN_H_
|
||||
|
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(InterfaceMVSNet "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS InterfaceMVSNet |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,706 @@
@@ -0,0 +1,706 @@
|
||||
/*
|
||||
* InterfaceMVSNet.cpp |
||||
* |
||||
* Copyright (c) 2014-2021 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
#define JSON_NOEXCEPTION |
||||
#include "../../libs/IO/json.hpp" |
||||
#include <boost/program_options.hpp> |
||||
|
||||
using namespace MVS; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
// uncomment to enable multi-threading based on OpenMP
|
||||
#ifdef _USE_OPENMP |
||||
#define MVSNET_USE_OPENMP |
||||
#endif |
||||
|
||||
#define APPNAME _T("InterfaceMVSNet") |
||||
#define MVS_FILE_EXTENSION _T(".mvs") |
||||
#define MVSNET_IMAGES_FOLDER _T("images") |
||||
#define MVSNET_CAMERAS_FOLDER _T("cams") |
||||
#define MVSNET_IMAGES_EXT _T(".jpg") |
||||
#define MVSNET_CAMERAS_NAME _T("_cam.txt") |
||||
#define RTMV_CAMERAS_EXT _T(".json") |
||||
#define NERFSTUDIO_TRANSFORMS _T("transforms.json") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace OPT { |
||||
String strInputFileName; |
||||
String strOutputFileName; |
||||
unsigned nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
String strConfigFileName; |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "produce this help message") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
boost::program_options::options_description config("Main options"); |
||||
config.add_options() |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
// initialize the log file
|
||||
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-") + Util::getUniqueName(0) + _T(".log")).c_str()); |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidPath(OPT::strInputFileName); |
||||
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower()); |
||||
const bool bInvalidCommand(OPT::strInputFileName.empty()); |
||||
if (OPT::vm.count("help") || bInvalidCommand) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config); |
||||
GET_LOG() << visible; |
||||
} |
||||
if (bInvalidCommand) |
||||
return false; |
||||
|
||||
// initialize optional options
|
||||
Util::ensureValidPath(OPT::strOutputFileName); |
||||
if (OPT::strOutputFileName.empty()) |
||||
OPT::strOutputFileName = Util::getFileName(OPT::strInputFileName) + "scene" MVS_FILE_EXTENSION; |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
void ImageListParse(const LPSTR* argv, Point3& C) |
||||
{ |
||||
// read position vector
|
||||
C.x = String::FromString<REAL>(argv[0]); |
||||
C.y = String::FromString<REAL>(argv[1]); |
||||
C.z = String::FromString<REAL>(argv[2]); |
||||
} |
||||
|
||||
void ImageListParse(const LPSTR* argv, Matrix3x3& R) |
||||
{ |
||||
// read rotation matrix
|
||||
R(0, 0) = String::FromString<REAL>(argv[0]); |
||||
R(0, 1) = String::FromString<REAL>(argv[1]); |
||||
R(0, 2) = String::FromString<REAL>(argv[2]); |
||||
R(1, 0) = String::FromString<REAL>(argv[3]); |
||||
R(1, 1) = String::FromString<REAL>(argv[4]); |
||||
R(1, 2) = String::FromString<REAL>(argv[5]); |
||||
R(2, 0) = String::FromString<REAL>(argv[6]); |
||||
R(2, 1) = String::FromString<REAL>(argv[7]); |
||||
R(2, 2) = String::FromString<REAL>(argv[8]); |
||||
} |
||||
|
||||
void ImageListParse(const LPSTR* argv, Matrix3x4& P) |
||||
{ |
||||
// read projection matrix
|
||||
P(0, 0) = String::FromString<REAL>(argv[0]); |
||||
P(0, 1) = String::FromString<REAL>(argv[1]); |
||||
P(0, 2) = String::FromString<REAL>(argv[2]); |
||||
P(0, 3) = String::FromString<REAL>(argv[3]); |
||||
P(1, 0) = String::FromString<REAL>(argv[4]); |
||||
P(1, 1) = String::FromString<REAL>(argv[5]); |
||||
P(1, 2) = String::FromString<REAL>(argv[6]); |
||||
P(1, 3) = String::FromString<REAL>(argv[7]); |
||||
P(2, 0) = String::FromString<REAL>(argv[8]); |
||||
P(2, 1) = String::FromString<REAL>(argv[9]); |
||||
P(2, 2) = String::FromString<REAL>(argv[10]); |
||||
P(2, 3) = String::FromString<REAL>(argv[11]); |
||||
} |
||||
|
||||
// convert a range-map to depth-map
|
||||
void RangeToDepthMap(const Image32F& rangeMap, const Camera& camera, DepthMap& depthMap) |
||||
{ |
||||
depthMap.create(rangeMap.size()); |
||||
for (int y = 0; y < depthMap.rows; ++y) { |
||||
const float* const rangeRow = rangeMap.ptr<float>(y); |
||||
float* const depthRow = depthMap.ptr<float>(y); |
||||
for (int x = 0; x < depthMap.cols; ++x) { |
||||
const float range = rangeRow[x]; |
||||
depthRow[x] = (Depth)(range <= 0 ? 0 : normalized(camera.TransformPointI2C(Point2(x,y))).z*range); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// parse scene stored in MVSNet format composed of undistorted images and camera poses
|
||||
// for example see GigaVision benchmark (http://gigamvs.net):
|
||||
// |--sceneX
|
||||
// |--images
|
||||
// |--xxx.jpg
|
||||
// |--xxx.jpg
|
||||
// ....
|
||||
// |--cams
|
||||
// |--xxx_cam.txt
|
||||
// |--xxx_cam.txt
|
||||
// ....
|
||||
// |--render_cams
|
||||
// |--xxx_cam.txt
|
||||
// |--xxx_cam.txt
|
||||
// ....
|
||||
//
|
||||
// where the camera parameter of one image stored in a cam.txt file contains the camera
|
||||
// extrinsic E = [R|t], intrinsic K and the depth range:
|
||||
// extrinsic
|
||||
// E00 E01 E02 E03
|
||||
// E10 E11 E12 E13
|
||||
// E20 E21 E22 E23
|
||||
// E30 E31 E32 E33
|
||||
//
|
||||
// intrinsic
|
||||
// K00 K01 K02
|
||||
// K10 K11 K12
|
||||
// K20 K21 K22
|
||||
//
|
||||
// DEPTH_MIN DEPTH_INTERVAL (DEPTH_NUM DEPTH_MAX)
|
||||
bool ParseSceneMVSNet(Scene& scene, const String& strPath) |
||||
{ |
||||
#if defined(_SUPPORT_CPP17) && (!defined(__GNUC__) || (__GNUC__ > 7)) |
||||
IIndex prevPlatformID = NO_ID; |
||||
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator((strPath + MVSNET_IMAGES_FOLDER).c_str())) { |
||||
if (entry.path().extension() != MVSNET_IMAGES_EXT) |
||||
continue; |
||||
// parse camera
|
||||
const std::string strCamFileName(strPath + MVSNET_CAMERAS_FOLDER PATH_SEPARATOR_STR + entry.path().stem().string().c_str() + MVSNET_CAMERAS_NAME); |
||||
std::ifstream fcam(strCamFileName); |
||||
if (!fcam) |
||||
continue; |
||||
String line; |
||||
do { |
||||
std::getline(fcam, line); |
||||
} while (line != "extrinsic" && fcam.good()); |
||||
String strP; |
||||
for (int i = 0; i < 3; ++i) { |
||||
std::getline(fcam, line); |
||||
strP += ' ' + line; |
||||
} |
||||
if (!fcam.good()) |
||||
continue; |
||||
size_t argc; |
||||
CAutoPtrArr<LPSTR> argv; |
||||
argv = Util::CommandLineToArgvA(strP, argc); |
||||
if (argc != 12) |
||||
continue; |
||||
Matrix3x4 P; |
||||
ImageListParse(argv, P); |
||||
do { |
||||
std::getline(fcam, line); |
||||
} while (line != "intrinsic" && fcam.good()); |
||||
strP.clear(); |
||||
for (int i = 0; i < 3; ++i) { |
||||
std::getline(fcam, line); |
||||
strP += ' ' + line; |
||||
} |
||||
if (!fcam.good()) |
||||
continue; |
||||
argv = Util::CommandLineToArgvA(strP, argc); |
||||
if (argc != 9) |
||||
continue; |
||||
Matrix3x3 K; |
||||
ImageListParse(argv, K); |
||||
// setup camera
|
||||
IIndex platformID; |
||||
if (prevPlatformID == NO_ID || !K.IsEqual(scene.platforms[prevPlatformID].cameras[0].K, 1e-3)) { |
||||
prevPlatformID = platformID = scene.platforms.size(); |
||||
Platform& platform = scene.platforms.emplace_back(); |
||||
Platform::Camera& camera = platform.cameras.emplace_back(); |
||||
camera.K = K; |
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
} else { |
||||
platformID = prevPlatformID; |
||||
} |
||||
Platform& platform = scene.platforms[platformID]; |
||||
// setup image
|
||||
const IIndex ID = scene.images.size(); |
||||
Image& imageData = scene.images.emplace_back(); |
||||
imageData.platformID = platformID; |
||||
imageData.cameraID = 0; // only one camera per platform supported by this format
|
||||
imageData.poseID = NO_ID; |
||||
imageData.ID = ID; |
||||
imageData.name = entry.path().string(); |
||||
Util::ensureUnifySlash(imageData.name); |
||||
imageData.name = MAKE_PATH_FULL(strPath, imageData.name); |
||||
// set image resolution
|
||||
IMAGEPTR pimage = Image::ReadImageHeader(imageData.name); |
||||
imageData.width = pimage->GetWidth(); |
||||
imageData.height = pimage->GetHeight(); |
||||
imageData.scale = 1; |
||||
// set camera pose
|
||||
imageData.poseID = platform.poses.size(); |
||||
Platform::Pose& pose = platform.poses.emplace_back(); |
||||
DecomposeProjectionMatrix(P, pose.R, pose.C); |
||||
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID); |
||||
} |
||||
if (scene.images.size() < 2) |
||||
return false; |
||||
scene.nCalibratedImages = (unsigned)scene.images.size(); |
||||
return true; |
||||
#else |
||||
VERBOSE("error: C++17 is required to parse MVSNet format"); |
||||
return false; |
||||
#endif // _SUPPORT_CPP17
|
||||
} |
||||
|
||||
// RTMV scene format: http://www.cs.umd.edu/~mmeshry/projects/rtmv
|
||||
// |--sceneX
|
||||
// |--images
|
||||
// |--xxx.jpg
|
||||
// |--xxx.jpg
|
||||
// ....
|
||||
// |--outputs (optional)
|
||||
// |--depthxxxx.exr
|
||||
// |--normalxxxx.exr
|
||||
// ....
|
||||
// |--transforms.json
|
||||
bool ParseSceneNerfstudio(Scene& scene, const String& strPath) |
||||
{ |
||||
const nlohmann::json data = nlohmann::json::parse(std::ifstream(strPath + NERFSTUDIO_TRANSFORMS)); |
||||
if (data.empty()) |
||||
return false; |
||||
// parse camera
|
||||
const cv::Size resolution(data["w"].get<uint32_t>(), data["h"].get<uint32_t>()); |
||||
const IIndex platformID = scene.platforms.size(); |
||||
Platform& platform = scene.platforms.emplace_back(); |
||||
Platform::Camera& camera = platform.cameras.emplace_back(); |
||||
camera.K = KMatrix::IDENTITY; |
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
camera.K(0,0) = data["fl_x"].get<REAL>(); |
||||
camera.K(1,1) = data["fl_y"].get<REAL>(); |
||||
camera.K(0,2) = data["cx"].get<REAL>(); |
||||
camera.K(1,2) = data["cy"].get<REAL>(); |
||||
const String cameraModel = data["camera_model"].get<std::string>(); |
||||
if (cameraModel == "SIMPLE_PINHOLE") { |
||||
} else |
||||
// check ZERO radial distortion for all "PERSPECTIVE" type cameras
|
||||
if (cameraModel == "PINHOLE" || cameraModel == "SIMPLE_RADIAL" || cameraModel == "RADIAL" || cameraModel == "OPENCV") { |
||||
const REAL k1 = data["k1"].get<REAL>(); |
||||
const REAL k2 = data["k2"].get<REAL>(); |
||||
const REAL p1 = data["p1"].get<REAL>(); |
||||
const REAL p2 = data["p2"].get<REAL>(); |
||||
if (k1 != 0 || k2 != 0 || p1 != 0 || p2 != 0) { |
||||
VERBOSE("error: radial distortion not supported"); |
||||
return false; |
||||
} |
||||
} else { |
||||
VERBOSE("error: camera model not supported"); |
||||
return false; |
||||
} |
||||
// parse images
|
||||
const nlohmann::json& frames = data["frames"]; |
||||
for (const nlohmann::json& frame: frames) { |
||||
// set image
|
||||
// frames expected to be ordered in JSON
|
||||
const IIndex imageID = scene.images.size(); |
||||
const String strFileName(strPath + frame["file_path"].get<std::string>().c_str()); |
||||
Image& imageData = scene.images.emplace_back(); |
||||
imageData.platformID = platformID; |
||||
imageData.cameraID = 0; // only one camera per platform supported by this format
|
||||
imageData.poseID = NO_ID; |
||||
imageData.ID = imageID; |
||||
imageData.name = strFileName; |
||||
ASSERT(Util::isFullPath(imageData.name)); |
||||
// set image resolution
|
||||
imageData.width = resolution.width; |
||||
imageData.height = resolution.height; |
||||
imageData.scale = 1; |
||||
// load camera pose
|
||||
imageData.poseID = platform.poses.size(); |
||||
Platform::Pose& pose = platform.poses.emplace_back(); |
||||
const auto Ps = frame["transform_matrix"].get<std::vector<std::vector<double>>>(); |
||||
Eigen::Matrix4d P{ |
||||
{Ps[0][0], Ps[0][1], Ps[0][2], Ps[0][3]}, |
||||
{Ps[1][0], Ps[1][1], Ps[1][2], Ps[1][3]}, |
||||
{Ps[2][0], Ps[2][1], Ps[2][2], Ps[2][3]}, |
||||
{Ps[3][0], Ps[3][1], Ps[3][2], Ps[3][3]} |
||||
}; |
||||
// revert nerfstudio conversion:
|
||||
// convert from COLMAP's camera coordinate system (OpenCV) to ours (OpenGL)
|
||||
// c2w[0:3, 1:3] *= -1
|
||||
// c2w = c2w[np.array([1, 0, 2, 3]), :]
|
||||
// c2w[2, :] *= -1
|
||||
P.row(2) *= -1; |
||||
P.row(0).swap(P.row(1)); |
||||
P.col(2) *= -1; |
||||
P.col(1) *= -1; |
||||
// set camera pose
|
||||
pose.R = P.topLeftCorner<3, 3>().transpose().eval(); |
||||
pose.R.EnforceOrthogonality(); |
||||
pose.C = P.topRightCorner<3, 1>().eval(); |
||||
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID); |
||||
// try reading depth-map and normal-map
|
||||
DepthMap depthMap; { |
||||
const String depthPath(strPath + String::FormatString("outputs/depth%04u.exr", imageID)); |
||||
const Image32F rangeMap = cv::imread(depthPath, cv::IMREAD_UNCHANGED); |
||||
if (rangeMap.empty()) { |
||||
VERBOSE("Unable to load depthmap %s.", depthPath.c_str()); |
||||
continue; |
||||
} |
||||
RangeToDepthMap(rangeMap, imageData.camera, depthMap); |
||||
} |
||||
NormalMap normalMap; { |
||||
const String normalPath(strPath + String::FormatString("outputs/normal%04u.exr", imageID)); |
||||
normalMap = cv::imread(normalPath, cv::IMREAD_UNCHANGED); |
||||
if (normalMap.empty()) { |
||||
VERBOSE("Unable to load normalMap %s.", normalPath.c_str()); |
||||
continue; |
||||
} |
||||
} |
||||
const ConfidenceMap confMap; |
||||
const ViewsMap viewsMap; |
||||
const IIndexArr IDs = {imageID}; |
||||
double dMin, dMax; |
||||
cv::minMaxIdx(depthMap, &dMin, &dMax, NULL, NULL, depthMap > 0); |
||||
const String dmapPath(strPath + String::FormatString("depth%04u.dmap", imageID)); |
||||
if (!ExportDepthDataRaw(dmapPath, |
||||
imageData.name, IDs, resolution, |
||||
camera.K, pose.R, pose.C, |
||||
(float)dMin, (float)dMax, |
||||
depthMap, normalMap, confMap, viewsMap)) |
||||
{ |
||||
VERBOSE("Unable to save dmap: %s", dmapPath.c_str()); |
||||
continue; |
||||
} |
||||
} |
||||
if (scene.images.size() < 2) |
||||
return false; |
||||
scene.nCalibratedImages = (unsigned)scene.images.size(); |
||||
return true; |
||||
} |
||||
|
||||
// RTMV scene format: http://www.cs.umd.edu/~mmeshry/projects/rtmv
|
||||
// |--sceneX
|
||||
// |--xxx.exr
|
||||
// |--xxx.seg.exr
|
||||
// |--xxx.depth.exr
|
||||
// |--xxx.json
|
||||
// ....
|
||||
bool ParseSceneRTMV(Scene& scene, const String& strPath) |
||||
{ |
||||
const String strImagePath(strPath + "images/"); |
||||
Util::ensureFolder(strImagePath); |
||||
std::vector<String> strImageNames; |
||||
#if defined(_SUPPORT_CPP17) && (!defined(__GNUC__) || (__GNUC__ > 7)) |
||||
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(strPath.c_str())) { |
||||
if (entry.path().extension() != RTMV_CAMERAS_EXT) |
||||
continue; |
||||
strImageNames.emplace_back(entry.path().stem().string()); |
||||
} |
||||
#else |
||||
VERBOSE("error: C++17 is required to parse RTMV format"); |
||||
return false; |
||||
#endif // _SUPPORT_CPP17
|
||||
IIndex prevPlatformID = NO_ID; |
||||
scene.images.resize((IIndex)strImageNames.size()); |
||||
scene.platforms.reserve((IIndex)strImageNames.size()); |
||||
#ifdef MVSNET_USE_OPENMP |
||||
#pragma omp parallel for schedule(dynamic) |
||||
for (int_t i=0; i<(int_t)strImageNames.size(); ++i) { |
||||
#else |
||||
FOREACH(i, strImageNames) { |
||||
#endif |
||||
const IIndex imageID((IIndex)i); |
||||
const String& strImageName(strImageNames[imageID]); |
||||
// parse camera
|
||||
const String strFileName(strPath + strImageName); |
||||
const nlohmann::json dataCamera = nlohmann::json::parse(std::ifstream(strFileName+RTMV_CAMERAS_EXT)); |
||||
if (dataCamera.empty()) |
||||
continue; |
||||
const nlohmann::json& data = dataCamera["camera_data"]; |
||||
const cv::Size resolution(data["width"].get<uint32_t>(), data["height"].get<uint32_t>()); |
||||
// set platform
|
||||
Matrix3x3 K = Matrix3x3::IDENTITY; |
||||
K(0,0) = data["intrinsics"]["fx"].get<REAL>(); |
||||
K(1,1) = data["intrinsics"]["fy"].get<REAL>(); |
||||
K(0,2) = data["intrinsics"]["cx"].get<REAL>(); |
||||
K(1,2) = data["intrinsics"]["cy"].get<REAL>(); |
||||
IIndex platformID; |
||||
if (prevPlatformID == NO_ID || !K.IsEqual(scene.platforms[prevPlatformID].cameras[0].K, 1e-3)) { |
||||
#ifdef MVSNET_USE_OPENMP |
||||
#pragma omp critical |
||||
#endif |
||||
{ |
||||
prevPlatformID = platformID = scene.platforms.size(); |
||||
Platform& platform = scene.platforms.emplace_back(); |
||||
Platform::Camera& camera = platform.cameras.emplace_back(); |
||||
platform.poses.reserve((IIndex)strImageNames.size()); |
||||
camera.K = K; |
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
} |
||||
} else { |
||||
platformID = prevPlatformID; |
||||
} |
||||
Platform& platform = scene.platforms[platformID]; |
||||
// set image
|
||||
Image& imageData = scene.images[imageID]; |
||||
imageData.platformID = platformID; |
||||
imageData.cameraID = 0; // only one camera per platform supported by this format
|
||||
imageData.poseID = NO_ID; |
||||
imageData.ID = imageID; |
||||
imageData.name = strImagePath+strImageName+".jpg"; |
||||
ASSERT(Util::isFullPath(imageData.name)); |
||||
{ |
||||
cv::Mat image = cv::imread(strFileName+".exr", cv::IMREAD_UNCHANGED); |
||||
ASSERT(image.type() == CV_32FC4); |
||||
std::vector<cv::Mat> channels; |
||||
cv::split(image, channels); |
||||
cv::merge(std::vector<cv::Mat>{channels[0], channels[1], channels[2]}, image); |
||||
image.convertTo(imageData.image, CV_8UC3, 255); |
||||
} |
||||
ASSERT(resolution == imageData.image.size()); |
||||
if (imageData.image.empty()) { |
||||
VERBOSE("Unable to load image %s.", (strFileName+".exr").c_str()); |
||||
continue; |
||||
} |
||||
cv::imwrite(imageData.name, imageData.image); |
||||
imageData.ReleaseImage(); |
||||
// set image resolution
|
||||
imageData.width = resolution.width; |
||||
imageData.height = resolution.height; |
||||
imageData.scale = 1; |
||||
// load camera pose
|
||||
#ifdef MVSNET_USE_OPENMP |
||||
#pragma omp critical |
||||
#endif |
||||
{ |
||||
imageData.poseID = platform.poses.size(); |
||||
platform.poses.emplace_back(); |
||||
} |
||||
Platform::Pose& pose = platform.poses[imageData.poseID]; |
||||
const auto Ps = data["cam2world"].get<std::vector<std::vector<double>>>(); |
||||
Eigen::Matrix4d P{ |
||||
{Ps[0][0], Ps[1][0], Ps[2][0], Ps[3][0]}, |
||||
{Ps[0][1], Ps[1][1], Ps[2][1], Ps[3][1]}, |
||||
{Ps[0][2], Ps[1][2], Ps[2][2], Ps[3][2]}, |
||||
{Ps[0][3], Ps[1][3], Ps[2][3], Ps[3][3]} |
||||
}; |
||||
// apply the same transforms as nerfstudio converter
|
||||
P.row(2) *= -1; |
||||
P.row(0).swap(P.row(1)); |
||||
P.col(2) *= -1; |
||||
P.col(1) *= -1; |
||||
// set camera pose
|
||||
pose.R = P.topLeftCorner<3, 3>().transpose().eval(); |
||||
pose.R.EnforceOrthogonality(); |
||||
pose.C = P.topRightCorner<3, 1>().eval(); |
||||
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID); |
||||
// try reading the segmentation mask
|
||||
{ |
||||
cv::Mat imgMask = cv::imread(strFileName+".seg.exr", cv::IMREAD_UNCHANGED); |
||||
if (imgMask.empty()) { |
||||
VERBOSE("Unable to load segmentation mask %s.", (strFileName+".seg.exr").c_str()); |
||||
continue; |
||||
} |
||||
ASSERT(imgMask.type() == CV_32FC4); |
||||
ASSERT(resolution == imgMask.size()); |
||||
std::vector<cv::Mat> channels; |
||||
cv::split(imgMask, channels); |
||||
channels[0].convertTo(imgMask, CV_16U); |
||||
imageData.maskName = strImagePath+strImageName+".mask.png"; |
||||
cv::imwrite(imageData.maskName, imgMask); |
||||
} |
||||
// try reading the depth-map
|
||||
DepthMap depthMap; { |
||||
const cv::Mat imgDepthMap = cv::imread(strFileName+".depth.exr", cv::IMREAD_UNCHANGED); |
||||
if (imgDepthMap.empty()) { |
||||
VERBOSE("Unable to load depthmap %s.", (strFileName+".depth.exr").c_str()); |
||||
continue; |
||||
} |
||||
ASSERT(imgDepthMap.type() == CV_32FC4); |
||||
ASSERT(resolution == imgDepthMap.size()); |
||||
std::vector<cv::Mat> channels; |
||||
cv::split(imgDepthMap, channels); |
||||
RangeToDepthMap(channels[0], imageData.camera, depthMap); |
||||
} |
||||
const NormalMap normalMap; |
||||
const ConfidenceMap confMap; |
||||
const ViewsMap viewsMap; |
||||
const IIndexArr IDs = {imageID}; |
||||
double dMin, dMax; |
||||
cv::minMaxIdx(depthMap, &dMin, &dMax, NULL, NULL, depthMap > 0); |
||||
const String dmapPath(strPath + String::FormatString("depth%04u.dmap", imageID)); |
||||
if (!ExportDepthDataRaw(dmapPath, |
||||
imageData.name, IDs, resolution, |
||||
K, pose.R, pose.C, |
||||
(float)dMin, (float)dMax, |
||||
depthMap, normalMap, confMap, viewsMap)) |
||||
{ |
||||
VERBOSE("Unable to save dmap: %s", dmapPath.c_str()); |
||||
continue; |
||||
} |
||||
} |
||||
if (scene.images.size() < 2) |
||||
return false; |
||||
scene.nCalibratedImages = (unsigned)scene.images.size(); |
||||
return true; |
||||
} |
||||
|
||||
bool ParseScene(Scene& scene) |
||||
{ |
||||
#if defined(_SUPPORT_CPP17) && (!defined(__GNUC__) || (__GNUC__ > 7)) |
||||
String strPath(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName)); |
||||
Util::ensureValidFolderPath(strPath); |
||||
const std::filesystem::path path(static_cast<const std::string&>(strPath)); |
||||
enum Type { |
||||
MVSNet = 0, |
||||
NERFSTUDIO, |
||||
RTMV, |
||||
} sceneType = MVSNet; |
||||
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(path)) { |
||||
if (entry.path().extension() == RTMV_CAMERAS_EXT) { |
||||
if (entry.path().filename() == NERFSTUDIO_TRANSFORMS) { |
||||
sceneType = NERFSTUDIO; |
||||
break; |
||||
} |
||||
sceneType = RTMV; |
||||
} |
||||
} |
||||
switch (sceneType) { |
||||
case NERFSTUDIO: return ParseSceneNerfstudio(scene, strPath); |
||||
case RTMV: return ParseSceneRTMV(scene, strPath); |
||||
default: return ParseSceneMVSNet(scene, strPath); |
||||
} |
||||
#else |
||||
VERBOSE("error: C++17 is required to parse MVSNet format"); |
||||
return false; |
||||
#endif // _SUPPORT_CPP17
|
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
TD_TIMER_START(); |
||||
|
||||
Scene scene(OPT::nMaxThreads); |
||||
|
||||
// convert data from MVSNet format to OpenMVS
|
||||
if (!ParseScene(scene)) |
||||
return EXIT_FAILURE; |
||||
|
||||
// write OpenMVS input data
|
||||
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
|
||||
VERBOSE("Imported data: %u platforms, %u images, %u vertices (%s)", |
||||
scene.platforms.size(), scene.images.size(), scene.pointcloud.GetSize(), |
||||
TD_TIMER_GET_FMT().c_str()); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(InterfaceMetashape "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS InterfaceMetashape |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,887 @@
@@ -0,0 +1,887 @@
|
||||
/*
|
||||
* InterfaceMetashape.cpp |
||||
* |
||||
* Copyright (c) 2014-2021 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
#include "../../libs/IO/TinyXML2.h" |
||||
#include <boost/program_options.hpp> |
||||
|
||||
using namespace MVS; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("InterfaceMetashape") // previously PhotoScan
|
||||
#define MVS_FILE_EXTENSION _T(".mvs") |
||||
#define XML_EXT _T(".xml") |
||||
#define PLY_EXT _T(".ply") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace OPT { |
||||
String strInputFileName; |
||||
String strPointsFileName; |
||||
String strOutputFileName; |
||||
String strOutputImageFolder; |
||||
unsigned nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
String strConfigFileName; |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "imports SfM scene stored either in Metashape Agisoft/BlocksExchange or ContextCapture BlocksExchange XML format") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
boost::program_options::options_description config("Main options"); |
||||
config.add_options() |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list") |
||||
("points-file,p", boost::program_options::value<std::string>(&OPT::strPointsFileName), "input filename containing the 3D points") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the scene") |
||||
("output-image-folder", boost::program_options::value<std::string>(&OPT::strOutputImageFolder)->default_value("undistorted_images"), "output folder to store undistorted images") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
// initialize the log file
|
||||
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str()); |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidPath(OPT::strPointsFileName); |
||||
Util::ensureValidPath(OPT::strInputFileName); |
||||
Util::ensureValidFolderPath(OPT::strOutputImageFolder); |
||||
const bool bInvalidCommand(OPT::strInputFileName.empty()); |
||||
if (OPT::vm.count("help") || bInvalidCommand) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config); |
||||
GET_LOG() << visible; |
||||
} |
||||
if (bInvalidCommand) |
||||
return false; |
||||
|
||||
// initialize optional options
|
||||
Util::ensureValidPath(OPT::strOutputFileName); |
||||
if (OPT::strOutputFileName.empty()) |
||||
OPT::strOutputFileName = Util::getFileName(OPT::strInputFileName) + MVS_FILE_EXTENSION; |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
struct DistCoeff { |
||||
union { |
||||
REAL coeff[8]; |
||||
struct { |
||||
REAL k1, k2, p1, p2, k3, k4, k5, k6; |
||||
}; |
||||
}; |
||||
DistCoeff() : k1(0), k2(0), p1(0), p2(0), k3(0), k4(0), k5(0), k6(0) {} |
||||
bool HasDistortion() const { return k1 != 0 || k2 != 0 || k3 != 0 || k4 != 0 || k5 != 0 || k6 != 0; } |
||||
}; |
||||
typedef cList<DistCoeff> DistCoeffs; |
||||
typedef cList<DistCoeffs> PlatformDistCoeffs; |
||||
|
||||
void ImageListParseC(const LPSTR* argv, Point3& C) |
||||
{ |
||||
// read position vector
|
||||
C.x = String::FromString<REAL>(argv[0]); |
||||
C.y = String::FromString<REAL>(argv[1]); |
||||
C.z = String::FromString<REAL>(argv[2]); |
||||
} |
||||
|
||||
void ImageListParseR(const LPSTR* argv, Matrix3x3& R) |
||||
{ |
||||
// read rotation matrix
|
||||
R(0, 0) = String::FromString<REAL>(argv[0]); |
||||
R(0, 1) = String::FromString<REAL>(argv[1]); |
||||
R(0, 2) = String::FromString<REAL>(argv[2]); |
||||
R(1, 0) = String::FromString<REAL>(argv[3]); |
||||
R(1, 1) = String::FromString<REAL>(argv[4]); |
||||
R(1, 2) = String::FromString<REAL>(argv[5]); |
||||
R(2, 0) = String::FromString<REAL>(argv[6]); |
||||
R(2, 1) = String::FromString<REAL>(argv[7]); |
||||
R(2, 2) = String::FromString<REAL>(argv[8]); |
||||
} |
||||
|
||||
void ImageListParseP(const LPSTR* argv, Matrix3x4& P) |
||||
{ |
||||
// read projection matrix
|
||||
P(0, 0) = String::FromString<REAL>(argv[0]); |
||||
P(0, 1) = String::FromString<REAL>(argv[1]); |
||||
P(0, 2) = String::FromString<REAL>(argv[2]); |
||||
P(0, 3) = String::FromString<REAL>(argv[3]); |
||||
P(1, 0) = String::FromString<REAL>(argv[4]); |
||||
P(1, 1) = String::FromString<REAL>(argv[5]); |
||||
P(1, 2) = String::FromString<REAL>(argv[6]); |
||||
P(1, 3) = String::FromString<REAL>(argv[7]); |
||||
P(2, 0) = String::FromString<REAL>(argv[8]); |
||||
P(2, 1) = String::FromString<REAL>(argv[9]); |
||||
P(2, 2) = String::FromString<REAL>(argv[10]); |
||||
P(2, 3) = String::FromString<REAL>(argv[11]); |
||||
} |
||||
|
||||
// parse images list containing calibration and pose information
|
||||
// and load the corresponding 3D point-cloud
|
||||
bool ParseImageListXML(tinyxml2::XMLDocument& doc, Scene& scene, PlatformDistCoeffs& pltDistCoeffs, size_t& nCameras, size_t& nPoses) |
||||
{ |
||||
String strInputFileName(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName)); |
||||
Util::ensureValidPath(strInputFileName); |
||||
tinyxml2::XMLElement* elem; |
||||
if (doc.ErrorID() != tinyxml2::XML_SUCCESS) |
||||
goto InvalidDocument; |
||||
{ |
||||
tinyxml2::XMLElement* document = doc.FirstChildElement(_T("document"))->FirstChildElement(_T("chunk")); |
||||
if (document == NULL) |
||||
goto InvalidDocument; |
||||
{ |
||||
bool bMetashapeFile(false); |
||||
CLISTDEF0(cv::Size) resolutions; |
||||
std::unordered_map<IIndex,IIndex> mapPlatformID; |
||||
std::unordered_map<IIndex,IIndex> mapImageID; |
||||
|
||||
// parse platform and camera models
|
||||
{ |
||||
tinyxml2::XMLElement* sensors = document->FirstChildElement(_T("sensors")); |
||||
if (sensors == NULL) |
||||
goto InvalidDocument; |
||||
{ |
||||
for (tinyxml2::XMLElement* sensor=sensors->FirstChildElement(); sensor!=NULL; sensor=sensor->NextSiblingElement()) { |
||||
unsigned ID; |
||||
if (0 != _tcsicmp(sensor->Value(), _T("sensor")) || sensor->QueryUnsignedAttribute(_T("id"), &ID) != tinyxml2::XML_SUCCESS) |
||||
goto InvalidDocument; |
||||
{ |
||||
// add new camera
|
||||
enum CameraModel {METASHAPE=0, VSFM}; |
||||
int model(METASHAPE); |
||||
sensor->QueryIntAttribute(_T("model"), &model); |
||||
mapPlatformID.emplace(ID, scene.platforms.size()); |
||||
Platform& platform = scene.platforms.AddEmpty(); |
||||
LPCTSTR name; |
||||
if ((name=sensor->Attribute(_T("label"))) != NULL) |
||||
platform.name = name; |
||||
// parse intrinsics
|
||||
tinyxml2::XMLElement* calibration = sensor->FirstChildElement(_T("calibration")); |
||||
if (calibration == NULL) |
||||
goto InvalidDocument; |
||||
{ |
||||
if ((elem=calibration->FirstChildElement(_T("resolution"))) != NULL) { |
||||
resolutions.emplace_back( |
||||
elem->UnsignedAttribute(_T("width")), |
||||
elem->UnsignedAttribute(_T("height")) |
||||
); |
||||
ASSERT(model == METASHAPE); |
||||
bMetashapeFile = true; |
||||
} |
||||
Platform::Camera& camera = platform.cameras.AddEmpty(); |
||||
camera.K = KMatrix::IDENTITY; |
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
DistCoeff& dc = pltDistCoeffs.AddEmpty().AddEmpty(); |
||||
for (elem=calibration->FirstChildElement(); elem!=NULL; elem=elem->NextSiblingElement()) { |
||||
if (0 == _tcsicmp(elem->Value(), _T("f"))) { |
||||
camera.K(0,0) = camera.K(1,1) = String::FromString<REAL>(elem->GetText()); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("fx"))) { |
||||
elem->QueryDoubleText(&camera.K(0,0)); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("fy"))) { |
||||
elem->QueryDoubleText(&camera.K(1,1)); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("cx"))) { |
||||
elem->QueryDoubleText(&camera.K(0,2)); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("cy"))) { |
||||
elem->QueryDoubleText(&camera.K(1,2)); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("k1"))) { |
||||
elem->QueryDoubleText(&dc.k1); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("k2"))) { |
||||
elem->QueryDoubleText(&dc.k2); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("k3"))) { |
||||
elem->QueryDoubleText(&dc.k3); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("p1"))) { |
||||
elem->QueryDoubleText(&dc.p1); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("p2"))) { |
||||
elem->QueryDoubleText(&dc.p2); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("k4"))) { |
||||
elem->QueryDoubleText(&dc.k4); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("k5"))) { |
||||
elem->QueryDoubleText(&dc.k5); |
||||
} else |
||||
if (0 == _tcsicmp(elem->Value(), _T("k6"))) { |
||||
elem->QueryDoubleText(&dc.k6); |
||||
} |
||||
} |
||||
if (bMetashapeFile) { |
||||
const cv::Size& resolution = resolutions.back(); |
||||
camera.K(0,2) += resolution.width*REAL(0.5); |
||||
camera.K(1,2) += resolution.height*REAL(0.5); |
||||
camera.K = camera.GetScaledK(REAL(1)/Camera::GetNormalizationScale(resolution.width, resolution.height)); |
||||
std::swap(dc.p1, dc.p2); |
||||
} |
||||
++nCameras; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// parse poses
|
||||
{ |
||||
tinyxml2::XMLElement* cameras = document->FirstChildElement(_T("cameras")); |
||||
if (cameras == NULL) |
||||
goto InvalidDocument; |
||||
{ |
||||
PMatrix P; |
||||
size_t argc; |
||||
const String strPath(Util::getFilePath(strInputFileName)); |
||||
for (tinyxml2::XMLElement* camera=cameras->FirstChildElement(); camera!=NULL; camera=camera->NextSiblingElement()) { |
||||
unsigned ID; |
||||
if (0 != _tcsicmp(camera->Value(), _T("camera")) || camera->QueryUnsignedAttribute(_T("id"), &ID) != tinyxml2::XML_SUCCESS) |
||||
goto InvalidDocument; |
||||
{ |
||||
// add new image
|
||||
mapImageID.emplace(ID, scene.images.size()); |
||||
Image& imageData = scene.images.AddEmpty(); |
||||
LPCTSTR name; |
||||
if ((name=camera->Attribute(_T("type"))) != NULL && _tcsicmp(name, _T("frame")) != 0) { |
||||
DEBUG_EXTRA("warning: unsupported camera calibration '%s'", name); |
||||
continue; |
||||
} |
||||
if ((name=camera->Attribute(_T("label"))) != NULL) |
||||
imageData.name = name; |
||||
Util::ensureUnifySlash(imageData.name); |
||||
if (Util::getFileExt(imageData.name).empty()) |
||||
imageData.name += _T(".jpg"); |
||||
imageData.name = MAKE_PATH_FULL(strPath, imageData.name); |
||||
imageData.platformID = mapPlatformID.at(camera->UnsignedAttribute(_T("sensor_id"))); |
||||
imageData.cameraID = 0; // only one camera per platform supported by this format
|
||||
imageData.ID = mapImageID.at(ID); |
||||
const cv::Size& resolution = resolutions[imageData.platformID]; |
||||
imageData.width = resolution.width; |
||||
imageData.height = resolution.height; |
||||
imageData.scale = 1; |
||||
if (!bMetashapeFile && !camera->BoolAttribute(_T("enabled"))) { |
||||
imageData.poseID = NO_ID; |
||||
DEBUG_EXTRA("warning: uncalibrated image '%s'", name); |
||||
continue; |
||||
} |
||||
// set pose
|
||||
CAutoPtrArr<LPSTR> argv; |
||||
if ((elem=camera->FirstChildElement(_T("transform"))) == NULL || |
||||
(argv=Util::CommandLineToArgvA(elem->GetText(), argc)) == NULL || |
||||
(argc != (bMetashapeFile ? 16 : 12))) |
||||
{ |
||||
VERBOSE("Invalid image list camera: %u", ID); |
||||
continue; |
||||
} |
||||
Platform& platform = scene.platforms[imageData.platformID]; |
||||
imageData.poseID = platform.poses.size(); |
||||
Platform::Pose& pose = platform.poses.AddEmpty(); |
||||
ImageListParseP(argv, P); |
||||
DecomposeProjectionMatrix(P, pose.R, pose.C); |
||||
if (bMetashapeFile) { |
||||
pose.C = pose.R*(-pose.C); |
||||
pose.R = pose.R.t(); |
||||
} |
||||
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID); |
||||
++nPoses; |
||||
} |
||||
} |
||||
scene.nCalibratedImages = (unsigned)nPoses; |
||||
} |
||||
|
||||
// parse bounding-box
|
||||
{ |
||||
tinyxml2::XMLElement* region = document->FirstChildElement(_T("region")); |
||||
if (region == NULL) |
||||
goto InvalidDocument; |
||||
{ |
||||
size_t argc; |
||||
CAutoPtrArr<LPSTR> argv; |
||||
Point3 C, E; Matrix3x3 R; |
||||
if ((elem=region->FirstChildElement(_T("center"))) == NULL || |
||||
(argv=Util::CommandLineToArgvA(elem->GetText(), argc)) == NULL || |
||||
argc != 3) |
||||
{ |
||||
VERBOSE("Invalid image list region: %s", elem->GetText()); |
||||
goto InvalidDocument; |
||||
} |
||||
ImageListParseC(argv, C); |
||||
if ((elem=region->FirstChildElement(_T("size"))) == NULL || |
||||
(argv=Util::CommandLineToArgvA(elem->GetText(), argc)) == NULL || |
||||
argc != 3) |
||||
{ |
||||
VERBOSE("Invalid image list region: %s", elem->GetText()); |
||||
goto InvalidDocument; |
||||
} |
||||
ImageListParseC(argv, E); |
||||
E *= REAL(0.5); |
||||
if ((elem=region->FirstChildElement(_T("R"))) == NULL || |
||||
(argv=Util::CommandLineToArgvA(elem->GetText(), argc)) == NULL || |
||||
argc != 9) |
||||
{ |
||||
VERBOSE("Invalid image list region: %s", elem->GetText()); |
||||
goto InvalidDocument; |
||||
} |
||||
ImageListParseR(argv, R); |
||||
scene.obb.m_rot = Cast<float>(R); |
||||
scene.obb.m_pos = Cast<float>(C); |
||||
scene.obb.m_ext = Cast<float>(E); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
InvalidDocument: |
||||
VERBOSE("Invalid camera list"); |
||||
return false; |
||||
} |
||||
|
||||
// parse scene stored in ContextCapture BlocksExchange format containing cameras, images and sparse point-cloud
|
||||
bool ParseBlocksExchangeXML(tinyxml2::XMLDocument& doc, Scene& scene, PlatformDistCoeffs& pltDistCoeffs, size_t& nCameras, size_t& nPoses) { |
||||
String strInputFileName(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName)); |
||||
Util::ensureValidPath(strInputFileName); |
||||
const tinyxml2::XMLElement* blocksExchange; |
||||
const tinyxml2::XMLElement* document; |
||||
const tinyxml2::XMLElement* photogroups; |
||||
if (doc.ErrorID() != tinyxml2::XML_SUCCESS || |
||||
(blocksExchange=doc.FirstChildElement("BlocksExchange")) == NULL || |
||||
(document=blocksExchange->FirstChildElement("Block")) == NULL || |
||||
(photogroups=document->FirstChildElement("Photogroups")) == NULL) { |
||||
VERBOSE("error: invalid scene file"); |
||||
return false; |
||||
} |
||||
CLISTDEF0(cv::Size) resolutions; |
||||
std::unordered_map<IIndex,IIndex> mapImageID; |
||||
const String strPath(Util::getFilePath(strInputFileName)); |
||||
const tinyxml2::XMLElement* elem; |
||||
for (const tinyxml2::XMLElement* photogroup=photogroups->FirstChildElement(); photogroup!=NULL; photogroup=photogroup->NextSiblingElement()) { |
||||
if ((elem=photogroup->FirstChildElement("CameraModelType")) == NULL || |
||||
std::strcmp(elem->GetText(), "Perspective") != 0) |
||||
continue; |
||||
if ((elem=photogroup->FirstChildElement("ImageDimensions")) == NULL) |
||||
continue; |
||||
const IIndex platformID = scene.platforms.size(); |
||||
Platform& platform = scene.platforms.AddEmpty(); |
||||
platform.name = photogroup->FirstChildElement("Name")->GetText(); |
||||
resolutions.emplace_back( |
||||
elem->FirstChildElement("Width")->UnsignedText(), |
||||
elem->FirstChildElement("Height")->UnsignedText() |
||||
); |
||||
// parse camera
|
||||
Platform::Camera& camera = platform.cameras.AddEmpty(); |
||||
camera.K = KMatrix::IDENTITY; |
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
const float resolutionScale = Camera::GetNormalizationScale(resolutions.back().width, resolutions.back().height); |
||||
if ((elem=photogroup->FirstChildElement("FocalLengthPixels")) != NULL) { |
||||
camera.K(0,0) = camera.K(1,1) = photogroup->FirstChildElement("FocalLengthPixels")->DoubleText(); |
||||
} else { |
||||
camera.K(0,0) = camera.K(1,1) = photogroup->FirstChildElement("FocalLength")->DoubleText() * resolutionScale / photogroup->FirstChildElement("SensorSize")->DoubleText(); |
||||
} |
||||
if ((elem=photogroup->FirstChildElement("PrincipalPoint")) != NULL) { |
||||
camera.K(0,2) = elem->FirstChildElement("x")->DoubleText(); |
||||
camera.K(1,2) = elem->FirstChildElement("y")->DoubleText(); |
||||
} else { |
||||
camera.K(0,2) = resolutions.back().width*REAL(0.5); |
||||
camera.K(1,2) = resolutions.back().height*REAL(0.5); |
||||
} |
||||
if ((elem=photogroup->FirstChildElement("AspectRatio")) != NULL) |
||||
camera.K(1,1) *= elem->DoubleText(); |
||||
if ((elem=photogroup->FirstChildElement("Skew")) != NULL) |
||||
camera.K(0,1) = elem->DoubleText(); |
||||
camera.K = camera.GetScaledK(REAL(1)/resolutionScale); |
||||
// parse distortion parameters
|
||||
DistCoeff& dc = pltDistCoeffs.AddEmpty().AddEmpty(); { |
||||
const tinyxml2::XMLElement* distortion=photogroup->FirstChildElement("Distortion"); |
||||
if (distortion) { |
||||
if ((elem=distortion->FirstChildElement("K1")) != NULL) |
||||
dc.k1 = elem->DoubleText(); |
||||
if ((elem=distortion->FirstChildElement("K2")) != NULL) |
||||
dc.k2 = elem->DoubleText(); |
||||
if ((elem=distortion->FirstChildElement("K3")) != NULL) |
||||
dc.k3 = elem->DoubleText(); |
||||
if ((elem=distortion->FirstChildElement("P1")) != NULL) |
||||
dc.p2 = elem->DoubleText(); |
||||
if ((elem=distortion->FirstChildElement("P2")) != NULL) |
||||
dc.p1 = elem->DoubleText(); |
||||
} |
||||
} |
||||
++nCameras; |
||||
for (const tinyxml2::XMLElement* photo=photogroup->FirstChildElement("Photo"); photo!=NULL; photo=photo->NextSiblingElement()) { |
||||
const IIndex idxImage = scene.images.size(); |
||||
Image& imageData = scene.images.AddEmpty(); |
||||
imageData.platformID = platformID; |
||||
imageData.cameraID = 0; // only one camera per platform supported by this format
|
||||
imageData.poseID = NO_ID; |
||||
imageData.ID = photo->FirstChildElement("Id")->UnsignedText(); |
||||
imageData.name = photo->FirstChildElement("ImagePath")->GetText(); |
||||
Util::ensureUnifySlash(imageData.name); |
||||
imageData.name = MAKE_PATH_FULL(strPath, imageData.name); |
||||
mapImageID.emplace(imageData.ID, idxImage); |
||||
// set image resolution
|
||||
const cv::Size& resolution = resolutions[imageData.platformID]; |
||||
imageData.width = resolution.width; |
||||
imageData.height = resolution.height; |
||||
imageData.scale = 1; |
||||
// set camera pose
|
||||
const tinyxml2::XMLElement* photoPose = photo->FirstChildElement("Pose"); |
||||
if (photoPose == NULL) |
||||
continue; |
||||
if ((elem=photoPose->FirstChildElement("Rotation")) == NULL) |
||||
continue; |
||||
imageData.poseID = platform.poses.size(); |
||||
Platform::Pose& pose = platform.poses.AddEmpty(); |
||||
pose.R = Matrix3x3( |
||||
elem->FirstChildElement("M_00")->DoubleText(), |
||||
elem->FirstChildElement("M_01")->DoubleText(), |
||||
elem->FirstChildElement("M_02")->DoubleText(), |
||||
elem->FirstChildElement("M_10")->DoubleText(), |
||||
elem->FirstChildElement("M_11")->DoubleText(), |
||||
elem->FirstChildElement("M_12")->DoubleText(), |
||||
elem->FirstChildElement("M_20")->DoubleText(), |
||||
elem->FirstChildElement("M_21")->DoubleText(), |
||||
elem->FirstChildElement("M_22")->DoubleText()); |
||||
if ((elem=photoPose->FirstChildElement("Center")) == NULL) |
||||
continue; |
||||
pose.C = Point3( |
||||
elem->FirstChildElement("x")->DoubleText(), |
||||
elem->FirstChildElement("y")->DoubleText(), |
||||
elem->FirstChildElement("z")->DoubleText()); |
||||
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID); |
||||
// set depth stats
|
||||
if ((elem=photo->FirstChildElement("MedianDepth")) != NULL) |
||||
imageData.avgDepth = (float)elem->DoubleText(); |
||||
else if (photo->FirstChildElement("NearDepth") != NULL && photo->FirstChildElement("FarDepth") != NULL) |
||||
imageData.avgDepth = (float)((photo->FirstChildElement("NearDepth")->DoubleText() + photo->FirstChildElement("FarDepth")->DoubleText())/2); |
||||
else |
||||
imageData.avgDepth = 0; |
||||
++nPoses; |
||||
} |
||||
} |
||||
if (scene.images.size() < 2) |
||||
return false; |
||||
scene.nCalibratedImages = (unsigned)nPoses; |
||||
// transform poses to a local coordinate system
|
||||
const bool bLocalCoords(document->FirstChildElement("SRSId") == NULL || |
||||
((elem=blocksExchange->FirstChildElement("SpatialReferenceSystems")) != NULL && (elem=elem->FirstChildElement("SRS")) != NULL && (elem=elem->FirstChildElement("Name")) != NULL && _tcsncmp(elem->GetText(), "Local Coordinates", 17) == 0)); |
||||
Point3 center = Point3::ZERO; |
||||
if (!bLocalCoords) { |
||||
for (const Image& imageData : scene.images) |
||||
center += imageData.camera.C; |
||||
center /= scene.images.size(); |
||||
for (Platform& platform : scene.platforms) |
||||
for (Platform::Pose& pose : platform.poses) |
||||
pose.C -= center; |
||||
} |
||||
// try to read also the sparse point-cloud
|
||||
const tinyxml2::XMLElement* tiepoints = document->FirstChildElement("TiePoints"); |
||||
if (tiepoints == NULL) |
||||
return true; |
||||
for (const tinyxml2::XMLElement* tiepoint=tiepoints->FirstChildElement(); tiepoint!=NULL; tiepoint=tiepoint->NextSiblingElement()) { |
||||
if ((elem=tiepoint->FirstChildElement("Position")) == NULL) |
||||
continue; |
||||
scene.pointcloud.points.emplace_back( |
||||
(float)elem->FirstChildElement("x")->DoubleText(), |
||||
(float)elem->FirstChildElement("y")->DoubleText(), |
||||
(float)elem->FirstChildElement("z")->DoubleText()); |
||||
if (!bLocalCoords) |
||||
scene.pointcloud.points.back() -= Cast<float>(center); |
||||
if ((elem=tiepoint->FirstChildElement("Color")) != NULL) |
||||
scene.pointcloud.colors.emplace_back( |
||||
(uint8_t)CLAMP(elem->FirstChildElement("Red")->DoubleText()*255, 0.0, 255.0), |
||||
(uint8_t)CLAMP(elem->FirstChildElement("Green")->DoubleText()*255, 0.0, 255.0), |
||||
(uint8_t)CLAMP(elem->FirstChildElement("Blue")->DoubleText()*255, 0.0, 255.0)); |
||||
PointCloud::ViewArr views; |
||||
for (const tinyxml2::XMLElement* view=tiepoint->FirstChildElement("Measurement"); view!=NULL; view=view->NextSiblingElement()) |
||||
views.emplace_back(mapImageID.at(view->FirstChildElement("PhotoId")->UnsignedText())); |
||||
scene.pointcloud.pointViews.emplace_back(std::move(views)); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
// parse scene stored either in Metashape images list format or ContextCapture BlocksExchange format
|
||||
bool ParseSceneXML(Scene& scene, PlatformDistCoeffs& pltDistCoeffs, size_t& nCameras, size_t& nPoses) |
||||
{ |
||||
// parse XML file
|
||||
const String strInputFileName(MAKE_PATH_SAFE(OPT::strInputFileName)); |
||||
tinyxml2::XMLDocument doc; { |
||||
ISTREAMPTR pStream(new File(strInputFileName, File::READ, File::OPEN)); |
||||
if (!((File*)(ISTREAM*)pStream)->isOpen()) { |
||||
VERBOSE("error: failed opening the input scene file"); |
||||
return false; |
||||
} |
||||
const size_t nLen(pStream->getSize()); |
||||
String str; str.resize(nLen); |
||||
pStream->read(&str[0], nLen); |
||||
doc.Parse(str.c_str(), nLen); |
||||
} |
||||
if (doc.ErrorID() != tinyxml2::XML_SUCCESS) { |
||||
VERBOSE("error: invalid XML file"); |
||||
return false; |
||||
} |
||||
// parse scene
|
||||
if (doc.FirstChildElement("BlocksExchange") == NULL) |
||||
return ParseImageListXML(doc, scene, pltDistCoeffs, nCameras, nPoses); |
||||
return ParseBlocksExchangeXML(doc, scene, pltDistCoeffs, nCameras, nPoses); |
||||
} |
||||
|
||||
// undistort image using Brown's model
|
||||
bool UndistortBrown(Image& imageData, uint32_t ID, const DistCoeff& dc, const String& pathData) |
||||
{ |
||||
// do we need to undistort?
|
||||
if (!dc.HasDistortion()) |
||||
return true; |
||||
|
||||
// load image pixels
|
||||
if (!imageData.ReloadImage()) |
||||
return false; |
||||
|
||||
// initialize intrinsics
|
||||
const cv::Vec<double,8>& distCoeffs = *reinterpret_cast<const cv::Vec<REAL,8>*>(dc.coeff); |
||||
const KMatrix prevK(imageData.camera.GetK<REAL>(imageData.width, imageData.height)); |
||||
#if 1 |
||||
const KMatrix& K(prevK); |
||||
#else |
||||
const KMatrix K(cv::getOptimalNewCameraMatrix(prevK, distCoeffs, imageData.size(), 0.0, cv::Size(), NULL, true)); |
||||
ASSERT(K(0,2) == Camera::ComposeK(prevK(0,0), prevK(1,1), imageData.width(), imageData.height())(0,2)); |
||||
ASSERT(K(1,2) == Camera::ComposeK(prevK(0,0), prevK(1,1), imageData.width(), imageData.height())(1,2)); |
||||
if (K.IsEqual(prevK)) { |
||||
int i(0); |
||||
while (distCoeffs(i++) == 0.0) { |
||||
if (i == 8) |
||||
return true; // nothing to do
|
||||
} |
||||
} |
||||
#endif |
||||
|
||||
// undistort image
|
||||
Image8U3 imgUndist; |
||||
cv::undistort(imageData.image, imgUndist, prevK, distCoeffs, K); |
||||
imageData.ReleaseImage(); |
||||
|
||||
// save undistorted image
|
||||
imageData.image = imgUndist; |
||||
imageData.name = pathData + String::FormatString(_T("%05u.jpg"), ID); |
||||
Util::ensureFolder(imageData.name); |
||||
return imageData.image.Save(imageData.name); |
||||
} |
||||
|
||||
// project all points in this image and keep those looking at the camera and are most in front
|
||||
void AssignPoints(const Image& imageData, uint32_t ID, PointCloud& pointcloud) |
||||
{ |
||||
ASSERT(pointcloud.IsValid()); |
||||
const int CHalfSize(1); |
||||
const int FHalfSize(5); |
||||
const Depth thCloseDepth(0.1f); |
||||
|
||||
// sort points by depth
|
||||
IndexScoreArr points(0, pointcloud.points.size()); |
||||
FOREACH(p, pointcloud.points) { |
||||
const PointCloud::Point& X(pointcloud.points[p]); |
||||
const float d((float)imageData.camera.PointDepth(X)); |
||||
if (d <= 0) |
||||
continue; |
||||
points.emplace_back((uint32_t)p, d); |
||||
} |
||||
points.Sort(); |
||||
|
||||
// project all points to this view
|
||||
DepthMap depthMap(imageData.GetSize()); |
||||
TImage<cuint32_t> pointMap(imageData.GetSize()); |
||||
depthMap.fill(FLT_MAX); |
||||
pointMap.memset((uint8_t)NO_ID); |
||||
RFOREACHPTR(pPD, points) { |
||||
const Point3 X(pointcloud.points[pPD->idx]); |
||||
const Point3f Xc(imageData.camera.TransformPointW2C(X)); |
||||
// (also the view to point vector cause the face is in camera view space)
|
||||
// point skip already in the previous step if the (cos) angle between
|
||||
// the view to point vector and the view direction is negative
|
||||
ASSERT(Xc.z > 0); |
||||
// skip point if the (cos) angle between
|
||||
// its normal and the point to view vector is negative
|
||||
if (!pointcloud.normals.empty() && Xc.dot(pointcloud.normals[pPD->idx]) > 0) |
||||
continue; |
||||
const Point2f x(imageData.camera.TransformPointC2I(Xc)); |
||||
const ImageRef ir(ROUND2INT(x)); |
||||
if (!depthMap.isInside(ir)) |
||||
continue; |
||||
// skip point if the there is a very near by point closer
|
||||
for (int i=-CHalfSize; i<=CHalfSize; ++i) { |
||||
const int rw(ir.y+i); |
||||
for (int j=-CHalfSize; j<=CHalfSize; ++j) { |
||||
const int cw(ir.x+j); |
||||
if (!depthMap.isInside(ImageRef(cw,rw))) |
||||
continue; |
||||
if (depthMap(rw,cw) < Xc.z) |
||||
goto NEXT_POINT; |
||||
} |
||||
} |
||||
// skip the point if there is a near by point much closer
|
||||
for (int i=-FHalfSize; i<=FHalfSize; ++i) { |
||||
const int rw(ir.y+i); |
||||
for (int j=-FHalfSize; j<=FHalfSize; ++j) { |
||||
const int cw(ir.x+j); |
||||
if (!depthMap.isInside(ImageRef(cw,rw))) |
||||
continue; |
||||
const Depth depth(depthMap(rw,cw)); |
||||
if (depth < Xc.z && !IsDepthSimilar(depth, Xc.z, thCloseDepth)) |
||||
goto NEXT_POINT; |
||||
} |
||||
} |
||||
// store this point
|
||||
depthMap(ir) = Xc.z; |
||||
pointMap(ir) = pPD->idx; |
||||
NEXT_POINT:; |
||||
} |
||||
|
||||
// add all points viewed by this camera
|
||||
const int HalfSize(1); |
||||
const int RowsEnd(pointMap.rows-HalfSize); |
||||
const int ColsEnd(pointMap.cols-HalfSize); |
||||
unsigned nNumPoints(0); |
||||
#ifdef _USE_OPENMP |
||||
#pragma omp critical |
||||
#endif |
||||
for (int r=HalfSize; r<RowsEnd; ++r) { |
||||
for (int c=HalfSize; c<ColsEnd; ++c) { |
||||
const uint32_t idx(pointMap(r,c)); |
||||
if (idx == NO_ID) |
||||
continue; |
||||
#ifdef _USE_OPENMP |
||||
pointcloud.pointViews[idx].InsertSort(ID); |
||||
#else |
||||
pointcloud.pointViews[idx].Insert(ID); |
||||
ASSERT(pointcloud.pointViews[idx].IsSorted()); |
||||
#endif |
||||
++nNumPoints; |
||||
} |
||||
} |
||||
|
||||
DEBUG_ULTIMATE("\tview %3u sees %u points", ID, nNumPoints); |
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
TD_TIMER_START(); |
||||
|
||||
Scene scene(OPT::nMaxThreads); |
||||
|
||||
// convert data from Metashape format to OpenMVS
|
||||
PlatformDistCoeffs pltDistCoeffs; |
||||
size_t nCameras(0), nPoses(0); |
||||
if (!ParseSceneXML(scene, pltDistCoeffs, nCameras, nPoses)) |
||||
return EXIT_FAILURE; |
||||
|
||||
// read the 3D point-cloud if available
|
||||
if (!OPT::strPointsFileName.empty() && !scene.pointcloud.Load(MAKE_PATH_SAFE(OPT::strPointsFileName))) |
||||
return EXIT_FAILURE; |
||||
const bool bAssignPoints(!scene.pointcloud.IsEmpty() && !scene.pointcloud.IsValid()); |
||||
if (bAssignPoints) |
||||
scene.pointcloud.pointViews.resize(scene.pointcloud.GetSize()); |
||||
|
||||
// undistort images
|
||||
const String pathData(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputImageFolder)); |
||||
Util::Progress progress(_T("Processed images"), scene.images.size()); |
||||
GET_LOGCONSOLE().Pause(); |
||||
#ifdef _USE_OPENMP |
||||
bool bAbort(false); |
||||
#pragma omp parallel for shared(bAbort) schedule(dynamic) |
||||
for (int ID=0; ID<(int)scene.images.size(); ++ID) { |
||||
#pragma omp flush (bAbort) |
||||
if (bAbort) |
||||
continue; |
||||
#else |
||||
FOREACH(ID, scene.images) { |
||||
#endif |
||||
++progress; |
||||
Image& imageData = scene.images[ID]; |
||||
if (!imageData.IsValid()) |
||||
continue; |
||||
if (!UndistortBrown(imageData, ID, pltDistCoeffs[imageData.platformID][imageData.cameraID], pathData)) { |
||||
#ifdef _USE_OPENMP |
||||
bAbort = true; |
||||
#pragma omp flush (bAbort) |
||||
continue; |
||||
#else |
||||
return EXIT_FAILURE; |
||||
#endif |
||||
} |
||||
imageData.UpdateCamera(scene.platforms); |
||||
if (bAssignPoints) |
||||
AssignPoints(imageData, ID, scene.pointcloud); |
||||
} |
||||
GET_LOGCONSOLE().Play(); |
||||
#ifdef _USE_OPENMP |
||||
if (bAbort) |
||||
return EXIT_FAILURE; |
||||
#endif |
||||
progress.close(); |
||||
|
||||
if (scene.pointcloud.IsValid()) { |
||||
// filter invalid points
|
||||
RFOREACH(i, scene.pointcloud.points) |
||||
if (scene.pointcloud.pointViews[i].size() < 2) |
||||
scene.pointcloud.RemovePoint(i); |
||||
// compute average scene depth per image
|
||||
if (!std::any_of(scene.images.begin(), scene.images.end(), [](const Image& imageData) { return imageData.avgDepth > 0; })) { |
||||
std::vector<float> avgDepths(scene.images.size(), 0.f); |
||||
std::vector<uint32_t> numDepths(scene.images.size(), 0u); |
||||
FOREACH(idxPoint, scene.pointcloud.points) { |
||||
const Point3 X(scene.pointcloud.points[idxPoint]); |
||||
for (const PointCloud::View& idxImage: scene.pointcloud.pointViews[idxPoint]) { |
||||
const Image& imageData = scene.images[idxImage]; |
||||
const float depth((float)imageData.camera.PointDepth(X)); |
||||
if (depth > 0) { |
||||
avgDepths[idxImage] += depth; |
||||
++numDepths[idxImage]; |
||||
} |
||||
} |
||||
} |
||||
FOREACH(idxImage, scene.images) { |
||||
Image& imageData = scene.images[idxImage]; |
||||
if (numDepths[idxImage] > 0) |
||||
imageData.avgDepth = avgDepths[idxImage] / numDepths[idxImage]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// print average scene depth per image stats
|
||||
MeanStdMinMax<float,double> acc; |
||||
for (const Image& imageData: scene.images) |
||||
if (imageData.avgDepth > 0) |
||||
acc.Update(imageData.avgDepth); |
||||
|
||||
// write OpenMVS input data
|
||||
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
|
||||
VERBOSE("Exported data: %u platforms, %u cameras, %u poses, %u images, %u vertices, %g min / %g mean (%g std) / %g max average scene depth per image (%s)", |
||||
scene.platforms.size(), nCameras, nPoses, scene.images.size(), scene.pointcloud.GetSize(), |
||||
acc.minVal, acc.GetMean(), acc.GetStdDev(), acc.maxVal, |
||||
TD_TIMER_GET_FMT().c_str()); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
FIND_PACKAGE(OpenMVG QUIET) |
||||
IF (OPENMVG_FOUND) |
||||
INCLUDE_DIRECTORIES(${OPENMVG_INCLUDE_DIRS}) |
||||
add_definitions(-D_USE_OPENMVG) |
||||
set(LIBS_DEPEND "MVS;${OPENMVG_LIBRARIES}") |
||||
ELSE() |
||||
set(LIBS_DEPEND "MVS") |
||||
MESSAGE("OPENMVG_NOT FOUND : OpenMVG importer with JSON support will not be build") |
||||
ENDIF() |
||||
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(InterfaceOpenMVG "Apps" "${cxx_default}" "${LIBS_DEPEND};${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS InterfaceOpenMVG |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,755 @@
@@ -0,0 +1,755 @@
|
||||
/*
|
||||
* InterfaceOpenMVG.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* Pierre MOULON <p.moulon@foxel.ch> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
#include <boost/program_options.hpp> |
||||
#ifdef _USE_OPENMVG |
||||
#undef D2R |
||||
#undef R2D |
||||
#include <openMVG/sfm/sfm_data.hpp> |
||||
#include <openMVG/sfm/sfm_data_io.hpp> |
||||
#include <openMVG/image/image.hpp> |
||||
#endif |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("InterfaceOpenMVG") |
||||
#define MVS_EXT _T(".mvs") |
||||
#define MVG_EXT _T(".baf") |
||||
#define MVG2_EXT _T(".json") |
||||
#define MVG3_EXT _T(".bin") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace openMVS { |
||||
namespace MVS_IO { |
||||
|
||||
typedef REAL RealT; |
||||
typedef Eigen::Matrix<RealT,3,3,Eigen::RowMajor> Mat33; |
||||
typedef Eigen::Matrix<RealT,3,1> Vec3; |
||||
|
||||
// Structure to model the pinhole camera projection model
|
||||
struct Camera |
||||
{ |
||||
Mat33 K; // camera's normalized intrinsics matrix
|
||||
}; |
||||
typedef std::vector<Camera> vec_Camera; |
||||
|
||||
// structure describing a pose along the trajectory of a platform
|
||||
struct Pose { |
||||
Mat33 R; // pose's rotation matrix
|
||||
Vec3 C; // pose's translation vector
|
||||
}; |
||||
typedef std::vector<Pose> vec_Pose; |
||||
|
||||
// structure describing an image
|
||||
struct Image { |
||||
uint32_t id_camera; // ID of the associated camera on the associated platform
|
||||
uint32_t id_pose; // ID of the pose of the associated platform
|
||||
std::string name; // image file name
|
||||
}; |
||||
typedef std::vector<Image> vec_Image; |
||||
|
||||
// structure describing a 3D point
|
||||
struct Vertex { |
||||
|
||||
typedef std::vector<uint32_t> vec_View; |
||||
|
||||
Vec3 X; // 3D point position
|
||||
vec_View views; // view visibility for this 3D feature
|
||||
}; |
||||
typedef std::vector<Vertex> vec_Vertex; |
||||
|
||||
struct SfM_Scene |
||||
{ |
||||
vec_Pose poses; // array of poses
|
||||
vec_Camera cameras; // array of cameras
|
||||
vec_Image images; // array of images
|
||||
vec_Vertex vertices; // array of reconstructed 3D points
|
||||
}; |
||||
|
||||
|
||||
bool ImportScene(const std::string& sList_filename, const std::string& sBaf_filename, SfM_Scene& sceneBAF) |
||||
{ |
||||
LOG_OUT() << "Reading:\n" |
||||
<< sList_filename << "\n" |
||||
<< sBaf_filename << std::endl; |
||||
|
||||
// Read view list file (view filename, id_intrinsic, id_pose)
|
||||
// Must be read first, since it allow to establish the link between the ViewId and the camera/poses ids.
|
||||
std::map< std::pair<uint32_t, uint32_t>, uint32_t > map_cam_pose_toViewId; |
||||
{ |
||||
std::ifstream file(sList_filename.c_str()); |
||||
if (!file.good()) { |
||||
VERBOSE("error: unable to open file '%s'", sList_filename.c_str()); |
||||
return false; |
||||
} |
||||
Image image; |
||||
uint32_t count = 0; |
||||
while (file >> image.name >> image.id_camera >> image.id_pose) { |
||||
sceneBAF.images.push_back(image); |
||||
map_cam_pose_toViewId[std::make_pair(image.id_camera, image.id_pose)] = count++; |
||||
LOG_OUT() << image.name << ' ' << image.id_camera << ' ' << image.id_pose << std::endl; |
||||
} |
||||
} |
||||
|
||||
// Read BAF file
|
||||
{ |
||||
std::ifstream file(sBaf_filename.c_str()); |
||||
if (!file.good()) { |
||||
VERBOSE("error: unable to open file '%s'", sBaf_filename.c_str()); |
||||
return false; |
||||
} |
||||
|
||||
uint32_t num_intrinsics, num_poses, num_points; |
||||
|
||||
// Read header
|
||||
file >> num_intrinsics; |
||||
file >> num_poses; |
||||
file >> num_points; |
||||
|
||||
LOG_OUT() << "Reading BAF file with:\n" |
||||
<< " num_intrinsics: " << num_intrinsics << "\n" |
||||
<< " num_poses: " << num_poses << "\n" |
||||
<< " num_points: " << num_points << "\n"; |
||||
|
||||
// Read the intrinsics (only support reading Pinhole Radial 3).
|
||||
{ |
||||
for (uint32_t i = 0; i < num_intrinsics; ++i) { |
||||
double focal, ppx, ppy, k1, k2, k3; |
||||
file >> focal >> ppx >> ppy >> k1 >> k2 >> k3; |
||||
Camera cam; |
||||
cam.K << |
||||
focal, 0, ppx, |
||||
0, focal, ppy, |
||||
0, 0, 1; |
||||
LOG_OUT() << "\n" << cam.K << std::endl; |
||||
sceneBAF.cameras.push_back(cam); |
||||
} |
||||
} |
||||
|
||||
// Read poses
|
||||
{ |
||||
for (uint32_t i = 0; i < num_poses; ++i) { |
||||
Pose pose; |
||||
for (int r = 0; r < 3; ++r) { |
||||
for (int c = 0; c < 3; ++c) { |
||||
file >> pose.R(r,c); |
||||
} |
||||
} |
||||
file >> pose.C[0] >> pose.C[1] >> pose.C[2]; |
||||
#ifndef _RELEASE |
||||
LOG_OUT() << "\n" << pose.R << "\n\n" << pose.C.transpose() << std::endl; |
||||
#endif |
||||
sceneBAF.poses.push_back(pose); |
||||
} |
||||
} |
||||
|
||||
// Read structure and visibility
|
||||
{ |
||||
#ifdef _RELEASE |
||||
Util::Progress progress(_T("Processed points"), num_points); |
||||
#endif |
||||
for (uint32_t i = 0; i < num_points; ++i) { |
||||
Vertex vertex; |
||||
file >> vertex.X[0] >> vertex.X[1] >> vertex.X[2]; |
||||
uint32_t num_observations_for_point = 0; |
||||
file >> num_observations_for_point; |
||||
for (uint32_t j = 0; j < num_observations_for_point; ++j) { |
||||
uint32_t id_intrinsics, id_pose; |
||||
double x, y; |
||||
file >> id_intrinsics >> id_pose >> x >> y; |
||||
#ifndef _RELEASE |
||||
LOG_OUT() << "observation:" |
||||
<< " " << id_intrinsics |
||||
<< " " << id_pose |
||||
<< " " << x << " " << y << std::endl; |
||||
#endif |
||||
const auto itIntrPose(map_cam_pose_toViewId.find(std::make_pair(id_intrinsics, id_pose))); |
||||
if (itIntrPose == map_cam_pose_toViewId.end()) { |
||||
LOG_OUT() << "error: intrinsics-pose pair not existing" << std::endl; |
||||
continue; |
||||
} |
||||
const uint32_t id_view(itIntrPose->second); |
||||
vertex.views.push_back(id_view); |
||||
} |
||||
sceneBAF.vertices.push_back(vertex); |
||||
#ifdef _RELEASE |
||||
progress.display(i); |
||||
#endif |
||||
} |
||||
#ifdef _RELEASE |
||||
progress.close(); |
||||
#endif |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool ExportScene(const std::string& sList_filename, const std::string& sBaf_filename, const SfM_Scene& sceneBAF) |
||||
{ |
||||
LOG_OUT() << "Writing:\n" |
||||
<< sList_filename << "\n" |
||||
<< sBaf_filename << std::endl; |
||||
|
||||
// Write view list file (view filename, id_intrinsic, id_pose)
|
||||
{ |
||||
std::ofstream file(sList_filename.c_str()); |
||||
if (!file.good()) { |
||||
VERBOSE("error: unable to open file '%s'", sList_filename.c_str()); |
||||
return false; |
||||
} |
||||
for (uint32_t i=0; i<sceneBAF.images.size(); ++i) { |
||||
const Image& image = sceneBAF.images[i]; |
||||
file << image.name << ' ' << image.id_camera << ' ' << image.id_pose << std::endl; |
||||
LOG_OUT() << image.name << ' ' << image.id_camera << ' ' << image.id_pose << std::endl; |
||||
} |
||||
} |
||||
|
||||
// Write BAF file
|
||||
{ |
||||
std::ofstream file(sBaf_filename.c_str()); |
||||
if (!file.good()) { |
||||
VERBOSE("error: unable to open file '%s'", sBaf_filename.c_str()); |
||||
return false; |
||||
} |
||||
|
||||
const uint32_t num_intrinsics = (uint32_t)sceneBAF.cameras.size(); |
||||
const uint32_t num_poses = (uint32_t)sceneBAF.poses.size(); |
||||
const uint32_t num_points = (uint32_t)sceneBAF.vertices.size(); |
||||
|
||||
LOG_OUT() << "Writing BAF file with:\n" |
||||
<< " num_intrinsics: " << num_intrinsics << "\n" |
||||
<< " num_poses: " << num_poses << "\n" |
||||
<< " num_points: " << num_points << "\n"; |
||||
|
||||
// Write header
|
||||
file << num_intrinsics << std::endl; |
||||
file << num_poses << std::endl; |
||||
file << num_points << std::endl; |
||||
|
||||
// Write the intrinsics (only support writing Pinhole Radial 3).
|
||||
{ |
||||
for (uint32_t i = 0; i < num_intrinsics; ++i) { |
||||
const Camera& cam = sceneBAF.cameras[i]; |
||||
file << cam.K(0,0) << ' ' << cam.K(0,2) << ' ' << cam.K(1,2) << ' ' << 0 << ' ' << 0 << ' ' << 0 << std::endl; |
||||
LOG_OUT() << "\n" << cam.K << std::endl; |
||||
} |
||||
} |
||||
|
||||
// Write poses
|
||||
{ |
||||
for (uint32_t i = 0; i < num_poses; ++i) { |
||||
const Pose& pose = sceneBAF.poses[i]; |
||||
for (int r = 0; r < 3; ++r) { |
||||
for (int c = 0; c < 3; ++c) { |
||||
file << pose.R(r,c) << ' '; |
||||
} |
||||
} |
||||
file << pose.C[0] << ' ' << pose.C[1] << ' ' << pose.C[2] << std::endl; |
||||
#ifndef _RELEASE |
||||
LOG_OUT() << "\n" << pose.R << "\n\n" << pose.C.transpose() << std::endl; |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
// Write structure and visibility
|
||||
{ |
||||
#ifdef _RELEASE |
||||
Util::Progress progress(_T("Processed points"), num_points); |
||||
#endif |
||||
for (uint32_t i = 0; i < num_points; ++i) { |
||||
const Vertex& vertex = sceneBAF.vertices[i]; |
||||
file << vertex.X[0] << ' ' << vertex.X[1] << ' ' << vertex.X[2] << std::endl; |
||||
const uint32_t num_observations_for_point = (uint32_t)vertex.views.size(); |
||||
file << num_observations_for_point << std::endl; |
||||
for (uint32_t j = 0; j < num_observations_for_point; ++j) { |
||||
const uint32_t id_view = vertex.views[j]; |
||||
const Image& image = sceneBAF.images[id_view]; |
||||
file << image.id_camera << ' ' << image.id_pose << ' ' << 0 << ' ' << 0 << std::endl; |
||||
#ifndef _RELEASE |
||||
LOG_OUT() << "observation:" |
||||
<< " " << image.id_camera |
||||
<< " " << image.id_pose << std::endl; |
||||
#endif |
||||
} |
||||
#ifdef _RELEASE |
||||
progress.display(i); |
||||
#endif |
||||
} |
||||
#ifdef _RELEASE |
||||
progress.close(); |
||||
#endif |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
} // MVS_IO
|
||||
} // openMVS
|
||||
|
||||
|
||||
namespace OPT { |
||||
#ifdef _USE_OPENMVG |
||||
bool bOpenMVGjson; // new import format
|
||||
#endif |
||||
bool bOpenMVS2OpenMVG; // conversion direction
|
||||
bool bNormalizeIntrinsics; |
||||
String strListFileName; |
||||
String strInputFileName; |
||||
String strOutputFileName; |
||||
String strOutputImageFolder; |
||||
unsigned nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
String strConfigFileName; |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "produce this help message") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
boost::program_options::options_description config("Main options"); |
||||
config.add_options() |
||||
("images-list-file,l", boost::program_options::value<std::string>(&OPT::strListFileName), "input filename containing image list") |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh") |
||||
("output-image-folder", boost::program_options::value<std::string>(&OPT::strOutputImageFolder)->default_value("undistorted_images"), "output folder to store undistorted images") |
||||
("normalize,f", boost::program_options::value(&OPT::bNormalizeIntrinsics)->default_value(true), "normalize intrinsics while exporting to OpenMVS format") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
// initialize the log file
|
||||
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str()); |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidPath(OPT::strListFileName); |
||||
Util::ensureUnifySlash(OPT::strListFileName); |
||||
Util::ensureValidPath(OPT::strInputFileName); |
||||
Util::ensureUnifySlash(OPT::strInputFileName); |
||||
Util::ensureUnifySlash(OPT::strOutputImageFolder); |
||||
Util::ensureFolderSlash(OPT::strOutputImageFolder); |
||||
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower()); |
||||
OPT::bOpenMVS2OpenMVG = (strInputFileNameExt == MVS_FILE_EXTENSION); |
||||
#ifdef _USE_OPENMVG |
||||
OPT::bOpenMVGjson = (strInputFileNameExt == MVG2_EXT || strInputFileNameExt == MVG3_EXT); |
||||
const bool bInvalidCommand(OPT::strInputFileName.IsEmpty() || (OPT::strListFileName.IsEmpty() && !OPT::bOpenMVGjson && !OPT::bOpenMVS2OpenMVG)); |
||||
#else |
||||
const bool bInvalidCommand(OPT::strInputFileName.IsEmpty() || (OPT::strListFileName.IsEmpty() && !OPT::bOpenMVS2OpenMVG)); |
||||
#endif |
||||
if (OPT::vm.count("help") || bInvalidCommand) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config); |
||||
GET_LOG() << visible; |
||||
#ifndef _USE_OPENMVG |
||||
GET_LOG() << "\nWARNING: Only " MVG_EXT " files supported! In order to import " MVG2_EXT " or " MVG3_EXT " files use the converter included in OpenMVG, or link OpenMVS to the latest version of OpenMVG during compile time.\n"; |
||||
#endif |
||||
} |
||||
if (bInvalidCommand) |
||||
return false; |
||||
|
||||
// initialize optional options
|
||||
Util::ensureValidPath(OPT::strOutputFileName); |
||||
Util::ensureUnifySlash(OPT::strOutputFileName); |
||||
if (OPT::bOpenMVS2OpenMVG) { |
||||
if (OPT::strOutputFileName.IsEmpty()) |
||||
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName); |
||||
} else { |
||||
if (OPT::strOutputFileName.IsEmpty()) |
||||
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + MVS_FILE_EXTENSION; |
||||
} |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
TD_TIMER_START(); |
||||
|
||||
if (OPT::bOpenMVS2OpenMVG) { |
||||
// read OpenMVS input data
|
||||
MVS::Scene scene(OPT::nMaxThreads); |
||||
if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName))) |
||||
return EXIT_FAILURE; |
||||
|
||||
// convert data from OpenMVS to OpenMVG
|
||||
openMVS::MVS_IO::SfM_Scene sceneBAF; |
||||
FOREACH(p, scene.platforms) { |
||||
const MVS::Platform& platform = scene.platforms[p]; |
||||
if (platform.cameras.GetSize() != 1) { |
||||
LOG("error: unsupported scene structure"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
const MVS::Platform::Camera& camera = platform.cameras[0]; |
||||
openMVS::MVS_IO::Camera cameraBAF; |
||||
cameraBAF.K = camera.K; |
||||
sceneBAF.cameras.push_back(cameraBAF); |
||||
} |
||||
FOREACH(i, scene.images) { |
||||
const MVS::Image& image = scene.images[i]; |
||||
const MVS::Platform& platform = scene.platforms[image.platformID]; |
||||
const MVS::Platform::Pose& pose = platform.poses[image.poseID]; |
||||
openMVS::MVS_IO::Image imageBAF; |
||||
imageBAF.name = image.name; |
||||
imageBAF.name = MAKE_PATH_REL(WORKING_FOLDER_FULL, imageBAF.name); |
||||
imageBAF.id_camera = image.platformID; |
||||
imageBAF.id_pose = (uint32_t)sceneBAF.poses.size(); |
||||
sceneBAF.images.push_back(imageBAF); |
||||
openMVS::MVS_IO::Pose poseBAF; |
||||
poseBAF.R = pose.R; |
||||
poseBAF.C = pose.C; |
||||
sceneBAF.poses.push_back(poseBAF); |
||||
} |
||||
sceneBAF.vertices.reserve(scene.pointcloud.points.GetSize()); |
||||
FOREACH(p, scene.pointcloud.points) { |
||||
const MVS::PointCloud::Point& point = scene.pointcloud.points[p]; |
||||
openMVS::MVS_IO::Vertex vertexBAF; |
||||
vertexBAF.X = ((const MVS::PointCloud::Point::EVec)point).cast<REAL>(); |
||||
const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[p]; |
||||
FOREACH(v, views) { |
||||
unsigned viewBAF = views[(uint32_t)v]; |
||||
vertexBAF.views.push_back(viewBAF); |
||||
} |
||||
sceneBAF.vertices.push_back(vertexBAF); |
||||
} |
||||
|
||||
// write OpenMVG input data
|
||||
const String strOutputFileNameMVG(OPT::strOutputFileName + MVG_EXT); |
||||
openMVS::MVS_IO::ExportScene(MAKE_PATH_SAFE(OPT::strListFileName), MAKE_PATH_SAFE(strOutputFileNameMVG), sceneBAF); |
||||
|
||||
VERBOSE("Input data exported: %u cameras & %u poses & %u images & %u vertices (%s)", sceneBAF.cameras.size(), sceneBAF.poses.size(), sceneBAF.images.size(), sceneBAF.vertices.size(), TD_TIMER_GET_FMT().c_str()); |
||||
} else { |
||||
// convert data from OpenMVG to OpenMVS
|
||||
MVS::Scene scene(OPT::nMaxThreads); |
||||
size_t nCameras(0), nPoses(0); |
||||
|
||||
#ifdef _USE_OPENMVG |
||||
if (OPT::bOpenMVGjson) { |
||||
// read OpenMVG input data from a JSON file
|
||||
using namespace openMVG::sfm; |
||||
using namespace openMVG::cameras; |
||||
SfM_Data sfm_data; |
||||
const String strSfM_Data_Filename(MAKE_PATH_SAFE(OPT::strInputFileName)); |
||||
if (!Load(sfm_data, strSfM_Data_Filename, ESfM_Data(ALL))) { |
||||
VERBOSE("error: the input SfM_Data file '%s' cannot be read", strSfM_Data_Filename.c_str()); |
||||
return EXIT_FAILURE; |
||||
} |
||||
VERBOSE("Imported data: %u cameras, %u poses, %u images, %u vertices", |
||||
sfm_data.GetIntrinsics().size(), |
||||
sfm_data.GetPoses().size(), |
||||
sfm_data.GetViews().size(), |
||||
sfm_data.GetLandmarks().size()); |
||||
|
||||
// OpenMVG can have not contiguous index, use a map to create the required OpenMVS contiguous ID index
|
||||
std::map<openMVG::IndexT, uint32_t> map_intrinsic, map_view; |
||||
|
||||
// define a platform with all the intrinsic group
|
||||
nCameras = sfm_data.GetIntrinsics().size(); |
||||
for (const auto& intrinsic: sfm_data.GetIntrinsics()) { |
||||
if (isPinhole(intrinsic.second.get()->getType())) { |
||||
const Pinhole_Intrinsic * cam = dynamic_cast<const Pinhole_Intrinsic*>(intrinsic.second.get()); |
||||
if (map_intrinsic.count(intrinsic.first) == 0) |
||||
map_intrinsic.insert(std::make_pair(intrinsic.first, scene.platforms.GetSize())); |
||||
MVS::Platform& platform = scene.platforms.AddEmpty(); |
||||
// add the camera
|
||||
MVS::Platform::Camera& camera = platform.cameras.AddEmpty(); |
||||
camera.K = cam->K(); |
||||
// sub-pose
|
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
} |
||||
} |
||||
|
||||
// define images & poses
|
||||
const uint32_t nViews((uint32_t)sfm_data.GetViews().size()); |
||||
scene.images.Reserve(nViews); |
||||
scene.nCalibratedImages = 0; |
||||
Util::Progress progress(_T("Processed images"), nViews); |
||||
GET_LOGCONSOLE().Pause(); |
||||
#ifdef _USE_OPENMP |
||||
const std::vector<Views::value_type> views(sfm_data.GetViews().cbegin(), sfm_data.GetViews().cend()); |
||||
#pragma omp parallel for schedule(dynamic) |
||||
for (int i=0; i<(int)nViews; ++i) { |
||||
const Views::value_type& view = views[i]; |
||||
#pragma omp critical |
||||
map_view[view.first] = scene.images.GetSize(); |
||||
#else |
||||
for (const auto& view : sfm_data.GetViews()) { |
||||
map_view[view.first] = scene.images.GetSize(); |
||||
#endif |
||||
MVS::Image& image = scene.images.AddEmpty(); |
||||
image.name = view.second->s_Img_path; |
||||
Util::ensureUnifySlash(image.name); |
||||
Util::strTrim(image.name, PATH_SEPARATOR_STR); |
||||
String pathRoot(sfm_data.s_root_path); Util::ensureFolderSlash(pathRoot); |
||||
const String srcImage(MAKE_PATH_FULL(WORKING_FOLDER_FULL, pathRoot+image.name)); |
||||
image.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputImageFolder+image.name); |
||||
Util::ensureDirectory(image.name); |
||||
image.ID = static_cast<MVS::IIndex>(view.first); |
||||
image.platformID = map_intrinsic.at(view.second->id_intrinsic); |
||||
MVS::Platform& platform = scene.platforms[image.platformID]; |
||||
image.cameraID = 0; |
||||
if (sfm_data.IsPoseAndIntrinsicDefined(view.second.get()) && |
||||
File::access(srcImage)) { |
||||
MVS::Platform::Pose* pPose; |
||||
#ifdef _USE_OPENMP |
||||
#pragma omp critical |
||||
#endif |
||||
{ |
||||
image.poseID = platform.poses.GetSize(); |
||||
pPose = &platform.poses.AddEmpty(); |
||||
++scene.nCalibratedImages; |
||||
} |
||||
const openMVG::geometry::Pose3 poseMVG(sfm_data.GetPoseOrDie(view.second.get())); |
||||
pPose->R = poseMVG.rotation(); |
||||
pPose->C = poseMVG.center(); |
||||
// export undistorted images
|
||||
const openMVG::cameras::IntrinsicBase * cam = sfm_data.GetIntrinsics().at(view.second->id_intrinsic).get(); |
||||
if (cam->have_disto()) { |
||||
// undistort and save the image
|
||||
openMVG::image::Image<openMVG::image::RGBColor> imageRGB, imageRGB_ud; |
||||
openMVG::image::ReadImage(srcImage, &imageRGB); |
||||
openMVG::cameras::UndistortImage(imageRGB, cam, imageRGB_ud, openMVG::image::BLACK); |
||||
openMVG::image::WriteImage(image.name, imageRGB_ud); |
||||
} else { |
||||
// no distortion, copy the image
|
||||
File::copyFile(srcImage, image.name); |
||||
} |
||||
++nPoses; |
||||
} else { |
||||
// image have not valid pose, so set an undefined pose
|
||||
image.poseID = NO_ID; |
||||
// just copy the image
|
||||
File::copyFile(srcImage, image.name); |
||||
DEBUG_EXTRA("warning: uncalibrated image '%s'", view.second->s_Img_path.c_str()); |
||||
} |
||||
++progress; |
||||
} |
||||
GET_LOGCONSOLE().Play(); |
||||
progress.close(); |
||||
|
||||
// define structure
|
||||
scene.pointcloud.points.Reserve(sfm_data.GetLandmarks().size()); |
||||
scene.pointcloud.pointViews.Reserve(sfm_data.GetLandmarks().size()); |
||||
for (const auto& vertex: sfm_data.GetLandmarks()) { |
||||
const Landmark & landmark = vertex.second; |
||||
MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews.AddEmpty(); |
||||
for (const auto& observation: landmark.obs) { |
||||
const auto it(map_view.find(observation.first)); |
||||
if (it != map_view.end()) |
||||
views.InsertSort(it->second); |
||||
} |
||||
if (views.GetSize() < 2) { |
||||
scene.pointcloud.pointViews.RemoveLast(); |
||||
continue; |
||||
} |
||||
MVS::PointCloud::Point& point = scene.pointcloud.points.AddEmpty(); |
||||
point = landmark.X.cast<float>(); |
||||
} |
||||
} else |
||||
#endif |
||||
{ |
||||
// read OpenMVG input data from BAF file
|
||||
openMVS::MVS_IO::SfM_Scene sceneBAF; |
||||
if (!openMVS::MVS_IO::ImportScene(MAKE_PATH_SAFE(OPT::strListFileName), MAKE_PATH_SAFE(OPT::strInputFileName), sceneBAF)) |
||||
return EXIT_FAILURE; |
||||
|
||||
// convert data from OpenMVG to OpenMVS
|
||||
nCameras = sceneBAF.cameras.size(); |
||||
scene.platforms.Reserve((uint32_t)nCameras); |
||||
for (const auto& cameraBAF: sceneBAF.cameras) { |
||||
MVS::Platform& platform = scene.platforms.AddEmpty(); |
||||
MVS::Platform::Camera& camera = platform.cameras.AddEmpty(); |
||||
camera.K = cameraBAF.K; |
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
} |
||||
nPoses = sceneBAF.images.size(); |
||||
scene.images.Reserve((uint32_t)nPoses); |
||||
for (const auto& imageBAF: sceneBAF.images) { |
||||
openMVS::MVS_IO::Pose& poseBAF = sceneBAF.poses[imageBAF.id_pose]; |
||||
MVS::Image& image = scene.images.AddEmpty(); |
||||
image.name = imageBAF.name; |
||||
Util::ensureUnifySlash(image.name); |
||||
image.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, image.name); |
||||
image.ID = imageBAF.id_camera; |
||||
image.platformID = imageBAF.id_camera; |
||||
MVS::Platform& platform = scene.platforms[image.platformID]; |
||||
image.cameraID = 0; |
||||
image.poseID = platform.poses.GetSize(); |
||||
MVS::Platform::Pose& pose = platform.poses.AddEmpty(); |
||||
pose.R = poseBAF.R; |
||||
pose.C = poseBAF.C; |
||||
} |
||||
scene.pointcloud.points.Reserve(sceneBAF.vertices.size()); |
||||
scene.pointcloud.pointViews.Reserve(sceneBAF.vertices.size()); |
||||
for (const auto& vertexBAF: sceneBAF.vertices) { |
||||
MVS::PointCloud::Point& point = scene.pointcloud.points.AddEmpty(); |
||||
point = vertexBAF.X.cast<float>(); |
||||
MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews.AddEmpty(); |
||||
for (const auto& viewBAF: vertexBAF.views) |
||||
views.InsertSort(viewBAF); |
||||
} |
||||
} |
||||
|
||||
// read images meta-data
|
||||
FOREACHPTR(pImage, scene.images) { |
||||
if (!pImage->ReloadImage(0, false)) |
||||
LOG("error: can not read image %s", pImage->name.c_str()); |
||||
} |
||||
if (OPT::bNormalizeIntrinsics) { |
||||
// normalize camera intrinsics
|
||||
FOREACH(p, scene.platforms) { |
||||
MVS::Platform& platform = scene.platforms[p]; |
||||
FOREACH(c, platform.cameras) { |
||||
MVS::Platform::Camera& camera = platform.cameras[c]; |
||||
// find one image using this camera
|
||||
MVS::Image* pImage(NULL); |
||||
FOREACHPTR(pImg, scene.images) { |
||||
if (pImg->platformID == p && pImg->cameraID == c && pImg->poseID != NO_ID) { |
||||
pImage = pImg; |
||||
break; |
||||
} |
||||
} |
||||
if (pImage == NULL) { |
||||
LOG("error: no image using camera %u of platform %u", c, p); |
||||
continue; |
||||
} |
||||
const REAL fScale(REAL(1)/MVS::Camera::GetNormalizationScale(pImage->width, pImage->height)); |
||||
camera.K(0,0) *= fScale; |
||||
camera.K(1,1) *= fScale; |
||||
camera.K(0,2) *= fScale; |
||||
camera.K(1,2) *= fScale; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// write OpenMVS input data
|
||||
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
|
||||
VERBOSE("Exported data: %u platforms, %u cameras, %u poses, %u images, %u vertices (%s)", |
||||
scene.platforms.GetSize(), nCameras, nPoses, scene.images.GetSize(), scene.pointcloud.GetSize(), |
||||
TD_TIMER_GET_FMT().c_str()); |
||||
} |
||||
|
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(InterfacePolycam "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS InterfacePolycam |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,362 @@
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* InterfacePolycam.cpp |
||||
* |
||||
* Copyright (c) 2014-2023 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
#define JSON_NOEXCEPTION |
||||
#include "../../libs/IO/json.hpp" |
||||
#include <boost/program_options.hpp> |
||||
|
||||
using namespace MVS; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("InterfacePolycam") |
||||
#define MVS_FILE_EXTENSION _T(".mvs") |
||||
#define JSON_EXT _T(".json") |
||||
#define DEPTH_EXT _T(".png") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace OPT { |
||||
String strInputFileName; |
||||
String strOutputFileName; |
||||
unsigned nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
String strConfigFileName; |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "imports SfM scene stored Polycam format") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
boost::program_options::options_description config("Main options"); |
||||
config.add_options() |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input folder containing Polycam camera poses, images and depth-maps") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the scene") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
// initialize the log file
|
||||
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str()); |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidFolderPath(OPT::strInputFileName); |
||||
const bool bInvalidCommand(OPT::strInputFileName.empty()); |
||||
if (OPT::vm.count("help") || bInvalidCommand) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config); |
||||
GET_LOG() << visible; |
||||
} |
||||
if (bInvalidCommand) |
||||
return false; |
||||
|
||||
// initialize optional options
|
||||
Util::ensureValidFolderPath(OPT::strOutputFileName); |
||||
if (OPT::strOutputFileName.empty()) |
||||
OPT::strOutputFileName = "scene" MVS_FILE_EXTENSION; |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
// parse image containing calibration, pose, and depth-map information
|
||||
bool ParseImage(Scene& scene, const String& imagePath, const String& cameraPath, const String& depthPath, |
||||
const std::unordered_map<String, IIndex>& mapImageName) |
||||
{ |
||||
nlohmann::json data = nlohmann::json::parse(std::ifstream(cameraPath)); |
||||
if (data.empty()) |
||||
return false; |
||||
const cv::Size resolution(data["width"].get<uint32_t>(), data["height"].get<uint32_t>()); |
||||
// set platform
|
||||
const IIndex platformID = scene.platforms.size(); |
||||
Platform& platform = scene.platforms.AddEmpty(); |
||||
Platform::Camera& camera = platform.cameras.AddEmpty(); |
||||
camera.K = KMatrix::IDENTITY; |
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
camera.K(0,0) = data["fx"].get<float>(); |
||||
camera.K(1,1) = data["fy"].get<float>(); |
||||
camera.K(0,2) = data["cx"].get<float>(); |
||||
camera.K(1,2) = data["cy"].get<float>(); |
||||
// set image
|
||||
const IIndex imageID = scene.images.size(); |
||||
Image& imageData = scene.images.AddEmpty(); |
||||
imageData.platformID = platformID; |
||||
imageData.cameraID = 0; // only one camera per platform supported by this format
|
||||
imageData.poseID = NO_ID; |
||||
imageData.ID = imageID; |
||||
imageData.name = imagePath; |
||||
ASSERT(Util::isFullPath(imageData.name)); |
||||
// set image resolution
|
||||
imageData.width = resolution.width; |
||||
imageData.height = resolution.height; |
||||
imageData.scale = 1; |
||||
// set camera pose
|
||||
imageData.poseID = platform.poses.size(); |
||||
Platform::Pose& pose = platform.poses.AddEmpty(); |
||||
const Eigen::Matrix3d R_session_arkitcam{ |
||||
{data["t_00"].get<double>(), data["t_01"].get<double>(), data["t_02"].get<double>()}, |
||||
{data["t_10"].get<double>(), data["t_11"].get<double>(), data["t_12"].get<double>()}, |
||||
{data["t_20"].get<double>(), data["t_21"].get<double>(), data["t_22"].get<double>()} |
||||
}; |
||||
const Eigen::Vector3d t_session_arkitcam{ |
||||
data["t_03"].get<double>(), |
||||
data["t_13"].get<double>(), |
||||
data["t_23"].get<double>() |
||||
}; |
||||
const Eigen::Affine3d T_session_arkitcam{ |
||||
Eigen::Affine3d(Eigen::Translation3d(t_session_arkitcam)) * Eigen::Affine3d(Eigen::AngleAxisd(R_session_arkitcam)) |
||||
}; |
||||
const Eigen::Affine3d T_cam_arkitcam{ |
||||
Eigen::AngleAxisd(M_PI, Eigen::Vector3d::UnitX()) |
||||
}; |
||||
const Eigen::Matrix4d P{ |
||||
(T_cam_arkitcam * T_session_arkitcam.inverse()).matrix() |
||||
}; |
||||
pose.R = P.topLeftCorner<3, 3>().eval(); |
||||
pose.R.EnforceOrthogonality(); |
||||
const Point3d t = P.topRightCorner<3, 1>().eval(); |
||||
pose.C = pose.R.t() * (-t); |
||||
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID); |
||||
// set image neighbors if available
|
||||
nlohmann::json::const_iterator itNeighbors = data.find("neighbors"); |
||||
if (itNeighbors != data.end()) { |
||||
const std::vector<uint64_t> neighborTimestamps = itNeighbors->get<std::vector<uint64_t>>(); |
||||
for (uint64_t timestamp: neighborTimestamps) { |
||||
const String neighborName = std::to_string(timestamp); |
||||
const IIndex neighborID = mapImageName.at(neighborName); |
||||
if (neighborID != imageData.ID) |
||||
imageData.neighbors.emplace_back(ViewScore{neighborID, 0, 1.f, FD2R(15.f), 0.5f, 3.f}); |
||||
} |
||||
} |
||||
// load and convert depth-map
|
||||
if (!depthPath.empty()) { |
||||
DepthMap depthMap; { |
||||
constexpr double depthScale{1000.0}; |
||||
const cv::Mat imgDepthMap = cv::imread(depthPath, cv::IMREAD_ANYDEPTH); |
||||
if (imgDepthMap.empty()) |
||||
return false; |
||||
imgDepthMap.convertTo(depthMap, CV_32FC1, 1.0/depthScale); |
||||
} |
||||
IIndexArr IDs = {imageData.ID}; |
||||
IDs.JoinFunctor(imageData.neighbors.size(), [&imageData](IIndex i) { |
||||
return imageData.neighbors[i].ID; |
||||
}); |
||||
double dMin, dMax; |
||||
cv::minMaxIdx(depthMap, &dMin, &dMax, NULL, NULL, depthMap > 0); |
||||
const NormalMap normalMap; |
||||
const ConfidenceMap confMap; |
||||
const ViewsMap viewsMap; |
||||
if (!ExportDepthDataRaw(MAKE_PATH(String::FormatString("depth%04u.dmap", imageData.ID)), |
||||
imageData.name, IDs, resolution, |
||||
camera.K, pose.R, pose.C, |
||||
(float)dMin, (float)dMax, |
||||
depthMap, normalMap, confMap, viewsMap)) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
// parse scene stored in Polycam format
|
||||
bool ParseScene(Scene& scene, const String& scenePath) |
||||
{ |
||||
#if defined(_SUPPORT_CPP17) && (!defined(__GNUC__) || (__GNUC__ > 7)) |
||||
size_t numCorrectedFolders(0), numCorrectedDepthFolders(0), numFolders(0), numDepthFolders(0); |
||||
for (const auto& file: std::filesystem::directory_iterator(scenePath.c_str())) { |
||||
if (file.path().stem() == "corrected_cameras" || |
||||
file.path().stem() == "corrected_images") |
||||
++numCorrectedFolders; |
||||
else if (file.path().stem() == "corrected_depth") |
||||
++numCorrectedDepthFolders; |
||||
else if (file.path().stem() == "cameras" || |
||||
file.path().stem() == "images") |
||||
++numFolders; |
||||
else if (file.path().stem() == "depth") |
||||
++numDepthFolders; |
||||
} |
||||
if (numFolders != 2) { |
||||
VERBOSE("Invalid scene folder"); |
||||
return false; |
||||
} |
||||
if (numCorrectedFolders == 2) { |
||||
// corrected data
|
||||
CLISTDEFIDX(String, IIndex) imagePaths; |
||||
for (const auto& file: std::filesystem::directory_iterator((scenePath + "corrected_images").c_str())) |
||||
imagePaths.emplace_back(file.path().string()); |
||||
VERBOSE("Parsing corrected data: %u...", imagePaths.size()); |
||||
std::unordered_map<String, IIndex> mapImageName; |
||||
mapImageName.reserve(imagePaths.size()); |
||||
for (String& imagePath: imagePaths) { |
||||
Util::ensureValidPath(imagePath); |
||||
mapImageName.emplace(Util::getFileName(imagePath), static_cast<IIndex>(mapImageName.size())); |
||||
} |
||||
for (const String& imagePath: imagePaths) { |
||||
const String imageName = Util::getFileName(imagePath); |
||||
const String cameraPath(scenePath + "corrected_cameras" + PATH_SEPARATOR_STR + imageName + JSON_EXT); |
||||
const String depthPath(numCorrectedDepthFolders ? scenePath + "corrected_depth" + PATH_SEPARATOR_STR + imageName + DEPTH_EXT : String()); |
||||
if (!ParseImage(scene, imagePath, cameraPath, depthPath, mapImageName)) |
||||
return false; |
||||
} |
||||
} else { |
||||
// raw data
|
||||
CLISTDEFIDX(String, IIndex) imagePaths; |
||||
for (const auto& file: std::filesystem::directory_iterator((scenePath + "images").c_str())) |
||||
imagePaths.emplace_back(file.path().string()); |
||||
VERBOSE("Parsing raw data: %u...", imagePaths.size()); |
||||
std::unordered_map<String, IIndex> mapImageName; |
||||
mapImageName.reserve(imagePaths.size()); |
||||
for (String& imagePath: imagePaths) { |
||||
Util::ensureValidPath(imagePath); |
||||
mapImageName.emplace(Util::getFileName(imagePath), static_cast<IIndex>(mapImageName.size())); |
||||
} |
||||
for (const String& imagePath: imagePaths) { |
||||
const String imageName = Util::getFileName(imagePath); |
||||
const String cameraPath(scenePath + "cameras" + PATH_SEPARATOR_STR + imageName + JSON_EXT); |
||||
const String depthPath(numDepthFolders ? scenePath + "depth" + PATH_SEPARATOR_STR + imageName + DEPTH_EXT : String()); |
||||
if (!ParseImage(scene, imagePath, cameraPath, depthPath, mapImageName)) |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
#else |
||||
return false; |
||||
#endif |
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
TD_TIMER_START(); |
||||
|
||||
Scene scene(OPT::nMaxThreads); |
||||
|
||||
// convert data from Polycam format to OpenMVS
|
||||
if (!ParseScene(scene, MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName))) |
||||
return EXIT_FAILURE; |
||||
|
||||
// write OpenMVS input data
|
||||
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
|
||||
VERBOSE("Exported data: %u platforms, %u cameras, %u poses, %u images (%s)", |
||||
scene.platforms.size(), scene.images.size(), scene.images.size(), scene.images.size(), |
||||
TD_TIMER_GET_FMT().c_str()); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(InterfaceVisualSFM "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS InterfaceVisualSFM |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,383 @@
@@ -0,0 +1,383 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// File: DataInterface.h
|
||||
// Author: Changchang Wu (ccwu@cs.washington.edu)
|
||||
// Description : data interface, the data format been uploaded to GPU
|
||||
//
|
||||
// Copyright (c) 2011 Changchang Wu (ccwu@cs.washington.edu)
|
||||
// and the University of Washington at Seattle
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// Version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef DATA_INTERFACE_GPU_H |
||||
#define DATA_INTERFACE_GPU_H |
||||
|
||||
namespace PBA { |
||||
|
||||
// ----------------------------WARNING------------------------------
|
||||
// -----------------------------------------------------------------
|
||||
// ROTATION CONVERSION:
|
||||
// The internal rotation representation is 3x3 float matrix. Reading
|
||||
// back the rotations as quaternion or Rodrigues's representation will
|
||||
// cause inaccuracy, IF you have wrongly reconstructed cameras with
|
||||
// a very very large focal length (typically also very far away).
|
||||
// In this case, any small change in the rotation matrix, will cause
|
||||
// a large reprojection error.
|
||||
//
|
||||
// ---------------------------------------------------------------------
|
||||
// RADIAL distortion is NOT enabled by default, use parameter "-md", -pd"
|
||||
// or set ConfigBA::__use_radial_distortion to 1 or -1 to enable it.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
//transfer data type with 4-float alignment
|
||||
#define CameraT CameraT_ |
||||
#define Point3D Point3D_ |
||||
template<class FT> |
||||
|
||||
struct CameraT_ |
||||
{ |
||||
typedef FT float_t; |
||||
//////////////////////////////////////////////////////
|
||||
float_t f; // single focal length, K = [f, 0, 0; 0 f 0; 0 0 1]
|
||||
float_t t[3]; // T in P = K[R T], T = - RC
|
||||
float_t m[3][3]; // R in P = K[R T].
|
||||
float_t radial; // WARNING: BE careful with the radial distortion model.
|
||||
float_t distortion_type; |
||||
float_t constant_camera; |
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
CameraT_() { radial = 0; distortion_type = 0; constant_camera = 0; } |
||||
|
||||
//////////////////////////////////////////////
|
||||
template <class CameraX> void SetCameraT(const CameraX & cam) |
||||
{ |
||||
f = (float_t)cam.f; |
||||
t[0] = (float_t)cam.t[0]; t[1] = (float_t)cam.t[1]; t[2] = (float_t)cam.t[2]; |
||||
for(int i = 0; i < 3; ++i) for(int j = 0; j < 3; ++j) m[i][j] = (float_t)cam.m[i][j]; |
||||
radial = (float_t)cam.radial; |
||||
distortion_type = (float_t)cam.distortion_type; |
||||
constant_camera = (float_t)cam.constant_camera; |
||||
} |
||||
|
||||
//////////////////////////////////////////
|
||||
enum { |
||||
CAMERA_VARIABLE = 0, |
||||
CAMERA_FIXEDINTRINSIC = (1<<0), |
||||
CAMERA_FIXEDEXTRINSIC = (1<<1), |
||||
}; |
||||
void SetVariableCamera() {(int&)constant_camera = CAMERA_VARIABLE;} |
||||
void SetFixedIntrinsic() {(int&)constant_camera = CAMERA_FIXEDINTRINSIC;} |
||||
void SetFixedExtrinsic() {(int&)constant_camera = CAMERA_FIXEDEXTRINSIC;} |
||||
void SetConstantCamera() {(int&)constant_camera = CAMERA_FIXEDINTRINSIC|CAMERA_FIXEDEXTRINSIC;} |
||||
|
||||
//////////////////////////////////////
|
||||
template <class Float> void SetFocalLength(Float F){ f = (float_t) F; } |
||||
float_t GetFocalLength() const{return f;} |
||||
|
||||
template <class Float> void SetMeasurementDistortion(Float r) {radial = (float_t) r; distortion_type = -1;} |
||||
float_t GetMeasurementDistortion() const {return distortion_type == -1 ? radial : 0; } |
||||
|
||||
//normalize radial distortion that applies to angle will be (radial * f * f);
|
||||
template <class Float> void SetNormalizedMeasurementDistortion(Float r) {SetMeasurementDistortion(r / (f * f)); } |
||||
float_t GetNormalizedMeasurementDistortion() const{return GetMeasurementDistortion() * (f * f); } |
||||
|
||||
//use projection distortion
|
||||
template <class Float> void SetProjectionDistortion(Float r) {radial = float_t(r); distortion_type = 1; } |
||||
template <class Float> void SetProjectionDistortion(const Float* r) {SetProjectionDistortion(r[0]); } |
||||
float_t GetProjectionDistortion() {return distortion_type == 1 ? radial : 0; } |
||||
|
||||
template <class Float> void SetRodriguesRotation(const Float r[3]) |
||||
{ |
||||
double a = sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]); |
||||
double ct = a==0.0?0.5:(1.0-cos(a))/a/a; |
||||
double st = a==0.0?1:sin(a)/a; |
||||
m[0][0]=float_t(1.0 - (r[1]*r[1] + r[2]*r[2])*ct); |
||||
m[0][1]=float_t(r[0]*r[1]*ct - r[2]*st); |
||||
m[0][2]=float_t(r[2]*r[0]*ct + r[1]*st); |
||||
m[1][0]=float_t(r[0]*r[1]*ct + r[2]*st); |
||||
m[1][1]=float_t(1.0 - (r[2]*r[2] + r[0]*r[0])*ct); |
||||
m[1][2]=float_t(r[1]*r[2]*ct - r[0]*st); |
||||
m[2][0]=float_t(r[2]*r[0]*ct - r[1]*st); |
||||
m[2][1]=float_t(r[1]*r[2]*ct + r[0]*st); |
||||
m[2][2]=float_t(1.0 - (r[0]*r[0] + r[1]*r[1])*ct ); |
||||
} |
||||
template <class Float> void GetRodriguesRotation(Float r[3]) const |
||||
{ |
||||
double a = (m[0][0]+m[1][1]+m[2][2]-1.0)/2.0; |
||||
const double epsilon = 0.01; |
||||
if( fabs(m[0][1] - m[1][0]) < epsilon && |
||||
fabs(m[1][2] - m[2][1]) < epsilon && |
||||
fabs(m[0][2] - m[2][0]) < epsilon ) |
||||
{ |
||||
if( fabs(m[0][1] + m[1][0]) < 0.1 && |
||||
fabs(m[1][2] + m[2][1]) < 0.1 && |
||||
fabs(m[0][2] + m[2][0]) < 0.1 && a > 0.9) |
||||
{ |
||||
r[0] = 0; |
||||
r[1] = 0; |
||||
r[2] = 0; |
||||
} |
||||
else |
||||
{ |
||||
const Float ha = Float(sqrt(0.5) * 3.14159265358979323846); |
||||
double xx = (m[0][0]+1.0)/2.0; |
||||
double yy = (m[1][1]+1.0)/2.0; |
||||
double zz = (m[2][2]+1.0)/2.0; |
||||
double xy = (m[0][1]+m[1][0])/4.0; |
||||
double xz = (m[0][2]+m[2][0])/4.0; |
||||
double yz = (m[1][2]+m[2][1])/4.0; |
||||
|
||||
if ((xx > yy) && (xx > zz)) |
||||
{ |
||||
if (xx< epsilon) |
||||
{ |
||||
r[0] = 0; r[1] = r[2] = ha; |
||||
} else |
||||
{ |
||||
double t = sqrt(xx) ; |
||||
r[0] = Float(t * 3.14159265358979323846); |
||||
r[1] = Float(xy/t * 3.14159265358979323846); |
||||
r[2] = Float(xz/t * 3.14159265358979323846); |
||||
} |
||||
} else if (yy > zz) |
||||
{ |
||||
if (yy< epsilon) |
||||
{ |
||||
r[0] = r[2] = ha; r[1] = 0; |
||||
} else |
||||
{ |
||||
double t = sqrt(yy); |
||||
r[0] = Float(xy/t* 3.14159265358979323846); |
||||
r[1] = Float( t * 3.14159265358979323846); |
||||
r[2] = Float(yz/t* 3.14159265358979323846); |
||||
} |
||||
} else |
||||
{ |
||||
if (zz< epsilon) |
||||
{ |
||||
r[0] = r[1] = ha; r[2] = 0; |
||||
} else |
||||
{ |
||||
double t = sqrt(zz); |
||||
r[0] = Float(xz/ t* 3.14159265358979323846); |
||||
r[1] = Float(yz/ t* 3.14159265358979323846); |
||||
r[2] = Float( t * 3.14159265358979323846); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
a = acos(a); |
||||
double b = 0.5*a/sin(a); |
||||
r[0] = Float(b*(m[2][1]-m[1][2])); |
||||
r[1] = Float(b*(m[0][2]-m[2][0])); |
||||
r[2] = Float(b*(m[1][0]-m[0][1])); |
||||
} |
||||
} |
||||
////////////////////////
|
||||
template <class Float> void SetQuaternionRotation(const Float q[4]) |
||||
{ |
||||
double qq = sqrt(q[0]*q[0]+q[1]*q[1]+q[2]*q[2]+q[3]*q[3]); |
||||
double qw, qx, qy, qz; |
||||
if(qq>0) |
||||
{ |
||||
qw=q[0]/qq; |
||||
qx=q[1]/qq; |
||||
qy=q[2]/qq; |
||||
qz=q[3]/qq; |
||||
}else |
||||
{ |
||||
qw = 1; |
||||
qx = qy = qz = 0; |
||||
} |
||||
m[0][0]=float_t(qw*qw + qx*qx- qz*qz- qy*qy ); |
||||
m[0][1]=float_t(2*qx*qy -2*qz*qw ); |
||||
m[0][2]=float_t(2*qy*qw + 2*qz*qx); |
||||
m[1][0]=float_t(2*qx*qy+ 2*qw*qz); |
||||
m[1][1]=float_t(qy*qy+ qw*qw - qz*qz- qx*qx); |
||||
m[1][2]=float_t(2*qz*qy- 2*qx*qw); |
||||
m[2][0]=float_t(2*qx*qz- 2*qy*qw); |
||||
m[2][1]=float_t(2*qy*qz + 2*qw*qx ); |
||||
m[2][2]=float_t(qz*qz+ qw*qw- qy*qy- qx*qx); |
||||
} |
||||
template <class Float> void GetQuaternionRotation(Float q[4]) const |
||||
{ |
||||
q[0]= 1 + m[0][0] + m[1][1] + m[2][2]; |
||||
if(q[0]>0.000000001) |
||||
{ |
||||
q[0] = sqrt(q[0])/2.0; |
||||
q[1]= (m[2][1] - m[1][2])/( 4.0 *q[0]); |
||||
q[2]= (m[0][2] - m[2][0])/( 4.0 *q[0]); |
||||
q[3]= (m[1][0] - m[0][1])/( 4.0 *q[0]); |
||||
}else |
||||
{ |
||||
double s; |
||||
if ( m[0][0] > m[1][1] && m[0][0] > m[2][2] ) |
||||
{ |
||||
s = 2.0 * sqrt( 1.0 + m[0][0] - m[1][1] - m[2][2]); |
||||
q[1] = 0.25 * s; |
||||
q[2] = (m[0][1] + m[1][0] ) / s; |
||||
q[3] = (m[0][2] + m[2][0] ) / s; |
||||
q[0] = (m[1][2] - m[2][1] ) / s; |
||||
} else if (m[1][1] > m[2][2]) |
||||
{ |
||||
s = 2.0 * sqrt( 1.0 + m[1][1] - m[0][0] - m[2][2]); |
||||
q[1] = (m[0][1] + m[1][0] ) / s; |
||||
q[2] = 0.25 * s; |
||||
q[3] = (m[1][2] + m[2][1] ) / s; |
||||
q[0] = (m[0][2] - m[2][0] ) / s; |
||||
} else |
||||
{ |
||||
s = 2.0 * sqrt( 1.0 + m[2][2] - m[0][0] - m[1][1]); |
||||
q[1] = (m[0][2] + m[2][0] ) / s; |
||||
q[2] = (m[1][2] + m[2][1] ) / s; |
||||
q[3] = 0.25f * s; |
||||
q[0] = (m[0][1] - m[1][0] ) / s; |
||||
} |
||||
} |
||||
} |
||||
////////////////////////////////////////////////
|
||||
template <class Float> void SetMatrixRotation(const Float * r) |
||||
{ |
||||
for(int i = 0; i < 9; ++i) m[0][i] = float_t(r[i]); |
||||
} |
||||
template <class Float> void GetMatrixRotation(Float * r) const |
||||
{ |
||||
for(int i = 0; i < 9; ++i) r[i] = Float(m[0][i]); |
||||
} |
||||
float GetRotationMatrixDeterminant()const |
||||
{ |
||||
return m[0][0]*m[1][1]*m[2][2] + |
||||
m[0][1]*m[1][2]*m[2][0] + |
||||
m[0][2]*m[1][0]*m[2][1] - |
||||
m[0][2]*m[1][1]*m[2][0] - |
||||
m[0][1]*m[1][0]*m[2][2] - |
||||
m[0][0]*m[1][2]*m[2][1]; |
||||
} |
||||
///////////////////////////////////////
|
||||
template <class Float> void SetTranslation(const Float T[3]) |
||||
{ |
||||
t[0] = (float_t)T[0]; |
||||
t[1] = (float_t)T[1]; |
||||
t[2] = (float_t)T[2]; |
||||
} |
||||
template <class Float> void GetTranslation(Float T[3]) const |
||||
{ |
||||
T[0] = (Float)t[0]; |
||||
T[1] = (Float)t[1]; |
||||
T[2] = (Float)t[2]; |
||||
} |
||||
/////////////////////////////////////////////
|
||||
template <class Float> void SetCameraCenterAfterRotation(const Float c[3]) |
||||
{ |
||||
//t = - R * C
|
||||
for(int j = 0; j < 3; ++j) t[j] = -float_t(double(m[j][0])*double(c[0]) + double(m[j][1])*double(c[1]) + double(m[j][2])*double(c[2])); |
||||
} |
||||
template <class Float> void GetCameraCenter(Float c[3]) const |
||||
{ |
||||
//C = - R' * t
|
||||
for(int j = 0; j < 3; ++j) c[j] = -Float(double(m[0][j])*double(t[0]) + double(m[1][j])*double(t[1]) + double(m[2][j])*double(t[2])); |
||||
} |
||||
////////////////////////////////////////////
|
||||
template <class Float> void SetInvertedRT(const Float e[3], const Float T[3]) |
||||
{ |
||||
SetRodriguesRotation(e); |
||||
for(int i = 3; i < 9; ++i) m[0][i] = - m[0][i]; |
||||
SetTranslation(T); t[1] = - t[1]; t[2] = -t[2]; |
||||
} |
||||
|
||||
template <class Float> void GetInvertedRT (Float e[3], Float T[3]) const |
||||
{ |
||||
CameraT ci; ci.SetMatrixRotation(m[0]); |
||||
for(int i = 3; i < 9; ++i) ci.m[0][i] = - ci.m[0][i]; |
||||
//for(int i = 1; i < 3; ++i) for(int j = 0; j < 3; ++j) ci.m[i][j] = - ci.m[i][j];
|
||||
ci.GetRodriguesRotation(e); |
||||
GetTranslation(T); T[1] = - T[1]; T[2] = -T[2]; |
||||
} |
||||
template <class Float> void SetInvertedR9T(const Float e[9], const Float T[3]) |
||||
{ |
||||
//for(int i = 0; i < 9; ++i) m[0][i] = (i < 3 ? e[i] : - e[i]);
|
||||
//SetTranslation(T); t[1] = - t[1]; t[2] = -t[2];
|
||||
m[0][0] = e[0]; m[0][1] = e[1]; m[0][2] = e[2]; |
||||
m[1][0] = -e[3]; m[1][1] = -e[4]; m[1][2] = -e[5]; |
||||
m[2][0] = -e[6]; m[2][1] = -e[7]; m[2][2] = -e[8]; |
||||
t[0] = T[0]; t[1] = -T[1]; t[2] = -T[2]; |
||||
} |
||||
template<class Float> void GetInvertedR9T(Float e[9], Float T[3]) const |
||||
{ |
||||
e[0] = m[0][0]; e[1] = m[0][1]; e[2] = m[0][2]; |
||||
e[3] = - m[1][0]; e[4] = -m[1][1]; e[5] = -m[1][2]; |
||||
e[6] = -m[2][0]; e[7] = -m[2][1]; e[8] = -m[2][2] ; |
||||
T[0] = t[0]; T[1] = -t[1]; T[2] = -t[2]; |
||||
} |
||||
}; |
||||
|
||||
|
||||
|
||||
template<class FT> |
||||
struct Point3D |
||||
{ |
||||
typedef FT float_t; |
||||
float_t xyz[3]; //3D point location
|
||||
float_t reserved; //alignment
|
||||
////////////////////////////////
|
||||
template <class Float> void SetPoint(Float x, Float y, Float z) |
||||
{ |
||||
xyz[0] = (float_t) x; |
||||
xyz[1] = (float_t) y; |
||||
xyz[2] = (float_t) z; |
||||
reserved = 0; |
||||
} |
||||
template <class Float> void SetPoint(const Float * p) |
||||
{ |
||||
xyz[0] = (float_t) p[0]; |
||||
xyz[1] = (float_t) p[1]; |
||||
xyz[2] = (float_t) p[2]; |
||||
reserved = 0; |
||||
} |
||||
template <class Float> void GetPoint(Float* p) const |
||||
{ |
||||
p[0] = (Float) xyz[0]; |
||||
p[1] = (Float) xyz[1]; |
||||
p[2] = (Float) xyz[2]; |
||||
} |
||||
template <class Float> void GetPoint(Float&x, Float&y, Float&z) const |
||||
{ |
||||
x = (Float) xyz[0]; |
||||
y = (Float) xyz[1]; |
||||
z = (Float) xyz[2]; |
||||
} |
||||
}; |
||||
|
||||
#undef CameraT |
||||
#undef Point3D |
||||
|
||||
typedef CameraT_<float> CameraT; |
||||
typedef CameraT_<double> CameraD; |
||||
typedef Point3D_<float> Point3D; |
||||
typedef Point3D_<double> Point3B; |
||||
|
||||
struct Point2D |
||||
{ |
||||
float x, y; |
||||
////////////////////////////////////////////////////////
|
||||
Point2D(){} |
||||
template <class Float> Point2D(Float X, Float Y) {SetPoint2D(X, Y); } |
||||
template <class Float> void SetPoint2D(Float X, Float Y) { x = (float) X; y = (float) Y; } |
||||
template <class Float> void GetPoint2D(Float&X, Float&Y) const { X = (Float) x; Y = (Float) y; } |
||||
}; |
||||
|
||||
} // namespace PBA
|
||||
|
||||
#endif |
||||
|
||||
@ -0,0 +1,607 @@
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
* InterfaceVisualSFM.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
#define LOG_OUT() GET_LOG() |
||||
#define LOG_ERR() GET_LOG() |
||||
#include "Util.h" |
||||
#include <boost/program_options.hpp> |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("InterfaceVisualSFM") |
||||
#define MVS_EXT _T(".mvs") |
||||
#define VSFM_EXT _T(".nvm") |
||||
#define BUNDLE_EXT _T(".out") |
||||
#define CMPMVS_EXT _T(".lst") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace OPT { |
||||
String strInputFileName; |
||||
String strOutputFileName; |
||||
String strOutputImageFolder; |
||||
bool IsFromOpenMVS; // conversion direction
|
||||
unsigned nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
String strConfigFileName; |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "produce this help message") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
boost::program_options::options_description config("Main options"); |
||||
config.add_options() |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list (NVM, undistorted OUT + image_list.TXT, LST)") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh") |
||||
("output-image-folder", boost::program_options::value<std::string>(&OPT::strOutputImageFolder)->default_value("undistorted_images"), "output folder to store undistorted images") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
// initialize the log file
|
||||
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str()); |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidPath(OPT::strInputFileName); |
||||
Util::ensureUnifySlash(OPT::strInputFileName); |
||||
if (OPT::vm.count("help") || OPT::strInputFileName.IsEmpty()) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config); |
||||
GET_LOG() << visible; |
||||
} |
||||
if (OPT::strInputFileName.IsEmpty()) |
||||
return false; |
||||
|
||||
// initialize optional options
|
||||
if (OPT::strInputFileName.IsEmpty()) |
||||
return false; |
||||
Util::ensureValidPath(OPT::strOutputFileName); |
||||
Util::ensureUnifySlash(OPT::strOutputFileName); |
||||
Util::ensureUnifySlash(OPT::strOutputImageFolder); |
||||
Util::ensureFolderSlash(OPT::strOutputImageFolder); |
||||
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower()); |
||||
OPT::IsFromOpenMVS = (strInputFileNameExt == MVS_FILE_EXTENSION); |
||||
if (OPT::IsFromOpenMVS) { |
||||
if (OPT::strOutputFileName.empty()) |
||||
OPT::strOutputFileName = Util::getFilePath(OPT::strInputFileName); |
||||
} else { |
||||
if (OPT::strOutputFileName.empty()) |
||||
OPT::strOutputFileName = Util::getFilePath(OPT::strInputFileName) + _T("scene") MVS_FILE_EXTENSION; |
||||
else |
||||
OPT::strOutputImageFolder = Util::getRelativePath(Util::getFilePath(OPT::strOutputFileName), Util::getFilePath(OPT::strInputFileName)+OPT::strOutputImageFolder); |
||||
} |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
#define PBA_PRECISION float |
||||
|
||||
namespace PBA { |
||||
template<class FT> struct CameraT_; |
||||
typedef CameraT_<PBA_PRECISION> Camera; |
||||
template<class FT> struct Point3D_; |
||||
typedef Point3D_<PBA_PRECISION> Point3D; |
||||
} // namespace PBA
|
||||
|
||||
namespace MVS { |
||||
// given an undistorted pixel coordinate and one radial-undistortion parameter,
|
||||
// compute the corresponding distorted coordinate
|
||||
template<typename TYPE> |
||||
inline TPoint2<TYPE> DistortPointR1(const TPoint2<TYPE>& pt, const REAL& k1) { |
||||
if (k1 == 0) |
||||
return pt; |
||||
const REAL y(pt.y == 0 ? REAL(1.e-12) : REAL(pt.y)); |
||||
const REAL t2(y*y); |
||||
const REAL t3(t2*t2*t2); |
||||
const REAL t4(pt.x*pt.x); |
||||
const REAL t7(k1*(t2+t4)); |
||||
const REAL t9(1.0/t7); |
||||
const REAL t10(t2*t9*y*0.5); |
||||
const REAL t11(t3*t9*t9*(0.25+t9/27.0)); |
||||
#ifndef _RELEASE |
||||
TPoint2<TYPE> upt; |
||||
#endif |
||||
if (k1 > 0) { |
||||
const REAL t17(CBRT(t10+SQRT(t11))); |
||||
const REAL t18(t17-t2*t9/(t17*3)); |
||||
#ifndef _RELEASE |
||||
upt = |
||||
#else |
||||
return |
||||
#endif |
||||
TPoint2<TYPE>(TYPE(t18*pt.x/y), TYPE(t18)); |
||||
} else { |
||||
ASSERT(t11 <= 0); |
||||
const std::complex<REAL> t16(t10, SQRT(-t11)); |
||||
const std::complex<REAL> t17(pow(t16, 1.0/3.0)); |
||||
const std::complex<REAL> t14((t2*t9)/(t17*3.0)); |
||||
const std::complex<REAL> t18((t17+t14)*std::complex<REAL>(0.0,SQRT_3)); |
||||
const std::complex<REAL> t19(0.5*(t14-t17-t18)); |
||||
#ifndef _RELEASE |
||||
upt = |
||||
#else |
||||
return |
||||
#endif |
||||
TPoint2<TYPE>(TYPE(t19.real()*pt.x/y), TYPE(t19.real())); |
||||
} |
||||
#ifndef _RELEASE |
||||
ASSERT(ABS(TYPE((1.0+k1*(upt.x*upt.x+upt.y*upt.y))*upt.x) - pt.x) < TYPE(0.001)); |
||||
ASSERT(ABS(TYPE((1.0+k1*(upt.x*upt.x+upt.y*upt.y))*upt.y) - pt.y) < TYPE(0.001)); |
||||
return upt; |
||||
#endif |
||||
} |
||||
|
||||
void UndistortImage(const Camera& camera, const REAL& k1, const Image8U3 imgIn, Image8U3& imgOut) |
||||
{ |
||||
// allocate the undistorted image
|
||||
if (imgOut.data == imgIn.data || |
||||
imgOut.cols != imgIn.cols || |
||||
imgOut.rows != imgIn.rows || |
||||
imgOut.type() != imgIn.type()) |
||||
imgOut = Image8U3(imgIn.rows, imgIn.cols); |
||||
|
||||
// compute each pixel
|
||||
const int w = imgIn.cols; |
||||
const int h = imgIn.rows; |
||||
const Matrix3x3f K(camera.K); |
||||
const Matrix3x3f invK(camera.GetInvK()); |
||||
ASSERT(ISEQUAL(K(0,2),0.5f*(w-1)) && ISEQUAL(K(1,2),0.5f*(h-1))); |
||||
typedef Sampler::Cubic<float> Sampler; |
||||
const Sampler sampler; |
||||
Point2f pt; |
||||
for (int v=0; v<h; ++v) { |
||||
for (int u=0; u<w; ++u) { |
||||
// compute corresponding coordinates in the distorted image
|
||||
pt.x = (float)u; pt.y = (float)v; |
||||
NormalizeProjection(invK.val, pt.ptr(), pt.ptr()); |
||||
pt = DistortPointR1(pt, k1); |
||||
NormalizeProjection(K.val, pt.ptr(), pt.ptr()); |
||||
|
||||
// if coordinates in range
|
||||
Pixel8U& col = imgOut(v,u); |
||||
if (imgIn.isInside(pt)) { |
||||
// get pixel color
|
||||
col = imgIn.sample<Sampler,Pixel32F>(sampler, pt).cast<uint8_t>(); |
||||
} else { |
||||
// set to black
|
||||
col = Pixel8U::BLACK; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} // namespace MVS
|
||||
|
||||
|
||||
bool ExportSceneVSFM() |
||||
{ |
||||
TD_TIMER_START(); |
||||
|
||||
// read MVS input data
|
||||
MVS::Scene scene(OPT::nMaxThreads); |
||||
if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName))) |
||||
return false; |
||||
|
||||
// convert and write data from OpenMVS to VisualSFM
|
||||
std::vector<PBA::Camera> cameras; |
||||
std::vector<PBA::Point3D> vertices; |
||||
std::vector<PBA::Point2D> measurements; // the array of 2D projections (only inliers)
|
||||
std::vector<int> correspondingPoint; // 3D point index corresponding to each 2D projection
|
||||
std::vector<int> correspondingView; // and camera index
|
||||
std::vector<std::string> names; |
||||
std::vector<int> ptc; |
||||
cameras.reserve(scene.images.size()); |
||||
names.reserve(scene.images.size()); |
||||
MVS::IIndexArr mapIdx(scene.images.size()); |
||||
bool bFocalWarning(false), bPrincipalpointWarning(false); |
||||
FOREACH(idx, scene.images) { |
||||
const MVS::Image& image = scene.images[idx]; |
||||
if (!image.IsValid()) { |
||||
mapIdx[idx] = NO_ID; |
||||
continue; |
||||
} |
||||
if (!bFocalWarning && !ISEQUAL(image.camera.K(0, 0), image.camera.K(1, 1))) { |
||||
DEBUG("warning: fx != fy and NVM format does not support it"); |
||||
bFocalWarning = true; |
||||
} |
||||
if (!bPrincipalpointWarning && (!ISEQUAL(REAL(image.width-1)*0.5, image.camera.K(0, 2)) || !ISEQUAL(REAL(image.height-1)*0.5, image.camera.K(1, 2)))) { |
||||
DEBUG("warning: cx, cy are not the image center and NVM format does not support it"); |
||||
bPrincipalpointWarning = true; |
||||
} |
||||
PBA::Camera cameraNVM; |
||||
cameraNVM.SetFocalLength((image.camera.K(0, 0) + image.camera.K(1, 1)) * 0.5); |
||||
cameraNVM.SetMatrixRotation(image.camera.R.val); |
||||
cameraNVM.SetCameraCenterAfterRotation(image.camera.C.ptr()); |
||||
mapIdx[idx] = static_cast<MVS::IIndex>(cameras.size()); |
||||
cameras.emplace_back(cameraNVM); |
||||
names.emplace_back(MAKE_PATH_REL(WORKING_FOLDER_FULL, image.name)); |
||||
} |
||||
vertices.reserve(scene.pointcloud.points.size()); |
||||
measurements.reserve(scene.pointcloud.pointViews.size()); |
||||
correspondingPoint.reserve(scene.pointcloud.pointViews.size()); |
||||
correspondingView.reserve(scene.pointcloud.pointViews.size()); |
||||
FOREACH(idx, scene.pointcloud.points) { |
||||
const MVS::PointCloud::Point& X = scene.pointcloud.points[idx]; |
||||
const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[idx]; |
||||
const size_t prevMeasurements(measurements.size()); |
||||
for (MVS::IIndex idxView: views) { |
||||
const MVS::Image& image = scene.images[idxView]; |
||||
const Point2f pt(image.camera.TransformPointW2I(Cast<REAL>(X))); |
||||
if (pt.x < 0 || pt.y < 0 || pt.x > image.width-1 || pt.y > image.height-1) |
||||
continue; |
||||
measurements.emplace_back(pt.x, pt.y); |
||||
correspondingView.emplace_back(static_cast<int>(mapIdx[idxView])); |
||||
correspondingPoint.emplace_back(static_cast<int>(vertices.size())); |
||||
} |
||||
if (prevMeasurements < measurements.size()) |
||||
vertices.emplace_back(PBA::Point3D{X.x, X.y, X.z}); |
||||
} |
||||
if (!scene.pointcloud.colors.empty()) { |
||||
ptc.reserve(scene.pointcloud.colors.size()*3); |
||||
FOREACH(idx, scene.pointcloud.points) { |
||||
const MVS::PointCloud::Color& c = scene.pointcloud.colors[idx]; |
||||
ptc.emplace_back(c.r); |
||||
ptc.emplace_back(c.g); |
||||
ptc.emplace_back(c.b); |
||||
} |
||||
} |
||||
PBA::SaveModelFile(MAKE_PATH_SAFE(OPT::strOutputFileName), cameras, vertices, measurements, correspondingPoint, correspondingView, names, ptc); |
||||
|
||||
VERBOSE("Input data exported: %u images & %u points (%s)", scene.images.size(), scene.pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str()); |
||||
return true; |
||||
} |
||||
|
||||
|
||||
bool ImportSceneVSFM() |
||||
{ |
||||
TD_TIMER_START(); |
||||
|
||||
// read VisualSFM input data
|
||||
std::vector<PBA::Camera> cameras; |
||||
std::vector<PBA::Point3D> vertices; |
||||
std::vector<PBA::Point2D> measurements; // the array of 2D projections (only inliers)
|
||||
std::vector<int> correspondingPoint; // 3D point index corresponding to each 2D projection
|
||||
std::vector<int> correspondingView; // and camera index
|
||||
std::vector<std::string> names; |
||||
std::vector<int> ptc; |
||||
if (!PBA::LoadModelFile(MAKE_PATH_SAFE(OPT::strInputFileName), cameras, vertices, measurements, correspondingPoint, correspondingView, names, ptc)) |
||||
return false; |
||||
|
||||
// convert data from VisualSFM to OpenMVS
|
||||
MVS::Scene scene(OPT::nMaxThreads); |
||||
scene.platforms.Reserve((uint32_t)cameras.size()); |
||||
scene.images.Reserve((MVS::IIndex)cameras.size()); |
||||
scene.nCalibratedImages = 0; |
||||
for (size_t idx=0; idx<cameras.size(); ++idx) { |
||||
MVS::Image& image = scene.images.AddEmpty(); |
||||
image.name = names[idx]; |
||||
Util::ensureUnifySlash(image.name); |
||||
image.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, image.name); |
||||
if (!image.ReloadImage(0, false)) { |
||||
LOG("error: can not read image %s", image.name.c_str()); |
||||
return false; |
||||
} |
||||
// set camera
|
||||
image.platformID = scene.platforms.GetSize(); |
||||
MVS::Platform& platform = scene.platforms.AddEmpty(); |
||||
MVS::Platform::Camera& camera = platform.cameras.AddEmpty(); |
||||
image.cameraID = 0; |
||||
image.ID = static_cast<MVS::IIndex>(idx); |
||||
const PBA::Camera& cameraNVM = cameras[idx]; |
||||
camera.K = MVS::Platform::Camera::ComposeK<REAL,REAL>(cameraNVM.GetFocalLength(), cameraNVM.GetFocalLength(), image.width, image.height); |
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
// normalize camera intrinsics
|
||||
camera.K = camera.GetScaledK(REAL(1)/MVS::Camera::GetNormalizationScale(image.width, image.height)); |
||||
// set pose
|
||||
image.poseID = platform.poses.GetSize(); |
||||
MVS::Platform::Pose& pose = platform.poses.AddEmpty(); |
||||
cameraNVM.GetMatrixRotation(pose.R.val); |
||||
cameraNVM.GetCameraCenter(pose.C.ptr()); |
||||
image.UpdateCamera(scene.platforms); |
||||
++scene.nCalibratedImages; |
||||
} |
||||
scene.pointcloud.points.Reserve(vertices.size()); |
||||
for (size_t idx=0; idx<vertices.size(); ++idx) { |
||||
const PBA::Point3D& X = vertices[idx]; |
||||
scene.pointcloud.points.AddConstruct(X.xyz[0], X.xyz[1], X.xyz[2]); |
||||
} |
||||
scene.pointcloud.pointViews.Resize(vertices.size()); |
||||
for (size_t idx=0; idx<measurements.size(); ++idx) { |
||||
MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[correspondingPoint[idx]]; |
||||
views.InsertSort(correspondingView[idx]); |
||||
} |
||||
if (ptc.size() == vertices.size()*3) { |
||||
scene.pointcloud.colors.Reserve(ptc.size()); |
||||
for (size_t idx=0; idx<ptc.size(); idx+=3) |
||||
scene.pointcloud.colors.AddConstruct((uint8_t)ptc[idx+0], (uint8_t)ptc[idx+1], (uint8_t)ptc[idx+2]); |
||||
} |
||||
|
||||
// undistort images
|
||||
const String pathData(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputImageFolder)); |
||||
Util::Progress progress(_T("Processed images"), scene.images.GetSize()); |
||||
#ifdef _USE_OPENMP |
||||
bool bAbort(false); |
||||
#pragma omp parallel for shared(bAbort) schedule(dynamic) |
||||
for (int i=0; i<(int)scene.images.GetSize(); ++i) { |
||||
#pragma omp flush (bAbort) |
||||
if (bAbort) |
||||
continue; |
||||
#else |
||||
FOREACH(i, scene.images) { |
||||
#endif |
||||
++progress; |
||||
MVS::Image& imageData = scene.images[i]; |
||||
const PBA::Camera& cameraNVM = cameras[i]; |
||||
if (cameraNVM.GetMeasurementDistortion() == 0) |
||||
continue; |
||||
if (!imageData.ReloadImage()) { |
||||
#ifdef _USE_OPENMP |
||||
bAbort = true; |
||||
#pragma omp flush (bAbort) |
||||
continue; |
||||
#else |
||||
return false; |
||||
#endif |
||||
} |
||||
MVS::UndistortImage(imageData.camera, cameraNVM.GetNormalizedMeasurementDistortion(), imageData.image, imageData.image); |
||||
const String name(pathData + String::FormatString(_T("%05u.png"), i)); |
||||
Util::ensureFolder(name); |
||||
if (!imageData.image.Save(name)) { |
||||
#ifdef _USE_OPENMP |
||||
bAbort = true; |
||||
#pragma omp flush (bAbort) |
||||
continue; |
||||
#else |
||||
return false; |
||||
#endif |
||||
} |
||||
imageData.ReleaseImage(); |
||||
} |
||||
#ifdef _USE_OPENMP |
||||
if (bAbort) |
||||
return false; |
||||
#endif |
||||
progress.close(); |
||||
|
||||
VERBOSE("Input data imported: %u cameras, %u poses, %u images, %u points (%s)", cameras.size(), cameras.size(), cameras.size(), vertices.size(), TD_TIMER_GET_FMT().c_str()); |
||||
|
||||
// write OpenMVS input data
|
||||
return scene.SaveInterface(MAKE_PATH_SAFE(OPT::strOutputFileName)); |
||||
} |
||||
|
||||
|
||||
template <typename T> |
||||
void _ImageListParseP(const LPSTR* argv, TMatrix<T,3,4>& P) |
||||
{ |
||||
// read projection matrix
|
||||
P(0,0) = String::FromString<T>(argv[0]); |
||||
P(0,1) = String::FromString<T>(argv[1]); |
||||
P(0,2) = String::FromString<T>(argv[2]); |
||||
P(0,3) = String::FromString<T>(argv[3]); |
||||
P(1,0) = String::FromString<T>(argv[4]); |
||||
P(1,1) = String::FromString<T>(argv[5]); |
||||
P(1,2) = String::FromString<T>(argv[6]); |
||||
P(1,3) = String::FromString<T>(argv[7]); |
||||
P(2,0) = String::FromString<T>(argv[8]); |
||||
P(2,1) = String::FromString<T>(argv[9]); |
||||
P(2,2) = String::FromString<T>(argv[10]); |
||||
P(2,3) = String::FromString<T>(argv[11]); |
||||
} |
||||
|
||||
int ImportSceneCMPMVS() |
||||
{ |
||||
TD_TIMER_START(); |
||||
|
||||
MVS::Scene scene(OPT::nMaxThreads); |
||||
|
||||
// read CmpMVS input data as a list of images and their projection matrices
|
||||
std::ifstream iFilein(MAKE_PATH_SAFE(OPT::strInputFileName)); |
||||
if (!iFilein.is_open()) |
||||
return false; |
||||
while (iFilein.good()) { |
||||
String strImageName; |
||||
std::getline(iFilein, strImageName); |
||||
if (strImageName.empty()) |
||||
continue; |
||||
if (!File::access(MAKE_PATH_SAFE(strImageName))) |
||||
return false; |
||||
const String strImageNameP(Util::getFileFullName(strImageName)+"_P.txt"); |
||||
std::ifstream iFileP(MAKE_PATH_SAFE(strImageNameP)); |
||||
if (!iFileP.is_open()) |
||||
return false; |
||||
String strP; int numLines(0); |
||||
while (iFileP.good()) { |
||||
String line; |
||||
std::getline(iFileP, line); |
||||
if (strImageName.empty()) |
||||
break; |
||||
if (strP.empty()) |
||||
strP = line; |
||||
else |
||||
strP += _T(' ') + line; |
||||
++numLines; |
||||
} |
||||
if (numLines != 3) |
||||
return false; |
||||
PMatrix P; |
||||
size_t argc; |
||||
CAutoPtrArr<LPSTR> argv(Util::CommandLineToArgvA(strP, argc)); |
||||
if (argc != 12) |
||||
return false; |
||||
_ImageListParseP(argv, P); |
||||
KMatrix K; RMatrix R; CMatrix C; |
||||
MVS::DecomposeProjectionMatrix(P, K, R, C); |
||||
// set image
|
||||
MVS::Image& image = scene.images.AddEmpty(); |
||||
image.name = strImageName; |
||||
Util::ensureUnifySlash(image.name); |
||||
image.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, image.name); |
||||
if (!image.ReloadImage(0, false)) { |
||||
LOG("error: can not read image %s", image.name.c_str()); |
||||
return false; |
||||
} |
||||
// set camera
|
||||
image.platformID = scene.platforms.GetSize(); |
||||
MVS::Platform& platform = scene.platforms.AddEmpty(); |
||||
MVS::Platform::Camera& camera = platform.cameras.AddEmpty(); |
||||
image.cameraID = 0; |
||||
camera.K = K; |
||||
camera.R = RMatrix::IDENTITY; |
||||
camera.C = CMatrix::ZERO; |
||||
// normalize camera intrinsics
|
||||
camera.K = camera.GetScaledK(REAL(1)/MVS::Camera::GetNormalizationScale(image.width, image.height)); |
||||
// set pose
|
||||
image.poseID = platform.poses.GetSize(); |
||||
MVS::Platform::Pose& pose = platform.poses.AddEmpty(); |
||||
pose.R = R; |
||||
pose.C = C; |
||||
image.UpdateCamera(scene.platforms); |
||||
++scene.nCalibratedImages; |
||||
} |
||||
|
||||
VERBOSE("Input data imported: %u images (%s)", scene.images.size(), TD_TIMER_GET_FMT().c_str()); |
||||
|
||||
// write OpenMVS input data
|
||||
return scene.SaveInterface(MAKE_PATH_SAFE(OPT::strOutputFileName)); |
||||
} |
||||
|
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
if (OPT::IsFromOpenMVS) { |
||||
ExportSceneVSFM(); |
||||
} else { |
||||
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower()); |
||||
if (strInputFileNameExt == VSFM_EXT || strInputFileNameExt == BUNDLE_EXT) { |
||||
if (!ImportSceneVSFM()) |
||||
return EXIT_FAILURE; |
||||
} else |
||||
if (strInputFileNameExt == CMPMVS_EXT) { |
||||
if (!ImportSceneCMPMVS()) |
||||
return EXIT_FAILURE; |
||||
} |
||||
} |
||||
|
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,756 @@
@@ -0,0 +1,756 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// File: util.h
|
||||
// Author: Changchang Wu (ccwu@cs.washington.edu)
|
||||
// Description : some utility functions for reading/writing SfM data
|
||||
//
|
||||
// Copyright (c) 2011 Changchang Wu (ccwu@cs.washington.edu)
|
||||
// and the University of Washington at Seattle
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// Version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <iostream> |
||||
#include <fstream> |
||||
#include <vector> |
||||
#include <string> |
||||
#include <math.h> |
||||
#include <time.h> |
||||
#include <iomanip> |
||||
#include <algorithm> |
||||
#include "DataInterface.h" |
||||
|
||||
namespace PBA { |
||||
|
||||
//File loader supports .nvm format and bundler format
|
||||
bool LoadModelFile(const char* name, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<std::string>& names, std::vector<int>& ptc); |
||||
void SaveNVM(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<std::string>& names, std::vector<int>& ptc); |
||||
void SaveBundlerModel(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx); |
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
void AddNoise(std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, float percent); |
||||
void AddStableNoise(std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
const std::vector<int>& ptidx, const std::vector<int>& camidx, float percent); |
||||
bool RemoveInvisiblePoints( std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<Point2D>& measurements, std::vector<std::string>& names, std::vector<int>& ptc); |
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
bool LoadNVM(std::ifstream& in, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<std::string>& names, std::vector<int>& ptc) |
||||
{ |
||||
int rotation_parameter_num = 4; |
||||
bool format_r9t = false; |
||||
std::string token; |
||||
if(in.peek() == 'N') |
||||
{ |
||||
in >> token; //file header
|
||||
if(strstr(token.c_str(), "R9T")) |
||||
{ |
||||
rotation_parameter_num = 9; //rotation as 3x3 matrix
|
||||
format_r9t = true; |
||||
} |
||||
} |
||||
|
||||
double fxFixed, fyFixed, cxFixed, cyFixed, k1(0); |
||||
int ncam = 0, npoint = 0, nproj = 0; |
||||
in >> token; |
||||
if (token == "FixedK") { |
||||
// read fixed intrinsics
|
||||
std::getline(in, token); |
||||
sscanf(token.c_str(), "%lf %lf %lf %lf %lf", &fxFixed, &cxFixed, &fyFixed, &cyFixed, &k1); |
||||
// read # of cameras
|
||||
in >> ncam; |
||||
} else { |
||||
// read # of cameras
|
||||
ncam = atoi(token.c_str()); |
||||
} |
||||
if(ncam <= 1) return false; |
||||
|
||||
//read the camera parameters
|
||||
camera_data.resize(ncam); // allocate the camera data
|
||||
names.resize(ncam); |
||||
for(int i = 0; i < ncam; ++i) |
||||
{ |
||||
double f, q[9], c[3], d[2]; |
||||
in >> token >> f ; |
||||
for(int j = 0; j < rotation_parameter_num; ++j) in >> q[j]; |
||||
in >> c[0] >> c[1] >> c[2] >> d[0] >> d[1]; |
||||
|
||||
camera_data[i].SetFocalLength(f); |
||||
if(format_r9t) |
||||
{ |
||||
camera_data[i].SetMatrixRotation(q); |
||||
camera_data[i].SetTranslation(c); |
||||
} |
||||
else |
||||
{ |
||||
//older format for compatibility
|
||||
camera_data[i].SetQuaternionRotation(q); //quaternion from the file
|
||||
camera_data[i].SetCameraCenterAfterRotation(c); //camera center from the file
|
||||
} |
||||
camera_data[i].SetNormalizedMeasurementDistortion(k1!=0 ? k1 : d[0]); |
||||
names[i] = token; |
||||
} |
||||
|
||||
//////////////////////////////////////
|
||||
in >> npoint; if(npoint <= 0) return false; |
||||
|
||||
//read image projections and 3D points.
|
||||
point_data.resize(npoint); |
||||
for(int i = 0; i < npoint; ++i) |
||||
{ |
||||
float pt[3]; int cc[3], npj; |
||||
in >> pt[0] >> pt[1] >> pt[2] |
||||
>> cc[0] >> cc[1] >> cc[2] >> npj; |
||||
for(int j = 0; j < npj; ++j) |
||||
{ |
||||
int cidx, fidx; float imx, imy; |
||||
in >> cidx >> fidx >> imx >> imy; |
||||
|
||||
camidx.push_back(cidx); //camera index
|
||||
ptidx.push_back(i); //point index
|
||||
|
||||
//add a measurement to the vector
|
||||
measurements.push_back(Point2D(imx, imy)); |
||||
nproj ++; |
||||
} |
||||
point_data[i].SetPoint(pt); |
||||
ptc.insert(ptc.end(), cc, cc + 3); |
||||
} |
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
LOG_OUT() << ncam << " cameras; " << npoint << " 3D points; " << nproj << " projections\n"; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
|
||||
void SaveNVM(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<std::string>& names, std::vector<int>& ptc) |
||||
{ |
||||
LOG_OUT() << "Saving model to " << filename << "...\n"; |
||||
std::ofstream out(filename); |
||||
|
||||
out << "NVM_V3_R9T\n" << camera_data.size() << '\n' << std::setprecision(12); |
||||
if(names.size() < camera_data.size()) names.resize(camera_data.size(),std::string("unknown")); |
||||
if(ptc.size() < 3 * point_data.size()) ptc.resize(point_data.size() * 3, 0); |
||||
|
||||
////////////////////////////////////
|
||||
for(size_t i = 0; i < camera_data.size(); ++i) |
||||
{ |
||||
CameraT& cam = camera_data[i]; |
||||
out << names[i] << ' ' << cam.GetFocalLength() << ' '; |
||||
for(int j = 0; j < 9; ++j) out << cam.m[0][j] << ' '; |
||||
out << cam.t[0] << ' ' << cam.t[1] << ' ' << cam.t[2] << ' ' |
||||
<< cam.GetNormalizedMeasurementDistortion() << " 0\n"; |
||||
} |
||||
|
||||
out << point_data.size() << '\n'; |
||||
|
||||
for(size_t i = 0, j = 0; i < point_data.size(); ++i) |
||||
{ |
||||
Point3D& pt = point_data[i]; |
||||
int * pc = &ptc[i * 3]; |
||||
out << pt.xyz[0] << ' ' << pt.xyz[1] << ' ' << pt.xyz[2] << ' ' |
||||
<< pc[0] << ' ' << pc[1] << ' ' << pc[2] << ' '; |
||||
|
||||
size_t je = j; |
||||
while(je < ptidx.size() && ptidx[je] == (int) i) je++; |
||||
|
||||
out << (je - j) << ' '; |
||||
|
||||
for(; j < je; ++j) out << camidx[j] << ' ' << " 0 " << measurements[j].x << ' ' << measurements[j].y << ' '; |
||||
|
||||
out << '\n'; |
||||
} |
||||
} |
||||
|
||||
|
||||
bool LoadBundlerOut(const char* name, std::ifstream& in, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<std::string>& names, std::vector<int>& ptc) |
||||
{ |
||||
int rotation_parameter_num = 9; |
||||
std::string token; |
||||
while(in.peek() == '#') std::getline(in, token); |
||||
|
||||
char listpath[1024], filepath[1024]; |
||||
strcpy(listpath, name); |
||||
char* ext = strstr(listpath, ".out"); |
||||
strcpy(ext, "-list.txt\0"); |
||||
|
||||
///////////////////////////////////
|
||||
std::ifstream listin(listpath); |
||||
if(!listin.is_open()) |
||||
{ |
||||
listin.close(); listin.clear(); |
||||
strcpy(ext, ".txt\0"); |
||||
listin.open(listpath); |
||||
} |
||||
if(!listin.is_open()) |
||||
{ |
||||
listin.close(); listin.clear(); |
||||
char * slash = strrchr(listpath, '/'); |
||||
if(slash == NULL) slash = strrchr(listpath, '\\'); |
||||
slash = slash ? slash + 1 : listpath; |
||||
strcpy(slash, "image_list.txt"); |
||||
listin.open(listpath); |
||||
} |
||||
if(listin) LOG_OUT() << "Using image list: " << listpath << '\n'; |
||||
|
||||
// read # of cameras
|
||||
int ncam = 0, npoint = 0, nproj = 0; |
||||
in >> ncam >> npoint; |
||||
if(ncam <= 1 || npoint <= 1) return false; |
||||
LOG_OUT() << ncam << " cameras; " << npoint << " 3D points;\n"; |
||||
|
||||
//read the camera parameters
|
||||
camera_data.resize(ncam); // allocate the camera data
|
||||
names.resize(ncam); |
||||
|
||||
bool det_checked = false; |
||||
for(int i = 0; i < ncam; ++i) |
||||
{ |
||||
float f, q[9], c[3], d[2]; |
||||
in >> f >> d[0] >> d[1]; |
||||
for(int j = 0; j < rotation_parameter_num; ++j) in >> q[j]; |
||||
in >> c[0] >> c[1] >> c[2]; |
||||
|
||||
camera_data[i].SetFocalLength(f); |
||||
camera_data[i].SetInvertedR9T(q, c); |
||||
camera_data[i].SetProjectionDistortion(d[0]); |
||||
|
||||
if(listin >> filepath && f != 0) |
||||
{ |
||||
names[i] = filepath; |
||||
std::getline(listin, token); |
||||
|
||||
if(!det_checked) |
||||
{ |
||||
float det = camera_data[i].GetRotationMatrixDeterminant(); |
||||
LOG_OUT() << "Check rotation matrix: " << det << '\n'; |
||||
det_checked = true; |
||||
} |
||||
}else |
||||
{ |
||||
names[i] = "unknown"; |
||||
} |
||||
} |
||||
|
||||
|
||||
//read image projections and 3D points.
|
||||
point_data.resize(npoint); |
||||
for(int i = 0; i < npoint; ++i) |
||||
{ |
||||
float pt[3]; int cc[3], npj; |
||||
in >> pt[0] >> pt[1] >> pt[2] |
||||
>> cc[0] >> cc[1] >> cc[2] >> npj; |
||||
for(int j = 0; j < npj; ++j) |
||||
{ |
||||
int cidx, fidx; float imx, imy; |
||||
in >> cidx >> fidx >> imx >> imy; |
||||
|
||||
camidx.push_back(cidx); //camera index
|
||||
ptidx.push_back(i); //point index
|
||||
|
||||
//add a measurement to the vector
|
||||
measurements.push_back(Point2D(imx, -imy)); |
||||
nproj ++; |
||||
} |
||||
point_data[i].SetPoint(pt[0], pt[1], pt[2]); |
||||
ptc.insert(ptc.end(), cc, cc + 3); |
||||
} |
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
LOG_OUT() << ncam << " cameras; " << npoint << " 3D points; " << nproj << " projections\n"; |
||||
return true; |
||||
} |
||||
|
||||
void SaveBundlerOut(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<std::string>& names, std::vector<int>& ptc) |
||||
{ |
||||
char listpath[1024]; strcpy(listpath, filename); |
||||
char* ext = strstr(listpath, ".out"); if(ext == NULL) return; |
||||
strcpy(ext, "-list.txt\0"); |
||||
|
||||
std::ofstream out(filename); |
||||
out << "# Bundle file v0.3\n"; |
||||
out << std::setprecision(12); //need enough precision
|
||||
out << camera_data.size() << " " << point_data.size() << '\n'; |
||||
|
||||
//save camera data
|
||||
for(size_t i = 0; i < camera_data.size(); ++i) |
||||
{ |
||||
float q[9], c[3]; |
||||
CameraT& ci = camera_data[i]; |
||||
out << ci.GetFocalLength() << ' ' << ci.GetProjectionDistortion() << " 0\n"; |
||||
ci.GetInvertedR9T(q, c); |
||||
for(int j = 0; j < 9; ++j) out << q[j] << (((j % 3) == 2)? '\n' : ' '); |
||||
out << c[0] << ' ' << c[1] << ' ' << c[2] << '\n'; |
||||
} |
||||
///
|
||||
for(size_t i = 0, j = 0; i < point_data.size(); ++i) |
||||
{ |
||||
int npj = 0, *ci = &ptc[i * 3]; Point3D& pt = point_data[i]; |
||||
while(j + npj < point_data.size() && ptidx[j + npj] == ptidx[j]) npj++; |
||||
///////////////////////////
|
||||
out << pt.xyz[0] << ' ' << pt.xyz[1] << ' ' << pt.xyz[2] << '\n'; |
||||
out << ci[0] << ' ' << ci[1] << ' ' << ci[2] << '\n'; |
||||
out << npj << ' '; |
||||
for(int k = 0; k < npj; ++k) out << camidx[j + k] << " 0 " |
||||
<< measurements[j + k].x << ' ' << -measurements[j + k].y << '\n'; |
||||
out << '\n'; j += npj; |
||||
} |
||||
|
||||
std::ofstream listout(listpath); |
||||
for(size_t i = 0; i < names.size(); ++i) listout << names[i] << '\n'; |
||||
} |
||||
|
||||
template<class CameraT, class Point3D> |
||||
bool LoadBundlerModel(std::ifstream& in, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx) |
||||
{ |
||||
// read bundle data from a file
|
||||
size_t ncam = 0, npt = 0, nproj = 0; |
||||
if(!(in >> ncam >> npt >> nproj)) return false; |
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
LOG_OUT() << ncam << " cameras; " << npt << " 3D points; " << nproj << " projections\n"; |
||||
|
||||
camera_data.resize(ncam); |
||||
point_data.resize(npt); |
||||
measurements.resize(nproj); |
||||
camidx.resize(nproj); |
||||
ptidx.resize(nproj); |
||||
|
||||
for(size_t i = 0; i < nproj; ++i) |
||||
{ |
||||
double x, y; int cidx, pidx; |
||||
in >> cidx >> pidx >> x >> y; |
||||
if(((size_t) pidx) == npt && camidx.size() > i) |
||||
{ |
||||
camidx.resize(i); |
||||
ptidx.resize(i); |
||||
measurements.resize(i); |
||||
LOG_OUT() << "Truncate measurements to " << i << '\n'; |
||||
}else if(((size_t) pidx) >= npt) |
||||
{ |
||||
continue; |
||||
}else |
||||
{ |
||||
camidx[i] = cidx; ptidx[i] = pidx; |
||||
measurements[i].SetPoint2D(x, -y); |
||||
} |
||||
} |
||||
|
||||
for(size_t i = 0; i < ncam; ++i) |
||||
{ |
||||
double p[9]; |
||||
for(int j = 0; j < 9; ++j) in >> p[j]; |
||||
CameraT& cam = camera_data[i]; |
||||
cam.SetFocalLength(p[6]); |
||||
cam.SetInvertedRT(p, p + 3); |
||||
cam.SetProjectionDistortion(p[7]); |
||||
} |
||||
|
||||
for(size_t i = 0; i < npt; ++i) |
||||
{ |
||||
double pt[3]; |
||||
in >> pt[0] >> pt[1] >> pt[2]; |
||||
point_data[i].SetPoint(pt); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
void SaveBundlerModel(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx) |
||||
{ |
||||
LOG_OUT() << "Saving model to " << filename << "...\n"; |
||||
std::ofstream out(filename); |
||||
out << std::setprecision(12); //need enough precision
|
||||
out << camera_data.size() << ' ' << point_data.size() << ' ' << measurements.size() << '\n'; |
||||
for(size_t i = 0; i < measurements.size(); ++i) |
||||
{ |
||||
out << camidx[i] << ' ' << ptidx[i] << ' ' << measurements[i].x << ' ' << -measurements[i].y << '\n'; |
||||
} |
||||
|
||||
for(size_t i = 0; i < camera_data.size(); ++i) |
||||
{ |
||||
CameraT& cam = camera_data[i]; |
||||
double r[3], t[3]; cam.GetInvertedRT(r, t); |
||||
out << r[0] << ' ' << r[1] << ' ' << r[2] << ' ' |
||||
<< t[0] << ' ' << t[1] << ' ' << t[2] << ' ' << cam.f |
||||
<< ' ' << cam.GetProjectionDistortion() << " 0\n"; |
||||
} |
||||
|
||||
for(size_t i = 0; i < point_data.size(); ++i) |
||||
{ |
||||
Point3D& pt = point_data[i]; |
||||
out << pt.xyz[0] << ' ' << pt.xyz[1] << ' ' << pt.xyz[2] << '\n'; |
||||
} |
||||
} |
||||
|
||||
bool LoadModelFile(const char* name, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<std::string>& names, std::vector<int>& ptc) |
||||
{ |
||||
if(name == NULL)return false; |
||||
std::ifstream in(name); |
||||
|
||||
LOG_OUT() << "Loading cameras/points: " << name <<"\n" ; |
||||
if(!in.is_open()) return false; |
||||
|
||||
if(strstr(name, ".nvm"))return LoadNVM(in, camera_data, point_data, measurements, ptidx, camidx, names, ptc); |
||||
else if(strstr(name, ".out")) return LoadBundlerOut(name, in, camera_data, point_data, measurements, ptidx, camidx, names, ptc); |
||||
else return LoadBundlerModel(in, camera_data, point_data, measurements, ptidx, camidx); |
||||
} |
||||
|
||||
|
||||
float random_ratio(float percent) |
||||
{ |
||||
return (rand() % 101 - 50) * 0.02f * percent + 1.0f; |
||||
} |
||||
|
||||
void AddNoise(std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, float percent) |
||||
{ |
||||
std::srand((unsigned int) time(NULL)); |
||||
for(size_t i = 0; i < camera_data.size(); ++i) |
||||
{ |
||||
camera_data[i].f *= random_ratio(percent); |
||||
camera_data[i].t[0] *= random_ratio(percent); |
||||
camera_data[i].t[1] *= random_ratio(percent); |
||||
camera_data[i].t[2] *= random_ratio(percent); |
||||
double e[3]; |
||||
camera_data[i].GetRodriguesRotation(e); |
||||
e[0] *= random_ratio(percent); |
||||
e[1] *= random_ratio(percent); |
||||
e[2] *= random_ratio(percent); |
||||
camera_data[i].SetRodriguesRotation(e); |
||||
} |
||||
|
||||
for(size_t i = 0; i < point_data.size(); ++i) |
||||
{ |
||||
point_data[i].xyz[0] *= random_ratio(percent); |
||||
point_data[i].xyz[1] *= random_ratio(percent); |
||||
point_data[i].xyz[2] *= random_ratio(percent); |
||||
} |
||||
} |
||||
|
||||
void AddStableNoise(std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
const std::vector<int>& ptidx, const std::vector<int>& camidx, float percent) |
||||
{ |
||||
///
|
||||
std::srand((unsigned int) time(NULL)); |
||||
//do not modify the visibility status..
|
||||
std::vector<float> zz0(ptidx.size()); |
||||
std::vector<CameraT> backup = camera_data; |
||||
std::vector<float> vx(point_data.size()), vy(point_data.size()), vz(point_data.size()); |
||||
for(size_t i = 0; i < point_data.size(); ++i) |
||||
{ |
||||
Point3D& pt = point_data[i]; |
||||
vx[i] = pt.xyz[0]; |
||||
vy[i] = pt.xyz[1]; |
||||
vz[i] = pt.xyz[2]; |
||||
} |
||||
|
||||
//find out the median location of all the 3D points.
|
||||
size_t median_idx = point_data.size() / 2; |
||||
|
||||
std::nth_element(vx.begin(), vx.begin() + median_idx, vx.end()); |
||||
std::nth_element(vy.begin(), vy.begin() + median_idx, vy.end()); |
||||
std::nth_element(vz.begin(), vz.begin() + median_idx, vz.end()); |
||||
float cx = vx[median_idx], cy = vy[median_idx], cz = vz[median_idx]; |
||||
|
||||
for(size_t i = 0; i < ptidx.size(); ++i) |
||||
{ |
||||
CameraT& cam = camera_data[camidx[i]]; |
||||
Point3D& pt = point_data[ptidx[i]]; |
||||
zz0[i] = cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2]; |
||||
} |
||||
|
||||
std::vector<float> z2 = zz0; median_idx = ptidx.size() / 2; |
||||
std::nth_element(z2.begin(), z2.begin() + median_idx, z2.end()); |
||||
float mz = z2[median_idx]; // median depth
|
||||
float dist_noise_base = mz * 0.2f; |
||||
|
||||
/////////////////////////////////////////////////
|
||||
//modify points first..
|
||||
for(size_t i = 0; i < point_data.size(); ++i) |
||||
{ |
||||
Point3D& pt = point_data[i]; |
||||
pt.xyz[0] = pt.xyz[0] - cx + dist_noise_base * random_ratio(percent); |
||||
pt.xyz[1] = pt.xyz[1] - cy + dist_noise_base * random_ratio(percent); |
||||
pt.xyz[2] = pt.xyz[2] - cz + dist_noise_base * random_ratio(percent); |
||||
} |
||||
|
||||
std::vector<bool> need_modification(camera_data.size(), true); |
||||
int invalid_count = 0, modify_iteration = 1; |
||||
|
||||
do |
||||
{ |
||||
if(invalid_count) LOG_OUT() << "NOTE" << std::setw(2) << modify_iteration |
||||
<< ": modify " << invalid_count << " camera to fix visibility\n"; |
||||
|
||||
//////////////////////////////////////////////////////
|
||||
for(size_t i = 0; i < camera_data.size(); ++i) |
||||
{ |
||||
if(!need_modification[i])continue; |
||||
CameraT & cam = camera_data[i]; |
||||
double e[3], c[3]; cam = backup[i]; |
||||
cam.f *= random_ratio(percent); |
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
cam.GetCameraCenter(c); |
||||
c[0] = c[0] - cx + dist_noise_base * random_ratio(percent); |
||||
c[1] = c[1] - cy + dist_noise_base * random_ratio(percent); |
||||
c[2] = c[2] - cz + dist_noise_base * random_ratio(percent); |
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
cam.GetRodriguesRotation(e); |
||||
e[0] *= random_ratio(percent); |
||||
e[1] *= random_ratio(percent); |
||||
e[2] *= random_ratio(percent); |
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
cam.SetRodriguesRotation(e); |
||||
cam.SetCameraCenterAfterRotation(c); |
||||
} |
||||
std::vector<bool> invalidc(camera_data.size(), false); |
||||
|
||||
invalid_count = 0; |
||||
for(size_t i = 0; i < ptidx.size(); ++i) |
||||
{ |
||||
int cid = camidx[i]; |
||||
if(need_modification[cid] ==false) continue; |
||||
if(invalidc[cid])continue; |
||||
CameraT& cam = camera_data[cid]; |
||||
Point3D& pt = point_data[ptidx[i]]; |
||||
float z = cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2]; |
||||
if (z * zz0[i] > 0)continue; |
||||
if (zz0[i] == 0 && z > 0) continue; |
||||
invalid_count++; |
||||
invalidc[cid] = true; |
||||
} |
||||
|
||||
need_modification = invalidc; |
||||
modify_iteration++; |
||||
|
||||
}while(invalid_count && modify_iteration < 20); |
||||
|
||||
} |
||||
|
||||
void ExamineVisiblity(const char* input_filename ) |
||||
{ |
||||
|
||||
//////////////
|
||||
std::vector<CameraD> camera_data; |
||||
std::vector<Point3B> point_data; |
||||
std::vector<int> ptidx, camidx; |
||||
std::vector<Point2D> measurements; |
||||
std::ifstream in (input_filename); |
||||
LoadBundlerModel(in, camera_data, point_data, measurements, ptidx, camidx); |
||||
|
||||
////////////////
|
||||
int count = 0; double d1 = 100, d2 = 100; |
||||
LOG_OUT() << "checking visibility...\n"; |
||||
std::vector<double> zz(ptidx.size()); |
||||
for(size_t i = 0; i < ptidx.size(); ++i) |
||||
{ |
||||
CameraD& cam = camera_data[camidx[i]]; |
||||
Point3B& pt = point_data[ptidx[i]]; |
||||
double dz = cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2]; |
||||
//double dx = cam.m[0][0] * pt.xyz[0] + cam.m[0][1] * pt.xyz[1] + cam.m[0][2] * pt.xyz[2] + cam.t[0];
|
||||
//double dy = cam.m[1][0] * pt.xyz[0] + cam.m[1][1] * pt.xyz[1] + cam.m[1][2] * pt.xyz[2] + cam.t[1];
|
||||
|
||||
////////////////////////////////////////
|
||||
float c[3]; cam.GetCameraCenter(c); |
||||
|
||||
CameraT camt; camt.SetCameraT(cam); |
||||
Point3D ptt; ptt.SetPoint(pt.xyz); |
||||
double fz = camt.m[2][0] * ptt.xyz[0] + camt.m[2][1] * ptt.xyz[1] + camt.m[2][2] * ptt.xyz[2] + camt.t[2]; |
||||
double fz2 = camt.m[2][0] * (ptt.xyz[0] - c[0]) + camt.m[2][1] * (ptt.xyz[1] - c[1]) |
||||
+ camt.m[2][2] * (ptt.xyz[2] - c[2]); |
||||
|
||||
|
||||
//if(dz == 0 && fz == 0) continue;
|
||||
|
||||
if(dz * fz <= 0 || fz == 0) |
||||
{ |
||||
LOG_OUT() << "cam " << camidx[i] //<<// "; dx = " << dx << "; dy = " << dy
|
||||
<< "; double: " << dz << "; float " << fz << "; float2 " << fz2 << "\n"; |
||||
//LOG_OUT() << cam.m[2][0] << " "<<cam.m[2][1]<< " " << cam.m[2][2] << " "<<cam.t[2] << "\n";
|
||||
//LOG_OUT() << camt.m[2][0] << " "<<camt.m[2][1]<< " " << camt.m[2][2] << " "<<camt.t[2] << "\n";
|
||||
//LOG_OUT() << cam.m[2][0] - camt.m[2][0] << " " <<cam.m[2][1] - camt.m[2][1]<< " "
|
||||
// << cam.m[2][2] - camt.m[2][2] << " " <<cam.t[2] - camt.t[2]<< "\n";
|
||||
} |
||||
|
||||
zz[i] = dz; |
||||
d1 = std::min(fabs(dz), d1); |
||||
d2 = std::min(fabs(fz), d2); |
||||
} |
||||
|
||||
LOG_OUT() << count << " points moved to wrong side " |
||||
<< d1 << ", " << d2 <<"\n"; |
||||
} |
||||
|
||||
bool RemoveInvisiblePoints( std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<Point2D>& measurements, std::vector<std::string>& names, std::vector<int>& ptc) |
||||
{ |
||||
std::vector<float> zz(ptidx.size()); |
||||
for(size_t i = 0; i < ptidx.size(); ++i) |
||||
{ |
||||
CameraT& cam = camera_data[camidx[i]]; |
||||
Point3D& pt = point_data[ptidx[i]]; |
||||
zz[i] = cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2]; |
||||
} |
||||
size_t median_idx = ptidx.size() / 2; |
||||
std::nth_element(zz.begin(), zz.begin() + median_idx, zz.end()); |
||||
float dist_threshold = zz[median_idx] * 0.001f; |
||||
|
||||
//keep removing 3D points. until all of them are infront of the cameras..
|
||||
std::vector<bool> pmask(point_data.size(), true); |
||||
int points_removed = 0; |
||||
for(size_t i = 0; i < ptidx.size(); ++i) |
||||
{ |
||||
int cid = camidx[i], pid = ptidx[i]; |
||||
if(!pmask[pid])continue; |
||||
CameraT& cam = camera_data[cid]; |
||||
Point3D& pt = point_data[pid]; |
||||
bool visible = (cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2] > dist_threshold); |
||||
pmask[pid] = visible; //this point should be removed
|
||||
if(!visible) points_removed++; |
||||
} |
||||
if(points_removed == 0) return false; |
||||
std::vector<int> cv(camera_data.size(), 0); |
||||
//should any cameras be removed ?
|
||||
int min_observation = 20; //cameras should see at least 20 points
|
||||
|
||||
do |
||||
{ |
||||
//count visible points for each camera
|
||||
std::fill(cv.begin(), cv.end(), 0); |
||||
for(size_t i = 0; i < ptidx.size(); ++i) |
||||
{ |
||||
int cid = camidx[i], pid = ptidx[i]; |
||||
if(pmask[pid]) cv[cid]++; |
||||
} |
||||
|
||||
//check if any more points should be removed
|
||||
std::vector<int> pv(point_data.size(), 0); |
||||
for(size_t i = 0; i < ptidx.size(); ++i) |
||||
{ |
||||
int cid = camidx[i], pid = ptidx[i]; |
||||
if(!pmask[pid]) continue; //point already removed
|
||||
if(cv[cid] < min_observation) //this camera shall be removed.
|
||||
{ |
||||
///
|
||||
}else |
||||
{ |
||||
pv[pid]++; |
||||
} |
||||
} |
||||
|
||||
points_removed = 0; |
||||
for(size_t i = 0; i < point_data.size(); ++i) |
||||
{ |
||||
if(pmask[i] == false) continue; |
||||
if(pv[i] >= 2) continue; |
||||
pmask[i] = false; |
||||
points_removed++; |
||||
} |
||||
}while(points_removed > 0); |
||||
|
||||
////////////////////////////////////
|
||||
std::vector<bool> cmask(camera_data.size(), true); |
||||
for(size_t i = 0; i < camera_data.size(); ++i) cmask[i] = cv[i] >= min_observation; |
||||
////////////////////////////////////////////////////////
|
||||
|
||||
std::vector<int> cidx(camera_data.size()); |
||||
std::vector<int> pidx(point_data.size()); |
||||
|
||||
|
||||
|
||||
|
||||
///modified model.
|
||||
std::vector<CameraT> camera_data2; |
||||
std::vector<Point3D> point_data2; |
||||
std::vector<int> ptidx2; |
||||
std::vector<int> camidx2; |
||||
std::vector<Point2D> measurements2; |
||||
std::vector<std::string> names2; |
||||
std::vector<int> ptc2; |
||||
|
||||
|
||||
//
|
||||
if(names.size() < camera_data.size()) names.resize(camera_data.size(),std::string("unknown")); |
||||
if(ptc.size() < 3 * point_data.size()) ptc.resize(point_data.size() * 3, 0); |
||||
|
||||
//////////////////////////////
|
||||
int new_camera_count = 0, new_point_count = 0; |
||||
for(size_t i = 0; i < camera_data.size(); ++i) |
||||
{ |
||||
if(!cmask[i])continue; |
||||
camera_data2.push_back(camera_data[i]); |
||||
names2.push_back(names[i]); |
||||
cidx[i] = new_camera_count++; |
||||
} |
||||
|
||||
for(size_t i = 0; i < point_data.size(); ++i) |
||||
{ |
||||
if(!pmask[i])continue; |
||||
point_data2.push_back(point_data[i]); |
||||
ptc.push_back(ptc[i]); |
||||
pidx[i] = new_point_count++; |
||||
} |
||||
|
||||
int new_observation_count = 0; |
||||
for(size_t i = 0; i < ptidx.size(); ++i) |
||||
{ |
||||
int pid = ptidx[i], cid = camidx[i]; |
||||
if(!pmask[pid] || ! cmask[cid]) continue; |
||||
ptidx2.push_back(pidx[pid]); |
||||
camidx2.push_back(cidx[cid]); |
||||
measurements2.push_back(measurements[i]); |
||||
new_observation_count++; |
||||
} |
||||
|
||||
LOG_OUT() << "NOTE: removing " << (camera_data.size() - new_camera_count) << " cameras; "<< (point_data.size() - new_point_count) |
||||
<< " 3D Points; " << (measurements.size() - new_observation_count) << " Observations;\n"; |
||||
|
||||
camera_data2.swap(camera_data); names2.swap(names); |
||||
point_data2.swap(point_data); ptc2.swap(ptc); |
||||
ptidx2.swap(ptidx); camidx2.swap(camidx); |
||||
measurements2.swap(measurements); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void SaveModelFile(const char* outpath, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, |
||||
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx, |
||||
std::vector<std::string>& names, std::vector<int>& ptc) |
||||
{ |
||||
if(outpath == NULL) return; |
||||
if(strstr(outpath, ".nvm")) |
||||
SaveNVM(outpath, camera_data, point_data, measurements, ptidx, camidx, names, ptc); |
||||
else if(strstr(outpath, ".out")) |
||||
SaveBundlerOut(outpath, camera_data, point_data, measurements, ptidx, camidx, names, ptc); |
||||
else |
||||
SaveBundlerModel(outpath, camera_data, point_data, measurements, ptidx, camidx); |
||||
} |
||||
|
||||
} // namespace PBA
|
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(ReconstructMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS ReconstructMesh |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,498 @@
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* ReconstructMesh.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
#include <boost/program_options.hpp> |
||||
|
||||
using namespace MVS; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("ReconstructMesh") |
||||
|
||||
// uncomment to enable multi-threading based on OpenMP
|
||||
#ifdef _USE_OPENMP |
||||
#define RECMESH_USE_OPENMP |
||||
#endif |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace OPT { |
||||
String strInputFileName; |
||||
String strPointCloudFileName; |
||||
String strOutputFileName; |
||||
String strMeshFileName; |
||||
String strImportROIFileName; |
||||
String strImagePointsFileName; |
||||
bool bMeshExport; |
||||
float fDistInsert; |
||||
bool bUseOnlyROI; |
||||
bool bUseConstantWeight; |
||||
bool bUseFreeSpaceSupport; |
||||
float fThicknessFactor; |
||||
float fQualityFactor; |
||||
float fDecimateMesh; |
||||
unsigned nTargetFaceNum; |
||||
float fRemoveSpurious; |
||||
bool bRemoveSpikes; |
||||
unsigned nCloseHoles; |
||||
unsigned nSmoothMesh; |
||||
float fEdgeLength; |
||||
bool bCrop2ROI; |
||||
float fBorderROI; |
||||
float fSplitMaxArea; |
||||
unsigned nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
String strExportType; |
||||
String strConfigFileName; |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "produce this help message") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("export-type", boost::program_options::value<std::string>(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply or obj)") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
#ifdef _USE_CUDA |
||||
("cuda-device", boost::program_options::value(&CUDA::desiredDeviceID)->default_value(-1), "CUDA device number to be used to reconstruct the mesh (-2 - CPU processing, -1 - best GPU, >=0 - device index)") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
boost::program_options::options_description config_main("Reconstruct options"); |
||||
config_main.add_options() |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list") |
||||
("pointcloud-file,p", boost::program_options::value<std::string>(&OPT::strPointCloudFileName), "dense point-cloud with views file name to reconstruct (overwrite existing point-cloud)") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh") |
||||
("min-point-distance,d", boost::program_options::value(&OPT::fDistInsert)->default_value(2.5f), "minimum distance in pixels between the projection of two 3D points to consider them different while triangulating (0 - disabled)") |
||||
("integrate-only-roi", boost::program_options::value(&OPT::bUseOnlyROI)->default_value(false), "use only the points inside the ROI") |
||||
("constant-weight", boost::program_options::value(&OPT::bUseConstantWeight)->default_value(true), "considers all view weights 1 instead of the available weight") |
||||
("free-space-support,f", boost::program_options::value(&OPT::bUseFreeSpaceSupport)->default_value(false), "exploits the free-space support in order to reconstruct weakly-represented surfaces") |
||||
("thickness-factor", boost::program_options::value(&OPT::fThicknessFactor)->default_value(1.f), "multiplier adjusting the minimum thickness considered during visibility weighting") |
||||
("quality-factor", boost::program_options::value(&OPT::fQualityFactor)->default_value(1.f), "multiplier adjusting the quality weight considered during graph-cut") |
||||
; |
||||
boost::program_options::options_description config_clean("Clean options"); |
||||
config_clean.add_options() |
||||
("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(1.f), "decimation factor in range (0..1] to be applied to the reconstructed surface (1 - disabled)") |
||||
("target-face-num", boost::program_options::value(&OPT::nTargetFaceNum)->default_value(0), "target number of faces to be applied to the reconstructed surface. (0 - disabled)") |
||||
("remove-spurious", boost::program_options::value(&OPT::fRemoveSpurious)->default_value(20.f), "spurious factor for removing faces with too long edges or isolated components (0 - disabled)") |
||||
("remove-spikes", boost::program_options::value(&OPT::bRemoveSpikes)->default_value(true), "flag controlling the removal of spike faces") |
||||
("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the reconstructed surface (0 - disabled)") |
||||
("smooth", boost::program_options::value(&OPT::nSmoothMesh)->default_value(2), "number of iterations to smooth the reconstructed surface (0 - disabled)") |
||||
("edge-length", boost::program_options::value(&OPT::fEdgeLength)->default_value(0.f), "remesh such that the average edge length is this size (0 - disabled)") |
||||
("roi-border", boost::program_options::value(&OPT::fBorderROI)->default_value(0), "add a border to the region-of-interest when cropping the scene (0 - disabled, >0 - percentage, <0 - absolute)") |
||||
("crop-to-roi", boost::program_options::value(&OPT::bCrop2ROI)->default_value(true), "crop scene using the region-of-interest") |
||||
; |
||||
|
||||
// hidden options, allowed both on command line and
|
||||
// in config file, but will not be shown to the user
|
||||
boost::program_options::options_description hidden("Hidden options"); |
||||
hidden.add_options() |
||||
("mesh-file", boost::program_options::value<std::string>(&OPT::strMeshFileName), "mesh file name to clean (skips the reconstruction step)") |
||||
("mesh-export", boost::program_options::value(&OPT::bMeshExport)->default_value(false), "just export the mesh contained in loaded project") |
||||
("split-max-area", boost::program_options::value(&OPT::fSplitMaxArea)->default_value(0.f), "maximum surface area that a sub-mesh can contain (0 - disabled)") |
||||
("import-roi-file", boost::program_options::value<std::string>(&OPT::strImportROIFileName), "ROI file name to be imported into the scene") |
||||
("image-points-file", boost::program_options::value<std::string>(&OPT::strImagePointsFileName), "input filename containing the list of points from an image to project on the mesh (optional)") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config_main).add(config_clean).add(hidden); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config_main).add(config_clean).add(hidden); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
// initialize the log file
|
||||
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str()); |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidPath(OPT::strInputFileName); |
||||
if (OPT::vm.count("help") || OPT::strInputFileName.empty()) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config_main).add(config_clean); |
||||
GET_LOG() << visible; |
||||
} |
||||
if (OPT::strInputFileName.empty()) |
||||
return false; |
||||
OPT::strExportType = OPT::strExportType.ToLower() == _T("obj") ? _T(".obj") : _T(".ply"); |
||||
|
||||
// initialize optional options
|
||||
Util::ensureValidPath(OPT::strPointCloudFileName); |
||||
Util::ensureValidPath(OPT::strOutputFileName); |
||||
Util::ensureValidPath(OPT::strImportROIFileName); |
||||
Util::ensureValidPath(OPT::strImagePointsFileName); |
||||
Util::ensureValidPath(OPT::strMeshFileName); |
||||
if (OPT::strPointCloudFileName.empty() && (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS) |
||||
OPT::strPointCloudFileName = Util::getFileFullName(OPT::strInputFileName) + _T(".ply"); |
||||
if (OPT::strOutputFileName.empty()) |
||||
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + _T("_mesh.mvs"); |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
// export 3D coordinates corresponding to 2D coordinates provided by inputFileName:
|
||||
// parse image point list; first line is the name of the image to project,
|
||||
// each consequent line store the xy coordinates to project:
|
||||
// <image-name> <number-of-points>
|
||||
// <x-coord1> <y-coord1>
|
||||
// <x-coord2> <y-coord2>
|
||||
// ...
|
||||
//
|
||||
// for example:
|
||||
// N01.JPG 3
|
||||
// 3090 2680
|
||||
// 3600 2100
|
||||
// 3640 2190
|
||||
bool Export3DProjections(Scene& scene, const String& inputFileName) { |
||||
SML smlPointList(_T("ImagePoints")); |
||||
smlPointList.Load(inputFileName); |
||||
ASSERT(smlPointList.GetArrChildren().size() <= 1); |
||||
IDX idx(0); |
||||
|
||||
// read image name
|
||||
size_t argc; |
||||
CAutoPtrArr<LPSTR> argv; |
||||
while (true) { |
||||
argv = Util::CommandLineToArgvA(smlPointList.GetValue(idx).val, argc); |
||||
if (argc > 0 && argv[0][0] != _T('#')) |
||||
break; |
||||
if (++idx == smlPointList.size()) |
||||
return false; |
||||
} |
||||
if (argc < 2) |
||||
return false; |
||||
String imgName(argv[0]); |
||||
IIndex imgID(NO_ID); |
||||
for (const Image& imageData : scene.images) { |
||||
if (!imageData.IsValid()) |
||||
continue; |
||||
if (imageData.name.substr(imageData.name.size() - imgName.size()) == imgName) { |
||||
imgID = imageData.ID; |
||||
break; |
||||
} |
||||
} |
||||
if (imgID == NO_ID) { |
||||
VERBOSE("Unable to find image named: %s", imgName.c_str()); |
||||
return false; |
||||
} |
||||
|
||||
// read image points
|
||||
std::vector<Point2f> imagePoints; |
||||
while (++idx != smlPointList.size()) { |
||||
// parse image element
|
||||
const String& line(smlPointList.GetValue(idx).val); |
||||
argv = Util::CommandLineToArgvA(line, argc); |
||||
if (argc > 0 && argv[0][0] == _T('#')) |
||||
continue; |
||||
if (argc < 2) { |
||||
VERBOSE("Invalid image coordinates: %s", line.c_str()); |
||||
continue; |
||||
} |
||||
const Point2f pt( |
||||
String::FromString<float>(argv[0], -1), |
||||
String::FromString<float>(argv[1], -1)); |
||||
if (pt.x > 0 && pt.y > 0) |
||||
imagePoints.emplace_back(pt); |
||||
} |
||||
if (imagePoints.empty()) { |
||||
VERBOSE("Unable to read image points from: %s", imgName.c_str()); |
||||
return false; |
||||
} |
||||
|
||||
// prepare output file
|
||||
String outFileName(Util::insertBeforeFileExt(inputFileName, "_3D")); |
||||
File oStream(outFileName, File::WRITE, File::CREATE | File::TRUNCATE); |
||||
if (!oStream.isOpen()) { |
||||
VERBOSE("Unable to open output file: %s", outFileName.c_str()); |
||||
return false; |
||||
} |
||||
|
||||
// print image name
|
||||
oStream.print("%s %u\n", imgName.c_str(), imagePoints.size()); |
||||
|
||||
// init mesh octree
|
||||
const Mesh::Octree octree(scene.mesh.vertices, [](Mesh::Octree::IDX_TYPE size, Mesh::Octree::Type /*radius*/) { |
||||
return size > 256; |
||||
}); |
||||
scene.mesh.ListIncidenteFaces(); |
||||
|
||||
// save 3D coord in the output file
|
||||
const Image& imgToExport = scene.images[imgID]; |
||||
for (const Point2f& pt : imagePoints) { |
||||
// define ray from camera center to each x,y image coord
|
||||
const Ray3 ray(imgToExport.camera.C, normalized(imgToExport.camera.RayPoint<REAL>(pt))); |
||||
// find ray intersection with the mesh
|
||||
const IntersectRayMesh intRay(octree, ray, scene.mesh); |
||||
if (intRay.pick.IsValid()) { |
||||
const Point3d ptHit(ray.GetPoint(intRay.pick.dist)); |
||||
oStream.print("%.7f %.7f %.7f\n", ptHit.x, ptHit.y, ptHit.z); |
||||
} else |
||||
oStream.print("NA\n"); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
Scene scene(OPT::nMaxThreads); |
||||
// load project
|
||||
const Scene::SCENE_TYPE sceneType(scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName), |
||||
OPT::fSplitMaxArea > 0 || OPT::fDecimateMesh < 1 || OPT::nTargetFaceNum > 0 || !OPT::strImportROIFileName.empty())); |
||||
if (sceneType == Scene::SCENE_NA) |
||||
return EXIT_FAILURE; |
||||
if (!OPT::strPointCloudFileName.empty() && (File::isFile(MAKE_PATH_SAFE(OPT::strPointCloudFileName)) ? |
||||
!scene.pointcloud.Load(MAKE_PATH_SAFE(OPT::strPointCloudFileName)) : |
||||
!scene.pointcloud.IsValid())) { |
||||
VERBOSE("error: cannot load point-cloud file"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName))) { |
||||
VERBOSE("error: cannot load mesh file"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))); |
||||
if (OPT::fSplitMaxArea > 0) { |
||||
// split mesh using max-area constraint
|
||||
Mesh::FacesChunkArr chunks; |
||||
if (scene.mesh.Split(chunks, OPT::fSplitMaxArea)) |
||||
scene.mesh.Save(chunks, baseFileName); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
|
||||
if (!OPT::strImportROIFileName.empty()) { |
||||
std::ifstream fs(MAKE_PATH_SAFE(OPT::strImportROIFileName)); |
||||
if (!fs) |
||||
return EXIT_FAILURE; |
||||
fs >> scene.obb; |
||||
if (OPT::bCrop2ROI && !scene.mesh.IsEmpty() && !scene.IsValid()) { |
||||
TD_TIMER_START(); |
||||
const size_t numVertices = scene.mesh.vertices.size(); |
||||
const size_t numFaces = scene.mesh.faces.size(); |
||||
scene.mesh.RemoveFacesOutside(scene.obb); |
||||
VERBOSE("Mesh trimmed to ROI: %u vertices and %u faces removed (%s)", |
||||
numVertices-scene.mesh.vertices.size(), numFaces-scene.mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); |
||||
scene.mesh.Save(baseFileName+OPT::strExportType); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
} |
||||
|
||||
if (!OPT::strImagePointsFileName.empty() && !scene.mesh.IsEmpty()) { |
||||
Export3DProjections(scene, MAKE_PATH_SAFE(OPT::strImagePointsFileName)); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
|
||||
if (OPT::bMeshExport) { |
||||
// check there is a mesh to export
|
||||
if (scene.mesh.IsEmpty()) |
||||
return EXIT_FAILURE; |
||||
// save mesh
|
||||
const String fileName(MAKE_PATH_SAFE(OPT::strOutputFileName)); |
||||
scene.mesh.Save(fileName); |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
if (VERBOSITY_LEVEL > 2) |
||||
scene.ExportCamerasMLP(baseFileName+_T(".mlp"), fileName); |
||||
#endif |
||||
} else { |
||||
const OBB3f initialOBB(scene.obb); |
||||
if (OPT::fBorderROI > 0) |
||||
scene.obb.EnlargePercent(OPT::fBorderROI); |
||||
else if (OPT::fBorderROI < 0) |
||||
scene.obb.Enlarge(-OPT::fBorderROI); |
||||
if (OPT::strMeshFileName.empty() && scene.mesh.IsEmpty()) { |
||||
// reset image resolution to the original size and
|
||||
// make sure the image neighbors are initialized before deleting the point-cloud
|
||||
#ifdef RECMESH_USE_OPENMP |
||||
bool bAbort(false); |
||||
#pragma omp parallel for |
||||
for (int_t idx=0; idx<(int_t)scene.images.size(); ++idx) { |
||||
#pragma omp flush (bAbort) |
||||
if (bAbort) |
||||
continue; |
||||
const uint32_t idxImage((uint32_t)idx); |
||||
#else |
||||
FOREACH(idxImage, scene.images) { |
||||
#endif |
||||
Image& imageData = scene.images[idxImage]; |
||||
if (!imageData.IsValid()) |
||||
continue; |
||||
// reset image resolution
|
||||
if (!imageData.ReloadImage(0, false)) { |
||||
#ifdef RECMESH_USE_OPENMP |
||||
bAbort = true; |
||||
#pragma omp flush (bAbort) |
||||
continue; |
||||
#else |
||||
return EXIT_FAILURE; |
||||
#endif |
||||
} |
||||
imageData.UpdateCamera(scene.platforms); |
||||
// select neighbor views
|
||||
if (imageData.neighbors.empty()) { |
||||
IndexArr points; |
||||
scene.SelectNeighborViews(idxImage, points); |
||||
} |
||||
} |
||||
#ifdef RECMESH_USE_OPENMP |
||||
if (bAbort) |
||||
return EXIT_FAILURE; |
||||
#endif |
||||
// reconstruct a coarse mesh from the given point-cloud
|
||||
TD_TIMER_START(); |
||||
if (OPT::bUseConstantWeight) |
||||
scene.pointcloud.pointWeights.Release(); |
||||
if (!scene.ReconstructMesh(OPT::fDistInsert, OPT::bUseFreeSpaceSupport, OPT::bUseOnlyROI, 4, OPT::fThicknessFactor, OPT::fQualityFactor)) |
||||
return EXIT_FAILURE; |
||||
VERBOSE("Mesh reconstruction completed: %u vertices, %u faces (%s)", scene.mesh.vertices.GetSize(), scene.mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str()); |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
if (VERBOSITY_LEVEL > 2) { |
||||
// dump raw mesh
|
||||
scene.mesh.Save(baseFileName+_T("_raw")+OPT::strExportType); |
||||
} |
||||
#endif |
||||
} else if (!OPT::strMeshFileName.empty()) { |
||||
// load existing mesh to clean
|
||||
scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName)); |
||||
} |
||||
|
||||
// clean the mesh
|
||||
if (OPT::bCrop2ROI && scene.IsBounded()) { |
||||
TD_TIMER_START(); |
||||
const size_t numVertices = scene.mesh.vertices.size(); |
||||
const size_t numFaces = scene.mesh.faces.size(); |
||||
scene.mesh.RemoveFacesOutside(scene.obb); |
||||
VERBOSE("Mesh trimmed to ROI: %u vertices and %u faces removed (%s)", |
||||
numVertices-scene.mesh.vertices.size(), numFaces-scene.mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); |
||||
} |
||||
const float fDecimate(OPT::nTargetFaceNum ? static_cast<float>(OPT::nTargetFaceNum) / scene.mesh.faces.size() : OPT::fDecimateMesh); |
||||
scene.mesh.Clean(fDecimate, OPT::fRemoveSpurious, OPT::bRemoveSpikes, OPT::nCloseHoles, OPT::nSmoothMesh, OPT::fEdgeLength, false); |
||||
scene.mesh.Clean(1.f, 0.f, OPT::bRemoveSpikes, OPT::nCloseHoles, 0u, 0.f, false); // extra cleaning trying to close more holes
|
||||
scene.mesh.Clean(1.f, 0.f, false, 0u, 0u, 0.f, true); // extra cleaning to remove non-manifold problems created by closing holes
|
||||
scene.obb = initialOBB; |
||||
|
||||
// save the final mesh
|
||||
scene.mesh.Save(baseFileName+OPT::strExportType); |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
if (VERBOSITY_LEVEL > 2) |
||||
scene.ExportCamerasMLP(baseFileName+_T(".mlp"), baseFileName+OPT::strExportType); |
||||
#endif |
||||
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS || sceneType != Scene::SCENE_INTERFACE) |
||||
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
} |
||||
|
||||
if (!OPT::strImagePointsFileName.empty()) { |
||||
Export3DProjections(scene, MAKE_PATH_SAFE(OPT::strImagePointsFileName)); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(RefineMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS RefineMesh |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,266 @@
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* RefineMesh.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
#include <boost/program_options.hpp> |
||||
|
||||
using namespace MVS; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("RefineMesh") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace OPT { |
||||
String strInputFileName; |
||||
String strMeshFileName; |
||||
String strOutputFileName; |
||||
unsigned nResolutionLevel; |
||||
unsigned nMinResolution; |
||||
unsigned nMaxViews; |
||||
float fDecimateMesh; |
||||
unsigned nCloseHoles; |
||||
unsigned nEnsureEdgeSize; |
||||
unsigned nScales; |
||||
float fScaleStep; |
||||
unsigned nReduceMemory; |
||||
unsigned nAlternatePair; |
||||
float fRegularityWeight; |
||||
float fRatioRigidityElasticity; |
||||
unsigned nMaxFaceArea; |
||||
float fPlanarVertexRatio; |
||||
float fGradientStep; |
||||
unsigned nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
String strExportType; |
||||
String strConfigFileName; |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "produce this help message") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("export-type", boost::program_options::value<std::string>(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply or obj)") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
#ifdef _USE_CUDA |
||||
("cuda-device", boost::program_options::value(&CUDA::desiredDeviceID)->default_value(-2), "CUDA device number to be used for mesh refinement (-2 - CPU processing, -1 - best GPU, >=0 - device index)") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
boost::program_options::options_description config("Refine options"); |
||||
config.add_options() |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list") |
||||
("mesh-file,m", boost::program_options::value<std::string>(&OPT::strMeshFileName), "mesh file name to refine (overwrite existing mesh)") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh") |
||||
("resolution-level", boost::program_options::value(&OPT::nResolutionLevel)->default_value(0), "how many times to scale down the images before mesh refinement") |
||||
("min-resolution", boost::program_options::value(&OPT::nMinResolution)->default_value(640), "do not scale images lower than this resolution") |
||||
("max-views", boost::program_options::value(&OPT::nMaxViews)->default_value(8), "maximum number of neighbor images used to refine the mesh") |
||||
("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(0.f), "decimation factor in range [0..1] to be applied to the input surface before refinement (0 - auto, 1 - disabled)") |
||||
("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the input surface (0 - disabled)") |
||||
("ensure-edge-size", boost::program_options::value(&OPT::nEnsureEdgeSize)->default_value(1), "ensure edge size and improve vertex valence of the input surface (0 - disabled, 1 - auto, 2 - force)") |
||||
("max-face-area", boost::program_options::value(&OPT::nMaxFaceArea)->default_value(32), "maximum face area projected in any pair of images that is not subdivided (0 - disabled)") |
||||
("scales", boost::program_options::value(&OPT::nScales)->default_value(2), "how many iterations to run mesh optimization on multi-scale images") |
||||
("scale-step", boost::program_options::value(&OPT::fScaleStep)->default_value(0.5f), "image scale factor used at each mesh optimization step") |
||||
("alternate-pair", boost::program_options::value(&OPT::nAlternatePair)->default_value(0), "refine mesh using an image pair alternatively as reference (0 - both, 1 - alternate, 2 - only left, 3 - only right)") |
||||
("regularity-weight", boost::program_options::value(&OPT::fRegularityWeight)->default_value(0.2f), "scalar regularity weight to balance between photo-consistency and regularization terms during mesh optimization") |
||||
("rigidity-elasticity-ratio", boost::program_options::value(&OPT::fRatioRigidityElasticity)->default_value(0.9f), "scalar ratio used to compute the regularity gradient as a combination of rigidity and elasticity") |
||||
("gradient-step", boost::program_options::value(&OPT::fGradientStep)->default_value(45.05f), "gradient step to be used instead (0 - auto)") |
||||
("planar-vertex-ratio", boost::program_options::value(&OPT::fPlanarVertexRatio)->default_value(0.f), "threshold used to remove vertices on planar patches (0 - disabled)") |
||||
("reduce-memory", boost::program_options::value(&OPT::nReduceMemory)->default_value(1), "recompute some data in order to reduce memory requirements") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
// initialize the log file
|
||||
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str()); |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidPath(OPT::strInputFileName); |
||||
if (OPT::vm.count("help") || OPT::strInputFileName.empty()) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config); |
||||
GET_LOG() << visible; |
||||
} |
||||
if (OPT::strInputFileName.empty()) |
||||
return false; |
||||
OPT::strExportType = OPT::strExportType.ToLower() == _T("obj") ? _T(".obj") : _T(".ply"); |
||||
|
||||
// initialize optional options
|
||||
Util::ensureValidPath(OPT::strMeshFileName); |
||||
Util::ensureValidPath(OPT::strOutputFileName); |
||||
if (OPT::strMeshFileName.empty() && (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS) |
||||
OPT::strMeshFileName = Util::getFileFullName(OPT::strInputFileName) + _T(".ply"); |
||||
if (OPT::strOutputFileName.empty()) |
||||
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + _T("_refine.mvs"); |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
Scene scene(OPT::nMaxThreads); |
||||
// load and refine the coarse mesh
|
||||
const Scene::SCENE_TYPE sceneType(scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName))); |
||||
if (sceneType == Scene::SCENE_NA) |
||||
return EXIT_FAILURE; |
||||
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName))) { |
||||
VERBOSE("error: cannot load mesh file"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
if (scene.mesh.IsEmpty()) { |
||||
VERBOSE("error: empty initial mesh"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
TD_TIMER_START(); |
||||
#ifdef _USE_CUDA |
||||
if (CUDA::desiredDeviceID < -1 || |
||||
!scene.RefineMeshCUDA(OPT::nResolutionLevel, OPT::nMinResolution, OPT::nMaxViews, |
||||
OPT::fDecimateMesh, OPT::nCloseHoles, OPT::nEnsureEdgeSize, |
||||
OPT::nMaxFaceArea, |
||||
OPT::nScales, OPT::fScaleStep, |
||||
OPT::nAlternatePair>10 ? OPT::nAlternatePair%10 : 0, |
||||
OPT::fRegularityWeight, |
||||
OPT::fRatioRigidityElasticity, |
||||
OPT::fGradientStep)) |
||||
#endif |
||||
if (!scene.RefineMesh(OPT::nResolutionLevel, OPT::nMinResolution, OPT::nMaxViews, |
||||
OPT::fDecimateMesh, OPT::nCloseHoles, OPT::nEnsureEdgeSize, |
||||
OPT::nMaxFaceArea, |
||||
OPT::nScales, OPT::fScaleStep, |
||||
OPT::nAlternatePair, |
||||
OPT::fRegularityWeight, |
||||
OPT::fRatioRigidityElasticity, |
||||
OPT::fGradientStep, |
||||
OPT::fPlanarVertexRatio, |
||||
OPT::nReduceMemory)) |
||||
return EXIT_FAILURE; |
||||
VERBOSE("Mesh refinement completed: %u vertices, %u faces (%s)", scene.mesh.vertices.GetSize(), scene.mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str()); |
||||
|
||||
// save the final mesh
|
||||
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))); |
||||
scene.mesh.Save(baseFileName+OPT::strExportType); |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
if (VERBOSITY_LEVEL > 2) |
||||
scene.ExportCamerasMLP(baseFileName+_T(".mlp"), baseFileName+OPT::strExportType); |
||||
#endif |
||||
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS || sceneType != Scene::SCENE_INTERFACE) |
||||
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
ADD_DEFINITIONS(-D_DATA_PATH="${CMAKE_CURRENT_SOURCE_DIR}/data/") |
||||
|
||||
cxx_executable_with_flags(Tests "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS Tests |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,136 @@
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Tests.cpp |
||||
* |
||||
* Copyright (c) 2014-2021 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
|
||||
using namespace MVS; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("Tests") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
// test various algorithms independently
|
||||
bool UnitTests() |
||||
{ |
||||
TD_TIMER_START(); |
||||
if (!SEACAVE::cListTest<true>(100)) { |
||||
VERBOSE("ERROR: cListTest failed!"); |
||||
return false; |
||||
} |
||||
if (!SEACAVE::OctreeTest<double,2>(100)) { |
||||
VERBOSE("ERROR: OctreeTest<double,2> failed!"); |
||||
return false; |
||||
} |
||||
if (!SEACAVE::OctreeTest<float,3>(100)) { |
||||
VERBOSE("ERROR: OctreeTest<float,3> failed!"); |
||||
return false; |
||||
} |
||||
if (!SEACAVE::TestRayTriangleIntersection<float>(1000)) { |
||||
VERBOSE("ERROR: TestRayTriangleIntersection<float> failed!"); |
||||
return false; |
||||
} |
||||
if (!SEACAVE::TestRayTriangleIntersection<double>(1000)) { |
||||
VERBOSE("ERROR: TestRayTriangleIntersection<double> failed!"); |
||||
return false; |
||||
} |
||||
VERBOSE("All unit tests passed (%s)", TD_TIMER_GET_FMT().c_str()); |
||||
return true; |
||||
} |
||||
|
||||
|
||||
// test MVS stages on a small sample dataset
|
||||
bool PipelineTest(bool verbose=false) |
||||
{ |
||||
TD_TIMER_START(); |
||||
Scene scene; |
||||
if (!scene.Load(MAKE_PATH("scene.mvs"))) { |
||||
VERBOSE("ERROR: TestDataset failed loading the scene!"); |
||||
return false; |
||||
} |
||||
OPTDENSE::init(); |
||||
OPTDENSE::bRemoveDmaps = true; |
||||
if (!scene.DenseReconstruction() || scene.pointcloud.GetSize() < 200000u) { |
||||
VERBOSE("ERROR: TestDataset failed estimating dense point cloud!"); |
||||
return false; |
||||
} |
||||
if (verbose) |
||||
scene.pointcloud.Save(MAKE_PATH("scene_dense.ply")); |
||||
if (!scene.ReconstructMesh() || scene.mesh.faces.size() < 75000u) { |
||||
VERBOSE("ERROR: TestDataset failed reconstructing the mesh!"); |
||||
return false; |
||||
} |
||||
if (verbose) |
||||
scene.mesh.Save(MAKE_PATH("scene_dense_mesh.ply")); |
||||
constexpr float decimate = 0.5f; |
||||
scene.mesh.Clean(decimate); |
||||
if (!ISINSIDE(scene.mesh.faces.size(), 35000u, 45000u)) { |
||||
VERBOSE("ERROR: TestDataset failed cleaning the mesh!"); |
||||
return false; |
||||
} |
||||
#ifdef _USE_OPENMP |
||||
TestMeshProjectionMT(scene.mesh, scene.images[1]); |
||||
#endif |
||||
if (!scene.TextureMesh(0, 0) || !scene.mesh.HasTexture()) { |
||||
VERBOSE("ERROR: TestDataset failed texturing the mesh!"); |
||||
return false; |
||||
} |
||||
if (verbose) |
||||
scene.mesh.Save(MAKE_PATH("scene_dense_mesh_texture.ply")); |
||||
VERBOSE("All pipeline stages passed (%s)", TD_TIMER_GET_FMT().c_str()); |
||||
return true; |
||||
} |
||||
|
||||
// test OpenMVS functionality
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
MVS::Initialize(APPNAME); |
||||
WORKING_FOLDER = _DATA_PATH; |
||||
INIT_WORKING_FOLDER; |
||||
if (argc < 2 || std::atoi(argv[1]) == 0) { |
||||
if (!UnitTests()) |
||||
return EXIT_FAILURE; |
||||
} else { |
||||
if (!PipelineTest()) |
||||
return EXIT_FAILURE; |
||||
} |
||||
MVS::Finalize(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
After Width: | Height: | Size: 280 KiB |
|
After Width: | Height: | Size: 296 KiB |
|
After Width: | Height: | Size: 289 KiB |
|
After Width: | Height: | Size: 290 KiB |
Binary file not shown.
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(TextureMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS TextureMesh |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(TransformScene "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Install |
||||
INSTALL(TARGETS TransformScene |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,329 @@
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* TransformScene.cpp |
||||
* |
||||
* Copyright (c) 2014-2021 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
#include <boost/program_options.hpp> |
||||
|
||||
using namespace MVS; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("TransformScene") |
||||
#define MVS_FILE_EXTENSION _T(".mvs") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace OPT { |
||||
String strInputFileName; |
||||
String strPointCloudFileName; |
||||
String strMeshFileName; |
||||
String strOutputFileName; |
||||
String strAlignFileName; |
||||
String strTransformFileName; |
||||
String strTransferTextureFileName; |
||||
String strIndicesFileName; |
||||
bool bComputeVolume; |
||||
float fPlaneThreshold; |
||||
float fSampleMesh; |
||||
unsigned nMaxResolution; |
||||
unsigned nUpAxis; |
||||
unsigned nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
String strExportType; |
||||
String strConfigFileName; |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "produce this help message") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("export-type", boost::program_options::value<std::string>(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply, obj, glb or gltf)") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
boost::program_options::options_description config("Main options"); |
||||
config.add_options() |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input scene filename") |
||||
("pointcloud-file,p", boost::program_options::value<std::string>(&OPT::strPointCloudFileName), "dense point-cloud with views file name to transform (overwrite existing point-cloud)") |
||||
("mesh-file,m", boost::program_options::value<std::string>(&OPT::strMeshFileName), "mesh file name to transform (overwrite existing mesh)") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the scene") |
||||
("align-file,a", boost::program_options::value<std::string>(&OPT::strAlignFileName), "input scene filename to which the scene will be cameras aligned") |
||||
("transform-file,t", boost::program_options::value<std::string>(&OPT::strTransformFileName), "input transform filename by which the scene will transformed") |
||||
("transfer-texture-file", boost::program_options::value<std::string>(&OPT::strTransferTextureFileName), "input mesh filename to which the texture of the scene's mesh will be transfered to (the two meshes should be aligned and the new mesh to have UV-map)") |
||||
("indices-file", boost::program_options::value<std::string>(&OPT::strIndicesFileName), "input indices filename to be used with ex. texture transfer to select a subset of the scene's mesh") |
||||
("compute-volume", boost::program_options::value(&OPT::bComputeVolume)->default_value(false), "compute the volume of the given watertight mesh, or else try to estimate the ground plane and assume the mesh is bounded by it") |
||||
("plane-threshold", boost::program_options::value(&OPT::fPlaneThreshold)->default_value(0.f), "threshold used to estimate the ground plane (<0 - disabled, 0 - auto, >0 - desired threshold)") |
||||
("sample-mesh", boost::program_options::value(&OPT::fSampleMesh)->default_value(-300000.f), "uniformly samples points on a mesh (0 - disabled, <0 - number of points, >0 - sample density per square unit)") |
||||
("max-resolution", boost::program_options::value(&OPT::nMaxResolution)->default_value(0), "make sure image resolution are not not larger than this (0 - disabled)") |
||||
("up-axis", boost::program_options::value(&OPT::nUpAxis)->default_value(2), "scene axis considered to point upwards (0 - x, 1 - y, 2 - z)") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
// initialize the log file
|
||||
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-") + Util::getUniqueName(0) + _T(".log")).c_str()); |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidPath(OPT::strInputFileName); |
||||
Util::ensureValidPath(OPT::strAlignFileName); |
||||
Util::ensureValidPath(OPT::strTransformFileName); |
||||
Util::ensureValidPath(OPT::strTransferTextureFileName); |
||||
Util::ensureValidPath(OPT::strIndicesFileName); |
||||
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower()); |
||||
const bool bInvalidCommand(OPT::strInputFileName.empty() || |
||||
(OPT::strAlignFileName.empty() && OPT::strTransformFileName.empty() && OPT::strTransferTextureFileName.empty() && !OPT::bComputeVolume)); |
||||
if (OPT::vm.count("help") || bInvalidCommand) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config); |
||||
GET_LOG() << visible; |
||||
} |
||||
if (bInvalidCommand) |
||||
return false; |
||||
OPT::strExportType = OPT::strExportType.ToLower(); |
||||
if (OPT::strExportType == _T("obj")) |
||||
OPT::strExportType = _T(".obj"); |
||||
else |
||||
if (OPT::strExportType == _T("glb")) |
||||
OPT::strExportType = _T(".glb"); |
||||
else |
||||
if (OPT::strExportType == _T("gltf")) |
||||
OPT::strExportType = _T(".gltf"); |
||||
else |
||||
OPT::strExportType = _T(".ply"); |
||||
|
||||
// initialize optional options
|
||||
Util::ensureValidPath(OPT::strPointCloudFileName); |
||||
Util::ensureValidPath(OPT::strMeshFileName); |
||||
Util::ensureValidPath(OPT::strOutputFileName); |
||||
if (OPT::strMeshFileName.empty() && (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS && strInputFileNameExt == MVS_FILE_EXTENSION) |
||||
OPT::strMeshFileName = Util::getFileFullName(OPT::strInputFileName) + _T(".ply"); |
||||
if (OPT::strOutputFileName.empty()) |
||||
OPT::strOutputFileName = Util::getFileName(OPT::strInputFileName) + _T("_transformed") MVS_FILE_EXTENSION; |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
TD_TIMER_START(); |
||||
|
||||
Scene scene(OPT::nMaxThreads); |
||||
|
||||
// load given scene
|
||||
const Scene::SCENE_TYPE sceneType(scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName), |
||||
!OPT::strTransformFileName.empty() || !OPT::strTransferTextureFileName.empty() || OPT::bComputeVolume)); |
||||
if (sceneType == Scene::SCENE_NA) |
||||
return EXIT_FAILURE; |
||||
if (!OPT::strPointCloudFileName.empty() && !scene.pointcloud.Load(MAKE_PATH_SAFE(OPT::strPointCloudFileName))) { |
||||
VERBOSE("error: cannot load point-cloud file"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName))) { |
||||
VERBOSE("error: cannot load mesh file"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))); |
||||
|
||||
if (!OPT::strAlignFileName.empty()) { |
||||
// transform this scene such that it best aligns with the given scene based on the camera positions
|
||||
Scene sceneRef(OPT::nMaxThreads); |
||||
if (!sceneRef.Load(MAKE_PATH_SAFE(OPT::strAlignFileName))) |
||||
return EXIT_FAILURE; |
||||
if (!scene.AlignTo(sceneRef)) |
||||
return EXIT_FAILURE; |
||||
VERBOSE("Scene aligned to the given reference scene (%s)", TD_TIMER_GET_FMT().c_str()); |
||||
} |
||||
|
||||
if (!OPT::strTransformFileName.empty()) { |
||||
// transform this scene by the given transform matrix
|
||||
std::ifstream file(MAKE_PATH_SAFE(OPT::strTransformFileName)); |
||||
std::string value; |
||||
std::vector<double> transformValues; |
||||
while (file >> value) { |
||||
double v; |
||||
try { |
||||
v = std::stod(value); |
||||
} |
||||
catch (...) { |
||||
continue; |
||||
} |
||||
transformValues.push_back(v); |
||||
} |
||||
if (transformValues.size() != 12 && |
||||
(transformValues.size() != 16 || transformValues[12] != 0 || transformValues[13] != 0 || transformValues[14] != 0 || transformValues[15] != 1)) { |
||||
VERBOSE("error: invalid transform"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
Matrix3x4 transform; |
||||
for (unsigned i=0; i<12; ++i) |
||||
transform[i] = transformValues[i]; |
||||
scene.Transform(transform); |
||||
VERBOSE("Scene transformed by the given transformation matrix (%s)", TD_TIMER_GET_FMT().c_str()); |
||||
} |
||||
|
||||
if (!OPT::strTransferTextureFileName.empty()) { |
||||
// transfer the texture of the scene's mesh to the new mesh;
|
||||
// the two meshes should be aligned and the new mesh to have UV-coordinates
|
||||
Mesh newMesh; |
||||
if (!newMesh.Load(MAKE_PATH_SAFE(OPT::strTransferTextureFileName))) |
||||
return EXIT_FAILURE; |
||||
Mesh::FaceIdxArr faceSubsetIndices; |
||||
if (!OPT::strIndicesFileName.empty()) { |
||||
std::ifstream in(OPT::strIndicesFileName.c_str()); |
||||
while (true) { |
||||
String index; |
||||
in >> index; |
||||
if (!in.good()) |
||||
break; |
||||
faceSubsetIndices.emplace_back(index.From<Mesh::FIndex>()); |
||||
} |
||||
} |
||||
if (!scene.mesh.TransferTexture(newMesh, faceSubsetIndices)) |
||||
return EXIT_FAILURE; |
||||
newMesh.Save(baseFileName + OPT::strExportType); |
||||
VERBOSE("Texture transfered (%s)", TD_TIMER_GET_FMT().c_str()); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
|
||||
if (OPT::nMaxResolution > 0) { |
||||
// scale scene images
|
||||
const String folderName(Util::getFilePath(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName)) + String::FormatString("images%u" PATH_SEPARATOR_STR, OPT::nMaxResolution)); |
||||
if (!scene.ScaleImages(OPT::nMaxResolution, 0, folderName)) { |
||||
DEBUG("error: can not scale scene images to '%s'", folderName.c_str()); |
||||
return EXIT_FAILURE; |
||||
} |
||||
} |
||||
|
||||
if (OPT::bComputeVolume && !scene.mesh.IsEmpty()) { |
||||
// compute the mesh volume
|
||||
const REAL volume(scene.ComputeLeveledVolume(OPT::fPlaneThreshold, OPT::fSampleMesh, OPT::nUpAxis)); |
||||
VERBOSE("Mesh volume: %g (%s)", volume, TD_TIMER_GET_FMT().c_str()); |
||||
} |
||||
|
||||
// write transformed scene
|
||||
if (scene.IsValid()) |
||||
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType); |
||||
if (!scene.IsValid() || (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS) { |
||||
if (!scene.pointcloud.IsEmpty()) |
||||
scene.pointcloud.Save(baseFileName + _T(".ply"), (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS); |
||||
if (!scene.mesh.IsEmpty()) |
||||
scene.mesh.Save(baseFileName + OPT::strExportType); |
||||
} |
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
if((NOT OpenMVS_USE_OPENGL) OR (NOT _USE_OPENGL)) |
||||
RETURN() |
||||
endif() |
||||
|
||||
if(NOT VIEWER_NAME) |
||||
set(VIEWER_NAME "Viewer") |
||||
endif() |
||||
|
||||
# Find required packages |
||||
FIND_PACKAGE(GLEW QUIET) |
||||
if(GLEW_FOUND) |
||||
INCLUDE_DIRECTORIES(${GLEW_INCLUDE_DIRS}) |
||||
ADD_DEFINITIONS(${GLEW_DEFINITIONS}) |
||||
MESSAGE(STATUS "GLEW ${GLEW_VERSION} found (include: ${GLEW_INCLUDE_DIRS})") |
||||
else() |
||||
MESSAGE("-- Can't find GLEW. Continuing without it.") |
||||
RETURN() |
||||
endif() |
||||
FIND_PACKAGE(glfw3 QUIET) |
||||
if(glfw3_FOUND) |
||||
INCLUDE_DIRECTORIES(${glfw3_INCLUDE_DIRS}) |
||||
ADD_DEFINITIONS(${glfw3_DEFINITIONS}) |
||||
MESSAGE(STATUS "GLFW3 ${glfw3_VERSION} found (include: ${glfw3_INCLUDE_DIRS})") |
||||
else() |
||||
MESSAGE("-- Can't find GLFW3. Continuing without it.") |
||||
RETURN() |
||||
endif() |
||||
|
||||
# List sources files |
||||
if(MSVC) |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") |
||||
else() |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
endif() |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_executable_with_flags(${VIEWER_NAME} "Apps" "${cxx_default}" "MVS;${OPENGL_LIBRARIES};${GLEW_LIBRARY};${GLFW_STATIC_LIBRARIES};GLEW::GLEW;${glfw3_LIBRARY};${GLFW3_LIBRARY};glfw;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) |
||||
|
||||
# Manually set Common.h as the precompiled header |
||||
IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16.0) |
||||
TARGET_PRECOMPILE_HEADERS(${VIEWER_NAME} PRIVATE "Common.h") |
||||
endif() |
||||
|
||||
# Install |
||||
INSTALL(TARGETS ${VIEWER_NAME} |
||||
EXPORT OpenMVSTargets |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) |
||||
@ -0,0 +1,180 @@
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Camera.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "Common.h" |
||||
#include "Camera.h" |
||||
|
||||
using namespace VIEWER; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
Camera::Camera(const AABB3d& _box, const Point3d& _center, float _scaleF, float _fov) |
||||
: |
||||
boxScene(_box), |
||||
centerScene(_center), |
||||
rotation(Eigen::Quaterniond::Identity()), |
||||
center(Eigen::Vector3d::Zero()), |
||||
dist(0), radius(100), |
||||
fovDef(_fov), scaleFDef(_scaleF), |
||||
prevCamID(NO_ID), currentCamID(NO_ID), maxCamID(0) |
||||
{ |
||||
Reset(); |
||||
} |
||||
|
||||
void Camera::Reset() |
||||
{ |
||||
if (boxScene.IsEmpty()) { |
||||
center = Point3d::ZERO; |
||||
radius = 1; |
||||
} else { |
||||
center = centerScene; |
||||
radius = boxScene.GetSize().norm()*0.5; |
||||
} |
||||
rotation = Eigen::Quaterniond::Identity(); |
||||
scaleF = scaleFDef; |
||||
prevCamID = currentCamID = NO_ID; |
||||
fov = fovDef; |
||||
dist = radius*0.5 / SIN(D2R((double)fov)); |
||||
if (size.area()) |
||||
Resize(size); |
||||
} |
||||
|
||||
void Camera::Resize(const cv::Size& _size) |
||||
{ |
||||
ASSERT(MINF(_size.width, _size.height) > 0); |
||||
size = _size; |
||||
glMatrixMode(GL_PROJECTION); |
||||
glLoadIdentity(); |
||||
const GLfloat zNear = 1e-3f; |
||||
const GLfloat zFar = (float)boxScene.GetSize().norm()*10; |
||||
const GLfloat aspect = float(size.width)/float(size.height); |
||||
if (fov == 5.f) { |
||||
// orthographic projection
|
||||
const GLfloat fH = (float)boxScene.GetSize().norm()*0.5f; |
||||
const GLfloat fW = fH * aspect; |
||||
glOrtho(-fW, fW, -fH, fH, zNear, zFar); |
||||
} else { |
||||
// perspective projection
|
||||
const GLfloat fH = TAN(FD2R(fov)) * zNear; |
||||
const GLfloat fW = fH * aspect; |
||||
glFrustum(-fW, fW, -fH, fH, zNear, zFar); |
||||
} |
||||
} |
||||
|
||||
void Camera::SetFOV(float _fov) |
||||
{ |
||||
fov = MAXF(_fov, 5.f); |
||||
Resize(size); |
||||
} |
||||
|
||||
|
||||
Eigen::Vector3d Camera::GetPosition() const |
||||
{ |
||||
const Eigen::Matrix3d R(GetRotation()); |
||||
return center + R.col(2) * dist; |
||||
} |
||||
|
||||
Eigen::Matrix3d Camera::GetRotation() const |
||||
{ |
||||
return rotation.toRotationMatrix(); |
||||
} |
||||
|
||||
Eigen::Matrix4d Camera::GetLookAt() const |
||||
{ |
||||
const Eigen::Matrix3d R(GetRotation()); |
||||
const Eigen::Vector3d eye(center + R.col(2) * dist); |
||||
const Eigen::Vector3d up(R.col(1)); |
||||
|
||||
const Eigen::Vector3d n((center-eye).normalized()); |
||||
const Eigen::Vector3d s(n.cross(up)); |
||||
const Eigen::Vector3d v(s.cross(n)); |
||||
|
||||
Eigen::Matrix4d m; m << |
||||
s(0), s(1), s(2), -eye.dot(s), |
||||
v(0), v(1), v(2), -eye.dot(v), |
||||
-n(0), -n(1), -n(2), eye.dot(n), |
||||
0.0, 0.0, 0.0, 1.0; |
||||
return m; |
||||
} |
||||
void Camera::GetLookAt(Eigen::Vector3d& _eye, Eigen::Vector3d& _center, Eigen::Vector3d& _up) const |
||||
{ |
||||
const Eigen::Matrix3d R(GetRotation()); |
||||
_eye = center + R.col(2) * dist; |
||||
_center = center; |
||||
_up = R.col(1); |
||||
} |
||||
|
||||
|
||||
void Camera::Rotate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos) |
||||
{ |
||||
if (pos.isApprox(prevPos, ZEROTOLERANCE<double>())) |
||||
return; |
||||
|
||||
Eigen::Vector3d oldp(prevPos.x(), prevPos.y(), 0); |
||||
Eigen::Vector3d newp(pos.x(), pos.y(), 0); |
||||
const double radiusSphere(0.9); |
||||
ProjectOnSphere(radiusSphere, oldp); |
||||
ProjectOnSphere(radiusSphere, newp); |
||||
rotation *= Eigen::Quaterniond().setFromTwoVectors(newp, oldp); |
||||
|
||||
// disable camera view mode
|
||||
prevCamID = currentCamID; |
||||
} |
||||
|
||||
void Camera::Translate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos) |
||||
{ |
||||
if (pos.isApprox(prevPos, ZEROTOLERANCE<double>())) |
||||
return; |
||||
|
||||
Eigen::Matrix<double,4,4,Eigen::ColMajor> P, V; |
||||
glGetDoublev(GL_MODELVIEW_MATRIX, V.data()); |
||||
glGetDoublev(GL_PROJECTION_MATRIX, P.data()); |
||||
Eigen::Vector3d centerScreen((P*V*center.homogeneous().eval()).hnormalized()); |
||||
centerScreen.head<2>() += prevPos - pos; |
||||
center = (V.inverse()*P.inverse()*centerScreen.homogeneous().eval()).hnormalized(); |
||||
|
||||
// disable camera view mode
|
||||
prevCamID = currentCamID; |
||||
} |
||||
|
||||
void Camera::ProjectOnSphere(double radius, Eigen::Vector3d& p) const |
||||
{ |
||||
p.z() = 0; |
||||
const double d = p.x()* p.x()+ p.y() * p.y(); |
||||
const double r = radius * radius; |
||||
if (d < r) p.z() = SQRT(r - d); |
||||
else p *= radius / p.norm(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Camera.h |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#ifndef _VIEWER_CAMERA_H_ |
||||
#define _VIEWER_CAMERA_H_ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace VIEWER { |
||||
|
||||
class Camera |
||||
{ |
||||
public: |
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW |
||||
|
||||
cv::Size size; |
||||
AABB3d boxScene; |
||||
Eigen::Vector3d centerScene; |
||||
Eigen::Quaterniond rotation; |
||||
Eigen::Vector3d center; |
||||
double dist, radius; |
||||
float fov, fovDef; |
||||
float scaleF, scaleFDef; |
||||
MVS::IIndex prevCamID, currentCamID, maxCamID; |
||||
|
||||
public: |
||||
Camera(const AABB3d& _box=AABB3d(true), const Point3d& _center=Point3d::ZERO, float _scaleF=1, float _fov=40); |
||||
|
||||
void Reset(); |
||||
void Resize(const cv::Size&); |
||||
void SetFOV(float _fov); |
||||
|
||||
const cv::Size& GetSize() const { return size; } |
||||
|
||||
Eigen::Vector3d GetPosition() const; |
||||
Eigen::Matrix3d GetRotation() const; |
||||
Eigen::Matrix4d GetLookAt() const; |
||||
|
||||
void GetLookAt(Eigen::Vector3d& eye, Eigen::Vector3d& center, Eigen::Vector3d& up) const; |
||||
void Rotate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos); |
||||
void Translate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos); |
||||
|
||||
bool IsCameraViewMode() const { return prevCamID != currentCamID && currentCamID != NO_ID; } |
||||
|
||||
protected: |
||||
void ProjectOnSphere(double radius, Eigen::Vector3d& p) const; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace VIEWER
|
||||
|
||||
#endif // _VIEWER_CAMERA_H_
|
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Common.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
// Source file that includes just the standard includes
|
||||
// Common.pch will be the pre-compiled header
|
||||
// Common.obj will contain the pre-compiled type information
|
||||
|
||||
#include "Common.h" |
||||
@ -0,0 +1,104 @@
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Common.h |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#ifndef _VIEWER_COMMON_H_ |
||||
#define _VIEWER_COMMON_H_ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include <GL/glew.h> |
||||
#include "../../libs/MVS/Common.h" |
||||
#include "../../libs/MVS/Scene.h" |
||||
|
||||
#if defined(_MSC_VER) |
||||
#include <gl/GLU.h> |
||||
#elif defined(__APPLE__) |
||||
#include <OpenGL/glu.h> |
||||
#else |
||||
#include <GL/glu.h> |
||||
#endif |
||||
#include <GLFW/glfw3.h> |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// P R O T O T Y P E S /////////////////////////////////////////////
|
||||
|
||||
using namespace SEACAVE; |
||||
|
||||
namespace VIEWER { |
||||
|
||||
// the conversion matrix from OpenGL default coordinate system
|
||||
// to the camera coordinate system (NADIR orientation):
|
||||
// [ 1 0 0 0] * [ x ] = [ x ]
|
||||
// 0 -1 0 0 y -y
|
||||
// 0 0 -1 0 z -z
|
||||
// 0 0 0 1 1 1
|
||||
static const Eigen::Matrix4d gs_convert = [] { |
||||
Eigen::Matrix4d tmp; tmp << |
||||
1, 0, 0, 0, |
||||
0, -1, 0, 0, |
||||
0, 0, -1, 0, |
||||
0, 0, 0, 1; |
||||
return tmp; |
||||
}(); |
||||
|
||||
/// given rotation matrix R and translation vector t,
|
||||
/// column-major matrix m is equal to:
|
||||
/// [ R11 R12 R13 t.x ]
|
||||
/// | R21 R22 R23 t.y |
|
||||
/// | R31 R32 R33 t.z |
|
||||
/// [ 0.0 0.0 0.0 1.0 ]
|
||||
//
|
||||
// World to Local
|
||||
inline Eigen::Matrix4d TransW2L(const Eigen::Matrix3d& R, const Eigen::Vector3d& t) |
||||
{ |
||||
Eigen::Matrix4d m(Eigen::Matrix4d::Identity()); |
||||
m.block(0,0,3,3) = R; |
||||
m.block(0,3,3,1) = t; |
||||
return m; |
||||
} |
||||
// Local to World
|
||||
// same as above, but with the inverse of the two
|
||||
inline Eigen::Matrix4d TransL2W(const Eigen::Matrix3d& R, const Eigen::Vector3d& t) |
||||
{ |
||||
Eigen::Matrix4d m(Eigen::Matrix4d::Identity()); |
||||
m.block(0,0,3,3) = R.transpose(); |
||||
m.block(0,3,3,1) = -t; |
||||
return m; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace MVS
|
||||
|
||||
#endif // _VIEWER_COMMON_H_
|
||||
@ -0,0 +1,124 @@
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Image.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "Common.h" |
||||
#include "Image.h" |
||||
|
||||
using namespace VIEWER; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
Image::Image(MVS::IIndex _idx) |
||||
: |
||||
idx(_idx), |
||||
texture(0) |
||||
{ |
||||
} |
||||
Image::~Image() |
||||
{ |
||||
Release(); |
||||
} |
||||
|
||||
void Image::Release() |
||||
{ |
||||
if (IsValid()) { |
||||
glDeleteTextures(1, &texture); |
||||
texture = 0; |
||||
} |
||||
ReleaseImage(); |
||||
} |
||||
void Image::ReleaseImage() |
||||
{ |
||||
if (IsImageValid()) { |
||||
cv::Mat* const p(pImage); |
||||
Thread::safeExchange(pImage.ptr, (int_t)IMG_NULL); |
||||
delete p; |
||||
} |
||||
} |
||||
|
||||
void Image::SetImageLoading() |
||||
{ |
||||
ASSERT(IsImageEmpty()); |
||||
Thread::safeExchange(pImage.ptr, (int_t)IMG_LOADING); |
||||
} |
||||
void Image::AssignImage(cv::InputArray img) |
||||
{ |
||||
ASSERT(IsImageLoading()); |
||||
ImagePtrInt pImg(new cv::Mat(img.getMat())); |
||||
if (pImg.pImage->cols%4 != 0) { |
||||
// make sure the width is multiple of 4 (seems to be an OpenGL limitation)
|
||||
cv::resize(*pImg.pImage, *pImg.pImage, cv::Size((pImg.pImage->cols/4)*4, pImg.pImage->rows), 0, 0, cv::INTER_AREA); |
||||
} |
||||
Thread::safeExchange(pImage.ptr, pImg.ptr); |
||||
} |
||||
bool Image::TransferImage() |
||||
{ |
||||
if (!IsImageValid()) |
||||
return false; |
||||
SetImage(*pImage); |
||||
glfwPostEmptyEvent(); |
||||
ReleaseImage(); |
||||
return true; |
||||
} |
||||
|
||||
void Image::SetImage(cv::InputArray img) |
||||
{ |
||||
cv::Mat image(img.getMat()); |
||||
glEnable(GL_TEXTURE_2D); |
||||
// create texture
|
||||
glGenTextures(1, &texture); |
||||
// select our current texture
|
||||
glBindTexture(GL_TEXTURE_2D, texture); |
||||
// load texture
|
||||
width = image.cols; |
||||
height = image.rows; |
||||
ASSERT(image.channels() == 1 || image.channels() == 3); |
||||
ASSERT(image.isContinuous()); |
||||
glTexImage2D(GL_TEXTURE_2D, |
||||
0, image.channels(), |
||||
width, height, |
||||
0, (image.channels() == 1) ? GL_LUMINANCE : GL_BGR, |
||||
GL_UNSIGNED_BYTE, image.ptr<uint8_t>()); |
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); |
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); |
||||
} |
||||
void Image::GenerateMipmap() const { |
||||
glBindTexture(GL_TEXTURE_2D, texture); |
||||
glGenerateMipmap(GL_TEXTURE_2D); |
||||
} |
||||
void Image::Bind() const { |
||||
glBindTexture(GL_TEXTURE_2D, texture); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,97 @@
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Image.h |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#ifndef _VIEWER_IMAGE_H_ |
||||
#define _VIEWER_IMAGE_H_ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace VIEWER { |
||||
|
||||
class Image |
||||
{ |
||||
public: |
||||
typedef CLISTDEFIDX(Image,uint32_t) ImageArr; |
||||
enum { |
||||
IMG_NULL = 0, |
||||
IMG_LOADING, |
||||
IMG_VALID |
||||
}; |
||||
union ImagePtrInt { |
||||
cv::Mat* pImage; |
||||
int_t ptr; |
||||
inline ImagePtrInt() : ptr(IMG_NULL) {} |
||||
inline ImagePtrInt(cv::Mat* p) : pImage(p) {} |
||||
inline operator cv::Mat* () const { return pImage; } |
||||
inline operator cv::Mat*& () { return pImage; } |
||||
}; |
||||
|
||||
public: |
||||
MVS::IIndex idx; // image index in the current scene
|
||||
int width, height; |
||||
GLuint texture; |
||||
double opacity; |
||||
ImagePtrInt pImage; |
||||
|
||||
public: |
||||
Image(MVS::IIndex = NO_ID); |
||||
~Image(); |
||||
|
||||
void Release(); |
||||
void ReleaseImage(); |
||||
inline bool IsValid() const { return texture > 0; } |
||||
inline bool IsImageEmpty() const { return pImage.ptr == IMG_NULL; } |
||||
inline bool IsImageLoading() const { return pImage.ptr == IMG_LOADING; } |
||||
inline bool IsImageValid() const { return pImage.ptr >= IMG_VALID; } |
||||
|
||||
void SetImageLoading(); |
||||
void AssignImage(cv::InputArray); |
||||
bool TransferImage(); |
||||
|
||||
void SetImage(cv::InputArray); |
||||
void GenerateMipmap() const; |
||||
void Bind() const; |
||||
|
||||
protected: |
||||
}; |
||||
typedef Image::ImageArr ImageArr; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace VIEWER
|
||||
|
||||
#endif // _VIEWER_IMAGE_H_
|
||||
@ -0,0 +1,823 @@
@@ -0,0 +1,823 @@
|
||||
/*
|
||||
* Scene.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "Common.h" |
||||
#include "Scene.h" |
||||
|
||||
using namespace VIEWER; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define IMAGE_MAX_RESOLUTION 1024 |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
enum EVENT_TYPE { |
||||
EVT_JOB = 0, |
||||
EVT_CLOSE, |
||||
}; |
||||
|
||||
class EVTClose : public Event |
||||
{ |
||||
public: |
||||
EVTClose() : Event(EVT_CLOSE) {} |
||||
}; |
||||
class EVTLoadImage : public Event |
||||
{ |
||||
public: |
||||
Scene* pScene; |
||||
MVS::IIndex idx; |
||||
unsigned nMaxResolution; |
||||
bool Run(void*) { |
||||
Image& image = pScene->images[idx]; |
||||
ASSERT(image.idx != NO_ID); |
||||
MVS::Image& imageData = pScene->scene.images[image.idx]; |
||||
ASSERT(imageData.IsValid()); |
||||
if (imageData.image.empty() && !imageData.ReloadImage(nMaxResolution)) |
||||
return false; |
||||
imageData.UpdateCamera(pScene->scene.platforms); |
||||
image.AssignImage(imageData.image); |
||||
imageData.ReleaseImage(); |
||||
glfwPostEmptyEvent(); |
||||
return true; |
||||
} |
||||
EVTLoadImage(Scene* _pScene, MVS::IIndex _idx, unsigned _nMaxResolution=0) |
||||
: Event(EVT_JOB), pScene(_pScene), idx(_idx), nMaxResolution(_nMaxResolution) {} |
||||
}; |
||||
class EVTComputeOctree : public Event |
||||
{ |
||||
public: |
||||
Scene* pScene; |
||||
bool Run(void*) { |
||||
MVS::Scene& scene = pScene->scene; |
||||
if (!scene.mesh.IsEmpty()) { |
||||
Scene::OctreeMesh octMesh(scene.mesh.vertices, [](Scene::OctreeMesh::IDX_TYPE size, Scene::OctreeMesh::Type /*radius*/) { |
||||
return size > 256; |
||||
}); |
||||
scene.mesh.ListIncidenteFaces(); |
||||
pScene->octMesh.Swap(octMesh); |
||||
} else |
||||
if (!scene.pointcloud.IsEmpty()) { |
||||
Scene::OctreePoints octPoints(scene.pointcloud.points, [](Scene::OctreePoints::IDX_TYPE size, Scene::OctreePoints::Type /*radius*/) { |
||||
return size > 512; |
||||
}); |
||||
pScene->octPoints.Swap(octPoints); |
||||
} |
||||
return true; |
||||
} |
||||
EVTComputeOctree(Scene* _pScene) |
||||
: Event(EVT_JOB), pScene(_pScene) {} |
||||
}; |
||||
|
||||
void* Scene::ThreadWorker(void*) { |
||||
while (true) { |
||||
CAutoPtr<Event> evt(events.GetEvent()); |
||||
switch (evt->GetID()) { |
||||
case EVT_JOB: |
||||
evt->Run(); |
||||
break; |
||||
case EVT_CLOSE: |
||||
return NULL; |
||||
default: |
||||
ASSERT("Should not happen!" == NULL); |
||||
} |
||||
} |
||||
return NULL; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
SEACAVE::EventQueue Scene::events; |
||||
SEACAVE::Thread Scene::thread; |
||||
|
||||
Scene::Scene(ARCHIVE_TYPE _nArchiveType) |
||||
: |
||||
nArchiveType(_nArchiveType), |
||||
listPointCloud(0) |
||||
{ |
||||
} |
||||
Scene::~Scene() |
||||
{ |
||||
Release(); |
||||
} |
||||
|
||||
void Scene::Empty() |
||||
{ |
||||
ReleasePointCloud(); |
||||
ReleaseMesh(); |
||||
obbPoints.Release(); |
||||
if (window.IsValid()) { |
||||
window.ReleaseClbk(); |
||||
window.Reset(); |
||||
window.SetName(_T("(empty)")); |
||||
} |
||||
textures.Release(); |
||||
images.Release(); |
||||
scene.Release(); |
||||
sceneName.clear(); |
||||
geometryName.clear(); |
||||
} |
||||
void Scene::Release() |
||||
{ |
||||
if (window.IsValid()) |
||||
window.SetVisible(false); |
||||
if (!thread.isRunning()) { |
||||
events.AddEvent(new EVTClose()); |
||||
thread.join(); |
||||
} |
||||
Empty(); |
||||
window.Release(); |
||||
glfwTerminate(); |
||||
} |
||||
void Scene::ReleasePointCloud() |
||||
{ |
||||
if (listPointCloud) { |
||||
glDeleteLists(listPointCloud, 1); |
||||
listPointCloud = 0; |
||||
} |
||||
} |
||||
void Scene::ReleaseMesh() |
||||
{ |
||||
if (!listMeshes.empty()) { |
||||
for (GLuint listMesh: listMeshes) |
||||
glDeleteLists(listMesh, 1); |
||||
listMeshes.Release(); |
||||
} |
||||
} |
||||
|
||||
bool Scene::Init(const cv::Size& size, LPCTSTR windowName, LPCTSTR fileName, LPCTSTR geometryFileName) |
||||
{ |
||||
ASSERT(scene.IsEmpty()); |
||||
|
||||
// init window
|
||||
if (glfwInit() == GL_FALSE) |
||||
return false; |
||||
if (!window.Init(size, windowName)) |
||||
return false; |
||||
if (glewInit() != GLEW_OK) |
||||
return false; |
||||
name = windowName; |
||||
window.clbkOpenScene = DELEGATEBINDCLASS(Window::ClbkOpenScene, &Scene::Open, this); |
||||
|
||||
// init OpenGL
|
||||
glPolygonMode(GL_FRONT, GL_FILL); |
||||
glEnable(GL_DEPTH_TEST); |
||||
glClearColor(0.f, 0.5f, 0.9f, 1.f); |
||||
|
||||
static const float light0_ambient[] = {0.1f, 0.1f, 0.1f, 1.0f}; |
||||
static const float light0_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; |
||||
static const float light0_position[] = {0.0f, 0.0f, 1000.0f, 0.0f}; |
||||
static const float light0_specular[] = {0.4f, 0.4f, 0.4f, 1.0f}; |
||||
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient); |
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); |
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular); |
||||
glLightfv(GL_LIGHT0, GL_POSITION, light0_position); |
||||
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); |
||||
|
||||
glEnable(GL_LIGHT0); |
||||
glDisable(GL_LIGHTING); |
||||
|
||||
// init working thread
|
||||
thread.start(ThreadWorker); |
||||
|
||||
// open scene or init empty scene
|
||||
window.SetCamera(Camera()); |
||||
if (fileName != NULL) |
||||
Open(fileName, geometryFileName); |
||||
window.SetVisible(true); |
||||
return true; |
||||
} |
||||
bool Scene::Open(LPCTSTR fileName, LPCTSTR geometryFileName) |
||||
{ |
||||
ASSERT(fileName); |
||||
DEBUG_EXTRA("Loading: '%s'", Util::getFileNameExt(fileName).c_str()); |
||||
Empty(); |
||||
sceneName = fileName; |
||||
|
||||
// load the scene
|
||||
WORKING_FOLDER = Util::getFilePath(fileName); |
||||
INIT_WORKING_FOLDER; |
||||
if (!scene.Load(fileName, true)) |
||||
return false; |
||||
if (geometryFileName) { |
||||
// try to load given mesh
|
||||
MVS::Mesh mesh; |
||||
MVS::PointCloud pointcloud; |
||||
if (mesh.Load(geometryFileName)) { |
||||
scene.mesh.Swap(mesh); |
||||
geometryName = geometryFileName; |
||||
geometryMesh = true; |
||||
} else |
||||
// try to load as a point-cloud
|
||||
if (pointcloud.Load(geometryFileName)) { |
||||
scene.pointcloud.Swap(pointcloud); |
||||
geometryName = geometryFileName; |
||||
geometryMesh = false; |
||||
} |
||||
} |
||||
if (!scene.pointcloud.IsEmpty()) |
||||
scene.pointcloud.PrintStatistics(scene.images.data(), &scene.obb); |
||||
|
||||
#if 1 |
||||
// create octree structure used to accelerate selection functionality
|
||||
if (!scene.IsEmpty()) |
||||
events.AddEvent(new EVTComputeOctree(this)); |
||||
#endif |
||||
|
||||
// init scene
|
||||
AABB3d bounds(true); |
||||
Point3d center(Point3d::INF); |
||||
if (scene.IsBounded()) { |
||||
bounds = AABB3d(scene.obb.GetAABB()); |
||||
center = bounds.GetCenter(); |
||||
} else { |
||||
if (!scene.pointcloud.IsEmpty()) { |
||||
bounds = scene.pointcloud.GetAABB(MINF(3u,scene.nCalibratedImages)); |
||||
if (bounds.IsEmpty()) |
||||
bounds = scene.pointcloud.GetAABB(); |
||||
center = scene.pointcloud.GetCenter(); |
||||
} |
||||
if (!scene.mesh.IsEmpty()) { |
||||
scene.mesh.ComputeNormalFaces(); |
||||
bounds.Insert(scene.mesh.GetAABB()); |
||||
center = scene.mesh.GetCenter(); |
||||
} |
||||
} |
||||
|
||||
// init images
|
||||
AABB3d imageBounds(true); |
||||
images.Reserve(scene.images.size()); |
||||
FOREACH(idxImage, scene.images) { |
||||
const MVS::Image& imageData = scene.images[idxImage]; |
||||
if (!imageData.IsValid()) |
||||
continue; |
||||
images.emplace_back(idxImage); |
||||
imageBounds.InsertFull(imageData.camera.C); |
||||
} |
||||
if (imageBounds.IsEmpty()) |
||||
imageBounds.Enlarge(0.5); |
||||
if (bounds.IsEmpty()) |
||||
bounds = imageBounds; |
||||
|
||||
// init and load texture
|
||||
if (scene.mesh.HasTexture()) { |
||||
FOREACH(i, scene.mesh.texturesDiffuse) { |
||||
Image& image = textures.emplace_back(); |
||||
ASSERT(image.idx == NO_ID); |
||||
#if 0 |
||||
Image8U3& textureDiffuse = scene.mesh.texturesDiffuse[i]; |
||||
cv::flip(textureDiffuse, textureDiffuse, 0); |
||||
image.SetImage(textureDiffuse); |
||||
textureDiffuse.release(); |
||||
#else // preserve texture, used only to be able to export the mesh
|
||||
Image8U3 textureDiffuse; |
||||
cv::flip(scene.mesh.texturesDiffuse[i], textureDiffuse, 0); |
||||
image.SetImage(textureDiffuse); |
||||
#endif |
||||
image.GenerateMipmap(); |
||||
} |
||||
} |
||||
|
||||
// init display lists
|
||||
// compile point-cloud
|
||||
CompilePointCloud(); |
||||
// compile mesh
|
||||
CompileMesh(); |
||||
// compile bounding-box
|
||||
CompileBounds(); |
||||
|
||||
// init camera
|
||||
window.SetCamera(Camera(bounds, |
||||
center == Point3d::INF ? Point3d(bounds.GetCenter()) : center, |
||||
images.size()<2?1.f:(float)imageBounds.EnlargePercent(REAL(1)/images.size()).GetSize().norm())); |
||||
window.camera.maxCamID = images.size(); |
||||
window.SetName(String::FormatString((name + _T(": %s")).c_str(), Util::getFileName(fileName).c_str())); |
||||
window.clbkSaveScene = DELEGATEBINDCLASS(Window::ClbkSaveScene, &Scene::Save, this); |
||||
window.clbkExportScene = DELEGATEBINDCLASS(Window::ClbkExportScene, &Scene::Export, this); |
||||
window.clbkCenterScene = DELEGATEBINDCLASS(Window::ClbkCenterScene, &Scene::Center, this); |
||||
window.clbkCompilePointCloud = DELEGATEBINDCLASS(Window::ClbkCompilePointCloud, &Scene::CompilePointCloud, this); |
||||
window.clbkCompileMesh = DELEGATEBINDCLASS(Window::ClbkCompileMesh, &Scene::CompileMesh, this); |
||||
window.clbkTogleSceneBox = DELEGATEBINDCLASS(Window::ClbkTogleSceneBox, &Scene::TogleSceneBox, this); |
||||
window.clbkCropToBounds = DELEGATEBINDCLASS(Window::ClbkCropToBounds, &Scene::CropToBounds, this); |
||||
if (scene.IsBounded()) |
||||
window.clbkCompileBounds = DELEGATEBINDCLASS(Window::ClbkCompileBounds, &Scene::CompileBounds, this); |
||||
if (!scene.IsEmpty()) |
||||
window.clbkRayScene = DELEGATEBINDCLASS(Window::ClbkRayScene, &Scene::CastRay, this); |
||||
window.Reset(!scene.pointcloud.IsEmpty()&&!scene.mesh.IsEmpty()?Window::SPR_NONE:Window::SPR_ALL, |
||||
MINF(2u,images.size())); |
||||
return true; |
||||
} |
||||
|
||||
// export the scene
|
||||
bool Scene::Save(LPCTSTR _fileName, bool bRescaleImages) |
||||
{ |
||||
if (!IsOpen()) |
||||
return false; |
||||
REAL imageScale = 0; |
||||
if (bRescaleImages) { |
||||
window.SetVisible(false); |
||||
std::cout << "Enter image resolution scale: "; |
||||
String strScale; |
||||
std::cin >> strScale; |
||||
window.SetVisible(true); |
||||
imageScale = strScale.From<REAL>(0); |
||||
} |
||||
const String fileName(_fileName != NULL ? String(_fileName) : Util::insertBeforeFileExt(sceneName, _T("_new"))); |
||||
MVS::Mesh mesh; |
||||
if (!scene.mesh.IsEmpty() && !geometryName.empty() && geometryMesh) |
||||
mesh.Swap(scene.mesh); |
||||
MVS::PointCloud pointcloud; |
||||
if (!scene.pointcloud.IsEmpty() && !geometryName.empty() && !geometryMesh) |
||||
pointcloud.Swap(scene.pointcloud); |
||||
if (imageScale > 0 && imageScale < 1) { |
||||
// scale and save images
|
||||
const String folderName(Util::getFilePath(MAKE_PATH_FULL(WORKING_FOLDER_FULL, fileName)) + String::FormatString("images%d" PATH_SEPARATOR_STR, ROUND2INT(imageScale*100))); |
||||
if (!scene.ScaleImages(0, imageScale, folderName)) { |
||||
DEBUG("error: can not scale scene images to '%s'", folderName.c_str()); |
||||
return false; |
||||
} |
||||
} |
||||
if (!scene.Save(fileName, nArchiveType)) { |
||||
DEBUG("error: can not save scene to '%s'", fileName.c_str()); |
||||
return false; |
||||
} |
||||
if (!mesh.IsEmpty()) |
||||
scene.mesh.Swap(mesh); |
||||
if (!pointcloud.IsEmpty()) |
||||
scene.pointcloud.Swap(pointcloud); |
||||
sceneName = fileName; |
||||
return true; |
||||
} |
||||
|
||||
// export the scene
|
||||
bool Scene::Export(LPCTSTR _fileName, LPCTSTR exportType) const |
||||
{ |
||||
if (!IsOpen()) |
||||
return false; |
||||
ASSERT(!sceneName.IsEmpty()); |
||||
String lastFileName; |
||||
const String fileName(_fileName != NULL ? String(_fileName) : sceneName); |
||||
const String baseFileName(Util::getFileFullName(fileName)); |
||||
const bool bPoints(scene.pointcloud.Save(lastFileName=(baseFileName+_T("_pointcloud.ply")), nArchiveType==ARCHIVE_MVS)); |
||||
const bool bMesh(scene.mesh.Save(lastFileName=(baseFileName+_T("_mesh")+(exportType?exportType:(Util::getFileExt(fileName)==_T(".obj")?_T(".obj"):_T(".ply")))), cList<String>(), true)); |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
if (VERBOSITY_LEVEL > 2 && (bPoints || bMesh)) |
||||
scene.ExportCamerasMLP(Util::getFileFullName(lastFileName)+_T(".mlp"), lastFileName); |
||||
#endif |
||||
AABB3f aabb(true); |
||||
if (scene.IsBounded()) { |
||||
std::ofstream fs(baseFileName+_T("_roi.txt")); |
||||
if (fs) |
||||
fs << scene.obb; |
||||
aabb = scene.obb.GetAABB(); |
||||
} else |
||||
if (!scene.pointcloud.IsEmpty()) { |
||||
aabb = scene.pointcloud.GetAABB(); |
||||
} else |
||||
if (!scene.mesh.IsEmpty()) { |
||||
aabb = scene.mesh.GetAABB(); |
||||
} |
||||
if (!aabb.IsEmpty()) { |
||||
std::ofstream fs(baseFileName+_T("_roi_box.txt")); |
||||
if (fs) |
||||
fs << aabb; |
||||
} |
||||
return bPoints || bMesh; |
||||
} |
||||
|
||||
void Scene::CompilePointCloud() |
||||
{ |
||||
if (scene.pointcloud.IsEmpty()) |
||||
return; |
||||
ReleasePointCloud(); |
||||
listPointCloud = glGenLists(1); |
||||
glNewList(listPointCloud, GL_COMPILE); |
||||
ASSERT((window.sparseType&(Window::SPR_POINTS|Window::SPR_LINES)) != 0); |
||||
// compile point-cloud
|
||||
if ((window.sparseType&Window::SPR_POINTS) != 0) { |
||||
ASSERT_ARE_SAME_TYPE(float, MVS::PointCloud::Point::Type); |
||||
glBegin(GL_POINTS); |
||||
glColor3f(1.f,1.f,1.f); |
||||
FOREACH(i, scene.pointcloud.points) { |
||||
if (!scene.pointcloud.pointViews.empty() && |
||||
scene.pointcloud.pointViews[i].size() < window.minViews) |
||||
continue; |
||||
if (!scene.pointcloud.colors.empty()) { |
||||
const MVS::PointCloud::Color& c = scene.pointcloud.colors[i]; |
||||
glColor3ub(c.r,c.g,c.b); |
||||
} |
||||
const MVS::PointCloud::Point& X = scene.pointcloud.points[i]; |
||||
glVertex3fv(X.ptr()); |
||||
} |
||||
glEnd(); |
||||
} |
||||
glEndList(); |
||||
} |
||||
|
||||
void Scene::CompileMesh() |
||||
{ |
||||
if (scene.mesh.IsEmpty()) |
||||
return; |
||||
ReleaseMesh(); |
||||
if (scene.mesh.faceNormals.empty()) |
||||
scene.mesh.ComputeNormalFaces(); |
||||
// translate, normalize and flip Y axis of the texture coordinates
|
||||
MVS::Mesh::TexCoordArr normFaceTexcoords; |
||||
if (scene.mesh.HasTexture() && window.bRenderTexture) |
||||
scene.mesh.FaceTexcoordsNormalize(normFaceTexcoords, true); |
||||
MVS::Mesh::TexIndex texIdx(0); |
||||
do { |
||||
GLuint& listMesh = listMeshes.emplace_back(glGenLists(1)); |
||||
listMesh = glGenLists(1); |
||||
glNewList(listMesh, GL_COMPILE); |
||||
// compile mesh
|
||||
ASSERT_ARE_SAME_TYPE(float, MVS::Mesh::Vertex::Type); |
||||
ASSERT_ARE_SAME_TYPE(float, MVS::Mesh::Normal::Type); |
||||
ASSERT_ARE_SAME_TYPE(float, MVS::Mesh::TexCoord::Type); |
||||
glColor3f(1.f, 1.f, 1.f); |
||||
glBegin(GL_TRIANGLES); |
||||
FOREACH(idxFace, scene.mesh.faces) { |
||||
if (!scene.mesh.faceTexindices.empty() && scene.mesh.faceTexindices[idxFace] != texIdx) |
||||
continue; |
||||
const MVS::Mesh::Face& face = scene.mesh.faces[idxFace]; |
||||
const MVS::Mesh::Normal& n = scene.mesh.faceNormals[idxFace]; |
||||
glNormal3fv(n.ptr()); |
||||
for (int j = 0; j < 3; ++j) { |
||||
if (!normFaceTexcoords.empty()) { |
||||
const MVS::Mesh::TexCoord& t = normFaceTexcoords[idxFace*3 + j]; |
||||
glTexCoord2fv(t.ptr()); |
||||
} |
||||
const MVS::Mesh::Vertex& p = scene.mesh.vertices[face[j]]; |
||||
glVertex3fv(p.ptr()); |
||||
} |
||||
} |
||||
glEnd(); |
||||
glEndList(); |
||||
} while (++texIdx < scene.mesh.texturesDiffuse.size()); |
||||
} |
||||
|
||||
void Scene::CompileBounds() |
||||
{ |
||||
obbPoints.Release(); |
||||
if (!scene.IsBounded()) { |
||||
window.bRenderBounds = false; |
||||
return; |
||||
} |
||||
window.bRenderBounds = !window.bRenderBounds; |
||||
if (window.bRenderBounds) { |
||||
static const uint8_t indices[12*2] = { |
||||
0,2, 2,3, 3,1, 1,0, |
||||
0,6, 2,4, 3,5, 1,7, |
||||
6,4, 4,5, 5,7, 7,6 |
||||
}; |
||||
OBB3f::POINT corners[OBB3f::numCorners]; |
||||
scene.obb.GetCorners(corners); |
||||
for (int i=0; i<12; ++i) { |
||||
obbPoints.emplace_back(corners[indices[i*2+0]]); |
||||
obbPoints.emplace_back(corners[indices[i*2+1]]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Scene::CropToBounds() |
||||
{ |
||||
if (!IsOpen()) |
||||
return; |
||||
if (!scene.IsBounded()) |
||||
return; |
||||
scene.pointcloud.RemovePointsOutside(scene.obb); |
||||
scene.mesh.RemoveFacesOutside(scene.obb); |
||||
Center(); |
||||
} |
||||
|
||||
void Scene::Draw() |
||||
{ |
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
||||
glPointSize(window.pointSize); |
||||
|
||||
// render point-cloud
|
||||
if (listPointCloud) { |
||||
glDisable(GL_TEXTURE_2D); |
||||
glCallList(listPointCloud); |
||||
} |
||||
// render mesh
|
||||
if (!listMeshes.empty()) { |
||||
glEnable(GL_DEPTH_TEST); |
||||
glEnable(GL_CULL_FACE); |
||||
if (!scene.mesh.faceTexcoords.empty() && window.bRenderTexture) { |
||||
glEnable(GL_TEXTURE_2D); |
||||
FOREACH(i, listMeshes) { |
||||
textures[i].Bind(); |
||||
glCallList(listMeshes[i]); |
||||
} |
||||
glDisable(GL_TEXTURE_2D); |
||||
} else { |
||||
glEnable(GL_LIGHTING); |
||||
for (GLuint listMesh: listMeshes) |
||||
glCallList(listMesh); |
||||
glDisable(GL_LIGHTING); |
||||
} |
||||
} |
||||
// render cameras
|
||||
if (window.bRenderCameras) { |
||||
glDisable(GL_CULL_FACE); |
||||
const Point3* ptrPrevC(NULL); |
||||
FOREACH(idx, images) { |
||||
Image& image = images[idx]; |
||||
const MVS::Image& imageData = scene.images[image.idx]; |
||||
const MVS::Camera& camera = imageData.camera; |
||||
// cache image corner coordinates
|
||||
const double scaleFocal(window.camera.scaleF); |
||||
const Point2d pp(camera.GetPrincipalPoint()); |
||||
const double focal(camera.GetFocalLength()/scaleFocal); |
||||
const double cx(-pp.x/focal); |
||||
const double cy(-pp.y/focal); |
||||
const double px((double)imageData.width/focal+cx); |
||||
const double py((double)imageData.height/focal+cy); |
||||
const Point3d ic1(cx, cy, scaleFocal); |
||||
const Point3d ic2(cx, py, scaleFocal); |
||||
const Point3d ic3(px, py, scaleFocal); |
||||
const Point3d ic4(px, cy, scaleFocal); |
||||
// change coordinates system to the camera space
|
||||
glPushMatrix(); |
||||
glMultMatrixd((GLdouble*)TransL2W((const Matrix3x3::EMat)camera.R, -(const Point3::EVec)camera.C).data()); |
||||
glPointSize(window.pointSize+1.f); |
||||
glDisable(GL_TEXTURE_2D); |
||||
// draw camera position and image center
|
||||
glBegin(GL_POINTS); |
||||
glColor3f(1,0,0); glVertex3f(0,0,0); // camera position
|
||||
glColor3f(0,1,0); glVertex3f(0,0,(float)scaleFocal); // image center
|
||||
glColor3f(0,0,1); glVertex3d((0.5*imageData.width-pp.x)/focal, cy, scaleFocal); // image up
|
||||
glEnd(); |
||||
// draw image thumbnail
|
||||
const bool bSelectedImage(idx == window.camera.currentCamID); |
||||
if (bSelectedImage) { |
||||
if (image.IsValid()) { |
||||
// render image
|
||||
glEnable(GL_TEXTURE_2D); |
||||
image.Bind(); |
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
||||
glEnable(GL_BLEND); |
||||
glDisable(GL_DEPTH_TEST); |
||||
glColor4f(1,1,1,window.cameraBlend); |
||||
glBegin(GL_QUADS); |
||||
glTexCoord2d(0,0); glVertex3dv(ic1.ptr()); |
||||
glTexCoord2d(0,1); glVertex3dv(ic2.ptr()); |
||||
glTexCoord2d(1,1); glVertex3dv(ic3.ptr()); |
||||
glTexCoord2d(1,0); glVertex3dv(ic4.ptr()); |
||||
glEnd(); |
||||
glDisable(GL_TEXTURE_2D); |
||||
glDisable(GL_BLEND); |
||||
glEnable(GL_DEPTH_TEST); |
||||
} else { |
||||
// start and wait to load the image
|
||||
if (image.IsImageEmpty()) { |
||||
// start loading
|
||||
image.SetImageLoading(); |
||||
events.AddEvent(new EVTLoadImage(this, idx, IMAGE_MAX_RESOLUTION)); |
||||
} else { |
||||
// check if the image is available and set it
|
||||
image.TransferImage(); |
||||
} |
||||
} |
||||
} |
||||
// draw camera frame
|
||||
glColor3f(bSelectedImage ? 0.f : 1.f, 1.f, 0.f); |
||||
glBegin(GL_LINES); |
||||
glVertex3d(0,0,0); glVertex3dv(ic1.ptr()); |
||||
glVertex3d(0,0,0); glVertex3dv(ic2.ptr()); |
||||
glVertex3d(0,0,0); glVertex3dv(ic3.ptr()); |
||||
glVertex3d(0,0,0); glVertex3dv(ic4.ptr()); |
||||
glVertex3dv(ic1.ptr()); glVertex3dv(ic2.ptr()); |
||||
glVertex3dv(ic2.ptr()); glVertex3dv(ic3.ptr()); |
||||
glVertex3dv(ic3.ptr()); glVertex3dv(ic4.ptr()); |
||||
glVertex3dv(ic4.ptr()); glVertex3dv(ic1.ptr()); |
||||
glEnd(); |
||||
// restore coordinate system
|
||||
glPopMatrix(); |
||||
// render image visibility info
|
||||
if (window.bRenderImageVisibility && idx != NO_ID && idx==window.camera.currentCamID) { |
||||
if (scene.pointcloud.IsValid()) { |
||||
const Image& image = images[idx]; |
||||
glPointSize(window.pointSize*1.1f); |
||||
glDisable(GL_DEPTH_TEST); |
||||
glBegin(GL_POINTS); |
||||
glColor3f(1.f,0.f,0.f); |
||||
FOREACH(i, scene.pointcloud.points) { |
||||
ASSERT(!scene.pointcloud.pointViews[i].empty()); |
||||
if (scene.pointcloud.pointViews[i].size() < window.minViews) |
||||
continue; |
||||
if (scene.pointcloud.pointViews[i].FindFirst(image.idx) == MVS::PointCloud::ViewArr::NO_INDEX) |
||||
continue; |
||||
glVertex3fv(scene.pointcloud.points[i].ptr()); |
||||
} |
||||
glEnd(); |
||||
glEnable(GL_DEPTH_TEST); |
||||
glPointSize(window.pointSize); |
||||
} |
||||
} |
||||
// render camera trajectory
|
||||
if (window.bRenderCameraTrajectory && ptrPrevC) { |
||||
glBegin(GL_LINES); |
||||
glColor3f(1.f,0.5f,0.f); |
||||
glVertex3dv(ptrPrevC->ptr()); |
||||
glVertex3dv(camera.C.ptr()); |
||||
glEnd(); |
||||
} |
||||
ptrPrevC = &camera.C; |
||||
} |
||||
} |
||||
// render selection
|
||||
if (window.selectionType != Window::SEL_NA) { |
||||
glPointSize(window.pointSize+4); |
||||
glDisable(GL_DEPTH_TEST); |
||||
glBegin(GL_POINTS); |
||||
glColor3f(1,0,0); glVertex3fv(window.selectionPoints[0].ptr()); |
||||
if (window.selectionType == Window::SEL_TRIANGLE) { |
||||
glColor3f(0,1,0); glVertex3fv(window.selectionPoints[1].ptr()); |
||||
glColor3f(0,0,1); glVertex3fv(window.selectionPoints[2].ptr()); |
||||
} |
||||
glEnd(); |
||||
if (window.bRenderViews && window.selectionType == Window::SEL_POINT) { |
||||
if (!scene.pointcloud.pointViews.empty()) { |
||||
glBegin(GL_LINES); |
||||
const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[(MVS::PointCloud::Index)window.selectionIdx]; |
||||
ASSERT(!views.empty()); |
||||
for (MVS::PointCloud::View idxImage: views) { |
||||
const MVS::Image& imageData = scene.images[idxImage]; |
||||
glVertex3dv(imageData.camera.C.ptr()); |
||||
glVertex3fv(window.selectionPoints[0].ptr()); |
||||
} |
||||
glEnd(); |
||||
} |
||||
} |
||||
glEnable(GL_DEPTH_TEST); |
||||
glPointSize(window.pointSize); |
||||
} |
||||
// render oriented-bounding-box
|
||||
if (!obbPoints.empty()) { |
||||
glDepthMask(GL_FALSE); |
||||
glBegin(GL_LINES); |
||||
glColor3f(0.5f,0.1f,0.8f); |
||||
for (IDX i=0; i<obbPoints.size(); i+=2) { |
||||
glVertex3fv(obbPoints[i+0].ptr()); |
||||
glVertex3fv(obbPoints[i+1].ptr()); |
||||
} |
||||
glEnd(); |
||||
glDepthMask(GL_TRUE); |
||||
} |
||||
glfwSwapBuffers(window.GetWindow()); |
||||
} |
||||
|
||||
void Scene::Loop() |
||||
{ |
||||
while (!glfwWindowShouldClose(window.GetWindow())) { |
||||
window.UpdateView(images, scene.images); |
||||
Draw(); |
||||
glfwWaitEvents(); |
||||
} |
||||
} |
||||
|
||||
|
||||
void Scene::Center() |
||||
{ |
||||
if (!IsOpen()) |
||||
return; |
||||
scene.Center(); |
||||
CompilePointCloud(); |
||||
CompileMesh(); |
||||
if (scene.IsBounded()) { |
||||
window.bRenderBounds = false; |
||||
CompileBounds(); |
||||
} |
||||
events.AddEvent(new EVTComputeOctree(this)); |
||||
} |
||||
|
||||
void Scene::TogleSceneBox() |
||||
{ |
||||
if (!IsOpen()) |
||||
return; |
||||
const auto EnlargeAABB = [](AABB3f aabb) { |
||||
return aabb.Enlarge(aabb.GetSize().maxCoeff()*0.03f); |
||||
}; |
||||
if (scene.IsBounded()) |
||||
scene.obb = OBB3f(true); |
||||
else if (!scene.mesh.IsEmpty()) |
||||
scene.obb.Set(EnlargeAABB(scene.mesh.GetAABB())); |
||||
else if (!scene.pointcloud.IsEmpty()) |
||||
scene.obb.Set(EnlargeAABB(scene.pointcloud.GetAABB(window.minViews))); |
||||
CompileBounds(); |
||||
} |
||||
|
||||
|
||||
void Scene::CastRay(const Ray3& ray, int action) |
||||
{ |
||||
if (!IsOctreeValid()) |
||||
return; |
||||
const double timeClick(0.2); |
||||
const double timeDblClick(0.3); |
||||
const double now(glfwGetTime()); |
||||
switch (action) { |
||||
case GLFW_PRESS: { |
||||
// remember when the click action started
|
||||
window.selectionTimeClick = now; |
||||
break; } |
||||
case GLFW_RELEASE: { |
||||
if (now-window.selectionTimeClick > timeClick) { |
||||
// this is a long click, ignore it
|
||||
break; |
||||
} else |
||||
if (window.selectionType != Window::SEL_NA && |
||||
now-window.selectionTime < timeDblClick) { |
||||
// this is a double click, center scene at the selected point
|
||||
window.CenterCamera(window.selectionPoints[3]); |
||||
window.selectionTime = now; |
||||
} else |
||||
if (!octMesh.IsEmpty()) { |
||||
// find ray intersection with the mesh
|
||||
const MVS::IntersectRayMesh intRay(octMesh, ray, scene.mesh); |
||||
if (intRay.pick.IsValid()) { |
||||
const MVS::Mesh::Face& face = scene.mesh.faces[(MVS::Mesh::FIndex)intRay.pick.idx]; |
||||
window.selectionPoints[0] = scene.mesh.vertices[face[0]]; |
||||
window.selectionPoints[1] = scene.mesh.vertices[face[1]]; |
||||
window.selectionPoints[2] = scene.mesh.vertices[face[2]]; |
||||
window.selectionPoints[3] = ray.GetPoint(intRay.pick.dist).cast<float>(); |
||||
window.selectionType = Window::SEL_TRIANGLE; |
||||
window.selectionTime = now; |
||||
window.selectionIdx = intRay.pick.idx; |
||||
DEBUG("Face selected:\n\tindex: %u\n\tvertex 1: %u (%g %g %g)\n\tvertex 2: %u (%g %g %g)\n\tvertex 3: %u (%g %g %g)", |
||||
intRay.pick.idx, |
||||
face[0], window.selectionPoints[0].x, window.selectionPoints[0].y, window.selectionPoints[0].z, |
||||
face[1], window.selectionPoints[1].x, window.selectionPoints[1].y, window.selectionPoints[1].z, |
||||
face[2], window.selectionPoints[2].x, window.selectionPoints[2].y, window.selectionPoints[2].z |
||||
); |
||||
} else { |
||||
window.selectionType = Window::SEL_NA; |
||||
} |
||||
} else |
||||
if (!octPoints.IsEmpty()) { |
||||
// find ray intersection with the points
|
||||
const MVS::IntersectRayPoints intRay(octPoints, ray, scene.pointcloud, window.minViews); |
||||
if (intRay.pick.IsValid()) { |
||||
window.selectionPoints[0] = window.selectionPoints[3] = scene.pointcloud.points[intRay.pick.idx]; |
||||
window.selectionType = Window::SEL_POINT; |
||||
window.selectionTime = now; |
||||
window.selectionIdx = intRay.pick.idx; |
||||
DEBUG("Point selected:\n\tindex: %u (%g %g %g)%s", |
||||
intRay.pick.idx, |
||||
window.selectionPoints[0].x, window.selectionPoints[0].y, window.selectionPoints[0].z, |
||||
[&]() { |
||||
if (scene.pointcloud.pointViews.empty()) |
||||
return String(); |
||||
const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[intRay.pick.idx]; |
||||
ASSERT(!views.empty()); |
||||
String strViews(String::FormatString("\n\tviews: %u", views.size())); |
||||
for (MVS::PointCloud::View idxImage: views) { |
||||
const MVS::Image& imageData = scene.images[idxImage]; |
||||
const Point2 x(imageData.camera.TransformPointW2I(Cast<REAL>(window.selectionPoints[0]))); |
||||
strViews += String::FormatString("\n\t\t%s (%.2f %.2f)", Util::getFileNameExt(imageData.name).c_str(), x.x, x.y); |
||||
} |
||||
return strViews; |
||||
}().c_str() |
||||
); |
||||
} else { |
||||
window.selectionType = Window::SEL_NA; |
||||
} |
||||
} |
||||
break; } |
||||
} |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Scene.h |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#ifndef _VIEWER_SCENE_H_ |
||||
#define _VIEWER_SCENE_H_ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Window.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace VIEWER { |
||||
|
||||
class Scene |
||||
{ |
||||
public: |
||||
typedef MVS::PointCloud::Octree OctreePoints; |
||||
typedef MVS::Mesh::Octree OctreeMesh; |
||||
|
||||
public: |
||||
ARCHIVE_TYPE nArchiveType; |
||||
String name; |
||||
|
||||
String sceneName; |
||||
String geometryName; |
||||
bool geometryMesh; |
||||
MVS::Scene scene; |
||||
Window window; |
||||
ImageArr images; // scene photos
|
||||
ImageArr textures; // mesh textures
|
||||
|
||||
OctreePoints octPoints; |
||||
OctreeMesh octMesh; |
||||
Point3fArr obbPoints; |
||||
|
||||
GLuint listPointCloud; |
||||
CLISTDEF0IDX(GLuint,MVS::Mesh::TexIndex) listMeshes; |
||||
|
||||
// multi-threading
|
||||
static SEACAVE::EventQueue events; // internal events queue (processed by the working threads)
|
||||
static SEACAVE::Thread thread; // worker thread
|
||||
|
||||
public: |
||||
explicit Scene(ARCHIVE_TYPE _nArchiveType = ARCHIVE_MVS); |
||||
~Scene(); |
||||
|
||||
void Empty(); |
||||
void Release(); |
||||
void ReleasePointCloud(); |
||||
void ReleaseMesh(); |
||||
inline bool IsValid() const { return window.IsValid(); } |
||||
inline bool IsOpen() const { return IsValid() && !scene.IsEmpty(); } |
||||
inline bool IsOctreeValid() const { return !octPoints.IsEmpty() || !octMesh.IsEmpty(); } |
||||
|
||||
bool Init(const cv::Size&, LPCTSTR windowName, LPCTSTR fileName=NULL, LPCTSTR geometryFileName=NULL); |
||||
bool Open(LPCTSTR fileName, LPCTSTR geometryFileName=NULL); |
||||
bool Save(LPCTSTR fileName=NULL, bool bRescaleImages=false); |
||||
bool Export(LPCTSTR fileName, LPCTSTR exportType=NULL) const; |
||||
void CompilePointCloud(); |
||||
void CompileMesh(); |
||||
void CompileBounds(); |
||||
void CropToBounds(); |
||||
|
||||
void Draw(); |
||||
void Loop(); |
||||
|
||||
void Center(); |
||||
void TogleSceneBox(); |
||||
void CastRay(const Ray3&, int); |
||||
protected: |
||||
static void* ThreadWorker(void*); |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace VIEWER
|
||||
|
||||
#endif // _VIEWER_SCENE_H_
|
||||
@ -0,0 +1,226 @@
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Viewer.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "Common.h" |
||||
#include <boost/program_options.hpp> |
||||
|
||||
#include "Scene.h" |
||||
|
||||
using namespace VIEWER; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define APPNAME _T("Viewer") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace { |
||||
|
||||
namespace OPT { |
||||
String strInputFileName; |
||||
String strGeometryFileName; |
||||
String strOutputFileName; |
||||
unsigned nArchiveType; |
||||
int nProcessPriority; |
||||
unsigned nMaxThreads; |
||||
unsigned nMaxMemory; |
||||
String strExportType; |
||||
String strConfigFileName; |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
bool bLogFile; |
||||
#endif |
||||
boost::program_options::variables_map vm; |
||||
} // namespace OPT
|
||||
|
||||
class Application { |
||||
public: |
||||
Application() {} |
||||
~Application() { Finalize(); } |
||||
|
||||
bool Initialize(size_t argc, LPCTSTR* argv); |
||||
void Finalize(); |
||||
}; // Application
|
||||
|
||||
// initialize and parse the command line parameters
|
||||
bool Application::Initialize(size_t argc, LPCTSTR* argv) |
||||
{ |
||||
// initialize log and console
|
||||
OPEN_LOG(); |
||||
OPEN_LOGCONSOLE(); |
||||
|
||||
// group of options allowed only on command line
|
||||
boost::program_options::options_description generic("Generic options"); |
||||
generic.add_options() |
||||
("help,h", "produce this help message") |
||||
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)") |
||||
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") |
||||
("export-type", boost::program_options::value<std::string>(&OPT::strExportType), "file type used to export the 3D scene (ply or obj)") |
||||
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary") |
||||
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(0), "process priority (normal by default)") |
||||
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads that this process should use (0 - use all available cores)") |
||||
("max-memory", boost::program_options::value(&OPT::nMaxMemory)->default_value(0), "maximum amount of memory in MB that this process should use (0 - use all available memory)") |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
("log-file", boost::program_options::value(&OPT::bLogFile)->default_value(false), "dump log to a file") |
||||
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
3 |
||||
#else |
||||
2 |
||||
#endif |
||||
), "verbosity level") |
||||
#endif |
||||
; |
||||
|
||||
// group of options allowed both on command line and in config file
|
||||
boost::program_options::options_description config("Viewer options"); |
||||
config.add_options() |
||||
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input project filename containing camera poses and scene (point-cloud/mesh)") |
||||
("geometry-file,g", boost::program_options::value<std::string>(&OPT::strGeometryFileName), "mesh or point-cloud with views file name (overwrite existing geometry)") |
||||
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh") |
||||
; |
||||
|
||||
boost::program_options::options_description cmdline_options; |
||||
cmdline_options.add(generic).add(config); |
||||
|
||||
boost::program_options::options_description config_file_options; |
||||
config_file_options.add(config); |
||||
|
||||
boost::program_options::positional_options_description p; |
||||
p.add("input-file", -1); |
||||
|
||||
try { |
||||
// parse command line options
|
||||
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
INIT_WORKING_FOLDER; |
||||
// parse configuration file
|
||||
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); |
||||
if (ifs) { |
||||
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm); |
||||
boost::program_options::notify(OPT::vm); |
||||
} |
||||
} |
||||
catch (const std::exception& e) { |
||||
LOG(e.what()); |
||||
return false; |
||||
} |
||||
|
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
// initialize the log file
|
||||
if (OPT::bLogFile) |
||||
OPEN_LOGFILE((MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log"))).c_str()); |
||||
#endif |
||||
|
||||
// print application details: version and command line
|
||||
Util::LogBuild(); |
||||
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); |
||||
|
||||
// validate input
|
||||
Util::ensureValidPath(OPT::strInputFileName); |
||||
if (OPT::vm.count("help")) { |
||||
boost::program_options::options_description visible("Available options"); |
||||
visible.add(generic).add(config); |
||||
GET_LOG() << _T("\n" |
||||
"Visualize any know point-cloud/mesh formats or MVS projects. Supply files through command line or Drag&Drop.\n" |
||||
"Keys:\n" |
||||
"\tE: export scene\n" |
||||
"\tR: reset scene\n" |
||||
"\tB: render bounds\n" |
||||
"\tB + Shift: togle bounds\n" |
||||
"\tC: render cameras\n" |
||||
"\tC + Shift: render camera trajectory\n" |
||||
"\tC + Ctrl: center scene\n" |
||||
"\tLeft/Right: select next camera to view the scene\n" |
||||
"\tS: save scene\n" |
||||
"\tS + Shift: rescale images and save scene\n" |
||||
"\tT: render mesh texture\n" |
||||
"\tW: render wire-frame mesh\n" |
||||
"\tV: render view rays to the selected point\n" |
||||
"\tV + Shift: render points seen by the current view\n" |
||||
"\tUp/Down: adjust point size\n" |
||||
"\tUp/Down + Shift: adjust minimum number of views accepted when displaying a point or line\n" |
||||
"\t+/-: adjust camera thumbnail transparency\n" |
||||
"\t+/- + Shift: adjust camera cones' length\n" |
||||
"\n") |
||||
<< visible; |
||||
} |
||||
if (!OPT::strExportType.empty()) |
||||
OPT::strExportType = OPT::strExportType.ToLower() == _T("obj") ? _T(".obj") : _T(".ply"); |
||||
|
||||
// initialize optional options
|
||||
Util::ensureValidPath(OPT::strGeometryFileName); |
||||
Util::ensureValidPath(OPT::strOutputFileName); |
||||
|
||||
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority); |
||||
return true; |
||||
} |
||||
|
||||
// finalize application instance
|
||||
void Application::Finalize() |
||||
{ |
||||
MVS::Finalize(); |
||||
|
||||
if (OPT::bLogFile) |
||||
CLOSE_LOGFILE(); |
||||
CLOSE_LOGCONSOLE(); |
||||
CLOSE_LOG(); |
||||
} |
||||
|
||||
} // unnamed namespace
|
||||
|
||||
int main(int argc, LPCTSTR* argv) |
||||
{ |
||||
#ifdef _DEBUGINFO |
||||
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
|
||||
#endif |
||||
|
||||
Application application; |
||||
if (!application.Initialize(argc, argv)) |
||||
return EXIT_FAILURE; |
||||
|
||||
// create viewer
|
||||
Scene viewer; |
||||
if (!viewer.Init(cv::Size(1280, 720), APPNAME, |
||||
OPT::strInputFileName.empty() ? NULL : MAKE_PATH_SAFE(OPT::strInputFileName).c_str(), |
||||
OPT::strGeometryFileName.empty() ? NULL : MAKE_PATH_SAFE(OPT::strGeometryFileName).c_str())) |
||||
return EXIT_FAILURE; |
||||
if (viewer.IsOpen() && !OPT::strOutputFileName.empty()) { |
||||
// export the scene
|
||||
viewer.Export(MAKE_PATH_SAFE(OPT::strOutputFileName), OPT::strExportType.empty()?LPCTSTR(NULL):OPT::strExportType.c_str()); |
||||
} |
||||
// enter viewer loop
|
||||
viewer.Loop(); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,493 @@
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
* Window.cpp |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#include "Common.h" |
||||
#include "Window.h" |
||||
|
||||
using namespace VIEWER; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
Window::WindowsMap Window::g_mapWindows; |
||||
|
||||
Window::Window() |
||||
: |
||||
window(NULL), |
||||
pos(Eigen::Vector2d::Zero()), |
||||
prevPos(Eigen::Vector2d::Zero()) |
||||
{ |
||||
} |
||||
Window::~Window() |
||||
{ |
||||
Release(); |
||||
} |
||||
|
||||
void Window::Release() |
||||
{ |
||||
if (IsValid()) { |
||||
#ifdef _USE_NUKLEAR |
||||
nk_glfw3_shutdown(); |
||||
#endif |
||||
glfwDestroyWindow(window); |
||||
window = NULL; |
||||
} |
||||
clbkOpenScene.reset(); |
||||
ReleaseClbk(); |
||||
} |
||||
|
||||
void Window::ReleaseClbk() |
||||
{ |
||||
clbkSaveScene.reset(); |
||||
clbkExportScene.reset(); |
||||
clbkCenterScene.reset(); |
||||
clbkRayScene.reset(); |
||||
clbkCompilePointCloud.reset(); |
||||
clbkCompileMesh.reset(); |
||||
clbkCompileBounds.reset(); |
||||
clbkTogleSceneBox.reset(); |
||||
clbkCropToBounds.reset(); |
||||
} |
||||
|
||||
bool Window::Init(const cv::Size& _size, LPCTSTR name) |
||||
{ |
||||
sizeScale = 1; |
||||
size = _size; |
||||
|
||||
glfwDefaultWindowHints(); |
||||
glfwWindowHint(GLFW_VISIBLE, 0); |
||||
window = glfwCreateWindow(size.width, size.height, name, NULL, NULL); |
||||
if (!window) |
||||
return false; |
||||
glfwMakeContextCurrent(window); |
||||
glfwSetFramebufferSizeCallback(window, Window::Resize); |
||||
glfwSetKeyCallback(window, Window::Key); |
||||
glfwSetMouseButtonCallback(window, Window::MouseButton); |
||||
glfwSetCursorPosCallback(window, Window::MouseMove); |
||||
glfwSetScrollCallback(window, Window::Scroll); |
||||
glfwSetDropCallback(window, Window::Drop); |
||||
g_mapWindows[window] = this; |
||||
|
||||
Reset(); |
||||
return true; |
||||
} |
||||
void Window::SetCamera(const Camera& cam) |
||||
{ |
||||
camera = cam; |
||||
cv::Size _size; |
||||
glfwGetFramebufferSize(window, &_size.width, &_size.height); |
||||
Resize(_size); |
||||
} |
||||
void Window::SetName(LPCTSTR name) |
||||
{ |
||||
glfwSetWindowTitle(window, name); |
||||
} |
||||
void Window::SetVisible(bool v) |
||||
{ |
||||
if (v) |
||||
glfwShowWindow(window); |
||||
else |
||||
glfwHideWindow(window); |
||||
} |
||||
bool Window::IsVisible() const |
||||
{ |
||||
return glfwGetWindowAttrib(window, GLFW_VISIBLE) != 0; |
||||
} |
||||
void Window::Reset(SPARSE _sparseType, unsigned _minViews) |
||||
{ |
||||
camera.Reset(); |
||||
inputType = INP_NA; |
||||
sparseType = _sparseType; |
||||
minViews = _minViews; |
||||
pointSize = 2.f; |
||||
cameraBlend = 0.5f; |
||||
bRenderCameras = true; |
||||
bRenderCameraTrajectory = true; |
||||
bRenderImageVisibility = false; |
||||
bRenderViews = true; |
||||
bRenderSolid = true; |
||||
bRenderTexture = true; |
||||
bRenderBounds = false; |
||||
selectionType = SEL_NA; |
||||
selectionIdx = NO_IDX; |
||||
if (clbkCompilePointCloud != NULL) |
||||
clbkCompilePointCloud(); |
||||
if (clbkCompileMesh != NULL) |
||||
clbkCompileMesh(); |
||||
glfwPostEmptyEvent(); |
||||
} |
||||
|
||||
|
||||
void Window::CenterCamera(const Point3& pos) |
||||
{ |
||||
camera.center = pos; |
||||
camera.dist *= 0.7; |
||||
} |
||||
|
||||
|
||||
void Window::UpdateView(const ImageArr& images, const MVS::ImageArr& sceneImagesMVS) |
||||
{ |
||||
if (camera.IsCameraViewMode()) { |
||||
// enable camera view mode and apply current camera transform
|
||||
const Image& image = images[camera.currentCamID]; |
||||
const MVS::Camera& camera = sceneImagesMVS[image.idx].camera; |
||||
UpdateView((const Matrix3x3::EMat)camera.R, camera.GetT()); |
||||
} else { |
||||
// apply view point transform
|
||||
glMatrixMode(GL_MODELVIEW); |
||||
const Eigen::Matrix4d trans(camera.GetLookAt()); |
||||
glLoadMatrixd((GLdouble*)trans.data()); |
||||
} |
||||
} |
||||
|
||||
void Window::UpdateView(const Eigen::Matrix3d& R, const Eigen::Vector3d& t) |
||||
{ |
||||
glMatrixMode(GL_MODELVIEW); |
||||
transform = gs_convert * TransW2L(R, t); |
||||
glLoadMatrixd((GLdouble*)transform.data()); |
||||
} |
||||
|
||||
void Window::UpdateMousePosition(double xpos, double ypos) |
||||
{ |
||||
prevPos = pos; |
||||
pos.x() = xpos; |
||||
pos.y() = ypos; |
||||
// normalize position to [-1:1] range
|
||||
const int w(camera.size.width); |
||||
const int h(camera.size.height); |
||||
pos.x() = (2.0 * pos.x() - w) / w; |
||||
pos.y() = (h - 2.0 * pos.y()) / h; |
||||
} |
||||
|
||||
|
||||
void Window::GetFrame(Image8U3& image) const |
||||
{ |
||||
image.create(GetSize()); |
||||
glReadPixels(0, 0, image.width(), image.height(), GL_BGR_EXT, GL_UNSIGNED_BYTE, image.ptr()); |
||||
cv::flip(image, image, 0); |
||||
} |
||||
|
||||
|
||||
cv::Size Window::GetSize() const |
||||
{ |
||||
cv::Size _size; |
||||
glfwGetWindowSize(window, &_size.width, &_size.height); |
||||
return _size; |
||||
} |
||||
void Window::Resize(const cv::Size& _size) |
||||
{ |
||||
// detect scaled window
|
||||
sizeScale = (double)GetSize().width/_size.width; |
||||
size = _size; |
||||
// update resolution
|
||||
glfwMakeContextCurrent(window); |
||||
glViewport(0, 0, size.width, size.height); |
||||
camera.Resize(cv::Size(ROUND2INT(size.width*sizeScale), ROUND2INT(size.height*sizeScale))); |
||||
} |
||||
void Window::Resize(GLFWwindow* window, int width, int height) |
||||
{ |
||||
g_mapWindows[window]->Resize(cv::Size(width, height)); |
||||
} |
||||
|
||||
void Window::Key(int k, int /*scancode*/, int action, int mod) |
||||
{ |
||||
switch (k) { |
||||
case GLFW_KEY_ESCAPE: |
||||
if (action == GLFW_RELEASE) |
||||
glfwSetWindowShouldClose(window, 1); |
||||
break; |
||||
case GLFW_KEY_DOWN: |
||||
if (action == GLFW_RELEASE) { |
||||
if (mod & GLFW_MOD_SHIFT) { |
||||
if (minViews > 2) { |
||||
minViews--; |
||||
if (clbkCompilePointCloud != NULL) |
||||
clbkCompilePointCloud(); |
||||
} |
||||
} else { |
||||
pointSize = MAXF(pointSize-0.5f, 0.5f); |
||||
} |
||||
} |
||||
break; |
||||
case GLFW_KEY_UP: |
||||
if (action == GLFW_RELEASE) { |
||||
if (mod & GLFW_MOD_SHIFT) { |
||||
minViews++; |
||||
if (clbkCompilePointCloud != NULL) |
||||
clbkCompilePointCloud(); |
||||
} else { |
||||
pointSize += 0.5f; |
||||
} |
||||
} |
||||
break; |
||||
case GLFW_KEY_LEFT: |
||||
if (action != GLFW_RELEASE) { |
||||
camera.prevCamID = camera.currentCamID; |
||||
camera.currentCamID--; |
||||
if (camera.currentCamID < NO_ID && camera.currentCamID >= camera.maxCamID) |
||||
camera.currentCamID = camera.maxCamID-1; |
||||
} |
||||
break; |
||||
case GLFW_KEY_RIGHT: |
||||
if (action != GLFW_RELEASE) { |
||||
camera.prevCamID = camera.currentCamID; |
||||
camera.currentCamID++; |
||||
if (camera.currentCamID >= camera.maxCamID) |
||||
camera.currentCamID = NO_ID; |
||||
} |
||||
break; |
||||
case GLFW_KEY_B: |
||||
if (action == GLFW_RELEASE) { |
||||
if (mod & GLFW_MOD_CONTROL) { |
||||
if (clbkCropToBounds != NULL) |
||||
clbkCropToBounds(); |
||||
} else if (mod & GLFW_MOD_SHIFT) { |
||||
if (clbkTogleSceneBox != NULL) |
||||
clbkTogleSceneBox(); |
||||
} else { |
||||
if (clbkCompileBounds != NULL) |
||||
clbkCompileBounds(); |
||||
} |
||||
} |
||||
break; |
||||
case GLFW_KEY_C: |
||||
if (action == GLFW_RELEASE) { |
||||
if (mod & GLFW_MOD_SHIFT) { |
||||
bRenderCameraTrajectory = !bRenderCameraTrajectory; |
||||
} else if (mod & GLFW_MOD_CONTROL) { |
||||
if (clbkCenterScene != NULL) |
||||
clbkCenterScene(); |
||||
} else { |
||||
bRenderCameras = !bRenderCameras; |
||||
} |
||||
} |
||||
break; |
||||
case GLFW_KEY_E: |
||||
if (action == GLFW_RELEASE && clbkExportScene != NULL) |
||||
clbkExportScene(NULL, NULL); |
||||
break; |
||||
case GLFW_KEY_P: |
||||
switch (sparseType) { |
||||
case SPR_POINTS: sparseType = SPR_LINES; break; |
||||
case SPR_LINES: sparseType = SPR_ALL; break; |
||||
case SPR_ALL: sparseType = SPR_POINTS; break; |
||||
} |
||||
if (clbkCompilePointCloud != NULL) |
||||
clbkCompilePointCloud(); |
||||
break; |
||||
case GLFW_KEY_R: |
||||
if (action == GLFW_RELEASE) |
||||
Reset(); |
||||
break; |
||||
case GLFW_KEY_S: |
||||
if (action == GLFW_RELEASE) { |
||||
if (clbkSaveScene != NULL) |
||||
clbkSaveScene(NULL, (mod & GLFW_MOD_SHIFT) != 0); |
||||
} |
||||
break; |
||||
case GLFW_KEY_T: |
||||
if (action == GLFW_RELEASE) { |
||||
bRenderTexture = !bRenderTexture; |
||||
if (clbkCompileMesh != NULL) |
||||
clbkCompileMesh(); |
||||
} |
||||
break; |
||||
case GLFW_KEY_V: |
||||
if (action == GLFW_RELEASE) { |
||||
if (mod & GLFW_MOD_SHIFT) { |
||||
bRenderImageVisibility = !bRenderImageVisibility; |
||||
} else { |
||||
bRenderViews = !bRenderViews; |
||||
} |
||||
} |
||||
break; |
||||
case GLFW_KEY_W: |
||||
if (action == GLFW_RELEASE) { |
||||
if (bRenderSolid) { |
||||
bRenderSolid = false; |
||||
glPolygonMode(GL_FRONT, GL_LINE); |
||||
} else { |
||||
bRenderSolid = true; |
||||
glPolygonMode(GL_FRONT, GL_FILL); |
||||
} |
||||
} |
||||
break; |
||||
case GLFW_KEY_KP_SUBTRACT: |
||||
if (action == GLFW_RELEASE) { |
||||
if (mod & GLFW_MOD_CONTROL) |
||||
camera.SetFOV(camera.fov-5.f); |
||||
else if (mod & GLFW_MOD_SHIFT) |
||||
camera.scaleF *= 0.9f; |
||||
else |
||||
cameraBlend = MAXF(cameraBlend-0.1f, 0.f); |
||||
} |
||||
break; |
||||
case GLFW_KEY_KP_ADD: |
||||
if (action == GLFW_RELEASE) { |
||||
if (mod & GLFW_MOD_CONTROL) |
||||
camera.SetFOV(camera.fov+5.f); |
||||
else if (mod & GLFW_MOD_SHIFT) |
||||
camera.scaleF *= 1.1111f; |
||||
else |
||||
cameraBlend = MINF(cameraBlend+0.1f, 1.f); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
void Window::Key(GLFWwindow* window, int k, int scancode, int action, int mod) |
||||
{ |
||||
g_mapWindows[window]->Key(k, scancode, action, mod); |
||||
} |
||||
|
||||
void Window::MouseButton(int button, int action, int /*mods*/) |
||||
{ |
||||
switch (button) { |
||||
case GLFW_MOUSE_BUTTON_LEFT: { |
||||
if (action == GLFW_PRESS) { |
||||
inputType.set(INP_MOUSE_LEFT); |
||||
} else |
||||
if (action == GLFW_RELEASE) { |
||||
inputType.unset(INP_MOUSE_LEFT); |
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); |
||||
} |
||||
if (clbkRayScene != NULL) { |
||||
typedef Eigen::Matrix<double,4,4,Eigen::ColMajor> Mat4; |
||||
Mat4 P, V; |
||||
glGetDoublev(GL_MODELVIEW_MATRIX, V.data()); |
||||
glGetDoublev(GL_PROJECTION_MATRIX, P.data()); |
||||
// 4d Homogeneous Clip Coordinates
|
||||
const Eigen::Vector4d ray_clip(pos.x(), pos.y(), -1.0, 1.0); |
||||
// 4d Eye (Camera) Coordinates
|
||||
Eigen::Vector4d ray_eye(P.inverse()*ray_clip); |
||||
ray_eye.z() = -1.0; |
||||
ray_eye.w() = 0.0; |
||||
// 4d World Coordinates
|
||||
const Mat4 invV(V.inverse()); |
||||
ASSERT(ISEQUAL(invV(3,3),1.0)); |
||||
const Eigen::Vector3d start(invV.topRightCorner<3,1>()); |
||||
const Eigen::Vector4d ray_wor(invV*ray_eye); |
||||
const Eigen::Vector3d dir(ray_wor.topRows<3>().normalized()); |
||||
clbkRayScene(Ray3d(start, dir), action); |
||||
} |
||||
} break; |
||||
case GLFW_MOUSE_BUTTON_MIDDLE: { |
||||
if (action == GLFW_PRESS) { |
||||
inputType.set(INP_MOUSE_MIDDLE); |
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); |
||||
} else |
||||
if (action == GLFW_RELEASE) { |
||||
inputType.unset(INP_MOUSE_MIDDLE); |
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); |
||||
} |
||||
} break; |
||||
case GLFW_MOUSE_BUTTON_RIGHT: { |
||||
if (action == GLFW_PRESS) { |
||||
inputType.set(INP_MOUSE_RIGHT); |
||||
} else |
||||
if (action == GLFW_RELEASE) { |
||||
inputType.unset(INP_MOUSE_RIGHT); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
void Window::MouseButton(GLFWwindow* window, int button, int action, int mods) |
||||
{ |
||||
g_mapWindows[window]->MouseButton(button, action, mods); |
||||
} |
||||
|
||||
void Window::MouseMove(double xpos, double ypos) |
||||
{ |
||||
UpdateMousePosition(xpos, ypos); |
||||
if (inputType.isSet(INP_MOUSE_LEFT)) { |
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); |
||||
camera.Rotate(pos, prevPos); |
||||
} else |
||||
if (inputType.isSet(INP_MOUSE_MIDDLE)) { |
||||
camera.Translate(pos, prevPos); |
||||
} |
||||
} |
||||
void Window::MouseMove(GLFWwindow* window, double xpos, double ypos) |
||||
{ |
||||
g_mapWindows[window]->MouseMove(xpos, ypos); |
||||
} |
||||
|
||||
void Window::Scroll(double /*xoffset*/, double yoffset) |
||||
{ |
||||
camera.dist *= (yoffset>0 ? POW(1.11,yoffset) : POW(0.9,-yoffset)); |
||||
} |
||||
void Window::Scroll(GLFWwindow* window, double xoffset, double yoffset) |
||||
{ |
||||
g_mapWindows[window]->Scroll(xoffset, yoffset); |
||||
} |
||||
|
||||
void Window::Drop(int count, const char** paths) |
||||
{ |
||||
if (clbkOpenScene && count > 0) { |
||||
SetVisible(false); |
||||
String fileName(paths[0]); |
||||
Util::ensureUnifySlash(fileName); |
||||
if (count > 1) { |
||||
String geometryFileName(paths[1]); |
||||
Util::ensureUnifySlash(geometryFileName); |
||||
clbkOpenScene(fileName, geometryFileName); |
||||
} else { |
||||
clbkOpenScene(fileName, NULL); |
||||
} |
||||
SetVisible(true); |
||||
} |
||||
} |
||||
void Window::Drop(GLFWwindow* window, int count, const char** paths) |
||||
{ |
||||
g_mapWindows[window]->Drop(count, paths); |
||||
} |
||||
|
||||
bool Window::IsShiftKeyPressed() const |
||||
{ |
||||
return |
||||
glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || |
||||
glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS; |
||||
} |
||||
bool Window::IsCtrlKeyPressed() const |
||||
{ |
||||
return |
||||
glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || |
||||
glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS; |
||||
} |
||||
bool Window::IsAltKeyPressed() const |
||||
{ |
||||
return |
||||
glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || |
||||
glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Window.h |
||||
* |
||||
* Copyright (c) 2014-2015 SEACAVE |
||||
* |
||||
* Author(s): |
||||
* |
||||
* cDc <cdc.seacave@gmail.com> |
||||
* |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* |
||||
* Additional Terms: |
||||
* |
||||
* You are required to preserve legal notices and author attributions in |
||||
* that material or in the Appropriate Legal Notices displayed by works |
||||
* containing it. |
||||
*/ |
||||
|
||||
#ifndef _VIEWER_WINDOW_H_ |
||||
#define _VIEWER_WINDOW_H_ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Camera.h" |
||||
#include "Image.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace VIEWER { |
||||
|
||||
class Window |
||||
{ |
||||
public: |
||||
GLFWwindow* window; // window handle
|
||||
Camera camera; // current camera
|
||||
Eigen::Vector2d pos, prevPos; // current and previous mouse position (normalized)
|
||||
Eigen::Matrix4d transform; // view matrix corresponding to the currently selected image
|
||||
cv::Size size; // resolution in pixels, sometimes not equal to window resolution, ex. on Retina display
|
||||
double sizeScale; // window/screen resolution scale
|
||||
|
||||
enum INPUT : unsigned { |
||||
INP_NA = 0, |
||||
INP_MOUSE_LEFT = (1 << 0), |
||||
INP_MOUSE_MIDDLE = (1 << 1), |
||||
INP_MOUSE_RIGHT = (1 << 2), |
||||
}; |
||||
Flags inputType; |
||||
|
||||
enum SPARSE { |
||||
SPR_NONE = 0, |
||||
SPR_POINTS = (1 << 0), |
||||
SPR_LINES = (1 << 1), |
||||
SPR_ALL = SPR_POINTS|SPR_LINES |
||||
}; |
||||
SPARSE sparseType; |
||||
unsigned minViews; |
||||
float pointSize; |
||||
float cameraBlend; |
||||
bool bRenderCameras; |
||||
bool bRenderCameraTrajectory; |
||||
bool bRenderImageVisibility; |
||||
bool bRenderViews; |
||||
bool bRenderSolid; |
||||
bool bRenderTexture; |
||||
bool bRenderBounds; |
||||
|
||||
enum SELECTION { |
||||
SEL_NA = 0, |
||||
SEL_POINT, |
||||
SEL_TRIANGLE |
||||
}; |
||||
SELECTION selectionType; |
||||
Point3f selectionPoints[4]; |
||||
double selectionTimeClick, selectionTime; |
||||
IDX selectionIdx; |
||||
|
||||
typedef DELEGATE<bool (LPCTSTR, LPCTSTR)> ClbkOpenScene; |
||||
ClbkOpenScene clbkOpenScene; |
||||
typedef DELEGATE<bool (LPCTSTR, bool)> ClbkSaveScene; |
||||
ClbkSaveScene clbkSaveScene; |
||||
typedef DELEGATE<bool (LPCTSTR, LPCTSTR)> ClbkExportScene; |
||||
ClbkExportScene clbkExportScene; |
||||
typedef DELEGATE<void (void)> ClbkCenterScene; |
||||
ClbkCenterScene clbkCenterScene; |
||||
typedef DELEGATE<void (const Ray3&, int)> ClbkRayScene; |
||||
ClbkRayScene clbkRayScene; |
||||
typedef DELEGATE<void (void)> ClbkCompilePointCloud; |
||||
ClbkCompilePointCloud clbkCompilePointCloud; |
||||
typedef DELEGATE<void (void)> ClbkCompileMesh; |
||||
ClbkCompileMesh clbkCompileMesh; |
||||
typedef DELEGATE<void (void)> ClbkCompileBounds; |
||||
ClbkCompileBounds clbkCompileBounds; |
||||
typedef DELEGATE<void (void)> ClbkTogleSceneBox; |
||||
ClbkTogleSceneBox clbkTogleSceneBox; |
||||
typedef DELEGATE<void (void)> ClbkCropToBounds; |
||||
ClbkCropToBounds clbkCropToBounds; |
||||
|
||||
typedef std::unordered_map<GLFWwindow*,Window*> WindowsMap; |
||||
static WindowsMap g_mapWindows; |
||||
|
||||
public: |
||||
Window(); |
||||
~Window(); |
||||
|
||||
void Release(); |
||||
void ReleaseClbk(); |
||||
inline bool IsValid() const { return window != NULL; } |
||||
|
||||
inline GLFWwindow* GetWindow() { return window; } |
||||
|
||||
bool Init(const cv::Size&, LPCTSTR name); |
||||
void SetCamera(const Camera&); |
||||
void SetName(LPCTSTR); |
||||
void SetVisible(bool); |
||||
bool IsVisible() const; |
||||
void Reset(SPARSE sparseType=SPR_ALL, unsigned minViews=2); |
||||
|
||||
void CenterCamera(const Point3&); |
||||
|
||||
void UpdateView(const ImageArr&, const MVS::ImageArr&); |
||||
void UpdateView(const Eigen::Matrix3d& R, const Eigen::Vector3d& t); |
||||
void UpdateMousePosition(double xpos, double ypos); |
||||
|
||||
void GetFrame(Image8U3&) const; |
||||
|
||||
cv::Size GetSize() const; |
||||
void Resize(const cv::Size&); |
||||
static void Resize(GLFWwindow* window, int width, int height); |
||||
void Key(int k, int scancode, int action, int mod); |
||||
static void Key(GLFWwindow* window, int k, int scancode, int action, int mod); |
||||
void MouseButton(int button, int action, int mods); |
||||
static void MouseButton(GLFWwindow* window, int button, int action, int mods); |
||||
void MouseMove(double xpos, double ypos); |
||||
static void MouseMove(GLFWwindow* window, double xpos, double ypos); |
||||
void Scroll(double xoffset, double yoffset); |
||||
static void Scroll(GLFWwindow* window, double xoffset, double yoffset); |
||||
void Drop(int count, const char** paths); |
||||
static void Drop(GLFWwindow* window, int count, const char** paths); |
||||
|
||||
protected: |
||||
bool IsShiftKeyPressed() const; |
||||
bool IsCtrlKeyPressed() const; |
||||
bool IsAltKeyPressed() const; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace VIEWER
|
||||
|
||||
#endif // _VIEWER_WINDOW_H_
|
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
# Add libraries |
||||
ADD_SUBDIRECTORY(Common) |
||||
ADD_SUBDIRECTORY(Math) |
||||
ADD_SUBDIRECTORY(IO) |
||||
ADD_SUBDIRECTORY(MVS) |
||||
|
||||
# Install |
||||
INSTALL(FILES "MVS.h" DESTINATION "${INSTALL_INCLUDE_DIR}") |
||||
@ -0,0 +1,121 @@
@@ -0,0 +1,121 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// AABB.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_AABB_H__ |
||||
#define __SEACAVE_AABB_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
// Basic axis-aligned bounding-box class
|
||||
template <typename TYPE, int DIMS> |
||||
class TAABB |
||||
{ |
||||
STATIC_ASSERT(DIMS > 0 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef TYPE Type; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
typedef Eigen::Matrix<TYPE,DIMS,DIMS,Eigen::RowMajor> MATRIX; |
||||
enum { numChildren = (2<<(DIMS-1)) }; |
||||
enum { numCorners = (DIMS==1 ? 2 : (DIMS==2 ? 4 : 8)) }; // 2^DIMS
|
||||
enum { numScalar = (2*DIMS) }; |
||||
|
||||
POINT ptMin, ptMax; // box extreme points
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TAABB() {} |
||||
inline TAABB(bool); |
||||
inline TAABB(const POINT& _pt); |
||||
inline TAABB(const POINT& _ptMin, const POINT& _ptMax); |
||||
inline TAABB(const POINT& center, const TYPE& radius); |
||||
template <typename TPoint> |
||||
inline TAABB(const TPoint* pts, size_t n); |
||||
template <typename CTYPE> |
||||
inline TAABB(const TAABB<CTYPE, DIMS>&); |
||||
|
||||
inline void Reset(); |
||||
inline void Set(const POINT& _pt); |
||||
inline void Set(const POINT& _ptMin, const POINT& _ptMax); |
||||
inline void Set(const POINT& center, const TYPE& radius); |
||||
template <typename TPoint> |
||||
inline void Set(const TPoint* pts, size_t n); |
||||
|
||||
inline bool IsEmpty() const; |
||||
|
||||
inline TAABB& Enlarge(TYPE); |
||||
inline TAABB& EnlargePercent(TYPE); |
||||
|
||||
void InsertFull(const POINT&); |
||||
void Insert(const POINT&); |
||||
void Insert(const TAABB&); |
||||
void BoundBy(const TAABB&); |
||||
|
||||
inline void Translate(const POINT&); |
||||
inline void Transform(const MATRIX&); |
||||
|
||||
inline POINT GetCenter() const; |
||||
inline void GetCenter(POINT&) const; |
||||
|
||||
inline POINT GetSize() const; |
||||
inline void GetSize(POINT&) const; |
||||
|
||||
inline void GetCorner(BYTE i, POINT&) const; |
||||
inline POINT GetCorner(BYTE i) const; |
||||
inline void GetCorners(POINT pts[numCorners]) const; |
||||
|
||||
bool Intersects(const TAABB&) const; |
||||
bool IntersectsComplete(const TAABB&, TYPE) const; |
||||
bool IntersectsComplete(const TAABB&) const; |
||||
bool Intersects(const POINT&) const; |
||||
bool IntersectsComplete(const POINT&, TYPE) const; |
||||
bool IntersectsComplete(const POINT&) const; |
||||
|
||||
unsigned SplitBy(TYPE, int, TAABB [2]) const; |
||||
unsigned SplitBy(const POINT&, TAABB [numChildren], unsigned&) const; |
||||
|
||||
inline TYPE& operator [] (BYTE i) { ASSERT(i<numScalar); return ptMin.data()[i]; } |
||||
inline TYPE operator [] (BYTE i) const { ASSERT(i<numScalar); return ptMin.data()[i]; } |
||||
|
||||
friend std::ostream& operator << (std::ostream& st, const TAABB& obb) { |
||||
st << obb.ptMin; st << std::endl; |
||||
st << obb.ptMax; st << std::endl; |
||||
return st; |
||||
} |
||||
friend std::istream& operator >> (std::istream& st, TAABB& obb) { |
||||
st >> obb.ptMin; |
||||
st >> obb.ptMax; |
||||
return st; |
||||
} |
||||
|
||||
#ifdef _USE_BOOST |
||||
// implement BOOST serialization
|
||||
template<class Archive> |
||||
void serialize(Archive& ar, const unsigned int /*version*/) { |
||||
ar & ptMin; |
||||
ar & ptMax; |
||||
} |
||||
#endif |
||||
}; // class TAABB
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
#include "AABB.inl" |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_AABB_H__
|
||||
@ -0,0 +1,419 @@
@@ -0,0 +1,419 @@
|
||||
//////////////////////////////////////////////////////////////////// |
||||
// AABB.inl |
||||
// |
||||
// Copyright 2007 cDc@seacave |
||||
// Distributed under the Boost Software License, Version 1.0 |
||||
// (See http://www.boost.org/LICENSE_1_0.txt) |
||||
|
||||
|
||||
// D E F I N E S /////////////////////////////////////////////////// |
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline TAABB<TYPE,DIMS>::TAABB(bool) |
||||
: |
||||
ptMin(POINT::Constant(std::numeric_limits<TYPE>::max())), |
||||
ptMax(POINT::Constant(std::numeric_limits<TYPE>::lowest())) |
||||
{ |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TAABB<TYPE,DIMS>::TAABB(const POINT& _pt) |
||||
: |
||||
ptMin(_pt), ptMax(_pt) |
||||
{ |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TAABB<TYPE,DIMS>::TAABB(const POINT& _ptMin, const POINT& _ptMax) |
||||
: |
||||
ptMin(_ptMin), ptMax(_ptMax) |
||||
{ |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TAABB<TYPE,DIMS>::TAABB(const POINT& center, const TYPE& radius) |
||||
: |
||||
ptMin(center-POINT::Constant(radius)), ptMax(center+POINT::Constant(radius)) |
||||
{ |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
template <typename TPoint> |
||||
inline TAABB<TYPE,DIMS>::TAABB(const TPoint* pts, size_t n) |
||||
{ |
||||
Set(pts, n); |
||||
} // constructor |
||||
template <typename TYPE, int DIMS> |
||||
template <typename CTYPE> |
||||
inline TAABB<TYPE,DIMS>::TAABB(const TAABB<CTYPE,DIMS>& rhs) |
||||
: |
||||
ptMin(rhs.ptMin.template cast<TYPE>()), ptMax(rhs.ptMax.template cast<TYPE>()) |
||||
{ |
||||
} // copy constructor |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::Reset() |
||||
{ |
||||
ptMin = POINT::Constant(std::numeric_limits<TYPE>::max()); |
||||
ptMax = POINT::Constant(std::numeric_limits<TYPE>::lowest()); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::Set(const POINT& _pt) |
||||
{ |
||||
ptMin = ptMax = _pt; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::Set(const POINT& _ptMin, const POINT& _ptMax) |
||||
{ |
||||
ptMin = _ptMin; |
||||
ptMax = _ptMax; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::Set(const POINT& center, const TYPE& radius) |
||||
{ |
||||
ptMin = center-POINT::Constant(radius); |
||||
ptMax = center+POINT::Constant(radius); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
template <typename TPoint> |
||||
inline void TAABB<TYPE,DIMS>::Set(const TPoint* pts, size_t n) |
||||
{ |
||||
ASSERT(n > 0); |
||||
ptMin = ptMax = pts[0]; |
||||
for (size_t i=1; i<n; ++i) |
||||
Insert(pts[i]); |
||||
} // Set |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline bool TAABB<TYPE,DIMS>::IsEmpty() const |
||||
{ |
||||
for (int i=0; i<DIMS; ++i) |
||||
if (ptMin[i] >= ptMax[i]) |
||||
return true; |
||||
return false; |
||||
} // IsEmpty |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline TAABB<TYPE,DIMS>& TAABB<TYPE,DIMS>::Enlarge(TYPE x) |
||||
{ |
||||
ptMin.array() -= x; |
||||
ptMax.array() += x; |
||||
return *this; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TAABB<TYPE,DIMS>& TAABB<TYPE,DIMS>::EnlargePercent(TYPE x) |
||||
{ |
||||
const POINT ptSizeDelta(GetSize() * (x - TYPE(1)) / TYPE(2)); |
||||
ptMin -= ptSizeDelta; |
||||
ptMax += ptSizeDelta; |
||||
return *this; |
||||
} // Enlarge |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline typename TAABB<TYPE,DIMS>::POINT TAABB<TYPE,DIMS>::GetCenter() const |
||||
{ |
||||
return (ptMax + ptMin) * TYPE(0.5); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::GetCenter(POINT& ptCenter) const |
||||
{ |
||||
ptCenter = (ptMax + ptMin) * TYPE(0.5); |
||||
} // GetCenter |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline typename TAABB<TYPE,DIMS>::POINT TAABB<TYPE,DIMS>::GetSize() const |
||||
{ |
||||
return ptMax - ptMin; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::GetSize(POINT& ptSize) const |
||||
{ |
||||
ptSize = ptMax - ptMin; |
||||
} // GetSize |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::GetCorner(BYTE i, POINT& ptCorner) const |
||||
{ |
||||
ASSERT(i<numCorners); |
||||
if (DIMS == 1) { |
||||
ptCorner(0) = operator[](i); |
||||
} |
||||
if (DIMS == 2) { |
||||
ptCorner(0) = operator[]((i/2)*2 + 0); |
||||
ptCorner(1) = operator[]((i%2)*2 + 1); |
||||
} |
||||
if (DIMS == 3) { |
||||
ptCorner(0) = operator[]((i/4)*3 + 0); |
||||
ptCorner(1) = operator[](((i/2)%2)*3 + 1); |
||||
ptCorner(2) = operator[]((i%2)*3 + 2); |
||||
} |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TAABB<TYPE,DIMS>::POINT TAABB<TYPE,DIMS>::GetCorner(BYTE i) const |
||||
{ |
||||
POINT ptCorner; |
||||
GetCorner(i, ptCorner); |
||||
return ptCorner; |
||||
} // GetCorner |
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::GetCorners(POINT pts[numCorners]) const |
||||
{ |
||||
for (BYTE i=0; i<numCorners; ++i) |
||||
GetCorner(i, pts[i]); |
||||
} // GetCorners |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Update the box by the given pos delta. |
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::Translate(const POINT& d) |
||||
{ |
||||
ptMin += d; |
||||
ptMax += d; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Update the box by the given transform. |
||||
template <typename TYPE, int DIMS> |
||||
inline void TAABB<TYPE,DIMS>::Transform(const MATRIX& m) |
||||
{ |
||||
ptMin = m * ptMin; |
||||
ptMax = m * ptMax; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Update the box such that it contains the given point. |
||||
template <typename TYPE, int DIMS> |
||||
void TAABB<TYPE,DIMS>::InsertFull(const POINT& pt) |
||||
{ |
||||
if (ptMin[0] > pt[0]) |
||||
ptMin[0] = pt[0]; |
||||
if (ptMax[0] < pt[0]) |
||||
ptMax[0] = pt[0]; |
||||
|
||||
if (DIMS > 1) { |
||||
if (ptMin[1] > pt[1]) |
||||
ptMin[1] = pt[1]; |
||||
if (ptMax[1] < pt[1]) |
||||
ptMax[1] = pt[1]; |
||||
} |
||||
|
||||
if (DIMS > 2) { |
||||
if (ptMin[2] > pt[2]) |
||||
ptMin[2] = pt[2]; |
||||
if (ptMax[2] < pt[2]) |
||||
ptMax[2] = pt[2]; |
||||
} |
||||
} |
||||
// same as above, but for the initialized case |
||||
template <typename TYPE, int DIMS> |
||||
void TAABB<TYPE,DIMS>::Insert(const POINT& pt) |
||||
{ |
||||
if (ptMin[0] > pt[0]) |
||||
ptMin[0] = pt[0]; |
||||
else if (ptMax[0] < pt[0]) |
||||
ptMax[0] = pt[0]; |
||||
|
||||
if (DIMS > 1) { |
||||
if (ptMin[1] > pt[1]) |
||||
ptMin[1] = pt[1]; |
||||
else if (ptMax[1] < pt[1]) |
||||
ptMax[1] = pt[1]; |
||||
} |
||||
|
||||
if (DIMS > 2) { |
||||
if (ptMin[2] > pt[2]) |
||||
ptMin[2] = pt[2]; |
||||
else if (ptMax[2] < pt[2]) |
||||
ptMax[2] = pt[2]; |
||||
} |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// Update the box such that it contains the given bounding box. |
||||
template <typename TYPE, int DIMS> |
||||
void TAABB<TYPE,DIMS>::Insert(const TAABB& aabb) |
||||
{ |
||||
if (ptMin[0] > aabb.ptMin[0]) |
||||
ptMin[0] = aabb.ptMin[0]; |
||||
if (ptMax[0] < aabb.ptMax[0]) |
||||
ptMax[0] = aabb.ptMax[0]; |
||||
|
||||
if (DIMS > 1) { |
||||
if (ptMin[1] > aabb.ptMin[1]) |
||||
ptMin[1] = aabb.ptMin[1]; |
||||
if (ptMax[1] < aabb.ptMax[1]) |
||||
ptMax[1] = aabb.ptMax[1]; |
||||
} |
||||
|
||||
if (DIMS > 2) { |
||||
if (ptMin[2] > aabb.ptMin[2]) |
||||
ptMin[2] = aabb.ptMin[2]; |
||||
if (ptMax[2] < aabb.ptMax[2]) |
||||
ptMax[2] = aabb.ptMax[2]; |
||||
} |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// Update the box such that it does not exceed the given bounding box. |
||||
template <typename TYPE, int DIMS> |
||||
void TAABB<TYPE,DIMS>::BoundBy(const TAABB& aabb) |
||||
{ |
||||
if (ptMin[0] < aabb.ptMin[0]) |
||||
ptMin[0] = aabb.ptMin[0]; |
||||
if (ptMax[0] > aabb.ptMax[0]) |
||||
ptMax[0] = aabb.ptMax[0]; |
||||
|
||||
if (DIMS > 1) { |
||||
if (ptMin[1] < aabb.ptMin[1]) |
||||
ptMin[1] = aabb.ptMin[1]; |
||||
if (ptMax[1] > aabb.ptMax[1]) |
||||
ptMax[1] = aabb.ptMax[1]; |
||||
} |
||||
|
||||
if (DIMS > 2) { |
||||
if (ptMin[2] < aabb.ptMin[2]) |
||||
ptMin[2] = aabb.ptMin[2]; |
||||
if (ptMax[2] > aabb.ptMax[2]) |
||||
ptMax[2] = aabb.ptMax[2]; |
||||
} |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// intersection between two AABBs |
||||
template <typename TYPE, int DIMS> |
||||
bool TAABB<TYPE,DIMS>::Intersects(const TAABB& aabb) const |
||||
{ |
||||
if (aabb.ptMax[0] < ptMin[0]) return false; |
||||
if (aabb.ptMin[0] >= ptMax[0]) return false; |
||||
if (DIMS > 1) { |
||||
if (aabb.ptMax[1] < ptMin[1]) return false; |
||||
if (aabb.ptMin[1] >= ptMax[1]) return false; |
||||
} |
||||
if (DIMS > 2) { |
||||
if (aabb.ptMax[2] < ptMin[2]) return false; |
||||
if (aabb.ptMin[2] >= ptMax[2]) return false; |
||||
} |
||||
return true; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
bool TAABB<TYPE,DIMS>::IntersectsComplete(const TAABB& aabb, TYPE tolerance) const |
||||
{ |
||||
if (aabb.ptMax[0]+tolerance < ptMin[0]) return false; |
||||
if (aabb.ptMin[0] > ptMax[0]+tolerance) return false; |
||||
if (DIMS > 1) { |
||||
if (aabb.ptMax[1]+tolerance < ptMin[1]) return false; |
||||
if (aabb.ptMin[1] > ptMax[1]+tolerance) return false; |
||||
} |
||||
if (DIMS > 2) { |
||||
if (aabb.ptMax[2]+tolerance < ptMin[2]) return false; |
||||
if (aabb.ptMin[2] > ptMax[2]+tolerance) return false; |
||||
} |
||||
return true; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
bool TAABB<TYPE,DIMS>::IntersectsComplete(const TAABB& aabb) const |
||||
{ |
||||
return IntersectsComplete(aabb, ZEROTOLERANCE<TYPE>()); |
||||
} // Intersects(TAABB) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// does TAABB contain the given point |
||||
template <typename TYPE, int DIMS> |
||||
bool TAABB<TYPE,DIMS>::Intersects(const POINT& pt) const |
||||
{ |
||||
if (pt[0] < ptMin[0]) return false; |
||||
if (pt[0] >= ptMax[0]) return false; |
||||
if (DIMS > 1) { |
||||
if (pt[1] < ptMin[1]) return false; |
||||
if (pt[1] >= ptMax[1]) return false; |
||||
} |
||||
if (DIMS > 2) { |
||||
if (pt[2] < ptMin[2]) return false; |
||||
if (pt[2] >= ptMax[2]) return false; |
||||
} |
||||
return true; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
bool TAABB<TYPE,DIMS>::IntersectsComplete(const POINT& pt, TYPE tolerance) const |
||||
{ |
||||
if (pt[0]+tolerance < ptMin[0]) return false; |
||||
if (pt[0] > ptMax[0]+tolerance) return false; |
||||
if (DIMS > 1) { |
||||
if (pt[1]+tolerance < ptMin[1]) return false; |
||||
if (pt[1] > ptMax[1]+tolerance) return false; |
||||
} |
||||
if (DIMS > 2) { |
||||
if (pt[2]+tolerance < ptMin[2]) return false; |
||||
if (pt[2] > ptMax[2]+tolerance) return false; |
||||
} |
||||
return true; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
bool TAABB<TYPE,DIMS>::IntersectsComplete(const POINT& pt) const |
||||
{ |
||||
return IntersectsComplete(pt, ZEROTOLERANCE<TYPE>()); |
||||
} // Intersects(point) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// split this TAABB by the given axis |
||||
// returns number of children (0 or 2) |
||||
template <typename TYPE, int DIMS> |
||||
unsigned TAABB<TYPE,DIMS>::SplitBy(TYPE d, int a, TAABB child[2]) const |
||||
{ |
||||
ASSERT(a < DIMS); |
||||
if (d >= ptMin[a] && d < ptMax[a]) { |
||||
TAABB& child0 = child[0]; |
||||
child0.Set(ptMin, ptMax); |
||||
TAABB& child1 = child[1]; |
||||
child1.Set(ptMin, ptMax); |
||||
child0.ptMax[a] = child1.ptMin[a] = d; |
||||
return 2; |
||||
} |
||||
return 0; |
||||
} // SplitBy(axis) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// split this TAABB by the given point |
||||
// returns number of children (0 - numScalar) |
||||
// idxFirst returns the index of the first aabb |
||||
template <typename TYPE, int DIMS> |
||||
unsigned TAABB<TYPE,DIMS>::SplitBy(const POINT& pt, TAABB child[numChildren], unsigned& idxFirst) const |
||||
{ |
||||
idxFirst = 0; |
||||
unsigned size; |
||||
int a = 0; |
||||
do { |
||||
size = SplitBy(pt[a], a, child); |
||||
if (++a == DIMS) |
||||
return size; |
||||
} while (size == 0); |
||||
do { |
||||
unsigned n = 0; |
||||
for (unsigned b=0; b<size; ++b) { |
||||
n += child[(idxFirst+b)%numChildren].SplitBy(pt[a], a, child+((idxFirst+size+n)%numChildren)); |
||||
if (n == 0) |
||||
goto MAIN_LOOP_END; |
||||
} |
||||
idxFirst += size; |
||||
size = n; |
||||
MAIN_LOOP_END:; |
||||
} while (++a < DIMS); |
||||
return size; |
||||
} // SplitBy(point) |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,668 @@
@@ -0,0 +1,668 @@
|
||||
#ifndef __SEACAVE_AUTO_ESTIMATOR_H__ |
||||
#define __SEACAVE_AUTO_ESTIMATOR_H__ |
||||
|
||||
//-------------------
|
||||
// Generic implementation of ACRANSAC
|
||||
//-------------------
|
||||
// The A contrario parametrization have been first explained in [1] and
|
||||
// later extended to generic model estimation in [2] (with a demonstration for
|
||||
// the homography) and extended and use at large scale for Structure from
|
||||
// Motion in [3].
|
||||
//
|
||||
//--
|
||||
// [1] Lionel Moisan, Berenger Stival,
|
||||
// A probalistic criterion to detect rigid point matches between
|
||||
// two images and estimate the fundamental matrix.
|
||||
// IJCV 04.
|
||||
//--
|
||||
// [2] Lionel Moisan, Pierre Moulon, Pascal Monasse.
|
||||
// Automatic Homographic Registration of a Pair of Images,
|
||||
// with A Contrario Elimination of Outliers
|
||||
// Image Processing On Line (IPOL), 2012.
|
||||
// http://dx.doi.org/10.5201/ipol.2012.mmm-oh
|
||||
//--
|
||||
// [3] Pierre Moulon, Pascal Monasse and Renaud Marlet.
|
||||
// Adaptive Structure from Motion with a contrario mode estimation.
|
||||
// In 11th Asian Conference on Computer Vision (ACCV 2012)
|
||||
//--
|
||||
// [4] cDc@seacave - rewrite it
|
||||
//
|
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
// uncomment it to enable std::vector implementation
|
||||
// (slower)
|
||||
//#define ACRANSAC_STD_VECTOR
|
||||
|
||||
// uncomment it to enable sampling from inliers
|
||||
// (sometimes does not find the right solution)
|
||||
//#define ACRANSAC_SAMPLE_INLIERS
|
||||
|
||||
|
||||
// P R O T O T Y P E S /////////////////////////////////////////////
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
/// logarithm (base 10) of binomial coefficient
|
||||
static inline double logcombi(size_t k, size_t n) |
||||
{ |
||||
if (k>=n) return(0.0); |
||||
if (n-k<k) k=n-k; |
||||
double r = 0.0; |
||||
for (size_t i = 1; i <= k; i++) |
||||
r += log10((double)(n-i+1))-log10((double)i); |
||||
return r; |
||||
} |
||||
|
||||
/// tabulate logcombi(.,n)
|
||||
template<typename Type> |
||||
static inline void makelogcombi_n(size_t n, std::vector<Type>& l) |
||||
{ |
||||
l.resize(n+1); |
||||
for (size_t k = 0; k <= n; ++k) |
||||
l[k] = static_cast<Type>(logcombi(k,n)); |
||||
} |
||||
|
||||
/// tabulate logcombi(k,.)
|
||||
template<typename Type, int k> |
||||
static inline void makelogcombi_k(size_t nmax, std::vector<Type>& l) |
||||
{ |
||||
l.resize(nmax+1); |
||||
for (size_t n = 0; n <= nmax; ++n) |
||||
l[n] = static_cast<Type>(logcombi(k,n)); |
||||
} |
||||
|
||||
/// Distance and associated index
|
||||
struct ErrorIndex { |
||||
double first; |
||||
size_t second; |
||||
inline ErrorIndex() {} |
||||
inline ErrorIndex(double _first, size_t _second) : first(_first), second(_second) {} |
||||
inline bool operator==(const ErrorIndex& r) const { return (first == r.first); } |
||||
inline bool operator <(const ErrorIndex& r) const { return (first < r.first); } |
||||
}; |
||||
#ifdef ACRANSAC_STD_VECTOR |
||||
typedef std::vector<ErrorIndex> ErrorIndexArr; |
||||
#else |
||||
typedef CLISTDEF0(ErrorIndex) ErrorIndexArr; |
||||
#endif |
||||
|
||||
/// Find best NFA and its index wrt square error threshold in e.
|
||||
template<size_t NSAMPLES> |
||||
static ErrorIndex bestNFA( |
||||
double logalpha0, |
||||
const ErrorIndexArr& e, |
||||
double loge0, |
||||
double maxThresholdSq, |
||||
const std::vector<float>& logc_n, |
||||
const std::vector<float>& logc_k, |
||||
double multError = 1.0) |
||||
{ |
||||
ErrorIndex bestIndex(DBL_MAX, NSAMPLES); |
||||
const size_t n = e.size(); |
||||
for (size_t k=NSAMPLES+1; k<=n && e[k-1].first<=maxThresholdSq; ++k) { |
||||
const double logalpha = logalpha0 + multError * log10(e[k-1].first+FLT_EPSILON); |
||||
const double NFA = loge0 + |
||||
logalpha * (double)(k-NSAMPLES) + |
||||
logc_n[k] + |
||||
logc_k[k]; |
||||
if (NFA < bestIndex.first) |
||||
bestIndex = ErrorIndex(NFA, k); |
||||
} |
||||
return bestIndex; |
||||
} |
||||
|
||||
/// Pick a random subset of the integers [0, total), in random order.
|
||||
/// Note that this can behave badly if num_samples is close to total; runtime
|
||||
/// could be unlimited!
|
||||
///
|
||||
/// This uses a quadratic rejection strategy and should only be used for small
|
||||
/// num_samples.
|
||||
///
|
||||
/// \param NSAMPLES The number of samples to produce.
|
||||
/// \param n The number of samples available.
|
||||
/// \param samples num_samples of numbers in [0, total_samples) is placed
|
||||
/// here on return.
|
||||
struct UniformSampler { |
||||
template <size_t NSAMPLES> |
||||
inline void Sample( |
||||
size_t n, |
||||
size_t* samples) |
||||
{ |
||||
ASSERT(NSAMPLES > 0); |
||||
size_t* const samplesBegin = samples; |
||||
size_t* const samplesEnd = samples + NSAMPLES; |
||||
*samples = size_t(RAND() % n); |
||||
while (++samples < samplesEnd) { |
||||
do { |
||||
*samples = size_t(RAND() % n); |
||||
} while (std::find(samplesBegin, samples, *samples) != samples); |
||||
} |
||||
} |
||||
}; |
||||
/// Same as above, but ensure that the first point is always included
|
||||
struct UniformSamplerLockFirst { |
||||
template <size_t NSAMPLES> |
||||
inline void Sample( |
||||
size_t n, |
||||
size_t* samples) |
||||
{ |
||||
ASSERT(NSAMPLES > 1); |
||||
size_t* const samplesBegin = samples; |
||||
size_t* const samplesEnd = samples + NSAMPLES; |
||||
*samples++ = 0; |
||||
do { |
||||
do { |
||||
*samples = size_t(RAND() % n); |
||||
} while (std::find(samplesBegin, samples, *samples) != samples); |
||||
} while (++samples < samplesEnd); |
||||
} |
||||
}; |
||||
|
||||
/// Pick a random sample:
|
||||
/// get a (sorted) random sample of size NSAMPLES in [0:n-1]
|
||||
/// \param NSAMPLES The number of samples to produce.
|
||||
/// \param vec_index The possible data indices.
|
||||
/// \param n The number of samples available.
|
||||
/// \param samples The random sample of NSAMPLES indices (output).
|
||||
struct UniformSamplerSorted { |
||||
template <size_t NSAMPLES> |
||||
inline void Sample( |
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
const std::vector<size_t>& vec_index, |
||||
#else |
||||
size_t n, |
||||
#endif |
||||
size_t* samples) |
||||
{ |
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
const size_t n = vec_index.size(); |
||||
#endif |
||||
for (size_t i=0; i<NSAMPLES; ++i) { |
||||
size_t r = (RAND()>>3)%(n-i), j; |
||||
for (j=0; j<i && r>=samples[j]; ++j) |
||||
++r; |
||||
size_t j0 = j; |
||||
for (j=i; j>j0; --j) |
||||
samples[j] = samples[j-1]; |
||||
samples[j0] = r; |
||||
} |
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
for (size_t i=0; i<NSAMPLES; ++i) |
||||
samples[i] = vec_index[samples[i]]; |
||||
#endif |
||||
} |
||||
}; |
||||
/// Same as above, but ensure that the first point is always included
|
||||
struct UniformSamplerSortedLockFirst { |
||||
template <size_t NSAMPLES> |
||||
inline void Sample( |
||||
size_t n, |
||||
size_t* samples) |
||||
{ |
||||
ASSERT(NSAMPLES > 1); |
||||
samples[0] = 0; |
||||
for (size_t i=1; i<NSAMPLES; ++i) { |
||||
size_t r = (RAND()>>3)%(n-i), j; |
||||
for (j=0; j<i && r>=samples[j]; ++j) |
||||
++r; |
||||
size_t j0 = j; |
||||
for (j=i; j>j0; --j) |
||||
samples[j] = samples[j-1]; |
||||
samples[j0] = r; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
/// Returns the requested matrix rows
|
||||
template <typename TMat, typename TRows> |
||||
TMat ExtractRows(const TMat &A, const TRows &rows) { |
||||
TMat compressed((int)rows.size(), A.cols); |
||||
for (size_t i=0; i<static_cast<size_t>(rows.size()); ++i) |
||||
A.row(rows[i]).copyTo(compressed.row((int)i)); |
||||
return compressed; |
||||
} |
||||
|
||||
/// ACRANSAC routine (ErrorThreshold, NFA)
|
||||
template <typename Kernel, typename Sampler> |
||||
std::pair<double, double> ACRANSAC( |
||||
Kernel& kernel, |
||||
Sampler& sampler, |
||||
std::vector<size_t>& vec_inliers, |
||||
typename Kernel::Model& model, |
||||
double maxThreshold = DBL_MAX, |
||||
#ifndef ACRANSAC_SAMPLE_INLIERS |
||||
double confidence = 0.9999, |
||||
#endif |
||||
size_t nIter = 0) |
||||
{ |
||||
const size_t nData = kernel.NumSamples(); |
||||
if (nData < Kernel::MINIMUM_SAMPLES) { |
||||
vec_inliers.clear(); |
||||
return std::make_pair(0.0,0.0); |
||||
} |
||||
const double maxThresholdSq = SQUARE(maxThreshold); |
||||
std::vector<size_t> vec_sample(Kernel::MINIMUM_SAMPLES); // sample indices
|
||||
typename Kernel::Models vec_models; // up to max_models solutions
|
||||
if (nData == Kernel::MINIMUM_SAMPLES) { |
||||
vec_inliers.resize(Kernel::MINIMUM_SAMPLES); |
||||
std::iota(vec_inliers.begin(), vec_inliers.end(), 0); |
||||
if (!kernel.Fit(vec_inliers, vec_models) || vec_models.size() != 1) { |
||||
vec_inliers.clear(); |
||||
return std::make_pair(0.0,0.0); |
||||
} |
||||
model = vec_models[0]; |
||||
return std::make_pair(maxThresholdSq,0.0); |
||||
} |
||||
|
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
// Possible sampling indices (could change in the optimization phase)
|
||||
std::vector<size_t> vec_index(nData); |
||||
for (size_t i = 0; i < nData; ++i) |
||||
vec_index[i] = i; |
||||
#endif |
||||
|
||||
// Precompute log combi
|
||||
const double loge0 = log10((double)Kernel::MAX_MODELS * (nData-Kernel::MINIMUM_SAMPLES)); |
||||
std::vector<float> vec_logc_n, vec_logc_k; |
||||
makelogcombi_n<float>(nData, vec_logc_n); |
||||
makelogcombi_k<float, Kernel::MINIMUM_SAMPLES>(nData, vec_logc_k); |
||||
|
||||
// Output parameters
|
||||
double minNFA = DBL_MAX; |
||||
double errorSqMax = DBL_MAX; |
||||
|
||||
// Reserve 10% of iterations for focused sampling
|
||||
if (nIter == 0) |
||||
nIter = MINF((size_t)4000, MAXF((size_t)300, nData*3/2)); |
||||
const size_t nMinIter = nIter*8/100; |
||||
|
||||
// Main estimation loop
|
||||
ErrorIndexArr vec_residuals(nData); // [residual,index]
|
||||
for (size_t iter=0; iter<nIter || iter<nMinIter; ++iter) { |
||||
// Get random sample
|
||||
sampler.template Sample<Kernel::MINIMUM_SAMPLES>( |
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
vec_index, |
||||
#else |
||||
nData, |
||||
#endif |
||||
&vec_sample[0] |
||||
); |
||||
|
||||
if (!kernel.Fit(vec_sample, vec_models)) |
||||
continue; |
||||
|
||||
// Evaluate models
|
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
bool better = false; |
||||
#else |
||||
const size_t nTrialsRound = nIter; |
||||
#endif |
||||
for (typename Kernel::Models::const_iterator itModel=vec_models.cbegin(); itModel!=vec_models.cend(); ++itModel) { |
||||
// Residuals computation and ordering
|
||||
kernel.EvaluateModel(*itModel); |
||||
for (size_t i = 0; i < nData; ++i) |
||||
vec_residuals[i] = ErrorIndex(kernel.Error(i), i); |
||||
std::sort(vec_residuals.begin(), vec_residuals.end()); |
||||
|
||||
// Most meaningful discrimination inliers/outliers
|
||||
ErrorIndex best = bestNFA<Kernel::MINIMUM_SAMPLES>( |
||||
kernel.logalpha0(), |
||||
vec_residuals, |
||||
loge0, |
||||
maxThresholdSq, |
||||
vec_logc_n, |
||||
vec_logc_k, |
||||
kernel.multError()); |
||||
|
||||
if (best.first < minNFA /*&& vec_residuals[best.second-1].first < errorSqMax*/) { |
||||
// A better model was found
|
||||
minNFA = best.first; |
||||
vec_inliers.resize(best.second); |
||||
for (size_t i=0; i<best.second; ++i) |
||||
vec_inliers[i] = vec_residuals[i].second; |
||||
errorSqMax = vec_residuals[best.second-1].first; // error threshold
|
||||
model = *itModel; |
||||
DEBUG_LEVEL(4, "\titer=% 3u inliers=%u threshold=%g nfa=%g", iter, best.second, SQRT(errorSqMax), minNFA); |
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
better = true; |
||||
#else |
||||
nIter = cvRANSACUpdateNumIters(confidence, (double)(nData - best.second)/nData, unsigned(Kernel::MINIMUM_SAMPLES), (unsigned)nTrialsRound); |
||||
#endif |
||||
} |
||||
} |
||||
vec_models.clear(); |
||||
|
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
if (better && minNFA<0 && vec_inliers.size()>nData/3) { |
||||
// Optimization: draw samples among best set of inliers so far
|
||||
vec_index = vec_inliers; |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
if (minNFA >= 0) |
||||
vec_inliers.clear(); |
||||
|
||||
return std::make_pair(errorSqMax, minNFA); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/// RANSAC routine (standard robust fitter, based on a known threshold)
|
||||
template <typename Kernel, typename Sampler> |
||||
void RANSAC( |
||||
Kernel& kernel, |
||||
Sampler& sampler, |
||||
std::vector<size_t>& vec_inliers, |
||||
typename Kernel::Model& model, |
||||
double threshold, |
||||
#ifndef ACRANSAC_SAMPLE_INLIERS |
||||
double confidence = 0.9999, |
||||
#endif |
||||
size_t nIter = 0) |
||||
{ |
||||
vec_inliers.clear(); |
||||
const size_t nData = kernel.NumSamples(); |
||||
if (nData < Kernel::MINIMUM_SAMPLES) |
||||
return; |
||||
const double thresholdSq = SQUARE(threshold); |
||||
std::vector<size_t> vec_sample(Kernel::MINIMUM_SAMPLES); // sample indices
|
||||
typename Kernel::Models vec_models; // up to max_models solutions
|
||||
|
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
// Possible sampling indices (could change in the optimization phase)
|
||||
std::vector<size_t> vec_index(nData); |
||||
for (size_t i = 0; i < nData; ++i) |
||||
vec_index[i] = i; |
||||
#endif |
||||
|
||||
// Reserve 10% of iterations for focused sampling
|
||||
if (nIter == 0) |
||||
nIter = MINF((size_t)4000, MAXF((size_t)300, nData*3/2)); |
||||
const size_t nMinIter = nIter*8/100; |
||||
|
||||
// Main estimation loop
|
||||
#ifdef _USE_MSAC |
||||
double best_score = thresholdSq*nData; |
||||
#endif |
||||
std::vector<size_t> vec_inliers_tmp; vec_inliers_tmp.reserve(nData); vec_inliers.reserve(nData); |
||||
for (size_t iter=0; iter<nIter || iter<nMinIter; ++iter) { |
||||
// Get random sample
|
||||
sampler.template Sample<Kernel::MINIMUM_SAMPLES>( |
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
vec_index, |
||||
#else |
||||
nData, |
||||
#endif |
||||
&vec_sample[0] |
||||
); |
||||
|
||||
if (!kernel.Fit(vec_sample, vec_models)) |
||||
continue; |
||||
|
||||
// Evaluate models
|
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
bool better = false; |
||||
#else |
||||
const size_t nTrialsRound = nIter; |
||||
#endif |
||||
for (typename Kernel::Models::const_iterator itModel=vec_models.cbegin(); itModel!=vec_models.cend(); ++itModel) { |
||||
// Residuals computation and ordering
|
||||
#ifdef _USE_MSAC |
||||
double score = 0; |
||||
#endif |
||||
vec_inliers_tmp.clear(); |
||||
kernel.EvaluateModel(*itModel); |
||||
for (size_t i = 0; i < nData; ++i) { |
||||
const double errSq = kernel.Error(i); |
||||
if (errSq <= thresholdSq) { |
||||
#ifdef _USE_MSAC |
||||
score += errSq; |
||||
#endif |
||||
vec_inliers_tmp.push_back(i); |
||||
} else { |
||||
#ifdef _USE_MSAC |
||||
score += thresholdSq; |
||||
#endif |
||||
} |
||||
#ifdef _USE_MSAC |
||||
if (score >= best_score) |
||||
break; |
||||
#endif |
||||
} |
||||
// Store best model
|
||||
#ifdef _USE_MSAC |
||||
if (score < best_score) { |
||||
#else |
||||
if (vec_inliers_tmp.size() > vec_inliers.size()) { |
||||
#endif |
||||
// A better model was found
|
||||
model = *itModel; |
||||
vec_inliers.swap(vec_inliers_tmp); |
||||
if (vec_inliers.size() == nData) { |
||||
nIter = 0; // force loop exit
|
||||
break; |
||||
} |
||||
#ifdef _USE_MSAC |
||||
best_score = score; |
||||
DEBUG_LEVEL(4, "\titer=%3u inliers=%u score=%g", iter, vec_inliers.size(), best_score); |
||||
#else |
||||
DEBUG_LEVEL(4, "\titer=%3u inliers=%u", iter, vec_inliers.size()); |
||||
#endif |
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
better = true; |
||||
#else |
||||
#ifdef _USE_MSAC |
||||
nIter = cvRANSACUpdateNumIters(confidence, (double)score/((double)nData*thresholdSq), unsigned(Kernel::MINIMUM_SAMPLES), (unsigned)nTrialsRound); |
||||
#else |
||||
nIter = cvRANSACUpdateNumIters(confidence, (double)(nData-vec_inliers.size())/nData, unsigned(Kernel::MINIMUM_SAMPLES), (unsigned)nTrialsRound); |
||||
#endif |
||||
#endif |
||||
} |
||||
} |
||||
vec_models.clear(); |
||||
|
||||
#ifdef ACRANSAC_SAMPLE_INLIERS |
||||
if (better && vec_inliers.size()>nData/3) { |
||||
// Optimization: draw samples among best set of inliers so far
|
||||
vec_index = vec_inliers; |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
if (vec_inliers.size() < Kernel::MINIMUM_SAMPLES) |
||||
vec_inliers.clear(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/// Two view Kernel adaptator for the A contrario model estimator
|
||||
/// Handle data normalization and compute the corresponding logalpha 0
|
||||
/// that depends of the error model (point to line, or point to point)
|
||||
/// This kernel adaptor is working for affine, homography, fundamental matrix
|
||||
/// estimation.
|
||||
template <typename SOLVER> |
||||
class ACKernelAdaptor |
||||
{ |
||||
public: |
||||
typedef SOLVER Solver; |
||||
typedef typename SOLVER::Model Model; |
||||
typedef typename SOLVER::Models Models; |
||||
|
||||
enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES }; |
||||
enum { MAX_MODELS = Solver::MAX_MODELS }; |
||||
|
||||
ACKernelAdaptor( |
||||
const DMatrix32F &x1, float w1, float h1, |
||||
const DMatrix32F &x2, float w2, float h2, |
||||
bool bPointToLine = true) |
||||
: x1_(x1), x2_(x2), |
||||
bPointToLine_(bPointToLine) |
||||
{ |
||||
ASSERT(2 == x1_.cols); |
||||
ASSERT(x1_.rows == x2_.rows); |
||||
ASSERT(x1_.cols == x2_.cols); |
||||
ASSERT(NumSamples() >= MINIMUM_SAMPLES); |
||||
|
||||
// LogAlpha0 is used to make error data scale invariant
|
||||
const float s2 = 1.0f; |
||||
if (bPointToLine) { |
||||
// Ratio of containing diagonal image rectangle over image area
|
||||
const float D = sqrt(w2*w2 + h2*h2); // diameter
|
||||
const float A = w2*h2; // area
|
||||
logalpha0_ = log10(2.0f*D/A/s2); |
||||
} |
||||
else { |
||||
// ratio of area : unit circle over image area
|
||||
logalpha0_ = log10(M_PI/(w2*h2)/(s2*s2)); |
||||
} |
||||
} |
||||
|
||||
inline bool Fit(const std::vector<size_t>& samples, Models& models) const { |
||||
const DMatrix32F x1(ExtractRows(x1_, samples)); |
||||
if (CheckCollinearity(x1.ptr<const Point2f>(), x1.rows)) |
||||
return false; |
||||
const DMatrix32F x2(ExtractRows(x2_, samples)); |
||||
if (CheckCollinearity(x2.ptr<const Point2f>(), x2.rows)) |
||||
return false; |
||||
Solver::Solve(x1, x2, models); |
||||
return true; |
||||
} |
||||
|
||||
inline void EvaluateModel(const Model& model) { |
||||
model2evaluate = model; |
||||
} |
||||
|
||||
inline double Error(size_t sample) const { |
||||
return Solver::Error(model2evaluate, *x1_.ptr<const Point2f>((int)sample), *x2_.ptr<const Point2f>((int)sample)); |
||||
} |
||||
|
||||
inline size_t NumSamples() const { return static_cast<size_t>(x1_.rows); } |
||||
inline double logalpha0() const { return logalpha0_; } |
||||
inline double multError() const { return (bPointToLine_ ? 0.5 : 1.0); } |
||||
|
||||
protected: |
||||
DMatrix32F x1_, x2_; // Normalized input data
|
||||
double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
|
||||
bool bPointToLine_;// Store if error model is pointToLine or point to point
|
||||
Model model2evaluate; // current model to be evaluated
|
||||
}; |
||||
|
||||
/// Pose/Resection Kernel adaptator for the A contrario model estimator
|
||||
template <typename SOLVER> |
||||
class ACKernelAdaptorResection |
||||
{ |
||||
public: |
||||
typedef SOLVER Solver; |
||||
typedef typename SOLVER::Model Model; |
||||
typedef typename SOLVER::Models Models; |
||||
|
||||
enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES }; |
||||
enum { MAX_MODELS = Solver::MAX_MODELS }; |
||||
|
||||
ACKernelAdaptorResection(const DMatrix32F &x2d, const DMatrix &x3D) |
||||
: x2d_(x2d), x3D_(x3D), |
||||
logalpha0_(log10(M_PI)) |
||||
{ |
||||
ASSERT(2 == x2d_.cols); |
||||
ASSERT(3 == x3D_.cols); |
||||
ASSERT(x2d_.rows == x3D_.rows); |
||||
ASSERT(NumSamples() >= MINIMUM_SAMPLES); |
||||
} |
||||
|
||||
inline bool Fit(const std::vector<size_t>& samples, Models& models) const { |
||||
const DMatrix32F x1(ExtractRows(x2d_, samples)); |
||||
if (CheckCollinearity(x1.ptr<const Point2f>(), x1.rows)) |
||||
return false; |
||||
const DMatrix x2(ExtractRows(x3D_, samples)); |
||||
Solver::Solve(x1, x2, models); |
||||
return true; |
||||
} |
||||
|
||||
inline void EvaluateModel(const Model &model) { |
||||
model2evaluate = model; |
||||
} |
||||
|
||||
inline double Error(size_t sample) const { |
||||
return Solver::Error(model2evaluate, *x2d_.ptr<const Point2f>((int)sample), *x3D_.ptr<const Point3>((int)sample)); |
||||
} |
||||
|
||||
inline size_t NumSamples() const { return x2d_.rows; } |
||||
inline double logalpha0() const { return logalpha0_; } |
||||
inline double multError() const { return 1.0; } // point to point error
|
||||
|
||||
protected: |
||||
DMatrix32F x2d_; |
||||
DMatrix x3D_; |
||||
double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
|
||||
Model model2evaluate; // current model to be evaluated
|
||||
}; |
||||
|
||||
/// Essential matrix Kernel adaptator for the A contrario model estimator
|
||||
template <typename SOLVER> |
||||
class ACKernelAdaptorEssential |
||||
{ |
||||
public: |
||||
typedef SOLVER Solver; |
||||
typedef typename SOLVER::Model Model; |
||||
typedef typename SOLVER::Models Models; |
||||
|
||||
enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES }; |
||||
enum { MAX_MODELS = Solver::MAX_MODELS }; |
||||
|
||||
ACKernelAdaptorEssential( |
||||
const DMatrix32F &x1, float w1, float h1, const Matrix3x3f& invK1, |
||||
const DMatrix32F &x2, float w2, float h2, const Matrix3x3f& invK2) |
||||
: solver(invK1, invK2), |
||||
x1_(x1), x2_(x2) |
||||
{ |
||||
ASSERT(2 == x1_.cols); |
||||
ASSERT(x1_.rows == x2_.rows); |
||||
ASSERT(x1_.cols == x2_.cols); |
||||
ASSERT(NumSamples() >= MINIMUM_SAMPLES); |
||||
|
||||
//Point to line probability (line is the epipolar line)
|
||||
const float D = sqrt(w2*w2 + h2*h2); // diameter
|
||||
const float A = w2*h2+1.f; // area
|
||||
logalpha0_ = log10(2.0f*D/A*0.5f); |
||||
} |
||||
|
||||
inline bool Fit(const std::vector<size_t>& samples, Models& models) const { |
||||
const DMatrix32F x1(ExtractRows(x1_, samples)); |
||||
if (CheckCollinearity(x1.ptr<const Point2f>(), x1.rows)) |
||||
return false; |
||||
const DMatrix32F x2(ExtractRows(x2_, samples)); |
||||
if (CheckCollinearity(x2.ptr<const Point2f>(), x2.rows)) |
||||
return false; |
||||
solver.Solve(x1, x2, models); |
||||
return true; |
||||
} |
||||
|
||||
inline void EvaluateModel(const Model &model) { |
||||
solver.EvaluateModel(model); |
||||
} |
||||
|
||||
inline double Error(size_t sample) const { |
||||
return solver.Error(*x1_.ptr<const Point2f>((int)sample), *x2_.ptr<const Point2f>((int)sample)); |
||||
} |
||||
|
||||
inline size_t NumSamples() const { return x1_.rows; } |
||||
inline double logalpha0() const { return logalpha0_; } |
||||
inline double multError() const { return 0.5; } // point to line error
|
||||
|
||||
protected: |
||||
Solver solver; |
||||
DMatrix32F x1_, x2_; // image point and camera plane point.
|
||||
double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
|
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_AUTO_ESTIMATOR_H__
|
||||
@ -0,0 +1,275 @@
@@ -0,0 +1,275 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// AutoPtr.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_AUTOPTR_H__ |
||||
#define __SEACAVE_AUTOPTR_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/**************************************************************************************
|
||||
* CAutoPtr template |
||||
* --------------- |
||||
* simple smart pointer |
||||
**************************************************************************************/ |
||||
|
||||
struct AutoPtrMoveCopy { |
||||
template <typename TYPE> |
||||
static inline void Copy(TYPE*& ptrLeft, TYPE*& ptrRight) { |
||||
ptrLeft = ptrRight; |
||||
ptrRight = NULL; |
||||
} |
||||
}; |
||||
|
||||
struct AutoPtrDeepCopy { |
||||
template <typename TYPE> |
||||
static inline void Copy(TYPE*& ptrLeft, TYPE*& ptrRight) { |
||||
ptrLeft = (ptrRight != NULL ? new TYPE(*ptrRight) : (TYPE*)NULL); |
||||
} |
||||
}; |
||||
|
||||
template<typename TYPE, typename CopyPolicy=AutoPtrMoveCopy> |
||||
class CAutoPtr |
||||
{ |
||||
private: |
||||
typedef TYPE Type; |
||||
typedef TYPE* TypePtr; |
||||
|
||||
public: |
||||
inline CAutoPtr() : m_pointer(NULL) |
||||
{ // construct with NULL pointer
|
||||
} |
||||
|
||||
inline explicit CAutoPtr(TypePtr _Ptr) : m_pointer(_Ptr) |
||||
{ // construct from object pointer
|
||||
} |
||||
|
||||
inline CAutoPtr(CAutoPtr& _Right) |
||||
{ // copy-construct by assuming pointer from _Right CAutoPtr
|
||||
CopyPolicy::Copy(m_pointer, _Right.m_pointer); |
||||
} |
||||
|
||||
inline ~CAutoPtr() |
||||
{ // destroy the object
|
||||
delete m_pointer; |
||||
} |
||||
|
||||
void Swap(CAutoPtr& _Right) |
||||
{ // swap compatible _Right (assume pointer)
|
||||
const TypePtr tmp(m_pointer); |
||||
m_pointer = _Right.m_pointer; |
||||
_Right.m_pointer = tmp; |
||||
} |
||||
|
||||
CAutoPtr& operator=(CAutoPtr& _Right) |
||||
{ // assign compatible _Right (assume pointer)
|
||||
if (this != &_Right) { |
||||
delete m_pointer; |
||||
CopyPolicy::Copy(m_pointer, _Right.m_pointer); |
||||
} |
||||
return (*this); |
||||
} |
||||
|
||||
CAutoPtr& operator=(TypePtr _Ptr) |
||||
{ // assign compatible _Right (assume pointer)
|
||||
if (m_pointer != _Ptr) { |
||||
delete m_pointer; |
||||
m_pointer = _Ptr; |
||||
} |
||||
return (*this); |
||||
} |
||||
|
||||
inline Type& operator*() const |
||||
{ // return designated value
|
||||
ASSERT(m_pointer); |
||||
return (*m_pointer); |
||||
} |
||||
|
||||
inline Type* operator->() const |
||||
{ // return pointer to class object
|
||||
ASSERT(m_pointer); |
||||
return m_pointer; |
||||
} |
||||
|
||||
inline operator TypePtr() const |
||||
{ // return pointer to class object
|
||||
return m_pointer; |
||||
} |
||||
|
||||
inline operator TypePtr&() |
||||
{ // return reference to class object
|
||||
return m_pointer; |
||||
} |
||||
|
||||
inline bool operator==(const TypePtr _Right) const |
||||
{ // return pointer to class object
|
||||
return (m_pointer == _Right); |
||||
} |
||||
|
||||
inline bool operator!=(const TypePtr _Right) const |
||||
{ // return pointer to class object
|
||||
return (m_pointer != _Right); |
||||
} |
||||
|
||||
inline void Release() |
||||
{ // release pointer
|
||||
delete m_pointer; |
||||
m_pointer = NULL; |
||||
} |
||||
|
||||
inline void Reset(TypePtr _Ptr = NULL) |
||||
{ // reset pointer
|
||||
m_pointer = _Ptr; |
||||
} |
||||
|
||||
protected: |
||||
TypePtr m_pointer; // the wrapped object pointer
|
||||
|
||||
#ifdef _USE_BOOST |
||||
// implement BOOST serialization
|
||||
friend class boost::serialization::access; |
||||
template<class Archive> |
||||
void save(Archive& ar, const unsigned int /*version*/) const |
||||
{ |
||||
ar & m_pointer; |
||||
} |
||||
template<class Archive> |
||||
void load(Archive& ar, const unsigned int /*version*/) |
||||
{ |
||||
TypePtr newPointer; |
||||
ar & newPointer; |
||||
operator=(newPointer); |
||||
} |
||||
BOOST_SERIALIZATION_SPLIT_MEMBER() |
||||
#endif |
||||
}; |
||||
|
||||
|
||||
template<class TYPE> |
||||
class CAutoPtrArr |
||||
{ |
||||
private: |
||||
typedef TYPE Type; |
||||
typedef TYPE* TypePtr; |
||||
|
||||
public: |
||||
inline CAutoPtrArr() : m_pointer(NULL) |
||||
{ // construct with NULL pointer
|
||||
} |
||||
|
||||
inline explicit CAutoPtrArr(TypePtr _Ptr) : m_pointer(_Ptr) |
||||
{ // construct from object pointer
|
||||
} |
||||
|
||||
inline CAutoPtrArr(CAutoPtrArr& _Right) : m_pointer(_Right.m_pointer) |
||||
{ // construct by assuming pointer from _Right CAutoPtrArr
|
||||
_Right.m_pointer = NULL; |
||||
} |
||||
|
||||
inline ~CAutoPtrArr() |
||||
{ // destroy the object
|
||||
delete[] m_pointer; |
||||
} |
||||
|
||||
CAutoPtrArr& operator=(CAutoPtrArr& _Right) |
||||
{ // assign compatible _Right (assume pointer)
|
||||
if (this != &_Right) |
||||
{ |
||||
delete[] m_pointer; |
||||
m_pointer = _Right.m_pointer; |
||||
_Right.m_pointer = NULL; |
||||
} |
||||
return (*this); |
||||
} |
||||
|
||||
CAutoPtrArr& operator=(TypePtr _Ptr) |
||||
{ // assign compatible _Right (assume pointer)
|
||||
if (m_pointer != _Ptr) |
||||
{ |
||||
delete[] m_pointer; |
||||
m_pointer = _Ptr; |
||||
} |
||||
return (*this); |
||||
} |
||||
|
||||
inline Type& operator*() const |
||||
{ // return designated value
|
||||
ASSERT(m_pointer); |
||||
return (*m_pointer); |
||||
} |
||||
|
||||
inline Type* operator->() const |
||||
{ // return pointer to class object
|
||||
ASSERT(m_pointer); |
||||
return m_pointer; |
||||
} |
||||
|
||||
inline operator TypePtr() const |
||||
{ // return pointer to class object
|
||||
return m_pointer; |
||||
} |
||||
|
||||
inline operator TypePtr&() |
||||
{ // return reference to class object
|
||||
return m_pointer; |
||||
} |
||||
|
||||
inline bool operator==(const TypePtr _Right) const |
||||
{ // return pointer to class object
|
||||
return (m_pointer == _Right); |
||||
} |
||||
|
||||
inline bool operator!=(const TypePtr _Right) const |
||||
{ // return pointer to class object
|
||||
return (m_pointer != _Right); |
||||
} |
||||
|
||||
inline void Release() |
||||
{ // release pointer
|
||||
delete[] m_pointer; |
||||
m_pointer = NULL; |
||||
} |
||||
|
||||
inline void Reset(TypePtr _Ptr = NULL) |
||||
{ // reset pointer
|
||||
m_pointer = _Ptr; |
||||
} |
||||
|
||||
protected: |
||||
TypePtr m_pointer; // the wrapped object pointer
|
||||
|
||||
#ifdef _USE_BOOST |
||||
// implement BOOST serialization
|
||||
friend class boost::serialization::access; |
||||
template<class Archive> |
||||
void save(Archive& ar, const unsigned int /*version*/) const |
||||
{ |
||||
ar & m_pointer; |
||||
} |
||||
template<class Archive> |
||||
void load(Archive& ar, const unsigned int /*version*/) |
||||
{ |
||||
TypePtr newPointer; |
||||
ar & newPointer; |
||||
operator=(newPointer); |
||||
} |
||||
BOOST_SERIALIZATION_SPLIT_MEMBER() |
||||
#endif |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_AUTOPTR_H__
|
||||
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
# List sources files |
||||
FILE(GLOB LIBRARY_FILES_C "*.cpp") |
||||
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") |
||||
|
||||
cxx_library_with_type(Common "Libs" "" "${cxx_default}" |
||||
${LIBRARY_FILES_C} ${LIBRARY_FILES_H} |
||||
) |
||||
|
||||
# Manually set Common.h as the precompiled header |
||||
IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16.0) |
||||
TARGET_PRECOMPILE_HEADERS(Common PRIVATE "Common.h") |
||||
endif() |
||||
|
||||
# Link its dependencies |
||||
TARGET_LINK_LIBRARIES(Common ${Boost_LIBRARIES} ${OpenCV_LIBS}) |
||||
|
||||
# Install |
||||
SET_TARGET_PROPERTIES(Common PROPERTIES |
||||
PUBLIC_HEADER "${LIBRARY_FILES_H}") |
||||
INSTALL(TARGETS Common |
||||
EXPORT OpenMVSTargets |
||||
LIBRARY DESTINATION "${INSTALL_LIB_DIR}" |
||||
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" |
||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" |
||||
PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/Common") |
||||
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Common.cpp
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// Source file that includes just the standard includes
|
||||
// Common.pch will be the pre-compiled header
|
||||
// Common.obj will contain the pre-compiled type information
|
||||
|
||||
#include "Common.h" |
||||
|
||||
namespace SEACAVE { |
||||
#if TD_VERBOSE == TD_VERBOSE_ON |
||||
int g_nVerbosityLevel(2); |
||||
#endif |
||||
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
||||
int g_nVerbosityLevel(3); |
||||
#endif |
||||
|
||||
String g_strWorkingFolder; |
||||
String g_strWorkingFolderFull; |
||||
} // namespace SEACAVE
|
||||
|
||||
#ifdef _USE_BOOST |
||||
#ifdef BOOST_NO_EXCEPTIONS |
||||
#if (BOOST_VERSION / 100000) > 1 || (BOOST_VERSION / 100 % 1000) > 72 |
||||
#include <boost/assert/source_location.hpp> |
||||
#endif |
||||
namespace boost { |
||||
void throw_exception(std::exception const & e) { |
||||
VERBOSE("exception thrown: %s", e.what()); |
||||
ASSERT("boost exception thrown" == NULL); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
#if (BOOST_VERSION / 100000) > 1 || (BOOST_VERSION / 100 % 1000) > 72 |
||||
void throw_exception(std::exception const & e, boost::source_location const & loc) { |
||||
std::ostringstream ostr; ostr << loc; |
||||
VERBOSE("exception thrown at %s: %s", ostr.str().c_str(), e.what()); |
||||
ASSERT("boost exception thrown" == NULL); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
#endif |
||||
} // namespace boost
|
||||
#endif |
||||
#endif |
||||
@ -0,0 +1,308 @@
@@ -0,0 +1,308 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Common.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef _COMMON_COMMON_H_ |
||||
#define _COMMON_COMMON_H_ |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#include "Config.h" |
||||
|
||||
// macros controlling the verbosity
|
||||
#define TD_VERBOSE_OFF 0 |
||||
#define TD_VERBOSE_ON 1 |
||||
#define TD_VERBOSE_DEBUG 2 |
||||
#ifndef TD_VERBOSE |
||||
#ifdef _RELEASE |
||||
#define TD_VERBOSE TD_VERBOSE_ON |
||||
#else |
||||
#define TD_VERBOSE TD_VERBOSE_DEBUG |
||||
#endif |
||||
#endif |
||||
|
||||
#if TD_VERBOSE == TD_VERBOSE_OFF |
||||
#define VERBOSE LOG |
||||
#define DEBUG_LEVEL(n,...) |
||||
#else |
||||
#ifndef VERBOSITY_LEVEL |
||||
namespace SEACAVE { extern int g_nVerbosityLevel; } |
||||
#define VERBOSITY_LEVEL g_nVerbosityLevel |
||||
#endif |
||||
#define VERBOSE LOG |
||||
#define DEBUG_LEVEL(n,...) { if (n < VERBOSITY_LEVEL) VERBOSE(__VA_ARGS__); } |
||||
#endif |
||||
#define DEBUG(...) DEBUG_LEVEL(0, __VA_ARGS__) |
||||
#define DEBUG_EXTRA(...) DEBUG_LEVEL(1, __VA_ARGS__) |
||||
#define DEBUG_ULTIMATE(...) DEBUG_LEVEL(2, __VA_ARGS__) |
||||
|
||||
|
||||
// macros that simplify timing tasks
|
||||
#define TD_TIMER_OFF 0 |
||||
#define TD_TIMER_ON 1 |
||||
#ifndef TD_TIMER |
||||
#define TD_TIMER TD_TIMER_ON |
||||
#endif |
||||
|
||||
#if TD_TIMER == TD_TIMER_OFF |
||||
#define TD_TIMER_START() |
||||
#define TD_TIMER_UPDATE() |
||||
#define TD_TIMER_GET() 0 |
||||
#define TD_TIMER_GET_INT() 0 |
||||
#define TD_TIMER_GET_FMT() String() |
||||
#define TD_TIMER_STARTD() |
||||
#define TD_TIMER_UPDATED() |
||||
#endif |
||||
#if TD_TIMER == TD_TIMER_ON |
||||
#define TD_TIMER_START() TIMER_START() |
||||
#define TD_TIMER_UPDATE() TIMER_UPDATE() |
||||
#define TD_TIMER_GET() TIMER_GET() |
||||
#define TD_TIMER_GET_INT() TIMER_GET_INT() |
||||
#define TD_TIMER_GET_FMT() TIMER_GET_FORMAT() |
||||
#if TD_VERBOSE == TD_VERBOSE_OFF |
||||
#define TD_TIMER_STARTD() |
||||
#define TD_TIMER_UPDATED() |
||||
#else |
||||
#define TD_TIMER_STARTD() TIMER_START() |
||||
#define TD_TIMER_UPDATED() TIMER_UPDATE() |
||||
#endif |
||||
#endif |
||||
|
||||
|
||||
// macros redirecting standard streams to the log
|
||||
#define LOG_OUT() GET_LOG() //or std::cout
|
||||
#define LOG_ERR() GET_LOG() //or std::cerr
|
||||
|
||||
|
||||
// macros simplifying the task of composing file paths;
|
||||
// WORKING_FOLDER and WORKING_FOLDER_FULL must be defined as strings
|
||||
// containing the relative/full path to the working folder
|
||||
#ifndef WORKING_FOLDER |
||||
namespace SEACAVE { |
||||
class String; |
||||
extern String g_strWorkingFolder; // empty by default (current folder)
|
||||
extern String g_strWorkingFolderFull; // full path to current folder
|
||||
} |
||||
#define WORKING_FOLDER g_strWorkingFolder // empty by default (current folder)
|
||||
#define WORKING_FOLDER_FULL g_strWorkingFolderFull // full path to current folder
|
||||
#endif |
||||
#define INIT_WORKING_FOLDER {SEACAVE::Util::ensureValidFolderPath(WORKING_FOLDER); WORKING_FOLDER_FULL = SEACAVE::Util::getFullPath(WORKING_FOLDER);} // initialize working folders
|
||||
#define MAKE_PATH(str) SEACAVE::Util::getSimplifiedPath(WORKING_FOLDER+SEACAVE::String(str)) // add working directory to the given file name
|
||||
#define MAKE_PATH_SAFE(str) (SEACAVE::Util::isFullPath((str).c_str()) ? SEACAVE::String(str) : MAKE_PATH(str)) // add working directory to the given file name only if not full path already
|
||||
#define MAKE_PATH_FULL(p,s) (SEACAVE::Util::isFullPath((s).c_str()) ? SEACAVE::String(s) : SEACAVE::Util::getSimplifiedPath((p)+(s))) // add the given path to the given file name
|
||||
#define MAKE_PATH_REL(p,s) SEACAVE::Util::getRelativePath(p,s) // remove the given path from the given file name
|
||||
#define GET_PATH_FULL(str) (SEACAVE::Util::isFullPath((str).c_str()) ? SEACAVE::Util::getFilePath(str) : SEACAVE::Util::getSimplifiedPath(WORKING_FOLDER_FULL+SEACAVE::Util::getFilePath(str))) // retrieve the full path to the given file
|
||||
|
||||
|
||||
// macros simplifying the task of managing options
|
||||
#define DECOPT_SPACE(SPACE) namespace SPACE { \ |
||||
void init(); \ |
||||
void update(); \ |
||||
extern SEACAVE::VoidArr arrFncOpt; \ |
||||
extern SEACAVE::CConfigTable oConfig; \ |
||||
} |
||||
#define DEFOPT_SPACE(SPACE, name) namespace SPACE { \ |
||||
SEACAVE::CConfigTable oConfig(name); \ |
||||
typedef LPCTSTR (*FNCINDEX)(); \ |
||||
typedef void (*FNCINIT)(SEACAVE::IDX); \ |
||||
typedef void (*FNCUPDATE)(); \ |
||||
VoidArr arrFncOpt; \ |
||||
void init() { \ |
||||
FOREACH(i, arrFncOpt) \ |
||||
((FNCINIT)arrFncOpt[i])(i); \ |
||||
} \ |
||||
void update() { \ |
||||
FOREACH(i, arrFncOpt) \ |
||||
((FNCUPDATE)arrFncOpt[i])(); \ |
||||
} \ |
||||
} |
||||
|
||||
#define DEFVAR_OPTION(SPACE, flags, type, name, title, desc, ...) namespace SPACE { \ |
||||
type name; \ |
||||
LPCTSTR defval_##name(NULL); \ |
||||
void update_##name() { \ |
||||
SEACAVE::String::FromString(oConfig[title].val, name); \ |
||||
} \ |
||||
void init_##name(SEACAVE::IDX idx) { \ |
||||
LPCTSTR const vals[] = {__VA_ARGS__}; \ |
||||
arrFncOpt[idx] = (void*)update_##name; \ |
||||
SMLVALUE& val = oConfig[title]; \ |
||||
CFGITEM& opt = *((CFGITEM*)val.data); \ |
||||
opt.state = flags; \ |
||||
val.val = opt.defval = (defval_##name != NULL ? defval_##name : vals[0]); \ |
||||
opt.vals.Insert(opt.defval); \ |
||||
for (size_t i=1; i<sizeof(vals)/sizeof(LPCTSTR); ++i) \ |
||||
opt.vals.Insert(vals[i]); \ |
||||
SEACAVE::String::FromString(opt.defval, name); \ |
||||
} \ |
||||
LPCTSTR index_##name() { \ |
||||
arrFncOpt.Insert((void*)init_##name); \ |
||||
return title; \ |
||||
} \ |
||||
LPCTSTR const name_##name(index_##name()); \ |
||||
} |
||||
|
||||
#define FDEFVAR_string(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, String, name, title, desc, __VA_ARGS__) |
||||
#define FDEFVAR_bool(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, bool, name, title, desc, __VA_ARGS__) |
||||
#define FDEFVAR_int32(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, int32_t, name, title, desc, __VA_ARGS__) |
||||
#define FDEFVAR_uint32(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, uint32_t, name, title, desc, __VA_ARGS__) |
||||
#define FDEFVAR_flags(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, Flags, name, title, desc, __VA_ARGS__) |
||||
#define FDEFVAR_float(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, float, name, title, desc, __VA_ARGS__) |
||||
#define FDEFVAR_double(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, double, name, title, desc, __VA_ARGS__) |
||||
|
||||
#define DEFVAR_string(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, String, name, title, desc, __VA_ARGS__) |
||||
#define DEFVAR_bool(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, bool, name, title, desc, __VA_ARGS__) |
||||
#define DEFVAR_int32(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, int32_t, name, title, desc, __VA_ARGS__) |
||||
#define DEFVAR_uint32(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, uint32_t, name, title, desc, __VA_ARGS__) |
||||
#define DEFVAR_flags(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, Flags, name, title, desc, __VA_ARGS__) |
||||
#define DEFVAR_float(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, float, name, title, desc, __VA_ARGS__) |
||||
#define DEFVAR_double(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, double, name, title, desc, __VA_ARGS__) |
||||
|
||||
#define TDEFVAR_string(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, String, name, title, desc, __VA_ARGS__) |
||||
#define TDEFVAR_bool(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, bool, name, title, desc, __VA_ARGS__) |
||||
#define TDEFVAR_int32(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, int32_t, name, title, desc, __VA_ARGS__) |
||||
#define TDEFVAR_uint32(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, uint32_t, name, title, desc, __VA_ARGS__) |
||||
#define TDEFVAR_flags(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, Flags, name, title, desc, __VA_ARGS__) |
||||
#define TDEFVAR_float(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, float, name, title, desc, __VA_ARGS__) |
||||
#define TDEFVAR_double(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, double, name, title, desc, __VA_ARGS__) |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Types.h" |
||||
|
||||
|
||||
// P R O T O T Y P E S /////////////////////////////////////////////
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
typedef TSphere<float, 2> Sphere2f; |
||||
typedef TSphere<float, 3> Sphere3f; |
||||
typedef TAABB<float, 2> AABB2f; |
||||
typedef TAABB<float, 3> AABB3f; |
||||
typedef TOBB<float, 2> OBB2f; |
||||
typedef TOBB<float, 3> OBB3f; |
||||
typedef TRay<float, 2> Ray2f; |
||||
typedef TRay<float, 3> Ray3f; |
||||
typedef TLine<float, 2> Line2f; |
||||
typedef TLine<float, 3> Line3f; |
||||
typedef TTriangle<float, 2> Triangle2f; |
||||
typedef TTriangle<float, 3> Triangle3f; |
||||
typedef TPlane<float> Planef; |
||||
typedef TPoint2<float> Point2f; |
||||
typedef TPoint3<float> Point3f; |
||||
typedef TMatrix<float,2,1> Vec2f; |
||||
typedef TMatrix<float,3,1> Vec3f; |
||||
typedef TMatrix<float,4,1> Vec4f; |
||||
typedef TMatrix<float,2,2> Matrix2x2f; |
||||
typedef TMatrix<float,3,3> Matrix3x3f; |
||||
typedef TMatrix<float,3,4> Matrix3x4f; |
||||
typedef TMatrix<float,4,4> Matrix4x4f; |
||||
|
||||
typedef TSphere<double, 2> Sphere2d; |
||||
typedef TSphere<double, 3> Sphere3d; |
||||
typedef TAABB<double, 2> AABB2d; |
||||
typedef TAABB<double, 3> AABB3d; |
||||
typedef TOBB<double, 2> OBB2d; |
||||
typedef TOBB<double, 3> OBB3d; |
||||
typedef TRay<double, 2> Ray2d; |
||||
typedef TRay<double, 3> Ray3d; |
||||
typedef TLine<double, 2> Line2d; |
||||
typedef TLine<double, 3> Line3d; |
||||
typedef TTriangle<double, 2> Triangle2d; |
||||
typedef TTriangle<double, 3> Triangle3d; |
||||
typedef TPlane<double> Planed; |
||||
typedef TPoint2<double> Point2d; |
||||
typedef TPoint3<double> Point3d; |
||||
typedef TMatrix<double,2,1> Vec2d; |
||||
typedef TMatrix<double,3,1> Vec3d; |
||||
typedef TMatrix<double,4,1> Vec4d; |
||||
typedef TMatrix<double,2,2> Matrix2x2d; |
||||
typedef TMatrix<double,3,3> Matrix3x3d; |
||||
typedef TMatrix<double,3,4> Matrix3x4d; |
||||
typedef TMatrix<double,4,4> Matrix4x4d; |
||||
|
||||
typedef TSphere<REAL, 2> Sphere2; |
||||
typedef TSphere<REAL, 3> Sphere3; |
||||
typedef TAABB<REAL, 2> AABB2; |
||||
typedef TAABB<REAL, 3> AABB3; |
||||
typedef TOBB<REAL, 2> OBB2; |
||||
typedef TOBB<REAL, 3> OBB3; |
||||
typedef TRay<REAL, 2> Ray2; |
||||
typedef TRay<REAL, 3> Ray3; |
||||
typedef TLine<REAL, 2> Line2; |
||||
typedef TLine<REAL, 3> Line3; |
||||
typedef TTriangle<REAL, 2> Triangle2; |
||||
typedef TTriangle<REAL, 3> Triangle3; |
||||
typedef TPlane<REAL> Plane; |
||||
typedef TPoint2<REAL> Point2; |
||||
typedef TPoint3<REAL> Point3; |
||||
typedef TMatrix<REAL,2,1> Vec2; |
||||
typedef TMatrix<REAL,3,1> Vec3; |
||||
typedef TMatrix<REAL,4,1> Vec4; |
||||
typedef TMatrix<REAL,2,2> Matrix2x2; |
||||
typedef TMatrix<REAL,3,3> Matrix3x3; |
||||
typedef TMatrix<REAL,3,4> Matrix3x4; |
||||
typedef TMatrix<REAL,4,4> Matrix4x4; |
||||
|
||||
typedef TQuaternion<REAL> Quaternion; |
||||
typedef TRMatrixBase<REAL> RMatrixBase; |
||||
|
||||
// camera matrix types
|
||||
typedef Point3 CMatrix; |
||||
typedef RMatrixBase RMatrix; |
||||
typedef Matrix3x3 KMatrix; |
||||
typedef Matrix3x4 PMatrix; |
||||
|
||||
// reconstructed 3D point type
|
||||
typedef Vec3 X3D; |
||||
typedef SEACAVE::cList<X3D, const X3D&, 0, 8192> X3DArr; |
||||
|
||||
typedef SEACAVE::cList<uint32_t, uint32_t, 0> IndexArr; |
||||
|
||||
typedef CLISTDEF0(REAL) REALArr; |
||||
typedef CLISTDEF0(Point2f) Point2fArr; |
||||
typedef CLISTDEF0(Point3f) Point3fArr; |
||||
typedef CLISTDEF0(Point2d) Point2dArr; |
||||
typedef CLISTDEF0(Point3d) Point3dArr; |
||||
typedef CLISTDEF0(Point2) Point2Arr; |
||||
typedef CLISTDEF0(Point3) Point3Arr; |
||||
typedef CLISTDEF0(Vec4) Vec4Arr; |
||||
typedef CLISTDEF0(Matrix3x3) Matrix3x3Arr; |
||||
typedef CLISTDEF0(Matrix3x4) Matrix3x4Arr; |
||||
typedef CLISTDEF0(CMatrix) CMatrixArr; |
||||
typedef CLISTDEF0(RMatrix) RMatrixArr; |
||||
typedef CLISTDEF0(KMatrix) KMatrixArr; |
||||
typedef CLISTDEF0(PMatrix) PMatrixArr; |
||||
typedef CLISTDEF0(ImageRef) ImageRefArr; |
||||
typedef CLISTDEF0(Pixel8U) Pixel8UArr; |
||||
typedef CLISTDEF0(Pixel32F) Pixel32FArr; |
||||
typedef CLISTDEF0(Color8U) Color8UArr; |
||||
typedef CLISTDEF0(Color32F) Color32FArr; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
// define specialized cv:DataType<>
|
||||
DEFINE_CVDATATYPE(SEACAVE::Vec2f) |
||||
DEFINE_CVDATATYPE(SEACAVE::Vec3f) |
||||
DEFINE_CVDATATYPE(SEACAVE::Vec4f) |
||||
DEFINE_CVDATATYPE(SEACAVE::Matrix2x2f) |
||||
DEFINE_CVDATATYPE(SEACAVE::Matrix3x3f) |
||||
DEFINE_CVDATATYPE(SEACAVE::Matrix3x4f) |
||||
DEFINE_CVDATATYPE(SEACAVE::Matrix4x4f) |
||||
|
||||
DEFINE_CVDATATYPE(SEACAVE::Vec2d) |
||||
DEFINE_CVDATATYPE(SEACAVE::Vec3d) |
||||
DEFINE_CVDATATYPE(SEACAVE::Vec4d) |
||||
DEFINE_CVDATATYPE(SEACAVE::Matrix2x2d) |
||||
DEFINE_CVDATATYPE(SEACAVE::Matrix3x3d) |
||||
DEFINE_CVDATATYPE(SEACAVE::Matrix3x4d) |
||||
DEFINE_CVDATATYPE(SEACAVE::Matrix4x4d) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
#endif // _COMMON_COMMON_H_
|
||||
@ -0,0 +1,281 @@
@@ -0,0 +1,281 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Config.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_CONFIG_H__ |
||||
#define __SEACAVE_CONFIG_H__ |
||||
|
||||
// Configure everything that needs to be globally known
|
||||
|
||||
#include "ConfigLocal.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#ifdef _MSC_VER |
||||
|
||||
// Modify the following defines if you have to target a platform prior to the ones specified below.
|
||||
// Refer to MSDN for the latest info on corresponding values for different platforms.
|
||||
#if _MSC_VER > 1400 |
||||
#ifndef NTDDI_VERSION // There's now just one symbol to specify the minimum target operating system.
|
||||
#define NTDDI_VERSION NTDDI_WIN7 // All the other symbols are set automatically to the appropriate values for the target operating system.
|
||||
#endif |
||||
#else |
||||
#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later.
|
||||
#define WINVER 0x0500 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
|
||||
#endif |
||||
#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later.
|
||||
#define _WIN32_WINNT 0x0500 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
|
||||
#endif |
||||
#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
|
||||
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
|
||||
#endif |
||||
#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later.
|
||||
#define _WIN32_IE 0x0501 // Change this to the appropriate value to target IE 5.0 or later.
|
||||
#endif |
||||
#endif |
||||
|
||||
#ifndef VC_EXTRALEAN |
||||
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
|
||||
#endif |
||||
|
||||
#ifndef STRICT |
||||
#define STRICT |
||||
#endif |
||||
|
||||
#define WIN32_MEAN_AND_LEAN |
||||
|
||||
#define NOMINMAX |
||||
|
||||
#define _USE_MATH_DEFINES |
||||
|
||||
#if _MSC_VER >= 1400 |
||||
#ifndef _CRT_SECURE_NO_WARNINGS |
||||
#define _CRT_SECURE_NO_WARNINGS 1 |
||||
#endif |
||||
#ifndef _CRT_SECURE_NO_DEPRECATE |
||||
#define _CRT_SECURE_NO_DEPRECATE 1 |
||||
#endif |
||||
#ifndef _ATL_SECURE_NO_DEPRECATE |
||||
#define _ATL_SECURE_NO_DEPRECATE 1 |
||||
#endif |
||||
#ifndef _CRT_NON_CONFORMING_SWPRINTFS |
||||
#define _CRT_NON_CONFORMING_SWPRINTFS 1 |
||||
#endif |
||||
#ifndef _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES |
||||
#define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES 0 // disable automatically overloading CPP names to secure versions
|
||||
#endif |
||||
#if 0 && defined(_DEBUG) && !defined(_ITERATOR_DEBUG_LEVEL) // might not build if linking statically to 3rd party libraries
|
||||
#define _ITERATOR_DEBUG_LEVEL 1 // disable std iterator debugging even in Debug, as it is very slow
|
||||
#endif |
||||
#endif |
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// For Microsoft Visual C++, externally accessible symbols must be
|
||||
// explicitly indicated with DLL_API.
|
||||
//
|
||||
// The following ifdef block is the standard way of creating macros
|
||||
// which make exporting from a DLL simpler. All files within this DLL
|
||||
// are compiled with the DLL_EXPORTS preprocessor symbol defined on the
|
||||
// command line. In contrast, projects that use (or import) the DLL
|
||||
// objects do not define the DLL_EXPORTS symbol. This way any other
|
||||
// project whose source files include this file see DLL_API functions as
|
||||
// being imported from a DLL, whereas this DLL sees symbols defined with
|
||||
// this macro as being exported.
|
||||
//----------------------------------------------------------------------
|
||||
#define EXPORT_API __declspec(dllexport) |
||||
#define IMPORT_API __declspec(dllimport) |
||||
/*----------------------------------------------------------------*/ |
||||
#ifdef _USRDLL |
||||
#ifdef Common_EXPORTS |
||||
#define GENERAL_API EXPORT_API |
||||
#define GENERAL_TPL |
||||
#else |
||||
#define GENERAL_API IMPORT_API |
||||
#define GENERAL_TPL extern |
||||
#endif |
||||
#else |
||||
#define GENERAL_API |
||||
#define GENERAL_TPL |
||||
#endif |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// Define platform type
|
||||
#if _WIN64 |
||||
#define _ENVIRONMENT64 |
||||
#else |
||||
#define _ENVIRONMENT32 |
||||
#endif |
||||
|
||||
#else // _MSC_VER
|
||||
|
||||
#if !defined(_DEBUG) && !defined(NDEBUG) |
||||
#define _DEBUG |
||||
#endif |
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DLL_API is ignored for all other systems
|
||||
//----------------------------------------------------------------------
|
||||
#define EXPORT_API |
||||
#define IMPORT_API |
||||
#define GENERAL_API |
||||
#define GENERAL_TPL |
||||
|
||||
// Define platform type
|
||||
#if __x86_64__ || __ppc64__ |
||||
#define _ENVIRONMENT64 |
||||
#else |
||||
#define _ENVIRONMENT32 |
||||
#endif |
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
#if __cplusplus >= 201103L || (__clang_major__ >= 4 || (__clang_major__ >= 3 && __clang_minor__ >= 3)) |
||||
#define _SUPPORT_CPP11 |
||||
#endif |
||||
#if __cplusplus >= 201402L || (__clang_major__ >= 4 || (__clang_major__ >= 3 && __clang_minor__ >= 4)) |
||||
#define _SUPPORT_CPP14 |
||||
#endif |
||||
#if __cplusplus >= 201703L || __clang_major__ >= 5 |
||||
#define _SUPPORT_CPP17 |
||||
#endif |
||||
#if __cplusplus >= 202002L || __clang_major__ >= 10 |
||||
#define _SUPPORT_CPP20 |
||||
#endif |
||||
|
||||
|
||||
#if defined(__powerpc__) |
||||
#define _PLATFORM_PPC 1 |
||||
#elif defined(__arm__) || defined (__arm64__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARMT) |
||||
#define _PLATFORM_ARM 1 |
||||
#else |
||||
#define _PLATFORM_X86 1 |
||||
#endif |
||||
|
||||
|
||||
#if !defined(_DEBUG) && !defined(_PROFILE) |
||||
#define _RELEASE // exclude code useful only for debug
|
||||
#endif |
||||
|
||||
|
||||
|
||||
//optimization flags
|
||||
#if defined(_MSC_VER) |
||||
# define ALIGN(n) __declspec(align(n)) |
||||
# define NOINITVTABLE __declspec(novtable) //disable generating code to initialize the vfptr in the constructor(s) and destructor of the class
|
||||
# define DECRESTRICT __declspec(restrict) //applied to a function declaration or definition that returns a pointer type and tells the compiler that the function returns an object that will not be aliased with any other pointers
|
||||
# define NOALIAS __declspec(noalias) //applied to a function declaration or definition that returns a pointer type and tells the compiler that the function call does not modify or reference visible global state and only modifies the memory pointed to directly by pointer parameters (first-level indirections)
|
||||
# define RESTRICT __restrict //applied to a function parameter
|
||||
# define MEMALLOC __declspec(noalias) __declspec(restrict) |
||||
# define DEPRECATED __declspec(deprecated) |
||||
# define MAYBEUNUSED |
||||
# define HOT |
||||
# define COLD |
||||
# define THREADLOCAL __declspec(thread) |
||||
# define FORCEINLINE __forceinline |
||||
#elif defined(__GNUC__) |
||||
# define ALIGN(n) __attribute__((aligned(n))) |
||||
# define NOINITVTABLE |
||||
# define DECRESTRICT |
||||
# define NOALIAS |
||||
# define RESTRICT __restrict__ |
||||
# define MEMALLOC __attribute__ ((__malloc__)) |
||||
# define DEPRECATED __attribute__ ((__deprecated__)) |
||||
# define MAYBEUNUSED __attribute__ ((unused)) |
||||
# define HOT __attribute__((hot)) __attribute__((optimize("-O3"))) __attribute__((optimize("-ffast-math"))) //optimize for speed, even in debug
|
||||
# define COLD __attribute__((cold)) //optimize for size
|
||||
# define THREADLOCAL __thread |
||||
# define FORCEINLINE inline //__attribute__((always_inline))
|
||||
#else |
||||
# define ALIGN(n) |
||||
# define NOINITVTABLE |
||||
# define DECRESTRICT |
||||
# define NOALIAS |
||||
# define RESTRICT |
||||
# define MEMALLOC |
||||
# define DEPRECATED |
||||
# define MAYBEUNUSED |
||||
# define HOT |
||||
# define COLD |
||||
# define THREADLOCAL __thread |
||||
# define FORCEINLINE inline |
||||
#endif |
||||
|
||||
#ifndef _SUPPORT_CPP11 |
||||
# define constexpr inline |
||||
#endif |
||||
#ifdef _SUPPORT_CPP17 |
||||
# undef MAYBEUNUSED |
||||
# define MAYBEUNUSED [[maybe_unused]] |
||||
#endif |
||||
|
||||
#define SAFE_DELETE(p) { if (p!=NULL) { delete (p); (p)=NULL; } } |
||||
#define SAFE_DELETE_ARR(p) { if (p!=NULL) { delete [] (p); (p)=NULL; } } |
||||
#define SAFE_FREE(p) { if (p!=NULL) { free(p); (p)=NULL; } } |
||||
#define SAFE_RELEASE(p) { if (p!=NULL) { (p)->Release(); (p)=NULL; } } |
||||
|
||||
|
||||
#ifdef _DEBUG |
||||
|
||||
#ifdef _MSC_VER |
||||
#define _DEBUGINFO |
||||
#define _CRTDBG_MAP_ALLOC //enable this to show also the filename (DEBUG_NEW should also be defined in each file)
|
||||
#include <cstdlib> |
||||
#include <crtdbg.h> |
||||
#ifdef _INC_CRTDBG |
||||
#define ASSERT(exp) {if (!(exp) && 1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, #exp)) _CrtDbgBreak();} |
||||
#else |
||||
#define ASSERT(exp) {if (!(exp)) __debugbreak();} |
||||
#endif // _INC_CRTDBG
|
||||
#define TRACE(...) {TCHAR buffer[2048]; _sntprintf(buffer, 2048, __VA_ARGS__); OutputDebugString(buffer);} |
||||
#else // _MSC_VER
|
||||
#include <assert.h> |
||||
#define ASSERT(exp) assert(exp) |
||||
#define TRACE(...) |
||||
#endif // _MSC_VER
|
||||
|
||||
#else |
||||
|
||||
#ifdef _RELEASE |
||||
#define ASSERT(exp) |
||||
#else |
||||
#ifdef _MSC_VER |
||||
#define ASSERT(exp) {if (!(exp)) __debugbreak();} |
||||
#else // _MSC_VER
|
||||
#define ASSERT(exp) {if (!(exp)) __builtin_trap();} |
||||
#endif // _MSC_VER
|
||||
#endif |
||||
#define TRACE(...) |
||||
|
||||
#endif // _DEBUG
|
||||
|
||||
#define ASSERTM(exp, msg) ASSERT(exp) |
||||
|
||||
namespace SEACAVE_ASSERT |
||||
{ |
||||
template <bool value> struct compile_time_assert; |
||||
template <> struct compile_time_assert<true> { enum {value=1}; }; |
||||
|
||||
template <typename T, typename U> struct assert_are_same_type; |
||||
template <typename T> struct assert_are_same_type<T,T> { enum{value=1}; }; |
||||
|
||||
template <typename T, typename U> struct assert_are_not_same_type { enum{value=1}; }; |
||||
template <typename T> struct assert_are_not_same_type<T,T> {}; |
||||
} |
||||
|
||||
#define STATIC_ASSERT(expression) \ |
||||
MAYBEUNUSED typedef char CTA##__LINE__[::SEACAVE_ASSERT::compile_time_assert<(bool)(expression)>::value] |
||||
|
||||
#define ASSERT_ARE_SAME_TYPE(type1, type2) \ |
||||
MAYBEUNUSED typedef char AAST##__LINE__[::SEACAVE_ASSERT::assert_are_same_type<type1,type2>::value] |
||||
|
||||
#define ASSERT_ARE_NOT_SAME_TYPE(type1, type2) \ |
||||
MAYBEUNUSED typedef char AANST##__LINE__[::SEACAVE_ASSERT::assert_are_not_same_type<type1,type2>::value] |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
#endif // __SEACAVE_CONFIG_H__
|
||||
@ -0,0 +1,153 @@
@@ -0,0 +1,153 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// ConfigTable.cpp
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "Common.h" |
||||
#include "ConfigTable.h" |
||||
|
||||
using namespace SEACAVE; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/*-----------------------------------------------------------*
|
||||
* CConfigTable class implementation * |
||||
*-----------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* Constructor |
||||
*/ |
||||
CConfigTable::CConfigTable(const String& name) : m_oSML(name), m_pParent(NULL) |
||||
{ |
||||
m_oSML.SetFncItem(ItemInitData, ItemSaveData, ItemReleaseData); |
||||
} |
||||
|
||||
/**
|
||||
* Destructor |
||||
*/ |
||||
CConfigTable::~CConfigTable() |
||||
{ |
||||
if (m_pParent) |
||||
m_pParent->RemoveChild(*this); |
||||
Release(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* Released all used memory |
||||
*/ |
||||
void CConfigTable::Release() |
||||
{ |
||||
m_oSML.Release(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Create a new child |
||||
*/ |
||||
void CConfigTable::Insert(const String& name) |
||||
{ |
||||
SML* const pChildSML = new SML(name); |
||||
pChildSML->SetFncItem(ItemInitData, ItemSaveData, ItemReleaseData); |
||||
const IDX idx = m_oSML.InsertChild(pChildSML); |
||||
// if child already exists, delete created node
|
||||
if (m_oSML.GetArrChildren()[idx] != pChildSML) |
||||
delete pChildSML; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Remove an existing child |
||||
*/ |
||||
void CConfigTable::Remove(const String& name) |
||||
{ |
||||
m_oSML.DestroyChild(name); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Get the config table for the given child |
||||
*/ |
||||
const SML& CConfigTable::GetConfig(const String& name) const |
||||
{ |
||||
const LPSMLARR& arrSML = m_oSML.GetArrChildren(); |
||||
const IDX idx = arrSML.FindFirst(&name, SML::CompareName); |
||||
ASSERT(idx != LPSMLARR::NO_INDEX); |
||||
return *arrSML[idx]; |
||||
} // GetConfig
|
||||
SML& CConfigTable::GetConfig(const String& name) |
||||
{ |
||||
const LPSMLARR& arrSML = m_oSML.GetArrChildren(); |
||||
const IDX idx = arrSML.FindFirst(&name, SML::CompareName); |
||||
ASSERT(idx != LPSMLARR::NO_INDEX); |
||||
return *arrSML[idx]; |
||||
} // GetConfig
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Load the configuration from a file. |
||||
*/ |
||||
bool CConfigTable::Load(const String& f) |
||||
{ |
||||
return m_oSML.Load(f); |
||||
} // Load
|
||||
bool CConfigTable::Load(ISTREAM& oStream) |
||||
{ |
||||
return m_oSML.Load(oStream); |
||||
} // Load
|
||||
/**
|
||||
* Write to a file the values of this node and its children. |
||||
* Set to false the second parameter in order not to save the empty children. |
||||
*/ |
||||
bool CConfigTable::Save(const String& f, SML::SAVEFLAG flags) const |
||||
{ |
||||
return m_oSML.Save(f, flags); |
||||
} // Save
|
||||
bool CConfigTable::Save(OSTREAM& oStream, SML::SAVEFLAG flags) const |
||||
{ |
||||
return m_oSML.Save(oStream, flags); |
||||
} // Save
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Create and initialize a config item for the given SML entry. |
||||
*/ |
||||
void STCALL CConfigTable::ItemInitData(const String& key, SMLVALUE& val, void*) |
||||
{ |
||||
CFGITEM* pItem = new CFGITEM; |
||||
pItem->name = key; |
||||
val.data = pItem; |
||||
} // ItemInitData
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* Save a config item for the given SML entry. Return false if this item should not be saved. |
||||
*/ |
||||
bool STCALL CConfigTable::ItemSaveData(const SMLVALUE& val, void*) |
||||
{ |
||||
const CFGITEM& item = *((const CFGITEM*)val.data); |
||||
return !item.state.isSet(CFGITEM::TEMP); |
||||
} // ItemSaveData
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* Release and destroy the config item for the given SML entry. |
||||
*/ |
||||
void STCALL CConfigTable::ItemReleaseData(SMLVALUE& val, void*) |
||||
{ |
||||
CFGITEM* pItem = (CFGITEM*)val.data; |
||||
val.data = NULL; |
||||
delete pItem; |
||||
} // ItemReleaseData
|
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// ConfigTable.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_CONFIGTABLE_H__ |
||||
#define __SEACAVE_CONFIGTABLE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "SML.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
typedef struct CFGITEM_TYPE { |
||||
enum { |
||||
NA = 0, // no special states available
|
||||
TEMP = (1 << 0), // this item lives only this instance and it will not be save
|
||||
}; |
||||
Flags state; |
||||
String name; |
||||
String desc; |
||||
String defval; |
||||
StringArr vals; |
||||
} CFGITEM; |
||||
|
||||
|
||||
// P R O T O T Y P E S /////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Configuration table interface. |
||||
*/ |
||||
|
||||
class GENERAL_API CConfigTable |
||||
{ |
||||
public: |
||||
CConfigTable(const String&); |
||||
~CConfigTable(); |
||||
|
||||
void Release(); |
||||
|
||||
// main methods
|
||||
void Insert(const String&); |
||||
void Remove(const String&); |
||||
const SML& GetConfig(const String&) const; |
||||
SML& GetConfig(const String&); |
||||
const SML& GetConfig() const { return m_oSML; } |
||||
SML& GetConfig() { return m_oSML; } |
||||
inline SMLVALUE& operator[] (const String& name) { return m_oSML[name]; } |
||||
inline IDX InsertChild(CConfigTable& oCfg) { oCfg.SetParent(this); return m_oSML.InsertChild(&oCfg.m_oSML); } |
||||
inline void RemoveChild(CConfigTable& oCfg) { oCfg.SetParent(NULL); m_oSML.RemoveChild(oCfg.m_oSML.GetName()); } |
||||
|
||||
// misc methods
|
||||
bool Load(const String&); |
||||
bool Load(ISTREAM&); |
||||
bool Save(const String&, SML::SAVEFLAG=SML::NONE) const; |
||||
bool Save(OSTREAM&, SML::SAVEFLAG=SML::NONE) const; |
||||
inline const String& GetName() const { return m_oSML.GetName(); } |
||||
inline CConfigTable* GetParent() const { return m_pParent; } |
||||
inline void SetParent(CConfigTable* pParent) { m_pParent = pParent; } |
||||
static void STCALL ItemInitData(const String&, SMLVALUE&, void*); |
||||
static bool STCALL ItemSaveData(const SMLVALUE&, void*); |
||||
static void STCALL ItemReleaseData(SMLVALUE&, void*); |
||||
|
||||
private: |
||||
SML m_oSML; |
||||
CConfigTable* m_pParent; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_CONFIGTABLE_H__
|
||||
@ -0,0 +1,264 @@
@@ -0,0 +1,264 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// CriticalSection.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_CRITCALSECTION_H__ |
||||
#define __SEACAVE_CRITCALSECTION_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Thread.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
class CriticalSection |
||||
{ |
||||
#ifdef _MSC_VER |
||||
|
||||
public: |
||||
CriticalSection() { |
||||
InitializeCriticalSection(&cs); |
||||
} |
||||
~CriticalSection() { |
||||
DeleteCriticalSection(&cs); |
||||
} |
||||
void Clear() { |
||||
DeleteCriticalSection(&cs); |
||||
InitializeCriticalSection(&cs); |
||||
} |
||||
void Enter() { |
||||
EnterCriticalSection(&cs); |
||||
} |
||||
bool TryEnter() { |
||||
return (TryEnterCriticalSection(&cs) != 0); |
||||
} |
||||
void Leave() { |
||||
LeaveCriticalSection(&cs); |
||||
} |
||||
|
||||
protected: |
||||
CRITICAL_SECTION cs; |
||||
|
||||
#else |
||||
|
||||
public: |
||||
CriticalSection() { |
||||
#ifdef __APPLE__ |
||||
pthread_mutex_init(&mtx, NULL); |
||||
#else |
||||
mtx = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; |
||||
#endif |
||||
} |
||||
~CriticalSection() { pthread_mutex_destroy(&mtx); } |
||||
void Clear() { |
||||
pthread_mutex_destroy(&mtx); |
||||
#ifdef __APPLE__ |
||||
pthread_mutex_init(&mtx, NULL); |
||||
#else |
||||
mtx = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; |
||||
#endif |
||||
} |
||||
void Enter() { pthread_mutex_lock(&mtx); } |
||||
bool TryEnter() { return (pthread_mutex_trylock(&mtx) == 0); } |
||||
void Leave() { pthread_mutex_unlock(&mtx); } |
||||
pthread_mutex_t& getMutex() { return mtx; } |
||||
|
||||
protected: |
||||
pthread_mutex_t mtx; |
||||
|
||||
#endif |
||||
|
||||
private: |
||||
CriticalSection(const CriticalSection&); |
||||
CriticalSection& operator=(const CriticalSection&); |
||||
}; |
||||
|
||||
/**
|
||||
* A fast, non-recursive and unfair implementation of the Critical Section. |
||||
* It is meant to be used in situations where the risk for lock conflict is very low, |
||||
* i e locks that are held for a very short time. The lock is _not_ recursive, i e if |
||||
* the same thread will try to grab the lock it'll hang in a never-ending loop. The lock |
||||
* is not fair, i e the first to try to enter a locked lock is not guaranteed to be the |
||||
* first to get it when it's freed... |
||||
*/ |
||||
class FastCriticalSection { |
||||
public: |
||||
FastCriticalSection() : state(0) {} |
||||
|
||||
void Clear() { |
||||
Thread::safeExchange(state, 0); |
||||
} |
||||
|
||||
void Enter() { |
||||
while (Thread::safeCompareExchange(state, 0, 1) != 0) |
||||
Thread::yield(); |
||||
} |
||||
bool TryEnter() { |
||||
return (Thread::safeCompareExchange(state, 0, 1) == 0); |
||||
} |
||||
void Leave() { |
||||
Thread::safeDec(state); |
||||
} |
||||
|
||||
protected: |
||||
volatile Thread::safe_t state; |
||||
}; |
||||
|
||||
template<class T> |
||||
class SimpleLock { |
||||
public: |
||||
SimpleLock(T& aCs) : cs(aCs) { cs.Enter(); } |
||||
~SimpleLock() { cs.Leave(); } |
||||
protected: |
||||
T& cs; |
||||
}; |
||||
|
||||
template<class T> |
||||
class SimpleLockTry { |
||||
public: |
||||
SimpleLockTry(T& aCs) : cs(aCs) { bLocked = cs.TryEnter(); } |
||||
~SimpleLockTry() { if (bLocked) cs.Leave(); } |
||||
bool IsLocked() const { return bLocked; } |
||||
protected: |
||||
T& cs; |
||||
bool bLocked; |
||||
}; |
||||
|
||||
typedef SimpleLock<CriticalSection> Lock; |
||||
typedef SimpleLock<FastCriticalSection> FastLock; |
||||
typedef SimpleLockTry<CriticalSection> LockTry; |
||||
typedef SimpleLockTry<FastCriticalSection> FastLockTry; |
||||
|
||||
class RWLock |
||||
{ |
||||
public: |
||||
RWLock() : cs(), readers(0) {} |
||||
~RWLock() { ASSERT(readers==0); } |
||||
void Clear() { cs.Clear(); readers = 0; } |
||||
|
||||
// Read
|
||||
void EnterRead() { |
||||
Lock l(cs); |
||||
++readers; |
||||
} |
||||
|
||||
bool TryEnterRead() { |
||||
LockTry l(cs); |
||||
if (!l.IsLocked()) |
||||
return false; |
||||
++readers; |
||||
return true; |
||||
} |
||||
|
||||
void LeaveRead() { |
||||
Lock l(cs); |
||||
ASSERT(readers > 0); |
||||
--readers; |
||||
} |
||||
|
||||
bool TryLeaveRead() { |
||||
LockTry l(cs); |
||||
if (!l.IsLocked()) |
||||
return false; |
||||
ASSERT(readers > 0); |
||||
--readers; |
||||
return true; |
||||
} |
||||
|
||||
// Write
|
||||
void EnterWrite() { |
||||
cs.Enter(); |
||||
while (readers) { |
||||
cs.Leave(); |
||||
Thread::yield(); |
||||
cs.Enter(); |
||||
} |
||||
} |
||||
|
||||
bool TryEnterWrite() { |
||||
if (cs.TryEnter()) { |
||||
if (readers == 0) |
||||
return true; |
||||
cs.Leave(); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
void LeaveWrite() { |
||||
cs.Leave(); |
||||
} |
||||
|
||||
private: |
||||
RWLock(const RWLock&); |
||||
RWLock& operator=(const RWLock&); |
||||
|
||||
protected: |
||||
CriticalSection cs; |
||||
unsigned readers; |
||||
}; |
||||
|
||||
class RLock { |
||||
public: |
||||
RLock(RWLock& aCs) : cs(aCs) { cs.EnterRead(); } |
||||
~RLock() { cs.LeaveRead(); } |
||||
private: |
||||
RLock(const RLock&); |
||||
RLock& operator=(const RLock&); |
||||
protected: |
||||
RWLock& cs; |
||||
}; |
||||
|
||||
class RLockTry { |
||||
public: |
||||
RLockTry(RWLock& aCs) : cs(aCs) { bLocked = cs.TryEnterRead(); } |
||||
~RLockTry() { if (bLocked) cs.LeaveRead(); } |
||||
bool IsLocked() const { return bLocked; } |
||||
bool TryEnter() { return (bLocked = cs.TryEnterRead()); } |
||||
bool TryLeave() { return !(bLocked = !cs.TryLeaveRead()); } |
||||
private: |
||||
RLockTry(const RLockTry&); |
||||
RLockTry& operator=(const RLockTry&); |
||||
protected: |
||||
RWLock& cs; |
||||
bool bLocked; |
||||
}; |
||||
|
||||
class WLock { |
||||
public: |
||||
WLock(RWLock& aCs) : cs(aCs) { cs.EnterWrite(); } |
||||
~WLock() { cs.LeaveWrite(); } |
||||
private: |
||||
WLock(const WLock&); |
||||
WLock& operator=(const WLock&); |
||||
protected: |
||||
RWLock& cs; |
||||
}; |
||||
|
||||
class WLockTry { |
||||
public: |
||||
WLockTry(RWLock& aCs) : cs(aCs) { bLocked = cs.TryEnterWrite(); } |
||||
~WLockTry() { if (bLocked) cs.LeaveWrite(); } |
||||
bool IsLocked() const { return bLocked; } |
||||
bool TryEnter() { return (bLocked = cs.TryEnterWrite()); } |
||||
private: |
||||
WLockTry(const WLockTry&); |
||||
WLockTry& operator=(const WLockTry&); |
||||
protected: |
||||
RWLock& cs; |
||||
bool bLocked; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_CRITCALSECTION_H__
|
||||
@ -0,0 +1,101 @@
@@ -0,0 +1,101 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// EventQueue.cpp
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "Common.h" |
||||
#include "EventQueue.h" |
||||
|
||||
using namespace SEACAVE; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/*-----------------------------------------------------------*
|
||||
* EventQueue class implementation * |
||||
*-----------------------------------------------------------*/ |
||||
|
||||
void EventQueue::Clear() |
||||
{ |
||||
m_cs.Clear(); |
||||
m_sem.Clear(); |
||||
m_events.Empty(); |
||||
} |
||||
|
||||
void EventQueue::AddEvent(Event* evt) |
||||
{ |
||||
Lock l(m_cs); |
||||
m_events.AddTail(evt); |
||||
m_sem.Signal(); |
||||
} |
||||
void EventQueue::AddEventFirst(Event* evt) |
||||
{ |
||||
Lock l(m_cs); |
||||
m_events.AddHead(evt); |
||||
m_sem.Signal(); |
||||
} |
||||
|
||||
Event* EventQueue::GetEvent() |
||||
{ |
||||
m_sem.Wait(); |
||||
Lock l(m_cs); |
||||
ASSERT(!m_events.IsEmpty()); |
||||
return m_events.RemoveHead(); |
||||
} |
||||
Event* EventQueue::GetEvent(uint32_t millis) |
||||
{ |
||||
if (!m_sem.Wait(millis)) |
||||
return NULL; |
||||
Lock l(m_cs); |
||||
if (m_events.IsEmpty()) |
||||
return NULL; |
||||
return m_events.RemoveHead(); |
||||
} |
||||
Event* EventQueue::GetEventLast() |
||||
{ |
||||
m_sem.Wait(); |
||||
Lock l(m_cs); |
||||
ASSERT(!m_events.IsEmpty()); |
||||
return m_events.RemoveTail(); |
||||
} |
||||
Event* EventQueue::GetEventLast(uint32_t millis) |
||||
{ |
||||
if (!m_sem.Wait(millis)) |
||||
return NULL; |
||||
Lock l(m_cs); |
||||
if (m_events.IsEmpty()) |
||||
return NULL; |
||||
return m_events.RemoveTail(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
bool EventQueue::IsEmpty() const |
||||
{ |
||||
Lock l(m_cs); |
||||
return m_events.IsEmpty(); |
||||
} |
||||
|
||||
uint_t EventQueue::GetSize() const |
||||
{ |
||||
Lock l(m_cs); |
||||
return m_events.GetSize(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------*
|
||||
* EventThreadPool class implementation * |
||||
*-----------------------------------------------------------*/ |
||||
|
||||
void EventThreadPool::stop() |
||||
{ |
||||
ThreadPool::stop(); |
||||
EventQueue::Clear(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// EventQueue.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_EVENTQUEUE_H__ |
||||
#define __SEACAVE_EVENTQUEUE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Types.h" |
||||
#include "CriticalSection.h" |
||||
#include "Semaphore.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
class Event |
||||
{ |
||||
public: |
||||
Event(uint32_t _id) : id(_id) {} |
||||
virtual ~Event() {} |
||||
|
||||
uint32_t GetID() const { return id; } |
||||
|
||||
virtual bool Run(void* /*pArgs*/ = NULL) { return true; } |
||||
|
||||
protected: |
||||
const uint32_t id; |
||||
}; |
||||
typedef cQueue<Event*,Event*,0> EVENTQUEUE; |
||||
|
||||
|
||||
/**************************************************************************************
|
||||
* Events Queue |
||||
* -------------- |
||||
* basic eventing mechanism |
||||
* multi-thread safe |
||||
**************************************************************************************/ |
||||
|
||||
class GENERAL_API EventQueue |
||||
{ |
||||
public: |
||||
EventQueue() {} |
||||
~EventQueue() {} |
||||
|
||||
void Clear(); // reset the state of the locks and empty the queue
|
||||
|
||||
void AddEvent(Event*); //add a new event to the end of the queue
|
||||
void AddEventFirst(Event*); //add a new event to the beginning of the queue
|
||||
Event* GetEvent(); //block until an event arrives and get the first event pending in the queue
|
||||
Event* GetEvent(uint32_t millis); //block until an event arrives or time expires and get the first event pending in the queue
|
||||
Event* GetEventLast(); //block until an event arrives and get the last event pending in the queue
|
||||
Event* GetEventLast(uint32_t millis); //block until an event arrives or time expires and get the last event pending in the queue
|
||||
|
||||
bool IsEmpty() const; //are there any events in the queue?
|
||||
uint_t GetSize() const; //number of events in the queue
|
||||
|
||||
protected: |
||||
Semaphore m_sem; |
||||
mutable CriticalSection m_cs; |
||||
EVENTQUEUE m_events; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// basic event and thread pool
|
||||
class GENERAL_API EventThreadPool : public ThreadPool, public EventQueue |
||||
{ |
||||
public: |
||||
inline EventThreadPool() {} |
||||
inline EventThreadPool(size_type nThreads) : ThreadPool(nThreads) {} |
||||
inline EventThreadPool(size_type nThreads, Thread::FncStart pfnStarter, void* pData=NULL) : ThreadPool(nThreads, pfnStarter, pData) {} |
||||
inline ~EventThreadPool() {} |
||||
|
||||
void stop(); //stop threads, reset locks state and empty event queue
|
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_EVENTQUEUE_H__
|
||||
@ -0,0 +1,240 @@
@@ -0,0 +1,240 @@
|
||||
// FastDelegateBind.h
|
||||
// Helper file for FastDelegates. Provides bind() function, enabling
|
||||
// FastDelegates to be rapidly compared to programs using boost::function and boost::bind.
|
||||
//
|
||||
// Documentation is found at http://www.codeproject.com/cpp/FastDelegate.asp
|
||||
//
|
||||
// Original author: Jody Hagins.
|
||||
// Minor changes by Don Clugston.
|
||||
//
|
||||
// Warning: The arguments to 'bind' are ignored! No actual binding is performed.
|
||||
// The behaviour is equivalent to boost::bind only when the basic placeholder
|
||||
// arguments _1, _2, _3, etc are used in order.
|
||||
//
|
||||
// HISTORY:
|
||||
// 1.4 Dec 2004. Initial release as part of FastDelegate 1.4.
|
||||
|
||||
|
||||
#ifndef FASTDELEGATEBIND_H |
||||
#define FASTDELEGATEBIND_H |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FastDelegate bind()
|
||||
//
|
||||
// bind() helper function for boost compatibility.
|
||||
// (Original author: Jody Hagins).
|
||||
//
|
||||
// Add another helper, so FastDelegate can be a dropin replacement
|
||||
// for boost::bind (in a fair number of cases).
|
||||
// Note the ellipsis, because boost::bind() takes place holders
|
||||
// but FastDelegate does not care about them. Getting the place holder
|
||||
// mechanism to work, and play well with boost is a bit tricky, so
|
||||
// we do the "easy" thing...
|
||||
// Assume we have the following code...
|
||||
// using boost::bind;
|
||||
// bind(&Foo:func, &foo, _1, _2);
|
||||
// we should be able to replace the "using" with...
|
||||
// using fastdelegate::bind;
|
||||
// and everything should work fine...
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX |
||||
|
||||
namespace fastdelegate { |
||||
|
||||
//N=0
|
||||
template <class X, class Y, class RetType> |
||||
FastDelegate< RetType ( ) > |
||||
bind( |
||||
RetType (X::*func)( ), |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( ) >(y, func); |
||||
} |
||||
|
||||
template <class X, class Y, class RetType> |
||||
FastDelegate< RetType ( ) > |
||||
bind( |
||||
RetType (X::*func)( ) const, |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( ) >(y, func); |
||||
} |
||||
|
||||
//N=1
|
||||
template <class X, class Y, class RetType, class Param1> |
||||
FastDelegate< RetType ( Param1 p1 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1 ), |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1 ) >(y, func); |
||||
} |
||||
|
||||
template <class X, class Y, class RetType, class Param1> |
||||
FastDelegate< RetType ( Param1 p1 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1 ) const, |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1 ) >(y, func); |
||||
} |
||||
|
||||
//N=2
|
||||
template <class X, class Y, class RetType, class Param1, class Param2> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2 ), |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2 ) >(y, func); |
||||
} |
||||
|
||||
template <class X, class Y, class RetType, class Param1, class Param2> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2 ) const, |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2 ) >(y, func); |
||||
} |
||||
|
||||
//N=3
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3 ), |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) >(y, func); |
||||
} |
||||
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3 ) const, |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) >(y, func); |
||||
} |
||||
|
||||
//N=4
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ), |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) >(y, func); |
||||
} |
||||
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) const, |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) >(y, func); |
||||
} |
||||
|
||||
//N=5
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ), |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) >(y, func); |
||||
} |
||||
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) const, |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) >(y, func); |
||||
} |
||||
|
||||
//N=6
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ), |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) >(y, func); |
||||
} |
||||
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) const, |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) >(y, func); |
||||
} |
||||
|
||||
//N=7
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6, class Param7> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ), |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) >(y, func); |
||||
} |
||||
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6, class Param7> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) const, |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) >(y, func); |
||||
} |
||||
|
||||
//N=8
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6, class Param7, class Param8> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ), |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) >(y, func); |
||||
} |
||||
|
||||
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6, class Param7, class Param8> |
||||
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) > |
||||
bind( |
||||
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) const, |
||||
Y * y, |
||||
...) |
||||
{ |
||||
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) >(y, func); |
||||
} |
||||
|
||||
|
||||
#endif //FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX
|
||||
|
||||
} // namespace fastdelegate
|
||||
|
||||
#endif // !defined(FASTDELEGATEBIND_H)
|
||||
|
||||
@ -0,0 +1,379 @@
@@ -0,0 +1,379 @@
|
||||
/** \file SRDelegate.hpp
|
||||
* |
||||
* This is a C++11 implementation by janezz55(code.google) for the original "The Impossibly Fast C++ Delegates" authored by Sergey Ryazanov. |
||||
* |
||||
* This is a copy checkouted from https://code.google.com/p/cpppractice/source/browse/trunk/delegate.hpp on 2014/06/07.
|
||||
* Last change in the chunk was r370 on Feb 9, 2014. |
||||
* |
||||
* The following modifications were added by Benjamin YanXiang Huang |
||||
* - replace light_ptr with std::shared_ptr |
||||
* - renamed src file |
||||
* |
||||
* Reference: |
||||
* - http://codereview.stackexchange.com/questions/14730/impossibly-fast-delegate-in-c11
|
||||
* - http://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates
|
||||
* - https://code.google.com/p/cpppractice/source/browse/trunk/delegate.hpp
|
||||
*/ |
||||
|
||||
#pragma once |
||||
#ifndef SRDELEGATE_HPP |
||||
#define SRDELEGATE_HPP |
||||
|
||||
#include <cassert> |
||||
#include <cstring> |
||||
#include <memory> |
||||
#include <new> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
// VC work around for constexpr and noexcept: VC2013 and below do not support these 2 keywords
|
||||
#if defined(_MSC_VER) && (_MSC_VER <= 1800) |
||||
#define constexpr const |
||||
#define noexcept throw() |
||||
#endif |
||||
|
||||
namespace fastdelegate |
||||
{ |
||||
|
||||
template <typename T> class delegate; |
||||
|
||||
template<class R, class ...A> |
||||
class delegate<R(A...)> |
||||
{ |
||||
using stub_ptr_type = R(*)(void *, A&&...); |
||||
|
||||
delegate(void * const o, stub_ptr_type const m) noexcept : object_ptr_(o), stub_ptr_(m) {} |
||||
|
||||
public: |
||||
delegate(void) = default; |
||||
|
||||
delegate(delegate const &) = default; |
||||
|
||||
delegate(delegate && d) |
||||
: object_ptr_(d.object_ptr_), stub_ptr_(d.stub_ptr_), deleter_(d.deleter_), store_(d.store_), store_size_(d.store_size_) |
||||
{ |
||||
d.object_ptr_ = nullptr; |
||||
d.stub_ptr_ = nullptr; |
||||
d.deleter_ = nullptr; |
||||
d.store_ = nullptr; |
||||
d.store_size_ = 0; |
||||
} |
||||
|
||||
delegate(::std::nullptr_t const) noexcept : delegate() { } |
||||
|
||||
template <class C, typename = typename ::std::enable_if< ::std::is_class<C>::value, C>::type> |
||||
explicit delegate(C const * const o) noexcept : |
||||
object_ptr_(const_cast<C *>(o)) |
||||
{} |
||||
|
||||
template <class C, typename = typename ::std::enable_if< ::std::is_class<C> {}>::type> |
||||
explicit delegate(C const & o) noexcept : |
||||
object_ptr_(const_cast<C *>(&o)) |
||||
{} |
||||
|
||||
template <class C> |
||||
delegate(C * const object_ptr, R(C::* const method_ptr)(A...)) |
||||
{ |
||||
*this = from(object_ptr, method_ptr); |
||||
} |
||||
|
||||
template <class C> |
||||
delegate(C * const object_ptr, R(C::* const method_ptr)(A...) const) |
||||
{ |
||||
*this = from(object_ptr, method_ptr); |
||||
} |
||||
|
||||
template <class C> |
||||
delegate(C & object, R(C::* const method_ptr)(A...)) |
||||
{ |
||||
*this = from(object, method_ptr); |
||||
} |
||||
|
||||
template <class C> |
||||
delegate(C const & object, R(C::* const method_ptr)(A...) const) |
||||
{ |
||||
*this = from(object, method_ptr); |
||||
} |
||||
|
||||
template < |
||||
typename T, |
||||
typename = typename ::std::enable_if<!::std::is_same<delegate, typename ::std::decay<T>::type>::value>::type |
||||
> |
||||
delegate(T&& f) |
||||
: store_(operator new(sizeof(typename ::std::decay<T>::type)) |
||||
, functor_deleter<typename ::std::decay<T>::type>) |
||||
, store_size_(sizeof(typename ::std::decay<T>::type)) |
||||
{ |
||||
using functor_type = typename ::std::decay<T>::type; |
||||
|
||||
new(store_.get()) functor_type(::std::forward<T>(f)); |
||||
object_ptr_ = store_.get(); |
||||
|
||||
stub_ptr_ = functor_stub<functor_type>; |
||||
deleter_ = deleter_stub<functor_type>; |
||||
} |
||||
|
||||
delegate & operator=(delegate const &) = default; |
||||
|
||||
delegate & operator=(delegate&& d) |
||||
{ |
||||
object_ptr_ = d.object_ptr_; |
||||
stub_ptr_ = d.stub_ptr_; |
||||
deleter_ = d.deleter_; |
||||
store_ = d.store_; |
||||
store_size_ = d.store_size_; |
||||
|
||||
d.object_ptr_ = nullptr; |
||||
d.stub_ptr_ = nullptr; |
||||
d.deleter_ = nullptr; |
||||
d.store_ = nullptr; |
||||
d.store_size_ = 0; |
||||
|
||||
return *this; |
||||
} |
||||
|
||||
template <class C> |
||||
delegate & operator=(R(C::* const rhs)(A...)) |
||||
{ |
||||
return *this = from(static_cast<C *>(object_ptr_), rhs); |
||||
} |
||||
|
||||
template <class C> |
||||
delegate & operator=(R(C::* const rhs)(A...) const) |
||||
{ |
||||
return *this = from(static_cast<C const *>(object_ptr_), rhs); |
||||
} |
||||
|
||||
template < |
||||
typename T |
||||
, typename = typename ::std::enable_if<!::std::is_same<delegate, typename ::std::decay<T>::type>::value>::type |
||||
> |
||||
delegate & operator=(T&& f) |
||||
{ |
||||
using functor_type = typename ::std::decay<T>::type; |
||||
|
||||
if ((sizeof(functor_type) > store_size_) || !store_.unique()) |
||||
{ |
||||
store_.reset(operator new(sizeof(functor_type)), functor_deleter<functor_type>); |
||||
store_size_ = sizeof(functor_type); |
||||
} |
||||
else |
||||
deleter_(store_.get()); |
||||
|
||||
new(store_.get()) functor_type(::std::forward<T>(f)); |
||||
object_ptr_ = store_.get(); |
||||
|
||||
stub_ptr_ = functor_stub<functor_type>; |
||||
deleter_ = deleter_stub<functor_type>; |
||||
|
||||
return *this; |
||||
} |
||||
|
||||
template <R(* const function_ptr)(A...)> |
||||
static delegate from(void) noexcept |
||||
{ |
||||
return { nullptr, function_stub<function_ptr> }; |
||||
} |
||||
|
||||
template <class C, R(C::* const method_ptr)(A...)> |
||||
static delegate from(C * const object_ptr) noexcept |
||||
{ |
||||
return { object_ptr, method_stub<C, method_ptr> }; |
||||
} |
||||
|
||||
template <class C, R(C::* const method_ptr)(A...) const> |
||||
static delegate from(C const * const object_ptr) noexcept |
||||
{ |
||||
return { const_cast<C *>(object_ptr), const_method_stub<C, method_ptr> }; |
||||
} |
||||
|
||||
template <class C, R(C::* const method_ptr)(A...)> |
||||
static delegate from(C & object) noexcept |
||||
{ |
||||
return { &object, method_stub<C, method_ptr> }; |
||||
} |
||||
|
||||
template <class C, R(C::* const method_ptr)(A...) const> |
||||
static delegate from(C const & object) noexcept |
||||
{ |
||||
return { const_cast<C *>(&object), const_method_stub<C, method_ptr> }; |
||||
} |
||||
|
||||
template <typename T> |
||||
static delegate from(T && f) |
||||
{ |
||||
return ::std::forward<T>(f); |
||||
} |
||||
|
||||
static delegate from(R(* const function_ptr)(A...)) |
||||
{ |
||||
return function_ptr; |
||||
} |
||||
|
||||
template <class C> |
||||
using member_pair = ::std::pair<C * const, R(C::* const)(A...)>; |
||||
|
||||
template <class C> |
||||
using const_member_pair = ::std::pair<C const * const, R(C::* const)(A...) const>; |
||||
|
||||
template <class C> |
||||
static delegate from(C * const object_ptr, R(C::* const method_ptr)(A...)) |
||||
{ |
||||
return member_pair<C>(object_ptr, method_ptr); |
||||
} |
||||
|
||||
template <class C> |
||||
static delegate from(C const * const object_ptr, R(C::* const method_ptr)(A...) const) |
||||
{ |
||||
return const_member_pair<C>(object_ptr, method_ptr); |
||||
} |
||||
|
||||
template <class C> |
||||
static delegate from(C & object, R(C::* const method_ptr)(A...)) |
||||
{ |
||||
return member_pair<C>(&object, method_ptr); |
||||
} |
||||
|
||||
template <class C> |
||||
static delegate from(C const & object, R(C::* const method_ptr)(A...) const) |
||||
{ |
||||
return const_member_pair<C>(&object, method_ptr); |
||||
} |
||||
|
||||
void reset(void) |
||||
{ |
||||
stub_ptr_ = nullptr; |
||||
store_.reset(); |
||||
} |
||||
|
||||
void reset_stub(void) noexcept { stub_ptr_ = nullptr; } |
||||
|
||||
void swap(delegate & other) noexcept { ::std::swap(*this, other); } |
||||
|
||||
bool operator==(delegate const & rhs) const noexcept |
||||
{ |
||||
// comparison between functor and non-functor is left as undefined at the moment.
|
||||
if (store_size_ && rhs.store_size_) // both functors
|
||||
return (std::memcmp(store_.get(), rhs.store_.get(), store_size_) == 0) && (stub_ptr_ == rhs.stub_ptr_); |
||||
return (object_ptr_ == rhs.object_ptr_) && (stub_ptr_ == rhs.stub_ptr_); |
||||
} |
||||
|
||||
bool operator!=(delegate const & rhs) const noexcept |
||||
{ |
||||
return !operator==(rhs); |
||||
} |
||||
|
||||
bool operator<(delegate const & rhs) const noexcept |
||||
{ |
||||
return (object_ptr_ < rhs.object_ptr_) || |
||||
((object_ptr_ == rhs.object_ptr_) && (stub_ptr_ < rhs.stub_ptr_)); |
||||
} |
||||
|
||||
bool operator==(::std::nullptr_t const) const noexcept |
||||
{ |
||||
return !stub_ptr_; |
||||
} |
||||
|
||||
bool operator!=(::std::nullptr_t const) const noexcept |
||||
{ |
||||
return stub_ptr_; |
||||
} |
||||
|
||||
explicit operator bool() const noexcept |
||||
{ |
||||
return stub_ptr_; |
||||
} |
||||
|
||||
R operator()(A... args) const |
||||
{ |
||||
// assert(stub_ptr);
|
||||
return stub_ptr_(object_ptr_, ::std::forward<A>(args)...); |
||||
} |
||||
|
||||
private: |
||||
friend struct ::std::hash<delegate>; |
||||
|
||||
using deleter_type = void (*)(void *); |
||||
|
||||
void * object_ptr_ = nullptr; |
||||
stub_ptr_type stub_ptr_ {}; |
||||
|
||||
deleter_type deleter_ = nullptr; |
||||
|
||||
::std::shared_ptr<void> store_ = nullptr; |
||||
::std::size_t store_size_ = 0; |
||||
|
||||
template <class T> |
||||
static void functor_deleter(void * const p) |
||||
{ |
||||
static_cast<T *>(p)->~T(); |
||||
operator delete(p); |
||||
} |
||||
|
||||
template <class T> |
||||
static void deleter_stub(void * const p) |
||||
{ |
||||
static_cast<T *>(p)->~T(); |
||||
} |
||||
|
||||
template <R(*function_ptr)(A...)> |
||||
static R function_stub(void * const, A && ... args) |
||||
{ |
||||
return function_ptr(::std::forward<A>(args)...); |
||||
} |
||||
|
||||
template <class C, R(C::*method_ptr)(A...)> |
||||
static R method_stub(void * const object_ptr, A && ... args) |
||||
{ |
||||
return (static_cast<C *>(object_ptr)->*method_ptr)(::std::forward<A>(args)...); |
||||
} |
||||
|
||||
template <class C, R(C::*method_ptr)(A...) const> |
||||
static R const_method_stub(void * const object_ptr, A && ... args) |
||||
{ |
||||
return (static_cast<C const *>(object_ptr)->*method_ptr)(::std::forward<A>(args)...); |
||||
} |
||||
|
||||
template <typename> |
||||
struct is_member_pair : ::std::false_type { }; |
||||
|
||||
template <class C> |
||||
struct is_member_pair< ::std::pair<C * const, R(C::* const)(A...)> > : ::std::true_type {}; |
||||
|
||||
template <typename> |
||||
struct is_const_member_pair : ::std::false_type { }; |
||||
|
||||
template <class C> |
||||
struct is_const_member_pair< ::std::pair<C const * const, R(C::* const)(A...) const> > : ::std::true_type {}; |
||||
|
||||
template <typename T> |
||||
static typename ::std::enable_if<!(is_member_pair<T>::value || is_const_member_pair<T>::value), R>::type |
||||
functor_stub(void * const object_ptr, A && ... args) |
||||
{ |
||||
return (*static_cast<T *>(object_ptr))(::std::forward<A>(args)...); |
||||
} |
||||
|
||||
template <typename T> |
||||
static typename ::std::enable_if<is_member_pair<T>::value || is_const_member_pair<T>::value, R>::type |
||||
functor_stub(void * const object_ptr, A && ... args) |
||||
{ |
||||
return (static_cast<T *>(object_ptr)->first->*static_cast<T *>(object_ptr)->second)(::std::forward<A>(args)...); |
||||
} |
||||
}; |
||||
|
||||
} |
||||
|
||||
namespace std |
||||
{ |
||||
template <typename R, typename ...A> |
||||
struct hash<::fastdelegate::delegate<R(A...)> > |
||||
{ |
||||
size_t operator()(::fastdelegate::delegate<R(A...)> const & d) const noexcept |
||||
{ |
||||
auto const seed(hash<void *>()(d.object_ptr_)); |
||||
return hash<decltype(d.stub_ptr_)>()(d.stub_ptr_) + 0x9e3779b9 + (seed << 6) + (seed >> 2); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
#endif // SRDELEGATE_HPP
|
||||
@ -0,0 +1,686 @@
@@ -0,0 +1,686 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// File.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_FILE_H__ |
||||
#define __SEACAVE_FILE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Streams.h" |
||||
|
||||
// Under both Windows and Unix, the stat function is used for classification
|
||||
|
||||
// Under Gnu/Linux, the following classifications are defined
|
||||
// source: Gnu/Linux man page for stat(2) http://linux.die.net/man/2/stat
|
||||
// S_IFMT 0170000 bitmask for the file type bitfields
|
||||
// S_IFSOCK 0140000 socket (Note this overlaps with S_IFDIR)
|
||||
// S_IFLNK 0120000 symbolic link
|
||||
// S_IFREG 0100000 regular file
|
||||
// S_IFBLK 0060000 block device
|
||||
// S_IFDIR 0040000 directory
|
||||
// S_IFCHR 0020000 character device
|
||||
// S_IFIFO 0010000 FIFO
|
||||
// There are also some Posix-standard macros:
|
||||
// S_ISREG(m) is it a regular file?
|
||||
// S_ISDIR(m) directory?
|
||||
// S_ISCHR(m) character device?
|
||||
// S_ISBLK(m) block device?
|
||||
// S_ISFIFO(m) FIFO (named pipe)?
|
||||
// S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
|
||||
// S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
|
||||
// Under Windows, the following are defined:
|
||||
// source: Header file sys/stat.h distributed with Visual Studio 10
|
||||
// _S_IFMT (S_IFMT) 0xF000 file type mask
|
||||
// _S_IFREG (S_IFREG) 0x8000 regular
|
||||
// _S_IFDIR (S_IFDIR) 0x4000 directory
|
||||
// _S_IFCHR (S_IFCHR) 0x2000 character special
|
||||
// _S_IFIFO 0x1000 pipe
|
||||
|
||||
#ifdef _MSC_VER |
||||
#include <io.h> |
||||
// file type tests are not defined for some reason on Windows despite them providing the stat() function!
|
||||
#define F_OK 0 |
||||
#define X_OK 1 |
||||
#define W_OK 2 |
||||
#define R_OK 4 |
||||
// Posix-style macros for Windows
|
||||
#ifndef S_ISREG |
||||
#define S_ISREG(mode) ((mode & _S_IFMT) == _S_IFREG) |
||||
#endif |
||||
#ifndef S_ISDIR |
||||
#define S_ISDIR(mode) ((mode & _S_IFMT) == _S_IFDIR) |
||||
#endif |
||||
#ifndef S_ISCHR |
||||
#define S_ISCHR(mode) ((mode & _S_IFMT) == _S_IFCHR) |
||||
#endif |
||||
#ifndef S_ISBLK |
||||
#define S_ISBLK(mode) (false) |
||||
#endif |
||||
#ifndef S_ISFIFO |
||||
#define S_ISFIFO(mode) ((mode & _S_IFMT) == _S_IFIFO) |
||||
#endif |
||||
#ifndef S_ISLNK |
||||
#define S_ISLNK(mode) (false) |
||||
#endif |
||||
#ifndef S_ISSOCK |
||||
#define S_ISSOCK(mode) (false) |
||||
#endif |
||||
#else |
||||
#include <unistd.h> |
||||
#include <dirent.h> |
||||
#define _taccess access |
||||
#endif |
||||
#ifdef __APPLE__ |
||||
#define fdatasync fsync |
||||
#endif |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
// size of the stored file size variable
|
||||
#ifdef LARGEFILESIZE |
||||
#define FILESIZE size_f_t |
||||
#else |
||||
#define FILESIZE size_t |
||||
#endif |
||||
|
||||
// invalid file handle
|
||||
#ifdef _MSC_VER |
||||
#define FILE_INVALID_HANDLE INVALID_HANDLE_VALUE |
||||
#else |
||||
#define FILE_INVALID_HANDLE int(-1) |
||||
#endif |
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
class GENERAL_API File : public IOStream { |
||||
public: |
||||
typedef struct FILEINFO_TYPE { |
||||
String path; |
||||
FILESIZE size; |
||||
DWORD attrib; |
||||
} FILEINFO; |
||||
typedef cList<FILEINFO> FileInfoArr; |
||||
|
||||
typedef enum FMCREATE_TYPE { |
||||
OPEN = 0x01, |
||||
CREATE = 0x02, |
||||
TRUNCATE = 0x04 |
||||
} FMCREATE; |
||||
|
||||
typedef enum FMFLAGS_TYPE { |
||||
SYNC = 0x01, |
||||
NOBUFFER = 0x02, |
||||
RANDOM = 0x04, |
||||
SEQUENTIAL = 0x08 |
||||
} FMFLAGS; |
||||
|
||||
inline File() : h(FILE_INVALID_HANDLE) { |
||||
#ifndef _RELEASE |
||||
breakRead = -1; |
||||
breakWrite = -1; |
||||
#endif |
||||
} |
||||
inline File(LPCTSTR aFileName, int access, int mode, int flags=0) : h(FILE_INVALID_HANDLE) { |
||||
#ifndef _RELEASE |
||||
breakRead = -1; |
||||
breakWrite = -1; |
||||
#endif |
||||
File::open(aFileName, access, mode, flags); |
||||
} |
||||
|
||||
virtual ~File() { |
||||
File::close(); |
||||
} |
||||
|
||||
#ifdef _SUPPORT_CPP11 |
||||
inline File(File&& rhs) : h(rhs.h) { |
||||
#ifndef _RELEASE |
||||
breakRead = rhs.breakRead; |
||||
breakWrite = rhs.breakWrite; |
||||
#endif |
||||
rhs.h = FILE_INVALID_HANDLE; |
||||
} |
||||
|
||||
inline File& operator=(File&& rhs) { |
||||
h = rhs.h; |
||||
#ifndef _RELEASE |
||||
breakRead = rhs.breakRead; |
||||
breakWrite = rhs.breakWrite; |
||||
#endif |
||||
rhs.h = FILE_INVALID_HANDLE; |
||||
return *this; |
||||
} |
||||
#endif |
||||
|
||||
bool isOpen() const { |
||||
return h != FILE_INVALID_HANDLE; |
||||
} |
||||
|
||||
#ifdef _MSC_VER |
||||
typedef enum FMACCESS_TYPE { |
||||
READ = GENERIC_READ, |
||||
WRITE = GENERIC_WRITE, |
||||
RW = READ | WRITE |
||||
} FMACCESS; |
||||
|
||||
typedef enum FMCHECKACCESS_TYPE { |
||||
CA_EXIST = F_OK, // existence
|
||||
CA_WRITE = W_OK, // write
|
||||
CA_READ = R_OK, // read
|
||||
CA_RW = R_OK | W_OK, |
||||
} FMCHECKACCESS; |
||||
|
||||
/**
|
||||
* Open the file specified. |
||||
* If there are errors, h is set to FILE_INVALID_HANDLE. |
||||
* Use isOpen() to check. |
||||
*/ |
||||
virtual void open(LPCTSTR aFileName, int access, int mode, int flags=0) { |
||||
ASSERT(access == WRITE || access == READ || access == (READ | WRITE)); |
||||
|
||||
close(); |
||||
|
||||
DWORD m = 0; |
||||
if (mode & OPEN) { |
||||
if (mode & CREATE) { |
||||
m = (mode & TRUNCATE) ? CREATE_ALWAYS : OPEN_ALWAYS; |
||||
} else { |
||||
m = (mode & TRUNCATE) ? TRUNCATE_EXISTING : OPEN_EXISTING; |
||||
} |
||||
} else { |
||||
ASSERT(mode & CREATE); |
||||
m = (mode & TRUNCATE) ? CREATE_ALWAYS : CREATE_NEW; |
||||
} |
||||
|
||||
DWORD f = 0; |
||||
if (flags & SYNC) |
||||
f |= FILE_FLAG_WRITE_THROUGH; |
||||
if (flags & NOBUFFER) |
||||
f |= FILE_FLAG_NO_BUFFERING; |
||||
if (flags & RANDOM) |
||||
f |= FILE_FLAG_RANDOM_ACCESS; |
||||
if (flags & SEQUENTIAL) |
||||
f |= FILE_FLAG_SEQUENTIAL_SCAN; |
||||
|
||||
h = ::CreateFile(aFileName, access, FILE_SHARE_READ, NULL, m, f, NULL); |
||||
} |
||||
|
||||
virtual void close() { |
||||
if (isOpen()) { |
||||
FlushFileBuffers(h); |
||||
CloseHandle(h); |
||||
h = FILE_INVALID_HANDLE; |
||||
} |
||||
} |
||||
|
||||
uint32_t getLastModified() { |
||||
ASSERT(isOpen()); |
||||
FILETIME f = {0}; |
||||
::GetFileTime(h, NULL, NULL, &f); |
||||
return convertTime(&f); |
||||
} |
||||
|
||||
static uint32_t convertTime(FILETIME* f) { |
||||
SYSTEMTIME s = { 1970, 1, 0, 1, 0, 0, 0, 0 }; |
||||
FILETIME f2 = {0}; |
||||
if (::SystemTimeToFileTime(&s, &f2)) { |
||||
uint64_t* a = (uint64_t*)f; |
||||
uint64_t* b = (uint64_t*)&f2; |
||||
*a -= *b; |
||||
*a /= (1000LL*1000LL*1000LL/100LL); // 100ns > s
|
||||
return (uint32_t)*a; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
size_f_t getSize() const override { |
||||
ASSERT(isOpen()); |
||||
DWORD x; |
||||
DWORD l = ::GetFileSize(h, &x); |
||||
if ((l == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR)) |
||||
return SIZE_NA; |
||||
return (size_f_t)l | ((size_f_t)x)<<32; |
||||
} |
||||
|
||||
virtual bool setSize(size_f_t newSize) { |
||||
const size_f_t pos = getPos(); |
||||
if (pos == SIZE_NA) |
||||
return false; |
||||
if (!setPos(newSize)) |
||||
return false; |
||||
if (!setEOF()) |
||||
return false; |
||||
if (!setPos(pos)) |
||||
return false; |
||||
return true; |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
ASSERT(isOpen()); |
||||
LONG x = 0; |
||||
const DWORD l = ::SetFilePointer(h, 0, &x, FILE_CURRENT); |
||||
if (l == INVALID_SET_FILE_POINTER) |
||||
return SIZE_NA; |
||||
return (size_f_t)l | ((size_f_t)x)<<32; |
||||
} |
||||
|
||||
bool setPos(size_f_t pos) override { |
||||
ASSERT(isOpen()); |
||||
LONG x = (LONG) (pos>>32); |
||||
return (::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_BEGIN) != INVALID_SET_FILE_POINTER); |
||||
} |
||||
|
||||
virtual bool setEndPos(size_f_t pos) { |
||||
ASSERT(isOpen()); |
||||
LONG x = (LONG) (pos>>32); |
||||
return (::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_END) != INVALID_SET_FILE_POINTER); |
||||
} |
||||
|
||||
virtual bool movePos(size_f_t pos) { |
||||
ASSERT(isOpen()); |
||||
LONG x = (LONG) (pos>>32); |
||||
return (::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_CURRENT) != INVALID_SET_FILE_POINTER); |
||||
} |
||||
|
||||
size_t read(void* buf, size_t len) override { |
||||
ASSERT(isOpen()); |
||||
#ifndef _RELEASE |
||||
if (breakRead != (size_t)(-1)) { |
||||
if (breakRead <= len) { |
||||
ASSERT("FILE::read() break" == NULL); |
||||
breakRead = -1; |
||||
} else { |
||||
breakRead -= len; |
||||
} |
||||
} |
||||
#endif |
||||
DWORD x; |
||||
if (!::ReadFile(h, buf, (DWORD)len, &x, NULL)) |
||||
return STREAM_ERROR; |
||||
return x; |
||||
} |
||||
|
||||
size_t write(const void* buf, size_t len) override { |
||||
ASSERT(isOpen()); |
||||
#ifndef _RELEASE |
||||
if (breakWrite != (size_t)(-1)) { |
||||
if (breakWrite <= len) { |
||||
ASSERT("FILE::write() break" == NULL); |
||||
breakWrite = -1; |
||||
} else { |
||||
breakWrite -= len; |
||||
} |
||||
} |
||||
#endif |
||||
DWORD x; |
||||
if (!::WriteFile(h, buf, (DWORD)len, &x, NULL)) |
||||
return STREAM_ERROR; |
||||
ASSERT(x == len); |
||||
return x; |
||||
} |
||||
virtual bool setEOF() { |
||||
ASSERT(isOpen()); |
||||
return (SetEndOfFile(h) != FALSE); |
||||
} |
||||
|
||||
size_t flush() override { |
||||
ASSERT(isOpen()); |
||||
return (FlushFileBuffers(h) ? 0 : STREAM_ERROR); |
||||
} |
||||
|
||||
virtual bool getInfo(BY_HANDLE_FILE_INFORMATION* fileInfo) { |
||||
ASSERT(isOpen()); |
||||
return (GetFileInformationByHandle(h, fileInfo) != FALSE); |
||||
} |
||||
|
||||
static uint32_t getAttrib(LPCTSTR aFileName) { |
||||
return GetFileAttributes(aFileName); |
||||
} |
||||
|
||||
static bool setAttrib(LPCTSTR aFileName, uint32_t attribs) { |
||||
return (SetFileAttributes(aFileName, attribs) != FALSE); |
||||
} |
||||
|
||||
static void deleteFile(LPCTSTR aFileName) { ::DeleteFile(aFileName); } |
||||
static bool renameFile(LPCTSTR source, LPCTSTR target) { |
||||
if (!::MoveFile(source, target)) { |
||||
// Can't move, try copy/delete...
|
||||
if (!::CopyFile(source, target, FALSE)) |
||||
return false; |
||||
deleteFile(source); |
||||
} |
||||
return true; |
||||
} |
||||
static bool copyFile(LPCTSTR source, LPCTSTR target) { return ::CopyFile(source, target, FALSE) == TRUE; } |
||||
|
||||
static size_f_t getSize(LPCTSTR aFileName) { |
||||
const HANDLE fh = ::CreateFile(aFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL); |
||||
if (fh == FILE_INVALID_HANDLE) |
||||
return SIZE_NA; |
||||
DWORD x; |
||||
DWORD l = ::GetFileSize(fh, &x); |
||||
CloseHandle(fh); |
||||
if ((l == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR)) |
||||
return SIZE_NA; |
||||
return (((size_f_t)l) | (((size_f_t)x)<<32)); |
||||
} |
||||
|
||||
|
||||
static size_f_t findFiles(const String& _strPath, const String& strMask, bool bProcessSubdir, FileInfoArr& arrFiles) { |
||||
// List all the files.
|
||||
WIN32_FIND_DATA fd; |
||||
HANDLE hFind; |
||||
size_f_t totalSize = 0; |
||||
String strPath(_strPath); |
||||
Util::ensureFolderSlash(strPath); |
||||
//Find all the files in this folder.
|
||||
hFind = FindFirstFile((strPath + strMask).c_str(), &fd); |
||||
if (hFind != FILE_INVALID_HANDLE) { |
||||
do { |
||||
// this is a file that can be used
|
||||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) |
||||
continue; |
||||
// Store the file name.
|
||||
FILEINFO& fileInfo = arrFiles.AddEmpty(); |
||||
fileInfo.path = strPath + fd.cFileName; |
||||
#ifdef LARGEFILESIZE |
||||
fileInfo.size = (((size_f_t)fd.nFileSizeLow) | (((size_f_t)fd.nFileSizeHigh)<<32)); |
||||
#else |
||||
fileInfo.size = fd.nFileSizeLow; |
||||
#endif |
||||
fileInfo.attrib = fd.dwFileAttributes; |
||||
totalSize += fileInfo.size; |
||||
} while (FindNextFile(hFind, &fd)); |
||||
FindClose(hFind); |
||||
} |
||||
//Process the subfolders also...
|
||||
if (!bProcessSubdir) |
||||
return totalSize; |
||||
hFind = FindFirstFile((strPath + '*').c_str(), &fd); |
||||
if (hFind != FILE_INVALID_HANDLE) { |
||||
do { |
||||
// if SUBDIR then process that too
|
||||
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) |
||||
continue; |
||||
if (!_tcscmp(fd.cFileName, _T("."))) |
||||
continue; |
||||
if (!_tcscmp(fd.cFileName, _T(".."))) |
||||
continue; |
||||
// Process all subfolders recursively
|
||||
totalSize += findFiles(strPath + fd.cFileName + PATH_SEPARATOR, strMask, true, arrFiles); |
||||
} while (FindNextFile(hFind, &fd)); |
||||
FindClose(hFind); |
||||
} |
||||
return totalSize; |
||||
} |
||||
|
||||
#else // _MSC_VER
|
||||
|
||||
typedef enum FMACCESS_TYPE { |
||||
READ = 0x01, |
||||
WRITE = 0x02, |
||||
RW = READ | WRITE, |
||||
} FMACCESS; |
||||
|
||||
typedef enum FMCHECKACCESS_TYPE { |
||||
CA_EXIST = F_OK, // existence
|
||||
CA_WRITE = W_OK, // write
|
||||
CA_READ = R_OK, // read
|
||||
CA_RW = R_OK | W_OK, |
||||
CA_EXEC = X_OK, // execute
|
||||
} FMCHECKACCESS; |
||||
|
||||
/**
|
||||
* Open the file specified. |
||||
* If there are errors, h is set to FILE_INVALID_HANDLE. |
||||
* Use isOpen() to check. |
||||
*/ |
||||
virtual void open(LPCTSTR aFileName, int access, int mode, int flags=0) { |
||||
ASSERT(access == WRITE || access == READ || access == (READ | WRITE)); |
||||
|
||||
close(); |
||||
|
||||
int m = 0; |
||||
if (access == READ) |
||||
m |= O_RDONLY; |
||||
else if (access == WRITE) |
||||
m |= O_WRONLY; |
||||
else |
||||
m |= O_RDWR; |
||||
|
||||
if (mode & CREATE) |
||||
m |= O_CREAT; |
||||
if (mode & TRUNCATE) |
||||
m |= O_TRUNC; |
||||
|
||||
if (flags & SYNC) |
||||
m |= O_DSYNC; |
||||
#ifndef __APPLE__ |
||||
if (flags & NOBUFFER) |
||||
m |= O_DIRECT; |
||||
#endif |
||||
h = ::open(aFileName, m, S_IRUSR | S_IWUSR); |
||||
} |
||||
|
||||
virtual void close() { |
||||
if (h != FILE_INVALID_HANDLE) { |
||||
::close(h); |
||||
h = FILE_INVALID_HANDLE; |
||||
} |
||||
} |
||||
|
||||
uint32_t getLastModified() { |
||||
ASSERT(isOpen()); |
||||
struct stat s; |
||||
if (::fstat(h, &s) == -1) |
||||
return 0; |
||||
return (uint32_t)s.st_mtime; |
||||
} |
||||
|
||||
size_f_t getSize() const override { |
||||
ASSERT(isOpen()); |
||||
struct stat s; |
||||
if (::fstat(h, &s) == -1) |
||||
return SIZE_NA; |
||||
return (size_f_t)s.st_size; |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
ASSERT(isOpen()); |
||||
return (size_f_t)lseek(h, 0, SEEK_CUR); |
||||
} |
||||
|
||||
bool setPos(size_f_t pos) override { ASSERT(isOpen()); return lseek(h, (off_t)pos, SEEK_SET) != (off_t)-1; } |
||||
virtual bool setEndPos(size_f_t pos) { ASSERT(isOpen()); return lseek(h, (off_t)pos, SEEK_END) != (off_t)-1; } |
||||
virtual bool movePos(size_f_t pos) { ASSERT(isOpen()); return lseek(h, (off_t)pos, SEEK_CUR) != (off_t)-1; } |
||||
|
||||
size_t read(void* buf, size_t len) override { |
||||
ASSERT(isOpen()); |
||||
#ifndef _RELEASE |
||||
if (breakRead != (size_t)(-1)) { |
||||
if (breakRead <= len) { |
||||
ASSERT("FILE::read() break" == NULL); |
||||
breakRead = -1; |
||||
} else { |
||||
breakRead -= len; |
||||
} |
||||
} |
||||
#endif |
||||
ssize_t x = ::read(h, buf, len); |
||||
if (x == -1) |
||||
return STREAM_ERROR; |
||||
return (size_t)x; |
||||
} |
||||
|
||||
size_t write(const void* buf, size_t len) override { |
||||
ASSERT(isOpen()); |
||||
#ifndef _RELEASE |
||||
if (breakWrite != (size_t)(-1)) { |
||||
if (breakWrite <= len) { |
||||
ASSERT("FILE::write() break" == NULL); |
||||
breakWrite = -1; |
||||
} else { |
||||
breakWrite -= len; |
||||
} |
||||
} |
||||
#endif |
||||
ssize_t x = ::write(h, buf, len); |
||||
if (x == -1) |
||||
return STREAM_ERROR; |
||||
if (x < (ssize_t)len) |
||||
return STREAM_ERROR; |
||||
return x; |
||||
} |
||||
|
||||
virtual bool setEOF() { |
||||
ASSERT(isOpen()); |
||||
return (ftruncate(h, (off_t)getPos()) != -1); |
||||
} |
||||
virtual bool setSize(size_f_t newSize) { |
||||
ASSERT(isOpen()); |
||||
return (ftruncate(h, (off_t)newSize) != -1); |
||||
} |
||||
|
||||
size_t flush() override { |
||||
ASSERT(isOpen()); |
||||
return fdatasync(h); |
||||
} |
||||
|
||||
static void deleteFile(LPCTSTR aFileName) { ::remove(aFileName); } |
||||
static bool renameFile(LPCTSTR source, LPCTSTR target) { return ::rename(source, target) == 0; } |
||||
static bool copyFile(LPCTSTR source, LPCTSTR target) { |
||||
std::ifstream src(source, std::ios::binary); |
||||
if (!src.is_open()) |
||||
return false; |
||||
std::ofstream dst(target, std::ios::binary); |
||||
if (!dst.is_open()) |
||||
return false; |
||||
dst << src.rdbuf(); |
||||
return true; |
||||
} |
||||
|
||||
static size_f_t getSize(LPCTSTR aFileName) { |
||||
struct stat buf; |
||||
if (stat(aFileName, &buf) != 0) |
||||
return SIZE_NA; |
||||
return buf.st_size; |
||||
} |
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
// test for whether there's something (i.e. folder or file) with this name
|
||||
// and what access mode is supported
|
||||
static bool isPresent(LPCTSTR path) { |
||||
struct stat buf; |
||||
return stat(path, &buf) == 0; |
||||
} |
||||
static bool access(LPCTSTR path, int mode=CA_EXIST) { |
||||
return ::_taccess(path, mode) == 0; |
||||
} |
||||
// test for whether there's something present and its a folder
|
||||
static bool isFolder(LPCTSTR path) { |
||||
struct stat buf; |
||||
if (!(stat(path, &buf) == 0)) |
||||
return false; |
||||
// If the object is present, see if it is a directory
|
||||
// this is the Posix-approved way of testing
|
||||
return S_ISDIR(buf.st_mode); |
||||
} |
||||
// test for whether there's something present and its a file
|
||||
// a file can be a regular file, a symbolic link, a FIFO or a socket, but not a device
|
||||
static bool isFile(LPCTSTR path) { |
||||
struct stat buf; |
||||
if (!(stat(path, &buf) == 0)) |
||||
return false; |
||||
// If the object is present, see if it is a file or file-like object
|
||||
// Note that devices are neither folders nor files
|
||||
// this is the Posix-approved way of testing
|
||||
return S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode) || S_ISSOCK(buf.st_mode) || S_ISFIFO(buf.st_mode); |
||||
} |
||||
|
||||
// time the file was originally created
|
||||
static time_t getCreated(LPCTSTR path) { |
||||
struct stat buf; |
||||
if (stat(path, &buf) != 0) return 0; |
||||
return buf.st_ctime; |
||||
} |
||||
// time the file was last modified
|
||||
static time_t getModified(LPCTSTR path) { |
||||
struct stat buf; |
||||
if (stat(path, &buf) != 0) return 0; |
||||
return buf.st_mtime; |
||||
} |
||||
// time the file was accessed
|
||||
static time_t getAccessed(LPCTSTR path) { |
||||
struct stat buf; |
||||
if (stat(path, &buf) != 0) return 0; |
||||
return buf.st_atime; |
||||
} |
||||
|
||||
// set the current folder
|
||||
static bool setCurrentFolder(LPCTSTR path) { |
||||
if (!isFolder(path)) |
||||
return false; |
||||
#ifdef _MSC_VER |
||||
// Windows implementation - this returns non-zero for success
|
||||
return (SetCurrentDirectory(path) != 0); |
||||
#else |
||||
// Unix implementation - this returns zero for success
|
||||
return (chdir(path) == 0); |
||||
#endif |
||||
} |
||||
|
||||
template <class VECTOR> |
||||
inline size_t write(const VECTOR& arr) { |
||||
const typename VECTOR::IDX nSize(arr.GetSize()); |
||||
size_t nBytes(write(&nSize, sizeof(typename VECTOR::IDX))); |
||||
nBytes += write(arr.GetData(), arr.GetDataSize()); |
||||
return nBytes; |
||||
} |
||||
|
||||
template <class VECTOR> |
||||
inline size_t read(VECTOR& arr) { |
||||
typename VECTOR::IDX nSize; |
||||
size_t nBytes(read(&nSize, sizeof(typename VECTOR::IDX))); |
||||
arr.Resize(nSize); |
||||
nBytes += read(arr.GetData(), arr.GetDataSize()); |
||||
return nBytes; |
||||
} |
||||
|
||||
enum { LAYER_ID_IN=3 }; |
||||
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : IOStream::getInputStream(typ)); } |
||||
enum { LAYER_ID_OUT=3 }; |
||||
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return (typ == LAYER_ID_OUT ? static_cast<OutputStream*>(this) : IOStream::getOutputStream(typ)); } |
||||
|
||||
protected: |
||||
#ifdef _MSC_VER |
||||
HANDLE h; |
||||
#else |
||||
int h; |
||||
#endif |
||||
|
||||
public: |
||||
#ifndef _RELEASE |
||||
size_t breakRead; |
||||
size_t breakWrite; |
||||
#endif |
||||
|
||||
private: |
||||
File(const File&); |
||||
File& operator=(const File&); |
||||
}; |
||||
typedef File* LPFILE; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_FILE_H__
|
||||
@ -0,0 +1,612 @@
@@ -0,0 +1,612 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Filters.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_FILTERS_H__ |
||||
#define __SEACAVE_FILTERS_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Streams.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
template<bool managed=true> |
||||
class BufferedInputStream : public LayerInputStream<managed> { |
||||
public: |
||||
typedef LayerInputStream<managed> Base; |
||||
|
||||
BufferedInputStream(InputStream* aStream, size_t aBufSize) |
||||
: Base(aStream), buf(new uint8_t[aBufSize]), bufSize(aBufSize), cache(0), pos(0) { ASSERT(aBufSize > 0); } |
||||
virtual ~BufferedInputStream() { |
||||
delete[] buf; |
||||
} |
||||
|
||||
size_t read(void* wbuf, size_t len) override { |
||||
uint8_t* b = (uint8_t*)wbuf; |
||||
const size_t l2 = len; |
||||
do { |
||||
ASSERT(pos <= cache); |
||||
if (pos == cache) { |
||||
if (len >= bufSize) { |
||||
const size_t r = Base::read(b, len); |
||||
if (r == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
return l2 - len + r; |
||||
} |
||||
pos = 0; |
||||
switch (cache = Base::read(buf, bufSize)) { |
||||
case 0: |
||||
return l2 - len; |
||||
case STREAM_ERROR: |
||||
return STREAM_ERROR; |
||||
} |
||||
} |
||||
const size_t n = MINF(cache - pos, len); |
||||
memcpy(b, buf + pos, n); |
||||
b += n; |
||||
pos += n; |
||||
len -= n; |
||||
} while (len > 0); |
||||
return l2; |
||||
} |
||||
|
||||
bool setPos(size_f_t wpos) override { |
||||
pos = cache = 0; |
||||
return Base::setPos(wpos); |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
const size_f_t r = Base::getPos(); |
||||
if (r == SIZE_NA) |
||||
return SIZE_NA; |
||||
return r-(cache-pos); |
||||
} |
||||
|
||||
enum { LAYER_ID_IN=1 }; |
||||
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : Base::getInputStream(typ)); } |
||||
|
||||
private: |
||||
uint8_t* const buf; |
||||
const size_t bufSize; |
||||
size_t cache; |
||||
size_t pos; |
||||
}; |
||||
|
||||
|
||||
template<bool managed=true> |
||||
class BufferedOutputStream : public LayerOutputStream<managed> { |
||||
public: |
||||
typedef LayerOutputStream<managed> Base; |
||||
|
||||
BufferedOutputStream(OutputStream* aStream, size_t aBufSize) |
||||
: Base(aStream), buf(new uint8_t[aBufSize]), bufSize(aBufSize), pos(0) { } |
||||
virtual ~BufferedOutputStream() { |
||||
flush(); |
||||
delete[] buf; |
||||
} |
||||
|
||||
size_t write(const void* wbuf, size_t len) override { |
||||
if (len < bufSize - pos) { |
||||
memcpy(buf + pos, (const uint8_t*)wbuf, len); |
||||
pos += len; |
||||
return len; |
||||
} |
||||
uint8_t* b = (uint8_t*)wbuf; |
||||
const size_t l2 = len; |
||||
do { |
||||
if (pos == bufSize) { |
||||
if (Base::write(buf, bufSize) == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
pos = 0; |
||||
if (len < bufSize) { |
||||
if (Base::write(b, len) == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
break; |
||||
} |
||||
} |
||||
const size_t n = MINF(bufSize - pos, len); |
||||
memcpy(buf + pos, b, n); |
||||
b += n; |
||||
pos += n; |
||||
len -= n; |
||||
} while (len > 0); |
||||
return l2; |
||||
} |
||||
|
||||
size_f_t getSize() const override { |
||||
const size_f_t r = Base::getSize(); |
||||
if (r == SIZE_NA) |
||||
return SIZE_NA; |
||||
return r + pos; |
||||
} |
||||
|
||||
bool setPos(size_f_t wpos) override { |
||||
if (pos > 0) { |
||||
const size_t ret = Base::write(buf, pos); |
||||
pos = 0; |
||||
if (ret == STREAM_ERROR) |
||||
return false; |
||||
} |
||||
return Base::setPos(wpos); |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
const size_f_t r = Base::getPos(); |
||||
if (r == SIZE_NA) |
||||
return SIZE_NA; |
||||
return r + pos; |
||||
} |
||||
|
||||
size_t flush() override { |
||||
size_t ret = 0; |
||||
if (pos > 0) { |
||||
ret = Base::write(buf, pos); |
||||
pos = 0; |
||||
if (ret == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
} |
||||
return ret + Base::flush(); |
||||
} |
||||
|
||||
enum { LAYER_ID_OUT=1 }; |
||||
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return (typ == LAYER_ID_OUT ? static_cast<OutputStream*>(this) : Base::getOutputStream(typ)); } |
||||
|
||||
private: |
||||
uint8_t* const buf; |
||||
const size_t bufSize; |
||||
size_t pos; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
|
||||
template<class Filter, bool managed=true> |
||||
class FilteredInputStream : public LayerInputStream<managed> { |
||||
public: |
||||
typedef LayerInputStream<managed> Base; |
||||
|
||||
FilteredInputStream(InputStream* pFile, size_t nFilteredSize, size_t nBufSize, void* pData) |
||||
: Base(pFile), buf(new uint8_t[nBufSize]), filteredSize(nFilteredSize), bufSize(nBufSize), valid(0), pos(0), filter(pData), more(true) {} |
||||
virtual ~FilteredInputStream() { |
||||
delete[] buf; |
||||
} |
||||
|
||||
/**
|
||||
* Read data through filter, keep calling until len returns 0. |
||||
* @param rbuf Data buffer |
||||
* @param len Buffer size on entry, bytes actually read on exit |
||||
* @return Length of data in buffer |
||||
*/ |
||||
size_t read(void* rbuf, size_t len) override { |
||||
uint8_t* rb = (uint8_t*)rbuf; |
||||
|
||||
const size_t l2 = len; |
||||
while (more && len) { |
||||
if (pos == valid) { |
||||
valid = Base::read(buf, bufSize); |
||||
if (valid == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
pos = 0; |
||||
} |
||||
size_t m = valid - pos; |
||||
size_t n = len; |
||||
more = filter(buf + pos, m, rb, n); |
||||
pos += m; |
||||
rb += n; |
||||
len -= n; |
||||
} |
||||
return l2-len; |
||||
} |
||||
|
||||
size_f_t getSize() const override { |
||||
return filteredSize; |
||||
} |
||||
|
||||
bool setPos(size_f_t wpos) override { |
||||
valid = pos = 0; |
||||
return Base::setPos(wpos); |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
const size_f_t r = Base::getPos(); |
||||
if (r == SIZE_NA) |
||||
return SIZE_NA; |
||||
return r-(valid-pos); |
||||
} |
||||
|
||||
enum { LAYER_ID_IN=2 }; |
||||
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : Base::getInputStream(typ)); } |
||||
|
||||
private: |
||||
uint8_t* const buf; |
||||
const size_t filteredSize; |
||||
const size_t bufSize; |
||||
size_t valid; |
||||
size_t pos; |
||||
Filter filter; |
||||
bool more; |
||||
}; |
||||
|
||||
|
||||
template<class Filter, bool managed=true> |
||||
class FilteredOutputStream : public LayerOutputStream<managed> { |
||||
public: |
||||
typedef LayerOutputStream<managed> Base; |
||||
|
||||
FilteredOutputStream(OutputStream* aFile, size_t aBufSize, void* pData) |
||||
: Base(aFile), buf(new uint8_t[aBufSize]), bufSize(aBufSize), filter(pData), flushed(false) {} |
||||
virtual ~FilteredOutputStream() { |
||||
flush(); |
||||
delete[] buf; |
||||
} |
||||
|
||||
size_t write(const void* wbuf, size_t len) override { |
||||
if (flushed) |
||||
return STREAM_ERROR; |
||||
|
||||
const uint8_t* wb = (const uint8_t*)wbuf; |
||||
size_t written = 0; |
||||
while (len > 0) { |
||||
size_t n = bufSize; |
||||
size_t m = len; |
||||
|
||||
const bool more = filter(wb, m, buf, n); |
||||
wb += m; |
||||
len -= m; |
||||
|
||||
const size_t r = Base::write(buf, n); |
||||
if (r == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
written += r; |
||||
|
||||
if (!more) { |
||||
if (len > 0) |
||||
return STREAM_ERROR; |
||||
flushed = true; |
||||
return written; |
||||
} |
||||
} |
||||
return written; |
||||
} |
||||
|
||||
size_t flush() override { |
||||
if (flushed) |
||||
return Base::flush(); |
||||
|
||||
flushed = true; |
||||
size_t written = 0; |
||||
|
||||
while (true) { |
||||
size_t n = bufSize; |
||||
size_t zero = 0; |
||||
bool more = filter(NULL, zero, buf, n); |
||||
|
||||
written += Base::write(buf, n); |
||||
|
||||
if (!more) |
||||
break; |
||||
} |
||||
const size_t r = Base::flush(); |
||||
if (r == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
return written + r; |
||||
} |
||||
|
||||
enum { LAYER_ID_OUT=2 }; |
||||
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return (typ == LAYER_ID_OUT ? static_cast<OutputStream*>(this) : Base::getOutputStream(typ)); } |
||||
|
||||
private: |
||||
uint8_t* const buf; |
||||
const size_t bufSize; |
||||
Filter filter; |
||||
bool flushed; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
|
||||
#ifdef _UNICODE |
||||
#define TOKEN_SIZE 2 //in bytes
|
||||
#else |
||||
#define TOKEN_SIZE 1 //in bytes
|
||||
#endif |
||||
#define TOKEN_MAXBUF (2048*TOKEN_SIZE) //in bytes
|
||||
#define TOKEN_MAXIGN 32 //in TCHARs
|
||||
|
||||
template<bool managed=true> |
||||
class TokenInputStream : public LayerInputStream<managed> { |
||||
public: |
||||
typedef LayerInputStream<managed> Base; |
||||
|
||||
TokenInputStream(InputStream* aStream, TCHAR aToken=_T('\n')) |
||||
: Base(aStream), pos(TOKEN_MAXBUF), eos(false), token(aToken) { arrIgnore[0] = _T('\0'); } |
||||
virtual ~TokenInputStream() { |
||||
} |
||||
|
||||
void emptyBuffer() { |
||||
setPos(getPos()); |
||||
} |
||||
|
||||
TCHAR getToken() const { |
||||
return token; |
||||
} |
||||
|
||||
void setToken(TCHAR aToken) { |
||||
token = aToken; |
||||
} |
||||
|
||||
LPCTSTR getTrimTokens() const { |
||||
return arrIgnore; |
||||
} |
||||
|
||||
size_t setTrimTokens(LPCTSTR szIgnore) { |
||||
const size_t len = _tcslen(szIgnore); |
||||
if (len >= TOKEN_MAXIGN) |
||||
return 0; |
||||
_tcscpy(arrIgnore, szIgnore); |
||||
return len; |
||||
} |
||||
|
||||
LPTSTR trimFrontLine(LPTSTR line) { |
||||
while (line[0] != _T('\0')) { |
||||
if (_tcschr(arrIgnore, line[0]) == NULL) |
||||
return line; |
||||
++line; |
||||
} |
||||
return line; |
||||
} |
||||
|
||||
LPTSTR trimFrontLine(MemFile& memFile) { |
||||
memFile.ensureSize(1); |
||||
LPTSTR line = (LPTSTR)memFile.getData(); |
||||
line[memFile.getSizeLeft()] = _T('\0'); |
||||
LPTSTR newLine = trimFrontLine(line); |
||||
memFile.movePos(newLine-line); |
||||
return newLine; |
||||
} |
||||
|
||||
size_t trimBackLine(LPTSTR line) { |
||||
const size_t len = _tcslen(line); |
||||
size_t i = len; |
||||
while (i > 0 && line[--i] != _T('\0')) { |
||||
if (_tcschr(arrIgnore, line[i]) == NULL) |
||||
return len-(i+1); |
||||
line[i] = _T('\0'); |
||||
} |
||||
return len; |
||||
} |
||||
|
||||
size_t trimBackLine(MemFile& memFile) { |
||||
memFile.ensureSize(1); |
||||
LPTSTR line = (LPTSTR)memFile.getData(); |
||||
line[memFile.getSizeLeft()] = _T('\0'); |
||||
const size_t trimedSize = trimBackLine(line); |
||||
memFile.moveSize(-((size_f_t)trimedSize)); |
||||
return trimedSize; |
||||
} |
||||
|
||||
// revert the deleted token during the last readLine()
|
||||
void restoreToken() { |
||||
LPTSTR line = (LPTSTR)buf; |
||||
ASSERT(pos > 0); |
||||
line[--pos] = token; |
||||
} |
||||
|
||||
size_t readLine(LPTSTR wbuf, size_t len) { |
||||
if (eos) |
||||
return 0; |
||||
uint8_t* b = (uint8_t*)wbuf; |
||||
const size_t l2 = len; |
||||
// process chunks of TOKEN_MAXBUF bytes
|
||||
while (len >= TOKEN_MAXBUF / TOKEN_SIZE) { |
||||
const size_t n = read(b, TOKEN_MAXBUF); |
||||
if (n == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
*((TCHAR*)(b+n)) = _T('\0'); |
||||
LPTSTR t = _tcschr((TCHAR*)b, token); |
||||
// if token found...
|
||||
if (t != NULL) { |
||||
// ... set the end of line and return it
|
||||
t[0] = _T('\0'); |
||||
if (n == TOKEN_SIZE && len != 1) { |
||||
eos = true; |
||||
return l2-len; |
||||
} |
||||
++t; |
||||
const size_t bytesParsed = (uint8_t*)t-b; |
||||
const size_t bytesNotParsed = n - bytesParsed; |
||||
pos = TOKEN_MAXBUF - bytesNotParsed; |
||||
// store the unprocessed data
|
||||
memcpy(buf+pos, (uint8_t*)t, bytesNotParsed); |
||||
len -= (bytesParsed - TOKEN_SIZE) / TOKEN_SIZE; |
||||
return l2-len; |
||||
} |
||||
len -= n / TOKEN_SIZE; |
||||
// if end of stream return
|
||||
if (n < TOKEN_MAXBUF) { |
||||
eos = true; |
||||
return l2-len; |
||||
} |
||||
b += n; |
||||
// if we reached the end of the buffer, signal it
|
||||
if (len == 0) |
||||
return l2+1; |
||||
} |
||||
// process the last sub-chunk part
|
||||
const size_t n = read(b, len*TOKEN_SIZE); |
||||
if (n == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
*((TCHAR*)(b+n)) = _T('\0'); |
||||
LPTSTR t = _tcschr((TCHAR*)b, token); |
||||
// if token found...
|
||||
if (t != NULL) { |
||||
// ... set the end of line and return it
|
||||
t[0] = _T('\0'); |
||||
if (n == TOKEN_SIZE && len != 1) { |
||||
eos = true; |
||||
return l2-len; |
||||
} |
||||
++t; |
||||
const size_t bytesParsed = (uint8_t*)t-b; |
||||
const size_t bytesNotParsed = n - bytesParsed; |
||||
pos = TOKEN_MAXBUF - bytesNotParsed; |
||||
// store the unprocessed data
|
||||
memcpy(buf+pos, (uint8_t*)t, bytesNotParsed); |
||||
len -= (bytesParsed - TOKEN_SIZE) / TOKEN_SIZE; |
||||
return l2-len; |
||||
} |
||||
// if end of stream return
|
||||
if (n < len * TOKEN_SIZE) { |
||||
eos = true; |
||||
return n / TOKEN_SIZE; |
||||
} |
||||
// we reached the end of the buffer, signal it
|
||||
return l2+1; |
||||
} |
||||
|
||||
size_t readLine(MemFile& memFile) |
||||
{ |
||||
if (eos) |
||||
return 0; |
||||
// make sure we read one full line
|
||||
const size_f_t oldSize = memFile.getSize(); |
||||
while (true) { |
||||
memFile.ensureSize((4096+1)*TOKEN_SIZE); |
||||
const size_t ret = readLine((LPTSTR)(memFile.getBuffer()+memFile.getSize()), 4096); |
||||
if (ret == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
if (ret <= 4096) { |
||||
memFile.growSize(ret*TOKEN_SIZE); |
||||
break; |
||||
} |
||||
memFile.growSize(4096*TOKEN_SIZE); |
||||
} |
||||
return size_t(memFile.getSize()-oldSize); |
||||
} |
||||
|
||||
// read all to the memfile
|
||||
size_t read(MemFile& memFile) |
||||
{ |
||||
if (eos) |
||||
return 0; |
||||
const size_t len = (size_t)(Base::getSize() - getPos()); |
||||
memFile.ensureSize(len); |
||||
const size_t ret = read(memFile.getBuffer()+memFile.getSize(), len); |
||||
if (ret == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
memFile.growSize(ret); |
||||
return ret; |
||||
} |
||||
|
||||
size_t read(void* wbuf, size_t len) override { |
||||
size_t n = 0; |
||||
if (pos < TOKEN_MAXBUF) { |
||||
n = TOKEN_MAXBUF-pos; |
||||
if (n > len) |
||||
n = len; |
||||
memcpy(wbuf, buf+pos, n); |
||||
len -= n; |
||||
pos += n; |
||||
} |
||||
if (len == 0) |
||||
return n; |
||||
const size_t r = Base::read((uint8_t*)wbuf+n, len); |
||||
if (r == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
return n+r; |
||||
} |
||||
|
||||
bool setPos(size_f_t wpos) override { |
||||
eos = false; |
||||
pos = TOKEN_MAXBUF; |
||||
return Base::setPos(wpos); |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
const size_f_t r = Base::getPos(); |
||||
if (r == SIZE_NA) |
||||
return SIZE_NA; |
||||
return r-(TOKEN_MAXBUF-pos); |
||||
} |
||||
|
||||
bool isEOS() const { |
||||
return eos; |
||||
} |
||||
|
||||
enum { LAYER_ID_IN=5 }; |
||||
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : Base::getInputStream(typ)); } |
||||
|
||||
private: |
||||
uint8_t buf[TOKEN_MAXBUF+TOKEN_SIZE]; |
||||
size_t pos; |
||||
bool eos; |
||||
TCHAR token; |
||||
TCHAR arrIgnore[TOKEN_MAXIGN]; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
|
||||
template<bool managed=true> |
||||
class MaskInputStream : public LayerInputStream<managed> { |
||||
public: |
||||
typedef LayerInputStream<managed> Base; |
||||
|
||||
MaskInputStream(InputStream* aStream, size_f_t nPos, size_f_t nSize) |
||||
: Base(aStream), startPos(nPos), size(nSize), pos(0) { } |
||||
virtual ~MaskInputStream() { } |
||||
|
||||
size_t read(void* wbuf, size_t len) override { |
||||
if (pos >= size) |
||||
return 0; |
||||
if (!Base::setPos(startPos + pos)) |
||||
return STREAM_ERROR; |
||||
if (pos+(size_f_t)len > size) |
||||
len = (size_t)(size - pos); |
||||
const size_t r = Base::read(wbuf, len); |
||||
if (r == STREAM_ERROR) |
||||
return STREAM_ERROR; |
||||
pos += r; |
||||
return r; |
||||
} |
||||
|
||||
size_f_t getSize() const override { |
||||
return size; |
||||
} |
||||
|
||||
bool setPos(size_f_t wpos) override { |
||||
if (wpos > size) |
||||
pos = size; |
||||
else |
||||
pos = wpos; |
||||
return true; |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
return pos; |
||||
} |
||||
|
||||
enum { LAYER_ID_IN=6 }; |
||||
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : Base::getInputStream(typ)); } |
||||
|
||||
private: |
||||
const size_f_t size; |
||||
const size_f_t startPos; |
||||
size_f_t pos; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_FILTERS_H__
|
||||
@ -0,0 +1,457 @@
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* Modified version of: |
||||
* |
||||
* @file htmlDoc.h |
||||
* @brief Simple HTML document writer and SVG drawer |
||||
* @author Pierre MOULON |
||||
* |
||||
* Copyright (c) 2011, 2012, 2013 Pierre MOULON |
||||
* All rights reserved. |
||||
* |
||||
* This Source Code Form is subject to the terms of the Mozilla Public |
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this |
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/ |
||||
|
||||
#ifndef _HTMLDOC_H_ |
||||
#define _HTMLDOC_H_ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define JSXCHART_BORDER 0.2f |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
namespace HTML { |
||||
|
||||
inline const std::string htmlMarkup(const std::string& markup, const std::string& text) { |
||||
std::ostringstream os; |
||||
os << '<'<< markup<<'>' << text << "</"<<markup<<'>' <<"\n"; |
||||
return os.str(); |
||||
} |
||||
inline const std::string htmlMarkup(const std::string& markup, const std::string& attributes, const std::string& text) { |
||||
std::ostringstream os; |
||||
os << '<'<<markup<<' '<<attributes<<'>' << text << "</"<<markup<<'>' <<"\n"; |
||||
return os.str(); |
||||
} |
||||
inline const std::string htmlOpenMarkup(const std::string& markup, const std::string& attributes) { |
||||
std::ostringstream os; |
||||
os << '<'<<markup<<' '<<attributes<<"/>" <<"\n"; |
||||
return os.str(); |
||||
} |
||||
|
||||
inline const std::string htmlComment(const std::string& text) { |
||||
std::ostringstream os; |
||||
os << "<!-- "<< text << " -->" << "\n"; |
||||
return os.str(); |
||||
} |
||||
|
||||
/// Return a chain in the form attributes="val"
|
||||
template<typename T> |
||||
inline const std::string quotedAttributes(const std::string& attributes, const T & val) { |
||||
std::ostringstream os; |
||||
os << attributes << "='" << val << '\''; |
||||
return os.str(); |
||||
} |
||||
|
||||
/// Return a chain of the value T
|
||||
template<typename T> |
||||
inline const std::string toString(const T& val) { |
||||
std::ostringstream os; |
||||
os << val; |
||||
return os.str(); |
||||
} |
||||
|
||||
class htmlDocumentStream |
||||
{ |
||||
public: |
||||
htmlDocumentStream(const std::string& title) { |
||||
htmlStream << "\n"; |
||||
htmlStream << htmlMarkup("head", |
||||
"\n" |
||||
"<link rel='stylesheet' type='text/css' href='http://jsxgraph.uni-bayreuth.de/distrib/jsxgraph.css' />\n" |
||||
"<script type='text/javascript' src='http://jsxgraph.uni-bayreuth.de/distrib/jsxgraphcore-0.96.js'></script>\n"); |
||||
htmlStream << htmlMarkup("title",title); |
||||
} |
||||
|
||||
htmlDocumentStream(const std::string& title, |
||||
const std::vector<std::string>& vec_css, |
||||
const std::vector<std::string>& vec_js) |
||||
{ |
||||
htmlStream << "\n<head>\n"; |
||||
htmlStream << htmlMarkup("title",title); |
||||
// CSS and JS resources
|
||||
for (std::vector<std::string>::const_iterator iter = vec_css.begin(); iter != vec_css.end(); ++iter) |
||||
htmlStream << "<link rel='stylesheet' type='text/css' href='" << *iter <<"' />\n"; |
||||
for (std::vector<std::string>::const_iterator iter = vec_js.begin(); iter != vec_js.end(); ++iter) |
||||
htmlStream << "<script type='text/javascript' src='" << *iter <<"'> </script>\n"; |
||||
htmlStream << "</head>\n"; |
||||
} |
||||
|
||||
void pushInfo(const std::string& text) { |
||||
htmlStream << text; |
||||
} |
||||
|
||||
std::string getDoc() { |
||||
return htmlMarkup("html", htmlStream.str()); |
||||
} |
||||
|
||||
std::ostringstream htmlStream; |
||||
}; |
||||
|
||||
|
||||
/// Class to draw with the JSXGraph library in HTML page.
|
||||
class JSXGraphWrapper |
||||
{ |
||||
public: |
||||
typedef float TRANGE; |
||||
typedef std::pair< std::pair<TRANGE,TRANGE>, std::pair<TRANGE,TRANGE> > RANGE; |
||||
|
||||
JSXGraphWrapper() { |
||||
cpt = 0; |
||||
} |
||||
|
||||
void reset() { |
||||
stream.str(""); |
||||
stream.precision(4); |
||||
//stream.setf(std::ios::fixed,std::ios::floatfield);
|
||||
cpt = 0; |
||||
} |
||||
|
||||
void init(unsigned W, unsigned H, LPCTSTR szGraphName=NULL) { |
||||
reset(); |
||||
std::string strGraphName; |
||||
if (szGraphName == NULL) { |
||||
strGraphName = SEACAVE::Util::getUniqueName(); |
||||
szGraphName = strGraphName.c_str(); |
||||
} |
||||
stream << |
||||
"\n" |
||||
"<div id='" << szGraphName << "' class='jxgbox' style='width:"<< W << "px; height:" << H <<"px;'></div>\n" |
||||
"<script type='text/javascript'>\n" |
||||
"var board = JXG.JSXGraph.initBoard('"<< szGraphName <<"', {axis:true,showCopyright:false});\n" |
||||
"board.suspendUpdate();\n"; |
||||
} |
||||
|
||||
template<typename VECTX, typename VECTY, typename STR> |
||||
void addXYChart(const VECTX& vec_x, const VECTY& vec_y, const STR& stype, LPCTSTR sface=_T("o"), LPCTSTR sizeStroke=_T("2"), LPCTSTR colFill=_T("#0077cc"), LPCTSTR colStroke=_T("#0044ee")) { |
||||
typedef typename VECTX::value_type TX; |
||||
typedef typename VECTY::value_type TY; |
||||
size_t index0 = cpt++; |
||||
size_t index1 = cpt++; |
||||
stream << "var data"<< index0<<"= ["; |
||||
std::copy(vec_x.begin(), vec_x.end(), std::ostream_iterator<TX>(stream, ",")); |
||||
stream << "];\n"; |
||||
stream << "var data"<< index1<<"= ["; |
||||
std::copy(vec_y.begin(), vec_y.end(), std::ostream_iterator<TY>(stream, ",")); |
||||
stream << "];\n"; |
||||
std::ostringstream osData; |
||||
osData << "[data" <<index0<<","<<"data"<<index1<<"]"; |
||||
stream << "board.createElement('chart', " <<osData.str() |
||||
<< ", {chartStyle:'"<< stype << "',labels:" << osData.str() << ",face:'" << sface |
||||
<< "',strokeWidth:" << sizeStroke << ",fillColor:'" << colFill << "',highlightStrokeColor:'" << colStroke << "',fixed:true});\n"; |
||||
} |
||||
|
||||
template<typename VECTY, typename STR> |
||||
void addYChart(const VECTY& vec_y, const STR& stype, LPCTSTR sface=_T("o"), LPCTSTR sizeStroke=_T("2"), LPCTSTR colFill=_T("#0077cc"), LPCTSTR colStroke=_T("#0044ee")) { |
||||
typedef typename VECTY::value_type TY; |
||||
size_t index0 = cpt++; |
||||
stream << "var data"<< index0<<"= ["; |
||||
std::copy(vec_y.begin(), vec_y.end(), std::ostream_iterator<TY>(stream, ",")); |
||||
stream << "];\n"; |
||||
stream << "board.createElement('chart', " << "data"<<index0 |
||||
<< ", {chartStyle:'" << stype << "',labels:" << "data"<<index0 << ",face:'" << sface |
||||
<< "',strokeWidth:" << sizeStroke << ",fillColor:'" << colFill << "',highlightStrokeColor:'" << colStroke << "',fixed:true});\n"; |
||||
} |
||||
|
||||
template<typename TX, typename TY> |
||||
void addLine(TX x0, TY y0, TX x1, TY y1, LPCTSTR color=_T("#00ff00")) { |
||||
size_t index0 = cpt++; |
||||
size_t index1 = cpt++; |
||||
stream << |
||||
"var p"<<index0<<" = board.create('point',["<<x0<<","<<y0<<"], {fixed:true});\n" |
||||
"var p"<<index1<<" = board.create('point',["<<x1<<","<<y1<<"], {fixed:true});\n" |
||||
"var li = board.create('line',[p"<<index0<<",p"<<index1<<"], " |
||||
"{strokeColor:'"<< color <<"',strokeWidth:2});\n"; |
||||
} |
||||
|
||||
void setViewport(const RANGE& range) { |
||||
stream |
||||
<< "board.setBoundingBox([" |
||||
<< range.first.first << ","<< range.second.second <<"," |
||||
<< range.first.second << ","<< range.second.first <<"]);\n"; |
||||
} |
||||
|
||||
void UnsuspendUpdate() { |
||||
stream << "board.unsuspendUpdate();\n"; |
||||
} |
||||
void close() { |
||||
stream << "</script>\n"; |
||||
} |
||||
|
||||
std::string toStr() const { |
||||
return stream.str(); |
||||
} |
||||
|
||||
template<typename TX, typename TY> |
||||
static inline RANGE autoViewport(TX maxValX, TY maxValY, TX minValX, TY minValY) { |
||||
//Use the value with a little margin
|
||||
const TX rangeX = maxValX-minValX; |
||||
const TY rangeY = maxValY-minValY; |
||||
return std::make_pair( |
||||
std::make_pair(-JSXCHART_BORDER*rangeX+minValX,JSXCHART_BORDER*rangeX+maxValX), |
||||
std::make_pair(-JSXCHART_BORDER*rangeY+minValY,JSXCHART_BORDER*rangeY+maxValY)); |
||||
} |
||||
template<typename VECTX, typename VECTY> |
||||
static RANGE autoViewport(const VECTX& vec_x, const VECTY& vec_y) { |
||||
typedef typename VECTX::value_type TX; |
||||
typedef typename VECTY::value_type TY; |
||||
if (vec_x.empty() || vec_y.empty() || vec_x.size() != vec_y.size()) |
||||
return RANGE(); |
||||
//For X values
|
||||
const TX minValX = *std::min_element(vec_x.begin(), vec_x.end()); |
||||
const TX maxValX = *std::max_element(vec_x.begin(), vec_x.end()); |
||||
//For Y values
|
||||
const TY minValY = *std::min_element(vec_y.begin(), vec_y.end()); |
||||
const TY maxValY = *std::max_element(vec_y.begin(), vec_y.end()); |
||||
return autoViewport(maxValX, maxValY, minValX, minValY); |
||||
} |
||||
template<typename T, typename VECTY> |
||||
static RANGE autoViewport(const VECTY& vec_y, bool bForceY0=true) { |
||||
typedef T TX; |
||||
typedef typename VECTY::value_type TY; |
||||
if (vec_y.empty()) |
||||
return RANGE(); |
||||
//For X values
|
||||
const TX minValX = TX(0); |
||||
const TX maxValX = static_cast<TX>(vec_y.size()); |
||||
//For Y values
|
||||
const TY minValY = (bForceY0 ? TY(0) : *std::min_element(vec_y.begin(), vec_y.end())); |
||||
const TY maxValY = *std::max_element(vec_y.begin(), vec_y.end()); |
||||
return autoViewport(maxValX, maxValY, minValX, minValY); |
||||
} |
||||
|
||||
std::ostringstream stream; |
||||
size_t cpt; //increment for variable
|
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace HTML
|
||||
|
||||
|
||||
|
||||
namespace SVG { |
||||
|
||||
/// Basic SVG style
|
||||
class svgStyle |
||||
{ |
||||
public: |
||||
svgStyle():_sFillCol(""), _sStrokeCol("black"), _sToolTip(""), _fillOpacity(1.f), _strokeW(1.f), _strokeOpacity(1.f) {} |
||||
|
||||
// Configure fill color
|
||||
svgStyle& fill(const std::string& col, float opacity = 1.f) |
||||
{ _sFillCol = col; _fillOpacity = opacity; return *this; } |
||||
|
||||
// Configure stroke color and width
|
||||
svgStyle& stroke(const std::string& col, float witdh = 1.f, float opacity = 1.f) |
||||
{ _sStrokeCol = col; _strokeW = witdh; _strokeOpacity = opacity; return *this; } |
||||
|
||||
// Configure with no stroke
|
||||
svgStyle& noStroke() |
||||
{ _sStrokeCol = ""; _strokeW = 0.f; _strokeOpacity = 0.f; return *this; } |
||||
|
||||
// Configure tooltip
|
||||
svgStyle& tooltip(const std::string& sTooltip) |
||||
{ _sToolTip = sTooltip; return *this; } |
||||
|
||||
const std::string getSvgStream() const { |
||||
std::ostringstream os; |
||||
if (!_sStrokeCol.empty()) { |
||||
os << " stroke='" << _sStrokeCol << "' stroke-width='" << _strokeW << "'"; |
||||
if (_strokeOpacity < 1) |
||||
os << " stroke-opacity='" << _strokeOpacity << "'"; |
||||
} |
||||
if (!_sFillCol.empty()) { |
||||
os << " fill='" << _sFillCol << "'"; |
||||
if (_fillOpacity < 1) |
||||
os << " fill-opacity='" << _fillOpacity << "'"; |
||||
} else { |
||||
os << " fill='none'"; |
||||
} |
||||
if (!_sToolTip.empty()) { |
||||
os << " tooltip='enable'>" << "<title>" << _sToolTip << "</title>"; |
||||
} |
||||
return os.str(); |
||||
} |
||||
|
||||
bool bTooltip() const { return !_sToolTip.empty();} |
||||
|
||||
std::string _sFillCol, _sStrokeCol, _sToolTip; |
||||
float _fillOpacity, _strokeW, _strokeOpacity; |
||||
}; |
||||
|
||||
|
||||
/// Basic class to handle simple SVG draw.
|
||||
/// You can draw line, square, rectangle, text and image (xlink)
|
||||
class svgDrawer |
||||
{ |
||||
public: |
||||
///Constructor
|
||||
svgDrawer(size_t W = 0, size_t H = 0) { |
||||
svgStream << |
||||
"<?xml version='1.0' standalone='yes'?>\n" |
||||
"<!-- SVG graphic -->\n" |
||||
"<svg"; |
||||
|
||||
if (W > 0 && H > 0) |
||||
svgStream << |
||||
" width='" << W << "px' height='"<< H << "px'" |
||||
" preserveAspectRatio='xMinYMin meet'" |
||||
" viewBox='0 0 " << W << ' ' << H <<"'"; |
||||
|
||||
svgStream << |
||||
" xmlns='http://www.w3.org/2000/svg'" |
||||
" xmlns:xlink='http://www.w3.org/1999/xlink'" |
||||
" version='1.1'>\n"; |
||||
} |
||||
///Circle draw -> x,y position and radius
|
||||
void drawCircle(float cx, float cy, float r, const svgStyle& style) { |
||||
svgStream |
||||
<< "<circle cx='" << cx << "'" << " cy='" << cy << "'" |
||||
<< " r='" << r << "'" |
||||
<< style.getSvgStream() + (style.bTooltip() ? "</circle>\n" : "/>\n"); |
||||
} |
||||
///Line draw -> start and end point
|
||||
void drawLine(float ax, float ay, float bx, float by, const svgStyle& style) { |
||||
svgStream |
||||
<< "<line x1='"<<ax<< "' y1='"<<ay<< "' x2='"<<bx<< "' y2='"<<by<< "'" |
||||
<< style.getSvgStream() + (style.bTooltip() ? "</line>\n" : "/>\n"); |
||||
} |
||||
|
||||
///Reference to an image (path must be relative to the SVG file)
|
||||
void drawImage(const std::string& simagePath, int W, int H, |
||||
int posx = 0, int posy = 0, float opacity =1.f) |
||||
{ |
||||
svgStream << |
||||
"<image x='"<< posx << "'" << " y='"<< posy << "'" |
||||
" width='"<< W << "px'" << " height='"<< H << "px'" |
||||
" opacity='"<< opacity << "'" |
||||
" xlink:href='" << simagePath << "'/>\n"; |
||||
} |
||||
|
||||
///Square draw -> x,y position and size
|
||||
void drawSquare(float cx, float cy, float W, const svgStyle& style) { |
||||
drawRectangle(cx, cy, W, W, style); |
||||
} |
||||
|
||||
///Circle draw -> x,y position and width and height
|
||||
void drawRectangle(float cx, float cy, float W, float H, const svgStyle& style) { |
||||
svgStream |
||||
<< "<rect x='" << cx << "'" |
||||
<< " y='" << cy << "'" |
||||
<< " width='" << W << "'" |
||||
<< " height='" << H << "'" |
||||
<< style.getSvgStream() + (style.bTooltip() ? "</rect>\n" : "/>\n"); |
||||
} |
||||
|
||||
///Text display -> x,y position, font size
|
||||
void drawText(float cx, float cy, const std::string& stext, const std::string& scol = "", const std::string& sattr = "", float fontSize = 1.f) { |
||||
svgStream << "<text" << " x='" << cx << "'" << " y='" << cy << "'" |
||||
<< " font-size='" << fontSize << "'"; |
||||
if (!sattr.empty()) |
||||
svgStream << ' ' << sattr; |
||||
if (!scol.empty()) |
||||
svgStream << " fill='" << scol << "'"; |
||||
svgStream << ">" << stext << "</text>\n"; |
||||
} |
||||
template< typename DataInputIteratorX, typename DataInputIteratorY> |
||||
void drawPolyline(DataInputIteratorX xStart, DataInputIteratorX xEnd, |
||||
DataInputIteratorY yStart, DataInputIteratorY /*yEnd*/, |
||||
const svgStyle& style) |
||||
{ |
||||
svgStream << "<polyline points='"; |
||||
DataInputIteratorY itery = yStart; |
||||
for(DataInputIteratorX iterx = xStart; |
||||
iterx != xEnd; std::advance(iterx, 1), std::advance(itery, 1)) |
||||
{ |
||||
svgStream << *iterx << ',' << *itery << ' '; |
||||
} |
||||
svgStream << "'" << style.getSvgStream() + (style.bTooltip() ? "</polyline>\n" : "/>\n"); |
||||
} |
||||
|
||||
///Close the svg tag.
|
||||
std::ostringstream& closeSvgFile() { |
||||
svgStream << "</svg>"; |
||||
return svgStream; |
||||
} |
||||
|
||||
std::ostringstream svgStream; |
||||
}; |
||||
|
||||
/// Helper to draw a SVG histogram
|
||||
/// ____
|
||||
/// | | ___ |
|
||||
/// | |__| | |
|
||||
/// | | | | |
|
||||
/// -----------|
|
||||
struct svgHisto |
||||
{ |
||||
template<typename VECT> |
||||
std::string draw(const VECT& vec_value, |
||||
const std::pair<float, float>& range, |
||||
float W, float H) |
||||
{ |
||||
if (vec_value.empty()) |
||||
return ""; |
||||
|
||||
//-- Max value
|
||||
typedef typename VECT::value_type T; |
||||
const T maxi = *max_element(vec_value.begin(), vec_value.end()); |
||||
const size_t n = vec_value.size(); |
||||
|
||||
const float scaleFactorY = H / static_cast<float>(maxi); |
||||
const float scaleFactorX = W / static_cast<float>(n); |
||||
|
||||
svgDrawer svgStream; |
||||
|
||||
for (typename VECT::const_iterator iter = vec_value.begin(); iter != vec_value.end(); ++iter) |
||||
{ |
||||
const T dist = std::distance(vec_value.begin(), iter); |
||||
const T& val = *iter; |
||||
std::ostringstream os; |
||||
os << '(' << range.first + dist/float(n) * (range.second-range.first) << ',' << val << ')'; |
||||
svgStyle style = svgStyle().fill("blue").stroke("black", 1.0).tooltip(os.str()); |
||||
svgStream.drawRectangle( |
||||
scaleFactorX * dist, H-val * scaleFactorY, |
||||
scaleFactorX, val * scaleFactorY, |
||||
style); |
||||
//_________
|
||||
//| |_________
|
||||
//| || |
|
||||
//| || |
|
||||
//| || |
|
||||
//0 sFactorX 2*sFactorX
|
||||
} |
||||
svgStyle styleAxis = svgStyle().stroke("black", 1.0f); |
||||
// Draw X Axis
|
||||
svgStream.drawText(.05f*W, 1.2f*H, HTML::toString(range.first), "black", "", .1f*H); |
||||
svgStream.drawText(W, 1.2*H, HTML::toString(range.second), "black", "", .1f*H); |
||||
svgStream.drawLine(0, 1.1f*H, W, 1.1f*H, styleAxis); |
||||
// Draw Y Axis
|
||||
svgStream.drawText(1.2f*W, .1f*H, HTML::toString(maxi), "black", "", .1f*H); |
||||
svgStream.drawText(1.2f*W, H, "0", "black", "", .1f*H); |
||||
svgStream.drawLine(1.1f*W, 0, 1.1f*W, H, styleAxis); |
||||
|
||||
return svgStream.closeSvgFile().str(); |
||||
} |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SVG
|
||||
|
||||
#endif // _HTMLDOC_H_
|
||||
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// HalfFoat.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_HALFFLOAT_H__ |
||||
#define __SEACAVE_HALFFLOAT_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// hfloat - a 16-bit floating point number class
|
||||
//
|
||||
// Can represent positive and negative numbers whose magnitude is between
|
||||
// roughly 6.1e-5 and 6.5e+4 with a relative error of 9.8e-4; numbers
|
||||
// smaller than 6.1e-5 can be represented with an absolute error of
|
||||
// 6.0e-8. All integers from -2048 to +2048 can be represented exactly.
|
||||
// Supports Denormals-as-zero (DAZ), but does not support infinities or NaN.
|
||||
//
|
||||
// Only conversions from half to float are lossless.
|
||||
class GENERAL_API hfloat |
||||
{ |
||||
public: |
||||
typedef short Type; |
||||
|
||||
inline hfloat() : val(fromFloat(float())) {} |
||||
explicit inline hfloat(float v) : val(fromFloat(v)) {} |
||||
template <typename T> |
||||
inline hfloat(T v) : val(fromFloat(static_cast<float>(v))) {} |
||||
|
||||
inline hfloat& operator = (float v) { |
||||
val = fromFloat(v); |
||||
return *this; |
||||
} |
||||
template <typename T> |
||||
inline hfloat& operator = (T v) { |
||||
val = fromFloat(static_cast<float>(v)); |
||||
return *this; |
||||
} |
||||
|
||||
inline operator float() const { |
||||
return toFloat(val); |
||||
} |
||||
inline hfloat operator - () const { |
||||
return fromFloat2HFloat(-toFloat(val)); |
||||
} |
||||
inline hfloat operator + (hfloat v) { |
||||
return fromFloat2HFloat(toFloat(val) + toFloat(v.val)); |
||||
} |
||||
inline hfloat operator + (float v) { |
||||
return fromFloat2HFloat(toFloat(val) + v); |
||||
} |
||||
inline hfloat& operator += (hfloat v) { |
||||
return *this = *this + v; |
||||
} |
||||
inline hfloat& operator += (float v) { |
||||
return *this = *this + v; |
||||
} |
||||
inline hfloat operator - (hfloat v) { |
||||
return fromFloat2HFloat(toFloat(val) - toFloat(v.val)); |
||||
} |
||||
inline hfloat operator - (float v) { |
||||
return fromFloat2HFloat(toFloat(val) - v); |
||||
} |
||||
inline hfloat& operator -= (hfloat v) { |
||||
return *this = *this - v; |
||||
} |
||||
inline hfloat& operator -= (float v) { |
||||
return *this = *this - v; |
||||
} |
||||
inline hfloat operator * (hfloat v) { |
||||
return fromFloat2HFloat(toFloat(val) * toFloat(v.val)); |
||||
} |
||||
inline hfloat operator * (float v) { |
||||
return fromFloat2HFloat(toFloat(val) * v); |
||||
} |
||||
inline hfloat& operator *= (hfloat v) { |
||||
return *this = *this * v; |
||||
} |
||||
inline hfloat& operator *= (float v) { |
||||
return *this = *this * v; |
||||
} |
||||
inline hfloat operator / (hfloat v) { |
||||
return fromFloat2HFloat(toFloat(val) / toFloat(v.val)); |
||||
} |
||||
inline hfloat operator / (float v) { |
||||
return fromFloat2HFloat(toFloat(val) / v); |
||||
} |
||||
inline hfloat& operator /= (hfloat v) { |
||||
return *this = *this / v; |
||||
} |
||||
inline hfloat& operator /= (float v) { |
||||
return *this = *this / v; |
||||
} |
||||
|
||||
static float min() { return 6.1e-5f; } |
||||
static float max() { return 6.5e+4f; } |
||||
|
||||
static inline short fromFloat(float f) { |
||||
ASSERT(ISFINITE(f) && (ABS(f) == 0.f || ABS(f) >= min()) && ABS(f) <= max()); |
||||
// ~8 clock cycles on modern x86-64
|
||||
const CastF2I ufi(f); |
||||
int32_t t1 = ufi.i & 0x7fffffff; // Non-sign bits
|
||||
int32_t t2 = ufi.i & 0x80000000; // Sign bit
|
||||
int32_t t3 = ufi.i & 0x7f800000; // Exponent
|
||||
t1 >>= 13; // Align mantissa on MSB
|
||||
t2 >>= 16; // Shift sign bit into position
|
||||
t1 -= 0x1c000; // Adjust bias
|
||||
t1 = (t3 < 0x38800000) ? 0 : t1; // Flush-to-zero
|
||||
t1 = (t3 > 0x47000000) ? 0x7bff : t1; // Clamp-to-max
|
||||
t1 = (t3 == 0 ? 0 : t1); // Denormals-as-zero
|
||||
return (short)(t1 | t2); // Re-insert sign bit
|
||||
} |
||||
static inline float toFloat(short h) { |
||||
// ~6 clock cycles on modern x86-64
|
||||
CastF2I ufi; |
||||
int32_t t1 = h & 0x7fff; // Non-sign bits
|
||||
int32_t t2 = h & 0x8000; // Sign bit
|
||||
int32_t t3 = h & 0x7c00; // Exponent
|
||||
t1 <<= 13; // Align mantissa on MSB
|
||||
t2 <<= 16; // Shift sign bit into position
|
||||
t1 += 0x38000000; // Adjust bias
|
||||
t1 = (t3 == 0 ? 0 : t1); // Denormals-as-zero
|
||||
ufi.i = t1 | t2; // Re-insert sign bit
|
||||
return ufi.f; |
||||
} |
||||
|
||||
#ifdef _USE_BOOST |
||||
// serialize
|
||||
template <class Archive> |
||||
void serialize(Archive& ar, const unsigned int /*version*/) { |
||||
ar & val; |
||||
} |
||||
#endif |
||||
|
||||
protected: |
||||
static inline hfloat fromFloat2HFloat(float f) { |
||||
return TAliasCast<short,hfloat>(fromFloat(f)).i; |
||||
} |
||||
|
||||
protected: |
||||
Type val; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_HALFFLOAT_H__
|
||||
@ -0,0 +1,303 @@
@@ -0,0 +1,303 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Hash.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_HASH_H__ |
||||
#define __SEACAVE_HASH_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/**************************************************************************************
|
||||
* StringHash template |
||||
* --------------- |
||||
* compile-time string hash template |
||||
**************************************************************************************/ |
||||
|
||||
class StringHash |
||||
{ |
||||
private: |
||||
uint32_t m_val; |
||||
|
||||
template<size_t N> inline uint32_t _Hash(const char (&str)[N]) const |
||||
{ |
||||
typedef const char (&truncated_str)[N-2]; |
||||
return str[N-1] + 65599 * (str[N-2] + 65599 * _Hash((truncated_str)str)); |
||||
} |
||||
inline uint32_t _Hash(const char (&str)[3]) const { return str[2] + 65599 * (str[1] + 65599 * str[0]); } |
||||
inline uint32_t _Hash(const char (&str)[2]) const { return str[1] + 65599 * str[0]; } |
||||
|
||||
public: |
||||
template <size_t N> StringHash(const char (&str)[N]) { m_val = _Hash(str); } |
||||
inline operator uint32_t() const { return m_val; } |
||||
}; |
||||
|
||||
|
||||
/**************************************************************************************
|
||||
* cHashTable template |
||||
* --------------- |
||||
* simple hash template (performs relatively well for less than 300 elements) |
||||
**************************************************************************************/ |
||||
|
||||
#define KEY_NA 0xFFFF |
||||
#define KEY_MAXSIZE 0x1000000 |
||||
|
||||
template <typename Type> |
||||
class cHashTable |
||||
{ |
||||
public: |
||||
typedef uint32_t Key; |
||||
typedef uint16_t Idx; |
||||
typedef uint32_t Size; |
||||
typedef cList<Type> Values; |
||||
typedef cList<Key, Key> Keys; |
||||
typedef cList<Idx, Idx> Indices; |
||||
|
||||
protected: |
||||
Keys m_keys; // Array of unique keys that have been set
|
||||
Values m_values; // Matching array of unique values that have been set
|
||||
Indices m_indices; // 1-based index array referencing the two arrays above
|
||||
|
||||
public: |
||||
cHashTable() : m_indices(32) { m_indices.Memset(0); } |
||||
~cHashTable() { Release(); } |
||||
|
||||
inline void Release() |
||||
{ |
||||
m_keys.Release(); |
||||
m_values.Release(); |
||||
m_indices.Release(); |
||||
} |
||||
|
||||
inline bool IsEmpty() const { return m_indices.IsEmpty(); } |
||||
inline size_t GetSize() const { return m_keys.GetSize(); } |
||||
inline const Type* GetBegin() const { return m_values.GetData(); } |
||||
inline const Type* GetEnd() const { return m_values.GetData()+m_values.GetSize(); } |
||||
inline Type* GetBegin() { return m_values.GetData(); } |
||||
inline Type* GetEnd() { return m_values.GetData()+m_values.GetSize(); } |
||||
|
||||
inline const Indices& GetArrIndices() const { return m_indices; } |
||||
inline const Keys& GetArrKeys() const { return m_keys; } |
||||
inline const Values& GetArrValues() const { return m_values; } |
||||
inline Values& GetArrValues() { return m_values; } |
||||
|
||||
private: |
||||
inline Size _StaticKeyToID(Key key, Size size) const { return key & (size-1); } |
||||
inline Size _KeyToID(Key key) const { return _StaticKeyToID(key, (Size)m_indices.GetSize()); } |
||||
inline Idx _IDToIndex(Size id) const { return m_indices[id] - 1; } |
||||
inline Idx _KeyToIndex(Key key) const { return (m_indices.IsEmpty() ? KEY_NA : _IDToIndex(_KeyToID(key))); } |
||||
|
||||
// Completely discards then rebuilds all indices based on the current set of keys
|
||||
void _RebuildIndices() |
||||
{ |
||||
// Clear all current memory
|
||||
m_indices.Memset(0); |
||||
// Run through all keys and recreate the indices
|
||||
for (Idx i=0; i<m_keys.GetSize(); ) |
||||
{ |
||||
const Size index = _KeyToID(m_keys[i]); |
||||
m_indices[index] = ++i; |
||||
} |
||||
} |
||||
|
||||
public: |
||||
// Returns a pointer to the value of a key if it exists, 0 otherwise
|
||||
inline const Type* Find(Key key) const |
||||
{ |
||||
const Idx index = _KeyToIndex(key); |
||||
return (index < m_keys.GetSize() && m_keys[index] == key) ? &m_values[index] : NULL; |
||||
} |
||||
|
||||
// Non-constant version of the function above
|
||||
inline Type* Find(Key key) |
||||
{ |
||||
const Idx index = _KeyToIndex(key); |
||||
return (index < m_keys.GetSize() && m_keys[index] == key) ? &m_values[index] : NULL; |
||||
} |
||||
|
||||
// Checks whether the specified key exists is in the hash
|
||||
inline bool Exists(Key key) const |
||||
{ |
||||
const Idx index = _KeyToIndex(key); |
||||
return (index < m_keys.GetSize() && m_keys[index] == key); |
||||
} |
||||
|
||||
// Removes a single entry from the list
|
||||
void Delete(Key key) |
||||
{ |
||||
const Size id = _KeyToID(key); |
||||
const Idx index = _IDToIndex(id); |
||||
|
||||
if (index < m_keys.GetSize() && m_keys[index] == key) |
||||
{ |
||||
m_keys.RemoveAt(index); |
||||
m_values.RemoveAt(index); |
||||
m_indices[id] = 0; |
||||
|
||||
// Adjust all the indices that follow this one
|
||||
for (Size i=m_indices.GetSize(); i>0; ) |
||||
{ |
||||
if (m_indices[--i] > index) |
||||
{ |
||||
--m_indices[i]; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Retrieves a value from the hash -- but it will only be valid if it exists, so be careful. In most
|
||||
// cases you will either want to use an Exists() check first, or simply use the Find function.
|
||||
inline const Type& operator [] (Key key) const |
||||
{ |
||||
const Idx index = _KeyToIndex(key); |
||||
return m_values[index]; |
||||
} |
||||
|
||||
// Retrieves a value from the hash, inserting a new one if necessary
|
||||
Type& operator [] (Key key) |
||||
{ |
||||
// Get the index for this key
|
||||
const Idx index = _KeyToIndex(key); |
||||
|
||||
if (index != KEY_NA) |
||||
{ |
||||
// If we found a valid entry, we need to match the actual key
|
||||
const Key oldKey = m_keys[index]; |
||||
|
||||
// If the key matches, return the value
|
||||
if (oldKey == key) |
||||
{ |
||||
return m_values[index]; |
||||
} |
||||
else |
||||
{ |
||||
// Setting the key was unsuccessful due to another entry colliding with our key;
|
||||
// we must expand the indices until we find a set of keys that will match.
|
||||
Size newSize = m_indices.GetSize(); |
||||
while (newSize < (KEY_MAXSIZE>>1)) |
||||
{ |
||||
newSize = newSize << 1; |
||||
// Find the next best size for the hash that would make both keys unique
|
||||
if (_StaticKeyToID(key, newSize) != _StaticKeyToID(oldKey, newSize)) |
||||
{ |
||||
m_indices.ResizeExact(newSize); |
||||
_RebuildIndices(); |
||||
break; |
||||
} |
||||
} |
||||
// Critical error if we didn't find a working size
|
||||
ASSERT(_StaticKeyToID(key, newSize) != _StaticKeyToID(oldKey, newSize)); |
||||
} |
||||
} |
||||
|
||||
// assert the total number of values stored is in Idx range
|
||||
ASSERT(m_keys.GetSize() < (Size)((Idx)-1)); |
||||
|
||||
// Append the new key to the end
|
||||
m_keys.Insert(key); |
||||
|
||||
// Add this new entry to the index list using the current array size as the 1-based index
|
||||
m_indices[_KeyToID(key)] = (Idx)m_keys.GetSize(); |
||||
|
||||
// Return the value
|
||||
return m_values.AddEmpty(); |
||||
} |
||||
|
||||
public: |
||||
// Fowler / Noll / Vo (FNV) Hash
|
||||
// magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
static inline Key HashKeyFNV(const uint8_t* data, Size size) |
||||
{ |
||||
Key hash = 2166136261U; //InitialFNV
|
||||
for (size_t i = 0; i < size; i++) |
||||
{ |
||||
hash = hash ^ (data[i]); // xor the low 8 bits
|
||||
hash = hash * 16777619; // multiply by the magic number (FNVMultiple)
|
||||
} |
||||
return hash; |
||||
} |
||||
// Function that calculates a hash value from a given data
|
||||
// tested for random binary data (3 <= size <= 33) and performs VERY bad
|
||||
static inline Key HashKeyR5(const uint8_t* data, Size size) |
||||
{ |
||||
Key key = 0; |
||||
Key offset = 1357980759; |
||||
for (Size i=0; i<size; ++i, ++data) |
||||
{ |
||||
key += (*data & 31) ^ offset; |
||||
key += i & (offset >> 15); |
||||
offset = key ^ (((~offset) >> 7) | (offset << 25)); |
||||
} |
||||
return key; |
||||
} |
||||
static inline Key HashKey(const uint8_t* data, Size size) { return HashKeyFNV(data, size); } |
||||
static inline Key HashKey(LPCTSTR sz) { return HashKey((const uint8_t*)sz, (Size)_tcslen(sz)); } |
||||
static inline Key HashKey(const String& str) { return HashKey((const uint8_t*)str.c_str(), (Size)str.size()); } |
||||
|
||||
// Convenience functions
|
||||
inline Type* Find (LPCTSTR key) { return Find ( HashKey(key) ); } |
||||
inline Type* Find (const String& key) { return Find ( HashKey(key) ); } |
||||
inline const Type* Find (LPCTSTR key) const { return Find ( HashKey(key) ); } |
||||
inline const Type* Find (const String& key) const { return Find ( HashKey(key) ); } |
||||
inline void Delete (LPCTSTR key) { Delete( HashKey(key) ); } |
||||
inline void Delete (const String& key) { Delete( HashKey(key.c_str()) ); } |
||||
inline bool Exists (LPCTSTR key) const { return Exists( HashKey(key) ); } |
||||
inline bool Exists (const String& key) const { return Exists( HashKey(key.c_str()) ); } |
||||
inline Type& operator [] (LPCTSTR key) { return (*this)[ HashKey(key) ]; } |
||||
inline Type& operator [] (const String& key) { return (*this)[ HashKey(key.c_str()) ]; } |
||||
inline const Type& operator [] (LPCTSTR key) const { return (*this)[ HashKey(key) ]; } |
||||
inline const Type& operator [] (const String& key) const { return (*this)[ HashKey(key.c_str()) ]; } |
||||
}; |
||||
|
||||
|
||||
/**************************************************************************************
|
||||
* cMapWrap template |
||||
* --------------- |
||||
* STL map wrapper |
||||
**************************************************************************************/ |
||||
|
||||
template <typename Key, typename Type> |
||||
class cMapWrap : public std::unordered_map<Key, Type> |
||||
{ |
||||
public: |
||||
typedef typename std::unordered_map<Key, Type> Base; |
||||
typedef typename Base::const_iterator const_iterator; |
||||
typedef typename Base::iterator iterator; |
||||
|
||||
public: |
||||
cMapWrap() {} |
||||
~cMapWrap() {} |
||||
|
||||
inline void Release() { this->clear(); } |
||||
inline bool IsEmpty() const { return this->empty(); } |
||||
inline size_t GetSize() const { return this->size(); } |
||||
inline const_iterator GetBegin() const { return this->begin(); } |
||||
inline const_iterator GetEnd() const { return this->end(); } |
||||
inline iterator GetBegin() { return this->begin(); } |
||||
inline iterator GetEnd() { return this->end(); } |
||||
inline const_iterator Find(const Key& key) const { return this->find(key); } |
||||
inline iterator Find(const Key& key) { return this->find(key); } |
||||
inline iterator Insert(const Key& key, bool& bExisted) { return Insert(key, Type(), bExisted); } |
||||
inline iterator Insert(const Key& key, const Type& val, bool& bExisted) { |
||||
const std::pair<iterator,bool> ret(this->emplace(key, val)); |
||||
bExisted = !ret.second; |
||||
return ret.first; |
||||
} |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_HASH_H__
|
||||
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Line.h
|
||||
//
|
||||
// Copyright 2023 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_LINE_H__ |
||||
#define __SEACAVE_LINE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
// Generic line class represented as two points
|
||||
template <typename TYPE, int DIMS> |
||||
class TLine |
||||
{ |
||||
STATIC_ASSERT(DIMS > 1 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
typedef SEACAVE::TAABB<TYPE,DIMS> AABB; |
||||
typedef SEACAVE::TRay<TYPE,DIMS> RAY; |
||||
enum { numScalar = (2*DIMS) }; |
||||
enum { numParams = numScalar-1 }; |
||||
|
||||
POINT pt1, pt2; // line description
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TLine() {} |
||||
inline TLine(const POINT& pt1, const POINT& pt2); |
||||
template <typename CTYPE> |
||||
inline TLine(const TLine<CTYPE,DIMS>&); |
||||
|
||||
inline void Set(const POINT& pt1, const POINT& pt2); |
||||
|
||||
int Optimize(const POINT*, size_t, int maxIters=100); |
||||
template <typename RobustNormFunctor> |
||||
int Optimize(const POINT*, size_t, const RobustNormFunctor& robust, int maxIters=100); |
||||
|
||||
inline TYPE GetLength() const; |
||||
inline TYPE GetLengthSq() const; |
||||
inline POINT GetCenter() const; |
||||
inline VECTOR GetDir() const; |
||||
inline VECTOR GetNormDir() const; |
||||
inline RAY GetRay() const; |
||||
|
||||
inline bool IsSame(const TLine&, TYPE th) const; |
||||
|
||||
bool Intersects(const AABB& aabb) const; |
||||
bool Intersects(const AABB& aabb, TYPE& t) const; |
||||
|
||||
inline TYPE DistanceSq(const POINT&) const; |
||||
inline TYPE Distance(const POINT&) const; |
||||
|
||||
inline TYPE Classify(const POINT&) const; |
||||
inline POINT ProjectPoint(const POINT&) const; |
||||
|
||||
inline TYPE& operator [] (BYTE i) { ASSERT(i<numScalar); return pt1.data()[i]; } |
||||
inline TYPE operator [] (BYTE i) const { ASSERT(i<numScalar); return pt1.data()[i]; } |
||||
|
||||
#ifdef _USE_BOOST |
||||
// implement BOOST serialization
|
||||
template<class Archive> |
||||
void serialize(Archive& ar, const unsigned int /*version*/) { |
||||
ar & pt1; |
||||
ar & pt2; |
||||
} |
||||
#endif |
||||
}; // class TLine
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
template <typename TYPE, typename TYPEW=TYPE> |
||||
struct FitLineOnline : FitPlaneOnline<TYPE,TYPEW,true> { |
||||
template <typename TYPEE> TPoint3<TYPEE> GetLine(TLine<TYPEE,3>& line) const; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
#include "Line.inl" |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_LINE_H__
|
||||
@ -0,0 +1,215 @@
@@ -0,0 +1,215 @@
|
||||
// D E F I N E S /////////////////////////////////////////////////// |
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
|
||||
// C L A S S ////////////////////////////////////////////////////// |
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline TLine<TYPE,DIMS>::TLine(const POINT& _pt1, const POINT& _pt2) |
||||
: |
||||
pt1(_pt1), pt2(_pt2) |
||||
{ |
||||
ASSERT(!ISZERO((_pt1-_pt2).norm())); |
||||
} // constructor |
||||
template <typename TYPE, int DIMS> |
||||
template <typename CTYPE> |
||||
inline TLine<TYPE,DIMS>::TLine(const TLine<CTYPE,DIMS>& rhs) |
||||
: |
||||
pt1(rhs.pt1.template cast<TYPE>()), pt2(rhs.pt2.template cast<TYPE>()) |
||||
{ |
||||
} // copy constructor |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// set attributes |
||||
template <typename TYPE, int DIMS> |
||||
inline void TLine<TYPE,DIMS>::Set(const POINT& _pt1, const POINT& _pt2) |
||||
{ |
||||
ASSERT(!ISZERO((_pt1-_pt2).norm())); |
||||
pt1 = _pt1; |
||||
pt2 = _pt2; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// least squares refinement of the line to the given 3D point set |
||||
// (return the number of iterations) |
||||
template <typename TYPE, int DIMS> |
||||
template <typename RobustNormFunctor> |
||||
int TLine<TYPE,DIMS>::Optimize(const POINT* points, size_t size, const RobustNormFunctor& robust, int maxIters) |
||||
{ |
||||
ASSERT(DIMS == 3); |
||||
ASSERT(size >= numParams); |
||||
struct OptimizationFunctor { |
||||
const POINT* points; |
||||
size_t size; |
||||
double scale; |
||||
const RobustNormFunctor& robust; |
||||
// construct with the data points |
||||
OptimizationFunctor(const POINT* _points, size_t _size, const RobustNormFunctor& _robust) |
||||
: points(_points), size(_size), robust(_robust) { ASSERT(size < std::numeric_limits<int>::max()); } |
||||
static void Residuals(const double* x, int nPoints, const void* pData, double* fvec, double* fjac, int* /*info*/) { |
||||
const OptimizationFunctor& data = *reinterpret_cast<const OptimizationFunctor*>(pData); |
||||
ASSERT((size_t)nPoints == data.size && fvec != NULL && fjac == NULL); |
||||
TLine<double,DIMS> line; |
||||
for (int j=0; j<DIMS; ++j) |
||||
line.pt1(j) = x[j]; |
||||
DirScale2Vector(x+DIMS, &data.scale, line.pt2.data()); |
||||
line.pt2 += line.pt1; |
||||
for (size_t i=0; i<data.size; ++i) |
||||
fvec[i] = data.robust(line.Distance(data.points[i].template cast<double>())); |
||||
} |
||||
} functor(points, size, robust); |
||||
double arrParams[numParams]; |
||||
for (int j=0; j<DIMS; ++j) |
||||
arrParams[j] = (double)pt1(j); |
||||
POINT dir(pt2-pt1); |
||||
Vector2DirScale(dir.data(), arrParams+DIMS, &functor.scale); |
||||
lm_control_struct control = {1.e-6, 1.e-7, 1.e-8, 1.e-8, 100.0, maxIters}; // lm_control_float; |
||||
lm_status_struct status; |
||||
lmmin(numParams, arrParams, (int)size, &functor, OptimizationFunctor::Residuals, &control, &status); |
||||
switch (status.info) { |
||||
//case 4: |
||||
case 5: |
||||
case 6: |
||||
case 7: |
||||
case 8: |
||||
case 9: |
||||
case 10: |
||||
case 11: |
||||
case 12: |
||||
DEBUG_ULTIMATE("error: refine line: %s", lm_infmsg[status.info]); |
||||
return 0; |
||||
} |
||||
for (int j=0; j<DIMS; ++j) |
||||
pt1(j) = (TYPE)arrParams[j]; |
||||
DirScale2Vector(arrParams+DIMS, &functor.scale, dir.data()); |
||||
pt2 = pt1+dir; |
||||
return status.nfev; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
int TLine<TYPE,DIMS>::Optimize(const POINT* points, size_t size, int maxIters) |
||||
{ |
||||
const auto identity = [](double x) { return x; }; |
||||
return Optimize(points, size, identity, maxIters); |
||||
} // Optimize |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// get attributes |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TLine<TYPE,DIMS>::GetLength() const |
||||
{ |
||||
return (pt2 - pt1).norm(); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TLine<TYPE,DIMS>::GetLengthSq() const |
||||
{ |
||||
return (pt2 - pt1).squaredNorm(); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TLine<TYPE,DIMS>::POINT TLine<TYPE,DIMS>::GetCenter() const |
||||
{ |
||||
return (pt2 + pt1) / TYPE(2); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TLine<TYPE,DIMS>::VECTOR TLine<TYPE,DIMS>::GetDir() const |
||||
{ |
||||
return (pt2 - pt1); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TLine<TYPE,DIMS>::VECTOR TLine<TYPE,DIMS>::GetNormDir() const |
||||
{ |
||||
return (pt2 - pt1).normalized(); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TLine<TYPE,DIMS>::RAY TLine<TYPE,DIMS>::GetRay() const |
||||
{ |
||||
return RAY(pt1, GetNormDir()); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline bool TLine<TYPE,DIMS>::IsSame(const TLine& line, TYPE th) const |
||||
{ |
||||
const TYPE thSq(SQUARE(th)); |
||||
const VECTOR l(pt2-pt1); |
||||
const TYPE invLenSq(INVERT(l.squaredNorm())); |
||||
const VECTOR r1(pt1-line.pt1); |
||||
const TYPE dSq1((l.cross(r1)).squaredNorm()*invLenSq); |
||||
if (dSq1 > thSq) |
||||
return false; |
||||
const VECTOR r2(pt1-line.pt2); |
||||
const TYPE dSq2((l.cross(r2)).squaredNorm()*invLenSq); |
||||
return dSq2 <= thSq; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// test for intersection with aabb |
||||
template <typename TYPE, int DIMS> |
||||
bool TLine<TYPE,DIMS>::Intersects(const AABB &aabb) const |
||||
{ |
||||
return GetRay().Intersects(aabb); |
||||
} // Intersects(AABB) |
||||
template <typename TYPE, int DIMS> |
||||
bool TLine<TYPE,DIMS>::Intersects(const AABB &aabb, TYPE& t) const |
||||
{ |
||||
return GetRay().Intersects(aabb, t); |
||||
} // Intersects(AABB) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// Computes the distance between the line and a point. |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TLine<TYPE,DIMS>::DistanceSq(const POINT& pt) const |
||||
{ |
||||
const VECTOR l(pt2-pt1), r(pt1-pt); |
||||
if (DIMS == 2) |
||||
return TYPE(SQUARE(l[0]*r[1]-r[0]*l[1])/(l[0]*l[0]+l[1]*l[1])); |
||||
ASSERT(DIMS == 3); |
||||
return TYPE((l.cross(r)).squaredNorm()/l.squaredNorm()); |
||||
} // DistanceSq(POINT) |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TLine<TYPE,DIMS>::Distance(const POINT& pt) const |
||||
{ |
||||
return SQRT(DistanceSq(pt)); |
||||
} // Distance(POINT) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Computes the position on the line segment of the point projection. |
||||
// Returns 0 if it coincides with the first point, and 1 if it coincides with the second point. |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TLine<TYPE,DIMS>::Classify(const POINT& p) const |
||||
{ |
||||
const VECTOR vL(pt2 - pt1); |
||||
ASSERT(!ISZERO(vL.squaredNorm())); |
||||
const VECTOR vP(p - pt1); |
||||
return vL.dot(vP) / vL.squaredNorm(); |
||||
} // Classify(POINT) |
||||
// Calculate point's projection on this line (closest point to this line). |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TLine<TYPE,DIMS>::POINT TLine<TYPE,DIMS>::ProjectPoint(const POINT& p) const |
||||
{ |
||||
const VECTOR vL(pt2 - pt1); |
||||
ASSERT(!ISZERO(vL.squaredNorm())); |
||||
const VECTOR vP(p - pt1); |
||||
return pt1 + vL * (vL.dot(vP) / vL.squaredNorm()); |
||||
} // ProjectPoint |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, typename TYPEW> |
||||
template <typename TYPEE> |
||||
TPoint3<TYPEE> FitLineOnline<TYPE,TYPEW>::GetLine(TLine<TYPEE,3>& line) const |
||||
{ |
||||
TPoint3<TYPEW> avg, dir; |
||||
const TPoint3<TYPEW> quality(this->GetModel(avg, dir)); |
||||
const TPoint3<TYPEW> pt2(avg+dir); |
||||
line.Set(TPoint3<TYPEE>(avg), TPoint3<TYPEE>(pt2)); |
||||
return TPoint3<TYPEE>(quality); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// LinkLib.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_LINKLIB_H__ |
||||
#define __SEACAVE_LINKLIB_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#ifdef _MSC_VER |
||||
#else |
||||
#include <dlfcn.h> |
||||
#endif |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**************************************************************************************
|
||||
* CLinkLib |
||||
* --------------- |
||||
* manage dynamic linked libraries |
||||
**************************************************************************************/ |
||||
|
||||
class GENERAL_API CLinkLib |
||||
{ |
||||
public: |
||||
#ifdef _MSC_VER |
||||
typedef HINSTANCE LibID; |
||||
#else |
||||
typedef void* LibID; |
||||
#endif |
||||
|
||||
CLinkLib() : m_hLib(NULL) |
||||
{ |
||||
} |
||||
|
||||
CLinkLib(const String& dllName) : m_hLib(NULL) |
||||
{ |
||||
Load(dllName); |
||||
} |
||||
|
||||
CLinkLib(const CLinkLib& lib) : m_hLib(lib.m_hLib) |
||||
{ |
||||
((CLinkLib&)lib).m_hLib = NULL; |
||||
} |
||||
|
||||
~CLinkLib() |
||||
{ |
||||
Free(); |
||||
} |
||||
|
||||
bool Load(const String& dllName) |
||||
{ |
||||
Free(); |
||||
#ifdef _MSC_VER |
||||
m_hLib = LoadLibrary(dllName); |
||||
#else |
||||
m_hLib = dlopen(dllName, RTLD_NOW); |
||||
#endif |
||||
return (m_hLib != NULL); |
||||
} |
||||
|
||||
void Free() |
||||
{ |
||||
if (!IsLoaded()) |
||||
return; |
||||
#ifdef _MSC_VER |
||||
FreeLibrary(m_hLib); |
||||
#else |
||||
dlclose(m_hLib); |
||||
#endif |
||||
m_hLib = NULL; |
||||
} |
||||
|
||||
void* GetFunction(const String& funcName) const |
||||
{ |
||||
#ifdef _MSC_VER |
||||
return GetProcAddress(m_hLib, funcName); |
||||
#else |
||||
return dlsym(m_hLib, funcName); |
||||
#endif |
||||
} |
||||
|
||||
bool IsLoaded() const |
||||
{ |
||||
return (m_hLib != NULL); |
||||
} |
||||
|
||||
LibID GetLibID() const |
||||
{ |
||||
return m_hLib; |
||||
} |
||||
|
||||
protected: |
||||
LibID m_hLib; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_LINKLIB_H__
|
||||
@ -0,0 +1,453 @@
@@ -0,0 +1,453 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Log.cpp
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "Common.h" |
||||
#include "Log.h" |
||||
|
||||
using namespace SEACAVE; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/*-----------------------------------------------------------*
|
||||
* Log class implementation * |
||||
*-----------------------------------------------------------*/ |
||||
|
||||
#ifndef DEFAULT_LOGTYPE |
||||
Log::LogType Log::g_appType; |
||||
#endif |
||||
|
||||
/**
|
||||
* Constructor |
||||
*/ |
||||
Log::Log() |
||||
{ |
||||
// generate default log type
|
||||
#ifndef DEFAULT_LOGTYPE |
||||
String appName = Util::getAppName(); |
||||
appName = (Util::getFileExt(appName) == _T(".exe") ? Util::getFileName(appName) : Util::getFileNameExt(appName)); |
||||
Idx n = MINF((Idx)appName.length(), (Idx)LOGTYPE_SIZE); |
||||
_tcsncpy(g_appType.szName, appName, n); |
||||
while (n < LOGTYPE_SIZE) |
||||
g_appType.szName[n++] = _T(' '); |
||||
g_appType.szName[LOGTYPE_SIZE] = _T('\0'); |
||||
#endif |
||||
ResetTypes(); |
||||
} |
||||
|
||||
void Log::RegisterListener(ClbkRecordMsg clbk) |
||||
{ |
||||
ASSERT(m_arrRecordClbk != NULL); |
||||
if (m_arrRecordClbk == NULL) |
||||
return; |
||||
m_arrRecordClbk->Insert(clbk); |
||||
} |
||||
void Log::UnregisterListener(ClbkRecordMsg clbk) |
||||
{ |
||||
ASSERT(m_arrRecordClbk != NULL); |
||||
if (m_arrRecordClbk == NULL) |
||||
return; |
||||
m_arrRecordClbk->Remove(clbk); |
||||
} |
||||
|
||||
//Register a new type of log messages (LOGTYPE_SIZE chars)
|
||||
Log::Idx Log::RegisterType(LPCTSTR lt) |
||||
{ |
||||
ASSERT(strlen(lt) == LOGTYPE_SIZE); |
||||
const Idx idx = (Idx)m_arrLogTypes.GetSize(); |
||||
LogType& logType = m_arrLogTypes.AddEmpty(); |
||||
Idx n = MINF((Idx)strlen(lt), (Idx)LOGTYPE_SIZE); |
||||
_tcsncpy(logType.szName, lt, n); |
||||
while (n < LOGTYPE_SIZE) |
||||
logType.szName[n++] = _T(' '); |
||||
logType.szName[LOGTYPE_SIZE] = _T('\0'); |
||||
return idx; |
||||
} |
||||
|
||||
/**
|
||||
* Empty the array with registered log types |
||||
*/ |
||||
void Log::ResetTypes() |
||||
{ |
||||
m_arrLogTypes.Empty(); |
||||
} |
||||
|
||||
void Log::Write(LPCTSTR szFormat, ...) |
||||
{ |
||||
if (m_arrRecordClbk == NULL) |
||||
return; |
||||
va_list args; |
||||
va_start(args, szFormat); |
||||
_Record(NO_ID, szFormat, args); |
||||
va_end(args); |
||||
} |
||||
void Log::Write(Idx lt, LPCTSTR szFormat, ...) |
||||
{ |
||||
if (m_arrRecordClbk == NULL) |
||||
return; |
||||
va_list args; |
||||
va_start(args, szFormat); |
||||
_Record(lt, szFormat, args); |
||||
va_end(args); |
||||
} |
||||
|
||||
/**
|
||||
* Write message to the log if this exists |
||||
* -> IN: Idx - log type |
||||
* LPCTSTR - format message |
||||
* ... - values |
||||
*/ |
||||
void Log::_Record(Idx lt, LPCTSTR szFormat, va_list args) |
||||
{ |
||||
ASSERT(m_arrRecordClbk != NULL); |
||||
if (m_arrRecordClbk->IsEmpty()) |
||||
return; |
||||
|
||||
// Format a message by adding the date (auto adds new line)
|
||||
TCHAR szTime[256]; |
||||
TCHAR szBuffer[2048]; |
||||
#if defined(LOG_DATE) || defined(LOG_TIME) |
||||
TCHAR* szPtrTime = szTime; |
||||
#ifdef _MSC_VER |
||||
SYSTEMTIME st; |
||||
GetLocalTime(&st); |
||||
#ifdef LOG_THREAD |
||||
WLock l(m_lock); |
||||
#endif |
||||
#ifdef LOG_DATE |
||||
szPtrTime += GetDateFormat(LOCALE_USER_DEFAULT,0,&st,_T("dd'.'MM'.'yy"),szPtrTime,80)-1; |
||||
#endif |
||||
#if defined(LOG_DATE) && defined(LOG_TIME) |
||||
szPtrTime[0] = _T('-'); ++szPtrTime; |
||||
#endif |
||||
#ifdef LOG_TIME |
||||
szPtrTime += GetTimeFormat(LOCALE_USER_DEFAULT,0,&st,_T("HH':'mm':'ss"),szPtrTime,80)-1; |
||||
#endif |
||||
#else // _MSC_VER
|
||||
const time_t t = time(NULL); |
||||
const struct tm *tmp = localtime(&t); |
||||
#ifdef LOG_DATE |
||||
szPtrTime += strftime(szPtrTime, 80, "%y.%m.%d", tmp); |
||||
#endif |
||||
#if defined(LOG_DATE) && defined(LOG_TIME) |
||||
szPtrTime[0] = _T('-'); ++szPtrTime; |
||||
#endif |
||||
#ifdef LOG_TIME |
||||
szPtrTime += strftime(szPtrTime, 80, "%H:%M:%S", tmp); |
||||
#endif |
||||
#endif // _MSC_VER
|
||||
#endif // LOG_DATE || LOG_TIME
|
||||
#ifdef DEFAULT_LOGTYPE |
||||
LPCTSTR const logType(lt<m_arrLogTypes.GetSize() ? m_arrLogTypes[lt] : (LPCSTR)DEFAULT_LOGTYPE); |
||||
#else |
||||
LPCTSTR const logType(lt<m_arrLogTypes.GetSize() ? m_arrLogTypes[lt] : g_appType); |
||||
#endif |
||||
if ((size_t)_vsntprintf(szBuffer, 2048, szFormat, args) > 2048) { |
||||
// not enough space for the full string, reprint dynamically
|
||||
m_message.FormatSafe("%s [%s] %s" LINE_SEPARATOR_STR, szTime, logType, String::FormatStringSafe(szFormat, args).c_str()); |
||||
} else { |
||||
// enough space for all the string, print directly
|
||||
m_message.Format("%s [%s] %s" LINE_SEPARATOR_STR, szTime, logType, szBuffer); |
||||
} |
||||
TRACE(m_message); |
||||
|
||||
// signal listeners
|
||||
FOREACHPTR(pClbk, *m_arrRecordClbk) |
||||
(*pClbk)(m_message); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/*-----------------------------------------------------------*
|
||||
* LogFile class implementation * |
||||
*-----------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* Constructor |
||||
*/ |
||||
LogFile::LogFile() |
||||
{ |
||||
} |
||||
|
||||
bool LogFile::Open(LPCTSTR logName) |
||||
{ |
||||
Util::ensureFolder(logName); |
||||
m_ptrFile = new File(logName, File::WRITE, File::CREATE | File::TRUNCATE); |
||||
if (!m_ptrFile->isOpen()) { |
||||
m_ptrFile = NULL; |
||||
return false; |
||||
} |
||||
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogFile::Record, this)); |
||||
return true; |
||||
} |
||||
void LogFile::Close() |
||||
{ |
||||
if (m_ptrFile == NULL) |
||||
return; |
||||
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogFile::Record, this)); |
||||
m_ptrFile = NULL; |
||||
} |
||||
|
||||
void LogFile::Pause() |
||||
{ |
||||
if (m_ptrFile == NULL) |
||||
return; |
||||
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogFile::Record, this)); |
||||
} |
||||
void LogFile::Play() |
||||
{ |
||||
if (m_ptrFile == NULL) |
||||
return; |
||||
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogFile::Record, this)); |
||||
} |
||||
|
||||
void LogFile::Record(const String& msg) |
||||
{ |
||||
ASSERT(m_ptrFile != NULL); |
||||
m_ptrFile->print(msg); |
||||
m_ptrFile->flush(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/*-----------------------------------------------------------*
|
||||
* LogConsole class implementation * |
||||
*-----------------------------------------------------------*/ |
||||
|
||||
#define MAX_CONSOLE_WIDTH 100 |
||||
#define MAX_CONSOLE_LINES 2000 |
||||
|
||||
#ifdef _MSC_VER |
||||
#include <io.h> |
||||
#define STDIN_FILENO 0 /* file descriptor for stdin */ |
||||
#define STDOUT_FILENO 1 /* file descriptor for stdout */ |
||||
#define STDERR_FILENO 2 /* file descriptor for stderr */ |
||||
#else |
||||
#include <unistd.h> |
||||
#endif |
||||
#include <fcntl.h> |
||||
|
||||
class LogConsoleOutbuf : public std::streambuf { |
||||
public: |
||||
LogConsoleOutbuf() { |
||||
setp(0, 0); |
||||
} |
||||
|
||||
virtual int_type overflow(int_type c = traits_type::eof()) { |
||||
return fputc(c, stdout) == EOF ? traits_type::eof() : c; |
||||
} |
||||
}; |
||||
|
||||
/**
|
||||
* Constructor |
||||
*/ |
||||
LogConsole::LogConsole() |
||||
: |
||||
#ifdef _USE_COSOLEFILEHANDLES |
||||
m_fileIn(NULL), m_fileOut(NULL), m_fileErr(NULL), |
||||
#else |
||||
m_fileIn(-1), m_fileOut(-1), m_fileErr(-1), |
||||
#endif |
||||
m_cout(NULL), m_coutOld(NULL), |
||||
m_cerr(NULL), m_cerrOld(NULL), |
||||
bManageConsole(false) |
||||
{ |
||||
} |
||||
|
||||
bool LogConsole::IsOpen() const |
||||
{ |
||||
#ifdef _USE_COSOLEFILEHANDLES |
||||
return (m_fileOut != NULL || m_fileIn != NULL || m_fileErr != NULL); |
||||
#else |
||||
return (m_fileOut != -1 || m_fileIn != -1 || m_fileErr != -1); |
||||
#endif |
||||
} |
||||
|
||||
#ifdef _MSC_VER |
||||
|
||||
void LogConsole::Open() |
||||
{ |
||||
if (IsOpen()) |
||||
return; |
||||
|
||||
// allocate a console for this app
|
||||
bManageConsole = (AllocConsole()!=FALSE?true:false); |
||||
|
||||
// capture std::cout and std::cerr
|
||||
if (bManageConsole && !m_cout) { |
||||
// set std::cout to use our custom streambuf
|
||||
m_cout = new LogConsoleOutbuf; |
||||
m_coutOld = std::cout.rdbuf(m_cout); |
||||
// use same buffer for std::cerr as well
|
||||
m_cerr = NULL; |
||||
m_cerrOld = std::cerr.rdbuf(m_cout); |
||||
} |
||||
|
||||
// set the screen buffer to be big enough to let us scroll text
|
||||
CONSOLE_SCREEN_BUFFER_INFO coninfo; |
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); |
||||
coninfo.dwSize.X = MAX_CONSOLE_WIDTH; // does not resize the console window
|
||||
coninfo.dwSize.Y = MAX_CONSOLE_LINES; |
||||
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); |
||||
|
||||
#if 1 && defined(_USE_COSOLEFILEHANDLES) |
||||
// redirect standard stream to the console
|
||||
m_fileIn = freopen("CONIN$", "r", stdin); |
||||
m_fileOut = freopen("CONOUT$", "w", stdout); |
||||
// there isn't any CONERR$, so that we merge stderr into CONOUT$
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
|
||||
m_fileErr = freopen("CONOUT$", "w", stderr); |
||||
#elif 1 && defined(_USE_COSOLEFILEHANDLES) |
||||
// Fix up the allocated console so that output works in all cases.
|
||||
// Change std handles to refer to new console handles. Before doing so, ensure
|
||||
// that stdout/stderr haven't been redirected to a valid file
|
||||
// See http://support.microsoft.com/kb/105305/en-us
|
||||
// stdout
|
||||
if (_fileno(stdout) == -1 || _get_osfhandle(fileno(stdout)) == -1) { |
||||
int hCrt = ::_open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT); |
||||
if (hCrt != -1) { |
||||
m_fileOut = ::_fdopen(hCrt, "w"); |
||||
if (m_fileOut) |
||||
*stdout = *m_fileOut; |
||||
} |
||||
} |
||||
// stderr
|
||||
if (_fileno(stderr) == -1 || _get_osfhandle(fileno(stderr)) == -1) { |
||||
int hCrt = ::_open_osfhandle((intptr_t)::GetStdHandle(STD_ERROR_HANDLE), _O_TEXT); |
||||
if (hCrt != -1) { |
||||
m_fileErr = ::_fdopen(hCrt, "w"); |
||||
if (m_fileErr) |
||||
*stderr = *m_fileErr; |
||||
} |
||||
} |
||||
// stdin
|
||||
if (_fileno(stdin) == -1 || _get_osfhandle(fileno(stdin)) == -1) { |
||||
int hCrt = ::_open_osfhandle((intptr_t)::GetStdHandle(STD_INPUT_HANDLE), _O_TEXT); |
||||
if (hCrt != -1) { |
||||
m_fileIn = ::_fdopen(hCrt, "r"); |
||||
if (m_fileIn) |
||||
*stdin = *m_fileIn; |
||||
} |
||||
} |
||||
#else |
||||
int hConHandle; |
||||
// redirect unbuffered STDIN to the console
|
||||
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT); |
||||
m_fileIn = _dup(STDIN_FILENO); |
||||
_dup2(hConHandle, STDIN_FILENO); |
||||
_close(hConHandle); |
||||
setvbuf(stdin, NULL, _IONBF, 0); |
||||
// redirect unbuffered STDOUT to the console
|
||||
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT); |
||||
m_fileOut = _dup(STDOUT_FILENO); |
||||
_dup2(hConHandle, STDOUT_FILENO); |
||||
_close(hConHandle); |
||||
setvbuf(stdout, NULL, _IONBF, 0); |
||||
// redirect unbuffered STDERR to the console
|
||||
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT); |
||||
m_fileErr = _dup(STDERR_FILENO); |
||||
_dup2(hConHandle, STDERR_FILENO); |
||||
_close(hConHandle); |
||||
setvbuf(stderr, NULL, _IONBF, 0); |
||||
// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
|
||||
// point to console as well
|
||||
std::ios::sync_with_stdio(); |
||||
#endif |
||||
|
||||
// register with our log system
|
||||
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this)); |
||||
} |
||||
|
||||
void LogConsole::Close() |
||||
{ |
||||
if (!IsOpen()) |
||||
return; |
||||
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this)); |
||||
#ifdef _USE_COSOLEFILEHANDLES |
||||
// close console stream handles
|
||||
fclose(m_fileIn); m_fileIn = NULL; |
||||
fclose(m_fileOut); m_fileOut = NULL; |
||||
fclose(m_fileErr); m_fileErr = NULL; |
||||
#else |
||||
// restore STDIN
|
||||
_dup2(m_fileIn, STDIN_FILENO); |
||||
_close(m_fileIn); |
||||
m_fileIn = -1; |
||||
// restore STDOUT
|
||||
_dup2(m_fileOut, STDOUT_FILENO); |
||||
_close(m_fileOut); |
||||
m_fileOut = -1; |
||||
// restore STDERR
|
||||
_dup2(m_fileErr, STDERR_FILENO); |
||||
_close(m_fileErr); |
||||
m_fileErr = -1; |
||||
#endif |
||||
// close console
|
||||
if (bManageConsole) { |
||||
if (m_cout) { |
||||
// set std::cout to the original streambuf
|
||||
std::cout.rdbuf(m_coutOld); |
||||
std::cerr.rdbuf(m_cerrOld); |
||||
delete m_cout; m_cout = NULL; |
||||
} |
||||
FreeConsole(); |
||||
} |
||||
#ifndef _USE_COSOLEFILEHANDLES |
||||
std::ios::sync_with_stdio(); |
||||
#endif |
||||
} |
||||
|
||||
void LogConsole::Record(const String& msg) |
||||
{ |
||||
ASSERT(IsOpen()); |
||||
printf(msg); |
||||
fflush(stdout); |
||||
} |
||||
|
||||
#else |
||||
|
||||
void LogConsole::Open() |
||||
{ |
||||
if (IsOpen()) |
||||
return; |
||||
++m_fileIn; |
||||
// register with our log system
|
||||
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this)); |
||||
} |
||||
|
||||
void LogConsole::Close() |
||||
{ |
||||
if (!IsOpen()) |
||||
return; |
||||
// unregister with our log system
|
||||
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this)); |
||||
--m_fileIn; |
||||
} |
||||
|
||||
void LogConsole::Record(const String& msg) |
||||
{ |
||||
ASSERT(IsOpen()); |
||||
printf(_T("%s"), msg.c_str()); |
||||
fflush(stdout); |
||||
} |
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
void LogConsole::Pause() |
||||
{ |
||||
if (IsOpen()) |
||||
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this)); |
||||
} |
||||
void LogConsole::Play() |
||||
{ |
||||
if (IsOpen()) |
||||
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this)); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,211 @@
@@ -0,0 +1,211 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Log.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_LOG_H__ |
||||
#define __SEACAVE_LOG_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
// use file handles to access console
|
||||
#define _USE_COSOLEFILEHANDLES |
||||
|
||||
//#define LOG_DATE // add date info for every log
|
||||
#define LOG_TIME // add time info for every log
|
||||
#define LOG_THREAD // make log multi-thread safe
|
||||
#define LOG_STREAM // add stream support (operator <<)
|
||||
#define LOGTYPE_SIZE 8 |
||||
#define DEFAULT_LOGTYPE _T("App ") |
||||
|
||||
#define DECLARE_LOG() \ |
||||
protected: static const Log::Idx ms_nLogType; |
||||
#define DEFINE_LOG(classname, log) \ |
||||
const Log::Idx classname::ms_nLogType(REGISTER_LOG(log)); |
||||
|
||||
#ifdef LOG_THREAD |
||||
#include "CriticalSection.h" |
||||
#endif |
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
class GENERAL_API Log |
||||
{ |
||||
DECLARE_SINGLETON(Log); |
||||
|
||||
public: |
||||
typedef uint32_t Idx; |
||||
typedef DELEGATE<void (const String&)> ClbkRecordMsg; |
||||
typedef cList<ClbkRecordMsg> ClbkRecordMsgArray; |
||||
typedef CSharedPtr<ClbkRecordMsgArray> ClbkRecordMsgArrayPtr; |
||||
|
||||
public: |
||||
// log methods
|
||||
void Open() { m_arrRecordClbk = new ClbkRecordMsgArray; } |
||||
void Close() { m_arrRecordClbk = NULL; } |
||||
void Join(Log& log) { m_arrRecordClbk = log.m_arrRecordClbk; } |
||||
void RegisterListener(ClbkRecordMsg); |
||||
void UnregisterListener(ClbkRecordMsg); |
||||
Idx RegisterType(LPCTSTR); |
||||
void ResetTypes(); |
||||
void Write(LPCTSTR, ...); |
||||
void Write(Idx, LPCTSTR, ...); |
||||
|
||||
#ifdef LOG_STREAM |
||||
template<class T> inline Log& operator<<(const T& val) { |
||||
#ifdef LOG_THREAD |
||||
Lock l(m_cs); |
||||
std::ostringstream& ostr = m_streams[__THREAD__]; |
||||
#else |
||||
std::ostringstream& ostr = m_stream; |
||||
#endif |
||||
ostr << val; |
||||
const std::string& line = ostr.str(); |
||||
if (!line.empty() && *(line.end()-1) == _T('\n')) { |
||||
Write(line.substr(0, line.size()-1).c_str()); |
||||
ostr.str(_T("")); |
||||
} |
||||
return *this; |
||||
} |
||||
// the type of std::cout
|
||||
typedef std::basic_ostream<char, std::char_traits<char> > CoutType; |
||||
// the function signature of std::endl
|
||||
typedef CoutType& (*StandardEndLine)(CoutType&); |
||||
// define an operator<< to take in std::endl
|
||||
inline Log& operator<<(StandardEndLine) { |
||||
#ifdef LOG_THREAD |
||||
Lock l(m_cs); |
||||
std::ostringstream& ostr = m_streams[__THREAD__]; |
||||
#else |
||||
std::ostringstream& ostr = m_stream; |
||||
#endif |
||||
Write(ostr.str().c_str()); |
||||
ostr.str(_T("")); |
||||
return *this; |
||||
} |
||||
#endif |
||||
|
||||
protected: |
||||
// write a message of a certain type to the log
|
||||
void _Record(Idx, LPCTSTR, va_list); |
||||
|
||||
protected: |
||||
struct LogType { |
||||
TCHAR szName[LOGTYPE_SIZE+1]; |
||||
inline operator LPCTSTR () const { return szName; } |
||||
inline operator LPTSTR () { return szName; } |
||||
}; |
||||
typedef cList<LogType, const LogType&, 0, 8> LogTypeArr; |
||||
|
||||
// log members
|
||||
String m_message; // last recorded message
|
||||
ClbkRecordMsgArrayPtr m_arrRecordClbk;// the array with all registered listeners
|
||||
LogTypeArr m_arrLogTypes; // the array with all the registered log types
|
||||
|
||||
#ifdef LOG_THREAD |
||||
// threading
|
||||
RWLock m_lock; // mutex used to ensure multi-thread safety
|
||||
#endif |
||||
|
||||
#ifdef LOG_STREAM |
||||
// streaming
|
||||
#ifdef LOG_THREAD |
||||
typedef std::unordered_map<unsigned,std::ostringstream> StreamMap; |
||||
StreamMap m_streams; // stream object used to handle one log with operator << (one for each thread)
|
||||
CriticalSection m_cs; // mutex used to ensure multi-thread safety for accessing m_streams
|
||||
#else |
||||
std::ostringstream m_stream; // stream object used to handle one log with operator <<
|
||||
#endif |
||||
#endif |
||||
|
||||
// static
|
||||
#ifndef DEFAULT_LOGTYPE |
||||
static LogType g_appType; |
||||
#endif |
||||
}; |
||||
#define GET_LOG() SEACAVE::Log::GetInstance() |
||||
#define OPEN_LOG() GET_LOG().Open() |
||||
#define CLOSE_LOG() GET_LOG().Close() |
||||
#define JOIN_LOG(log) GET_LOG().Join(log) |
||||
#define REGISTER_LOG(lt) GET_LOG().RegisterType(lt) |
||||
#define LOG GET_LOG().Write |
||||
#define SLOG(msg) GET_LOG() << msg |
||||
#ifndef _RELEASE |
||||
#define LOGV LOG // include extra details in the log
|
||||
#else |
||||
#define LOGV(...) |
||||
#endif |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
class GENERAL_API LogFile |
||||
{ |
||||
DECLARE_SINGLETON(LogFile); |
||||
|
||||
public: |
||||
~LogFile() { Close(); } |
||||
|
||||
// log methods
|
||||
bool Open(LPCTSTR); |
||||
void Close(); |
||||
void Pause(); |
||||
void Play(); |
||||
void Record(const String&); |
||||
|
||||
protected: |
||||
FilePtr m_ptrFile; // the log file
|
||||
}; |
||||
#define GET_LOGFILE() LogFile::GetInstance() |
||||
#define OPEN_LOGFILE(log) GET_LOGFILE().Open(log) |
||||
#define CLOSE_LOGFILE() GET_LOGFILE().Close() |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
class GENERAL_API LogConsole |
||||
{ |
||||
DECLARE_SINGLETON(LogConsole); |
||||
|
||||
public: |
||||
~LogConsole() { Close(); } |
||||
|
||||
bool IsOpen() const; |
||||
|
||||
// log methods
|
||||
void Open(); |
||||
void Close(); |
||||
void Pause(); |
||||
void Play(); |
||||
void Record(const String&); |
||||
|
||||
protected: |
||||
#ifdef _USE_COSOLEFILEHANDLES |
||||
typedef FILE* StreamHandle; |
||||
#else |
||||
typedef int StreamHandle; |
||||
#endif |
||||
StreamHandle m_fileIn; // the log file in
|
||||
StreamHandle m_fileOut; // the log file out
|
||||
StreamHandle m_fileErr; // the log file error
|
||||
std::streambuf* m_cout; // the redirected cout stream
|
||||
std::streambuf* m_coutOld; // the original cout stream
|
||||
std::streambuf* m_cerr; // the redirected cerr stream
|
||||
std::streambuf* m_cerrOld; // the original cout stream
|
||||
bool bManageConsole; // remember if the console is created here or is an existing console
|
||||
}; |
||||
#define GET_LOGCONSOLE() LogConsole::GetInstance() |
||||
#define OPEN_LOGCONSOLE() GET_LOGCONSOLE().Open() |
||||
#define CLOSE_LOGCONSOLE() GET_LOGCONSOLE().Close() |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_LOG_H__
|
||||
@ -0,0 +1,182 @@
@@ -0,0 +1,182 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// MemFile.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_MEMFILE_H__ |
||||
#define __SEACAVE_MEMFILE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Streams.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
class GENERAL_API MemFile : public IOStream { |
||||
public: |
||||
MemFile() : m_buffer(NULL), m_sizeBuffer(0), m_size(0), m_pos(0) { |
||||
} |
||||
MemFile(size_t initialSize) : m_buffer(new BYTE[initialSize]), m_sizeBuffer(initialSize), m_size(0), m_pos(0) { |
||||
} |
||||
MemFile(BYTE* data, size_t size) : m_buffer(data), m_sizeBuffer(0), m_size(size), m_pos(0) { |
||||
} |
||||
virtual ~MemFile() { |
||||
close(); |
||||
} |
||||
|
||||
static LPCSTR getClassType() { return "MemFile"; } |
||||
virtual LPCSTR getClassName() const { return MemFile::getClassType(); } |
||||
|
||||
bool isOpen() const { return m_buffer != NULL; } |
||||
|
||||
virtual void close() { |
||||
if (!isOpen()) |
||||
return; |
||||
if (m_sizeBuffer) |
||||
delete[] m_buffer; |
||||
m_sizeBuffer = 0; |
||||
m_buffer = NULL; |
||||
m_size = 0; |
||||
m_pos = 0; |
||||
} |
||||
|
||||
virtual size_f_t getSizeBuffer() const { |
||||
return m_sizeBuffer; |
||||
} |
||||
|
||||
virtual size_f_t getSizeLeft() const { |
||||
return m_size - m_pos; |
||||
} |
||||
|
||||
virtual size_f_t getSize() const { |
||||
return m_size; |
||||
} |
||||
|
||||
virtual bool setSize(size_f_t newSize) { |
||||
ASSERT(newSize >= 0); |
||||
if (newSize > m_sizeBuffer) |
||||
setMaxSize(newSize); |
||||
m_size = newSize; |
||||
if (m_pos > m_size) |
||||
m_pos = m_size; |
||||
return true; |
||||
} |
||||
|
||||
virtual bool growSize(size_f_t deltaSize) { |
||||
return setSize(m_size+deltaSize); |
||||
} |
||||
|
||||
virtual bool moveSize(size_f_t delataSize) { |
||||
return setSize(m_size + delataSize); |
||||
} |
||||
|
||||
virtual bool setMaxSize(size_f_t newSize) { |
||||
ASSERT(newSize > m_sizeBuffer); |
||||
// grow by 50% or at least to minNewVectorSize
|
||||
const size_f_t expoSize(m_sizeBuffer + (m_sizeBuffer>>1)); |
||||
if (newSize < expoSize) |
||||
newSize = expoSize; |
||||
// allocate a larger chunk of memory, copy the data and delete the old chunk
|
||||
BYTE* const tmp(m_buffer); |
||||
m_buffer = new BYTE[(size_t)newSize]; |
||||
if (!m_buffer) { |
||||
m_buffer = tmp; |
||||
return false; |
||||
} |
||||
if (m_size > newSize) { |
||||
m_size = newSize; |
||||
if (m_pos > m_size) |
||||
m_pos = m_size; |
||||
} |
||||
memcpy(m_buffer, tmp, (size_t)m_size); |
||||
if (m_sizeBuffer) |
||||
delete[] tmp; |
||||
m_sizeBuffer = newSize; |
||||
return true; |
||||
} |
||||
|
||||
virtual bool growMaxSize(size_f_t deltaSize) { |
||||
return setMaxSize(m_sizeBuffer+deltaSize); |
||||
} |
||||
|
||||
virtual bool ensureSize(size_f_t extraSize) { |
||||
const size_f_t newSize = m_size + extraSize; |
||||
if (newSize > m_sizeBuffer) |
||||
return setMaxSize(newSize); |
||||
return true; |
||||
} |
||||
|
||||
virtual size_f_t getPos() const { |
||||
return m_pos; |
||||
} |
||||
|
||||
virtual bool setPos(size_f_t pos) { |
||||
if (pos > m_size && !setSize(pos)) |
||||
return false; |
||||
m_pos = pos; |
||||
return true; |
||||
} |
||||
|
||||
virtual bool movePos(size_f_t delataPos) { |
||||
return setPos(m_pos + delataPos); |
||||
} |
||||
|
||||
virtual bool isEOF() { |
||||
return (m_pos == m_size); |
||||
} |
||||
|
||||
virtual size_t read(void* buf, size_t len) { |
||||
if (m_pos >= m_size) |
||||
return 0; |
||||
if (m_pos+(size_f_t)len > m_size) |
||||
len = (size_t)(m_size-m_pos); |
||||
memcpy(buf, m_buffer+m_pos, len); |
||||
m_pos += len; |
||||
return len; |
||||
} |
||||
|
||||
virtual size_t write(const void* buf, size_t len) { |
||||
const size_f_t endSize = m_pos+len; |
||||
if (endSize > m_size && !setSize(endSize)) |
||||
return 0; |
||||
memcpy(m_buffer+m_pos, buf, len); |
||||
m_pos += len; |
||||
return len; |
||||
} |
||||
|
||||
virtual BYTE* getData() { |
||||
return m_buffer + m_pos; |
||||
} |
||||
|
||||
virtual BYTE* getBuffer() { |
||||
return m_buffer; |
||||
} |
||||
|
||||
virtual size_t flush() { |
||||
return 0; |
||||
} |
||||
|
||||
protected: |
||||
BYTE* m_buffer; //buffer where we will store the data
|
||||
size_f_t m_sizeBuffer; //size of the whole buffer, 0 if no buffer or not allocated here
|
||||
size_f_t m_size; //size of the stored data
|
||||
size_f_t m_pos; //current position in the stored data
|
||||
|
||||
private: |
||||
MemFile(const MemFile&); |
||||
MemFile& operator=(const MemFile&); |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_MEMFILE_H__
|
||||
@ -0,0 +1,127 @@
@@ -0,0 +1,127 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// OBB.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_OBB_H__ |
||||
#define __SEACAVE_OBB_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
class TAABB; |
||||
|
||||
template <typename TYPE, int DIMS> |
||||
class TRay; |
||||
|
||||
// Basic oriented bounding-box class
|
||||
template <typename TYPE, int DIMS> |
||||
class TOBB |
||||
{ |
||||
STATIC_ASSERT(DIMS > 0 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef TYPE Type; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
typedef Eigen::Matrix<TYPE,DIMS,DIMS,Eigen::RowMajor> MATRIX; |
||||
typedef SEACAVE::TAABB<TYPE,DIMS> AABB; |
||||
typedef SEACAVE::TRay<TYPE,DIMS> RAY; |
||||
typedef unsigned ITYPE; |
||||
typedef Eigen::Matrix<ITYPE,DIMS,1> TRIANGLE; |
||||
enum { numCorners = (DIMS==1 ? 2 : (DIMS==2 ? 4 : 8)) }; // 2^DIMS
|
||||
enum { numScalar = (5*DIMS) }; |
||||
|
||||
MATRIX m_rot; // rotation matrix from world to local (orthonormal axes)
|
||||
POINT m_pos; // translation from local to world (center-point)
|
||||
POINT m_ext; // bounding box extents in local (half axis length)
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TOBB() {} |
||||
inline TOBB(bool); |
||||
inline TOBB(const AABB&); |
||||
inline TOBB(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax); |
||||
inline TOBB(const POINT* pts, size_t n); |
||||
inline TOBB(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s); |
||||
template <typename CTYPE> |
||||
inline TOBB(const TOBB<CTYPE, DIMS>&); |
||||
|
||||
inline void Set(const AABB&); // build from AABB
|
||||
inline void Set(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax); // build from rotation matrix from world to local, and local min/max corners
|
||||
inline void Set(const POINT* pts, size_t n); // build from points
|
||||
inline void Set(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s); // build from triangles
|
||||
inline void Set(const MATRIX& C, const POINT* pts, size_t n); // build from covariance matrix
|
||||
inline void SetRotation(const MATRIX& C); // build rotation only from covariance matrix
|
||||
inline void SetBounds(const POINT* pts, size_t n); // build size and center only from given points
|
||||
|
||||
inline void BuildBegin(); // start online build for computing the rotation
|
||||
inline void BuildAdd(const POINT&); // add a new point to the online build
|
||||
inline void BuildEnd(); // end online build for computing the rotation
|
||||
|
||||
inline bool IsValid() const; |
||||
|
||||
inline TOBB& Enlarge(TYPE); |
||||
inline TOBB& EnlargePercent(TYPE); |
||||
|
||||
inline void Translate(const POINT&); |
||||
inline void Transform(const MATRIX&); |
||||
|
||||
inline POINT GetCenter() const; |
||||
inline void GetCenter(POINT&) const; |
||||
|
||||
inline POINT GetSize() const; |
||||
inline void GetSize(POINT&) const; |
||||
|
||||
inline void GetCorners(POINT pts[numCorners]) const; |
||||
inline AABB GetAABB() const; |
||||
|
||||
inline TYPE GetVolume() const; |
||||
|
||||
bool Intersects(const POINT&) const; |
||||
|
||||
inline TYPE& operator [] (BYTE i) { ASSERT(i<numScalar); return m_rot.data()[i]; } |
||||
inline TYPE operator [] (BYTE i) const { ASSERT(i<numScalar); return m_rot.data()[i]; } |
||||
|
||||
friend std::ostream& operator << (std::ostream& st, const TOBB& obb) { |
||||
st << obb.m_rot; st << std::endl; |
||||
st << obb.m_pos; st << std::endl; |
||||
st << obb.m_ext; st << std::endl; |
||||
return st; |
||||
} |
||||
friend std::istream& operator >> (std::istream& st, TOBB& obb) { |
||||
st >> obb.m_rot; |
||||
st >> obb.m_pos; |
||||
st >> obb.m_ext; |
||||
return st; |
||||
} |
||||
|
||||
#ifdef _USE_BOOST |
||||
// implement BOOST serialization
|
||||
template<class Archive> |
||||
void serialize(Archive& ar, const unsigned int /*version*/) { |
||||
ar & m_rot; |
||||
ar & m_pos; |
||||
ar & m_ext; |
||||
} |
||||
#endif |
||||
}; // class TOBB
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
#include "OBB.inl" |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_OBB_H__
|
||||
@ -0,0 +1,401 @@
@@ -0,0 +1,401 @@
|
||||
//////////////////////////////////////////////////////////////////// |
||||
// OBB.inl |
||||
// |
||||
// Copyright 2007 cDc@seacave |
||||
// Distributed under the Boost Software License, Version 1.0 |
||||
// (See http://www.boost.org/LICENSE_1_0.txt) |
||||
|
||||
|
||||
// D E F I N E S /////////////////////////////////////////////////// |
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline TOBB<TYPE,DIMS>::TOBB(bool) |
||||
: |
||||
m_rot(MATRIX::Identity()), |
||||
m_pos(POINT::Zero()), |
||||
m_ext(POINT::Zero()) |
||||
{ |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TOBB<TYPE,DIMS>::TOBB(const AABB& aabb) |
||||
{ |
||||
Set(aabb); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TOBB<TYPE,DIMS>::TOBB(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax) |
||||
{ |
||||
Set(rot, ptMin, ptMax); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TOBB<TYPE,DIMS>::TOBB(const POINT* pts, size_t n) |
||||
{ |
||||
Set(pts, n); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TOBB<TYPE,DIMS>::TOBB(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s) |
||||
{ |
||||
Set(pts, n, tris, s); |
||||
} // constructor |
||||
template <typename TYPE, int DIMS> |
||||
template <typename CTYPE> |
||||
inline TOBB<TYPE,DIMS>::TOBB(const TOBB<CTYPE,DIMS>& rhs) |
||||
: |
||||
m_rot(rhs.m_rot.template cast<TYPE>()), |
||||
m_pos(rhs.m_pos.template cast<TYPE>()), |
||||
m_ext(rhs.m_ext.template cast<TYPE>()) |
||||
{ |
||||
} // copy constructor |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::Set(const AABB& aabb) |
||||
{ |
||||
m_rot.setIdentity(); |
||||
m_pos = aabb.GetCenter(); |
||||
m_ext = aabb.GetSize()/TYPE(2); |
||||
} |
||||
|
||||
// build from rotation matrix from world to local, and local min/max corners |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::Set(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax) |
||||
{ |
||||
m_rot = rot; |
||||
m_pos = (ptMax + ptMin) * TYPE(0.5); |
||||
m_ext = (ptMax - ptMin) * TYPE(0.5); |
||||
} |
||||
|
||||
// Inspired from "Fitting Oriented Bounding Boxes" by James Gregson |
||||
// http://jamesgregson.blogspot.ro/2011/03/latex-test.html |
||||
|
||||
// build an OBB from a vector of input points. This |
||||
// method just forms the covariance matrix and hands |
||||
// it to the build_from_covariance_matrix method |
||||
// which handles fitting the box to the points |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::Set(const POINT* pts, size_t n) |
||||
{ |
||||
ASSERT(n >= DIMS); |
||||
|
||||
// loop over the points to find the mean point |
||||
// location and to build the covariance matrix; |
||||
// note that we only have |
||||
// to build terms for the upper triangular |
||||
// portion since the matrix is symmetric |
||||
POINT mu(POINT::Zero()); |
||||
TYPE cxx=0, cxy=0, cxz=0, cyy=0, cyz=0, czz=0; |
||||
for (size_t i=0; i<n; ++i) { |
||||
const POINT& p = pts[i]; |
||||
mu += p; |
||||
cxx += p(0)*p(0); |
||||
cxy += p(0)*p(1); |
||||
cxz += p(0)*p(2); |
||||
cyy += p(1)*p(1); |
||||
cyz += p(1)*p(2); |
||||
czz += p(2)*p(2); |
||||
} |
||||
const TYPE invN(TYPE(1)/TYPE(n)); |
||||
cxx = (cxx - mu(0)*mu(0)*invN)*invN; |
||||
cxy = (cxy - mu(0)*mu(1)*invN)*invN; |
||||
cxz = (cxz - mu(0)*mu(2)*invN)*invN; |
||||
cyy = (cyy - mu(1)*mu(1)*invN)*invN; |
||||
cyz = (cyz - mu(1)*mu(2)*invN)*invN; |
||||
czz = (czz - mu(2)*mu(2)*invN)*invN; |
||||
|
||||
// now build the covariance matrix |
||||
MATRIX C; |
||||
C(0,0) = cxx; C(0,1) = cxy; C(0,2) = cxz; |
||||
C(1,0) = cxy; C(1,1) = cyy; C(1,2) = cyz; |
||||
C(2,0) = cxz; C(2,1) = cyz; C(2,2) = czz; |
||||
|
||||
// set the OBB parameters from the covariance matrix |
||||
Set(C, pts, n); |
||||
} |
||||
// builds an OBB from triangles specified as an array of |
||||
// points with integer indices into the point array. Forms |
||||
// the covariance matrix for the triangles, then uses the |
||||
// method build_from_covariance_matrix method to fit |
||||
// the box. ALL points will be fit in the box, regardless |
||||
// of whether they are indexed by a triangle or not. |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::Set(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s) |
||||
{ |
||||
ASSERT(n >= DIMS); |
||||
|
||||
// loop over the triangles this time to find the |
||||
// mean location |
||||
POINT mu(POINT::Zero()); |
||||
TYPE Am=0; |
||||
TYPE cxx=0, cxy=0, cxz=0, cyy=0, cyz=0, czz=0; |
||||
for (size_t i=0; i<s; ++i) { |
||||
ASSERT(tris[i](0)<n && tris[i](1)<n && tris[i](2)<n); |
||||
const POINT& p = pts[tris[i](0)]; |
||||
const POINT& q = pts[tris[i](1)]; |
||||
const POINT& r = pts[tris[i](2)]; |
||||
const POINT mui = (p+q+r)/TYPE(3); |
||||
const TYPE Ai = (q-p).cross(r-p).normalize()/TYPE(2); |
||||
mu += mui*Ai; |
||||
Am += Ai; |
||||
|
||||
// these bits set the c terms to Am*E[xx], Am*E[xy], Am*E[xz].... |
||||
const TYPE Ai12 = Ai/TYPE(12); |
||||
cxx += (TYPE(9)*mui(0)*mui(0) + p(0)*p(0) + q(0)*q(0) + r(0)*r(0))*Ai12; |
||||
cxy += (TYPE(9)*mui(0)*mui(1) + p(0)*p(1) + q(0)*q(1) + r(0)*r(1))*Ai12; |
||||
cxz += (TYPE(9)*mui(0)*mui(2) + p(0)*p(2) + q(0)*q(2) + r(0)*r(2))*Ai12; |
||||
cyy += (TYPE(9)*mui(1)*mui(1) + p(1)*p(1) + q(1)*q(1) + r(1)*r(1))*Ai12; |
||||
cyz += (TYPE(9)*mui(1)*mui(2) + p(1)*p(2) + q(1)*q(2) + r(1)*r(2))*Ai12; |
||||
czz += (TYPE(9)*mui(2)*mui(2) + p(2)*p(2) + q(2)*q(2) + r(2)*r(2))*Ai12; |
||||
} |
||||
|
||||
// divide out the Am fraction from the average position and |
||||
// covariance terms |
||||
mu /= Am; |
||||
cxx /= Am; cxy /= Am; cxz /= Am; cyy /= Am; cyz /= Am; czz /= Am; |
||||
|
||||
// now subtract off the E[x]*E[x], E[x]*E[y], ... terms |
||||
cxx -= mu(0)*mu(0); cxy -= mu(0)*mu(1); cxz -= mu(0)*mu(2); |
||||
cyy -= mu(1)*mu(1); cyz -= mu(1)*mu(2); czz -= mu(2)*mu(2); |
||||
|
||||
// now build the covariance matrix |
||||
MATRIX C; |
||||
C(0,0)=cxx; C(0,1)=cxy; C(0,2)=cxz; |
||||
C(1,0)=cxy; C(1,1)=cyy; C(1,2)=cyz; |
||||
C(2,0)=cxz; C(1,2)=cyz; C(2,2)=czz; |
||||
|
||||
// set the obb parameters from the covariance matrix |
||||
Set(C, pts, n); |
||||
} |
||||
// method to set the OBB parameters which produce a box oriented according to |
||||
// the covariance matrix C, and that contains the given points |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::Set(const MATRIX& C, const POINT* pts, size_t n) |
||||
{ |
||||
// extract rotation from the covariance matrix |
||||
SetRotation(C); |
||||
// extract size and center from the given points |
||||
SetBounds(pts, n); |
||||
} |
||||
// method to set the OBB rotation which produce a box oriented according to |
||||
// the covariance matrix C (only the rotations is set) |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::SetRotation(const MATRIX& C) |
||||
{ |
||||
// extract the eigenvalues and eigenvectors from C |
||||
const Eigen::SelfAdjointEigenSolver<MATRIX> es(C); |
||||
ASSERT(es.info() == Eigen::Success); |
||||
// find the right, up and forward vectors from the eigenvectors |
||||
// and set the rotation matrix using the eigenvectors |
||||
ASSERT(es.eigenvalues()(0) < es.eigenvalues()(1) && es.eigenvalues()(1) < es.eigenvalues()(2)); |
||||
m_rot = es.eigenvectors().transpose(); |
||||
if (m_rot.determinant() < 0) |
||||
m_rot = -m_rot; |
||||
} |
||||
// method to set the OBB center and size that contains the given points |
||||
// the rotations should be already set |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::SetBounds(const POINT* pts, size_t n) |
||||
{ |
||||
ASSERT(n >= DIMS); |
||||
ASSERT(ISEQUAL((m_rot*m_rot.transpose()).trace(), TYPE(3)) && ISEQUAL(m_rot.determinant(), TYPE(1))); |
||||
|
||||
// build the bounding box extents in the rotated frame |
||||
const TYPE tmax = std::numeric_limits<TYPE>::max(); |
||||
POINT minim(tmax, tmax, tmax), maxim(-tmax, -tmax, -tmax); |
||||
for (size_t i=0; i<n; ++i) { |
||||
const POINT p_prime(m_rot * pts[i]); |
||||
if (minim(0) > p_prime(0)) minim(0) = p_prime(0); |
||||
if (minim(1) > p_prime(1)) minim(1) = p_prime(1); |
||||
if (minim(2) > p_prime(2)) minim(2) = p_prime(2); |
||||
if (maxim(0) < p_prime(0)) maxim(0) = p_prime(0); |
||||
if (maxim(1) < p_prime(1)) maxim(1) = p_prime(1); |
||||
if (maxim(2) < p_prime(2)) maxim(2) = p_prime(2); |
||||
} |
||||
|
||||
// set the center of the OBB to be the average of the |
||||
// minimum and maximum, and the extents be half of the |
||||
// difference between the minimum and maximum |
||||
const POINT center((maxim+minim)*TYPE(0.5)); |
||||
m_pos = m_rot.transpose() * center; |
||||
m_ext = (maxim-minim)*TYPE(0.5); |
||||
} // Set |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::BuildBegin() |
||||
{ |
||||
m_rot = MATRIX::Zero(); |
||||
m_pos = POINT::Zero(); |
||||
m_ext = POINT::Zero(); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::BuildAdd(const POINT& p) |
||||
{ |
||||
// store mean in m_pos |
||||
m_pos += p; |
||||
// store covariance params in m_rot |
||||
m_rot(0,0) += p(0)*p(0); |
||||
m_rot(0,1) += p(0)*p(1); |
||||
m_rot(0,2) += p(0)*p(2); |
||||
m_rot(1,0) += p(1)*p(1); |
||||
m_rot(1,1) += p(1)*p(2); |
||||
m_rot(1,2) += p(2)*p(2); |
||||
// store count in m_ext |
||||
++(*((size_t*)m_ext.data())); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::BuildEnd() |
||||
{ |
||||
const TYPE invN(TYPE(1)/TYPE(*((size_t*)m_ext.data()))); |
||||
const TYPE cxx = (m_rot(0,0) - m_pos(0)*m_pos(0)*invN)*invN; |
||||
const TYPE cxy = (m_rot(0,1) - m_pos(0)*m_pos(1)*invN)*invN; |
||||
const TYPE cxz = (m_rot(0,2) - m_pos(0)*m_pos(2)*invN)*invN; |
||||
const TYPE cyy = (m_rot(1,0) - m_pos(1)*m_pos(1)*invN)*invN; |
||||
const TYPE cyz = (m_rot(1,1) - m_pos(1)*m_pos(2)*invN)*invN; |
||||
const TYPE czz = (m_rot(1,2) - m_pos(2)*m_pos(2)*invN)*invN; |
||||
|
||||
// now build the covariance matrix |
||||
MATRIX C; |
||||
C(0,0) = cxx; C(0,1) = cxy; C(0,2) = cxz; |
||||
C(1,0) = cxy; C(1,1) = cyy; C(1,2) = cyz; |
||||
C(2,0) = cxz; C(2,1) = cyz; C(2,2) = czz; |
||||
SetRotation(C); |
||||
} // Build |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// check if the oriented bounding box has positive size |
||||
template <typename TYPE, int DIMS> |
||||
inline bool TOBB<TYPE,DIMS>::IsValid() const |
||||
{ |
||||
return m_ext.minCoeff() > TYPE(0); |
||||
} // IsValid |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline TOBB<TYPE,DIMS>& TOBB<TYPE,DIMS>::Enlarge(TYPE x) |
||||
{ |
||||
m_ext.array() += x; |
||||
return *this; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TOBB<TYPE,DIMS>& TOBB<TYPE,DIMS>::EnlargePercent(TYPE x) |
||||
{ |
||||
m_ext *= x; |
||||
return *this; |
||||
} // Enlarge |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Update the box by the given pos delta. |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::Translate(const POINT& d) |
||||
{ |
||||
m_pos += d; |
||||
} |
||||
// Update the box by the given transform. |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::Transform(const MATRIX& m) |
||||
{ |
||||
m_rot = m * m_rot; |
||||
m_pos = m * m_pos; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline typename TOBB<TYPE,DIMS>::POINT TOBB<TYPE,DIMS>::GetCenter() const |
||||
{ |
||||
return m_pos; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::GetCenter(POINT& ptCenter) const |
||||
{ |
||||
ptCenter = m_pos; |
||||
} // GetCenter |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline typename TOBB<TYPE,DIMS>::POINT TOBB<TYPE,DIMS>::GetSize() const |
||||
{ |
||||
return m_ext*2; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::GetSize(POINT& ptSize) const |
||||
{ |
||||
ptSize = m_ext*2; |
||||
} // GetSize |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline void TOBB<TYPE,DIMS>::GetCorners(POINT pts[numCorners]) const |
||||
{ |
||||
if (DIMS == 2) { |
||||
const POINT pEAxis[2] = { |
||||
m_rot.row(0)*m_ext[0], |
||||
m_rot.row(1)*m_ext[1] |
||||
}; |
||||
const POINT pos(m_rot.transpose()*m_pos); |
||||
pts[0] = pos - pEAxis[0] - pEAxis[1]; |
||||
pts[1] = pos + pEAxis[0] - pEAxis[1]; |
||||
pts[2] = pos + pEAxis[0] + pEAxis[1]; |
||||
pts[3] = pos - pEAxis[0] + pEAxis[1]; |
||||
} |
||||
if (DIMS == 3) { |
||||
const POINT pEAxis[3] = { |
||||
m_rot.row(0)*m_ext[0], |
||||
m_rot.row(1)*m_ext[1], |
||||
m_rot.row(2)*m_ext[2] |
||||
}; |
||||
const POINT pos(m_rot.transpose()*m_pos); |
||||
pts[0] = pos - pEAxis[0] - pEAxis[1] - pEAxis[2]; |
||||
pts[1] = pos - pEAxis[0] - pEAxis[1] + pEAxis[2]; |
||||
pts[2] = pos + pEAxis[0] - pEAxis[1] - pEAxis[2]; |
||||
pts[3] = pos + pEAxis[0] - pEAxis[1] + pEAxis[2]; |
||||
pts[4] = pos + pEAxis[0] + pEAxis[1] - pEAxis[2]; |
||||
pts[5] = pos + pEAxis[0] + pEAxis[1] + pEAxis[2]; |
||||
pts[6] = pos - pEAxis[0] + pEAxis[1] - pEAxis[2]; |
||||
pts[7] = pos - pEAxis[0] + pEAxis[1] + pEAxis[2]; |
||||
} |
||||
} // GetCorners |
||||
// constructs the corner of the aligned bounding box in world space |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TOBB<TYPE,DIMS>::AABB TOBB<TYPE,DIMS>::GetAABB() const |
||||
{ |
||||
POINT pts[numCorners]; |
||||
GetCorners(pts); |
||||
return AABB(pts, numCorners); |
||||
} // GetAABB |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// computes the volume of the OBB, which is a measure of |
||||
// how tight the fit is (better OBBs will have smaller volumes) |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TOBB<TYPE,DIMS>::GetVolume() const |
||||
{ |
||||
return m_ext.prod()*numCorners; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
bool TOBB<TYPE,DIMS>::Intersects(const POINT& pt) const |
||||
{ |
||||
const POINT dist(m_rot * (pt - m_pos)); |
||||
if (DIMS == 2) { |
||||
return ABS(dist[0]) <= m_ext[0] |
||||
&& ABS(dist[1]) <= m_ext[1]; |
||||
} |
||||
if (DIMS == 3) { |
||||
return ABS(dist[0]) <= m_ext[0] |
||||
&& ABS(dist[1]) <= m_ext[1] |
||||
&& ABS(dist[2]) <= m_ext[2]; |
||||
} |
||||
} // Intersects(POINT) |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,237 @@
@@ -0,0 +1,237 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Octree.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_OCTREE_H__ |
||||
#define __SEACAVE_OCTREE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "AABB.h" |
||||
#include "Ray.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
// basic octree class
|
||||
// each item should define the operator const POINT_TYPE& returning its center;
|
||||
// at build time, a functor must be supplied that returns true as long as
|
||||
// the current cell should be split farther, given its number of items and radius, ex:
|
||||
// [](Octree::IDX_TYPE size, Octree::Type radius) {
|
||||
// return size > SIZE && radius > RADIUS;
|
||||
// }
|
||||
// where:
|
||||
// SIZE is the minimum number of items contained by the cell so that this to be divided further
|
||||
// RADIUS is the minimum size of the cell allowed to be divided further
|
||||
// both conditions represent exclusive limits and both should be true for the division to take place
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE=uint32_t> |
||||
class TOctree |
||||
{ |
||||
STATIC_ASSERT(DIMS > 0 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef TYPE Type; |
||||
typedef typename ITEMARR_TYPE::Type ITEM_TYPE; |
||||
typedef typename ITEMARR_TYPE::IDX IDX_TYPE; |
||||
typedef SEACAVE::cList<IDX_TYPE,IDX_TYPE,0,1024,IDX_TYPE> IDXARR_TYPE; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT_TYPE; |
||||
typedef SEACAVE::TAABB<TYPE,DIMS> AABB_TYPE; |
||||
typedef uint32_t SIZE_TYPE; |
||||
|
||||
class CELL_TYPE { |
||||
public: |
||||
typedef struct { |
||||
POINT_TYPE center; // center of the current cell
|
||||
} NODE_TYPE; |
||||
typedef struct { |
||||
IDX_TYPE idxBegin; // index in the global array of the first item contained by this cell
|
||||
SIZE_TYPE size; // number of items contained by this cell in the global array
|
||||
DATA_TYPE data; // user data associated with this leaf
|
||||
} LEAF_TYPE; |
||||
enum { numChildren = (2<<(DIMS-1)) }; |
||||
|
||||
public: |
||||
inline CELL_TYPE(); |
||||
inline ~CELL_TYPE(); |
||||
|
||||
inline void Release(); |
||||
inline void Swap(CELL_TYPE&); |
||||
|
||||
inline unsigned ComputeChild(const POINT_TYPE& item) const; |
||||
static void ComputeCenter(POINT_TYPE []); |
||||
static inline POINT_TYPE ComputeChildCenter(const POINT_TYPE&, TYPE, unsigned); |
||||
|
||||
inline bool IsLeaf() const { return (m_child==NULL); } |
||||
inline const CELL_TYPE& GetChild(int i) const { ASSERT(!IsLeaf() && i<numChildren); return m_child[i]; } |
||||
inline const NODE_TYPE& Node() const { ASSERT(!IsLeaf()); return m_node; } |
||||
inline NODE_TYPE& Node() { ASSERT(!IsLeaf()); return m_node; } |
||||
inline const LEAF_TYPE& Leaf() const { ASSERT(IsLeaf()); return m_leaf; } |
||||
inline LEAF_TYPE& Leaf() { ASSERT(IsLeaf()); return m_leaf; } |
||||
inline const POINT_TYPE& GetCenter() const { return Node().center; } |
||||
inline AABB_TYPE GetAabb(TYPE radius) const { return AABB_TYPE(Node().center, radius); } |
||||
inline AABB_TYPE GetChildAabb(unsigned idxChild, TYPE radius) const { const TYPE childRadius(radius / TYPE(2)); return AABB_TYPE(ComputeChildCenter(Node().center, childRadius, idxChild), childRadius); } |
||||
inline IDX_TYPE GetFirstItemIdx() const { return Leaf().idxBegin; } |
||||
inline IDX_TYPE GetLastItemIdx() const { return Leaf().idxBegin + Leaf().size; } |
||||
inline SIZE_TYPE GetNumItems() const { return Leaf().size; } |
||||
inline const DATA_TYPE& GetUserData() const { return Leaf().data; } |
||||
inline DATA_TYPE& GetUserData() { return Leaf().data; } |
||||
size_t GetNumItemsHeld() const; |
||||
|
||||
public: |
||||
CELL_TYPE* m_child; // if not a leaf, 2^DIMS child objects
|
||||
union { // a LEAF_TYPE or NODE_TYPE object, if it is a leaf or not respectively
|
||||
NODE_TYPE m_node; |
||||
LEAF_TYPE m_leaf; |
||||
}; |
||||
}; |
||||
typedef SEACAVE::cList<CELL_TYPE*,CELL_TYPE*,0,256,IDX_TYPE> CELLPTRARR_TYPE; |
||||
|
||||
struct IndexInserter { |
||||
IDXARR_TYPE& indices; |
||||
IndexInserter(IDXARR_TYPE& _indices) : indices(_indices) {} |
||||
void operator()(IDX_TYPE idx) { indices.Insert(idx); } |
||||
void operator()(const IDX_TYPE* idices, SIZE_TYPE size) { indices.Join(idices, size); } |
||||
}; |
||||
|
||||
struct CellInserter { |
||||
CELLPTRARR_TYPE& cells; |
||||
CellInserter(CELLPTRARR_TYPE& _cells) : cells(_cells) {} |
||||
void operator()(CELL_TYPE& cell) { cells.Insert(&cell); } |
||||
}; |
||||
|
||||
public: |
||||
inline TOctree() {} |
||||
template <typename Functor> |
||||
inline TOctree(const ITEMARR_TYPE&, Functor split); |
||||
template <typename Functor> |
||||
inline TOctree(const ITEMARR_TYPE&, const AABB_TYPE&, Functor split); |
||||
|
||||
inline void Release(); |
||||
inline void Swap(TOctree&); |
||||
|
||||
template <typename Functor> |
||||
void Insert(const ITEMARR_TYPE&, Functor split); |
||||
template <typename Functor> |
||||
void Insert(const ITEMARR_TYPE&, const AABB_TYPE&, Functor split); |
||||
|
||||
template <typename INSERTER> |
||||
inline void CollectCells(INSERTER&) const; |
||||
template <typename INSERTER> |
||||
void CollectCells(const CELL_TYPE&, INSERTER&) const; |
||||
template <typename PARSER> |
||||
inline void ParseCells(PARSER&); |
||||
|
||||
template <typename INSERTER> |
||||
inline void Collect(INSERTER& inserter, const AABB_TYPE& aabb) const; |
||||
inline void Collect(IDXARR_TYPE& indices, const AABB_TYPE& aabb) const; |
||||
inline void Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const AABB_TYPE& aabb) const; |
||||
|
||||
template <typename INSERTER> |
||||
inline void Collect(INSERTER& inserter, const POINT_TYPE& center, TYPE radius) const; |
||||
inline void Collect(IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const; |
||||
inline void Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const; |
||||
|
||||
template <typename INSERTER, typename COLLECTOR> |
||||
inline void Collect(INSERTER& inserter, const COLLECTOR& collector) const; |
||||
template <typename COLLECTOR> |
||||
inline void Collect(IDXARR_TYPE& indices, const COLLECTOR& collector) const; |
||||
|
||||
template <typename FTYPE, int FDIMS, typename INSERTER> |
||||
inline void Traverse(const TFrustum<FTYPE,FDIMS>&, INSERTER&) const; |
||||
template <typename FTYPE, int FDIMS> |
||||
inline void Traverse(const TFrustum<FTYPE,FDIMS>&, IDXARR_TYPE&) const; |
||||
template <typename FTYPE, int FDIMS, typename PARSER> |
||||
inline void TraverseCells(const TFrustum<FTYPE,FDIMS>&, PARSER&); |
||||
template <typename FTYPE, int FDIMS> |
||||
inline void TraverseCells(const TFrustum<FTYPE,FDIMS>&, CELLPTRARR_TYPE&); |
||||
|
||||
template <typename AREAESTIMATOR, typename CHUNKINSERTER> |
||||
void SplitVolume(float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter); |
||||
|
||||
inline const CELL_TYPE& GetRoot() const { return m_root; } |
||||
inline TYPE GetRadius() const { return m_radius; } |
||||
inline AABB_TYPE GetAabb() const { return m_root.GetAabb(m_radius); } |
||||
inline bool IsEmpty() const { return m_indices.empty(); } |
||||
inline size_t GetNumItems() const { return m_indices.size(); } |
||||
inline const IDXARR_TYPE& GetIndexArr() const { return m_indices; } |
||||
inline const ITEM_TYPE* GetItems() const { return m_items; } |
||||
inline void ResetItems() { m_items = NULL; } |
||||
|
||||
protected: |
||||
template <typename Functor> |
||||
struct _InsertData { |
||||
enum : IDX_TYPE { NO_INDEX = DECLARE_NO_INDEX(IDX_TYPE) }; |
||||
IDXARR_TYPE successors; // single connected list of next item indices
|
||||
Functor split; // used to decide if a cell needs to be split farther
|
||||
}; |
||||
template <typename Functor, bool bForceSplit> |
||||
void _Insert(CELL_TYPE&, const POINT_TYPE& center, TYPE radius, IDX_TYPE start, IDX_TYPE size, _InsertData<Functor>&); |
||||
|
||||
template <typename PARSER> |
||||
void _ParseCells(CELL_TYPE&, TYPE, PARSER&); |
||||
|
||||
template <typename INSERTER> |
||||
void _Collect(const CELL_TYPE&, const AABB_TYPE&, INSERTER&) const; |
||||
template <typename INSERTER, typename COLLECTOR> |
||||
void _Collect(const CELL_TYPE&, TYPE, const COLLECTOR&, INSERTER&) const; |
||||
|
||||
template <typename FTYPE, int FDIMS, typename INSERTER> |
||||
void _Traverse(const CELL_TYPE&, TYPE, const TFrustum<FTYPE,FDIMS>&, INSERTER&) const; |
||||
template <typename FTYPE, int FDIMS, typename PARSER> |
||||
void _TraverseCells(CELL_TYPE&, TYPE, const TFrustum<FTYPE,FDIMS>&, PARSER&); |
||||
|
||||
template <typename AREAESTIMATOR, typename CHUNKINSERTER> |
||||
void _SplitVolume(const CELL_TYPE& parentCell, TYPE parentRadius, unsigned idxChild, float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter, const UnsignedArr& indices=UnsignedArr{0,1,2,3,4,5,6,7}); |
||||
|
||||
protected: |
||||
const ITEM_TYPE* m_items; // original input items (the only condition is that every item to resolve to a position)
|
||||
IDXARR_TYPE m_indices; // indices to input items re-arranged spatially (as dictated by the octree)
|
||||
CELL_TYPE m_root; // first cell of the tree (always of Node type)
|
||||
TYPE m_radius; // size of the sphere containing all cells
|
||||
|
||||
public: |
||||
typedef struct DEBUGINFO_TYPE { |
||||
size_t memSize; // total memory used
|
||||
size_t memStruct; // memory used for the tree structure
|
||||
size_t memItems; // memory used for the contained items
|
||||
size_t numItems; // number of contained items
|
||||
size_t numNodes; // total nodes...
|
||||
size_t numLeaves; // ... from which this number of leaves
|
||||
size_t minDepth; // minimum tree depth
|
||||
size_t maxDepth; // maximum tree depth
|
||||
float avgDepth; // average tree depth
|
||||
void Init() { memset(this, 0, sizeof(DEBUGINFO_TYPE)); } |
||||
void operator += (const DEBUGINFO_TYPE& r) { |
||||
avgDepth = avgDepth*numNodes + r.avgDepth*r.numNodes; |
||||
memSize += r.memSize; memStruct += r.memStruct; memItems += r.memItems; |
||||
numItems += r.numItems; numNodes += r.numNodes; numLeaves += r.numLeaves; |
||||
if (minDepth > r.minDepth) minDepth = r.minDepth; |
||||
if (maxDepth < r.maxDepth) maxDepth = r.maxDepth; |
||||
avgDepth /= numNodes; |
||||
} |
||||
} DEBUGINFO; |
||||
|
||||
void GetDebugInfo(DEBUGINFO* =NULL, bool bPrintStats=false) const; |
||||
static void LogDebugInfo(const DEBUGINFO&); |
||||
|
||||
protected: |
||||
void _GetDebugInfo(const CELL_TYPE&, unsigned, DEBUGINFO&) const; |
||||
}; // class TOctree
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
#include "Octree.inl" |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_OCTREE_H__
|
||||
@ -0,0 +1,851 @@
@@ -0,0 +1,851 @@
|
||||
//////////////////////////////////////////////////////////////////// |
||||
// Octree.inl |
||||
// |
||||
// Copyright 2007 cDc@seacave |
||||
// Distributed under the Boost Software License, Version 1.0 |
||||
// (See http://www.boost.org/LICENSE_1_0.txt) |
||||
|
||||
|
||||
// D E F I N E S /////////////////////////////////////////////////// |
||||
|
||||
#ifdef _USE_OPENMP |
||||
// minimum number of polygons for which we do multi-threading |
||||
#define OCTREE_MIN_ITEMS_MINTHREAD 1024*2 |
||||
#endif |
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::CELL_TYPE() |
||||
: |
||||
m_child(NULL) |
||||
{ |
||||
} // constructor |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::~CELL_TYPE() |
||||
{ |
||||
delete[] m_child; |
||||
} // destructor |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::Release() |
||||
{ |
||||
delete[] m_child; |
||||
m_child = NULL; |
||||
} // Release |
||||
// swap the two octrees |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::Swap(CELL_TYPE& rhs) |
||||
{ |
||||
std::swap(m_child, rhs.m_child); |
||||
if (IsLeaf()) |
||||
std::swap(m_leaf, rhs.m_leaf); |
||||
else |
||||
std::swap(m_node, rhs.m_node); |
||||
} // Swap |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// compute item's index corresponding to the containing cell |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline unsigned TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::ComputeChild(const POINT_TYPE& item) const |
||||
{ |
||||
ASSERT(!IsLeaf()); |
||||
unsigned idx = 0; |
||||
if (item[0] >= Node().center[0]) |
||||
idx |= (1<<0); |
||||
if (DIMS > 1) |
||||
if (item[1] >= Node().center[1]) |
||||
idx |= (1<<1); |
||||
if (DIMS > 2) |
||||
if (item[2] >= Node().center[2]) |
||||
idx |= (1<<2); |
||||
return idx; |
||||
} // ComputeChild |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::ComputeCenter(POINT_TYPE centers[]) |
||||
{ |
||||
if (DIMS == 1) { |
||||
centers[0] << -1; |
||||
centers[1] << 1; |
||||
} else |
||||
if (DIMS == 2) { |
||||
centers[0] << -1,-1; |
||||
centers[1] << 1,-1; |
||||
centers[2] << -1, 1; |
||||
centers[3] << 1, 1; |
||||
} else |
||||
if (DIMS == 3) { |
||||
centers[0] << -1,-1,-1; |
||||
centers[1] << 1,-1,-1; |
||||
centers[2] << -1, 1,-1; |
||||
centers[3] << 1, 1,-1; |
||||
centers[4] << -1,-1, 1; |
||||
centers[5] << 1,-1, 1; |
||||
centers[6] << -1, 1, 1; |
||||
centers[7] << 1, 1, 1; |
||||
} |
||||
} // ComputeCenter |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline typename TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::POINT_TYPE TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::ComputeChildCenter(const POINT_TYPE& center, TYPE radius, unsigned idxChild) |
||||
{ |
||||
struct CENTERARR_TYPE { |
||||
POINT_TYPE child[CELL_TYPE::numChildren]; |
||||
inline CENTERARR_TYPE() { CELL_TYPE::ComputeCenter(child); } |
||||
}; |
||||
static const CENTERARR_TYPE centers; |
||||
return center + centers.child[idxChild] * radius; |
||||
} // ComputeChildCenter |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// count the number of items contained by the given octree-cell |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
size_t TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::GetNumItemsHeld() const |
||||
{ |
||||
if (IsLeaf()) |
||||
return GetNumItems(); |
||||
size_t numItems = 0; |
||||
for (int i=0; i<numChildren; ++i) |
||||
numItems += GetChild(i).GetNumItemsHeld(); |
||||
return numItems; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
// build tree with the given items |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename Functor> |
||||
inline TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::TOctree(const ITEMARR_TYPE& items, Functor split) |
||||
{ |
||||
Insert(items, split); |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename Functor> |
||||
inline TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::TOctree(const ITEMARR_TYPE& items, const AABB_TYPE& aabb, Functor split) |
||||
{ |
||||
Insert(items, aabb, split); |
||||
} // constructor |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// destroy tree |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Release() |
||||
{ |
||||
m_indices.Release(); |
||||
m_root.Release(); |
||||
} // Release |
||||
// swap the two octrees |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Swap(TOctree& rhs) |
||||
{ |
||||
std::swap(m_items, rhs.m_items); |
||||
m_indices.Swap(rhs.m_indices); |
||||
m_root.Swap(rhs.m_root); |
||||
std::swap(m_radius, rhs.m_radius); |
||||
} // Swap |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// add the given item to the tree |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename Functor, bool bForceSplit> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_Insert(CELL_TYPE& cell, const POINT_TYPE& center, TYPE radius, IDX_TYPE start, IDX_TYPE size, _InsertData<Functor>& insertData) |
||||
{ |
||||
ASSERT(size > 0); |
||||
// if this child cell needs to be divided further |
||||
if (bForceSplit || insertData.split(size, radius)) { |
||||
// init node and proceed recursively |
||||
ASSERT(cell.m_child == NULL); |
||||
cell.m_child = new CELL_TYPE[CELL_TYPE::numChildren]; |
||||
cell.Node().center = center; |
||||
struct ChildData { |
||||
enum { ESTART=0, EEND=CELL_TYPE::numChildren, ESIZE=CELL_TYPE::numChildren*2, EALL=CELL_TYPE::numChildren*3}; |
||||
IDX_TYPE data[EALL]; |
||||
ChildData() { memset(data, 0, sizeof(IDX_TYPE)*EALL); } |
||||
inline IDX_TYPE Start(unsigned i) const { return data[ESTART+i]; } |
||||
inline IDX_TYPE& Start(unsigned i) { return data[ESTART+i]; } |
||||
inline IDX_TYPE End(unsigned i) const { return data[EEND+i]; } |
||||
inline IDX_TYPE& End(unsigned i) { return data[EEND+i]; } |
||||
inline IDX_TYPE Size(unsigned i) const { return data[ESIZE+i]; } |
||||
inline IDX_TYPE& Size(unsigned i) { return data[ESIZE+i]; } |
||||
} childD; |
||||
IDX_TYPE idx(start); |
||||
for (IDX_TYPE i=0; i<size; ++i) { |
||||
const unsigned idxChild(cell.ComputeChild(m_items[idx])); |
||||
if (childD.Size(idxChild) == 0) |
||||
childD.Start(idxChild) = idx; |
||||
else |
||||
insertData.successors[childD.End(idxChild)] = idx; |
||||
childD.End(idxChild) = idx; |
||||
++childD.Size(idxChild); |
||||
idx = insertData.successors[idx]; |
||||
} |
||||
ASSERT(idx == _InsertData<Functor>::NO_INDEX); |
||||
const TYPE childRadius(radius / TYPE(2)); |
||||
for (unsigned i=0; i<CELL_TYPE::numChildren; ++i) { |
||||
CELL_TYPE& child = cell.m_child[i]; |
||||
if (childD.Size(i) == 0) { |
||||
child.Leaf().idxBegin = m_indices.size(); |
||||
child.Leaf().size = 0; |
||||
continue; |
||||
} |
||||
insertData.successors[childD.End(i)] = _InsertData<Functor>::NO_INDEX; // mark the end of child successors |
||||
const POINT_TYPE childCenter(CELL_TYPE::ComputeChildCenter(center, childRadius, i)); |
||||
_Insert<Functor,false>(child, childCenter, childRadius, childD.Start(i), childD.Size(i), insertData); |
||||
} |
||||
} else { |
||||
// init leaf |
||||
cell.Leaf().idxBegin = m_indices.size(); |
||||
cell.Leaf().size = (SIZE_TYPE)size; |
||||
for (IDX_TYPE idx=start; idx!=_InsertData<Functor>::NO_INDEX; idx=insertData.successors[idx]) |
||||
m_indices.push_back(idx); |
||||
} |
||||
} // _Insert |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename Functor> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Insert(const ITEMARR_TYPE& items, const AABB_TYPE& aabb, Functor split) |
||||
{ |
||||
Release(); |
||||
m_items = items.data(); |
||||
// create root as node, even if we do not need to divide |
||||
m_indices.Reserve(items.size()); |
||||
// divide cell |
||||
const POINT_TYPE center = aabb.GetCenter(); |
||||
m_radius = aabb.GetSize().maxCoeff()/Type(2); |
||||
// single connected list of next item indices |
||||
_InsertData<Functor> insertData = {items.size(), split}; |
||||
std::iota(insertData.successors.begin(), insertData.successors.end(), IDX_TYPE(1)); |
||||
insertData.successors.back() = _InsertData<Functor>::NO_INDEX; |
||||
// setup each cell |
||||
_Insert<Functor,true>(m_root, center, m_radius, 0, items.size(), insertData); |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename Functor> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Insert(const ITEMARR_TYPE& items, Functor split) |
||||
{ |
||||
ASSERT(!items.IsEmpty()); |
||||
ASSERT(sizeof(POINT_TYPE) == sizeof(typename ITEMARR_TYPE::Type)); |
||||
AABB_TYPE aabb((const POINT_TYPE*)items.data(), items.size()); |
||||
aabb.Enlarge(ZEROTOLERANCE<TYPE>()*TYPE(10)); |
||||
Insert(items, aabb, split); |
||||
} // Insert |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename INSERTER> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CollectCells(const CELL_TYPE& cell, INSERTER& inserter) const |
||||
{ |
||||
if (cell.IsLeaf()) { |
||||
inserter(m_indices.data()+cell.GetFirstItemIdx(), cell.GetNumItems()); |
||||
return; |
||||
} |
||||
for (int i=0; i<CELL_TYPE::numChildren; ++i) |
||||
CollectCells(cell.m_child[i], inserter); |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename PARSER> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_ParseCells(CELL_TYPE& cell, TYPE radius, PARSER& parser) |
||||
{ |
||||
if (cell.IsLeaf()) { |
||||
parser(cell, radius); |
||||
return; |
||||
} |
||||
const TYPE childRadius = radius / TYPE(2); |
||||
for (int i=0; i<CELL_TYPE::numChildren; ++i) |
||||
_ParseCells(cell.m_child[i], childRadius, parser); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// calls parser for each leaf of the octree (the IDX_TYPE operator has to be defined) |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename INSERTER> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CollectCells(INSERTER& inserter) const |
||||
{ |
||||
CollectCells(m_root, inserter); |
||||
} |
||||
// calls parser for each leaf of the octree (the CELL_TYPE operator has to be defined) |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename PARSER> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::ParseCells(PARSER& parser) |
||||
{ |
||||
_ParseCells(m_root, m_radius, parser); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// find all items contained by the given bounding box |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename INSERTER> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_Collect(const CELL_TYPE& cell, const AABB_TYPE& aabb, INSERTER& inserter) const |
||||
{ |
||||
if (cell.IsLeaf()) { |
||||
// add all items contained by the bounding-box |
||||
for (IDX_TYPE i=0; i<cell.Leaf().size; ++i) { |
||||
const IDX_TYPE idx = m_indices[cell.Leaf().idxBegin + i]; |
||||
if (aabb.Intersects(m_items[idx])) |
||||
inserter(idx); |
||||
} |
||||
return; |
||||
} |
||||
// find the cells containing the box extremes |
||||
const unsigned idxMin = cell.ComputeChild(aabb.ptMin); |
||||
const unsigned idxMax = cell.ComputeChild(aabb.ptMax); |
||||
// if both extremes are in the same cell |
||||
if (idxMin == idxMax) { |
||||
// all searched items are inside a single child |
||||
return _Collect(cell.m_child[idxMin], aabb, inserter); |
||||
} |
||||
// divide the bounding-box in 2^DIMS boxes split by the cell center |
||||
AABB_TYPE aabbs[CELL_TYPE::numChildren]; |
||||
unsigned first; |
||||
unsigned n = aabb.SplitBy(cell.Node().center, aabbs, first); |
||||
if (n == 0) { |
||||
// the aabb has one of the sides right on the cell division line |
||||
#if 0 |
||||
// so find the cell containing the aabb center and add its items |
||||
const unsigned idx = cell.ComputeChild(aabb.GetCenter()); |
||||
_Collect(cell.m_child[idx], aabb, inserter); |
||||
#else |
||||
// so collect both intersecting cells |
||||
// (seems to work better for points right on the border) |
||||
_Collect(cell.m_child[idxMin], aabb, inserter); |
||||
_Collect(cell.m_child[idxMax], aabb, inserter); |
||||
#endif |
||||
return; |
||||
} |
||||
// collect each cell |
||||
for (unsigned i=0; i<n; ++i) { |
||||
// all searched items are inside a single child |
||||
const AABB_TYPE& aabbChild = aabbs[(first+i)%CELL_TYPE::numChildren]; |
||||
const unsigned idx = cell.ComputeChild(aabbChild.ptMin); |
||||
_Collect(cell.m_child[idx], aabbChild, inserter); |
||||
} |
||||
} // Collect |
||||
// find all items contained by the cells intersected by the given line |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename INSERTER, typename COLLECTOR> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_Collect(const CELL_TYPE& cell, TYPE radius, const COLLECTOR& collector, INSERTER& inserter) const |
||||
{ |
||||
ASSERT(!cell.IsLeaf()); |
||||
const TYPE childRadius = radius / TYPE(2); |
||||
for (int i=0; i<CELL_TYPE::numChildren; ++i) { |
||||
const CELL_TYPE& childCell = cell.m_child[i]; |
||||
if (childCell.IsLeaf()) { |
||||
if (collector.Intersects(CELL_TYPE::ComputeChildCenter(cell.GetCenter(), childRadius, i), childRadius)) |
||||
inserter(m_indices.data()+childCell.GetFirstItemIdx(), childCell.GetNumItems()); |
||||
} else { |
||||
if (collector.Intersects(childCell.Node().center, childRadius)) |
||||
_Collect(childCell, childRadius, collector, inserter); |
||||
} |
||||
} |
||||
} // Collect |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename INSERTER> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(INSERTER& inserter, const AABB_TYPE& aabb) const |
||||
{ |
||||
_Collect(m_root, aabb, inserter); |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDXARR_TYPE& indices, const AABB_TYPE& aabb) const |
||||
{ |
||||
IndexInserter inserter(indices); |
||||
_Collect(m_root, aabb, inserter); |
||||
} |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename INSERTER> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(INSERTER& inserter, const POINT_TYPE& center, TYPE radius) const |
||||
{ |
||||
_Collect(m_root, AABB_TYPE(center, radius), inserter); |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const |
||||
{ |
||||
IndexInserter inserter(indices); |
||||
_Collect(m_root, AABB_TYPE(center, radius), inserter); |
||||
} |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename INSERTER, typename COLLECTOR> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(INSERTER& inserter, const COLLECTOR& collector) const |
||||
{ |
||||
_Collect(m_root, m_radius, collector, inserter); |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename COLLECTOR> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDXARR_TYPE& indices, const COLLECTOR& collector) const |
||||
{ |
||||
IndexInserter inserter(indices); |
||||
_Collect(m_root, m_radius, collector, inserter); |
||||
} |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const AABB_TYPE& aabb) const |
||||
{ |
||||
_Collect(m_root, aabb, IndexInserter(indices)); |
||||
if (indices.size() > maxNeighbors) { |
||||
// keep only the closest neighbors |
||||
typedef TIndexScore<IDX_TYPE,TYPE> ItemIndexScore; |
||||
typedef cList<ItemIndexScore, const ItemIndexScore&, 0> ItemIndexScoreArr; |
||||
ItemIndexScoreArr indexscores(indices.size()); |
||||
const POINT_TYPE center(aabb.GetCenter()); |
||||
FOREACH(i, indices) { |
||||
const IDX_TYPE& idx = indices[i]; |
||||
const TYPE score(-(center-m_items[idx]).squaredNorm()); |
||||
indexscores[i] = ItemIndexScore(idx,score); |
||||
} |
||||
indices.Empty(); |
||||
indexscores.Sort(); |
||||
for (IDX_TYPE i=0; i<maxNeighbors; ++i) |
||||
indices.Insert(indexscores[i].idx); |
||||
} |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const |
||||
{ |
||||
Collect(maxNeighbors, indices, AABB_TYPE(center, radius)); |
||||
} // Collect |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// walk through the tree and collect visible indices |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename FTYPE, int FDIMS, typename INSERTER> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_Traverse(const CELL_TYPE& cell, TYPE radius, const TFrustum<FTYPE,FDIMS>& frustum, INSERTER& inserter) const |
||||
{ |
||||
ASSERT(!cell.IsLeaf()); |
||||
switch (frustum.Classify(cell.GetAabb(radius))) { |
||||
case CLIPPED: { |
||||
const TYPE childRadius = radius / TYPE(2); |
||||
for (int i=0; i<CELL_TYPE::numChildren; ++i) { |
||||
const CELL_TYPE& childCell = cell.m_child[i]; |
||||
if (childCell.IsLeaf()) { |
||||
const AABB_TYPE childAabb(CELL_TYPE::ComputeChildCenter(cell.GetCenter(), childRadius, i), childRadius); |
||||
if (frustum.Classify(childAabb) != CULLED) |
||||
inserter(m_indices.data()+childCell.GetFirstItemIdx(), childCell.GetNumItems()); |
||||
} else { |
||||
_Traverse(childCell, childRadius, frustum, inserter); |
||||
} |
||||
} |
||||
break; } |
||||
case VISIBLE: { |
||||
for (int i=0; i<CELL_TYPE::numChildren; ++i) |
||||
CollectCells(cell.m_child[i], inserter); |
||||
break; } |
||||
} |
||||
} |
||||
// walk through the tree and collect visible leafs |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename FTYPE, int FDIMS, typename PARSER> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_TraverseCells(CELL_TYPE& cell, TYPE radius, const TFrustum<FTYPE,FDIMS>& frustum, PARSER& parser) |
||||
{ |
||||
ASSERT(!cell.IsLeaf()); |
||||
switch (frustum.Classify(cell.GetAabb(radius))) { |
||||
case CLIPPED: { |
||||
const TYPE childRadius = radius / TYPE(2); |
||||
for (int i=0; i<CELL_TYPE::numChildren; ++i) { |
||||
const CELL_TYPE& childCell = cell.m_child[i]; |
||||
if (childCell.IsLeaf()) { |
||||
const AABB_TYPE childAabb(CELL_TYPE::ComputeChildCenter(cell.GetCenter(), childRadius, i), childRadius); |
||||
if (frustum.Classify(childAabb) != CULLED) |
||||
parser(childCell); |
||||
} else { |
||||
_TraverseCells(childCell, childRadius, frustum, parser); |
||||
} |
||||
} |
||||
break; } |
||||
case VISIBLE: { |
||||
for (int i=0; i<CELL_TYPE::numChildren; ++i) |
||||
_ParseCells(cell.m_child[i], parser); |
||||
break; } |
||||
} |
||||
} |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename FTYPE, int FDIMS, typename INSERTER> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Traverse(const TFrustum<FTYPE,FDIMS>& frustum, INSERTER& inserter) const |
||||
{ |
||||
_Traverse(m_root, m_radius, frustum, inserter); |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename FTYPE, int FDIMS> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Traverse(const TFrustum<FTYPE,FDIMS>& frustum, IDXARR_TYPE& indices) const |
||||
{ |
||||
_Traverse(m_root, m_radius, frustum, IndexInserter(indices)); |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename FTYPE, int FDIMS, typename PARSER> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::TraverseCells(const TFrustum<FTYPE,FDIMS>& frustum, PARSER& parser) |
||||
{ |
||||
_TraverseCells(m_root, m_radius, frustum, parser); |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename FTYPE, int FDIMS> |
||||
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::TraverseCells(const TFrustum<FTYPE,FDIMS>& frustum, CELLPTRARR_TYPE& leaves) |
||||
{ |
||||
_TraverseCells(m_root, m_radius, frustum, CellInserter(leaves)); |
||||
} // Traverse |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename AREAESTIMATOR, typename CHUNKINSERTER> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_SplitVolume(const CELL_TYPE& parentCell, TYPE parentRadius, unsigned idxChild, float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter, const UnsignedArr& indices) |
||||
{ |
||||
ASSERT(!indices.empty()); |
||||
typedef std::pair<UnsignedArr,UnsignedArr> PairIndices; |
||||
struct GenerateSamples { |
||||
const UnsignedArr& indices; |
||||
const unsigned numSamples; |
||||
const unsigned halfSamples; |
||||
const unsigned numCommonAxis; |
||||
POINT_TYPE centers[8]; |
||||
cList<PairIndices> arrHalfIndices; |
||||
GenerateSamples(const UnsignedArr& _indices) |
||||
: indices(_indices), numSamples((unsigned)indices.size()), halfSamples(numSamples/2), numCommonAxis(halfSamples==4?1:2), arrHalfIndices(0, numSamples) |
||||
{ |
||||
ASSERT(indices.size()%2 == 0 && indices.IsSorted()); |
||||
ASSERT(halfSamples == 4 || halfSamples == 2); |
||||
CELL_TYPE::ComputeCenter(centers); |
||||
UnsignedArr samples(halfSamples); |
||||
for (unsigned hs=0; hs<halfSamples; ++hs) { |
||||
samples[0] = hs; |
||||
GenerateIndices(1, samples); |
||||
} |
||||
} |
||||
void GenerateIndices(unsigned idxSample, UnsignedArr& samples) { |
||||
if (idxSample == samples.size()) { |
||||
InsertIndices(samples); |
||||
return; |
||||
} |
||||
for (unsigned i=samples[idxSample-1]+1; i<numSamples; ++i) { |
||||
samples[idxSample] = i; |
||||
GenerateIndices(idxSample+1, samples); |
||||
} |
||||
} |
||||
void InsertIndices(const UnsignedArr& samples) { |
||||
UnsignedArr halfIndices(halfSamples); |
||||
for (unsigned s=0; s<halfSamples; ++s) |
||||
halfIndices[s] = indices[samples[s]]; |
||||
// check all samples have one/two common axis |
||||
unsigned commonAxis(0); |
||||
for (unsigned a=0; a<3; ++a) { |
||||
unsigned na(1); |
||||
for (unsigned s=1; s<halfSamples; ++s) { |
||||
if (centers[halfIndices[0]][a] == centers[halfIndices[s]][a]) |
||||
++na; |
||||
} |
||||
if (na == halfSamples && ++commonAxis == numCommonAxis) |
||||
goto CommonAxis; |
||||
} |
||||
return; |
||||
CommonAxis: |
||||
// check not complementary samples |
||||
for (const PairIndices& pairHalfIndices: arrHalfIndices) { |
||||
unsigned nCommon(0); |
||||
for (unsigned s=0; s<halfSamples; ++s) |
||||
if (halfIndices[s] == pairHalfIndices.second[s]) |
||||
++nCommon; |
||||
if (nCommon == halfSamples) |
||||
return; |
||||
} |
||||
// generate complementary indices |
||||
ASSERT(halfIndices.IsSorted()); |
||||
UnsignedArr compHalfIndices(0, halfSamples); |
||||
unsigned i(0); |
||||
for (unsigned idx: indices) { |
||||
if (i < halfSamples && idx == halfIndices[i]) |
||||
++i; |
||||
else |
||||
compHalfIndices.push_back(idx); |
||||
} |
||||
ASSERT(compHalfIndices.size() == halfSamples); |
||||
ASSERT(compHalfIndices.IsSorted()); |
||||
arrHalfIndices.emplace_back(std::move(halfIndices), std::move(compHalfIndices)); |
||||
} |
||||
}; |
||||
const CELL_TYPE& cell(parentCell.GetChild(idxChild)); |
||||
const TYPE radius(parentRadius / TYPE(2)); |
||||
// handle particular cases |
||||
if (cell.IsLeaf()) { |
||||
chunkInserter(parentCell, parentRadius, UnsignedArr{idxChild}); |
||||
return; |
||||
} |
||||
if (indices.size() == 1) { |
||||
_SplitVolume(cell, radius, indices.front(), maxArea, areaEstimator, chunkInserter); |
||||
return; |
||||
} |
||||
if (indices.size() == 2) { |
||||
_SplitVolume(cell, radius, indices.front(), maxArea, areaEstimator, chunkInserter); |
||||
_SplitVolume(cell, radius, indices.back(), maxArea, areaEstimator, chunkInserter); |
||||
return; |
||||
} |
||||
// measure surface area for each child |
||||
float childArea[8], totalArea(0); |
||||
for (unsigned c=0; c<8; ++c) { |
||||
CollectCells(cell.GetChild(c), areaEstimator); |
||||
totalArea += childArea[c] = areaEstimator.PopArea(); |
||||
} |
||||
if (totalArea < maxArea*1.01f) { |
||||
chunkInserter(parentCell, parentRadius, UnsignedArr{idxChild}); |
||||
return; |
||||
} |
||||
// check if all parts are over the limit |
||||
unsigned numOverAreas(0); |
||||
for (unsigned c: indices) |
||||
if (childArea[c] == 0 || childArea[c] >= maxArea) |
||||
++numOverAreas; |
||||
if (numOverAreas == indices.size()) { |
||||
for (unsigned c: indices) |
||||
if (childArea[c] > 0) |
||||
_SplitVolume(cell, radius, c, maxArea, areaEstimator, chunkInserter); |
||||
return; |
||||
} |
||||
// split mesh children and retain the components with surface smaller than the given area |
||||
const cList<PairIndices> halfIndices(std::move(GenerateSamples(indices).arrHalfIndices)); |
||||
IDX bestSplit(NO_ID); |
||||
float bestArea(0); |
||||
Point2f bestAs; |
||||
FOREACH(idx, halfIndices) { |
||||
const PairIndices& pairHalfIndices = halfIndices[idx]; |
||||
ASSERT(pairHalfIndices.first.size() == pairHalfIndices.second.size()); |
||||
Point2f as(Point2f::ZERO); |
||||
for (unsigned i=0; i<pairHalfIndices.first.size(); ++i) { |
||||
as[0] += childArea[pairHalfIndices.first[i]]; |
||||
as[1] += childArea[pairHalfIndices.second[i]]; |
||||
} |
||||
for (unsigned i=0; i<2; ++i) { |
||||
if (as[i] < maxArea && bestArea < as[i]) { |
||||
bestArea = as[i]; |
||||
bestAs = as; |
||||
bestSplit = idx; |
||||
} |
||||
} |
||||
} |
||||
if (bestSplit != NO_ID) { |
||||
// store found clusters |
||||
const PairIndices& pairHalfIndices = halfIndices[bestSplit]; |
||||
if (bestAs[0] < maxArea) |
||||
chunkInserter(cell, radius, pairHalfIndices.first); |
||||
else |
||||
_SplitVolume(parentCell, parentRadius, idxChild, maxArea, areaEstimator, chunkInserter, pairHalfIndices.first); |
||||
if (bestAs[1] < maxArea) |
||||
chunkInserter(cell, radius, pairHalfIndices.second); |
||||
else |
||||
_SplitVolume(parentCell, parentRadius, idxChild, maxArea, areaEstimator, chunkInserter, pairHalfIndices.second); |
||||
return; |
||||
} |
||||
// farther split each half into quarters |
||||
if (halfIndices.front().first.size() == 4) { |
||||
UnsignedArr bestQIndices[4]; |
||||
float bestArea(0); |
||||
Eigen::Vector4f bestAs; |
||||
for (const PairIndices& pairHalfIndices: halfIndices) { |
||||
const cList<PairIndices> qIndicesFirst(std::move(GenerateSamples(pairHalfIndices.first).arrHalfIndices)); |
||||
const cList<PairIndices> qIndicesSecond(std::move(GenerateSamples(pairHalfIndices.second).arrHalfIndices)); |
||||
ASSERT(qIndicesFirst.size() == qIndicesSecond.size()); |
||||
FOREACH(q, qIndicesFirst) { |
||||
const PairIndices& qFirst = qIndicesFirst[q]; |
||||
const PairIndices& qSecond = qIndicesSecond[q]; |
||||
Eigen::Vector4f as(Eigen::Vector4f::Zero()); |
||||
for (unsigned i=0; i<qFirst.first.size(); ++i) { |
||||
as[0] += childArea[qFirst.first[i]]; |
||||
as[1] += childArea[qFirst.second[i]]; |
||||
as[2] += childArea[qSecond.first[i]]; |
||||
as[3] += childArea[qSecond.second[i]]; |
||||
} |
||||
float area(0); |
||||
for (unsigned i=0; i<4; ++i) { |
||||
if (as[i] < maxArea) |
||||
area += as[i]; |
||||
} |
||||
if (bestArea < area) { |
||||
bestArea = area; |
||||
bestAs = as; |
||||
bestQIndices[0] = qFirst.first; |
||||
bestQIndices[1] = qFirst.second; |
||||
bestQIndices[2] = qSecond.first; |
||||
bestQIndices[3] = qSecond.second; |
||||
} |
||||
} |
||||
} |
||||
if (bestArea > 0) { |
||||
// store found clusters |
||||
for (unsigned i=0; i<4; ++i) { |
||||
if (bestAs[i] < maxArea) { |
||||
chunkInserter(cell, radius, bestQIndices[i]); |
||||
} else { |
||||
_SplitVolume(cell, radius, bestQIndices[i][0], maxArea, areaEstimator, chunkInserter); |
||||
_SplitVolume(cell, radius, bestQIndices[i][1], maxArea, areaEstimator, chunkInserter); |
||||
} |
||||
} |
||||
return; |
||||
} |
||||
} |
||||
// split each child |
||||
for (unsigned c: indices) { |
||||
if (childArea[c] == 0) |
||||
continue; |
||||
if (childArea[c] < maxArea) |
||||
chunkInserter(cell, radius, UnsignedArr{c}); |
||||
else |
||||
_SplitVolume(cell, radius, c, maxArea, areaEstimator, chunkInserter); |
||||
} |
||||
} |
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
template <typename AREAESTIMATOR, typename CHUNKINSERTER> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::SplitVolume(float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter) |
||||
{ |
||||
CELL_TYPE parent; |
||||
parent.m_child = new CELL_TYPE[1]; |
||||
parent.m_child[0].m_child = m_root.m_child; |
||||
parent.m_child[0].Node() = m_root.Node(); |
||||
parent.Node().center = m_root.Node().center + POINT_TYPE::Constant(m_radius); |
||||
_SplitVolume(parent, m_radius*TYPE(2), 0, maxArea, areaEstimator, chunkInserter); |
||||
parent.m_child[0].m_child = NULL; |
||||
} // SplitVolume |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_GetDebugInfo(const CELL_TYPE& cell, unsigned nDepth, DEBUGINFO& info) const |
||||
{ |
||||
if (cell.IsLeaf()) { |
||||
if (info.minDepth > nDepth) |
||||
info.minDepth = nDepth; |
||||
if (info.maxDepth < nDepth) |
||||
info.maxDepth = nDepth; |
||||
info.avgDepth += nDepth; |
||||
info.numLeaves++; |
||||
return; |
||||
} |
||||
nDepth++; |
||||
info.numNodes++; |
||||
for (int i=0; i<CELL_TYPE::numChildren; ++i) |
||||
_GetDebugInfo(cell.m_child[i], nDepth, info); |
||||
} |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::GetDebugInfo(DEBUGINFO* pInfo, bool bPrintStats) const |
||||
{ |
||||
DEBUGINFO localInfo; |
||||
DEBUGINFO& info = (pInfo ? *pInfo : localInfo); |
||||
info.Init(); |
||||
_GetDebugInfo(m_root, 0, info); |
||||
info.avgDepth /= info.numLeaves; |
||||
info.numItems = GetNumItems(); |
||||
info.numNodes += info.numLeaves; |
||||
info.memStruct = info.numNodes*sizeof(CELL_TYPE) + sizeof(TOctree); |
||||
info.memItems = sizeof(IDX_TYPE)*info.numItems; |
||||
info.memSize = info.memStruct + info.memItems; |
||||
if (pInfo == NULL || bPrintStats) |
||||
LogDebugInfo(info); |
||||
} // GetDebugInfo |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE> |
||||
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::LogDebugInfo(const DEBUGINFO& info) |
||||
{ |
||||
//VERBOSE("NoItems: %d; Mem %s; MemItems %s; MemStruct %s; AvgMemStruct %.2f%%%%; NoNodes %d; NoLeaf %d; AvgLeaf %.2f%%%%; AvgDepth %.2f; MinDepth %d; MaxDepth %d", |
||||
VERBOSE("NumItems %d; Mem %s (%s items, %s struct - %.2f%%%%); NumNodes %d (leaves %d - %.2f%%%%); Depth %.2f (%d min, %d max)", |
||||
info.numItems, |
||||
Util::formatBytes(info.memSize).c_str(), Util::formatBytes(info.memItems).c_str(), Util::formatBytes(info.memStruct).c_str(), double(info.memStruct)*100.0/info.memSize, |
||||
info.numNodes, info.numLeaves, float(info.numLeaves*100)/info.numNodes, |
||||
info.avgDepth, info.minDepth, info.maxDepth); |
||||
} // LogDebugInfo |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// if everything works fine, this function should return true |
||||
template <typename TYPE, int DIMS> |
||||
inline bool OctreeTest(unsigned iters, unsigned maxItems=1000, bool bRandom=true) { |
||||
STATIC_ASSERT(DIMS > 0 && DIMS <= 3); |
||||
srand(bRandom ? (unsigned)time(NULL) : 0); |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT_TYPE; |
||||
typedef CLISTDEF0(POINT_TYPE) TestArr; |
||||
typedef TOctree<TestArr,TYPE,DIMS,uint32_t> TestTree; |
||||
const TYPE ptMinData[] = {0,0,0}, ptMaxData[] = {640,480,240}; |
||||
typename TestTree::AABB_TYPE aabb; |
||||
aabb.Set(Eigen::Map<const POINT_TYPE>(ptMinData), Eigen::Map<const POINT_TYPE>(ptMaxData)); |
||||
aabb.Enlarge(ZEROTOLERANCE<TYPE>()*TYPE(10)); |
||||
unsigned nTotalMatches = 0; |
||||
unsigned nTotalMissed = 0; |
||||
unsigned nTotalExtra = 0; |
||||
#ifndef _RELEASE |
||||
typename TestTree::DEBUGINFO_TYPE totalInfo; |
||||
totalInfo.Init(); |
||||
#endif |
||||
for (unsigned iter=0; iter<iters; ++iter) { |
||||
// generate random items |
||||
const unsigned elems = maxItems/10+RAND()%maxItems; |
||||
TestArr items(elems); |
||||
FOREACH(i, items) |
||||
for (int j=0; j<DIMS; ++j) |
||||
items[i](j) = static_cast<TYPE>(RAND()%ROUND2INT(ptMaxData[j])); |
||||
// random query point |
||||
POINT_TYPE pt; |
||||
for (int j=0; j<DIMS; ++j) |
||||
pt(j) = static_cast<TYPE>(RAND()%ROUND2INT(ptMaxData[j])); |
||||
const TYPE radius(TYPE(3+RAND()%30)); |
||||
// build octree and find interest items |
||||
TestTree tree(items, aabb, [](typename TestTree::IDX_TYPE size, typename TestTree::Type radius) { |
||||
return size > 16 && radius > 10; |
||||
}); |
||||
typename TestTree::IDXARR_TYPE indices; |
||||
tree.Collect(indices, pt, radius); |
||||
// find interest items by brute force |
||||
typename TestTree::IDXARR_TYPE trueIndices; |
||||
#if 1 |
||||
// use square bound |
||||
typename TestTree::AABB_TYPE aabbQuery(pt, radius); |
||||
FOREACH(i, items) |
||||
if (aabbQuery.Intersects(items[i])) |
||||
trueIndices.Insert(i); |
||||
#else |
||||
// use circle bound |
||||
FOREACH(i, items) |
||||
if ((items[i]-pt).norm() < radius) |
||||
trueIndices.Insert(i); |
||||
#endif |
||||
// compare results |
||||
unsigned nMatches = 0; |
||||
FOREACH(i, trueIndices) { |
||||
const typename TestTree::IDX_TYPE idx = trueIndices[i]; |
||||
FOREACH(j, indices) { |
||||
if (indices[j] == idx) { |
||||
++nMatches; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
nTotalMatches += nMatches; |
||||
nTotalMissed += (unsigned)trueIndices.size()-nMatches; |
||||
nTotalExtra += (unsigned)indices.size()-nMatches; |
||||
#ifndef _RELEASE |
||||
// print stats |
||||
typename TestTree::DEBUGINFO_TYPE info; |
||||
tree.GetDebugInfo(&info); |
||||
totalInfo += info; |
||||
#endif |
||||
} |
||||
#ifndef _RELEASE |
||||
TestTree::LogDebugInfo(totalInfo); |
||||
VERBOSE("Test %s (TotalMissed %d, TotalExtra %d)", (nTotalMissed == 0 && nTotalExtra == 0 ? "successful" : "FAILED"), nTotalMissed, nTotalExtra); |
||||
#endif |
||||
return (nTotalMissed == 0 && nTotalExtra == 0); |
||||
} |
||||
@ -0,0 +1,161 @@
@@ -0,0 +1,161 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Plane.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_PLANE_H__ |
||||
#define __SEACAVE_PLANE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
// Basic hyper-plane class
|
||||
// (plane represented in Hessian Normal Form: n.x+d=0 <=> ax+by+cz+d=0)
|
||||
template <typename TYPE, int DIMS=3> |
||||
class TPlane |
||||
{ |
||||
STATIC_ASSERT(DIMS > 0 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
typedef SEACAVE::TAABB<TYPE,DIMS> AABB; |
||||
typedef SEACAVE::TRay<TYPE,DIMS> RAY; |
||||
enum { numScalar = DIMS+1 }; |
||||
enum { numParams = numScalar-1 }; |
||||
|
||||
VECTOR m_vN; // plane normal vector
|
||||
TYPE m_fD; // distance to origin
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TPlane() {} |
||||
inline TPlane(const VECTOR&, TYPE); |
||||
inline TPlane(const VECTOR&, const POINT&); |
||||
inline TPlane(const POINT&, const POINT&, const POINT&); |
||||
inline TPlane(const TYPE p[DIMS+1]); |
||||
inline TPlane(const Eigen::Matrix<TYPE,DIMS+1,1>&); |
||||
|
||||
inline void Set(const VECTOR&, TYPE); |
||||
inline void Set(const VECTOR&, const POINT&); |
||||
inline void Set(const POINT&, const POINT&, const POINT&); |
||||
inline void Set(const TYPE p[DIMS+1]); |
||||
inline void Set(const Eigen::Matrix<TYPE,DIMS+1,1>&); |
||||
|
||||
int Optimize(const POINT*, size_t, int maxIters=100); |
||||
template <typename RobustNormFunctor> |
||||
int Optimize(const POINT*, size_t, const RobustNormFunctor& robust, int maxIters=100); |
||||
|
||||
inline void Invalidate(); |
||||
inline bool IsValid() const; |
||||
|
||||
inline void Negate(); |
||||
inline TPlane Negated() const; |
||||
|
||||
inline TYPE Distance(const TPlane&) const; |
||||
inline TYPE Distance(const POINT&) const; |
||||
inline TYPE DistanceAbs(const POINT&) const; |
||||
|
||||
inline POINT ProjectPoint(const POINT&) const; |
||||
|
||||
inline GCLASS Classify(const POINT&) const; |
||||
inline GCLASS Classify(const AABB&) const; |
||||
|
||||
bool Clip(const RAY&, TYPE, RAY*, RAY*) const; |
||||
|
||||
bool Intersects(const POINT& p0, const POINT& p1, const POINT& p2) const; |
||||
bool Intersects(const TPlane& plane, RAY& ray) const; |
||||
bool Intersects(const AABB& aabb) const; |
||||
|
||||
inline TYPE& operator [] (BYTE i) { ASSERT(i<numScalar); return m_vN.data()[i]; } |
||||
inline TYPE operator [] (BYTE i) const { ASSERT(i<numScalar); return m_vN.data()[i]; } |
||||
|
||||
#ifdef _USE_BOOST |
||||
// implement BOOST serialization
|
||||
template<class Archive> |
||||
void serialize(Archive& ar, const unsigned int /*version*/) { |
||||
ar & m_vN; |
||||
ar & m_fD; |
||||
} |
||||
#endif |
||||
}; // class TPlane
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
template <typename TYPE, typename TYPEW=TYPE, bool bFitLineMode=false> |
||||
struct FitPlaneOnline { |
||||
TYPEW sumX, sumSqX, sumXY, sumXZ; |
||||
TYPEW sumY, sumSqY, sumYZ; |
||||
TYPEW sumZ, sumSqZ; |
||||
size_t size; |
||||
FitPlaneOnline(); |
||||
void Update(const TPoint3<TYPE>& P); |
||||
TPoint3<TYPEW> GetModel(TPoint3<TYPEW>& avg, TPoint3<TYPEW>& dir) const; |
||||
template <typename TYPEE> TPoint3<TYPEE> GetPlane(TPlane<TYPEE,3>& plane) const; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Basic 3D frustum class
|
||||
// (represented as 6 planes oriented toward outside the frustum volume)
|
||||
template <typename TYPE, int DIMS=6> |
||||
class TFrustum |
||||
{ |
||||
STATIC_ASSERT(DIMS > 0 && DIMS <= 6); |
||||
|
||||
public: |
||||
typedef Eigen::Matrix<TYPE,4,4,Eigen::RowMajor> MATRIX4x4; |
||||
typedef Eigen::Matrix<TYPE,3,4,Eigen::RowMajor> MATRIX3x4; |
||||
typedef Eigen::Matrix<TYPE,3,1> VECTOR; |
||||
typedef Eigen::Matrix<TYPE,3,1> POINT; |
||||
typedef SEACAVE::TPlane<TYPE,3> PLANE; |
||||
typedef SEACAVE::TSphere<TYPE,3> SPHERE; |
||||
typedef SEACAVE::TAABB<TYPE,3> AABB; |
||||
enum { numCorners = (1<<3) }; |
||||
|
||||
PLANE m_planes[DIMS]; // left, right, top, bottom, near and far planes
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TFrustum() {} |
||||
inline TFrustum(const MATRIX4x4&, TYPE width, TYPE height, TYPE nearZ=TYPE(0.0001), TYPE farZ=TYPE(1000)); |
||||
inline TFrustum(const MATRIX3x4&, TYPE width, TYPE height, TYPE nearZ=TYPE(0.0001), TYPE farZ=TYPE(1000)); |
||||
|
||||
void Set(const MATRIX4x4&, TYPE width, TYPE height, TYPE nearZ=TYPE(0.0001), TYPE farZ=TYPE(1000)); |
||||
void Set(const MATRIX3x4&, TYPE width, TYPE height, TYPE nearZ=TYPE(0.0001), TYPE farZ=TYPE(1000)); |
||||
void Set(const VECTOR corners[numCorners]); |
||||
void SetProjectionGL(const MATRIX4x4&); |
||||
|
||||
GCLASS Classify(const POINT&) const; |
||||
GCLASS Classify(const SPHERE&) const; |
||||
GCLASS Classify(const AABB&) const; |
||||
|
||||
inline TYPE& operator [] (BYTE i) { ASSERT(i<DIMS); return m_planes[i]; } |
||||
inline TYPE operator [] (BYTE i) const { ASSERT(i<DIMS); return m_planes[i]; } |
||||
|
||||
#ifdef _USE_BOOST |
||||
// implement BOOST serialization
|
||||
template<class Archive> |
||||
void serialize(Archive& ar, const unsigned int /*version*/) { |
||||
ar & m_planes; |
||||
} |
||||
#endif |
||||
}; // class TPlane
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
#include "Plane.inl" |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_PLANE_H__
|
||||
@ -0,0 +1,296 @@
@@ -0,0 +1,296 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Queue.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_QUEUE_H__ |
||||
#define __SEACAVE_QUEUE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/**************************************************************************************
|
||||
// Queue template
|
||||
**************************************************************************************/ |
||||
template <class TYPE, class ARG_TYPE=const TYPE&, int useConstruct=1, int xGrow=16> |
||||
class cQueue |
||||
{ |
||||
public: |
||||
cQueue(int_t xSize = 16) : |
||||
first(-1), last(-1), vectorSize(0), vector(NULL) |
||||
{ |
||||
if (xSize > 0) |
||||
Grow(xSize); |
||||
} |
||||
|
||||
~cQueue() |
||||
{ |
||||
Release(); |
||||
} |
||||
|
||||
void Grow(int_t gr) |
||||
{ |
||||
ASSERT(gr > 0); |
||||
if (IsEmpty()) { |
||||
ASSERT(first == -1 && last == -1); |
||||
operator delete[] (vector); |
||||
vectorSize += gr; |
||||
vector = (TYPE*) operator new[] (vectorSize * sizeof(TYPE)); |
||||
return; |
||||
} |
||||
TYPE* tmp = (TYPE*) operator new[] ((vectorSize + gr) * sizeof(TYPE)); |
||||
if (first > last) { |
||||
memcpy(tmp, vector + first, (vectorSize - first) * sizeof(TYPE)); |
||||
memcpy(tmp + (vectorSize - first), vector, (last + 1) * sizeof(TYPE)); |
||||
last += vectorSize - first; |
||||
} else { |
||||
memcpy(tmp, vector + first, (last - first + 1) * sizeof(TYPE)); |
||||
last = last - first; |
||||
} |
||||
first = 0; |
||||
vectorSize += gr; |
||||
operator delete[] (vector); |
||||
vector = tmp; |
||||
} |
||||
|
||||
inline TYPE* GetData() |
||||
{ |
||||
return vector; |
||||
} |
||||
|
||||
inline void PushHead() |
||||
{ |
||||
_PushHead(); |
||||
if (useConstruct) |
||||
new(vector+(--first)) TYPE(); |
||||
else |
||||
--first; |
||||
} |
||||
inline void PushTail() |
||||
{ |
||||
_PushTail(); |
||||
if (useConstruct) |
||||
new(vector+(++last)) TYPE(); |
||||
else |
||||
++last; |
||||
} |
||||
|
||||
inline TYPE& AddEmptyHead() |
||||
{ |
||||
_PushHead(); |
||||
if (useConstruct) |
||||
return *(new(vector+(--first)) TYPE()); |
||||
else |
||||
return vector[--first]; |
||||
} |
||||
inline TYPE& AddEmptyTail() |
||||
{ |
||||
_PushTail(); |
||||
if (useConstruct) |
||||
return *(new(vector+(++last)) TYPE()); |
||||
else |
||||
return vector[++last]; |
||||
} |
||||
|
||||
inline void AddHead(ARG_TYPE elem) |
||||
{ |
||||
_PushHead(); |
||||
if (useConstruct) |
||||
new(vector+(--first)) TYPE(elem); |
||||
else |
||||
vector[--first] = elem; |
||||
} |
||||
inline void AddTail(ARG_TYPE elem) |
||||
{ |
||||
_PushTail(); |
||||
if (useConstruct) |
||||
new(vector+(++last)) TYPE(elem); |
||||
else |
||||
vector[++last] = elem; |
||||
} |
||||
|
||||
inline TYPE& GetHead() const |
||||
{ |
||||
ASSERT(first >= 0 && last >= 0); |
||||
return vector[first]; |
||||
} |
||||
inline TYPE& GetTail() const |
||||
{ |
||||
ASSERT(first >= 0 && last >= 0); |
||||
return vector[last]; |
||||
} |
||||
|
||||
inline TYPE& GetHead(uint_t delta) const |
||||
{ |
||||
ASSERT(first >= 0 && last >= 0 && delta < GetSize()); |
||||
delta += first; |
||||
if ((int_t)delta < vectorSize) |
||||
return vector[delta]; |
||||
return vector[delta-vectorSize]; |
||||
} |
||||
inline TYPE& GetTail(uint_t delta) const |
||||
{ |
||||
ASSERT(first >= 0 && last >= 0 && delta < GetSize()); |
||||
if ((int_t)delta <= last) |
||||
return vector[last-delta]; |
||||
return vector[vectorSize-(delta-last)]; |
||||
} |
||||
|
||||
inline void PopHead() |
||||
{ |
||||
ASSERT(first >= 0 && last >= 0); |
||||
if (useConstruct) |
||||
(vector+first)->~TYPE(); |
||||
if (first > last) { |
||||
if (++first >= vectorSize) |
||||
first = 0; |
||||
} else { |
||||
if (++first > last) |
||||
first = last = -1; |
||||
} |
||||
} |
||||
inline void PopTail() |
||||
{ |
||||
ASSERT(first >= 0 && last >= 0); |
||||
if (useConstruct) |
||||
(vector+last)->~TYPE(); |
||||
if (last >= first) { |
||||
if (--last < first) |
||||
first = last = -1; |
||||
} else { |
||||
if (--last < 0) |
||||
last = vectorSize-1; |
||||
} |
||||
} |
||||
|
||||
inline TYPE RemoveHead() |
||||
{ |
||||
ASSERT(first >= 0 && last >= 0); |
||||
if (useConstruct) { |
||||
TYPE elem(vector[first]); |
||||
PopHead(); |
||||
return elem; |
||||
} else { |
||||
TYPE& elem(vector[first]); |
||||
PopHead(); |
||||
return elem; |
||||
} |
||||
} |
||||
inline TYPE RemoveTail() |
||||
{ |
||||
ASSERT(first >= 0 && last >= 0); |
||||
if (useConstruct) { |
||||
TYPE elem(vector[last]); |
||||
PopTail(); |
||||
return elem; |
||||
} else { |
||||
TYPE& elem(vector[last]); |
||||
PopTail(); |
||||
return elem; |
||||
} |
||||
} |
||||
|
||||
inline uint_t GetSize() const |
||||
{ |
||||
if (first < 0) |
||||
return 0; |
||||
if (first > last) // circular
|
||||
return (vectorSize - (first - last) + 1); |
||||
return uint_t(last - first + 1); |
||||
} |
||||
|
||||
inline void Empty() |
||||
{ |
||||
first = last = -1; |
||||
} |
||||
|
||||
// same as Empty(), plus free all allocated memory
|
||||
inline void Release() |
||||
{ |
||||
ASSERT((vector != NULL && vectorSize > 0) || (vector == NULL && vectorSize == 0)); |
||||
if (vector != NULL) { |
||||
if (useConstruct) { |
||||
if (first > last) { |
||||
for (; first < vectorSize; first++) |
||||
(vector+first)->~TYPE(); |
||||
for (; last >= 0; last--) |
||||
(vector+last)->~TYPE(); |
||||
} else if (first >= 0) { |
||||
for (; first <= last; first++) |
||||
(vector+first)->~TYPE(); |
||||
} |
||||
} |
||||
operator delete[] (vector); |
||||
vector = NULL; |
||||
vectorSize = 0; |
||||
} |
||||
Empty(); |
||||
} |
||||
|
||||
inline bool IsEmpty() const |
||||
{ |
||||
return (first < 0); |
||||
} |
||||
|
||||
inline TYPE& operator[](uint_t index) const |
||||
{ |
||||
return GetHead(index); |
||||
} |
||||
|
||||
protected: |
||||
inline void _PushHead() |
||||
{ |
||||
if (first > last) { |
||||
if (last + 1 == first) { |
||||
Grow(xGrow); |
||||
first = vectorSize; |
||||
} |
||||
} else { |
||||
if (first <= 0) { |
||||
if (last + 1 >= vectorSize) |
||||
Grow(xGrow); |
||||
first = vectorSize; |
||||
} |
||||
if (last < 0) |
||||
last = first - 1; |
||||
} |
||||
} |
||||
inline void _PushTail() |
||||
{ |
||||
if (last >= first) { |
||||
if (last + 1 >= vectorSize) { |
||||
if (first <= 0) |
||||
Grow(xGrow); |
||||
else |
||||
last = -1; |
||||
} |
||||
if (first < 0) |
||||
first = 0; |
||||
} else { |
||||
if (last + 1 == first) |
||||
Grow(xGrow); |
||||
} |
||||
} |
||||
|
||||
protected: |
||||
int_t first; |
||||
int_t last; |
||||
int_t vectorSize; |
||||
TYPE* vector; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_QUEUE_H__
|
||||
@ -0,0 +1,164 @@
@@ -0,0 +1,164 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Random.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_RANDOM_H__ |
||||
#define __SEACAVE_RANDOM_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
// Stateless random number generation
|
||||
// uniform random number generation
|
||||
FORCEINLINE float random() { |
||||
return RANDOM<float>(); |
||||
} |
||||
FORCEINLINE double randomd() { |
||||
return RANDOM<double>(); |
||||
} |
||||
FORCEINLINE long double randomld() { |
||||
return RANDOM<long double>(); |
||||
} |
||||
STATIC_ASSERT(RAND_MAX < 2147483648); // integer randomRange assumes this is capped
|
||||
template<typename T> |
||||
FORCEINLINE T randomRange(T nMin, T nMax) { |
||||
ASSERT(nMin <= nMax && nMax-nMin+1 < 8589934596); // not to overflow a uint64_t
|
||||
return nMin + T((uint64_t(nMax-nMin)*RAND()+RAND_MAX/2)/RAND_MAX); |
||||
} |
||||
template<> |
||||
FORCEINLINE float randomRange<float>(float fMin, float fMax) { |
||||
return fMin + (fMax - fMin) * random(); |
||||
} |
||||
template<> |
||||
FORCEINLINE double randomRange<double>(double fMin, double fMax) { |
||||
return fMin + (fMax - fMin) * randomd(); |
||||
} |
||||
template<> |
||||
FORCEINLINE long double randomRange<long double>(long double fMin, long double fMax) { |
||||
return fMin + (fMax - fMin) * randomld(); |
||||
} |
||||
template<typename T> |
||||
FORCEINLINE T randomMeanRange(T mean, T delta/*=(max-min)/2*/) { |
||||
ASSERT(delta >= 0 && delta*2+1 < 8589934596); // not to overflow a uint64_t
|
||||
return (mean + T((uint64_t(delta)*2*RAND()+RAND_MAX/2)/RAND_MAX)) - delta; |
||||
} |
||||
template<> |
||||
FORCEINLINE float randomMeanRange<float>(float mean, float delta/*=(max-min)/2*/) { |
||||
return mean + delta * (2.f * random() - 1.f); |
||||
} |
||||
template<> |
||||
FORCEINLINE double randomMeanRange<double>(double mean, double delta/*=(max-min)/2*/) { |
||||
return mean + delta * (2.0 * randomd() - 1.0); |
||||
} |
||||
template<> |
||||
FORCEINLINE long double randomMeanRange<long double>(long double mean, long double delta/*=(max-min)/2*/) { |
||||
return mean + delta * (2.0L * randomld() - 1.0L); |
||||
} |
||||
// gaussian random number generation
|
||||
template<typename T> |
||||
FORCEINLINE T gaussian(T val, T sigma) { |
||||
return EXP(-SQUARE(val/sigma)/2)/(SQRT(T(M_PI*2))*sigma); |
||||
} |
||||
template<typename T> |
||||
FORCEINLINE T randomGaussian(T mean, T sigma) { |
||||
T x, y, r2; |
||||
do { |
||||
x = T(-1) + T(2) * RANDOM<T>(); |
||||
y = T(-1) + T(2) * RANDOM<T>(); |
||||
r2 = x * x + y * y; |
||||
} while (r2 > T(1) || r2 == T(0)); |
||||
return mean + sigma * y * SQRT(T(-2) * LOGN(r2) / r2); |
||||
} |
||||
template<typename T> |
||||
FORCEINLINE T randomGaussian(T sigma) { |
||||
return randomGaussian(T(0), sigma); |
||||
} |
||||
FORCEINLINE float randomGaussian() { |
||||
return randomGaussian(0.f, 1.f); |
||||
} |
||||
FORCEINLINE double randomGaussiand() { |
||||
return randomGaussian(0.0, 1.0); |
||||
} |
||||
FORCEINLINE long double randomGaussianld() { |
||||
return randomGaussian(0.0L, 1.0L); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Encapsulates state for random number generation
|
||||
// based on C++11 random number generator functionality
|
||||
struct Random : std::mt19937 { |
||||
typedef std::mt19937 generator_type; |
||||
|
||||
Random() : generator_type(std::random_device()()) {} |
||||
Random(result_type seed) : generator_type(seed) {} |
||||
|
||||
// integer randomRange assumes this is capped
|
||||
STATIC_ASSERT(max() < 4294967296); |
||||
|
||||
// returns a uniform random number in the range [0, 1]
|
||||
template<typename T=result_type> |
||||
FORCEINLINE typename std::enable_if<std::is_floating_point<T>::value, T>::type random() { |
||||
return (T)random()/(T)max(); |
||||
} |
||||
// returns a uniform random number in the range [0, max()]
|
||||
template<typename T=result_type> |
||||
FORCEINLINE typename std::enable_if<std::is_integral<T>::value, T>::type random() { |
||||
return (T)operator()(); |
||||
} |
||||
|
||||
// returns a uniform random number in the range [nMin, nMax]
|
||||
template<typename T=result_type> |
||||
FORCEINLINE typename std::enable_if<std::is_floating_point<T>::value, T>::type randomRange(T nMin, T nMax) { |
||||
return nMin + (nMax-nMin) * random<T>(); |
||||
} |
||||
template<typename T=result_type> |
||||
FORCEINLINE typename std::enable_if<std::is_integral<T>::value, T>::type randomRange(T nMin, T nMax) { |
||||
ASSERT(nMin <= nMax && nMax-nMin+1 < 4294967297); // not to overflow a uint64_t
|
||||
return nMin + (T)(((uint64_t)(nMax-nMin) * random() + max()/2)/max()); |
||||
} |
||||
|
||||
// returns a uniform random number in the range [mean-delta, mean+delta]
|
||||
template<typename T=result_type> |
||||
FORCEINLINE typename std::enable_if<std::is_floating_point<T>::value, T>::type randomMeanRange(T mean, T delta/*=(max-min)/2*/) { |
||||
return mean + delta * (T(2) * random<T>() - T(1)); |
||||
} |
||||
template<typename T=result_type> |
||||
FORCEINLINE typename std::enable_if<std::is_integral<T>::value, T>::type randomMeanRange(T mean, T delta/*=(max-min)/2*/) { |
||||
ASSERT(delta >= 0 && delta*T(2)+1 < 4294967297); // not to overflow a uint64_t
|
||||
return mean + (T)(((uint64_t)delta*2 * random() + max()/2)/max()) - delta; |
||||
} |
||||
|
||||
// returns a uniform random number in the range [nMin, nMax] using std implementation
|
||||
template<typename T=result_type> |
||||
FORCEINLINE typename std::enable_if<std::is_floating_point<T>::value, T>::type randomUniform(T nMin, T nMax) { |
||||
return std::uniform_real_distribution<T>(nMin, nMax)(*this); |
||||
} |
||||
template<typename T=result_type> |
||||
FORCEINLINE typename std::enable_if<std::is_integral<T>::value, T>::type randomUniform(T nMin, T nMax) { |
||||
return std::uniform_int_distribution<T>(nMin, nMax)(*this); |
||||
} |
||||
|
||||
// returns a gaussian random number using std implementation
|
||||
template<typename T=result_type> |
||||
FORCEINLINE T randomGaussian(T mean, T stddev) { |
||||
return std::normal_distribution<T>(mean, stddev)(*this); |
||||
} |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_RANDOM_H__
|
||||
@ -0,0 +1,226 @@
@@ -0,0 +1,226 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Ray.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_RAY_H__ |
||||
#define __SEACAVE_RAY_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
// Basic triangle class
|
||||
template <typename TYPE, int DIMS> |
||||
class TTriangle |
||||
{ |
||||
STATIC_ASSERT(DIMS > 1 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
typedef SEACAVE::TAABB<TYPE,DIMS> AABB; |
||||
typedef SEACAVE::TPlane<TYPE,DIMS> PLANE; |
||||
enum { numScalar = (3*DIMS) }; |
||||
|
||||
POINT a, b, c; // triangle vertices
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TTriangle() {} |
||||
inline TTriangle(const POINT&, const POINT&, const POINT&); |
||||
|
||||
inline void Set(const POINT&, const POINT&, const POINT&); |
||||
|
||||
inline POINT GetCenter() const; |
||||
inline AABB GetAABB() const; |
||||
inline PLANE GetPlane() const; |
||||
|
||||
inline const POINT& operator [] (BYTE i) const { ASSERT(i<3); return (&a)[i]; } |
||||
inline POINT& operator [] (BYTE i) { ASSERT(i<3); return (&a)[i]; } |
||||
}; // class TTriangle
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Basic ray class
|
||||
template <typename TYPE, int DIMS> |
||||
class TRay |
||||
{ |
||||
STATIC_ASSERT(DIMS > 1 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef Eigen::Matrix<TYPE,DIMS+1,DIMS+1,Eigen::RowMajor> MATRIX; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
typedef SEACAVE::TTriangle<TYPE,DIMS> TRIANGLE; |
||||
typedef SEACAVE::TSphere<TYPE,DIMS> SPHERE; |
||||
typedef SEACAVE::TAABB<TYPE,DIMS> AABB; |
||||
typedef SEACAVE::TOBB<TYPE,DIMS> OBB; |
||||
typedef SEACAVE::TPlane<TYPE,DIMS> PLANE; |
||||
enum { numScalar = (2*DIMS) }; |
||||
|
||||
VECTOR m_vDir; // ray direction (normalized)
|
||||
POINT m_pOrig; // ray origin
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TRay() {} |
||||
inline TRay(const POINT& pOrig, const VECTOR& vDir); |
||||
inline TRay(const POINT& pt0, const POINT& pt1, bool bPoints); |
||||
|
||||
inline void Set(const POINT& pOrig, const VECTOR& vDir); |
||||
inline void SetFromPoints(const POINT& pt0, const POINT& pt1); |
||||
inline TYPE SetFromPointsLen(const POINT& pt0, const POINT& pt1); |
||||
inline void DeTransform(const MATRIX&); // move to matrix space
|
||||
|
||||
template <bool bCull> |
||||
bool Intersects(const TRIANGLE&, TYPE *t) const; |
||||
template <bool bCull> |
||||
bool Intersects(const TRIANGLE&, TYPE fL, TYPE *t) const; |
||||
bool Intersects(const PLANE& plane, bool bCull, |
||||
TYPE *t, POINT* pPtHit) const; |
||||
inline TYPE IntersectsDist(const PLANE& plane) const; |
||||
inline POINT Intersects(const PLANE& plane) const; |
||||
bool Intersects(const PLANE& plane, bool bCull, |
||||
TYPE fL, TYPE *t, POINT* pPtHit) const; |
||||
bool Intersects(const SPHERE& sphere) const; |
||||
bool Intersects(const SPHERE& sphere, TYPE& t) const; |
||||
bool Intersects(const AABB& aabb) const; |
||||
bool Intersects(const AABB& aabb, TYPE& t) const; |
||||
bool Intersects(const AABB& aabb, TYPE fL, TYPE *t) const; |
||||
bool Intersects(const OBB& obb) const; |
||||
bool Intersects(const OBB& obb, TYPE &t) const; |
||||
bool Intersects(const OBB& obb, TYPE fL, TYPE *t) const; |
||||
bool Intersects(const TRay& ray, TYPE& s) const; |
||||
bool Intersects(const TRay& ray, POINT& p) const; |
||||
bool Intersects(const TRay& ray, TYPE& s1, TYPE& s2) const; |
||||
bool IntersectsAprox(const TRay& ray, POINT& p) const; |
||||
bool IntersectsAprox2(const TRay& ray, POINT& p) const; |
||||
|
||||
inline TYPE CosAngle(const TRay&) const; |
||||
inline bool Coplanar(const TRay&) const; |
||||
inline bool Parallel(const TRay&) const; |
||||
|
||||
inline TYPE Classify(const POINT&) const; |
||||
inline POINT ProjectPoint(const POINT&) const; |
||||
bool DistanceSq(const POINT&, TYPE&) const; |
||||
bool Distance(const POINT&, TYPE&) const; |
||||
TYPE DistanceSq(const POINT&) const; |
||||
TYPE Distance(const POINT&) const; |
||||
POINT GetPoint(TYPE) const; |
||||
|
||||
TRay operator * (const MATRIX&) const; // matrix multiplication
|
||||
inline TRay& operator *= (const MATRIX&); // matrix multiplication
|
||||
|
||||
inline TYPE& operator [] (BYTE i) { ASSERT(i<6); return m_vDir.data()[i]; } |
||||
inline TYPE operator [] (BYTE i) const { ASSERT(i<6); return m_vDir.data()[i]; } |
||||
}; // class TRay
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Basic cylinder class
|
||||
template <typename TYPE, int DIMS> |
||||
class TCylinder |
||||
{ |
||||
STATIC_ASSERT(DIMS > 1 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
typedef SEACAVE::TRay<TYPE,DIMS> RAY; |
||||
typedef SEACAVE::TSphere<TYPE,DIMS> SPHERE; |
||||
enum { numScalar = (2*DIMS+2) }; |
||||
|
||||
RAY ray; // central ray defining starting the point and direction
|
||||
TYPE radius; // cylinder radius
|
||||
TYPE minHeight; // cylinder heights satisfying:
|
||||
TYPE maxHeight; // std::numeric_limits<TYPE>::lowest() <= minHeight <= 0 < maxHeight <= std::numeric_limits<TYPE>::max()
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TCylinder() {} |
||||
|
||||
inline TCylinder(const RAY& _ray, TYPE _radius, TYPE _minHeight=TYPE(0), TYPE _maxHeight=std::numeric_limits<TYPE>::max()) |
||||
: ray(_ray), radius(_radius), minHeight(_minHeight), maxHeight(_maxHeight) { ASSERT(TYPE(0) >= minHeight && minHeight < maxHeight); } |
||||
|
||||
inline bool IsFinite() const { return maxHeight < std::numeric_limits<TYPE>::max() && minHeight > std::numeric_limits<TYPE>::lowest(); } |
||||
inline TYPE GetLength() const { return maxHeight - minHeight; } |
||||
|
||||
bool Intersects(const SPHERE&) const; |
||||
|
||||
inline GCLASS Classify(const POINT&, TYPE& t) const; |
||||
}; // class TCylinder
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Basic cone class
|
||||
template <typename TYPE, int DIMS> |
||||
class TCone |
||||
{ |
||||
STATIC_ASSERT(DIMS > 1 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
typedef SEACAVE::TRay<TYPE,DIMS> RAY; |
||||
typedef SEACAVE::TSphere<TYPE,DIMS> SPHERE; |
||||
enum { numScalar = (2*DIMS+3) }; |
||||
|
||||
RAY ray; // ray origin is the cone vertex and ray direction is the cone axis direction
|
||||
TYPE angle; // cone angle, in radians, must be inside [0, pi/2]
|
||||
TYPE minHeight; // heights satisfying:
|
||||
TYPE maxHeight; // 0 <= minHeight < maxHeight <= std::numeric_limits<TYPE>::max()
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TCone() {} |
||||
inline TCone(const RAY& _ray, TYPE _angle, TYPE _minHeight=TYPE(0), TYPE _maxHeight=std::numeric_limits<TYPE>::max()) |
||||
: ray(_ray), angle(_angle), minHeight(_minHeight), maxHeight(_maxHeight) { ASSERT(TYPE(0) <= minHeight && minHeight < maxHeight); } |
||||
}; // class TCone
|
||||
|
||||
// Structure used to compute the intersection between a cone and the given sphere/point
|
||||
template <typename TYPE, int DIMS> |
||||
class TConeIntersect |
||||
{ |
||||
STATIC_ASSERT(DIMS > 1 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
typedef SEACAVE::TCone<TYPE,DIMS> CONE; |
||||
typedef SEACAVE::TSphere<TYPE,DIMS> SPHERE; |
||||
|
||||
const CONE& cone; |
||||
|
||||
// cache angle derivatives, to avoid calling trigonometric functions in geometric queries
|
||||
const TYPE cosAngle, sinAngle, sinAngleSq, cosAngleSq, invSinAngle; |
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TConeIntersect(const CONE& _cone) |
||||
: cone(_cone), cosAngle(COS(cone.angle)), sinAngle(SIN(cone.angle)), |
||||
sinAngleSq(SQUARE(sinAngle)), cosAngleSq(SQUARE(cosAngle)), |
||||
invSinAngle(TYPE(1)/sinAngle) {} |
||||
|
||||
bool operator()(const SPHERE&) const; |
||||
|
||||
GCLASS Classify(const POINT&, TYPE& t) const; |
||||
}; // class TConeIntersect
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
#include "Ray.inl" |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_RAY_H__
|
||||
@ -0,0 +1,951 @@
@@ -0,0 +1,951 @@
|
||||
//////////////////////////////////////////////////////////////////// |
||||
// Ray.inl |
||||
// |
||||
// Copyright 2007 cDc@seacave |
||||
// Distributed under the Boost Software License, Version 1.0 |
||||
// (See http://www.boost.org/LICENSE_1_0.txt) |
||||
|
||||
|
||||
// D E F I N E S /////////////////////////////////////////////////// |
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline TTriangle<TYPE,DIMS>::TTriangle(const POINT& p0, const POINT& p1, const POINT& p2) |
||||
: |
||||
a(p0), b(p1), c(p2) |
||||
{ |
||||
} // constructors |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// set attributes |
||||
template <typename TYPE, int DIMS> |
||||
inline void TTriangle<TYPE,DIMS>::Set(const POINT& p0, const POINT& p1, const POINT& p2) |
||||
{ |
||||
a = p0; |
||||
b = p1; |
||||
c = p2; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// get center |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TTriangle<TYPE,DIMS>::POINT TTriangle<TYPE,DIMS>::GetCenter() const |
||||
{ |
||||
return (a + b + c) / TYPE(3); |
||||
} |
||||
// get AABB |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TTriangle<TYPE,DIMS>::AABB TTriangle<TYPE,DIMS>::GetAABB() const |
||||
{ |
||||
return AABB(& operator [] (0), 3); |
||||
} |
||||
// get plane |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TTriangle<TYPE,DIMS>::PLANE TTriangle<TYPE,DIMS>::GetPlane() const |
||||
{ |
||||
return PLANE(a, b, c); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline TRay<TYPE,DIMS>::TRay(const POINT& pOrig, const VECTOR& vDir) |
||||
: |
||||
m_vDir(vDir), m_pOrig(pOrig) |
||||
{ |
||||
ASSERT(ISEQUAL(m_vDir.squaredNorm(), TYPE(1))); |
||||
} // constructors |
||||
template <typename TYPE, int DIMS> |
||||
inline TRay<TYPE,DIMS>::TRay(const POINT& pt0, const POINT& pt1, bool /*bPoints*/) |
||||
: |
||||
m_vDir((pt1-pt0).normalized()), m_pOrig(pt0) |
||||
{ |
||||
} // constructors |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// set attributes |
||||
template <typename TYPE, int DIMS> |
||||
inline void TRay<TYPE,DIMS>::Set(const POINT& pOrig, const VECTOR& vDir) |
||||
{ |
||||
m_vDir = vDir; |
||||
m_pOrig = pOrig; |
||||
ASSERT(ISEQUAL(m_vDir.squaredNorm(), TYPE(1))); |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TRay<TYPE,DIMS>::SetFromPoints(const POINT& pt0, const POINT& pt1) |
||||
{ |
||||
m_vDir = (pt1-pt0).normalized(); |
||||
m_pOrig = pt0; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TRay<TYPE,DIMS>::SetFromPointsLen(const POINT& pt0, const POINT& pt1) |
||||
{ |
||||
m_vDir = pt1-pt0; |
||||
const TYPE len = m_vDir.norm(); |
||||
if (len > TYPE(0)) |
||||
m_vDir *= TYPE(1)/len; |
||||
m_pOrig = pt0; |
||||
return len; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// transform ray into matrix space |
||||
template <typename TYPE, int DIMS> |
||||
inline void TRay<TYPE,DIMS>::DeTransform(const MATRIX& _m) |
||||
{ |
||||
STATIC_ASSERT(DIMS == 3); |
||||
MATRIX m(_m); |
||||
|
||||
// invert translation |
||||
typedef Eigen::Matrix<TYPE,4,1> VECTOR4; |
||||
const VECTOR4 vcOrig(m_pOrig[0]-m(3,0), m_pOrig[1]-m(3,1), m_pOrig[2]-m(3,2)); |
||||
|
||||
// delete it from matrix |
||||
m(3,0) = m(3,1) = m(3,2) = TYPE(0); |
||||
|
||||
// invert matrix and apply to ray |
||||
const MATRIX mInv = m.inverse(); |
||||
m_pOrig = vcOrig * mInv; |
||||
m_vDir = VECTOR4(m_vDir) * mInv; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Transform the ray by a matrix. |
||||
template <typename TYPE, int DIMS> |
||||
TRay<TYPE,DIMS> TRay<TYPE,DIMS>::operator*(const MATRIX& m) const |
||||
{ |
||||
TRay ray; |
||||
ray.SetFromPoints(m_pOrig*m, (m_pOrig+m_vDir)*m); |
||||
return ray; |
||||
} // operator * |
||||
template <typename TYPE, int DIMS> |
||||
inline TRay<TYPE,DIMS>& TRay<TYPE,DIMS>::operator*=(const MATRIX& m) |
||||
{ |
||||
*this = operator * (m); |
||||
return *this; |
||||
} // operator *= |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// test for intersection with triangle |
||||
template <typename TYPE, int DIMS> |
||||
template <bool bCull> |
||||
bool TRay<TYPE,DIMS>::Intersects(const TRIANGLE& tri, TYPE *pt) const |
||||
{ |
||||
const VECTOR edge1(tri.b - tri.a); |
||||
const VECTOR edge2(tri.c - tri.a); |
||||
const VECTOR pvec(m_vDir.cross(edge2)); |
||||
const TYPE det(edge1.dot(pvec)); |
||||
// check if ray and triangle plane are parallel |
||||
if ((bCull ? det : ABS(det)) < ZEROTOLERANCE<TYPE>() * TYPE(0.01)) |
||||
return false; |
||||
const TYPE invDet(TYPE(1) / det); |
||||
const VECTOR tvec(m_pOrig - tri.a); |
||||
const TYPE u(tvec.dot(pvec) * invDet); |
||||
// check if intersection is outside of the line segment defined by points a and c |
||||
if (u < -ZEROTOLERANCE<TYPE>() * TYPE(10)) |
||||
return false; |
||||
const VECTOR qvec(tvec.cross(edge1)); |
||||
const TYPE v(m_vDir.dot(qvec) * invDet); |
||||
// check if intersection is outside of the line segment defined by points a and b |
||||
if (v < -ZEROTOLERANCE<TYPE>() * TYPE(10)) |
||||
return false; |
||||
// check if intersection is outside of the line segment defined by points b and c |
||||
if (u + v > TYPE(1) + ZEROTOLERANCE<TYPE>() * TYPE(10)) |
||||
return false; |
||||
const TYPE t(edge2.dot(qvec) * invDet); |
||||
// check if intersection is behind the ray origin |
||||
if (bCull && t < -ZEROTOLERANCE<TYPE>()) |
||||
return false; |
||||
if (pt) |
||||
*pt = t; |
||||
return true; |
||||
} // Intersects(Tri) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// test for intersection with triangle at certain length (line segment); |
||||
// same as above, but test distance to intersection vs segment length |
||||
template <typename TYPE, int DIMS> |
||||
template <bool bCull> |
||||
bool TRay<TYPE,DIMS>::Intersects(const TRIANGLE& tri, TYPE fL, TYPE *t) const |
||||
{ |
||||
TYPE _t; |
||||
TYPE* const pt(t ? t : &_t); |
||||
Intersects(tri, pt); |
||||
// collision but not on segment? |
||||
return *pt <= fL; |
||||
} // Intersects(Tri at length) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// test if the ray intersects the given sphere |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const SPHERE& sphere) const |
||||
{ |
||||
TYPE dSq; |
||||
if (!DistanceSq(sphere.center, dSq)) |
||||
return false; |
||||
return dSq <= SQUARE(sphere.radius); |
||||
} |
||||
// same as above, but returns also the distance on the ray corresponding to the closest point |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const SPHERE& sphere, TYPE& t) const |
||||
{ |
||||
const VECTOR a(sphere.center - m_pOrig); |
||||
t = a.dot(m_vDir); |
||||
// point behind the ray origin |
||||
if (t < TYPE(0)) |
||||
return false; |
||||
const TYPE dSq((a - m_vDir*t).squaredNorm()); |
||||
return dSq <= SQUARE(sphere.radius); |
||||
} // Intersects(Sphere) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const AABB &aabb) const |
||||
{ |
||||
bool to_infinity = true; |
||||
TYPE _min, _max; |
||||
// first on x value |
||||
if (m_vDir[0] == TYPE(0)) { |
||||
if (m_pOrig[0] < aabb.ptMin[0] || m_pOrig[0] > aabb.ptMax[0]) |
||||
return false; // NO_INTERSECTION |
||||
} else { |
||||
if (m_vDir[0] > TYPE(0)) { |
||||
_min = (aabb.ptMin[0]-m_pOrig[0])/m_vDir[0]; |
||||
_max = (aabb.ptMax[0]-m_pOrig[0])/m_vDir[0]; |
||||
} else { |
||||
_min = (aabb.ptMax[0]-m_pOrig[0])/m_vDir[0]; |
||||
_max = (aabb.ptMin[0]-m_pOrig[0])/m_vDir[0]; |
||||
} |
||||
to_infinity = false; |
||||
} |
||||
// now on y value |
||||
if (m_vDir[1] == TYPE(0)) { |
||||
if (m_pOrig[1] < aabb.ptMin[1] || m_pOrig[1] > aabb.ptMax[1]) |
||||
return false; // NO_INTERSECTION |
||||
} else { |
||||
TYPE newmin, newmax; |
||||
if (m_vDir[1] > TYPE(0)) { |
||||
newmin = (aabb.ptMin[1]-m_pOrig[1])/m_vDir[1]; |
||||
newmax = (aabb.ptMax[1]-m_pOrig[1])/m_vDir[1]; |
||||
} else { |
||||
newmin = (aabb.ptMax[1]-m_pOrig[1])/m_vDir[1]; |
||||
newmax = (aabb.ptMin[1]-m_pOrig[1])/m_vDir[1]; |
||||
} |
||||
#if 0 |
||||
if (to_infinity) { |
||||
_min = newmin; |
||||
_max = newmax; |
||||
} else { |
||||
#else |
||||
if (to_infinity) |
||||
return true; |
||||
{ |
||||
#endif |
||||
if (newmin > _min) |
||||
_min = newmin; |
||||
if (newmax < _max) |
||||
_max = newmax; |
||||
if (_max < _min) |
||||
return false; // NO_INTERSECTION |
||||
} |
||||
to_infinity = false; |
||||
} |
||||
// now on z value |
||||
if (DIMS == 3) { |
||||
if (m_vDir[2] == TYPE(0)) { |
||||
if (m_pOrig[2] < aabb.ptMin[2] || m_pOrig[2] > aabb.ptMax[2]) |
||||
return false; // NO_INTERSECTION |
||||
} else { |
||||
TYPE newmin, newmax; |
||||
if (m_vDir[2] > TYPE(0)) { |
||||
newmin = (aabb.ptMin[2]-m_pOrig[2])/m_vDir[2]; |
||||
newmax = (aabb.ptMax[2]-m_pOrig[2])/m_vDir[2]; |
||||
} else { |
||||
newmin = (aabb.ptMax[2]-m_pOrig[2])/m_vDir[2]; |
||||
newmax = (aabb.ptMin[2]-m_pOrig[2])/m_vDir[2]; |
||||
} |
||||
#if 0 |
||||
if (to_infinity) { |
||||
_min = newmin; |
||||
_max = newmax; |
||||
} else { |
||||
#else |
||||
if (to_infinity) |
||||
return true; |
||||
{ |
||||
#endif |
||||
if (newmin > _min) |
||||
_min = newmin; |
||||
if (newmax < _max) |
||||
_max = newmax; |
||||
if (_max < _min) |
||||
return false; // NO_INTERSECTION |
||||
} |
||||
to_infinity = false; |
||||
} |
||||
} |
||||
ASSERT(!to_infinity); |
||||
#if 0 |
||||
if (_max < _min) |
||||
return true; // POINT_INTERSECTION |
||||
#endif |
||||
return true; // SEGMENT_INTERSECTION |
||||
} // Intersects(AABB) |
||||
|
||||
// test for intersection with aabb, original code by Andrew Woo, |
||||
// from "Geometric Tools...", Morgan Kaufmann Publ., 2002 |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const AABB &aabb, TYPE& t) const |
||||
{ |
||||
TYPE t0, t1, tmp; |
||||
TYPE tNear(-999999); |
||||
TYPE tFar ( 999999); |
||||
|
||||
// first pair of planes |
||||
if (ISZERO(m_vDir[0])) { |
||||
if ((m_pOrig[0] < aabb.ptMin[0]) || |
||||
(m_pOrig[0] > aabb.ptMax[0])) |
||||
return false; |
||||
} |
||||
t0 = (aabb.ptMin[0] - m_pOrig[0]) / m_vDir[0]; |
||||
t1 = (aabb.ptMax[0] - m_pOrig[0]) / m_vDir[0]; |
||||
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; } |
||||
if (t0 > tNear) tNear = t0; |
||||
if (t1 < tFar) tFar = t1; |
||||
if (tNear > tFar) return false; |
||||
if (tFar < TYPE(0)) return false; |
||||
|
||||
// second pair of planes |
||||
if (ISZERO(m_vDir[1])) { |
||||
if ((m_pOrig[1] < aabb.ptMin[1]) || |
||||
(m_pOrig[1] > aabb.ptMax[1]) ) |
||||
return false; |
||||
} |
||||
t0 = (aabb.ptMin[1] - m_pOrig[1]) / m_vDir[1]; |
||||
t1 = (aabb.ptMax[1] - m_pOrig[1]) / m_vDir[1]; |
||||
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; } |
||||
if (t0 > tNear) tNear = t0; |
||||
if (t1 < tFar) tFar = t1; |
||||
if (tNear > tFar) return false; |
||||
if (tFar < TYPE(0)) return false; |
||||
|
||||
if (DIMS == 3) { |
||||
// third pair of planes |
||||
if (ISZERO(m_vDir[2])) { |
||||
if ((m_pOrig[2] < aabb.ptMin[2]) || |
||||
(m_pOrig[2] > aabb.ptMax[2]) ) |
||||
return false; |
||||
} |
||||
t0 = (aabb.ptMin[2] - m_pOrig[2]) / m_vDir[2]; |
||||
t1 = (aabb.ptMax[2] - m_pOrig[2]) / m_vDir[2]; |
||||
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; } |
||||
if (t0 > tNear) tNear = t0; |
||||
if (t1 < tFar) tFar = t1; |
||||
if (tNear > tFar) return false; |
||||
if (tFar < TYPE(0)) return false; |
||||
} |
||||
|
||||
t = (tNear > TYPE(0) ? tNear : tFar); |
||||
return true; |
||||
} // Intersects(AABB) |
||||
|
||||
// test for intersection with aabb, original code by Andrew Woo, |
||||
// from "Geometric Tools...", Morgan Kaufmann Publ., 2002 |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const AABB &aabb, TYPE fL, TYPE *t) const |
||||
{ |
||||
TYPE t0, t1, tmp, tFinal; |
||||
TYPE tNear(-999999); |
||||
TYPE tFar ( 999999); |
||||
|
||||
// first pair of planes |
||||
if (ISZERO(m_vDir[0])) { |
||||
if ((m_pOrig[0] < aabb.ptMin[0]) || |
||||
(m_pOrig[0] > aabb.ptMax[0]) ) |
||||
return false; |
||||
} |
||||
t0 = (aabb.ptMin[0] - m_pOrig[0]) / m_vDir[0]; |
||||
t1 = (aabb.ptMax[0] - m_pOrig[0]) / m_vDir[0]; |
||||
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; } |
||||
if (t0 > tNear) tNear = t0; |
||||
if (t1 < tFar) tFar = t1; |
||||
if (tNear > tFar) return false; |
||||
if (tFar < TYPE(0)) return false; |
||||
|
||||
// second pair of planes |
||||
if (ISZERO(m_vDir[1])) { |
||||
if ((m_pOrig[1] < aabb.ptMin[1]) || |
||||
(m_pOrig[1] > aabb.ptMax[1]) ) |
||||
return false; |
||||
} |
||||
t0 = (aabb.ptMin[1] - m_pOrig[1]) / m_vDir[1]; |
||||
t1 = (aabb.ptMax[1] - m_pOrig[1]) / m_vDir[1]; |
||||
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; } |
||||
if (t0 > tNear) tNear = t0; |
||||
if (t1 < tFar) tFar = t1; |
||||
if (tNear > tFar) return false; |
||||
if (tFar < TYPE(0)) return false; |
||||
|
||||
if (DIMS == 3) { |
||||
// third pair of planes |
||||
if (ISZERO(m_vDir[2])) { |
||||
if ((m_pOrig[2] < aabb.ptMin[2]) || |
||||
(m_pOrig[2] > aabb.ptMax[2]) ) |
||||
return false; |
||||
} |
||||
t0 = (aabb.ptMin[2] - m_pOrig[2]) / m_vDir[2]; |
||||
t1 = (aabb.ptMax[2] - m_pOrig[2]) / m_vDir[2]; |
||||
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; } |
||||
if (t0 > tNear) tNear = t0; |
||||
if (t1 < tFar) tFar = t1; |
||||
if (tNear > tFar) return false; |
||||
if (tFar < 0) return false; |
||||
} |
||||
|
||||
tFinal = (tNear > TYPE(0) ? tNear : tFar); |
||||
|
||||
if (tFinal > fL) return false; |
||||
if (t) *t = tFinal; |
||||
return true; |
||||
} // Intersects(AABB) at length |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const OBB& obb) const |
||||
{ |
||||
TYPE t; |
||||
return Intersects(obb, t); |
||||
} // Intersects(OBB) |
||||
|
||||
// test for intersection with obb, slaps method |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const OBB& obb, TYPE& t) const |
||||
{ |
||||
TYPE e, f, t1, t2, temp; |
||||
TYPE tmin(-999999); |
||||
TYPE tmax( 999999); |
||||
|
||||
const POINT vP = obb.m_pos - m_pOrig; |
||||
|
||||
// 1st slap |
||||
e = obb.m_rot.row(0) * vP; |
||||
f = obb.m_rot.row(0) * m_vDir; |
||||
if (!ISZERO(f)) { |
||||
t1 = (e + obb.m_ext[0]) / f; |
||||
t2 = (e - obb.m_ext[0]) / f; |
||||
|
||||
if (t1 > t2) { temp=t1; t1=t2; t2=temp; } |
||||
if (t1 > tmin) tmin = t1; |
||||
if (t2 < tmax) tmax = t2; |
||||
if (tmin > tmax) return false; |
||||
if (tmax < 0.0f) return false; |
||||
} else |
||||
if (((-e - obb.m_ext[0]) > 0.0f) || ((-e + obb.m_ext[0]) < 0.0f)) |
||||
return false; |
||||
|
||||
// 2nd slap |
||||
e = obb.m_rot.row(1) * vP; |
||||
f = obb.m_rot.row(1) * m_vDir; |
||||
if (!ISZERO(f)) { |
||||
t1 = (e + obb.m_ext[1]) / f; |
||||
t2 = (e - obb.m_ext[1]) / f; |
||||
|
||||
if (t1 > t2) { temp=t1; t1=t2; t2=temp; } |
||||
if (t1 > tmin) tmin = t1; |
||||
if (t2 < tmax) tmax = t2; |
||||
if (tmin > tmax) return false; |
||||
if (tmax < 0.0f) return false; |
||||
} else |
||||
if (((-e - obb.m_ext[1]) > 0.0f) || ((-e + obb.m_ext[1]) < 0.0f)) |
||||
return false; |
||||
|
||||
// 3rd slap |
||||
e = obb.m_rot.row(2) * vP; |
||||
f = obb.m_rot.row(2) * m_vDir; |
||||
if (!ISZERO(f)) { |
||||
t1 = (e + obb.m_ext[2]) / f; |
||||
t2 = (e - obb.m_ext[2]) / f; |
||||
|
||||
if (t1 > t2) { temp=t1; t1=t2; t2=temp; } |
||||
if (t1 > tmin) tmin = t1; |
||||
if (t2 < tmax) tmax = t2; |
||||
if (tmin > tmax) return false; |
||||
if (tmax < 0.0f) return false; |
||||
} else |
||||
if (((-e - obb.m_ext[2]) > 0) || ((-e + obb.m_ext[2]) < 0)) |
||||
return false; |
||||
|
||||
if (tmin > 0) { |
||||
if (t) *t = tmin; |
||||
return true; |
||||
} |
||||
|
||||
t = tmax; |
||||
return true; |
||||
} // Intersects(AABB) |
||||
|
||||
// test for intersection with obb at certain length (line segment), |
||||
// slaps method but compare result if true to length prior return. |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const OBB& obb, TYPE fL, TYPE *t) const |
||||
{ |
||||
TYPE e, f, t1, t2, temp; |
||||
TYPE tmin(-999999); |
||||
TYPE tmax( 999999); |
||||
|
||||
const POINT vP = obb.m_pos - m_pOrig; |
||||
|
||||
// 1st slap |
||||
e = obb.m_rot.row(0) * vP; |
||||
f = obb.m_rot.row(0) * m_vDir; |
||||
if (!ISZERO(f)) { |
||||
t1 = (e + obb.m_ext[0]) / f; |
||||
t2 = (e - obb.m_ext[0]) / f; |
||||
|
||||
if (t1 > t2) { temp=t1; t1=t2; t2=temp; } |
||||
if (t1 > tmin) tmin = t1; |
||||
if (t2 < tmax) tmax = t2; |
||||
if (tmin > tmax) return false; |
||||
if (tmax < 0.0f) return false; |
||||
} else |
||||
if (((-e - obb.m_ext[0]) > 0) || ((-e + obb.m_ext[0]) < 0)) |
||||
return false; |
||||
|
||||
// 2nd slap |
||||
e = obb.m_rot.row(1) * vP; |
||||
f = obb.m_rot.row(1) * m_vDir; |
||||
if (!ISZERO(f)) { |
||||
t1 = (e + obb.m_ext[1]) / f; |
||||
t2 = (e - obb.m_ext[1]) / f; |
||||
|
||||
if (t1 > t2) { temp=t1; t1=t2; t2=temp; } |
||||
if (t1 > tmin) tmin = t1; |
||||
if (t2 < tmax) tmax = t2; |
||||
if (tmin > tmax) return false; |
||||
if (tmax < 0.0f) return false; |
||||
} else |
||||
if (((-e - obb.m_ext[1]) > 0) || ((-e + obb.m_ext[1]) < 0)) |
||||
return false; |
||||
|
||||
// 3rd slap |
||||
e = obb.m_rot.row(2) * vP; |
||||
f = obb.m_rot.row(2) * m_vDir; |
||||
if (!ISZERO(f)) { |
||||
t1 = (e + obb.m_ext[2]) / f; |
||||
t2 = (e - obb.m_ext[2]) / f; |
||||
|
||||
if (t1 > t2) { temp=t1; t1=t2; t2=temp; } |
||||
if (t1 > tmin) tmin = t1; |
||||
if (t2 < tmax) tmax = t2; |
||||
if (tmin > tmax) return false; |
||||
if (tmax < 0.0f) return false; |
||||
} else |
||||
if (((-e - obb.m_ext[2]) > 0) || ((-e + obb.m_ext[2]) < 0)) |
||||
return false; |
||||
|
||||
if ((tmin > 0) && (tmin <= fL)) { |
||||
if (t) *t = tmin; |
||||
return true; |
||||
} |
||||
|
||||
// intersection on line but not on segment |
||||
if (tmax > fL) return false; |
||||
|
||||
if (t) *t = tmax; |
||||
|
||||
return true; |
||||
} // Intersects(AABB) at length |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Intersection with PLANE from origin till infinity. |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const PLANE& plane, bool bCull, TYPE *t, POINT* pPtHit) const |
||||
{ |
||||
const TYPE Vd(plane.m_vN.dot(m_vDir)); |
||||
|
||||
// ray parallel to plane |
||||
if (ISZERO(Vd)) |
||||
return false; |
||||
|
||||
// normal pointing away from ray dir |
||||
// => intersection backface if any |
||||
if (bCull && (Vd > TYPE(0))) |
||||
return false; |
||||
|
||||
const TYPE Vo(-plane.Distance(m_pOrig)); |
||||
|
||||
const TYPE _t(Vo / Vd); |
||||
|
||||
// intersection behind ray origin |
||||
if (_t < TYPE(0)) |
||||
return false; |
||||
|
||||
if (pPtHit) |
||||
(*pPtHit) = m_pOrig + (m_vDir * _t); |
||||
|
||||
if (t) |
||||
(*t) = _t; |
||||
|
||||
return true; |
||||
} // Intersects(PLANE) |
||||
// same as above, but no checks |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TRay<TYPE,DIMS>::IntersectsDist(const PLANE& plane) const |
||||
{ |
||||
const TYPE Vd(plane.m_vN.dot(m_vDir)); |
||||
const TYPE Vo(-plane.Distance(m_pOrig)); |
||||
return SAFEDIVIDE(Vo, Vd); |
||||
} // IntersectsDist(PLANE) |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TRay<TYPE,DIMS>::POINT TRay<TYPE,DIMS>::Intersects(const PLANE& plane) const |
||||
{ |
||||
return m_pOrig + (m_vDir * IntersectsDist(plane)); |
||||
} // Intersects(PLANE) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Intersection with PLANE at distance fL. |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const PLANE& plane, bool bCull, TYPE fL, TYPE *t, POINT* pPtHit) const |
||||
{ |
||||
const TYPE Vd = plane.m_vN.dot(m_vDir); |
||||
|
||||
// ray parallel to plane |
||||
if (ISZERO(Vd)) |
||||
return false; |
||||
|
||||
// normal pointing away from ray dir |
||||
// => intersection backface if any |
||||
if (bCull && (Vd > TYPE(0))) |
||||
return false; |
||||
|
||||
const TYPE Vo(-plane.Distance(m_pOrig)); |
||||
|
||||
const TYPE _t(Vo / Vd); |
||||
|
||||
// intersection behind ray origin or beyond valid range |
||||
if ((_t < TYPE(0)) || (_t > fL)) |
||||
return false; |
||||
|
||||
if (pPtHit) |
||||
(*pPtHit) = m_pOrig + (m_vDir * _t); |
||||
|
||||
if (t) |
||||
(*t) = _t; |
||||
|
||||
return true; |
||||
} // Intersects(PLANE) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Intersection with another ray. |
||||
// Rays assumed to be coplanar; returns only the distance from the origin of the first ray. |
||||
// P = R.origin + s * R.dir |
||||
// http://mathworld.wolfram.com/Line-LineIntersection.html |
||||
// (works also for 2D lines if z components is set to 0) |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const TRay& ray, TYPE& s) const |
||||
{ |
||||
const POINT dir(ray.m_pOrig - m_pOrig); |
||||
const POINT n(m_vDir.cross(ray.m_vDir)); |
||||
if (!ISZERO(dir.dot(n))) |
||||
return false; // lines are not coplanar |
||||
s = dir.cross(ray.m_vDir).dot(n) / n.squaredNorm(); |
||||
return true; |
||||
} // Intersects(Ray) |
||||
// Same as above, but returns the actual intersection point: |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const TRay& ray, POINT& p) const |
||||
{ |
||||
TYPE s; |
||||
if (!Intersects(ray, s)) |
||||
return false; // lines are not coplanar |
||||
if (s < TYPE(0)) |
||||
return false; // intersection behind ray origin |
||||
p = m_pOrig + m_vDir * s; |
||||
return true; |
||||
} // Intersects(Ray) |
||||
// No coplanarity assumption; returns the shortest line segment connecting the two rays: |
||||
// P1 = R1.origin + s1 * R1.dir |
||||
// P2 = R2.origin + s2 * R2.dir |
||||
// http://paulbourke.net/geometry/pointlineplane/ |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Intersects(const TRay& ray, TYPE& s1, TYPE& s2) const |
||||
{ |
||||
const TYPE d4321(ray.m_vDir.dot(m_vDir)); |
||||
const TYPE d4343(ray.m_vDir.dot(ray.m_vDir)); |
||||
const TYPE d2121(m_vDir.dot(m_vDir)); |
||||
const TYPE denom = d2121 * d4343 - d4321 * d4321; |
||||
if (ISZERO(denom)) |
||||
return false; // lines are parallel |
||||
const POINT dir(m_pOrig - ray.m_pOrig); |
||||
const TYPE d1321(dir.dot(m_vDir)); |
||||
const TYPE d1343(dir.dot(ray.m_vDir)); |
||||
const TYPE numer = d1343 * d4321 - d1321 * d4343; |
||||
s1 = numer / denom; |
||||
s2 = (d1343 + d4321 * s1) / d4343; |
||||
return true; |
||||
} // Intersects(Ray) |
||||
// Same as above, but returns only middle point of the shortest line segment: |
||||
// P = (P1 + P2) / 2 |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::IntersectsAprox(const TRay& ray, POINT& p) const |
||||
{ |
||||
TYPE s1, s2; |
||||
if (!Intersects(ray, s1, s2)) |
||||
return false; |
||||
const POINT P1 = m_pOrig + m_vDir * s1; |
||||
const POINT P2 = ray.m_pOrig + ray.m_vDir * s2; |
||||
p = (P1+P2)*TYPE(0.5); |
||||
return true; |
||||
} // Intersects(Ray) |
||||
// Same as above, but returns the point on the given ray. |
||||
// Returns false if intersection not in front of main ray. |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::IntersectsAprox2(const TRay& ray, POINT& p) const |
||||
{ |
||||
TYPE s1, s2; |
||||
if (!Intersects(ray, s1, s2)) |
||||
return false; |
||||
if (s1 < TYPE(0)) |
||||
return false; // intersection behind main ray origin |
||||
p = ray.m_pOrig + ray.m_vDir * s2; |
||||
return true; |
||||
} // Intersects(Ray) |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// computes the angle between the two lines |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TRay<TYPE,DIMS>::CosAngle(const TRay& ray) const |
||||
{ |
||||
ASSERT(ISEQUAL(m_vDir.norm(), TYPE(1))); |
||||
ASSERT(ISEQUAL(ray.m_vDir.norm(), TYPE(1))); |
||||
return m_vDir.dot(ray.m_vDir); |
||||
} |
||||
// tests if the two lines are coplanar (intersect) |
||||
template <typename TYPE, int DIMS> |
||||
inline bool TRay<TYPE,DIMS>::Coplanar(const TRay& ray) const |
||||
{ |
||||
const POINT dir(ray.m_pOrig - m_pOrig); |
||||
const POINT n(m_vDir.cross(ray.m_vDir)); |
||||
return ISZERO(dir.dot(n)); |
||||
} |
||||
// tests if the two lines are parallel |
||||
template <typename TYPE, int DIMS> |
||||
inline bool TRay<TYPE,DIMS>::Parallel(const TRay& ray) const |
||||
{ |
||||
return ISZERO(m_vDir.cross(ray.m_vDir).norm()); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Computes the position on the ray of the point projection. |
||||
// Returns 0 if it coincides with the ray origin, positive value if in front, and negative if behind. |
||||
template <typename TYPE, int DIMS> |
||||
inline TYPE TRay<TYPE,DIMS>::Classify(const POINT& pt) const |
||||
{ |
||||
ASSERT(ISEQUAL(m_vDir.norm(), TYPE(1))); |
||||
const VECTOR a(pt - m_pOrig); |
||||
return a.dot(m_vDir); |
||||
} // Classify(POINT) |
||||
template <typename TYPE, int DIMS> |
||||
inline typename TRay<TYPE,DIMS>::POINT TRay<TYPE,DIMS>::ProjectPoint(const POINT& pt) const |
||||
{ |
||||
ASSERT(ISEQUAL(m_vDir.norm(), TYPE(1))); |
||||
return m_pOrig + m_vDir*Classify(pt); |
||||
} // ProjectPoint |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// Computes the distance between the ray and a point. |
||||
// Returns false if the point is projecting behind the ray origin. |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::DistanceSq(const POINT& pt, TYPE& d) const |
||||
{ |
||||
const VECTOR a(pt - m_pOrig); |
||||
const TYPE LenACos(a.dot(m_vDir)); |
||||
// point behind the ray origin |
||||
if (LenACos < TYPE(0)) |
||||
return false; |
||||
d = (a - m_vDir*LenACos).squaredNorm(); |
||||
return true; |
||||
} // DistanceSq(POINT) |
||||
template <typename TYPE, int DIMS> |
||||
bool TRay<TYPE,DIMS>::Distance(const POINT& pt, TYPE& d) const |
||||
{ |
||||
const VECTOR a(pt - m_pOrig); |
||||
const TYPE LenACos(a.dot(m_vDir)); |
||||
// point behind the ray origin |
||||
if (LenACos < TYPE(0)) |
||||
return false; |
||||
d = (a - m_vDir*LenACos).norm(); |
||||
return true; |
||||
} // Distance(POINT) |
||||
// Same as above, but returns the distance even if the point projection is behind the origin. |
||||
template <typename TYPE, int DIMS> |
||||
TYPE TRay<TYPE,DIMS>::DistanceSq(const POINT& pt) const |
||||
{ |
||||
ASSERT(ISEQUAL(m_vDir.norm(), TYPE(1))); |
||||
const VECTOR a(pt - m_pOrig); |
||||
const VECTOR b(a - m_vDir); |
||||
return b.cross(a).squaredNorm(); |
||||
} // DistanceSq(POINT) |
||||
template <typename TYPE, int DIMS> |
||||
TYPE TRay<TYPE,DIMS>::Distance(const POINT& pt) const |
||||
{ |
||||
return SQRT(DistanceSq(pt)); |
||||
} // Distance(POINT) |
||||
// Get the point on the ray at distance t from origin |
||||
template <typename TYPE, int DIMS> |
||||
typename TRay<TYPE, DIMS>::POINT TRay<TYPE,DIMS>::GetPoint(TYPE t) const |
||||
{ |
||||
return m_pOrig + m_vDir * t; |
||||
} // GetPoint |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
template <typename TYPE, int DIMS> |
||||
bool TCylinder<TYPE,DIMS>::Intersects(const SPHERE& sphere) const |
||||
{ |
||||
struct Check { |
||||
static bool Intersection(const RAY& ray, const SPHERE& sphere, const VECTOR& CmV, TYPE t, TYPE radius, TYPE height) { |
||||
const POINT O(ray.m_pOrig + ray.m_vDir * height); |
||||
const VECTOR a(sphere.center - O); |
||||
if (a.squaredNorm() <= SQUARE(sphere.radius)) |
||||
return true; // ray origin inside the sphere |
||||
const POINT D((CmV - ray.m_vDir * t).normalized()); |
||||
const TYPE d = a.dot(D); |
||||
if (d < TYPE(0)) |
||||
return false; // intersection behind the ray origin |
||||
const TYPE dSq((a - D*d).squaredNorm()); |
||||
const TYPE srSq = SQUARE(sphere.radius); |
||||
if (dSq <= srSq) { |
||||
const TYPE r = d - SQRT(srSq-dSq); |
||||
ASSERT(r >= TYPE(0)); |
||||
return r <= radius; // intersection before the ray end |
||||
} |
||||
return false; |
||||
} |
||||
}; |
||||
const VECTOR CmV(sphere.center - ray.m_pOrig); |
||||
const TYPE t(ray.m_vDir.dot(CmV)); |
||||
// sphere projects behind the cylinder origin |
||||
if (t < minHeight) { |
||||
if (t+sphere.radius < minHeight) |
||||
return false; |
||||
return Check::Intersection(ray, sphere, CmV, t, radius, minHeight); |
||||
} |
||||
// sphere projects after the cylinder end |
||||
if (t > maxHeight) { |
||||
if (t-sphere.radius > maxHeight) |
||||
return false; |
||||
return Check::Intersection(ray, sphere, CmV, t, radius, maxHeight); |
||||
} |
||||
const TYPE lenSq((CmV - ray.m_vDir * t).squaredNorm()); |
||||
return lenSq <= SQUARE(sphere.radius + radius); |
||||
} // Intersects |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// Classify point to cylinder. |
||||
template <typename TYPE, int DIMS> |
||||
GCLASS TCylinder<TYPE,DIMS>::Classify(const POINT& p, TYPE& t) const |
||||
{ |
||||
ASSERT(ISEQUAL(ray.m_vDir.norm(), TYPE(1))); |
||||
const VECTOR D(p - ray.m_pOrig); |
||||
t = ray.m_vDir.dot(D); |
||||
if (t < minHeight) return BACK; |
||||
if (t > maxHeight) return FRONT; |
||||
const TYPE rSq((D - ray.m_vDir*t).squaredNorm()); |
||||
const TYPE radiusSq(SQUARE(radius)); |
||||
if (rSq > radiusSq) return CULLED; |
||||
if (rSq < radiusSq) return VISIBLE; |
||||
return PLANAR; |
||||
} // Classify |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
template <typename TYPE, int DIMS> |
||||
bool TConeIntersect<TYPE,DIMS>::operator()(const SPHERE& sphere) const |
||||
{ |
||||
const VECTOR CmV(sphere.center - cone.ray.m_pOrig); |
||||
const VECTOR D(CmV + cone.ray.m_vDir * (sphere.radius*invSinAngle)); |
||||
TYPE e = D.dot(cone.ray.m_vDir); |
||||
if (e <= TYPE(0) || e*e < D.squaredNorm()*cosAngleSq) |
||||
return false; |
||||
e = CmV.dot(cone.ray.m_vDir); |
||||
if (e-sphere.radius > cone.maxHeight) |
||||
return false; |
||||
if (e < cone.minHeight) { |
||||
const TYPE lenSq = CmV.squaredNorm(); |
||||
if (e*e >= lenSq*sinAngleSq) |
||||
return lenSq <= SQUARE(sphere.radius); |
||||
} |
||||
return true; |
||||
} // Intersect |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
// Classify point to cone. |
||||
template <typename TYPE, int DIMS> |
||||
GCLASS TConeIntersect<TYPE,DIMS>::Classify(const POINT& p, TYPE& t) const |
||||
{ |
||||
ASSERT(ISEQUAL(cone.ray.m_vDir.norm(), TYPE(1))); |
||||
const VECTOR D(p - cone.ray.m_pOrig); |
||||
t = cone.ray.m_vDir.dot(D); |
||||
if (ISZERO(t)) |
||||
return PLANAR; |
||||
if (t < cone.minHeight) return BACK; |
||||
if (t > cone.maxHeight) return FRONT; |
||||
ASSERT(!ISZERO(D.norm())); |
||||
const TYPE tSq(SQUARE(t)); |
||||
const TYPE dSq(cosAngleSq*D.squaredNorm()); |
||||
if (tSq < dSq) return CULLED; |
||||
if (tSq > dSq) return VISIBLE; |
||||
return PLANAR; |
||||
} // Classify |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
template <typename TYPE> |
||||
bool TestRayTriangleIntersection(unsigned iters) { |
||||
typedef SEACAVE::TTriangle<TYPE,3> Triangle; |
||||
typedef SEACAVE::TRay<TYPE,3> Ray; |
||||
typedef typename Ray::POINT Point; |
||||
typedef typename Point::Scalar Type; |
||||
constexpr Type zeroEps(std::is_same<TYPE,float>::value ? 0.01f : 0.00001f); |
||||
for (unsigned iter=0, lives=iters/100; iter<iters; ++iter) { |
||||
const Type scale(10); |
||||
const Triangle triangle(Point::Random()*scale, Point::Random()*scale, Point::Random()*scale); |
||||
Type t; |
||||
const Point center(triangle.GetCenter()); |
||||
const Ray rayCenter(Point::Random()*scale, center, true); |
||||
if (!rayCenter.template Intersects<false>(triangle, &t) && !lives--) |
||||
return false; |
||||
const Point _center(rayCenter.GetPoint(t)); |
||||
if ((_center-center).norm() > zeroEps && !lives--) |
||||
return false; |
||||
const BYTE o((BYTE)(RAND()%3)); |
||||
const Point side(((triangle[o].template cast<double>()+triangle[(o+1)%3].template cast<double>()) / 2.0).template cast<TYPE>()); |
||||
const Ray raySide(rayCenter.m_pOrig, side, true); |
||||
if (!raySide.template Intersects<false>(triangle, &t) && !lives--) |
||||
return false; |
||||
const Point _side(raySide.GetPoint(t)); |
||||
if ((_side-side).norm() > zeroEps && !lives--) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
@ -0,0 +1,595 @@
@@ -0,0 +1,595 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Rotation.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_ROTATION_H__ |
||||
#define __SEACAVE_ROTATION_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#ifndef _RELEASE |
||||
#define _CPC_DEBUG |
||||
#endif |
||||
|
||||
#define CPC_ERROR(msg) { SLOG(msg); ASSERT(false); } |
||||
#if TD_VERBOSE != TD_VERBOSE_OFF |
||||
#define CPC_SLOG(msg) if (VERBOSITY_LEVEL > 0) SLOG(msg) |
||||
#define CPC_LOG(t, msg) CPC_SLOG(msg) |
||||
#else |
||||
#define CPC_SLOG(msg) |
||||
#define CPC_LOG(t, msg) |
||||
#endif |
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
// generic matrix struct
|
||||
#define QUATERNION_EPSILON 1E-10 // DBL_EPSILON, too small (dherzog)
|
||||
|
||||
// forward declaration to avoid mutual inclusion
|
||||
template <typename TYPE> class TRMatrixBase; |
||||
|
||||
/** @class TQuaternion
|
||||
* @test tested with TestRotationConversion.cpp |
||||
@ingroup g_geometry |
||||
@brief class for rotation with axis and angle |
||||
|
||||
Quaternions can be used for rotation around an axis through the |
||||
origin by spcecifing the normal direction vector of the axis and |
||||
an angle. |
||||
|
||||
The rotation for positive angles is clockwise, |
||||
when looking in direction of the axis. |
||||
|
||||
Contains qx=[0], qy=[1], qz=[2], qw=[3]. |
||||
|
||||
Not every quaternion describes a rotation. |
||||
Only quaternions of the form: |
||||
|
||||
qx = x * sin(phi/2) , which is the imaginary part i |
||||
|
||||
qy = y * sin(phi/2) , which is the imaginary part j |
||||
|
||||
qz = z * sin(phi/2) , which is the imaginary part k |
||||
|
||||
qw = cos(phi/2), , which is the real part |
||||
|
||||
where (x, y, z) is the normalized direction vector of the axis |
||||
and phi is the angle of rtoation. |
||||
|
||||
Some useful quaternions: |
||||
x y z w Description |
||||
0 0 0 1 Identity quaternion, |
||||
no rotation |
||||
1 0 0 0 180' turn around X axis |
||||
0 1 0 0 180' turn around Y axis |
||||
0 0 1 0 180' turn around Z axis |
||||
sqrt(0.5) 0 0 sqrt(0.5) 90' rotation around X axis |
||||
0 sqrt(0.5) 0 sqrt(0.5) 90' rotation around Y axis |
||||
0 0 sqrt(0.5) sqrt(0.5) 90' rotation around Z axis |
||||
-sqrt(0.5) 0 0 sqrt(0.5) -90' rotation around X axis |
||||
0 -sqrt(0.5) 0 sqrt(0.5) -90' rotation around Y axis |
||||
0 0 -sqrt(0.5) sqrt(0.5) -90' rotation around Z axis |
||||
|
||||
@author grest 06/2003 |
||||
*/ |
||||
template <typename TYPE> |
||||
class TQuaternion : public TMatrix<TYPE,4,1> |
||||
{ |
||||
public: |
||||
typedef TMatrix<TYPE,4,1> Base; |
||||
typedef TMatrix<TYPE,4,4> Mat; |
||||
typedef TMatrix<TYPE,3,1> Vec; |
||||
typedef TMatrix<TYPE,3,3> Rot; |
||||
|
||||
using Base::val; |
||||
|
||||
public: |
||||
static const TQuaternion IDENTITY; |
||||
|
||||
public: |
||||
inline ~TQuaternion(); |
||||
|
||||
inline TQuaternion(); |
||||
|
||||
inline TQuaternion(const Base& q); |
||||
|
||||
inline TQuaternion(TYPE i, TYPE j, TYPE k, TYPE r); |
||||
|
||||
inline TQuaternion(const Rot& R); |
||||
|
||||
inline void SetIdentity(); |
||||
|
||||
/**
|
||||
* Sets all parts of quaternion in mathematical order, |
||||
* real part first, then 1.,2. and 3. imaginary part |
||||
*/ |
||||
inline void SetQuaternion(TYPE real, TYPE i, TYPE j, TYPE k); |
||||
|
||||
/** returns the Inverse rotation TQuaternion
|
||||
*/ |
||||
inline TQuaternion<TYPE> Inverse() const; |
||||
|
||||
/** inverts this, by changing the rotation axis by *=-1
|
||||
*/ |
||||
inline void Invert(); |
||||
|
||||
/** makes TQuaternion-representation unique, ensuring vector lies
|
||||
in upper (by real part) hemisphere. (inplace) |
||||
*/ |
||||
inline void MakeUnique(); |
||||
|
||||
/** quaternion multiplication: this= this * quat
|
||||
*/ |
||||
inline void Mult(const TQuaternion<TYPE> &quat); |
||||
|
||||
/** quaternion multiplication: res = this * arg
|
||||
*/ |
||||
inline void Mult(const TQuaternion<TYPE> &arg, |
||||
TQuaternion<TYPE> &res) const; |
||||
|
||||
/** quaternion multiplication: this = quat * this
|
||||
*/ |
||||
inline void MultLeft(const TQuaternion<TYPE> &quat); |
||||
|
||||
/** rotates the given Vector with the quaternion ( q v q* )
|
||||
the resulting vector is given in res |
||||
@returns 0 in case of no error |
||||
@author Daniel Grest, June 2003 |
||||
*/ |
||||
inline int MultVec(const Vec &vec, Vec &res) const; |
||||
|
||||
/* rets result from MultVec()
|
||||
@author herzog 2005-07-15 */ |
||||
inline Vec MultVec(const Vec &vec) const; |
||||
|
||||
/** Computes this^(scale), which scales the angle from axis/angle-
|
||||
representation with 'scale'. This is the same like inter-/extra- |
||||
polating between (0,0,0,1)-quaternion and this! |
||||
*/ |
||||
TQuaternion<TYPE> Power(const TYPE & scale) const; |
||||
|
||||
/** Linear interpolation between this and given quaternion.
|
||||
@note Quaternions are assumed to be unit quaternions! */ |
||||
TQuaternion<TYPE> InterpolateLinear(const TQuaternion<TYPE> &to, const TYPE & t) const; |
||||
|
||||
/** Spherical interpolation between this and given quaternion.
|
||||
@note Quaternions are assumed to be unit quaternions! */ |
||||
TQuaternion<TYPE> Interpolate(const TQuaternion<TYPE> &to, const TYPE & t) const; |
||||
|
||||
/** sets the quaternion with given rotation axis and angle (in rad)
|
||||
@returns 0 in case of no error |
||||
@author Daniel Grest, June 2003 |
||||
*/ |
||||
inline void SetValueAsAxisRad(const Vec &axis, TYPE angle); |
||||
|
||||
/** sets the quaternion with given rotation axis and angle (in rad)
|
||||
@returns 0 in case of no error |
||||
@author Daniel Grest, June 2003 |
||||
*/ |
||||
|
||||
inline void SetValueAsAxisRad(TYPE axisX, TYPE axisY, TYPE axisZ, TYPE angle); |
||||
|
||||
/** Returns matrix which expresses (left) quaternion multiplication.
|
||||
* A quaternions stored in an object can be understood as a 4-vector |
||||
* q =(ix iy iz r)^T. |
||||
* Left multiplying a quaternion q2 with a quaternion q1 can be |
||||
* expressed in terms of matrix multiplication as |
||||
* qRes = q1*q2 = M(q1)*q2. |
||||
* This method returns the matrix M(*this). |
||||
*/ |
||||
Mat GetQuaternionMultMatrixLeft() const; |
||||
|
||||
/** Returns matrix which expresses right quaternion multiplication.
|
||||
* Right multiplying a quaternion q1 with quaternion q2 can be |
||||
* expressed as qRes = q1*q2 = N(q2)*q1. |
||||
* This method returns the matrix N(*this). |
||||
*/ |
||||
Mat GetQuaternionMultMatrixRight() const; |
||||
|
||||
/** Returns rotation axis. See GetRotationAxisAngle(). */ |
||||
Vec GetRotationAxis() const; |
||||
|
||||
/** Returns rotation angle notation in radians.
|
||||
Note that the returned angle resides in the interval [0,PI)! */ |
||||
TYPE GetRotationAngle() const; |
||||
|
||||
/** Returns rotation in axis and angle notation (angle in radians).
|
||||
Note that the returned angle resides in the interval [0,PI) and |
||||
the axis points into the according direction! |
||||
@author woelk 12 2002 */ |
||||
int GetAxisAngle(Vec& axis, TYPE& angle) const; |
||||
|
||||
/** Sets quaternion as concatenated rotations around
|
||||
x,y,z-axis; this = q_x * q_y * q_z (BIAS-RMatrix conform) |
||||
@author herzog 2005-07-19 */ |
||||
int SetXYZ(TYPE radX, TYPE radY, TYPE radZ); |
||||
|
||||
/** Sets quaternion as concatenated rotations around
|
||||
x,y,z-axis; this = q_z * q_y * q_x (BIAS-RMatrix conform) |
||||
@author herzog 2005-07-19 */ |
||||
int SetZYX(TYPE radX, TYPE radY, TYPE radZ); |
||||
|
||||
|
||||
/** Scales quaternion to unit length, i.e. norm equals 1. */ |
||||
inline void Normalize() { |
||||
const TYPE n = norm(*this); |
||||
if (ISZERO(n)) |
||||
*this = IDENTITY; |
||||
else |
||||
*this *= TYPE(1) / n; |
||||
} |
||||
inline bool IsNormalized() const { |
||||
return ISEQUAL(norm(*this), TYPE(1)); |
||||
} |
||||
|
||||
/** assignment operator
|
||||
@author grest 06 2003 */ |
||||
TQuaternion<TYPE>& operator=(const TQuaternion<TYPE>& vec); |
||||
|
||||
/** Enforces rigid coupling constraint for this and the other given unit
|
||||
quaternion (i.e. equal rotation angle resp. scalar part of both). |
||||
Computes direct simple interpolation of both unit quaternion where |
||||
the scalar parts of the resulting unit quaternion are identical. |
||||
This solution can be used as initial guess for numerical refinement. |
||||
@author esquivel 02/2012 */ |
||||
void EnforceRigidCouplingConstraint(TQuaternion<TYPE> &other); |
||||
|
||||
/** Enforces rigid coupling constraint for all quaternion in given vector.
|
||||
@author esquivel 02/2012 */ |
||||
static void EnforceRigidCouplingConstraint(std::vector< TQuaternion<TYPE> > &quats); |
||||
}; // class
|
||||
template <typename TYPE> const TQuaternion<TYPE> TQuaternion<TYPE>::IDENTITY(0,0,0,1); |
||||
/*----------------------------------------------------------------*/ |
||||
typedef TQuaternion<float> QuaternionF; |
||||
typedef TQuaternion<double> QuaternionD; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// generic rotation matrix struct
|
||||
#define ROTATION_MATRIX_EPSILON DBL_EPSILON |
||||
|
||||
/** @class TRMatrixBase
|
||||
@ingroup g_geometry |
||||
@brief 3D rotation matrix |
||||
@author frahm, woelk **/ |
||||
template <typename TYPE> |
||||
class TRMatrixBase : public TMatrix<TYPE,3,3> |
||||
{ |
||||
public: |
||||
typedef TMatrix<TYPE,3,3> Base; |
||||
typedef Base Mat; |
||||
typedef TMatrix<TYPE,3,1> Vec; |
||||
typedef TQuaternion<TYPE> Quat; |
||||
typedef typename Base::Base BaseBase; |
||||
|
||||
using Base::val; |
||||
|
||||
public: |
||||
/** @brief Constructor setting matrix to identity */ |
||||
inline TRMatrixBase(); |
||||
inline TRMatrixBase(const BaseBase& rhs) : Base(rhs) {} |
||||
template <typename T> inline TRMatrixBase(const cv::Matx<T,3,3>& rhs) : Base(rhs) {} |
||||
|
||||
/** @brief Copy constructor from 3x3 matrix
|
||||
@attention Orthonormality of matrix is enforced automatically! */ |
||||
inline TRMatrixBase(const Mat& mat); |
||||
|
||||
/** @brief Initialization from parametrized rotation (axis-angle) */ |
||||
inline TRMatrixBase(const Vec& rot); |
||||
|
||||
/** @brief Initialization from rotation axis w and angle phi (in rad) using Rodrigues' formula */ |
||||
inline TRMatrixBase(const Vec& w, const TYPE phi); |
||||
|
||||
/** @brief Initialization from quaternion */ |
||||
inline TRMatrixBase(const Quat& q); |
||||
|
||||
/** @brief Initialization with the rotation from roll/pitch/yaw (in rad) */ |
||||
inline TRMatrixBase(TYPE roll, TYPE pitch, TYPE yaw); |
||||
|
||||
/** @brief Initialization with the rotation from direction dir0 to direction dir1 */ |
||||
inline TRMatrixBase(const Vec& dir0, const Vec& dir1); |
||||
|
||||
template <typename T> inline TRMatrixBase& operator = (const cv::Matx<T,3,3>& rhs) { BaseBase::operator = (rhs); return *this; } |
||||
inline TRMatrixBase& operator = (const cv::Mat& rhs) { BaseBase::operator = (rhs); return *this; } |
||||
#ifdef _USE_EIGEN |
||||
inline TRMatrixBase& operator = (const typename Base::EMat& rhs) { Base::operator = (rhs); return *this; } |
||||
#endif |
||||
|
||||
/** @brief Set Euler angles (in rad) in order XYZ
|
||||
|
||||
Set angles either in order 1. z, 2. y, 3. x and fixed axes |
||||
or in order 1. x, 2. y, 3. z and moving axes. |
||||
Angles are measured as in mathematics (counter-clockwise), i.e. |
||||
Set(x, 0, 0) results in the following matrix: |
||||
|
||||
| 1 0 0 | |
||||
| 0 cos(x) -sin(x) | |
||||
| 0 sin(x) cos(x) | |
||||
|
||||
(*this) = Rx*Ry*Rz = |
||||
|
||||
| c(y)c(z) -c(y)s(z) s(y) | |
||||
| c(z)s(x)s(y)+c(x)s(z) c(x)c(z)-s(x)s(y)s(z) -c(y)s(x) | |
||||
| -c(x)c(z)s(y)+s(x)s(z) c(z)s(x)+c(x)s(y)s(z) c(x)c(y) | |
||||
|
||||
with s(g) = sin(g), c(g) = cos(g), x = PhiX, y = PhiY and z = PhiZ |
||||
|
||||
@author frahm, woelk */ |
||||
void SetXYZ(TYPE PhiX, TYPE PhiY, TYPE PhiZ); |
||||
|
||||
|
||||
/** @brief Set Euler angles (in rad) in order XYZ from vector */ |
||||
inline void SetXYZ(const Vec& r) |
||||
{ SetXYZ(r[0], r[1], r[2]); } |
||||
|
||||
/** @brief Set Euler angles (in rad) in order ZYX
|
||||
|
||||
Set angles either in order 1. z, 2. y, 3. x and moving axes |
||||
or in order 1. x, 2. y, 3. z and fixed axes. |
||||
Angles are measured as in mathematics (counter-clockwise), i.e. |
||||
Set(x, 0, 0) results in the following matrix: |
||||
|
||||
| 1 0 0 | |
||||
| 0 cos(x) -sin(x) | |
||||
| 0 sin(x) cos(x) | |
||||
|
||||
(*this) = Rz*Ry*Rx = |
||||
|
||||
| c(y)c(z) s(x)s(y)c(z)-c(x)s(z) c(x)s(y)c(z)+s(x)s(z) | |
||||
| c(y)s(z) s(x)s(y)s(z)+c(x)c(z) c(x)s(y)s(z)-s(x)s(z) | |
||||
| -s(y) s(x)c(y) c(x)c(y) | |
||||
|
||||
with s(g) = sin(g), c(g) = cos(g), x = PhiX, y = PhiY and z = PhiZ |
||||
|
||||
@author frahm, woelk */ |
||||
void SetZYX(TYPE PhiX, TYPE PhiY, TYPE PhiZ); |
||||
|
||||
/** @brief Set Euler angles (in rad) in order ZYX from vector */ |
||||
inline void SetZYX(const Vec& r) |
||||
{ SetZYX(r[0], r[1], r[2]); } |
||||
|
||||
/** @brief Set Euler angles (in rad) in order ZXY
|
||||
|
||||
Set angles either in order 1. z, 2. x, 3. y and moving axes |
||||
or in order 1. y, 2. x, 3. z and fixed axes. |
||||
Angles are measured as in mathematics (counter-clockwise), i.e. |
||||
Set(x, 0, 0) results in the following matrix: |
||||
|
||||
| 1 0 0 | |
||||
| 0 cos(x) -sin(x) | |
||||
| 0 sin(x) cos(x) | |
||||
|
||||
(*this) = Rz*Rx*Ry = |
||||
|
||||
| c(y)c(z)-s(x)s(y)s(z), c(x)s(z), s(y)c(z)+s(x)c(y)s(z) | |
||||
| s(z)c(y)+s(x)s(y)c(z), c(x)c(z), s(y)*s(z)+s(x)-c(y)c(z) | |
||||
| -c(x)s(y) , s(x) , c(x)c(y) | |
||||
|
||||
with s(g) = sin(g), c(g) = cos(g), x = PhiX, y = PhiY and z = PhiZ |
||||
|
||||
@author haase */ |
||||
void SetZXY(TYPE PhiX, TYPE PhiY, TYPE PhiZ); |
||||
|
||||
/** @brief Set Euler angles (in rad) in order ZXY from vector */ |
||||
inline void SetZXY(const Vec& r) |
||||
{ SetZXY(r[0], r[1], r[2]); } |
||||
|
||||
/** @brief Set Euler angles (in rad) in order YXZ
|
||||
|
||||
Set angles either in order 1. y, 2. x, 3. z and moving axes |
||||
or in order 1. z, 2. x, 3. y and fixed axes. |
||||
Angles are measured as in mathematics (counter-clockwise), i.e. |
||||
Set(x, 0, 0) results in the following matrix: |
||||
|
||||
| 1 0 0 | |
||||
| 0 cos(x) sin(x) | |
||||
| 0 -sin(x) cos(x) | |
||||
|
||||
(*this) = Rz*Rx*Ry = |
||||
|
||||
| c(y)c(z)+s(x)s(y)s(z), c(x)s(z), -s(y)c(z)+s(x)c(y)s(z) | |
||||
| -s(z)c(y)+s(x)s(y)c(z), c(x)c(z), -s(y)*-s(z)+s(x)c(y)c(z) | |
||||
| c(x)s(y) , -s(x) , c(x)c(y) | |
||||
|
||||
with s(g) = sin(g), c(g) = cos(g), x = PhiX, y = PhiY and z = PhiZ |
||||
|
||||
@author haase */ |
||||
void SetYXZ(TYPE PhiZ, TYPE PhiX, TYPE PhiY); |
||||
|
||||
/** @brief Set Euler angles (in rad) in order YXZ from vector */ |
||||
inline void SetYXZ(const Vec& r) |
||||
{ SetYXZ(r[0], r[1], r[2]); } |
||||
|
||||
/** @brief Set from rotation axis w and angle phi (in rad)
|
||||
@param w Axis vector w will be normalized to length 1, so we need |
||||
|w|>1e-6 if phi != 0, otherwise an exception is thrown |
||||
@param phi Rotation angle is given in radians |
||||
@author evers, woelk */ |
||||
void Set(const Vec& w, TYPE phi); |
||||
|
||||
/** set this matrix from 3 vectors each representing a column*/ |
||||
void SetFromColumnVectors(const Vec& v0, |
||||
const Vec& v1, |
||||
const Vec& v2); |
||||
|
||||
/** set this matrix from 3 vectors, each representing a row */ |
||||
void SetFromRowVectors(const Vec& v0, |
||||
const Vec& v1, |
||||
const Vec& v2); |
||||
|
||||
/** @brief Set from rotation axis * angle (modified Rodrigues vector)
|
||||
@author evers */ |
||||
void SetFromAxisAngle(const Vec& w); |
||||
|
||||
/* @brief Set rotation matrix from an orthogonal basis given in world
|
||||
coordinate system (WCS) |
||||
|
||||
As R' is expressing the transformation from WCS to ICS (image |
||||
coordinate system), R is ICS to WCS and hereby represents the ICS |
||||
base vectors expressed in WCS. These are the *rows* of R because |
||||
the transformation is the scalar product. |
||||
|
||||
Assume xxx,yyy,zzz are right-hand orthogonal (RHS), e.g. the orthogonal |
||||
image coordinate system (ICS) base vectors expressed in world |
||||
coordinates (WCS). |
||||
(Inverse doesn't make sense because base would be identity then). |
||||
Normal vectors of length 1 are computed. |
||||
|
||||
@todo Warning if (xxx,yyy,zzz) are not an orthogonal basis! |
||||
|
||||
@author jw 09/2003, added exception */ |
||||
void SetFromOrthogonalBasis(const Vec &xxx, const Vec &yyy, const Vec &zzz); |
||||
|
||||
/** @brief Set rotation matrix from two vectors from an orthonormal basis
|
||||
@param xh represents the first base vector and is left unchanged |
||||
@param vy should be orthogonal to xh, it is orthogonalized otherwise |
||||
|
||||
You can think of this routine as computing R from an image plane |
||||
given by two (usually orthogonal) base vectors. |
||||
If the given base vectors are not orthogonal, xh is kept and yv is |
||||
orthogonalized appropriately. |
||||
|
||||
@author jw */ |
||||
void SetFromHV(const Vec& xh, const Vec& vy); |
||||
|
||||
/** @brief Create rotation matrix that rotates from dir0 to dir1
|
||||
@param dir0 represents the first (reference) direction vector |
||||
@param dir1 represents the second (target) direction vector |
||||
@author cDc */ |
||||
TRMatrixBase& SetFromDir2Dir(const Vec& dir0, const Vec& dir1); |
||||
|
||||
/** @brief Calculates quaternion representation for this rotation matrix
|
||||
@attention Scalar part of quaternion will always be non-negative |
||||
for sake of uniqueness of the resulting quaternion! |
||||
@author woelk 12/2003 |
||||
@author esquivel 03/2011 (changed algorithm, bugfix) */ |
||||
void GetQuaternion(Quat& q) const; |
||||
inline Quat GetQuaternion() const { Quat q; GetQuaternion(q); return q; } |
||||
|
||||
/** @brief Set rotation matrix from a quaternion
|
||||
@author grest 06/2003 */ |
||||
void SetFromQuaternion(const Quat& q); |
||||
|
||||
/** @brief Set rotation matrix from direction and up vector
|
||||
@note This is openGL conform, similar to gluLookAt(), i.e. if |
||||
direction is (0,0,-1) and up is (0,1,0) the resulting matrix is identity. |
||||
@author grest 12/2005 */ |
||||
void SetFromDirUpGL(const Vec& viewDir, const Vec& viewUp); |
||||
|
||||
/** @brief Set rotation matrix from direction and up vector */ |
||||
void SetFromDirUp(const Vec& viewDir, const Vec& viewUp); |
||||
|
||||
/** @brief Set rotation matrix from an eye point and a look-at target point and the up vector */ |
||||
void LookAt(const Vec& from, const Vec& to, const Vec& up); |
||||
|
||||
// get parametrized rotation (axis-angle) from the rotation matrix
|
||||
inline void SetRotationAxisAngle(const Vec& rot); |
||||
|
||||
// modify the rotation matrix by the given parametrized delta rotation (axis-angle)
|
||||
inline void Apply(const Vec& delta); |
||||
|
||||
// set rotation matrix to the given parametrized rotation (axis-angle)
|
||||
inline Vec GetRotationAxisAngle() const; |
||||
|
||||
/** @brief Calculates angle and rotation axis representation for
|
||||
this rotation matrix |
||||
@param angle Rotation angle is returned in radians |
||||
@author woelk 12/2003 */ |
||||
void GetRotationAxisAngle(Vec& axis, TYPE& angle) const; |
||||
|
||||
/** @brief Interface for axis component of GetRotationAxisAngle() */ |
||||
Vec GetRotationAxis() const; |
||||
|
||||
/** @brief Interface for angle component of GetRotationAxisAngle() */ |
||||
TYPE GetRotationAngle() const; |
||||
|
||||
/** @brief Get Euler angles for this rotation matrix in order XYZ
|
||||
@attention Representation is not unique and has singularities at |
||||
+-pi/2. Assume for example (*this) = Rx * Ry * Rz, then we have |
||||
either Euler angles in order 1. x, 2. y, 3. z and moving axes |
||||
or in order 1. z, 2. y, 3. x and fixed axes. |
||||
@author woelk 01/2003 */ |
||||
int GetRotationAnglesXYZ(TYPE& PhiX, TYPE& PhiY, TYPE& PhiZ) const; |
||||
|
||||
/** @brief Get Euler angles for this rotation matrix in order XYZ
|
||||
@see GetRotationAnglesXYZ(TYPE&, TYPE&, TYPE&) */ |
||||
inline int GetRotationAnglesXYZ(Vec& r) const |
||||
{ return GetRotationAnglesXYZ(r[0], r[1], r[2]); } |
||||
|
||||
/** @brief Get Euler angles for this rotation matrix in order YZX
|
||||
@attention Representation is not unique and has singularities at |
||||
+-pi/2. Assume for example (*this) = Rz * Ry * Rx, then we have |
||||
either Euler angles in order 1. x, 2. y, 3. z and fixed axes |
||||
or in order 1. z, 2. y, 3. x and moving axes. |
||||
@author woelk 01 2003 */ |
||||
int GetRotationAnglesZYX(TYPE& PhiX, TYPE& PhiY, TYPE& PhiZ) const; |
||||
|
||||
/** @brief Get Euler angles for this rotation matrix in order ZYX
|
||||
@see GetRotationAnglesZYX(TYPE&, TYPE&, TYPE&) */ |
||||
inline int GetRotationAnglesZYX(Vec& r) const |
||||
{ return GetRotationAnglesZYX(r[0], r[1], r[2]); } |
||||
|
||||
/** @brief Get Euler angles for this rotation matrix in order ZXY
|
||||
@attention Representation is not unique and has singularities at |
||||
+-pi/2. Rotation order ZXY refers to rotation around point/vector! |
||||
@author haase 2007 */ |
||||
int GetRotationAnglesZXY(TYPE& PhiZ, TYPE& PhiX, TYPE& PhiY) const; |
||||
|
||||
/** @brief Get Euler angles for this rotation matrix in order ZXY
|
||||
@see GetRotationAnglesZXY(TYPE&, TYPE&, TYPE&) */ |
||||
inline int GetRotationAnglesZXY(Vec& r) const |
||||
{ return GetRotationAnglesZXY(r[2], r[0], r[1]); } |
||||
|
||||
/** @brief Get Euler angles for this rotation matrix in order YXZ
|
||||
@attention Representation is not unique and has singularities at |
||||
+-pi/2. Rotation order YXZ refers to rotation with moving axes! |
||||
@author haase 2007 */ |
||||
int GetRotationAnglesYXZ(TYPE& PhiY, TYPE& PhiX, TYPE& PhiZ) const; |
||||
|
||||
/** @brief Get Euler angles for this rotation matrix in order YXZ
|
||||
@see GetRotationAnglesYXZ(TYPE&, TYPE&, TYPE&) */ |
||||
inline int GetRotationAnglesYXZ(Vec& r) const |
||||
{ return GetRotationAnglesYXZ(r[1], r[0], r[2]); } |
||||
|
||||
/** @brief Check that the matrix is a valid rotation matrix (orthogonal) */ |
||||
inline bool IsValid() const { |
||||
// the trace should be three and the determinant should be one
|
||||
#if 1 |
||||
return (ISEQUAL((float)cv::determinant(*this), 1.f) && ISEQUAL((float)cv::trace((*this)*(*this).t()), 3.f)); |
||||
#else |
||||
return (ISEQUAL((TYPE)cv::determinant(*this), TYPE(1)) && ISEQUAL((TYPE)cv::trace((*this)*(*this).t()), TYPE(3))); |
||||
#endif |
||||
} |
||||
|
||||
/** @brief Check if this is a rotation matrix, i.e. if the determinant
|
||||
is +1 and the columns are orthonormal |
||||
@param eps Numerical limit for constraint evaluation |
||||
@param verbose Show reason in case of failure */ |
||||
bool Check(const TYPE eps = std::numeric_limits<TYPE>::epsilon(), |
||||
int verbose = 0) const; |
||||
|
||||
/** @brief Enforce orthogonality constraint on rotation matrix and
|
||||
sets determinant to +1 */ |
||||
void EnforceOrthogonality(); |
||||
}; // class
|
||||
/*----------------------------------------------------------------*/ |
||||
typedef TRMatrixBase<float> RMatrixBaseF; |
||||
typedef TRMatrixBase<double> RMatrixBaseD; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#include "Rotation.inl" |
||||
|
||||
#endif // __SEACAVE_ROTATION_H__
|
||||
|
||||
@ -0,0 +1,419 @@
@@ -0,0 +1,419 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// SML.cpp
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "Common.h" |
||||
#include "SML.h" |
||||
|
||||
using namespace SEACAVE; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define SML_AUTOVALUES_OFF 0 |
||||
#define SML_AUTOVALUES_ON 1 |
||||
#ifndef SML_AUTOVALUES |
||||
#define SML_AUTOVALUES SML_AUTOVALUES_ON |
||||
#endif |
||||
|
||||
#define SML_SECTIONOPENBRACKET _T("{") |
||||
#define SML_SECTIONCLOSEBRACKET _T("}") |
||||
#define SML_NAMEOPENBRACKET _T("[") |
||||
#define SML_NAMECLOSEBRACKET _T("]") |
||||
#define SML_NAMETOKEN _T("=") |
||||
#define SML_VALUETOKEN _T("\n") |
||||
#define SML_INDENT _T("\t") |
||||
|
||||
#define SML_TOKEN_SECTIONOPENBRACKET SML_SECTIONOPENBRACKET[0] |
||||
#define SML_TOKEN_SECTIONCLOSEBRACKET SML_SECTIONCLOSEBRACKET[0] |
||||
#define SML_TOKEN_NAMEOPENBRACKET SML_NAMEOPENBRACKET[0] |
||||
#define SML_TOKEN_NAMECLOSEBRACKET SML_NAMECLOSEBRACKET[0] |
||||
#define SML_TOKEN_NAMETOKEN SML_NAMETOKEN[0] |
||||
#define SML_TOKEN_VALUETOKEN SML_VALUETOKEN[0] |
||||
#define SML_TOKEN_INDENT SML_INDENT[0] |
||||
#define SML_TOKEN_IGNORECHARS _T("\n\r\t ") |
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/*-----------------------------------------------------------*
|
||||
* SML class implementation * |
||||
*-----------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* Constructor |
||||
*/ |
||||
SML::SML(const String& name) |
||||
: |
||||
m_strName(name), |
||||
m_fncInitItem(NULL), |
||||
m_fncSaveItem(NULL), |
||||
m_fncReleaseItem(NULL), |
||||
m_fncItemData(NULL) |
||||
{ |
||||
} |
||||
|
||||
/**
|
||||
* Destructor |
||||
*/ |
||||
SML::~SML() |
||||
{ |
||||
Release(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* Released all used memory for this nod and its children |
||||
*/ |
||||
void SML::Release() |
||||
{ |
||||
m_arrChildren.ReleaseDelete(); |
||||
if (m_fncReleaseItem != NULL) { |
||||
for (SMLITEMMAP::iterator it=GetBegin(); it!=GetEnd(); ++it) |
||||
m_fncReleaseItem(it->second, m_fncItemData); |
||||
} |
||||
SMLITEMMAP::Release(); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Load the values from a file; |
||||
* all children are generated if they do not exist |
||||
*/ |
||||
bool SML::Load(const String& fileName) |
||||
{ |
||||
File oStream(fileName, File::READ, File::OPEN); |
||||
if (!oStream.isOpen()) |
||||
return false; |
||||
return Load(oStream); |
||||
} |
||||
bool SML::Load(ISTREAM& oStream) |
||||
{ |
||||
// create the section token filter and mem-file
|
||||
MemFile memFile; |
||||
TokenIStream filter(&oStream); |
||||
filter.setTrimTokens(SML_TOKEN_IGNORECHARS); |
||||
// and parse section
|
||||
return ParseSection(filter, memFile); |
||||
} |
||||
bool SML::ParseSection(TokenIStream& filter, MemFile& memFile) |
||||
{ |
||||
while (true) { |
||||
// find first section start (NameOpenBracket)
|
||||
filter.setToken(SML_TOKEN_NAMEOPENBRACKET); |
||||
filter.readLine(memFile); |
||||
|
||||
// parse values before the new section or end of section
|
||||
const size_f_t posMemFile = memFile.getPos(); |
||||
MemFile valuesMemFile; |
||||
TokenIStream sectionFilter(&memFile, SML_TOKEN_SECTIONCLOSEBRACKET); |
||||
const size_f_t lenValues = sectionFilter.readLine(valuesMemFile); |
||||
if (ParseValues(valuesMemFile) == false) |
||||
return false; // Parse Error: invalid values
|
||||
ASSERT(valuesMemFile.getSize() == 0); |
||||
|
||||
// if end of section found, return
|
||||
if (!sectionFilter.isEOS()) { |
||||
// if a name open bracket was found before the EOS,
|
||||
// then restore it for the parent next ParseSection()
|
||||
memFile.setPos(posMemFile+(lenValues+1)*sizeof(TCHAR)); |
||||
if (!filter.isEOS()) |
||||
filter.restoreToken(); |
||||
break; |
||||
} |
||||
else { |
||||
ASSERT(memFile.getSize()-posMemFile == lenValues*(size_f_t)sizeof(TCHAR)); |
||||
ASSERT(posMemFile == 0 || memFile.getSize()-posMemFile == memFile.getSizeLeft() || memFile.getSizeLeft() == 0); |
||||
memFile.setSize(0); |
||||
} |
||||
|
||||
// if no more data, return
|
||||
if (filter.isEOS()) |
||||
break; |
||||
|
||||
// parse child section name
|
||||
filter.setToken(SML_TOKEN_NAMECLOSEBRACKET); |
||||
const size_t lenName = filter.readLine(memFile); |
||||
ASSERT(!filter.isEOS()); |
||||
if (lenName == 0) |
||||
return false; // Parse Error: invalid section name
|
||||
const String strChildName((LPCTSTR)memFile.getData(), lenName); |
||||
memFile.growSize(-((size_f_t)(lenName*sizeof(TCHAR)))); |
||||
ASSERT(memFile.getSize() == 0); |
||||
// create the child with the given name
|
||||
const IDX idxChild = CreateChildUnique(strChildName); |
||||
LPSML const pChild = m_arrChildren[idxChild]; |
||||
pChild->SetFncItem(m_fncInitItem, m_fncSaveItem, m_fncReleaseItem, m_fncItemData); |
||||
|
||||
// parse child section
|
||||
filter.setToken(SML_TOKEN_SECTIONOPENBRACKET); |
||||
filter.readLine(memFile); |
||||
filter.trimBackLine(memFile); |
||||
ASSERT(memFile.getSize() == 0); |
||||
if (pChild->ParseSection(filter, memFile) == false) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
bool SML::ParseValues(MemFile& valuesMemFile) |
||||
{ |
||||
if (valuesMemFile.getSize() == 0) |
||||
return true; |
||||
// loop through all name/value pairs
|
||||
MemFile memLine; |
||||
TokenIStream filterLine(&valuesMemFile); |
||||
filterLine.setTrimTokens(SML_TOKEN_IGNORECHARS); |
||||
filterLine.setToken(SML_TOKEN_VALUETOKEN); |
||||
MemFile memValue; |
||||
TokenIStream filterValue(&memLine); |
||||
filterValue.setTrimTokens(SML_TOKEN_IGNORECHARS); |
||||
filterValue.setToken(SML_TOKEN_NAMETOKEN); |
||||
do { |
||||
// parse value name and value
|
||||
filterLine.readLine(memLine); |
||||
filterLine.trimBackLine(memLine); |
||||
filterLine.trimFrontLine(memLine); |
||||
if (memLine.isEOF()) |
||||
continue; // empty line, return
|
||||
// parse value name
|
||||
const size_t lenNameValueReal = (size_t)memLine.getSizeLeft(); |
||||
const size_t lenName = filterValue.readLine(memValue); |
||||
#if SML_AUTOVALUES == SML_AUTOVALUES_ON |
||||
String szName; |
||||
if (filterValue.trimBackLine(memValue) == lenName || lenNameValueReal == lenName) { |
||||
// no name found, auto generate the name
|
||||
szName = _T("Item") + String::ToString(size()); |
||||
} else { |
||||
// read the name
|
||||
szName = (LPCTSTR)memValue.getData(); |
||||
memValue.setSize(0); |
||||
} |
||||
#else |
||||
filterValue.trimBackLine(memValue); |
||||
String szName = (LPCTSTR)memValue.getData(); |
||||
ASSERT(!filterValue.isEOS() && !szName.IsEmpty()); |
||||
if (filterValue.isEOS() || szName.IsEmpty()) { |
||||
memValue.setSize(0); |
||||
memLine.setSize(0); |
||||
filterValue.setPos(0); |
||||
continue; // Parse Error: invalid syntax ('=' not found)
|
||||
} |
||||
ASSERT(szName.size() == memValue.getSizeLeft()); |
||||
memValue.setSize(0); |
||||
#endif |
||||
SMLVALUE& val = operator[](szName); |
||||
// parse value
|
||||
filterValue.read(memValue); |
||||
LPCTSTR szValue = filterValue.trimFrontLine(memValue); |
||||
val.val = szValue; |
||||
ASSERT((size_f_t)_tcslen(szValue) == memValue.getSizeLeft()); |
||||
memValue.setSize(0); |
||||
memLine.setSize(0); |
||||
filterValue.setPos(0); |
||||
} while (!filterLine.isEOS()); |
||||
// all file processed, safe to reset it to 0
|
||||
valuesMemFile.setSize(0); |
||||
return true; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Write to a file the values of this node and its children. |
||||
* Set to false the second parameter in order not to save the empty children. |
||||
*/ |
||||
bool SML::Save(const String& fileName, SAVEFLAG flags) const |
||||
{ |
||||
File oStream(fileName, File::WRITE, File::CREATE | File::TRUNCATE); |
||||
if (!oStream.isOpen()) |
||||
return false; |
||||
return Save(oStream, flags); |
||||
} |
||||
bool SML::Save(OSTREAM& oStream, SAVEFLAG flags) const |
||||
{ |
||||
// save all values and all children
|
||||
return SaveIntern(oStream, _T(""), flags); |
||||
} |
||||
bool SML::SaveBracketize(OSTREAM& oStream, const String& strIndent, SAVEFLAG flags) const |
||||
{ |
||||
// save its name
|
||||
oStream.print(_T("%s" SML_NAMEOPENBRACKET "%s" SML_NAMECLOSEBRACKET "\n"), strIndent.c_str(), m_strName.c_str()); |
||||
oStream.print(_T("%s" SML_SECTIONOPENBRACKET "\n"), strIndent.c_str()); |
||||
// save all values and all children
|
||||
SaveIntern(oStream, strIndent+SML_TOKEN_INDENT, flags); |
||||
// close bracket
|
||||
oStream.print(_T("%s" SML_SECTIONCLOSEBRACKET "\n"), strIndent.c_str()); |
||||
return true; |
||||
} |
||||
bool SML::SaveIntern(OSTREAM& oStream, const String& strIndent, SAVEFLAG flags) const |
||||
{ |
||||
const Flags& flgs = (const Flags&)flags; |
||||
if (flgs.isAnySet(SORT | SORTINV)) { |
||||
// sort values alphabetically and save them
|
||||
StringArr lines(0, GetSize()); |
||||
for (SMLITEMMAP::const_iterator item=GetBegin(); item!=GetEnd(); ++item) |
||||
if (!m_fncSaveItem || m_fncSaveItem((*item).second, m_fncItemData)) |
||||
lines.InsertSort(String::FormatString(_T("%s%s " SML_NAMETOKEN " %s" SML_VALUETOKEN), strIndent.c_str(), (*item).first.c_str(), (*item).second.val.c_str()), |
||||
(flgs.isAnySet(SORT) ? String::CompareAlphabetically : String::CompareAlphabeticallyInv)); |
||||
FOREACH(l, lines) |
||||
oStream.print(lines[l]); |
||||
} else { |
||||
// save all values directly
|
||||
for (SMLITEMMAP::const_iterator item=GetBegin(); item!=GetEnd(); ++item) |
||||
if (!m_fncSaveItem || m_fncSaveItem((*item).second, m_fncItemData)) |
||||
oStream.print(_T("%s%s " SML_NAMETOKEN " %s" SML_VALUETOKEN), strIndent.c_str(), (*item).first.c_str(), (*item).second.val.c_str()); |
||||
} |
||||
// save now all children
|
||||
bool bFirst = IsEmpty(); |
||||
FOREACH(i, m_arrChildren) { |
||||
const SML* pSML = m_arrChildren[i]; |
||||
// skip empty children
|
||||
if (!flgs.isSet(SAVEEMPTY) && pSML->IsEmpty() && pSML->m_arrChildren.IsEmpty()) |
||||
continue; |
||||
// insert a new line to separate from the above section (only for visual aspect)
|
||||
if (bFirst) |
||||
bFirst = false; |
||||
else |
||||
oStream.print(_T("\n")); |
||||
// save child
|
||||
pSML->SaveBracketize(oStream, strIndent, flags); |
||||
} |
||||
return true; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Create and insert a new child |
||||
*/ |
||||
IDX SML::CreateChild(const String& strChildName) |
||||
{ |
||||
return InsertChild(new SML(strChildName)); |
||||
} |
||||
IDX SML::CreateChildUnique(const String& strChildName) |
||||
{ |
||||
return InsertChildUnique(new SML(strChildName)); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Insert a new child |
||||
*/ |
||||
IDX SML::InsertChild(const LPSML pSML) |
||||
{ |
||||
return m_arrChildren.InsertSort(pSML, SML::Compare); |
||||
} |
||||
IDX SML::InsertChildUnique(const LPSML pSML) |
||||
{ |
||||
const std::pair<IDX,bool> res(m_arrChildren.InsertSortUnique(pSML, SML::Compare)); |
||||
if (res.second) |
||||
delete pSML; |
||||
return res.first; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Remove an existing child |
||||
*/ |
||||
void SML::RemoveChild(IDX idx) |
||||
{ |
||||
m_arrChildren.RemoveAtMove(idx); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* Remove and destroy an existing child |
||||
*/ |
||||
void SML::DestroyChild(IDX idx) |
||||
{ |
||||
delete m_arrChildren[idx]; |
||||
RemoveChild(idx); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Retrieve an child by its name; NO_INDEX if unexistent |
||||
*/ |
||||
IDX SML::GetChild(const String& name) const |
||||
{ |
||||
return m_arrChildren.FindFirst(&name, SML::CompareName); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Retrieve an item; NULL if unexistent |
||||
*/ |
||||
const SMLVALUE* SML::GetValue(const String& key) const |
||||
{ |
||||
const_iterator it = Find(key); |
||||
if (it == GetEnd()) |
||||
return NULL; |
||||
return &(it->second); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Insert or retrieve an item; initialize it if necessary |
||||
*/ |
||||
SMLVALUE& SML::GetValue(const String& key) |
||||
{ |
||||
bool bExisted; |
||||
SMLVALUE& val = (*Insert(key, bExisted)).second; |
||||
if (!bExisted && m_fncInitItem != NULL) |
||||
m_fncInitItem(key, val, m_fncItemData); |
||||
return val; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Retrieve an unnamed item by index |
||||
*/ |
||||
const SMLVALUE& SML::GetValue(IDX idx) const |
||||
{ |
||||
ASSERT(idx < this->size()); |
||||
const String szName(_T("Item") + String::ToString(idx)); |
||||
return this->at(szName); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Reset items' init and release functions |
||||
*/ |
||||
void SML::SetFncItem(TFncInitItem fncInit, TFncSaveItem fncSave, TFncReleaseItem fncRelease, void* data) |
||||
{ |
||||
m_fncInitItem = fncInit; |
||||
m_fncSaveItem = fncSave; |
||||
m_fncReleaseItem = fncRelease; |
||||
m_fncItemData = data; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Compare two SML nodes by name |
||||
*/ |
||||
int STCALL SML::Compare(const void* l, const void* r) |
||||
{ |
||||
return _tcscmp((*((const SML**)l))->GetName(), (*((const SML**)r))->GetName()); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
/**
|
||||
* Compare a name and a SML node name |
||||
*/ |
||||
int STCALL SML::CompareName(const void* l, const void* r) |
||||
{ |
||||
return _tcscmp((*((const SML**)l))->GetName(), ((const String*)r)->c_str()); |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// SML.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_SML_H__ |
||||
#define __SEACAVE_SML_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Filters.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
typedef struct SMLVALUE_TYPE { |
||||
String val; //item's value
|
||||
void* data; //user data
|
||||
SMLVALUE_TYPE() : data(NULL) {} |
||||
SMLVALUE_TYPE(const SMLVALUE_TYPE& r) : data(r.data) { ((SMLVALUE_TYPE&)r).data = NULL; } |
||||
~SMLVALUE_TYPE() { delete data; } |
||||
} SMLVALUE; |
||||
|
||||
class SML; |
||||
typedef SML* LPSML; |
||||
typedef cList<LPSML, LPSML, 0, 8> LPSMLARR; |
||||
|
||||
//typedef cHashTable<SMLVALUE> SMLITEMMAP;
|
||||
typedef cMapWrap<String, SMLVALUE> SMLITEMMAP; |
||||
|
||||
/**************************************************************************************
|
||||
* Simple Markup Language |
||||
* -------------- |
||||
* fast and easy to use markup language; |
||||
* contains a map of (name, value) pairs |
||||
**************************************************************************************/ |
||||
|
||||
class GENERAL_API SML : public SMLITEMMAP |
||||
{ |
||||
public: |
||||
typedef void (STCALL *TFncInitItem)(const String&, SMLVALUE&, void*); |
||||
typedef bool (STCALL *TFncSaveItem)(const SMLVALUE&, void*); |
||||
typedef void (STCALL *TFncReleaseItem)(SMLVALUE&, void*); |
||||
|
||||
enum SAVEFLAG { |
||||
NONE = 0, |
||||
SAVEEMPTY = (1 << 0), //save empty children
|
||||
SORT = (1 << 1), //sort all entries alphabetically
|
||||
SORTINV = (1 << 2), //sort all entries inverse alphabetically
|
||||
}; |
||||
|
||||
public: |
||||
SML(const String& =String()); |
||||
~SML(); |
||||
|
||||
void Release(); |
||||
|
||||
// main methods
|
||||
bool Load(const String&); |
||||
bool Load(ISTREAM&); |
||||
bool Save(const String&, SAVEFLAG=NONE) const; |
||||
bool Save(OSTREAM&, SAVEFLAG=NONE) const; |
||||
IDX CreateChild(const String& =String()); |
||||
IDX CreateChildUnique(const String& =String()); |
||||
IDX InsertChild(const LPSML); |
||||
IDX InsertChildUnique(const LPSML); |
||||
void RemoveChild(IDX); |
||||
inline void RemoveChild(const String& name) { RemoveChild(GetChild(name)); } |
||||
void DestroyChild(IDX); |
||||
inline void DestroyChild(const String& name) { DestroyChild(GetChild(name)); } |
||||
IDX GetChild(const String&) const; |
||||
const SMLVALUE* GetValue(const String&) const; |
||||
SMLVALUE& GetValue(const String&); |
||||
const SMLVALUE& GetValue(IDX) const; |
||||
inline SMLVALUE& operator[] (const String& key) { return GetValue(key); } |
||||
|
||||
// misc methods
|
||||
inline const String& GetName() const { return m_strName; } |
||||
inline LPSML GetChild(IDX idx) const { return m_arrChildren[idx]; } |
||||
inline const LPSMLARR& GetArrChildren() const { return m_arrChildren; } |
||||
inline LPSMLARR& GetArrChildren() { return m_arrChildren; } |
||||
void SetFncItem(TFncInitItem, TFncSaveItem, TFncReleaseItem, void* data=NULL); |
||||
|
||||
public: |
||||
static int STCALL Compare(const void*, const void*); |
||||
static int STCALL CompareName(const void*, const void*); |
||||
|
||||
private: |
||||
typedef TokenInputStream<false> TokenIStream; |
||||
|
||||
bool ParseSection(TokenIStream&, MemFile&); |
||||
bool ParseValues(MemFile&); |
||||
bool SaveBracketize(OSTREAM&, const String&, SAVEFLAG) const; |
||||
bool SaveIntern(OSTREAM&, const String&, SAVEFLAG) const; |
||||
|
||||
private: |
||||
const String m_strName; // node name
|
||||
LPSMLARR m_arrChildren; // the array with all the sub-nodes
|
||||
TFncInitItem m_fncInitItem; // callback function used to initialize each item
|
||||
TFncSaveItem m_fncSaveItem; // callback function used to save each item
|
||||
TFncReleaseItem m_fncReleaseItem; // callback function used to release each item
|
||||
void* m_fncItemData; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_SML_H__
|
||||
@ -0,0 +1,274 @@
@@ -0,0 +1,274 @@
|
||||
// Copyright (c) 2012-2015 OpenMVG. |
||||
// Copyright (c) 2012-2015 Pierre MOULON. |
||||
// Copyright (c) 2015 Romuald Perrot. |
||||
// Copyright (c) 2015 cDc@seacave. |
||||
// |
||||
// This Source Code Form is subject to the terms of the Mozilla Public |
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this |
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/. |
||||
|
||||
namespace Sampler { |
||||
|
||||
// Sampling functors |
||||
// These functors computes weight associated to each pixels |
||||
// |
||||
// For a (relative) sampling position x (in [0,1]) between two (consecutive) points : |
||||
// |
||||
// A .... x ......... B |
||||
// |
||||
// w[0] is the weight associated to A |
||||
// w[1] is the weight associated to B |
||||
// |
||||
// Note: The following functors generalize the sampling to more than two neighbors |
||||
// They all contains the width variable that specify the number of neighbors used for sampling |
||||
// |
||||
// All contain the operator() with the following definition: |
||||
// |
||||
// @brief Computes weight associated to neighboring pixels |
||||
// @author Romuald Perrot <perrot.romuald_AT_gmail.com> |
||||
// @param x Sampling position |
||||
// @param[out] weight Sampling factors associated to the neighboring |
||||
// @note weight must be at least width length |
||||
// Linear sampling (ie: linear interpolation between two pixels) |
||||
template <typename TYPE> |
||||
struct Linear { |
||||
typedef TYPE Type; |
||||
|
||||
enum { halfWidth = 1 }; |
||||
enum { width = halfWidth*2 }; |
||||
|
||||
inline Linear() {} |
||||
|
||||
inline void operator() (const TYPE x, TYPE* const weight) const { |
||||
weight[0] = TYPE(1) - x; |
||||
weight[1] = x; |
||||
} |
||||
}; |
||||
|
||||
// Cubic interpolation between 4 pixels |
||||
// |
||||
// Interpolation weight is for A,B,C and D pixels given a x position as illustrated as follow : |
||||
// |
||||
// A B x C D |
||||
// |
||||
// Cubic Convolution Interpolation for Digital Image Processing , R. Keys, eq(4) |
||||
// |
||||
// The cubic filter family with the two free parameters usually named B and C: |
||||
// if |x|<1 |
||||
// ((12-9B-6C)|x|^3 + (-18+12B+6C)|x|^2 + (6-2B))/6 |
||||
// if 1<=|x|<2 |
||||
// ((-B-6C)|x|^3 + (6B+30C)|x|^2 + (-12B-48C)|x| + (8B+24C))/6 |
||||
template <typename TYPE> |
||||
struct Cubic { |
||||
typedef TYPE Type; |
||||
|
||||
enum { halfWidth = 2 }; |
||||
enum { width = halfWidth*2 }; |
||||
|
||||
// Sharpness coefficient used to control sharpness of the cubic curve (between 0.5 to 0.75) |
||||
// cubic(0,1/2): 0.5 gives better mathematically result (ie: approximation at 3 order precision - Catmull-Rom) |
||||
const TYPE sharpness; |
||||
inline Cubic(const TYPE& _sharpness=TYPE(0.5)) : sharpness(_sharpness) {} |
||||
|
||||
inline void operator() (const TYPE x, TYPE* const weight) const { |
||||
// remember : |
||||
// A B x C D |
||||
|
||||
// weight[0] -> weight for A |
||||
// weight[1] -> weight for B |
||||
// weight[2] -> weight for C |
||||
// weight[3] -> weight for D |
||||
|
||||
weight[0] = CubicInter12(x + TYPE(1)); |
||||
weight[1] = CubicInter01(x); |
||||
weight[2] = CubicInter01(TYPE(1) - x); |
||||
weight[3] = CubicInter12(TYPE(2) - x); |
||||
} |
||||
|
||||
// Cubic interpolation for x (in [0,1]) |
||||
inline TYPE CubicInter01(const TYPE x) const { |
||||
// A = -sharpness |
||||
// f(x) = (A + 2) * x^3 - (A + 3) * x^2 + 1 |
||||
return ((TYPE(2)-sharpness)*x - (TYPE(3)-sharpness))*x*x + TYPE(1); |
||||
} |
||||
// Cubic interpolation for x (in [1,2]) |
||||
inline TYPE CubicInter12(const TYPE x) const { |
||||
// A = -sharpness |
||||
// f(x) = A * x^3 - 5 * A * x^2 + 8 * A * x - 4 * A |
||||
return (((TYPE(5)-x)*x - TYPE(8))*x + TYPE(4))*sharpness; |
||||
} |
||||
}; |
||||
|
||||
// Sampler spline16 -> Interpolation on 4 points used for 2D ressampling (16 = 4x4 sampling) |
||||
// Cubic interpolation with 0-derivative at edges (ie at A and D points) |
||||
// See Helmut Dersch for more details |
||||
// |
||||
// Some refs : |
||||
// - http://forum.doom9.org/archive/index.php/t-147117.html |
||||
// - http://avisynth.nl/index.php/Resampling |
||||
// - http://www.ipol.im/pub/art/2011/g_lmii/ |
||||
// |
||||
// The idea is to consider 3 cubic splines (f1,f2,f3) in the sampling interval : |
||||
// |
||||
// A f1 B f2 C f3 D |
||||
// |
||||
// with curves defined as follow : |
||||
// f1(x) = a1 x^3 + b1 x^2 + c1 x + d1 |
||||
// f2(x) = a2 x^3 + b2 x^2 + c2 x + d2 |
||||
// f3(x) = a3 x^3 + b2 x^2 + c3 x + d3 |
||||
// |
||||
// We want to compute spline coefs for A,B,C,D assuming that: |
||||
// y0 = coef[A] = f1(-1) |
||||
// y1 = coef[B] = f1(0) = f2(0) |
||||
// y2 = coef[C] = f2(1) = f3(1) |
||||
// y3 = coef[D] = f3(2) |
||||
// |
||||
// coef are computed using the following constraints : |
||||
// Curve is continuous, ie: |
||||
// f1(0) = f2(0) |
||||
// f2(1) = f3(1) |
||||
// First derivative are equals, ie: |
||||
// f1'(0) = f2'(0) |
||||
// f2'(1) = f3'(1) |
||||
// Second derivative are equals, ie: |
||||
// f1''(0) = f2''(0) |
||||
// f2''(1) = f3''(0) |
||||
// Curve is, at boundary, with second derivative set to zero (it's a constraint introduced by Dersch), ie: |
||||
// f1''(-1) = 0 |
||||
// f3''(2) = 0 |
||||
// |
||||
// Then, you can solve for (a1,a2,a3,b1,b2,b3,c1,c2,c3,d1,d2,d3) |
||||
// |
||||
// for ex, for curve f2 you find : |
||||
// |
||||
// d2 = y1 // easy since y1 = f2(0) |
||||
// c2 = - 7/15 y0 - 1/5 y1 + 4/5 y2 - 2/15 y3 |
||||
// b2 = 4/5 y0 - 9/5 y1 + 6/5 y2 - 1/5 y3 |
||||
// a2 = - 1/3 y0 + y1 - y2 + 1/3 y3 |
||||
// |
||||
// |
||||
// When you have coefs, you just have to express your curve as a linear combination of the control points, fort ex |
||||
// with f2 : |
||||
// |
||||
// |
||||
// f2(x) = w0(x) * y0 + w1(x) + y1 + w2(x) * y2 + w3(x) * y3 |
||||
// |
||||
// with : |
||||
// |
||||
// w0(x) = - 1/3 * x^3 + 4/5 * x^2 - 7/15 * x |
||||
// w1(x) = x^3 - 9/5 * x^2 - 1/5 * x + 1 |
||||
// w2(x) = -x^3 + 6/5 * x^2 + 4/5 * x |
||||
// w3(x) = 1/3 * x^3 - 1/5 * x^2 - 2/15 * x |
||||
// |
||||
// substituting boundary conditions gives the correct coefficients for y0,y1,y2,y3 giving the final sampling scheme |
||||
template <typename TYPE> |
||||
struct Spline16 { |
||||
typedef TYPE Type; |
||||
|
||||
enum { halfWidth = 2 }; |
||||
enum { width = halfWidth*2 }; |
||||
|
||||
inline Spline16() {} |
||||
|
||||
inline void operator() (const TYPE x, TYPE* const weight) const { |
||||
weight[0] = ((TYPE(-1) / TYPE(3) * x + TYPE(4) / TYPE(5)) * x - TYPE(7) / TYPE(15)) * x; |
||||
weight[1] = ((x - TYPE(9) / TYPE(5)) * x - TYPE(1) / TYPE(5)) * x + TYPE(1); |
||||
weight[2] = ((TYPE(6) / TYPE(5) - x) * x + TYPE(4) / TYPE(5)) * x; |
||||
weight[3] = ((TYPE(1) / TYPE(3) * x - TYPE(1) / TYPE(5)) * x - TYPE(2) / TYPE(15)) * x; |
||||
} |
||||
}; |
||||
|
||||
// Sampler spline 36 |
||||
// Same as spline 16 but on 6 neighbors (used for 6x6 frame) |
||||
template <typename TYPE> |
||||
struct Spline36 { |
||||
typedef TYPE Type; |
||||
|
||||
enum { halfWidth = 3 }; |
||||
enum { width = halfWidth*2 }; |
||||
|
||||
inline Spline36() {} |
||||
|
||||
inline void operator() (const TYPE x, TYPE* const weight) const { |
||||
weight[0] = ((TYPE(1) / TYPE(11) * x - TYPE(45) / TYPE(209)) * x + TYPE(26) / TYPE(209)) * x; |
||||
weight[1] = ((TYPE(-6) / TYPE(11) * x + TYPE(270) / TYPE(209)) * x - TYPE(156) / TYPE(209)) * x; |
||||
weight[2] = ((TYPE(13) / TYPE(11) * x - TYPE(453) / TYPE(209)) * x - TYPE(3) / TYPE(209)) * x + TYPE(1); |
||||
weight[3] = ((TYPE(-13) / TYPE(11) * x + TYPE(288) / TYPE(209)) * x + TYPE(168) / TYPE(209)) * x; |
||||
weight[4] = ((TYPE(6) / TYPE(11) * x - TYPE(72) / TYPE(209)) * x - TYPE(42) / TYPE(209)) * x; |
||||
weight[5] = ((TYPE(-1) / TYPE(11) * x + TYPE(12) / TYPE(209)) * x + TYPE(7) / TYPE(209)) * x; |
||||
} |
||||
}; |
||||
|
||||
// Sampler spline 64 |
||||
// Same as spline 16 but on 8 neighbors (used for 8x8 frame) |
||||
template <typename TYPE> |
||||
struct Spline64 { |
||||
typedef TYPE Type; |
||||
|
||||
enum { halfWidth = 4 }; |
||||
enum { width = halfWidth*2 }; |
||||
|
||||
inline Spline64() {} |
||||
|
||||
inline void operator() (const TYPE x, TYPE* const weight) const { |
||||
weight[0] = ((TYPE(-1) / TYPE(41) * x + TYPE(168) / TYPE(2911)) * x - TYPE(97) / TYPE(2911)) * x; |
||||
weight[1] = ((TYPE(6) / TYPE(41) * x - TYPE(1008) / TYPE(2911)) * x + TYPE(582) / TYPE(2911)) * x; |
||||
weight[2] = ((TYPE(-24) / TYPE(41) * x + TYPE(4032) / TYPE(2911)) * x - TYPE(2328) / TYPE(2911)) * x; |
||||
weight[3] = ((TYPE(49) / TYPE(41) * x - TYPE(6387) / TYPE(2911)) * x - TYPE(3) / TYPE(2911)) * x + TYPE(1); |
||||
weight[4] = ((TYPE(-49) / TYPE(41) * x + TYPE(4050) / TYPE(2911)) * x + TYPE(2340) / TYPE(2911)) * x; |
||||
weight[5] = ((TYPE(24) / TYPE(41) * x - TYPE(1080) / TYPE(2911)) * x - TYPE(624) / TYPE(2911)) * x; |
||||
weight[6] = ((TYPE(-6) / TYPE(41) * x + TYPE(270) / TYPE(2911)) * x + TYPE(156) / TYPE(2911)) * x; |
||||
weight[7] = ((TYPE(1) / TYPE(41) * x - TYPE(45) / TYPE(2911)) * x - TYPE(26) / TYPE(2911)) * x; |
||||
} |
||||
}; |
||||
|
||||
// Sample image at a specified position |
||||
// @param image to be sampled |
||||
// @param sampler used to make the sampling |
||||
// @param pt X and Y-coordinate of sampling |
||||
// @return sampled value |
||||
template <typename IMAGE, typename SAMPLER, typename POINT, typename TYPE> |
||||
inline TYPE Sample(const IMAGE& image, const SAMPLER& sampler, const POINT& pt) |
||||
{ |
||||
typedef typename SAMPLER::Type T; |
||||
|
||||
// integer position of sample (x,y) |
||||
const int grid_x(FLOOR2INT(pt.x)); |
||||
const int grid_y(FLOOR2INT(pt.y)); |
||||
|
||||
// compute difference between exact pixel location and sample |
||||
const T dx(pt.x-(T)grid_x); |
||||
const T dy(pt.y-(T)grid_y); |
||||
|
||||
// get sampler weights |
||||
T coefs_x[SAMPLER::width]; |
||||
sampler(dx, coefs_x); |
||||
T coefs_y[SAMPLER::width]; |
||||
sampler(dy, coefs_y); |
||||
|
||||
// Sample a grid around specified grid point |
||||
TYPE res(0); |
||||
for (int i = 0; i < SAMPLER::width; ++i) { |
||||
// get current i value |
||||
// +1 for correct scheme (draw it to be convinced) |
||||
const int cur_i(grid_y + 1 + i - SAMPLER::halfWidth); |
||||
// handle out of range |
||||
if (cur_i < 0 || cur_i >= image.rows) |
||||
continue; |
||||
for (int j = 0; j < SAMPLER::width; ++j) { |
||||
// get current j value |
||||
// +1 for the same reason |
||||
const int cur_j(grid_x + 1 + j - SAMPLER::halfWidth); |
||||
// handle out of range |
||||
if (cur_j < 0 || cur_j >= image.cols) |
||||
continue; |
||||
// sample input image and weight according to sampler |
||||
const T w = coefs_x[j] * coefs_y[i]; |
||||
const TYPE pixel = image(cur_i, cur_j); |
||||
res += pixel * w; |
||||
} |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
} // namespace Sampler |
||||
@ -0,0 +1,170 @@
@@ -0,0 +1,170 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Semaphore.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_SEMAPHORE_H__ |
||||
#define __SEACAVE_SEMAPHORE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Thread.h" |
||||
|
||||
#ifndef _MSC_VER |
||||
#ifdef _SUPPORT_CPP11 |
||||
#include <mutex> |
||||
#include <condition_variable> |
||||
#else |
||||
#include "CriticalSection.h" |
||||
#include <sys/time.h> |
||||
#endif |
||||
#endif |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
class Semaphore |
||||
{ |
||||
#ifdef _MSC_VER |
||||
public: |
||||
Semaphore(unsigned count=0) { |
||||
h = CreateSemaphore(NULL, count, MAXLONG, NULL); |
||||
}; |
||||
~Semaphore() { |
||||
CloseHandle(h); |
||||
}; |
||||
|
||||
void Clear(unsigned count=0) { |
||||
CloseHandle(h); |
||||
h = CreateSemaphore(NULL, count, MAXLONG, NULL); |
||||
} |
||||
|
||||
void Signal() { |
||||
ReleaseSemaphore(h, 1, NULL); |
||||
} |
||||
void Signal(unsigned count) { |
||||
ASSERT(count > 0); |
||||
ReleaseSemaphore(h, count, NULL); |
||||
} |
||||
|
||||
void Wait() { |
||||
WaitForSingleObject(h, INFINITE); |
||||
} |
||||
bool Wait(uint32_t millis) { |
||||
return WaitForSingleObject(h, millis) == WAIT_OBJECT_0; |
||||
} |
||||
|
||||
protected: |
||||
HANDLE h; |
||||
|
||||
#elif !defined(_SUPPORT_CPP11) |
||||
// pthread implementation
|
||||
public: |
||||
Semaphore(unsigned c=0) : count(c) { pthread_cond_init(&cond, NULL); } |
||||
~Semaphore() { pthread_cond_destroy(&cond); } |
||||
|
||||
void Clear(unsigned c=0) { |
||||
cs.Clear(); |
||||
pthread_cond_destroy(&cond); |
||||
pthread_cond_init(&cond, NULL); |
||||
count = c; |
||||
} |
||||
|
||||
void Signal() { |
||||
Lock l(cs); |
||||
++count; |
||||
pthread_cond_signal(&cond); |
||||
} |
||||
void Signal(unsigned c) { |
||||
ASSERT(c > 0); |
||||
for (unsigned i=0; i<c; ++i) |
||||
Signal(); |
||||
} |
||||
|
||||
void Wait() { |
||||
Lock l(cs); |
||||
while (!count) |
||||
pthread_cond_wait(&cond, &cs.getMutex()); |
||||
--count; |
||||
} |
||||
bool Wait(uint32_t millis) { |
||||
Lock l(cs); |
||||
if (count == 0) { |
||||
timeval timev; |
||||
gettimeofday(&timev, NULL); |
||||
millis += timev.tv_usec/1000; |
||||
timespec t = { |
||||
timev.tv_sec + (millis/1000), |
||||
(millis%1000)*1000*1000 |
||||
}; |
||||
pthread_cond_timedwait(&cond, &cs.getMutex(), &t); |
||||
if (count == 0) |
||||
return false; |
||||
} |
||||
--count; |
||||
return true; |
||||
} |
||||
|
||||
protected: |
||||
pthread_cond_t cond; |
||||
CriticalSection cs; |
||||
unsigned count; |
||||
|
||||
#else |
||||
// C++11 implementation
|
||||
public: |
||||
Semaphore(unsigned c=0) : count(c) {} |
||||
~Semaphore() {} |
||||
|
||||
void Clear(unsigned c=0) { |
||||
std::lock_guard<std::mutex> lock{mtx}; |
||||
count = c; |
||||
} |
||||
|
||||
void Signal() { |
||||
std::lock_guard<std::mutex> lock{mtx}; |
||||
++count; |
||||
cv.notify_one(); |
||||
} |
||||
void Signal(unsigned c) { |
||||
ASSERT(c > 0); |
||||
for (unsigned i=0; i<c; ++i) |
||||
Signal(); |
||||
} |
||||
|
||||
void Wait() { |
||||
std::unique_lock<std::mutex> lock{mtx}; |
||||
cv.wait(lock, [&] { return count > 0; }); |
||||
--count; |
||||
} |
||||
bool Wait(uint32_t millis) { |
||||
std::unique_lock<std::mutex> lock{mtx}; |
||||
if (!cv.wait_for(lock, std::chrono::milliseconds(millis), [&] { return count > 0; })) |
||||
return false; |
||||
--count; |
||||
return true; |
||||
} |
||||
|
||||
protected: |
||||
std::condition_variable cv; |
||||
std::mutex mtx; |
||||
unsigned count; |
||||
#endif |
||||
|
||||
private: |
||||
Semaphore(const Semaphore&); |
||||
Semaphore& operator=(const Semaphore&); |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_SEMAPHORE_H__
|
||||
@ -0,0 +1,209 @@
@@ -0,0 +1,209 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// SharedPtr.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_SHAREDPTR_H__ |
||||
#define __SEACAVE_SHAREDPTR_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/**************************************************************************************
|
||||
* CSharedPtr template |
||||
* --------------- |
||||
* shared smart pointer |
||||
**************************************************************************************/ |
||||
|
||||
template<class TYPE> |
||||
struct SharedRef { |
||||
typedef TYPE TYPE_REF; |
||||
|
||||
#ifdef SEACAVE_NO_MULTITHREAD |
||||
TYPE_REF val; |
||||
#else |
||||
volatile TYPE_REF val; |
||||
#endif |
||||
|
||||
inline SharedRef() {} |
||||
inline SharedRef(TYPE_REF v) : val(v) {} |
||||
|
||||
inline TYPE_REF Inc() { |
||||
#ifdef SEACAVE_NO_MULTITHREAD |
||||
ASSERT(val >= 0); |
||||
return ++val; |
||||
#else |
||||
return Thread::safeInc(val); |
||||
#endif |
||||
} |
||||
inline TYPE_REF Dec() { |
||||
#ifdef SEACAVE_NO_MULTITHREAD |
||||
ASSERT(val > 0); |
||||
return --val; |
||||
#else |
||||
return Thread::safeDec(val); |
||||
#endif |
||||
} |
||||
|
||||
#ifdef _USE_BOOST |
||||
// serialize
|
||||
template <class Archive> |
||||
void serialize(Archive& ar, const unsigned int /*version*/) { |
||||
#ifdef SEACAVE_NO_MULTITHREAD |
||||
ar & val; |
||||
#else |
||||
ar & (TYPE_REF&)val; |
||||
#endif |
||||
} |
||||
#endif |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template<class TYPE> |
||||
class CSharedPtr |
||||
{ |
||||
protected: |
||||
typedef SharedRef<int32_t> TYPE_REF; |
||||
typedef TYPE* TYPE_PTR; |
||||
|
||||
public: |
||||
inline CSharedPtr() : m_pointer(NULL), m_pNoRef(NULL) |
||||
{ // empty construct
|
||||
} |
||||
|
||||
inline explicit CSharedPtr(TYPE_PTR _Ptr) : m_pointer(_Ptr), m_pNoRef(_Ptr ? new TYPE_REF(1) : NULL) |
||||
{ // construct from object pointer
|
||||
} |
||||
|
||||
inline CSharedPtr(const CSharedPtr& _Right) : m_pointer(_Right.m_pointer), m_pNoRef(_Right.m_pNoRef) |
||||
{ // construct by assuming pointer from _Right CSharedPtr
|
||||
IncRef(); |
||||
} |
||||
|
||||
inline ~CSharedPtr() |
||||
{ // destroy the object
|
||||
DecRef(); |
||||
} |
||||
|
||||
CSharedPtr& operator=(const CSharedPtr& _Right) |
||||
{ // assign compatible _Right (assume pointer)
|
||||
if (this != &_Right) |
||||
{ |
||||
ASSERT(m_pointer != _Right.m_pointer || m_pNoRef == _Right.m_pNoRef); |
||||
DecRef(); |
||||
m_pointer = _Right.m_pointer; |
||||
m_pNoRef = _Right.m_pNoRef; |
||||
IncRef(); |
||||
} |
||||
return (*this); |
||||
} |
||||
|
||||
CSharedPtr& operator=(TYPE_PTR _Ptr) |
||||
{ // assign compatible _Right (assume pointer)
|
||||
if (m_pointer != _Ptr) |
||||
{ |
||||
DecRef(); |
||||
m_pointer = _Ptr; |
||||
m_pNoRef = (_Ptr ? new TYPE_REF(1) : NULL); |
||||
} |
||||
return (*this); |
||||
} |
||||
|
||||
inline TYPE& operator*() const |
||||
{ // return designated value
|
||||
ASSERT(m_pointer); |
||||
return (*m_pointer); |
||||
} |
||||
|
||||
inline TYPE* operator->() const |
||||
{ // return pointer to class object
|
||||
ASSERT(m_pointer); |
||||
return m_pointer; |
||||
} |
||||
|
||||
inline operator TYPE_PTR() const |
||||
{ // return pointer to class object
|
||||
return m_pointer; |
||||
} |
||||
|
||||
inline bool operator==(const CSharedPtr& _Right) const |
||||
{ // return pointer to class object
|
||||
return (m_pointer == _Right.m_pointer); |
||||
} |
||||
|
||||
inline bool operator!=(const CSharedPtr& _Right) const |
||||
{ // return pointer to class object
|
||||
return (m_pointer != _Right.m_pointer); |
||||
} |
||||
|
||||
inline bool operator==(const void* _Right) const |
||||
{ // return pointer to class object
|
||||
return (m_pointer == _Right); |
||||
} |
||||
|
||||
inline bool operator!=(const void* _Right) const |
||||
{ // return pointer to class object
|
||||
return (m_pointer != _Right); |
||||
} |
||||
|
||||
void Release() |
||||
{ // release pointer
|
||||
DecRef(); |
||||
m_pointer = NULL; |
||||
m_pNoRef = NULL; |
||||
} |
||||
|
||||
protected: |
||||
inline void IncRef() |
||||
{ |
||||
if (m_pointer == NULL) |
||||
return; |
||||
ASSERT(m_pNoRef); |
||||
m_pNoRef->Inc(); |
||||
} |
||||
|
||||
inline void DecRef() |
||||
{ |
||||
if (m_pointer == NULL) |
||||
return; |
||||
ASSERT(m_pNoRef); |
||||
if (m_pNoRef->Dec() == 0) |
||||
{ |
||||
delete m_pointer; |
||||
m_pointer = NULL; |
||||
delete m_pNoRef; |
||||
m_pNoRef = NULL; |
||||
} |
||||
} |
||||
|
||||
TYPE_PTR m_pointer; // the wrapped object pointer
|
||||
TYPE_REF* m_pNoRef; // number of references to this pointer
|
||||
|
||||
#ifdef _USE_BOOST |
||||
protected: |
||||
// implement BOOST serialization
|
||||
friend class boost::serialization::access; |
||||
template <class Archive> |
||||
void serialize(Archive& ar, const unsigned int /*version*/) { |
||||
ar & m_pointer; |
||||
ar & m_pNoRef; |
||||
} |
||||
#endif |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_SHAREDPTR_H__
|
||||
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Sphere.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_SPHERE_H__ |
||||
#define __SEACAVE_SPHERE_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
// Basic sphere class
|
||||
template <typename TYPE, int DIMS> |
||||
class TSphere |
||||
{ |
||||
STATIC_ASSERT(DIMS > 1 && DIMS <= 3); |
||||
|
||||
public: |
||||
typedef TYPE Type; |
||||
typedef Eigen::Matrix<TYPE,DIMS,1> POINT; |
||||
|
||||
POINT center; // sphere center point
|
||||
TYPE radius; // sphere radius
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
inline TSphere() {} |
||||
inline TSphere(const POINT& c, TYPE r) : center(c), radius(r) {} |
||||
inline TSphere(const POINT& p1, const POINT& p2, const POINT& p3); |
||||
|
||||
inline void Set(const POINT& c, TYPE r); |
||||
inline void Set(const POINT& p1, const POINT& p2, const POINT& p3); |
||||
|
||||
inline void Enlarge(TYPE); |
||||
inline void EnlargePercent(TYPE); |
||||
|
||||
inline GCLASS Classify(const POINT&) const; |
||||
}; // class TSphere
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
#include "Sphere.inl" |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_SPHERE_H__
|
||||
@ -0,0 +1,80 @@
@@ -0,0 +1,80 @@
|
||||
//////////////////////////////////////////////////////////////////// |
||||
// Sphere.inl |
||||
// |
||||
// Copyright 2007 cDc@seacave |
||||
// Distributed under the Boost Software License, Version 1.0 |
||||
// (See http://www.boost.org/LICENSE_1_0.txt) |
||||
|
||||
|
||||
// D E F I N E S /////////////////////////////////////////////////// |
||||
|
||||
|
||||
// S T R U C T S /////////////////////////////////////////////////// |
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline TSphere<TYPE,DIMS>::TSphere(const POINT& p1, const POINT& p2, const POINT& p3) |
||||
{ |
||||
Set(p1, p2, p3); |
||||
} // constructor |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline void TSphere<TYPE,DIMS>::Set(const POINT& c, TYPE r) |
||||
{ |
||||
center = c; |
||||
radius = r; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TSphere<TYPE, DIMS>::Set(const POINT& p1, const POINT& p2, const POINT& p3) |
||||
{ |
||||
// compute relative distances |
||||
TYPE A((p1 - p2).squaredNorm()); |
||||
TYPE B((p2 - p3).squaredNorm()); |
||||
TYPE C((p3 - p1).squaredNorm()); |
||||
|
||||
// re-orient triangle (make A longest side) |
||||
const POINT *a(&p3), *b(&p1), *c(&p2); |
||||
if (B < C) std::swap(B, C), std::swap(b, c); |
||||
if (A < B) std::swap(A, B), std::swap(a, b); |
||||
|
||||
// if obtuse |
||||
if (B + C <= A) { |
||||
// just use longest diameter |
||||
radius = SQRT(A) / TYPE(2); |
||||
center = (*b + *c) / TYPE(2); |
||||
} else { |
||||
// otherwise circumscribe (http://en.wikipedia.org/wiki/Circumscribed_circle) |
||||
const TYPE cos_a(SQUARE(B + C - A) / (B*C*TYPE(4))); |
||||
radius = SQRT(A / ((TYPE(1) - cos_a)*TYPE(4))); |
||||
const POINT alpha(*a - *c), beta(*b - *c); |
||||
const POINT alphaXbeta(alpha.cross(beta)); |
||||
center = (beta * alpha.squaredNorm() - alpha * beta.squaredNorm()).cross(alphaXbeta) / |
||||
(alphaXbeta.squaredNorm() * TYPE(2)) + *c; |
||||
} |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
template <typename TYPE, int DIMS> |
||||
inline void TSphere<TYPE,DIMS>::Enlarge(TYPE x) |
||||
{ |
||||
radius += x; |
||||
} |
||||
template <typename TYPE, int DIMS> |
||||
inline void TSphere<TYPE,DIMS>::EnlargePercent(TYPE x) |
||||
{ |
||||
radius *= x; |
||||
} // Enlarge |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
// Classify point to sphere. |
||||
template <typename TYPE, int DIMS> |
||||
inline GCLASS TSphere<TYPE, DIMS>::Classify(const POINT& p) const |
||||
{ |
||||
if ((center - p).squaredNorm() > SQUARE(radius)) |
||||
return CULLED; |
||||
return VISIBLE; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
@ -0,0 +1,244 @@
@@ -0,0 +1,244 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Streams.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_STREAMS_H__ |
||||
#define __SEACAVE_STREAMS_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "AutoPtr.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
#define SIZE_NA ((size_f_t)-1) |
||||
#define STREAM_ERROR ((size_t)-1) |
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
class InputStream; |
||||
typedef InputStream ISTREAM; |
||||
|
||||
class OutputStream; |
||||
typedef OutputStream OSTREAM; |
||||
|
||||
class IOStream; |
||||
typedef IOStream IOSTREAM; |
||||
|
||||
class GENERAL_API NOINITVTABLE Stream { |
||||
public: |
||||
virtual ~Stream() { } |
||||
|
||||
// Identify stream type
|
||||
virtual InputStream* getInputStream(int) { return NULL; } |
||||
virtual OutputStream* getOutputStream(int) { return NULL; } |
||||
virtual IOStream* getIOStream(int, int) { return NULL; } |
||||
|
||||
// Get the length of the stream.
|
||||
// SIZE_NA if there were errors.
|
||||
virtual size_f_t getSize() const = 0; |
||||
// Position the stream at the given offset from the beginning.
|
||||
virtual bool setPos(size_f_t pos) = 0; |
||||
// Get the current position in the stream (offset from the beginning).
|
||||
// SIZE_NA if there were errors.
|
||||
virtual size_f_t getPos() const = 0; |
||||
}; |
||||
|
||||
class GENERAL_API NOINITVTABLE InputStream : public Stream { |
||||
public: |
||||
virtual ~InputStream() { } |
||||
/**
|
||||
* Call this function until it returns 0 to get all bytes. |
||||
* @return The number of bytes read. len reflects the number of bytes |
||||
* actually read from the stream source in this call. |
||||
* STREAM_ERROR if there were errors. |
||||
*/ |
||||
virtual size_t read(void* buf, size_t len) = 0; |
||||
|
||||
enum { LAYER_ID_IN=0 }; |
||||
InputStream* getInputStream(int typ=LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? this : (InputStream*)NULL); } |
||||
}; |
||||
|
||||
class GENERAL_API NOINITVTABLE OutputStream : public Stream { |
||||
public: |
||||
virtual ~OutputStream() { } |
||||
|
||||
/**
|
||||
* @return The actual number of bytes written. len bytes will always be |
||||
* consumed, but fewer or more bytes may actually be written, |
||||
* for example if the stream is being compressed. |
||||
* STREAM_ERROR if there were errors. |
||||
*/ |
||||
virtual size_t write(const void* buf, size_t len) = 0; |
||||
inline size_t print(LPCTSTR szFormat, ...) { |
||||
va_list args; |
||||
va_start(args, szFormat); |
||||
TCHAR szBuffer[2048]; |
||||
const size_t len((size_t)_vsntprintf(szBuffer, 2048, szFormat, args)); |
||||
if (len > 2048) { |
||||
const size_t count((size_t)_vsctprintf(szFormat, args)); |
||||
ASSERT(count != (size_t)-1); |
||||
CAutoPtrArr<TCHAR> szBufferDyn(new TCHAR[count+1]); |
||||
_vsntprintf(szBufferDyn, count+1, szFormat, args); |
||||
va_end(args); |
||||
return write(szBufferDyn, count); |
||||
} |
||||
va_end(args); |
||||
return write(szBuffer, len); |
||||
} |
||||
inline size_t print(const std::string& str) { |
||||
return write(str.c_str(), str.length()); |
||||
} |
||||
template<class T> |
||||
inline OutputStream& operator<<(const T& val) { |
||||
std::ostringstream ostr; |
||||
ostr << val; |
||||
print(ostr.str()); |
||||
return *this; |
||||
} |
||||
/**
|
||||
* This must be called before destroying the object to make sure all data |
||||
* is properly written. Note that some implementations |
||||
* might not need it... |
||||
* |
||||
* @return The actual number of bytes written. |
||||
* STREAM_ERROR if there were errors. |
||||
*/ |
||||
virtual size_t flush() = 0; |
||||
|
||||
enum { LAYER_ID_OUT=0 }; |
||||
OutputStream* getOutputStream(int typ=LAYER_ID_OUT) override { return (typ == LAYER_ID_OUT ? this : (OutputStream*)NULL); } |
||||
}; |
||||
|
||||
class GENERAL_API NOINITVTABLE IOStream : public InputStream, public OutputStream { |
||||
public: |
||||
InputStream* getInputStream(int typ=LAYER_ID_IN) override { return InputStream::getInputStream(typ); } |
||||
OutputStream* getOutputStream(int typ=LAYER_ID_OUT) override { return OutputStream::getOutputStream(typ); } |
||||
IOStream* getIOStream(int typIn=LAYER_ID_IN, int typOut=LAYER_ID_OUT) override { |
||||
return ((InputStream*)this)->getInputStream(typIn) != NULL && |
||||
((OutputStream*)this)->getOutputStream(typOut) != NULL ? this : (IOStream*)NULL; |
||||
} |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
|
||||
template<bool managed=true> |
||||
class LayerInputStream : public InputStream { |
||||
public: |
||||
LayerInputStream(InputStream* aStream) : s(aStream) { ASSERT(s != NULL); } |
||||
virtual ~LayerInputStream() noexcept { if (managed) delete s; } |
||||
|
||||
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return s->getInputStream(typ); } |
||||
|
||||
size_t read(void* wbuf, size_t len) override { |
||||
return s->read(wbuf, len); |
||||
} |
||||
|
||||
size_f_t getSize() const override { |
||||
return s->getSize(); |
||||
} |
||||
|
||||
bool setPos(size_f_t wpos) override { |
||||
return s->setPos(wpos); |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
return s->getPos(); |
||||
} |
||||
|
||||
protected: |
||||
InputStream* const s; |
||||
}; |
||||
|
||||
template<bool managed=true> |
||||
class LayerOutputStream : public OutputStream { |
||||
public: |
||||
LayerOutputStream(OutputStream* aStream) : s(aStream) {} |
||||
virtual ~LayerOutputStream() noexcept { if (managed) delete s; } |
||||
|
||||
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return s->getOutputStream(typ); } |
||||
|
||||
size_t write(const void* buf, size_t len) override { |
||||
return s->write(buf, len); |
||||
} |
||||
|
||||
size_t flush() override { |
||||
return s->flush(); |
||||
} |
||||
|
||||
size_f_t getSize() const override { |
||||
return s->getSize(); |
||||
} |
||||
|
||||
bool setPos(size_f_t wpos) override { |
||||
return s->setPos(wpos); |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
return s->getPos(); |
||||
} |
||||
|
||||
protected: |
||||
OutputStream* const s; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
|
||||
template<bool managed=true> |
||||
class LayerIOStream : public IOStream { |
||||
public: |
||||
LayerIOStream(InputStream* aStream) : s(aStream), sIn(aStream), sOut(NULL) { ASSERT(aStream != NULL); } |
||||
LayerIOStream(OutputStream* aStream) : s(aStream), sIn(NULL), sOut(aStream) { ASSERT(aStream != NULL); } |
||||
LayerIOStream(InputStream* streamIn, OutputStream* streamOut) : s(NULL), sIn(streamIn), sOut(streamOut) { ASSERT(sIn != NULL || sOut != NULL); } |
||||
virtual ~LayerIOStream() noexcept { if (managed) { delete sIn; delete sOut; } } |
||||
|
||||
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (sIn ? sIn->getInputStream(typ) : NULL); } |
||||
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return (sOut ? sOut->getOutputStream(typ) : NULL); } |
||||
|
||||
size_f_t getSize() const override { |
||||
return s->getSize(); |
||||
} |
||||
|
||||
bool setPos(size_f_t wpos) override { |
||||
return s->setPos(wpos); |
||||
} |
||||
|
||||
size_f_t getPos() const override { |
||||
return s->getPos(); |
||||
} |
||||
|
||||
size_t read(void* wbuf, size_t len) override { |
||||
return sIn->read(wbuf, len); |
||||
} |
||||
|
||||
size_t write(const void* buf, size_t len) override { |
||||
return sOut->write(buf, len); |
||||
} |
||||
|
||||
size_t flush() override { |
||||
return sOut->flush(); |
||||
} |
||||
|
||||
operator InputStream* () const { return sIn; } |
||||
operator OutputStream* () const { return sOut; } |
||||
|
||||
protected: |
||||
Stream* const s; |
||||
InputStream* const sIn; |
||||
OutputStream* const sOut; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_STREAMS_H__
|
||||
@ -0,0 +1,239 @@
@@ -0,0 +1,239 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Strings.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_STRING_H__ |
||||
#define __SEACAVE_STRING_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#include "Streams.h" |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/// String class: enhanced std::string
|
||||
class GENERAL_API String : public std::string |
||||
{ |
||||
public: |
||||
typedef std::string Base; |
||||
|
||||
public: |
||||
inline String() {} |
||||
inline String(LPCTSTR sz) : Base(sz) {} |
||||
inline String(const Base& str) : Base(str) {} |
||||
inline String(size_t n, value_type v) : Base(n, v) {} |
||||
inline String(LPCTSTR sz, size_t count) : Base(sz, count) {} |
||||
inline String(LPCTSTR sz, size_t offset, size_t count) : Base(sz, offset, count) {} |
||||
#ifdef _SUPPORT_CPP11 |
||||
inline String(Base&& rhs) : Base(std::forward<Base>(rhs)) {} |
||||
inline String(String&& rhs) : Base(std::forward<Base>(rhs)) {} |
||||
inline String(const String& rhs) : Base(rhs) {} |
||||
|
||||
inline String& operator=(Base&& rhs) { Base::operator=(std::forward<Base>(rhs)); return *this; } |
||||
inline String& operator=(String&& rhs) { Base::operator=(std::forward<Base>(rhs)); return *this; } |
||||
#endif |
||||
inline String& operator=(TCHAR rhs) { Base::operator=(rhs); return *this; } |
||||
inline String& operator=(LPCTSTR rhs) { Base::operator=(rhs); return *this; } |
||||
inline String& operator=(const String& rhs) { Base::operator=(rhs); return *this; } |
||||
|
||||
inline String& operator+=(TCHAR rhs) { *this = (*this) + rhs; return *this; } |
||||
inline String& operator+=(LPCTSTR rhs) { *this = (*this) + rhs; return *this; } |
||||
inline String& operator+=(const Base& rhs) { *this = (*this) + rhs; return *this; } |
||||
inline String& operator+=(const String& rhs) { *this = (*this) + rhs; return *this; } |
||||
|
||||
inline void Release() { return clear(); } |
||||
inline bool IsEmpty() const { return empty(); } |
||||
|
||||
inline operator LPCTSTR() const { return c_str(); } |
||||
|
||||
String& Format(LPCTSTR szFormat, ...) { |
||||
va_list args; |
||||
va_start(args, szFormat); |
||||
TCHAR szBuffer[2048]; |
||||
const size_t len((size_t)_vsntprintf(szBuffer, 2048, szFormat, args)); |
||||
if (len > 2048) { |
||||
*this = FormatStringSafe(szFormat, args); |
||||
va_end(args); |
||||
} else { |
||||
va_end(args); |
||||
this->assign(szBuffer, len); |
||||
} |
||||
return *this; |
||||
} |
||||
String& FormatSafe(LPCTSTR szFormat, ...) { |
||||
va_list args; |
||||
va_start(args, szFormat); |
||||
const size_t len((size_t)_vsctprintf(szFormat, args)); |
||||
ASSERT(len != (size_t)-1); |
||||
TCHAR* szBuffer(new TCHAR[len]); |
||||
_vsntprintf(szBuffer, len, szFormat, args); |
||||
va_end(args); |
||||
this->assign(szBuffer, len); |
||||
delete[] szBuffer; |
||||
return *this; |
||||
} |
||||
static String FormatString(LPCTSTR szFormat, ...) { |
||||
va_list args; |
||||
va_start(args, szFormat); |
||||
TCHAR szBuffer[2048]; |
||||
const size_t len((size_t)_vsntprintf(szBuffer, 2048, szFormat, args)); |
||||
if (len > 2048) { |
||||
const String str(FormatStringSafe(szFormat, args)); |
||||
va_end(args); |
||||
return str; |
||||
} |
||||
va_end(args); |
||||
return String(szBuffer, len); |
||||
} |
||||
static inline String FormatStringSafe(LPCTSTR szFormat, va_list args) { |
||||
const size_t len((size_t)_vsctprintf(szFormat, args)); |
||||
ASSERT(len != (size_t)-1); |
||||
TCHAR* szBuffer(new TCHAR[len]); |
||||
_vsntprintf(szBuffer, len, szFormat, args); |
||||
String str(szBuffer, len); |
||||
delete[] szBuffer; |
||||
return str; |
||||
} |
||||
|
||||
inline void ToUpper(String& out) const { |
||||
out.resize(size()); |
||||
std::transform(begin(), end(), out.begin(), [](TCHAR c) { return (TCHAR)std::toupper(c); }); |
||||
} |
||||
inline String ToUpper() const { |
||||
String str; |
||||
ToUpper(str); |
||||
return str; |
||||
} |
||||
|
||||
inline void ToLower(String& out) const { |
||||
out.resize(size()); |
||||
std::transform(begin(), end(), out.begin(), [](TCHAR c) { return (TCHAR)std::tolower(c); }); |
||||
} |
||||
inline String ToLower() const { |
||||
String str; |
||||
ToLower(str); |
||||
return str; |
||||
} |
||||
|
||||
inline void Save(OSTREAM& oStream) const { |
||||
const WORD nSize = (WORD)size(); |
||||
oStream.write(&nSize, sizeof(WORD)); |
||||
oStream.write(c_str(), nSize); |
||||
} |
||||
inline void Load(ISTREAM& oStream) { |
||||
WORD nSize; |
||||
oStream.read(&nSize, sizeof(WORD)); |
||||
if (nSize == 0) { |
||||
clear(); |
||||
return; |
||||
} |
||||
char* pBuffer = new char[nSize]; |
||||
oStream.read(pBuffer, nSize); |
||||
assign(pBuffer, nSize); |
||||
delete[] pBuffer; |
||||
} |
||||
|
||||
template <class T> |
||||
static String ToString(const T& val) { |
||||
std::ostringstream os; |
||||
os << val; |
||||
return os.str(); |
||||
} |
||||
template <class T> |
||||
static String ToStringHex(const T& val) { |
||||
std::ostringstream os; |
||||
os << std::hex << val; |
||||
return os.str(); |
||||
} |
||||
|
||||
template <class T> |
||||
static void FromString(const String& str, T& val) { |
||||
std::istringstream is(str); |
||||
is >> val; |
||||
} |
||||
template <class T> |
||||
static T FromString(const String& str, const T& def) { |
||||
T val(def); |
||||
FromString(str, val); |
||||
return val; |
||||
} |
||||
template <class T> |
||||
static T FromString(const String& str) { |
||||
T val; |
||||
FromString(str, val); |
||||
return val; |
||||
} |
||||
template <class T> |
||||
inline void From(T& val) const { |
||||
FromString(*this, val); |
||||
} |
||||
template <class T> |
||||
inline T From(const T& def) const { |
||||
T val(def); |
||||
From(val); |
||||
return val; |
||||
} |
||||
template <class T> |
||||
inline T From() const { |
||||
T val; |
||||
From(val); |
||||
return val; |
||||
} |
||||
|
||||
static int CompareAlphabetically(const void* elem, const void* key) { return _tcscmp(((const String*)elem)->c_str(), ((const String*)key)->c_str()); } |
||||
static int CompareAlphabeticallyInv(const void* elem, const void* key) { return _tcscmp(((const String*)key)->c_str(), ((const String*)elem)->c_str()); } |
||||
|
||||
#ifdef _USE_BOOST |
||||
protected: |
||||
// implement BOOST serialization
|
||||
friend class boost::serialization::access; |
||||
template<class Archive> |
||||
void serialize(Archive& ar, const unsigned int /*version*/) { |
||||
ar & boost::serialization::base_object<Base>(*this); |
||||
} |
||||
#endif |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
inline String operator+(const String& lhs, TCHAR rhs) { return std::operator+(lhs, rhs); } |
||||
inline String operator+(const String& lhs, LPCTSTR rhs) { return std::operator+(lhs, rhs); } |
||||
inline String operator+(const String& lhs, const std::string& rhs) { return std::operator+(lhs, rhs); } |
||||
inline String operator+(TCHAR lhs, const String& rhs) { return std::operator+(lhs, rhs); } |
||||
inline String operator+(LPCTSTR lhs, const String& rhs) { return std::operator+(lhs, rhs); } |
||||
inline String operator+(const std::string& lhs, const String& rhs) { return std::operator+(lhs, rhs); } |
||||
inline String operator+(const String& lhs, const String& rhs) { return std::operator+(lhs, rhs); } |
||||
#ifdef _SUPPORT_CPP11 |
||||
inline String operator+(String&& lhs, TCHAR rhs) { return std::operator+(std::forward<String::Base>(lhs), rhs); } |
||||
inline String operator+(String&& lhs, LPCTSTR rhs) { return std::operator+(std::forward<String::Base>(lhs), rhs); } |
||||
inline String operator+(String&& lhs, const std::string& rhs) { return std::operator+(std::forward<String::Base>(lhs), rhs); } |
||||
inline String operator+(TCHAR lhs, String&& rhs) { return std::operator+(lhs, std::forward<String::Base>(rhs)); } |
||||
inline String operator+(LPCTSTR lhs, String&& rhs) { return std::operator+(lhs, std::forward<String::Base>(rhs)); } |
||||
inline String operator+(const std::string& lhs, String&& rhs) { return std::operator+(lhs, std::forward<String::Base>(rhs)); } |
||||
inline String operator+(const String& lhs, String&& rhs) { return std::operator+(lhs, std::forward<String::Base>(rhs)); } |
||||
inline String operator+(String&& lhs, const String& rhs) { return std::operator+(std::forward<String::Base>(lhs), rhs); } |
||||
inline String operator+(String&& lhs, String&& rhs) { return std::operator+(std::forward<String::Base>(lhs), std::forward<String::Base>(rhs)); } |
||||
#endif |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
|
||||
namespace std { |
||||
//namespace tr1 {
|
||||
// Specializations for unordered containers
|
||||
template <> struct hash<SEACAVE::String> : public hash<string>{}; |
||||
//} // namespace tr1
|
||||
template <> struct equal_to<SEACAVE::String> : public equal_to<string>{}; |
||||
} // namespace std
|
||||
|
||||
#endif // __SEACAVE_STRING_H__
|
||||
@ -0,0 +1,435 @@
@@ -0,0 +1,435 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Thread.h
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef __SEACAVE_THREAD_H__ |
||||
#define __SEACAVE_THREAD_H__ |
||||
|
||||
|
||||
// I N C L U D E S /////////////////////////////////////////////////
|
||||
|
||||
#ifdef _MSC_VER |
||||
#include <windows.h> |
||||
#ifdef _SUPPORT_CPP11 |
||||
#include <cstdint> |
||||
#else |
||||
#include <stdint.h> |
||||
#endif |
||||
#else |
||||
#include <pthread.h> |
||||
#include <sched.h> |
||||
#include <sys/resource.h> |
||||
#include <unistd.h> |
||||
#endif |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace SEACAVE { |
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/**************************************************************************************
|
||||
* Thread |
||||
* -------------- |
||||
* basic thread control |
||||
**************************************************************************************/ |
||||
|
||||
class GENERAL_API Thread |
||||
{ |
||||
public: |
||||
#ifdef _ENVIRONMENT64 |
||||
typedef int64_t safe_t; |
||||
#else |
||||
typedef int32_t safe_t; |
||||
#endif |
||||
|
||||
enum Priority { |
||||
IDLE = -2, |
||||
LOW = -1, |
||||
NORMAL = 0, |
||||
HIGH = 1 |
||||
}; |
||||
|
||||
#ifdef _MSC_VER |
||||
|
||||
typedef HANDLE thread_t; |
||||
typedef void* (STCALL *FncStart)(void*); |
||||
|
||||
typedef struct STARTER_DATA { |
||||
FncStart fnStarter; |
||||
void* pData; |
||||
STARTER_DATA(FncStart _fnStarter, void* _pData) : fnStarter(_fnStarter), pData(_pData) {} |
||||
} StarterData; |
||||
|
||||
Thread() : threadHandle(NULL), threadId(0) {} |
||||
virtual ~Thread() { stop(); } |
||||
|
||||
inline bool isRunning() const { return (threadHandle != NULL); } |
||||
|
||||
bool start(FncStart pfnStarter, void* pData=NULL) { |
||||
join(); |
||||
return ((threadHandle = CreateThread(NULL, 0, &starterConvertor, new StarterData(pfnStarter, pData), 0, &threadId)) != NULL); |
||||
} |
||||
inline bool start() { |
||||
return start(&starter, this); |
||||
} |
||||
void stop() { |
||||
if (threadHandle) { |
||||
TerminateThread(threadHandle, -1); |
||||
CloseHandle(threadHandle); |
||||
threadHandle = NULL; |
||||
threadId = 0; |
||||
} |
||||
} |
||||
void join() { |
||||
if (threadHandle == NULL) |
||||
return; |
||||
WaitForSingleObject(threadHandle, INFINITE); |
||||
CloseHandle(threadHandle); |
||||
threadHandle = NULL; |
||||
threadId = 0; |
||||
} |
||||
|
||||
inline void setThreadPriority(Priority p) const { ::SetThreadPriority(threadHandle, convertPriority(p)); } |
||||
inline Priority getThreadPriority() const { return convertPriority((PriorityOS)::GetThreadPriority(threadHandle)); } |
||||
static inline void setThreadPriority(thread_t th, Priority p) { ::SetThreadPriority(th, convertPriority(p)); } |
||||
static inline Priority getThreadPriority(thread_t th) { return convertPriority((PriorityOS)::GetThreadPriority(th)); } |
||||
|
||||
static inline void sleep(uint32_t millis) { ::Sleep(millis); } |
||||
static inline void yield() { ::Sleep(0); } |
||||
static inline thread_t currentThread() { return ::GetCurrentThread(); } |
||||
static uint32_t hardwareConcurrency() { |
||||
SYSTEM_INFO info={{0}}; |
||||
GetSystemInfo(&info); |
||||
return info.dwNumberOfProcessors; |
||||
} |
||||
|
||||
STATIC_ASSERT(sizeof(int32_t)==sizeof(LONG)); |
||||
static inline int32_t safeInc(volatile int32_t& v) { return InterlockedIncrement((volatile LONG*)&v); }; |
||||
static inline int32_t safeDec(volatile int32_t& v) { return InterlockedDecrement((volatile LONG*)&v); }; |
||||
static inline int32_t safeExchange(volatile int32_t& target, int32_t value) { return InterlockedExchange((volatile LONG*)&target, value); }; |
||||
static inline int32_t safeCompareExchange(volatile int32_t& target, int32_t comp, int32_t value) { return InterlockedCompareExchange((volatile LONG*)&target, value, comp); }; |
||||
|
||||
STATIC_ASSERT(sizeof(int64_t)==sizeof(LONGLONG)); |
||||
static inline int64_t safeInc(volatile int64_t& v) { return InterlockedIncrement64((volatile LONGLONG*)&v); }; |
||||
static inline int64_t safeDec(volatile int64_t& v) { return InterlockedDecrement64((volatile LONGLONG*)&v); }; |
||||
static inline int64_t safeExchange(volatile int64_t& target, int64_t value) { return InterlockedExchange64((volatile LONGLONG*)&target, value); }; |
||||
static inline int64_t safeCompareExchange(volatile int64_t& target, int64_t comp, int64_t value) { return InterlockedCompareExchange64((volatile LONGLONG*)&target, value, comp); }; |
||||
|
||||
#else //_MSC_VER
|
||||
|
||||
typedef pthread_t thread_t; |
||||
typedef void* (STCALL *FncStart)(void*); |
||||
|
||||
Thread() : threadHandle(0) {} |
||||
virtual ~Thread() { stop(); } |
||||
|
||||
inline bool isRunning() const { return (threadHandle != 0); } |
||||
|
||||
bool start(FncStart pfnStarter, void* pData=NULL) { |
||||
join(); |
||||
return (pthread_create(&threadHandle, NULL, pfnStarter, pData) == 0); |
||||
} |
||||
bool start() { |
||||
return start(&starter, this); |
||||
} |
||||
void stop() { |
||||
if (threadHandle != 0) { |
||||
pthread_detach(threadHandle); |
||||
threadHandle = 0; |
||||
} |
||||
} |
||||
void join() { |
||||
if (threadHandle) { |
||||
pthread_join(threadHandle, 0); |
||||
threadHandle = 0; |
||||
} |
||||
} |
||||
|
||||
inline void setThreadPriority(Priority p) const { setThreadPriority(threadHandle, p); } |
||||
inline Priority getThreadPriority() const { return getThreadPriority(threadHandle); } |
||||
static void setThreadPriority(thread_t th, Priority p) { |
||||
struct sched_param param; |
||||
param.sched_priority = convertPriority(p); |
||||
pthread_setschedparam(th, SCHED_OTHER, ¶m); |
||||
} |
||||
static Priority getThreadPriority(thread_t th) { |
||||
struct sched_param param; |
||||
int policy; |
||||
pthread_getschedparam(th, &policy, ¶m); |
||||
return convertPriority((PriorityOS)param.sched_priority); |
||||
} |
||||
|
||||
static void sleep(uint32_t millis) { ::usleep(millis*1000); } |
||||
static void yield() { ::sched_yield(); } |
||||
static inline thread_t currentThread(){ return pthread_self(); } |
||||
static uint32_t hardwareConcurrency() { return sysconf(_SC_NPROCESSORS_ONLN); } |
||||
|
||||
static inline int32_t safeInc(volatile int32_t& v) { return __sync_add_and_fetch(&v, 1); } |
||||
static inline int32_t safeDec(volatile int32_t& v) { return __sync_sub_and_fetch(&v, 1); } |
||||
static inline int32_t safeExchange(volatile int32_t& target, int32_t value) { return __sync_val_compare_and_swap(&target, target, value); } |
||||
static inline int32_t safeCompareExchange(volatile int32_t& target, int32_t comp, int32_t value) { return __sync_val_compare_and_swap(&target, comp, value); } |
||||
|
||||
static inline int64_t safeInc(volatile int64_t& v) { return __sync_add_and_fetch(&v, 1); } |
||||
static inline int64_t safeDec(volatile int64_t& v) { return __sync_sub_and_fetch(&v, 1); } |
||||
static inline int64_t safeExchange(volatile int64_t& target, int64_t value) { return __sync_val_compare_and_swap(&target, target, value); } |
||||
static inline int64_t safeCompareExchange(volatile int64_t& target, int64_t comp, int64_t value) { return __sync_val_compare_and_swap(&target, comp, value); } |
||||
|
||||
#endif //_MSC_VER
|
||||
|
||||
static unsigned getMaxThreads(unsigned threads) { |
||||
if (threads == 1) |
||||
return 1; |
||||
const unsigned maxThreads = hardwareConcurrency(); |
||||
if (threads > 0 && threads < maxThreads) |
||||
return threads; |
||||
return maxThreads; |
||||
} |
||||
|
||||
protected: |
||||
virtual void run() {} |
||||
|
||||
protected: |
||||
#ifdef _MSC_VER |
||||
|
||||
enum PriorityOS { |
||||
OS_IDLE = THREAD_PRIORITY_IDLE, |
||||
OS_LOW = THREAD_PRIORITY_BELOW_NORMAL, |
||||
OS_NORMAL = THREAD_PRIORITY_NORMAL, |
||||
OS_HIGH = THREAD_PRIORITY_ABOVE_NORMAL |
||||
}; |
||||
|
||||
static DWORD WINAPI starterConvertor(void* p) |
||||
{ |
||||
CAutoPtr<StarterData> pData((StarterData*)p); |
||||
return (DWORD)reinterpret_cast<size_t>(pData->fnStarter(pData->pData)); |
||||
} |
||||
|
||||
#else //_MSC_VER
|
||||
|
||||
enum PriorityOS { |
||||
OS_IDLE = 19, |
||||
OS_LOW = 10, |
||||
OS_NORMAL = 0, |
||||
OS_HIGH = -10 |
||||
}; |
||||
|
||||
#endif //_MSC_VER
|
||||
|
||||
static inline PriorityOS convertPriority(Priority p) { |
||||
switch (p) { |
||||
case IDLE: return OS_IDLE; |
||||
case LOW: return OS_LOW; |
||||
case NORMAL: return OS_NORMAL; |
||||
case HIGH: return OS_HIGH; |
||||
} |
||||
return OS_NORMAL; |
||||
} |
||||
static inline Priority convertPriority(PriorityOS p) { |
||||
switch (p) { |
||||
case OS_IDLE: return IDLE; |
||||
case OS_LOW: return LOW; |
||||
case OS_NORMAL: return NORMAL; |
||||
case OS_HIGH: return HIGH; |
||||
} |
||||
return NORMAL; |
||||
} |
||||
|
||||
static void* starter(void* p) |
||||
{ |
||||
((Thread*)p)->run(); |
||||
return NULL; |
||||
} |
||||
|
||||
protected: |
||||
thread_t threadHandle; |
||||
#ifdef _MSC_VER |
||||
DWORD threadId; |
||||
#endif |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
|
||||
/**************************************************************************************
|
||||
* ThreadPool |
||||
* -------------- |
||||
* basic thread pool |
||||
**************************************************************************************/ |
||||
|
||||
class GENERAL_API ThreadPool |
||||
{ |
||||
public: |
||||
typedef uint32_t size_type; |
||||
typedef Thread value_type; |
||||
typedef value_type* iterator; |
||||
typedef const value_type* const_iterator; |
||||
typedef value_type& reference; |
||||
typedef const value_type& const_reference; |
||||
typedef CLISTDEFIDX(Thread,size_type) Threads; |
||||
|
||||
public: |
||||
inline ThreadPool() {} |
||||
inline ThreadPool(size_type nThreads) : _threads(nThreads>0?nThreads:Thread::hardwareConcurrency()) {} |
||||
inline ThreadPool(size_type nThreads, Thread::FncStart pfnStarter, void* pData=NULL) : _threads(nThreads>0?nThreads:Thread::hardwareConcurrency()) { start(pfnStarter, pData); } |
||||
inline ~ThreadPool() { join(); } |
||||
|
||||
#ifdef _SUPPORT_CPP11 |
||||
inline ThreadPool(ThreadPool&& rhs) : _threads(std::forward<Threads>(rhs._threads)) { |
||||
} |
||||
|
||||
inline ThreadPool& operator=(ThreadPool&& rhs) { |
||||
_threads.Swap(rhs._threads); |
||||
return *this; |
||||
} |
||||
#endif |
||||
|
||||
// wait for all running threads to finish and resize threads array
|
||||
void resize(size_type nThreads) { |
||||
join(); |
||||
_threads.resize(nThreads); |
||||
} |
||||
// start all threads with the given function
|
||||
bool start(Thread::FncStart pfnStarter, void* pData=NULL) { |
||||
for (Thread& thread: _threads) |
||||
if (!thread.start(pfnStarter, pData)) |
||||
return false; |
||||
return true; |
||||
} |
||||
// wait for all running threads to finish
|
||||
void join() { |
||||
for (Thread& thread: _threads) |
||||
thread.join(); |
||||
} |
||||
// stop all threads
|
||||
void stop() { |
||||
for (Thread& thread: _threads) |
||||
thread.stop(); |
||||
} |
||||
// wait for all running threads to finish and release threads array
|
||||
void Release() { |
||||
join(); |
||||
_threads.Release(); |
||||
} |
||||
|
||||
inline bool empty() const { return _threads.empty(); } |
||||
inline size_type size() const { return _threads.size(); } |
||||
inline const_iterator cbegin() const { return _threads.cbegin(); } |
||||
inline const_iterator cend() const { return _threads.cend(); } |
||||
inline const_iterator begin() const { return _threads.begin(); } |
||||
inline const_iterator end() const { return _threads.end(); } |
||||
inline iterator begin() { return _threads.begin(); } |
||||
inline iterator end() { return _threads.end(); } |
||||
inline const_reference operator[](size_type index) const { return _threads[index]; } |
||||
inline reference operator[](size_type index) { return _threads[index]; } |
||||
|
||||
protected: |
||||
Threads _threads; |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
|
||||
/**************************************************************************************
|
||||
* Process |
||||
* -------------- |
||||
* basic process control |
||||
**************************************************************************************/ |
||||
|
||||
class GENERAL_API Process |
||||
{ |
||||
public: |
||||
enum Priority { |
||||
IDLE = -3, |
||||
LOW = -2, |
||||
BELOWNORMAL = -1, |
||||
NORMAL = 0, |
||||
ABOVENORMAL = 1, |
||||
HIGH = 2, |
||||
REALTIME = 3 |
||||
}; |
||||
|
||||
#ifdef _MSC_VER |
||||
|
||||
typedef HANDLE process_t; |
||||
|
||||
static inline process_t getCurrentProcessID() { return ::GetCurrentProcess(); } |
||||
static inline void setProcessPriority(process_t id, Priority p) { ::SetPriorityClass(id, convertPriority(p)); } |
||||
static inline Priority getProcessPriority(process_t id) { return convertPriority((PriorityOS)::GetPriorityClass(id)); } |
||||
|
||||
#else //_MSC_VER
|
||||
|
||||
typedef id_t process_t; |
||||
|
||||
static inline process_t getCurrentProcessID() { return ::getpid(); } |
||||
static inline void setProcessPriority(process_t id, Priority p) { ::setpriority(PRIO_PROCESS, id, convertPriority(p)); } |
||||
static inline Priority getProcessPriority(process_t id) { return convertPriority((PriorityOS)::getpriority(PRIO_PROCESS, id)); } |
||||
|
||||
#endif //_MSC_VER
|
||||
|
||||
static inline void setCurrentProcessPriority(Priority p) { setProcessPriority(getCurrentProcessID(), p); } |
||||
static inline Priority getCurrentProcessPriority() { return getProcessPriority(getCurrentProcessID()); } |
||||
|
||||
protected: |
||||
|
||||
#ifdef _MSC_VER |
||||
|
||||
enum PriorityOS { |
||||
OS_IDLE = IDLE_PRIORITY_CLASS, |
||||
OS_LOW = PROCESS_MODE_BACKGROUND_BEGIN, |
||||
OS_BELOWNORMAL = BELOW_NORMAL_PRIORITY_CLASS, |
||||
OS_NORMAL = NORMAL_PRIORITY_CLASS, |
||||
OS_ABOVENORMAL = ABOVE_NORMAL_PRIORITY_CLASS, |
||||
OS_HIGH = HIGH_PRIORITY_CLASS, |
||||
OS_REALTIME = REALTIME_PRIORITY_CLASS |
||||
}; |
||||
|
||||
#else //_MSC_VER
|
||||
|
||||
enum PriorityOS { |
||||
OS_IDLE = 19, |
||||
OS_LOW = 15, |
||||
OS_BELOWNORMAL = 10, |
||||
OS_NORMAL = 0, |
||||
OS_ABOVENORMAL = -10, |
||||
OS_HIGH = -15, |
||||
OS_REALTIME = -20 |
||||
}; |
||||
|
||||
#endif //_MSC_VER
|
||||
|
||||
static inline PriorityOS convertPriority(Priority p) { |
||||
switch (p) { |
||||
case IDLE: return OS_IDLE; |
||||
case LOW: return OS_LOW; |
||||
case BELOWNORMAL: return OS_BELOWNORMAL; |
||||
case NORMAL: return OS_NORMAL; |
||||
case ABOVENORMAL: return OS_ABOVENORMAL; |
||||
case HIGH: return OS_HIGH; |
||||
case REALTIME: return OS_REALTIME; |
||||
} |
||||
return OS_NORMAL; |
||||
} |
||||
static inline Priority convertPriority(PriorityOS p) { |
||||
switch (p) { |
||||
case OS_IDLE: return IDLE; |
||||
case OS_LOW: return LOW; |
||||
case OS_BELOWNORMAL: return BELOWNORMAL; |
||||
case OS_NORMAL: return NORMAL; |
||||
case OS_ABOVENORMAL: return ABOVENORMAL; |
||||
case OS_HIGH: return HIGH; |
||||
case OS_REALTIME: return REALTIME; |
||||
} |
||||
return NORMAL; |
||||
} |
||||
}; |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
} // namespace SEACAVE
|
||||
|
||||
#endif // __SEACAVE_THREAD_H__
|
||||
@ -0,0 +1,165 @@
@@ -0,0 +1,165 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Timer.cpp
|
||||
//
|
||||
// Copyright 2007 cDc@seacave
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// (See http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "Common.h" |
||||
#include "Timer.h" |
||||
|
||||
using namespace SEACAVE; |
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Constructor |
||||
*/ |
||||
Timer::Timer(uint8_t nHH, uint8_t nMM) |
||||
{ |
||||
m_fElapsedTime = 1.f; |
||||
m_fTimeScale = 1.f; |
||||
#ifdef FIX_FPS |
||||
m_fCurrentFramesTime = 0.f; |
||||
m_nCurrentFrames = 0; |
||||
m_nLastSecFrames = 0; |
||||
#endif // FIX_FPS
|
||||
|
||||
// set clock
|
||||
m_fClock = 0.f; |
||||
if (nHH > 23) |
||||
nHH = 0; |
||||
if (nMM > 59) |
||||
nMM = 0; |
||||
m_nHH = nHH; |
||||
m_nMM = nMM; |
||||
m_nSS = 0; |
||||
|
||||
m_nCrntTime = GetSysTime(); |
||||
} // constructor
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
Timer::~Timer() |
||||
{ |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Counts the actual time and adjusts clock. |
||||
*/ |
||||
void Timer::Update() |
||||
{ |
||||
const SysType timeNow = GetSysTime(); |
||||
m_fElapsedTime = SysTime2Time(timeNow - m_nCrntTime); |
||||
m_nCrntTime = timeNow; |
||||
|
||||
#ifdef FIX_FPS |
||||
// compute FPS
|
||||
++m_nCurrentFrames; |
||||
if ((m_fCurrentFramesTime += m_fElapsedTime) >= 1.f) { |
||||
m_nLastSecFrames = (uint32_t)((Type)m_nCurrentFrames/m_fCurrentFramesTime); |
||||
m_fCurrentFramesTime = 0.f; |
||||
m_nCurrentFrames = 0; |
||||
} |
||||
#endif // FIX_FPS
|
||||
|
||||
// adjust clock by seconds passed
|
||||
m_fClock += (m_fElapsedTime * m_fTimeScale); |
||||
if (m_fClock >= 1.f) { |
||||
m_nSS++; |
||||
m_fClock = 0.f; |
||||
} |
||||
if (m_nSS >= 60) { |
||||
m_nMM++; |
||||
m_nSS = 0; |
||||
} |
||||
if (m_nMM >= 60) { |
||||
m_nHH++; |
||||
m_nMM = 0; |
||||
} |
||||
if (m_nHH >= 24) |
||||
m_nHH = 0; |
||||
} // Update
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Sets the clock to any given starting time. |
||||
*/ |
||||
void Timer::SetClock(uint8_t nHH, uint8_t nMM) |
||||
{ |
||||
// set clock
|
||||
if (nHH > 23) |
||||
nHH = 0; |
||||
if (nMM > 59) |
||||
nMM = 0; |
||||
|
||||
m_nHH = nHH; |
||||
m_nMM = nMM; |
||||
} // SetClock
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
/**
|
||||
* Gives you a time string with hours, minutes and seconds as return |
||||
* and hours and/or minutes as reference parameters. |
||||
*/ |
||||
LPTSTR Timer::GetClock(uint8_t* nHH, uint8_t* nMM, LPTSTR szChar) const |
||||
{ |
||||
if (nHH != NULL) |
||||
*nHH = m_nHH; |
||||
if (nMM != NULL) |
||||
*nMM = m_nMM; |
||||
|
||||
if (szChar == NULL) |
||||
return NULL; |
||||
|
||||
_sntprintf(szChar, 32, "%.2d:%.2d:%.2d", m_nHH, m_nMM, m_nSS); |
||||
return szChar; |
||||
} // GetClock
|
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
|
||||
#ifdef TIMER_OLDSUPPORT |
||||
// Check performance counter
|
||||
bool HasPerfCounter() |
||||
{ |
||||
SysType nPerfCnt; |
||||
return QueryPerformanceFrequency((LARGE_INTEGER*)&nPerfCnt) == TRUE; |
||||
} |
||||
const bool Timer::ms_bPerfFlag = HasPerfCounter(); |
||||
/*----------------------------------------------------------------*/ |
||||
#endif |
||||
|
||||
|
||||
// Get system time factor used to convert to milliseconds
|
||||
Timer::Type GetSysTimeFactor() |
||||
{ |
||||
#ifdef _MSC_VER |
||||
Timer::SysType nPerfCnt; |
||||
const BOOL bPerfFlag = QueryPerformanceFrequency((LARGE_INTEGER*)&nPerfCnt); |
||||
#ifdef TIMER_OLDSUPPORT |
||||
// either QueryPerformanceCounter or GetTickCount
|
||||
return (bPerfFlag ? 1000.f / nPerfCnt : 1.f); |
||||
#else |
||||
ASSERT(bPerfFlag); |
||||
return 1000.f / nPerfCnt; |
||||
#endif |
||||
#else // _MSC_VER
|
||||
return 0.001f; |
||||
#endif // _MSC_VER
|
||||
} |
||||
const Timer::Type Timer::ms_fTimeFactor = GetSysTimeFactor(); |
||||
/*----------------------------------------------------------------*/ |
||||
|
||||
Timer::Type Timer::GetTimeFactor() |
||||
{ |
||||
return ms_fTimeFactor; |
||||
} |
||||
/*----------------------------------------------------------------*/ |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue