void R_Sky(void) { Matrix4x4_CreateFromQuakeEntity(&skymatrix, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], 0, 0, 0, r_refdef.farclip * (0.5f / 16.0f)); Matrix4x4_Invert_Simple(&skyinversematrix, &skymatrix); if (skyrendersphere) { // this does not modify depth buffer R_SkySphere(); } else if (skyrenderbox) { // this does not modify depth buffer R_SkyBox(); } /* this will be skyroom someday else { // this modifies the depth buffer so we have to clear it afterward //R_SkyRoom(); // clear the depthbuffer that was used while rendering the skyroom //GL_Clear(GL_DEPTH_BUFFER_BIT); } */ }
/** * Transforms a point by the inverse of the world-model matrix for the * specified entity. */ void R_TransformForEntity (const entity_t* e, const vec3_t in, vec3_t out) { matrix4x4_t tmp, mat; Matrix4x4_CreateFromQuakeEntity(&tmp, e->origin[0], e->origin[1], e->origin[2], e->angles[0], e->angles[1], e->angles[2], e->getScaleX()); Matrix4x4_Invert_Simple(&mat, &tmp); Matrix4x4_Transform(&mat, in, out); }
/** * @brief Applies any configuration and tag alignment, populating the model-view * matrix for the entity in the process. */ void R_SetMatrixForEntity(r_entity_t *e) { if (e->parent) { vec3_t forward; if (!IS_MESH_MODEL(e->model)) { Com_Warn("Invalid model for linked entity\n"); return; } const r_entity_t *p = e->parent; while (p->parent) { p = p->parent; } AngleVectors(p->angles, forward, NULL, NULL); VectorClear(e->origin); VectorClear(e->angles); Matrix4x4_CreateFromEntity(&e->matrix, e->origin, e->angles, e->scale); R_ApplyMeshModelTag(e); R_ApplyMeshModelConfig(e); Matrix4x4_Invert_Simple(&e->inverse_matrix, &e->matrix); Matrix4x4_Transform(&e->matrix, vec3_origin, e->origin); Matrix4x4_Transform(&e->matrix, vec3_forward, forward); VectorAngles(forward, e->angles); return; } Matrix4x4_CreateFromEntity(&e->matrix, e->origin, e->angles, e->scale); if (IS_MESH_MODEL(e->model)) { R_ApplyMeshModelConfig(e); } Matrix4x4_Invert_Simple(&e->inverse_matrix, &e->matrix); }
/** * @brief */ static void Cl_ParseBaseline(void) { static entity_state_t null_state; const uint16_t number = Net_ReadShort(&net_message); const uint16_t bits = Net_ReadShort(&net_message); cl_entity_t *ent = &cl.entities[number]; Net_ReadDeltaEntity(&net_message, &null_state, &ent->baseline, number, bits); // initialize clipping matrices if (ent->baseline.solid) { if (ent->baseline.solid == SOLID_BSP) { Matrix4x4_CreateFromEntity(&ent->matrix, ent->baseline.origin, ent->baseline.angles, 1.0); Matrix4x4_Invert_Simple(&ent->inverse_matrix, &ent->matrix); } else { // bounding-box entities Matrix4x4_CreateFromEntity(&ent->matrix, ent->baseline.origin, vec3_origin, 1.0); Matrix4x4_Invert_Simple(&ent->inverse_matrix, &ent->matrix); } } }
void recalcLightViewMats(light_t *l){ // printf("updatin"); //todo if it is attached, just grab the attached matrix (same as entities do it) Matrix4x4_CreateRotate(&l->view, l->angle[2], 0.0f, 0.0f, 1.0f); Matrix4x4_ConcatRotate(&l->view, l->angle[0], 1.0f, 0.0f, 0.0f); Matrix4x4_ConcatRotate(&l->view, l->angle[1], 0.0f, 1.0f, 0.0f); Matrix4x4_ConcatTranslate(&l->view, -l->pos[0], -l->pos[1], -l->pos[2]); // Matrix4x4_CreateFromQuakeEntity(&l->cam, l->pos[0], l->pos[1], l->pos[2], l->angle[2], l->angle[1], l->angle[0], 1.0); // Matrix4x4_CreateFromQuakeEntity(&l->cam, l->pos[0], l->pos[1], l->pos[2], l->angle[2], l->angle[1], l->angle[0], l->scale); Matrix4x4_Invert_Simple(&l->cam, &l->view); //temp? hack }
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; }
/* ================== World_TransformAABB ================== */ void World_TransformAABB( matrix4x4 transform, const vec3_t mins, const vec3_t maxs, vec3_t outmins, vec3_t outmaxs ) { vec3_t p1, p2; matrix4x4 itransform; int i; if( !outmins || !outmaxs ) return; Matrix4x4_Invert_Simple( itransform, transform ); ClearBounds( outmins, outmaxs ); // compute a full bounding box for( i = 0; i < 8; i++ ) { p1[0] = ( i & 1 ) ? mins[0] : maxs[0]; p1[1] = ( i & 2 ) ? mins[1] : maxs[1]; p1[2] = ( i & 4 ) ? mins[2] : maxs[2]; p2[0] = DotProduct( p1, itransform[0] ); p2[1] = DotProduct( p1, itransform[1] ); p2[2] = DotProduct( p1, itransform[2] ); if( p2[0] < outmins[0] ) outmins[0] = p2[0]; if( p2[0] > outmaxs[0] ) outmaxs[0] = p2[0]; if( p2[1] < outmins[1] ) outmins[1] = p2[1]; if( p2[1] > outmaxs[1] ) outmaxs[1] = p2[1]; if( p2[2] < outmins[2] ) outmins[2] = p2[2]; if( p2[2] > outmaxs[2] ) outmaxs[2] = p2[2]; } // sanity check for( i = 0; i < 3; i++ ) { if( outmins[i] > outmaxs[i] ) { MsgDev( D_ERROR, "World_TransformAABB: backwards mins/maxs\n" ); VectorClear( outmins ); VectorClear( outmaxs ); return; } } }
trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities) #endif { vec3_t hullmins, hullmaxs; int i, bodysupercontents; int passedictprog; qboolean pointtrace; prvm_edict_t *traceowner, *touch; trace_t trace; // bounding box of entire move area vec3_t clipboxmins, clipboxmaxs; // size of the moving object vec3_t clipmins, clipmaxs; // size when clipping against monsters vec3_t clipmins2, clipmaxs2; // start and end origin of move vec3_t clipstart, clipend; // trace results trace_t cliptrace; // matrices to transform into/out of other entity's space matrix4x4_t matrix, imatrix; // model of other entity dp_model_t *model; // list of entities to test for collisions int numtouchedicts; static prvm_edict_t *touchedicts[MAX_EDICTS]; #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND vec3_t end; vec_t len = 0; if (VectorCompare(mins, maxs)) { vec3_t shiftstart, shiftend; VectorAdd(start, mins, shiftstart); VectorAdd(pEnd, mins, shiftend); if (VectorCompare(start, pEnd)) trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities); else trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false); VectorSubtract(trace.endpos, mins, trace.endpos); return trace; } if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0) { // TRICK: make the trace 1 qu longer! VectorSubtract(pEnd, start, end); len = VectorNormalizeLength(end); VectorMA(pEnd, collision_endposnudge.value, end, end); } else VectorCopy(pEnd, end); #else if (VectorCompare(mins, maxs)) { vec3_t shiftstart, shiftend; VectorAdd(start, mins, shiftstart); VectorAdd(end, mins, shiftend); if (VectorCompare(start, end)) trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities); else trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities); VectorSubtract(trace.endpos, mins, trace.endpos); return trace; } #endif if (hitnetworkentity) *hitnetworkentity = 0; VectorCopy(start, clipstart); VectorCopy(end, clipend); VectorCopy(mins, clipmins); VectorCopy(maxs, clipmaxs); VectorCopy(mins, clipmins2); VectorCopy(maxs, clipmaxs2); #if COLLISIONPARANOID >= 3 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]); #endif // clip to world Collision_ClipToWorld(&cliptrace, cl.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask); cliptrace.bmodelstartsolid = cliptrace.startsolid; if (cliptrace.startsolid || cliptrace.fraction < 1) cliptrace.ent = prog ? prog->edicts : NULL; if (type == MOVE_WORLDONLY) goto finished; if (type == MOVE_MISSILE) { // LordHavoc: modified this, was = -15, now -= 15 for (i = 0;i < 3;i++) { clipmins2[i] -= 15; clipmaxs2[i] += 15; } } // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize) cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs); else { VectorCopy(clipmins, hullmins); VectorCopy(clipmaxs, hullmaxs); } // create the bounding box of the entire move for (i = 0;i < 3;i++) { clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1; clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1; } // debug override to test against everything if (sv_debugmove.integer) { clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999; clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999; } // if the passedict is world, make it NULL (to avoid two checks each time) // this checks prog because this function is often called without a CSQC // VM context if (prog == NULL || passedict == prog->edicts) passedict = NULL; // precalculate prog value for passedict for comparisons passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0; // figure out whether this is a point trace for comparisons pointtrace = VectorCompare(clipmins, clipmaxs); // precalculate passedict's owner edict pointer for comparisons traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.client->owner) : NULL; // collide against network entities if (hitnetworkbrushmodels) { for (i = 0;i < cl.num_brushmodel_entities;i++) { entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render; if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs)) continue; Collision_ClipToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, mins, maxs, end, hitsupercontentsmask); if (cliptrace.realfraction > trace.realfraction && hitnetworkentity) *hitnetworkentity = cl.brushmodel_entities[i]; Collision_CombineTraces(&cliptrace, &trace, NULL, true); } } // collide against player entities if (hitnetworkplayers) { vec3_t origin, entmins, entmaxs; matrix4x4_t entmatrix, entinversematrix; if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) { // don't hit network players, if we are a nonsolid player if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616) goto skipnetworkplayers; } for (i = 1;i <= cl.maxclients;i++) { entity_render_t *ent = &cl.entities[i].render; // don't hit ourselves if (i == cl.playerentity) continue; // don't hit players that don't exist if (!cl.scores[i-1].name[0]) continue; if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) { // don't hit spectators or nonsolid players if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616) continue; } Matrix4x4_OriginFromMatrix(&ent->matrix, origin); VectorAdd(origin, cl.playerstandmins, entmins); VectorAdd(origin, cl.playerstandmaxs, entmaxs); if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs)) continue; Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]); Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]); Collision_ClipToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask); if (cliptrace.realfraction > trace.realfraction && hitnetworkentity) *hitnetworkentity = i; Collision_CombineTraces(&cliptrace, &trace, NULL, false); } skipnetworkplayers: ; } // clip to entities // because this uses World_EntitiestoBox, we know all entity boxes overlap // the clip region, so we can skip culling checks in the loop below // note: if prog is NULL then there won't be any linked entities numtouchedicts = 0; if (hitcsqcentities && prog != NULL) { numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts); if (numtouchedicts > MAX_EDICTS) { // this never happens Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS); numtouchedicts = MAX_EDICTS; } } for (i = 0;i < numtouchedicts;i++) { touch = touchedicts[i]; if (touch->fields.client->solid < SOLID_BBOX) continue; if (type == MOVE_NOMONSTERS && touch->fields.client->solid != SOLID_BSP) continue; if (passedict) { // don't clip against self if (passedict == touch) continue; // don't clip owned entities against owner if (traceowner == touch) continue; // don't clip owner against owned entities if (passedictprog == touch->fields.client->owner) continue; // don't clip points against points (they can't collide) if (pointtrace && VectorCompare(touch->fields.client->mins, touch->fields.client->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.client->flags & FL_MONSTER))) continue; } bodysupercontents = touch->fields.client->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY; // might interact, so do an exact clip model = NULL; if ((int) touch->fields.client->solid == SOLID_BSP || type == MOVE_HITMODEL) model = CL_GetModelFromEdict(touch); if (model) Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2], touch->fields.client->angles[0], touch->fields.client->angles[1], touch->fields.client->angles[2], 1); else Matrix4x4_CreateTranslate(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2]); Matrix4x4_Invert_Simple(&imatrix, &matrix); if ((int)touch->fields.client->flags & FL_MONSTER) Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask); else Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask); if (cliptrace.realfraction > trace.realfraction && hitnetworkentity) *hitnetworkentity = 0; Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.client->solid == SOLID_BSP); } finished: #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0) Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd); #endif return cliptrace; }
/* ============= SV_Physics_Compound a glue two entities together ============= */ void SV_Physics_Compound( edict_t *ent ) { edict_t *parent; // regular thinking if( !SV_RunThink( ent )) return; parent = ent->v.aiment; if( !SV_IsValidEdict( parent )) { MsgDev( D_ERROR, "%s have MOVETYPE_COMPOUND with no corresponding ent!", SV_ClassName( ent )); ent->v.movetype = MOVETYPE_NONE; return; } if( ent->v.solid != SOLID_TRIGGER ) ent->v.solid = SOLID_NOT; switch( parent->v.movetype ) { case MOVETYPE_PUSH: case MOVETYPE_PUSHSTEP: break; default: return; } // not initialized ? if( ent->v.ltime == 0.0f ) { VectorCopy( parent->v.origin, ent->v.oldorigin ); VectorCopy( parent->v.angles, ent->v.avelocity ); ent->v.ltime = host.frametime; return; } if( !VectorCompare( parent->v.origin, ent->v.oldorigin ) || !VectorCompare( parent->v.angles, ent->v.avelocity )) { matrix4x4 start_l, end_l, temp_l, child; // create parent old position Matrix4x4_CreateFromEntity( temp_l, ent->v.avelocity, ent->v.oldorigin, 1.0f ); Matrix4x4_Invert_Simple( start_l, temp_l ); // create parent actual position Matrix4x4_CreateFromEntity( end_l, parent->v.angles, parent->v.origin, 1.0f ); // stupid quake bug!!! if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG )) ent->v.angles[PITCH] = -ent->v.angles[PITCH]; // create child actual position Matrix4x4_CreateFromEntity( child, ent->v.angles, ent->v.origin, 1.0f ); // transform child from start to end Matrix4x4_ConcatTransforms( temp_l, start_l, child ); Matrix4x4_ConcatTransforms( child, end_l, temp_l ); // create child final position Matrix4x4_ConvertToEntity( child, ent->v.angles, ent->v.origin ); // stupid quake bug!!! if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG )) ent->v.angles[PITCH] = -ent->v.angles[PITCH]; } // notsolid ents never touch triggers SV_LinkEdict( ent, (ent->v.solid == SOLID_NOT) ? false : true ); // shuffle states VectorCopy( parent->v.origin, ent->v.oldorigin ); VectorCopy( parent->v.angles, ent->v.avelocity ); }
/* * 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; }
/** * @brief Called whenever an entity changes origin, mins, maxs, or solid to add it to * the clipping hull. */ void Sv_LinkEntity(g_entity_t *ent) { int32_t leafs[MAX_ENT_LEAFS]; int32_t clusters[MAX_ENT_LEAFS]; size_t i, j; int32_t top_node; if (ent == svs.game->entities) { // never bother with the world return; } // remove it from its current sector Sv_UnlinkEntity(ent); if (!ent->in_use) { // and if its free, we're done return; } // set the size VectorSubtract(ent->maxs, ent->mins, ent->size); // encode the size into the entity state for client prediction ent->s.solid = ent->solid; switch (ent->s.solid) { case SOLID_TRIGGER: case SOLID_PROJECTILE: case SOLID_DEAD: case SOLID_BOX: PackBounds(ent->mins, ent->maxs, &ent->s.bounds); break; default: PackBounds(vec3_origin, vec3_origin, &ent->s.bounds); break; } // set the absolute bounding box; ensure it is symmetrical Cm_EntityBounds(ent->solid, ent->s.origin, ent->s.angles, ent->mins, ent->maxs, ent->abs_mins, ent->abs_maxs); sv_entity_t *sent = &sv.entities[NUM_FOR_ENTITY(ent)]; // link to PVS leafs sent->num_clusters = 0; sent->areas[0] = sent->areas[1] = 0; // get all leafs, including solids const size_t len = Cm_BoxLeafnums(ent->abs_mins, ent->abs_maxs, leafs, lengthof(leafs), &top_node, 0); // set areas, allowing entities (doors) to occupy up to two for (i = 0; i < len; i++) { clusters[i] = Cm_LeafCluster(leafs[i]); const int32_t area = Cm_LeafArea(leafs[i]); if (area) { if (sent->areas[0] && sent->areas[0] != area) { if (sent->areas[1] && sent->areas[1] != area && sv.state == SV_LOADING) { Com_Warn("Object touching 3 areas at %s\n", vtos(ent->abs_mins)); } sent->areas[1] = area; } else { sent->areas[0] = area; } } } if (len == MAX_ENT_LEAFS) { // use top_node sent->num_clusters = -1; sent->top_node = top_node; } else { sent->num_clusters = 0; for (i = 0; i < len; i++) { if (clusters[i] == -1) { continue; // not a visible leaf } for (j = 0; j < i; j++) if (clusters[j] == clusters[i]) { break; } if (j == i) { if (sent->num_clusters == MAX_ENT_CLUSTERS) { // use top_node Com_Debug(DEBUG_SERVER, "%s exceeds MAX_ENT_CLUSTERS\n", etos(ent)); sent->num_clusters = -1; sent->top_node = top_node; break; } sent->clusters[sent->num_clusters++] = clusters[i]; } } } if (ent->solid == SOLID_NOT) { return; } // find the first sector that the ent's box crosses sv_sector_t *sector = sv_world.sectors; while (true) { if (sector->axis == -1) { break; } if (ent->abs_mins[sector->axis] > sector->dist) { sector = sector->children[0]; } else if (ent->abs_maxs[sector->axis] < sector->dist) { sector = sector->children[1]; } else { break; // crosses the node } } // add it to the sector sent->sector = sector; sector->entities = g_list_prepend(sector->entities, ent); // and update its clipping matrices const vec_t *angles = ent->solid == SOLID_BSP ? ent->s.angles : vec3_origin; Matrix4x4_CreateFromEntity(&sent->matrix, ent->s.origin, angles, 1.0); Matrix4x4_Invert_Simple(&sent->inverse_matrix, &sent->matrix); }