224 changed files with 130146 additions and 0 deletions
@ -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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
# 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
//////////////////////////////////////////////////////////////////// |
||||||
|
// 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 @@ |
|||||||
|
#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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
# 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
/** \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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
//////////////////////////////////////////////////////////////////// |
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
//////////////////////////////////////////////////////////////////// |
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
//////////////////////////////////////////////////////////////////// |
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
//////////////////////////////////////////////////////////////////// |
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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