/* * R_EnableWarp */ void R_EnableWarp(r_program_t *program, boolean_t enable) { if (!r_programs->value) return; if (enable && (!program || !program->id)) return; if (!r_warp->value || r_state.warp_enabled == enable) return; r_state.warp_enabled = enable; R_SelectTexture(&texunit_lightmap); if (enable) { glEnable(GL_TEXTURE_2D); R_BindTexture(r_warp_image->texnum); R_UseProgram(program); } else { glDisable(GL_TEXTURE_2D); R_UseProgram(NULL); } R_SelectTexture(&texunit_diffuse); }
/* * R_EnableTexture */ void R_EnableTexture(r_texunit_t *texunit, boolean_t enable) { if (enable == texunit->enabled) return; texunit->enabled = enable; R_SelectTexture(texunit); if (enable) { // activate texture unit glEnable(GL_TEXTURE_2D); glEnableClientState(GL_TEXTURE_COORD_ARRAY); if (texunit == &texunit_lightmap) { if (r_draw_lightmaps->value) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); else glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } } else { // or deactivate it glDisable(GL_TEXTURE_2D); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } R_SelectTexture(&texunit_diffuse); }
/* * R_BindNormalmapTexture */ void R_BindNormalmapTexture(GLuint texnum) { if (texnum == texunit_normalmap.texnum) return; // small optimization to save state changes R_SelectTexture(&texunit_normalmap); R_BindTexture(texnum); R_SelectTexture(&texunit_diffuse); }
/* * @brief */ static void R_SetVertexArrayState(const r_model_t *mod, uint32_t mask) { // vertex array if (mask & R_ARRAY_VERTEX) R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, mod->verts); // normals and tangents if (r_state.lighting_enabled) { if (mask & R_ARRAY_NORMAL) R_BindArray(GL_NORMAL_ARRAY, GL_FLOAT, mod->normals); if (r_bumpmap->value) { if (mask & R_ARRAY_TANGENT && mod->tangents) R_BindArray(GL_TANGENT_ARRAY, GL_FLOAT, mod->tangents); } } // diffuse texcoords if (texunit_diffuse.enabled) { if (mask & R_ARRAY_TEX_DIFFUSE) R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, mod->texcoords); } // lightmap coords if (texunit_lightmap.enabled) { if (mask & R_ARRAY_TEX_LIGHTMAP) { R_SelectTexture(&texunit_lightmap); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, mod->lightmap_texcoords); R_SelectTexture(&texunit_diffuse); } } }
/* * R_SetDEfaultState * * Sets OpenGL state parameters to appropiate defaults. */ void R_SetDefaultState(void) { int i; r_texunit_t *tex; // setup vertex array pointers glEnableClientState(GL_VERTEX_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); R_EnableColorArray(true); R_BindDefaultArray(GL_COLOR_ARRAY); R_EnableColorArray(false); glEnableClientState(GL_NORMAL_ARRAY); R_BindDefaultArray(GL_NORMAL_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); // setup texture units for (i = 0; i < r_config.max_texunits && i < MAX_GL_TEXUNITS; i++) { tex = &r_state.texunits[i]; tex->texture = GL_TEXTURE0_ARB + i; R_EnableTexture(tex, true); R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); if (i > 0) // turn them off for now R_EnableTexture(tex, false); } R_SelectTexture(&texunit_diffuse); // alpha test parameters glAlphaFunc(GL_GREATER, 0.25); // stencil test parameters glStencilFunc(GL_GEQUAL, 1, 0xff); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // fog parameters glFogi(GL_FOG_MODE, GL_LINEAR); glFogf(GL_FOG_DENSITY, 0.0); glFogf(GL_FOG_START, FOG_START); glFogf(GL_FOG_END, FOG_END); // alpha blend parameters R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); }
/** * @brief Draw the day and night images of a flat geoscape * multitexture feature is used to blend the images * @sa R_Draw3DGlobe * @param[in] p The horizontal shift of the night map * @param[in] cx The x texture coordinate * @param[in] cy The y texture coordinate * @param[in] iz The zoomlevel of the geoscape - see ccs.zoom * @param[in] map The geoscape map to draw (can be changed in the campaign definition) * @param[in] overlayNation,overlayXVI,overlayRadar Whether these overlays should be drawn or not */ void R_DrawFlatGeoscape (const vec2_t nodePos, const vec2_t nodeSize, float p, float cx, float cy, float iz, const char *map, bool overlayNation, bool overlayXVI, bool overlayRadar, image_t *r_dayandnightTexture, image_t *r_xviTexture, image_t *r_radarTexture) { image_t *gl; float geoscape_texcoords[4 * 2]; short geoscape_verts[4 * 2]; /* normalize */ const float nx = nodePos[0] * viddef.rx; const float ny = nodePos[1] * viddef.ry; const float nw = nodeSize[0] * viddef.rx; const float nh = nodeSize[1] * viddef.ry; /* load day image */ gl = R_FindImage(va("pics/geoscape/%s_day", map), it_wrappic); if (gl == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape day image"); /* alter the array pointers */ glVertexPointer(2, GL_SHORT, 0, geoscape_verts); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, geoscape_texcoords); geoscape_texcoords[0] = cx - iz; geoscape_texcoords[1] = cy - iz; geoscape_texcoords[2] = cx + iz; geoscape_texcoords[3] = cy - iz; geoscape_texcoords[4] = cx + iz; geoscape_texcoords[5] = cy + iz; geoscape_texcoords[6] = cx - iz; geoscape_texcoords[7] = cy + iz; geoscape_verts[0] = nx; geoscape_verts[1] = ny; geoscape_verts[2] = nx + nw; geoscape_verts[3] = ny; geoscape_verts[4] = nx + nw; geoscape_verts[5] = ny + nh; geoscape_verts[6] = nx; geoscape_verts[7] = ny + nh; /* draw day image */ R_BindTexture(gl->texnum); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; /* draw night map */ gl = R_FindImage(va("pics/geoscape/%s_night", map), it_wrappic); /* maybe the campaign map doesn't have a night image */ if (gl != r_noTexture) { float geoscape_nighttexcoords[4 * 2]; R_BindTexture(gl->texnum); R_EnableTexture(&texunit_lightmap, true); R_SelectTexture(&texunit_lightmap); geoscape_nighttexcoords[0] = geoscape_texcoords[0] + p; geoscape_nighttexcoords[1] = geoscape_texcoords[1]; geoscape_nighttexcoords[2] = geoscape_texcoords[2] + p; geoscape_nighttexcoords[3] = geoscape_texcoords[3]; geoscape_nighttexcoords[4] = geoscape_texcoords[4] + p; geoscape_nighttexcoords[5] = geoscape_texcoords[5]; geoscape_nighttexcoords[6] = geoscape_texcoords[6] + p; geoscape_nighttexcoords[7] = geoscape_texcoords[7]; R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, geoscape_nighttexcoords); R_BindTexture(r_dayandnightTexture->texnum); R_SelectTexture(&texunit_diffuse); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; R_SelectTexture(&texunit_lightmap); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, geoscape_texcoords); R_EnableTexture(&texunit_lightmap, false); } /* draw nation overlay */ if (overlayNation) { gl = R_FindImage(va("pics/geoscape/%s_nations_overlay", map), it_wrappic); if (gl == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape nation overlay image"); /* draw day image */ R_BindTexture(gl->texnum); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; } /* draw XVI image */ if (overlayXVI) { gl = R_FindImage(va("pics/geoscape/%s_xvi_overlay", map), it_wrappic); if (gl == r_noTexture) Com_Error(ERR_FATAL, "Could not load xvi overlay image"); R_BindTexture(gl->texnum); R_EnableTexture(&texunit_lightmap, true); R_BindLightmapTexture(r_xviTexture->texnum); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; R_EnableTexture(&texunit_lightmap, false); } /* draw radar image */ if (overlayRadar) { R_BindTexture(r_radarTexture->texnum); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; } /* and restore them */ R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); }
/** * @brief responsible for drawing the 3d globe on geoscape * param[in] rotate the rotate angle of the globe * param[in] zoom the current globe zoon * param[in] map the prefix of the map to use (image must be at base/pics/menu/\<map\>_[day|night]) * @sa R_DrawFlatGeoscape * @sa R_SphereGenerate */ void R_Draw3DGlobe (const vec2_t pos, const vec2_t size, int day, int second, const vec3_t rotate, float zoom, const char *map, bool disableSolarRender, float ambient, bool overlayNation, bool overlayXVI, bool overlayRadar, image_t *r_xviTexture, image_t *r_radarTexture, bool renderNationGlow) { /* globe scaling */ const float fullscale = zoom / STANDARD_3D_ZOOM; /* lighting colors */ static const vec4_t diffuseLightColor = { 1.75f, 1.75f, 1.75f, 1.0f }; static const vec4_t specularLightColor = { 2.0f, 1.9f, 1.7f, 1.0f }; static const vec4_t darknessLightColor = { 0.0f, 0.0f, 0.0f, 1.0f }; static const vec4_t brightDiffuseLightColor = { 5.0f, 5.0f, 5.0f, 1.0f }; const vec4_t ambientLightColor = { ambient + 0.2f, ambient + 0.2f, ambient + 0.2f, ambient + 0.2f }; /* billboard textures */ image_t *starfield; image_t *halo; image_t *sun; image_t *sunOverlay; /* set distance of the sun and moon to make them static on starfield when * time is stoped. this distance should be used for any celestial body * considered at infinite location (sun, moon) */ static const float celestialDist = 1.37f * SKYBOX_HALFSIZE; static const float moonSize = 0.025f; vec4_t sunPos; vec4_t antiSunPos; vec4_t moonLoc; vec4_t sunLoc; /* normalize */ const float nx = pos[0] * viddef.rx; const float ny = pos[1] * viddef.ry; const float nw = size[0] * viddef.rx; const float nh = size[1] * viddef.ry; /* Earth center is in the middle of node. * Due to Orthographic view, this is also camera position */ const vec3_t earthPos = { nx + nw / 2.0f, ny + nh / 2.0f, 0.0f }; /* estimate the progress through the current season so we can do * smooth transitions between textures. Currently there are 12 * "seasons", because we have one image per Earth-month. */ const float season = (float) (day % DAYS_PER_YEAR) / ((float) (DAYS_PER_YEAR) / (float) (SEASONS_PER_YEAR)); const int currSeason = (int) floorf(season) % SEASONS_PER_YEAR; const int nextSeason = (int) ceilf(season) % SEASONS_PER_YEAR; const float seasonProgress = season - (float) currSeason; /* Compute sun position in absolute frame */ const float q = (day % DAYS_PER_YEAR * SECONDS_PER_DAY + second) * (2.0f * M_PI / (SECONDS_PER_DAY * DAYS_PER_YEAR)); /* sun rotation (year) */ const float a = cos(q) * SIN_ALPHA; /* due to earth obliquity */ const float sqrta = sqrt(0.5f * (1 - a * a)); /* earth rotation (day) */ const float p = (second - SECONDS_PER_DAY / 4) * (2.0f * M_PI / SECONDS_PER_DAY); /* lunar orbit */ const float m = p + (((double)((10 * day % 249) / 10.0f) + ((double)second / (double)SECONDS_PER_DAY)) / 24.9f) * (2.0f * M_PI); glPushMatrix(); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDisable(GL_LIGHTING); /* draw the starfield, rotating with the planet */ starfield = R_FindImage(va("pics/geoscape/%s_stars", map), it_wrappic); if (starfield != r_noTexture) R_DrawStarfield(starfield->texnum, earthPos, rotate, p); glPopMatrix(); /* set up position vectors for celestial bodies */ Vector4Set(sunPos, cos(p) * sqrta, -sin(p) * sqrta, a, 0); Vector4Set(antiSunPos, -cos(p) * sqrta, sin(p) * sqrta, -a, 0); /* Rotate the sun in the relative frame of player view, to get sun location */ R_RotateCelestialBody(sunPos, sunLoc, rotate, earthPos, 1.0f); /* load sun texture image */ sun = R_FindImage(va("pics/geoscape/%s_sun", map), it_wrappic); sunOverlay = R_FindImage(va("pics/geoscape/%s_sun_overlay", map), it_pic); if (sun != r_noTexture && sunOverlay != r_noTexture && sunLoc[2] > 0 && !disableSolarRender) { const int sunx = earthPos[0] + viddef.rx * (-128.0f + celestialDist * (sunLoc[0] - earthPos[0])); const int suny = earthPos[1] + viddef.ry * (-128.0f + celestialDist * (sunLoc[1] - earthPos[1])); R_DrawTexture(sunOverlay->texnum, sunx, suny, 256.0f * viddef.rx, 256.0f * viddef.ry); R_DrawBuffers(2); R_DrawTexture(sun->texnum, sunx, suny, 256.0 * viddef.rx, 256.0 * viddef.ry); R_DrawBuffers(1); } /* calculate position of the moon (it rotates around earth with a period of * about 24.9 h, and we must take day into account to avoid moon to "jump" * every time the day is changing) */ VectorSet(moonLoc, cos(m) * sqrta, -sin(m) * sqrta, a); R_RotateCelestialBody(moonLoc, moonLoc, rotate, earthPos, celestialDist); /* free last month's texture image */ if (r_globeEarth.season != currSeason) { r_globeEarth.season = currSeason; R_FreeImage(r_globeEarth.texture); } /* load diffuse texture map (with embedded night-glow map as alpha channel) */ r_globeEarth.texture = R_FindImage(va("pics/geoscape/%s/%s_season_%02d", r_config.lodDir, map, currSeason), it_wrappic); if (r_globeEarth.texture == r_noTexture) Com_Error(ERR_FATAL, "Could not find pics/geoscape/%s/%s_season_%02d\n", r_config.lodDir, map, currSeason); /* set up for advanced GLSL rendering if we have the capability */ if (r_programs->integer) { r_globeEarth.glslProgram = r_state.geoscape_program; /* load earth image for the next month so we can blend them */ r_globeEarth.blendTexture = R_FindImage(va("pics/geoscape/%s/%s_season_%02d", r_config.lodDir, map, nextSeason), it_wrappic); if (r_globeEarth.blendTexture == r_noTexture) Com_Error(ERR_FATAL, "Could not find pics/geoscape/%s/%s_season_%02d\n", r_config.lodDir, map, nextSeason); /* load normal map (with embedded gloss map as alpha channel) */ r_globeEarth.normalMap = R_FindImage(va("pics/geoscape/%s/%s_bump", r_config.lodDir, map), it_wrappic); if (r_globeEarth.normalMap == r_noTexture) r_globeEarth.normalMap = nullptr; /* weight the blending based on how much of the month has elapsed */ r_globeEarth.blendScale = seasonProgress; /* set up lights for nighttime city glow */ VectorCopy(antiSunPos, r_globeEarth.nightLightPos); glLightfv(GL_LIGHT1, GL_AMBIENT, darknessLightColor); glLightfv(GL_LIGHT1, GL_DIFFUSE, brightDiffuseLightColor); glLightfv(GL_LIGHT1, GL_SPECULAR, darknessLightColor); r_globeEarth.glowScale = 0.7f; } /* load moon texture image */ r_globeMoon.texture = R_FindImage(va("pics/geoscape/%s_moon", map), it_wrappic); /* globe texture scaling */ glMatrixMode(GL_TEXTURE); glLoadIdentity(); glScalef(2.0f, 1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); /* enable the lighting */ glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLightColor); glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLightColor); glLightfv(GL_LIGHT0, GL_SPECULAR, specularLightColor); /* draw the moon */ if (r_globeMoon.texture != r_noTexture && moonLoc[2] > 0 && !disableSolarRender) R_SphereRender(&r_globeMoon, moonLoc, rotate, moonSize, sunPos); /* activate depth to hide 3D models behind earth */ glEnable(GL_DEPTH_TEST); /* draw the earth */ R_DrawBuffers(2); #if 0 /* old rendering code which doesn't render city lights in FFP */ if (r_programs->integer == 0) /* ignore alpha channel, since the city-light map is stored there */ glBlendFunc(GL_ONE, GL_ZERO); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); if (r_programs->integer == 0) /* restore default blend function */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #else /* new which does render city lights in FFP */ if (r_programs->integer == 0) { /* set up rendering of city lights map, which is stored in alpha channel; OpenGL 1.3 required */ R_SelectTexture(&texunit_diffuse); /* select texture to edit texture environment for */ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); /* enable color combiner */ /* setup texture combiner to blend between daylight diffuse map stored in the RGB channels of the diffuse texture * and the monochomatic emission map (which simulates city lights) stored in the alpha channel; * incoming color value is the blend factor. */ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); /* set day color as blending target*/ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); /* set night color as blending source */ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS); /* set incoming color as blending factor */ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); /* set blending mode to interpolation from src1 to src0 */ /* copy alpha from incoming color, bypassing the value read from texture, which is not a "real" alpha anyway */ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); } R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); if (r_programs->integer == 0) { /* disable combiner */ R_SelectTexture(&texunit_diffuse); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } #endif r_globeEarthAtmosphere.texture = R_FindImage(va("pics/geoscape/%s_atmosphere", map), it_wrappic); /* Draw earth atmosphere */ /** @todo render atmosphere glow even when postprocessing is disabled */ if (r_programs->integer && r_postprocess->integer) { r_globeEarthAtmosphere.normalMap = r_globeEarth.normalMap; r_globeEarthAtmosphere.glowScale = 1.0; r_globeEarthAtmosphere.blendScale = -1.0; r_globeEarthAtmosphere.glslProgram = r_state.atmosphere_program; R_SphereRender(&r_globeEarthAtmosphere, earthPos, rotate, fullscale, sunPos); } else { halo = R_FindImage("pics/geoscape/map_earth_halo", it_pic); if (halo != r_noTexture) { /** @todo Replace this magic number with some speaking constant */ const float earthSizeX = fullscale * 20500.0f * viddef.rx; const float earthSizeY = fullscale * 20500.0f * viddef.ry; glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); glDisable(GL_LIGHTING); R_DrawTexture(halo->texnum, earthPos[0] - earthSizeX * 0.5f, earthPos[1] - earthSizeY * 0.5f, earthSizeX, earthSizeY); glEnable(GL_LIGHTING); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } } R_DrawBuffers(1); glDisable(GL_DEPTH_TEST); /* draw nation overlay */ if (overlayNation) { r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_nations_overlay", map), it_wrappic); if (r_globeEarth.overlay == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape nation overlay image"); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); if (renderNationGlow) { /* draw glowing borders */ r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_nations_overlay_glow", map), it_wrappic); if (r_globeEarth.overlay == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape nation overlay glow image"); R_DrawBuffers(2); glDisable(GL_LIGHTING); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); glEnable(GL_LIGHTING); R_DrawBuffers(1); } r_globeEarth.overlay = nullptr; } /* draw XVI overlay */ if (overlayXVI) { r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_xvi_overlay", map), it_wrappic); r_globeEarth.overlayAlphaMask = r_xviTexture; assert(r_globeEarth.overlayAlphaMask); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); r_globeEarth.overlayAlphaMask = nullptr; r_globeEarth.overlay = nullptr; } /* draw radar overlay */ if (overlayRadar) { r_globeEarth.overlay = r_radarTexture; assert(r_globeEarth.overlay); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); r_globeEarth.overlay = nullptr; } /* disable 3d geoscape lighting */ glDisable(GL_LIGHTING); /* restore the previous matrix */ glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); }
/* * @brief */ void R_ResetArrayState(void) { uint32_t arrays, mask; mask = 0xffff, arrays = R_ArraysMask(); // resolve the desired arrays mask if (r_array_state.model == NULL) { const int32_t xor = r_array_state.arrays ^ arrays; if (!xor) // no changes, we're done return; // resolve what's left to turn on mask = arrays & xor; } else // vbo R_BindBuffer(0, 0, 0); // vertex array if (mask & R_ARRAY_VERTEX) R_BindDefaultArray(GL_VERTEX_ARRAY); // color array if (r_state.color_array_enabled) { if (mask & R_ARRAY_COLOR) R_BindDefaultArray(GL_COLOR_ARRAY); } // normals and tangents if (r_state.lighting_enabled) { if (mask & R_ARRAY_NORMAL) R_BindDefaultArray(GL_NORMAL_ARRAY); if (r_bumpmap->value) { if (mask & R_ARRAY_TANGENT) R_BindDefaultArray(GL_TANGENT_ARRAY); } } // diffuse texcoords if (texunit_diffuse.enabled) { if (mask & R_ARRAY_TEX_DIFFUSE) R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); } // lightmap texcoords if (texunit_lightmap.enabled) { if (mask & R_ARRAY_TEX_LIGHTMAP) { R_SelectTexture(&texunit_lightmap); R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_SelectTexture(&texunit_diffuse); } } r_array_state.model = NULL; r_array_state.arrays = arrays; }