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