射线选择不完美:让我选择错误的立方体

时间:2012-11-26 16:20:16

标签: java opengl

我在JOGL中编写了一个简单的选择演示。有27个立方体,放置在RubikCube中。但问题是采摘很少选择好的立方体。 对于这种情况,光线投射是最好的方法吗?否则,我应该使用哪种方法,如果立方体有不同的旋转(例如,并不总是蓝色面对我们)?

TheGLEventListener.java(我删除了一些已实现的方法,因为它们没有被使用)

package com.gmail.bernabe.laurent.java_opengl.picking_test.views;

public class TheGLEventListener implements GLEventListener,MouseListener,MouseMotionListener {

    @Override
    public void init(GLAutoDrawable drawable) {
        GL2 gl = drawable.getGL().getGL2();
        gl.glEnable(GL2.GL_DEPTH_TEST);
        gl.glClearColor(0.67f, 0.16f, 0.51f, 0.0f);

        cubesLocations = new ArrayList<>();
        for (int i = 0; i < 3; i++){
            for (int j = 0; j < 3; j++){
                for (int k = 0; k < 3; k++){
                    cubesLocations.add(new Vector3f((float) (-3.0 + 3.0 * i), (float) (-3.0 + 3.0 * j), (float) (-3.0 + 3.0 * k)));
                }
            }
        }
    }

    @Override
    public void display(GLAutoDrawable drawable) {
        GL2 gl = drawable.getGL().getGL2();

        gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();

        glu.gluLookAt(
                0.0f, 0.0f, 20.0f,
                0.0f, 0.0f, 0.0f,
                0.0f, 1.0f, 0.0f
        );

        if (pendingEvent != null){
                    // Mouse pressed event
            if (pendingEvent.getID() == MouseEvent.MOUSE_PRESSED) {
                Ray rayFromMousePointer = Ray.fromMouseCoords(pendingEvent.getX(), pendingEvent.getY(),
                        gl);

                Vector3f cubeCenter = null;
                mouseIntersection = null;

                            // for each cube, we look for intersection
                for (int cubeIndex = 0; cubeIndex < cubesLocations.size(); cubeIndex++) {
                    cubeCenter = cubesLocations.get(cubeIndex);

                                    // ray cast intersection computed here
                                    mouseIntersection = rayFromMousePointer.intersectionWithSphere(cubeCenter, 1.0f * 1.414f);

                                    // if there is an intersection
                    if (mouseIntersection != null){
                        selectedCubeIndex = cubeIndex;
                        mouseOffsetInCube = mouseIntersection.clone().sub(cubeCenter);
                        break;
                    }
                }

                pendingEvent = null;
            }
            else if (pendingEvent.getID() == MouseEvent.MOUSE_DRAGGED){
                if (selectedCubeIndex >= 0) {
                    Ray rayFromMousePointer = Ray.fromMouseCoords(
                            pendingEvent.getX(), pendingEvent.getY(), gl);
                    Vector3f newLocation = rayFromMousePointer
                        .getPointWhoseZ_Is(mouseIntersection.z);
                    mouseIntersection = newLocation;
                    cubesLocations.set(selectedCubeIndex, mouseIntersection.clone().sub(mouseOffsetInCube));
                }
            }
            else if (pendingEvent.getID() == MouseEvent.MOUSE_RELEASED){
                selectedCubeIndex = -1;
            }
        }

        drawScene(gl);
    }

    private void drawScene(GL2 gl) {
        for (Vector3f place : cubesLocations) {
            gl.glPushMatrix();
            gl.glTranslatef(place.x, place.y, place.z);
            GLLittleCube.draw(gl);
            gl.glPopMatrix();
        }
    }

    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int width,
            int height) {
        GL2 gl = drawable.getGL().getGL2();
        gl.glViewport(0, 0, width, height);

        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(60.0, width * 1.0f / height, 0.1, 40);
        gl.glMatrixMode(GL2.GL_MODELVIEW);
    }

    private GLU glu = new GLUgl2();

    @Override
    public void mousePressed(MouseEvent e) {
        pendingEvent = e;
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        pendingEvent = e;
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        pendingEvent = e;
    }

    private MouseEvent pendingEvent;
    private ArrayList<Vector3f> cubesLocations;
    private int selectedCubeIndex = -1;
    /**
     * Distance from the center of the selected cube, to its
     * clicked point.
     */
    private Vector3f mouseOffsetInCube;

    /**
     * The original clicked point, in the selected cube : it is this point which is
     * moved when dragging mouse (and not the cube center) !!! Otherwise, I get a strange effect
     * in the beginning.
     */
    private Vector3f mouseIntersection;

}

GLLittleCube.java(我只是放了一张脸的代码)

package com.gmail.bernabe.laurent.java_opengl.picking_test.logic;

import javax.media.opengl.GL2;

public class GLLittleCube {

    /**
     * Draws a little cube bounds within [-1.0 and 1.0] for the three directions x,y and z.
     * @param gl - GL2
     */
    public static void draw(GL2 gl){
        gl.glBegin(GL2.GL_QUADS);

          //FRONT face (BLUE)
          gl.glColor3f(0.0f, 0.13f, 0.66f); 
          gl.glVertex3f(-1.0f, -1.0f, +1.0f);
          gl.glVertex3f(+1.0f, -1.0f, +1.0f);
          gl.glVertex3f(+1.0f, +1.0f, +1.0f);
          gl.glVertex3f(-1.0f, +1.0f, +1.0f);

              // similar code for other faces

          gl.glEnd();
    }

}

Ray.java

package com.gmail.bernabe.laurent.java_opengl.picking_test.logic;

import javax.media.opengl.GL2;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.gl2.GLUgl2;

public class Ray {
    private Vector3f origin, direction;

    public Ray(Vector3f origin, Vector3f direction) {
        this.origin = origin;
        this.direction = direction;
    }

    public Vector3f getOrigin() {
        return origin;
    }

    public Vector3f getDirection() {
        return direction;
    }

    /**
     * Returns the point, belonging to this ray, whose x coordinate is given.
     * @param soughtX - float
     * @return - Vector3f
     */
    public Vector3f getPointWhoseX_Is(float soughtX){
        float period = (float) (soughtX - origin.x) / direction.x; 
        return origin.clone().add(direction.clone().scale(period));
    }

    /**
     * Returns the point, belonging to this ray, whose y coordinate is given.
     * @param soughtY - float
     * @return - Vector3f
     */
    public Vector3f getPointWhoseY_Is(float soughtY){
        float period = (float) (soughtY - origin.y) / direction.y; 
        return origin.clone().add(direction.clone().scale(period));
    }

    /**
     * Returns the point, belonging to this ray, whose z coordinate is given.
     * @param soughtZ - float
     * @return - Vector3f
     */
    public Vector3f getPointWhoseZ_Is(float soughtX){
        float period = (float) (soughtX - origin.z) / direction.z; 
        return origin.clone().add(direction.clone().scale(period));
    }

    /**
     * Returns the intersection point : the closest one from the ray origin.
     * @param sphereCenter - Vector3f
     * @param sphereRadius - float
     * @return Vector3f
     */
    public Vector3f intersectionWithSphere(Vector3f sphereCenter, float sphereRadius){
        // sphere center values
        double scx, scy, scz;

        // sphere radius
        double sr;

        //ray direction values
        double rdx, rdy, rdz;

        //ray origin values
        double rox, roy, roz;

        //quadratic equation values
        double b,c, delta, t0, t1;

        sr = sphereRadius;
        scx = sphereCenter.x;
        scy = sphereCenter.y;
        scz = sphereCenter.z;

        rdx = direction.x;
        rdy = direction.y;
        rdz = direction.z;

        rox = origin.x;
        roy = origin.y;
        roz = origin.z;

        b = 2 * (rdx * (rox - scx) + rdy * (roy - scy) + rdz * (roz - scz));
        c = (rox - scx)*(rox - scx) + (roy - scy)*(roy - scy) + (roz - scz)*(roz - scz) - sr*sr;
        delta = b*b - 4*c;

        if (delta < 0){
            return null;
        }
        else {
            t0 = (-b-Math.sqrt(delta))/2;
            t1 = (+b-Math.sqrt(delta))/2;

            // Must be the smallest POSITIVE root between t0 and t1
            float t;

            if (t0 < 0 && t1 < 0)
                return null;
            else if (t0 > 0 && t1 > 0){
                t = t0 > t1 ? (float) t0 : (float) t1;
            }
            else if (t0 > 0)
                t = (float) t0;
            else 
                t = (float) t1;

            return origin.clone().add(direction.clone().scale(t));
        }
    }

    public static Ray fromMouseCoords(int mouseX, int mouseY, GL2 gl){
        int viewport [] = new int[4];
        float modelViewMatrix [] = new float[16];
        float projectionMatrix [] = new float[16];
        float objectPos[] = new float[3];

        gl.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0);
        gl.glGetFloatv(GL2.GL_MODELVIEW_MATRIX, modelViewMatrix, 0);
        gl.glGetFloatv(GL2.GL_PROJECTION_MATRIX, projectionMatrix, 0);

        float winY = viewport[3] * 1.0f - mouseY;
        float winX = mouseX * 1.0f;     

        glu.gluUnProject(winX, winY, 0.0f, modelViewMatrix, 0, projectionMatrix, 0, viewport, 0, objectPos, 0);
        Vector3f tempOrigin = new Vector3f(objectPos[0], objectPos[1], objectPos[2]);

        glu.gluUnProject(winX, winY, 1.0f, modelViewMatrix, 0, projectionMatrix, 0, viewport, 0, objectPos, 0);
        Vector3f tempDest = new Vector3f(objectPos[0], objectPos[1], objectPos[2]);

        Vector3f tempDirection = tempDest.clone().sub(tempOrigin).normalize();

        return new Ray(tempOrigin, tempDirection);
    }

    public String toString(){
        return String.format("Ray{\norigin = %s,\ndirection = %s\n}", origin, direction);
    }

    private static GLU glu = new GLUgl2();
}

0 个答案:

没有答案
相关问题