Controlling the Camera
Default Camera Control System
By default, Panda3D runs a task that allows you to move the camera using the mouse.
To enable it, use the following command:
window->setup_trackball();
The keys to navigate are:
Mouse Button |
Action |
---|---|
Left Button |
Pan left and right. |
Right Button |
Move forwards and backwards. |
Middle Button |
Rotate around the origin of the application. |
Right and Middle Buttons |
Roll the point of view around the view axis. |
Go ahead and try this camera control system. The problem with it is that it is sometimes awkward. It is not always easy to get the camera pointed in the direction we want.
Tasks
Update the Code
Instead, we are going to write a task that controls the camera’s position explicitly. A task is nothing but a procedure that gets called every frame. Update your code as follows:
1#include "pandaFramework.h"
2#include "pandaSystem.h"
3
4#include "genericAsyncTask.h"
5#include "asyncTaskManager.h"
6
7// The global task manager
8PT(AsyncTaskManager) taskMgr = AsyncTaskManager::get_global_ptr();
9// The global clock
10PT(ClockObject) globalClock = ClockObject::get_global_clock();
11// Here's what we'll store the camera in.
12NodePath camera;
13
14// This is our task - a global or static function that has to return DoneStatus.
15// The task object is passed as argument, plus a void* pointer, containing custom data.
16// For more advanced usage, we can subclass AsyncTask and override the do_task method.
17AsyncTask::DoneStatus spinCameraTask(GenericAsyncTask *task, void *data) {
18 // Calculate the new position and orientation (inefficient - change me!)
19 double time = globalClock->get_real_time();
20 double angledegrees = time * 6.0;
21 double angleradians = angledegrees * (3.14 / 180.0);
22 camera.set_pos(20*sin(angleradians),-20.0*cos(angleradians),3);
23 camera.set_hpr(angledegrees, 0, 0);
24
25 // Tell the task manager to continue this task the next frame.
26 return AsyncTask::DS_cont;
27}
28
29int main(int argc, char *argv[]) {
30 // Load the window and set its title.
31 PandaFramework framework;
32 framework.open_framework(argc, argv);
33 framework.set_window_title("My Panda3D Window");
34 WindowFramework *window = framework.open_window();
35 // Get the camera and store it in a variable.
36 camera = window->get_camera_group();
37
38 // Load the environment model.
39 NodePath scene = window->load_model(framework.get_models(), "models/environment");
40 // Reparent the model to render.
41 scene.reparent_to(window->get_render());
42 // Apply scale and position transforms to the model.
43 scene.set_scale(0.25, 0.25, 0.25);
44 scene.set_pos(-8, 42, 0);
45
46 // Add our task.
47 // If we specify custom data instead of NULL, it will be passed as the second argument
48 // to the task function.
49 taskMgr->add(new GenericAsyncTask("Spins the camera", &spinCameraTask, nullptr));
50
51 // Run the engine.
52 framework.main_loop();
53 // Shut down the engine when done.
54 framework.close_framework();
55 return 0;
56}
The procedure taskMgr->add()
tells Panda3D’s
task manager to call the procedure spinCameraTask()
every frame. This is
a procedure that we have written to control the camera. As long as the
procedure spinCameraTask()
returns the constant AsyncTask.DS_cont
,
the task manager will continue to call it every frame.
The object passed to taskMgr->add()
is an
AsyncTask
object. We can use GenericAsyncTask
to wrap a global
function or static method around a task. We can also pass an additional
void*
parameter that we can cast into a pointer of any data type we like,
which is passed as argument to the task function. A GenericAsyncTask function
must look like the following:
AsyncTask::DoneStatus your_task(GenericAsyncTask *task, void *data) {
// Do your stuff here.
// Tell the task manager to continue this task the next frame.
// You can also pass DS_done if this task should not be run again.
return AsyncTask::DS_cont;
}
For more advanced usage, you can subclass AsyncTask and override the
do_task
method to make it do what you want.
In our code, the procedure spinCameraTask()
calculates the desired position
of the camera based on how much time has elapsed. The camera rotates 6 degrees
every second. The first two lines compute the desired orientation of the camera;
first in degrees, and then in radians. The set_pos()
call
actually sets the position of the camera. (Remember that Y is horizontal and Z
is vertical, so the position is changed by animating X and Y while Z is left
fixed at 3 units above ground level.) The set_hpr()
call
actually sets the orientation.
Run the Program
The camera should no longer be underground; and furthermore, it should now be rotating about the clearing: