Hi @jimyoung,
My tinkering for a left-handed y-up camera is below. Because the rest of the Processing library assumes y-down, though, the impact of a y-up camera ripples out to the appearance of text, images, lights. Sounds like you’ve already looked at the source code, but for comparison the original camera
is here and frustum
, which is called by perspective
, is here.
Best,
Jeremy
PGraphics3D rndr;
void settings() {
size(512, 512, P3D);
}
void setup() {
rndr = (PGraphics3D)getGraphics();
}
void draw() {
float theta = TAU * mouseX / (float)width - PI;
directionalLight(
255.0, 245.0, 215.0,
0.0, -0.6, 0.8);
perspective(rndr);
camera(rndr,
sin(theta) * height, 0.0, cos(theta) * height,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0);
background(#ffffff);
stroke(#ff0000);
line(0.0, 0.0, 0.0, 100.0, 0.0, 0.0);
stroke(#00ff00);
line(0.0, 0.0, 0.0, 0.0, 100.0, 0.0);
stroke(#0000ff);
line(0.0, 0.0, 0.0, 0.0, 0.0, 100.0);
noStroke();
fill(#fff7d5);
rotateY(frameCount * 0.01);
box(100.0);
fill(#000000);
text("Upside down", 150.0, 0.0);
}
void perspective(PGraphics3D rndr) {
perspective(rndr, THIRD_PI, rndr.width / (float)rndr.height,
0.01, 1000.0);
}
void perspective(
PGraphics3D rndr,
float fov,
float aspect,
float near,
float far ) {
rndr.cameraFOV = fov;
rndr.cameraAspect = aspect;
rndr.cameraNear = near;
rndr.cameraFar = far;
float cotfov = 1.0 / tan(fov * 0.5f);
float d = 1.0 / (far - near);
rndr.projection.set(
cotfov / aspect, 0.0, 0.0, 0.0,
0.0, cotfov, 0.0, 0.0,
0.0, 0.0, (far + near) * -d, (near + near) * far * -d,
0.0, 0.0, -1.0, 0.0);
}
void camera(
PGraphics3D rndr,
float xEye, float yEye, float zEye,
float xCenter, float yCenter, float zCenter) {
camera(
rndr,
xEye, yEye, zEye,
xCenter, yCenter, zCenter,
0.0, 1.0, 0.0);
}
void camera(
PGraphics3D rndr,
float xEye, float yEye, float zEye,
float xCenter, float yCenter, float zCenter,
float xUp, float yUp, float zUp) {
// Subtract center from eye to get forward.
// Camera orbits target.
float kx = xEye - xCenter;
float ky = yEye - yCenter;
float kz = zEye - zCenter;
// Normalize forward. mag(v) = sqrt(dot(v, v)).
float kmagsq = kx * kx + ky * ky + kz * kz;
float kmag = sqrt(kmagsq);
kx /= kmag;
ky /= kmag;
kz /= kmag;
// Avoid case where forward parallel to reference up.
float dotp = kx * xUp + ky * yUp + kz * zUp;
if (dotp < -0.9999 || dotp > 0.9999) {
return;
}
// Cross forward with reference up.
float ix = ky * zUp - kz * yUp;
float iy = kz * xUp - kx * zUp;
float iz = kx * yUp - ky * xUp;
// Normalize right.
float imagsq = ix * ix + iy * iy + iz * iz;
float imag = sqrt(imagsq);
ix /= imag;
iy /= imag;
iz /= imag;
// Cross right with forward.
float jx = iy * kz - iz * ky;
float jy = iz * kx - ix * kz;
float jz = ix * ky - iy * kx;
// Normalize up.
float jmagsq = jx * jx + jy * jy + jz * jz;
float jmag = sqrt(jmagsq);
jx /= jmag;
jy /= jmag;
jz /= jmag;
// Set loose renderer camera variables.
rndr.cameraX = xEye;
rndr.cameraY = yEye;
rndr.cameraZ = zEye;
//rndr.eyeDist = kmag; // eyeDist not visible.
// Set renderer camera inverse.
rndr.cameraInv.set(
ix, jx, kx, xEye,
iy, jy, ky, yEye,
iz, jz, kz, zEye,
0.0, 0.0, 0.0, 1.0);
// Set the camera.
// rndr.camera.set(rndr.cameraInv);
// rndr.camera.invert();
rndr.camera.set(
ix, iy, iz, -xEye * ix - yEye * iy - zEye * iz,
jx, jy, jz, -xEye * jx - yEye * jy - zEye * jz,
kx, ky, kz, -xEye * kx - yEye * ky - zEye * kz,
0.0, 0.0, 0.0, 1.0);
// Update project model view.
rndr.modelview.set(rndr.camera);
rndr.modelviewInv.set(rndr.cameraInv);
rndr.projmodelview.set(rndr.projection);
rndr.projmodelview.apply(rndr.modelview);
}