void Matrix4x4_ConcatScale3( matrix4x4 out, float x, float y, float z ) { matrix4x4 base, temp; Matrix4x4_Copy( base, out ); Matrix4x4_CreateScale3( temp, x, y, z ); Matrix4x4_Concat( out, base, temp ); }
void Matrix4x4_ConcatRotate( matrix4x4 out, float angle, float x, float y, float z ) { matrix4x4 base, temp; Matrix4x4_Copy( base, out ); Matrix4x4_CreateRotate( temp, angle, x, y, z ); Matrix4x4_Concat( out, base, temp ); }
void Matrix4x4_ConcatScale( matrix4x4 out, float x ) { matrix4x4 base, temp; Matrix4x4_Copy( base, out ); Matrix4x4_CreateScale( temp, x ); Matrix4x4_Concat( out, base, temp ); }
void Matrix4x4_ConcatTranslate( matrix4x4 out, float x, float y, float z ) { matrix4x4 base, temp; Matrix4x4_Copy( base, out ); Matrix4x4_CreateTranslate( temp, x, y, z ); Matrix4x4_Concat( out, base, temp ); }
/* ================ R_BeginDrawMirror Setup texture matrix for mirror texture ================ */ void R_BeginDrawMirror( msurface_t *fa ) { matrix4x4 m1, m2, matrix; GLfloat genVector[4][4]; mextrasurf_t *es; int i; es = SURF_INFO( fa, RI.currentmodel ); Matrix4x4_Copy( matrix, es->mirrormatrix ); Matrix4x4_LoadIdentity( m1 ); Matrix4x4_ConcatScale( m1, 0.5f ); Matrix4x4_Concat( m2, m1, matrix ); Matrix4x4_LoadIdentity( m1 ); Matrix4x4_ConcatTranslate( m1, 0.5f, 0.5f, 0.5f ); Matrix4x4_Concat( matrix, m1, m2 ); for( i = 0; i < 4; i++ ) { genVector[0][i] = i == 0 ? 1 : 0; genVector[1][i] = i == 1 ? 1 : 0; genVector[2][i] = i == 2 ? 1 : 0; genVector[3][i] = i == 3 ? 1 : 0; } GL_TexGen( GL_S, GL_OBJECT_LINEAR ); GL_TexGen( GL_T, GL_OBJECT_LINEAR ); GL_TexGen( GL_R, GL_OBJECT_LINEAR ); GL_TexGen( GL_Q, GL_OBJECT_LINEAR ); pglTexGenfv( GL_S, GL_OBJECT_PLANE, genVector[0] ); pglTexGenfv( GL_T, GL_OBJECT_PLANE, genVector[1] ); pglTexGenfv( GL_R, GL_OBJECT_PLANE, genVector[2] ); pglTexGenfv( GL_Q, GL_OBJECT_PLANE, genVector[3] ); GL_LoadTexMatrix( matrix ); }
/* ============= R_SetupGL ============= */ static void R_SetupGL( void ) { if( RI.refdef.waterlevel >= 3 ) { float f; f = sin( cl.time * 0.4f * ( M_PI * 2.7f )); RI.refdef.fov_x += f; RI.refdef.fov_y -= f; } R_SetupModelviewMatrix( &RI.refdef, RI.worldviewMatrix ); R_SetupProjectionMatrix( &RI.refdef, RI.projectionMatrix ); // if( RI.params & RP_MIRRORVIEW ) RI.projectionMatrix[0][0] = -RI.projectionMatrix[0][0]; Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix ); pglScissor( RI.scissor[0], RI.scissor[1], RI.scissor[2], RI.scissor[3] ); pglViewport( RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3] ); pglMatrixMode( GL_PROJECTION ); GL_LoadMatrix( RI.projectionMatrix ); pglMatrixMode( GL_MODELVIEW ); GL_LoadMatrix( RI.worldviewMatrix ); if( RI.params & RP_CLIPPLANE ) { GLdouble clip[4]; mplane_t *p = &RI.clipPlane; clip[0] = p->normal[0]; clip[1] = p->normal[1]; clip[2] = p->normal[2]; clip[3] = -p->dist; pglClipPlane( GL_CLIP_PLANE0, clip ); pglEnable( GL_CLIP_PLANE0 ); } if( RI.params & RP_FLIPFRONTFACE ) GL_FrontFace( !glState.frontFace ); GL_Cull( GL_FRONT ); pglDisable( GL_BLEND ); pglDisable( GL_ALPHA_TEST ); pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); }
/** * @brief Applies translation, rotation, and scale for the specified entity. */ void R_RotateForEntity(const r_entity_t *e) { if (!e) { R_PopMatrix(R_MATRIX_MODELVIEW); return; } R_PushMatrix(R_MATRIX_MODELVIEW); matrix4x4_t modelview; R_GetMatrix(R_MATRIX_MODELVIEW, &modelview); Matrix4x4_Concat(&modelview, &modelview, &e->matrix); R_SetMatrix(R_MATRIX_MODELVIEW, &modelview); }
qboolean R_Shader_StartLightPass( unsigned int lightIndex ) { GLint valid; R_ShaderLight *light = GetLightFromIndex( lightIndex ); matrix4x4_t *worldToViewMatrix = &r_refdef.lightShader.worldToViewMatrix; vec3_t lightPosition, newcolor; float f; assert( light->active == true ); // setup cubemap texture generation if( gl_support_cubemaps ) { matrix4x4_t worldToLightMatrix; matrix4x4_t viewToWorldMatrix; matrix4x4_t viewToLightMatrix; // setup the cubemap qglSelectTextureARB( GL_TEXTURE1_ARB ); glEnable( GL_TEXTURE_CUBE_MAP_ARB ); glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, GL_LoadCubeTexImage( light->cubemapname, false, true ) ); qglSelectTextureARB( GL_TEXTURE0_ARB ); // invert worldToViewMatrix worldToLightMatrix = GetWorldToLightMatrix( light ); Matrix4x4_Invert_Simple( &viewToWorldMatrix, worldToViewMatrix ); Matrix4x4_Concat( &viewToLightMatrix, &worldToLightMatrix, &viewToWorldMatrix ); qglUniformMatrix4fvARB( r_refdef.lightShader.viewToLightMatrix, 1, true, (float *)&viewToLightMatrix.m ); } Matrix4x4_Transform( worldToViewMatrix, light->origin, lightPosition ); //Con_Printf( "Light distance to origin: %f (vs %f)\n", VectorDistance( light->origin, r_refdef.vieworg ), VectorLength( lightPosition ) ); qglUniform3fvARB( r_refdef.lightShader.lightPosition, 1, lightPosition ); f = (light->style >= 0 ? d_lightstylevalue[light->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value; VectorScale(light->color, f, newcolor); qglUniform3fvARB( r_refdef.lightShader.lightColor, 1, newcolor ); qglUniform1fARB( r_refdef.lightShader.lightMaxDistance, light->maxDistance ); qglValidateProgramARB( r_refdef.lightShader.programObject ); qglGetObjectParameterivARB( r_refdef.lightShader.programObject, GL_OBJECT_VALIDATE_STATUS_ARB, &valid ); return valid == true; }
/* ============= R_SetupGL ============= */ static void R_SetupGL( void ) { if( r_underwater_distortion->value && RI.refdef.waterlevel >= 3 ) { float f; f = sin( cl.time * r_underwater_distortion->value * ( M_PI * 2.7f )); RI.refdef.fov_x += f; RI.refdef.fov_y -= f; } R_SetupModelviewMatrix( &RI.refdef, RI.worldviewMatrix ); R_SetupProjectionMatrix( &RI.refdef, RI.projectionMatrix ); // if( RI.params & RP_MIRRORVIEW ) RI.projectionMatrix[0][0] = -RI.projectionMatrix[0][0]; Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix ); if( RP_NORMALPASS( )) { int x, x2, y, y2; // set up viewport (main, playersetup) x = floor( RI.viewport[0] * glState.width / glState.width ); x2 = ceil(( RI.viewport[0] + RI.viewport[2] ) * glState.width / glState.width ); y = floor( glState.height - RI.viewport[1] * glState.height / glState.height ); y2 = ceil( glState.height - ( RI.viewport[1] + RI.viewport[3] ) * glState.height / glState.height ); pglViewport( x, y2, x2 - x, y - y2 ); } else { // envpass, mirrorpass pglViewport( RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3] ); } pglMatrixMode( GL_PROJECTION ); GL_LoadMatrix( RI.projectionMatrix ); pglMatrixMode( GL_MODELVIEW ); GL_LoadMatrix( RI.worldviewMatrix ); if( RI.params & RP_CLIPPLANE ) { GLdouble clip[4]; mplane_t *p = &RI.clipPlane; clip[0] = p->normal[0]; clip[1] = p->normal[1]; clip[2] = p->normal[2]; clip[3] = -p->dist; pglClipPlane( GL_CLIP_PLANE0, clip ); pglEnable( GL_CLIP_PLANE0 ); } if( RI.params & RP_FLIPFRONTFACE ) GL_FrontFace( !glState.frontFace ); GL_Cull( GL_FRONT ); pglDisable( GL_BLEND ); pglDisable( GL_ALPHA_TEST ); pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); }
/* ================ R_DrawMirrors Draw all viewpasess from mirror position Mirror textures will be drawn in normal pass ================ */ void R_DrawMirrors( void ) { ref_instance_t oldRI; mplane_t plane; msurface_t *surf, *surf2; int i, oldframecount; mextrasurf_t *es, *tmp, *mirrorchain; vec3_t forward, right, up; vec3_t origin, angles; matrix4x4 mirrormatrix; cl_entity_t *e; model_t *m; float d; if( !tr.num_mirror_entities ) return; // mo mirrors for this frame oldRI = RI; // make refinst backup oldframecount = tr.framecount; for( i = 0; i < tr.num_mirror_entities; i++ ) { mirrorchain = tr.mirror_entities[i].chain; for( es = mirrorchain; es != NULL; es = es->mirrorchain ) { RI.currententity = e = tr.mirror_entities[i].ent; RI.currentmodel = m = RI.currententity->model; surf = INFO_SURF( es, m ); ASSERT( RI.currententity != NULL ); ASSERT( RI.currentmodel != NULL ); // NOTE: copy mirrortexture and mirrormatrix from another surfaces // from this entity\world that has same planes and reduce number of viewpasses // make sure what we have one pass at least if( es != mirrorchain ) { for( tmp = mirrorchain; tmp != es; tmp = tmp->mirrorchain ) { surf2 = INFO_SURF( tmp, m ); if( !tmp->mirrortexturenum ) continue; // not filled? if( surf->plane->dist != surf2->plane->dist ) continue; if( !VectorCompare( surf->plane->normal, surf2->plane->normal )) continue; // found surface with same plane! break; } if( tmp != es && tmp && tmp->mirrortexturenum ) { // just copy reflection texture from surface with same plane Matrix4x4_Copy( es->mirrormatrix, tmp->mirrormatrix ); es->mirrortexturenum = tmp->mirrortexturenum; continue; // pass skiped } } R_PlaneForMirror( surf, &plane, mirrormatrix ); d = -2.0f * ( DotProduct( RI.vieworg, plane.normal ) - plane.dist ); VectorMA( RI.vieworg, d, plane.normal, origin ); d = -2.0f * DotProduct( RI.vforward, plane.normal ); VectorMA( RI.vforward, d, plane.normal, forward ); VectorNormalize( forward ); d = -2.0f * DotProduct( RI.vright, plane.normal ); VectorMA( RI.vright, d, plane.normal, right ); VectorNormalize( right ); d = -2.0f * DotProduct( RI.vup, plane.normal ); VectorMA( RI.vup, d, plane.normal, up ); VectorNormalize( up ); VectorsAngles( forward, right, up, angles ); angles[ROLL] = -angles[ROLL]; RI.params = RP_MIRRORVIEW|RP_CLIPPLANE|RP_OLDVIEWLEAF; RI.clipPlane = plane; RI.clipFlags |= ( 1<<5 ); RI.frustum[5] = plane; RI.frustum[5].signbits = SignbitsForPlane( RI.frustum[5].normal ); RI.frustum[5].type = PLANE_NONAXIAL; RI.refdef.viewangles[0] = anglemod( angles[0] ); RI.refdef.viewangles[1] = anglemod( angles[1] ); RI.refdef.viewangles[2] = anglemod( angles[2] ); VectorCopy( origin, RI.refdef.vieworg ); VectorCopy( origin, RI.cullorigin ); // put pvsorigin before the mirror plane to avoid get full visibility on world mirrors if( RI.currententity == clgame.entities ) { VectorMA( es->origin, 1.0f, plane.normal, origin ); } else { Matrix4x4_VectorTransform( mirrormatrix, es->origin, origin ); VectorMA( origin, 1.0f, plane.normal, origin ); } VectorCopy( origin, RI.pvsorigin ); // combine two leafs from client and mirror views r_viewleaf = Mod_PointInLeaf( oldRI.pvsorigin, cl.worldmodel->nodes ); r_viewleaf2 = Mod_PointInLeaf( RI.pvsorigin, cl.worldmodel->nodes ); if( GL_Support( GL_ARB_TEXTURE_NPOT_EXT )) { // allow screen size RI.viewport[2] = bound( 96, RI.viewport[2], 1024 ); RI.viewport[3] = bound( 72, RI.viewport[3], 768 ); } else { RI.viewport[2] = NearestPOW( RI.viewport[2], true ); RI.viewport[3] = NearestPOW( RI.viewport[3], true ); RI.viewport[2] = bound( 128, RI.viewport[2], 1024 ); RI.viewport[3] = bound( 64, RI.viewport[3], 512 ); } tr.framecount++; R_RenderScene( &RI.refdef ); r_stats.c_mirror_passes++; es->mirrortexturenum = R_AllocateMirrorTexture(); // create personal projection matrix for mirror if( VectorIsNull( e->origin ) && VectorIsNull( e->angles )) { Matrix4x4_Copy( es->mirrormatrix, RI.worldviewProjectionMatrix ); } else { Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, mirrormatrix ); Matrix4x4_Concat( es->mirrormatrix, RI.projectionMatrix, RI.modelviewMatrix ); } RI = oldRI; // restore ref instance } // clear chain for this entity for( es = mirrorchain; es != NULL; ) { tmp = es->mirrorchain; es->mirrorchain = NULL; es = tmp; } tr.mirror_entities[i].chain = NULL; // done tr.mirror_entities[i].ent = NULL; } r_oldviewleaf = r_viewleaf = NULL; // force markleafs next frame tr.framecount = oldframecount; // restore real framecount tr.num_mirror_entities = 0; tr.num_mirrors_used = 0; }
/** * @brief Draws bounding boxes for all non-linked entities in `ents`. */ static void R_DrawEntityBounds(const r_entities_t *ents, const vec4_t color) { if (!r_draw_entity_bounds->value) { return; } if (ents->count == 0) { return; } R_BindDiffuseTexture(r_image_state.null->texnum); R_EnableColorArray(true); R_BindAttributeInterleaveBuffer(&r_model_state.bound_vertice_buffer, R_ARRAY_MASK_ALL); R_BindAttributeBuffer(R_ARRAY_ELEMENTS, &r_model_state.bound_element_buffer); u8vec4_t bc; ColorDecompose(color, bc); for (int32_t i = 0; i < 8; ++i) { Vector4Set(r_model_state.bound_vertices[i].color, bc[0], bc[1], bc[2], bc[3]); } static matrix4x4_t mat, modelview; R_GetMatrix(R_MATRIX_MODELVIEW, &modelview); for (size_t i = 0; i < ents->count; i++) { const r_entity_t *e = ents->entities[i]; if (e->parent || (e->effects & EF_WEAPON) || !IS_MESH_MODEL(e->model)) { continue; } VectorSet(r_model_state.bound_vertices[0].position, e->mins[0], e->mins[1], e->mins[2]); VectorSet(r_model_state.bound_vertices[1].position, e->maxs[0], e->mins[1], e->mins[2]); VectorSet(r_model_state.bound_vertices[2].position, e->maxs[0], e->maxs[1], e->mins[2]); VectorSet(r_model_state.bound_vertices[3].position, e->mins[0], e->maxs[1], e->mins[2]); VectorSet(r_model_state.bound_vertices[4].position, e->mins[0], e->mins[1], e->maxs[2]); VectorSet(r_model_state.bound_vertices[5].position, e->maxs[0], e->mins[1], e->maxs[2]); VectorSet(r_model_state.bound_vertices[6].position, e->maxs[0], e->maxs[1], e->maxs[2]); VectorSet(r_model_state.bound_vertices[7].position, e->mins[0], e->maxs[1], e->maxs[2]); R_UploadToBuffer(&r_model_state.bound_vertice_buffer, sizeof(r_bound_interleave_vertex_t) * 8, r_model_state.bound_vertices); // draw box const vec_t *origin; if (e->effects & EF_BOB) { origin = e->termination; } else { origin = e->origin; } Matrix4x4_CreateFromEntity(&mat, origin, vec3_origin, e->scale); Matrix4x4_Concat(&mat, &modelview, &mat); R_SetMatrix(R_MATRIX_MODELVIEW, &mat); R_DrawArrays(GL_LINES, 0, (GLint) r_model_state.bound_element_count - 6); // draw origin Matrix4x4_CreateFromEntity(&mat, origin, e->angles, e->scale); Matrix4x4_Concat(&mat, &modelview, &mat); R_SetMatrix(R_MATRIX_MODELVIEW, &mat); R_DrawArrays(GL_LINES, (GLint) r_model_state.bound_element_count - 6, 6); } R_SetMatrix(R_MATRIX_MODELVIEW, &modelview); R_UnbindAttributeBuffer(R_ARRAY_ELEMENTS); R_EnableColorArray(false); R_Color(NULL); }
/* * State: * cl.bob2_smooth * cl.bobfall_speed * cl.bobfall_swing * cl.gunangles_adjustment_highpass * cl.gunangles_adjustment_lowpass * cl.gunangles_highpass * cl.gunangles_prev * cl.gunorg_adjustment_highpass * cl.gunorg_adjustment_lowpass * cl.gunorg_highpass * cl.gunorg_prev * cl.hitgroundtime * cl.lastongroundtime * cl.oldongrounbd * cl.stairsmoothtime * cl.stairsmoothz * cl.calcrefdef_prevtime * Extra input: * cl.movecmd[0].time * cl.movevars_stepheight * cl.movevars_timescale * cl.oldtime * cl.punchangle * cl.punchvector * cl.qw_intermission_angles * cl.qw_intermission_origin * cl.qw_weaponkick * cls.protocol * cl.time * Output: * cl.csqc_viewanglesfromengine * cl.csqc_viewmodelmatrixfromengine * cl.csqc_vieworiginfromengine * r_refdef.view.matrix * viewmodelmatrix_nobob * viewmodelmatrix_withbob */ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewangles, qboolean teleported, qboolean clonground, qboolean clcmdjump, float clstatsviewheight, qboolean cldead, qboolean clintermission, const vec3_t clvelocity) { float vieworg[3], viewangles[3], smoothtime; float gunorg[3], gunangles[3]; matrix4x4_t tmpmatrix; static float viewheightavg; float viewheight; #if 0 // begin of chase camera bounding box size for proper collisions by Alexander Zubov vec3_t camboxmins = {-3, -3, -3}; vec3_t camboxmaxs = {3, 3, 3}; // end of chase camera bounding box size for proper collisions by Alexander Zubov #endif trace_t trace; // react to clonground state changes (for gun bob) if (clonground) { if (!cl.oldonground) cl.hitgroundtime = cl.movecmd[0].time; cl.lastongroundtime = cl.movecmd[0].time; } cl.oldonground = clonground; cl.calcrefdef_prevtime = max(cl.calcrefdef_prevtime, cl.oldtime); VectorClear(gunorg); viewmodelmatrix_nobob = identitymatrix; viewmodelmatrix_withbob = identitymatrix; r_refdef.view.matrix = identitymatrix; // player can look around, so take the origin from the entity, // and the angles from the input system Matrix4x4_OriginFromMatrix(entrendermatrix, vieworg); VectorCopy(clviewangles, viewangles); // calculate how much time has passed since the last V_CalcRefdef smoothtime = bound(0, cl.time - cl.stairsmoothtime, 0.1); cl.stairsmoothtime = cl.time; // fade damage flash if (v_dmg_time > 0) v_dmg_time -= bound(0, smoothtime, 0.1); if (clintermission) { // entity is a fixed camera, just copy the matrix if (cls.protocol == PROTOCOL_QUAKEWORLD) Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, cl.qw_intermission_origin[0], cl.qw_intermission_origin[1], cl.qw_intermission_origin[2], cl.qw_intermission_angles[0], cl.qw_intermission_angles[1], cl.qw_intermission_angles[2], 1); else { r_refdef.view.matrix = *entrendermatrix; Matrix4x4_AdjustOrigin(&r_refdef.view.matrix, 0, 0, clstatsviewheight); } Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix); Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value); Matrix4x4_Copy(&viewmodelmatrix_withbob, &viewmodelmatrix_nobob); VectorCopy(vieworg, cl.csqc_vieworiginfromengine); VectorCopy(viewangles, cl.csqc_viewanglesfromengine); Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix); Matrix4x4_CreateScale(&cl.csqc_viewmodelmatrixfromengine, cl_viewmodel_scale.value); } else { // smooth stair stepping, but only if clonground and enabled if (!clonground || cl_stairsmoothspeed.value <= 0 || teleported) cl.stairsmoothz = vieworg[2]; else { if (cl.stairsmoothz < vieworg[2]) vieworg[2] = cl.stairsmoothz = bound(vieworg[2] - cl.movevars_stepheight, cl.stairsmoothz + smoothtime * cl_stairsmoothspeed.value, vieworg[2]); else if (cl.stairsmoothz > vieworg[2]) vieworg[2] = cl.stairsmoothz = bound(vieworg[2], cl.stairsmoothz - smoothtime * cl_stairsmoothspeed.value, vieworg[2] + cl.movevars_stepheight); } // apply qw weapon recoil effect (this did not work in QW) // TODO: add a cvar to disable this viewangles[PITCH] += cl.qw_weaponkick; // apply the viewofs (even if chasecam is used) // Samual: Lets add smoothing for this too so that things like crouching are done with a transition. viewheight = bound(0, (cl.time - cl.calcrefdef_prevtime) / max(0.0001, cl_smoothviewheight.value), 1); viewheightavg = viewheightavg * (1 - viewheight) + clstatsviewheight * viewheight; vieworg[2] += viewheightavg; if (chase_active.value) { // observing entity from third person. Added "campitch" by Alexander "motorsep" Zubov vec_t camback, camup, dist, campitch, forward[3], chase_dest[3]; camback = chase_back.value; camup = chase_up.value; campitch = chase_pitchangle.value; AngleVectors(viewangles, forward, NULL, NULL); if (chase_overhead.integer) { #if 1 vec3_t offset; vec3_t bestvieworg; #endif vec3_t up; viewangles[PITCH] = 0; AngleVectors(viewangles, forward, NULL, up); // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range) chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup; chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup; chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup; #if 0 #if 1 //trace = CL_TraceLine(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); trace = CL_TraceLine(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); #else //trace = CL_TraceBox(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); #endif VectorCopy(trace.endpos, vieworg); vieworg[2] -= 8; #else // trace from first person view location to our chosen third person view location #if 1 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true); #else trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); #endif VectorCopy(trace.endpos, bestvieworg); offset[2] = 0; for (offset[0] = -16;offset[0] <= 16;offset[0] += 8) { for (offset[1] = -16;offset[1] <= 16;offset[1] += 8) { AngleVectors(viewangles, NULL, NULL, up); chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup + offset[0]; chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup + offset[1]; chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup + offset[2]; #if 1 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true); #else trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); #endif if (bestvieworg[2] > trace.endpos[2]) bestvieworg[2] = trace.endpos[2]; } } bestvieworg[2] -= 8; VectorCopy(bestvieworg, vieworg); #endif viewangles[PITCH] = campitch; } else { if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer) { // look straight down from high above viewangles[PITCH] = 90; camback = 2048; VectorSet(forward, 0, 0, -1); } // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range) dist = -camback - 8; chase_dest[0] = vieworg[0] + forward[0] * dist; chase_dest[1] = vieworg[1] + forward[1] * dist; chase_dest[2] = vieworg[2] + forward[2] * dist + camup; trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true); VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg); } } else { // first person view from entity // angles if (cldead && v_deathtilt.integer) viewangles[ROLL] = v_deathtiltangle.value; if (cl_weaponrecoil.integer > 0) VectorAdd(viewangles, cl.punchangle, viewangles); viewangles[ROLL] += V_CalcRoll(clviewangles, clvelocity); if (v_dmg_time > 0) { viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; } // origin if (cl_weaponrecoil.integer > 0) VectorAdd(vieworg, cl.punchvector, vieworg); if (!cldead) { double xyspeed, bob, bobfall; float cycle; vec_t frametime; frametime = (cl.time - cl.calcrefdef_prevtime) * cl.movevars_timescale; // 1. if we teleported, clear the frametime... the lowpass will recover the previous value then if(teleported) { // try to fix the first highpass; result is NOT // perfect! TODO find a better fix VectorCopy(viewangles, cl.gunangles_prev); VectorCopy(vieworg, cl.gunorg_prev); } // 2. for the gun origin, only keep the high frequency (non-DC) parts, which is "somewhat like velocity" VectorAdd(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass); highpass3_limited(vieworg, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_up_highpass1.value, cl_followmodel_up_limit.value, cl.gunorg_highpass, gunorg); VectorCopy(vieworg, cl.gunorg_prev); VectorSubtract(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass); // in the highpass, we _store_ the DIFFERENCE to the actual view angles... VectorAdd(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass); cl.gunangles_highpass[PITCH] += 360 * floor((viewangles[PITCH] - cl.gunangles_highpass[PITCH]) / 360 + 0.5); cl.gunangles_highpass[YAW] += 360 * floor((viewangles[YAW] - cl.gunangles_highpass[YAW]) / 360 + 0.5); cl.gunangles_highpass[ROLL] += 360 * floor((viewangles[ROLL] - cl.gunangles_highpass[ROLL]) / 360 + 0.5); highpass3_limited(viewangles, frametime*cl_leanmodel_up_highpass1.value, cl_leanmodel_up_limit.value, frametime*cl_leanmodel_side_highpass1.value, cl_leanmodel_side_limit.value, 0, 0, cl.gunangles_highpass, gunangles); VectorCopy(viewangles, cl.gunangles_prev); VectorSubtract(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass); // 3. calculate the RAW adjustment vectors gunorg[0] *= (cl_followmodel.value ? -cl_followmodel_side_speed.value : 0); gunorg[1] *= (cl_followmodel.value ? -cl_followmodel_side_speed.value : 0); gunorg[2] *= (cl_followmodel.value ? -cl_followmodel_up_speed.value : 0); gunangles[PITCH] *= (cl_leanmodel.value ? -cl_leanmodel_up_speed.value : 0); gunangles[YAW] *= (cl_leanmodel.value ? -cl_leanmodel_side_speed.value : 0); gunangles[ROLL] = 0; // 4. perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!) // trick: we must do the lowpass LAST, so the lowpass vector IS the final vector! highpass3(gunorg, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_up_highpass.value, cl.gunorg_adjustment_highpass, gunorg); lowpass3(gunorg, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_up_lowpass.value, cl.gunorg_adjustment_lowpass, gunorg); // we assume here: PITCH = 0, YAW = 1, ROLL = 2 highpass3(gunangles, frametime*cl_leanmodel_up_highpass.value, frametime*cl_leanmodel_side_highpass.value, 0, cl.gunangles_adjustment_highpass, gunangles); lowpass3(gunangles, frametime*cl_leanmodel_up_lowpass.value, frametime*cl_leanmodel_side_lowpass.value, 0, cl.gunangles_adjustment_lowpass, gunangles); // 5. use the adjusted vectors VectorAdd(vieworg, gunorg, gunorg); VectorAdd(viewangles, gunangles, gunangles); // bounded XY speed, used by several effects below xyspeed = bound (0, sqrt(clvelocity[0]*clvelocity[0] + clvelocity[1]*clvelocity[1]), 400); // vertical view bobbing code if (cl_bob.value && cl_bobcycle.value) { // LordHavoc: this code is *weird*, but not replacable (I think it // should be done in QC on the server, but oh well, quake is quake) // LordHavoc: figured out bobup: the time at which the sin is at 180 // degrees (which allows lengthening or squishing the peak or valley) cycle = cl.time / cl_bobcycle.value; cycle -= (int) cycle; if (cycle < cl_bobup.value) cycle = sin(M_PI * cycle / cl_bobup.value); else cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value)); // bob is proportional to velocity in the xy plane // (don't count Z, or jumping messes it up) bob = xyspeed * bound(0, cl_bob.value, 0.05); bob = bob*0.3 + bob*0.7*cycle; vieworg[2] += bob; // we also need to adjust gunorg, or this appears like pushing the gun! // In the old code, this was applied to vieworg BEFORE copying to gunorg, // but this is not viable with the new followmodel code as that would mean // that followmodel would work on the munged-by-bob vieworg and do feedback gunorg[2] += bob; } // horizontal view bobbing code if (cl_bob2.value && cl_bob2cycle.value) { vec3_t bob2vel; vec3_t forward, right, up; float side, front; cycle = cl.time / cl_bob2cycle.value; cycle -= (int) cycle; if (cycle < 0.5) cycle = cos(M_PI * cycle / 0.5); // cos looks better here with the other view bobbing using sin else cycle = cos(M_PI + M_PI * (cycle-0.5)/0.5); bob = bound(0, cl_bob2.value, 0.05) * cycle; // this value slowly decreases from 1 to 0 when we stop touching the ground. // The cycle is later multiplied with it so the view smooths back to normal if (clonground && !clcmdjump) // also block the effect while the jump button is pressed, to avoid twitches when bunny-hopping cl.bob2_smooth = 1; else { if(cl.bob2_smooth > 0) cl.bob2_smooth -= bound(0, cl_bob2smooth.value, 1); else cl.bob2_smooth = 0; } // calculate the front and side of the player between the X and Y axes AngleVectors(viewangles, forward, right, up); // now get the speed based on those angles. The bounds should match the same value as xyspeed's side = bound(-400, DotProduct (clvelocity, right) * cl.bob2_smooth, 400); front = bound(-400, DotProduct (clvelocity, forward) * cl.bob2_smooth, 400); VectorScale(forward, bob, forward); VectorScale(right, bob, right); // we use side with forward and front with right, so the bobbing goes // to the side when we walk forward and to the front when we strafe VectorMAMAM(side, forward, front, right, 0, up, bob2vel); vieworg[0] += bob2vel[0]; vieworg[1] += bob2vel[1]; // we also need to adjust gunorg, or this appears like pushing the gun! // In the old code, this was applied to vieworg BEFORE copying to gunorg, // but this is not viable with the new followmodel code as that would mean // that followmodel would work on the munged-by-bob vieworg and do feedback gunorg[0] += bob2vel[0]; gunorg[1] += bob2vel[1]; } // fall bobbing code // causes the view to swing down and back up when touching the ground if (cl_bobfall.value && cl_bobfallcycle.value) { if (!clonground) { cl.bobfall_speed = bound(-400, clvelocity[2], 0) * bound(0, cl_bobfall.value, 0.1); if (clvelocity[2] < -cl_bobfallminspeed.value) cl.bobfall_swing = 1; else cl.bobfall_swing = 0; // TODO really? } else { cl.bobfall_swing = max(0, cl.bobfall_swing - cl_bobfallcycle.value * frametime); bobfall = sin(M_PI * cl.bobfall_swing) * cl.bobfall_speed; vieworg[2] += bobfall; gunorg[2] += bobfall; } } // gun model bobbing code if (cl_bobmodel.value) { // calculate for swinging gun model // the gun bobs when running on the ground, but doesn't bob when you're in the air. // Sajt: I tried to smooth out the transitions between bob and no bob, which works // for the most part, but for some reason when you go through a message trigger or // pick up an item or anything like that it will momentarily jolt the gun. vec3_t forward, right, up; float bspeed; float s; float t; s = cl.time * cl_bobmodel_speed.value; if (clonground) { if (cl.time - cl.hitgroundtime < 0.2) { // just hit the ground, speed the bob back up over the next 0.2 seconds t = cl.time - cl.hitgroundtime; t = bound(0, t, 0.2); t *= 5; } else t = 1; } else { // recently left the ground, slow the bob down over the next 0.2 seconds t = cl.time - cl.lastongroundtime; t = 0.2 - bound(0, t, 0.2); t *= 5; } bspeed = xyspeed * 0.01f; AngleVectors (gunangles, forward, right, up); bob = bspeed * cl_bobmodel_side.value * cl_viewmodel_scale.value * sin (s) * t; VectorMA (gunorg, bob, right, gunorg); bob = bspeed * cl_bobmodel_up.value * cl_viewmodel_scale.value * cos (s * 2) * t; VectorMA (gunorg, bob, up, gunorg); } } } // calculate a view matrix for rendering the scene if (v_idlescale.value) { viewangles[0] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; viewangles[1] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; viewangles[2] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; } Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 1); // calculate a viewmodel matrix for use in view-attached entities Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix); Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value); Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix_withbob, gunorg[0], gunorg[1], gunorg[2], gunangles[0], gunangles[1], gunangles[2], cl_viewmodel_scale.value); VectorCopy(vieworg, cl.csqc_vieworiginfromengine); VectorCopy(viewangles, cl.csqc_viewanglesfromengine); Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix); Matrix4x4_Concat(&cl.csqc_viewmodelmatrixfromengine, &tmpmatrix, &viewmodelmatrix_withbob); } cl.calcrefdef_prevtime = cl.time; }