www.jpct.net

jPCT - a 3d engine for Java => Support => Topic started by: theFALCO on December 17, 2006, 09:19:00 pm

Title: the swept
Post by: theFALCO on December 17, 2006, 09:19:00 pm
what exactly is this swept algorithm? is it like I have an object and if it collides with a ramp it moves up the ramp instead of getting blocked? if yes, how do I set the max hight?
Title: the swept
Post by: EgonOlsen on December 18, 2006, 01:51:29 pm
Yes, basically. It's called "swept", because it sweeps across the scene. Not like the sphere-implementation, which just evaluates the final position. For every obstacle, a sliding plane is calculated as the tangent plane to the ellipsoid in the point of contact with the obstacle. So yes, it will move up that ramp. Anything else (how far...etc) is application logic and has to be done in your code somehow.
Title: the swept
Post by: theFALCO on December 18, 2006, 07:22:55 pm
I'm interested in the 'somehow' part. What I mean is that I have created an object and a terrain. In the loop I call the checkForCollisionEllipsoid() (terrain set to COLLISION_CHECK_OTHERS and the model a.k.a. cube is COLLISION_CHECK_SELF) here is the code:
Code: [Select]
/* in the initialization */
        float[] bf = cube.getMesh().getBoundingBox();
        Csiz.x = Math.abs(bf[0])+Math.abs(bf[1]);
        Csiz.y = Math.abs(bf[2])+Math.abs(bf[3]);
        Csiz.z = Math.abs(bf[4])+Math.abs(bf[5]);

/* the following is in the main loop */

        SimpleVector grav = new SimpleVector(0, GRAVITY, 0);
        grav = cube.checkForCollisionEllipsoid(grav, Csiz, 1);
        grav.x=0;
        grav.z=0;
        cube.translate(grav);
        t=1;
        if(cube.wasTargetOfLastCollision()) {
            inAir=false;
        }
        if(right) {
            cube.rotateY(0.01f);
        }
        if(left) {
            cube.rotateY(-0.01f);
        }
        if(up) {
            cube.rotateAxis(cube.getXAxis(), -0.01f);
        }
        if(down) {
            cube.rotateAxis(cube.getXAxis(), 0.01f);
        }
        if(forward) {
            t=2;
            SimpleVector fax = cube.getZAxis();
            fax.scalarMul(1f);
            fax=cube.checkForCollisionEllipsoid(fax, Csiz, 1);
            cube.translate(fax);
        }


(http://img471.imageshack.us/img471/3481/1in3.jpg)
As you can see not only my model sticks in the ground to knees but also it can't climb even the smallest ramp. On this screenshot it is blocked.

However I don't think it's really a swept-related problem. I have created a simple map which is flat and cube gets stuck even there :( .

I'm using here the trrain from car example and bill model somewhere from this forum (btw. thanks for it :) ).
Title: the swept
Post by: EgonOlsen on December 18, 2006, 07:35:35 pm
First of all, your ellipsoid calculation isn't correct. If, for example, xMin is -4 and xMax is 4, your formula gets you a x-radius of 8, where it actually should be 4. Ellipsoid collision detection is not suitable to push you out of a collision, so maybe you are already colliding with the ground (given that large radius) when doing the gravity check. Make sure that the ellipsoid is collision free before starting.
Second, a recursion depth of 1 will never let you slide, because you'll stop at the first point of contact. Values like 4 to 6 are usually reasonable.
Title: the swept
Post by: theFALCO on December 18, 2006, 07:55:02 pm
Quote from: "EgonOlsen"
Make sure that the ellipsoid is collision free before starting

It isn't

When I change to Csiz.x/y/z = (blahblah) / 2; the model gets stuck even further to the ground.

Increasing 1 to 6 doesn't help.

When I change the cube from new Object3D(400) to Primitives.getCube(1) it works perfectly with no blocking but when I change to getCube(10) it gets blocked...
Title: the swept
Post by: EgonOlsen on December 18, 2006, 08:01:02 pm
Well, then make sure that it is collision free. The algorithm simply can't move you out of a collision, it can just prevent a collision to happen. So if you start collision free, you should never run into one. The problem is: You can't calculate a sliding plane that pushs you out of the collision if you are already in one.
You may try to do a single call to the spherical collision detection with rMax(+0.1 to be sure) (of the ellipsoid) as radius of the sphere and no translation. The resulting translation may push you out of this initial collision.
Title: the swept
Post by: theFALCO on December 18, 2006, 08:19:49 pm
it is collision free (it is spawned to fall down at the beginning)

what I have noticed is the bigger it is the less the swept is working...
Title: the swept
Post by: EgonOlsen on December 18, 2006, 08:25:21 pm
Try to increase Config.collideOffset. Maybe the collision isn't recognized correctly because the vertices are too far away. If that doesn't help, please post a simple test case.
Title: the swept
Post by: theFALCO on December 18, 2006, 08:31:00 pm
Config.collideOffset = 10000;

still nothing
what's a test case?
Title: the swept
Post by: EgonOlsen on December 18, 2006, 08:40:07 pm
Some code that shows the problem that i can take, compile and execute to see what's actually wrong here.
Title: the swept
Post by: theFALCO on December 18, 2006, 08:51:24 pm
actually it's everything :roll:
Code: [Select]
import java.io.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;

import com.threed.jpct.*;
import com.threed.jpct.util.*;

class Cube {

    /* Engine Specific */
    private final static int SWITCH_RENDERER=35;
    private boolean fullscreen=false;
    private boolean openGL=true;
    private boolean wireframe=false;

    private FrameBuffer buffer=null;
    private World theWorld=null;
    private TextureManager texMan=null;
    private Camera camera=null;

    private int width=640;
    private int height=480;

    private Frame frame=null;
    private Graphics gFrame=null;
    private BufferStrategy bufferStrategy=null;
    private GraphicsDevice device=null;
    private int titleBarHeight=0;
    private int leftBorderWidth=0;

    private int switchMode=0;

    private int fps;
    private int lastFps;
    private long totalFps;

    private int pps;
    private int lastPps;

    private boolean isIdle=false;
    private boolean exit=false;

    private boolean left=false;
    private boolean right=false;
    private boolean up=false;
    private boolean down=false;
    private boolean forward=false;
    private boolean back=false;
    private boolean jump=false;
    private boolean inAir=false;
    private boolean fire=false;
    private int fireCount=3;
    private float GRAVITY=1f;
    private int theIlluminator;

    private float camAlt=125f;
    private float camDist=150f;
    private boolean camUp=false;
    private boolean camDown=false;
    private boolean camIn=false;
    private boolean camOut=false;

    private float speed=0;
    private int t=0;
    private float z=0f;

    private KeyMapper keyMapper=null;

    private Object3D cube = /*Primitives.getCube(10);*/new Object3D(400);
    private Object3D terrain = new Object3D(400);
    private SimpleVector Csiz = new SimpleVector();

    private Cube(String[] args) {
        /* init stuff */
        char c=File.separatorChar;
        Config.glFullscreen=false;
        Config.glMipmap=true;
        Config.glColorDepth=16;
        Config.maxPolysVisible=10000;
        /* process commandline arguments */
        for(int i=0;i<args.length;i++) {
            if(args[i].equals("fullscreen")) {
                Config.glFullscreen=true;
            }
            if(args[i].equals("noGL")) {
                openGL=false;
            }
        }
        isIdle=false;
        switchMode=0;
        totalFps=0;
        fps=0;
        lastFps=0;
        theWorld=new World();
        //Lights!
        Config.fadeoutLight=false;
        theWorld.getLights().setOverbrightLighting(Lights.OVERBRIGHT_LIGHTING_DISABLED);
        theWorld.getLights().setRGBScale(Lights.RGB_SCALE_2X);
        theWorld.setAmbientLight(25, 30, 30);
        theWorld.addLight(new SimpleVector(0, -150, 0), 10, 10, 10);
        theWorld.addLight(new SimpleVector(-1000, -1000, 1000), 5, 5, 5);
        theWorld.addLight(new SimpleVector(1000, -1000, -1000), 5, 5, 5);
        theIlluminator=theWorld.addLight(new SimpleVector(1000, -1000, -1000), 255, 0, 0);
        theWorld.setLightDiscardDistance(theIlluminator, 20);
        //Fog!
        theWorld.setFogging(World.FOGGING_ENABLED);
        theWorld.setFogParameters(5000, 0, 0, 0);
        Config.farPlane=5000;
        //Textures!
        texMan=TextureManager.getInstance();
        texMan.addTexture("teks", new Texture("textures"+c+"ql0.jpg"));
        texMan.addTexture("rocks", new Texture("textures"+c+"rocks.jpg"));
        //World!
        terrain=Loader.load3DS("models"+c+"terascene.3ds", 1000)[0];
        terrain.setOrigin(new SimpleVector(0, 0, 0));
        terrain.rotateX((float)-Math.PI/2f);
        terrain.setTexture("rocks");
        terrain.createTriangleStrips(2);
        OcTree oc = new OcTree(terrain, 50, OcTree.MODE_OPTIMIZED);
        terrain.setOcTree(oc);
        oc.setCollisionUse(OcTree.COLLISION_USE);
        Config.collideOffset=250;
        terrain.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
        terrain.setCollisionOptimization(Object3D.COLLISION_DETECTION_OPTIMIZED);
        //add listener
        theWorld.addObject(terrain);
        //theCUBE!
        cube=Loader.loadMD2("models"+c+"bill"+c+"tris.MD2", 1f);
        cube.rotateY((float)-Math.PI/2f);
        cube.rotateMesh();
        cube.setRotationMatrix(new Matrix());
        texMan.addTexture("suit", new Texture("models"+c+"bill"+c+"billywork.jpg"));
        //cube.setBaseTexture("suit");
        cube.setTexture("suit");
        cube.setCollisionMode(Object3D.COLLISION_CHECK_SELF);
        //cube.setCollisionOptimization(Object3D.COLLISION_DETECTION_OPTIMIZED);
        cube.setTranslationMatrix(new Matrix());
        theWorld.addObject(cube);

        float[] bf = cube.getMesh().getBoundingBox();
        Csiz.x = (Math.abs(bf[0])+Math.abs(bf[1]))/2f;
        Csiz.y = (Math.abs(bf[2])+Math.abs(bf[3]))/2f;
        Csiz.z = (Math.abs(bf[4])+Math.abs(bf[5]))/2f;
        Logger.log("============================", Logger.MESSAGE);
        for(int i=0;i<6;i++) {
            Logger.log(String.valueOf(bf[i]), Logger.MESSAGE);
        }
        Logger.log("============================", Logger.MESSAGE);
        Logger.log(String.valueOf(Csiz.x), Logger.MESSAGE);
        Logger.log(String.valueOf(Csiz.y), Logger.MESSAGE);
        Logger.log(String.valueOf(Csiz.z), Logger.MESSAGE);
        Logger.log("============================", Logger.MESSAGE);
        //Camera!
        camera=theWorld.getCamera();
        camera.setPosition(0, 0, 10);
        camera.lookAt(cube.getTransformedCenter());

        theWorld.buildAllObjects();
        Config.tuneForOutdoor();
        initializeFrame();
        gameLoop();
    }

    public static void main(String[] args) {
        /* main stuff */
        new Cube(args);
    }

    private void initializeFrame() {

        if (Config.glFullscreen) {
            GraphicsEnvironment env=GraphicsEnvironment.getLocalGraphicsEnvironment();
            device=env.getDefaultScreenDevice();
            GraphicsConfiguration gc=device.getDefaultConfiguration();
            frame=new Frame(gc);
            frame.setUndecorated(true);
            frame.setIgnoreRepaint(true);
            device.setFullScreenWindow(frame);
            if (device.isDisplayChangeSupported()) {
                device.setDisplayMode(new DisplayMode(width, height, 32, 0));
            }
            frame.createBufferStrategy(2);
            bufferStrategy=frame.getBufferStrategy();
            Graphics g=bufferStrategy.getDrawGraphics();
            bufferStrategy.show();
            g.dispose();
        } else {
            frame=new Frame("jPCT "+Config.getVersion());
            frame.pack();
            Insets insets = frame.getInsets();
            titleBarHeight=insets.top;
            leftBorderWidth=insets.left;
            frame.setSize(width+leftBorderWidth+insets.right, height+titleBarHeight+insets.bottom);
            frame.setResizable(false);
            frame.show();
            gFrame=frame.getGraphics();
        }
        frame.addWindowListener(new WindowEvents());
        keyMapper=new KeyMapper(frame);
    }

    private void moveCube() {
        SimpleVector grav = new SimpleVector(0, GRAVITY, 0);
        grav = cube.checkForCollisionEllipsoid(grav, Csiz, 6);
        grav.x=0;
        grav.z=0;
        cube.translate(grav);
        t=1;
        if(cube.wasTargetOfLastCollision()) {
            inAir=false;
        }
        if(right) {
            cube.rotateY(0.01f);
        }
        if(left) {
            cube.rotateY(-0.01f);
        }
        if(up) {
            cube.rotateAxis(cube.getXAxis(), -0.01f);
        }
        if(down) {
            cube.rotateAxis(cube.getXAxis(), 0.01f);
        }
        if(forward) {
            t=2;
            SimpleVector fax = cube.getZAxis();
            fax.scalarMul(1f);
            fax=cube.checkForCollisionEllipsoid(fax, Csiz, 6);
            cube.translate(fax);
        }
        if(back) {
            SimpleVector bax = cube.getZAxis();
            bax.scalarMul(-0.5f);
            bax=cube.checkForCollisionEllipsoid(bax, Csiz, 25);
            cube.translate(bax);
        }
        if(jump) {
            if(!inAir) {
                SimpleVector jax = cube.getZAxis();
                jax.scalarMul(-1f);
                jax.add(new SimpleVector(0, -5, 0));
                jax.x=0;
                jax.z=0;
                jax=cube.checkForCollisionEllipsoid(jax, Csiz, 25);
                cube.translate(jax);
                //inAir=true;
            }
        }
        if(camUp) {
            camAlt+=0.5f;
        }
        if(camDown) {
            camAlt-=0.5f;
        }
        if(camIn) {
            camDist-=0.5f;
        }
        if(camOut) {
            camDist+=0.5f;
        }
        // Anim
        if(z>1f) {
            z=0f;
        }
        cube.animate(z, t);
        z+=0.001f;
    }

    private void display() {
        if (buffer.usesRenderer(IRenderer.RENDERER_SOFTWARE)) {
            if (Config.glFullscreen) {
                Graphics g=bufferStrategy.getDrawGraphics();
                g.drawImage(buffer.getOutputBuffer(), 0, 0, null);
                bufferStrategy.show();
                g.dispose();
            } else {
                buffer.display(gFrame, leftBorderWidth, titleBarHeight);
            }
        } else {
            buffer.displayGLOnly();
        }
    }

    private void gameLoop() {
        World.setDefaultThread(Thread.currentThread());
        buffer=new FrameBuffer(width, height, FrameBuffer.SAMPLINGMODE_NORMAL);
        buffer.enableRenderer(IRenderer.RENDERER_SOFTWARE);
        if(openGL) {
            frame.hide();
            keyMapper.destroy();
            buffer.enableRenderer(IRenderer.RENDERER_OPENGL, IRenderer.MODE_OPENGL);
            buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
            keyMapper=new KeyMapper();
        }
        buffer.setBoundingBoxMode(FrameBuffer.BOUNDINGBOX_NOT_USED);
        buffer.optimizeBufferAccess();
        Timer timer = new Timer(25);
        timer.start();
        while(!exit) {
            if(!isIdle) {
                poll();
                buffer.clear();
                theWorld.renderScene(buffer);
                theWorld.draw(buffer);
                buffer.update();
                moveCube();
                SimpleVector ilpos=cube.getTransformedCenter();
                theWorld.setLightPosition(theIlluminator, ilpos);
                camera.setPosition(cube.getTransformedCenter());
                camera.align(cube);
                camera.moveCamera(Camera.CAMERA_MOVEOUT, camDist);
                camera.moveCamera(Camera.CAMERA_MOVEUP, camAlt);
                camera.lookAt(cube.getTransformedCenter());
                display();
                Thread.yield();
            }
        }
        if (openGL) {
            buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
        } else {
            if (fullscreen) {
                device.setFullScreenWindow(null);
            }
        }
        System.exit(0);
    }

    private void keyAffected(KeyState state) {
        int code = state.getKeyCode();
        boolean event = state.getState();

        switch(code) {
            case(KeyEvent.VK_ESCAPE): {
                exit=true;
                break;
            }
            case(KeyEvent.VK_RIGHT): {
                right=event;
                break;
            }
            case(KeyEvent.VK_LEFT): {
                left=event;
                break;
            }
            case(KeyEvent.VK_UP): {
                forward=event;
                break;
            }
            case(KeyEvent.VK_DOWN): {
                back=event;
                break;
            }
            case(KeyEvent.VK_SPACE): {
                jump=event;
                break;
            }
            case(KeyEvent.VK_NUMPAD4): {
                camUp=event;
                break;
            }
            case(KeyEvent.VK_NUMPAD1): {
                camDown=event;
                break;
            }
            case(KeyEvent.VK_NUMPAD5): {
                camIn=event;
                break;
            }
            case(KeyEvent.VK_NUMPAD2): {
                camOut=event;
                break;
            }
            case(KeyEvent.VK_INSERT): {
                t++;
                if(t>17) {
                    t=0;
                }
            }
        }
    }

    private void poll() {
        KeyState state = null;
        do {
            state=keyMapper.poll();
            if(state != KeyState.NONE) {
                keyAffected(state);
            }
        } while (state != KeyState.NONE);
    }

    private class WindowEvents extends WindowAdapter {
        public void windowIconified(WindowEvent e) {
            isIdle=true;
        }

        public void windowDeiconified(WindowEvent e) {
            isIdle=false;
        }
    }

    private class Timer {

        private long ticks=0;
        private long granularity=0;

        public Timer(int granularity) {
            this.granularity=granularity;
        }

        public void start() {
            ticks=System.currentTimeMillis();
        }

        public void reset() {
            start();
        }

        public long getElapsedTicks() {
            long cur=System.currentTimeMillis();
            long l=cur-ticks;

            if (l>=granularity) {
                ticks=cur-(l%granularity);
                return l/granularity;
            }
            return 0;
        }
    }

}


you'll also need a model (cause the cube isn't sticking in the ground, it's just getting blocked if too big, for instance 10)
Title: the swept
Post by: EgonOlsen on December 18, 2006, 10:20:58 pm
There are a couple of issues with your approach. What you are trying to do is terrain following. Ellipsoid collision detection isn't exactly the best approach to do this (that's why the car-example doesn't use it).
The sliding planes will cause your moving entity to leave the straight line it's supposed to travel, so when climbing steep edges, you'll most likely move around somehow but not in a straight line.
Doing gravity with it can be troublesome too. The sliding plane for "forward" can be totally different from that for "down" so the result looks strange if it moves at all. I strongly suggest to consider an approach like the car example was using it, if terrain following really it what you are planning to do. If the terrain acts as a placeholder for something else (like an indoor level), this may not be an issue later on.
Anyway, it wasn't moving at all, so...: If you want to do it that way, combine gravity and translation to one SimpleVector which results in one sliding plane. This avoids the problem that one translation says "hoh" while the other says "huh" and the result is "hohuh"...which may look strange.
I've modified the moveCube-method to reflect this. This may not be a  100% solution. I'll leave it up to you to come up with a better one:

Code: [Select]

private void moveCube() {
        t=1;
        Config.collideEllipsoidThreshold=0.00005f;
        terrain.setCollisionOptimization(Object3D.COLLISION_DETECTION_OPTIMIZED);

        if(cube.wasTargetOfLastCollision()) {
            inAir=false;
        }
        if(right) {
            cube.rotateY(0.01f);
        }
        if(left) {
            cube.rotateY(-0.01f);
        }
        if(up) {
            cube.rotateAxis(cube.getXAxis(), -0.01f);
        }
        if(down) {
            cube.rotateAxis(cube.getXAxis(), 0.01f);
        }

        boolean moved=false;

        if(forward) {
            t=2;
            SimpleVector fax = cube.getZAxis();
            fax.y+=1;
            fax=fax.normalize();
            fax.scalarMul(1f);
            fax=cube.checkForCollisionEllipsoid(fax, Csiz, 25);
            cube.translate(fax);
            moved=true;
        }
        if(back) {
            SimpleVector bax = cube.getZAxis();
            bax.scalarMul(-0.5f);
            bax.y+=1;
            bax=bax.normalize();
            bax=cube.checkForCollisionEllipsoid(bax, Csiz, 25);
            cube.translate(bax);
            moved=true;
        }
        if(jump) {
            if(!inAir) {
                SimpleVector jax = cube.getZAxis();
                jax.scalarMul(-1f);
                jax.add(new SimpleVector(0, -5, 0));
                jax=cube.checkForCollisionEllipsoid(jax, Csiz, 6);
                cube.translate(jax);
                moved=true;
            }
        }
       
        if (!moved) {
           SimpleVector grav=new SimpleVector(0,GRAVITY,0);
           grav=cube.checkForCollisionEllipsoid(grav, Csiz, 1);
           cube.translate(grav);
        }
       

        if(camUp) {
            camAlt+=0.5f;
        }
        if(camDown) {
            camAlt-=0.5f;
        }
        if(camIn) {
            camDist-=0.5f;
        }
        if(camOut) {
            camDist+=0.5f;
        }
        // Anim
        if(z>1f) {
            z=0f;
        }
        cube.animate(z, t);
        z+=0.001f;
    }


Another setting you've to adjust is Config.collideEllipsoidThreshold. I've set it to 0.00005f; which seemed reasonable. A value too high can block the recursion too early while a too low one can cause problems due to rounding errors in the calculation. A good value depends on the scene and the ellipsoid's size. It's hard to predict what works best...at least i've never found a good way to get a reasonable value without trying.

About the model sticking in the ground: An objects center as calculated by jPCT is the average of its vertices. On a human body, this tends to be located somewhere in the chest, because more vertices define the head and the body than the legs. So either increase the size of the ellipsoid (which will make it extend above the head) or adjust the center (try (0,0,0) instead).

Hope this helps.
Title: the swept
Post by: theFALCO on December 31, 2006, 07:12:57 pm
Could you provide any small (only the essentials - the less, the better) example of a working swept? A simple "cube moves up the ramp", please?
Title: the swept
Post by: EgonOlsen on January 01, 2007, 02:35:31 am
Yes, but it'll take some time. Maybe this thursday, if i find the time. The fps-example uses this algoritm too. Maybe it helps until then.
Title: the swept
Post by: theFALCO on January 01, 2007, 12:21:41 pm
the FPS example uses cameraCheckCollisionEllipsoid(), maybe it differs

maybe it's because I use cube.checkForCollisionEllipsoid, not theWorld.check..., maybe... I don't know, maybe I have to make my own SWEPT, but I don't know how... :?
Title: the swept
Post by: EgonOlsen on January 01, 2007, 03:13:26 pm
All of these methods map to the same one in World, so they don't really differ. What exactly is your problem with the collision detection? What do you expect it to do that it doesn't?
Title: the swept
Post by: theFALCO on January 01, 2007, 05:24:17 pm
The cube doesn't go up the ramp, it gets blocked by everything that's not flat or lower, and doesn't go along the wall when I collide with it under an angle... according to the manual the Ellipsoid collision should not only stop the movement but also try to avoid the obstacle by modifying the vector

or maybe I got it all wrong and there's nothing like thet in jpct?
Title: the swept
Post by: EgonOlsen on January 01, 2007, 05:29:11 pm
What's the recursion depth you are using? What's the size of the ellipsoid? Also try to play around with Config.collideEllipsoidThreshold as i did in the modified version of your here posted code. I can only recommend to look at the fps example again. It doesn't matter if it uses the camera instead of an Object3D for this...the algorithm uses the exactly same method internally. There you can see that it is possible to slide up ramps/obstacles and along walls
Title: the swept
Post by: theFALCO on January 01, 2007, 06:07:40 pm
depth - i tried 1, 4, 6, 25
size - generated dynamically with the getMesh().getBoundingBox
threshold - i tried 0.01 and it started working but only for the smallest ramp (it's almost unnoticably rised)
Title: the swept
Post by: EgonOlsen on January 01, 2007, 07:10:18 pm
Ok, i found the time to write a little example today. It lets a cube (move it via the CRSR-keys) collide with several primitives and a plane. Here it is, hope this helps:

Code: [Select]
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.threed.jpct.*;
import com.threed.jpct.util.*;

public class CollisionDemo extends JFrame {

    private Graphics g = null;
    private KeyMapper keyMapper = 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 up = false;
    private boolean down = false;
    private boolean left = false;
    private boolean right = false;

    private SimpleVector ellipsoid = new SimpleVector(2, 2, 2);

    public CollisionDemo() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setSize(800, 600);
        setResizable(false);
        setLocationRelativeTo(null);
        setVisible(true);
        g = getGraphics();
    }

    private void initStuff() {
        keyMapper = new KeyMapper(this);
        fb = new FrameBuffer(800, 600, FrameBuffer.SAMPLINGMODE_NORMAL);
        world = new World();
        fb.enableRenderer(IRenderer.RENDERER_SOFTWARE, IRenderer.MODE_OPENGL);

        plane = Primitives.getPlane(20, 10);
        ramp = Primitives.getCube(20);
        ramp.setAdditionalColor(Color.BLUE);
        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);
        cube.setCollisionMode(Object3D.COLLISION_CHECK_SELF);

        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 move() {
        KeyState ks = null;
        while ((ks = keyMapper.poll()) != KeyState.NONE) {
            if (ks.getKeyCode() == KeyEvent.VK_UP) {
                up = ks.getState();
            }
            if (ks.getKeyCode() == KeyEvent.VK_DOWN) {
                down = ks.getState();
            }
            if (ks.getKeyCode() == KeyEvent.VK_LEFT) {
                left = ks.getState();
            }
            if (ks.getKeyCode() == KeyEvent.VK_RIGHT) {
                right = ks.getState();
            }
        }

        // move the cube
        if (up) {
            SimpleVector t = new SimpleVector(0, 0, 1);
            t = cube.checkForCollisionEllipsoid(t, ellipsoid, 5);
            cube.translate(t);
        }

        if (down) {
            SimpleVector t = new SimpleVector(0, 0, -1);
            t = cube.checkForCollisionEllipsoid(t, ellipsoid, 5);
            cube.translate(t);
        }

        if (left) {
            SimpleVector t = new SimpleVector( -1, 0, 0);
            t = cube.checkForCollisionEllipsoid(t, ellipsoid, 5);
            cube.translate(t);
        }

        if (right) {
            SimpleVector t = new SimpleVector(1, 0, 0);
            t = cube.checkForCollisionEllipsoid(t, ellipsoid, 5);
            cube.translate(t);
        }
       
        // finally apply the gravity:
        SimpleVector t=new SimpleVector(0,1,0);
        t=cube.checkForCollisionEllipsoid(t,ellipsoid,1);
        cube.translate(t);
    }

    private void doIt() throws Exception {
        Camera cam = world.getCamera();
        cam.moveCamera(Camera.CAMERA_MOVEOUT, 100);
        cam.moveCamera(Camera.CAMERA_MOVEUP, 100);
        cam.lookAt(ramp.getTransformedCenter());

        while (true) {
            move();
            fb.clear(Color.RED);
            world.renderScene(fb);
            world.draw(fb);
            fb.update();
            fb.display(g);
            //Thread.sleep(20);
        }
    }

    public static void main(String[] args) throws Exception {
        CollisionDemo cd = new CollisionDemo();
        cd.initStuff();
        cd.doIt();
    }
}
Title: the swept
Post by: Melssj5 on January 02, 2007, 06:43:00 pm
Well, The ellipsoid on the fps works with the gravity, when it detects an inferior limit it moves the camera up. but this doesnt really correct the angle of the objetcs.
Title: the swept
Post by: theFALCO on January 05, 2007, 10:06:29 pm
YAY! (or not...)
I found it! I took this example, copy-pasted it, compiled... it works, so OK. Let's start adding things one by one so that it get's like my code until it stops working...
it stopped working after I set the speed (i mean the value which multiplies the vector which is checked and then added to the resultant) to 0.2

here is the code, the SPEED is set to 1 and it works, but if you change it to 0.2 (should be bigger than moveDamp) it stops working (oh, and BTW, I changed the renderer to OpenGL and made a camera on the back of the cube)

Code: [Select]
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.threed.jpct.*;
import com.threed.jpct.util.*;

public class CollisionDemo {

    private static float moveDamp = 0.1f;
    private static float SPEED = 1f;
    private static float MAXSPEED = 1f;

    private Graphics g = null;
    private KeyMapper keyMapper = 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 SimpleVector moveRes = new SimpleVector(0, 0, 0);

    private boolean up = false;
    private boolean down = false;
    private boolean left = false;
    private boolean right = false;

    private boolean doloop = true;

    private SimpleVector ellipsoid = new SimpleVector(2, 2, 2);

    public CollisionDemo() {
        Config.glFullscreen=false;
    }

    private void initStuff() {
        //keyMapper = new KeyMapper(this);
        fb = new FrameBuffer(1024, 768, FrameBuffer.SAMPLINGMODE_NORMAL);
        world = new World();
        fb.enableRenderer(IRenderer.RENDERER_SOFTWARE);
        fb.enableRenderer(IRenderer.RENDERER_OPENGL, IRenderer.MODE_OPENGL);
        fb.disableRenderer(IRenderer.RENDERER_SOFTWARE);
        keyMapper = new KeyMapper();

        plane = Primitives.getPlane(20, 10);
        ramp = Primitives.getCube(20);
        ramp.setAdditionalColor(Color.BLUE);
        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);
        cube.setCollisionMode(Object3D.COLLISION_CHECK_SELF);

        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 move() {
        KeyState ks = null;
        while ((ks = keyMapper.poll()) != KeyState.NONE) {
            if (ks.getKeyCode() == KeyEvent.VK_UP) {
                up = ks.getState();
            }
            if (ks.getKeyCode() == KeyEvent.VK_DOWN) {
                down = ks.getState();
            }
            if (ks.getKeyCode() == KeyEvent.VK_LEFT) {
                left = ks.getState();
            }
            if (ks.getKeyCode() == KeyEvent.VK_RIGHT) {
                right = ks.getState();
            }
            if(ks.getKeyCode() == KeyEvent.VK_ESCAPE) {
                doloop = false;
            }
        }

        // move the cube
        if (up) {
            SimpleVector t = cube.getZAxis();
            t.scalarMul(SPEED);
            //t = cube.checkForCollisionEllipsoid(t, ellipsoid, 5);
            //cube.translate(t);
            moveRes.add(t);
        }

        if (down) {
            SimpleVector t = cube.getZAxis();
            t.scalarMul(-SPEED);
            //t = cube.checkForCollisionEllipsoid(t, ellipsoid, 5);
            //cube.translate(t);
            moveRes.add(t);
        }

        if (left) {
            cube.rotateY((float)Math.toRadians(-1));
        }

        if (right) {
            cube.rotateY((float)Math.toRadians(1));
        }

        // finally apply the gravity:
        SimpleVector t=new SimpleVector(0,1,0);
        t=cube.checkForCollisionEllipsoid(t,ellipsoid,1);
        cube.translate(t);

        //avoid too high speed
        if(moveRes.length()>MAXSPEED) {
            moveRes.makeEqualLength(new SimpleVector(0,0,MAXSPEED));
        }

        moveRes = cube.checkForCollisionEllipsoid(moveRes, ellipsoid, 5);
        cube.translate(moveRes);

        //damping
        if(moveRes.length()>moveDamp) {
            moveRes.makeEqualLength(new SimpleVector(0,0,moveDamp));
        } else {
            moveRes = new SimpleVector(0,0,0);
        }
    }

    private void doIt() throws Exception {
        Camera cam = world.getCamera();
        cam.moveCamera(Camera.CAMERA_MOVEOUT, 100);
        cam.moveCamera(Camera.CAMERA_MOVEUP, 100);
        cam.lookAt(ramp.getTransformedCenter());

        while(doloop) {
            move();
            cam.setPositionToCenter(cube);
            cam.align(cube);
            cam.rotateCameraX((float)Math.toRadians(30));
            cam.moveCamera(Camera.CAMERA_MOVEOUT, 50);
            fb.clear(Color.RED);
            world.renderScene(fb);
            world.draw(fb);
            fb.update();
            fb.displayGLOnly();
        }
        fb.disableRenderer(IRenderer.RENDERER_OPENGL);
        System.exit(0);
    }

    public static void main(String[] args) throws Exception {
        CollisionDemo cd = new CollisionDemo();
        cd.initStuff();
        cd.doIt();
    }
}
Title: the swept
Post by: EgonOlsen on January 05, 2007, 10:38:09 pm
Yes, 0.1 as the threshold is for sure below 0.2, but the possible movement vector may not. So it's a good idea to adjust that value to, let's say, 0.0001f when applying a rather small translation.
This improves the situation but it doesn't solve all problems. Climping up the sphere is still a problem....i'm not sure what causes this, but i assume that it's an accuracy problem, so that the box is detected as stuck in the sphere where it actually isn't. I worked around this in the example by moving the gravity effect after the actual translation (something that i recommend to do anyway) and adding a small y-offset before doing the actual check for collision, i.e. something like this:

 
Code: [Select]


cube.translate(0,-0.02f,0);

moveRes = cube.checkForCollisionEllipsoid(moveRes, ellipsoid, 8);
cube.translate(moveRes);

// finally apply the gravity:
SimpleVector t=new SimpleVector(0,1,0);
t=cube.checkForCollisionEllipsoid(t,ellipsoid,1);
cube.translate(t);


This still isn't 100% perfect. But to be honest, this kind of algorithm will never work 100%...it doesn't do this in any game that i know. Yeah, sounds like a lame excuse, but the stuff is definitely not bugged...just inaccurate in some situations. I'm saying this, because i checked every single instruction in the methods a dozen times ones, because there was a crazy "shaking bug" in it that i've found after doing so. The rest works as it is supposed to be. It's just not 100% in every situation. It can't be.
Title: the swept
Post by: theFALCO on January 06, 2007, 02:59:42 pm
everything works great
i think that rescaling everything by 10 or 100 will do the job, cause I won't have to use so small values (I use such a small speed because even 1 is slightly too fast)

thanks for your effort