I have been following along with this tutorial series, whilst customising the code for my own goals (to render a 3D point cloud). I am able to render and move the point cloud around based on the mouse_input callback and can scroll in/out using the scroll callback. From what I've read/understood, the camera should be able to orbit around the point cloud (model) via the keyboard input. I am using W,S,A,D as forward, back, left, right inputs. I've tried lowercase and uppercase input (dont know if that makes a difference). I cant seem to get a response from the model.
I have gone over the code a few times and really can't see where I am going wrong.
Code below.
I am using Visual Studio 2017 Community.
Source.cpp
#include <glad/glad.h>
#include <C:\\Users\\jhansen\\Desktop\\OpenGL\\glad\\KHR\\khrplatform.h>
#include <C:\\Users\\jhansen\\Desktop\\OpenGL\\glad\\glad.c>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <Shader.h>
#include <Camera.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 800;
// camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// tell GLFW to capture our mouse
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
Shader ourShader("VertexShader.vs",
"FragShader.fs");
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
struct Point
{
float x;
float y;
float z;
};
Point points[32000];
// Generate 32000 points
for (int i = 0; i < 32000; i++)
{
points[i].x = (float)((rand() % SCR_WIDTH) + 1);
points[i].y = (float)((rand() % SCR_WIDTH) + 1);
points[i].z = (float)((rand() % SCR_WIDTH) + 1);
// X Coords to Normalised Device coordinates
if (points[i].x > 400)
{
points[i].x = points[i].x * 0.00125f;
}
else if (points[i].x < 400)
{
points[i].x = points[i].x * -0.00125f;
}
else if (points[i].x == 400)
{
points[i].x = 0.0f;
}
// Y Coords to Normalised Device coordinates
if (points[i].y > 400)
{
points[i].y = points[i].y * 0.00125f;
}
else if (points[i].y < 400)
{
points[i].y = points[i].y * -0.00125f;
}
else if (points[i].y == 400)
{
points[i].y = 0.0f;
}
// Z Coords to Normalised Device coordinates
if (points[i].z > 400)
{
points[i].z = points[i].z * 0.00125f;
}
else if (points[i].z < 400)
{
points[i].z = points[i].z * -0.00125f;
}
else if (points[i].z == 400)
{
points[i].z = 0.0f;
}
//cout << points[i].x << ", " << points[i].y << ", " << points[i].z << endl;
}
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
glBindVertexArray(0);
// uncomment this call to draw in wireframe polygons.
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// activate shader
ourShader.use();
// pass projection matrix to shader (note that in this case it could change every frame)
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
ourShader.setMat4("projection", projection);
// camera/view transformation
glm::mat4 view = camera.GetViewMatrix();
ourShader.setMat4("view", view);
glm::mat4 model;
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.3f, 0.5f));
ourShader.setMat4("model", model);
// draw our points array
//glUseProgram(shaderProgram);
glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
glPointSize(3.0f);
glDrawArrays(GL_POINTS, 0, 32000);
// glBindVertexArray(0); // no need to unbind it every time
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
Camera.h
#ifndef CAMERA_H
#define CAMERA_H
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>
// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
// Default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVTY = 0.1f;
const float ZOOM = 45.0f;
// An abstract camera class that processes input and calculates the corresponding Eular Angles, Vectors and Matrices for use in OpenGL
class Camera
{
public:
// Camera Attributes
glm::vec3 Position;
glm::vec3 Front;
glm::vec3 Up;
glm::vec3 Right;
glm::vec3 WorldUp;
// Eular Angles
float Yaw;
float Pitch;
// Camera options
float MovementSpeed;
float MouseSensitivity;
float Zoom;
// Constructor with vectors
Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)
{
Position = position;
WorldUp = up;
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
// Constructor with scalar values
Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)
{
Position = glm::vec3(posX, posY, posZ);
WorldUp = glm::vec3(upX, upY, upZ);
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
// Returns the view matrix calculated using Eular Angles and the LookAt Matrix
glm::mat4 GetViewMatrix()
{
return glm::lookAt(Position, Position + Front, Up);
}
// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
void ProcessKeyboard(Camera_Movement direction, float deltaTime)
{
float velocity = MovementSpeed * deltaTime;
if (direction == FORWARD)
Position += Front * velocity;
if (direction == BACKWARD)
Position -= Front * velocity;
if (direction == LEFT)
Position -= Right * velocity;
if (direction == RIGHT)
Position += Right * velocity;
}
// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
{
xoffset *= MouseSensitivity;
yoffset *= MouseSensitivity;
Yaw += xoffset;
Pitch += yoffset;
// Make sure that when pitch is out of bounds, screen doesn't get flipped
if (constrainPitch)
{
if (Pitch > 89.0f)
Pitch = 89.0f;
if (Pitch < -89.0f)
Pitch = -89.0f;
}
// Update Front, Right and Up Vectors using the updated Eular angles
updateCameraVectors();
}
// Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
void ProcessMouseScroll(float yoffset)
{
if (Zoom >= 1.0f && Zoom <= 45.0f)
Zoom -= yoffset;
if (Zoom <= 1.0f)
Zoom = 1.0f;
if (Zoom >= 45.0f)
Zoom = 45.0f;
}
private:
// Calculates the front vector from the Camera's (updated) Eular Angles
void updateCameraVectors()
{
// Calculate the new Front vector
glm::vec3 front;
front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
front.y = sin(glm::radians(Pitch));
front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
Front = glm::normalize(front);
// Also re-calculate the Right and Up vector
Right = glm::normalize(glm::cross(Front, WorldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
Up = glm::normalize(glm::cross(Right, Front));
}
};
#endif
This might help you as a good reference for this is out of my Player::move() method where different movements are enumerated types.
// -----------------------------------------------------------------------
// move()
// Move The Player In A Desired Direction
void Player::move( Action action, float fDeltaTime ) {
Vector3 v3LookDirection;
v3LookDirection = m_v3LookCenter - m_v3Position;
switch ( action ) {
case MOVING_FORWARD: {
// Prevent Vertical Motion
v3LookDirection.m_fY = 0.0f;
m_v3Position += v3LookDirection * fDeltaTime * m_fLinearSpeed;
m_v3LookCenter += v3LookDirection * fDeltaTime * m_fLinearSpeed;
break;
}
case MOVING_BACK: {
// Prevent Vertical Motion
v3LookDirection.m_fY = 0.0f;
m_v3Position -= v3LookDirection * fDeltaTime * m_fLinearSpeed;
m_v3LookCenter -= v3LookDirection * fDeltaTime * m_fLinearSpeed;
break;
}
case MOVING_LEFT: {
// Get "Side" Direction & Prevent Vertical Motion
v3LookDirection.m_fY = v3LookDirection.m_fX;
v3LookDirection.m_fX = -v3LookDirection.m_fZ;
v3LookDirection.m_fZ = v3LookDirection.m_fY;
v3LookDirection.m_fY = 0.0f;
m_v3Position -= v3LookDirection * fDeltaTime * m_fLinearSpeed;
m_v3LookCenter -= v3LookDirection * fDeltaTime * m_fLinearSpeed;
break;
}
case MOVING_RIGHT: {
// Get "Side" Direction & Prevent Vertical Motion
v3LookDirection.m_fY = v3LookDirection.m_fX;
v3LookDirection.m_fX = -v3LookDirection.m_fZ;
v3LookDirection.m_fZ = v3LookDirection.m_fY;
v3LookDirection.m_fY = 0.0f;
m_v3Position += v3LookDirection * fDeltaTime * m_fLinearSpeed;
m_v3LookCenter += v3LookDirection * fDeltaTime * m_fLinearSpeed;
break;
}
case LOOKING_LEFT: {
/*float fSin = -sin( fDeltaTime * m_fAngularSpeed );
float fCos = cos( fDeltaTime * m_fAngularSpeed );
m_v3LookCenter.m_fX = m_v3Position.m_fX + (-fSin * v3LookDirection.m_fZ + fCos * v3LookDirection.m_fX );
m_v3LookCenter.m_fZ = m_v3Position.m_fZ + ( fCos * v3LookDirection.m_fZ + fSin * v3LookDirection.m_fX );
break;*/
// Third Person
float fSin = sin( fDeltaTime * m_fAngularSpeed );
float fCos = -cos( fDeltaTime * m_fAngularSpeed );
m_v3Position.m_fX = m_v3LookCenter.m_fX + (-fSin * v3LookDirection.m_fZ + fCos * v3LookDirection.m_fX );
m_v3Position.m_fZ = m_v3LookCenter.m_fZ + ( fCos * v3LookDirection.m_fZ + fSin * v3LookDirection.m_fX );
break;
}
case LOOKING_RIGHT: {
/*float fSin = sin( fDeltaTime * m_fAngularSpeed );
float fCos = cos( fDeltaTime * m_fAngularSpeed );
m_v3LookCenter.m_fX = m_v3Position.m_fX + (-fSin * v3LookDirection.m_fZ + fCos * v3LookDirection.m_fX );
m_v3LookCenter.m_fZ = m_v3Position.m_fZ + ( fCos * v3LookDirection.m_fZ + fSin * v3LookDirection.m_fX );
break;*/
// Third Person
float fSin = -sin( fDeltaTime * _fAngularSpeed );
float fCos = -cos( fDeltaTime * _fAngularSpeed );
m_v3Position.m_fX = m_v3LookCenter.m_fX + (-fSin * v3LookDirection.m_fZ + fCos * v3LookDirection.m_fX );
m_v3Position.m_fZ = m_v3LookCenter.m_fZ + ( fCos * v3LookDirection.m_fZ + fSin * v3LookDirection.m_fX );
break;
}
case LOOKING_UP: {
m_v3LookCenter.m_fY -= fDeltaTime * m_fAngularSpeed * m_MouseLookState;
// Check Maximum Values
if ( m_v3LookCenter.m_fY > (m_v3Position.m_fY + m_fMaxUp ) ) {
m_v3LookCenter.m_fY = m_v3Position.m_fY + m_fMaxUp;
} else if ( m_v3LookCenter.m_fY < (m_v3Position.m_fY - m_fMaxDown) ) {
m_v3LookCenter.m_fY = m_v3Position.m_fY - _fMaxDown;
}
break;
}
}
} // move
This is coming from an old project when I was learning OpenGL 1.0 (Legacy). Everything was done manually without existing libraries; even had to write a few vector libraries. This player class is independent from the Camera class but gets it's position and look direction vectors from it. These are tightly integrated into an GameOGL class that creates a window, message proc, message handler and sets up all of OpenGL stuff as well as a very large Scene class object. The math used here works as this belongs to a 3D Game Engine in which a Third Person View dungeon type game is made. Just make sure you are using the appropriate rotation matrices and trig methods for doing rotations based on the handedness of your 3D graph system.
Also the rotational motion that you are trying to achieve might be different. This causes the player - camera to turn left and right as if you are looking into the distance. The type of rotation you might want would be almost considered the inverse of that where your looking direction & distance remains fixed to the model but your camera is rotating through the world at a specific rotational velocity.
Related
I'm making a level editor for my game with OpenGL in C++. I'm trying to make Editor Camera just like in Unity Engine 2D Scene Camera, but I have an issue when I try to implement mouse movement for the camera (Camera Panning). I'm converting mouse position from screen to world space.
ScreenToWorldSpace Method:
Vector3 Application::ScreenToWorldSpace(int mousex, int mousey)
{
double x = 2.0 * mousex / viewportWidth - 1;
double y = 2.0 * mousey / viewportHeight - 1;
Vector4 screenPos = Vector4(x, -y, -1.0f, 1.0f);
Matrix4 ProjectionViewMatrix = camera1->GetProjectionMatrix() * camera1->GetViewMatrix();
Matrix4 InverseProjectionViewMatrix = glm::inverse(ProjectionViewMatrix);
Vector4 worldPos = InverseProjectionViewMatrix * screenPos;
return Vector3(worldPos);
}
The above method works correctly.
But I'm using ScreenToWorldSpace coordinates to update camera position.
Render Method:
void Application::Render(float deltaTime)
{
Vector3 pos = ScreenToWorldSpace(mousePosition.x, mousePosition.y);
// This is the position of a tile not the camera
position = Vector3(0, 0, 0);
Vector3 rotation = Vector3(0, 0, 0);
Vector3 scale = Vector3(1);
Matrix4 translationMatrix = glm::translate(Matrix4(1.0f), position);
Matrix4 rotationMatrix = glm::eulerAngleYXZ(rotation.y, rotation.x, rotation.z);
Matrix4 scaleMatrix = glm::scale(Matrix4(1.0f), scale);
modelMatrix = translationMatrix * rotationMatrix * scaleMatrix;
if (mouseButtonDown)
{
Console << pos.x << ", " << pos.y << Endl;
camera1->position = Vector3(pos.x, pos.y, -10);
}
{
glScissor(0, 0, 900, 600);
glEnable(GL_SCISSOR_TEST);
glClearColor(236 / 255.0f, 64 / 255.0f, 122 / 255.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, 900, 600);
basicShader->Use();
dirt_grass_tex->Use();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
camera1->SetZoom(zoomFactor);
camera1->Update();
Matrix4 mvp = camera1->GetProjectionMatrix() * camera1->GetViewMatrix() * modelMatrix;
basicShader->SetUniformMat4("MVP", mvp);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glDisable(GL_SCISSOR_TEST);
}
}
Camera Class:
#include "camera.h"
Camera::Camera(int width, int height)
{
swidth = width;
sheight = height;
position = Vector3(0, 0, -10);
rotation = Vector3(0, 0, 0);
m_direction = Vector3(0, 0, -5);
m_up = Vector3(0, 1, 0);
m_right = Vector3(1, 0, 0);
m_offset = Vector3(-swidth / 2 * m_zoom, -sheight / 2 * m_zoom, 0);
m_projection = glm::ortho(0.0f * m_zoom, (float)swidth * m_zoom, 0.0f * m_zoom, (float)sheight * m_zoom, -1000.0f, 0.0f);
}
Camera::~Camera()
{
}
void Camera::Update()
{
Vector3 finalPos = position + m_offset;
m_up = glm::cross(m_right, m_direction);
m_viewMatrix = glm::lookAt(finalPos, finalPos + m_direction, m_up);
m_viewMatrix = glm::scale(m_viewMatrix, Vector3(100));
}
void Camera::SetZoom(float zoom)
{
m_zoom = zoom;
m_offset = Vector3(-swidth / 2 * m_zoom, -sheight / 2 * m_zoom, 0);
m_projection = glm::ortho(0.0f * m_zoom, (float)swidth * m_zoom, 0.0f * m_zoom, (float)sheight * m_zoom, -1000.0f, 0.0f);
}
The following is the output I get when I try to move camera with mouse position converted from Screen to World Space:
if (mouseButtonDown)
{
Console << pos.x << ", " << pos.y << Endl;
position = Vector3(pos.x, pos.y, 0);
}
But if I use mouse position converted from Screen to World space using ScreenToWorldSpace Method the object moves perfectly. Have a look at the following gif:
Following is what I'm trying to achieve:
So I'm Trying to make Game Engine Editor, in that I want to implement Editor Scene Camera like unity / unreal engine scene camera. Following is the editor I'm currently working on:
I tried looking into different resources, but i'm clueless. Help me understand how to move the camera with mouse.
What I think is happening:
Since I'm converting mouse position from screen to world space using camera's projectionView matrix and using those world coordinates to move camera position is causing the problem, because when ever camera moves, projectionView is updated which in turn changes mouse position relative to viewMatrix recursively.
I would Appreciate some help.
Ordinarily, you wouldn't want to write the mouse position directly into the camera location (because that will be of limited use in practice - whenever you click on the screen, the camera would jump).
What you probably want to do something along these lines:
Vector3 g_lastPosition;
void onMousePressed(int x, int y) {
// record starting position!
g_lastPosition = ScreenToWorldSpace(x, y);
}
void onMouseMove(int x, int y) {
// find the difference between new position, and last, in world space
Vector3 new_pos = ScreenToWorldSpace(x, y);
Vector3 offset = new_pos - g_lastPosition;
g_lastPosition = new_pos;
// now move camera by offset
camera->position += offset
}
If you are in an orthographic view, then really you don't need to worry about the projection matrix at all.
int g_lastX;
int g_lastY;
void onMousePressed(int x, int y) {
// store mouse pos
g_lastX = x;
g_lastY = y;
}
void onMouseMove(int x, int y) {
// find the difference between new position, and last, in pixels
int offsetX = x - g_lastX;
int offsetY = y - g_lastY;
// update mouse pos
g_lastX = x;
g_lastY = y;
// get as ratio +/- 1
float dx = ((float) offsetX) / swidth;
float dy = ((float) offsetY) / sheight;
// now move camera by offset (might need to multiply by 2 here?)
camera->position.x += camera->m_offset.x * dx;
camera->position.y += camera->m_offset.y * dy;
}
But in general, for any mouse based movement, you always want to be thinking in terms of adding an offset, rather than setting an exact position.
I am following along with this tutorial series, but also trying to customise my solution as I go along (essentially I am trying to render a 3D point cloud -ie. a whole bunch of XYZ points).
I have been able to get the camera working and a 3D environment going. I am happy to work on the rest of that by myself, but the problem I am having is that the scroll wheel is not responding. I am hoping this is blindingly obvious to someone. It seems that the only user callback the program is getting is that of the mouse position - 100% of the time - and that's preventing the scroll_callback function form being heard. Can someone explain why my scroll wheel callback is not being received. Code below.
Let me know if any further info is required.
I am using Visual Studio 2017 Community.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <Shader.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 800;
// camera
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
bool firstMouse = true;
float yaw = -90.0f; // yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction vector pointing to the right so we initially rotate a bit to the left.
float pitch = 0.0f;
float lastX = 800.0f / 2.0;
float lastY = 600.0 / 2.0;
float fov = 45.0f;
// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// tell GLFW to capture our mouse
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
Shader ourShader("VertexShader.vs", "FragShader.fs");
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
struct Point
{
float x;
float y;
float z;
};
Point points[500];
for (int i = 0; i < 500; i++)
{
points[i].x = (float)((rand() % SCR_WIDTH) + 1);
points[i].y = (float)((rand() % SCR_WIDTH) + 1);
points[i].z = (float)((rand() % SCR_WIDTH) + 1);
// X Coords to Normalised Device coordinates
if (points[i].x > 400)
{
points[i].x = points[i].x * 0.00125f;
}
else if (points[i].x < 400)
{
points[i].x = points[i].x * -0.00125f;
}
else if (points[i].x == 400)
{
points[i].x = 0.0f;
}
// Y Coords to Normalised Device coordinates
if (points[i].y > 400)
{
points[i].y = points[i].y * 0.00125f;
}
else if (points[i].y < 400)
{
points[i].y = points[i].y * -0.00125f;
}
else if (points[i].y == 400)
{
points[i].y = 0.0f;
}
// Z Coords to Normalised Device coordinates
if (points[i].z > 400)
{
points[i].z = points[i].z * 0.00125f;
}
else if (points[i].z < 400)
{
points[i].z = points[i].z * -0.00125f;
}
else if (points[i].z == 400)
{
points[i].z = 0.0f;
}
//cout << points[i].x << ", " << points[i].y << ", " << points[i].z << endl;
}
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
glBindVertexArray(0);
// uncomment this call to draw in wireframe polygons.
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// activate shader
ourShader.use();
// create transformations
glm::mat4 projection;
projection = glm::perspective(glm::radians(95.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
ourShader.setMat4("projection", projection); // note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.
// camera/view transformation
glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
ourShader.setMat4("view", view);
glm::mat4 model;
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
ourShader.setMat4("model", model);
// draw our points array
//glUseProgram(shaderProgram);
glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
glPointSize(3.0f);
glDrawArrays(GL_POINTS, 0, 500);
// glBindVertexArray(0); // no need to unbind it every time
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
float cameraSpeed = 2.5 * deltaTime;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
cameraPos += cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
cameraPos -= cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
lastX = xpos;
lastY = ypos;
float sensitivity = 0.1f; // change this value to your liking
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw += xoffset;
pitch += yoffset;
// make sure that when pitch is out of bounds, screen doesn't get flipped
if (pitch > 89.0f)
pitch = 89.0f;
if (pitch < -89.0f)
pitch = -89.0f;
glm::vec3 front;
front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
front.y = sin(glm::radians(pitch));
front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
cameraFront = glm::normalize(front);
}
// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
if (fov >= 1.0f && fov <= 45.0f)
fov -= yoffset;
if (fov <= 1.0f)
fov = 1.0f;
if (fov >= 45.0f)
fov = 45.0f;
}
You're going to facepalm a little:
projection = glm::perspective(glm::radians(95.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
should be:
projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
I ran your code in a debugger and the scroll callback function is running every time but the fov value was never being used. You can use the same technique if you ever want to check if a function is being called by adding a break point anywhere in the function.
The exception says:
Unhandled exception at 0x00F52157 in Foundry.exe: 0xC0000005: access violation reading location 0x00000030.
and points to this line in controls.cpp:
glfwGetCursorPos(window, &xpos, &ypos);
Controls code in separate file controls.cpp:
#include "stdafx.h"
#include <GLFW/glfw3.h>
extern GLFWwindow* window;
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;
#include "controls.hpp"
glm::mat4 ViewMatrix;
glm::mat4 ProjectionMatrix;
glm::mat4 getViewMatrix(){
return ViewMatrix;
}
glm::mat4 getProjectionMatrix(){
return ProjectionMatrix;
}
glm::vec3 position = glm::vec3( 0, 0, 5 );
float horizontalAngle = 3.14f;
float verticalAngle = 0.0f;
float initialFoV = 45.0f;
float speed = 3.0f;
float mouseSpeed = 0.005f;
void computeMatricesFromInputs(){
static double lastTime = glfwGetTime();
double currentTime = glfwGetTime();
float deltaTime = float(currentTime - lastTime);
double xpos;
double ypos;
glfwGetCursorPos(window, &xpos, &ypos);
glfwSetCursorPos(window, 1280/2, 1024/2);
horizontalAngle += mouseSpeed * float (1280/2 - xpos );
verticalAngle += mouseSpeed * float (1024/2 - ypos );
glm::vec3 direction(
cos(verticalAngle) * sin(horizontalAngle),
sin(verticalAngle),
cos(verticalAngle) * cos(horizontalAngle)
);
glm::vec3 right = glm::vec3(
sin(horizontalAngle - 3.14f/2.0f),
0,
cos(horizontalAngle - 3.14f/2.0f)
);
glm::vec3 up = glm::cross( right, direction );
if (glfwGetKey( window, GLFW_KEY_UP || GLFW_KEY_W ) == GLFW_PRESS){
position += direction * deltaTime * speed;
}
if (glfwGetKey( window, GLFW_KEY_DOWN || GLFW_KEY_S ) == GLFW_PRESS){
position -= direction * deltaTime * speed;
}
if (glfwGetKey( window, GLFW_KEY_RIGHT || GLFW_KEY_D ) == GLFW_PRESS){
position += right * deltaTime * speed;
}
if (glfwGetKey( window, GLFW_KEY_LEFT || GLFW_KEY_A ) == GLFW_PRESS){
position -= right * deltaTime * speed;
}
float FoV = initialFoV;
ProjectionMatrix = glm::perspective(FoV, 5.0f / 4.0f, 0.1f, 100.0f);
ViewMatrix = glm::lookAt(position,position+direction,up);
lastTime = currentTime;
}
The program works well without matrices being modified by input controls.
To be honest, I don't know much about low level programming and memory allocation, so this might be the cause.
This is memory access violation not exception that you get there.
That window is likely bad pointer when matrices are computed. Somewhere in your code is result of glfwCreateWindow() assigned to that window. Is it done before computing the matrices? Does this result with not NULL value? there may be glfwDestroyWindow(window); somewhere in your code. If so is it done after computing the matrices?
I'm very new to OpenGL. I have a simple program that allows me to have once bouncing ball. Do you know how I can tweek the code to have two or more balls bounce (using multithreads)? I'm also supposed to have the balls bounce off each other should a collision occur. Here is the code I have for one bouncing ball.
/*
* GL07BouncingBall.cpp: A ball bouncing inside the window
*/
#include <string>
#include <iostream>
#include <thread>
using namespace std;
#include <windows.h> // for MS Windows
#include <GL/glut.h> // GLUT, includes glu.h and gl.h
#include <Math.h> // Needed for sin, cos
#define PI 3.14159265f
// Global variables
char title[] = "Bouncing Ball (2D)"; // Windowed mode's title
int windowWidth = 640; // Windowed mode's width
int windowHeight = 480; // Windowed mode's height
int windowPosX = 50; // Windowed mode's top-left corner x
int windowPosY = 50; // Windowed mode's top-left corner y
GLfloat ballRadius = 0.2f; // Radius of the bouncing ball
GLfloat ballX = 0.0f; // Ball's center (x, y) position
GLfloat ballY = 0.0f;
GLfloat ballXMax, ballXMin, ballYMax, ballYMin; // Ball's center (x, y) bounds
GLfloat xSpeed = 0.02f; // Ball's speed in x and y directions
GLfloat ySpeed = 0.007f;
int refreshMillis = 30; // Refresh period in milliseconds
// Projection clipping area
GLdouble clipAreaXLeft, clipAreaXRight, clipAreaYBottom, clipAreaYTop;
/* Initialize OpenGL Graphics */
void initGL() {
glClearColor(0.0, 0.0, 0.0, 1.0); // Set background (clear) color to black
}
/* Callback handler for window re-paint event */
void display() {
glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer
glMatrixMode(GL_MODELVIEW); // To operate on the model-view matrix
glLoadIdentity(); // Reset model-view matrix
glTranslatef(ballX, ballY, 0.0f); // Translate to (xPos, yPos)
// Use triangular segments to form a circle
glBegin(GL_TRIANGLE_FAN);
glColor3f(0.0f, 0.0f, 1.0f); // Blue
glVertex2f(0.0f, 0.0f); // Center of circle
int numSegments = 100;
GLfloat angle;
for (int i = 0; i <= numSegments; i++) { // Last vertex same as first vertex
angle = i * 2.0f * PI / numSegments; // 360 deg for all segments
glVertex2f(cos(angle) * ballRadius, sin(angle) * ballRadius);
}
glEnd();
glutSwapBuffers(); // Swap front and back buffers (of double buffered mode)
// Animation Control - compute the location for the next refresh
ballX += xSpeed;
ballY += ySpeed;
// Check if the ball exceeds the edges
if (ballX > ballXMax) {
ballX = ballXMax;
xSpeed = -xSpeed;
} else if (ballX < ballXMin) {
ballX = ballXMin;
xSpeed = -xSpeed;
}
if (ballY > ballYMax) {
ballY = ballYMax;
ySpeed = -ySpeed;
} else if (ballY < ballYMin) {
ballY = ballYMin;
ySpeed = -ySpeed;
}
}
/* Call back when the windows is re-sized */
void reshape(GLsizei width, GLsizei height) {
// Compute aspect ratio of the new window
if (height == 0) height = 1; // To prevent divide by 0
GLfloat aspect = (GLfloat)width / (GLfloat)height;
// Set the viewport to cover the new window
glViewport(0, 0, width, height);
// Set the aspect ratio of the clipping area to match the viewport
glMatrixMode(GL_PROJECTION); // To operate on the Projection matrix
glLoadIdentity(); // Reset the projection matrix
if (width >= height) {
clipAreaXLeft = -1.0 * aspect;
clipAreaXRight = 1.0 * aspect;
clipAreaYBottom = -1.0;
clipAreaYTop = 1.0;
} else {
clipAreaXLeft = -1.0;
clipAreaXRight = 1.0;
clipAreaYBottom = -1.0 / aspect;
clipAreaYTop = 1.0 / aspect;
}
gluOrtho2D(clipAreaXLeft, clipAreaXRight, clipAreaYBottom, clipAreaYTop);
ballXMin = clipAreaXLeft + ballRadius;
ballXMax = clipAreaXRight - ballRadius;
ballYMin = clipAreaYBottom + ballRadius;
ballYMax = clipAreaYTop - ballRadius;
}
/* Called back when the timer expired */
void Timer(int value) {
glutPostRedisplay(); // Post a paint request to activate display()
glutTimerFunc(refreshMillis, Timer, 0); // subsequent timer call at milliseconds
}
/* Main function: GLUT runs as a console application starting at main() */
int main(int argc, char** argv) {
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode(GLUT_DOUBLE); // Enable double buffered mode
glutInitWindowSize(windowWidth, windowHeight); // Initial window width and height
glutInitWindowPosition(windowPosX, windowPosY); // Initial window top-left corner (x, y)
glutCreateWindow(title); // Create window with given title
glutDisplayFunc(display); // Register callback handler for window re-paint
glutReshapeFunc(reshape); // Register callback handler for window re-shape
glutTimerFunc(0, Timer, 0); // First timer call immediately
initGL(); // Our own OpenGL initialization
glutMainLoop(); // Enter event-processing loop
return 0;
}
**Edit:
I've added improvements to my code. I've now created two balls, each with its own speed, color, and center. Although, the two balls remain static. The function in which I create the balls is defined by:
void create(double s, GLfloat ballRadius, GLfloat ballX, GLfloat ballY, GLfloat xSpeed, GLfloat ySpeed)//, double r, double t)
{
//ballRadius = f;
//xSpeed = r;
//ySpeed = t;
glTranslatef(ballX, ballY, 0.0f); // Translate to (xPos, yPos)
glBegin(GL_TRIANGLE_FAN);
glColor3f(s, 0.0f, 1.0f); // Blue
glVertex2f(0.0, 0.0f); // Center of circle
int numSegments = 100;
GLfloat angle;
for (int i = 0; i <= numSegments; i++) { // Last vertex same as first vertex
angle = i * 2.0f * PI / numSegments; // 360 deg for all segments
glVertex2f(cos(angle) * ballRadius, sin(angle) * ballRadius);
}
glEnd();
// Animation Control - compute the location for the next refresh
ballX += xSpeed;
ballY += ySpeed;
// Check if the ball exceeds the edges
if (ballX > ballXMax) {
ballX = ballXMax;
xSpeed = -xSpeed;
} else if (ballX < ballXMin) {
ballX = ballXMin;
xSpeed = -xSpeed;
}
if (ballY > ballYMax) {
ballY = ballYMax;
ySpeed = -ySpeed;
} else if (ballY < ballYMin) {
ballY = ballYMin;
ySpeed = -ySpeed;
}
}
And I call this function by:
create(1.0, 0.2f, 0.0f, 0.0f, 0.02f, 0.007f);
create(0.0, 0.1f, 0.0f, 0.0f, 0.04f, 0.014f);
for two separate balls
This code "creates" a circle
// Use triangular segments to form a circle
glBegin(GL_TRIANGLE_FAN);
glColor3f(0.0f, 0.0f, 1.0f); // Blue
glVertex2f(0.0f, 0.0f); // Center of circle
int numSegments = 100;
GLfloat angle;
for (int i = 0; i <= numSegments; i++) { // Last vertex same as first vertex
angle = i * 2.0f * PI / numSegments; // 360 deg for all segments
glVertex2f(cos(angle) * ballRadius, sin(angle) * ballRadius);
}
glEnd();
I've put together a 3rd person camera system using OpenGL and C++ from tutorials and such online, but I can't seem to figure out a specific problem. When I turn using mouse movement, my character is rotated around the camera rather than the camera around the character and the character turning on the spot. What should I do to have the character turn on the spot?
// variables ..
void checkMouse(){
if (mouseXPos > SCREEN_WIDTH/2){
// turn right
yrot += abs(mouseXPos - SCREEN_WIDTH/2) * .005;
} else if (mouseXPos < SCREEN_WIDTH/2){
// turn left
yrot -= abs(mouseXPos - SCREEN_WIDTH/2) * .005;
}
if (mouseYPos > SCREEN_HEIGHT/2){
// look up
xrot += abs(mouseYPos - SCREEN_HEIGHT/2) * .005;
} else if (mouseYPos < SCREEN_HEIGHT/2){
// look down
xrot -= abs(mouseYPos - SCREEN_HEIGHT/2) * .005;
}
}
void checkKeys(){
if(keys['t'] == true){
wireframe=!wireframe;
if(wireframe){
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
if (keys['w'] == true){
float xrotrad, yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xrotrad = (xrot / 180 * 3.141592654f);
xpos += float(sin(yrotrad)) * 10 ;
zpos -= float(cos(yrotrad)) * 10 ;
}
if (keys['s'] == true){
float xrotrad, yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xrotrad = (xrot / 180 * 3.141592654f);
xpos -= float(sin(yrotrad)) * 10;
zpos += float(cos(yrotrad)) * 10;
}
if (keys['a'] == true){
float yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xpos -= float(cos(yrotrad)) * 10;
zpos -= float(sin(yrotrad)) * 10;
}
if (keys['d'] == true){
float yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xpos += float(cos(yrotrad)) * 10;
zpos += float(sin(yrotrad)) * 10;
}
}
void renderScene(){
// Clear framebuffer & depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset Modelview matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Set view position & direction
gluLookAt(0,0,5, 0,0,-1, 0,1,0);
checkKeys();
checkMouse();
// 3rd person object
// draw body
glPushMatrix();
glRotatef(xrot,1.0,0.0,0.0); // keeps object on ground level rather than always in front of camera
glTranslatef(0,-90,-400.0); // keep object 400 away from camera
glRotatef(-90,0.0,1.0,0.0);
glutSolidCube(20);
glPopMatrix();
// CAMERA
glRotatef(xrot,1.0,0.0,0.0); //rotate our camera on the x-axis (left and right)
glRotatef(yrot,0.0,1.0,0.0); //rotate our camera on the y-axis (up and down)
glTranslated(-xpos,-ypos-200,-zpos);
// rest of world
glPushMatrix();
glutSolidCube(30);
glPopMatrix();
// ..
glDisable(GL_TEXTURE_2D);
// Swap double buffer for flicker-free animation
glutSwapBuffers();
}
void updateScene(){
// Wait until at least 16ms passed since start of last frame
// Effectively caps framerate at ~60fps
while(timeGetTime()-lastTickCount<16);
lastTickCount=timeGetTime();
// Draw the next frame
glutPostRedisplay();
}
void keypress (unsigned char key, int x, int y) {
keys[key] = true;
// Test if user pressed ESCAPE (ascii 27)
// If so, exit the program
if(key==27){
exitScene();
}
}
void keypressup (unsigned char key, int x, int y) {
keys[key] = false;
wheel_turn = 0;
}
void mouseMovement(int x, int y) {
mouseXPos = x;
mouseYPos = y;
}
void mouseClick(int button, int state, int x, int y){
if (button == GLUT_LEFT_BUTTON){
if (state == GLUT_DOWN)
lButton = true;
else
lButton = false;
}
}
void setupScene(){
forwards = 0;
strafe = 0;
turn = 0;
std::cout<<"Initializing scene..."<<std::endl;
//Set up Lighting Stuff
glLightfv(GL_LIGHT0, GL_POSITION, left_light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, white_light);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
}
void exitScene(){
std::cout<<"Exiting scene..."<<std::endl;
// Close window
glutDestroyWindow(windowId);
// Free any allocated memory
// Exit program
exit(0);
}
void setViewport(int width, int height) {
// Work out window ratio, avoid divide-by-zero
if(height==0)height=1;
float ratio = float(width)/float(height);
// Reset projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Fill screen with viewport
glViewport(0, 0, width, height);
// Set a 45 degree perspective
gluPerspective(45, ratio, .1, 200000);
}
int main(int argc, char *argv[]){
// Initialise OpenGL
glutInit(&argc, argv);
// Set window position, size & create window
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(50,50);
glutInitWindowSize(SCREEN_WIDTH,SCREEN_HEIGHT);
windowId = glutCreateWindow("3rd person cam");
// Set GLUT callback functions
glutReshapeFunc(setViewport);
glutDisplayFunc(renderScene);
glutIdleFunc(updateScene);
glutKeyboardFunc(keypress);
glutKeyboardUpFunc(keypressup);
glutPassiveMotionFunc(mouseMovement); //check for mouse movement
glutMotionFunc(mouseMovement);
glutMouseFunc(mouseClick);
// Setup OpenGL state & scene resources (models, textures etc)
setupScene();
// Show window & start update loop
glutMainLoop();
return 0;
}
You're rotating the camera around itself — it's akin to you turning your head. You want to change the camera position, revolving around your object of interest.
1. Find your camera position
Look up 'spherical coordinates' for this
Your horizontal angle should vary between (0 and 2*PI) based on mouse x move
Your vertical angle should vary between (0 and PI) based on mouse y move
You can scale the found (x,y,z) position with a value to vary the distance between camera and object
Add the object position to this found position
You now have a valid camera position around your object
2. Find View matrix
There's a handy glut method called gluLookAt, just use that to find your final camera matrix. It needs (camera positoin, object position, and world up(0,1,0))