提交scripts
This commit is contained in:
1 parent
82caf72036
commit
07dabbd186
224 files changed
+130146
No files matched your search
@@ -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_
|
||||
Reference in new issue
Block a user