]> icculus.org git repositories - taylor/freespace2.git/blob - src/pofview/pofviewcanvas.cpp
pofview: it works
[taylor/freespace2.git] / src / pofview / pofviewcanvas.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 #include "pofview.h"
10
11 #include "pstypes.h"
12 #include "2d.h"
13 #include "3d.h"
14 #include "bmpman.h"
15 #include "systemvars.h"
16 #include "cfile.h"
17 #include "timer.h"
18 #include "key.h"
19 #include "mouse.h"
20
21 #include "floating.h"
22 #include "lighting.h"
23
24
25 float flFrametime;              // used by game lib; DO NOT REMOVE!!
26
27 extern bool in_dialog;
28
29
30 // Stuff for showing ship thrusters.
31 typedef struct thrust_anim {
32         int     num_frames;
33         int     first_frame;
34         float time;                             // in seconds
35 } thrust_anim;
36
37 #define NUM_THRUST_ANIMS                                4
38
39 #define THRUST_ANIM_NORMAL                              0
40 #define THRUST_ANIM_AFTERBURNER                 1
41 #define THRUST_ANIM_GLOW_NORMAL                 2
42 #define THRUST_ANIM_GLOW_AFTERBURNER    3
43
44 static thrust_anim Thrust_anims[NUM_THRUST_ANIMS];
45
46 static char Thrust_anim_names[NUM_THRUST_ANIMS][MAX_FILENAME_LEN] = {
47         "thruster01",
48         "thruster01a",
49         "thrusterglow01",
50         "thrusterglow01a"
51 };
52
53
54 ///////////////////////////////////////////////////////////////////////////////
55
56 PofViewTimer::PofViewTimer(PofViewCanvas *canvas)
57         : wxTimer()
58 {
59         m_canvas = canvas;
60         m_thrust_timer = -1;
61 }
62
63 PofViewTimer::~PofViewTimer()
64 {
65 }
66
67 void PofViewTimer::Notify()
68 {
69         float frame_time;
70
71         if (m_thrust_timer == -1) {
72                 m_thrust_timer = timer_get_milliseconds();
73         }
74
75         int tmp_time = timer_get_milliseconds();
76
77         frame_time = ((float)(tmp_time-m_thrust_timer)) / 1000.0f;
78         flFrametime = frame_time;
79
80         m_thrust_timer = tmp_time;
81
82         m_canvas->DoThrusterFrame(frame_time);
83         m_canvas->MoveViewer(frame_time);
84         m_canvas->Render();
85 }
86
87 wxBEGIN_EVENT_TABLE(PofViewCanvas, wxGLCanvas)
88         EVT_SIZE(PofViewCanvas::OnSize)
89         EVT_PAINT(PofViewCanvas::OnPaint)
90         EVT_ERASE_BACKGROUND(PofViewCanvas::OnEraseBackground)
91         EVT_MOUSE_EVENTS(PofViewCanvas::OnMouse)
92 wxEND_EVENT_TABLE()
93
94 PofViewCanvas::PofViewCanvas(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name)
95         : wxGLCanvas(parent, id, NULL, pos, size, style | wxFULL_REPAINT_ON_RESIZE, name)
96 {
97         first_frame = true;
98         m_ViewerZoom = 0.8f;
99         m_ViewerPos = { { { 0.0f, 0.0f, -10.0f } } };
100         m_ViewerOrient = IDENTITY_MATRIX;
101         m_ObjectOrient = IDENTITY_MATRIX;
102
103         physics_init( &m_ViewerPhysics );
104         m_ViewerPhysics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED;
105
106         memset( &m_Viewer_ci, 0, sizeof(control_info) );
107
108         m_mouse_inited = 0;
109         m_mouse_dx = 0;
110         m_mouse_dy = 0;
111
112         m_timer = new PofViewTimer(this);
113         m_timer->Start(1000/30);
114
115         thrust_anim_inited = false;
116
117         model_thrust = 0.5f;
118         model_afterburner = 0;
119
120         shipp_thruster_bitmap = -1;
121         shipp_thruster_frame = 0.0f;
122
123         shipp_thruster_glow_bitmap = -1;
124         shipp_thruster_glow_frame = 0.0f;
125         shipp_thruster_glow_noise = 1.0f;
126 }
127
128 PofViewCanvas::~PofViewCanvas()
129 {
130         delete m_timer;
131 }
132
133
134 extern float Ambient_light;
135 extern void opengl1_tcache_frame();
136
137 static vector Global_light_world = { { { -0.208758f, -0.688253f, 0.694782f } } };
138
139
140 void PofViewCanvas::Render()
141 {
142         PofViewFrame *parent = (PofViewFrame *)GetParent();
143
144         int model_num = parent->GetModelnum();
145
146         if (model_num < 0) {
147                 return;
148         }
149
150         polymodel *pm = model_get(model_num);
151         wxASSERT( pm );
152
153         if (first_frame) {
154                 m_ViewerPos.xyz.z = -pm->rad * 1.5f;
155                 first_frame = false;
156         }
157
158         int w, h;
159
160         GetClientSize(&w, &h);
161
162         gr_reset_clip();
163         gr_set_clip(0, 0, w, h);
164
165         gr_set_clear_color(0, 0, 0);
166         gr_clear();
167         gr_set_color(127, 127, 127);
168
169         float saved_Ambient_light = Ambient_light;
170
171         light_reset();
172
173         if ( parent->LightingOn() ) {
174                 light_add_directional(&Global_light_world, 1.0f, 1.0f, 1.0f, 1.0f);
175         } else {
176                 Ambient_light = 0.0f;
177         }
178
179         g3_start_frame(1);
180
181         g3_set_view_matrix(&m_ViewerPos, &m_ViewerOrient, m_ViewerZoom);
182
183         uint render_flags = 0;
184
185         if ( !parent->UseLighting() ) {
186                 render_flags |= MR_NO_LIGHTING;
187         }
188
189         if ( parent->ShowOutline() ) {
190                 render_flags |= MR_SHOW_OUTLINE;
191                 model_set_outline_color(0,0,0);
192         }
193
194         if ( parent->ShowShields() ){
195                 render_flags |= MR_SHOW_SHIELDS;
196         }
197
198         if ( parent->ShowInvisible() )  {
199                 render_flags |= MR_SHOW_INVISIBLE_FACES;
200         }
201 /*
202         if ( parent->ShowOverwrite() ) {
203                 Tmap_show_layers = 1;
204         } else {
205                 Tmap_show_layers = 0;
206         }
207 */
208         if ( parent->ShowPivots() ) {
209                 render_flags |= MR_SHOW_PIVOTS;
210         }
211
212         if ( parent->ShowPaths() ) {
213                 render_flags |= MR_SHOW_PATHS;
214         }
215
216         if ( parent->ShowRadius() ) {
217                 render_flags |= MR_SHOW_RADIUS;
218         }
219
220         if ( !parent->UseSmoothing() ) {
221                 render_flags |= MR_NO_SMOOTHING;
222         }
223
224         if ( !parent->UseTexturing() ) {
225                 render_flags |= MR_NO_TEXTURING;
226         }
227
228         if ( parent->GetDetailLevel() > 0 ) {
229                 render_flags |= MR_LOCK_DETAIL;
230                 model_set_detail_level(parent->GetDetailLevel()-1);
231         }
232
233         if ( parent->ShowBayPaths() ) {
234                 render_flags |= MR_BAY_PATHS;
235         }
236
237         if ( parent->UseAutocenter() ) {
238                 render_flags |= MR_AUTOCENTER;
239         }
240
241         vector temp_pos = { { { 0.0f, 0.0f, 0.0f } } };
242
243         model_clear_instance( model_num );
244
245         model_show_damaged(model_num, parent->ShowDamaged() );
246
247         if ( model_afterburner ){
248                 model_set_thrust( model_num, 1.0f, shipp_thruster_bitmap, shipp_thruster_glow_bitmap, shipp_thruster_glow_noise );
249         } else {
250                 model_set_thrust( model_num, model_thrust, shipp_thruster_bitmap, shipp_thruster_glow_bitmap, shipp_thruster_glow_noise );
251         }
252
253         if ( parent->ShowThrusters() ) {
254                 render_flags |= MR_SHOW_THRUSTERS;
255         }
256
257         static int whee = -1;
258         if(whee == -1){
259                 whee = bm_load("helvig.pcx");
260         }
261         if(whee != -1){
262                 model_set_insignia_bitmap(whee);
263         }
264
265         if ( parent->GetDetailLevel() > 0 )     {
266                 model_render( model_num, &m_ObjectOrient, &temp_pos, render_flags );
267         } else {
268                 for (int i = 0; i < pm->num_debris_objects; i++) {
269                         vector tmp = { { { 0.0f, 0.0f, 0.0f } } };
270                         vector tmp1 = pm->submodel[pm->debris_objects[i]].offset;
271                         model_find_world_point(&tmp, &tmp1, model_num, -1,&m_ObjectOrient, &temp_pos );
272                         submodel_render( model_num, pm->debris_objects[i],&m_ObjectOrient, &tmp, render_flags );
273                 }
274         }
275
276         g3_end_frame();
277
278         this->SwapBuffers();
279
280         opengl1_tcache_frame();
281
282         Ambient_light = saved_Ambient_light;
283 }
284
285 // JAS - figure out which thruster bitmap will get rendered next
286 // time around.  ship_render needs to have shipp_thruster_bitmap set to
287 // a valid bitmap number, or -1 if we shouldn't render thrusters.
288 void PofViewCanvas::DoThrusterFrame(float frame_time)
289 {
290         float rate;
291         int framenum;
292         thrust_anim *the_anim;
293
294         if ( !thrust_anim_inited ) {
295                 InitThrusters();
296         }
297
298         if (model_afterburner) {
299                 the_anim = &Thrust_anims[THRUST_ANIM_AFTERBURNER];
300                 rate = 1.2f;            // go at 1.2x faster when afterburners on
301         } else {
302                 the_anim = &Thrust_anims[THRUST_ANIM_NORMAL];
303                 // If thrust at 0, go at half as fast, full thrust; full framerate
304                 // so set rate from 0.5 to 1.0, depending on thrust from 0 to 1
305                 rate = 0.5f + model_thrust / 2.0f;
306         }
307
308         shipp_thruster_frame += frame_time * rate;
309
310         // Sanity checks
311         if ( shipp_thruster_frame < 0.0f )      shipp_thruster_frame = 0.0f;
312         if ( shipp_thruster_frame > 100.0f ) shipp_thruster_frame = 0.0f;
313
314         if ( shipp_thruster_frame > the_anim->time )    {
315                 shipp_thruster_frame -= the_anim->time;
316         }
317         framenum = fl2i( (shipp_thruster_frame*the_anim->num_frames) / the_anim->time );
318         if ( framenum < 0 ) framenum = 0;
319         if ( framenum >= the_anim->num_frames ) framenum = the_anim->num_frames-1;
320
321         // Get the bitmap for this frame
322         shipp_thruster_bitmap = the_anim->first_frame + framenum;
323
324 //      mprintf(( "TF: %.2f\n", shipp_thruster_frame ));
325
326
327         // Do for glows
328
329         if (model_afterburner) {
330                 the_anim = &Thrust_anims[THRUST_ANIM_GLOW_AFTERBURNER];
331                 rate = 1.2f;            // go at 1.2x faster when afterburners on
332         } else {
333                 the_anim = &Thrust_anims[THRUST_ANIM_GLOW_NORMAL];
334                 // If thrust at 0, go at half as fast, full thrust; full framerate
335                 // so set rate from 0.5 to 1.0, depending on thrust from 0 to 1
336                 rate = 0.5f + model_thrust / 2.0f;
337         }
338
339         shipp_thruster_glow_frame += frame_time * rate;
340
341         // Sanity checks
342         if ( shipp_thruster_glow_frame < 0.0f ) shipp_thruster_glow_frame = 0.0f;
343         if ( shipp_thruster_glow_frame > 100.0f ) shipp_thruster_glow_frame = 0.0f;
344
345         while (shipp_thruster_glow_frame > the_anim->time) {
346                 shipp_thruster_glow_frame -= the_anim->time;
347         }
348         framenum = fl2i( (shipp_thruster_glow_frame*the_anim->num_frames) / the_anim->time );
349         if ( framenum < 0 ) framenum = 0;
350         if ( framenum >= the_anim->num_frames ) framenum = the_anim->num_frames-1;
351
352         // Get the bitmap for this frame
353         shipp_thruster_glow_bitmap = the_anim->first_frame;
354         shipp_thruster_glow_noise = Noise[framenum];
355
356 //      mprintf(( "TF: %.2f\n", shipp_thruster_frame ));
357
358 }
359
360 void PofViewCanvas::MoveViewer(float frame_time)
361 {
362         int detail_lvl = -1;
363
364         if (in_dialog) {
365                 return;
366         }
367
368         if ( wxGetKeyState(wxKeyCode('1')) ) {
369                 detail_lvl = 1;
370         } else if ( wxGetKeyState(wxKeyCode('2')) ) {
371                 detail_lvl = 2;
372         } else if ( wxGetKeyState(wxKeyCode('3')) ) {
373                 detail_lvl = 3;
374         } else if ( wxGetKeyState(wxKeyCode('4')) ) {
375                 detail_lvl = 4;
376         } else if ( wxGetKeyState(wxKeyCode('5')) ) {
377                 detail_lvl = 5;
378         } else if ( wxGetKeyState(wxKeyCode('6')) ) {
379                 detail_lvl = 6;
380         }
381
382         if (detail_lvl >= 0) {
383                 ((PofViewFrame*)GetParent())->SetDetailLevel(detail_lvl);
384         }
385
386         if ( wxGetKeyState(wxKeyCode('-')) ) {
387                 // Scales the engines thrusters by this much
388                 model_thrust -= 0.1f;
389
390                 if (model_thrust < 0.0f) {
391                         model_thrust = 0.0f;
392                 }
393         } else if ( wxGetKeyState(wxKeyCode('=')) ) {
394                 // Scales the engines thrusters by this much
395                 model_thrust += 0.1f;
396
397                 if (model_thrust > 1.0f) {
398                         model_thrust = 1.0f;
399                 }
400         }
401
402         if ( wxGetKeyState(WXK_BACK) ) {
403                 model_afterburner = 1;
404         } else {
405                 model_afterburner = 0;
406         }
407
408         int model_num = ((PofViewFrame*)GetParent())->GetModelnum();
409
410         if (model_num < 0) {
411                 return;
412         }
413
414         polymodel *pm = model_get(model_num);
415         wxASSERT( pm );
416
417         control_info *ci = &m_Viewer_ci;
418         float kh = 0.0f;
419
420         float c_scale = pm->core_radius; //1.0f;
421
422 //      if (pm->core_radius < 200.0f) {
423 //              c_scale = 1.0f / 6.0f;
424 //      }
425
426         float temp = ci->heading;
427         float temp1 = ci->pitch;
428         memset( ci, 0, sizeof(control_info) );
429         ci->heading = temp;
430         ci->pitch = temp1;
431
432         if ( wxGetKeyState(WXK_NUMPAD6) ) {
433                 kh = frame_time;
434         } else if ( wxGetKeyState(WXK_NUMPAD4) ) {
435                 kh = -frame_time;
436         } else {
437                 kh = 0.0f;
438         }
439
440         if (kh == 0.0f) {
441                 ci->heading = 0.0f;
442         } else if (kh > 0.0f) {
443                 if (ci->heading < 0.0f) {
444                         ci->heading = 0.0f;
445                 }
446         } else { // kh < 0
447                 if (ci->heading > 0.0f) {
448                         ci->heading = 0.0f;
449                 }
450         }
451
452         ci->heading += kh;
453
454         if ( wxGetKeyState(WXK_NUMPAD8) ) {
455                 kh = frame_time;
456         } else if ( wxGetKeyState(WXK_NUMPAD2) ) {
457                 kh = -frame_time;
458         } else {
459                 kh = 0.0f;
460         }
461
462         if (kh == 0.0f){
463                 ci->pitch = 0.0f;
464         } else if (kh > 0.0f) {
465                 if (ci->pitch < 0.0f){
466                         ci->pitch = 0.0f;
467                 }
468         } else { // kh < 0
469                 if (ci->pitch > 0.0f){
470                         ci->pitch = 0.0f;
471                 }
472         }
473
474         ci->pitch += kh;
475
476         if ( wxGetKeyState(WXK_NUMPAD7) ) {
477                 ci->bank = frame_time / 8.0f;
478         } else if ( wxGetKeyState(WXK_NUMPAD9) ) {
479                 ci->bank = -frame_time / 8.0f;
480         }
481
482         if ( wxGetKeyState(wxKeyCode('A')) ) {
483                 ci->forward = frame_time * c_scale;
484         } else if ( wxGetKeyState(wxKeyCode('Z')) ) {
485                 ci->forward = -frame_time * c_scale;
486         }
487
488         if ( wxGetKeyState(WXK_NUMPAD3) ) {
489                 ci->sideways = frame_time * c_scale;
490         } else if ( wxGetKeyState(WXK_NUMPAD1) ) {
491                 ci->sideways = -frame_time * c_scale;
492         }
493
494         if ( wxGetKeyState(WXK_NUMPAD_SUBTRACT) ) {
495                 ci->vertical = frame_time * c_scale;
496         } else if ( wxGetKeyState(WXK_NUMPAD_ADD) ) {
497                 ci->vertical = -frame_time * c_scale;
498         }
499
500         physics_read_flying_controls( &m_ViewerOrient, &m_ViewerPhysics, &m_Viewer_ci, frame_time );
501
502         physics_sim(&m_ViewerPos, &m_ViewerOrient, &m_ViewerPhysics, frame_time * 6.0f );
503 }
504
505 void PofViewCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) )
506 {
507         wxPaintDC dc(this);
508
509         Render();
510 }
511
512 void PofViewCanvas::OnSize(wxSizeEvent& WXUNUSED(event))
513 {
514         if ( !this->IsShownOnScreen() ) {
515                 return;
516         }
517
518         int x = 640, y = 480;
519
520         this->GetClientSize(&x, &y);
521
522         gr_set_viewport(x, y);
523 }
524
525 void PofViewCanvas::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
526 {
527         // Do nothing, to avoid flashing on MSW
528 }
529
530 void PofViewCanvas::OnMouse(wxMouseEvent& event)
531 {
532         if ( event.Dragging() ) {
533                 if (m_mouse_inited) {
534                         m_mouse_dx = event.GetX() - m_mouse_x;
535                         m_mouse_dy = event.GetY() - m_mouse_y;
536                 } else {
537                         m_mouse_inited = 1;
538                 }
539
540                 m_mouse_x = event.GetX();
541                 m_mouse_y = event.GetY();
542
543                 matrix tempm, mousem;
544
545                 if ( m_mouse_dx || m_mouse_dy ) {
546                         vm_trackball( -m_mouse_dx, m_mouse_dy, &mousem );
547                         vm_matrix_x_matrix(&tempm, &m_ObjectOrient, &mousem);
548                         m_ObjectOrient = tempm;
549
550                         m_mouse_dx = m_mouse_dy = 0;
551                 }
552         } else {
553                 m_mouse_inited = 0;
554         }
555 }
556
557 // loads the animations for ship's afterburners
558 void PofViewCanvas::InitThrusters()
559 {
560         int                     fps, i;
561         thrust_anim     *ta;
562
563         if (thrust_anim_inited)
564                 return;
565
566         for ( i = 0; i < NUM_THRUST_ANIMS; i++ ) {
567                 ta = &Thrust_anims[i];
568
569                 // two anims
570                 if (i < THRUST_ANIM_GLOW_NORMAL) {
571                         ta->first_frame = bm_load_animation(Thrust_anim_names[i],  &ta->num_frames, &fps, 1);
572                         if ( ta->first_frame == -1 ) {
573                                 Error(LOCATION,"Error loading animation file: %s\n",Thrust_anim_names[i]);
574                                 return;
575                         }
576                         SDL_assert(fps != 0);
577                         ta->time = i2fl(ta->num_frames)/fps;
578                 }
579                 // two glow bitmaps
580                 else {
581                         ta->num_frames = NOISE_NUM_FRAMES;
582                         fps = 15;
583                         ta->first_frame = bm_load( Thrust_anim_names[i] );
584                         if ( ta->first_frame == -1 ) {
585                                 Error(LOCATION,"Error loading bitmap file: %s\n",Thrust_anim_names[i]);
586                                 return;
587                         }
588                         SDL_assert(fps != 0);
589                         ta->time = i2fl(ta->num_frames)/fps;
590                 }
591         }
592
593         thrust_anim_inited = true;
594 }