Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

Chase camera fixes Goto page 1, 2 Next

| Inside3d Forums Forum Index -> Programming Tutorials

View previous topic :: View next topic
Author | Message
--- | ---
mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Mon Dec 22, 2008 7:16 pm Post subject: Chase camera fixes |

Here we're going to fix the horrible effects when Quake's chase camera goes inside a solid leaf. You can test these on the start map by setting chase_active 1, going to the wall right behind you, and turning around. This fix will accomplish two things: (1) a chase camera can never go inside a leaf which has different contents to the viewleaf, and (2) fixing the resulting effect when a surface is viewed edge-on and partially clipped by the near plane.
All changes are to the Chase_Update function, so I'm just going to give it in full and discuss how it works afterwards.

Code:
#define NUM_TESTS 64 #define CHASE_DEST_OFFSET 2.0f void Chase_Update (void) { int i;
float dist;
vec3_t forward, up, right;
vec3_t dest, stop;
int best;
int viewcontents;
// if can't see player, reset
AngleVectors (cl.viewangles, forward, right, up);
// calc exact destination
for (i = 0; i < 3; i++)
chase_dest[i] = r_refdef.vieworg[i] - forward[i] * chase_back.value - right[i] * chase_right.value;
chase_dest[2] = r_refdef.vieworg[2] + chase_up.value;
// take the contents of the view leaf
viewcontents = (Mod_PointInLeaf (r_refdef.vieworg, cl.worldmodel))->contents;
for (best = 0; best < NUM_TESTS; best++)
{
float chase_newdest[3];
chase_newdest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / NUM_TESTS;
chase_newdest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / NUM_TESTS;
chase_newdest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / NUM_TESTS;
// check for a leaf hit with different contents
if ((Mod_PointInLeaf (chase_newdest, cl.worldmodel))->contents != viewcontents)
{
// go back to the previous best as this one is bad
// unless the first one was also bad, (viewleaf contents != viewleaf contents!!!)
if (best > 0)
best--;
else best = NUM_TESTS;
break;
}
}
// certain surfaces can be viewed at an oblique enough angle that they are partially clipped
// by znear, so now we fix that too...
for (; best >= 0; best--)
{
// number of matches
int nummatches = 0;
// adjust
chase_dest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / NUM_TESTS;
chase_dest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / NUM_TESTS;
chase_dest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / NUM_TESTS;
// move x to neg
chase_dest[0] -= CHASE_DEST_OFFSET;
if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
chase_dest[0] += CHASE_DEST_OFFSET;
// move x to pos
chase_dest[0] += CHASE_DEST_OFFSET;
if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
chase_dest[0] -= CHASE_DEST_OFFSET;
// move y to neg
chase_dest[1] -= CHASE_DEST_OFFSET;
if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
chase_dest[1] += CHASE_DEST_OFFSET;
// move y to pos
chase_dest[1] += CHASE_DEST_OFFSET;
if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
chase_dest[1] -= CHASE_DEST_OFFSET;
// move z to neg
chase_dest[2] -= CHASE_DEST_OFFSET;
if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
chase_dest[2] += CHASE_DEST_OFFSET;
// move z to pos
chase_dest[2] += CHASE_DEST_OFFSET;
if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
chase_dest[2] -= CHASE_DEST_OFFSET;
// all tests passed so we're good!
if (nummatches == 6) break;
}
// find the spot the player is looking at
VectorMA (r_refdef.vieworg, 4096, forward, dest);
TraceLine (r_refdef.vieworg, dest, stop);
// calculate pitch to look at the same spot from camera
VectorSubtract (stop, r_refdef.vieworg, stop);
dist = DotProduct (stop, forward);
if (dist < 1)
dist = 1;
r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180;
// move towards destination
VectorCopy (chase_dest, r_refdef.vieworg);
}

There are two major new sections here.
The first one starts at the normal view origin and gradually edges towards the chase camera origin until it hits a leaf that has different contents to the view leaf. The previous good test was therefore the correct spot where the chase camera position was last inside a valid leaf. I picked 64 tests above, which might seem like a lot, but Mod_PointInLeaf is a fast enough function, and this is peanuts compared to some of what goes on at the server side.
Reduce it to 32 or even 16 if you like, but be warned that camera positioning can get very jerky if you do.
Running with this alone, (put a break statement after the assignation of chase_dest inside the next for loop if you want to test it) will work fine until you start viewing surfaces edge-on or almost-edge on. If you test it you'll see what I mean; I'm certain that there's some correct terminology but I don't know it!
The second section fixes that by building a small bounding box around the camera position and testing up/down/left/right/front/back, edging forward towards the original origin again, until all 6 pass.
Compile and run, but there are two remaining bugs. The first one is that when the camera gets near enough to the original origin, the player model will start to be clipped away. This can be fixed by just not drawing the model at this point; for a nicer effect you could gradually fade it out.
The second one concerns our old friends brush models. I'm not testing against brush models here, so the camera can sometimes go behind an intervening brush model (such as a door). The obvious solution is to just split off the contents tests into a separate function and add a test against the bboxes of all visedicts.
For the first one, assuming a NUM_TESTS of 64, if the camera gets to less than point 18 you probably don't want to draw.
I'll publish code for the second one later (I haven't written it yet!).
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Mon Dec 22, 2008 8:29 pm Post subject: |

OK, as promised, here are the fixes. The first one is simple enough, just declare a global qboolean called chase_nodraw in chase.c, then add these lines after the second new for loop in Chase_Update:

Code:
chase_length = (r_refdef.vieworg[0] - chase_dest[0]) * (r_refdef.vieworg[0] - chase_dest[0]); chase_length += (r_refdef.vieworg[1] - chase_dest[1]) * (r_refdef.vieworg[1] - chase_dest[1]); chase_length += (r_refdef.vieworg[2] - chase_dest[2]) * (r_refdef.vieworg[2] - chase_dest[2]);
if (chase_length < 150)
chase_nodraw = true;
else
chase_nodraw = false;

(Declare chase_length as a float wherever suits you.)
You'll notice that I don't bother taking the square root; there's no need to here as we can compare with the squared value just as well.
Now go tp gl_rmain.c, find R_DrawEntitiesOnList, and extern the chase_nodraw variable somewhere, then add this right after case mod_alias:

Code:
if (currententity == &cl_entities[cl.viewentity] && chase_active.value) if (chase_nodraw) break;

And here's the fix for the second one:

Code:
qboolean Chase_Check (vec3_t checkpoint, int viewcontents) { int i; vec3_t mins; vec3_t maxs;
// check against world model
if ((Mod_PointInLeaf (checkpoint, cl.worldmodel))->contents != viewcontents)
return false;
// check visedicts - this happens *after* CL_ReadFromServer so the list will be valid
for (i = 0; i < cl_numvisedicts; i++)
{
// retrieve the current entity
entity_t *e = cl_visedicts[i];
// don't check against self
if (e == &cl_entities[cl.viewentity]) continue;
// derive the bbox
if (e->model->type == mod_brush && (e->angles[0] || e->angles[1] || e->angles[2]))
{
// copied from R_CullBox rotation test for inline bmodels, loop just unrolled
mins[0] = e->origin[0] - e->model->radius;
maxs[0] = e->origin[0] + e->model->radius;
mins[1] = e->origin[1] - e->model->radius;
maxs[1] = e->origin[1] + e->model->radius;
mins[2] = e->origin[2] - e->model->radius;
maxs[2] = e->origin[2] + e->model->radius;
}
else
{
VectorAdd (e->origin, e->model->mins, mins);
VectorAdd (e->origin, e->model->maxs, maxs);
}
// check against bbox
if (checkpoint[0] < mins[0]) continue;
if (checkpoint[1] < mins[1]) continue;
if (checkpoint[2] < mins[2]) continue;
if (checkpoint[0] > maxs[0]) continue;
if (checkpoint[1] > maxs[1]) continue;
if (checkpoint[2] > maxs[2]) continue;
// point inside
return false;
}
// it's good now
return true;
}

Then replace the Mod_PointInLeaf test in the first loop with:

Code:
if (!Chase_Check (chase_newdest, viewcontents))

And replace each Mod_PointInLeaf test in the second loop with:

Code:
if (Chase_Check (chase_dest, viewcontents)) nummatches++;

Job done.
Aside from another two remaining things; func_illusionary entities and dead bodies. These two are things that the client generally doesn't know about, so unless we hack the code fairly savagely we're kinda stuck. I think enough has been accomplished for now.
And if you're wondering why I just didn't use the various SV_ functions... three reasons. First is that there's no fun in not figuring this stuff out for yourself, second is that I tried them and they didn't quite work right, third is that they really have no business being called from client-side code anyway.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

MauveBib Joined: 04 Nov 2004 Posts: 602 |

Posted: Mon Dec 22, 2008 8:38 pm Post subject: |

inside3d has definately benefitted from the death of quakesrc in terms of engine tutorials _________________ Apathy Now!

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Tue Dec 23, 2008 12:17 am Post subject: |

Damn, MH! If this is what I think it is, a million thanks!

Back to top |

mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Tue Dec 23, 2008 1:57 am Post subject: |

I think it might be... _____________ I fixed dead bodies by changing the alias model bboxes (on the client side only) to be per-frame rather than a single bbox for the entire model. This info is already loaded in Mod_LoadAliasFrame and Mod_LoadAliasGroup, so it's just a matter of (1) pre-scaling and translating it (change from a trivertx_t to a vec3_t also - lightnormalindex isn't used here, per modelgen.h); (2) retrieving the aliashdr_t from Mod_ExtraData if it's a mod_alias; and (3) using paliashdr->frames[e->frame]->bboxmin and paliashdr->frames[e->frame]->bboxmax instead of e->model->mins and e->model->maxs.
If you do this, and you've implemented the aliasbboxmins/aliasbboxmaxs fix, be sure to remove the scaling and translation from that.
Also watch out for the bug in stock ID code where it has "frame->bboxmax.v[i] = pdaliasframe->bboxmin.v[i];" - this should be "pdaliasframe->bboxmax.v[i]", of course.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Wed Dec 24, 2008 7:54 am Post subject: |

I haven't exhaustively tested this with all situations, but it works very well with the standard chase_back/chase_up/chase_right values in all the testing I've done. Very well written! It can get a little jerky at times if you change the standard defaults greatly, but no doubt I could interpolate the positioning to smooth it out. I want to learn more about controlling the chasecam anyway

Back to top |

mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Wed Dec 24, 2008 10:34 am Post subject: |

Another option that's occurred to me is that you could change the value of NUM_TESTS; 64 is OK I suppose for the defaults, but like you said if you increase them you'll lose a lot of granularity. Using something based on the distance between r_refdef.vieworg and the original chase_dest would be much better, then perhaps also adding a cvar to allow the user to control how granular they want it to be (say, 0 to 1 with 0 being a reversion to the default behaviour, 1 using the full scale, and points between using a partial scale). Allowing user control over this kind of behaviour is definitely a good thing, IMO.
The main thing I'm unhappy with is that first new for loop; it's a "late-out" loop, so in the majority of non-extreme cases it will run for most or all of it's iterations. Increasing the number of tests for smoother camera positioning could end up hurting a bit.
Using a coarser scale to establish an initial good point and bad point, then a finer scale to position more correctly between the two seems to be the way to go. I don't have my code with me right now, but I think the loop for the finer scale could be written as "early-out".
Another option would be to impose PVS restrictions on the chasecam positions.
The second loop is fine; it looks a little frightening - 6 checks per iteration! - but it will "early-out" in the vast majority of cases - one iteration most of the time, no more than 2 or 3 otherwise.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Wed Dec 24, 2008 11:48 am Post subject: |

I've done a little bit more testing. It appears that the main source of jerkiness I get is when the player is backed up against a wall with a diagonal ceiling a big chase_up and chase_back value. The start map beginning is one place with such a ceiling, another is the DM6 tunnel to the red armor.
If I move slightly, I get a lot of jerking with the view as if one frame it decides x,y is the right place to be and if I'm forward just a little, it decides x2, y2 is the best place to be.
I can remember walking through early DarkPlaces builds and trying them and LordHavoc had this same issue and then one release it was fixed. I'm more pointing out that it was funny that LH had the chase camera fixed except for diagonal ceilings as well
I used chase_up 200 and chase_back 200, it appears using a big value with only 1 of the cvars you still get a smooth transition. I didn't mess with chase_right as honestly I can't think of an obvious use for chase_right at the moment.
Hmmmm ...
Hey I can spot part of the original Quake code even without looking at the source:

Code:
// calc exact destination for (i = 0; i < 3; i++) chase_dest[i] = r_refdef.vieworg[i] - forward[i] * chase_back.value - right[i] * chase_right.value; chase_dest[2] = r_refdef.vieworg[2] + chase_up.value;

Back to top |

mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Wed Dec 24, 2008 12:01 pm Post subject: |

Interesting... With chase_back of 512 and chase_up of 256 it's incredibly jerky indeed. Changing NUM_TESTS to 1024 resolves that - very smooth. It seems obvious that distance-based is the way to go with this, so I'm gonna try that out and report back. I'll also try the initial coarse/then fine-tune method and see what happens. All code will be posted, of course, but be warned that it's my D3D engine and not GLQuake at this point; there won't be any rendering code (naturally) but there will be some minor C++isms.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Wed Dec 24, 2008 12:25 pm Post subject: |

mh wrote:
Interesting... With chase_back of 512 and chase_up of 256 it's incredibly jerky indeed. Changing NUM_TESTS to 1024 resolves that - very smooth. It seems obvious that distance-based is the way to go with this, so I'm gonna try that out and report back. I'll also try the initial coarse/then fine-tune method and see what happens. All code will be posted, of course, but be warned that it's my D3D engine and not GLQuake at this point; there won't be any rendering code (naturally) but there will be some minor C++isms.

I just tried with up/back 512/256 like above using NUM_TESTS 1024. I still get a lot of jerkiness near the beginning of the start map.
Update: No I don't, it works fine with 1024. I was using VC 2008 unlike normal and didn't properly set the output path.

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Wed Dec 24, 2008 1:56 pm Post subject: |

Ok, based on your above comments, it seems like you recommend checking half a unit per chase_x unit. Here is a slightly revised version for your consideration that instead of using a fixed number of divides, just checks to see the total distance * 2. About the equivalent but should adjust to whatever the chase camera cvar values are.

Quote:
#define NUM_TESTS 64 (delete) #define CHASE_DEST_OFFSET 2.0f qboolean chase_nodraw; void Chase_Update (void) {
int i;
float dist;
vec3_t forward, up, right;
vec3_t dest, stop;
int best;
int viewcontents;
float num_tests;
vec3_t delta;
// if can't see player, reset
AngleVectors (cl.viewangles, forward, right, up);
// calc exact destination
for (i = 0; i < 3; i++)
chase_dest[i] = r_refdef.vieworg[i] - forward[i] * chase_back.value - right[i] * chase_right.value;
chase_dest[2] = r_refdef.vieworg[2] + chase_up.value;
VectorSubtract(chase_dest, r_refdef.vieworg, delta);
num_tests = Length(delta)*2 + 1;
// take the contents of the view leaf
viewcontents = (Mod_PointInLeaf (r_refdef.vieworg, cl.worldmodel))->contents;
for (best = 0; best < num_tests; best++)
{
float chase_newdest[3];
chase_newdest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / num_tests;
chase_newdest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / num_tests;
chase_newdest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / num_tests;
// check for a leaf hit with different contents
if (!Chase_Check (chase_newdest, viewcontents))
{
// go back to the previous best as this one is bad
// unless the first one was also bad, (viewleaf contents != viewleaf contents!!!)
if (best > 0)
best--;
else best = num_tests;
break;
}
}
// certain surfaces can be viewed at an oblique enough angle that they are partially clipped
// by znear, so now we fix that too...
for (; best >= 0; best--)
{
// number of matches
int nummatches = 0;
// adjust
chase_dest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / num_tests;
chase_dest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / num_tests;
chase_dest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / num_tests;
// move x to neg
chase_dest[0] -= CHASE_DEST_OFFSET;
if (Chase_Check (chase_dest, viewcontents)) nummatches++;
chase_dest[0] += CHASE_DEST_OFFSET;
// move x to pos
chase_dest[0] += CHASE_DEST_OFFSET;
if (Chase_Check (chase_dest, viewcontents)) nummatches++;
chase_dest[0] -= CHASE_DEST_OFFSET;
// move y to neg
chase_dest[1] -= CHASE_DEST_OFFSET;
if (Chase_Check (chase_dest, viewcontents)) nummatches++;
chase_dest[1] += CHASE_DEST_OFFSET;
// move y to pos
chase_dest[1] += CHASE_DEST_OFFSET;
if (Chase_Check (chase_dest, viewcontents)) nummatches++;
chase_dest[1] -= CHASE_DEST_OFFSET;
// move z to neg
chase_dest[2] -= CHASE_DEST_OFFSET;
if (Chase_Check (chase_dest, viewcontents)) nummatches++;
chase_dest[2] += CHASE_DEST_OFFSET;
// move z to pos
chase_dest[2] += CHASE_DEST_OFFSET;
if (Chase_Check (chase_dest, viewcontents)) nummatches++;
chase_dest[2] -= CHASE_DEST_OFFSET;
// all tests passed so we're good!
if (nummatches == 6) break;
}
// find the spot the player is looking at
VectorMA (r_refdef.vieworg, 4096, forward, dest);
TraceLine (r_refdef.vieworg, dest, stop);
// calculate pitch to look at the same spot from camera
VectorSubtract (stop, r_refdef.vieworg, stop);
dist = DotProduct (stop, forward);
if (dist < 1)
dist = 1;
r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180;
// move towards destination
VectorCopy (chase_dest, r_refdef.vieworg);
}

This is at least the best tutorial/code since Rook condensed the anti-wallhack stuff down to a single function way early in the year.
Having a truly fixed chase cam solves so many problems. There are so many things that just aren't right if the chase cam pokes into walls.
It makes demos look stupid, especially if you turn them into a video and upload to YouTube. And a 3rd person view mod is going to act up and not look right.
Plus you have the uber-nice bounding box check and hide the model.
Tutorials like this make me feel really good about this game.

Back to top |

mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Wed Dec 24, 2008 3:58 pm Post subject: |

Yup, that's exactly the way I'm just after doing it myself, except instead of multiplying by 2 I used a cvar (value of 0 = revert to old behaviour). I also used an int for num_tests. I tried using a coarser initial scale on the first loop, then fine-tuning between two points, but the coarseness of the scale meant that the old "intervening walls" came back, so we're stuck with using the full scale, but to be honest the performance loss is very low, so no great shakes there.
Here's what I ended up with:

Code:
void Chase_Adjust (vec3_t chase_dest) { // calculate distance between chasecam and original org to establish number of tests we need. // an int is good enough here.:) add a cvar multiplier to this... int num_tests = sqrt ((r_refdef.vieworg[0] - chase_dest[0]) * (r_refdef.vieworg[0] - chase_dest[0]) +
(r_refdef.vieworg[1] - chase_dest[1]) * (r_refdef.vieworg[1] - chase_dest[1]) +
(r_refdef.vieworg[2] - chase_dest[2]) * (r_refdef.vieworg[2] - chase_dest[2])) * chase_scale.value;
// take the contents of the view leaf
int viewcontents = (Mod_PointInLeaf (r_refdef.vieworg, cl.worldmodel))->contents;
int best;
// move along path from r_refdef.vieworg to chase_dest
for (best = 0; best < num_tests; best++)
{
vec3_t chase_newdest;
chase_newdest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / num_tests;
chase_newdest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / num_tests;
chase_newdest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / num_tests;
// check for a leaf hit with different contents
if (!Chase_Check (chase_newdest, viewcontents))
{
// go back to the previous best as this one is bad
if (best > 1)
best--;
else best = num_tests;
break;
}
}
// certain surfaces can be viewed at an oblique enough angle that they are partially clipped
// by znear, so now we fix that too...
int chase_vert[] = {0, 0, 1, 1, 2, 2};
int dest_offset[] = {CHASE_DEST_OFFSET, -CHASE_DEST_OFFSET};
// move along path from chase_dest to r_refdef.vieworg
for (; best >= 0; best--)
{
// number of matches
int nummatches = 0;
// adjust
chase_dest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / num_tests;
chase_dest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / num_tests;
chase_dest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / num_tests;
// run 6 tests: -x/+x/-y/+y/-z/+z
for (int test = 0; test < 6; test++)
{
// adjust, test and put back.
chase_dest[chase_vert[test]] -= dest_offset[test & 1];
if (Chase_Check (chase_dest, viewcontents)) nummatches++;
chase_dest[chase_vert[test]] += dest_offset[test & 1];
}
// test result, if all match we're done in here
if (nummatches == 6) break;
}
float chase_length = (r_refdef.vieworg[0] - chase_dest[0]) * (r_refdef.vieworg[0] - chase_dest[0]);
chase_length += (r_refdef.vieworg[1] - chase_dest[1]) * (r_refdef.vieworg[1] - chase_dest[1]);
chase_length += (r_refdef.vieworg[2] - chase_dest[2]) * (r_refdef.vieworg[2] - chase_dest[2]);
if (chase_length < 150)
{
chase_nodraw = true;
chase_alpha = 255;
}
else
{
chase_nodraw = false;
chase_alpha = (chase_length - 150);
if (chase_alpha > 255) chase_alpha = 255;
}
}
void Chase_Update (void)
{
int i;
float dist;
vec3_t forward, up, right;
vec3_t dest, stop;
vec3_t chase_dest;
// if can't see player, reset
AngleVectors (cl.viewangles, forward, right, up);
// calc exact destination
for (i = 0; i < 3; i++)
chase_dest[i] = r_refdef.vieworg[i] - forward[i] * chase_back.value - right[i] * chase_right.value;
chase_dest[2] = r_refdef.vieworg[2] + chase_up.value;
// don't allow really small or negative scaling values
if (chase_scale.value < 0.01)
{
// revert to default Q1 chasecam behaviour
// store out alpha and nodraw so that they're valid
chase_alpha = 255;
chase_nodraw = false;
}
else
{
// adjust the chasecam to prevent it going into solid
Chase_Adjust (chase_dest);
}
// store alpha to entity
cl_entities[cl.viewentity].alphaval = chase_alpha;
// find the spot the player is looking at
VectorMA (r_refdef.vieworg, 4096, forward, dest);
TraceLine (r_refdef.vieworg, dest, stop);
// calculate pitch to look at the same spot from camera
VectorSubtract (stop, r_refdef.vieworg, stop);
dist = DotProduct (stop, forward);
if (dist < 1)
dist = 1;
r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180;
// move towards destination
VectorCopy (chase_dest, r_refdef.vieworg);
}

The last thing I want to fix is the sudden transitions from one point to another when there is some small intervening detail geometry, like the metal trim in start.bsp (the horizontal white brick just behind you is another good example); the camera should smoothly move aound the obstacle rather than suddenly snap from one side to the other.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Tomaz Joined: 05 Nov 2004 Posts: 49 |

Posted: Wed Dec 24, 2008 9:37 pm Post subject: |

Out of curiosity, why isnt there just a traceline from player to the camera postion? And position the camera where there was an intersection ( or slightly in front of it ). Seems like alot smoother and way more optimized than running 1024 leaf tests. And the current code itself could benefit from quite alof of optimizations.
Just throwing out ideas, perhaps mh has thought of something that I have overlooked.

Back to top |

scar3crow Inside3D Staff Joined: 18 Jan 2005 Posts: 837 Location: Las Vegas, NV

Posted: Wed Dec 24, 2008 9:42 pm Post subject: |

Quote:
why isnt there just a traceline from player to the camera postion? And position the camera where there was an intersection ( or slightly in front of it ).

You know, I always had assumed it did this, but that there were complications, and these complications were far beyond my comprehension.
Sometimes its really weird not being a coder in this community. =)
_________________
...and all around me was the chaos of battle and the reek of running blood.... and for the first time in my life I knew true happiness.

Back to top |

mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Wed Dec 24, 2008 10:04 pm Post subject: |

Simple reason - I tried it and it didn't quite work (and from the look of the DP chasecam code, the same can be said of LH). Slightly less simple reason - it uses SV_RecursiveHullCheck, meaning that server-only data is potentially being exposed client-side. OK, the damage is already done with the existing TraceLine, but let's not make it any worse.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Display posts from previous:

| Inside3d Forums Forum Index -> Programming Tutorials | All times are GMT Goto page 1, 2 Next
Page 1 of 2

Jump to:

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2004 phpBB Group