]> icculus.org git repositories - taylor/freespace2.git/blob - src/movie/mveplayer.cpp
first pass at OpenGL ES 2 support
[taylor/freespace2.git] / src / movie / mveplayer.cpp
1 /*
2  * $Logfile: /Freespace2/code/movie/mveplayer.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * MVE movie playing routines
8  *
9  * $Log$
10  * Revision 1.7  2005/10/01 21:48:01  taylor
11  * various cleanups
12  * fix decoder to swap opcode 0xb since it screws up on PPC
13  * the previous opcode 0xc change was wrong since we had already determined that it messes up FS1 movies
14  *
15  * Revision 1.6  2005/08/12 08:47:24  taylor
16  * use new audiostr code rather than old windows/unix version
17  * update all OpenAL commands with new error checking macros
18  * fix play_position to properly account for real position, fixes the talking heads and message text cutting out early
19  * movies will now use better filtering when scaled
20  *
21  * Revision 1.5  2005/03/31 21:26:02  taylor
22  * s/alGetSourceiv/alGetSourcei/
23  *
24  * Revision 1.4  2005/03/31 00:06:20  taylor
25  * go back to more accurate timer and allow video scaling for movies
26  *
27  * Revision 1.3  2005/03/29 07:50:34  taylor
28  * Update to newest movie code with much better video support and audio support from
29  *   Pierre Willenbrock.  Movies are enabled always now (no longer a build option)
30  *   and but can be skipped with the "--nomovies" or "-n" cmdline options.
31  *
32  *
33  * $NoKeywords: $
34  */
35  
36 #include "SDL_opengl.h"
37
38 #include "pstypes.h"
39 #include "mvelib.h"
40 #include "movie.h"
41 #include "2d.h"
42 #include "key.h"
43 #include "osapi.h"
44 #include "timer.h"
45 #include "sound.h"
46 #include "bmpman.h"
47 #include "osregistry.h"
48 #include "oal.h"
49 #include "gropengl.h"
50 #include <vector>
51
52 static int mve_playing;
53
54
55 // timer variables
56 static int micro_frame_delay = 0;
57 static int timer_started = 0;
58 static int timer_created = 0;
59 static int timer_expire;
60 static Uint64 micro_timer_start = 0;
61 static Uint64 micro_timer_freq = 0;
62
63 // audio variables
64 #define MVE_AUDIO_BUFFERS 8  // total buffers to interact with stream
65
66 static std::vector<ALuint> mve_audio_bufl_free;
67 static ubyte *mve_audio_buf = NULL;
68 static size_t mve_audio_buf_size = 0;
69 static size_t mve_audio_buf_offset = 0;
70
71 static int mve_audio_playing = 0;
72 static int mve_audio_canplay = 0;
73 static int mve_audio_compressed = 0;
74 static int audiobuf_created = 0;
75
76 // struct for the audio stream information
77 struct mve_audio_t {
78         sound_channel *chan;
79         ALenum format;
80         int sample_rate;
81         int bytes_per_sec;
82         int channels;
83         int bitsize;
84         ALuint buffers[MVE_AUDIO_BUFFERS];
85 };
86
87 mve_audio_t *mas = NULL;  // mve_audio_stream
88
89
90
91 // video variables
92 int g_width, g_height;
93 void *g_vBuffers = NULL;
94 void *g_vBackBuf1, *g_vBackBuf2;
95 ushort *pixelbuf = NULL;
96 static ubyte *g_pCurMap=NULL;
97 static int g_nMapLength=0;
98 static int videobuf_created;
99 static int mve_scale_video = 0;
100 static int mve_viewport_w = 0;
101 static float mve_viewport_scale = 1.0f;
102 static int mve_needs_clear = 0;
103 static GLuint tex = 0;
104
105 struct g_coords_t {
106         int x, y;
107         float u, v;
108 };
109
110 static g_coords_t g_coords[4];
111
112
113 // the decoder
114 void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
115
116 /*************************
117  * general handlers
118  *************************/
119 void mve_end_movie()
120 {
121         mve_playing = 0;
122 }
123
124 /*************************
125  * timer handlers
126  *************************/
127
128 int mve_timer_create(ubyte *data)
129 {
130         micro_frame_delay = mve_get_int(data) * (int)mve_get_short(data+4);
131
132         micro_timer_start = SDL_GetPerformanceCounter();
133         micro_timer_freq = SDL_GetPerformanceFrequency();
134
135         if (micro_timer_freq < 1000) {
136                 micro_timer_freq = 1000;
137         }
138
139         timer_created = 1;
140
141         return 1;
142 }
143
144 static int mve_timer_get_microseconds()
145 {
146
147         Uint64 us = SDL_GetPerformanceCounter() - micro_timer_start;
148
149         if (micro_timer_freq >= 1000000) {
150                 us /= (micro_timer_freq / 1000000);
151         } else {
152                 us *= (1000000 / micro_timer_freq);
153         }
154
155         return (int)us;
156 }
157
158 static void mve_timer_start(void)
159 {
160         if (!timer_created)
161                 return;
162
163         timer_expire = mve_timer_get_microseconds();
164         timer_expire += micro_frame_delay;
165
166         timer_started = 1;
167 }
168
169 static int mve_do_timer_wait(void)
170 {
171         if (!timer_started)
172                 return 0;
173
174         int tv, ts;
175
176         tv = mve_timer_get_microseconds();
177
178         if (tv > timer_expire)
179                 goto end;
180
181         ts = timer_expire - tv;
182
183         SDL_Delay(ts / 1000);
184
185         // try and burn off excess in attempt to keep sync
186         if (ts % 1000) {
187                 for (int i = 0; i < 10; i++) {
188                         SDL_Delay(0);
189                 }
190         }
191 end:
192         timer_expire += micro_frame_delay;
193
194         return 0;
195 }
196
197 static void mve_timer_stop()
198 {
199         timer_expire = 0;
200         timer_started = 0;
201         timer_created = 0;
202
203         micro_frame_delay = 0;
204
205         micro_timer_start = 0;
206         micro_timer_freq = 0;
207 }
208
209 /*************************
210  * audio handlers
211  *************************/
212
213 // setup the audio information from the data stream
214 void mve_audio_createbuf(ubyte minor, ubyte *data)
215 {
216         if (audiobuf_created)
217                 return;
218
219         // if game sound disabled don't try and play movie audio
220         if ( !Sound_enabled ) {
221                 mve_audio_canplay = 0;
222                 audiobuf_created = 1;
223                 return;
224         }
225
226         int flags, desired_buffer, sample_rate;
227
228         mas = (mve_audio_t *) malloc ( sizeof(mve_audio_t) );
229
230         if (mas == NULL) {
231                 mve_audio_canplay = 0;
232                 audiobuf_created = 1;
233                 return;
234         }
235
236         memset(mas, 0, sizeof(mve_audio_t));
237
238         mas->format = AL_INVALID;
239
240         flags = mve_get_ushort(data + 2);
241         sample_rate = mve_get_ushort(data + 4);
242         desired_buffer = mve_get_int(data + 6);
243
244         if (desired_buffer > 0) {
245                 mve_audio_buf = (ubyte*) malloc (desired_buffer);
246
247                 if (mve_audio_buf == NULL) {
248                         mve_audio_canplay = 0;
249                         audiobuf_created = 1;
250                         return;
251                 }
252
253                 mve_audio_buf_size = desired_buffer;
254                 mve_audio_buf_offset = 0;
255         } else {
256                 mve_audio_canplay = 0;
257                 audiobuf_created = 1;
258                 return;
259         }
260
261         mas->channels = (flags & 0x0001) ? 2 : 1;
262         mas->bitsize = (flags & 0x0002) ? 16 : 8;
263
264         mas->sample_rate = sample_rate;
265
266         if (minor > 0) {
267                 mve_audio_compressed = flags & 0x0004 ? 1 : 0;
268         } else {
269                 mve_audio_compressed = 0;
270         }
271
272         if (mas->bitsize == 16) {
273                 if (mas->channels == 2) {
274                         mas->format = AL_FORMAT_STEREO16;
275                 } else if (mas->channels == 1) {
276                         mas->format = AL_FORMAT_MONO16;
277                 }
278         } else if (mas->bitsize == 8) {
279                 if (mas->channels == 2) {
280                         mas->format = AL_FORMAT_STEREO8;
281                 } else if (mas->channels == 1) {
282                         mas->format = AL_FORMAT_MONO8;
283                 }
284         }
285
286         // somethings wrong, bail now
287         if (mas->format == AL_INVALID) {
288                 mve_audio_canplay = 0;
289                 audiobuf_created = 1;
290                 return;
291         }
292
293         oal_check_for_errors("mve_audio_createbuf() begin");
294
295         for (int i = 0; i < MVE_AUDIO_BUFFERS; i++) {
296                 alGenBuffers(1, &mas->buffers[i]);
297
298                 if ( !mas->buffers[i] ) {
299                         mve_audio_canplay = 0;
300                         audiobuf_created = 1;
301                         return;
302                 }
303         }
304
305         mve_audio_bufl_free.assign(mas->buffers, mas->buffers+MVE_AUDIO_BUFFERS);
306
307         mas->chan = oal_get_free_channel(1.0f, -1, SND_PRIORITY_MUST_PLAY);
308
309         if (mas->chan == NULL) {
310                 mve_audio_canplay = 0;
311                 audiobuf_created = 1;
312                 return;
313         }
314
315         alSourcef(mas->chan->source_id, AL_GAIN, 1.0f);
316         alSource3f(mas->chan->source_id, AL_POSITION, 0.0f, 0.0f, 0.0f);
317         alSource3f(mas->chan->source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
318         alSource3f(mas->chan->source_id, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
319         alSourcef(mas->chan->source_id, AL_ROLLOFF_FACTOR, 0.0f);
320         alSourcei(mas->chan->source_id, AL_SOURCE_RELATIVE, AL_TRUE);
321         alSourcei(mas->chan->source_id, AL_LOOPING, AL_FALSE);
322
323         oal_check_for_errors("mve_audio_createbuf() end");
324
325         audiobuf_created = 1;
326         mve_audio_canplay = 1;
327 }
328
329 // play and stream the audio
330 void mve_audio_play()
331 {
332         if (mve_audio_canplay) {
333                 ALint queued = 0;
334                 ALint status = AL_INVALID;
335
336                 oal_check_for_errors("mve_audio_play() begin");
337
338                 alGetSourcei(mas->chan->source_id, AL_BUFFERS_QUEUED, &queued);
339                 alGetSourcei(mas->chan->source_id, AL_SOURCE_STATE, &status);
340
341                 if ( (status != AL_PLAYING) && (queued > 0) ) {
342                         alSourcePlay(mas->chan->source_id);
343                         mve_audio_playing = 1;
344                 }
345
346                 oal_check_for_errors("mve_audio_play() end");
347         }
348 }
349
350 // call this in shutdown to stop and close audio
351 static void mve_audio_stop()
352 {
353         if (!audiobuf_created)
354                 return;
355
356         oal_check_for_errors("mve_audio_stop() begin");
357
358         mve_audio_playing = 0;
359         mve_audio_canplay = 0;
360         mve_audio_compressed = 0;
361
362         audiobuf_created = 0;
363
364         mve_audio_bufl_free.clear();
365
366         if (mas) {
367                 if (mas->chan) {
368                         alSourceStop(mas->chan->source_id);
369
370                         // detach buffers from source so that we can delete them
371                         alSourcei(mas->chan->source_id, AL_BUFFER, 0);
372                 }
373
374                 for (int i = 0; i < MVE_AUDIO_BUFFERS; i++) {
375                         if ( alIsBuffer(mas->buffers[i]) ) {
376                                 alDeleteBuffers(1, &mas->buffers[i]);
377                         }
378                 }
379         }
380
381         if (mas != NULL) {
382                 free(mas);
383                 mas = NULL;
384         }
385
386         if (mve_audio_buf != NULL) {
387                 free(mve_audio_buf);
388                 mve_audio_buf = NULL;
389         }
390
391         mve_audio_buf_size = 0;
392         mve_audio_buf_offset = 0;
393
394         oal_check_for_errors("mve_audio_stop() end");
395 }
396
397 int mve_audio_data(ubyte major, ubyte *data)
398 {
399         static const int selected_chan = 1;
400         int chan;
401         int nsamp;
402         ALint processed = 0;
403         ALuint bid;
404
405         if (mve_audio_canplay) {
406                 chan = mve_get_ushort(data + 2);
407                 nsamp = mve_get_ushort(data + 4);
408
409                 if (chan & selected_chan) {
410                         oal_check_for_errors("mve_audio_data() begin");
411
412                         if ( (mve_audio_buf_offset+nsamp+4) <= mve_audio_buf_size ) {
413                                 if (major == 8) {
414                                         if (mve_audio_compressed) {
415                                                 /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
416                                                 nsamp += 4;
417
418                                                 mveaudio_uncompress(mve_audio_buf+mve_audio_buf_offset, data, -1);
419                                         } else {
420                                                 nsamp -= 8;
421                                                 data += 8;
422
423                                                 memcpy(mve_audio_buf+mve_audio_buf_offset, data, nsamp);
424                                         }
425                                 } else {
426                                         // silence
427                                         memset(mve_audio_buf+mve_audio_buf_offset, 0, nsamp);
428                                 }
429
430                                 mve_audio_buf_offset += nsamp;
431                         } else {
432                                 mprintf(("MVE audio_buf overrun!!\n"));
433                         }
434
435                         alGetSourcei(mas->chan->source_id, AL_BUFFERS_PROCESSED, &processed);
436
437                         while (processed) {
438                                 alSourceUnqueueBuffers(mas->chan->source_id, 1, &bid);
439
440                                 mve_audio_bufl_free.push_back(bid);
441                                 --processed;
442                         }
443
444                         if ( !mve_audio_bufl_free.empty() ) {
445                                 bid = mve_audio_bufl_free.back();
446
447                                 alBufferData(bid, mas->format, mve_audio_buf, mve_audio_buf_offset, mas->sample_rate);
448                                 alSourceQueueBuffers(mas->chan->source_id, 1, &bid);
449
450                                 mve_audio_buf_offset = 0;
451                                 mve_audio_bufl_free.pop_back();
452                         }
453
454                         if ( !mve_audio_playing ) {
455                                 mve_audio_play();
456                         }
457
458                         oal_check_for_errors("mve_audio_data() end");
459                 }
460         }
461
462         return 1;
463 }
464
465 /*************************
466  * video handlers
467  *************************/
468
469 int mve_video_createbuf(ubyte minor, ubyte *data)
470 {
471         if (videobuf_created)
472                 return 1;
473
474         if (gr_screen.mode != GR_OPENGL) {
475                 mprintf(("MVE-ERROR: Movie playback requires OpenGL renderer\n"));
476                 videobuf_created = 1;
477                 return 0;
478         }
479
480         if (gr_screen.use_sections) {
481                 mprintf(("MVE-ERROR: Bitmap sections not supported\n"));
482                 videobuf_created = 1;
483                 return 0;
484         }
485
486         int tex_w, tex_h;
487         short w, h;
488
489         w = mve_get_short(data);
490         h = mve_get_short(data+2);
491
492         g_width = w << 3;
493         g_height = h << 3;
494
495         // with Pierre's decoder16 fix in opcode 0xc, 8 should no longer be needed
496         g_vBackBuf1 = g_vBuffers = malloc(g_width * g_height * 4);
497
498         if (g_vBackBuf1 == NULL) {
499                 mprintf(("MVE-ERROR: Can't allocate video buffer\n"));
500                 videobuf_created = 1;
501                 return 0;
502         }
503
504         g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
505
506         memset(g_vBackBuf1, 0, g_width * g_height * 4);
507
508         // DDOI - Allocate RGB565 pixel buffer
509         pixelbuf = (ushort *)malloc (g_width * g_height * 2);
510
511         if (pixelbuf == NULL) {
512                 mprintf(("MVE-ERROR: Can't allocate memory for pixelbuf\n"));
513                 videobuf_created = 1;
514                 return 0;
515         }
516
517         memset(pixelbuf, 0, g_width * g_height * 2);
518
519         // set height and width to a power of 2
520         tex_w = next_pow2(g_width);
521         tex_h = next_pow2(g_height);
522
523         SDL_assert(tex_w > 0);
524         SDL_assert(tex_h > 0);
525
526         glGenTextures(1, &tex);
527
528         if (tex == 0) {
529                 mprintf(("MVE-ERROR: Can't create a GL texture\n"));
530                 videobuf_created = 1;
531                 return 0;
532         }
533
534         glBindTexture(GL_TEXTURE_2D, tex);
535
536         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
537         glDepthFunc(GL_ALWAYS);
538         glDepthMask(GL_FALSE);
539         glDisable(GL_DEPTH_TEST);
540         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
541         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
542
543         int x, y;
544
545         // centers
546         x = ((gr_screen.max_w - g_width) / 2);
547         y = ((gr_screen.max_h - g_height) / 2);
548
549         if ( os_config_read_uint("Video", "ScaleMovies", 1) ) {
550                 extern int GL_viewport_w;
551
552                 mve_scale_video = 1;
553
554                 mve_viewport_scale = GL_viewport_w / (float)g_width;
555                 mve_viewport_w = GL_viewport_w;
556
557                 x = 0;
558                 y = ((480 - g_height) / 2);
559         }
560
561         g_coords[0].x = x;
562         g_coords[0].y = y;
563         g_coords[0].u = 0.0f;
564         g_coords[0].v = 0.0f;
565
566         g_coords[1].x = x;
567         g_coords[1].y = y + g_height;
568         g_coords[1].u = 0.0f;
569         g_coords[1].v = i2fl(g_height) / i2fl(tex_h);
570
571         g_coords[2].x = x + g_width;
572         g_coords[2].y = y;
573         g_coords[2].u = i2fl(g_width) / i2fl(tex_w);
574         g_coords[2].v = 0.0f;
575
576         g_coords[3].x = x + g_width;
577         g_coords[3].y = y + g_height;
578         g_coords[3].u = i2fl(g_width) / i2fl(tex_w);
579         g_coords[3].v = i2fl(g_height) / i2fl(tex_h);
580
581         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
582         glEnableClientState(GL_VERTEX_ARRAY);
583
584         glTexCoordPointer(2, GL_FLOAT, sizeof(g_coords_t), &g_coords[0].u);
585         glVertexPointer(2, GL_INT, sizeof(g_coords_t), &g_coords[0].x);
586
587         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, tex_w, tex_h, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL);
588
589         videobuf_created = 1;
590
591         return 1;
592 }
593
594 static void mve_convert_and_draw()
595 {
596         ushort *pDests;
597         ushort *pSrcs;
598         ushort *pixels = (ushort *)g_vBackBuf1;
599         int x, y;
600
601         pSrcs = pixels;
602
603         pDests = pixelbuf;
604
605         for (y=0; y<g_height; y++) {
606                 for (x = 0; x < g_width; x++) {
607                         pDests[x] = (1<<15)|*pSrcs;
608
609                         pSrcs++;
610                 }
611                 pDests += g_width;
612         }
613 }
614
615 void mve_video_display()
616 {
617         static uint mve_video_skiptimer = 0;
618
619         fix t1 = timer_get_fixed_seconds();
620
621         // micro_frame_delay is divided by 10 to match mve_video_skiptimer overflow catch
622         if ( mve_video_skiptimer > (uint)(micro_frame_delay/10) ) {
623                 // we are running slow so subtract desired time from actual and skip this frame
624                 mve_video_skiptimer -= (micro_frame_delay/10);
625                 return;
626         } else {
627                 // zero out so we can get a new count
628                 mve_video_skiptimer = 0;
629         }
630
631         mve_convert_and_draw();
632
633         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_width, g_height, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixelbuf);
634
635         if (mve_scale_video) {
636                 glPushMatrix();
637                 glLoadIdentity();
638                 glScalef(mve_viewport_scale, mve_viewport_scale, 1.0f);
639         }
640
641         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
642
643         if (mve_scale_video) {
644                 glPopMatrix();
645         }
646
647         gr_flip();
648
649         if (mve_needs_clear) {
650                 gr_clear();
651                 mve_needs_clear = 0;
652         }
653
654         fix t2 = timer_get_fixed_seconds();
655
656         // only get a new count if we are definitely through with old count
657         if ( mve_video_skiptimer == 0 ) {
658                 // for a more accurate count convert the frame rate to a float and multiply
659                 // by one-hundred-thousand before converting to an uint.
660                 mve_video_skiptimer = (uint)(f2fl(t2-t1) * 100000);
661         }
662 }
663
664 void mve_video_codemap(ubyte *data, int len)
665 {
666         g_pCurMap = data;
667         g_nMapLength = len;
668 }
669
670 void mve_video_data(ubyte *data, int len)
671 {
672         ushort nFlags;
673         ubyte *temp;
674
675         nFlags = mve_get_ushort(data+12);
676
677         if (nFlags & 1) {
678                 temp = (ubyte *)g_vBackBuf1;
679                 g_vBackBuf1 = g_vBackBuf2;
680                 g_vBackBuf2 = temp;
681         }
682
683         decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
684 }
685
686 void mve_end_chunk()
687 {
688         g_pCurMap = NULL;
689 }
690
691 void mve_init(MVESTREAM *mve)
692 {
693         // reset to default values
694         mve_audio_playing = 0;
695         mve_audio_canplay = 0;
696         mve_audio_compressed = 0;
697         mve_audio_buf_offset = 0;
698         audiobuf_created = 0;
699
700         videobuf_created = 0;
701         mve_scale_video = 0;
702         mve_viewport_w = 0;
703         mve_viewport_scale = 1.0f;
704
705         mve_playing = 1;
706 }
707
708 void mve_play(MVESTREAM *mve)
709 {
710         int init_timer = 0, timer_error = 0;
711         int cont = 1;
712
713         if (!timer_started)
714                 mve_timer_start();
715
716         while (cont && mve_playing && !timer_error) {
717                 cont = mve_play_next_chunk(mve);
718
719                 if (micro_frame_delay && !init_timer) {
720                         mve_timer_start();
721                         init_timer = 1;
722                 }
723
724                 timer_error = mve_do_timer_wait();
725
726                 os_poll();
727
728                 if (key_inkey() == SDLK_ESCAPE) {
729                         mve_playing = 0;
730                 }
731
732                 // check if viewport size changed and adjust scaling accordingly
733                 extern int GL_viewport_w;
734
735                 if (mve_viewport_w != GL_viewport_w) {
736                         mve_viewport_w = GL_viewport_w;
737                         mve_needs_clear = 1;
738
739                         if (mve_scale_video) {
740                                 mve_viewport_scale = GL_viewport_w / (float)g_width;
741                         }
742                 }
743         }
744 }
745
746 void mve_shutdown()
747 {
748         mve_audio_stop();
749
750         mve_timer_stop();
751
752         if (pixelbuf != NULL) {
753                 free(pixelbuf);
754                 pixelbuf = NULL;
755         }
756
757         if (g_vBuffers != NULL) {
758                 free(g_vBuffers);
759                 g_vBuffers = NULL;
760         }
761
762         if (gr_screen.mode == GR_OPENGL) {
763                 if (tex > 0) {
764                         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
765                         glDisableClientState(GL_VERTEX_ARRAY);
766
767                         glBindTexture(GL_TEXTURE_2D, 0);
768                         glDeleteTextures(1, &tex);
769                         tex = 0;
770                 }
771
772                 if (mve_scale_video) {
773                         glMatrixMode(GL_MODELVIEW);
774                         glPopMatrix();
775                 }
776
777                 glEnable(GL_DEPTH_TEST);
778         }
779 }