Principle of a camera trackball
The name trackball has this weird device that replaces the mouse where you turn right into a ball. Here I learned the term free software cross-platform Google Earth
Google Earth
In Google Earth indeed using the mouse to rotate around the Earth. We will therefore reproduce this principle that we will have a camera to look at an object / part of a scene from all angles.
By holding down the left mouse button, the mouse movements will shoot the scene:
These movements are illustrated in the diagrams below:
Horizontal movement of the mouse
Vertical movement of the mouse
To take back or otherwise bring us closer to the object / scene we want to see, we'll just use the wheel. A shot wheel forward to zoom in, a shot back wheel to zoom out, nothing more intuitive:
Rotate the mouse wheel
It is possible to argue that all the mice do not have a wheel. In this case nothing prevents you to use the keyboard to zoom out.
Here we only use the keyboard for one thing: Reset all the rotation stage with the key ( SDLK_HOME SDL).
Google Earth
In Google Earth indeed using the mouse to rotate around the Earth. We will therefore reproduce this principle that we will have a camera to look at an object / part of a scene from all angles.
Rotate the mouse
By holding down the left mouse button, the mouse movements will shoot the scene:
- a horizontal movement of the mouse provides a horizontal rotation of the stage (thus about its vertical axis).
- a vertical movement of the mouse provides a vertical rotation of the stage.
These movements are illustrated in the diagrams below:
Horizontal movement of the mouse
Vertical movement of the mouse
Note in passing, and we see the equivalent in the code, that is not really the camera turns but the scene , even if it is more or less the same. This is the case for this type of camera. For the camera FreeFly we'll see after this is indeed the camera and not the scene move.
Zoom wheel
To take back or otherwise bring us closer to the object / scene we want to see, we'll just use the wheel. A shot wheel forward to zoom in, a shot back wheel to zoom out, nothing more intuitive:
Rotate the mouse wheel
Here it is the camera that moves, we see once again how it affects the code.
And keyboard?
It is possible to argue that all the mice do not have a wheel. In this case nothing prevents you to use the keyboard to zoom out.
Here we only use the keyboard for one thing: Reset all the rotation stage with the key ( SDLK_HOME SDL).
Some basics of C + +
Now that we know what we do with our camera, must be a little interlude learning C + +.
We are going to use and combine all the features of our camera in a class : TrackBallCamera. Course M @ teo explain the concept classes in detail. Let's look at it as an extension of a structure . remember C struct allowed to store multiple fields in a single type:Code: C-Select
Class has, in addition to attributes , the methods . These methods are like functions that apply to instances of this class.
Imagine a class named chair. A chair has certain attributes: height, number of feet area.
So write:Code: C + +-Select
Once the class is declared in the c% u0153ur program you want to be able to use (chairs). It thus creates "instances" of the class "chair" by simply declaring a variable of type chair.
Example:Code: C + +-Select
You could see a strange word in my example: protected . Without going into detail, this means that the attributes declared protected can not be accessed from outside the class, but only by its methods.
A method is a function but as it applies to a specific instance of the class.
Returning to our example of the chair. Suppose we wanted to remove one foot at our chair.
In C we should use a function enleverPied passing in any chair change. In C + + is called a direct method on an instance of the class. Example: The class declaration chair in Chaise.hCode: C + +-Select
The implementation of the methods of the class chair in Chaise.cpp
Code: C + + - Select
Call in the program body
And now we are interested in the method call enleverPied on an instance:Code: C + +-Select
As you can see it calls a method would be used as an attribute instance . MyMethod ();
When the program enters the code of the method he applies therefore to the desired instance, and uses the attributes of the proceeding.
There are two special methods that are not called directly by the user: the constructor and destructor. The constructor is called when the object is initialized, usually to his statement. This is a method without a return type, which carries the name of the class and initializes the attributes with initial values: Example: Declaration of the Manufacturer in Chaise.hCode: C + +-Select
Constructor implementation in Chaise.cpp
Code: C + + - Select
And so this constructor will be called as soon as you instantiate an object in the main body of the program:Code: C + +-Select
The destructive meanwhile is called automatically when the object is destroyed. In this case I have nothing special to do in the destructor, but if we had allocated memory dynamically (dynamic attributes of the class), it is destructive in that it must be destroyed for not memory leak. As the constructor, the destructor is called the class preceded by the symbol " ~ ". Here I will simply display a message when the destruction: Declaration destructive in Chaise.hCode: C + +-Select
Implementation of the destructor in Chaise.cpp
Code: C + + - Select
In the body of the program call the destructor is automatically at the end of the block where the instance is declared.
Example:Code: C + +-Select
If you are reading the OpenGL tutorial is that you probably know the dynamic allocation in C:Code: C-Select
In C + + operator is generally used new like this:Code: C + +-Select
We note here the use of () after chair which clearly sought to build an object. Once the allocated memory, the class constructor is called automatically. For Destruction, delete replaces free you know:Code: C + +-Select
I'm not going to do a course in modeling UML but just show you a graphical way to represent a class. I will use this symbolism throughout the tutorial to briefly summarize the features of a class:
This gives for example take our beloved chair:
We are going to use and combine all the features of our camera in a class : TrackBallCamera. Course M @ teo explain the concept classes in detail. Let's look at it as an extension of a structure . remember C struct allowed to store multiple fields in a single type:Code: C-Select
1 2 3 4 5 6 7 | struct NomDeVotreStructure { long variable1 , long variable2 , int autreVariable ; dual nombreDecimal ; } |
Class has, in addition to attributes , the methods . These methods are like functions that apply to instances of this class.
Ooh là là many new words! Bodies eg what is it?
Imagine a class named chair. A chair has certain attributes: height, number of feet area.
So write:Code: C + +-Select
1 2 3 4 5 6 7 | class Chair { protected : int height , int nombre_de_pieds , string matter ; } |
Once the class is declared in the c% u0153ur program you want to be able to use (chairs). It thus creates "instances" of the class "chair" by simply declaring a variable of type chair.
Example:Code: C + +-Select
1 | Chair machaise ; |
Note that C + + structures and classes are types. So no need to write a typedef Class Chair Chair, for example.
You could see a strange word in my example: protected . Without going into detail, this means that the attributes declared protected can not be accessed from outside the class, but only by its methods.
Methods precisely what is it?
A method is a function but as it applies to a specific instance of the class.
Returning to our example of the chair. Suppose we wanted to remove one foot at our chair.
In C we should use a function enleverPied passing in any chair change. In C + + is called a direct method on an instance of the class. Example: The class declaration chair in Chaise.hCode: C + +-Select
1 2 3 4 5 6 7 8 9 | class Chair { public : void enleverPied (); protected : int height , int nombre_de_pieds , string matter ; } |
The implementation of the methods of the class chair in Chaise.cpp
Code: C + + - Select
1 2 3 4 5 6 | # Include "chaise.h" void chair :: enleverPied () { nombre_de_pieds - ; } |
Call in the program body
And now we are interested in the method call enleverPied on an instance:Code: C + +-Select
1 2 | Chair machaise ; machaise . enleverPied (); |
As you can see it calls a method would be used as an attribute instance . MyMethod ();
When the program enters the code of the method he applies therefore to the desired instance, and uses the attributes of the proceeding.
Note that here I put the method in public and not protected so you can call from outside the class (that is to say from the body of the program).
Two special methods
There are two special methods that are not called directly by the user: the constructor and destructor. The constructor is called when the object is initialized, usually to his statement. This is a method without a return type, which carries the name of the class and initializes the attributes with initial values: Example: Declaration of the Manufacturer in Chaise.hCode: C + +-Select
1 2 3 4 5 6 7 8 9 10 | Class Chair { public : Chaise () / / constructor not returns nothing, but it may have parameters void enleverPied (); protected : int height , int nombre_de_pieds , string matter ; } |
Constructor implementation in Chaise.cpp
Code: C + + - Select
1 2 3 4 5 6 | Chair :: chair () { height = 1 ; nombre_de_pieds = 4 ; matter = "black" ; } |
And so this constructor will be called as soon as you instantiate an object in the main body of the program:Code: C + +-Select
1 2 | Chair machaise ; / / triggers the constructor call machaise . enleverPied () / / so I know that she now has a chair for 3 to 4 feet start thanks to constructor |
The destructive meanwhile is called automatically when the object is destroyed. In this case I have nothing special to do in the destructor, but if we had allocated memory dynamically (dynamic attributes of the class), it is destructive in that it must be destroyed for not memory leak. As the constructor, the destructor is called the class preceded by the symbol " ~ ". Here I will simply display a message when the destruction: Declaration destructive in Chaise.hCode: C + +-Select
1 2 3 4 5 6 7 8 9 10 11 | Class Chair { public : Chaise () / / constructor returns nothing, but it may have arguments void enleverPied (); ~ chair () / / destructor returns nothing, has no arguments, and above the symbol ~ protected : int height ; int nombre_de_pieds , string matter ; } |
Implementation of the destructor in Chaise.cpp
Code: C + + - Select
1 2 3 4 5 6 | # Include <iostream> ... chair :: ~ chair () { std :: cout << "Goodbye little chair." << std :: endl ; } |
In the body of the program call the destructor is automatically at the end of the block where the instance is declared.
Example:Code: C + +-Select
1 2 3 4 5 6 7 | int main () { chair machaise ; / / constructor call machaise . enleverPied () / / the poor that must hurt return 0 ; / / on the left hand block, so it destroys all variables -> Automatic call the destructor of the instance machaise chair. } |
Dynamic allocation
If you are reading the OpenGL tutorial is that you probably know the dynamic allocation in C:Code: C-Select
1 2 | struct chair * machaise ; machaise = malloc ( sizeof ( struct chair )); |
In C + + operator is generally used new like this:Code: C + +-Select
1 2 | Chair * machaise ; machaise = new Chair (); |
We note here the use of () after chair which clearly sought to build an object. Once the allocated memory, the class constructor is called automatically. For Destruction, delete replaces free you know:Code: C + +-Select
1 2 3 4 | Chair * machaise ; machaise = new Chair (); machaise -> enleverPied (); delete machaise ; |
UML representation
I'm not going to do a course in modeling UML but just show you a graphical way to represent a class. I will use this symbolism throughout the tutorial to briefly summarize the features of a class:
This gives for example take our beloved chair:
Implementation of the camera
We implement the trackball camera with the concept of class as we have just seen.
As we have seen above we have to manage three types of events:
In the main body of our program SDL (next section) we will have to send the events necessary for the operation of the camera.know how you put a camera manually with gluLookAt . Here is the method look TrackBallCamera our class who will call thegluLookAt for us. In the code for displaying the scene so that we will call this method. We will also add two other methods to configure the sensitivity of our camera:
All this therefore results in the following UML and C + +:
I took the opportunity to add all the attributes that we will use. A little explanation is therefore required:
The last two attributes are two mouse cursors that we use:
_hand1 in normal _hand2 when the left mouse button is pressed.
In the constructor we simply initialize all attributes to known initial values. Do not leave anything that can be used without having been initialized. portion the less obvious may be the creation of two cursors. To make things easier I relegated all the work in a function added to sdlglutils : cursorFromXPM (included in the final archive).Code: C + +-Select
This method is the most important class and yet one of the shortest. Remember the principle of the camera: when the mouse is moved, if the left mouse button is pressed, then the scene turns. Let's see how this translates into code:Code: C + +-Select
This method allows us to manage two things:
Code: C + + - Select
The last method is to use events management is the keyboard for pressing the HOME . We simply get back to the rotation of the scene to zero:Code: C + +-Select
All this is fine, we know how to change variables with the mouse and keyboard but it does nothing to move the camera in our scene. In fact we have so far not seen any OpenGL commands!
So it's time to get started with the method Look who will replace in your display function, the call to gluLookAt .
No no. Look the method calls itself gluLookAt but with parameters that depend on the position of the camera, this is why you do not have to call yourself.
Referring to the diagrams earlier in this chapter explaining the principle of the camera trackball, there are several things:
It is sufficient to translate all this in code:Code: C + +-Select
And this really was a no brainer.
The last thing we need to do in the code itself is the camera's own destruction that has been dynamically allocated.
The only things are dynamically allocated cursors that we must destroy when the camera is destroyed:Code: C + +-Select
As we have seen above we have to manage three types of events:
- pressing the left button of the mouse we will activate the mouse movement if this button is pressed;
- the movement of the mouse to change the orientation of the scene;
- pressing the HOME key to return the orientation of the scene to its original value.
In the main body of our program SDL (next section) we will have to send the events necessary for the operation of the camera.know how you put a camera manually with gluLookAt . Here is the method look TrackBallCamera our class who will call thegluLookAt for us. In the code for displaying the scene so that we will call this method. We will also add two other methods to configure the sensitivity of our camera:
- setMotionSensivity: to determine the speed of rotation of the scene according to the movement of the pixel mouse;
- setScrollSensivity: to determine how much zoom in / out when using the mouse wheel.
All this therefore results in the following UML and C + +:
Simplified UML | Statement C + + | ||
---|---|---|---|
Code: C + + - Select
|
I took the opportunity to add all the attributes that we will use. A little explanation is therefore required:
- Double _motionSensivity : used to store the camera's sensitivity to movements of the mouse;
- Double _scrollSensivity : camera sensitivity to mouse scroll ('not' a shift);
- _hold bool : are we currently hold the left mouse button?
- Double _distance : distance between the camera and the center of the stage;
- Double _angleY : tilt angle of the scene (in green in the diagram above);
- Double _angleZ : horizontal rotation angle of the scene (ie around the vertical blue on the diagram).
The last two attributes are two mouse cursors that we use:
_hand1 in normal _hand2 when the left mouse button is pressed.
You will notice in the above passage that I class attributes underscore symbol "_". This allows the implementation of methods to identify more quickly temporary variables (or parameters) and attributes. You're obviously not forced to follow this rule.
Builder
In the constructor we simply initialize all attributes to known initial values. Do not leave anything that can be used without having been initialized. portion the less obvious may be the creation of two cursors. To make things easier I relegated all the work in a function added to sdlglutils : cursorFromXPM (included in the final archive).Code: C + +-Select
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | TrackBallCamera :: TrackBallCamera () { const char * hand1 [] = { / * width height num_colors chars_per_pixel * / "16 16 3 1" , / * colors * / "X c # 000000" , ". c # ffffff" , " c None " , / * pixels * / "XX" , "XX X.. XXX" , "X.. XX .. X.. X" , "X. X. .. XX. XX" , "X . X.. X.. XX.X " , "X.. X.. X.. X.. X" , "XX X. ...... X.. X" , "X. XX. ......... X " , "X. .. X. ........ X" , "X ........... X" , "X .......... X " , "X ......... X" , "X ........ X" , "X ..... . X " , "X ..... X" , "X X. ....." , "0,0" }; const char * hand2 [] = { / * width height num_colors chars_per_pixel * / "16 16 3 1" , / * colors * / "X c # 000000" , ". c # ffffff" , "c None" , / * pixels * / "" , "" , "" , "" , "XX XX XX" , "X. X.. X.. XX" , "X ....... XX" , "X. ....... X " , "XX ......... X" , "X .......... X" , "X ....... ... X " , "X ......... X" , "X ........ X" , "X ...... X" , "X X ..... " , "X. ..... X" , "0.0" }; _hand1 = cursorFromXPM ( hand1 ) / / create the normal cursor _hand2 = cursorFromXPM ( hand2 ) / / creation of cursor used when the button is pressed SDL_SetCursor ( _hand1 ) / / cursor activation normal _hold = false ; / / initially we assume that the button is not held _angleY = 0 ; _angleZ = 0 ; _distance = 2 ; / / initial distance from the camera to the center stage _motionSensivity = 0.3 ; _scrollSensivity = 1 ; } |
As you can see, the constructor function uses SDL SDL_SetCursor, which should not be performed before creating your SDL window. Thus the camera will be created after initialization of the application.
On Mouse Motion
This method is the most important class and yet one of the shortest. Remember the principle of the camera: when the mouse is moved, if the left mouse button is pressed, then the scene turns. Let's see how this translates into code:Code: C + +-Select
1 2 3 4 5 6 7 8 9 10 11 12 13 | void TrackBallCamera :: OnMouseMotion ( const SDL_MouseMotionEvent & event ) { if ( _hold ) / / if we keep the left mouse button { _angleZ + = event . xrel * _motionSensivity ; / / X movement of the mouse -> change the horizontal rotation _angleY + = event . yrel * _motionSensivity ; / / Y movement of the mouse -> change the vertical rotation / / to avoid problems, we limit the vertical rotation angles between -90 ° and 90 ° if ( _angleY > 90 ) _angleY = 90 ; else if ( _angleY < - 90 ) _angleY = - 90 ; } } |
On Mouse Button
This method allows us to manage two things:
- support and releasing the left mouse button;
- the movement of the mouse wheel.
When you move the wheel, two successive events are generated: SDL_MOUSEBUTTONDOWN and SDL_MOUSEBUTTONUP .So we will use that first.
Code: C + + - Select
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | for the left button { if (( _hold ) && ( event . kind == SDL_MOUSEBUTTONUP )) / / release when we were down { _hold = false ; / / the movement of the mouse will move the scene SDL_SetCursor ( _hand1 ) / / we put the normal cursor } else if (( ! _hold ) && ( event . kind == SDL_MOUSEBUTTONDOWN )) / / support when we were released { _hold = true ; / / the movement of the mouse will move scene SDL_SetCursor ( _hand2 ) / / puts the cursor on special } } else if (( event . button == SDL_BUTTON_WHEELUP ) && ( event . kind == SDL_MOUSEBUTTONDOWN )) / / shot wheel up { _distance - = _scrollSensivity , / / zoom factor, so the camera closer to the center if ( _distance < 0.1 ) / / minimum distance, change if needed (with attribute Wheel down { _distance + = _scrollSensivity , / / so it zooms away from the camera } } |
OnKeyboard
The last method is to use events management is the keyboard for pressing the HOME . We simply get back to the rotation of the scene to zero:Code: C + +-Select
1 2 3 4 5 6 7 8 | HOME button { _angleY = 0 ; / / reset angles _angleZ = 0 ; } } |
Look
All this is fine, we know how to change variables with the mouse and keyboard but it does nothing to move the camera in our scene. In fact we have so far not seen any OpenGL commands!
So it's time to get started with the method Look who will replace in your display function, the call to gluLookAt .
Replace gluLookAt ? Because there is something better that thou hast hidden! ?
No no. Look the method calls itself gluLookAt but with parameters that depend on the position of the camera, this is why you do not have to call yourself.
Referring to the diagrams earlier in this chapter explaining the principle of the camera trackball, there are several things:
- she looks at the center of the stage;
- this is not the camera, but the stage which is rotated around Y and Z.
It is sufficient to translate all this in code:Code: C + +-Select
1 2 3 4 5 6 7 8 | void TrackBallCamera :: look () { gluLookAt ( _distance , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 ), / / the camera looks at the center (0,0,0) and is on the X axis at a distance from the center so (_distance, 0.0) glRotated ( _angleY , 0 , 1 , 0 ); / / scene is rotated about the axis Y glRotated ( _angleZ , 0 , 0 , 1 ); / / the stage is rotated about the axis Z } |
And this really was a no brainer.
The last thing we need to do in the code itself is the camera's own destruction that has been dynamically allocated.
Destructive
The only things are dynamically allocated cursors that we must destroy when the camera is destroyed:Code: C + +-Select
1 2 3 4 5 6 | TrackBallCamera :: ~ TrackBallCamera () { SDL_FreeCursor ( _hand1 ) / / destroy the normal cursor SDL_FreeCursor ( _hand2 ) / / destroy the special cursor SDL_SetCursor ( NULL ) / / it returns the default cursor. } |
Test scene
The class code TrackBallCamera is complete and needs nothing more. However a camera object will not receive any single events, we must give him. We will see a small scene with simple test how to use the camera that we just created. To original and not inspired by Google Earth, we will create a sphere with the texture of the earth. Ahem! By global variables we will use:Code: C + +-Select
1 2 | GLuint earth ; / / the identifier of the texture of the Earth TrackBallCamera * camera ; / / a pointer to our camera |
Why not a camera directly?
Remember, the manufacturer calls functions that require SDL SDL window that already exists. So do not think the camera is built from the start of the program (which would be the case here if we did not use pointer). So we create dynamically after the window:Code: C + +-Select
1 2 3 4 5 6 7 | atexit ( stop ) / / stop () will be called when |
As the camera was dynamically created is up to us to destroy itself at the end of the program. This is done in the stop function, when called will exit (0); into the body of the program:Code: C + +-Select
1 2 3 4 5 | void stop () { delete camera ; / / destroy the camera dynamically allocated SDL_Quit (); } |
As I have said above, the camera does not receive keyboard events / mouse if you do not give it. That is why our party event management must give the camera the events that are not used:Code: C + +-Select
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | used the P key and the ESC key, the rest is given to the camera camera -> OnKeyboard ( event . key ); } break ; box SDL_MOUSEMOTION: / / mouse is moved, it affects only the events buttons (up or down) are given to the camera break ; } } |
The last thing left to do is draw the scene here is very basic.
We no longer call gluLookAt ourselves but the method Look object of our camera.
Code: C + + - Select
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void DrawGL () { glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glMatrixMode ( GL_MODELVIEW ); glLoadIdentity ( ); Camera -> look (); GLUquadric* params = gluNewQuadric(); gluQuadricTexture(params,GL_TRUE); glBindTexture(GL_TEXTURE_2D,earth); gluSphere(params,1,20,20); gluDeleteQuadric(params); glFlush (); SDL_GL_SwapBuffers (); } |
No comments:
Post a Comment