It has been months! I have had this solution for very long, but I absolutely never posted it.
And yes, I have realized, that apparently, it is better to use screenX()
, screenY()
and screenZ()
for hit/intersection/collision tests rather than un-projecting.
This, is still good to have, however.
Please inform me if this breaks. Sometimes, however, you WILL need to adjust the
mouse.sub()
line, in which case, I cannot help! Ask any questions you like, too!
(Also, the cx
and cy
variables are width / 2
and height / 2
, respectively.)
You might need to not use the line with cameraInv
as well, if using perspective()
.
Please note that you need to declare this somewhere in your code, outside any functions or classes:
PGraphicsOpenGL glGraphics;
Then inside setup()
, write this!:
glGraphics = (PGraphicsOpenGL)g;
(I feel that it should be glGraphics = (PGraphicsOpenGL)getGraphics();
instead, but the code given above works, so… I didn’t worry as much…)
…alright, here you go! ":D
:
PVector mouse = new PVector();
void unprojectMouse() {
mouse.set(mouseX, mouseY);
mouse = glGraphics.modelviewInv.mult(mouse, null);
mouse = glGraphics.cameraInv.mult(mouse, null);
// This part of the code changes depending on what projection you are using.
// If it is orthographic (`ortho()`), this may work:
//mouse.sub(width + cx, height + cy);
//mouse.x = -mouse.x;
//mouse.z = 0;
// Use this otherwise:
//mouse.sub(width, height);
}
// I generally like to call this inside a method called "`pre()`".
// To use it, however, we need to do this:
void setup() {
registerMethod("pre", this);
}
// ..the reason being, that this is a method that Processing has made for libraries to use.
// It is called before `draw()`, and helps me write cleaner code.
void pre() {
unprojectMouse();
}
// .. you can choose to not do all these weird things
// and just call `unprojectMouse()` inside `draw()` :)
}
I also used this on Android (Only the version that uses perspective()
was tested, although both should work given proper modifications).
void unprojectTouches() {
for (int i = 0;
i < nTouches; i++) {
projectedTouches[i]
.set(touches[i].x,
touches[i].y);
projectedTouches[i]
= glGraphics.modelviewInv
.mult(projectedTouches[i],
null);
projectedTouches[i]
= glGraphics.cameraInv
.mult(projectedTouches[i],
null);
projectedTouches[i].sub(
width, height);
}
}
(I know it looks messy, but those who choose to paste this into APDE will love it, :D
!
…here’s a cleaner version
void unprojectTouches() {
for (int i = 0; i < nTouches; i++) {
projectedTouches[i].set(touches[i].x, touches[i].y);
projectedTouches[i] = glGraphics
.modelviewInv.mult(projectedTouches[i], null);
projectedTouches[i] = glGraphics
.cameraInv.mult(projectedTouches[i], null);
projectedTouches[i].sub(width, height);
}
}
)
…but, you also need to include this part:
int nTouches;
PVector[] projectedTouches; // Should be called
// `unprojectedTouches` - I know, I know...
PGraphicsOpenGL glGraphics; // This was mentioned at the top of this post.
void setup() {
fullScreen(P3D); // Make sure you're using `P3D`.
registerMethod("pre", this); // As stated earlier, this is optional.
glGraphics = (PGraphicsOpenGL)g; // Necessary.
// ..or,
//glGraphics = (PGraphicsOpenGL)getGraphics();
// Free boilerplate code!!!:
for (int i = 0; i < 10; i++)
projectedTouches[i] = new PVector();
}
// ..and this is optional too!
// Call `unprojectTouches()` in `draw()` all you like, ":D!
void pre() {
unprojectTouches();
}
void touchStarted() {
nTouches++;
}
void touchEnded() {
nTouches--;
}