Perhaps better "world" interaction with mouse
Moderator: InsideQC Admins
3 posts
• Page 1 of 1
Perhaps better "world" interaction with mouse
Thanks to some pointers from various usual suspects, I was able to make it so map surfaces can be selected in game (haven't taken the time to look at LordHavoc's suggestion about throwing brush models and other entities into an array, so it only works on "world surfaces" not doors and lifts and platforms, etc).
But what about selecting entities like monster or a gun? Well ... this has probably been done in DarkPlaces or FTEQW, I have a very hard time imagining it hasn't been done.
So raw thoughts on selecting entities with the mouse ...
1) Need to translate screen mouse coordinates to client area mouse coordinates. On Windows, that goes like this ...
2) Need to translate 3D coordinates to our screen coordinates. gluProject and gluUnProject are our friends here. But rather than bloat up stuff, better to borrow from FTEQW:
3) In order to use glProject/glUnproject, we need the projection matrix, the modelview matrix and the viewport. There is probably an equivalent in FTEQW that is renderer neutral. Might look for that later.
4) We'll need to figure out some way to determine if an entity is visible on-screen. Playing around with MH's camera fix stuff can be a component of this. Probably have to do some fiddling around with Z sorting of candidates (actually Y sorting in Quake, since Quake makes the Z axis "vertical").
5) Then step 5 would be to take the closest matching entity (via "Z" testing ... errr.. well Quake so it's Y testing) to the viewpoint and highlight it in some manner. Like red shade it, draw lines around the bounding box or whatever.
But what about selecting entities like monster or a gun? Well ... this has probably been done in DarkPlaces or FTEQW, I have a very hard time imagining it hasn't been done.
So raw thoughts on selecting entities with the mouse ...
1) Need to translate screen mouse coordinates to client area mouse coordinates. On Windows, that goes like this ...
- Code: Select all
POINT current_pos;
int client_x, client_y;
GetCursorPos (¤t_pos);
ScreenToClient(mainwindow /* HWND global in Quake */, ¤t_pos);
client_x = current_pos.x;
client_y = current_pos.y;
2) Need to translate 3D coordinates to our screen coordinates. gluProject and gluUnProject are our friends here. But rather than bloat up stuff, better to borrow from FTEQW:
- Code: Select all
int qglProject (float objx, float objy, float objz, float *model, float *proj, int *view, float* winx, float* winy, float* winz) {
float in[4], out[4];
int i;
in[0] = objx; in[1] = objy; in[2] = objz; in[3] = 1.0;
for (i = 0; i < 4; i++)
out[i] = in[0] * model[0 * 4 + i] + in[1] * model[1 * 4 + i] + in[2] * model[2 * 4 + i] + in[3] * model[3 * 4 + i];
for (i = 0; i < 4; i++)
in[i] = out[0] * proj[0 * 4 + i] + out[1] * proj[1 * 4 + i] + out[2] * proj[2 * 4 + i] + out[3] * proj[3 * 4 + i];
if (!in[3])
return 0;
VectorScale(in, 1 / in[3], in);
*winx = view[0] + (1 + in[0]) * view[2] / 2;
*winy = view[1] + (1 + in[1]) * view[3] / 2;
*winz = (1 + in[2]) / 2;
return 1;
}
3) In order to use glProject/glUnproject, we need the projection matrix, the modelview matrix and the viewport. There is probably an equivalent in FTEQW that is renderer neutral. Might look for that later.
- Code: Select all
glGetFloatv(GL_MODELVIEW_MATRIX, model);
glGetFloatv(GL_PROJECTION_MATRIX, project);
glGetIntegerv(GL_VIEWPORT, view);
4) We'll need to figure out some way to determine if an entity is visible on-screen. Playing around with MH's camera fix stuff can be a component of this. Probably have to do some fiddling around with Z sorting of candidates (actually Y sorting in Quake, since Quake makes the Z axis "vertical").
- Code: Select all
qbool 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;
}
qbool 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;
}
5) Then step 5 would be to take the closest matching entity (via "Z" testing ... errr.. well Quake so it's Y testing) to the viewpoint and highlight it in some manner. Like red shade it, draw lines around the bounding box or whatever.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Lardarse wrote:PRYDON_CLIENTCURSOR (as documented in dpextensions.qc) is another way of doing this.
Thanks!
Hehe, well it has been 3 years or so since I played Prydon
/So EF_SELECTABLE ...
- Code: Select all
float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent)
{
float maxfrac, maxrealfrac;
int n;
entity_render_t *ent;
float tracemins[3], tracemaxs[3];
trace_t trace;
float tempnormal[3], starttransformed[3], endtransformed[3];
memset (&trace, 0 , sizeof(trace_t));
trace.fraction = 1;
trace.realfraction = 1;
VectorCopy (end, trace.endpos);
if (hitent)
*hitent = 0;
if (cl.worldmodel && cl.worldmodel->TraceBox)
cl.worldmodel->TraceBox(cl.worldmodel, 0, &trace, start, vec3_origin, vec3_origin, end, SUPERCONTENTS_SOLID);
if (normal)
VectorCopy(trace.plane.normal, normal);
maxfrac = trace.fraction;
maxrealfrac = trace.realfraction;
tracemins[0] = min(start[0], end[0]);
tracemaxs[0] = max(start[0], end[0]);
tracemins[1] = min(start[1], end[1]);
tracemaxs[1] = max(start[1], end[1]);
tracemins[2] = min(start[2], end[2]);
tracemaxs[2] = max(start[2], end[2]);
// look for embedded bmodels
for (n = 0;n < cl.num_entities;n++)
{
if (!cl.entities_active[n])
continue;
ent = &cl.entities[n].render;
if (!BoxesOverlap(ent->mins, ent->maxs, tracemins, tracemaxs))
continue;
if (!ent->model || !ent->model->TraceBox)
continue;
if ((ent->flags & RENDER_EXTERIORMODEL) && !chase_active.integer)
continue;
// if transparent and not selectable, skip entity
if (!(cl.entities[n].state_current.effects & EF_SELECTABLE) && (ent->alpha < 1 || (ent->effects & (EF_ADDITIVE | EF_NODEPTHTEST))))
continue;
if (ent == ignoreent)
continue;
Matrix4x4_Transform(&ent->inversematrix, start, starttransformed);
Matrix4x4_Transform(&ent->inversematrix, end, endtransformed);
Collision_ClipTrace_Box(&trace, ent->model->normalmins, ent->model->normalmaxs, starttransformed, vec3_origin, vec3_origin, endtransformed, SUPERCONTENTS_SOLID, SUPERCONTENTS_SOLID, 0, NULL);
if (maxrealfrac < trace.realfraction)
continue;
//if (ent->model && ent->model->TraceBox)
ent->model->TraceBox(ent->model, ent->frameblend[0].frame, &trace, starttransformed, vec3_origin, vec3_origin, endtransformed, SUPERCONTENTS_SOLID);
if (maxrealfrac > trace.realfraction)
{
if (hitent)
*hitent = n;
maxfrac = trace.fraction;
maxrealfrac = trace.realfraction;
if (normal)
{
VectorCopy(trace.plane.normal, tempnormal);
Matrix4x4_Transform3x3(&ent->matrix, tempnormal, normal);
}
}
}
maxfrac = bound(0, maxfrac, 1);
maxrealfrac = bound(0, maxrealfrac, 1);
//if (maxfrac < 0 || maxfrac > 1) Con_Printf("fraction out of bounds %f %s:%d\n", maxfrac, __FILE__, __LINE__);
if (impact)
VectorLerp(start, maxfrac, end, impact);
return maxfrac;
}
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
3 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest