mirror of
https://github.com/opencv/opencv.git
synced 2026-09-25 04:09:57 +03:00
ptcloud: cv::viz3d OpenGL point-cloud/mesh visualization
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
set(the_description "High level point cloud and mesh operations")
|
||||
|
||||
set(debug_modules "")
|
||||
if(DEBUG_opencv_ptcloud)
|
||||
list(APPEND debug_modules opencv_highgui)
|
||||
endif()
|
||||
ocv_define_module(ptcloud opencv_geometry opencv_imgproc opencv_features opencv_video ${debug_modules}
|
||||
# viz3d visualization lives here and imports the window + OpenGL context from highgui.
|
||||
ocv_define_module(ptcloud opencv_geometry opencv_imgproc opencv_features opencv_video opencv_flann opencv_highgui
|
||||
WRAP java objc python js
|
||||
)
|
||||
ocv_target_link_libraries(${the_module} ${LAPACK_LIBRARIES})
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "opencv2/ptcloud/odometry_frame.hpp"
|
||||
#include "opencv2/ptcloud/odometry_settings.hpp"
|
||||
#include "opencv2/ptcloud/slam.hpp"
|
||||
#include "opencv2/ptcloud/viz3d.hpp"
|
||||
|
||||
/**
|
||||
@defgroup ptcloud Point Cloud Processing
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_PTCLOUD_VIZ3D_HPP
|
||||
#define OPENCV_PTCLOUD_VIZ3D_HPP
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cv { namespace viz3d {
|
||||
|
||||
//! Render modes for cv::viz3d::showBox / cv::viz3d::showPlane / cv::viz3d::showSphere
|
||||
enum RenderMode
|
||||
{
|
||||
RENDER_SIMPLE = 0,
|
||||
RENDER_SHADING = 1,
|
||||
RENDER_WIREFRAME = 2,
|
||||
};
|
||||
|
||||
/** @brief Sets the view's perspective on a viz3d window.
|
||||
@param win_name Name of the viz3d window.
|
||||
@param fov Vertical field of view in radians.
|
||||
@param z_near Minimum clip distance.
|
||||
@param z_far Maximum clip distance.
|
||||
*/
|
||||
CV_EXPORTS_W void setPerspective(const String& win_name, float fov, float z_near, float z_far);
|
||||
|
||||
/** @brief Hides or shows the grid on a viz3d window.
|
||||
@param win_name Name of the viz3d window.
|
||||
@param visible Should the grid be shown?
|
||||
*/
|
||||
CV_EXPORTS_W void setGridVisible(const String& win_name, bool visible);
|
||||
|
||||
/** @brief Sets the properties of the light used on a viz3d window.
|
||||
@param win_name Name of the viz3d window.
|
||||
@param direction Direction any point to the sun.
|
||||
@param ambient Ambient light color.
|
||||
@param diffuse Diffuse light color.
|
||||
*/
|
||||
CV_EXPORTS_W void setSun(const String& win_name, const Vec3f& direction, const Vec3f& ambient, const Vec3f& diffuse);
|
||||
|
||||
/** @brief Sets the properties of the sky used on a viz3d window.
|
||||
@param win_name Name of the viz3d window.
|
||||
@param color Sky color.
|
||||
*/
|
||||
CV_EXPORTS_W void setSky(const String& win_name, const Vec3f& color);
|
||||
|
||||
/** @brief Shows a box object in the specified viz3d window. See cv::viz3d::destroyObject.
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param size Box size.
|
||||
@param color Box color.
|
||||
@param mode Render mode.
|
||||
*/
|
||||
CV_EXPORTS_W void showBox(const String& win_name, const String& obj_name, const Vec3f& size, const Vec3f& color, RenderMode mode = RENDER_SIMPLE);
|
||||
|
||||
/** @brief Shows a plane object in the specified viz3d window. See cv::viz3d::destroyObject.
|
||||
|
||||
The place faces the positive Y axis by default.
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param size Plane size.
|
||||
@param color Plane color.
|
||||
@param mode Render mode.
|
||||
*/
|
||||
CV_EXPORTS_W void showPlane(const String& win_name, const String& obj_name, const Vec2f& size, const Vec3f& color, RenderMode mode = RENDER_SIMPLE);
|
||||
|
||||
/** @brief Shows a sphere object in the specified viz3d window. See cv::viz3d::destroyObject.
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param radius Sphere radius.
|
||||
@param color Sphere color.
|
||||
@param mode Render mode.
|
||||
@param divs The higher this integer is the more detail (triangles) the sphere has.
|
||||
*/
|
||||
CV_EXPORTS_W void showSphere(const String& win_name, const String& obj_name, float radius, const Vec3f& color, RenderMode mode = RENDER_SIMPLE, int divs = 3);
|
||||
|
||||
/** @brief Shows a camera trajectory object in the specified viz3d window. See cv::viz3d::destroyObject.
|
||||
|
||||
The camera trajectory data array must be 2D. Each row has a width of 6, where the first
|
||||
3 components are the position and the second 3 components are the direction of the camera.
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param trajectory Camera trajectory data.
|
||||
@param aspect Aspect ratio of the camera frustum.
|
||||
@param scale Scale applied to camera frustums.
|
||||
@param frustum_color Color of the frustums.
|
||||
@param line_color Color of the line.
|
||||
*/
|
||||
// Not CV_EXPORTS_W: the auto-bindings generator mishandles the two trailing
|
||||
// Vec3f defaults (like the other Vec3f-taking viz3d functions, it stays C++-only).
|
||||
CV_EXPORTS void showCameraTrajectory(
|
||||
const String& win_name, const String& obj_name, InputArray trajectory,
|
||||
float aspect, float scale, Vec3f frustum_color = Vec3f(1.0f, 1.0f, 1.0f),
|
||||
Vec3f line_color = Vec3f(0.5f, 0.5f, 0.5f));
|
||||
|
||||
/** @brief Shows a mesh object in the specified viz3d window. See cv::viz3d::destroyObject.
|
||||
|
||||
The vertices array must be 2D. If shading is disabled (the default option), each row has a width of
|
||||
3 (xyz), 6 (xyz, rgb) or 9 (xyz, rgb, uvw) where uvw is the normal, so that each row represents a vertex.
|
||||
Shading is only enabled when using normals.
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param verts Vertices input array.
|
||||
@param indices Indices input array.
|
||||
*/
|
||||
CV_EXPORTS void showMesh(const String& win_name, const String& obj_name, InputArray verts, InputArray indices);
|
||||
|
||||
/** @overload Shows a mesh object in the specified viz3d window. See cv::viz3d::destroyObject.
|
||||
|
||||
The vertices array must be 2D, where each row has a width of 3 (xyz), 6 (xyz, rgb) or 9 (xyz, rgb, uvw)
|
||||
where uvw is the normal, so that each row represents a vertex. Shading is only enabled when using normals.
|
||||
Points are grouped in triplets to make triangles (points 1, 2 and 3, points 4, 5 and 6, etc).
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param verts Vertices input array.
|
||||
@param indices Indices input array.
|
||||
*/
|
||||
CV_EXPORTS_AS(showMesh2) void showMesh(const String& win_name, const String& obj_name, InputArray verts);
|
||||
|
||||
/** @brief Shows a point cloud object in the specified viz3d window. See cv::viz3d::destroyObject.
|
||||
|
||||
The points array must be 2D, where each row has either a width of 6 (xyz, rgb), so that
|
||||
each row represents a point.
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param points Vertices input array.
|
||||
*/
|
||||
CV_EXPORTS_W void showPoints(const String& win_name, const String& obj_name, InputArray points);
|
||||
|
||||
/** @brief Shows a colored depth map as a point cloud in the specified viz3d window. See cv::viz3d::destroyObject.
|
||||
|
||||
The colored depth map must be a 2D image with 4 channels (RGBD). The points are created assuming
|
||||
that positive depth means positive position in the Z axis.
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param img Input colored depth map.
|
||||
@param intrinsics Camera intrinsics matrix.
|
||||
@param scale Scale applied to the coordinates of the points.
|
||||
*/
|
||||
CV_EXPORTS_W void showRGBD(const String& win_name, const String& obj_name, InputArray img, const Matx33f& intrinsics, float scale = 1.0f);
|
||||
|
||||
/** @brief Shows a lines object in the specified viz3d window. See cv::viz3d::destroyObject.
|
||||
|
||||
The points array must be 2D, where each row has either a width of 6 (xyz, rgb), so that
|
||||
each row represents a point. Points are grouped in pairs to make lines (points 1 and 2,
|
||||
points 3 and 4, etc).
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param points Vertices input array.
|
||||
*/
|
||||
CV_EXPORTS_W void showLines(const String& win_name, const String& obj_name, InputArray points);
|
||||
|
||||
/** @brief Sets an object's position in the specified viz3d window.
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param position New position.
|
||||
*/
|
||||
CV_EXPORTS_W void setObjectPosition(const String& win_name, const String& obj_name, const Vec3f& position);
|
||||
|
||||
/** @brief Sets an object's rotation in the specified viz3d window.
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
@param rotation New rotation in euler angles (radians).
|
||||
*/
|
||||
CV_EXPORTS_W void setObjectRotation(const String& win_name, const String& obj_name, const Vec3f& rotation);
|
||||
|
||||
/** @brief Destroys an object created with cv::viz3d::showMesh or cv::viz3d::showPoints.
|
||||
|
||||
@param win_name Name of the viz3d window.
|
||||
@param obj_name Name of the object.
|
||||
*/
|
||||
CV_EXPORTS_W void destroyObject(const String& win_name, const String& obj_name);
|
||||
|
||||
} // namespace viz3d
|
||||
} // namespace cv
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include <fstream>
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/ptcloud.hpp> // cv::viz3d now lives in the ptcloud module
|
||||
|
||||
using namespace cv;
|
||||
|
||||
Mat loadPoints(const String& path)
|
||||
{
|
||||
Mat points;
|
||||
std::ifstream ifs(path);
|
||||
|
||||
int _;
|
||||
float x, y, z, r, g, b;
|
||||
Vec3f c = Vec3f::all(0.0f);
|
||||
int count;
|
||||
|
||||
std::string str;
|
||||
std::getline(ifs, str);
|
||||
|
||||
for (count = 0; ifs >> _ && ifs >> x && ifs >> z && ifs >> y && ifs >> r && ifs >> g && ifs >> b; ++count)
|
||||
{
|
||||
y = -y;
|
||||
float data[] = { x, y, z, r / 255.0f, g / 255.0f, b / 255.0f };
|
||||
points.push_back(Mat(Size(6, 1), CV_32F, &data));
|
||||
c += Vec3f(x, y, z);
|
||||
}
|
||||
|
||||
c /= count;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
for (int j = 0; j < 3; ++j)
|
||||
points.at<float>(i, j) -= c(j);
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
float verts_data[] = {
|
||||
-0.5, -0.5, 0.0,
|
||||
-0.5, +0.5, 0.0,
|
||||
+0.5, +0.5, 0.0,
|
||||
+0.5, -0.5, 0.0,
|
||||
};
|
||||
|
||||
Mat verts_mat = Mat(Size(3, 4), CV_32F, &verts_data);
|
||||
|
||||
int indices_data[] = {
|
||||
0, 1, 2,
|
||||
2, 3, 0,
|
||||
};
|
||||
|
||||
Mat indices_mat = Mat(Size(3, 2), CV_32S, &indices_data);
|
||||
|
||||
float point_data[] = {
|
||||
-0.5, -0.5, 0.0, 1.0f, 0.0f, 0.0f,
|
||||
-0.5, +0.5, 0.0, 0.0f, 1.0f, 0.0f,
|
||||
+0.5, +0.5, 0.0, 0.0f, 0.0f, 1.0f,
|
||||
+0.5, -0.5, 0.0, 1.0f, 1.0f, 1.0f,
|
||||
};
|
||||
|
||||
Mat points_mat = Mat(Size(6, 4), CV_32F, &point_data);
|
||||
|
||||
float trajectory_data[] = {
|
||||
-0.0, +5.0, +0.0, -1.0f, -1.0f, -1.0f,
|
||||
-5.0, +4.0, +2.0, +0.0f, +0.0f, -1.0f,
|
||||
-10.0, +3.0, +5.0, -0.5f, +0.2f, -0.5f,
|
||||
};
|
||||
|
||||
Mat trajectory_mat = Mat(Size(6, 3), CV_32F, &trajectory_data);
|
||||
|
||||
// Images taken from https://rgbd-dataset.cs.washington.edu
|
||||
// Papers that need to be cited to use this data:
|
||||
// [1] N. Silberman, D. Hoiem, P. Kohli, R. Fergus. Indoor segmentation and support inference from rgbd images. In ECCV, 2012.
|
||||
// [2] A.Janoch, S.Karayev, Y.Jia, J.T.Barron, M.Fritz, K.Saenko, and T.Darrell.A category - level 3 - d object dataset : Putting the kinect to work.In ICCV Workshop on Consumer Depth Cameras for Computer Vision, 2011.
|
||||
// [3] J.Xiao, A.Owens, and A.Torralba.SUN3D : A database of big spaces reconstructed using SfMand object labels.In ICCV, 2013
|
||||
Mat rgbd_components[] = {
|
||||
imread(samples::findFile("rgbd-color.jpg"), IMREAD_COLOR),
|
||||
imread(samples::findFile("rgbd-depth.png"), IMREAD_ANYDEPTH | IMREAD_ANYCOLOR)
|
||||
};
|
||||
|
||||
rgbd_components[0].convertTo(rgbd_components[0], CV_32F);
|
||||
rgbd_components[1].convertTo(rgbd_components[1], CV_32F);
|
||||
|
||||
Mat rgbd;
|
||||
merge(rgbd_components, 2, rgbd);
|
||||
cvtColor(rgbd, rgbd, COLOR_BGRA2RGBA);
|
||||
|
||||
// Point cloud data taken from https://sketchfab.com/3d-models/anthidium-forcipatum-point-cloud-3493da15a8db4f34929fc38d9d0fcb2c
|
||||
Mat bee_mat = loadPoints(samples::findFile("anthidium-forcipatum.csv"));
|
||||
|
||||
// Show two instances of the same example mesh
|
||||
viz3d::showMesh("viz3d", "mesh1", verts_mat, indices_mat);
|
||||
viz3d::showMesh("viz3d", "mesh2", verts_mat, indices_mat);
|
||||
viz3d::setObjectPosition("viz3d", "mesh1", { -2.0f, 0.0f, 0.0f });
|
||||
viz3d::setObjectPosition("viz3d", "mesh2", { 2.0f, 0.0f, 0.0f });
|
||||
|
||||
// Show an example point cloud
|
||||
viz3d::showPoints("viz3d", "points", points_mat);
|
||||
|
||||
// Show a bee point cloud
|
||||
viz3d::showPoints("viz3d", "bee", bee_mat);
|
||||
viz3d::setObjectPosition("viz3d", "bee", { 0.0f, 0.0f, 5.0f });
|
||||
|
||||
// Show RGBD image as points
|
||||
viz3d::showRGBD("rgbd", "rgbd", rgbd, {
|
||||
529.5f, 0.0f, 365.0f,
|
||||
0.0f, 529.5f, 265.0f,
|
||||
0.0f, 0.0f, 1.0f
|
||||
}, 0.1f);
|
||||
viz3d::setObjectPosition("rgbd", "rgbd", { 0.0f, 0.0f, 0.0f });
|
||||
viz3d::setGridVisible("rgbd", true);
|
||||
|
||||
// Show a solid box
|
||||
viz3d::showBox("viz3d", "box1", { 0.25f, 0.25f, 0.25f }, { 0.5f, 1.0f, 0.5f });
|
||||
viz3d::setObjectPosition("viz3d", "box1", { -5.0f, 0.0f, -5.0f });
|
||||
|
||||
// Show 3 wireframe boxes
|
||||
viz3d::showBox("viz3d", "box2", { 1.0f, 0.5f, 0.5f }, { 1.0f, 0.5f, 0.5f }, viz3d::RENDER_SIMPLE);
|
||||
viz3d::showBox("viz3d", "box3", { 0.5f, 1.0f, 0.5f }, { 0.5f, 1.0f, 0.5f }, viz3d::RENDER_WIREFRAME);
|
||||
viz3d::showBox("viz3d", "box4", { 0.5f, 0.5f, 1.0f }, { 0.5f, 0.5f, 1.0f}, viz3d::RENDER_SHADING);
|
||||
viz3d::setObjectPosition("viz3d", "box2", { 5.0f, 0.0f, 5.0f });
|
||||
viz3d::setObjectPosition("viz3d", "box3", { -5.0f, 0.0f, 5.0f });
|
||||
viz3d::setObjectPosition("viz3d", "box4", { -5.0f, 0.0f, 0.0f });
|
||||
|
||||
// Show a solid sphere
|
||||
viz3d::showSphere("viz3d", "sphere1", 1.0f, { 0.7f, 0.9f, 0.7f }, viz3d::RENDER_SHADING);
|
||||
viz3d::setObjectPosition("viz3d", "sphere1", { 0.0f, 0.0f, -5.0f });
|
||||
|
||||
// Show wireframe sphere
|
||||
viz3d::showSphere("viz3d", "sphere2", 1.0f, { 0.7f, 0.9f, 0.7f }, viz3d::RENDER_WIREFRAME);
|
||||
viz3d::setObjectPosition("viz3d", "sphere2", { 5.0f, 0.0f, 0.0f });
|
||||
|
||||
// Show plane
|
||||
viz3d::showPlane("viz3d", "plane1", { 1.5f, 1.0f }, { 0.8f, 0.5f, 0.3f });
|
||||
viz3d::setObjectPosition("viz3d", "plane1", { 5.0f, 0.0f, -5.0f });
|
||||
|
||||
viz3d::showCameraTrajectory("viz3d", "trajectory", trajectory_mat, 1.5f, 0.2f);
|
||||
|
||||
float x = 0.0f;
|
||||
while (waitKey(16) != 27)
|
||||
{
|
||||
// Animate objects
|
||||
viz3d::setObjectRotation("viz3d", "mesh1", { x, 0.0f, 0.0f });
|
||||
viz3d::setObjectRotation("viz3d", "mesh2", { 0.0f, 0.0f, x });
|
||||
x += 0.01f;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_PTCLOUD_VIZ3D_GRID_TICKS_HPP
|
||||
#define OPENCV_PTCLOUD_VIZ3D_GRID_TICKS_HPP
|
||||
|
||||
namespace cv { namespace viz3d { namespace detail {
|
||||
|
||||
// Grid line spacing, snapped so dist_scale / tick_step stays in [2, 4].
|
||||
// Pure math (no OpenGL) so it is unit-testable headless. Terminates for any
|
||||
// finite dist_scale (regression guard for the old logf(1.0)=0 -> inf hang).
|
||||
inline float gridTickStep(float dist_scale)
|
||||
{
|
||||
float tick_step = 1.0f;
|
||||
while (dist_scale / tick_step > 4.0f)
|
||||
tick_step *= 2.0f;
|
||||
while (tick_step > 1e-6f && dist_scale / tick_step < 2.0f)
|
||||
tick_step *= 0.5f;
|
||||
return tick_step;
|
||||
}
|
||||
|
||||
}}} // namespace cv::viz3d::detail
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,1592 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "viz3d_private.hpp"
|
||||
#include "grid_ticks.hpp"
|
||||
#include "opencv2/core/utils/logger.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
namespace cv { namespace viz3d {
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
|
||||
static void openGlDrawCallback(void* data)
|
||||
{
|
||||
Window* win = static_cast<Window*>(data);
|
||||
if (win)
|
||||
win->draw();
|
||||
}
|
||||
|
||||
static void openGlFreeCallback(void* data)
|
||||
{
|
||||
Window* win = static_cast<Window*>(data);
|
||||
if (win)
|
||||
delete win;
|
||||
}
|
||||
|
||||
static void mouseCallback(int event, int x, int y, int flags, void* data)
|
||||
{
|
||||
Window* win = static_cast<Window*>(data);
|
||||
if (win)
|
||||
win->onMouse(event, x, y, flags);
|
||||
}
|
||||
|
||||
static Window* getWindow(const String& win_name)
|
||||
{
|
||||
namedWindow(win_name, WINDOW_OPENGL);
|
||||
const double useGl = getWindowProperty(win_name, WND_PROP_OPENGL);
|
||||
if (useGl <= 0)
|
||||
CV_Error(cv::Error::StsBadArg, "OpenCV/UI: viz3d can't be used because the window was created without an OpenGL context");
|
||||
|
||||
Window* win = static_cast<Window*>(getOpenGlUserData(win_name));
|
||||
|
||||
if (!win)
|
||||
{
|
||||
setOpenGlContext(win_name);
|
||||
std::unique_ptr<Window> new_win(new Window(win_name));
|
||||
setOpenGlDrawCallback(win_name, &openGlDrawCallback, new_win.get());
|
||||
setOpenGlFreeCallback(win_name, &openGlFreeCallback);
|
||||
setMouseCallback(win_name, &mouseCallback, new_win.get());
|
||||
win = new_win.release();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto callback = getOpenGlDrawCallback(win_name);
|
||||
if (callback && callback != openGlDrawCallback)
|
||||
CV_Error(cv::Error::StsBadArg, "OpenCV/UI: viz3d can't be used because the OpenGL callback is already being used on this window");
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
// Generates vertices for a box.
|
||||
static void generateBox(Mat& mat, const Vec3f& size, const Vec3f& color, RenderMode mode)
|
||||
{
|
||||
if (mode == RENDER_SHADING)
|
||||
mat.create(6 * 6, 9, CV_32F); // 6 faces, each with 6 vertices, and 9 floats per vertex (position + color + normal)
|
||||
else if (mode == RENDER_SIMPLE)
|
||||
mat.create(6 * 6, 6, CV_32F); // 6 faces, each with 6 vertices, and 6 floats per vertex (position + color)
|
||||
else if (mode == RENDER_WIREFRAME)
|
||||
mat.create(8 * 6, 6, CV_32F); // 6 faces, each with 8 vertices, and 6 floats per vertex (position + color)
|
||||
int next_row = 0;
|
||||
|
||||
for (int s = -1; s <= 1; s += 2) // For each side
|
||||
for (int a = 0; a < 3; ++a) // For each axis
|
||||
{
|
||||
// Get the normal vector.
|
||||
Vec3f normal = Vec3f::all(0.0f);
|
||||
normal((a + 2) % 3) = static_cast<float>(s);
|
||||
|
||||
// Get offset vectors
|
||||
Vec3f offset_x, offset_y, offset_z;
|
||||
offset_x = offset_y = offset_z = Vec3f::all(0.0f);
|
||||
offset_x((a + 0) % 3) = size((a + 0) % 3);
|
||||
offset_y((a + 1) % 3) = size((a + 1) % 3);
|
||||
offset_z((a + 2) % 3) = static_cast<float>(s) * size((a + 2) % 3);
|
||||
|
||||
// Generate vertex positions
|
||||
Vec3f positions[8];
|
||||
int count = 0;
|
||||
|
||||
if (mode == RENDER_WIREFRAME) {
|
||||
// Face line segments
|
||||
count = 8;
|
||||
positions[0] = offset_z - offset_x - offset_y;
|
||||
positions[1] = offset_z + offset_x - offset_y;
|
||||
positions[2] = offset_z + offset_x - offset_y;
|
||||
positions[3] = offset_z + offset_x + offset_y;
|
||||
positions[4] = offset_z + offset_x + offset_y;
|
||||
positions[5] = offset_z - offset_x + offset_y;
|
||||
positions[6] = offset_z - offset_x + offset_y;
|
||||
positions[7] = offset_z - offset_x - offset_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Face triangles
|
||||
count = 6;
|
||||
positions[0] = offset_z - offset_x - offset_y;
|
||||
positions[1] = offset_z + offset_x - offset_y;
|
||||
positions[2] = offset_z + offset_x + offset_y;
|
||||
positions[3] = offset_z + offset_x + offset_y;
|
||||
positions[4] = offset_z - offset_x + offset_y;
|
||||
positions[5] = offset_z - offset_x - offset_y;
|
||||
}
|
||||
|
||||
// Add the vertices
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
mat.at<float>(next_row, 0) = positions[i](0);
|
||||
mat.at<float>(next_row, 1) = positions[i](1);
|
||||
mat.at<float>(next_row, 2) = positions[i](2);
|
||||
mat.at<float>(next_row, 3) = color(0);
|
||||
mat.at<float>(next_row, 4) = color(1);
|
||||
mat.at<float>(next_row, 5) = color(2);
|
||||
if (mode == RENDER_SHADING)
|
||||
{
|
||||
mat.at<float>(next_row, 6) = normal(0);
|
||||
mat.at<float>(next_row, 7) = normal(1);
|
||||
mat.at<float>(next_row, 8) = normal(2);
|
||||
}
|
||||
next_row += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generates vertices for a plane.
|
||||
static void generatePlane(Mat& mat, const Vec2f& size, const Vec3f& color, RenderMode mode)
|
||||
{
|
||||
if (mode == RENDER_SHADING)
|
||||
mat.create(6, 9, CV_32F); // 6 vertices, and 9 floats per vertex (position + color + normal)
|
||||
else if (mode == RENDER_SIMPLE)
|
||||
mat.create(6, 6, CV_32F); // 6 vertices, and 6 floats per vertex (position + color)
|
||||
else if (mode == RENDER_WIREFRAME)
|
||||
mat.create(8, 6, CV_32F); // 8 vertices, and 6 floats per vertex (position + color)
|
||||
int next_row = 0;
|
||||
|
||||
// Get offset vectors
|
||||
Vec3f offset_x = Vec3f(size(0), 0.0f, 0.0f);
|
||||
Vec3f offset_y = Vec3f(0.0f, 0.0f, size(1));
|
||||
|
||||
// Generate vertex positions
|
||||
Vec3f positions[8];
|
||||
int count = 0;
|
||||
|
||||
if (mode == RENDER_WIREFRAME) {
|
||||
// Quad line segments
|
||||
count = 8;
|
||||
positions[0] = - offset_x - offset_y;
|
||||
positions[1] = offset_x - offset_y;
|
||||
positions[2] = offset_x - offset_y;
|
||||
positions[3] = offset_x + offset_y;
|
||||
positions[4] = offset_x + offset_y;
|
||||
positions[5] = - offset_x + offset_y;
|
||||
positions[6] = - offset_x + offset_y;
|
||||
positions[7] = - offset_x - offset_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Quad triangles
|
||||
count = 6;
|
||||
positions[0] = - offset_x - offset_y;
|
||||
positions[1] = offset_x - offset_y;
|
||||
positions[2] = offset_x + offset_y;
|
||||
positions[3] = offset_x + offset_y;
|
||||
positions[4] = - offset_x + offset_y;
|
||||
positions[5] = - offset_x - offset_y;
|
||||
}
|
||||
|
||||
// Add the vertices
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
mat.at<float>(next_row, 0) = positions[i](0);
|
||||
mat.at<float>(next_row, 1) = positions[i](1);
|
||||
mat.at<float>(next_row, 2) = positions[i](2);
|
||||
mat.at<float>(next_row, 3) = color(0);
|
||||
mat.at<float>(next_row, 4) = color(1);
|
||||
mat.at<float>(next_row, 5) = color(2);
|
||||
if (mode == RENDER_SHADING)
|
||||
{
|
||||
mat.at<float>(next_row, 6) = 0.0f;
|
||||
mat.at<float>(next_row, 7) = 1.0f;
|
||||
mat.at<float>(next_row, 8) = 0.0f;
|
||||
}
|
||||
next_row += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_OPENGL
|
||||
|
||||
void setPerspective(const String& win_name, float fov, float z_near, float z_far)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(fov);
|
||||
CV_UNUSED(z_near);
|
||||
CV_UNUSED(z_far);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
win->getView().setPerspective(fov, z_near, z_far);
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setGridVisible(const String& win_name, bool visible)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(visible);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
win->setGridVisible(visible);
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setSun(const String& win_name, const Vec3f& direction, const Vec3f& ambient, const Vec3f& diffuse)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(direction);
|
||||
CV_UNUSED(ambient);
|
||||
CV_UNUSED(diffuse);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
win->setSun(direction, ambient, diffuse);
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setSky(const String& win_name, const Vec3f& color)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(color);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
win->setSky(color);
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void showBox(const String& win_name, const String& obj_name, const Vec3f& size, const Vec3f& color, RenderMode mode)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(size);
|
||||
CV_UNUSED(color);
|
||||
CV_UNUSED(mode);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Mat mat;
|
||||
generateBox(mat, size, color, mode);
|
||||
if (mode == RENDER_WIREFRAME)
|
||||
showLines(win_name, obj_name, mat);
|
||||
else
|
||||
showMesh(win_name, obj_name, mat);
|
||||
#endif
|
||||
}
|
||||
|
||||
void showPlane(const String& win_name, const String& obj_name, const Vec2f& size, const Vec3f& color, RenderMode mode)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(size);
|
||||
CV_UNUSED(color);
|
||||
CV_UNUSED(mode);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Mat mat;
|
||||
generatePlane(mat, size, color, mode);
|
||||
if (mode == RENDER_WIREFRAME)
|
||||
showLines(win_name, obj_name, mat);
|
||||
else
|
||||
showMesh(win_name, obj_name, mat);
|
||||
#endif
|
||||
}
|
||||
|
||||
void showSphere(const String& win_name, const String& obj_name, float radius, const Vec3f& color, RenderMode mode, int divs)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(radius);
|
||||
CV_UNUSED(color);
|
||||
CV_UNUSED(mode);
|
||||
CV_UNUSED(divs);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
CV_Assert(divs >= 1);
|
||||
|
||||
if (mode == RENDER_WIREFRAME)
|
||||
{
|
||||
static const float PI = 3.14159265359f;
|
||||
static const float LIMIT = 2.0f * PI;
|
||||
|
||||
std::vector<float> points_data;
|
||||
points_data.reserve(3 * divs * 4 * 2 * 6); // 3 axis, divs * 4 segments per axis, 2 points per segment, 6 values per point
|
||||
|
||||
for (int e = 0; e < 3; ++e)
|
||||
{
|
||||
auto ex = Vec3f::zeros();
|
||||
auto ey = Vec3f::zeros();
|
||||
ex((e + 0) % 3) = radius;
|
||||
ey((e + 1) % 3) = radius;
|
||||
|
||||
for (float t = 0.0f, n; t < LIMIT;)
|
||||
{
|
||||
n = t + LIMIT / (divs * 4);
|
||||
auto p1 = ex * cosf(t) + ey * sinf(t);
|
||||
auto p2 = ex * cosf(n) + ey * sinf(n);
|
||||
points_data.insert(points_data.end(), {
|
||||
p1(0), p1(1), p1(2), color(0), color(1), color(2),
|
||||
p2(0), p2(1), p2(2), color(0), color(1), color(2),
|
||||
});
|
||||
t = n;
|
||||
}
|
||||
}
|
||||
|
||||
const Mat points_mat = Mat(Size(6, static_cast<int>(points_data.size() / 6)), CV_32F, points_data.data());
|
||||
|
||||
showLines(win_name, obj_name, points_mat);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<float> verts_data;
|
||||
if (mode == RENDER_SHADING)
|
||||
verts_data.reserve(6 * divs * divs * 4 * 9); // 6 sides divs * divs * 4 quads per side, 6 vertices per face, 9 values per vertex
|
||||
else
|
||||
verts_data.reserve(6 * divs * divs * 4 * 6); // 6 sides, divs * divs * 4 quads per side, 6 vertices per face, 6 values per vertex
|
||||
|
||||
for (int s = -1; s <= 1; s += 2)
|
||||
for (int e = 0; e < 3; ++e)
|
||||
{
|
||||
auto ex = Vec3f::all(0.0f);
|
||||
auto ey = Vec3f::all(0.0f);
|
||||
auto pz = Vec3f::all(0.0f);
|
||||
ex((e + 1) % 3) = 1.0f / divs;
|
||||
ey((e + 2) % 3) = 1.0f / divs;
|
||||
pz(e) = s;
|
||||
|
||||
for (int x = -divs; x < divs; ++x)
|
||||
for (int y = -divs; y < divs; ++y)
|
||||
{
|
||||
// Quad positions
|
||||
Vec3f positions[6] = {
|
||||
(x + 0) * ex + (y + 0) * ey + pz,
|
||||
(x + 0) * ex + (y + 1) * ey + pz,
|
||||
(x + 1) * ex + (y + 1) * ey + pz,
|
||||
(x + 1) * ex + (y + 1) * ey + pz,
|
||||
(x + 1) * ex + (y + 0) * ey + pz,
|
||||
(x + 0) * ex + (y + 0) * ey + pz,
|
||||
};
|
||||
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
verts_data.insert(verts_data.end(), {
|
||||
positions[i](0), positions[i](1), positions[i](2),
|
||||
color(0), color(1), color(2),
|
||||
});
|
||||
if (mode == RENDER_SHADING)
|
||||
verts_data.insert(verts_data.end(), { 0.0f, 0.0f, 0.0f });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == RENDER_SIMPLE)
|
||||
{
|
||||
for (int i = 0; i < (int)(verts_data.size() / 6); ++i)
|
||||
{
|
||||
float l = sqrtf(verts_data[6 * i + 0] * verts_data[6 * i + 0] + verts_data[6 * i + 1] * verts_data[6 * i + 1] + verts_data[6 * i + 2] * verts_data[6 * i + 2]);
|
||||
float r = radius / l;
|
||||
verts_data[6 * i + 0] *= r;
|
||||
verts_data[6 * i + 1] *= r;
|
||||
verts_data[6 * i + 2] *= r;
|
||||
}
|
||||
|
||||
const Mat verts_mat = Mat(Size(6, static_cast<int>(verts_data.size() / 6)), CV_32F, verts_data.data());
|
||||
showMesh(win_name, obj_name, verts_mat);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < (int)(verts_data.size() / 9); ++i)
|
||||
{
|
||||
float l = sqrtf(verts_data[9 * i + 0] * verts_data[9 * i + 0] + verts_data[9 * i + 1] * verts_data[9 * i + 1] + verts_data[9 * i + 2] * verts_data[9 * i + 2]);
|
||||
float r = radius / l;
|
||||
verts_data[9 * i + 6] = verts_data[9 * i + 0] / l;
|
||||
verts_data[9 * i + 7] = verts_data[9 * i + 1] / l;
|
||||
verts_data[9 * i + 8] = verts_data[9 * i + 2] / l;
|
||||
verts_data[9 * i + 0] *= r;
|
||||
verts_data[9 * i + 1] *= r;
|
||||
verts_data[9 * i + 2] *= r;
|
||||
}
|
||||
|
||||
const Mat verts_mat = Mat(Size(9, static_cast<int>(verts_data.size() / 9)), CV_32F, verts_data.data());
|
||||
showMesh(win_name, obj_name, verts_mat);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void showCameraTrajectory(
|
||||
const String& win_name, const String& obj_name, InputArray trajectory,
|
||||
float aspect, float scale, Vec3f frustum_color, Vec3f line_color)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(trajectory);
|
||||
CV_UNUSED(aspect);
|
||||
CV_UNUSED(scale);
|
||||
CV_UNUSED(frustum_color);
|
||||
CV_UNUSED(line_color);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
CV_Assert(trajectory.dims() == 2 && trajectory.cols() == 6 && trajectory.depth() == CV_32F);
|
||||
|
||||
auto data = trajectory.getMat();
|
||||
std::vector<float> points_data;
|
||||
points_data.reserve(data.rows * 12 * 2 * 6); // data.rows frustums, 12 lines per frustum, 2 points per line, 6 values per point
|
||||
|
||||
// Add frustums
|
||||
for (int i = 0; i < data.rows; ++i)
|
||||
{
|
||||
Vec3f position = { data.at<float>(i, 0), data.at<float>(i, 1), data.at<float>(i, 2) };
|
||||
Vec3f forward = normalize(Vec3f { data.at<float>(i, 3), data.at<float>(i, 4), data.at<float>(i, 5) });
|
||||
// Avoid a zero 'right' when forward is (anti)parallel to world up.
|
||||
Vec3f world_up = (fabsf(forward[1]) > 0.99f) ? Vec3f{0.0f, 0.0f, 1.0f} : Vec3f{0.0f, 1.0f, 0.0f};
|
||||
Vec3f right = normalize(forward.cross(world_up));
|
||||
Vec3f up = forward.cross(right);
|
||||
|
||||
Vec3f back_f[4] = {
|
||||
position + (-right * aspect - up) * scale,
|
||||
position + ( right * aspect - up) * scale,
|
||||
position + ( right * aspect + up) * scale,
|
||||
position + (-right * aspect + up) * scale,
|
||||
};
|
||||
|
||||
Vec3f front_f[4] = {
|
||||
position + (-right * aspect - up) * scale * 1.5f + forward * scale * 2.0f,
|
||||
position + (right * aspect - up) * scale * 1.5f + forward * scale * 2.0f,
|
||||
position + (right * aspect + up) * scale * 1.5f + forward * scale * 2.0f,
|
||||
position + (-right * aspect + up) * scale * 1.5f + forward * scale * 2.0f,
|
||||
};
|
||||
|
||||
// Get line points
|
||||
Vec3f lines[24] = {
|
||||
// Back face
|
||||
back_f[0], back_f[1],
|
||||
back_f[1], back_f[2],
|
||||
back_f[2], back_f[3],
|
||||
back_f[3], back_f[0],
|
||||
|
||||
// Front face
|
||||
front_f[0], front_f[1],
|
||||
front_f[1], front_f[2],
|
||||
front_f[2], front_f[3],
|
||||
front_f[3], front_f[0],
|
||||
|
||||
// Side lines
|
||||
back_f[0], front_f[0],
|
||||
back_f[1], front_f[1],
|
||||
back_f[2], front_f[2],
|
||||
back_f[3], front_f[3],
|
||||
};
|
||||
|
||||
// Add line points
|
||||
for (int j = 0; j < (int)(sizeof(lines) / sizeof(Vec3f)); ++j)
|
||||
points_data.insert(points_data.end(), {
|
||||
lines[j](0), lines[j](1), lines[j](2),
|
||||
frustum_color[0], frustum_color[1], frustum_color[2],
|
||||
});
|
||||
}
|
||||
|
||||
// Add trajectory line
|
||||
for (int i = 1; i < data.rows; ++i)
|
||||
{
|
||||
points_data.insert(points_data.end(), {
|
||||
data.at<float>(i - 1, 0), data.at<float>(i - 1, 1), data.at<float>(i - 1, 2), line_color(0), line_color(1), line_color(2),
|
||||
data.at<float>(i, 0), data.at<float>(i, 1), data.at<float>(i, 2), line_color(0), line_color(1), line_color(2),
|
||||
});
|
||||
}
|
||||
|
||||
const Mat points_mat = Mat(Size(6, static_cast<int>(points_data.size() / 6)), CV_32F, points_data.data());
|
||||
showLines(win_name, obj_name, points_mat);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void showMesh(const String& win_name, const String& obj_name, InputArray verts, InputArray indices)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(verts);
|
||||
CV_UNUSED(indices);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
setOpenGlContext(win_name);
|
||||
win->set(obj_name, new Mesh(verts, indices));
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void showMesh(const String& win_name, const String& obj_name, InputArray verts)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(verts);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
setOpenGlContext(win_name);
|
||||
win->set(obj_name, new Mesh(verts));
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void showPoints(const String& win_name, const String& obj_name, InputArray points)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(points);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
setOpenGlContext(win_name);
|
||||
win->set(obj_name, new PointCloud(points));
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void showRGBD(const String& win_name, const String& obj_name, InputArray img, const Matx33f& intrinsics, float scale)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(img);
|
||||
CV_UNUSED(intrinsics);
|
||||
CV_UNUSED(scale);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
CV_Assert(img.dims() == 2 && img.channels() == 4 && img.type() == CV_32FC4);
|
||||
|
||||
Mat mat = img.getMat();
|
||||
|
||||
// This section (RGBD to point cloud) should be changed to use the 3d module when
|
||||
// #20013 is merged.
|
||||
|
||||
float fx = intrinsics(0, 0);
|
||||
float fy = intrinsics(1, 1);
|
||||
float cx = intrinsics(0, 2);
|
||||
float cy = intrinsics(1, 2);
|
||||
|
||||
// Pre-size and fill row-major (avoids per-pixel Mat reallocation).
|
||||
Mat points(mat.rows * mat.cols, 6, CV_32F);
|
||||
for (int v = 0; v < mat.rows; ++v)
|
||||
for (int u = 0; u < mat.cols; ++u)
|
||||
{
|
||||
Vec4f c = mat.at<Vec4f>(v, u);
|
||||
float d = c(3) * 0.001f; // mm to m
|
||||
|
||||
float x_over_z = (cx - static_cast<float>(u)) / fx;
|
||||
float y_over_z = (cy - static_cast<float>(v)) / fy;
|
||||
float z = d;
|
||||
float x = x_over_z * z;
|
||||
float y = y_over_z * z;
|
||||
|
||||
float* p = points.ptr<float>(v * mat.cols + u);
|
||||
p[0] = x * scale; p[1] = y * scale; p[2] = z * scale;
|
||||
p[3] = c(0) / 255.0f; p[4] = c(1) / 255.0f; p[5] = c(2) / 255.0f;
|
||||
}
|
||||
|
||||
showPoints(win_name, obj_name, points);
|
||||
#endif
|
||||
}
|
||||
|
||||
void showLines(const String& win_name, const String& obj_name, InputArray points)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(points);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
setOpenGlContext(win_name);
|
||||
win->set(obj_name, new Lines(points));
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setObjectPosition(const String& win_name, const String& obj_name, const Vec3f& position)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(position);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
Object* obj = win->get(obj_name);
|
||||
if (!obj)
|
||||
CV_Error(cv::Error::StsObjectNotFound, "Object not found");
|
||||
obj->setPosition(position);
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setObjectRotation(const String& win_name, const String& obj_name, const Vec3f& rotation)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_UNUSED(rotation);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
Object* obj = win->get(obj_name);
|
||||
if (!obj)
|
||||
CV_Error(cv::Error::StsObjectNotFound, "Object not found");
|
||||
obj->setRotation(rotation);
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void destroyObject(const String& win_name, const String& obj_name)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(win_name);
|
||||
CV_UNUSED(obj_name);
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
#else
|
||||
Window* win = getWindow(win_name);
|
||||
win->set(obj_name, nullptr);
|
||||
updateWindow(win_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
|
||||
View::View()
|
||||
{
|
||||
this->origin = { 0.0f, 0.0f, 0.0f };
|
||||
this->distance = 10.0f;
|
||||
this->position = { 0.0f, 0.0f, this->distance };
|
||||
this->up = { 0.0f, 1.0f, 0.0f };
|
||||
|
||||
this->aspect = 1.0f;
|
||||
this->setPerspective(1.3f, 0.1f, 2000.0f);
|
||||
this->lookAt(this->origin, { 0.0f, 1.0f, 0.0f });
|
||||
}
|
||||
|
||||
void View::setAspect(float aspect_)
|
||||
{
|
||||
if (this->aspect != aspect_)
|
||||
{
|
||||
this->aspect = aspect_;
|
||||
this->setPerspective(this->fov, this->z_near, this->z_far);
|
||||
}
|
||||
}
|
||||
|
||||
void View::setPerspective(float fov_, float z_near_, float z_far_)
|
||||
{
|
||||
this->fov = fov_;
|
||||
this->z_near = z_near_;
|
||||
this->z_far = z_far_;
|
||||
|
||||
float tan_half_fovy = ::tan(this->fov / 2.0f);
|
||||
this->proj = Matx44f::zeros();
|
||||
this->proj(0, 0) = 1.0f / (this->aspect * tan_half_fovy);
|
||||
this->proj(1, 1) = 1.0f / tan_half_fovy;
|
||||
this->proj(2, 2) = (this->z_far + this->z_near) / (this->z_far - this->z_near);
|
||||
this->proj(2, 3) = 1.0f;
|
||||
this->proj(3, 2) = -(2.0f * this->z_far * this->z_near) / (this->z_far - this->z_near);
|
||||
}
|
||||
|
||||
void View::rotate(float dx, float dy)
|
||||
{
|
||||
this->position = normalize(this->position - this->origin);
|
||||
float theta = atan2(this->position(2), this->position(0));
|
||||
float phi = ::asin(this->position(1));
|
||||
theta -= dx * 0.05f;
|
||||
phi += dy * 0.05f;
|
||||
phi = max(-1.5f, min(1.5f, phi));
|
||||
|
||||
this->position(0) = ::cos(theta) * ::cos(phi) * this->distance;
|
||||
this->position(1) = ::sin(phi) * this->distance;
|
||||
this->position(2) = ::sin(theta) * ::cos(phi) * this->distance;
|
||||
this->position += this->origin;
|
||||
|
||||
this->lookAt(this->origin, this->up);
|
||||
}
|
||||
|
||||
void View::move(float dx, float dy)
|
||||
{
|
||||
Vec3f forward = normalize(this->position - this->origin);
|
||||
Vec3f right = normalize(this->up.cross(forward));
|
||||
Vec3f up_v = right.cross(forward);
|
||||
Vec3f delta = normalize(right * dx - up_v * dy) * this->distance * 0.01f;
|
||||
|
||||
this->origin += delta;
|
||||
this->position += delta;
|
||||
this->lookAt(this->origin, this->up);
|
||||
}
|
||||
|
||||
void View::scaleDistance(float amount)
|
||||
{
|
||||
this->distance *= amount;
|
||||
this->distance = max(0.1f, this->distance);
|
||||
this->position = normalize(this->position - this->origin) * this->distance + this->origin;
|
||||
|
||||
this->lookAt(this->origin, this->up);
|
||||
}
|
||||
|
||||
void View::lookAt(const Vec3f& point, const Vec3f& up_)
|
||||
{
|
||||
Vec3f f = normalize(point - this->position);
|
||||
Vec3f s = normalize(up_.cross(f));
|
||||
Vec3f u = f.cross(s);
|
||||
|
||||
this->view = Matx44f(s(0), u(0), f(0), 0.0f,
|
||||
s(1), u(1), f(1), 0.0f,
|
||||
s(2), u(2), f(2), 0.0f,
|
||||
-s.dot(this->position), -u.dot(this->position), -f.dot(this->position), 1.0f);
|
||||
}
|
||||
|
||||
Window::Window(const String& name_)
|
||||
{
|
||||
this->name = name_;
|
||||
this->sun.direction = normalize(Vec3f(0.3f, 1.0f, 0.5f));
|
||||
this->sun.ambient = { 0.1f, 0.1f, 0.1f };
|
||||
this->sun.diffuse = { 1.0f, 1.0f, 1.0f };
|
||||
this->sky_color = { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
float points[] = {
|
||||
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
Mat points_mat = Mat(Size(6, 6), CV_32F, points);
|
||||
|
||||
this->crosshair = new Lines(points_mat);
|
||||
this->shaders[this->crosshair->getShaderName()] = this->crosshair->buildShader();
|
||||
this->crosshair->setShader(this->shaders[this->crosshair->getShaderName()]);
|
||||
|
||||
this->grid = nullptr;
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
delete this->crosshair;
|
||||
if (this->grid)
|
||||
delete this->grid;
|
||||
|
||||
for (auto obj : this->objects)
|
||||
delete obj.second;
|
||||
}
|
||||
|
||||
Object* Window::get(const String& obj_name)
|
||||
{
|
||||
auto it = this->objects.find(obj_name);
|
||||
if (it == this->objects.end())
|
||||
return nullptr;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void Window::set(const String& obj_name, Object* obj)
|
||||
{
|
||||
auto it = this->objects.find(obj_name);
|
||||
if (it != this->objects.end() && it->second != obj)
|
||||
{
|
||||
delete it->second;
|
||||
|
||||
if (obj == nullptr)
|
||||
this->objects.erase(it);
|
||||
else
|
||||
it->second = obj;
|
||||
}
|
||||
else if (obj)
|
||||
this->objects[obj_name] = obj;
|
||||
|
||||
if (obj)
|
||||
{
|
||||
String shaderName = obj->getShaderName();
|
||||
auto sit = this->shaders.find(shaderName);
|
||||
if (sit == this->shaders.end())
|
||||
this->shaders[shaderName] = obj->buildShader();
|
||||
obj->setShader(this->shaders[shaderName]);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::setSun(const Vec3f& direction, const Vec3f& ambient, const Vec3f& diffuse)
|
||||
{
|
||||
this->sun.direction = normalize(direction);
|
||||
this->sun.ambient = ambient;
|
||||
this->sun.diffuse = diffuse;
|
||||
}
|
||||
|
||||
void Window::setSky(const Vec3f& color)
|
||||
{
|
||||
this->sky_color = color;
|
||||
}
|
||||
|
||||
static Mat getGridVertices(const View& view)
|
||||
{
|
||||
const Vec3f grid_color = { 0.5f, 0.5f, 0.5f };
|
||||
const Vec3f center = view.getOrigin();
|
||||
const Vec3f camera_dir = view.getOrigin() - view.getPosition();
|
||||
const float scale = 0.3f;
|
||||
|
||||
const float tick_step = detail::gridTickStep(view.getDistance() * scale);
|
||||
|
||||
// Accumulate line vertices (6 floats each: xyz + rgb), then build one Mat --
|
||||
// avoids the per-frame Mat::push_back reallocation on the render thread.
|
||||
std::vector<float> verts;
|
||||
verts.reserve(4096 * 6);
|
||||
auto addRows = [&](const float* d, int rows) { verts.insert(verts.end(), d, d + rows * 6); };
|
||||
|
||||
float face_sign[3];
|
||||
|
||||
const Vec3f min_p = center - Vec3f(1.0f, 1.0f, 1.0f) * view.getDistance() * scale;
|
||||
const Vec3f max_p = center + Vec3f(1.0f, 1.0f, 1.0f) * view.getDistance() * scale;
|
||||
|
||||
// For each axis add a grid
|
||||
for (int ai = 0; ai < 3; ++ai)
|
||||
{
|
||||
Vec3f az = { 0.0f, 0.0f, 0.0f };
|
||||
az((ai + 2) % 3) = 1.0f;
|
||||
|
||||
// Check if face is positive or negative along the az axis
|
||||
face_sign[ai] = camera_dir.dot(az) > 0.0f ? 1.0f : -1.0f;
|
||||
|
||||
float x = (floor(min_p(ai) / tick_step) + 1.0f) * tick_step;
|
||||
for (; x < max_p(ai); x += tick_step)
|
||||
{
|
||||
Vec3f a = min_p;
|
||||
Vec3f b = max_p;
|
||||
a((ai + 0) % 3) = x;
|
||||
b((ai + 0) % 3) = x;
|
||||
if (face_sign[ai] > 0.0f)
|
||||
a((ai + 2) % 3) = b((ai + 2) % 3);
|
||||
else
|
||||
b((ai + 2) % 3) = a((ai + 2) % 3);
|
||||
|
||||
float data[] = {
|
||||
a(0), a(1), a(2), grid_color(0), grid_color(1), grid_color(2),
|
||||
b(0), b(1), b(2), grid_color(0), grid_color(1), grid_color(2),
|
||||
};
|
||||
|
||||
addRows(data, 2);
|
||||
}
|
||||
|
||||
float y = (floor(min_p((ai + 1) % 3) / tick_step) + 1.0f) * tick_step;
|
||||
for (; y < max_p((ai + 1) % 3); y += tick_step)
|
||||
{
|
||||
Vec3f a = min_p;
|
||||
Vec3f b = max_p;
|
||||
a((ai + 1) % 3) = y;
|
||||
b((ai + 1) % 3) = y;
|
||||
if (face_sign[ai] > 0.0f)
|
||||
a((ai + 2) % 3) = b((ai + 2) % 3);
|
||||
else
|
||||
b((ai + 2) % 3) = a((ai + 2) % 3);
|
||||
|
||||
float data[] = {
|
||||
a(0), a(1), a(2), grid_color(0), grid_color(1), grid_color(2),
|
||||
b(0), b(1), b(2), grid_color(0), grid_color(1), grid_color(2),
|
||||
};
|
||||
|
||||
addRows(data, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Ox, Oy and Oz axes and ticks
|
||||
{
|
||||
Vec3f a = { face_sign[1] > 0.0f ? min_p(0) : max_p(0), face_sign[2] > 0.0f ? max_p(1) : min_p(1), face_sign[0] > 0.0f ? max_p(2) : min_p(2) };
|
||||
Vec3f b = { face_sign[1] > 0.0f ? min_p(0) : max_p(0), face_sign[2] > 0.0f ? max_p(1) : min_p(1), face_sign[0] > 0.0f ? min_p(2) : max_p(2) };
|
||||
Vec3f c = { face_sign[1] > 0.0f ? max_p(0) : min_p(0), face_sign[2] > 0.0f ? max_p(1) : min_p(1), face_sign[0] > 0.0f ? min_p(2) : max_p(2) };
|
||||
Vec3f d = { face_sign[1] > 0.0f ? max_p(0) : min_p(0), face_sign[2] > 0.0f ? min_p(1) : max_p(1), face_sign[0] > 0.0f ? min_p(2) : max_p(2) };
|
||||
|
||||
float data[] = {
|
||||
a(0), a(1), a(2), 0.0f, 0.0f, 0.8f,
|
||||
b(0), b(1), b(2), 0.0f, 0.0f, 0.8f,
|
||||
b(0), b(1), b(2), 0.8f, 0.0f, 0.0f,
|
||||
c(0), c(1), c(2), 0.8f, 0.0f, 0.0f,
|
||||
c(0), c(1), c(2), 0.0f, 0.8f, 0.0f,
|
||||
d(0), d(1), d(2), 0.0f, 0.8f, 0.0f,
|
||||
};
|
||||
|
||||
addRows(data, 6);
|
||||
|
||||
float x = (floor(min_p(0) / tick_step) + 1.0f) * tick_step;
|
||||
for (; x < max_p(0); x += tick_step)
|
||||
{
|
||||
Vec3f la, lb;
|
||||
la(0) = lb(0) = x;
|
||||
la(1) = lb(1) = face_sign[2] > 0.0f ? max_p(1) : min_p(1);
|
||||
la(2) = face_sign[0] > 0.0f ? min_p(2) : max_p(2);
|
||||
lb(2) = la(2) - face_sign[0] * 0.03f * scale * view.getDistance();
|
||||
|
||||
float line[] = {
|
||||
la(0), la(1), la(2), 0.8f, 0.0f, 0.0f,
|
||||
lb(0), lb(1), lb(2), 0.8f, 0.0f, 0.0f,
|
||||
};
|
||||
|
||||
addRows(line, 2);
|
||||
}
|
||||
|
||||
float y = (floor(min_p(1) / tick_step) + 1.0f) * tick_step;
|
||||
for (; y < max_p(1); y += tick_step)
|
||||
{
|
||||
Vec3f la, lb;
|
||||
la(0) = lb(0) = face_sign[1] > 0.0f ? max_p(0) : min_p(0);
|
||||
la(1) = lb(1) = y;
|
||||
la(2) = face_sign[0] > 0.0f ? min_p(2) : max_p(2);
|
||||
lb(2) = la(2) - face_sign[0] * 0.03f * scale * view.getDistance();
|
||||
|
||||
float line[] = {
|
||||
la(0), la(1), la(2), 0.0f, 0.8f, 0.0f,
|
||||
lb(0), lb(1), lb(2), 0.0f, 0.8f, 0.0f,
|
||||
};
|
||||
|
||||
addRows(line, 2);
|
||||
}
|
||||
|
||||
float z = (floor(min_p(2) / tick_step) + 1.0f) * tick_step;
|
||||
for (; z < max_p(2); z += tick_step)
|
||||
{
|
||||
Vec3f la, lb;
|
||||
la(0) = face_sign[1] > 0.0f ? min_p(0) : max_p(0);
|
||||
lb(0) = la(0) - face_sign[1] * 0.03f * scale * view.getDistance();
|
||||
la(1) = lb(1) = face_sign[2] > 0.0f ? max_p(1) : min_p(1);
|
||||
la(2) = lb(2) = z;
|
||||
|
||||
float line[] = {
|
||||
la(0), la(1), la(2), 0.0f, 0.0f, 0.8f,
|
||||
lb(0), lb(1), lb(2), 0.0f, 0.0f, 0.8f,
|
||||
};
|
||||
|
||||
addRows(line, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (verts.empty())
|
||||
return Mat();
|
||||
return Mat((int)(verts.size() / 6), 6, CV_32F, verts.data()).clone();
|
||||
}
|
||||
|
||||
void Window::setGridVisible(bool visible)
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
this->grid = new Lines(Mat(4096, 6, CV_32F), 0);
|
||||
this->grid->setShader(this->shaders[this->grid->getShaderName()]);
|
||||
}
|
||||
else if (this->grid)
|
||||
{
|
||||
delete this->grid;
|
||||
this->grid = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Window::draw()
|
||||
{
|
||||
Rect rect = getWindowImageRect(this->name);
|
||||
float aspect = static_cast<float>(rect.width) / static_cast<float>(rect.height);
|
||||
this->view.setAspect(aspect);
|
||||
|
||||
ogl::enable(ogl::DEPTH_TEST);
|
||||
ogl::clearColor(this->sky_color.mul(255.0f));
|
||||
|
||||
if (this->grid)
|
||||
{
|
||||
static_cast<Lines*>(this->grid)->update(getGridVertices(this->view));
|
||||
this->grid->draw(this->view, this->sun);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->crosshair->setPosition(this->view.getOrigin());
|
||||
this->crosshair->draw(this->view, this->sun);
|
||||
}
|
||||
|
||||
for (auto& obj : this->objects)
|
||||
obj.second->draw(this->view, this->sun);
|
||||
}
|
||||
|
||||
void Window::onMouse(int event, int x, int y, int flags)
|
||||
{
|
||||
if (event == EVENT_LBUTTONDOWN || event == EVENT_RBUTTONDOWN)
|
||||
{
|
||||
this->l_mouse_x = x;
|
||||
this->l_mouse_y = y;
|
||||
}
|
||||
else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
|
||||
{
|
||||
this->view.rotate(x - this->l_mouse_x, y - this->l_mouse_y);
|
||||
updateWindow(this->name);
|
||||
this->l_mouse_x = x;
|
||||
this->l_mouse_y = y;
|
||||
}
|
||||
else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_RBUTTON))
|
||||
{
|
||||
this->view.move(x - this->l_mouse_x, y - this->l_mouse_y);
|
||||
updateWindow(this->name);
|
||||
this->l_mouse_x = x;
|
||||
this->l_mouse_y = y;
|
||||
}
|
||||
else if (event == EVENT_MOUSEWHEEL)
|
||||
{
|
||||
this->view.scaleDistance(min(1.2f, max(0.8f, 1.0f - getMouseWheelDelta(flags) / 12.0f)));
|
||||
updateWindow(this->name);
|
||||
}
|
||||
}
|
||||
|
||||
Object::Object()
|
||||
{
|
||||
this->position = { 0.0f, 0.0f, 0.0f };
|
||||
this->rotation = { 0.0f, 0.0f, 0.0f };
|
||||
this->model = Matx44f::eye();
|
||||
}
|
||||
|
||||
void Object::setPosition(const Vec3f& position_)
|
||||
{
|
||||
this->position = position_;
|
||||
this->updateModel();
|
||||
}
|
||||
|
||||
void Object::setRotation(const Vec3f& rotation_)
|
||||
{
|
||||
this->rotation = rotation_;
|
||||
this->updateModel();
|
||||
}
|
||||
|
||||
void Object::updateModel()
|
||||
{
|
||||
// Calculate rotation matrices
|
||||
Matx44f rot_a = Matx44f::eye();
|
||||
rot_a(0, 0) = ::cos(this->rotation(0));
|
||||
rot_a(1, 0) = ::sin(this->rotation(0));
|
||||
rot_a(0, 1) = -::sin(this->rotation(0));
|
||||
rot_a(1, 1) = ::cos(this->rotation(0));
|
||||
|
||||
Matx44f rot_b = Matx44f::eye();
|
||||
rot_b(1, 1) = ::cos(this->rotation(1));
|
||||
rot_b(2, 1) = ::sin(this->rotation(1));
|
||||
rot_b(1, 2) = -::sin(this->rotation(1));
|
||||
rot_b(2, 2) = ::cos(this->rotation(1));
|
||||
|
||||
Matx44f rot_c = Matx44f::eye();
|
||||
rot_c(0, 0) = ::cos(this->rotation(2));
|
||||
rot_c(2, 0) = ::sin(this->rotation(2));
|
||||
rot_c(0, 2) = -::sin(this->rotation(2));
|
||||
rot_c(2, 2) = ::cos(this->rotation(2));
|
||||
|
||||
// Calculate translation matrix
|
||||
Matx44f trans = Matx44f::eye();
|
||||
trans(3, 0) = this->position(0);
|
||||
trans(3, 1) = this->position(1);
|
||||
trans(3, 2) = this->position(2);
|
||||
|
||||
// Multiply matrices
|
||||
this->model = rot_c * rot_b * rot_a * trans;
|
||||
}
|
||||
|
||||
Mesh::Mesh(InputArray verts_, InputArray indices_)
|
||||
{
|
||||
// Check parameter validity
|
||||
CV_Assert(verts_.channels() == 1 && verts_.dims() == 2 && (verts_.size().width == 3 || verts_.size().width == 6 || verts_.size().width == 9));
|
||||
CV_Assert(verts_.depth() == CV_32F);
|
||||
CV_Assert(indices_.channels() == 1 && indices_.dims() == 2 && indices_.size().width == 3);
|
||||
CV_Assert(indices_.depth() == CV_8U || indices_.depth() == CV_16U || indices_.depth() == CV_32S);
|
||||
|
||||
// Prepare buffers
|
||||
if (verts_.kind() == _InputArray::OPENGL_BUFFER)
|
||||
this->verts = verts_.getOGlBuffer();
|
||||
else
|
||||
this->verts.copyFrom(verts_, ogl::Buffer::ARRAY_BUFFER);
|
||||
|
||||
if (indices_.kind() == _InputArray::OPENGL_BUFFER)
|
||||
this->indices = indices_.getOGlBuffer();
|
||||
else
|
||||
this->indices.copyFrom(indices_, ogl::Buffer::ELEMENT_ARRAY_BUFFER);
|
||||
|
||||
switch (indices_.depth())
|
||||
{
|
||||
case CV_8U:
|
||||
this->index_type = ogl::UNSIGNED_BYTE;
|
||||
break;
|
||||
case CV_16U:
|
||||
this->index_type = ogl::UNSIGNED_SHORT;
|
||||
break;
|
||||
case CV_32S:
|
||||
this->index_type = ogl::UNSIGNED_INT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Prepare vertex array
|
||||
this->initVA(verts_.size().width);
|
||||
}
|
||||
|
||||
Mesh::Mesh(InputArray verts_)
|
||||
{
|
||||
// Check parameter validity
|
||||
CV_Assert(verts_.channels() == 1 && verts_.dims() == 2 && (verts_.size().width == 3 || verts_.size().width == 6 || verts_.size().width == 9));
|
||||
CV_Assert(verts_.depth() == CV_32F);
|
||||
|
||||
// Prepare buffers
|
||||
if (verts_.kind() == _InputArray::OPENGL_BUFFER)
|
||||
this->verts = verts_.getOGlBuffer();
|
||||
else
|
||||
this->verts.copyFrom(verts_, ogl::Buffer::ARRAY_BUFFER);
|
||||
|
||||
this->index_type = 0;
|
||||
|
||||
// Prepare vertex array
|
||||
this->initVA(verts_.size().width);
|
||||
}
|
||||
|
||||
void Mesh::initVA(int width)
|
||||
{
|
||||
// Prepare vertex array
|
||||
if (width == 3)
|
||||
{
|
||||
this->va.create({
|
||||
{
|
||||
this->verts,
|
||||
3 * sizeof(float), 0,
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
0
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (width == 6)
|
||||
{
|
||||
this->va.create({
|
||||
{
|
||||
this->verts,
|
||||
6 * sizeof(float), 0,
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
0
|
||||
},
|
||||
{
|
||||
this->verts,
|
||||
6 * sizeof(float), 3 * sizeof(float),
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
1
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (width == 9)
|
||||
{
|
||||
this->va.create({
|
||||
{
|
||||
this->verts,
|
||||
9 * sizeof(float), 0,
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
0
|
||||
},
|
||||
{
|
||||
this->verts,
|
||||
9 * sizeof(float), 3 * sizeof(float),
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
1
|
||||
},
|
||||
{
|
||||
this->verts,
|
||||
9 * sizeof(float), 6 * sizeof(float),
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
2
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::draw(const View& view, const Light& light)
|
||||
{
|
||||
this->program.bind();
|
||||
this->va.bind();
|
||||
ogl::Program::setUniformMat4x4(this->model_loc, this->getModel());
|
||||
ogl::Program::setUniformMat4x4(this->view_loc, view.getView());
|
||||
ogl::Program::setUniformMat4x4(this->proj_loc, view.getProj());
|
||||
|
||||
if (this->sun_direction_loc != -1)
|
||||
{
|
||||
ogl::Program::setUniformVec3(this->sun_direction_loc, light.direction);
|
||||
ogl::Program::setUniformVec3(this->sun_ambient_loc, light.ambient);
|
||||
ogl::Program::setUniformVec3(this->sun_diffuse_loc, light.diffuse);
|
||||
}
|
||||
|
||||
if (this->index_type == 0)
|
||||
ogl::drawArrays(0, this->verts.size().height, ogl::TRIANGLES);
|
||||
else
|
||||
{
|
||||
this->indices.bind(ogl::Buffer::ELEMENT_ARRAY_BUFFER);
|
||||
ogl::drawElements(0, this->indices.size().area(), this->index_type, ogl::TRIANGLES);
|
||||
}
|
||||
}
|
||||
|
||||
String Mesh::getShaderName()
|
||||
{
|
||||
if (this->verts.size().width == 3)
|
||||
return "mesh-xyz";
|
||||
else if (this->verts.size().width == 6)
|
||||
return "mesh-xyz-rgb";
|
||||
else
|
||||
return "mesh-xyz-rgb-uvw";
|
||||
}
|
||||
|
||||
ogl::Program Mesh::buildShader()
|
||||
{
|
||||
ogl::Shader vs, fs;
|
||||
|
||||
// Setup shader pipeline
|
||||
if (this->verts.size().width == 3)
|
||||
{
|
||||
vs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 vert_pos;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 proj;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(vert_pos, 1.0) * model * view * proj;
|
||||
}
|
||||
)", ogl::Shader::VERTEX);
|
||||
|
||||
fs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
)", ogl::Shader::FRAGMENT);
|
||||
}
|
||||
else if (this->verts.size().width == 6)
|
||||
{
|
||||
vs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 vert_pos;
|
||||
layout (location = 1) in vec3 vert_color;
|
||||
|
||||
out vec3 frag_color;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 proj;
|
||||
|
||||
void main() {
|
||||
frag_color = vert_color;
|
||||
gl_Position = vec4(vert_pos, 1.0) * model * view * proj;
|
||||
}
|
||||
)", ogl::Shader::VERTEX);
|
||||
|
||||
fs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
in vec3 frag_color;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
color = vec4(frag_color, 1.0);
|
||||
}
|
||||
)", ogl::Shader::FRAGMENT);
|
||||
}
|
||||
else if (this->verts.size().width == 9)
|
||||
{
|
||||
vs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 vert_pos;
|
||||
layout (location = 1) in vec3 vert_color;
|
||||
layout (location = 2) in vec3 vert_normal;
|
||||
|
||||
out vec3 frag_color;
|
||||
out vec3 frag_normal;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 proj;
|
||||
|
||||
void main() {
|
||||
frag_color = vert_color;
|
||||
frag_normal = vert_normal * mat3(transpose(inverse(model)));
|
||||
gl_Position = vec4(vert_pos, 1.0) * model * view * proj;
|
||||
}
|
||||
)", ogl::Shader::VERTEX);
|
||||
|
||||
fs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
in vec3 frag_color;
|
||||
in vec3 frag_normal;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform vec3 sun_direction;
|
||||
uniform vec3 sun_ambient;
|
||||
uniform vec3 sun_diffuse;
|
||||
|
||||
void main() {
|
||||
float diff = max(dot(frag_normal, sun_direction), 0.0);
|
||||
vec3 ambient = frag_color * sun_ambient;
|
||||
vec3 diffuse = frag_color * diff * sun_diffuse;
|
||||
color = vec4(ambient + diffuse, 1.0);
|
||||
}
|
||||
)", ogl::Shader::FRAGMENT);
|
||||
}
|
||||
|
||||
return ogl::Program(vs, fs);
|
||||
}
|
||||
|
||||
void Mesh::setShader(ogl::Program program_)
|
||||
{
|
||||
this->program = program_;
|
||||
this->model_loc = this->program.getUniformLocation("model");
|
||||
this->view_loc = this->program.getUniformLocation("view");
|
||||
this->proj_loc = this->program.getUniformLocation("proj");
|
||||
this->sun_direction_loc = -1;
|
||||
if (this->verts.size().width == 9)
|
||||
{
|
||||
this->sun_direction_loc = this->program.getUniformLocation("sun_direction");
|
||||
this->sun_ambient_loc = this->program.getUniformLocation("sun_ambient");
|
||||
this->sun_diffuse_loc = this->program.getUniformLocation("sun_diffuse");
|
||||
}
|
||||
}
|
||||
|
||||
Lines::Lines(InputArray points_, int count_)
|
||||
{
|
||||
// Check parameter validity
|
||||
CV_Assert(points_.channels() == 1 && points_.dims() == 2 && points_.size().width == 6);
|
||||
CV_Assert(points_.depth() == CV_32F);
|
||||
|
||||
// Prepare buffers
|
||||
if (points_.kind() == _InputArray::OPENGL_BUFFER)
|
||||
this->points = points_.getOGlBuffer();
|
||||
else
|
||||
{
|
||||
this->points.create(points_.size(), points_.type(), ogl::Buffer::ARRAY_BUFFER);
|
||||
if (count_ == -1 || count_ > 0)
|
||||
this->points.copyFrom(points_, ogl::Buffer::ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
// Prepare vertex array
|
||||
this->va.create({
|
||||
{
|
||||
this->points,
|
||||
6 * sizeof(float), 0,
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
0
|
||||
},
|
||||
{
|
||||
this->points,
|
||||
6 * sizeof(float), 3 * sizeof(float),
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
1
|
||||
}
|
||||
});
|
||||
|
||||
if (count_ == -1)
|
||||
this->count = this->points.size().height;
|
||||
else
|
||||
this->count = count_;
|
||||
}
|
||||
|
||||
void Lines::draw(const View& view, const Light& light)
|
||||
{
|
||||
CV_UNUSED(light);
|
||||
|
||||
if (this->count > 0)
|
||||
{
|
||||
this->program.bind();
|
||||
this->va.bind();
|
||||
|
||||
ogl::Program::setUniformMat4x4(this->model_loc, this->getModel());
|
||||
ogl::Program::setUniformMat4x4(this->view_loc, view.getView());
|
||||
ogl::Program::setUniformMat4x4(this->proj_loc, view.getProj());
|
||||
|
||||
ogl::drawArrays(0, this->count, ogl::LINES);
|
||||
}
|
||||
}
|
||||
|
||||
void Lines::update(InputArray points_)
|
||||
{
|
||||
// Check parameter validity
|
||||
CV_Assert(points_.channels() == 1 && points_.dims() == 2 && points_.size().width == 6);
|
||||
CV_Assert(points_.depth() == CV_32F);
|
||||
|
||||
this->points.copyFrom(points_, ogl::Buffer::ARRAY_BUFFER);
|
||||
this->count = points_.size().height;
|
||||
}
|
||||
|
||||
String Lines::getShaderName()
|
||||
{
|
||||
return "lines";
|
||||
}
|
||||
|
||||
ogl::Program Lines::buildShader()
|
||||
{
|
||||
// Setup shader pipeline
|
||||
auto vs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 vert_pos;
|
||||
layout (location = 1) in vec3 vert_color;
|
||||
|
||||
out vec3 frag_color;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 proj;
|
||||
|
||||
void main() {
|
||||
frag_color = vert_color;
|
||||
gl_Position = vec4(vert_pos, 1.0) * model * view * proj;
|
||||
}
|
||||
)", ogl::Shader::VERTEX);
|
||||
|
||||
auto fs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
in vec3 frag_color;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
color = vec4(frag_color, 1.0);
|
||||
}
|
||||
)", ogl::Shader::FRAGMENT);
|
||||
|
||||
return ogl::Program(vs, fs);
|
||||
}
|
||||
|
||||
void Lines::setShader(ogl::Program program_)
|
||||
{
|
||||
this->program = program_;
|
||||
this->model_loc = this->program.getUniformLocation("model");
|
||||
this->view_loc = this->program.getUniformLocation("view");
|
||||
this->proj_loc = this->program.getUniformLocation("proj");
|
||||
}
|
||||
|
||||
PointCloud::PointCloud(InputArray points_)
|
||||
{
|
||||
// Check parameter validity
|
||||
CV_Assert(points_.channels() == 1 && points_.dims() == 2 && points_.size().width == 6);
|
||||
CV_Assert(points_.depth() == CV_32F);
|
||||
|
||||
// Prepare buffers
|
||||
if (points_.kind() == _InputArray::OPENGL_BUFFER)
|
||||
this->points = points_.getOGlBuffer();
|
||||
else
|
||||
this->points.copyFrom(points_, ogl::Buffer::ARRAY_BUFFER);
|
||||
|
||||
// Prepare vertex array
|
||||
this->va.create({
|
||||
{
|
||||
this->points,
|
||||
6 * sizeof(float), 0,
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
0
|
||||
},
|
||||
{
|
||||
this->points,
|
||||
6 * sizeof(float), 3 * sizeof(float),
|
||||
3, ogl::Attribute::FLOAT,
|
||||
false, false,
|
||||
1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PointCloud::draw(const View& view, const Light& light)
|
||||
{
|
||||
CV_UNUSED(light);
|
||||
|
||||
this->program.bind();
|
||||
this->va.bind();
|
||||
|
||||
ogl::Program::setUniformMat4x4(this->model_loc, this->getModel());
|
||||
ogl::Program::setUniformMat4x4(this->view_loc, view.getView());
|
||||
ogl::Program::setUniformMat4x4(this->proj_loc, view.getProj());
|
||||
|
||||
ogl::drawArrays(0, this->points.size().height, ogl::POINTS);
|
||||
}
|
||||
|
||||
String PointCloud::getShaderName()
|
||||
{
|
||||
return "points";
|
||||
}
|
||||
|
||||
ogl::Program PointCloud::buildShader()
|
||||
{
|
||||
// Setup shader pipeline
|
||||
auto vs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 vert_pos;
|
||||
layout (location = 1) in vec3 vert_color;
|
||||
|
||||
out vec3 frag_color;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 proj;
|
||||
|
||||
void main() {
|
||||
frag_color = vert_color;
|
||||
gl_Position = vec4(vert_pos, 1.0) * model * view * proj;
|
||||
}
|
||||
)", ogl::Shader::VERTEX);
|
||||
|
||||
auto fs = ogl::Shader(R"(
|
||||
#version 330 core
|
||||
|
||||
in vec3 frag_color;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
color = vec4(frag_color, 1.0);
|
||||
}
|
||||
)", ogl::Shader::FRAGMENT);
|
||||
|
||||
return ogl::Program(vs, fs);
|
||||
}
|
||||
|
||||
void PointCloud::setShader(ogl::Program program_)
|
||||
{
|
||||
this->program = program_;
|
||||
this->model_loc = this->program.getUniformLocation("model");
|
||||
this->view_loc = this->program.getUniformLocation("view");
|
||||
this->proj_loc = this->program.getUniformLocation("proj");
|
||||
}
|
||||
|
||||
} // namespace viz3d
|
||||
} // namespace cv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,209 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_PTCLOUD_VIZ3D_PRIVATE_HPP
|
||||
#define OPENCV_PTCLOUD_VIZ3D_PRIVATE_HPP
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "opencv2/core/private.hpp" // HAVE_OPENGL from cvconfig.h
|
||||
#include "opencv2/core/opengl.hpp"
|
||||
#include "opencv2/highgui.hpp" // window + OpenGL context (imported from highgui)
|
||||
#include "opencv2/ptcloud/viz3d.hpp" // public viz3d API declarations
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
|
||||
namespace cv { namespace viz3d {
|
||||
|
||||
// Stores a view's matrices
|
||||
class View
|
||||
{
|
||||
public:
|
||||
View();
|
||||
|
||||
void setAspect(float aspect);
|
||||
void setPerspective(float fov, float z_near, float z_far);
|
||||
|
||||
void rotate(float dx, float dy); // Rotates the camera using mouse input
|
||||
void move(float dx, float dy); // Moves the camera using mouse input
|
||||
void scaleDistance(float amount);
|
||||
|
||||
inline Vec3f getOrigin() const { return this->origin; }
|
||||
inline Vec3f getPosition() const { return this->position; }
|
||||
inline float getDistance() const { return this->distance; }
|
||||
inline Matx44f getView() const { return this->view; }
|
||||
inline Matx44f getProj() const { return this->proj; }
|
||||
|
||||
private:
|
||||
void lookAt(const Vec3f& point, const Vec3f& up);
|
||||
|
||||
float aspect;
|
||||
float fov;
|
||||
float z_near;
|
||||
float z_far;
|
||||
|
||||
Matx44f proj;
|
||||
Matx44f view;
|
||||
|
||||
Vec3f origin;
|
||||
Vec3f position;
|
||||
Vec3f up;
|
||||
float distance;
|
||||
};
|
||||
|
||||
// Stores information about a light
|
||||
struct Light
|
||||
{
|
||||
Vec3f direction;
|
||||
Vec3f ambient;
|
||||
Vec3f diffuse;
|
||||
};
|
||||
|
||||
// Base class for viz3d objects which can be rendered
|
||||
class Object
|
||||
{
|
||||
public:
|
||||
Object();
|
||||
virtual ~Object() = default;
|
||||
|
||||
void setPosition(const Vec3f& position);
|
||||
void setRotation(const Vec3f& rotation);
|
||||
|
||||
virtual void draw(const View& view, const Light& light) = 0;
|
||||
|
||||
virtual String getShaderName() = 0;
|
||||
virtual ogl::Program buildShader() = 0;
|
||||
virtual void setShader(ogl::Program program) = 0;
|
||||
|
||||
inline Matx44f getModel() const { return this->model; }
|
||||
|
||||
private:
|
||||
void updateModel();
|
||||
|
||||
Vec3f position;
|
||||
Vec3f rotation;
|
||||
|
||||
Matx44f model;
|
||||
};
|
||||
|
||||
// Class which stores the viz3d data associated to a window.
|
||||
class Window
|
||||
{
|
||||
public:
|
||||
Window(const String& name);
|
||||
~Window();
|
||||
|
||||
Object* get(const String& obj_name);
|
||||
void set(const String& obj_name, Object* obj);
|
||||
|
||||
void setSun(const Vec3f& direction, const Vec3f& ambient, const Vec3f& diffuse);
|
||||
void setSky(const Vec3f& color);
|
||||
void setGridVisible(bool visible);
|
||||
|
||||
void draw();
|
||||
void onMouse(int event, int x, int y, int flags);
|
||||
|
||||
inline View& getView() { return this->view; }
|
||||
|
||||
private:
|
||||
String name;
|
||||
Size size;
|
||||
|
||||
Light sun;
|
||||
Vec3f sky_color;
|
||||
|
||||
View view;
|
||||
int l_mouse_x;
|
||||
int l_mouse_y;
|
||||
|
||||
Object* crosshair;
|
||||
Object* grid;
|
||||
std::map<String, Object*> objects;
|
||||
std::map<String, ogl::Program> shaders;
|
||||
};
|
||||
|
||||
// Class which stores the viz3d data associated to a mesh object.
|
||||
class Mesh : public Object
|
||||
{
|
||||
public:
|
||||
Mesh(InputArray verts, InputArray indices);
|
||||
Mesh(InputArray verts);
|
||||
|
||||
virtual void draw(const View& view, const Light& light) override;
|
||||
|
||||
virtual String getShaderName() override;
|
||||
virtual ogl::Program buildShader() override;
|
||||
virtual void setShader(ogl::Program program) override;
|
||||
|
||||
private:
|
||||
void initVA(int width);
|
||||
|
||||
ogl::Program program;
|
||||
ogl::VertexArray va;
|
||||
ogl::Buffer verts;
|
||||
ogl::Buffer indices;
|
||||
|
||||
int index_type;
|
||||
|
||||
int model_loc;
|
||||
int view_loc;
|
||||
int proj_loc;
|
||||
int sun_direction_loc;
|
||||
int sun_ambient_loc;
|
||||
int sun_diffuse_loc;
|
||||
};
|
||||
|
||||
// Class which stores the viz3d data associated to a lines object.
|
||||
class Lines : public Object
|
||||
{
|
||||
public:
|
||||
Lines(InputArray points, int count = -1);
|
||||
|
||||
virtual void draw(const View& view, const Light& light) override;
|
||||
void update(InputArray points);
|
||||
|
||||
virtual String getShaderName() override;
|
||||
virtual ogl::Program buildShader() override;
|
||||
virtual void setShader(ogl::Program program) override;
|
||||
|
||||
private:
|
||||
ogl::Program program;
|
||||
ogl::VertexArray va;
|
||||
ogl::Buffer points;
|
||||
|
||||
int model_loc;
|
||||
int view_loc;
|
||||
int proj_loc;
|
||||
|
||||
int count;
|
||||
};
|
||||
|
||||
// Data necessary for drawing a point cloud on a window.
|
||||
class PointCloud : public Object
|
||||
{
|
||||
public:
|
||||
PointCloud(InputArray points);
|
||||
|
||||
virtual void draw(const View& view, const Light& light) override;
|
||||
|
||||
virtual String getShaderName() override;
|
||||
virtual ogl::Program buildShader() override;
|
||||
virtual void setShader(ogl::Program program) override;
|
||||
|
||||
private:
|
||||
ogl::Program program;
|
||||
ogl::VertexArray va;
|
||||
ogl::Buffer points;
|
||||
|
||||
int model_loc;
|
||||
int view_loc;
|
||||
int proj_loc;
|
||||
};
|
||||
|
||||
}} // namespace cv::viz3d
|
||||
|
||||
#endif // HAVE_OPENGL
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include <opencv2/highgui.hpp> // updateWindow / destroyWindow
|
||||
#include "../src/viz3d/grid_ticks.hpp" // GL-free grid spacing helper
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
using namespace cv;
|
||||
|
||||
// Regression for the grid tick_step infinite-loop hang (#1): the old code did
|
||||
// logf(1.0) == 0 -> division -> inf -> the halving loop never terminated for
|
||||
// distance*scale > 4. gridTickStep must terminate and stay finite/positive for
|
||||
// every distance, including the region that used to hang. GL-free (runs in CI).
|
||||
TEST(Viz3D, grid_tick_step)
|
||||
{
|
||||
// Values span below/at/above the old hang threshold (dist_scale > 4).
|
||||
const float samples[] = { 0.03f, 0.9f, 3.0f, 4.0f, 5.0f, 13.4f, 40.0f, 400.0f, 4000.0f };
|
||||
for (float ds : samples)
|
||||
{
|
||||
float ts = viz3d::detail::gridTickStep(ds);
|
||||
ASSERT_TRUE(std::isfinite(ts)) << "ds=" << ds;
|
||||
ASSERT_GT(ts, 0.0f) << "ds=" << ds;
|
||||
float ratio = ds / ts;
|
||||
EXPECT_GE(ratio, 2.0f - 1e-3f) << "ds=" << ds << " ratio=" << ratio;
|
||||
EXPECT_LE(ratio, 4.0f + 1e-3f) << "ds=" << ds << " ratio=" << ratio;
|
||||
}
|
||||
}
|
||||
|
||||
// viz3d is OpenGL/window based. Skip when no GL context can be created
|
||||
// (e.g. headless CI without a display).
|
||||
static bool viz3dAvailable()
|
||||
{
|
||||
try
|
||||
{
|
||||
viz3d::showPoints("viz3d_gl_probe", "p", Mat::zeros(4, 6, CV_32F));
|
||||
destroyWindow("viz3d_gl_probe");
|
||||
return true;
|
||||
}
|
||||
catch (const cv::Exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Smoke test for the public viz3d API. Builds a scene and forces redraws; passes
|
||||
// if nothing throws. Exercises the paths touched by review fixes: #3 (showRGBD),
|
||||
// #4 (degenerate up-vector trajectory), and the grid render path. The #1 hang
|
||||
// itself is distance-triggered (no public camera-distance setter) and is covered
|
||||
// by the GL-free Viz3D.grid_tick_step test above.
|
||||
TEST(Viz3D, render_scene_smoke)
|
||||
{
|
||||
if (!viz3dAvailable())
|
||||
throw cvtest::SkipTestException("viz3d/OpenGL not available (no GL context)");
|
||||
|
||||
const String w = "viz3d_test";
|
||||
|
||||
Mat pts(256, 6, CV_32F);
|
||||
randu(pts, 0.0f, 1.0f);
|
||||
EXPECT_NO_THROW(viz3d::showPoints(w, "pts", pts));
|
||||
EXPECT_NO_THROW(viz3d::setGridVisible(w, true));
|
||||
|
||||
EXPECT_NO_THROW(viz3d::showBox(w, "box", Vec3f::all(1.0f), Vec3f(1, 0, 0)));
|
||||
EXPECT_NO_THROW(viz3d::showSphere(w, "sphere", 1.0f, Vec3f(0, 1, 0)));
|
||||
|
||||
// forward = (0,+1,0) and (0,-1,0): the degenerate up-vector case guarded by #4.
|
||||
float traj[] = { 0,0,0, 0,1,0, 1,0,0, 0,-1,0 };
|
||||
EXPECT_NO_THROW(viz3d::showCameraTrajectory(w, "traj", Mat(2, 6, CV_32F, traj), 1.0f, 0.5f));
|
||||
|
||||
Mat rgbd(16, 16, CV_32FC4, Scalar(120, 120, 120, 500)); // #3 showRGBD path
|
||||
EXPECT_NO_THROW(viz3d::showRGBD(w, "rgbd", rgbd, Matx33f(8, 0, 8, 0, 8, 8, 0, 0, 1), 0.1f));
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
EXPECT_NO_THROW(updateWindow(w));
|
||||
|
||||
EXPECT_NO_THROW(viz3d::destroyObject(w, "pts"));
|
||||
destroyAllWindows();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 211 KiB |
Reference in New Issue
Block a user