I've implemented a camera in Java using a position vector and three direction vectors so I can use gluLookAt(); moving around in `ghost mode' works fine enough, but I want to add collision detection. I can't seem to figure out how to transform my position vector to coordinates in which OpenGL draws my objects.
A rough sketch of my drawing loop is this:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
camera.setView();
drawer.drawTheScene();
I'm at a loss of how to proceed; looking at the ModelView matrix between calls and my position vector, I haven't found any kind of correlation.
Finally figured it out by reviewing http://fly.cc.fer.hr/~unreal/theredbook/chapter03.html again. To get from eye space (camera) to object space, you have to multiply that vector with the inverse of the ModelView matrix, or in code:
Vector4f vpos = new Vector4f(0, 0, 0, 1);
// (0,0,0,1) because it's relative to the cam
float mv[]=new float[16];
ByteBuffer temp = ByteBuffer.allocateDirect(64);
temp.order(ByteOrder.nativeOrder());
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, (FloatBuffer)temp.asFloatBuffer());
temp.asFloatBuffer().get(mv);
Matrix4f m4 = new Matrix4f();
m4.load((FloatBuffer)temp.asFloatBuffer());
m4.invert();
vpos = Matrix4f.transform(m4, vpos, vpos);
Related
I am loading chunk values from a height map to create infinite terrain, but I'm having issues with the rendering of each mesh chunk. When I load a single 500x500 unit mesh, it is a smooth mesh. When I load a 5x5 of 100x100 meshes, it is a jumbled up mess.
I made the two different types of meshes save an image of the height of each mesh, and they both give a smooth height map with gradual value changes.
...But when I render them, this is what I see:
500x500 mesh
5x5 100x100 mesh
As you can see, the 5x5 mesh isn't at all aligned. The first three chunks 0:0, 0:1, and 1:0 seem to look correct, but the others are all different. All rotations in their transformation matrix is vec3(0,0,0), but they generate like this. Here is the code used to render them:
public void render() {
shader.start();
shader.loadViewMatrix(Maths.createViewMatrix(engine.getPlayer()));
shader.loadLight(engine.getManagers().getLightManager().getLights().get(0));
for(Chunk chunk : engine.getManagers().getTerrainManager().getLoadedChunks().values()) {
RawModel model = chunk.getModel();
bindChunk(chunk);
loadShaderUniforms(chunk);
GL11.glDrawElements(GL11.GL_TRIANGLES, model.getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
unbindChunk();
}
shader.stop();
}
private void bindChunk(Chunk chunk) {
RawModel rawModel = chunk.getModel();
GL30.glBindVertexArray(rawModel.getVaoID());
GL20.glEnableVertexAttribArray(0); // position
GL20.glEnableVertexAttribArray(1); // textureCoordinates
GL20.glEnableVertexAttribArray(2); // normal
ModelTexture texture = chunk.getTexture();
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture.getTextureID());
}
private void unbindChunk() {
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);
}
private void loadShaderUniforms(Chunk chunk) {
Matrix4f transformation = Maths.createTransformationMatrix(chunk.getLocation().getPosition(),
chunk.getLocation().getRotation(), 1, true);
System.out.println(chunk.getLocation().getRotation());
shader.loadTransformationMatrix(transformation);
shader.setShowHeightMap(chunk.shouldShowHeightMap());
}
I'm not familiar with the details of how LWJGL uses VAOs, so it might be a simple issue I'm forgetting. Any help would be appreciated as I've spent several days just narrowing down the issue to a rendering problem instead of a problem with the perlin noise/mesh generation.
I discovered the issue! There was a flaw in how I created the mesh which caused it to render the triangles flipped over. I discovered this by turning off culling and observing it from both sides and moving the three vertex coordinates around. Fixing the vertex face (e.g. swapping it from a clockwise to counter-clockwise and then reversing the X and Z coordinates) fixed it.
I am also fiddling with the global/local rotation problem and I cannnot put my finger on it. I used a lwjgl book for the implementation of my game, using openGL and LWJGL. I am using the JOML lib for vectors and matrices.
The modelview matrix construction is below. By the book, this is originally without local rotations, I added them myself. The idea is that each object has a global and local rotation. Those rotations get individually calculated and then are multiplied left/right side with the modelview matrix.
public Matrix4f getModelViewMatrix(Object obj, Matrix4f viewMatrix) {
Vector3f rotation = obj.getRot();
Vector3f localRot = obj.getLocalRot();
Matrix4f localRotMat = new Matrix4f().identity();
Matrix4f worldRotMat = new Matrix4f().identity();
localRotMat.rotateLocalX((float)Math.toRadians(localRot.x)).
rotateLocalY((float)Math.toRadians(localRot.y)).
rotateLocalZ((float)Math.toRadians(localRot.z));
worldRotMat.rotateX((float)Math.toRadians(-rotation.x)).
rotateY((float)Math.toRadians(-rotation.y)).
rotateZ((float)Math.toRadians(-rotation.z));
modelViewMatrix.identity().translate(obj.getPos());
modelViewMatrix.mulLocal(localRotMat);
modelViewMatrix.mul(worldRotMat);
modelViewMatrix.scale(obj.getScale());
Matrix4f viewCurr = new Matrix4f(viewMatrix);
return viewCurr.mul(modelViewMatrix);
}
This still results in local rotations around the 'wrong' axes. I've seen implementations using quaternions and read about gimbal lock and the like, but either the answers are very specific or too general for me. Furthermore, it would be great if I wouldn't need to use a quaternions implementation, as I would have to refactor a lot of code possibly.
Relevant code for the object class:
// Object class
private final Vector3f rot;
private final Vector3f localRot;
public Object() {
pos = new Vector3f(0, 0, 0);
scale = 1;
rot = new Vector3f(0, 0, 0);
localRot = new Vector3f(0, 0, 0);
}
// getters and setters for above
Can somebody explain what is wrong about the calculation of the rotations for the modelview matrix?
EDIT:
I can rewrite the code like below, which is a bit more in line with the hints from #GeestWagen. However, the 'local rotation' of my object is still displayed as global, so it indeed seems like it is applied 'the same' rotation twice. However, now I am stuck, because I cant find more documentation on these functions (rotateLocal/rotate).
modelViewMatrix.identity().translate(obj.getPos()).
rotateLocalX((float)Math.toRadians(-localRot.x)).
rotateLocalY((float)Math.toRadians(-localRot.y)).
rotateLocalZ((float)Math.toRadians(-localRot.z)).
rotateX((float)Math.toRadians(-rotation.x)).
rotateY((float)Math.toRadians(-rotation.y)).
rotateZ((float)Math.toRadians(-rotation.z)).
scale(obj.getScale());
Okay, I finally fixed it. It resulted in me doing a bunch more research. What I came up with was the following:
Vector3f rotation = obj.getRot();
Vector3f localRot = obj.getLocalRot();
Quaternionf rotationQ = new Quaternionf().
rotateAxis((float)Math.toRadians(-localRot.z), new Vector3f(0f, 0f, 1f)).
rotateAxis((float)Math.toRadians(-localRot.y), new Vector3f(0f, 1f, 0f)).
rotateAxis((float)Math.toRadians(-localRot.x), new Vector3f(1f, 0f, 0f)).
premul(new Quaternionf().
rotateX((float)Math.toRadians(-rotation.x)).
rotateY((float)Math.toRadians(-rotation.y)).
rotateZ((float)Math.toRadians(-rotation.z))
);
modelViewMatrix.identity().
translate(obj.getPos()).
rotate(rotationQ).
scale(obj.getScale());
This is inspired by among others this and this. What confused me a lot was the lack of hits on doing local and global rotations. Most stuff I was able to find was either. This creates a quaternion and sets the x, y, and z axes to the local rotation of the object. Then it pre-multiplies by a quaternion which axes are set to the global rotation of the object. Then this resulting quaternion is used for the modelView matrix.
Thus, for combining local and global rotations, a quaternion seems to be necessary. I thought it was used to make sure the axes do not change in a local/global rotation, but they should also be used when combining both.
i try to rotate a 3D instance when I move the mouse. i use Orthographic Camera, and i have a tiled isomap in background.
[EDIT] I use this code :
public boolean mouseMoved (int x, int y) {
Vector3 direction = new Vector3();
Vector3 worldCoordinate = cam.unproject(new Vector3(x, y, 0));
direction = (worldCoordinate).sub(position).nor();
direction.set(-direction.x, -direction.y, -direction.z);
Matrix4 instanceRotation = instance.transform.cpy().mul(instance.transform);
instanceRotation.setToLookAt(direction, new Vector3(0,-1,0));
instanceRotation.rotate(0, 0, 1, 180);
quat = new Quaternion();
instanceRotation.getRotation(quat);
instance.transform.set(position, quat);
return false;
}
Now, the instance follows the cursor, but in all axes. I just want to turn on the Y axis. I can change quat.x = 0 and quat.z = 0 to lock the rotation. After that, the rotation only works on the Y axis. But, the model should turn over if my cursor is at the top of the screen, but it remains facing, no matter where my cursor is.
I don't know how to convert the coordinates to tell my rotation that it needs to turn over. And to modify the quat in this way is not very elegant
Edit : Some image for illustrate what i want
Here, with cursor in the bottom of the screen, the model turn to look in good direction :
Here, the model dont turn over, like the cursor is in bottom of the screen :
I am trying to find the coordinates of my mouse on a flat 3D surface. After googling a bit on that, I found out that you use gluUnProject for doing so. So, I implemented that. Here is my code (when taking away the parts that are not interesting):
public class Input {
private FloatBuffer modelView = BufferUtils.createFloatBuffer(16);
private FloatBuffer projection = BufferUtils.createFloatBuffer(16);
private IntBuffer viewport = BufferUtils.createIntBuffer(16);
private FloatBuffer location = BufferUtils.createFloatBuffer(3);
private FloatBuffer winZ = BufferUtils.createFloatBuffer(1);
public float[] getMapCoords(int x, int y)
{
modelView.clear().rewind();
projection.clear().rewind();
viewport.clear().rewind();
location.clear().rewind();
winZ.clear().rewind();
glGetFloat(GL_MODELVIEW_MATRIX, modelView);
glGetFloat(GL_PROJECTION_MATRIX, projection);
glGetInteger(GL_VIEWPORT, viewport);
float winX = (float)x;
float winY = (float)viewport.get(3) - (float)y;
glReadPixels(x, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, winZ);
gluUnProject(winX, winY, winZ.get(0), modelView, projection, viewport, location);
return new float[] {location.get(0), location.get(1), location.get(2)};
}
}
When I call this function, passing in the X and Y coordinates of the mouse, I get some numbers that increase by 100 for every pixel I move with my mouse (it should be around 1). After doing some prints from various variables, I found out that the winZ buffer contains the value 1.0. My gluPerspective is set up in such a way that its near clipping point it at 0.1 and its far point at 10000, which would explain why the number is increasing that rapidly. Yet I don't know how to force openGl to use my flat plane instead for finding this distance.
So now I am wondering; if this is the correct/best/easiest method for finding the mouse coordinates on a surface in the 3D world, what could I be doing wrong? If it is not, what is a better way of doing it?
Yes, this is the correct method to use. You are probably just calling it at the point where the matrices do not contain "good values" (might be caused by glPush/PopMatrix() functions in your code).
To confirm you are getting good coordinates, draw a single GL_POINT at the coordinates you get, after you get them (disable depth test and increase point size to, say, 10 pixels). If the point is moving under your mouse, then the calculated coords are correct but the matrices are not.
I really have an issue with mouseclicks in an isometric view.
Simply i have a flat map, and, like i said, a camera in an isometric view. Now when i click in the window i want to get the Coordinates where i clicked on the map. Any Help?
actually, i figured it out.
int winX = ... //the x coordinate of the Click, given Parameter
int winY = ... //the y Coordinate of the Click, given Parameter
FloatBuffer winZ = BufferUtils.createFloatBuffer(1); //the x coordinate of the click, will be calculated
FloatBuffer pos = BufferUtils.createFloatBuffer(3); // the final position of the click
FloatBuffer modelview = BufferUtils.createFloatBuffer(16);
FloatBuffer projection = BufferUtils.createFloatBuffer(16);
IntBuffer viewport = BufferUtils.createIntBuffer(16);
GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelview);
GL11.glGetFloat(GL11.GL_PROJECTIONMATRIX, projection);
GL11.glReadPixels(winX, winY, 1,1, GL11._GL_DEPTH_COMPONENT, GL11.GL_FLOAT, winZ) //calculate the Z Coordinate of the Click
GLU.gluUnProject((float)(winX), (float)(winY), (float)(winZ.get(0)), modelview, projection, viewport, pos); //Calculating the 3D Position of the click, saved in pos
Now since you have the 3D Coordinates you can do some simple vector calculation and collision detection to get the point you clicked on