1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     Gas.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 46365 $
14  *---------------------------------------------------------------------------*/
15 
16 /*
17  *------------------------------------------------------------
18  * Copyright(c) 2009-2010 by Digital Media Professionals Inc.
19  * All rights reserved.
20  *------------------------------------------------------------
21  * This source code is the confidential and proprietary
22  * of Digital Media Professionals Inc.
23  *------------------------------------------------------------
24  */
25 
26 #include <math.h>
27 #include "Gas.h"
28 #include "Util.h"
29 
30 #include <assert.h>
31 
32 extern GLuint pAccId;
33 extern GLuint pPostId;
34 extern struct gas_data gas;
35 
36 /*
37  * Local definition
38  */
39 #define SHADING_WIDTH       nn::gx::DISPLAY0_WIDTH
40 #define SHADING_HEIGHT      nn::gx::DISPLAY0_HEIGHT
41 // SHADING_BUFFER uses the display buffer created by demo::RenderSystem.
42 // #define SHADING_BUFFER      DISPLAY_BUFFER
43 
44 /* buffer id */
45 static struct tagBufID
46 {
47     GLuint  acc;        /* frame buffer for accumulation */
48     GLuint  shading;    /* frame buffer for shading */
49     GLuint  accColor;   /* Color buffer for accumulation */
50 } gasbuf;
51 
52 /* texture id */
53 GLuint gastex;
54 
55 /* particle pattern file names */
56 static char* particle_files[PARTICLE_PATTERNS] = {PARTICLE_FILES};
57 
58 /* vertex buffer id */
59 GLuint element_array_buffer_id;
60 
61 /*=======================================================*/
62 /* buffer initialization                                 */
63 /*=======================================================*/
GasInitialize(void)64 void GasInitialize(void)
65 {
66     /*
67      * Generate framebuffers and texture for gas rendering
68     */
69 
70     /* generation */
71     glGenFramebuffers(1, (GLuint*)&gasbuf.acc);
72     glGenTextures(1, (GLuint*)&gastex);
73     glGenRenderbuffers(1, (GLuint*)&gasbuf.accColor);
74 
75     /* set shading buffer
76      * shading buffer is DISPLAY_BUFFER */
77 //    gasbuf.shading = SHADING_BUFFER;
78 
79     /* initialize renderbuffer for color */
80     glBindRenderbuffer(GL_RENDERBUFFER, gasbuf.accColor);
81     glRenderbufferStorage(GL_RENDERBUFFER, GL_GAS_DMP, GAS_ACC_WIDTH, GAS_ACC_HEIGHT);
82 
83     /* Attach renderbuffer to framebuffer */
84     glBindFramebuffer(GL_FRAMEBUFFER, gasbuf.acc);
85     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gasbuf.accColor);
86 
87     /*
88      * Initialize accumulation pow2 texture area (copy destination of accumulation result or shading result)
89     */
90     glBindTexture(GL_TEXTURE_2D, gastex);
91     glTexImage2D(GL_TEXTURE_2D, 0, GL_GAS_DMP, GAS_TEX_WIDTH, GAS_TEX_HEIGHT, 0, GL_GAS_DMP, GL_UNSIGNED_SHORT, 0);
92     glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
93     glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
94 
95     /* element array buffer initialize */
96     GLushort index[4] = {0, 1, 2, 3};
97     glGenBuffers(1, &element_array_buffer_id);
98     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer_id);
99     glBufferData(GL_ELEMENT_ARRAY_BUFFER, 4 * sizeof(GLushort), &index[0], GL_STATIC_DRAW);
100 }
101 /*=======================================================*/
102 /* Setup of default gasesous object data structure       */
103 /*=======================================================*/
DefaultGasObject(struct gas_data * gas,float * gasColorTable)104 void DefaultGasObject(struct gas_data *gas, float *gasColorTable)
105 {
106     gas->_dela_z = 5000.f;
107     gas->_autoAcc = GL_TRUE;
108     gas->_densMax = 1.0f;
109     gas->_lightDirX = 0.0f;
110     gas->_lightDirY = 0.0f;
111 
112     gas->_LightXY[0] = 0.0f;
113     gas->_LightXY[1] = 0.0f;
114     gas->_LightXY[2] = 0.0f;
115     gas->_LightXY[3] = 0.0f;    /* unsused */
116 
117     gas->_LightZ[0] = 1.0f;
118     gas->_LightZ[1] = 0.0f;
119     gas->_LightZ[2] = 1.0f;
120     gas->_LightZ[3] = 1.0f;
121 
122     gas->shadingDensitySrc = GL_GAS_DEPTH_DENSITY_DMP;      /* GL_GAS_PLAIN_DENSITY_DMP/GL_GAS_DEPTH_DENSITY_DMP */
123     gas->colorLutInput = GL_GAS_DENSITY_DMP;
124 
125     float dxt = 1.0f / 127.0f;
126     float xt = 0;
127     for (int i = 0; i < 128; i++)
128     {
129         gas->fogTable[i]= 1.0f - exp(-4.0f * xt);
130         xt += dxt;
131     }
132 
133     for (int i = 0; i < 128; i++)
134     {
135         gas->fogTable[128 + i] = gas->fogTable[i + 1] - gas->fogTable[i];
136     }
137 
138     gas->fogTable[255] = 0;
139     glGenTextures(1, &gas->CollectionLUT_ID);
140     glGenTextures(1, &gas->FogLut_ID);
141 
142     glBindTexture(GL_TEXTURE_COLLECTION_DMP, gas->CollectionLUT_ID);
143     glBindTexture(GL_LUT_TEXTURE0_DMP, gas->FogLut_ID);
144     glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 256, 0, GL_LUMINANCEF_DMP, GL_FLOAT, gas->fogTable);
145 
146     glGenTextures(3,&gas->gasTransfert_ID[0]);
147 
148     SetGasColorTable(gas,gasColorTable);
149 
150     float u0 = 0.0f;
151     float v0 = 0.0f;
152 
153     float u1 = (GAS_ACC_WIDTH * 1.0f) / (GAS_TEX_WIDTH * 1.0f);
154     float v1 = (GAS_ACC_HEIGHT * 1.0f) / (GAS_TEX_HEIGHT * 1.0f);
155 
156     GLfloat tex_unit[8]= {u0, v0, u0, v1, u1, v1, u1, v0};
157 
158     GLfloat LX0, LY0, LX1, LY1;
159     LX0 = 0.0f;
160     LY0 = 0.0f;
161     LX1 = gas->_lightDirX;
162     LY1 = gas->_lightDirY;
163     GLfloat vertex_color[16] =
164     {
165         LX0, 0.0f, 0.0f, LY0,
166         LX0, 0.0f, 0.0f, LY1,
167         LX1, 0.0f, 0.0f, LY1,
168         LX1, 0.0f, 0.0f, LY0
169     };
170 
171     GLushort _quadIndex[6] = {0, 1, 2, 0, 2, 3};
172     GLfloat vertex_unit[16] =
173     {
174         -1.0f, -1.0f, 0.0f, 1.0f,
175         -1.0f,  1.0f, 0.0f, 1.0f,
176          1.0f,  1.0f, 0.0f, 1.0f,
177          1.0f, -1.0f, 0.0f, 1.0f
178     };
179 
180     glGenBuffers(1, &gas->quad_index_ID);
181     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gas->quad_index_ID);
182     glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6*sizeof(GLushort), &_quadIndex, GL_STATIC_DRAW);
183 
184     glGenBuffers(1, &gas->quad_vertBuf_ID);
185     glBindBuffer(GL_ARRAY_BUFFER,gas->quad_vertBuf_ID);
186     glBufferData(GL_ARRAY_BUFFER, 16*sizeof(GLfloat), &vertex_unit, GL_STATIC_DRAW);
187 
188     glGenBuffers(1, &gas->quad_texBuf_ID);
189     glBindBuffer(GL_ARRAY_BUFFER,gas->quad_texBuf_ID);
190     glBufferData(GL_ARRAY_BUFFER, 8*sizeof(GLfloat), &tex_unit, GL_STATIC_DRAW);
191 
192     glGenBuffers(1, &gas->quad_colBuf_ID);
193     glBindBuffer(GL_ARRAY_BUFFER,gas->quad_colBuf_ID);
194     glBufferData(GL_ARRAY_BUFFER, 16*sizeof(GLfloat), &vertex_color, GL_STATIC_DRAW);
195 
196     /*
197      * Load particle patterns
198     */
199     glGenTextures(PARTICLE_PATTERNS, &gas->pattern[0]);
200     for (int i = 0; i < PARTICLE_PATTERNS; i++)
201     {
202         bool alpha;
203         glBindTexture(GL_TEXTURE_2D, gas->pattern[i]);
204         glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
205         glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
206         loadTexture(particle_files[i], GL_TEXTURE_2D, 0, alpha);
207     }
208 }
209 /*=======================================================*/
210 /* Load Gas color lookup table                           */
211 /*=======================================================*/
SetGasColorTable(struct gas_data * gas,float * gasColorTable)212 void SetGasColorTable(struct gas_data *gas, float *gasColorTable)
213 {
214     glBindTexture(GL_TEXTURE_COLLECTION_DMP, gas->CollectionLUT_ID);
215     for(int i = 0; i < 8; i++)
216     {
217         gas->RR[i] = gasColorTable[3 * i + 0];
218         gas->GG[i] = gasColorTable[3 * i + 1];
219         gas->BB[i] = gasColorTable[3 * i + 2];
220 
221         gas->RR[8 + i] = gasColorTable[3 * (i + 1) + 0] - gasColorTable[3 * i + 0];
222         gas->GG[8 + i] = gasColorTable[3 * (i + 1) + 1] - gasColorTable[3 * i + 1];
223         gas->BB[8 + i] = gasColorTable[3 * (i + 1) + 2] - gasColorTable[3 * i + 2];
224     }
225     gas->RR[15] = 0.0f;
226     gas->GG[15] = 0.0f;
227     gas->BB[15] = 0.0f;
228 
229     glBindTexture(GL_LUT_TEXTURE1_DMP, gas->gasTransfert_ID[0]);
230     glTexImage1D(GL_LUT_TEXTURE1_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, gas->RR);
231 
232     glBindTexture(GL_LUT_TEXTURE2_DMP, gas->gasTransfert_ID[1]);
233     glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, gas->GG);
234 
235     glBindTexture(GL_LUT_TEXTURE3_DMP, gas->gasTransfert_ID[2]);
236     glTexImage1D(GL_LUT_TEXTURE3_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, gas->BB);
237 }
238 
239 /*=======================================================*/
240 /* accumulation pass                                     */
241 /*=======================================================*/
GasAccumulation()242 void GasAccumulation()
243 {
244     /*
245      * In accumulation pass, particles are accumulated into gas accumulation buffer as
246      * density information. This buffer is 'gasbuf.acc' in this sample.
247      * this buffer is exactly same size as DISPLAY_BUFFER (possible not to be
248      * pow2 size). So, for next pass, the accumulation result should be copy to pow2 size texture.
249     */
250 
251     /* Bind accumulation buffer as a rendering target */
252     glBindFramebuffer(GL_FRAMEBUFFER, gasbuf.acc);
253     /* Set viewport */
254     glViewport(0, 0, GAS_ACC_WIDTH, GAS_ACC_HEIGHT);
255     /* Clear the buffer content (only color!) */
256     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
257     glClear(GL_COLOR_BUFFER_BIT);
258     /* use gas accumulation related program */
259     glUseProgram(pAccId);
260 
261     /* misc state */
262     glDisable(GL_DEPTH_TEST);
263 
264     glDisable(GL_CULL_FACE);
265 
266     /* texture pattern binding */
267     /* assuming only 1 pattern  */
268     glActiveTexture(GL_TEXTURE0);
269     glBindTexture(GL_TEXTURE_2D, gas.pattern[0]);
270 
271     /* setup uniforms */
272     /* note that the program is shared with standard geometry to uniforms need to be reapply for each drawing call */
273     glUniform1i(glGetUniformLocation(pAccId, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D);
274     glUniform1i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].combineRgb"), GL_MODULATE);
275     glUniform1i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].combineAlpha"), GL_MODULATE);
276     glUniform3i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_ALPHA, GL_SRC_COLOR);
277     glUniform3i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA);
278     glUniform3i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].srcRgb"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR);
279     glUniform3i(glGetUniformLocation(pAccId, "dmp_TexEnv[0].srcAlpha"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR);
280 
281     /* Set the mode for the per fragment operation (gas accumulation mode) */
282     glUniform1i(glGetUniformLocation(pAccId, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GAS_ACC_DMP);
283 
284     /* Set this value to control the accuracy of z intersection of surface and gaseous objects */
285     glUniform1f(glGetUniformLocation(pAccId, "dmp_Gas.deltaZ"), gas._dela_z);
286 
287     glUniform1i(glGetUniformLocation(pAccId, "dmp_Fog.mode"), GL_FALSE);
288 
289     nn::math::Matrix44 m;
290     nn::math::MTX44Transpose(&m, &gas.pSys.m_partsys_center);
291     glUniformMatrix4fv(glGetUniformLocation(pAccId, "uCenter"), 1, GL_FALSE, static_cast<f32*>(m));
292 
293     nn::math::MTX44Transpose(&m, &gas.pSys.m_partsys_radius);
294     glUniformMatrix4fv(glGetUniformLocation(pAccId, "uRadii"), 1, GL_FALSE, static_cast<f32*>(m));
295 
296     nn::math::MTX44Transpose(&m, &gas.pSys.m_partsys_color);
297     glUniformMatrix4fv(glGetUniformLocation(pAccId, "dmp_PartSys.color"), 1, GL_FALSE, static_cast<f32*>(m));
298 
299     nn::math::MTX44Transpose(&m, &gas.pSys.m_partsys_aspect);
300     glUniformMatrix4fv(glGetUniformLocation(pAccId, "dmp_PartSys.aspect"), 1, GL_FALSE, static_cast<f32*>(m));
301 
302     GLfloat viewport_size[2] = {0.0f, 0.0f};
303     viewport_size[0] = 1.f / (GLfloat)GAS_ACC_WIDTH;
304     viewport_size[1] = 1.f / (GLfloat)GAS_ACC_HEIGHT;
305     glUniform2fv(glGetUniformLocation(pAccId, "dmp_PartSys.viewport"), 1, &viewport_size[0]);
306 
307     GLfloat distance_attenuation[3] = {0.0f, 0.0f, 0.0f};
308     distance_attenuation[0] = 0.0f;
309     distance_attenuation[1] = 0.0f;
310     distance_attenuation[2] = 1.0f / (float)(GAS_ACC_WIDTH * GAS_ACC_HEIGHT);
311 
312     glUniform3fv(glGetUniformLocation(pAccId, "dmp_PartSys.distanceAttenuation"), 1, &distance_attenuation[0]);
313 
314     glUniform1fv(glGetUniformLocation(pAccId, "dmp_PartSys.countMax"), 1, &gas.pSys.m_particleCountMax);
315     glUniform2fv(glGetUniformLocation(pAccId, "dmp_PartSys.pointSize"), 1, &gas.pSys.m_size_min_max[0]);
316     glUniform1fv(glGetUniformLocation(pAccId, "dmp_PartSys.speed"), 1, &gas.pSys.m_speed);
317 
318     glUniform4fv(glGetUniformLocation(pAccId, "dmp_PartSys.randomCore"), 1, &gas.pSys.m_prng[0]);
319     glUniform4fv(glGetUniformLocation(pAccId, "dmp_PartSys.randSeed"), 1, &gas.pSys.m_random_seed[0]);
320 
321     glUniform1fv(glGetUniformLocation(pAccId, "dmp_PartSys.time"), 1, &gas.pSys.simulationTime);
322 
323     glEnableVertexAttribArray(0);
324         glBindBuffer(GL_ARRAY_BUFFER, gas.pSys.m_pSysPositionID);
325         glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, 0);
326         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer_id);
327         glDrawElements(GL_GEOMETRY_PRIMITIVE_DMP, 4, GL_UNSIGNED_SHORT, 0);
328     glDisableVertexAttribArray(0);
329 
330     glFinish();
331 
332     /*
333      * Copy accumulation result to the texture buffer
334     */
335     glUniform1i(glGetUniformLocation(pAccId, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D);
336     glBindTexture(GL_TEXTURE_2D, gastex);
337     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GAS_ACC_WIDTH, GAS_ACC_HEIGHT);
338     glFinish();
339 
340     glBindTexture(GL_TEXTURE_2D, 0);
341 }
342 
343 /*=======================================================*/
344 /* shading pass                                          */
345 /*=======================================================*/
GasShading(void)346 void GasShading(void)
347 {
348     /*
349      * In shading pass, shaded gaseous image is blended to DISPLAY_BUFFER.
350      * Accumulated density information is used as a gas texture.
351     */
352 
353     /* Bind shading buffer and set viewport
354      * shading buffer is DISPLAY_BUFFER */
355 //    glBindFramebuffer(GL_FRAMEBUFFER, gasbuf.shading);
356 //    glViewport(0, 0, SHADING_WIDTH, SHADING_HEIGHT);
357 
358     /* use gas accumulation related program */
359     glUseProgram(pPostId);
360 
361     /* Bind gas texture (accumulation result) */
362     glActiveTexture(GL_TEXTURE0);
363     glBindTexture(GL_TEXTURE_2D, gastex);
364 
365     /* Setup blending unit #0
366      * r component of primary color has the influence of LIGHT_X and LIGHT_Y
367      * this output is transfered to FOG unit when FOG_MODE is set to GAS_DMP */
368     glUniform1i(glGetUniformLocation(pPostId, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D);
369     glUniform1i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].combineRgb"), GL_ADD);
370     glUniform1i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].combineAlpha"), GL_ADD);
371     glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_ALPHA, GL_SRC_COLOR);
372     glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA);
373     glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].srcRgb"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR);
374     glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[0].srcAlpha"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR);
375 
376     /* Setup blending unit #5
377      * This is HW requirement. See specification document for more details
378     */
379     glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[5].srcRgb"), GL_PREVIOUS, GL_PREVIOUS, GL_TEXTURE0);
380     glUniform3i(glGetUniformLocation(pPostId, "dmp_TexEnv[5].srcAlpha"), GL_PREVIOUS, GL_PREVIOUS, GL_TEXTURE0);
381 
382     /* setup of gas shading */
383     glUniform1i(glGetUniformLocation(pPostId, "dmp_Fog.sampler"), 0);
384     glUniform1i(glGetUniformLocation(pPostId, "dmp_Fog.mode"), GL_GAS_DMP);
385     glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.autoAcc"), 0);
386     glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.samplerTR"), 1);
387     glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.samplerTG"), 2);
388     glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.samplerTB"), 3);
389     glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.shadingDensitySrc"), gas.shadingDensitySrc);
390     glUniform1i(glGetUniformLocation(pPostId, "dmp_Gas.colorLutInput"), gas.colorLutInput);
391     glUniform1f(glGetUniformLocation(pPostId, "dmp_Gas.accMax"), gas._densMax);
392     glUniform4fv(glGetUniformLocation(pPostId, "dmp_Gas.lightZ"), 1, gas._LightZ);
393     glUniform3fv(glGetUniformLocation(pPostId, "dmp_Gas.lightXY"), 1, gas._LightXY);
394 
395     glUniform1i(glGetUniformLocation(pPostId, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GL_DMP);
396 
397     /*
398      * misc settings
399     */
400     glEnable(GL_BLEND);
401     glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
402     glDisable(GL_DEPTH_TEST);
403     glDepthMask(0);
404 
405     glBindTexture(GL_TEXTURE_COLLECTION_DMP, gas.CollectionLUT_ID);
406     glBindTexture(GL_TEXTURE_2D, gastex);
407 
408     glEnableVertexAttribArray(0);
409     glEnableVertexAttribArray(1);
410     glEnableVertexAttribArray(2);
411 
412     glBindBuffer(GL_ARRAY_BUFFER, gas.quad_vertBuf_ID);
413     glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
414 
415     glBindBuffer(GL_ARRAY_BUFFER, gas.quad_texBuf_ID);
416     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
417 
418     glBindBuffer(GL_ARRAY_BUFFER, gas.quad_colBuf_ID);
419     glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, 0);
420 
421     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gas.quad_index_ID);
422     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
423 
424     glDisableVertexAttribArray(0);
425     glDisableVertexAttribArray(1);
426     glDisableVertexAttribArray(2);
427 
428     glDisable(GL_BLEND);
429     glEnable(GL_DEPTH_TEST);
430     glDepthFunc(GL_LESS);
431 
432     glDisable(GL_CULL_FACE);
433     glFrontFace(GL_CCW);
434     glCullFace(GL_BACK);
435 }
436 
437