package openmods.gui.misc;
import com.google.common.base.Preconditions;
import openmods.utils.render.OpenGLUtils;
import org.lwjgl.input.Mouse;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
public class Trackball {
public static class TrackballWrapper {
private final Trackball target = new Trackball();
private final float radius;
private final int mouseButton;
private boolean isDragging;
public TrackballWrapper(int mouseButton, int radiusPx) {
this.mouseButton = mouseButton;
this.radius = radiusPx;
}
public void update(int mouseX, int mouseY) {
float mx = mouseX / radius;
float my = mouseY / radius;
boolean buttonState = Mouse.isButtonDown(mouseButton);
if (!isDragging && buttonState) {
isDragging = true;
target.startDrag(mx, my);
} else if (isDragging && !buttonState) {
isDragging = false;
target.endDrag(mx, my);
}
target.applyTransform(mx, my, isDragging);
}
public void setTransform(Matrix4f transform) {
target.lastTransform = transform;
}
}
private Vector3f dragStart;
private Matrix4f lastTransform;
public Trackball() {
lastTransform = new Matrix4f();
}
private static Vector3f calculateSpherePoint(float x, float y) {
Vector3f result = new Vector3f(x, y, 0);
float sqrZ = 1 - Vector3f.dot(result, result);
if (sqrZ > 0) result.z = (float)Math.sqrt(sqrZ);
else result.normalise();
return result;
}
private Matrix4f getTransform(float mouseX, float mouseY) {
Preconditions.checkNotNull(dragStart, "Draging not started");
Vector3f current = calculateSpherePoint(mouseX, mouseY);
float dot = Vector3f.dot(dragStart, current);
if (Math.abs(dot - 1) < 0.0001) return lastTransform;
Vector3f axis = Vector3f.cross(dragStart, current, null);
try {
axis.normalise();
} catch (IllegalStateException e) { // Zero length vector
return lastTransform;
}
float angle = 2 * (float)(Math.acos(dot));
Matrix4f rotation = new Matrix4f();
rotation.rotate(angle, axis);
return Matrix4f.mul(rotation, lastTransform, null);
}
public void applyTransform(float mouseX, float mouseY, boolean isDragging) {
OpenGLUtils.loadMatrix(isDragging? getTransform(mouseX, mouseY) : lastTransform);
}
public void startDrag(float mouseX, float mouseY) {
dragStart = calculateSpherePoint(mouseX, mouseY);
}
public void endDrag(float mouseX, float mouseY) {
lastTransform = getTransform(mouseX, mouseY);
}
}