I've been working on an animation of the Solar System. So far I've gotten the planets to rotate in circles around the Sun. Now I want to make them rotate in ellipses with perihelion and aphelion so that the planets move towards and away from the Sun.
I have some problems understanding the math behind it. I've seen examples like this one where he simply draw the ellipse using GL_LINE_LOOP. I use makeRotationAxis() and makeTranslation() from the FloatUtil class, and I use time as a way to tell how fast the rotation should go. I think my code says more than I can express, so here we go (I've edited the code using the ellipse-equation commented below).
Edit: I've fixed some bugs and simplified the code (also, the planets are moving in the X and Z axis, that's why makeTranslation(.., .., tx, 0f, ty);), but I still cannot get the elliptical orbit, now the planets' rotation is circularly.
The main class:
.....
//Inside init()
// Store the starting time of the application
start = System.currentTimeMillis();
// The planets rotational speed around the planet's own axis (planetary day)
float[] ownRotation = {15.0f, 10.0f, 06.0f, 03.0f, 02.0f, 01.5f, 00.5f, 00.2f, 00.1f};
// The planets rotational speed around the Sun's axis (planetary year)
float[] sunRotation = {10.0f, 08.0f, 06.0f, 04.0f, 02.0f, 01.3f, 01.0f, 00.5f, 00.2f};
// The planets distance from the Sun on the X axis
float[] xDistanceFromSun = {1.6f, 4.0f, 9.0f, 13.0f, 20.0f, 24.0f, 27.0f, 30.0f, 34.0f};
// The planets distance from the Sun on the Y axis
float[] zDistanceFromSun = {0.8f, 2.0f, 4.5f, 6.5f, 10.0f, 12.0f, 13.5f, 15.0f, 17.0f};
// The scale size of the planets
float[] scale = {0.2f, 0.3f, 0.5f, 0.2f, 1.2f, 1.2f, 1.0f, 0.4f, 0.1f};
// The sun object, specifies it's rotation
sun = new Sun(0.5f, 1f);
// Make the planet objects and put them in the array
for(int i = 0; i < scale.length; i++)
planets.add(new Planet( ownRotation[i],
sunRotation[i],
xDistanceFromSun[i],
zDistanceFromSun[i],
scale[i]
));
// Make the moon object
moon = new Moon(
planets.get(2).getPlanetMatrix(),
1.0f,
02.0f,
0.3f);
}
.....
// Inside display()
long now = System.currentTimeMillis();
// Find a time delta for the time passed since the start of execution
float diff = (float) (now - start) / 10000;
// Copy the Sun's matrix to the server
pointer[0].asFloatBuffer().put(sun.getSunMatrix());
// Update the Sun's Matrix
sun.updateSun(diff);
// Copy the plantes's matrixes to the server and then update them
for(int i = 0; i < 9; i++) {
pointer[i+1].asFloatBuffer().put(planets.get(i).getPlanetMatrix());
planets.get(i).updatePlanet(diff);
}
// Copy the moon's matrix to the server
pointer[10].asFloatBuffer().put(moon.getMoonMatrix());
// Update the Moon's Matrix
moon.updateMoon(diff, planets.get(2).getPlanetMatrix());
.....
The Planet class:
public class Planet {
private float sunRotation;
private float ownRotation;
private float diff;
private float[] ownRotationMatrix;
private float[] sunRotationMatrix;
private float[] scaleMatrix;
private float[] ellipticRotationMatrix;
private float[] planetMatrix;
/**
* #param ownRotation The planet's rotation around it's own axis
* #param sunRotation The planet's rotation around the Sun
* #param xDistance The distance from the Planet to the Sun at X = max/min
* #param zDistance The distance from the Planet to the Sun at Y = max/min
* #param scale The scale/size of the Planet
*/
public Planet (float ownRotation, float sunRotation, float xDistance, float zDistance, float scale) {
// Set the values
this.ownRotation = ownRotation;
this.sunRotation = sunRotation;
// Create the matrixes needed
setOwnRotationMatrix(ownRotation);
setSunRotationMatrix(sunRotation);
setEllipticRotationMatrix(xDistance, zDistance);
setScaleMatrix(scale);
// Create the planet matrix
createPlanet();
}
public void setOwnRotationMatrix(float ownRotation) {
this.ownRotationMatrix = FloatUtil.makeRotationAxis(new float[16], 0, ownRotation, 0f, 1f, 0f, new float[3]);
}
public void setSunRotationMatrix(float sunRotation) {
this.sunRotationMatrix = FloatUtil.makeRotationAxis(new float[16], 0, sunRotation, 0f, 1f, 0f, new float[3]);
}
public void setScaleMatrix(float scale) {
this.scaleMatrix = FloatUtil.makeScale(new float[16], false, scale, scale, scale);
}
// tx = a*sin(ang_rad) + sqrt(a*a-b*b);
// tz = b*cos(ang_rad);
public void setEllipticRotationMatrix(float xDistance, float zDistance) {
float tx = (float)(xDistance * Math.sin(diff) + Math.sqrt(xDistance * xDistance - zDistance * zDistance));
float tz = (float)(zDistance * Math.cos(diff));
System.out.println("tx: " + tx);
System.out.println("tz: " + tz);
this.ellipticRotationMatrix = FloatUtil.makeTranslation(new float[16], false, tx, 0f, tz);
}
public void createPlanet() {
this.planetMatrix =
FloatUtil.multMatrix(
FloatUtil.multMatrix(
FloatUtil.multMatrix(
sunRotationMatrix,
ellipticRotationMatrix,
new float[16]
),
ownRotationMatrix,
new float[16]
),
scaleMatrix,
new float[16]
);
}
public void updatePlanet(float diff) {
setOwnRotationMatrix(ownRotation * diff);
setSunRotationMatrix(sunRotation * diff);
createPlanet();
}
public float[] getOwnRotationMatrix() {
return this.ownRotationMatrix;
}
public float[] getPlanetMatrix() {
return this.planetMatrix;
}
}
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'm trying to implement a simple paint program and now I have a problem with zoom, I can't understand how to do it? I tried to adapt the code from here to myself, but it did not work, I just get a black screen. What my problem?
Not using glut or glew!
Here my camera code:
.h
class Camera2d
{
public:
Camera2d(const glm::vec3& pos = glm::vec3(0.f, 0.f, 0.f),
const glm::vec3& up = glm::vec3(0.f, 1.f, 0.f));
//~Camera2d();
void setZoom(const float& zoom);
float getZoom() const noexcept;
glm::mat4 getViewMatrix() const noexcept;
void mouseScrollCallback(const float& yOffset);
protected:
void update();
private:
// Camera zoom
float m_zoom;
// Euler Angles
float m_yaw;
float m_pitch;
public:
// Camera Attributes
glm::vec3 position;
glm::vec3 worldUp;
glm::vec3 front;
glm::vec3 up;
glm::vec3 right;
};
.cpp
Camera2d::Camera2d(
const glm::vec3& pos /* = glm::vec3(0.f, 0.f, 0.f) */,
const glm::vec3& up /* = glm::vec3(0.f, 1.f, 0.f) */
)
: m_zoom(45.f)
, m_yaw(-90.f)
, m_pitch(0.f)
, position(pos)
, worldUp(up)
, front(glm::vec3(0.f, 0.f, -1.f))
{
this->update();
}
void Camera2d::setZoom(const float& zoom)
{
this->m_zoom = zoom;
}
float Camera2d::getZoom() const noexcept
{
return this->m_zoom;
}
glm::mat4 Camera2d::getViewMatrix() const noexcept
{
return glm::lookAt(this->position, this->position + this->front, this->up);
}
void Camera2d::mouseScrollCallback(const float& yOffset)
{
if (m_zoom >= 1.f && m_zoom <= 45.f)
m_zoom -= yOffset;
else if (m_zoom <= 1.f)
m_zoom = 1.f;
else if (m_zoom >= 45.f)
m_zoom = 45.f;
}
void Camera2d::update()
{
// Calculate the new Front vector
glm::vec3 _front;
_front.x = cos(glm::radians(this->m_yaw)) * cos(glm::radians(this->m_pitch));
_front.y = sin(glm::radians(this->m_pitch));
_front.z = cos(glm::radians(this->m_pitch)) * sin(glm::radians(this->m_yaw));
this->front = glm::normalize(_front);
// Also re-calculate the Right and Up vector
this->right = glm::normalize(glm::cross(this->front, this->worldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
this->up = glm::normalize(glm::cross(this->right, this->front));
}
and in main i try smth like this in render loop
// pass projection matrix to shader
glm::mat4 projection = glm::perspective(glm::radians(camera.getZoom()),
static_cast<float>(WIDTH) / static_cast<float>(HEIGHT),
0.1f,
10000.f);
shaderProg.setMat4("projecton", projection);
// camera view transformation
glm::mat4 view = camera.getViewMatrix();
shaderProg.setMat4("view", view);
here i have just 1 model its my white bg-texture
glm::mat4 model = glm::translate(model, glm::vec3(0.f, 0.f, 0.f));
model = glm::rotate(model, glm::radians(0.f), glm::vec3(1.0f, 0.3f, 0.5f));
shaderProg.setMat4("model", model);
All code on github: here
You're working in 2D, forget about the camera, forget about projection, forget about following OpenGL tutorials, they're aimed at 3D graphics.
What you need is just a rectangle to fill your screen. Start with the vertices at the the corners of the screen, starting from the top-left corner and moving counterclockwise: (-1,1,0) (-1,-1,0) (1,-1,0) (1,1,0). Forget about Z, you're working in 2D.
You draw on a texture, and the texture coordinates are (0,1) (0,0) (1,0) (1,1), same order as above. Zooming is now just a matter of scaling the rectangle. You have a matrix to determine the scale and one to determine the position. Forget about rotations, front vectors and all that stuff. In the vertex shader you scale and then translate the vertices as usual, in this order. Done.
To interact for example you can have mouse wheel up increasing the scale factor and mouse wheel down decreasing it. Hold click and move the mouse to change the (x,y) position. Again forget about Z. Throw those values into the vertex shader and do the usual transformations.
I've been trying to emulate gluLookAt functionality, but with Quaternions. Each of my game object have a TranslationComponent. This component stores the object's position (glm::vec3), rotation (glm::quat) and scale (glm::vec3). The camera calculates its position each tick doing the following:
// UP = glm::vec3(0,1,0);
//FORWARD = glm::vec3(0,0,1);
cameraPosition = playerPosition - (UP * distanceUP) - (FORWARD * distanceAway);
This position code works as expexted, the camera is place 3 metres behind the player and 1 metre up. Now, the camera's Quaternion is set to the follow:
//Looking at the player's feet
cameraRotation = quatFromToRotation(FORWARD, playerPosition);
The rendering engine now takes these values and generates the ViewMatrix (camera) and the ModelMatrix (player) and then renders the scene. The code looks like this:
glm::mat4 viewTranslationMatrix =
glm::translate(glm::mat4(1.0f), cameraTransform->getPosition());
glm::mat4 viewScaleMatrix =
glm::scale(glm::mat4(1.0f), cameraTransform->getScale());
glm::mat4 viewRotationMatrix =
glm::mat4_cast(cameraTransform->getRotation());
viewMatrix = viewTranslationMatrix * viewRotationMatrix * viewScaleMatrix;
quatFromToRotation(glm::vec3 from, glm::vec3 to) is defined as the following:
glm::quat quatFromToRotation(glm::vec3 from, glm::vec3 to)
{
from = glm::normalize(from); to = glm::normalize(to);
float cosTheta = glm::dot(from, to);
glm::vec3 rotationAxis;
if (cosTheta < -1 + 0.001f)
{
rotationAxis = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), from);
if (glm::length2(rotationAxis) < 0.01f)
rotationAxis = glm::cross(glm::vec3(1.0f, 0.0f, 0.0f), from);
rotationAxis = glm::normalize(rotationAxis);
return glm::angleAxis(180.0f, rotationAxis);
}
rotationAxis = glm::cross(from, to);
float s = sqrt((1.0f + cosTheta) * 2.0f);
float invis = 1.0f / s;
return glm::quat(
s * 0.5f,
rotationAxis.x * invis,
rotationAxis.y * invis,
rotationAxis.z * invis
);
}
What I'm having troubles with is the fact the cameraRotation isn't being set correctly. No matter where the player is, the camera's forward is always (0,0,-1)
Your problem is in the line
//Looking at the player's feet
cameraRotation = quatToFromRotation(FORWARD, playerPosition);
You need to look from the camera position to the player's feet - not from "one meter above the player" (assuming the player is at (0,0,0) when you initially do this). Replace FORWARD with cameraPosition:
cameraRotation = quatToFromRotation(cameraPosition, playerPosition);
EDIT I believe you have an error in your quatToFromRotation function as well. See https://stackoverflow.com/a/11741520/1967396 for a very nice explanation (and some pseudo code) of quaternion rotation.
So I'm trying to figure out how to mannually create a camera class that creates a local frame for camera transformations. I've created a player object based on OpenGL SuperBible's GLFrame class.
I got keyboard keys mapped to the MoveUp, MoveRight and MoveForward functions and the horizontal and vertical mouse movements are mapped to the xRot variable and rotateLocalY function. This is done to create a FPS style camera.
The problem however is in the RotateLocalY. Translation works fine and so does the vertical mouse movement but the horizontal movement scales all my objects down or up in a weird way. Besides the scaling, the rotation also seems to restrict itself to 180 degrees and rotates around the world origin (0.0) instead of my player's local position.
I figured that the scaling had something to do with normalizing vectors but the GLframe class (which I used for reference) never normalized any vectors and that class works just fine. Normalizing most of my vectors only solved the scaling and all the other problems were still there so I'm figuring one piece of code is causing all these problems?
I can't seem to figure out where the problem lies, I'll post all the appropriate code here and a screenshot to show the scaling.
Player object
Player::Player()
{
location[0] = 0.0f; location[1] = 0.0f; location[2] = 0.0f;
up[0] = 0.0f; up[1] = 1.0f; up[2] = 0.0f;
forward[0] = 0.0f; forward[1] = 0.0f; forward[2] = -1.0f;
}
// Does all the camera transformation. Should be called before scene rendering!
void Player::ApplyTransform()
{
M3DMatrix44f cameraMatrix;
this->getTransformationMatrix(cameraMatrix);
glRotatef(xAngle, 1.0f, 0.0f, 0.0f);
glMultMatrixf(cameraMatrix);
}
void Player::MoveForward(GLfloat delta)
{
location[0] += forward[0] * delta;
location[1] += forward[1] * delta;
location[2] += forward[2] * delta;
}
void Player::MoveUp(GLfloat delta)
{
location[0] += up[0] * delta;
location[1] += up[1] * delta;
location[2] += up[2] * delta;
}
void Player::MoveRight(GLfloat delta)
{
// Get X axis vector first via cross product
M3DVector3f xAxis;
m3dCrossProduct(xAxis, up, forward);
location[0] += xAxis[0] * delta;
location[1] += xAxis[1] * delta;
location[2] += xAxis[2] * delta;
}
void Player::RotateLocalY(GLfloat angle)
{
// Calculate a rotation matrix first
M3DMatrix44f rotationMatrix;
// Rotate around the up vector
m3dRotationMatrix44(rotationMatrix, angle, up[0], up[1], up[2]); // Use up vector to get correct rotations even with multiple rotations used.
// Get new forward vector out of the rotation matrix
M3DVector3f newForward;
newForward[0] = rotationMatrix[0] * forward[0] + rotationMatrix[4] * forward[1] + rotationMatrix[8] * forward[2];
newForward[1] = rotationMatrix[1] * forward[1] + rotationMatrix[5] * forward[1] + rotationMatrix[9] * forward[2];
newForward[2] = rotationMatrix[2] * forward[2] + rotationMatrix[6] * forward[1] + rotationMatrix[10] * forward[2];
m3dCopyVector3(forward, newForward);
}
void Player::getTransformationMatrix(M3DMatrix44f matrix)
{
// Get Z axis (Z axis is reversed with camera transformations)
M3DVector3f zAxis;
zAxis[0] = -forward[0];
zAxis[1] = -forward[1];
zAxis[2] = -forward[2];
// Get X axis
M3DVector3f xAxis;
m3dCrossProduct(xAxis, up, zAxis);
// Fill in X column in transformation matrix
m3dSetMatrixColumn44(matrix, xAxis, 0); // first column
matrix[3] = 0.0f; // Set 4th value to 0
// Fill in the Y column
m3dSetMatrixColumn44(matrix, up, 1); // 2nd column
matrix[7] = 0.0f;
// Fill in the Z column
m3dSetMatrixColumn44(matrix, zAxis, 2); // 3rd column
matrix[11] = 0.0f;
// Do the translation
M3DVector3f negativeLocation; // Required for camera transform (right handed OpenGL system. Looking down negative Z axis)
negativeLocation[0] = -location[0];
negativeLocation[1] = -location[1];
negativeLocation[2] = -location[2];
m3dSetMatrixColumn44(matrix, negativeLocation, 3); // 4th column
matrix[15] = 1.0f;
}
Player object header
class Player
{
public:
//////////////////////////////////////
// Variables
M3DVector3f location;
M3DVector3f up;
M3DVector3f forward;
GLfloat xAngle; // Used for FPS divided X angle rotation (can't combine yaw and pitch since we'll also get a Roll which we don't want for FPS)
/////////////////////////////////////
// Functions
Player();
void ApplyTransform();
void MoveForward(GLfloat delta);
void MoveUp(GLfloat delta);
void MoveRight(GLfloat delta);
void RotateLocalY(GLfloat angle); // Only need rotation on local axis for FPS camera style. Then a translation on world X axis. (done in apply transform)
private:
void getTransformationMatrix(M3DMatrix44f matrix);
};
Applying transformations
// Clear screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Apply camera transforms
player.ApplyTransform();
// Set up lights
...
// Use shaders
...
// Render the scene
RenderScene();
// Do post rendering operations
glutSwapBuffers();
and mouse
float mouseSensitivity = 500.0f;
float horizontal = (width / 2) - mouseX;
float vertical = (height / 2) - mouseY;
horizontal /= mouseSensitivity;
vertical /= (mouseSensitivity / 25);
player.xAngle += -vertical;
player.RotateLocalY(horizontal);
glutWarpPointer((width / 2), (height / 2));
Honestly I think you are taking a way to complicated approach to your problem. There are many ways to create a camera. My favorite is using a R3-Vector and a Quaternion, but you could also work with a R3-Vector and two floats (pitch and yaw).
The setup with two angles is simple:
glLoadIdentity();
glTranslatef(-pos[0], -pos[1], -pos[2]);
glRotatef(-yaw, 0.0f, 0.0f, 1.0f);
glRotatef(-pitch, 0.0f, 1.0f, 0.0f);
The tricky part now is moving the camera. You must do something along the lines of:
flaot ds = speed * dt;
position += tranform_y(pich, tranform_z(yaw, Vector3(ds, 0, 0)));
How to do the transforms, I would have to look that up, but you could to it by using a rotation matrix
Rotation is trivial, just add or subtract from the pitch and yaw values.
I like using a quaternion for the orientation because it is general and thus you have a camera (any entity that is) that independent of any movement scheme. In this case you have a camera that looks like so:
class Camera
{
public:
// lots of stuff omitted
void setup();
void move_local(Vector3f value);
void rotate(float dy, float dz);
private:
mx::Vector3f position;
mx::Quaternionf orientation;
};
Then the setup code uses shamelessly gluLookAt; you could make a transformation matrix out of it, but I never got it to work right.
void Camera::setup()
{
// projection related stuff
mx::Vector3f eye = position;
mx::Vector3f forward = mx::transform(orientation, mx::Vector3f(1, 0, 0));
mx::Vector3f center = eye + forward;
mx::Vector3f up = mx::transform(orientation, mx::Vector3f(0, 0, 1));
gluLookAt(eye(0), eye(1), eye(2), center(0), center(1), center(2), up(0), up(1), up(2));
}
Moving the camera in local frame is also simple:
void Camera::move_local(Vector3f value)
{
position += mx::transform(orientation, value);
}
The rotation is also straight forward.
void Camera::rotate(float dy, float dz)
{
mx::Quaternionf o = orientation;
o = mx::axis_angle_to_quaternion(horizontal, mx::Vector3f(0, 0, 1)) * o;
o = o * mx::axis_angle_to_quaternion(vertical, mx::Vector3f(0, 1, 0));
orientation = o;
}
(Shameless plug):
If you are asking what math library I use, it is mathex. I wrote it...
I am using ray picking to find the boundaries for a character. It's not optimal, but it's the best I can do and will have to do; I need to have (close to) pixelperfect collisions and also I have lots and lots of objects.
I do not however get the raypicking to work correctly. It collides, but not at the right places. I tried adjust the size of the ray, and more to no avail.
Excuse me for the messy code, I just threw it together.
private void renderCollision(Vector3f dir){
//
// Render the models
//
glPushMatrix();
glPushAttrib(GL_ALL_ATTRIB_BITS);
float size = 10.0f;
Picker.startPicking2D(10, 10, 20, 20, -1.0f, size);
//Picker.startPicking2D(10, 10, 20, 20, 0.1f, 20.0f);
glClear (GL_COLOR_BUFFER_BIT);
glDisable(GL_BLEND);
glDisable(GL_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(true);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearDepth(100.0f);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_TEXTURE_2D);
glLoadIdentity ();
//box.drawMoving(camera);
player.move(dir);
glTranslatef(-player.position.x, -player.position.y + size / 2.0f, -player.position.z);
glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
box.drawAll();
glDisable(GL_DEPTH_TEST);
boolean hit = Picker.getHit();
if (hit) {
player.move(new Vector3f(-dir.x, -dir.y, -dir.z));
}
Picker.stopPicking();
glPopAttrib();
glPopMatrix();
}
public class Picker {
private static IntBuffer selBuffer;
private static int hits;
private static int xSelected;
private static int ySelected;
/**
* Makes the game available for picking (when in 3D mode)
*
* #param xMouse The x coordinate of the mouse on the screen
* #param yMouse The y coordinate of the mouse on the screen
*/
public static void startPicking3D(int xMouse, int yMouse, int screenWidth, int screenHeight, float near, float far) {
startPickingGeneric(xMouse, yMouse);
GLU.gluPerspective(SCREEN_FOV, SCREEN_RAT,
near, far);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
}
/**
* Makes the game available for picking (when in 2D mode)
*
* #param xMouse The x coordinate of the mouse on the screen
* #param yMouse The y coordinate of the mouse on the screen
*/
public static void startPicking2D(int xMouse, int yMouse, int screenWidth, int screenHeight, float near, float far) {
startPickingGeneric(xMouse, yMouse);
GL11.glOrtho(0, screenWidth, 0, screenHeight, near, far);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
}
/**
* Makes the game available for picking (generic)
*
* #param xMouse The x coordinate of the mouse on the screen
* #param yMouse The y coordinate of the mouse on the screen
*/
private static void startPickingGeneric(int xMouse, int yMouse){
// The selection buffer
selBuffer = ByteBuffer.allocateDirect(1024).order(ByteOrder.nativeOrder()).
asIntBuffer();
IntBuffer vpBuffer = ByteBuffer.allocateDirect(64).
order(ByteOrder.nativeOrder()).asIntBuffer();
// Size of the viewport. [0] Is <x>, [1] Is <y>, [2] Is <width>, [3] Is <height>
int[] viewport = new int[4];
// Get the viewport info
GL11.glGetInteger(GL11.GL_VIEWPORT, vpBuffer);
vpBuffer.get(viewport);
// Set the buffer that OpenGL uses for selection to our buffer
GL11.glSelectBuffer(selBuffer);
// Change to selection mode
GL11.glRenderMode(GL11.GL_SELECT);
// Initialize the name stack (used for identifying which object was selected)
GL11.glInitNames();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPushMatrix();
GL11.glLoadIdentity();
// Create 5x5 pixel picking region near cursor location
GLU.gluPickMatrix((float) xMouse, (float) yMouse,
5.0f, 5.0f, IntBuffer.wrap(viewport));
}
/**
* Stops the picking mode
*/
public static void stopPicking(){
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
hits = 0;
hits = GL11.glRenderMode(GL11.GL_RENDER);
}
/**
* Gets the tile the mouse points to
*
* #return TileCoords object with the coordinates of the selected tile
*/
public static boolean getHit(){
int[] buffer = new int[256];
xSelected = -1000;
ySelected = -1000;
selBuffer.get(buffer);
if (hits > 0) {
// If there were more than 0 hits
xSelected = buffer[3]; // Make our selection the first object
ySelected = buffer[4];
int depth = buffer[1]; // Store how far away it is
for (int i = 1; i < hits; i++) {
// Loop through all the detected hits
// If this object is closer to us than the one we have selected
if (buffer[i * 4 + 1] < (int) depth) {
xSelected = buffer[i * 4 + 3]; // Select the closest object
ySelected = buffer[i * 4 + 4];
depth = buffer[i * 4 + 1]; // Store how far away it is
}
}
return true;
}
return false;
}
}
I should rotate around x-axis instead of y-axis and then translate.