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);
}