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