Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save companje/26dd86c60e86d9044f8a93918a863ecf to your computer and use it in GitHub Desktop.
Save companje/26dd86c60e86d9044f8a93918a863ecf to your computer and use it in GitHub Desktop.
Rotation lat/lon Sphere and unwarp to texture, cubemap, lens, projections etc.
import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
PGraphics dome, ortho3D, lens, cubemap[] = new PGraphics[6];
PShape globe;
PShader shader;
PImage tex;
float h=512, d=h, hd2=h/2, r=hd2;
int cubemapSize=512;
Rotation qTo = new Rotation(new Vector3D(0, 0, 1), 0);
Rotation qNow = new Rotation(new Vector3D(0, 0, 1), 0);
double zRotation;
float progress = .5;
float zoomScaler = .5;
boolean rotationEnabled = true;
boolean locationsVisible = true;
boolean isCamInSphere = false;
GeoPoint nl = new GeoPoint("nl", 52.37, 4.91, color(255, 0, 0));
GeoPoint ny = new GeoPoint("ny", 40.79, -73.96, color(255, 0, 0));
GeoPoint jp = new GeoPoint("jp", 36.14, 137.86, color(0, 255, 0));
GeoPoint north = new GeoPoint("north", 90, 0, color(0, 0, 255));
GeoPoint south = new GeoPoint("south", -90, 0, color(255, 0, 255));
GeoPoint west = new GeoPoint("west", 0, -90, color(0, 255, 255));
GeoPoint east = new GeoPoint("east", 0, 90, color(255, 255, 0));
GeoPoint india = new GeoPoint("india", 22, 77, color(0));
GeoPoint front = new GeoPoint("front", 0, 0, color(255));
GeoPoint places[] = {front, nl, ny, india, jp, north, south};
GeoPoint selectedLocation = null;
void setup() {
size(1536, 512, P3D);
if (h!=height) throw new Error("test");
//tex = createImage(4096, 2048, RGB);
tex = createImage(1024, 512, RGB);
dome = createGraphics((int)h, (int)h, P3D);
ortho3D = createGraphics((int)h, (int)h, P3D);
lens = createGraphics((int)h/2, (int)h/2, P3D);
globe = createShape(SPHERE, hd2);
globe.rotateY(HALF_PI);
globe.setStroke(false);
globe.setTexture(loadImage("earth.jpg")); //https://gitc.earthdata.nasa.gov/wms/epsg4326/best/?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&BBOX=-90,-180,90,180&CRS=EPSG:4326&WIDTH=2048&HEIGHT=1024&LAYERS=Blue%20Marble&STYLES=&FORMAT=image/jpeg&DPI=144&MAP_RESOLUTION=144&FORMAT_OPTIONS=dpi:144&TRANSPARENT=TRUE&.jpg
shader = loadShader("waterworld.glsl");
shader.set("heightmap", loadImage("earth4k_elevation_red_alpha.png"));
shader.set("palette", loadImage("palette-org.png"));
shader.set("borders", loadImage("borders2k.png"));
}
void draw() {
background(0);
shader.set("progress", progress);
qNow = lerp(qNow, qTo, .1);
renderTexture(cubemap, tex);
image(tex, h, 0, 2*h, h);
renderOrtho(ortho3D);
image(ortho3D, 0, 0);
//renderDome(dome);
//image(dome, 0, 0);
drawLens();
drawTarget();
}
void drawLens() {
renderLens(lens);
pushMatrix();
translate(h+50, 50);
rect(0, 0, lens.width, lens.height);
image(lens, 0, 0);
popMatrix();
}
void drawTarget() {
noFill();
strokeWeight(2);
stroke(0);
pushMatrix();
translate(h/2, h/2);
ellipse(0, 0, 20, 20);
for (int i=0; i<4; i++) {
line(5, 0, 15, 0);
rotate(HALF_PI);
}
popMatrix();
}
void render(PGraphics pg) {
if (rotationEnabled) applyRotation(pg, qNow);
pg.background(0);
pg.shader(shader);
pg.shape(globe);
pg.resetShader();
pg.fill(255, 255, 0);
pg.textAlign(CENTER);
pg.textSize(30);
if (locationsVisible) renderLocations(pg);
}
void renderLocations(PGraphics pg) {
float o = isCamInSphere ? -1 : 1; //direction for z-movement towards camera
for (GeoPoint l : places) {
pg.pushMatrix();
pg.noStroke();
pg.rotateY(radians(l.lon));
pg.rotateX(radians(l.lat));
pg.translate(0, 0, r + o*5);
for (int y=-1; y<=1; y++) {
for (int x=-1; x<=1; x++) {
pg.textAlign(CENTER, CENTER);
pg.textSize(24);
pg.fill(255);
pg.text(l.name, x, y-25);
pg.translate(0, 0, o*.01);
}
}
pg.translate(0, 0, o*.01);
pg.fill(l.clr);
pg.text(l.name, 0, -25);
pg.stroke(255);
pg.strokeWeight(3);
pg.ellipse(0, 0, 15, 15);
pg.popMatrix();
}
}
void mouseDragged() {
if (mouseX<h && mouseY<h) {
Vector3D from = getMouseOnSphere(pmouseX, pmouseY, h, h);
Vector3D to = getMouseOnSphere(mouseX, mouseY, h, h);
drag(from, to);
}
}
void selectLocation(GeoPoint p) {
selectedLocation = p;
qTo = getRotationToPoint(p);
println(getZRotationAngle(qTo));
}
void mouseWheel(MouseEvent event) {
if (keyPressed && key=='t') {
progress += event.getCount()*.00001f;
progress = constrain(progress, 0, 1);
}
if (keyPressed && key=='z') {
zoomScaler -= event.getCount()*.001f;
zoomScaler = constrain(zoomScaler, 0, 1);
}
}
void keyPressed() {
if (key=='s') {
renderTexture(cubemap, tex);
tex.save("tex.png");
println("saved");
}
if (key>='0' && key<='6') {
selectLocation(places[key-'0']);
}
if (key=='[') {
Rotation z_rot = new Rotation(front, .1, RotationConvention.VECTOR_OPERATOR);
qTo = qTo.applyTo(z_rot);
}
if (key==']') {
Rotation z_rot = new Rotation(front, -.1, RotationConvention.VECTOR_OPERATOR);
qTo = qTo.applyTo(z_rot);
}
}
static class GeoPoint extends Vector3D {
float lat, lon;
String name;
color clr;
GeoPoint(String name, float lat, float lon, color clr) {
super(
cos(radians(lat)) * sin(-radians(lon)),
sin(radians(lat)),
cos(radians(lat)) * cos(-radians(lon)));
this.lat = lat;
this.lon = lon%180; //180 becomes 0
this.name = name;
this.clr = clr;
}
static GeoPoint fromRotation(Rotation qNow, Vector3D front) {
Vector3D inverse = qNow.applyInverseTo(front);
float lat = degrees(asin((float)inverse.getY()));
float lon = degrees(-atan2((float)inverse.getX(), (float)inverse.getZ())); //X&Z swapped
return new GeoPoint("", lat, lon, #ffffff);
}
String toString() {
String s = nf1(getX())+","+nf1(getY())+","+nf1(getZ());
return "latlon="+nf1(lat)+","+nf1(lon) + " " + "xyz=("+s+")";
}
}
void renderLens(PGraphics pg) {
locationsVisible = false;
rotationEnabled = true;
pg.beginDraw();
pg.perspective();
float eyeZ = map(zoomScaler, 0, 1, 310, 400); //280 is echte minimum
pg.camera(0, 0, eyeZ, 0, 0, 0, 0, 1, 0); //340
render(pg);
pg.endDraw();
}
void renderOrtho(PGraphics pg) {
locationsVisible = true;
rotationEnabled = true;
pg.beginDraw();
pg.ortho(-r,r,-r,r,-10,r);
pg.camera(0, 0, r, 0, 0, 0, 0, 1, 0);
render(pg);
pg.endDraw();
}
void renderDome(PGraphics pg) {
locationsVisible = true;
rotationEnabled = true;
isCamInSphere = true;
float distToCam = 800; //=extreme //regular: 1900;
pg.beginDraw();
pg.perspective(atan(hd2/distToCam)*2, 1, distToCam, 10000); //fovy, aspect, zNear, zFar
pg.camera(0, 0, -distToCam, 0, 0, 0, 0, 1, 0);
pg.scale(-1, 1, 1);
render(pg);
pg.endDraw();
isCamInSphere = false;
}
void renderTexture(PGraphics pg[], PImage tex) { //via cubemap
locationsVisible = true;
rotationEnabled = false;
isCamInSphere = true; // deze zou aangepast kunnen worden dat de camera buiten de bol zit. of zit ie dat? ivm zichtbaarheid tekst
int c[][] = {
{0, 0, 1, 0, -1, 0},
{0, 0, -1, 0, 1, 0},
{0, -1, 0, 1, 0, 0},
{0, 1, 0, -1, 0, 0},
{1, 0, 0, 0, -1, 0},
{-1, 0, 0, 0, 1, 0}};
for (int i = 0; i < 6; i++) {
if (pg[i]==null) pg[i] = createGraphics(cubemapSize, cubemapSize, P3D);
pg[i].beginDraw();
pg[i].camera(0, 0, 0, c[i][0], c[i][1], c[i][2], c[i][3], c[i][4], c[i][5]);
pg[i].perspective(HALF_PI, 1.0, 1, 1000);
render(pg[i]);
pg[i].endDraw();
pg[i].loadPixels();
}
cubemapToEquirectangular(pg, tex);
isCamInSphere = false;
}
PImage cubemapToEquirectangular(PGraphics pg[], PImage tex) {
tex.loadPixels();
for (int y=0, w=tex.width, h=tex.height; y < h; y++) {
for (int x = 0; x < w; x++) {
float u = map(x, 0, w, -PI, PI);
float v = map(y, 0, h, HALF_PI, -HALF_PI);
PVector dir = new PVector(cos(v) * cos(u), sin(v), cos(v) * sin(u));
tex.pixels[y * w + x] = sampleFromCubemap(pg, dir);
}
}
tex.updatePixels();
return tex;
}
color sampleFromCubemap(PGraphics pg[], PVector dir) {
float ax = abs(dir.x), ay = abs(dir.y), az = abs(dir.z);
boolean xGreatest = ax >= ay && ax >= az, yGreatest = ay >= az;
int face = xGreatest ? (dir.x > 0 ? 0 : 1) :
yGreatest ? (dir.y > 0 ? 2 : 3) :
dir.z > 0 ? 4 : 5;
float uc = xGreatest ? dir.z / ax :
yGreatest ? dir.x / ay :
-dir.x / az;
float vc = (face % 2 == 0 ? 1 : -1) *
(xGreatest ? dir.y / ax :
yGreatest ? dir.z / ay :
dir.y / az);
int px = int(map(uc, -1, 1, 0, pg[face].width - 1));
int py = int(map(vc, -1, 1, 0, pg[face].height - 1));
return pg[face].pixels[py*pg[face].width+px];
}
double getZRotationAngle(Rotation rotation) {
double[][] m = rotation.getMatrix(); // Haal de rotatiematrix op
return Math.atan2(m[1][0], m[0][0]); // Bereken de yaw (rotatie rond de Z-as) atan2(sinYaw, cosYaw)
}
Rotation getRotationToPoint(Vector3D point) {
//dit lijkt aardig te werken!!! Gek genoeg bij india niet helemaal
Rotation r = new Rotation(point, front);
double a = -getZRotationAngle(r); //yaw
Rotation z_rot = new Rotation(front, a, RotationConvention.VECTOR_OPERATOR);
r = r.applyTo(z_rot);
return r;
}
void applyRotation(PGraphics pg, Rotation rotation) {
Vector3D axis = rotation.getAxis();
float angle = (float)rotation.getAngle();
pg.rotate(-angle, (float)axis.getX(), (float)axis.getY(), (float)axis.getZ());
}
Rotation lerp(Rotation start, Rotation end, float t) {
return new Rotation(start.getQ0() + t * (end.getQ0() - start.getQ0()),
start.getQ1() + t * (end.getQ1() - start.getQ1()),
start.getQ2() + t * (end.getQ2() - start.getQ2()),
start.getQ3() + t * (end.getQ3() - start.getQ3()),
true);
}
Vector3D getMouseOnSphere(float x, float y, float w, float h) {
x = map(x, 0, w, -1, 1);
y = map(y, 0, h, -1, 1);
float r = x*x+y*y;
return new Vector3D(x, y, r>1 ? 0 : sqrt(1-r)).normalize();
}
void drag(Vector3D from, Vector3D to) {
Vector3D axis = Vector3D.crossProduct(from, to);
if (axis.getNorm() > 0) {
axis = axis.normalize();
double angle = -Math.acos(Vector3D.dotProduct(from, to));
qTo = qTo.compose(new Rotation(axis, angle), RotationConvention.VECTOR_OPERATOR);
}
}
static String nf1(double f) {
if (f==int((float)f)) return int((float)f)+"";
else return nf((float)f, 0, 1).replace(",", ".");
}
@companje
Copy link
Author

Screenshot 2025-02-19 at 18 11 56

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment