Here's the code (a small demo to be exact) of the plane-based solution. The drawback is, that you need that plane and have to orientate it correctly. The pro is, that you can do this with any kind of geometry, i.e. you can add ramps and such easier.
The reprojection solution should be doable too, but i got lost in the magic of converting between spaces, got wrong results and ran out of time. I hope that this solution is appropriate for your problem:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.threed.jpct.*;
import com.threed.jpct.util.*;
public class MouseFollow extends JFrame implements MouseMotionListener {
private final static long serialVersionUID=1L;
private Graphics g = null;
private FrameBuffer fb = null;
private World world = null;
private Object3D plane = null;
private Object3D ramp = null;
private Object3D cube = null;
private Object3D cube2 = null;
private Object3D sphere = null;
private boolean doloop = true;
private int x=320;
private int y=240;
public MouseFollow() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setSize(640, 480);
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
this.addMouseMotionListener(this);
g = getGraphics();
}
public void mouseMoved(MouseEvent m) {
x=m.getX();
y=m.getY();
}
public void mouseDragged(MouseEvent m) {}
private void initStuff() {
fb = new FrameBuffer(640, 480, FrameBuffer.SAMPLINGMODE_NORMAL);
world = new World();
fb.enableRenderer(IRenderer.RENDERER_SOFTWARE);
ramp = Primitives.getCube(20);
ramp.setAdditionalColor(Color.red);
plane = Primitives.getPlane(1, 200);
plane.setAdditionalColor(Color.GREEN);
sphere=Primitives.getSphere(30);
sphere.setAdditionalColor(Color.CYAN);
sphere.translate(-50,10,50);
cube2=Primitives.getCube(20);
cube2.setAdditionalColor(Color.ORANGE);
cube2.translate(60,-20,60);
plane.rotateX((float) Math.PI / 2f);
ramp.rotateX((float) Math.PI / 2f);
cube = Primitives.getCube(2);
plane.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
ramp.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
sphere.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
cube2.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
world.addObject(plane);
world.addObject(ramp);
world.addObject(cube);
world.addObject(sphere);
world.addObject(cube2);
cube.translate(-50, -10, -50);
Light light = new Light(world);
light.setPosition(new SimpleVector(0, -80, 0));
light.setIntensity(40, 25, 22);
world.buildAllObjects();
}
private void follow() {
SimpleVector ray=Interact2D.reproject2D3D(world.getCamera(), fb, x, y);
if (ray!=null) {
SimpleVector norm=ray.normalize();
Matrix mat=world.getCamera().getBack();
mat=mat.invert3x3();
norm.matMul(mat);
SimpleVector ws=new SimpleVector(ray);
ws.matMul(world.getCamera().getBack().invert3x3());
ws.add(world.getCamera().getPosition());
float f=world.calcMinDistance(world.getCamera().getPosition(), norm, 1000);
if (f!=Object3D.COLLISION_NONE) {
SimpleVector offset=new SimpleVector(norm);
norm.scalarMul(f);
norm=norm.calcSub(offset);
cube.getTranslationMatrix().setIdentity();
cube.translate(norm);
cube.translate(world.getCamera().getPosition());
}
}
}
private void doIt() throws Exception {
Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 100);
cam.moveCamera(Camera.CAMERA_MOVEUP, 160);
cam.lookAt(plane.getTransformedCenter());
world.getLights().setOverbrightLighting(Lights.OVERBRIGHT_LIGHTING_DISABLED);
while(doloop) {
follow();
fb.clear();
world.renderScene(fb);
world.draw(fb);
fb.update();
fb.display(g);
Thread.sleep(10);
}
fb.disableRenderer(IRenderer.RENDERER_OPENGL);
System.exit(0);
}
public static void main(String[] args) throws Exception {
MouseFollow cd = new MouseFollow();
cd.initStuff();
cd.doIt();
}
}