My MD2 runs sideways!

Started by ToddMcF2002, March 26, 2007, 06:13:21 PM

Previous topic - Next topic

ToddMcF2002

Using the Car example I loaded a few MD2's to have models running next to the car.  Everything works great except my models are oriented at 45 degree angles to the car.  My models are pacing the car just fine buy they run sideways.

The only way I could fix was to initialize by turning 45 degrees during load and change the "moveForward" method to use the xaxis instead of the yaxis.

I think I need something like rotateMesh() but it doesnt seem to work.  Any ideas?

Thanks!
- Todd

EgonOlsen

rotateMesh should work when applied on the MD2. Can you post some code to see exactly how you are doing it?

manumoi

#2
I use md2 in my own project and the following code is correct for rotating it in the correct position

  ...
        this.setCollisionMode(Object3D.COLLISION_CHECK_SELF/*|Object3D.COLLISION_CHECK_OTHERS*/);
this.setCollisionOptimization(Object3D.COLLISION_DETECTION_OPTIMIZED);
this.rotateY((float)Math.PI*1.5f);
this.rotateMesh();
        ...


Then i use the following piece of code for moving forward


private void moveForward(){
SimpleVector a = this.getZAxis();
a.scalarMul(5f);
a = this.checkForCollisionEllipsoid(a,ELLIPSOID_RADIUS, 5);
this.translate(a);
        ...
    }


Where this refers to the Object3D obtained from the Md2 file

ToddMcF2002

#3
Excellent guys.  That fixed it.  Much appreciated!

Now I need to figure out how to click ahead of the models and have them walk to the spot. 

Obviously I need to figure out the rotation amount and the distance and speed.  This is basic angle
math I think!  Given the "known" currentY I need to rotate to the new Y and move forward until I reach
the new SimpleVector.

Has anyone done this?

BTW I love this engine guys!  Hats off to Egon (I assume he wrote it?)

EDIT:
I started to think about implementing my own Object3D.lookAt() and apparently others have thought of this. 
I remember an old CQuake routine called "castBearingLine" that used to iterate around its axis looking for objects
in its sphere.  Part of AI routines I think.  I wish I remembered the code!  Anyway, this holds promise....

http://www.jpct.net/forum2/index.php/topic,513.0.html




EgonOlsen

Quote from: ToddMcF2002 on March 27, 2007, 12:16:02 AM
Anyway, this holds promise....
That's what i thought of too. A picking approach may work too, where you click onto the ground, create a vector pointing from the mouse to the ground (Interact2D.reproject2D3D()), calculate the distance to the ground and derive the intersection point from both values as the new target point for your object. This still requires you to do a "lookAt" for the object, but that's easy. Code should be somewhere around here IIRC.

ToddMcF2002

OK I really need help.  I can't make heads or tails of these problems.  Again I'm using a variation of the Car sample.  The Object selection code works great, and I have nice selection "disks" under my MD2 guys who are running around the screen.  Progress!  Yay!  So now I'm trying to click in front of my guys and get them to walk over to the point I clicked at (like Neverwinter Nights etc). 


I thought that would be easy but its not.  My first problem is conceptual.  I realize I'm totally disoriented on the y Axis in JPCT.  I'm confused why my MD2 gets a more negative y (getTransformedCenter) when he is taller than the car?  If I walk forward and to the right the x and z axis increase (from 0's) which makes sense.  Interact2D.reproject2D3D return negative as you go up too.  The negative y axis is driving me crazy!
I'm sure you are laughing at me now and that is OK I'm a total noob at 3D vectors.  :P

The second problem is that the function below (found on the forum) seems to return a reasonable x axis value when I click on the flat terrain but the y and z values are wildly negative.  For example, if I click on the terrain as close to my MD2 as possible without moving the MD2 from its initial position (close to 0,0,0) I get x:y:z -1.19205055E-7:-328.108:-753.85815.   Vector.x makes sense to me.  Vector.z gets deeper negative as I click toward the horizon.  I can't even speak for y.

I'm lost!  Any assistance would be great.


public static SimpleVector getAbsoluteCoordinate(Camera camera, FrameBuffer buffer, int x, int y, Object3D object) {
        SimpleVector rezult = null;
        if (camera != null && buffer != null) {
            SimpleVector rayTemp = Interact2D.reproject2D3D(camera, buffer, x, y);

            rayTemp.normalize();
            Matrix orient = camera.getFront();
            float[] dump = orient.getDump();
            SimpleVector ray = new SimpleVector();
            ray.x = dump[0] * rayTemp.x + dump[1] * rayTemp.y + dump[2] * rayTemp.z + dump[3] * 1;
            ray.y = dump[4] * rayTemp.x + dump[5] * rayTemp.y + dump[6] * rayTemp.z + dump[7] * 1;
            ray.z = dump[8] * rayTemp.x + dump[9] * rayTemp.y + dump[10] * rayTemp.z + dump[11] * 1;

            float distance = object.rayIntersectsAABB(camera.getPosition(), ray);
         
         System.out.println("getAbsoluteCoordinate() distance: " + distance);

            if (distance != Object3D.RAY_MISSES_BOX) {
            
                rezult = new SimpleVector(camera.getPosition());
                ray.scalarMul(distance);
                rezult.add(ray);
            }
        }
        return rezult;
    }

EgonOlsen

Something like this?:


import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.threed.jpct.*;
import com.threed.jpct.util.*;

public class MouseFollowDemo extends JFrame implements MouseMotionListener {

    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 MouseFollowDemo() {
        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(20, 10);
        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);

            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 {
        MouseFollowDemo cd = new MouseFollowDemo();
        cd.initStuff();
        cd.doIt();
    }
}

ToddMcF2002

LOL Yeah exactly!!!!  Thanks a ton!

Can you please explain something though - its a noob thing.

Looking at follow()...

Can you tell me (conceptually) where I'm right, wrong and/or need help?
1.  norm is a directional vector (normal vector)
2.  I don't grasp why we multiply the normalized ray by an inverted matrix from the camera.
3.  "f" gets you the scalar to turn the directional vector into a vector with length to the click point.
4.  You subtract the small directional vector otherwise its a bit too long
5.  cube goes back to identity, then relative to the camera, and finally remove the camera vector impact.

I guess part "2" is where I'm pretty confused.

EgonOlsen

#8
Yes, it admit that this isn't that easy to understand, but i didn't have the time to comment it better when writing it. So here is a version with better comments. I've also changed two small things: The camera's matrix is now being taken from the front matrix, because that is what you acutally see on screen and the translations have been switched to make it more clearer what happens there...


private void follow() {
       // This gets us a direction vector from the origin to the point on the near plane
       // where the mouse is located. In other words: This is the ray from the eye/camera
       // to the mouse on the screen's surface.
       SimpleVector ray=Interact2D.reproject2D3D(world.getCamera(), fb, x, y);
       if (ray!=null) {

          // The lenght depends on the near plane...normalize it!
          SimpleVector norm=ray.normalize();
         
          // The problem is: The ray as well as it's starting point (the origin) are in camera
          // space. We need coordinates in world space for doing the calculations, so we
          // need a matrix to convert camera space back to world space, which we create here.
          // The rotation matrix is sufficient, because it's not needed to translate the origin back
          // into world space...we already have this: It's the camera's current location.
          Matrix mat=world.getCamera().getFront();
          mat=mat.invert3x3();
          norm.matMul(mat);

          // We now have a point in world space, where a camera is located as well as a direction
          // vector from it to mouse (but this time, in world space). We can now calculate the distance
          // from the camera to the nearest polygon.
          float f=world.calcMinDistance(world.getCamera().getPosition(), norm, 1000);
          if (f!=Object3D.COLLISION_NONE) {
             // Something has been hit by the ray...
             SimpleVector offset=new SimpleVector(norm);
             
             // The direction vector is normalized. We want it to have the length of the distance
             // from the camera to the intersection point, so that we can calculate the intersection
             // point later on.
             norm.scalarMul(f);
             
             // We subtract a small offset...this is just to make sure that the cube in this example
             // is visible and not hidden by the geometry it's supposed to follow.
             norm=norm.calcSub(offset);
             
             // Cube goes back to origin...
             cube.getTranslationMatrix().setIdentity();
             
             // ...then goes to the camera's position...
             cube.translate(world.getCamera().getPosition());
             
             // ...and finally, it rides along the ray until it reaches the intersection point...:-)
             cube.translate(norm);
          }
       }
    }


ToddMcF2002

Thanks.  I'm still a bit lost on the matrix but I think get the intent?  For example after the matrix adjustment the z axis direction goes from near 0 (strait down) toward 1 (far plane).  I get that.

How could you describe what the normal vector z direction is before adjustment?  It ranges from .9 to about .94.  Is that because its only considering the "camera cone" relative to the world so it doesnt reflect much movement?

I'm just trying to find a way to conceptualize things.




EgonOlsen

After? Before(!) the matrix multiplication, z is going from 0 to 1. That's because it's pointing from the origin to the near plane, which is at z=1. After the multiplication, it's going from camera position to the tranformed near plane in world space.
World space is the space, you are viewing and in which you are doing the translations. But it's not how you are viewing the scene. That's camera space. In camera space, the camera is always at 0,0,0 looking down the z-axis.
Maybe it'll help you to draw these things on a piece of paper. I know that it can twist your mind sometimes... ;)

ToddMcF2002

#11
Well now my MD2 runs to where I click.  Works and looks fantastic! 

The only thing bugging me about the implementation is now I'm holding the rotationMatrix on the stack instead of float yRot like in the Car example.  I'm wondering if I should convert back to an absolute angle (via Math.atan etc).  Without yRot I can't easily support turning via keys and clicking via mouse like Neverwinter Nights.  Can you think of a better way to maintain yRot or is math after every rotationMatrix adjustment the way to go?

Also, I might actually have a real small contribution here LOL.  I noticed my X pitch kept getting mangled on the rotation adjustments since the Y axis wasnt normalized between the objects - so target.add(new SimpleVector(0, (xd.y + target.y)*-1, 0)) keeps the height constant while allowing x-z adjustment.  Anyway here is the revised routine.  Of course its always possible I broke your working code and this is all a bad hack ha ha!

private void rotateToMarker(){
      // mdl is the object to turn here...
      SimpleVector xd=mdl.getTransformedCenter();
      xd.scalarMul(-1f);

     SimpleVector target = marker.getTransformedCenter();
    
     // normalize the y axis between the objects to avoid x tilt...
     target.add(new SimpleVector(0, (xd.y + target.y)*-1, 0));    

      xd.add(target);
     
      // Copy it...
      SimpleVector yd=new SimpleVector(xd);     
      // store the matrix for other placement routines
      _rotY=yd.getRotationMatrix();
     
      mdl.setRotationMatrix(_rotY);

   }

ToddMcF2002

Alright this one could definately help somebody out there.  I figured out how to convert the matrix to a PI based rotation.  So now I can support key turns and mouse follow since they both now can track the PI based rotation on Y.  The only caveat is that this will work only when the rotations are on the Y axis (a typical isometric RPG).


         
          // this measures (PI/2 radians) -1.5707 to 1.5707 aka -180 to 180 degrees on x... 
          float[] md =_rotY.getDump();
          float rad =(float) Math.asin(-md[2]);

         // this will give us x and z rotations around y...
         SimpleVector vz = _rotY.getZAxis();   
       
         float fPi = 0f;   
         // once we know which 90 degree quadrant we are in
         // we can adjust the raidians and add to magnitudes of PI to get 0 - 6.28... PI
         if(vz.x > 0 && vz.z > 0)
        fPi = (float)rad;
         else if(vz.x > 0 && vz.z < 0)
        fPi = (float)Math.PI - (float)rad;
         else if(vz.x < 0 && vz.z < 0)
        fPi = (float)Math.PI - (float)rad;
          else if(vz.x < 0 && vz.z > 0)
        fPi = (float)Math.PI*2  + (float)rad;
               

ToddMcF2002

Once again thinking it would take 2 seconds to implement -  lwjgl setNativeCursor was like having a root canal.

At least it works.  I had to place my textures inverted for some reason!