2

What I have implemented already

I have implemented an app which is able to render to the screen a list of polygons, with a specific static camera setting (position, look at and up vector), all in plain Java AWT without OpenGL.

I apply a model-view matrix first, then projection-to-2D matrix and then a viewport matrix.

I also implemented some basic transformation matrices on the world, such as translation, rotation of X/Y/Z axis around lookAt point and scaling around lookAt point.

What I want to achieve now

I want to be able to "move" in the world. Specifically, I want to navigate forward, backward, left and right with the keyboard arrows, and to be able to look at different points with the mouse. Just like in real games.

I guess this is done via changing the camera parameters each time and render the world again.

Is it that simple, though?

Moving in the world is just adding x,y values to camera position and look at a point?

In addition, is moving the mouse is just adding x,y to the look at a point?

Do I have to touch the up vector under any condition?

Informative answers, with additional relevant links, would also really help.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Jjang
  • 11,250
  • 11
  • 51
  • 87
  • 3
    Commonly you just move the world around your position. Almost every opengl tutorial I have seen discusses this topic. The method will be similar regardless of language. – Steven Walton May 26 '16 at 21:43
  • If you're using lookAt, then "moving" changes both the camera position and the look at point (unless you want to keep looking at the same point, of course). Turning around changes the look-at point in a somewhat complicated way. You might not want to use lookAt for turning. – user253751 May 26 '16 at 23:26
  • Real games most likely use translate and rotate operations to compute the view matrix. Not lookAt. (But it depends on the game, I'm assuming an FPS here, but if you think of something like Age of Mythology or Zelda it could well be using lookAt) – user253751 May 26 '16 at 23:28
  • So you want to tell me that moving the world is preferable? In both moving and turning around, I only move the world? – Jjang May 27 '16 at 07:54

2 Answers2

1

You can implement this behavior with a structured management of matrices. Especially the view matrix. Adding something to the x/y position is not correct in most cases because the camera may be rotated.

Let's denote the current view matrix with V. If you want to move the camera, you have to calculate a new view transform:

V := Translate(-x, -y, -z) * V

, where Translate(a, b, c) is a translation matrix. -x represents the movement in the left/right direction. -y represents up/down. -z represents forward/backward.

If you want to rotate the camera, this can be done similarly:

V := Rotate(-angle, axis) * V

, where Rotate(-angle, axis) is a rotation matrix. Use rotation about the x-axis to look up/down and rotation about the y-axis to look left/right. Rotation about the z-axis is usually not needed because it introduces a roll to the camera.

The reason why most parameters are negated is that view transforms are inverse transforms. I.e. they do not position the camera object, but they re-position the world as if the camera was at the origin.

Nico Schertler
  • 32,049
  • 4
  • 39
  • 70
1

first take a look at Understanding 4x4 homogenous transform matrices

  1. extract your camera axises vectors

    If you look at the 1st image in the linked answer you will see where are the vectors stored. Beware if you got transposed layout then the vectors are also transposed.

  2. movement

    The movement is easy you just add/sub the direction vector to the origin of your camera matrix. For example my cameras have Z- as a forward direction. So I take Z-axis vector from the matrix (which is in my case already unit) multiply it with speed and time (of the timer interval where the movement code is running). Here is example how my movement code usually looks like:

    //---------------------------------------------------------------------------
    void __fastcall Twin_main::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
        {
        // Keyboard event called on any key press
        keys.set(Key,Shift); // set key in my keymap as pressed
        _redraw=true; // key press means something in scene might change so redraw
        }
    //---------------------------------------------------------------------------
    void __fastcall Twin_main::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
        {
        // Keyboard event called on any key release
        keys.rst(Key,Shift); // set key in my keymap as not pressed
        }
    //---------------------------------------------------------------------------
    void __fastcall Twin_main::tim_updateTimer(TObject *Sender)
        {
        // Timer runing every tim_update->Interval [ms]
        double   dt=double(tim_update->Interval)/1000.0;
    
        // set speeds and transitions between view modes
        double   alfa=2.0*deg;       // angular speed
        double   v=100000.0/3.6;     // movement speed [km/hod] -> [m/s]
        // precision control
        if (keys.Shift.Contains(ssAlt )) { v*= 0.1; alfa*= 0.1; }
        if (keys.Shift.Contains(ssCtrl)) { v*=10.0; alfa*=10.0; }
    
        // rotations (local to camera space)
        if (keys.get(104)) { _redraw=true; eye.rep.lroty(+alfa); }                              // num8
        if (keys.get(105)) { _redraw=true; eye.rep.lroty(-alfa); }                              // num9
        if (keys.get(100)) { _redraw=true; eye.rep.lrotx(+alfa); }                              // num4
        if (keys.get( 97)) { _redraw=true; eye.rep.lrotx(-alfa); }                              // num1
        if (keys.get(111)) { _redraw=true; eye.rep.lrotz(+alfa); }                              // num/
        if (keys.get(106)) { _redraw=true; eye.rep.lrotz(-alfa); }                              // num*
        if (keys.get( 37)) { _redraw=true; eye.rep.lroty(+alfa); }                              // left
        if (keys.get( 39)) { _redraw=true; eye.rep.lroty(-alfa); }                              // right
    
        // movements
        if (keys.get( 96)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,0.0,-v*dt)); }        // num0
        if (keys.get(110)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,0.0,+v*dt)); }        // num.
        if (keys.get( 98)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,-v*dt,0.0)); }        // num2
        if (keys.get(101)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,+v*dt,0.0)); }        // num5
        if (keys.get(102)) { _redraw=true; eye.rep.lpos_set(vector_ld(-v*dt,0.0,0.0)); }        // num6
        if (keys.get( 99)) { _redraw=true; eye.rep.lpos_set(vector_ld(+v*dt,0.0,0.0)); }        // num3
        if (keys.get( 38)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,0.0,-v*dt)); }        // up
        if (keys.get( 40)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,0.0,+v*dt)); }        // down
    
        keys.rfskey(); // just do some stuff for advanced keyboard things not used here
        if (_redraw) { draw(); }
        }
    //---------------------------------------------------------------------------
    

    The keys is my class holding single bit bool for eaach of the 16 bit key code allowing me to handle multiple pressed keys at once. The eye.rep is my class holding both direct and inverse transformation matrix. Its member lpos_set takes a 3D vector transforms it from local to global coordinates and set this as matrix origin. It has the same effect as the above process I mentioned. The deg is just constant 1 degree in radians and lrot? are the local rotations see next bullet.

  3. rotations

    I do not use Euler angles as I hate them. They got glitches needed handling poles differently and often causing problems. Have you play any games and the camera angular rotation got stuck not allowing you rotate where you want? Or even reverse? Then it is due to Euler angles used for camera.

    I use local rotations lrot? instead (see the linked answer how they work). They have no bounds or glitches. The only thing that you need to take in mind is accuracy. If you are rotating matrix many times you loosing precision. So every few rotations ensure its orthogonality or orthonormality. For that you need just use cross product to make the axises perpendicular again and set their length to 1 or whatever you use. I have a counter in my matrix class incrementing each change and when it hits a treshold the orthonormality is restored.

As you can see I use numpad for the movements the arrows are for turn left/right and go forward/backward. Most people nowadays use WSAD but that is extremly uncomfortable for me. The code is taken from one of my VCL apps so you need to change the events to match yours.

You can also add mouse handler for turning ... In case you need Joystick see

but I doubt if it is applicable for JAVA as it is for Windows.

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Thanks for the detailed answer, but it was difficult for me to get any helpful info out of it, since I have basic knowledge in the area. The attached answer was long but I also read it, but neither one gave me the simple answer on how to implement movement in the world once I am able to render it staticly with basic transformations on objects. – Jjang May 27 '16 at 08:19
  • This sums up to two sub questions: how to implement moving and how to implement rotation of the view. Do I have to move the world or the camera in a simple movement? In rotation, do I rotate world or camera, and how to calculate the angle? – Jjang May 27 '16 at 08:21
  • @Jjang I move the camera .. (`eye` is my camera matrix) to move world has no sense because that would mean you would have to move all the objects in it which is insane. You can use your `lookat` as a start point (call only once while init) of camera and from that just integrate the movements as they go. You can look at the movements and turns as incremental steps. – Spektre May 27 '16 at 08:30
  • @Jjang I got `obj` matrix for each object in my world and single `eye` matrix for the camera view. They all are initialized just once. After that all of them are integrating their movements in time. I do not have any world matrix see http://stackoverflow.com/a/21100338/2521214 (the world coordinate system is constant unit matrix). You can also make a follow camera by simply taking object matrix translate locally behind it and invert the resulting matrix. – Spektre May 27 '16 at 08:46
  • Can you take a look on the above answer? How would I calculate the angle in which I need to rotate the world? Up until now I understood that I need to actually translate/rotate the world, and how to apply the translation matrix, but I'm stuck at calculating the angle for the rotation matrix. – Jjang May 28 '16 at 08:14
  • @Jjang `angle = angular_speed*time` where `time` is the time of timer or between events you use for movement computaton/update and `angular_speed` is the turn speed of your player or vehicle or whatever. So you can use constant speed (like in my example `alfa`) or variable that is on you. For the latter use [Newtonian - d'Lambert physics](http://stackoverflow.com/a/19241804/2521214) Also that answer does not change the world it changes the camera view similar to my approach. World is alway constant (do not know where you come with it may be wrong translation somewhere). – Spektre May 28 '16 at 08:27
  • Ok I was mistaken, I meant that after world-view transformation we apply the appropriate translate/rotate matrix. World stays constant, only the world-view matrix (which is pre-calculated) is now changed as the transformations are applied on it. Am I right up to this point? – Jjang May 28 '16 at 08:49
  • @Jjang looks like yes. – Spektre May 28 '16 at 08:50
  • Plus, I seem to have a big hole in the understanding of a rotation of a player. I understand that the rotation angle should be affected with speed and time, but, where is the actual angle in which the player rotated with? you know, he can look right, up, upper-right, etc.. each one has it's own different angle, without yet speaking about the speed and time which would also affect the angle as I understand. – Jjang May 28 '16 at 08:51
  • @Jjang you are rotating with incremental steps (in my example `alfa` but even better would be `alfa*dt`) The whole angle is accumulated inside the matrix as the direction of all axises involved. You need to look at matrices as a coordinate system representations instead of numbers. – Spektre May 28 '16 at 08:55
  • @Jjang for example if your turn speed is `5 deg/sec` and you want to turn by `90 deg` you need to hold the turn key for `18 sec` causing all the incremental steps to accumulate to 90 degree turn. – Spektre May 28 '16 at 08:58
  • @Jjang if you got mouse turning the angle step is usually directly the mouse angle... so if you travel with mouse by half of x-resolution of screen that means you turn by half of the projection angle of your projection matrix (usually 60deg so turn by 30deg) ... – Spektre May 28 '16 at 09:00
  • Can I simplify this to: Let's look at the rotation action as if the user was moving the center of the screen. Then, if user "moved" the center to another x,y point, I'd calculate the angle between the center of the screen to this point, and denote it as Thetha. Now, if turn speed is 5deg/sec I'd apply Theta/5 rotations of 5 deg (actually -5 deg because Nico's answer) every 1 second, so after Theta/5 seconds the whole rotation would be finished. Am I right? – Jjang May 28 '16 at 09:19
  • @Jjang yes but such computations are neaded only if you got AI ... with normal keyboard driven player by a human the human brain do this automatically and you do not need to blow your mind with stuff like this... Also there are 2 angles one in x axis and one in y axis .... – Spektre May 28 '16 at 09:31
  • You mean "such computations" to my intention to "slowly" apply the rotation every x seconds? Or what? How else could I integrate the "deg per sec" approach? – Jjang May 28 '16 at 09:49
  • @Jjang If you read the answer carefully you will see that the keyboard handler runs `inside timer`. That means it is called with constant frequency over time and that allows to use constant incremental steps for movement. May be it would be better if you start coding and solve the problems as they go. Because abstract thinking about problems that might or not be is pointless if you do not have the experience with the stuff. This way you will directly see what is happening which is much better for understanding. – Spektre May 28 '16 at 09:55
  • K, I'll start coding this week and come back with more precise questions. Thanks. – Jjang May 28 '16 at 09:57