I'm playing around with creating a small voxel based project with LWJGL. Part of the project is loading small chunks of landscape around the player as they move. The loading part of this works okay, but I ran into an issue where as I walked along the +X axis, the chunks of landscape moving the same distance along the -X axis would load. This would also happen for the Z axis.
I got curious, so I tried reversing the X and Z axis rendering direction on the chunks, which seemed to fix the issue. However, I also decided to render the axis as lines as well, and verify that everything was now drawing correctly, with which I generated the following image:
(I can't embed images apparently, so link: http://i.imgur.com/y5hO1Im.png)
In this image, the red, blue and green lines are drawn along the negative axes, whereas the purple, yellow and cyan lines are drawn along the positive axes. What's really weird about this is that the image is showing that the camera is in the +X and +Z range, but internally, the position vector of the camera is in the -X and -Z range. This would make sense as to why the chunks were loading on the opposite axis, as if the camera was rendering on +X but was internally at a position of -X, then the -X chunks would be loaded instead.
So I'm not sure what's going on here anymore. I'm sure there's a small setting or incorrect positive/negative that I'm missing, but I just can't seem to find anything. So I guess my question is, is the camera rendering correctly with the internal position? If so, do I need to just reverse everything that I render? If not, is there something clearly visible in the camera that is messing up the rendering?
Some snippets of relevant code, trying to not to overflow the post with code blocks
Camera.java
public class Camera {
// Camera position
private Vector3f position = new Vector3f(x, y, z);
// Camera view properties
private float pitch = 1f, yaw = 0.0f, roll = 0.0f;
// Mouse sensitivity
private float mouseSensitivity = 0.25f;
// Used to change the yaw of the camera
public void yaw(float amount) {
this.yaw += (amount * this.mouseSensitivity);
}
// Used to change the pitch of the camera
public void pitch(float amount) {
this.pitch += (amount * this.mouseSensitivity);
}
// Used to change the roll of the camera
public void roll(float amount) {
this.roll += amount;
}
// Moves the camera forward relative to its current rotation (yaw)
public void walkForward(float distance) {
position.x -= distance * (float)Math.sin(Math.toRadians(yaw));
position.z += distance * (float)Math.cos(Math.toRadians(yaw));
}
// Moves the camera backward relative to its current rotation (yaw)
public void walkBackwards(float distance) {
position.x += distance * (float)Math.sin(Math.toRadians(yaw));
position.z -= distance * (float)Math.cos(Math.toRadians(yaw));
}
// Strafes the camera left relative to its current rotation (yaw)
public void strafeLeft(float distance) {
position.x -= distance * (float)Math.sin(Math.toRadians(yaw-90));
position.z += distance* (float)Math.cos(Math.toRadians(yaw-90));
}
// Strafes the camera right relative to its current rotation (yaw)
public void strafeRight(float distance) {
position.x -= distance * (float)Math.sin(Math.toRadians(yaw+90));
position.z += distance * (float)Math.cos(Math.toRadians(yaw+90));
}
// Translates and rotates the matrix so that it looks through the camera
public void lookThrough() {
GL11.glRotatef(pitch, 1.0f, 0.0f, 0.0f);
GL11.glRotatef(yaw, 0.0f, 1.0f, 0.0f);
GL11.glTranslatef(position.x, position.y, position.z);
}
}
Main.java render code
private void render() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glLoadIdentity();
// Set the view matrix to the player's view
this.player.lookThrough();
// Render the visible chunks
this.chunkManager.render();
// Draw axis
GL11.glBegin(GL11.GL_LINES);
// X Axis
GL11.glColor3f(1, 0, 0);
GL11.glVertex3f(-100, 0, 0);
GL11.glVertex3f(0, 0, 0);
GL11.glColor3f(1, 1, 0);
GL11.glVertex3f(0, 0, 0);
GL11.glVertex3f(100, 0, 0);
// Y Axis
GL11.glColor3f(0, 1, 0);
GL11.glVertex3f(0, -100, 0);
GL11.glVertex3f(0, 0, 0);
GL11.glColor3f(0, 1, 1);
GL11.glVertex3f(0, 0, 0);
GL11.glVertex3f(0, 100, 0);
// Z Axis
GL11.glColor3f(0, 0, 1);
GL11.glVertex3f(0, 0, -100);
GL11.glVertex3f(0, 0, 0);
GL11.glColor3f(1, 0, 1);
GL11.glVertex3f(0, 0, 0);
GL11.glVertex3f(0, 0, 100);
GL11.glEnd();
// Render the origin
this.origin.render();
}
chunkManager.render() just iterates through each of the loaded chunks and calls .render() on them, which in turn creates a giant solid cube that is rendered at the origin of the chunk.
More code can be provided if needed.
Replace
GL11.glTranslatef(position.x, position.y, position.z);
with
GL11.glTranslatef(-position.x, -position.y, -position.z);
Think about it, you want to be translating the world to the inverse of where the camera is so that that 0,0,0 is where the camera is.
Related
I am creating a game in LibGDX. I have taken the tiles used in Pixel Dungeon and created a tile map using Tiled.
The main character's class is a subclass of Actor, and because the character is animated, I am using this code to draw the sprite:
if (direction.isEastwards() || direction.isNorthwards()) {
Vector3 projectedPosition = getLocation().getCamera().project(
new Vector3(16 * getX() + 1, Gdx.graphics.getHeight()
- (16 * getY()) - 15, 0));
batch.draw(current.getKeyFrame(
animTime += Gdx.graphics.getDeltaTime(), true),
projectedPosition.x, projectedPosition.y);
} else {
Vector3 projectedPosition = getLocation().getCamera().project(
new Vector3(16 * getX() + 13, Gdx.graphics.getHeight()
- (16 * getY()) - 15, 0));
batch.draw(current.getKeyFrame(
animTime += Gdx.graphics.getDeltaTime(), true),
projectedPosition.x, projectedPosition.y);
}
When I launch the game in Eclipse, the sprite initially appears in the correct location. However, if I resize the screen, the sprite ceases to be in the correct location and eventually will disappear off the map.
The same thing happened before I started using projection, and I figured the problem was related to projection. As this is an area I have not explored before I decided after an hour of being able to solve this to ask for help.
The animation flips depending on which direction the character faces, which is why the if/else clause is there.
Addendum: The stage is created with new Stage(new ExtendViewport(800,600),batch);, the resize method updates the camera, and the batch is set to the projection matrix.
Here is more relevant code:
Camera and map initialisation:
camera=new OrthographicCamera();
camera.setToOrtho(false,Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
mapRenderer=new OrthogonalTiledMapRenderer(map,batch);
Render method:
camera.update();
batch.setProjectionMatrix(camera.combined);
mapRenderer.setView(camera);
mapRenderer.render();
stage.act();
stage.draw();
Resize method:
camera.viewportWidth=width;
camera.viewportHeight=height;
camera.update();
Actor draw method:
#Override
public void draw(Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
if (direction.isEastwards() || direction.isNorthwards()) {
Vector3 projectedPosition = getLocation().getCamera().project(
new Vector3(16 * getX() + 1, Gdx.graphics.getHeight()
- (16 * getY()) - 15, 0));
batch.draw(current.getKeyFrame(
animTime += Gdx.graphics.getDeltaTime(), true),
projectedPosition.x, projectedPosition.y);
} else {
Vector3 projectedPosition = getLocation().getCamera().project(
new Vector3(16 * getX() + 13, Gdx.graphics.getHeight()
- (16 * getY()) - 15, 0));
batch.draw(current.getKeyFrame(
animTime += Gdx.graphics.getDeltaTime(), true),
projectedPosition.x, projectedPosition.y);
}
// Walk animation displays for 0.2 seconds
if (current == walk && animTime >= 0.2) {
current = idle;
animTime = 0;
}
}
I think the problem it caused because you dont combine the sprite with camera projection. set your spriteBatch like this :
spriteBatch.setProjectionMatrix(camera.combined);
And dont miss update viewport in resize method :
public void resize(int width, int height) {
camera.viewportWidth = width;
camera.viewportHeight = height;
camera.update();
}
With that, you can resize or zoomin zoomout, the spriteBatch it scalled to the camera projection.
I am trying to move a triangle in the direction of the top vertex.
Depending on the rotation angle.
This is my code:
private static void render() {
// Clear the pixels on the screen and clear the contents of the depth buffer (3D contents of the scene)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset any translations the camera made last frame update
glLoadIdentity();
// Apply the camera position and orientation to the scene
//camera.applyTranslations();
glTranslated(0,0,-5);
glPushMatrix();
glRotated(f.get_direction(),0,0,1);
glTranslated(x,y,0);
f.draw();
glPopMatrix();
x+=(f.get_speed()/30)*cos(f.get_direction()+90);
y+=(f.get_speed()/30)*sin(f.get_direction()+90);
}
The point is that no matter what is the rotation angle that is the direction,
i want to move the triangle according to it.
Did you try to translate according to direction vector but there is simple problem cos and sin arguments is in radians glRotate in degrees:
so we must create static function
static double degToRad(double x)
{
return (x / 180.0) * Math.PI;
}
than use it
glLoadIdenity();
x += Math.cos(degToRad(getDirection() + 90)) * getSpeed();
y += Math.sin(degToRad(getDirection() + 90)) * getSpeed();
glTranslatef(x, y, 0);
glRotatef(getDirection(), 0, 0, 1);
drawObject();
you can also invert direction of movement just subsctacting current angle from 360:
glLoadIdenity();
x += Math.cos(degToRad(360 - getDirection() + 90)) * getSpeed();
y += Math.sin(degToRad(360 - getDirection() + 90)) * getSpeed();
glTranslatef(x, y, 0);
glRotatef(getDirection(), 0, 0, 1);
drawObject();
I am attempting to create a 3D game using solely LWJGL and Slick-util, and I intend for it to be a first-person game, so I need a way to navigate the map using 3D movement. I have a position Vector3f and another vector acc to store acceleration. I have set up the following methods, all bound (in another class) to W, A, S, and D:
public void walkForward()
{
acc.x += walkSpeed * (float) Math.sin(Math.toRadians(yaw)); // Calculations for 3D Movement (please correct me if wrong)
acc.z -= walkSpeed * (float) Math.cos(Math.toRadians(yaw));
}
public void walkBackward()
{
acc.x -= walkSpeed * (float) Math.sin(Math.toRadians(yaw));
acc.z += walkSpeed * (float) Math.cos(Math.toRadians(yaw));
}
public void strafeLeft()
{
acc.x += walkSpeed * (float) Math.sin(Math.toRadians(yaw - 90));
acc.z -= walkSpeed * (float) Math.cos(Math.toRadians(yaw - 90));
}
public void strafeRight()
{
acc.x += walkSpeed * (float) Math.sin(Math.toRadians(yaw + 90));
acc.z -= walkSpeed * (float) Math.cos(Math.toRadians(yaw + 90));
}
Where walkSpeed is a positive float. Also, in my move() method, where movement is actually processed, I have the following:
void move()
{
acc.y = 0.0f; // Keep the player's height stable, for testing purposes.
getPosition().x += acc.x; // Add current velocity to the position.
getPosition().y += acc.y;
getPosition().z += acc.z;
if(acc.x > 0.0f) // Gradually bring player's velocity to 0.
acc.x -= 0.01f;
else if(acc.x < 0.0f)
acc.x += 0.01f;
if(acc.y > 0.0f)
acc.y -= 0.01f;
else if(acc.y < 0.0f)
acc.y += 0.01f;
if(acc.z > 0.0f)
acc.z -= 0.01f;
else if(acc.z < 0.0f)
acc.z += 0.01f;
}
Finally, in the render() method, where transformations are actually made to the game, I have this:
public void render()
{
move();
glRotatef(pitch, 1.0f, 0.0f, 0.0f);
glRotatef(yaw, 0.0f, 1.0f, 0.0f);
glTranslatef(-position.x, -position.y, -position.z);
}
Expected result: Ordinary 3D movement, proper directional motion, etc.
Actual result: Player moves in general direction, speed changes based on both yaw and pitch, releasing all keys (debugging confirms that NO input is being received to the walk() methods causes jittering and strange movement in random directions, whose speed changes based on both yaw and pitch, holding both W + A or W + D causes a massive increase in horizontal speed, etc.
I have a feeling this could be due to missing a pop or push in the matrix, or forgetting to init the identity somewhere. Any help would be appreciated. Thanks in advance!
Methods that manipulate the legacy OpenGL matrix stack, like glRotatef() and glTranslatef(), concatenate the specified transformation with the transformation that is currently on the matrix stack. Since you never reset/restore the transformation, you just keep concatenating more transformations every time render() is called.
To avoid this, you can either reset the transformation before starting to apply the current transformation:
glLoadIdentity();
glRotatef(pitch, 1.0f, 0.0f, 0.0f);
glRotatef(yaw, 0.0f, 1.0f, 0.0f);
glTranslatef(-position.x, -position.y, -position.z);
or save the previous matrix at the start, and restore it at the end:
glPushMatrix();
glRotatef(pitch, 1.0f, 0.0f, 0.0f);
glRotatef(yaw, 0.0f, 1.0f, 0.0f);
glTranslatef(-position.x, -position.y, -position.z);
glPopMatrix();
The second approach has the advantage that it will work correctly if you already had a matrix on the stack (e.g. a view transformation) that you also want to apply, while the first one resets all previous transformations.
Also note that transformations are applied to vertices in the reverse order of being specified. So your transformation sequence will first translate the vertices, then apply the yaw rotation, then the pitch rotation. If that's what you want, it's all good. Otherwise, you'll need to reverse the order.
I have this mouse function in my OpenGL program:
public void mouseInput(){
int mouseX = Mouse.getX();
int mouseY = 600 - Mouse.getY();
int mouseDX = 0, mouseDY = 0;
int lastX = 0, lastY = 0;
mouseDX = mouseX - lastX;
mouseDY = mouseY - lastY;
lastX = mouseX;
lastY = mouseY;
xrot += (float) mouseDX;
yrot += (float) mouseDY;
}
I rotate the "camera" using this code:
glRotatef(xrot, 1.0f, 0.0f, 0.0f);
glRotatef(yrot, 0.f, 1.0f, 0.0f);
And I call the mouseInput() function in the !DisplayIsClosedRequested loop. Currently this causes my game to freak out and my camera rotates all over the place even without me touching the mouse. The cubes I have rendered out also move around the screen randomely. I am using LWJGL, so I cant use any glut functions like glutPassiveMotionFunc(). Can anyone offer help? Basically in summary, my camera is very jerky and rotates the camera in random patterns very fast.
If the camera is rotating even when you are not touching the mouse, you are probably applying the rotation over and over again. You could reset the camera view matrix first (glLoadIdentity() in OpenGL 2 fixed-functionality), every frame, and then apply the rotation. That way you will only rotate from a fixed reference point every frame, instead of the last reference point which was the result of a rotation from a previous frame.
I'm trying to fade in/out an image using JOGL. I can render the image fine, however it is always rendered with full intensity. Below is the code:
private void render(GLAutoDrawable glad) {
GL2 gl = glad.getGL().getGL2();
// Prepares the rendering, things such as disabling depth, clearing the buffer bit etc
setUpGL(gl);
BufferedImage background = main.getCurrentState().getImage();
// Render the background, adapted from here: http://bit.ly/QjeusP
renderImage(gl, background, background.getWidth(), background.getHeight());
gl.glEnable(GL2.GL_BLEND);
gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
gl.glColor4f(0, 0, 0, alpha);
gl.glBegin(GL2.GL_QUADS);
gl.glVertex2f(0, background.getHeight());
gl.glVertex2f(background.getWidth(), background.getHeight());
gl.glVertex2f(background.getWidth(), 0);
gl.glVertex2f(0, 0);
gl.glEnd();
gl.glDisable(GL2.GL_BLEND);
}
the variable alpha is a float, which is incremented by 0.1 for each render, if it exceeds 1.0, it is decremented by 0.1 until it is less than 0, then incremented, and so on.
EDIT 1: Here is the code that updates the alpha value:
private void update() {
if (isTransparent) {
alpha += 0.1f;
if (alpha >= 1.0f) {
alpha = 1.0f;
isTransparent = false;
}
}
else {
alpha -= 0.1f;
if (alpha <= 0.0f) {
alpha = 0.0f;
isTransparent = true;
}
}
}
It may be a depth testing issue. I don't know what renderImage does, but if it just renders a screen-aligned textured quad at z = 0, then the quad you render over it will not get rendered when depth testing is enabled, as it is at the same depth as the texture quad. So try to glDisable(GL_DEPTH_TEST) while rendering your half-black quad (and glEnable it afterwards).
EDIT: Stupid thought, but could it be you're changing the alpha that fast so that you in the end only have a constant gray shade?