/** * @brief Developer tool for viewing BSP vertex normals. Only Phong interpolated * surfaces show their normals when r_shownormals > 1. */ void R_DrawBspNormals (int tile) { int i, j, k; const mBspSurface_t* surf; const mBspModel_t* bsp; const vec4_t color = {1.0, 0.0, 0.0, 1.0}; if (!r_shownormals->integer) return; R_EnableTexture(&texunit_diffuse, false); R_ResetArrayState(); /* default arrays */ R_Color(color); k = 0; bsp = &r_mapTiles[tile]->bsp; surf = bsp->surfaces; for (i = 0; i < bsp->numsurfaces; i++, surf++) { if (surf->frame != r_locals.frame) continue; /* not visible */ if (surf->texinfo->flags & SURF_WARP) continue; /* don't care */ if (r_shownormals->integer > 1 && !(surf->texinfo->flags & SURF_PHONG)) continue; /* don't care */ /* avoid overflows, draw in batches */ if (k > r_state.array_size - 512) { glDrawArrays(GL_LINES, 0, k / 3); k = 0; refdef.batchCount++; } for (j = 0; j < surf->numedges; j++) { vec3_t end; const GLfloat* vertex = &bsp->verts[(surf->index + j) * 3]; const GLfloat* normal = &bsp->normals[(surf->index + j) * 3]; VectorMA(vertex, 12.0, normal, end); memcpy(&r_state.vertex_array_3d[k], vertex, sizeof(vec3_t)); memcpy(&r_state.vertex_array_3d[k + 3], end, sizeof(vec3_t)); k += sizeof(vec3_t) / sizeof(vec_t) * 2; R_ReallocateStateArrays(k); } } glDrawArrays(GL_LINES, 0, k / 3); refdef.batchCount++; R_EnableTexture(&texunit_diffuse, true); R_Color(nullptr); }
/* * @brief */ void R_DrawCoronas(void) { if (!r_coronas->value) return; if (!r_view.num_coronas) return; R_EnableTexture(&texunit_diffuse, false); R_EnableColorArray(true); R_ResetArrayState(); R_BlendFunc(GL_SRC_ALPHA, GL_ONE); for (uint16_t i = 0; i < r_view.num_coronas; i++) { const r_corona_t *c = &r_view.coronas[i]; const vec_t f = c->radius * c->flicker * sin(0.09 * r_view.time); // use at least 12 verts, more for larger coronas const uint16_t num_verts = Clamp(c->radius / 8.0, 12, 64); memcpy(&r_state.color_array[0], c->color, sizeof(vec3_t)); r_state.color_array[3] = r_coronas->value; // set origin color // and the corner colors memset(&r_state.color_array[4], 0, num_verts * 2 * sizeof(vec4_t)); memcpy(&r_state.vertex_array_3d[0], c->origin, sizeof(vec3_t)); uint32_t vert_index = 3; // and the origin for (uint16_t j = 0; j <= num_verts; j++) { // now draw the corners const vec_t a = j / (vec_t) num_verts * M_PI * 2; vec3_t v; VectorCopy(c->origin, v); VectorMA(v, cos(a) * (c->radius + f), r_view.right, v); VectorMA(v, sin(a) * (c->radius + f), r_view.up, v); memcpy(&r_state.vertex_array_3d[vert_index], v, sizeof(vec3_t)); vert_index += 3; } glDrawArrays(GL_TRIANGLE_FAN, 0, vert_index / 3); } R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); R_EnableColorArray(false); R_EnableTexture(&texunit_diffuse, true); R_Color(NULL); }
/** * @brief Restores renderer state for the given entity. */ static void R_ResetMeshShellState_default(const r_entity_t *e) { if (e->effects & EF_WEAPON) { R_DepthRange(0.0, 1.0); } R_RotateForEntity(NULL); R_ResetArrayState(); R_UseInterpolation(0.0); }
/* * @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(); }
/** * @param[in] drawFunc The function pointer to the surface draw function * @param[in] surfType The primary rendering type of the surface */ static void R_RenderBspRRefs (drawSurfaceFunc drawFunc, surfaceArrayType_t surfType) { glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); /* our triangles are backwards to what OpenGL expects, so tell it to render only back faces */ for (int i = 0; i < numBspRRefs; i++) { const bspRenderRef_t* const bspRR = &bspRRefs[i]; const mBspModel_t* const bsp = bspRR->bsp; const mBspModel_t* const tile = &r_mapTiles[bsp->maptile]->bsp; /* This is required to find the tile (world) bsp model to which arrays belong (submodels do not own arrays, but use world model ones) */ glElementIndex_t* indexPtr; if (!bsp->sorted_surfaces[surfType]->count) continue; R_SetArrayState(tile); /* Vertex buffers are nullptr-based, arrays are not */ if (qglBindBuffer && r_vertexbuffers->integer) indexPtr = nullptr; else indexPtr = tile->indexes; glPushMatrix(); glTranslatef(bspRR->origin[0], bspRR->origin[1], bspRR->origin[2]); glRotatef(bspRR->angles[YAW], 0, 0, 1); glRotatef(bspRR->angles[PITCH], 0, 1, 0); glRotatef(bspRR->angles[ROLL], 1, 0, 0); drawFunc(bsp->sorted_surfaces[surfType], indexPtr); /** @todo make it work again; also reimplement r_showbox 2 */ #if 0 /* show model bounding box */ if (r_showbox->integer) { const model_t* model = bspRR->bsp; R_DrawBoundingBox(model->mins, model->maxs); } #endif glPopMatrix(); } /* and restore array pointers */ R_ResetArrayState(); glCullFace(GL_BACK); glDisable(GL_CULL_FACE); }
static mAliasMesh_t* R_DrawAliasModelBuffer (entity_t *e) { mAliasModel_t *mod = &e->model->alias; mAliasMesh_t* lodMesh; R_ResetArrayState(); /** @todo what about the origin of a tagged model here? */ lodMesh = R_GetLevelOfDetailForModel(e->origin, mod); refdef.aliasCount += lodMesh->num_tris; if (mod->num_frames == 1) R_DrawAliasStatic(lodMesh, e->shell); else R_DrawAliasFrameLerp(mod, lodMesh, e->as.backlerp, e->as.frame, e->as.oldframe, e->shell); return lodMesh; }
/* * @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_DrawCoronas (void) { int i, j, k; vec3_t v; if (!r_coronas->integer) return; if (!refdef.numCoronas) return; R_EnableTexture(&texunit_diffuse, false); R_EnableColorArray(true); R_ResetArrayState(); R_BlendFunc(GL_ONE, GL_ONE); for (k = 0; k < refdef.numCoronas; k++) { const corona_t *c = &refdef.coronas[k]; int verts, vertind; if (!c->radius) continue; /* use at least 12 verts, more for larger coronas */ verts = 12 + c->radius / 8; memcpy(&r_state.color_array[0], c->color, sizeof(vec3_t)); r_state.color_array[3] = 1.0f; /* set origin color */ /* and the corner colors */ memset(&r_state.color_array[4], 0, verts * 2 * sizeof(vec4_t)); memcpy(&r_state.vertex_array_3d[0], c->org, sizeof(vec3_t)); vertind = 3; /* and the origin */ for (i = verts; i >= 0; i--) { /* now draw the corners */ const float a = (M_PI * 2 / verts) * i; for (j = 0; j < 3; j++) v[j] = c->org[j] + r_locals.right[j] * (float) cos(a) * c->radius + r_locals.up[j] * (float) sin(a) * c->radius; memcpy(&r_state.vertex_array_3d[vertind], v, sizeof(vec3_t)); vertind += 3; } glDrawArrays(GL_TRIANGLE_FAN, 0, vertind / 3); refdef.batchCount++; } R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); R_EnableColorArray(false); R_EnableTexture(&texunit_diffuse, true); R_Color(nullptr); }
/* * @brief Main entry point for drawing the scene (world and entities). */ void R_DrawView(void) { R_UpdateFrustum(); R_UpdateVis(); R_MarkBspSurfaces(); R_EnableFog(true); R_DrawSkyBox(); // wait for the client to populate our lights array Thread_Wait(&r_view.thread); // now dispatch another thread to cull entities while we draw the world r_view.thread = Thread_Create(R_CullEntities, NULL); R_MarkLights(); const r_sorted_bsp_surfaces_t *surfs = r_model_state.world->bsp->sorted_surfaces; R_DrawOpaqueBspSurfaces(&surfs->opaque); R_DrawOpaqueWarpBspSurfaces(&surfs->opaque_warp); R_DrawAlphaTestBspSurfaces(&surfs->alpha_test); R_EnableBlend(true); R_DrawBackBspSurfaces(&surfs->back); R_DrawMaterialBspSurfaces(&surfs->material); R_DrawFlareBspSurfaces(&surfs->flare); R_EnableBlend(false); R_DrawBspNormals(); // ensure the thread has finished culling entities Thread_Wait(&r_view.thread); // now dispatch another thread to update particles while we draw entities r_view.thread = Thread_Create(R_UpdateParticles, NULL); R_DrawEntities(); R_EnableBlend(true); R_DrawBspLights(); R_DrawBlendBspSurfaces(&surfs->blend); R_DrawBlendWarpBspSurfaces(&surfs->blend_warp); // ensure the thread has finished updating particles Thread_Wait(&r_view.thread); R_DrawParticles(); R_EnableFog(false); R_DrawCoronas(); R_DrawBspLeafs(); R_EnableBlend(false); R_ResetArrayState(); }
/** * @brief Iterates the specified surfaces list, updating materials as they are * encountered, and rendering all visible stages. State is lazily managed * throughout the iteration, so there is a concerted effort to restore the * state after all surface stages have been rendered. */ void R_DrawMaterialSurfaces (const mBspSurfaces_t *surfs, GLushort *indexPtr) { int i; if (!r_materials->integer || r_wire->integer) return; if (!surfs->count) return; assert(r_state.blend_enabled); /** @todo - integrate BSP lighting with model lighting */ R_EnableModelLights(nullptr, 0, false, false); R_EnableColorArray(true); R_ResetArrayState(); R_EnableColorArray(false); R_EnableLighting(nullptr, false); R_EnableTexture(&texunit_lightmap, false); #ifndef GL_VERSION_ES_CM_1_0 glEnable(GL_POLYGON_OFFSET_FILL); #endif glPolygonOffset(-1.f, -1.f); glMatrixMode(GL_TEXTURE); /* some stages will manipulate texcoords */ for (i = 0; i < surfs->count; i++) { materialStage_t *s; mBspSurface_t *surf = surfs->surfaces[i]; material_t *m = &surf->texinfo->image->material; int j = -1; if (surf->frame != r_locals.frame) continue; R_UpdateMaterial(m); for (s = m->stages; s; s = s->next, j--) { if (!(s->flags & STAGE_RENDER)) continue; R_SetSurfaceStageState(surf, s); R_DrawSurfaceStage(surf, s); } } R_Color(nullptr); /* polygon offset parameters */ glPolygonOffset(0.0, 0.0); #ifndef GL_VERSION_ES_CM_1_0 glDisable(GL_POLYGON_OFFSET_FILL); #endif glLoadIdentity(); glMatrixMode(GL_MODELVIEW); R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); R_EnableFog(true); R_EnableColorArray(false); R_EnableTexture(&texunit_lightmap, false); R_EnableBumpmap(nullptr); R_EnableLighting(nullptr, false); R_EnableGlowMap(nullptr); R_Color(nullptr); }
/* * @brief Main entry point for drawing the scene (world and entities). */ void R_DrawView(void) { R_UpdateFrustum(); R_UpdateVis(); R_MarkBspSurfaces(); R_EnableFog(true); R_DrawSkyBox(); // wait for the client to fully populate the scene Thread_Wait(r_view.thread); // dispatch threads to cull entities and sort elements while we draw the world thread_t *cull_entities = Thread_Create(R_CullEntities, NULL); thread_t *sort_elements = Thread_Create(R_SortElements, NULL); R_MarkLights(); const r_sorted_bsp_surfaces_t *surfs = r_model_state.world->bsp->sorted_surfaces; R_DrawOpaqueBspSurfaces(&surfs->opaque); R_DrawOpaqueWarpBspSurfaces(&surfs->opaque_warp); R_DrawAlphaTestBspSurfaces(&surfs->alpha_test); R_EnableBlend(true); R_DrawBackBspSurfaces(&surfs->back); R_DrawMaterialBspSurfaces(&surfs->material); R_DrawFlareBspSurfaces(&surfs->flare); R_EnableBlend(false); // wait for entity culling to complete Thread_Wait(cull_entities); R_DrawEntities(); R_EnableBlend(true); // wait for element sorting to complete Thread_Wait(sort_elements); R_DrawElements(); R_EnableFog(false); R_DrawDeveloperTools(); R_DrawCoronas(); R_EnableBlend(false); R_ResetArrayState(); #if 0 vec3_t tmp; VectorMA(r_view.origin, MAX_WORLD_DIST, r_view.forward, tmp); cm_trace_t tr = Cl_Trace(r_view.origin, tmp, NULL, NULL, cl.client_num + 1, MASK_SOLID); if (tr.fraction > 0.0 && tr.fraction < 1.0) { Com_Print("%s: %d: %s\n", tr.surface->name, tr.plane.num, vtos(tr.plane.normal)); } #endif }
/** * @sa R_BeginFrame * @sa R_EndFrame */ void R_RenderFrame (void) { R_Setup3D(); /* activate wire mode */ if (r_wire->integer) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) { int tile; if (r_threads->integer) { while (r_threadstate.state != THREAD_RENDERER) Sys_Sleep(0); r_threadstate.state = THREAD_CLIENT; } else { R_SetupFrustum(); /* draw brushes on current worldlevel */ R_GetLevelSurfaceLists(); } R_UpdateSustainedLights(); R_CheckError(); for (tile = 0; tile < r_numMapTiles; tile++) { const model_t *mapTile = r_mapTiles[tile]; const mBspModel_t *bsp = &mapTile->bsp; R_AddBspRRef(bsp, vec3_origin, vec3_origin, false); } R_GetEntityLists(); R_EnableFog(true); R_RenderOpaqueBspRRefs(); R_RenderOpaqueWarpBspRRefs(); R_DrawOpaqueMeshEntities(r_opaque_mesh_entities); R_RenderAlphaTestBspRRefs(); R_EnableBlend(true); R_RenderMaterialBspRRefs(); R_EnableFog(false); R_RenderBlendBspRRefs(); R_RenderBlendWarpBspRRefs(); R_DrawBlendMeshEntities(r_blend_mesh_entities); R_EnableFog(true); R_RenderFlareBspRRefs(); R_EnableFog(false); if (r_debug_lights->integer) { int i; for (i = 0; i < refdef.numStaticLights; i++) { const light_t *l = &refdef.staticLights[i]; R_AddCorona(l->origin, l->radius, l->color); } for (i = 0; i < refdef.numDynamicLights; i++) { const light_t *l = &refdef.dynamicLights[i]; R_AddCorona(l->origin, l->radius, l->color); } } R_DrawCoronas(); R_EnableBlend(false); for (tile = 0; tile < r_numMapTiles; tile++) { R_DrawBspNormals(tile); } R_Color(NULL); R_DrawSpecialEntities(r_special_entities); R_DrawNullEntities(r_null_entities); R_DrawEntityEffects(); } else { glClear(GL_DEPTH_BUFFER_BIT); R_GetEntityLists(); R_RenderOpaqueBspRRefs(); R_RenderOpaqueWarpBspRRefs(); R_DrawOpaqueMeshEntities(r_opaque_mesh_entities); R_RenderAlphaTestBspRRefs(); R_EnableBlend(true); R_RenderMaterialBspRRefs(); R_RenderBlendBspRRefs(); R_RenderBlendWarpBspRRefs(); R_DrawBlendMeshEntities(r_blend_mesh_entities); R_RenderFlareBspRRefs(); R_EnableBlend(false); R_Color(NULL); R_DrawSpecialEntities(r_special_entities); R_DrawNullEntities(r_null_entities); R_DrawEntityEffects(); } R_EnableBlend(true); R_DrawParticles(); R_EnableBlend(false); /* leave wire mode again */ if (r_wire->integer) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); R_DrawBloom(); R_DrawBoundingBoxes(); R_ResetArrayState(); /* go back into 2D mode for hud and the like */ R_Setup2D(); R_CheckError(); }
/** * @brief Re-initializes OpenGL state machine, all textures and renderer variables, this needed when application is put to background on Android. */ void R_ReinitOpenglContext (void) { /* De-allocate old GL state, these functinos will call glDeleteTexture(), so they should go before everything else */ R_FontCleanCache(); R_ShutdownFBObjects(); R_ShutdownPrograms(); R_BeginBuildingLightmaps(); /* This function will also call glDeleteTexture() */ /* Re-initialize GL state */ R_SetDefaultState(); R_InitPrograms(); R_ResetArrayState(); /* Re-upload all textures */ R_InitMiscTexture(); R_ReloadImages(); /* Re-upload other GL stuff */ R_InitFBObjects(); R_UpdateDefaultMaterial("", "", "", NULL); /* Re-upload the battlescape terrain geometry */ if (!qglBindBuffer) return; for (int tile = 0; tile < r_numMapTiles; tile++) { model_t *mod = r_mapTiles[tile]; int vertind = 0, coordind = 0, tangind = 0; mBspSurface_t *surf = mod->bsp.surfaces; for (int i = 0; i < mod->bsp.numsurfaces; i++, surf++) { vertind += 3 * surf->numedges; coordind += 2 * surf->numedges; tangind += 4 * surf->numedges; } qglGenBuffers(1, &mod->bsp.vertex_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.vertex_buffer); qglBufferData(GL_ARRAY_BUFFER, vertind * sizeof(GLfloat), mod->bsp.verts, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.texcoord_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.texcoord_buffer); qglBufferData(GL_ARRAY_BUFFER, coordind * sizeof(GLfloat), mod->bsp.texcoords, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.lmtexcoord_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.lmtexcoord_buffer); qglBufferData(GL_ARRAY_BUFFER, coordind * sizeof(GLfloat), mod->bsp.lmtexcoords, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.normal_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.normal_buffer); qglBufferData(GL_ARRAY_BUFFER, vertind * sizeof(GLfloat), mod->bsp.normals, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.tangent_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.tangent_buffer); qglBufferData(GL_ARRAY_BUFFER, tangind * sizeof(GLfloat), mod->bsp.tangents, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.index_buffer); qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mod->bsp.index_buffer); qglBufferData(GL_ELEMENT_ARRAY_BUFFER, mod->bsp.numIndexes * sizeof(GLushort), mod->bsp.indexes, GL_STATIC_DRAW); qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); for (int i = 0; i < mod->bsp.numsurfaces; i++) R_CreateSurfaceLightmap(&mod->bsp.surfaces[i]); } R_EndBuildingLightmaps(); qglBindBuffer(GL_ARRAY_BUFFER, 0); }
/** * @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 Draws weather effects */ void Weather::render (void) { /* Don't play the weather particles if the user doesn't want them */ if (!Cvar_GetInteger("cl_particleweather")) { return; } GLfloat prtPos[3 * 4 * Weather::MAX_PARTICLES]; GLfloat prtTexcoord[2 * 4 * Weather::MAX_PARTICLES]; GLushort prtIndex[3 * 2 * Weather::MAX_PARTICLES]; size_t prtCount = 0; //const float splashTimeScale = 0.001f / splashTime; /* from msec to 1/sec units, so division is done only once per frame */ /** @todo shadowcasting at least for the sunlight */ #if 0 // disabled because of bizarre colors vec4_t prtColor = {color[0] * (refdef.ambientColor[0] + refdef.sunDiffuseColor[0]), color[1] * (refdef.ambientColor[1] + refdef.sunDiffuseColor[1]), color[2] * (refdef.ambientColor[2] + refdef.sunDiffuseColor[2]), color[3]}; #else vec_t prtBrightness = VectorLength(refdef.ambientColor) + VectorLength(refdef.sunDiffuseColor); /** @todo proper color to brightness mapping */ vec4_t prtColor = {color[0] * prtBrightness, color[1] * prtBrightness, color[2] * prtBrightness, color[3]}; /* alpha is not to be scaled */ #endif for (size_t i = 0; i < Weather::MAX_PARTICLES; i++) { Weather::particle &prt = particles[i]; if (prt.ttl < 0) continue; /* if particle is alive, construct a camera-facing quad */ GLfloat x, y, z; GLfloat dx, dy, dz; GLfloat thisParticleSize = particleSize; if (prt.vz == 0 && splashTime > 0) { /* splash particle, do zoom and other things */ /** @todo alpha decay */ const float splashFactor = prt.ttl / 500.0f;//1.0f - prt.ttl * splashTimeScale; thisParticleSize *= (splashSize - 1.0f) * splashFactor + 1.0f; dx = dy = thisParticleSize; } else { dx = i & 1 ? thisParticleSize* 2 : thisParticleSize; dy = i & 1 ? thisParticleSize : thisParticleSize * 2 ; /** @todo make a proper projection instead of this hack */ } x = prt.x; y = prt.y; z = prt.z; dz = smearLength * prt.vz * 0.5; /** @todo should use proper velocity vector ... or maybe not */ /* construct a particle geometry; */ GLfloat *pos = &prtPos[3 * 4 * prtCount]; GLfloat *texcoord = &prtTexcoord[2 * 4 * prtCount]; GLushort *idx = &prtIndex[3 * 2 * prtCount]; /** @todo actually rotate billboard in the correct position */ pos[0] = x - dx; pos[1] = y - dy; pos[2] = z + dz * 2; pos[3] = x + dx; pos[4] = y - dy; pos[5] = z + dz *2; pos[6] = x + dx; pos[7] = y + dy; pos[8] = z; pos[9] = x - dx; pos[10] = y + dy; pos[11] = z; texcoord[0] = 0; texcoord[1] = 0; /* upper left vertex */ texcoord[2] = 1.0f; texcoord[3] = 0; /* upper right vertex */ texcoord[4] = 1.0f; texcoord[5] = 1.0f; /* bottom right vertex */ texcoord[6] = 0; texcoord[7] = 1.0f; /* bottom left vertex */ idx[0] = 4 * prtCount; idx[1] = 4 * prtCount + 1; idx[2] = 4 * prtCount + 2; idx[3] = 4 * prtCount + 2; idx[4] = 4 * prtCount + 3; idx[5] = 4 * prtCount; prtCount++; } if (prtCount < 1) return; /* draw the generated array */ R_Color(prtColor); glBindTexture(GL_TEXTURE_2D, wpTexture()); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, prtPos); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, prtTexcoord); R_EnableBlend(true); glDrawElements(GL_TRIANGLES, prtCount * 3 * 2, GL_UNSIGNED_SHORT, prtIndex); R_EnableBlend(false); R_ResetArrayState(); R_Color(nullptr); refdef.batchCount++; }