1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
4
5 /**
6 * @file camera.c
7 *
8 * @brief Handles the camera.
9 */
10
11
12 #include "camera.h"
13
14 #include "naev.h"
15
16 #include "log.h"
17 #include "conf.h"
18 #include "space.h"
19 #include "gui.h"
20 #include "nebula.h"
21 #include "pause.h"
22 #include "background.h"
23 #include "player.h"
24
25
26 #define CAMERA_DIR (M_PI/2.)
27
28
29 static unsigned int camera_followpilot = 0; /**< Pilot to follow. */
30 /* Current camera position. */
31 static double camera_Z = 1.; /**< Current in-game zoom. */
32 static double camera_X = 0.; /**< X position of camera. */
33 static double camera_Y = 0.; /**< Y position of camera. */
34 /* Old is used to compensate pilot movement. */
35 static double old_X = 0.; /**< Old X positiion. */
36 static double old_Y = 0.; /**< Old Y position. */
37 /* Target is used why flying over with a target set. */
38 static double target_Z = 0.; /**< Target zoom. */
39 static double target_X = 0.; /**< Target X position. */
40 static double target_Y = 0.; /**< Target Y position. */
41 static int camera_fly = 0; /**< Camera is flying to target. */
42 static double camera_flyspeed = 0.; /**< Speed when flying. */
43
44
45 /*
46 * Prototypes.
47 */
48 static void cam_updateFly( double x, double y, double dt );
49 static void cam_updatePilot( Pilot *follow, double dt );
50 static void cam_updatePilotZoom( Pilot *follow, Pilot *target, double dt );
51 static void cam_updateManualZoom( double dt );
52
53
54 /**
55 * @brief Sets the camera zoom.
56 *
57 * This is the zoom used in game coordinates.
58 *
59 * @param zoom Zoom to set to.
60 */
cam_setZoom(double zoom)61 void cam_setZoom( double zoom )
62 {
63 camera_Z = CLAMP( conf.zoom_far, conf.zoom_near, zoom );
64 }
65
66
67 /**
68 * @brief Sets the camera zoom target.
69 *
70 * This should be used in conjunction with manual zoom.
71 *
72 * @param zoom Zoom to try to set camera to.
73 */
cam_setZoomTarget(double zoom)74 void cam_setZoomTarget( double zoom )
75 {
76 target_Z = CLAMP( conf.zoom_far, conf.zoom_near, zoom );
77 }
78
79
80 /**
81 * @brief Gets the camera zoom.
82 *
83 * @return The camera's zoom.
84 */
cam_getZoom(void)85 double cam_getZoom (void)
86 {
87 return camera_Z;
88 }
89
90
91 /**
92 * @brief Gets the camera zoom.
93 *
94 * @return The camera's zoom.
95 */
cam_getZoomTarget(void)96 double cam_getZoomTarget (void)
97 {
98 return target_Z;
99 }
100
101
102 /**
103 * @brief Gets the camera position.
104 *
105 * @param[out] x X position to get.
106 * @param[out] y Y position to get.
107 */
cam_getPos(double * x,double * y)108 void cam_getPos( double *x, double *y )
109 {
110 *x = camera_X;
111 *y = camera_Y;
112 }
113
114
115 /**
116 * @brief Sets the target to follow.
117 */
cam_setTargetPilot(unsigned int follow,int soft_over)118 void cam_setTargetPilot( unsigned int follow, int soft_over )
119 {
120 Pilot *p;
121 double dir, x, y;
122
123 /* Set the target. */
124 camera_followpilot = follow;
125 dir = CAMERA_DIR;
126
127 /* Set camera if necessary. */
128 if (!soft_over) {
129 if (follow != 0) {
130 p = pilot_get( follow );
131 if (p != NULL) {
132 dir = p->solid->dir;
133 x = p->solid->pos.x;
134 y = p->solid->pos.y;
135 camera_X = x;
136 camera_Y = y;
137 old_X = x;
138 old_Y = y;
139 }
140 }
141 camera_fly = 0;
142 }
143 else {
144 old_X = camera_X;
145 old_Y = camera_Y;
146 camera_fly = 1;
147 camera_flyspeed = (double) soft_over;
148 }
149 sound_updateListener( dir, camera_X, camera_Y, 0., 0. );
150 }
151
152
153 /**
154 * @brief Sets the camera target to a position.
155 */
cam_setTargetPos(double x,double y,int soft_over)156 void cam_setTargetPos( double x, double y, int soft_over )
157 {
158 /* Disable pilot override. */
159 camera_followpilot = 0;
160
161 /* Handle non soft. */
162 if (!soft_over) {
163 camera_X = x;
164 camera_Y = y;
165 old_X = x;
166 old_Y = y;
167 camera_fly = 0;
168 }
169 else {
170 target_X = x;
171 target_Y = y;
172 old_X = camera_X;
173 old_Y = camera_Y;
174 camera_fly = 1;
175 camera_flyspeed = (double) soft_over;
176 }
177 sound_updateListener( CAMERA_DIR, camera_X, camera_Y, 0., 0. );
178 }
179
180
181 /**
182 * @brief Returns the camera's current target.
183 *
184 * @return 0 if focused on position, else returns pilot ID.
185 */
cam_getTarget(void)186 int cam_getTarget( void )
187 {
188 return camera_followpilot;
189 }
190
191
192 /**
193 * @brief Updates the camera.
194 *
195 * @param dt Current delta tick.
196 */
cam_update(double dt)197 void cam_update( double dt )
198 {
199 Pilot *p;
200 double dx, dy;
201
202 /* Calculate differential. */
203 dx = old_X;
204 dy = old_Y;
205
206 /* Going to position. */
207 p = NULL;
208 if (camera_fly) {
209 if (camera_followpilot != 0) {
210 p = pilot_get( camera_followpilot );
211 if (p == NULL) {
212 camera_followpilot = 0;
213 camera_fly = 0;
214 }
215 else {
216 cam_updateFly( p->solid->pos.x, p->solid->pos.y, dt );
217 cam_updatePilotZoom( p, NULL, dt );
218 }
219 }
220 else
221 cam_updateFly( target_X, target_Y, dt );
222 }
223 else {
224 /* Track pilot. */
225 if (camera_followpilot != 0) {
226 p = pilot_get( camera_followpilot );
227 if (p == NULL)
228 camera_followpilot = 0;
229 else
230 cam_updatePilot( p, dt );
231 }
232 }
233
234 /* Update manual zoom. */
235 if (conf.zoom_manual)
236 cam_updateManualZoom( dt );
237
238 /* Set the sound. */
239 if ((p==NULL) || !conf.snd_pilotrel) {
240 dx = dt*(dx-camera_X);
241 dy = dt*(dy-camera_Y);
242 sound_updateListener( CAMERA_DIR, camera_X, camera_Y, dx, dy );
243 }
244 else {
245 sound_updateListener( p->solid->dir,
246 p->solid->pos.x, p->solid->pos.y,
247 p->solid->vel.x, p->solid->vel.y );
248 }
249 }
250
251
252 /**
253 * @brief Updates the camera flying to a position.
254 */
cam_updateFly(double x,double y,double dt)255 static void cam_updateFly( double x, double y, double dt )
256 {
257 double k, dx,dy, max;
258 double a, r;
259
260 max = camera_flyspeed*dt;
261 k = 25.*dt;
262 dx = (x - camera_X)*k;
263 dy = (y - camera_Y)*k;
264 if (pow2(dx)+pow2(dy) > pow2(max)) {
265 a = atan2( dy, dx );
266 r = max;
267 dx = r*cos(a);
268 dy = r*sin(a);
269 }
270 camera_X += dx;
271 camera_Y += dy;
272 background_moveStars( -dx, -dy );
273
274 /* Stop within 100 pixels. */
275 if (fabs((pow2(camera_X)+pow2(camera_Y)) - (pow2(x)+pow2(y))) < 100*100) {
276 old_X = camera_X;
277 old_Y = camera_Y;
278 camera_fly = 0;
279 }
280 }
281
282
283 /**
284 * @brief Updates a camera following a pilot.
285 */
cam_updatePilot(Pilot * follow,double dt)286 static void cam_updatePilot( Pilot *follow, double dt )
287 {
288 Pilot *target;
289 double diag2, a, r, dir, k;
290 double x,y, dx,dy, mx,my, targ_x,targ_y, bias_x,bias_y, vx,vy;
291
292 /* Get target. */
293 if (!pilot_isFlag(follow, PILOT_HYPERSPACE) && (follow->target != follow->id))
294 target = pilot_get( follow->target );
295 else
296 target = NULL;
297
298 /* Real diagonal might be a bit too harsh since it can cut out the ship,
299 * we'll just use the largest of the two. */
300 /*diag2 = pow2(SCREEN_W) + pow2(SCREEN_H);*/
301 /*diag2 = pow2( MIN(SCREEN_W, SCREEN_H) );*/
302 diag2 = 100*100;
303 x = follow->solid->pos.x;
304 y = follow->solid->pos.y;
305
306 /* Compensate player movement. */
307 mx = x - old_X;
308 my = y - old_Y;
309 camera_X += mx;
310 camera_Y += my;
311
312 /* Set old position. */
313 old_X = x;
314 old_Y = y;
315
316 /* No bias by default. */
317 bias_x = 0.;
318 bias_y = 0.;
319
320 /* Bias towards target. */
321 if (target != NULL) {
322 bias_x += target->solid->pos.x - x;
323 bias_y += target->solid->pos.y - y;
324 }
325
326 /* Bias towards velocity and facing. */
327 vx = follow->solid->vel.x*1.5;
328 vy = follow->solid->vel.y*1.5;
329 dir = angle_diff( atan2(vy,vx), follow->solid->dir);
330 dir = (M_PI - fabs(dir)) / M_PI; /* Normalize. */
331 vx *= dir;
332 vy *= dir;
333 bias_x += vx;
334 bias_y += vy;
335
336 /* Limit bias. */
337 if (pow2(bias_x)+pow2(bias_y) > diag2/2.) {
338 a = atan2( bias_y, bias_x );
339 r = sqrt(diag2)/2.;
340 bias_x = r*cos(a);
341 bias_y = r*sin(a);
342 }
343
344 /* Compose the target. */
345 targ_x = x + bias_x;
346 targ_y = y + bias_y;
347
348 /* Head towards target. */
349 k = 0.5*dt/dt_mod;
350 dx = (targ_x-camera_X)*k;
351 dy = (targ_y-camera_Y)*k;
352 background_moveStars( -(mx+dx), -(my+dy) );
353
354 /* Update camera. */
355 camera_X += dx;
356 camera_Y += dy;
357
358 /* DEBUG. */
359 #if 0
360 glColor4d( 1., 1., 1., 1. );
361 glBegin(GL_LINES);
362 gl_gameToScreenCoords( &x, &y, x, y );
363 glVertex2d( x, y );
364 gl_gameToScreenCoords( &x, &y, camera_X, camera_Y );
365 glVertex2d( x, y );
366 glEnd(); /* GL_LINES */
367 #endif
368
369 /* Update zoom. */
370 cam_updatePilotZoom( follow, target, dt );
371 }
372
373 /**
374 * @brief Updates the manual zoom target.
375 */
cam_updateManualZoom(double dt)376 static void cam_updateManualZoom( double dt )
377 {
378 double d;
379
380 if (player.p == NULL)
381 return;
382
383 /* Gradually zoom in/out. */
384 d = CLAMP( -conf.zoom_speed, conf.zoom_speed, target_Z - camera_Z);
385 d *= dt / dt_mod; /* Remove dt dependence. */
386 if (d < 0) /** Speed up if needed. */
387 d *= 2.;
388 camera_Z = CLAMP( conf.zoom_far, conf.zoom_near, camera_Z + d );
389 }
390
391
392 /**
393 * @brief Updates the camera zoom.
394 */
cam_updatePilotZoom(Pilot * follow,Pilot * target,double dt)395 static void cam_updatePilotZoom( Pilot *follow, Pilot *target, double dt )
396 {
397 double d, x,y, z,tz, dx, dy;
398 double zfar, znear;
399 double c;
400
401 /* Must have auto zoom enabled. */
402 if (conf.zoom_manual)
403 return;
404
405 /* Minimum depends on velocity normally.
406 *
407 * w*h = A, cte area constant
408 * w/h = K, cte proportion constant
409 * d^2 = A, cte geometric longitud
410 *
411 * A_v = A*(1+v/d) area of view is based on speed
412 * A_v / A = (1 + v/d)
413 *
414 * z = A / A_v = 1. / (1 + v/d)
415 */
416 d = sqrt(SCREEN_W*SCREEN_H);
417 znear = MIN( conf.zoom_near, 1. / (0.8 + VMOD(follow->solid->vel)/d) );
418
419 /* Maximum is limited by nebulae. */
420 if (cur_system->nebu_density > 0.) {
421 c = MIN( SCREEN_W, SCREEN_H ) / 2;
422 zfar = CLAMP( conf.zoom_far, conf.zoom_near, c / nebu_getSightRadius() );
423 }
424 else
425 zfar = conf.zoom_far;
426 znear = MAX( znear, zfar );
427
428 /*
429 * Set Zoom to pilot target.
430 */
431 z = cam_getZoom();
432 if (target != NULL) {
433 /* Get current relative target position. */
434 gui_getOffset( &x, &y );
435 x += target->solid->pos.x - follow->solid->pos.x;
436 y += target->solid->pos.y - follow->solid->pos.y;
437
438 /* Get distance ratio. */
439 dx = (SCREEN_W/2.) / (FABS(x) + 2*target->ship->gfx_space->sw);
440 dy = (SCREEN_H/2.) / (FABS(y) + 2*target->ship->gfx_space->sh);
441
442 /* Get zoom. */
443 tz = MIN( dx, dy );
444 }
445 else
446 tz = znear; /* Aim at in. */
447
448 /* Gradually zoom in/out. */
449 d = CLAMP(-conf.zoom_speed, conf.zoom_speed, tz - z);
450 d *= dt / dt_mod; /* Remove dt dependence. */
451 if (d < 0) /** Speed up if needed. */
452 d *= 2.;
453 camera_Z = CLAMP( zfar, znear, z + d);
454 }
455
456
457