View previous topic :: View next topic |
Author |
Message |
MDave

Joined: 17 Dec 2007 Posts: 75
|
Posted: Thu Dec 25, 2008 11:18 pm Post subject: |
|
|
It could be worth checking out how quake 3 manages to fix this old age problem Seems to be smaller code, although I haven't checked if it depends on a lot of other systems built in quake 3. |
|
Back to top |
|
 |
Team Xlink
Joined: 25 Jun 2009 Posts: 320
|
Posted: Tue Dec 22, 2009 5:28 am Post subject: |
|
|
Thank you for the tutorial, it has been useful.
There is a chase cam bug I can't figure out how to fix.
Enable the chase cam and backup all the way to the wall and your chase cam goes inside your player.How would one fix that so it doesn't go inside the player? _________________
Anonymous wrote: | if it works, it works. if it doesn't, HAHAHA! |
|
|
Back to top |
|
 |
Baker

Joined: 14 Mar 2006 Posts: 1538
|
Posted: Tue Dec 22, 2009 7:39 am Post subject: |
|
|
Team Xlink wrote: | Thank you for the tutorial, it has been useful.
There is a chase cam bug I can't figure out how to fix.
Enable the chase cam and backup all the way to the wall and your chase cam goes inside your player.How would one fix that so it doesn't go inside the player? |
There are going to be instances when the camera has to go inside the player because it doesn't go inside the wall and there isn't anywhere else for the camera to be.
For example, what if you are in an elevator only exactly the right size to contain the player?
That isn't a bug. It is up to you to decide what to do if the camera goes into the player model, but at least it isn't going outside the wall.
In DirectQ and Qrack, they make the player transparent (alpha level less than 1). In DarkPlaces, I don't remember ... I think it just lets the camera go inside the player. Option #3 is, of course, to not render the player if the camera goes inside the player. |
|
Back to top |
|
 |
r00k
Joined: 13 Nov 2004 Posts: 483
|
Posted: Tue Dec 22, 2009 10:02 am Post subject: |
|
|
Here's what has evolved on my end.
Code: |
void Chase_Update (void)
{
int i;
float dist;
vec3_t forward, up, right, dest, stop;
float alpha, alphadist;
// if can't see player, reset
AngleVectors (cl.lerpangles, 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;
// 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 = max(1, DotProduct(stop, forward));
if ( dist < 1 )
{
dist = 1; // should never happen
}
r_refdef.viewangles[PITCH] = -180 / M_PI * atan2( stop[2], dist );
TraceLine (r_refdef.vieworg, chase_dest, stop);
if (stop[0] != 0 || stop[1] != 0 || stop[2] != 0)
{
VectorCopy (stop, chase_dest);//update the camera destination to where we hit the wall
alphadist = VecLength2(r_refdef.vieworg, chase_dest);
alpha = bound(0.1,(alphadist / chase_back.value), 1);
cl_entities[cl.viewentity].transparency = alpha;
//R00k, this prevents the camera from poking into the wall by rounding off the traceline...
LerpVector (r_refdef.vieworg, chase_dest, 0.9f, chase_dest);
}
VectorCopy (chase_dest, r_refdef.vieworg);
}
|
MH's code will clip against brush models where this does not but its fast. |
|
Back to top |
|
 |
Team Xlink
Joined: 25 Jun 2009 Posts: 320
|
Posted: Tue Dec 22, 2009 5:10 pm Post subject: |
|
|
Well, I find that it doesn't create a noticeable slowdown for me.
This is a great tutorial.
Also, Tomaz was in IRC last night and there was a conversation and he mentioned the idea of a chasecam falling behind the player and then catching up to the player.
You could always do this via qc by using a stuffcmd for the chase_back commands, but is it possible via engine? _________________
Anonymous wrote: | if it works, it works. if it doesn't, HAHAHA! |
|
|
Back to top |
|
 |
mh

Joined: 12 Jan 2008 Posts: 909
|
Posted: Tue Dec 22, 2009 11:36 pm Post subject: |
|
|
I actually think it's quite important to clip against brush models, and I wouldn't have put this tutorial out if I hadn't got that part working.
OK, so ammo boxes and the like can be ignored, but doors at least need to be included. The way I see it is that not clipping against doors could let you see some of what's behind a door, and besides - it looks unnatural.
Also, the thing with the view going inside the player - that's not a bug, it's a feature.
Joking aside, if we're clipping against walls then the view has to go somewhere if you back up against a wall. The choices are inside the wall, inside the player or somewhere inbetween.
Somewhere inbetween is right out; the player model is not defined by the actual verts and tris in the model, it's defined by a bounding box, which would virtually be in contact with the wall in such a situation. There is no inbetween.
Inside the wall is out because you would need to know the dimensions of the wall and how it related to everthing else in the world otherwise you risk visual artefacts (and it's the whole point of this code to remove these, so that would defeat the purpose).
So inside the player it is. Turning the player model transparent when this happens is a compromise, because in cases where the view is so close behind the player (and this applies to even using the view inside the wall) the player model will actually occlude most of the view. This way you still get to see somthing.
Another option might be to do what Tomb Raider does and swing the view around to in front of the player. To my mind that kind of thing goes well beyond the scope of what is supposed to be a first-person shooter. It's an exercise for you to do yourself if you want it.
So yes, it was a deliberate choice, and yes, I was completely aware of what the options were and what was good and bad about each of them when I wrote that code. _________________ 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: Tue Jun 29, 2010 1:09 pm Post subject: |
|
|
I morphed MH's code in a CL_Visible_To_Client function.
A faked SV_ClipMove_To_Entity might be better (it appears this is what at least FuhQuake and other modern QW engines do) ... which apparently QW engines have due to the need for prediction.
But after looking at Rook's and my own personal testing, what is the failure of TraceLine?
Now first, I see with this comments that it doesn't test against brush models and other entities.
Is it otherwise reliable? Do past engines get the camera stuck in the wall because of TraceLine being bad or because they don't update the camera position based on the result or is it a combination of both?
Just looking to understand this more. _________________ Tomorrow Never Dies. I feel this Tomorrow knocking on the door ... |
|
Back to top |
|
 |
mh

Joined: 12 Jan 2008 Posts: 909
|
Posted: Tue Jun 29, 2010 1:31 pm Post subject: |
|
|
My main problem with TraceLine is that it doesn't work against brush models. The fact that you could back up to a door and have the camera go through the door, showing you the other side of the door, was for me a deal-breaker from the outset.
A secondary problem was that it made calls to server-side code. Now, in stock NetQuake this isn't actually a problem as the client and the server share models, so it's the very same data to begin with. But from the perspective of keeping the client and the server as separate as possible it's a no-go zone. However there's no reason why one couldn't duplicate the server-side functions on the client and make a CL_TraceLine, CL_RecursiveHullCheck and CL_HullPointContents to handle that.
But it still doesn't work against brush models. And if you're going to do something like this you need to do it right, otherwise not at all. Think of a map likep ne_tower where maybe 50% of what you see on-screen is brush models and you can imagine how bad things can get. Duplicating the other movement functions on the client is not going to happen, because then you need to handle sv.edicts and ent.v structs in a manner that's not going to break. So your options are to create a mess or find a different approach.
Thinking a little further about the whole chase cam matter, it seems to me that an alternative aproach might be to make use of PVS data for setting an initial bounds. I'd need to think more about that and write some experimental code before I could say that it's a viable alternative though. _________________ 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: Tue Jun 29, 2010 4:46 pm Post subject: |
|
|
I was looking through R00k's version and only saw a Traceline and I guess I was under the impression that the traceline was part of the reason that the chasecam poked into walls.
But apparently not.
Still the brush model thing makes a big difference.
Either way, I'll post the raw function I made of your chase cam fix called CL_Visible_To_Client.
Some of the things I'd like to experiment around with in are highly dependent on knowing whether or not a given entity can be seen.
Code: | qboolean Still_Visible (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;
// don't check against players
if (e->modelindex == cl_modelindex[mi_player])
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;
}
qboolean CL_Visible_To_Client (vec3_t viewer, vec3_t seen)
{
// 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 ((viewer[0] - seen[0]) * (viewer[0] - seen[0]) +
(viewer[1] - seen[1]) * (viewer[1] - seen[1]) +
(viewer[2] - seen[2]) * (viewer[2] - seen[2])));
// take the contents of the view leaf
int viewcontents = (Mod_PointInLeaf (viewer, cl.worldmodel))->contents;
int best;
// move along path from viewer to seen
for (best = 0; best < num_tests; best++)
{
vec3_t step_to_entity;
step_to_entity[0] = viewer[0] + (seen[0] - viewer[0]) * best / num_tests;
step_to_entity[1] = viewer[1] + (seen[1] - viewer[1]) * best / num_tests;
step_to_entity[2] = viewer[2] + (seen[2] - viewer[2]) * best / num_tests;
// check for a leaf hit with different contents
if (!Still_Visible (step_to_entity, viewcontents))
{
return false;
}
}
return true;
} |
Operating on the idea that traceline is faster, someone can have the traceline "pre-screen" a quick test and follow that up with a more detailed test. Most entities would fail the traceline visibility test, leaving far fewer entities to check with the more precise visibility check. _________________ Tomorrow Never Dies. I feel this Tomorrow knocking on the door ... |
|
Back to top |
|
 |
mh

Joined: 12 Jan 2008 Posts: 909
|
Posted: Tue Jun 29, 2010 6:01 pm Post subject: |
|
|
Neato. It would be really great to get a solid client-side entity visibility test that runs in software. I'm currently doing stuff with occlusion queries; they're fine once you figure out how to avoid stalling the pipeline with them, but there's a frame of latency in the results (which is mostly OK but sometimes gives you blinky), they require hardware support (which is pretty much ubiquitous but I'd still prefer to not have the requirement), and there's an awful lot of messing about with the way Quake reuses entity slots (which is most definitely NOT OK). _________________ DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines |
|
Back to top |
|
 |
mk

Joined: 04 Jul 2008 Posts: 94
|
Posted: Thu Jul 01, 2010 3:59 am Post subject: |
|
|
Really good. I just implemented the latest version of this fix in Makaqu, and I'm thinking of using some of its code to dinamically position the crosshair on the screen.
The only other engine I knew that had the chasecam fully fixed is ToChriS 1.67, but it's code seems to require a lot of reenginering all over the renderer.
And dunno if this is interesting, but here's the hackish workaround that I had implemented in the latest releases of Makaqu:
Code: |
if (sv.active)
{
// hack to prevent the camera from seeing through solid objects.
// not all solid objects are detected, and it crashes on demo play (hence the "if (sv.active)").
// start, mins, maxs, end, nomonsters, ignore_ent
trace1 = SV_Move (chase_origin1, vec3_origin, vec3_origin, chase_dest1, false, G_EDICT(cl.viewentity));
trace2 = SV_Move (chase_origin2, vec3_origin, vec3_origin, chase_dest2, false, G_EDICT(cl.viewentity));
trace3 = SV_Move (chase_origin3, vec3_origin, vec3_origin, chase_dest3, false, G_EDICT(cl.viewentity));
trace4 = SV_Move (chase_origin4, vec3_origin, vec3_origin, chase_dest4, false, G_EDICT(cl.viewentity));
}
else
{
memset (&trace1, 0, sizeof(trace1));
memset (&trace2, 0, sizeof(trace2));
memset (&trace3, 0, sizeof(trace3));
memset (&trace4, 0, sizeof(trace4));
SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, chase_origin1, chase_dest1, &trace1);
SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, chase_origin2, chase_dest2, &trace2);
SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, chase_origin3, chase_dest3, &trace3);
SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, chase_origin4, chase_dest4, &trace4);
}
|
About the "not all solid objects" comment: In the start map, the floor brush entity that covers the start of the ladder that leads to the portal to Shub-Niggurath's pit is an example of a solid brush entity that's not detected by this workaround. _________________ Makaqu engine blog / website.
Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn. |
|
Back to top |
|
 |
mk

Joined: 04 Jul 2008 Posts: 94
|
Posted: Thu Jul 01, 2010 5:52 am Post subject: |
|
|
I just noticed that the chase camera doesn't go through liquid surfaces, and that if you use noclip to walk inside the solid areas of the map, the chase camera refuses to go into empty areas.
To fix these, go into Chase_Check and change this
Code: | // check against world model
if ((Mod_PointInLeaf (checkpoint, cl.worldmodel))->contents != viewcontents)
return false;
|
... to this:
Code: | // check against world model
#define CHASE_CONTENTS (0x1<<(CONTENTS_EMPTY*-1)|0x1<<(CONTENTS_WATER*-1)|0x1<<(CONTENTS_SLIME*-1))
if (!(0x1 << ((Mod_PointInLeaf (checkpoint, cl.worldmodel))->contents * -1) & (0x1<<(viewcontents*-1)|CHASE_CONTENTS)))
return false;
|
This will make the camera go through empty areas, water and slime, but not lava. _________________ Makaqu engine blog / website.
Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn. |
|
Back to top |
|
 |
|
|
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
|