test_pie/external/pmp/TrackballViewer.cpp

459 lines
13 KiB
C++
Raw Normal View History

2023-09-14 11:12:02 +02:00
//=============================================================================
// Copyright (C) 2011-2017 The pmp-library developers
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//=============================================================================
#include "TrackballViewer.h"
#include <algorithm>
//=============================================================================
namespace pmp {
//=============================================================================
TrackballViewer::TrackballViewer(const char* title, int width, int height,
bool showgui)
: Window(title, width, height, showgui)
{
// init mouse buttons
for (bool& i : m_buttonDown)
i = false;
m_wheelPos = 0;
// define basic draw modes
addDrawMode("Wireframe");
addDrawMode("Solid Flat");
addDrawMode("Solid Smooth");
setDrawMode("Solid Smooth");
// init OpenGL state
init();
}
//-----------------------------------------------------------------------------
TrackballViewer::~TrackballViewer() = default;
//-----------------------------------------------------------------------------
void TrackballViewer::clearDrawModes()
{
m_nDrawModes = 0;
m_drawModeNames.clear();
}
//-----------------------------------------------------------------------------
unsigned int TrackballViewer::addDrawMode(const std::string& _s)
{
++m_nDrawModes;
m_drawModeNames.push_back(_s);
return m_nDrawModes - 1;
}
//-----------------------------------------------------------------------------
void TrackballViewer::setDrawMode(const std::string& _s)
{
for (unsigned int i = 0; i < m_drawModeNames.size(); ++i)
{
if (m_drawModeNames[i] == _s)
{
m_drawMode = i;
break;
}
}
}
//-----------------------------------------------------------------------------
void TrackballViewer::keyboard(int key, int /*code*/, int action, int /*mods*/)
{
if (action != GLFW_PRESS && action != GLFW_REPEAT)
return;
switch (key)
{
#ifndef __EMSCRIPTEN__
case GLFW_KEY_ESCAPE:
case GLFW_KEY_Q:
{
exit(0);
break;
}
#endif
case GLFW_KEY_G:
{
showImGUI(!showImGUI());
break;
}
case GLFW_KEY_SPACE:
{
if (++m_drawMode >= m_nDrawModes)
m_drawMode = 0;
std::string mode = m_drawModeNames[m_drawMode];
std::cout << "setting draw mode to " << mode << std::endl;
setDrawMode(mode);
break;
}
case GLFW_KEY_LEFT:
{
rotate(vec3(0,1,0), -5.0);
break;
}
case GLFW_KEY_RIGHT:
{
rotate(vec3(0,1,0), 5.0);
break;
}
case GLFW_KEY_UP:
{
rotate(vec3(1,0,0), -5.0);
break;
}
case GLFW_KEY_DOWN:
{
rotate(vec3(1,0,0), 5.0);
break;
}
}
}
//-----------------------------------------------------------------------------
void TrackballViewer::resize(int width, int height)
{
m_width = width;
m_height = height;
glViewport(0, 0, width, height);
}
//-----------------------------------------------------------------------------
void TrackballViewer::display()
{
// clear buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// adjust clipping planes to tightly fit bounding sphere
vec4 mc(m_center, 1.0);
vec4 ec = m_modelviewMatrix * mc;
float z = -ec[2];
m_near = 0.01 * m_radius;
m_far = 10.0 * m_radius;
m_fovy = 45.0;
m_near = std::max(0.001f * m_radius, z - m_radius);
m_far = std::max(0.002f * m_radius, z + 2*m_radius);
// update projection matrix
m_projectionMatrix = perspectiveMatrix(
m_fovy, (float)m_width / (float)m_height, m_near, m_far);
// draw the scene in current draw mode
if (m_drawMode < m_drawModeNames.size())
draw(m_drawModeNames[m_drawMode]);
else
draw("");
}
//-----------------------------------------------------------------------------
void TrackballViewer::mouse(int button, int action, int mods)
{
// record current modifier keys
m_modifiers = mods;
// mouse press
if (action == GLFW_PRESS)
{
double x, y;
cursorPos(x,y);
m_lastPoint2D = ivec2(x, y);
m_lastPointOk = mapToSphere(m_lastPoint2D, m_lastPoint3D);
m_buttonDown[button] = true;
// set rotation center
if (m_modifiers == GLFW_MOD_CONTROL)
{
double x, y;
cursorPos(x, y);
flyTo(x, y);
}
}
// mouse release
else
{
m_lastPointOk = false;
m_buttonDown[button] = false;
}
}
//-----------------------------------------------------------------------------
void TrackballViewer::scroll(double /*xoffset*/, double yoffset)
{
m_wheelPos += yoffset;
float d = -(float)yoffset * 0.12 * m_radius;
#ifdef __EMSCRIPTEN__
d *= 0.5; // scrolling in browser is faster
#endif
translate(vec3(0.0, 0.0, d));
}
//-----------------------------------------------------------------------------
void TrackballViewer::motion(double xpos, double ypos)
{
// zoom
if ((m_buttonDown[GLFW_MOUSE_BUTTON_MIDDLE]) ||
(m_buttonDown[GLFW_MOUSE_BUTTON_LEFT] &&
(m_modifiers == GLFW_MOD_SHIFT)))
{
zoom(xpos, ypos);
}
// translation
else if (m_buttonDown[GLFW_MOUSE_BUTTON_RIGHT] ||
(m_buttonDown[GLFW_MOUSE_BUTTON_LEFT] &&
(m_modifiers == GLFW_MOD_ALT)))
{
translation(xpos, ypos);
}
// rotation
else if (m_buttonDown[GLFW_MOUSE_BUTTON_LEFT])
{
rotation(xpos, ypos);
}
// remember points
m_lastPoint2D = ivec2(xpos, ypos);
m_lastPointOk = mapToSphere(m_lastPoint2D, m_lastPoint3D);
}
//-----------------------------------------------------------------------------
void TrackballViewer::init()
{
// set initial state
glClearColor(1.0, 1.0, 1.0, 1.0);
glEnable(GL_DEPTH_TEST);
// init modelview
m_modelviewMatrix = mat4::identity();
// turn on multi-sampling to anti-alias lines
#ifndef __EMSCRIPTEN__
glEnable(GL_MULTISAMPLE);
GLint n_samples;
glGetIntegerv(GL_SAMPLES, &n_samples);
std::cout << "Multi-sampling uses " << n_samples << " per pixel\n";
#endif
}
//-----------------------------------------------------------------------------
void TrackballViewer::setScene(const vec3& center, float radius)
{
m_center = center;
m_radius = radius;
viewAll();
}
//-----------------------------------------------------------------------------
void TrackballViewer::viewAll()
{
vec4 c = vec4(m_center, 1.0);
vec4 t = m_modelviewMatrix * c;
translate(vec3(-t[0], -t[1], -t[2] - 2.5 * m_radius));
}
//-----------------------------------------------------------------------------
bool TrackballViewer::pick(int x, int y, vec3& result)
{
#ifndef __EMSCRIPTEN__ // WebGL cannot read depth buffer
// get viewport data
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
// take into accout highDPI scaling
x *= m_scaling;
y *= m_scaling;
// in OpenGL y=0 is at the 'bottom'
y = viewport[3] - y;
// read depth buffer value at (x, y_new)
float zf;
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &zf);
if (zf != 1.0f)
{
float xf = ((float)x - (float) viewport[0]) / ((float) viewport[2]) * 2.0f - 1.0f;
float yf = ((float)y - (float) viewport[1]) / ((float) viewport[3]) * 2.0f - 1.0f;
zf = zf * 2.0f - 1.0f;
mat4 mvp = m_projectionMatrix * m_modelviewMatrix;
mat4 inv = inverse(mvp);
vec4 p = inv * vec4(xf, yf, zf, 1.0f);
p /= p[3];
result = vec3(p[0], p[1], p[2]);
return true;
}
#endif
return false;
}
//-----------------------------------------------------------------------------
void TrackballViewer::flyTo(int x, int y)
{
vec3 p;
if (pick(x,y,p))
{
m_center = p;
vec4 c = vec4(m_center, 1.0);
vec4 t = m_modelviewMatrix * c;
translate(vec3(-t[0], -t[1], -0.5*t[2]));
}
}
//-----------------------------------------------------------------------------
bool TrackballViewer::mapToSphere(const ivec2& point2D, vec3& result)
{
if ((point2D[0] >= 0) && (point2D[0] <= m_width) && (point2D[1] >= 0) &&
(point2D[1] <= m_height))
{
double x = (double)(point2D[0] - 0.5 * m_width) / (double)m_width;
double y = (double)(0.5 * m_height - point2D[1]) / (double)m_height;
double sinx = sin(M_PI * x * 0.5);
double siny = sin(M_PI * y * 0.5);
double sinx2siny2 = sinx * sinx + siny * siny;
result[0] = sinx;
result[1] = siny;
result[2] = sinx2siny2 < 1.0 ? sqrt(1.0 - sinx2siny2) : 0.0;
return true;
}
else
return false;
}
//-----------------------------------------------------------------------------
void TrackballViewer::rotation(int x, int y)
{
if (m_lastPointOk)
{
ivec2 newPoint2D;
vec3 newPoint3D;
bool newPointok;
newPoint2D = ivec2(x, y);
newPointok = mapToSphere(newPoint2D, newPoint3D);
if (newPointok)
{
vec3 axis = cross(m_lastPoint3D, newPoint3D);
float cosAngle = dot(m_lastPoint3D, newPoint3D);
if (fabs(cosAngle) < 1.0)
{
float angle = 2.0 * acos(cosAngle) * 180.0 / M_PI;
rotate(axis, angle);
}
}
}
}
//-----------------------------------------------------------------------------
void TrackballViewer::translation(int x, int y)
{
float dx = x - m_lastPoint2D[0];
float dy = y - m_lastPoint2D[1];
vec4 mc = vec4(m_center, 1.0);
vec4 ec = m_modelviewMatrix * mc;
float z = -(ec[2] / ec[3]);
float aspect = (float)m_width / (float)m_height;
float up = tan(m_fovy / 2.0f * M_PI / 180.f) * m_near;
float right = aspect * up;
translate(vec3(2.0 * dx / m_width * right / m_near * z,
-2.0 * dy / m_height * up / m_near * z, 0.0f));
}
//-----------------------------------------------------------------------------
void TrackballViewer::zoom(int, int y)
{
float dy = y - m_lastPoint2D[1];
float h = m_height;
translate(vec3(0.0, 0.0, m_radius * dy * 3.0 / h));
}
//-----------------------------------------------------------------------------
void TrackballViewer::translate(const vec3& t)
{
m_modelviewMatrix = translationMatrix(t) * m_modelviewMatrix;
}
//-----------------------------------------------------------------------------
void TrackballViewer::rotate(const vec3& axis, float angle)
{
// center in eye coordinates
vec4 mc = vec4(m_center, 1.0);
vec4 ec = m_modelviewMatrix * mc;
vec3 c(ec[0] / ec[3], ec[1] / ec[3], ec[2] / ec[3]);
m_modelviewMatrix = translationMatrix(c) * rotationMatrix(axis, angle) *
translationMatrix(-c) * m_modelviewMatrix;
}
//=============================================================================
} // namespace pmp
//=============================================================================