Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better Mouse Position Unprojection #16

Merged
merged 12 commits into from
May 6, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ public void render(int x, int y, int width, int height, int mouseX, int mouseY)
&& mouseX < positionedRect.position.x + positionedRect.size.width
&& mouseY > positionedRect.position.y
&& mouseY < positionedRect.position.y + positionedRect.size.height) {
Vector3f hitPos = ProjectionUtils.unProject(mouseX, mouseY);
MovingObjectPosition result = rayTrace(hitPos);
Vector3f lookVec = ProjectionUtils.unProject(positionedRect, eyePos, lookAt, mouseX, mouseY);
MovingObjectPosition result = rayTrace(lookVec);
if (result != null) {
this.lastTraceResult = result;
onLookingAt.accept(result);
Expand Down Expand Up @@ -357,55 +357,13 @@ public static void setDefaultPassRenderState(int pass) {
}
}

public MovingObjectPosition rayTrace(Vector3f hitPos) {
public MovingObjectPosition rayTrace(Vector3f lookVec) {
Vec3 startPos = Vec3.createVectorHelper(this.eyePos.x, this.eyePos.y, this.eyePos.z);
hitPos.scale(2); // Double view range to ensure pos can be seen.
lookVec.scale(100); // range: 100 Blocks
Vec3 endPos = Vec3.createVectorHelper(
(hitPos.x - startPos.xCoord),
(hitPos.y - startPos.yCoord),
(hitPos.z - startPos.zCoord));
(lookVec.x + startPos.xCoord),
(lookVec.y + startPos.yCoord),
(lookVec.z + startPos.zCoord));
return ((TrackedDummyWorld) this.world).rayTraceBlockswithTargetMap(startPos, endPos, renderedBlocks);
}

/***
* For better performance, You'd better handle the event setOnLookingAt(Consumer) or getLastTraceResult()
*
* @param mouseX xPos in Texture
* @param mouseY yPos in Texture
* @return RayTraceResult Hit
*/
protected MovingObjectPosition screenPos2BlockPosFace(int mouseX, int mouseY, int x, int y, int width, int height) {
// render a frame
glEnable(GL_DEPTH_TEST);
setupCamera(getPositionedRect(x, y, width, height));

drawWorld();

Vector3f hitPos = ProjectionUtils.unProject(mouseX, mouseY);
MovingObjectPosition result = rayTrace(hitPos);

resetCamera();

return result;
}

/***
* For better performance, You'd better do project in setOnWorldRender(Consumer)
*
* @param pos BlockPos
* @param depth should pass Depth Test
* @return x, y, z
*/
protected Vector3f blockPos2ScreenPos(BlockPosition pos, boolean depth, int x, int y, int width, int height) {
// render a frame
glEnable(GL_DEPTH_TEST);
setupCamera(getPositionedRect(x, y, width, height));

drawWorld();
Vector3f winPos = ProjectionUtils.project(pos);

resetCamera();

return winPos;
}
}
152 changes: 48 additions & 104 deletions src/main/java/blockrenderer6343/client/utils/ProjectionUtils.java
Original file line number Diff line number Diff line change
@@ -1,117 +1,61 @@
package blockrenderer6343.client.utils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;

import blockrenderer6343.api.utils.BlockPosition;
import blockrenderer6343.api.utils.PositionedRect;

public class ProjectionUtils {

private static final FloatBuffer MODELVIEW_MATRIX_BUFFER = ByteBuffer.allocateDirect(16 * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
private static final FloatBuffer PROJECTION_MATRIX_BUFFER = ByteBuffer.allocateDirect(16 * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
private static final IntBuffer VIEWPORT_BUFFER = ByteBuffer.allocateDirect(16 * 4).order(ByteOrder.nativeOrder())
.asIntBuffer();
protected static final FloatBuffer PIXEL_DEPTH_BUFFER = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder())
.asFloatBuffer();
protected static final FloatBuffer OBJECT_POS_BUFFER = ByteBuffer.allocateDirect(3 * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();

public static Vector3f project(BlockPosition pos) {
// read current rendering parameters
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MODELVIEW_MATRIX_BUFFER);
GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, PROJECTION_MATRIX_BUFFER);
GL11.glGetInteger(GL11.GL_VIEWPORT, VIEWPORT_BUFFER);

// rewind buffers after write by OpenGL glGet calls
MODELVIEW_MATRIX_BUFFER.rewind();
PROJECTION_MATRIX_BUFFER.rewind();
VIEWPORT_BUFFER.rewind();

// call gluProject with retrieved parameters
GLU.gluProject(
pos.x + 0.5f,
pos.y + 0.5f,
pos.z + 0.5f,
MODELVIEW_MATRIX_BUFFER,
PROJECTION_MATRIX_BUFFER,
VIEWPORT_BUFFER,
OBJECT_POS_BUFFER);

// rewind buffers after read by gluProject
VIEWPORT_BUFFER.rewind();
PROJECTION_MATRIX_BUFFER.rewind();
MODELVIEW_MATRIX_BUFFER.rewind();

// rewind buffer after write by gluProject
OBJECT_POS_BUFFER.rewind();

// obtain position in Screen
float winX = OBJECT_POS_BUFFER.get();
float winY = OBJECT_POS_BUFFER.get();
float winZ = OBJECT_POS_BUFFER.get();

// rewind buffer after read
OBJECT_POS_BUFFER.rewind();

return new Vector3f(winX, winY, winZ);
public static Vector3f unProject(PositionedRect rect, Vector3f eyePos, Vector3f lookat, int mouseX, int mouseY) {
int width = rect.size.width;
int height = rect.size.height;

double aspectRatio = ((double) width / (double) height);
double fov = ((60 / 2d)) * (Math.PI / 180);

double a = -((double) (mouseX - rect.position.x) / (double) width - 0.5) * 2;
double b = -((double) (height - (mouseY - rect.position.y)) / (double) height - 0.5) * 2;
double tanf = Math.tan(fov);

Vector3f lookVec = new Vector3f();
Vector3f.sub(eyePos, lookat, lookVec);
float yawn = (float) Math.atan2(lookVec.x, -lookVec.z);
float pitch = (float) Math.atan2(lookVec.y, Math.sqrt(lookVec.x * lookVec.x + lookVec.z * lookVec.z));

Matrix4f rot = new Matrix4f();
rot.rotate(yawn, new Vector3f(0, -1, 0));
rot.rotate(pitch, new Vector3f(1, 0, 0));
Vector4f foward = new Vector4f(0, 0, 1, 0);
Vector4f up = new Vector4f(0, 1, 0, 0);
Vector4f left = new Vector4f(1, 0, 0, 0);
Matrix4f.transform(rot, foward, foward);
Matrix4f.transform(rot, up, up);
Matrix4f.transform(rot, left, left);

Vector3f result = new Vector3f(foward.x, foward.y, foward.z);
Vector3f.add(
result,
new Vector3f(
(float) (left.x * tanf * aspectRatio * a),
(float) (left.y * tanf * aspectRatio * a),
(float) (left.z * tanf * aspectRatio * a)),
result);
Vector3f.add(
result,
new Vector3f((float) (up.x * tanf * b), (float) (up.y * tanf * b), (float) (up.z * tanf * b)),
result);
return normalize(result);
}

public static Vector3f unProject(int mouseX, int mouseY) {
// read depth of pixel under mouse
GL11.glReadPixels(mouseX, mouseY, 1, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, PIXEL_DEPTH_BUFFER);

// rewind buffer after write by glReadPixels
PIXEL_DEPTH_BUFFER.rewind();

// retrieve depth from buffer (0.0-1.0f)
float pixelDepth = PIXEL_DEPTH_BUFFER.get();

// rewind buffer after read
PIXEL_DEPTH_BUFFER.rewind();

// read current rendering parameters
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MODELVIEW_MATRIX_BUFFER);
GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, PROJECTION_MATRIX_BUFFER);
GL11.glGetInteger(GL11.GL_VIEWPORT, VIEWPORT_BUFFER);

// rewind buffers after write by OpenGL glGet calls
MODELVIEW_MATRIX_BUFFER.rewind();
PROJECTION_MATRIX_BUFFER.rewind();
VIEWPORT_BUFFER.rewind();

// call gluUnProject with retrieved parameters
GLU.gluUnProject(
mouseX,
mouseY,
pixelDepth,
MODELVIEW_MATRIX_BUFFER,
PROJECTION_MATRIX_BUFFER,
VIEWPORT_BUFFER,
OBJECT_POS_BUFFER);

// rewind buffers after read by gluUnProject
VIEWPORT_BUFFER.rewind();
PROJECTION_MATRIX_BUFFER.rewind();
MODELVIEW_MATRIX_BUFFER.rewind();

// rewind buffer after write by gluUnProject
OBJECT_POS_BUFFER.rewind();
public static Vector3f normalize(Vector3f vec) {
float length = (float) Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);

// obtain absolute position in world
float posX = OBJECT_POS_BUFFER.get();
float posY = OBJECT_POS_BUFFER.get();
float posZ = OBJECT_POS_BUFFER.get();
vec.x /= length;
vec.y /= length;
vec.z /= length;

// rewind buffer after read
OBJECT_POS_BUFFER.rewind();
return new Vector3f(posX, posY, posZ);
return vec;
}
}