Hello,
I’m trying to put together a 3D simulation of the solar system. I’ve seen a couple good opengl tutorials, but they all seem to use fps game type cameras, which are very different from my needs. I want to have three main controls for the view:
- Left click and drag to translate everything
- scroll to zoom in to cursor. In other words, the point under the cursor remains at the same point on the screen, but the viewable area around it shrinks. Think google maps.
- Rotation around the center of the current view (not necessarily the origin!) using WASD.
I tried using glm:: perspective and glm::lookAt, but I couldn’t find a simple way to get them to work. Instead, I call this during the main loop:
void Visual::updateModelViewProjection() {
model = glm::mat4();
projection = glm::mat4();
view = glm::mat4();
//set view dimensions
projection = glm::ortho(xRange[0], xRange[1], yRange[0], yRange[1], zRange[0], zRange[1]);
double midX = xRange[0] + (xRange[1] - xRange[0]) / 2;
double midY = yRange[0] + (yRange[1] - yRange[0]) / 2;
//translate center of current view to origin, rotate, and translate back
model = glm::translate(model, glm::vec3(midX, midY, 0.0));
model = glm::rotate(model, glm::radians((GLfloat)elevation - 90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, glm::radians((GLfloat)azimuth), glm::vec3(0.0f, 0.0f, 1.0f));
model = glm::translate(model, glm::vec3(-midX, -midY, 0.0));
}
I set the various xyz ranges, aximuth, and elevation in my callbacks:
void Visual::cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
double scaleX = (xRange[1] - xRange[0]) / viewport[2];
double scaleY = (yRange[1] - yRange[0]) / viewport[3];
if (leftMousePressed) {
//positions in pixels * scale = position in world coordinates
dx = scaleX * (xpos - cursorPrevX);
dy = scaleY * (cursorPrevY - ypos);
xRange[0] -= dx;
xRange[1] -= dx;
yRange[0] -= dy;
yRange[1] -= dy;
}
cursorPrevX = xpos;
cursorPrevY = ypos;
}
void Visual::scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
double zoomFactor = 1.1;
if (yoffset > 0)
zoomFactor = 1 / 1.1;
updateModelViewProjection();
glm::mat4 modelview = view*model;
glm::vec4 viewport = { 0.0, 0.0, width, height };
double winX = cursorPrevX;
double winY = width - cursorPrevY;
glm::vec3 screenCoords = { winX, winY, 0.0 };
glm::vec3 position = glm::unProject(screenCoords, modelview, projection, viewport);
double leftSegment = (position.x - xRange[0]) * zoomFactor;
xRange[0] = position.x - leftSegment;
double rightSegment = (xRange[1] - position.x) * zoomFactor;
xRange[1] = position.x + rightSegment;
double bottomSegment = (position.y - yRange[0]) * zoomFactor;
yRange[0] = position.y - bottomSegment;
double topSegment = (yRange[1] - position.y) * zoomFactor;
yRange[1] = position.y + topSegment;
}
void Visual::updateFromKeyPress()
{
// Camera controls
GLfloat cameraSpeed = 200.0f * deltaTime;
if (keys[GLFW_KEY_W]) {
elevation += cameraSpeed;
if (elevation > 90.0f)
elevation = 90.0f;
}
if (keys[GLFW_KEY_S]) {
elevation -= cameraSpeed;
if (elevation < 0.0f)
elevation = 0.0f;
}
if (keys[GLFW_KEY_D]) {
azimuth += cameraSpeed;
if (azimuth > 360.0f)
azimuth = 360.0f;
}
if (keys[GLFW_KEY_A]) {
azimuth -= cameraSpeed;
if (azimuth < 0.0f)
azimuth = 0.0f;
}
}
This mostly works. I can translate, I can zoom, and I can rotate. However, if I rotate and then try to zoom, the point under the cursor no longer remains fixed. Any ideas? Also, this project is my first attempt to learn opengl. I haven’t seen any examples of the same method being used, so I’d definitely appreciate the opinions of more experienced coders on my overall strategy. Am I completely overcomplicating things for myself?
I’m using opengl version 3.2.
Thanks for the help