Instructions on building a View collider

Anybody know how far the collision detection goes?

I have no problem writting similar test to DavidYazel’s but when it comes to adding collision to a view or an object navigating using awt events to code compiles but does nothing. ???

Please someone tell me I’m being stupid :slight_smile:

Ok step by step instructions.

The basic technique is to create a dummy node which represents your avatar, then build a collider for it. Then create a Force which is used to move the avatar around. Then once the dummy collision node has been updated, you sync the view with the location of the node.

So step one, define a Force for moving an avatar around:


  /**
     * Movement force used for the renderer walkthrough
     */
    public static class MovementForce implements Force {

        public static final int TURN_RIGHT = 1;
        public static final int TURN_LEFT = 2;
        public static final int TURN_STOP = 0;

        public static final int FALL_NONE = 0;
        public static final int FALL_UP = 1;
        public static final int JUMP_UP = 3;
        public static final int FALL_DOWN = 2;

        AngleInterpolater turnAngle;
        AngleInterpolater lookudAngle;
        float turnSpeed;
        float lookudSpeed;
        float speed;
        long totalTime = 0;
        Coord3f movement = new Coord3f();
        Vector3f eulers = new Vector3f();
        Vector3f firstPersonEulers = new Vector3f();
        Transform3D firstPersonTransform = new Transform3D();
        Vector3f curpos = new Vector3f();
        int turnDirection = TURN_STOP;
        boolean applyGravity = false;
        int fallDirection = FALL_NONE;

        public MovementForce() {
            // set other speeds
            turnSpeed = 180f; // 180 degrees per second
            turnAngle = new AngleInterpolater((float) Math.toRadians(206.65),
                    (float) Math.toRadians(turnSpeed), 0,
                    (float) Math.toRadians(360), true);
            lookudSpeed = 40f;

            // set up the look up and down interpolater
            lookudAngle = new AngleInterpolater((float) Math.toRadians(0),
                    (float) Math.toRadians(45), (float) Math.toRadians(-30),
                    (float) Math.toRadians(40), false);

        }

        public synchronized void setSpeed(float speed) {
            this.speed = speed;
        }

        public void turnLeft() {
            turnDirection = TURN_LEFT;
        }

        public void turnRight() {
            turnDirection = TURN_RIGHT;
        }

        public void turnStop() {
            turnDirection = TURN_STOP;
        }

        public void fallDown() {
            fallDirection = FALL_DOWN;
        }

        public void fallUp() {
            fallDirection = FALL_UP;
        }

        public void jump() {
            fallDirection = JUMP_UP;
        }

        public void fallNone() {
            fallDirection = FALL_NONE;
        }

        public void toggleGravity() {
            applyGravity = !applyGravity;
        }

        public synchronized void updateView(ColliderNode node, View view, long time) {

            totalTime += time;

            // adjust the interpolaters

            switch (turnDirection) {
            case TURN_STOP:
                turnAngle.stop();
                break;
            case TURN_RIGHT:
                turnAngle.startDecreasing(totalTime);
                break;
            case TURN_LEFT:
                turnAngle.startIncreasing(totalTime);
                break;
            }

            firstPersonEulers.y = turnAngle.getValue(totalTime);
            firstPersonEulers.x = lookudAngle.getValue(totalTime);
            firstPersonEulers.z = 0;
            firstPersonTransform.setEuler(firstPersonEulers);
            Transform3D viewTransform = view.getTransform();
            viewTransform.setEuler(firstPersonEulers);
            node.getNode().getTransformGroup().getTransform().getTranslation(curpos);
            curpos.y += 1.6f;
            viewTransform.setTranslation(curpos);
            view.setTransform(viewTransform);

        }

        public synchronized boolean apply(ColliderNode node, long time, Coord3f velocity) {

            firstPersonEulers.y = turnAngle.getValue(totalTime);
            firstPersonEulers.x = 0;
            firstPersonEulers.z = 0;
            firstPersonTransform.setEuler(firstPersonEulers);

            float seconds = (float) time / 1000f;
            velocity.x = 0;
            velocity.z = -speed;
            firstPersonTransform.transform(velocity);

            if (!applyGravity) {
                switch (fallDirection) {
                case FALL_NONE: velocity.y = 0; break;
                case FALL_UP: velocity.y = 0.5f; break;
                case JUMP_UP:
                    applyGravity=true;
                    break;
                case FALL_DOWN: velocity.y = -0.5f; break;
                }
            } else {
                if (fallDirection==JUMP_UP) {
                    velocity.y = 12;
                    fallDirection=FALL_NONE;
                }

                if (velocity.y >= -5) velocity.y -= 9.8 * seconds;
                else velocity.y = -5;
            }

            return false;
        }

    }

Next step is to define a dummy node for your avatar and attach to the collision system and your scene:


// build a dummy node to represent the avatar for collision and movement
// purposes.  we do this by creating a branch group with set bounds around an
// avatar of 1.83 meters high.  Then we make a collider node of an ellipsoid which is
// half as wide as it is tall.

        MovementForce force = new MovementForce();
        TransformGroup aTG = new TransformGroup();
        Node n = new BranchGroup();
        n.setBoundsAutoCompute(false);
        n.setBounds(new BoundingSphere(new Point3f(0, 0.915f, 0), 0.915f));
        aTG.addChild(n);
        converter.getRoot().addChild(aTG);
        avatarNode = new ColliderNode(n, ColliderNode.CT_ELLIPSOID, ColliderNode.CT_ELLIPSOID, true, new Coord3f(0.5f, 1, 0.5f));
        cs.addCollider(avatarNode);

// now make a force for the collider which controlls movement

        avatarNode.addForce(force);

Then you need to capture your AWT events and use them to modify your force:


  class KeyboardEventListener implements AWTEventListener {
            public void eventDispatched(AWTEvent event) {
                if (event instanceof KeyEvent) {
                    KeyEvent e = (KeyEvent) event;

                    if ((e.getID() == KeyEvent.KEY_TYPED) && (e.getKeyChar() == '-')) {
                        takeScreenshot=true;
                    }
                    if ((e.getID() == KeyEvent.KEY_TYPED) && (e.getKeyChar() == 27)) {
                        System.exit(0);
                        e.consume();
                    }

                    // turning right

                    else if ((e.getID() == KeyEvent.KEY_PRESSED) && (e.getKeyCode() == KeyEvent.VK_RIGHT)) {
                        force.turnRight();
                        e.consume();
                    } else if ((e.getID() == KeyEvent.KEY_RELEASED) && (e.getKeyCode() == KeyEvent.VK_RIGHT)) {
                        force.turnStop();
                        e.consume();
                    }

                    // turning left

                    else if ((e.getID() == KeyEvent.KEY_PRESSED) && (e.getKeyCode() == KeyEvent.VK_LEFT)) {
                        force.turnLeft();
                        e.consume();
                    } else if ((e.getID() == KeyEvent.KEY_RELEASED) && (e.getKeyCode() == KeyEvent.VK_LEFT)) {
                        force.turnStop();
                        e.consume();
                    }

                    // turning forward

                    else if ((e.getID() == KeyEvent.KEY_PRESSED) && (e.getKeyCode() == KeyEvent.VK_UP)) {
                        force.setSpeed(4f);
                        e.consume();
                    } else if ((e.getID() == KeyEvent.KEY_RELEASED) && (e.getKeyCode() == KeyEvent.VK_UP)) {
                        force.setSpeed(0);
                        e.consume();
                    }

                    else if ((e.getID() == KeyEvent.KEY_PRESSED) && (e.getKeyCode() == KeyEvent.VK_DOWN)) {
                        force.setSpeed(-4f);
                        e.consume();
                    } else if ((e.getID() == KeyEvent.KEY_RELEASED) && (e.getKeyCode() == KeyEvent.VK_DOWN)) {
                        force.setSpeed(0);
                        e.consume();
                    }

                    // go down

                    else if ((e.getID() == KeyEvent.KEY_PRESSED) && (e.getKeyCode() == KeyEvent.VK_DELETE)) {
                        force.fallDown();
                        e.consume();
                    } else if ((e.getID() == KeyEvent.KEY_RELEASED) && (e.getKeyCode() == KeyEvent.VK_DELETE)) {
                        force.fallNone();
                        e.consume();
                    }

                    // go up

                    else if ((e.getID() == KeyEvent.KEY_PRESSED) && (e.getKeyCode() == KeyEvent.VK_INSERT)) {
                        force.fallUp();
                        e.consume();
                    } else if ((e.getID() == KeyEvent.KEY_RELEASED) && (e.getKeyCode() == KeyEvent.VK_INSERT)) {
                        force.fallNone();
                        e.consume();
                    }

                    // gravity

                    else if ((e.getID() == KeyEvent.KEY_TYPED) && (e.getKeyChar()=='g')) {
                        force.toggleGravity();
                        e.consume();
                    }

                    else if ((e.getID() == KeyEvent.KEY_TYPED) && (e.getKeyChar()==' ')) {
                        force.jump();
                        e.consume();
                    }

                }
            }
        }

        Toolkit.getDefaultToolkit().addAWTEventListener(
                new KeyboardEventListener(),
                AWTEvent.KEY_EVENT_MASK);

Then in your renderer loop you let the collision system handle the avatar and then sync the view.


            cs.newFrame(deltaTime);
            force.updateView(avatarNode, view, deltaTime);
            view.renderOnce();

Thats it!

You can check out the Quake 3 demo I just posted to see this work and to study the complete source code.

Thanks for posting. Can’t wait to see the quake demo.

:slight_smile: