static void R_UploadLightmapBlock (void) { #ifdef GL_VERSION_ES_CM_1_0 const int texFormat = GL_RGB; #else const int texFormat = r_config.gl_compressed_solid_format ? r_config.gl_compressed_solid_format : r_config.gl_solid_format; #endif GLuint texid; if (r_lightmaps.lightmap_count >= MAX_GL_LIGHTMAPS) { Com_Printf("R_UploadLightmapBlock: MAX_GL_LIGHTMAPS reached.\n"); return; } if (!r_lightmaps.incomplete_atlas) { glGenTextures(1, &texid); r_lightmaps.lightmap_texnums[r_lightmaps.lightmap_count++] = texid; } else { texid = r_lightmaps.lightmap_texnums[r_lightmaps.lightmap_count]; } R_BindTexture(texid); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, texFormat, r_lightmaps.size, r_lightmaps.size, 0, GL_RGB, GL_UNSIGNED_BYTE, r_lightmaps.sample_buffer); R_CheckError(); if (r_lightmaps.deluxemap_count >= MAX_GL_DELUXEMAPS) { Com_Printf("R_UploadLightmapBlock: MAX_GL_DELUXEMAPS reached.\n"); return; } if (!r_lightmaps.incomplete_atlas) { glGenTextures(1, &texid); r_lightmaps.deluxemap_texnums[r_lightmaps.deluxemap_count++] = texid; } else { texid = r_lightmaps.deluxemap_texnums[r_lightmaps.deluxemap_count]; } R_BindTexture(texid); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, texFormat, r_lightmaps.size, r_lightmaps.size, 0, GL_RGB, GL_UNSIGNED_BYTE, r_lightmaps.direction_buffer); /* clear the allocation block and buffers */ memset(r_lightmaps.allocated, 0, r_lightmaps.size * sizeof(unsigned)); memset(r_lightmaps.sample_buffer, 0, r_lightmaps.size * r_lightmaps.size * sizeof(unsigned)); memset(r_lightmaps.direction_buffer, 0, r_lightmaps.size * r_lightmaps.size * sizeof(unsigned)); r_lightmaps.incomplete_atlas = false; }
/** * @brief Bind and draw a texture * @param[in] texnum The texture id (already uploaded of course) * @param[in] x normalized x value on the screen * @param[in] y normalized y value on the screen * @param[in] w normalized width value * @param[in] h normalized height value */ void R_DrawTexture (int texnum, int x, int y, int w, int h) { const vec2_t vertexes[] = {{x, y}, {x + w, y}, {x + w, y + h}, {x, y + h}}; R_BindTexture(texnum); R_DrawImageArray(default_texcoords, vertexes, NULL); }
/** * @brief Set the surface state according to surface flags and bind the texture * @sa R_DrawSurfaces */ static void R_SetSurfaceState (const mBspSurface_t *surf) { image_t *image; if (r_state.blend_enabled) { /* alpha blend */ vec4_t color = {1.0, 1.0, 1.0, 1.0}; switch (surf->texinfo->flags & (SURF_BLEND33 | SURF_BLEND66)) { case SURF_BLEND33: color[3] = 0.33; break; case SURF_BLEND66: color[3] = 0.66; break; } R_Color(color); } image = surf->texinfo->image; R_BindTexture(image->texnum); /* texture */ if (texunit_lightmap.enabled) { /* lightmap */ if (surf->flags & MSURF_LIGHTMAP) R_BindLightmapTexture(surf->lightmap_texnum); } R_SetSurfaceBumpMappingParameters(surf, image->normalmap, image->specularmap); R_EnableGlowMap(image->glowmap); R_CheckError(); }
/** * @brief Draws a marker on the ground to indicate pathing CL_AddPathingBox * @sa CL_AddPathing * @sa RF_BOX */ static void R_DrawFloor (const entity_t * e) { image_t *cellIndicator = R_FindImage("pics/sfx/cell", it_pic); const float dx = PLAYER_WIDTH * 2; const vec4_t color = {e->color[0], e->color[1], e->color[2], e->alpha}; const float size = 4.0; /** @todo use default_texcoords */ const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } }; const vec3_t points[] = { { e->origin[0] - size, e->origin[1] + dx + size, e->origin[2] }, { e->origin[0] + dx + size, e->origin[1] + dx + size, e->origin[2] }, { e->origin[0] + dx + size, e->origin[1] - size, e->origin[2] }, { e->origin[0] - size, e->origin[1] - size, e->origin[2] } }; glDisable(GL_DEPTH_TEST); R_Color(color); R_BindTexture(cellIndicator->texnum); /* circle points */ R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); refdef.batchCount++; glEnable(GL_DEPTH_TEST); R_Color(NULL); }
/* * @brief Uploads the specified image to the OpenGL implementation. Images that * do not have a GL texture reserved (which is most diffuse textures) will have * one generated for them. This flexibility allows for explicitly managed * textures (such as lightmaps) to be here as well. */ void R_UploadImage(r_image_t *image, GLenum format, byte *data) { if (!image || !data) { Com_Error(ERR_DROP, "NULL image or data\n"); } if (!image->texnum) { glGenTextures(1, &(image->texnum)); } R_BindTexture(image->texnum); if (image->type & IT_MASK_MIPMAP) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_image_state.filter_min); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_image_state.filter_mag); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_image_state.anisotropy); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_image_state.filter_mag); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_image_state.filter_mag); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); } glTexImage2D(GL_TEXTURE_2D, 0, format, image->width, image->height, 0, format, GL_UNSIGNED_BYTE, data); R_RegisterMedia((r_media_t *) image); R_GetError(image->media.name); }
/* * @brief */ static void R_DrawChars(void) { uint16_t i; for (i = 0; i < r_draw.num_fonts; i++) { r_char_arrays_t *chars = &r_draw.char_arrays[i]; if (!chars->vert_index) continue; R_BindTexture(r_draw.fonts[i].image->texnum); R_EnableColorArray(true); // alter the array pointers R_BindArray(GL_COLOR_ARRAY, GL_UNSIGNED_BYTE, chars->colors); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, chars->texcoords); R_BindArray(GL_VERTEX_ARRAY, GL_SHORT, chars->verts); glDrawArrays(GL_QUADS, 0, chars->vert_index / 2); chars->color_index = 0; chars->texcoord_index = 0; chars->vert_index = 0; } // restore array pointers R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); R_BindDefaultArray(GL_COLOR_ARRAY); R_EnableColorArray(false); // restore draw color R_Color(NULL); }
void R_DrawChars (void) { if (!r_char_arrays.vert_index) return; R_BindTexture(draw_chars->texnum); R_EnableColorArray(true); /* alter the array pointers */ R_BindArray(GL_COLOR_ARRAY, GL_UNSIGNED_BYTE, r_char_arrays.colors); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, r_char_arrays.texcoords); glVertexPointer(2, GL_SHORT, 0, r_char_arrays.verts); glDrawArrays(GL_QUADS, 0, r_char_arrays.vert_index / 2); refdef.batchCount++; r_char_arrays.color_index = 0; r_char_arrays.texcoord_index = 0; r_char_arrays.vert_index = 0; /* and restore them */ R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); R_BindDefaultArray(GL_COLOR_ARRAY); R_EnableColorArray(false); }
/* * 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); }
/** * @sa R_DrawParticles */ static void R_DrawSprite (const ptl_t * p) { const ptl_t *q; vec3_t up, right; vec3_t nup, nright; vec3_t pos; float texcoords[8]; /* load texture set up coordinates */ assert(p->pic); R_BindTexture(p->pic->art.image->texnum); /* calculate main position and normalised up and right vectors */ q = p->parent ? p->parent : p; R_GetSpriteVectors(q, right, up); /* Calculate normalised */ VectorCopy(up, nup); VectorCopy(right, nright); VectorNormalizeFast(nup); VectorNormalizeFast(nright); /* offset */ VectorCopy(q->s, pos); VectorMA(pos, q->offset[0], nup, pos); VectorMA(pos, q->offset[1], nright, pos); if (p->parent) { /* if this is a child then calculate our own up and right vectors and offsets */ R_GetSpriteVectors(p, right, up); /* but offset by our parent's nup and nright */ VectorMA(pos, p->offset[0], nup, pos); VectorMA(pos, p->offset[1], nright, pos); } /* center image */ VectorMA(pos, -0.5, up, pos); VectorMA(pos, -0.5, right, pos); R_SpriteTexcoords(p, texcoords); R_Color(p->color); { /* draw it */ const vec3_t points[] = { { pos[0], pos[1], pos[2] }, { pos[0] + up[0], pos[1] + up[1], pos[2] + up[2] }, { pos[0] + up[0] + right[0], pos[1] + up[1] + right[1], pos[2] + up[2] + right[2] }, { pos[0] + right[0], pos[1] + right[1], pos[2] + right[2] } }; R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); R_BindDefaultArray(GL_VERTEX_ARRAY); R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); refdef.batchCount++; } }
/** * @brief Draws the field marker entity is specified in CL_AddTargeting * @sa CL_AddTargeting * @sa RF_BOX */ static void R_DrawBox (const entity_t* e) { const vec4_t color = {e->color[0], e->color[1], e->color[2], e->alpha}; if (e->texture) { R_Color(color); R_BindTexture(e->texture->texnum); if (VectorNotEmpty(e->eBox.mins) && VectorNotEmpty(e->eBox.maxs)) { R_DrawTexturedBox(e->eBox.mins, e->eBox.maxs); } else { R_DrawTexturedBox(e->oldorigin, e->origin); } R_Color(nullptr); return; } glDisable(GL_TEXTURE_2D); R_Color(color); if (VectorNotEmpty(e->eBox.mins) && VectorNotEmpty(e->eBox.maxs)) { R_DrawBoundingBox(e->eBox); } else { vec3_t points[] = { { e->oldorigin[0], e->oldorigin[1], e->oldorigin[2] }, { e->oldorigin[0], e->origin[1], e->oldorigin[2] }, { e->origin[0], e->origin[1], e->oldorigin[2] }, { e->origin[0], e->oldorigin[1], e->oldorigin[2] } }; glLineWidth(2.0f); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points); /** @todo fill one array */ glDrawArrays(GL_LINE_LOOP, 0, 4); refdef.batchCount++; points[0][2] = e->origin[2]; points[1][2] = e->origin[2]; points[2][2] = e->origin[2]; points[3][2] = e->origin[2]; glDrawArrays(GL_LINE_LOOP, 0, 4); refdef.batchCount++; points[0][2] = e->oldorigin[2]; points[1][1] = e->oldorigin[1]; points[2][2] = e->oldorigin[2]; points[3][1] = e->origin[1]; glDrawArrays(GL_LINES, 0, 4); refdef.batchCount++; points[0][0] = e->origin[0]; points[1][0] = e->origin[0]; points[2][0] = e->oldorigin[0]; points[3][0] = e->oldorigin[0]; glDrawArrays(GL_LINES, 0, 4); refdef.batchCount++; R_BindDefaultArray(GL_VERTEX_ARRAY); } glEnable(GL_TEXTURE_2D); R_Color(nullptr); }
/** * @brief Uploads image data * @param[in] name The name of the texture to use for this data * @param[in] frame The frame data that is uploaded * @param[in] width The width of the texture * @param[in] height The height of the texture * @return the texture number of the uploaded images */ int R_UploadData (const char *name, byte *frame, int width, int height) { image_t *img; unsigned *scaled; int scaledWidth, scaledHeight; int samples = r_config.gl_compressed_solid_format ? r_config.gl_compressed_solid_format : r_config.gl_solid_format; int i, c; byte *scan; R_GetScaledTextureSize(width, height, &scaledWidth, &scaledHeight); img = R_FindImage(name, it_pic); if (img == r_noTexture) Com_Error(ERR_FATAL, "Could not find the searched image: %s", name); /* scan the texture for any non-255 alpha */ c = scaledWidth * scaledHeight; /* set scan to the first alpha byte */ for (i = 0, scan = ((byte *) frame) + 3; i < c; i++, scan += 4) { if (*scan != 255) { samples = r_config.gl_compressed_alpha_format ? r_config.gl_compressed_alpha_format : r_config.gl_alpha_format; break; } } if (scaledWidth != width || scaledHeight != height) { /* whereas others need to be scaled */ scaled = (unsigned *)Mem_PoolAllocExt(scaledWidth * scaledHeight * sizeof(unsigned), qfalse, vid_imagePool, 0); R_ScaleTexture((unsigned *)frame, width, height, scaled, scaledWidth, scaledHeight); } else { scaled = (unsigned *)frame; } R_BindTexture(img->texnum); if (img->upload_width == scaledWidth && img->upload_height == scaledHeight) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, scaledWidth, scaledHeight, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } else { /* Reallocate the texture */ img->width = width; img->height = height; img->upload_width = scaledWidth; img->upload_height = scaledHeight; #ifdef HAVE_GLES glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, scaledWidth, scaledHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); #else glTexImage2D(GL_TEXTURE_2D, 0, samples, scaledWidth, scaledHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); #endif } glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); R_CheckError(); if (scaled != (unsigned *)frame) Mem_Free(scaled); return img->texnum; }
/* * 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 Manages all state for the specified surface and stage. * @sa R_DrawMaterialSurfaces */ static void R_SetSurfaceStageState (const mBspSurface_t *surf, const materialStage_t *stage) { /* bind the texture */ R_BindTexture(stage->image->texnum); /* and optionally the lightmap */ R_StageLighting(surf, stage); R_StageGlow(stage); /* load the texture matrix for rotations, stretches, etc.. */ R_StageTextureMatrix(surf, stage); /* set the blend function, ensuring a good default */ if (stage->flags & STAGE_BLEND) R_BlendFunc(stage->blend.src, stage->blend.dest); else R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* for terrain, enable the color array */ if (stage->flags & (STAGE_TAPE | STAGE_TERRAIN | STAGE_DIRTMAP)) R_EnableColorArray(true); else R_EnableColorArray(false); /* when not using the color array, resolve the shade color */ if (!r_state.color_array_enabled) { vec4_t color; if (stage->flags & STAGE_COLOR) /* explicit */ VectorCopy(stage->color, color); else if (stage->flags & STAGE_ENVMAP) /* implied */ VectorCopy(surf->lightColor, color); /** @todo WTF? surely it was supposed to use the specular color */ else /* default */ VectorSet(color, 1.0, 1.0, 1.0); /* modulate the alpha value for pulses */ if (stage->flags & STAGE_PULSE) { /* disable fog, since it also sets alpha */ R_EnableFog(false); color[3] = stage->pulse.dhz; } else { /* ensure fog is available */ R_EnableFog(true); color[3] = 1.0; } R_Color(color); } }
/* * @brief */ void R_DrawSkyBox(void) { const int32_t sky_order[6] = { 0, 2, 1, 3, 4, 5 }; r_bsp_surfaces_t *surfs; uint32_t i, j; surfs = &r_model_state.world->bsp->sorted_surfaces->sky; j = 0; // first add all visible sky surfaces to the sky bounds for (i = 0; i < surfs->count; i++) { if (surfs->surfaces[i]->frame == r_locals.frame) { R_AddSkySurface(surfs->surfaces[i]); j++; } } if (!j) // no visible sky surfaces return; R_ResetArrayState(); glPushMatrix(); glTranslatef(r_view.origin[0], r_view.origin[1], r_view.origin[2]); if (r_state.fog_enabled) glFogf(GL_FOG_END, FOG_END * 8); r_sky.texcoord_index = r_sky.vert_index = 0; for (i = 0; i < 6; i++) { if (r_sky.st_mins[0][i] >= r_sky.st_maxs[0][i] || r_sky.st_mins[1][i] >= r_sky.st_maxs[1][i]) continue; // nothing on this plane R_BindTexture(r_sky.images[sky_order[i]]->texnum); R_MakeSkyVec(r_sky.st_mins[0][i], r_sky.st_mins[1][i], i); R_MakeSkyVec(r_sky.st_mins[0][i], r_sky.st_maxs[1][i], i); R_MakeSkyVec(r_sky.st_maxs[0][i], r_sky.st_maxs[1][i], i); R_MakeSkyVec(r_sky.st_maxs[0][i], r_sky.st_mins[1][i], i); glDrawArrays(GL_QUADS, 0, r_sky.vert_index / 3); r_sky.texcoord_index = r_sky.vert_index = 0; } if (r_state.fog_enabled) glFogf(GL_FOG_END, FOG_END); glPopMatrix(); }
/* * R_SetSurfaceState_default */ static void R_SetSurfaceState_default(const r_bsp_surface_t *surf) { r_image_t *image; float a; if (r_state.blend_enabled) { // alpha blend switch (surf->texinfo->flags & (SURF_BLEND_33 | SURF_BLEND_66)) { case SURF_BLEND_33: a = 0.33; break; case SURF_BLEND_66: a = 0.66; break; default: // both flags mean use the texture's alpha channel a = 1.0; break; } glColor4f(1.0, 1.0, 1.0, a); } image = surf->texinfo->image; if (texunit_diffuse.enabled) // diffuse texture R_BindTexture(image->texnum); if (texunit_lightmap.enabled) // lightmap texture R_BindLightmapTexture(surf->lightmap_texnum); if (r_state.lighting_enabled) { // hardware lighting if (r_bumpmap->value) { // bump mapping if (image->normalmap) { R_BindDeluxemapTexture(surf->deluxemap_texnum); R_BindNormalmapTexture(image->normalmap->texnum); R_EnableBumpmap(&image->material, true); } else R_EnableBumpmap(NULL, false); } if (surf->light_frame == r_locals.light_frame) // dynamic light sources R_EnableLights(surf->lights); else R_EnableLights(0); } }
/* * @brief Draws all particles for the current frame. */ void R_DrawParticles(const r_element_t *e, const size_t count) { size_t i, j; R_EnableColorArray(true); R_ResetArrayState(); // alter the array pointers R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, r_particle_state.verts); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, r_particle_state.texcoords); R_BindArray(GL_COLOR_ARRAY, GL_UNSIGNED_BYTE, r_particle_state.colors); const GLuint base = (uintptr_t) e->data; for (i = j = 0; i < count; i++, e++) { const r_particle_t *p = (const r_particle_t *) e->element; // bind the particle's texture if (p->image->texnum != texunit_diffuse.texnum) { if (i > j) { // draw pending particles glDrawArrays(GL_QUADS, (base + j) * 4, (i - j) * 4); j = i; } R_BindTexture(p->image->texnum); R_BlendFunc(GL_SRC_ALPHA, p->blend); } } if (i > j) { // draw any remaining particles glDrawArrays(GL_QUADS, (base + j) * 4, (i - j) * 4); } // restore array pointers R_BindDefaultArray(GL_VERTEX_ARRAY); R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_COLOR_ARRAY); R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); R_EnableColorArray(false); R_Color(NULL); }
void R_DrawGrass () { if (clumpTriangleCount <= 0) return; R_BindTexture(R_FindImage("models/objects/vegi/plants2/plant_skin3", it_pic)->texnum); R_EnableAlphaTest(true); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, gfv_texcoord); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, gfv_pos); glDrawArrays(GL_TRIANGLES, 0, clumpTrianglesForLevel[refdef.worldlevel] * 3); R_BindDefaultArray(GL_VERTEX_ARRAY); R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_EnableAlphaTest(false); refdef.batchCount++; }
const image_t *R_DrawImageArray (const vec2_t texcoords[4], const vec2_t verts[4], const image_t *image) { /* alter the array pointers */ glVertexPointer(2, GL_FLOAT, 0, verts); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords); if (image != NULL) R_BindTexture(image->texnum); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; /* and restore them */ R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); return image; }
/** * @brief Renders a particle model for the battlescape * @param[in,out] mi The model information that is used to render the particle model. * @sa R_DrawPtlModel */ void R_DrawModelParticle (modelInfo_t * mi) { image_t *skin; mAliasMesh_t *mesh; /* check if the model exists */ if (!mi->model) return; skin = R_AliasModelState(mi->model, &mi->mesh, &mi->frame, &mi->oldframe, &mi->skin); if (skin == NULL) { Com_Printf("Model '%s' is broken\n", mi->name); return; } R_Color(mi->color); glPushMatrix(); glTranslatef(mi->origin[0], mi->origin[1], mi->origin[2]); glRotatef(mi->angles[YAW], 0, 0, 1); glRotatef(mi->angles[PITCH], 0, 1, 0); glRotatef(-mi->angles[ROLL], 1, 0, 0); /* draw it */ R_BindTexture(skin->texnum); /* draw the model */ mesh = &mi->model->alias.meshes[0]; refdef.aliasCount += mesh->num_tris; if (mi->model->alias.num_frames == 1) R_DrawAliasStaticWithReset(mesh, vec4_origin); else R_DrawAliasFrameLerp(&mi->model->alias, mesh, mi->backlerp, mi->frame, mi->oldframe, vec4_origin); /* show model bounding box */ if (r_showbox->integer) R_DrawBoundingBox(mi->model->alias.frames[mi->frame].mins, mi->model->alias.frames[mi->frame].maxs); glPopMatrix(); R_Color(NULL); }
/* * @brief */ void R_DrawImage(r_pixel_t x, r_pixel_t y, vec_t scale, const r_image_t *image) { R_BindTexture(image->texnum); // our texcoords are already setup, just set verts and draw r_state.vertex_array_2d[0] = x; r_state.vertex_array_2d[1] = y; r_state.vertex_array_2d[2] = x + image->width * scale; r_state.vertex_array_2d[3] = y; r_state.vertex_array_2d[4] = x + image->width * scale; r_state.vertex_array_2d[5] = y + image->height * scale; r_state.vertex_array_2d[6] = x; r_state.vertex_array_2d[7] = y + image->height * scale; glDrawArrays(GL_QUADS, 0, 4); }
/** * @brief Draws an animated, colored shell for the specified entity. Rather than * re-lerping or re-scaling the entity, the currently bound vertex arrays * are simply re-drawn using a small depth offset and varying texcoord delta. */ static void R_DrawMeshModelShell (const mAliasMesh_t *mesh, const vec4_t color) { /* check whether rgb is set */ if (!VectorNotEmpty(color)) return; R_Color(color); R_BindTexture(r_envmaptextures[1]->texnum); R_EnableShell(true); glDrawArrays(GL_TRIANGLES, 0, mesh->num_tris * 3); refdef.batchCount++; R_EnableShell(false); R_Color(NULL); }
/** * @brief Draws a marker on the ground to indicate pathing CL_AddPathingBox * @sa CL_AddPathing * @sa RF_BOX */ static void R_DrawFloor (const entity_t* e) { GLint oldDepthFunc; glGetIntegerv(GL_DEPTH_FUNC, &oldDepthFunc); image_t* cellIndicator = R_FindImage("pics/sfx/cell", it_pic); const float dx = PLAYER_WIDTH * 2; const vec4_t color = {e->color[0], e->color[1], e->color[2], e->alpha}; const float size = 4.0; /** @todo use default_texcoords */ const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } }; const vec3_t points[] = { { e->origin[0] - size, e->origin[1] + dx + size, e->origin[2] }, { e->origin[0] + dx + size, e->origin[1] + dx + size, e->origin[2] }, { e->origin[0] + dx + size, e->origin[1] - size, e->origin[2] }, { e->origin[0] - size, e->origin[1] - size, e->origin[2] } }; /* Draw it twice, with and without depth check, so it will still be visible if obscured by a wall */ R_Color(color); R_BindTexture(cellIndicator->texnum); /* circle points */ R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDepthFunc(GL_GREATER); glColor4f(color[0], color[1], color[2], color[3] * 0.25f); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDepthFunc(oldDepthFunc); R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); refdef.batchCount += 2; R_Color(nullptr); }
/** * @brief Bind and draw starfield. * @param[in] texnum The texture id (already uploaded of course) * @note We draw a skybox: the camera is inside a cube rotating at earth rotation speed * (stars seems to rotate because we see earth as idle, but in reality stars are statics * and earth rotate around itself) * @sa R_Setup2D * @sa R_Draw3DGlobe */ static void R_DrawStarfield (int texnum, const vec3_t pos, const vec3_t rotate, float p) { vec3_t angle; /**< Angle of rotation of starfield */ /* go to a new matrix */ glPushMatrix(); /* we must center the skybox on the camera border of view, and not on the earth, in order * to see only the inside of the cube */ glTranslatef(pos[0], pos[1], -SKYBOX_DEPTH); /* rotates starfield: only time and rotation of earth around itself causes starfield to rotate. */ VectorSet(angle, rotate[0] - p * todeg, rotate[1], rotate[2]); glRotatef(angle[YAW], 1, 0, 0); glRotatef(angle[ROLL], 0, 1, 0); glRotatef(angle[PITCH], 0, 0, 1); R_BindTexture(texnum); /* alter the array pointers */ glVertexPointer(3, GL_FLOAT, 0, starFieldVerts); glTexCoordPointer(2, GL_FLOAT, 0, starFieldTexCoords); /* draw the cube */ #ifdef GL_VERSION_ES_CM_1_0 for( int ii = 0; ii < 6; ii++ ) glDrawArrays(GL_TRIANGLE_FAN, ii * 4, 4); #else glDrawArrays(GL_QUADS, 0, 24); #endif refdef.batchCount++; /* restore previous matrix */ glPopMatrix(); }
/* * RB_BindTexture */ void RB_BindTexture( int tmu, const image_t *tex ) { if( R_BindTexture( tmu, tex ) ) { rb.stats.c_totalBinds++; } }
/** * @brief Draws shadow and highlight effects for the entities (actors) * @note The origins are already transformed */ void R_DrawEntityEffects (void) { const int mask = r_stencilshadows->integer ? RF_BLOOD : (RF_SHADOW | RF_BLOOD); GLint oldDepthFunc; glGetIntegerv(GL_DEPTH_FUNC, &oldDepthFunc); R_EnableBlend(true); if (actorIndicator == nullptr) { selectedActorIndicator = R_FindImage("pics/sfx/actor_selected", it_effect); actorIndicator = R_FindImage("pics/sfx/actor", it_effect); } for (int i = 0; i < refdef.numEntities; i++) { const entity_t* e = &r_entities[i]; if (e->flags <= RF_BOX) continue; glPushMatrix(); glMultMatrixf(e->transform.matrix); if (e->flags & mask) { const vec3_t points[] = { { -18.0, 14.0, -28.5 }, { 10.0, 14.0, -28.5 }, { 10.0, -14.0, -28.5 }, { -18.0, -14.0, -28.5 } }; /** @todo use default_texcoords */ const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } }; if (e->flags & RF_SHADOW) { R_BindTexture(shadow->texnum); } else { assert(e->texture); R_BindTexture(e->texture->texnum); } R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); refdef.batchCount++; } if (e->flags & RF_ACTOR) { const float size = 15.0; int texnum; /* draw the circles for team-members and allied troops */ vec4_t color = {1, 1, 1, 1}; const vec3_t points[] = { { -size, size, -SELECTION_DELTA }, { size, size, -SELECTION_DELTA }, { size, -size, -SELECTION_DELTA }, { -size, -size, -SELECTION_DELTA } }; /** @todo use default_texcoords */ const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } }; if (e->flags & RF_SELECTED) Vector4Set(color, 0, 1, 0, 0.5); else if (e->flags & RF_MEMBER) Vector4Set(color, 0, 0.8, 0, 0.5); else if (e->flags & RF_ALLIED) Vector4Set(color, 0, 1, 0.5, 0.5); else if (e->flags & RF_NEUTRAL) Vector4Set(color, 1, 1, 0, 0.5); else if (e->flags & RF_OPPONENT) Vector4Set(color, 1, 0, 0, 0.5); else Vector4Set(color, 0.3, 0.3, 0.3, 0.5); if (e->flags & RF_SELECTED) texnum = selectedActorIndicator->texnum; else texnum = actorIndicator->texnum; R_BindTexture(texnum); R_Color(color); R_EnableDrawAsGlow(true); /* circle points */ R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; /* add transparency when something is in front of the circle */ color[3] *= 0.25; R_Color(color); glDepthFunc(GL_GREATER); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDepthFunc(oldDepthFunc); refdef.batchCount++; R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); R_Color(nullptr); R_EnableDrawAsGlow(false); } glPopMatrix(); } }
/** * @brief handle post-processing bloom */ void R_DrawBloom (void) { int i; bool renderBufferState; if (!r_config.frameBufferObject || !r_postprocess->integer || !r_programs->integer) return; /* save state, then set up for blit-style rendering to quads */ renderBufferState = R_RenderbufferEnabled(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); #ifndef GL_VERSION_ES_CM_1_0 glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT); #endif glOrtho(0, viddef.context.width, viddef.context.height, 0, 9999.0f, SKYBOX_DEPTH); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); /* downsample into image pyramid */ R_ResolveMSAA(fbo_render); R_BindTexture(fbo_render->textures[1]); qglGenerateMipmapEXT(GL_TEXTURE_2D); R_Blur(fbo_render, fbo_bloom0, 1, 0); R_Blur(fbo_bloom0, fbo_bloom1, 0, 1); R_UseFramebuffer(r_state.buffers0[0]); R_BindTexture(fbo_bloom1->textures[0]); qglGenerateMipmapEXT(GL_TEXTURE_2D); R_UseViewport(r_state.buffers0[0]); R_DrawQuad(); for (i = 1; i < DOWNSAMPLE_PASSES; i++) { R_Blur(r_state.buffers0[i - 1], r_state.buffers1[i - 1], 0, 0); R_Blur(r_state.buffers1[i - 1], r_state.buffers2[i - 1], 0, 1); R_UseFramebuffer(r_state.buffers0[i]); R_BindTexture(r_state.buffers2[i - 1]->textures[0]); R_UseViewport(r_state.buffers0[i]); R_DrawQuad(); } /* blur and combine downsampled images */ R_BlurStack(DOWNSAMPLE_PASSES, r_state.buffers0, r_state.buffers1); /* re-combine the blurred version with the original "glow" image */ R_UseProgram(r_state.combine2_program); R_UseFramebuffer(fbo_bloom0); R_BindTextureForTexUnit(fbo_render->textures[1], &texunit_0); R_BindTextureForTexUnit(r_state.buffers1[0]->textures[0], &texunit_1); R_UseViewport(fbo_screen); R_DrawQuad(); /* draw final result to the screenbuffer */ R_UseFramebuffer(fbo_screen); R_UseProgram(r_state.combine2_program); R_BindTextureForTexUnit(fbo_render->textures[0], &texunit_0); R_BindTextureForTexUnit(fbo_bloom0->textures[0], &texunit_1); R_DrawQuad(); /* cleanup before returning */ R_UseProgram(default_program); R_CheckError(); #ifndef GL_VERSION_ES_CM_1_0 glPopAttrib(); #endif glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); R_CheckError(); /* reset renderbuffer state to what it was before */ R_EnableRenderbuffer(renderBufferState); }
/** * @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 Draws a model in 2d mode (for rendering model data from the ui) * @param[in,out] mi All the needed model information to render the model * @param[in,out] pmi The model information of the parent model. This is used * in those cases, where the model that should get rendered here is placed relativly * to an already existing model in the world. * @param[in] tagname If a parent model is given, a @c tagname is given in most cases, too. It's used * to transform the model location relative to the parent model location again. E.g. a * @c tagname of tag_rweapon will transform the location to the right hand of an actor. * @sa R_DrawAliasModel */ void R_DrawModelDirect (modelInfo_t * mi, modelInfo_t * pmi, const char *tagname) { image_t *skin; mAliasMesh_t *mesh; if (Q_strnull(mi->name)) return; /* register the model */ mi->model = R_FindModel(mi->name); /* check if the model exists */ if (!mi->model) { Com_Printf("No model found for '%s'\n", mi->name); return; } skin = R_AliasModelState(mi->model, &mi->mesh, &mi->frame, &mi->oldframe, &mi->skin); if (skin == NULL) { Com_Printf("Model '%s' is broken\n", mi->name); return; } glPushMatrix(); glScalef(viddef.rx, viddef.ry, (viddef.rx + viddef.ry) / 2); R_Color(mi->color); if (pmi) { /* register the parent model */ pmi->model = R_FindModel(pmi->name); /* transform - the next transform for the child model will be relative from the * parent model location now */ R_TransformModelDirect(pmi); /* tag transformation */ if (tagname) { const mAliasTagOrientation_t *current = NULL; const mAliasTagOrientation_t *old = NULL; R_GetTags(pmi->model, tagname, pmi->frame, pmi->oldframe, ¤t, &old); if (current != NULL && old != NULL) { float interpolated[16]; /* do interpolation */ R_InterpolateTransform(pmi->backlerp, pmi->model->alias.num_frames, current, old, interpolated); /* transform */ glMultMatrixf(interpolated); R_CheckError(); } } } /* transform */ R_TransformModelDirect(mi); /* we have to reenable this here - we are in 2d mode here already */ glEnable(GL_DEPTH_TEST); /* draw it */ R_BindTexture(skin->texnum); /* draw the model */ mesh = &mi->model->alias.meshes[0]; refdef.aliasCount += mesh->num_tris; if (mi->model->alias.num_frames == 1) R_DrawAliasStaticWithReset(mesh, vec4_origin); else R_DrawAliasFrameLerp(&mi->model->alias, mesh, mi->backlerp, mi->frame, mi->oldframe, vec4_origin); /* show model bounding box */ if (r_showbox->integer) R_DrawBoundingBox(mi->model->alias.frames[mi->frame].mins, mi->model->alias.frames[mi->frame].maxs); glDisable(GL_DEPTH_TEST); glPopMatrix(); R_Color(NULL); }
/** * @brief Flares are batched by their texture. Usually, this means one draw operation * for all flares in view. Flare visibility is calculated every few millis, and * flare alpha is ramped up or down depending on the results of the visibility * trace. Flares are also faded according to the angle of their surface to the * view origin. */ void R_DrawFlareSurfaces (const mBspSurfaces_t* surfs, glElementIndex_t* indexPtr) { const image_t* image; int i, j, k, l, m; vec3_t view, verts[4]; vec3_t right, up, upright, downright; float dot, dist, scale, alpha; bool visible; bool oldblend; if (!r_flares->integer) return; if (!surfs->count) return; oldblend = r_state.blend_enabled; R_EnableColorArray(true); R_ResetArrayState(); /** @todo better GL state handling */ glDisable(GL_DEPTH_TEST); R_EnableBlend(true); R_BlendFunc(GL_SRC_ALPHA, GL_ONE); image = r_flaretextures[0]; R_BindTexture(image->texnum); j = k = l = 0; for (i = 0; i < surfs->count; i++) { mBspSurface_t* surf = surfs->surfaces[i]; mBspFlare_t* f; if (surf->frame != r_locals.frame) continue; f = surf->flare; /* bind the flare's texture */ if (f->image != image) { R_DrawArrays(0, l / 3); j = k = l = 0; refdef.batchCount++; image = f->image; R_BindTexture(image->texnum); } /* periodically test visibility to ramp alpha */ if (refdef.time - f->time > 0.02) { if (refdef.time - f->time > 0.5) /* reset old flares */ f->alpha = 0; R_Trace(refdef.viewOrigin, f->origin, 0, MASK_SOLID); visible = refdef.trace.fraction == 1.0; f->alpha += (visible ? 0.03 : -0.15); /* ramp */ if (f->alpha > 0.75) /* clamp */ f->alpha = 0.75; else if (f->alpha < 0) f->alpha = 0.0; f->time = refdef.time; } VectorSubtract(f->origin, refdef.viewOrigin, view); dist = VectorNormalize(view); /* fade according to angle */ dot = DotProduct(surf->normal, view); if (dot > 0) continue; alpha = 0.1 + -dot * r_flares->value; if (alpha > 1.0) alpha = 1.0; alpha = f->alpha * alpha; /* scale according to distance */ scale = f->radius + (f->radius * dist * .0005); VectorScale(r_locals.right, scale, right); VectorScale(r_locals.up, scale, up); VectorAdd(up, right, upright); VectorSubtract(right, up, downright); VectorSubtract(f->origin, downright, verts[0]); VectorAdd(f->origin, upright, verts[1]); VectorAdd(f->origin, downright, verts[2]); VectorSubtract(f->origin, upright, verts[3]); for (m = 0; m < 4; m++) { /* duplicate color data to all 4 verts */ memcpy(&r_state.color_array[j], f->color, sizeof(vec3_t)); r_state.color_array[j + 3] = alpha; j += 4; } /* copy texcoord info */ memcpy(&texunit_diffuse.texcoord_array[k], default_texcoords, sizeof(vec2_t) * 4); k += sizeof(vec2_t) / sizeof(vec_t) * 4; /* and lastly copy the 4 verts */ memcpy(&r_state.vertex_array_3d[l], verts, sizeof(vec3_t) * 4); l += sizeof(vec3_t) / sizeof(vec_t) * 4; } R_DrawArrays(0, l / 3); refdef.batchCount++; R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); R_EnableBlend(oldblend); glEnable(GL_DEPTH_TEST); R_EnableColorArray(false); R_Color(nullptr); }
/** * @brief Draw a model from the battlescape entity list * @sa R_GetEntityLists */ void R_DrawAliasModel (entity_t *e) { mAliasModel_t *mod = &e->model->alias; /* the values are sane here already - see R_GetEntityLists */ const image_t *skin = mod->meshes[e->as.mesh].skins[e->skinnum].skin; int i; float g; vec4_t color = {0.8, 0.8, 0.8, 1.0}; mAliasMesh_t *mesh; /* IR goggles override color for entities that are affected */ if ((refdef.rendererFlags & RDF_IRGOGGLES) && (e->flags & RF_IRGOGGLES)) Vector4Set(e->shell, 1.0, 0.3, 0.3, 1.0); if (e->flags & RF_PULSE) { /* and then adding in a pulse */ const float f = 1.0 + sin((refdef.time + (e->model->alias.meshes[0].num_tris)) * 6.0) * 0.33; VectorScale(color, 1.0 + f, color); } g = 0.0; /* find brightest component */ for (i = 0; i < 3; i++) { if (color[i] > g) /* keep it */ g = color[i]; } /* scale it back to 1.0 */ if (g > 1.0) VectorScale(color, 1.0 / g, color); R_Color(color); assert(skin->texnum > 0); R_BindTexture(skin->texnum); R_EnableGlowMap(skin->glowmap); R_UpdateLightList(e); R_EnableModelLights(e->lights, e->numLights, e->inShadow, true); /** @todo this breaks the encapsulation - don't call CL_* functions from within the renderer code */ if (r_debug_lights->integer) { for (i = 0; i < e->numLights && i < r_dynamic_lights->integer; i++) CL_ParticleSpawn("lightTracerDebug", 0, e->transform.matrix + 12, e->lights[i]->origin); } if (skin->normalmap) R_EnableBumpmap(skin->normalmap); if (skin->specularmap) R_EnableSpecularMap(skin->specularmap, true); if (skin->roughnessmap) R_EnableRoughnessMap(skin->roughnessmap, true); glPushMatrix(); glMultMatrixf(e->transform.matrix); if (VectorNotEmpty(e->scale)) glScalef(e->scale[0], e->scale[1], e->scale[2]); mesh = R_DrawAliasModelBuffer(e); if (r_state.specularmap_enabled) R_EnableSpecularMap(NULL, false); if (r_state.roughnessmap_enabled) R_EnableRoughnessMap(NULL, false); R_EnableModelLights(NULL, 0, false, false); R_EnableGlowMap(NULL); if (r_state.active_normalmap) R_EnableBumpmap(NULL); R_DrawMeshShadow(e, mesh); if (mod->num_frames == 1) R_ResetArraysAfterStaticMeshRender(); glPopMatrix(); /* show model bounding box */ if (r_showbox->integer) R_DrawBoundingBox(mod->frames[e->as.frame].mins, mod->frames[e->as.frame].maxs); R_Color(NULL); }