2023-02-05 10:07:35 +00:00
// ============================================================================
// Navmesh editing functions
// ============================================================================
# define NAV_MAX_SELECTED_VERTS 4
int * selected_verts ;
float selected_vert_count ;
navmesh_vertex * cl_navmesh_verts ;
float cl_navmesh_vert_count ;
navmesh_poly * cl_navmesh_polies ;
float cl_navmesh_poly_count ;
2023-02-05 15:04:21 +00:00
navmesh_traversal * cl_navmesh_traversals ;
float cl_navmesh_traversal_count ;
float cl_navmesh_selected_traversal ;
float cl_navmesh_traversal_edit_mode ;
float cl_navmesh_traversal_editor_cur_point ;
2023-02-05 10:07:35 +00:00
// float selected_verts[NAV_MAX_SELECTED_VERTS];
// float selected_vert_count;
// navmesh_vertex cl_navmesh_verts[NAV_MAX_VERTS];
// float cl_navmesh_vert_count;
// navmesh_poly cl_navmesh_polies[NAV_MAX_POLIES];
// float cl_navmesh_poly_count;
float cl_navmesh_place_corner_state ;
# define NAVMESH_PLACE_CORNER_PLACING 1
# define NAVMESH_PLACE_CORNER_CONFIRM 2
// Declarations
void ( vector pos , vector scale , vector color , float alpha ) cl_navmesh_draw_test_ent ;
2023-08-01 10:20:35 +00:00
float ( vector pos , float poly_index ) cl_navmesh_is_inside_poly ;
2023-02-05 10:07:35 +00:00
void ( ) cl_navmesh_delete_verts ;
void ( float poly_index ) cl_navmesh_delete_poly_at_index ;
float ( vector pos ) cl_navmesh_get_containing_poly ;
//This is for placing a temp entity to test the pathfinding
vector goalent_pos ;
float goalent_set ;
vector startent_pos ;
float startent_set ;
void cl_navmesh_draw_box ( vector pos , vector size , vector color , float alpha ) {
//Assigning the shader as something else so that fte doesn't batch the calls (leading to colors not changing between draw calls)
R_BeginPolygon ( " debug/wireframe " , 0 ) ;
R_BeginPolygon ( " debug/solid_nocull " , 0 ) ;
// Size is radius:
size = size * 0.5 ;
//bottom face
R_PolygonVertex ( pos + [ - size . x , size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , - size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ - size . x , - size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Top face
R_PolygonVertex ( pos + [ - size . x , size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , - size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ - size . x , - size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Front face
R_PolygonVertex ( pos + [ - size . x , - size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , - size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , - size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ - size . x , - size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Back face
R_PolygonVertex ( pos + [ - size . x , size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ - size . x , size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Left face
R_PolygonVertex ( pos + [ - size . x , - size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ - size . x , size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ - size . x , size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ - size . x , - size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Right face
R_PolygonVertex ( pos + [ size . x , - size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , size . y , - size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ size . x , - size . y , size . z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
}
void cl_navmesh_draw_vert ( vector pos , vector color , float alpha ) {
cl_navmesh_draw_box ( pos , [ 4 , 4 , 4 ] , color , alpha ) ;
}
void cl_navmesh_draw_plane ( vector pos , vector nor , float size , vector color , float alpha ) {
//Assigning the shader as something else so that fte doesn't batch the calls (leading to colors not changing between draw calls)
R_BeginPolygon ( " debug/wireframe " , 0 ) ;
R_BeginPolygon ( " debug/solid_nocull " , 0 ) ;
vector nor_angles = vectoangles ( nor ) ;
makevectors ( nor_angles ) ;
vector tl = pos + ( - v_right + v_up ) * size ;
vector tr = pos + ( v_right + v_up ) * size ;
vector bl = pos + ( - v_right - v_up ) * size ;
vector br = pos + ( v_right - v_up ) * size ;
// Front face
R_PolygonVertex ( bl , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( br , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( tr , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( tl , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
// Back face -- not needed when backface culling is disabled
// R_PolygonVertex(bl, [0,0,0], color,alpha);
// R_PolygonVertex(tl, [0,0,0], color,alpha);
// R_PolygonVertex(tr, [0,0,0], color,alpha);
// R_PolygonVertex(br, [0,0,0], color,alpha);
// R_EndPolygon();
}
void cl_navmesh_draw_line ( vector start , vector end , float edge_width , vector color , float alpha ) {
R_BeginPolygon ( " debug/wireframe " , 0 ) ;
R_BeginPolygon ( " debug/solid_nocull " , 0 ) ;
R_PolygonVertex ( end + [ 0 , 0 , - edge_width ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( end + [ 0 , 0 , edge_width ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( start + [ 0 , 0 , edge_width ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( start + [ 0 , 0 , - edge_width ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
}
2023-08-01 10:20:35 +00:00
void cl_navmesh_draw_edge ( vector start , vector end , vector color , float alpha ) {
cl_navmesh_draw_line ( start , end , 1 , color , alpha ) ;
2023-02-05 10:07:35 +00:00
}
void cl_navmesh_draw_quad ( vector a , vector b , vector c , vector d , vector color , float alpha , int draw_edges ) {
//Assigning the shader as something else so that fte doesn't batch the calls (leading to colors not changing between draw calls)
R_BeginPolygon ( " debug/wireframe " , 0 ) ;
R_BeginPolygon ( " debug/solid_nocull " , 0 ) ;
R_PolygonVertex ( a , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( b , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( c , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( d , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Drawing polygon border
2023-08-01 10:20:35 +00:00
if ( draw_edges = = TRUE ) {
vector border_color = [ 0.5 , 0.5 , 0.5 ] ;
float border_alpha = 0.2 ;
cl_navmesh_draw_edge ( a , b , border_color , border_alpha ) ;
cl_navmesh_draw_edge ( b , c , border_color , border_alpha ) ;
cl_navmesh_draw_edge ( c , d , border_color , border_alpha ) ;
cl_navmesh_draw_edge ( d , a , border_color , border_alpha ) ;
2023-02-05 10:07:35 +00:00
}
}
2023-02-05 15:04:21 +00:00
// Draws a 3D line using a box
void cl_navmesh_draw_line_3d ( vector start , vector end , float edge_width , vector color , float alpha ) {
// drawline(edge_width, start, end, color, alpha, drawflag???);
// return;
vector v_f = normalize ( end - start ) ;
vector v_r = normalize ( crossproduct ( v_f , ' 0 0 1 ' ) ) ;
vector v_u = normalize ( crossproduct ( v_r , v_f ) ) ;
// Special degenerate case: v_f is straight up
if ( end . x = = start . x & & end . y = = start . y ) {
v_f = ' 0 0 1 ' ;
v_r = ' 1 0 0 ' ;
v_u = ' 0 1 0 ' ;
}
// 4 corners around start
vector start_bl = start + edge_width * ( - v_r - v_u ) ;
vector start_br = start + edge_width * ( v_r - v_u ) ;
vector start_tr = start + edge_width * ( v_r + v_u ) ;
vector start_tl = start + edge_width * ( - v_r + v_u ) ;
// 4 corners around end
vector end_bl = end + edge_width * ( - v_r - v_u ) ;
vector end_br = end + edge_width * ( v_r - v_u ) ;
vector end_tr = end + edge_width * ( v_r + v_u ) ;
vector end_tl = end + edge_width * ( - v_r + v_u ) ;
cl_navmesh_draw_quad ( start_bl , start_br , start_tr , start_tl , color , alpha , false ) ; // Start quad
cl_navmesh_draw_quad ( start_tr , end_tr , end_tl , start_tl , color , alpha , false ) ; // Top quad
cl_navmesh_draw_quad ( start_br , end_br , end_bl , start_bl , color , alpha , false ) ; // Bottom quad
cl_navmesh_draw_quad ( start_br , end_br , end_tr , start_tr , color , alpha , false ) ; // Right quad
cl_navmesh_draw_quad ( start_bl , end_bl , end_tl , start_tl , color , alpha , false ) ; // Left quad
cl_navmesh_draw_quad ( end_bl , end_tl , end_tr , end_br , color , alpha , false ) ; // End quad
}
2023-02-05 10:07:35 +00:00
void cl_navmesh_draw_tri ( vector a , vector b , vector c , vector color , float alpha , int draw_edges ) {
//Assigning the shader as something else so that fte doesn't batch the calls (leading to colors not changing between draw calls)
R_BeginPolygon ( " debug/wireframe " , 0 ) ;
R_BeginPolygon ( " debug/solid_nocull " , 0 ) ;
R_PolygonVertex ( a , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( b , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( c , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Drawing polygon border
if ( draw_edges = = TRUE )
{
2023-08-01 10:20:35 +00:00
vector border_color = [ 0.5 , 0.5 , 0.5 ] ;
float border_alpha = 0.2 ;
cl_navmesh_draw_edge ( a , b , border_color , border_alpha ) ;
cl_navmesh_draw_edge ( b , c , border_color , border_alpha ) ;
cl_navmesh_draw_edge ( c , a , border_color , border_alpha ) ;
2023-02-05 10:07:35 +00:00
}
}
void cl_navmesh_draw_poly ( float poly_index ) {
vector face_color = [ 0.2 , 0.8 , 0.2 ] ;
float face_alpha = 0.1 ;
vector a = cl_navmesh_verts [ cl_navmesh_polies [ poly_index ] . verts [ 0 ] ] . pos ;
vector b = cl_navmesh_verts [ cl_navmesh_polies [ poly_index ] . verts [ 1 ] ] . pos ;
vector c = cl_navmesh_verts [ cl_navmesh_polies [ poly_index ] . verts [ 2 ] ] . pos ;
// Draw door polygons as red
if ( cl_navmesh_polies [ poly_index ] . doortarget ! = " " ) {
face_color = [ 0.8 , 0.2 , 0.2 ] ;
}
2023-08-08 05:24:15 +00:00
if ( cl_navmesh_polies [ poly_index ] . vert_count = = 3 ) {
2023-02-05 10:07:35 +00:00
cl_navmesh_draw_tri ( a , b , c , face_color , face_alpha , TRUE ) ;
}
2023-08-08 05:24:15 +00:00
else {
2023-02-05 10:07:35 +00:00
vector d = cl_navmesh_verts [ cl_navmesh_polies [ poly_index ] . verts [ 3 ] ] . pos ;
cl_navmesh_draw_quad ( a , b , c , d , face_color , face_alpha , TRUE ) ;
}
}
2023-02-05 15:04:21 +00:00
2023-08-01 10:20:35 +00:00
// Calculates world-space coordinate of traversal endpoint
2023-08-07 05:50:46 +00:00
vector cl_navmesh_get_traversal_midpoint_pos ( float traversal_index ) {
2023-08-01 10:20:35 +00:00
vector start_pos = cl_navmesh_traversals [ traversal_index ] . start_pos ;
vector midpoint_pos = cl_navmesh_traversals [ traversal_index ] . midpoint_pos ;
// vector end_pos = cl_navmesh_traversals[traversal_index].end_pos;
float angle = cl_navmesh_traversals [ traversal_index ] . angle ;
// Midpoint / endpoint pos are relative to start_pos:
makevectors ( [ 0 , angle , 0 ] ) ;
midpoint_pos = start_pos + ( v_right * midpoint_pos . x ) + ( v_forward * midpoint_pos . y ) + ( v_up * midpoint_pos . z ) ;
// end_pos = start_pos + (v_right * end_pos.x) + (v_forward * end_pos.y) + (v_up * end_pos.z);
return midpoint_pos ;
}
// Calculates world-space coordinate of traversal midpoint
vector cl_navmesh_get_traversal_end_pos ( float traversal_index ) {
vector start_pos = cl_navmesh_traversals [ traversal_index ] . start_pos ;
// vector midpoint_pos = cl_navmesh_traversals[traversal_index].midpoint_pos;
vector end_pos = cl_navmesh_traversals [ traversal_index ] . end_pos ;
float angle = cl_navmesh_traversals [ traversal_index ] . angle ;
// Midpoint / endpoint pos are relative to start_pos:
makevectors ( [ 0 , angle , 0 ] ) ;
// midpoint_pos = start_pos + (v_right * midpoint_pos.x) + (v_forward * midpoint_pos.y) + (v_up * midpoint_pos.z);
end_pos = start_pos + ( v_right * end_pos . x ) + ( v_forward * end_pos . y ) + ( v_up * end_pos . z ) ;
return end_pos ;
}
2023-02-05 15:04:21 +00:00
void cl_navmesh_draw_traversal ( float traversal_index ) {
vector start_pos = cl_navmesh_traversals [ traversal_index ] . start_pos ;
vector midpoint_pos = cl_navmesh_traversals [ traversal_index ] . midpoint_pos ;
vector end_pos = cl_navmesh_traversals [ traversal_index ] . end_pos ;
float angle = cl_navmesh_traversals [ traversal_index ] . angle ;
// Midpoint / endpoint pos are relative to start_pos:
makevectors ( [ 0 , angle , 0 ] ) ;
midpoint_pos = start_pos + ( v_right * midpoint_pos . x ) + ( v_forward * midpoint_pos . y ) + ( v_up * midpoint_pos . z ) ;
end_pos = start_pos + ( v_right * end_pos . x ) + ( v_forward * end_pos . y ) + ( v_up * end_pos . z ) ;
vector active_color = ' 0 1 1 ' ;
vector inactive_color = ' 0.5 0 0 ' ;
vector edge_color = ' 0.8 0.8 0.8 ' ;
float active_alpha = 0.4 ;
float inactive_alpha = 0.2 ;
float edge_alpha = 0.3 ;
float edge_width = 0.1 ;
if ( cl_navmesh_selected_traversal ! = traversal_index ) {
2023-08-06 05:53:44 +00:00
active_alpha = 0.4 ;
2023-02-05 15:04:21 +00:00
inactive_alpha = 0.0 ;
2023-08-06 05:53:44 +00:00
edge_alpha = 0.4 ;
2023-02-05 15:04:21 +00:00
edge_color = ' 0 0.2 0.5 ' ;
active_color = ' 0 0.2 0.5 ' ;
}
cl_navmesh_draw_box ( start_pos , [ 4 , 4 , 4 ] , active_color , 0.8 ) ;
cl_navmesh_draw_box ( end_pos , [ 4 , 4 , 4 ] , active_color , 0.8 ) ;
if ( cl_navmesh_traversals [ traversal_index ] . use_midpoint ) {
cl_navmesh_draw_box ( midpoint_pos , [ 4 , 4 , 4 ] , active_color , active_alpha ) ;
cl_navmesh_draw_line_3d ( start_pos , midpoint_pos , edge_width , edge_color , active_alpha ) ;
cl_navmesh_draw_line_3d ( midpoint_pos , end_pos , edge_width , edge_color , edge_alpha ) ;
}
else {
cl_navmesh_draw_box ( midpoint_pos , [ 4 , 4 , 4 ] , inactive_color , inactive_alpha ) ;
cl_navmesh_draw_line_3d ( start_pos , end_pos , edge_width , edge_color , edge_alpha ) ;
}
// Draw arrows along edge:
vector arrow_pos ;
float arrowhead_length = 2 ;
vector v_f ;
vector v_r ;
vector v_u ;
float inc = 0.09 ;
float start = inc * ( time % 1.0 ) ;
for ( float t = start ; t < = 1.0 ; t + = inc ) {
if ( cl_navmesh_traversals [ traversal_index ] . use_midpoint ) {
if ( t < 0.5 ) {
arrow_pos = start_pos + 2.0 * t * ( midpoint_pos - start_pos ) ;
v_f = normalize ( midpoint_pos - start_pos ) ;
v_r = normalize ( crossproduct ( v_f , ' 0 0 1 ' ) ) ;
v_u = normalize ( crossproduct ( v_r , v_f ) ) ;
}
else {
arrow_pos = midpoint_pos + 2.0 * ( t - 0.5 ) * ( end_pos - midpoint_pos ) ;
v_f = normalize ( end_pos - midpoint_pos ) ;
v_r = normalize ( crossproduct ( v_f , ' 0 0 1 ' ) ) ;
v_u = normalize ( crossproduct ( v_r , v_f ) ) ;
}
}
else {
arrow_pos = start_pos + t * ( end_pos - start_pos ) ;
v_f = normalize ( end_pos - start_pos ) ;
v_r = normalize ( crossproduct ( v_f , ' 0 0 1 ' ) ) ;
v_u = normalize ( crossproduct ( v_r , v_f ) ) ;
}
cl_navmesh_draw_line_3d ( arrow_pos , arrow_pos + arrowhead_length * ( - v_f - v_r ) , edge_width , edge_color , edge_alpha ) ;
cl_navmesh_draw_line_3d ( arrow_pos , arrow_pos + arrowhead_length * ( - v_f + v_r ) , edge_width , edge_color , edge_alpha ) ;
}
// Traversal edit mode modifications:
if ( cl_navmesh_selected_traversal = = traversal_index ) {
if ( cl_navmesh_traversal_edit_mode ) {
2023-08-01 10:20:35 +00:00
vector pos = ' 0 0 0 ' ;
2023-02-05 15:04:21 +00:00
if ( cl_navmesh_traversal_editor_cur_point = = 0 ) {
pos = start_pos ;
}
else if ( cl_navmesh_traversal_editor_cur_point = = 1 ) {
pos = midpoint_pos ;
}
else if ( cl_navmesh_traversal_editor_cur_point = = 2 ) {
pos = end_pos ;
}
float g = 2 ; // gap from center
float l = 1.5 ; // line length
vector select_color = ' 1 1 0 ' ;
float select_width = 0.1 ; // line width
float select_alpha = 0.8 ;
// FTL
cl_navmesh_draw_line_3d ( pos + [ - g , - g , g ] , pos + [ - g , - g , g ] + [ l , 0 , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ - g , - g , g ] , pos + [ - g , - g , g ] + [ 0 , l , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ - g , - g , g ] , pos + [ - g , - g , g ] + [ 0 , 0 , - l ] , select_width , select_color , select_alpha ) ;
// FTR
cl_navmesh_draw_line_3d ( pos + [ g , - g , g ] , pos + [ g , - g , g ] + [ - l , 0 , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ g , - g , g ] , pos + [ g , - g , g ] + [ 0 , l , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ g , - g , g ] , pos + [ g , - g , g ] + [ 0 , 0 , - l ] , select_width , select_color , select_alpha ) ;
// BTL
cl_navmesh_draw_line_3d ( pos + [ - g , g , g ] , pos + [ - g , g , g ] + [ l , 0 , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ - g , g , g ] , pos + [ - g , g , g ] + [ 0 , - l , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ - g , g , g ] , pos + [ - g , g , g ] + [ 0 , 0 , - l ] , select_width , select_color , select_alpha ) ;
// BTR
cl_navmesh_draw_line_3d ( pos + [ g , g , g ] , pos + [ g , g , g ] + [ - l , 0 , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ g , g , g ] , pos + [ g , g , g ] + [ 0 , - l , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ g , g , g ] , pos + [ g , g , g ] + [ 0 , 0 , - l ] , select_width , select_color , select_alpha ) ;
// FBL
cl_navmesh_draw_line_3d ( pos + [ - g , - g , - g ] , pos + [ - g , - g , - g ] + [ l , 0 , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ - g , - g , - g ] , pos + [ - g , - g , - g ] + [ 0 , l , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ - g , - g , - g ] , pos + [ - g , - g , - g ] + [ 0 , 0 , l ] , select_width , select_color , select_alpha ) ;
// FBR
cl_navmesh_draw_line_3d ( pos + [ g , - g , - g ] , pos + [ g , - g , - g ] + [ - l , 0 , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ g , - g , - g ] , pos + [ g , - g , - g ] + [ 0 , l , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ g , - g , - g ] , pos + [ g , - g , - g ] + [ 0 , 0 , l ] , select_width , select_color , select_alpha ) ;
// BBL
cl_navmesh_draw_line_3d ( pos + [ - g , g , - g ] , pos + [ - g , g , - g ] + [ l , 0 , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ - g , g , - g ] , pos + [ - g , g , - g ] + [ 0 , - l , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ - g , g , - g ] , pos + [ - g , g , - g ] + [ 0 , 0 , l ] , select_width , select_color , select_alpha ) ;
// BBR
cl_navmesh_draw_line_3d ( pos + [ g , g , - g ] , pos + [ g , g , - g ] + [ - l , 0 , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ g , g , - g ] , pos + [ g , g , - g ] + [ 0 , - l , 0 ] , select_width , select_color , select_alpha ) ;
cl_navmesh_draw_line_3d ( pos + [ g , g , - g ] , pos + [ g , g , - g ] + [ 0 , 0 , l ] , select_width , select_color , select_alpha ) ;
}
}
}
2023-02-05 10:07:35 +00:00
float cl_navmesh_is_vert_selected ( float vert_index )
{
for ( float i = 0 ; i < selected_vert_count ; i + + )
{
if ( selected_verts [ i ] = = vert_index )
return TRUE ;
}
return FALSE ;
}
void ( ) cl_navmesh_pathfind_draw_result_path ;
2023-08-01 10:20:35 +00:00
void ( ) cl_navmesh_pathfind_draw_result_portals ;
void ( ) cl_navmesh_pathfind_draw_result_point_path ;
2023-02-05 10:07:35 +00:00
2023-08-12 06:04:06 +00:00
entity cl_navmesh_traversal_test_ent ;
void cl_navmesh_draw_traversal_test_ent ( ) {
if ( cl_navmesh_selected_traversal = = - 1 ) {
return ;
}
if ( cl_navmesh_traversal_test_ent = = world ) {
cl_navmesh_traversal_test_ent = spawn ( ) ;
setmodel ( cl_navmesh_traversal_test_ent , " models/ai/zfull.mdl " ) ;
print ( " woo \n " ) ;
}
float traversal_speed_scale = 1.0 ; // 4x slower
float lerp_frac = ( time * traversal_speed_scale ) % 1.0 ;
cl_navmesh_traversal_test_ent . angles . y = cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . angle ;
makevectors ( [ 0 , cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . angle , 0 ] ) ;
cl_navmesh_traversal_test_ent . drawmask = MASK_ENGINE ;
vector start_pos = cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . start_pos ;
vector end_pos = cl_navmesh_get_traversal_end_pos ( cl_navmesh_selected_traversal ) ;
// cl_navmesh_traversal_test_ent.origin = start_pos + lerp_frac * (end_pos - start_pos);
float trav_state_a_dur ;
float trav_state_b_dur ;
float trav_state_c_dur ;
float lerp_subfrac ;
float ent_framenum ;
float ent_frametime = 0.1 ;
// TODO - Detect traversal type
// string traversal_type = "ledge";
string traversal_type = " leap " ;
if ( traversal_type = = " ledge " ) {
vector ledge_pos ;
float traversal_height = end_pos . z - start_pos . z ;
if ( traversal_height < 0 ) {
trav_state_a_dur = min ( - traversal_height * ( 0.35 / 100.0 ) , 2.0 ) ;
trav_state_b_dur = 6 * ent_frametime * 0.5 ; // double-speed
lerp_frac * = ( trav_state_a_dur + trav_state_b_dur ) ;
if ( lerp_frac < trav_state_a_dur ) {
lerp_subfrac = lerp_frac / ( trav_state_a_dur ) ;
cl_navmesh_traversal_test_ent . origin = lerp_vector ( start_pos , end_pos , lerp_subfrac * lerp_subfrac ) ;
ent_framenum = 150 + lerp_subfrac * 4 ;
}
else {
lerp_subfrac = ( lerp_frac - ( trav_state_a_dur ) ) / trav_state_b_dur ;
cl_navmesh_traversal_test_ent . origin = end_pos ;
ent_framenum = 154 + lerp_subfrac * 6 ;
}
}
else if ( traversal_height < 98 ) {
ledge_pos = end_pos - ' 0 0 85 ' - v_forward * 28 ;
trav_state_a_dur = 3 * ent_frametime ;
trav_state_b_dur = 11 * ent_frametime ;
lerp_frac * = trav_state_a_dur + trav_state_b_dur ;
// Play low jump anim (frames 163-165)
if ( lerp_frac < trav_state_a_dur ) {
lerp_subfrac = lerp_frac / ( trav_state_a_dur ) ;
cl_navmesh_traversal_test_ent . origin = lerp_vector ( start_pos , ledge_pos , lerp_subfrac ) ;
ent_framenum = 163 + lerp_subfrac * 6 ;
}
// Lerp to endpos, play low climb anim (frames 170-180)
else {
lerp_subfrac = ( lerp_frac - ( trav_state_a_dur ) ) / trav_state_b_dur ;
cl_navmesh_traversal_test_ent . origin = lerp_vector ( ledge_pos , end_pos , lerp_subfrac ) ;
ent_framenum = 170 + lerp_subfrac * 10 ;
}
}
else {
// Traversal states:
ledge_pos = end_pos - ' 0 0 98 ' - v_forward * 28 ;
trav_state_a_dur = 6 * ent_frametime ;
trav_state_b_dur = 0.15 ;
trav_state_c_dur = 14 * ent_frametime ;
lerp_frac * = trav_state_a_dur + trav_state_b_dur + trav_state_c_dur ;
// Play jump anim (frames 160-166)
if ( lerp_frac < trav_state_a_dur ) {
lerp_subfrac = lerp_frac / ( trav_state_a_dur ) ;
cl_navmesh_traversal_test_ent . origin = start_pos ;
ent_framenum = 160 + lerp_subfrac * 6 ;
}
// Lerp to ledge pos
else if ( lerp_frac < trav_state_a_dur + trav_state_b_dur ) {
lerp_subfrac = ( lerp_frac - ( trav_state_a_dur ) ) / trav_state_b_dur ;
cl_navmesh_traversal_test_ent . origin = lerp_vector ( start_pos , ledge_pos , lerp_subfrac ) ;
ent_framenum = 166 + lerp_subfrac * 1 ;
}
// Lerp to endpos, play climb anim (frames 167-180)
else {
lerp_subfrac = ( lerp_frac - ( trav_state_a_dur + trav_state_b_dur ) ) / trav_state_c_dur ;
cl_navmesh_traversal_test_ent . origin = lerp_vector ( ledge_pos , end_pos , lerp_subfrac ) ;
ent_framenum = 167 + lerp_subfrac * 14 ;
}
}
}
else if ( traversal_type = = " leap " ) {
trav_state_a_dur = 5 * ent_frametime ; // Play anim (218-233)
trav_state_b_dur = 8 * ent_frametime ; // Play anim (233-241), lerp across gap
trav_state_c_dur = 5 * ent_frametime ; // Play anim (242-247)
lerp_frac * = ( trav_state_a_dur + trav_state_b_dur + trav_state_c_dur ) ;
lerp_subfrac = lerp_frac ;
if ( lerp_frac < trav_state_a_dur ) {
lerp_subfrac = lerp_frac / ( trav_state_a_dur ) ;
cl_navmesh_traversal_test_ent . origin = start_pos ;
ent_framenum = 228 + lerp_subfrac * 5 ;
}
else if ( lerp_frac < trav_state_a_dur + trav_state_b_dur ) {
lerp_subfrac = ( lerp_frac - ( trav_state_a_dur ) ) / trav_state_b_dur ;
vector midpoint_pos = cl_navmesh_get_traversal_midpoint_pos ( cl_navmesh_selected_traversal ) ;
cl_navmesh_traversal_test_ent . origin = lerp_vector_bezier ( start_pos , midpoint_pos , end_pos , lerp_subfrac ) ;
ent_framenum = 233 + lerp_subfrac * 8 ;
}
else {
lerp_subfrac = ( lerp_frac - ( trav_state_a_dur + trav_state_b_dur ) ) / trav_state_c_dur ;
cl_navmesh_traversal_test_ent . origin = end_pos ;
ent_framenum = 242 + lerp_subfrac * 5 ;
}
}
cl_navmesh_traversal_test_ent . frame = floor ( ent_framenum ) ;
cl_navmesh_traversal_test_ent . frame2 = floor ( ent_framenum + 1 ) ;
cl_navmesh_traversal_test_ent . lerpfrac = ent_framenum % 1.0 ;
}
2023-02-05 10:07:35 +00:00
2023-02-05 15:04:21 +00:00
void cl_navmesh_editor_draw ( ) {
for ( float i = 0 ; i < cl_navmesh_vert_count ; i + + ) {
2023-02-05 10:07:35 +00:00
vector color = [ 0 , 0 , 1 ] ;
2023-02-05 15:04:21 +00:00
if ( cl_navmesh_is_vert_selected ( i ) ) {
2023-02-05 10:07:35 +00:00
color = [ 1 , 1 , 0 ] ;
2023-02-05 15:04:21 +00:00
}
2023-02-05 10:07:35 +00:00
cl_navmesh_draw_vert ( cl_navmesh_verts [ i ] . pos , color , 0.4 ) ;
}
2023-02-05 15:04:21 +00:00
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + ) {
2023-02-05 10:07:35 +00:00
cl_navmesh_draw_poly ( i ) ;
}
2023-02-05 15:04:21 +00:00
for ( float i = 0 ; i < cl_navmesh_traversal_count ; i + + ) {
cl_navmesh_draw_traversal ( i ) ;
}
2023-02-05 10:07:35 +00:00
2023-08-28 08:11:51 +00:00
if ( startent_set ) {
cl_navmesh_draw_test_ent ( startent_pos , [ 1 , 1 , 1 ] , [ 0 , 1 , 0 ] , 0.4 ) ;
}
if ( goalent_set ) {
cl_navmesh_draw_test_ent ( goalent_pos , [ 1 , 1 , 1 ] , [ 1 , 0 , 0 ] , 0.4 ) ;
}
2023-02-05 10:07:35 +00:00
cl_navmesh_pathfind_draw_result_path ( ) ;
2023-08-01 10:20:35 +00:00
cl_navmesh_pathfind_draw_result_portals ( ) ;
cl_navmesh_pathfind_draw_result_point_path ( ) ;
2023-08-12 06:04:06 +00:00
// If a traversal is selected, draw a zombie being animated
if ( cl_navmesh_selected_traversal ! = - 1 ) {
cl_navmesh_draw_traversal_test_ent ( ) ;
}
2023-02-05 10:07:35 +00:00
//The following code block is for detecting and placing waypoints at bsp map corners
2023-08-08 05:24:15 +00:00
if ( cl_navmesh_place_corner_state = = NAVMESH_PLACE_CORNER_PLACING | | cl_navmesh_place_corner_state = = NAVMESH_PLACE_CORNER_CONFIRM ) {
2023-02-05 10:07:35 +00:00
//vector vorg = getentity(player_localentnum, GE_ORIGIN);
//vector vorg = getviewprop(VF_ORIGIN) - VEC_VIEW_OFS;
vector vorg = getviewprop ( VF_ORIGIN ) ;
vector vang = getviewprop ( VF_ANGLES ) ;
vector vang_left1 = vang + [ 0 , 6 , 0 ] ; //Two degrees to the left
vector vang_left2 = vang + [ 0 , 5 , 0 ] ; //Three degrees to the left
vector vang_right1 = vang + [ 0 , - 6 , 0 ] ; //Two degrees to the right
vector vang_right2 = vang + [ 0 , - 5 , 0 ] ; //Three degrees to the right
makevectors ( vang ) ;
vector vang_fwd = v_forward ;
makevectors ( vang_left1 ) ;
vector vang_left1_fwd = v_forward ;
//Trace out to wall
tracebox ( vorg , VEC_HULL_MIN , VEC_HULL_MAX , vorg + ( vang_left1_fwd * 2000 ) , 1 , self ) ;
vector wall_hit_left1 = vorg + ( vang_left1_fwd * 2000 ) * trace_fraction ;
//Trace down to ground
tracebox ( wall_hit_left1 , VEC_HULL_MIN , VEC_HULL_MAX , wall_hit_left1 + ( [ 0 , 0 , - 1 ] * 2000 ) , 1 , self ) ;
vector hit_left1 = wall_hit_left1 + ( ( [ 0 , 0 , - 1 ] * 2000 ) * trace_fraction ) ;
makevectors ( vang_left2 ) ;
vector vang_left2_fwd = v_forward ;
//Trace out to wall
tracebox ( vorg , VEC_HULL_MIN , VEC_HULL_MAX , vorg + ( vang_left2_fwd * 2000 ) , 1 , self ) ;
vector wall_hit_left2 = vorg + ( vang_left2_fwd * 2000 ) * trace_fraction ;
//Trace down to ground
tracebox ( wall_hit_left2 , VEC_HULL_MIN , VEC_HULL_MAX , wall_hit_left2 + ( [ 0 , 0 , - 1 ] * 2000 ) , 1 , self ) ;
vector hit_left2 = wall_hit_left2 + ( ( [ 0 , 0 , - 1 ] * 2000 ) * trace_fraction ) ;
makevectors ( vang_right1 ) ;
vector vang_right1_fwd = v_forward ;
//Trace out to wall
tracebox ( vorg , VEC_HULL_MIN , VEC_HULL_MAX , vorg + ( vang_right1_fwd * 2000 ) , 1 , self ) ;
vector wall_hit_right1 = vorg + ( vang_right1_fwd * 2000 ) * trace_fraction ;
//Trace down to ground
tracebox ( wall_hit_right1 , VEC_HULL_MIN , VEC_HULL_MAX , wall_hit_right1 + ( [ 0 , 0 , - 1 ] * 2000 ) , 1 , self ) ;
vector hit_right1 = wall_hit_right1 + ( ( [ 0 , 0 , - 1 ] * 2000 ) * trace_fraction ) ;
makevectors ( vang_right2 ) ;
vector vang_right2_fwd = v_forward ;
//Trace out to wall
tracebox ( vorg , VEC_HULL_MIN , VEC_HULL_MAX , vorg + ( vang_right2_fwd * 2000 ) , 1 , self ) ;
vector wall_hit_right2 = vorg + ( vang_right2_fwd * 2000 ) * trace_fraction ;
//Trace down to ground
tracebox ( wall_hit_right2 , VEC_HULL_MIN , VEC_HULL_MAX , wall_hit_right2 + ( [ 0 , 0 , - 1 ] * 2000 ) , 1 , self ) ;
vector hit_right2 = wall_hit_right2 + ( ( [ 0 , 0 , - 1 ] * 2000 ) * trace_fraction ) ;
cl_navmesh_draw_vert ( hit_left1 , [ 0 , 0 , 1 ] , 0.4 ) ;
cl_navmesh_draw_vert ( hit_left2 , [ 0 , 0 , 1 ] , 0.4 ) ;
cl_navmesh_draw_vert ( hit_right1 , [ 0 , 0 , 1 ] , 0.4 ) ;
cl_navmesh_draw_vert ( hit_right2 , [ 0 , 0 , 1 ] , 0.4 ) ;
// Draw walls that we have hit (2 flat planes on the walls we hit)
vector up = [ 0 , 0 , 1 ] ;
vector left_wall_pos = ( hit_left1 + hit_left2 ) * 0.5 ;
vector left_wall_nor = - 1 * crossproduct ( hit_left1 - hit_left2 , up ) ;
vector right_wall_pos = ( hit_right1 + hit_right2 ) * 0.5 ;
vector right_wall_nor = - 1 * crossproduct ( hit_right1 - hit_right2 , up ) ;
cl_navmesh_draw_plane ( left_wall_pos , left_wall_nor , 50 , [ 0 , 1 , 1 ] , 0.1 ) ;
cl_navmesh_draw_plane ( right_wall_pos , right_wall_nor , 50 , [ 1 , 0 , 1 ] , 0.1 ) ;
//===================== Calculate the resolved vertex pos =======================
//This solution only works for 90 degree corners
//Line 1 extends from l1a in direction l1_dir
/*vector l1a;
l1a = hit_left2 ;
vector l1_dir = normalize ( hit_left1 - l1a ) ;
//The third point
vector p2 = hit_right1 ;
//Considering only l1_dir in the 2d plane of z=0
vector vert_loc ;
vert_loc . z = l1a . z ;
//Is the line vertical?
if ( l1_dir . x = = 0 ) //Is the line vertical?
{
vert_loc . x = l1a . x ;
vert_loc . y = p2 . y ;
//FIXME
}
else if ( l1_dir . y = = 0 ) //Is the line horizontal?
{
vert_loc . x = p2 . x ;
vert_loc . y = l1a . y ;
//FIXME
}
else
{
//Getting slope of line 1
float m = ( hit_left1 . y - hit_left2 . y ) / ( hit_left1 . x - hit_left2 . x ) ;
float x1 = p2 . x - l1a . x ;
float y1 = p2 . y - l1a . y ;
vert_loc . x = ( x1 + m * y1 ) / ( m * m + 1 ) ;
vert_loc . y = ( - 1 / m ) * ( vert_loc . x - x1 ) + y1 ;
}
vert_loc . x + = l1a . x ;
vert_loc . y + = l1a . y ; */
//===============================================================================
//This solution should work with any corner
//===============================================================================
//Line 1: hit_left1 -> hit_left2
//Line 2: hit_right1 -> hit_right2
vector vert_loc ;
float skew = 0 ;
float horizontal = 1 ;
float vertical = 2 ;
float line_1_orient = skew ;
float line_2_orient = skew ;
//Check left wall is horizontal
if ( hit_left1 . y = = hit_left2 . y )
{
line_1_orient = horizontal ;
}
//Check if the left wall is vertical
else if ( hit_left1 . x = = hit_left2 . x )
{
line_1_orient = vertical ;
}
//Check if the right wall is horizontal
if ( hit_right1 . y = = hit_right2 . y )
{
line_2_orient = horizontal ;
}
//Check if the right wall is vertical
else if ( hit_right1 . x = = hit_right2 . x )
{
line_2_orient = vertical ;
}
//Checking if the lines are parallel and not skew
if ( line_1_orient = = line_2_orient & & line_1_orient ! = skew )
{
if ( cl_navmesh_place_corner_state = = NAVMESH_PLACE_CORNER_CONFIRM )
{
print ( " Cannot place corner for parallel walls (there is no corner). \n " ) ;
cl_navmesh_place_corner_state = NAVMESH_PLACE_CORNER_PLACING ;
}
return ;
}
//Checking other special cases
else if ( line_1_orient = = horizontal & & line_2_orient = = vertical )
{
vert_loc . x = hit_right1 . x ;
vert_loc . y = hit_left1 . y ;
}
else if ( line_1_orient = = vertical & & line_2_orient = = horizontal )
{
vert_loc . x = hit_left1 . x ;
vert_loc . y = hit_right1 . y ;
}
else if ( line_1_orient = = vertical ) //line 2 is skew
{
vert_loc . x = hit_left1 . x ;
//Plugging in vert_loc.x into equation of line 2 to get vert_loc.y
//Slope of line 2
float m2_a = ( hit_right2 . y - hit_right1 . y ) / ( hit_right2 . x - hit_right1 . x ) ;
vert_loc . y = m2_a * ( vert_loc . x - hit_right1 . x ) + hit_right1 . y ;
}
else if ( line_2_orient = = vertical ) //line 1 is skew
{
vert_loc . x = hit_right1 . x ;
//P;ugging in vert_loc.x into equation of line 1 to get vert_loc.y
//Slope of line 1
float m1_a = ( hit_left2 . y - hit_left1 . y ) / ( hit_left2 . x - hit_left1 . x ) ;
vert_loc . y = m1_a * ( vert_loc . x - hit_left1 . x ) + hit_left1 . y ;
}
else //Both lines are skew or horizontal
{
//Slope of line 1
float m1 = ( hit_left2 . y - hit_left1 . y ) / ( hit_left2 . x - hit_left1 . x ) ;
//Slope of line 2
float m2 = ( hit_right2 . y - hit_right1 . y ) / ( hit_right2 . x - hit_right1 . x ) ;
//Checking if the walls are parallel
if ( m1 = = m2 )
{
if ( cl_navmesh_place_corner_state = = NAVMESH_PLACE_CORNER_CONFIRM )
{
print ( " Cannot place corner for skew parallel walls (there is no corner). \n " ) ;
cl_navmesh_place_corner_state = NAVMESH_PLACE_CORNER_PLACING ;
}
return ;
}
//Resolving the corner vertex location (this is derived from the solution of the two lines)
vert_loc . x = ( ( m1 * hit_left1 . x ) - ( m2 * hit_right1 . x ) + hit_right1 . y - hit_left1 . y ) / ( m1 - m2 ) ;
//Plugging in vert_loc.x to the equation of the first line to get vert_loc.y
vert_loc . y = m1 * ( vert_loc . x - hit_left1 . x ) + hit_left1 . y ;
}
//Placing the vert as the highest z-value
float highest_z = hit_left1 . z ;
if ( hit_left2 . z > highest_z )
highest_z = hit_left2 . z ;
if ( hit_right1 . z > highest_z )
highest_z = hit_right1 . z ;
if ( hit_right2 . z > highest_z )
highest_z = hit_right2 . z ;
vert_loc . z = highest_z ;
//Traceboxing down from that vert position to get actual height
tracebox ( vert_loc , VEC_HULL_MIN , VEC_HULL_MAX , vert_loc + ( [ 0 , 0 , - 1 ] * 2000 ) , 1 , self ) ;
vert_loc . z = vert_loc . z + ( - 2000 * trace_fraction ) ;
//================================================================================
//Render the resolved vertex pos
cl_navmesh_draw_vert ( vert_loc , [ 1 , 1 , 0 ] , 0.4 ) ;
// Draw a vertical line at the resolved vert pos:
cl_navmesh_draw_box ( vert_loc , [ 0.5 , 0.5 , 120 ] , [ 1 , 1 , 1 ] , 0.05 ) ;
if ( cl_navmesh_place_corner_state = = NAVMESH_PLACE_CORNER_CONFIRM )
{
cl_navmesh_verts [ cl_navmesh_vert_count ] . pos . x = vert_loc . x ;
cl_navmesh_verts [ cl_navmesh_vert_count ] . pos . y = vert_loc . y ;
cl_navmesh_verts [ cl_navmesh_vert_count ] . pos . z = vert_loc . z ;
cl_navmesh_vert_count + + ;
cl_navmesh_place_corner_state = 0 ;
}
}
//For using tracebox to place vertices
/*vector vorg = getviewprop(VF_ORIGIN);
vector vang = getviewprop ( VF_ANGLES ) ;
makevectors ( vang ) ;
//v_forward, v_right, v_up
getviewprop ( VF_ACTIVESEAT ) ;
//Tracebox out from there
//Zombie dimensions
tracebox ( vorg , [ - 8 , - 8 , - 32 ] , [ 8 , 8 , 30 ] , vorg + ( v_forward * 2000 ) , 1 , self ) ;
//Tracebox down from there
vector vpos1 = vorg + ( v_forward * 2000 ) * trace_fraction ;
cl_navmesh_draw_vert ( vpos1 , [ 0 , 0 , 1 ] , 0.4 ) ;
tracebox ( vpos1 , [ - 8 , - 8 , - 32 ] , [ 8 , 8 , 30 ] , vpos1 + ( [ 0 , 0 , - 1 ] * 2000 ) , 1 , self ) ;
vector vpos2 = vpos1 + ( ( [ 0 , 0 , - 1 ] * 2000 ) * trace_fraction ) ;
cl_navmesh_draw_vert ( vpos2 , [ 1 , 1 , 0 ] , 0.4 ) ; */
//Tracebox down from there
}
//Places a vertex at player position
void cl_navmesh_place_vert ( )
{
if ( cl_navmesh_vert_count > = NAV_MAX_VERTS )
{
print ( " Can't add vertex, max vert count has been reached. \n " ) ;
return ;
}
//Placing the vert at player origin
vector vert_pos = getentity ( player_localentnum , GE_ORIGIN ) ;
cl_navmesh_verts [ cl_navmesh_vert_count ] . pos . x = vert_pos . x ;
cl_navmesh_verts [ cl_navmesh_vert_count ] . pos . y = vert_pos . y ;
cl_navmesh_verts [ cl_navmesh_vert_count ] . pos . z = vert_pos . z ;
print ( " Vertex " ) ;
print ( ftos ( cl_navmesh_vert_count ) ) ;
print ( " created at " , vtos ( vert_pos ) , " \n " ) ;
cl_navmesh_vert_count + + ;
}
//Deletes all selected vertices
void cl_navmesh_delete_verts ( )
{
if ( selected_vert_count < = 0 )
{
print ( " No vertices selected. \n " ) ;
return ;
}
for ( float i = 0 ; i < selected_vert_count ; i + + )
{
float vert = selected_verts [ i ] ;
print ( " Deleting vert: " ) ;
print ( ftos ( vert ) ) ;
print ( " . \n " ) ;
//Check if vertex is in any polygons, if so delete that polygon
for ( float j = 0 ; j < cl_navmesh_poly_count ; j + + )
{
for ( float k = 0 ; k < cl_navmesh_polies [ j ] . vert_count ; k + + )
{
if ( cl_navmesh_polies [ j ] . verts [ k ] = = vert )
{
cl_navmesh_delete_poly_at_index ( j ) ;
j - = 1 ;
break ;
}
}
}
//Bringing all vertices down to not leave any holes in the array
//Moving down every index to the right of what we deselected to not leave any holes
for ( float j = vert ; j < cl_navmesh_vert_count - 1 ; j + + )
{
cl_navmesh_verts [ j ] . pos . x = cl_navmesh_verts [ j + 1 ] . pos . x ;
cl_navmesh_verts [ j ] . pos . y = cl_navmesh_verts [ j + 1 ] . pos . y ;
cl_navmesh_verts [ j ] . pos . z = cl_navmesh_verts [ j + 1 ] . pos . z ;
}
//Clearing the last one
cl_navmesh_verts [ cl_navmesh_vert_count - 1 ] . pos = [ 0 , 0 , 0 ] ;
cl_navmesh_vert_count - - ;
//Fixing references to verts in all polygons
for ( float j = 0 ; j < cl_navmesh_poly_count ; j + + )
{
for ( float k = 0 ; k < cl_navmesh_polies [ j ] . vert_count ; k + + )
{
if ( cl_navmesh_polies [ j ] . verts [ k ] > = vert )
cl_navmesh_polies [ j ] . verts [ k ] - - ;
}
}
//Fixing references of selected verts
for ( float j = i ; j < selected_vert_count ; j + + )
{
if ( selected_verts [ j ] > = vert )
selected_verts [ j ] - - ;
}
}
selected_vert_count = 0 ;
for ( float i = 0 ; i < NAV_MAX_SELECTED_VERTS ; i + + )
{
selected_verts [ i ] = - 1 ;
}
}
//=================================== Navmesh vertex selection functions ===================================
//Returns index of the vertex that is nearest to the player
float cl_navmesh_get_nearest_vert ( )
{
if ( cl_navmesh_vert_count < = 0 )
{
return - 1 ;
}
vector player_pos = getentity ( player_localentnum , GE_ORIGIN ) ;
float closest_dist = vlen ( player_pos - cl_navmesh_verts [ 0 ] . pos ) ;
float closest_index = 0 ;
float temp_dist ;
for ( float i = 1 ; i < cl_navmesh_vert_count ; i + + )
{
temp_dist = vlen ( player_pos - cl_navmesh_verts [ i ] . pos ) ;
if ( temp_dist < closest_dist )
{
closest_dist = temp_dist ;
closest_index = i ;
}
}
return closest_index ;
}
//Selects the nearest vertex
void cl_navmesh_select_vert ( )
{
if ( selected_vert_count > = NAV_MAX_SELECTED_VERTS )
{
print ( " Can't select another vertex, max vertices selected. \n " ) ;
return ;
}
float vert = cl_navmesh_get_nearest_vert ( ) ;
if ( vert = = - 1 )
{
print ( " No vertices to select " ) ;
return ;
}
if ( cl_navmesh_is_vert_selected ( vert ) )
{
print ( " Vert is already selected. \n " ) ;
return ;
}
print ( " Vertex " ) ;
print ( ftos ( vert ) ) ;
print ( " selected. \n " ) ;
selected_verts [ selected_vert_count + + ] = vert ;
}
//Deselects the nearest vertex
void cl_navmesh_deselect_vert ( )
{
float vert = cl_navmesh_get_nearest_vert ( ) ;
if ( vert = = - 1 )
{
print ( " No vertices to select. \n " ) ;
return ;
}
for ( float i = 0 ; i < selected_vert_count ; i + + )
{
if ( selected_verts [ i ] = = vert )
{
print ( " Vertex " ) ;
print ( ftos ( selected_verts [ i ] ) ) ;
print ( " deselected. \n " ) ;
// Clear selected vert
selected_verts [ i ] = - 1 ;
//Moving down every index to the right of what we deselected to not leave any holes
for ( float j = i ; j < selected_vert_count - 1 ; j + + )
{
selected_verts [ j ] = selected_verts [ j + 1 ] ;
}
selected_verts [ selected_vert_count - 1 ] = - 1 ;
selected_vert_count - - ;
return ;
}
}
print ( " Vertex is not selected. \n " ) ;
}
//Deselects all vertices
void cl_navmesh_deselect_all ( )
{
for ( float i = 0 ; i < NAV_MAX_SELECTED_VERTS ; i + + )
{
selected_verts [ i ] = - 1 ;
}
selected_vert_count = 0 ;
2023-08-01 10:20:35 +00:00
cl_navmesh_selected_traversal = - 1 ;
cl_navmesh_traversal_edit_mode = false ;
cl_navmesh_traversal_editor_cur_point = 0 ;
2023-02-05 10:07:35 +00:00
//print("All vertices deselected.\n");
}
//=============================================================================================================
// Returns the index of the currently selected polygon
// Otherwise, returns -1
int cl_navmesh_get_selected_poly ( )
{
//Check if this polygon already exists
float verts_selected [ 4 ] ;
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + )
{
verts_selected [ 0 ] = FALSE ;
verts_selected [ 1 ] = FALSE ;
verts_selected [ 2 ] = FALSE ;
verts_selected [ 3 ] = FALSE ;
//Check each of the poly's verts
for ( float j = 0 ; j < cl_navmesh_polies [ i ] . vert_count ; j + + )
{
//Check each of our selected verts to see if they are in this polygon
for ( float k = 0 ; k < selected_vert_count ; k + + )
{
if ( cl_navmesh_polies [ i ] . verts [ j ] = = selected_verts [ k ] )
{
verts_selected [ j ] = TRUE ;
}
}
//This poly vert was not selected
if ( verts_selected [ j ] = = FALSE )
{
break ;
}
}
if ( cl_navmesh_polies [ i ] . vert_count = = 3 )
{
if ( verts_selected [ 0 ] = = TRUE & & verts_selected [ 1 ] = = TRUE & & verts_selected [ 2 ] = = TRUE )
{
return i ;
}
}
else if ( cl_navmesh_polies [ i ] . vert_count = = 4 )
{
if ( verts_selected [ 0 ] = = TRUE & & verts_selected [ 1 ] = = TRUE & & verts_selected [ 2 ] = = TRUE & & verts_selected [ 3 ] = = TRUE )
{
return i ;
}
}
}
return - 1 ;
}
void cl_navmesh_make_poly ( )
{
if ( selected_vert_count < 3 )
{
print ( " Not enough selected vertices to make a polygon (need 3). \n " ) ;
return ;
}
if ( cl_navmesh_poly_count > = NAV_MAX_POLIES )
{
print ( " Max polygon count reached. \n " ) ;
return ;
}
if ( cl_navmesh_get_selected_poly ( ) ! = - 1 ) {
print ( " This polygon already exists. \n " ) ;
return ;
}
//Sorting the verts so the polygon is build consecutively (i.e. vert 0 -> vert 1 is an edge, 1->2, 2->3, and 3->0 are all edges)
//Furthermore, this sorting will ensure the verts are sorted in a counter clockwise order, so we also apply it to tris
//Printing all polygon vertices
print ( " Selected Verts: [ " ) ;
for ( float j = 0 ; j < selected_vert_count ; j + + )
{
print ( " ( " , ftos ( j ) , " , " , ftos ( selected_verts [ j ] ) , " , " , vtos ( cl_navmesh_verts [ selected_verts [ j ] ] . pos ) , " ) , " ) ;
}
print ( " ] \n \n " ) ;
//Calculating center of the polygon
local vector center = [ 0 , 0 , 0 ] ;
for ( float j = 0 ; j < selected_vert_count ; j + + )
{
center + = cl_navmesh_verts [ selected_verts [ j ] ] . pos ;
}
center / = selected_vert_count ;
print ( " Center of selected polygon: " , vtos ( center ) , " . \n \n " ) ;
float vert_angle [ NAV_MAX_SELECTED_VERTS ] ;
//For suppressing uninitialised vert_angle compiler warning
vert_angle [ 0 ] = 0 ;
//Calculating vertex angles:
for ( float j = 0 ; j < selected_vert_count ; j + + ) {
//Calculating angle of vert
vert_angle [ j ] = atan2 ( cl_navmesh_verts [ selected_verts [ j ] ] . pos . y - center . y , cl_navmesh_verts [ selected_verts [ j ] ] . pos . x - center . x ) ;
}
//Sorting from least to greatest
for ( float j = 0 ; j < selected_vert_count ; j + + ) {
//Finding lowest value from j to selected_vert_count
float lowest_value = vert_angle [ j ] ;
float lowest_index = j ;
for ( float k = j ; k < selected_vert_count ; k + + ) {
if ( vert_angle [ k ] < lowest_value ) {
lowest_value = vert_angle [ k ] ;
lowest_index = k ;
}
}
//Swapping the lowest value with index j
float temp_angle = vert_angle [ j ] ;
vert_angle [ j ] = vert_angle [ lowest_index ] ;
vert_angle [ lowest_index ] = temp_angle ;
float temp_vert = selected_verts [ j ] ;
selected_verts [ j ] = selected_verts [ lowest_index ] ;
selected_verts [ lowest_index ] = temp_vert ;
}
//Vert angles
print ( " Sorted Verts: [ " ) ;
for ( float j = 0 ; j < selected_vert_count ; j + + ) {
print ( " ( " , ftos ( j ) , " , " , ftos ( selected_verts [ j ] ) , " , " , vtos ( cl_navmesh_verts [ selected_verts [ j ] ] . pos ) , " ) , " ) ;
}
print ( " ] \n \n " ) ;
print ( " Sorted Vert angles: [ " ) ;
for ( float j = 0 ; j < selected_vert_count ; j + + ) {
print ( " , " , ftos ( vert_angle [ j ] ) ) ;
}
print ( " ] \n \n " ) ;
// Verify that the polygon is concave.
for ( int i = 0 ; i < selected_vert_count ; i + + ) {
vector prev_vert_pos = cl_navmesh_verts [ selected_verts [ ( i + selected_vert_count - 1 ) % selected_vert_count ] ] . pos ;
vector vert_pos = cl_navmesh_verts [ selected_verts [ i ] ] . pos ;
vector next_vert_pos = cl_navmesh_verts [ selected_verts [ ( i + 1 ) % selected_vert_count ] ] . pos ;
// For the polygon to be concave, next_vert _must_ be to the left of the line prev_vert -> vert
if ( pathfind_point_is_to_left ( prev_vert_pos , vert_pos , next_vert_pos ) < = 0 ) {
print ( " Specified polygon is not concave. \n " ) ;
print ( " \t prev vert: " , ftos ( selected_verts [ ( i + selected_vert_count - 1 ) % selected_vert_count ] ) , " \n " ) ;
print ( " \t vert: " , ftos ( selected_verts [ ( i ) % selected_vert_count ] ) , " \n " ) ;
print ( " \t next vert: " , ftos ( selected_verts [ ( i + 1 ) % selected_vert_count ] ) , " \n " ) ;
return ;
}
}
for ( float i = 0 ; i < selected_vert_count ; i + + )
{
cl_navmesh_polies [ cl_navmesh_poly_count ] . verts [ i ] = selected_verts [ i ] ;
}
cl_navmesh_polies [ cl_navmesh_poly_count ] . vert_count = selected_vert_count ;
cl_navmesh_poly_count + + ;
}
void cl_navmesh_delete_poly_at_index ( float poly_index )
{
//Starting at this polygon, move every polygon after in the array down one (leave no holes in the array)
//Moving down every index to the right of what we deselected to not leave any holes
for ( float i = poly_index ; i < cl_navmesh_poly_count - 1 ; i + + )
{
// Copy down the verts
cl_navmesh_polies [ i ] . vert_count = cl_navmesh_polies [ i + 1 ] . vert_count ;
for ( float j = 0 ; j < cl_navmesh_polies [ i ] . verts . length ; j + + )
{
cl_navmesh_polies [ i ] . verts [ j ] = cl_navmesh_polies [ i + 1 ] . verts [ j ] ;
}
// Copy down other fields:
cl_navmesh_polies [ i ] . doortarget = cl_navmesh_polies [ i + 1 ] . doortarget ;
}
//Clear the last polygon in the list
cl_navmesh_polies [ cl_navmesh_poly_count - 1 ] . vert_count = 0 ;
for ( float j = 0 ; j < cl_navmesh_polies [ cl_navmesh_poly_count - 1 ] . verts . length ; j + + )
{
cl_navmesh_polies [ cl_navmesh_poly_count - 1 ] . verts [ j ] = - 1 ;
}
cl_navmesh_polies [ cl_navmesh_poly_count - 1 ] . doortarget = " " ;
cl_navmesh_poly_count - - ;
}
void cl_navmesh_delete_poly ( )
{
float verts_selected [ 4 ] ;
float poly_index = - 1 ;
//Find index of the polygon whose vertices are all selected
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + )
{
verts_selected [ 0 ] = FALSE ;
verts_selected [ 1 ] = FALSE ;
verts_selected [ 2 ] = FALSE ;
verts_selected [ 3 ] = FALSE ;
//Check each of the poly's verts
for ( float j = 0 ; j < cl_navmesh_polies [ i ] . vert_count ; j + + )
{
//Check each of our selected verts to see if they are in this polygon
for ( float k = 0 ; k < selected_vert_count ; k + + )
{
if ( cl_navmesh_polies [ i ] . verts [ j ] = = selected_verts [ k ] )
{
verts_selected [ j ] = TRUE ;
}
}
//This poly vert was not selected
if ( verts_selected [ j ] = = FALSE )
{
break ;
}
}
if ( cl_navmesh_polies [ i ] . vert_count = = 3 )
{
if ( verts_selected [ 0 ] = = TRUE & & verts_selected [ 1 ] = = TRUE & & verts_selected [ 2 ] = = TRUE )
{
poly_index = i ;
break ;
}
}
else if ( cl_navmesh_polies [ i ] . vert_count = = 4 )
{
if ( verts_selected [ 0 ] = = TRUE & & verts_selected [ 1 ] = = TRUE & & verts_selected [ 2 ] = = TRUE & & verts_selected [ 3 ] = = TRUE )
{
poly_index = i ;
break ;
}
}
}
if ( poly_index = = - 1 )
{
print ( " A polygon is not selected. \n " ) ;
return ;
}
cl_navmesh_delete_poly_at_index ( poly_index ) ;
print ( " Deleted polygon. \n " ) ;
}
//Treats 1st and 2nd selected verts as line 1, 3rd and 4th verts as line 2, places a new vertex at the intersection of these lines
void cl_navmesh_resolve_corner ( )
{
if ( selected_vert_count < 3 )
{
print ( " You need to select at least 3 vertices to resolve a corner. \n " ) ;
return ;
}
//Line 1 extends from l1a in direction l1_dir
vector l1a ;
l1a = cl_navmesh_verts [ selected_verts [ 0 ] ] . pos ;
vector l1_dir = normalize ( cl_navmesh_verts [ selected_verts [ 1 ] ] . pos - l1a ) ;
//The third point
vector p2 = cl_navmesh_verts [ selected_verts [ 2 ] ] . pos ;
//Considering only l1_dir in the 2d plane of z=0
vector vert_loc ;
vert_loc . z = l1a . z ;
//Is the line vertical?
if ( l1_dir . x = = 0 ) //Is the line vertical?
{
vert_loc . x = l1a . x ;
vert_loc . y = p2 . y ;
}
else if ( l1_dir . y = = 0 ) //Is the line horizontal?
{
vert_loc . x = p2 . x ;
vert_loc . y = l1a . y ;
}
else
{
//Getting slope of line 1
float m = l1_dir . y / l1_dir . x ;
float x1 = p2 . x - l1a . x ;
float y1 = p2 . y - l1a . y ;
vert_loc . x = ( x1 + m * y1 ) / ( m * m + 1 ) ;
vert_loc . y = ( - 1 / m ) * ( vert_loc . x - x1 ) + y1 ;
}
vert_loc . x + = l1a . x ;
vert_loc . y + = l1a . y ;
//Place a new vert at the location
cl_navmesh_verts [ cl_navmesh_vert_count ] . pos . x = vert_loc . x ;
cl_navmesh_verts [ cl_navmesh_vert_count ] . pos . y = vert_loc . y ;
cl_navmesh_verts [ cl_navmesh_vert_count ] . pos . z = vert_loc . z ;
print ( " Resolved corner with vertex " ) ;
print ( ftos ( cl_navmesh_vert_count ) ) ;
print ( " at ( " ) ;
print ( ftos ( vert_loc . x ) ) ;
print ( " , " ) ;
print ( ftos ( vert_loc . y ) ) ;
print ( " , " ) ;
print ( ftos ( vert_loc . z ) ) ;
print ( " ). \n " ) ;
cl_navmesh_vert_count + + ;
}
void cl_navmesh_place_corner ( )
{
cl_navmesh_place_corner_state = NAVMESH_PLACE_CORNER_PLACING ;
}
void cl_navmesh_cancel_corner ( )
{
cl_navmesh_place_corner_state = 0 ;
}
void cl_navmesh_confirm_corner ( )
{
if ( cl_navmesh_place_corner_state = = NAVMESH_PLACE_CORNER_PLACING )
cl_navmesh_place_corner_state = NAVMESH_PLACE_CORNER_CONFIRM ;
}
void cl_navmesh_clear_connected_polies ( )
{
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + )
{
cl_navmesh_polies [ i ] . connected_polies_count = 0 ;
for ( float j = 0 ; j < 4 ; j + + )
{
cl_navmesh_polies [ i ] . connected_polies [ j ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_left_vert [ j ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_right_vert [ j ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ j ] = - 1 ;
}
2023-08-01 10:20:35 +00:00
cl_navmesh_polies [ i ] . connected_traversals_count = 0 ;
for ( float j = 0 ; j < NAV_MAX_POLY_TRAVERSALS ; j + + ) {
cl_navmesh_polies [ i ] . connected_traversals [ j ] = - 1 ;
}
2023-02-05 10:07:35 +00:00
}
}
void cl_navmesh_calc_connected_polies ( ) {
cl_navmesh_clear_connected_polies ( ) ;
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + ) {
for ( float j = i + 1 ; j < cl_navmesh_poly_count ; j + + ) {
//Check if poly j shares an edge with poly i
//Edge case: where two opposite corners (two corners not on the same edge) of a quad are shared with another poly
//but this edge case is a result of terrible topology, so I will not consider it
//Check if poly j shares at least 2 vertices with poly i
//I'm not sure what sharing more than 2 verts means... but it's a result of bad topology, so I will not consider it
// if two quads share 4: they are the same -> bad topology
// if two quads share 3: one of them is convex, one is concave -> bad topology
// if a quad shares 3 with a tri: the tri is within the quad -> bad topology
// if two tris share 3: they are the same -> bad topology
float shared_vert_ids [ 2 ] ;
shared_vert_ids [ 0 ] = - 1 ;
shared_vert_ids [ 1 ] = - 1 ;
float poly_i_shared_verts_index [ 2 ] ; // The index of the vert in the ith polygon's vertex list
float poly_j_shared_verts_index [ 2 ] ; // The index of the vert in the ith polygon's vertex list
poly_i_shared_verts_index [ 0 ] = - 1 ;
poly_i_shared_verts_index [ 1 ] = - 1 ;
poly_j_shared_verts_index [ 0 ] = - 1 ;
poly_j_shared_verts_index [ 1 ] = - 1 ;
float verts_found = 0 ;
for ( float k = 0 ; k < cl_navmesh_polies [ i ] . vert_count ; k + + ) {
for ( float l = 0 ; l < cl_navmesh_polies [ j ] . vert_count ; l + + ) {
if ( cl_navmesh_polies [ i ] . verts [ k ] = = cl_navmesh_polies [ j ] . verts [ l ] ) {
//If we have found more verts than should be allowed, count them for error print
if ( verts_found > = 2 ) {
verts_found + + ;
continue ;
}
shared_vert_ids [ verts_found ] = cl_navmesh_polies [ i ] . verts [ k ] ;
poly_i_shared_verts_index [ verts_found ] = k ;
poly_j_shared_verts_index [ verts_found ] = l ;
verts_found + + ;
}
}
}
//Print a warning if more than 2, should be impossible
if ( verts_found > 2 ) {
print ( " Warning: polygon " ) ;
print ( ftos ( i ) ) ;
print ( " shares " ) ;
print ( ftos ( verts_found ) ) ;
print ( " vertices with polygon " ) ;
print ( ftos ( j ) ) ;
print ( " . There is an issue somewhere. \n " ) ;
}
else if ( verts_found = = 2 ) {
//Setting the polygons as connected to each other
if ( cl_navmesh_polies [ i ] . connected_polies_count > = 4 ) {
print ( " Warning: polygon " ) ;
print ( ftos ( i ) ) ;
print ( " shares an edge with more than 4 other polygons. \n " ) ;
break ;
}
if ( cl_navmesh_polies [ j ] . connected_polies_count > = 4 ) {
print ( " Warning: polygon " ) ;
print ( ftos ( j ) ) ;
print ( " shares an edge with more than 4 other polygons. \n " ) ;
break ;
}
cl_navmesh_polies [ i ] . connected_polies [ cl_navmesh_polies [ i ] . connected_polies_count ] = j ;
cl_navmesh_polies [ j ] . connected_polies [ cl_navmesh_polies [ j ] . connected_polies_count ] = i ;
//=======================================================================================
//Calculating shared edge left / right vertices
//Calculating from the perspective of moving from poly[i] to poly[j]
//Because polygon's vertices are stored in a ccw winding order:
//connected polygons must share two consecutive verts.
//the CCW winding of the triangle dictates that the next shared vertex is to the left of, and in the index after the first shared vertex
//i.e. of the two consecutive verts shard, the former is to the right, the latter is to the left
//if first one is a quad: shared edge must be, where the first index is right and second index is left
//3-0, 0-1, 1-2, 2-3
//if first one is a tri: shared edge must be, where the first index is right and second index is left
//2-0, 0-1, 1-2
//Two distinct cases:
//The index of second is one greater than the index of the first
//OR
//The index of the first is 0, and index of second is 2 or 3
// Sort the vertices to be from left to right when crossing from polygon i to j
//
// let a,b = poly_i_shared_verts_index
//
// 0 ---------- 3
// | |
// | |
// | |
// | |
// 1 ---------- 2
//
// Possible Values:
//
// b = 0 b = 1 b = 2 b = 3
// a = 0 X B F F
// a = 1 F X B X
// a = 2 B F X B
// a = 3 B X F X
//
// F -- Forward (a is left vert, b is right vert)
// B -- Backward (a is right vert, b is left vert)
// X -- Can't happen
// if(a == (b + 1) % poly_i_vert_count)
if ( poly_i_shared_verts_index [ 0 ] = = ( poly_i_shared_verts_index [ 1 ] + 1 ) % cl_navmesh_polies [ i ] . vert_count ) {
// poly_i_shared_verts_index[0] denotes the left vertex when crossing from i to j
// poly_i_shared_verts_index[1] denotes the right vertex when crossing from i to j
// Do nothing
}
else {
// Flip the vert lists so the above is true.
float temp ;
temp = shared_vert_ids [ 0 ] ;
shared_vert_ids [ 0 ] = shared_vert_ids [ 1 ] ;
shared_vert_ids [ 1 ] = temp ;
temp = poly_i_shared_verts_index [ 0 ] ;
poly_i_shared_verts_index [ 0 ] = poly_i_shared_verts_index [ 1 ] ;
poly_i_shared_verts_index [ 1 ] = temp ;
temp = poly_j_shared_verts_index [ 0 ] ;
poly_j_shared_verts_index [ 0 ] = poly_j_shared_verts_index [ 1 ] ;
poly_j_shared_verts_index [ 1 ] = temp ;
}
//Assigning the link's left/right edges
cl_navmesh_polies [ i ] . connected_polies_left_vert [ cl_navmesh_polies [ i ] . connected_polies_count ] = shared_vert_ids [ 0 ] ;
cl_navmesh_polies [ i ] . connected_polies_right_vert [ cl_navmesh_polies [ i ] . connected_polies_count ] = shared_vert_ids [ 1 ] ;
//From j to i, left vert is on the right, and right vert is on the left
cl_navmesh_polies [ j ] . connected_polies_left_vert [ cl_navmesh_polies [ j ] . connected_polies_count ] = shared_vert_ids [ 1 ] ;
cl_navmesh_polies [ j ] . connected_polies_right_vert [ cl_navmesh_polies [ j ] . connected_polies_count ] = shared_vert_ids [ 0 ] ;
// When crossing from polygon i to polygon j, the right-hand vertex index is the edge index for polygon i
float poly_i_edge_index = poly_i_shared_verts_index [ 1 ] ;
// When crossing from polygon j to polygon i, the left-hand vertex index is the edge index for polygon j
float poly_j_edge_index = poly_j_shared_verts_index [ 0 ] ;
// For polygon i, record which of polygon's j edges we are crossing to get into polygon j
cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ cl_navmesh_polies [ i ] . connected_polies_count ] = poly_j_edge_index ;
// For polygon j, record which of polygon's i edges we are crossing to get into polygon i
cl_navmesh_polies [ j ] . connected_polies_neighbor_edge_index [ cl_navmesh_polies [ j ] . connected_polies_count ] = poly_i_edge_index ;
cl_navmesh_polies [ i ] . connected_polies_count + + ;
cl_navmesh_polies [ j ] . connected_polies_count + + ;
//=======================================================================================
2023-08-02 08:27:40 +00:00
// print("Poly ");
// print(ftos(i));
// print(" is connected to ");
// print(ftos(j));
// print(" and poly ");
// print(ftos(j));
// print(" is connected to ");
// print(ftos(i));
// print(".\n");
2023-02-05 10:07:35 +00:00
}
}
}
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < cl_navmesh_traversal_count ; i + + ) {
print ( " Checking traversal for connected polies \n " ) ;
makevectors ( [ 0 , cl_navmesh_traversals [ i ] . angle , 0 ] ) ;
// Get start_pos and end_pos in world-space
vector start_pos = cl_navmesh_traversals [ i ] . start_pos ;
vector end_pos = start_pos + ( v_right * cl_navmesh_traversals [ i ] . end_pos . x ) + ( v_forward * cl_navmesh_traversals [ i ] . end_pos . y ) + ( v_up * cl_navmesh_traversals [ i ] . end_pos . z ) ;
float start_pos_poly = - 1 ;
float end_pos_poly = - 1 ;
for ( float j = 0 ; j < cl_navmesh_poly_count ; j + + ) {
if ( start_pos_poly = = - 1 & & cl_navmesh_is_inside_poly ( start_pos , j ) ) {
start_pos_poly = j ;
}
if ( end_pos_poly = = - 1 & & cl_navmesh_is_inside_poly ( end_pos , j ) ) {
end_pos_poly = j ;
}
if ( start_pos_poly ! = - 1 & & end_pos_poly ! = - 1 ) {
break ;
}
}
print ( " Traversal " ) ;
print ( ftos ( i ) ) ;
print ( " starts in poly " ) ;
print ( ftos ( start_pos_poly ) ) ;
print ( " and ends in poly " ) ;
print ( ftos ( end_pos_poly ) ) ;
print ( " \n " ) ;
if ( start_pos_poly = = - 1 ) {
print ( " Warning: Traversal " ) ;
print ( ftos ( i ) ) ;
print ( " does not start inside of a navmesh polygon. This traversal will not be used. \n " ) ;
continue ;
}
else if ( end_pos_poly = = - 1 ) {
print ( " Warning: Traversal " ) ;
print ( ftos ( i ) ) ;
print ( " does not end inside of a navmesh polygon. This traversal will not be used. \n " ) ;
continue ;
}
else if ( start_pos_poly = = end_pos_poly ) {
print ( " Warning: Traversal " ) ;
print ( ftos ( i ) ) ;
print ( " starts and ends inside of the same navmesh polygon. This traversal will not be used. \n " ) ;
continue ;
}
if ( cl_navmesh_polies [ start_pos_poly ] . connected_traversals_count > = NAV_MAX_POLY_TRAVERSALS ) {
print ( " Warning: Traversal " ) ;
print ( ftos ( i ) ) ;
print ( " starts inside of a polygon that is already connected to the maximum number of traversals allowed ( " ) ;
print ( ftos ( NAV_MAX_POLY_TRAVERSALS ) ) ;
print ( " ). This traversal will not be used. \n " ) ;
continue ;
}
// Add to polygon's list of connected traversals
cl_navmesh_polies [ start_pos_poly ] . connected_traversals [ cl_navmesh_polies [ start_pos_poly ] . connected_traversals_count ] = i ;
cl_navmesh_polies [ start_pos_poly ] . connected_traversals_count + = 1 ;
cl_navmesh_traversals [ i ] . end_poly = end_pos_poly ;
// TODO - Add traversal to end polygon list of traversals?
}
2023-02-05 10:07:35 +00:00
print ( " Connected polygons calculated. \n " ) ;
}
//We calculate this outside of polygon creation / editing because the polygon centers are not important to creation, only to actually using in pathfinding
void cl_navmesh_calc_polies_centers ( )
{
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + )
{
local vector cen = [ 0 , 0 , 0 ] ;
for ( float j = 0 ; j < cl_navmesh_polies [ i ] . vert_count ; j + + )
{
cen + = cl_navmesh_verts [ cl_navmesh_polies [ i ] . verts [ j ] ] . pos ;
}
cen / = cl_navmesh_polies [ i ] . vert_count ;
cl_navmesh_polies [ i ] . center . x = cen . x ;
cl_navmesh_polies [ i ] . center . y = cen . y ;
cl_navmesh_polies [ i ] . center . z = cen . z ;
}
print ( " Centers calculated. \n " ) ;
}
//Navmesh file specification for v0.0.0:
//Vertex count
//For each vert:
//Vertex position vector
//Polygon count
//For each polygon:
//vertex count (must be 3 or 4)
//vertex 0 index
//vertex 1 index
//vertex 2 index
//vertex 3 index (will be -1 for a tri)
//center (vector)
//link count
//link 0 index (will be set or -1 for a tri)
//link 1 index (will be set or -1 for a tri)
//link 2 index (will be set or -1 for a tri)
//link 3 index (will be set or -1 for a tri)
//link 0 left vert index (will be set or -1 for a tri)
//link 1 left vert index (will be set or -1 for a tri)
//link 2 left vert index (will be set or -1 for a tri)
//link 3 left vert index (will be set or -1 for a tri)
//link 0 right vert index (will be set or -1 for a tri)
//link 1 right vert index (will be set or -1 for a tri)
//link 2 right vert index (will be set or -1 for a tri)
//link 3 right vert index (will be set or -1 for a tri)
//Saves the current cl_navmesh to a file
2023-08-01 10:20:35 +00:00
void cl_navmesh_editor_save_navmesh ( ) {
2023-02-05 10:07:35 +00:00
int v_major = 0 ;
2023-08-02 08:27:40 +00:00
int v_minor = 1 ;
2023-02-05 10:07:35 +00:00
int v_patch = 0 ;
float file ;
string h ;
h = strcat ( mappath , " .nav " ) ;
file = fopen ( h , FILE_WRITE ) ;
if ( file = = - 1 ) {
print ( " Error: unable to write to file \" " , h , " \" . \n " ) ;
return ;
}
// Write version number:
fputs ( file , itos ( v_major ) , " . " , itos ( v_minor ) , " . " , itos ( v_patch ) , " \n " ) ;
//Calculating all links and polygon centers
//===========================================
cl_navmesh_calc_polies_centers ( ) ;
cl_navmesh_calc_connected_polies ( ) ;
//===========================================
//Write vertex count
fputs ( file , ftos ( cl_navmesh_vert_count ) , " \n " ) ;
//Write all vertex positions
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < cl_navmesh_vert_count ; i + + ) {
2023-02-05 10:07:35 +00:00
fputs ( file , vtos ( cl_navmesh_verts [ i ] . pos ) , " \n " ) ;
}
//Write polygon count
fputs ( file , ftos ( cl_navmesh_poly_count ) , " \n " ) ;
//Write all polygon data to file
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + ) {
2023-02-05 10:07:35 +00:00
// Write vert count
fputs ( file , ftos ( cl_navmesh_polies [ i ] . vert_count ) , " \n " ) ;
// Write vertices
fputs ( file , ftos ( cl_navmesh_polies [ i ] . verts [ 0 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . verts [ 1 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . verts [ 2 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . verts [ 3 ] ) , " \n " ) ;
fputs ( file , vtos ( cl_navmesh_polies [ i ] . center ) , " \n " ) ;
// Write link count
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_count ) , " \n " ) ;
// Write links
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies [ 0 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies [ 1 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies [ 2 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies [ 3 ] ) , " \n " ) ;
// Write left vertices of the links
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_left_vert [ 0 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_left_vert [ 1 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_left_vert [ 2 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_left_vert [ 3 ] ) , " \n " ) ;
// Write right vertices of the links
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_right_vert [ 0 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_right_vert [ 1 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_right_vert [ 2 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_right_vert [ 3 ] ) , " \n " ) ;
// Write the neighbor edge indices
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ 0 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ 1 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ 2 ] ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ 3 ] ) , " \n " ) ;
2023-08-01 10:20:35 +00:00
// Write connected traversals
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_traversals_count ) , " \n " ) ;
for ( float j = 0 ; j < cl_navmesh_polies [ i ] . connected_traversals_count ; j + + ) {
fputs ( file , ftos ( cl_navmesh_polies [ i ] . connected_traversals [ j ] ) , " \n " ) ;
}
2023-02-05 10:07:35 +00:00
// Write polygon doortarget
fputs ( file , cl_navmesh_polies [ i ] . doortarget , " \n " ) ;
if ( cl_navmesh_polies [ i ] . doortarget ! = " " ) {
print ( " CLSAVENAVMESH - Polygon at index " , ftos ( i ) , " has doortarget: \" " , cl_navmesh_polies [ i ] . doortarget , " \" " ) ;
}
}
2023-08-01 10:20:35 +00:00
// -----------------------------------------------------------------------
// Write Traversals
// -----------------------------------------------------------------------
print ( " Writing " ) ;
print ( ( ftos ( cl_navmesh_traversal_count ) ) ) ;
print ( " traversals. \n " ) ;
fputs ( file , ftos ( cl_navmesh_traversal_count ) , " \n " ) ;
for ( float i = 0 ; i < cl_navmesh_traversal_count ; i + + ) {
fputs ( file , vtos ( cl_navmesh_traversals [ i ] . start_pos ) , " \n " ) ;
fputs ( file , vtos ( cl_navmesh_traversals [ i ] . midpoint_pos ) , " \n " ) ;
fputs ( file , vtos ( cl_navmesh_traversals [ i ] . end_pos ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_traversals [ i ] . angle ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_traversals [ i ] . use_midpoint ) , " \n " ) ;
fputs ( file , ftos ( cl_navmesh_traversals [ i ] . end_poly ) , " \n " ) ;
}
// -----------------------------------------------------------------------
2023-02-05 10:07:35 +00:00
fclose ( file ) ;
print ( " Navmesh saved to file \" " , h , " \" . \n " ) ;
}
//Overwrites current navmesh with an empty navmesh
void cl_navmesh_editor_clear_navmesh ( )
{
2023-08-01 10:20:35 +00:00
cl_navmesh_deselect_all ( ) ;
2023-02-05 10:07:35 +00:00
cl_navmesh_vert_count = 0 ;
for ( float i = 0 ; i < NAV_MAX_VERTS ; i + + )
{
cl_navmesh_verts [ i ] . pos . x = 0 ;
cl_navmesh_verts [ i ] . pos . y = 0 ;
cl_navmesh_verts [ i ] . pos . z = 0 ;
}
cl_navmesh_poly_count = 0 ;
for ( float i = 0 ; i < NAV_MAX_POLIES ; i + + )
{
cl_navmesh_polies [ i ] . vert_count = 0 ;
cl_navmesh_polies [ i ] . verts [ 0 ] = - 1 ;
cl_navmesh_polies [ i ] . verts [ 1 ] = - 1 ;
cl_navmesh_polies [ i ] . verts [ 2 ] = - 1 ;
cl_navmesh_polies [ i ] . verts [ 3 ] = - 1 ;
cl_navmesh_polies [ i ] . center . x = 0 ;
cl_navmesh_polies [ i ] . center . y = 0 ;
cl_navmesh_polies [ i ] . center . z = 0 ;
cl_navmesh_polies [ i ] . connected_polies_count = 0 ;
cl_navmesh_polies [ i ] . connected_polies [ 0 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies [ 1 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies [ 2 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies [ 3 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_left_vert [ 0 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_left_vert [ 1 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_left_vert [ 2 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_left_vert [ 3 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_right_vert [ 0 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_right_vert [ 1 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_right_vert [ 2 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_right_vert [ 3 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ 0 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ 1 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ 2 ] = - 1 ;
cl_navmesh_polies [ i ] . connected_polies_neighbor_edge_index [ 3 ] = - 1 ;
2023-08-01 10:20:35 +00:00
for ( float j = 0 ; j < NAV_MAX_POLY_TRAVERSALS ; j + + ) {
cl_navmesh_polies [ i ] . connected_traversals [ j ] = - 1 ;
}
cl_navmesh_polies [ i ] . connected_traversals_count = 0 ;
2023-02-05 10:07:35 +00:00
cl_navmesh_polies [ i ] . doortarget = " " ;
}
2023-08-01 10:20:35 +00:00
cl_navmesh_traversal_count = 0 ;
for ( float i = 0 ; i < NAV_MAX_TRAVERSALS ; i + + ) {
cl_navmesh_traversals [ i ] . start_pos . x = 0 ;
cl_navmesh_traversals [ i ] . start_pos . y = 0 ;
cl_navmesh_traversals [ i ] . start_pos . z = 0 ;
cl_navmesh_traversals [ i ] . midpoint_pos . x = 0 ;
cl_navmesh_traversals [ i ] . midpoint_pos . y = 0 ;
cl_navmesh_traversals [ i ] . midpoint_pos . z = 0 ;
cl_navmesh_traversals [ i ] . end_pos . x = 0 ;
cl_navmesh_traversals [ i ] . end_pos . y = 0 ;
cl_navmesh_traversals [ i ] . end_pos . z = 0 ;
cl_navmesh_traversals [ i ] . angle = 0 ;
cl_navmesh_traversals [ i ] . use_midpoint = false ;
cl_navmesh_traversals [ i ] . end_poly = - 1 ;
}
2023-02-05 10:07:35 +00:00
}
//Overwrites currently loaded navmesh with navmesh stored in fail
2023-08-01 10:20:35 +00:00
void cl_navmesh_editor_load_navmesh ( ) {
2023-02-05 10:07:35 +00:00
string filepath ;
float file ;
filepath = strcat ( mappath , " .nav " ) ;
file = fopen ( filepath , FILE_READ ) ;
if ( file = = - 1 ) {
print ( " Error: file \" " , filepath , " \" not found. \n " ) ;
return ;
}
2023-08-06 05:53:44 +00:00
// First line contains navmesh file semver
2023-02-05 10:07:35 +00:00
string nav_file_version = fgets ( file ) ;
print ( " Loading v " , nav_file_version , " navmesh file \" " , filepath , " \" ... \n " ) ;
// TODO - Add backwards compatibility for older navmesh file versions.
// Next line contains vertex count
2023-08-06 05:53:44 +00:00
float vert_count = stof ( fgets ( file ) ) ;
2023-02-05 10:07:35 +00:00
if ( vert_count > NAV_MAX_VERTS ) {
2023-08-06 05:53:44 +00:00
print ( " Error: navmesh file \" " , filepath , " \" has an invalid vert count. ( " , vert_count , " > " , ftos ( NAV_MAX_VERTS ) , " ). \n " ) ;
2023-02-05 10:07:35 +00:00
fclose ( file ) ;
return ;
}
//It appears to be valid, so clear the current navmesh before continuing.
cl_navmesh_editor_clear_navmesh ( ) ;
cl_navmesh_vert_count = vert_count ;
2023-08-06 05:53:44 +00:00
print ( " Vert count: " , vert_count , " \n " ) ;
2023-02-05 10:07:35 +00:00
//Reading all of the vertex positions
for ( float i = 0 ; i < cl_navmesh_vert_count ; i + + ) {
2023-08-06 05:53:44 +00:00
cl_navmesh_verts [ i ] . pos = stov ( fgets ( file ) ) ;
2023-02-05 10:07:35 +00:00
}
//Next line contains the number of polygons
cl_navmesh_poly_count = stof ( fgets ( file ) ) ;
//The next lines are each polygon
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + ) {
//Getting vertex count
cl_navmesh_polies [ i ] . vert_count = stof ( fgets ( file ) ) ;
//Getting vertices
cl_navmesh_polies [ i ] . verts [ 0 ] = stof ( fgets ( file ) ) ;
cl_navmesh_polies [ i ] . verts [ 1 ] = stof ( fgets ( file ) ) ;
cl_navmesh_polies [ i ] . verts [ 2 ] = stof ( fgets ( file ) ) ;
cl_navmesh_polies [ i ] . verts [ 3 ] = stof ( fgets ( file ) ) ;
//Don't care about polygon center
fgets ( file ) ;
//Don't care about link count
fgets ( file ) ;
//Don't care about links for now FIXME HANDLE DIRECTIONAL LINKS
fgets ( file ) ; //0
fgets ( file ) ; //1
fgets ( file ) ; //2
fgets ( file ) ; //3
//Don't care about link's left vertices
fgets ( file ) ; //0
fgets ( file ) ; //1
fgets ( file ) ; //2
fgets ( file ) ; //3
//Don't care about link's right vertices
fgets ( file ) ; //0
fgets ( file ) ; //1
fgets ( file ) ; //2
fgets ( file ) ; //3
// Don't care about neighbor entrance edge indices
fgets ( file ) ; //0
fgets ( file ) ; //1
fgets ( file ) ; //2
fgets ( file ) ; //3
2023-08-02 08:27:40 +00:00
// If navmesh file version 0.0.0, no traversals
// FIXME - Need a better way to do this..
2023-08-06 05:53:44 +00:00
if ( nav_file_version ! = " 0.0.0 " ) {
2023-08-02 08:27:40 +00:00
// Don't care about connected traversals
float n_traversals = stof ( fgets ( file ) ) ;
for ( float j = 0 ; j < n_traversals ; j + + ) {
fgets ( file ) ; // discard the j-th traversal index
}
2023-08-01 10:20:35 +00:00
}
2023-02-05 10:07:35 +00:00
// Load doortarget field
cl_navmesh_polies [ i ] . doortarget = fgets ( file ) ;
if ( cl_navmesh_polies [ i ] . doortarget ! = " " ) {
2023-08-12 06:04:06 +00:00
print ( " CLLOADNAVMESH - Polygon at index " , ftos ( i ) , " has doortarget: \" " , cl_navmesh_polies [ i ] . doortarget , " \" \n " ) ;
2023-02-05 10:07:35 +00:00
}
// cl_navmesh_polies[i].doortarget = ""; // Temp fix to load old files
2023-08-06 05:53:44 +00:00
// If v0.0.0, discard entrance edge (no longer used)
if ( nav_file_version = = " 0.0.0 " ) {
fgets ( file ) ;
}
2023-02-05 10:07:35 +00:00
}
2023-08-01 10:20:35 +00:00
// -----------------------------------------------------------------------
// Load Traversals
// -----------------------------------------------------------------------
2023-08-02 08:27:40 +00:00
// If navmesh file version 0.0.0, no traversals
2023-08-06 05:53:44 +00:00
if ( nav_file_version ! = " 0.0.0 " ) {
2023-08-02 08:27:40 +00:00
// Next line contains the number of traverals
cl_navmesh_traversal_count = stof ( fgets ( file ) ) ;
// Next lines are each traversal
for ( float i = 0 ; i < cl_navmesh_traversal_count ; i + + ) {
2023-08-06 05:53:44 +00:00
cl_navmesh_traversals [ i ] . start_pos = stov ( fgets ( file ) ) ;
cl_navmesh_traversals [ i ] . midpoint_pos = stov ( fgets ( file ) ) ;
cl_navmesh_traversals [ i ] . end_pos = stov ( fgets ( file ) ) ;
2023-08-02 08:27:40 +00:00
cl_navmesh_traversals [ i ] . angle = stof ( fgets ( file ) ) ;
cl_navmesh_traversals [ i ] . use_midpoint = stof ( fgets ( file ) ) ;
// Don't care about traversal end polygon index
fgets ( file ) ;
}
2023-08-01 10:20:35 +00:00
}
// -----------------------------------------------------------------------
2023-02-05 10:07:35 +00:00
fclose ( file ) ;
}
//==================================================================
//==========================================================================================================================
//============================ The following methods are methods required in the pathfinding code ==========================
//==========================================================================================================================
float ( float start , float goal , vector start_pos , vector end_pos ) cl_navmesh_pathfind_start ;
2023-08-28 08:11:51 +00:00
void cl_navmesh_draw_test_ent ( vector pos , vector scale , vector color , float alpha ) {
2023-02-05 10:07:35 +00:00
//Assigning the shader as something else so that fte doesn't batch the calls (leading to colors not changing between draw calls)
R_BeginPolygon ( " debug/wireframe " , 0 ) ;
R_BeginPolygon ( " debug/solid_nocull " , 0 ) ;
//vector VEC_HULL_MIN = '-16 -16 -32';
//vector VEC_HULL_MAX = '16 16 40';
float min_x = - 16 * scale_x ;
float min_y = - 16 * scale_y ;
float min_z = - 32 * scale_z ;
float max_x = 16 * scale_x ;
float max_y = 16 * scale_y ;
float max_z = 40 * scale_z ;
//bottom face
R_PolygonVertex ( pos + [ min_x , max_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , max_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , min_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ min_x , min_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Top face
R_PolygonVertex ( pos + [ min_x , max_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , max_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , min_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ min_x , min_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Front face
R_PolygonVertex ( pos + [ min_x , min_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , min_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , min_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ min_x , min_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Back face
R_PolygonVertex ( pos + [ min_x , max_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , max_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , max_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ min_x , max_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Left face
R_PolygonVertex ( pos + [ min_x , min_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ min_x , max_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ min_x , max_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ min_x , min_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
//Right face
R_PolygonVertex ( pos + [ max_x , min_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , max_y , min_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , max_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_PolygonVertex ( pos + [ max_x , min_y , max_z ] , [ 0 , 0 , 0 ] , color , alpha ) ;
R_EndPolygon ( ) ;
}
2023-08-01 10:20:35 +00:00
void cl_navmesh_place_test_goalent ( ) {
2023-02-05 10:07:35 +00:00
goalent_pos = getentity ( player_localentnum , GE_ORIGIN ) ;
goalent_set = TRUE ;
//Calculate data that the navmesh exporer will calculate
cl_navmesh_calc_polies_centers ( ) ;
cl_navmesh_calc_connected_polies ( ) ;
2023-08-01 10:20:35 +00:00
if ( goalent_set = = TRUE & & startent_set = = TRUE ) {
print ( " Getting start poly: " ) ;
2023-02-05 10:07:35 +00:00
float start = cl_navmesh_get_containing_poly ( startent_pos ) ;
2023-08-01 10:20:35 +00:00
print ( " Getting goal poly: " ) ;
2023-02-05 10:07:35 +00:00
float goal = cl_navmesh_get_containing_poly ( goalent_pos ) ;
cl_navmesh_pathfind_start ( start , goal , startent_pos , goalent_pos ) ;
}
}
2023-08-01 10:20:35 +00:00
void cl_navmesh_place_test_startent ( ) {
2023-02-05 10:07:35 +00:00
startent_pos = getentity ( player_localentnum , GE_ORIGIN ) ;
startent_set = TRUE ;
//Calculate data that the navmesh exporer will calculate
cl_navmesh_calc_polies_centers ( ) ;
cl_navmesh_calc_connected_polies ( ) ;
2023-08-01 10:20:35 +00:00
if ( goalent_set = = TRUE & & startent_set = = TRUE ) {
print ( " Getting start poly: " ) ;
2023-02-05 10:07:35 +00:00
float start = cl_navmesh_get_containing_poly ( startent_pos ) ;
2023-08-01 10:20:35 +00:00
print ( " Getting goal poly: " ) ;
2023-02-05 10:07:35 +00:00
float goal = cl_navmesh_get_containing_poly ( goalent_pos ) ;
cl_navmesh_pathfind_start ( start , goal , startent_pos , goalent_pos ) ;
}
}
//Returns 1 if pos is inside poly at index poly_index, 0 otherwise
2023-08-01 10:20:35 +00:00
float cl_navmesh_is_inside_poly ( vector pos , float poly_index ) {
2023-02-05 10:07:35 +00:00
//TODO: check if z coord is close enough to poly
local vector vert_to_pos ; //points from vert to pos
local vector vert_to_next ; //points from vert to the next vertex
local vector vert ;
local vector next_vert ;
float vert_count = cl_navmesh_polies [ poly_index ] . vert_count ;
//We are considered to be in the polygon, if pos is on the left of all edges of the polygon (if verts are ordered in CW order)
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < vert_count ; i + + ) {
2023-02-05 10:07:35 +00:00
vert = cl_navmesh_verts [ cl_navmesh_polies [ poly_index ] . verts [ i ] ] . pos ;
next_vert = cl_navmesh_verts [ cl_navmesh_polies [ poly_index ] . verts [ ( i + 1 ) % vert_count ] ] . pos ;
vert_to_pos = pos - vert ;
vert_to_next = next_vert - vert ;
//Check if vert_to_pos is to the left of vert_to_next
if ( vert_to_next . x * vert_to_pos . y - vert_to_next . y * vert_to_pos . x < 0 )
return 0 ;
}
//FOR DEBUG: select that polygon if succesfull
cl_navmesh_deselect_all ( ) ;
selected_vert_count = cl_navmesh_polies [ poly_index ] . vert_count ;
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < selected_vert_count ; i + + ) {
2023-02-05 10:07:35 +00:00
selected_verts [ i ] = cl_navmesh_polies [ poly_index ] . verts [ i ] ;
}
//============================================
return 1 ;
}
//Returns distance from pos to an edge of the poly if pos is very close to an edge of the poly at index poly_index, -1 otherwise
float cl_navmesh_dist_to_poly ( vector pos , float poly_index )
{
float leeway = 30 ; //Must be within 30 qu of edge to be considered close
//TODO: check if close enough on z-axis
local vector vert_to_pos ; //points from vert to pos
local vector vert_to_next ; //points from vert to the next vertex
local vector vert ;
local vector next_vert ;
float vert_count = cl_navmesh_polies [ poly_index ] . vert_count ;
float shortest_dist = 100000 ;
//Only considering 2D
//pos.z = 0;
//We are considered to be in the polygon, if pos is on the left of all edges of the polygon (if verts are ordered in CW order)
for ( float i = 0 ; i < vert_count ; i + + )
{
vert = cl_navmesh_verts [ cl_navmesh_polies [ poly_index ] . verts [ i ] ] . pos ;
next_vert = cl_navmesh_verts [ cl_navmesh_polies [ poly_index ] . verts [ ( i + 1 ) % vert_count ] ] . pos ;
//Calculating in 2D:
//vert.z = 0;
//next_vert.z = 0;
float temp_dist = navmesh_2D_line_point_dist ( vert , next_vert , pos ) ;
if ( temp_dist < shortest_dist )
{
shortest_dist = temp_dist ;
}
}
if ( shortest_dist < leeway )
return shortest_dist ;
return - 1 ;
}
//Returns polygon that pos is inside of.
//If we are in no polygon, returns a polygon whose edge we are sufficiently close to (might be in but are not due to a small error)
//Otherwise, returns -1
float cl_navmesh_get_containing_poly ( vector pos )
{
//Get nearest polygon, and check if pos is in that polygon
float closest_poly = - 1 ;
float closest_poly_dist = 1000000 ;
//In reality, there's no need to try ALL polygons, the one we are in should be within the nearest 10 or so, but checking all to be safe
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + )
{
//Check if we are sufficiently close enough to polygon i in the z-axis
//Current method of checking: if pos is within 30 qu of polygon z-axis bounds
float poly_min_z = cl_navmesh_verts [ cl_navmesh_polies [ i ] . verts [ 0 ] ] . pos . z ;
float poly_max_z = poly_min_z ;
for ( float j = 0 ; j < cl_navmesh_polies [ i ] . vert_count ; j + + )
{
float vert_z = cl_navmesh_verts [ cl_navmesh_polies [ i ] . verts [ j ] ] . pos . z ;
poly_min_z = min ( poly_min_z , vert_z ) ;
poly_max_z = max ( poly_max_z , vert_z ) ;
}
// If we NOT between [min - 30, max + 30], skip this polygon
// if(fabs(sv_navmesh_polies[0].center.z - pos.z) >= 30)
if ( pos . z < poly_min_z - 30 | | pos . z > poly_max_z + 30 )
{
continue ;
}
//Check if we are in the 2d plane of polygon i
if ( cl_navmesh_is_inside_poly ( pos , i ) )
{
// NOTE - Because is_inside_poly only checks if we are in it on X & Y axes,
// NOTE we may have found a polygon on a floor below us or above us.
// NOTE However, this may not be an issue. A polygon on a floor below
// NOTE us would be at least about 80-ish qu away, and the checks done for
// NOTE z proximity might be enough to stop us from choosing polygons on
// NOTE different floors. I shall assume this works fine, and wait for a
// NOTE counter-example that shows we'll need a more sophisticated strategy.
print ( " Ent pos is inside poly: " ) ;
print ( ftos ( i ) ) ;
print ( " . \n " ) ;
return i ;
}
//If we are not in polygon i, check if we are very close to one of its edges
float dist = cl_navmesh_dist_to_poly ( pos , i ) ;
if ( dist > = 0 )
{
if ( dist < closest_poly_dist )
{
closest_poly = i ;
closest_poly_dist = dist ;
}
}
}
//FOR DEBUG: select that polygon if successful
if ( closest_poly ! = - 1 )
{
cl_navmesh_deselect_all ( ) ;
selected_vert_count = cl_navmesh_polies [ closest_poly ] . vert_count ;
for ( float i = 0 ; i < selected_vert_count ; i + + )
{
selected_verts [ i ] = cl_navmesh_polies [ closest_poly ] . verts [ i ] ;
}
}
//============================================
if ( closest_poly = = - 1 )
{
print ( " Ent pos is not in or near any polygons. \n " ) ;
}
else
{
print ( " Ent pos is near but not in poly: closest_poly. \n " ) ;
}
return closest_poly ;
}
//================================================================================================================
//=============================================== Actual A* functions ============================================
//================================================================================================================
2023-08-01 10:20:35 +00:00
//Calculates the heuristic h score value for a navmesh polygon (Our best guess for how far this node is from the goal node)
float cl_pathfind_calc_poly_h_score ( float current_poly_idx , float goal_poly_idx ) {
//FIXME: we could just as easily return vlen()^2 for comparisons... (saves a sqrt operation)
return vlen ( cl_navmesh_polies [ goal_poly_idx ] . center - cl_navmesh_polies [ current_poly_idx ] . center ) ;
}
//Calculates the heuristic h score value for a navmesh traversal (Our best guess for how far this node is from the goal node)
float cl_pathfind_calc_traversal_h_score ( float current_traversal_idx , float goal_poly_idx ) {
2023-02-05 10:07:35 +00:00
//FIXME: we could just as easily return vlen()^2 for comparisons... (saves a sqrt operation)
2023-08-01 10:20:35 +00:00
vector traversal_end_pos = cl_navmesh_get_traversal_end_pos ( current_traversal_idx ) ;
return vlen ( cl_navmesh_polies [ goal_poly_idx ] . center - traversal_end_pos ) ;
2023-02-05 10:07:35 +00:00
}
//Struct containing all arrays that the pathfind code requires to operate
2023-08-01 10:20:35 +00:00
navmesh_pathfind_result * cl_test_pathfind_result ;
2023-02-05 10:07:35 +00:00
//Returns the polygon with the lowest f score from polygons the open set
2023-08-01 10:20:35 +00:00
float cl_pathfind_get_lowest_f_score_poly ( ) {
2023-02-05 10:07:35 +00:00
//TODO: implement a better algorithm for finding the lowest score
float best_score = 100000 ;
float best_score_index = - 1 ;
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < cl_navmesh_poly_count ; i + + ) {
if ( cl_test_pathfind_result - > poly_set [ i ] = = PATHFIND_POLY_SET_OPEN ) {
if ( cl_test_pathfind_result - > poly_f_score [ i ] < best_score ) {
2023-02-05 10:07:35 +00:00
best_score = cl_test_pathfind_result - > poly_f_score [ i ] ;
best_score_index = i ;
}
}
}
2023-08-01 10:20:35 +00:00
return best_score_index ;
}
2023-08-03 08:27:01 +00:00
//Returns the traversal with the lowest f score from traversals the open set
2023-08-01 10:20:35 +00:00
float cl_pathfind_get_lowest_f_score_traversal ( ) {
//TODO: implement a better algorithm for finding the lowest score
float best_score = 100000 ;
float best_score_index = - 1 ;
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < cl_navmesh_traversal_count ; i + + ) {
if ( cl_test_pathfind_result - > traversal_set [ i ] = = PATHFIND_POLY_SET_OPEN ) {
if ( cl_test_pathfind_result - > traversal_f_score [ i ] < best_score ) {
best_score = cl_test_pathfind_result - > traversal_f_score [ i ] ;
best_score_index = i ;
}
}
}
2023-02-05 10:07:35 +00:00
return best_score_index ;
}
2023-08-01 10:20:35 +00:00
2023-02-05 10:07:35 +00:00
void cl_pathfind_clear_result_data ( ) {
//Technically we only need to iterate over navmesh_poly_count...
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < NAV_MAX_POLIES ; i + + ) {
2023-02-05 10:07:35 +00:00
cl_test_pathfind_result - > poly_set [ i ] = PATHFIND_POLY_SET_NONE ;
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result - > poly_prev_poly [ i ] = - 1 ;
cl_test_pathfind_result - > poly_prev_traversal [ i ] = - 1 ;
2023-02-05 10:07:35 +00:00
cl_test_pathfind_result - > poly_g_score [ i ] = 0 ;
cl_test_pathfind_result - > poly_f_score [ i ] = 0 ;
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result - > path_polygons [ i ] = - 1 ;
cl_test_pathfind_result - > path_traversals [ i ] = - 1 ;
2023-08-12 06:04:06 +00:00
cl_test_pathfind_result - > portals_left_pos [ i ] = ' 0 0 0 ' ;
cl_test_pathfind_result - > portals_right_pos [ i ] = ' 0 0 0 ' ;
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result - > portals_traversal [ i ] = - 1 ;
2023-08-12 06:04:06 +00:00
cl_test_pathfind_result - > point_path_points [ i ] = ' 0 0 0 ' ;
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result - > point_path_traversals [ i ] = - 1 ;
}
for ( float i = 0 ; i < NAV_MAX_TRAVERSALS ; i + + ) {
cl_test_pathfind_result - > traversal_set [ i ] = PATHFIND_POLY_SET_NONE ;
cl_test_pathfind_result - > traversal_prev_poly [ i ] = - 1 ;
cl_test_pathfind_result - > traversal_g_score [ i ] = 0 ;
cl_test_pathfind_result - > traversal_f_score [ i ] = 0 ;
}
cl_test_pathfind_result - > path_length = 0 ;
2023-02-05 10:07:35 +00:00
cl_test_pathfind_result - > portals_length = 0 ;
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result - > point_path_length = 0 ;
2023-02-05 10:07:35 +00:00
}
2023-08-01 10:20:35 +00:00
// vector cl_pathfind_get_left_portal_corner(navmesh_pathfind_result *res, int portal_idx) {
// // return cl_navmesh_verts[res->portals_left_vert[portal_idx]].pos;
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
// // Find the vertex opposite this portal edge
// vector corner = cl_navmesh_verts[res->portals_left_vert[portal_idx]].pos;
// vector opposite_corner = cl_navmesh_verts[res->portals_right_vert[portal_idx]].pos;
// // Move 20 qu or 20% of the edge length, whichever is smaller:
// float edge_len = vlen(opposite_corner - corner);
// vector edge_dir = normalize(opposite_corner - corner);
// return corner + edge_dir * min(edge_len * 0.2, 20);
// }
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
// vector cl_pathfind_get_right_portal_corner(navmesh_pathfind_result *res, int portal_idx) {
// // return cl_navmesh_verts[res->portals_right_vert[portal_idx]].pos;
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
// // Find the vertex opposite this portal edge
// vector corner = cl_navmesh_verts[res->portals_right_vert[portal_idx]].pos;
// vector opposite_corner = cl_navmesh_verts[res->portals_left_vert[portal_idx]].pos;
// // Move 20 qu or 20% of the edge length, whichever is smaller:
// float edge_len = vlen(opposite_corner - corner);
// vector edge_dir = normalize(opposite_corner - corner);
// return corner + edge_dir * min(edge_len * 0.2, 20);
// }
2023-02-05 10:07:35 +00:00
//Applies a funnel algorithm to the path defined by the array cl_test_pathfind_result->result_node_path
// and populates pathfind result path
void cl_pathfind_smooth_path ( vector start_point , vector goal_point ) {
2023-08-01 10:20:35 +00:00
float cur_poly ;
float cur_traversal ;
float prev_poly ;
float prev_traversal ;
vector cur_portal_left_pos = ' 0 0 0 ' ;
vector cur_portal_right_pos = ' 0 0 0 ' ;
float cur_portal_traversal = - 1 ;
vector traversal_start_pos ;
vector traversal_end_pos ;
// Build the list of portals
for ( float i = 1 ; i < cl_test_pathfind_result - > path_length ; i + + ) {
cur_poly = cl_test_pathfind_result - > path_polygons [ i ] ;
cur_traversal = cl_test_pathfind_result - > path_traversals [ i ] ;
prev_poly = cl_test_pathfind_result - > path_polygons [ i - 1 ] ;
prev_traversal = cl_test_pathfind_result - > path_traversals [ i - 1 ] ;
// If polygon and came from polygon, portal = shared edge
if ( cur_poly ! = - 1 & & prev_poly ! = - 1 ) {
// Find which of prev_poly's edges connects to cur_poly
float prev_poly_edge_index = - 1 ;
for ( float j = 0 ; j < cl_navmesh_polies [ prev_poly ] . connected_polies_count ; j + + ) {
if ( cl_navmesh_polies [ prev_poly ] . connected_polies [ j ] = = cur_poly ) {
prev_poly_edge_index = j ;
break ;
}
2023-02-05 10:07:35 +00:00
}
2023-08-01 10:20:35 +00:00
cur_portal_left_pos = cl_navmesh_verts [ cl_navmesh_polies [ prev_poly ] . connected_polies_left_vert [ prev_poly_edge_index ] ] . pos ;
cur_portal_right_pos = cl_navmesh_verts [ cl_navmesh_polies [ prev_poly ] . connected_polies_right_vert [ prev_poly_edge_index ] ] . pos ;
cur_portal_traversal = - 1 ;
// ----------------------------------------------------------------
// Narrow the portal from each side by 20% or 20qu, whichever is smaller
// ----------------------------------------------------------------
float edge_len = vlen ( cur_portal_right_pos - cur_portal_left_pos ) ;
vector edge_dir = normalize ( cur_portal_right_pos - cur_portal_left_pos ) ;
float ofs = min ( edge_len * 0.2 , 20 ) ;
cur_portal_left_pos = cur_portal_left_pos + edge_dir * ofs ;
cur_portal_right_pos = cur_portal_right_pos - edge_dir * ofs ;
// ----------------------------------------------------------------
}
// If polygon and came from traversal, portal = traversal exit
else if ( cur_poly ! = - 1 & & prev_traversal ! = - 1 ) {
traversal_end_pos = cl_navmesh_get_traversal_end_pos ( prev_traversal ) ;
cur_portal_left_pos = traversal_end_pos ;
cur_portal_right_pos = traversal_end_pos ;
cur_portal_traversal = - 1 ;
// TODO - Denote that it came from traversal? maybe we don't care?
}
// If traversal and came from polygon, portal = traversal start
else if ( cur_traversal ! = - 1 & & prev_poly ! = - 1 ) {
traversal_start_pos = cl_navmesh_traversals [ cur_traversal ] . start_pos ;
cur_portal_left_pos = traversal_start_pos ;
cur_portal_right_pos = traversal_start_pos ;
cur_portal_traversal = cur_traversal ;
2023-02-05 10:07:35 +00:00
}
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result - > portals_left_pos [ cl_test_pathfind_result - > portals_length ] = cur_portal_left_pos ;
cl_test_pathfind_result - > portals_right_pos [ cl_test_pathfind_result - > portals_length ] = cur_portal_right_pos ;
cl_test_pathfind_result - > portals_traversal [ cl_test_pathfind_result - > portals_length ] = cur_portal_traversal ;
cl_test_pathfind_result - > portals_length + + ;
2023-02-05 10:07:35 +00:00
}
2023-08-01 10:20:35 +00:00
// TODO - Should I add goal_pos as the final portal?
cl_test_pathfind_result - > portals_left_pos [ cl_test_pathfind_result - > portals_length ] = goal_point ;
cl_test_pathfind_result - > portals_right_pos [ cl_test_pathfind_result - > portals_length ] = goal_point ;
cl_test_pathfind_result - > portals_traversal [ cl_test_pathfind_result - > portals_length ] = - 1 ;
cl_test_pathfind_result - > portals_length + + ;
vector cur_funnel_corner = start_point ;
vector cur_funnel_left = cl_test_pathfind_result - > portals_left_pos [ 0 ] ;
vector cur_funnel_right = cl_test_pathfind_result - > portals_right_pos [ 0 ] ;
2023-08-02 08:27:40 +00:00
float cur_funnel_left_portal_idx = 0 ;
float cur_funnel_right_portal_idx = 0 ;
2023-08-01 10:20:35 +00:00
// Loop through all portals, adding necessary funnel corners to final path
for ( float i = 0 ; i < cl_test_pathfind_result - > portals_length ; i + + ) {
cur_portal_left_pos = cl_test_pathfind_result - > portals_left_pos [ i ] ;
cur_portal_right_pos = cl_test_pathfind_result - > portals_right_pos [ i ] ;
cur_portal_traversal = cl_test_pathfind_result - > portals_traversal [ i ] ;
// TODO - Project portal points to axis made by cur funnel points
// Then we can check
// TODO - Compute value for a point:
// TODO 0: On funnel center
// TODO < -1 outside of funnel to the left
// TODO > 1 outside of funnel to the right
float cur_portal_left_in_funnel = pathfind_point_in_funnel ( cur_portal_left_pos , cur_funnel_corner , cur_funnel_left , cur_funnel_right ) ;
float cur_portal_right_in_funnel = pathfind_point_in_funnel ( cur_portal_right_pos , cur_funnel_corner , cur_funnel_left , cur_funnel_right ) ;
2023-08-02 08:27:40 +00:00
print ( " {'portal_idx': " , ftos ( i ) , " , 'portal': " ) ;
print ( " [( " , ftos ( cur_portal_left_pos . x ) , " , " , ftos ( cur_portal_left_pos . y ) , " ), ( " , ftos ( cur_portal_right_pos . x ) , " , " , ftos ( cur_portal_right_pos . y ) ) ;
print ( " )], 'traversal': " , ftos ( cur_portal_traversal ) , " , " ) ;
print ( " 'funnel_result': ( " , ftos ( cur_portal_left_in_funnel ) , " , " , ftos ( cur_portal_right_in_funnel ) , " ), " ) ;
print ( " 'funnel': [ " ) ;
print ( " ( " , ftos ( cur_funnel_left . x ) , " , " , ftos ( cur_funnel_left . y ) , " ), " ) ;
print ( " ( " , ftos ( cur_funnel_corner . x ) , " , " , ftos ( cur_funnel_corner . y ) , " ), " ) ;
print ( " ( " , ftos ( cur_funnel_right . x ) , " , " , ftos ( cur_funnel_right . y ) , " ) " ) ;
print ( " ]}, \n " ) ;
2023-08-01 10:20:35 +00:00
// print(vtos(cur_funnel_left));
// If cur portal left point is in cur funnel, narrow the cur funnel
if ( cur_portal_left_in_funnel > = - 1 & & cur_portal_left_in_funnel < = 1 ) {
cur_funnel_left = cur_portal_left_pos ;
2023-08-02 08:27:40 +00:00
cur_funnel_left_portal_idx = i ;
2023-08-01 10:20:35 +00:00
}
// If cur portal right point is in cur funnel, narrow the cur funnel
if ( cur_portal_right_in_funnel > = - 1 & & cur_portal_right_in_funnel < = 1 ) {
cur_funnel_right = cur_portal_right_pos ;
2023-08-02 08:27:40 +00:00
cur_funnel_right_portal_idx = i ;
2023-08-01 10:20:35 +00:00
}
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
// If cur portal is completely outside (to left of) current funnel
if ( cur_portal_left_in_funnel < - 1 & & cur_portal_right_in_funnel < - 1 ) {
// Add cur funnel left point to final path:
cl_test_pathfind_result - > point_path_points [ cl_test_pathfind_result - > point_path_length ] = cur_funnel_left ;
cl_test_pathfind_result - > point_path_traversals [ cl_test_pathfind_result - > point_path_length ] = - 1 ;
cl_test_pathfind_result - > point_path_length + + ;
2023-08-02 08:27:40 +00:00
// Update funnel to start at the current funnel left endpoint, pointing to the next portal in the list
2023-08-01 10:20:35 +00:00
// Set next funnel to start at cur funnel left point
cur_funnel_corner = cur_funnel_left ;
2023-08-02 08:27:40 +00:00
// Advance the funnel's left point to the next portal
cur_funnel_left_portal_idx + = 1 ;
cur_funnel_right = cl_test_pathfind_result - > portals_right_pos [ cur_funnel_left_portal_idx ] ;
cur_funnel_left = cl_test_pathfind_result - > portals_left_pos [ cur_funnel_left_portal_idx ] ;
cur_funnel_right_portal_idx = cur_funnel_left_portal_idx ;
// Restart funnel algorithm from the funnel's endpoint
i = cur_funnel_left_portal_idx - 1 ;
continue ;
2023-08-01 10:20:35 +00:00
}
// If cur portal is completely outside (to right of) current funnel
else if ( cur_portal_left_in_funnel > 1 & & cur_portal_right_in_funnel > 1 ) {
// Add cur funnel right point to final path:
cl_test_pathfind_result - > point_path_points [ cl_test_pathfind_result - > point_path_length ] = cur_funnel_right ;
cl_test_pathfind_result - > point_path_traversals [ cl_test_pathfind_result - > point_path_length ] = - 1 ;
cl_test_pathfind_result - > point_path_length + + ;
2023-08-02 08:27:40 +00:00
// Update funnel to start at the current funnel right endpoint, pointing to the next portal in the list
2023-08-01 10:20:35 +00:00
// Set next funnel to start at cur funnel right point
cur_funnel_corner = cur_funnel_right ;
2023-08-02 08:27:40 +00:00
// Advance the funnel's right point to the next portal
cur_funnel_right_portal_idx + = 1 ;
cur_funnel_right = cl_test_pathfind_result - > portals_right_pos [ cur_funnel_right_portal_idx ] ;
cur_funnel_left = cl_test_pathfind_result - > portals_left_pos [ cur_funnel_right_portal_idx ] ;
cur_funnel_left_portal_idx = cur_funnel_right_portal_idx ;
// Restart funnel algorithm from the funnel's endpoint
i = cur_funnel_right_portal_idx - 1 ;
continue ;
2023-08-01 10:20:35 +00:00
}
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
// If cur portal is a traversal
if ( cur_portal_traversal ! = - 1 ) {
vector start_pos = cl_navmesh_traversals [ cur_portal_traversal ] . start_pos ;
vector end_pos = cl_navmesh_get_traversal_end_pos ( cur_portal_traversal ) ;
// Add traversal start point to final path:
cl_test_pathfind_result - > point_path_points [ cl_test_pathfind_result - > point_path_length ] = start_pos ;
cl_test_pathfind_result - > point_path_traversals [ cl_test_pathfind_result - > point_path_length ] = cur_portal_traversal ;
cl_test_pathfind_result - > point_path_length + + ;
// Add traversal end point to final path:
// FIXME - Should I remove this?
cl_test_pathfind_result - > point_path_points [ cl_test_pathfind_result - > point_path_length ] = end_pos ;
cl_test_pathfind_result - > point_path_traversals [ cl_test_pathfind_result - > point_path_length ] = - 1 ;
cl_test_pathfind_result - > point_path_length + + ;
// Set funnel to start at traversal endpoint and use the next portal
cur_funnel_corner = end_pos ;
cur_funnel_left = cl_test_pathfind_result - > portals_left_pos [ i + 1 ] ;
cur_funnel_right = cl_test_pathfind_result - > portals_right_pos [ i + 1 ] ;
2023-08-02 08:27:40 +00:00
cur_funnel_left_portal_idx = i + 1 ;
cur_funnel_right_portal_idx = i + 1 ;
2023-08-01 10:20:35 +00:00
}
}
// Always add goal point to the path
cl_test_pathfind_result - > point_path_points [ cl_test_pathfind_result - > point_path_length ] = goal_point ;
cl_test_pathfind_result - > point_path_traversals [ cl_test_pathfind_result - > point_path_length ] = - 1 ;
cl_test_pathfind_result - > point_path_length + + ;
}
2023-02-05 10:07:35 +00:00
//Evaluates the path that is found in the pathfinding algorithm and populates an array with the nodes in the path from start to goal
2023-08-03 08:27:01 +00:00
void cl_pathfind_trace_path ( float start_poly , float goal_poly ) {
2023-08-01 10:20:35 +00:00
// print("Polygon from poly: ");
// for(float i = 0; i < cl_navmesh_poly_count; i++) {
// print(ftos(i));
// print(": ");
// print(ftos(cl_test_pathfind_result->poly_prev_poly[i]));
// print(", ");
// }
// print("\n");
// print("Polygon from traversal: ");
// for(float i = 0; i < cl_navmesh_poly_count; i++) {
// print(ftos(i));
// print(": ");
// print(ftos(cl_test_pathfind_result->poly_prev_traversal[i]));
// print(", ");
// }
// print("\n");
// print("Traversal from polygon:");
// for(float i = 0; i < cl_navmesh_traversal_count; i++) {
// print(ftos(i));
// print(": ");
// print(ftos(cl_test_pathfind_result->traversal_prev_poly[i]));
// print(", ");
// }
// print("\n");
2023-02-05 10:07:35 +00:00
//Count the length of the path (how many polygons the path traverses)
2023-08-01 10:20:35 +00:00
float current_poly = goal_poly ;
float current_traversal = - 1 ;
cl_test_pathfind_result - > path_length = 1 ;
do {
// Current node is a polygon
if ( current_poly ! = - 1 ) {
current_traversal = cl_test_pathfind_result - > poly_prev_traversal [ current_poly ] ;
current_poly = cl_test_pathfind_result - > poly_prev_poly [ current_poly ] ;
}
// Current node is a traversal
else if ( current_traversal ! = - 1 ) {
current_poly = cl_test_pathfind_result - > traversal_prev_poly [ current_traversal ] ;
current_traversal = - 1 ;
}
cl_test_pathfind_result - > path_length + + ;
} while ( current_poly ! = start_poly ) ;
// Starting at goal_poly, add the traversed polygons / traversals to the result path in reverse order
current_poly = goal_poly ;
current_traversal = - 1 ;
for ( float i = 0 ; i < cl_test_pathfind_result - > path_length ; i + + ) {
// print("Current node (P=");
// print(ftos(current_poly));
// print(",T=");
// print(ftos(current_traversal));
// print(") came from ");
cl_test_pathfind_result - > path_polygons [ cl_test_pathfind_result - > path_length - 1 - i ] = current_poly ;
cl_test_pathfind_result - > path_traversals [ cl_test_pathfind_result - > path_length - 1 - i ] = current_traversal ;
// Current node is a polygon
if ( current_poly ! = - 1 ) {
current_traversal = cl_test_pathfind_result - > poly_prev_traversal [ current_poly ] ;
current_poly = cl_test_pathfind_result - > poly_prev_poly [ current_poly ] ;
}
// Current node is a traversal
else if ( current_traversal ! = - 1 ) {
current_poly = cl_test_pathfind_result - > traversal_prev_poly [ current_traversal ] ;
current_traversal = - 1 ;
}
// print(" (P=");
// print(ftos(current_poly));
// print(",T=");
// print(ftos(current_traversal));
// print(")\n");
2023-02-05 10:07:35 +00:00
}
2023-08-01 10:20:35 +00:00
print ( " Pathfind success, path length: " ) ;
print ( ftos ( cl_test_pathfind_result - > path_length ) ) ;
print ( " . \n " ) ;
print ( " Path: " ) ;
for ( float i = 0 ; i < cl_test_pathfind_result - > path_length ; i + + ) {
if ( i > 0 ) {
print ( " , " ) ;
}
if ( cl_test_pathfind_result - > path_polygons [ i ] ! = - 1 ) {
print ( " P " ) ;
print ( ftos ( cl_test_pathfind_result - > path_polygons [ i ] ) ) ;
}
else if ( cl_test_pathfind_result - > path_traversals [ i ] ! = - 1 ) {
print ( " T " ) ;
print ( ftos ( cl_test_pathfind_result - > path_traversals [ i ] ) ) ;
}
}
print ( " . \n " ) ;
2023-02-05 10:07:35 +00:00
}
//Accepts start polygon and goal polygon
//Returns 1 on success.
//Returns 0 on fail.
2023-08-01 10:20:35 +00:00
float cl_navmesh_pathfind_start ( float start_poly , float goal_poly , vector start_pos , vector end_pos ) {
2023-08-06 05:53:44 +00:00
//Clearing previous data
cl_pathfind_clear_result_data ( ) ;
2023-08-01 10:20:35 +00:00
if ( start_poly = = - 1 ) {
2023-02-05 10:07:35 +00:00
print ( " Error: pathfind start node invalid. \n " ) ;
return 0 ;
}
2023-08-01 10:20:35 +00:00
if ( goal_poly = = - 1 ) {
2023-02-05 10:07:35 +00:00
print ( " Error: pathfind goal node invalid. \n " ) ;
return 0 ;
}
2023-08-01 10:20:35 +00:00
if ( start_poly = = goal_poly ) {
2023-02-05 10:07:35 +00:00
//Calculating node path
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result - > path_polygons [ 0 ] = start_poly ;
cl_test_pathfind_result - > path_traversals [ 0 ] = - 1 ;
cl_test_pathfind_result - > path_length = 1 ;
2023-02-05 10:07:35 +00:00
print ( " Pathind success: trivial case (start = goal). \n " ) ;
//Calculating vector based path (go directly to goal)
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result - > point_path_points [ 0 ] . x = end_pos . x ;
cl_test_pathfind_result - > point_path_points [ 0 ] . y = end_pos . y ;
cl_test_pathfind_result - > point_path_points [ 0 ] . z = end_pos . z ;
// Indicate non-traversal
cl_test_pathfind_result - > point_path_traversals [ 0 ] = - 1 ;
cl_test_pathfind_result - > point_path_length = 1 ;
2023-02-05 10:07:35 +00:00
return 1 ;
}
//Adding start polygon to the open set
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result - > poly_set [ start_poly ] = PATHFIND_POLY_SET_OPEN ;
cl_test_pathfind_result - > poly_g_score [ start_poly ] = 0 ;
cl_test_pathfind_result - > poly_f_score [ start_poly ] = 0 + cl_pathfind_calc_poly_h_score ( start_poly , goal_poly ) ;
2023-08-03 08:27:01 +00:00
2023-08-01 10:20:35 +00:00
// print("Pathfind init. Start: ");
// print(ftos(start_poly));
// print(" , Goal: ");
// print(ftos(goal_poly));
// print(".\n");
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
float current_poly = start_poly ;
float current_traversal = - 1 ;
2023-02-05 10:07:35 +00:00
float pathfind_success = FALSE ;
2023-08-01 10:20:35 +00:00
// While we have a traversal OR a polygon
while ( current_poly ! = - 1 | | current_traversal ! = - 1 ) {
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
// If we are currently evaluating a polygon:
if ( current_poly ! = - 1 ) {
if ( current_poly = = goal_poly ) {
//print("Current is now goal. Breaking.\n");
pathfind_success = TRUE ;
break ;
}
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
//Add current node to the closed set
cl_test_pathfind_result - > poly_set [ current_poly ] = PATHFIND_POLY_SET_CLOSED ;
//Add connected neighbor polygons to the open set
for ( float i = 0 ; i < cl_navmesh_polies [ current_poly ] . connected_polies_count ; i + + ) {
float neighbor_poly = cl_navmesh_polies [ current_poly ] . connected_polies [ i ] ;
// print("Checking poly ");
// print(ftos(current_poly));
// print("'s neighbor poly ");
// print(ftos(neighbor_poly));
// print(".\n");
// ----------------------------------------------------------------
// Door check
// ----------------------------------------------------------------
// NOTE - If polygon's door hasn't been opened, don't consider it.
// NOTE - This check isn't done in the navmesh editor.
// ----------------------------------------------------------------
if ( cl_test_pathfind_result - > poly_set [ neighbor_poly ] ! = PATHFIND_POLY_SET_CLOSED ) {
//print("Neighbor is not in closed list.\n");
//Calculate tentative f score
//Calculate tentative g score (distance from start to current + distance from current to neighbor)
float tentative_g_score = cl_test_pathfind_result - > poly_g_score [ current_poly ] + vlen ( cl_navmesh_polies [ neighbor_poly ] . center - cl_navmesh_polies [ current_poly ] . center ) ;
// If not in open-set, or this is a better score, set score for this polygon
if ( cl_test_pathfind_result - > poly_set [ neighbor_poly ] ! = PATHFIND_POLY_SET_OPEN | | tentative_g_score < cl_test_pathfind_result - > poly_g_score [ neighbor_poly ] ) {
//print("Neighbor is not in open list, or a better g score has been found.\n");
//Adding neighbor to open set
cl_test_pathfind_result - > poly_set [ neighbor_poly ] = PATHFIND_POLY_SET_OPEN ;
//Updating scores for neighbor node
float tentative_f_score = tentative_g_score + cl_pathfind_calc_poly_h_score ( neighbor_poly , goal_poly ) ;
cl_test_pathfind_result - > poly_g_score [ neighbor_poly ] = tentative_g_score ;
cl_test_pathfind_result - > poly_f_score [ neighbor_poly ] = tentative_f_score ;
//Linking neighbor node to current node (for tracing path)
cl_test_pathfind_result - > poly_prev_poly [ neighbor_poly ] = current_poly ;
cl_test_pathfind_result - > poly_prev_traversal [ neighbor_poly ] = - 1 ;
// print("Assigning Poly ");
// print(ftos(neighbor_poly));
// print(" came from poly ");
// print(ftos(current_poly));
// print(".\n");
}
2023-02-05 10:07:35 +00:00
}
}
2023-08-01 10:20:35 +00:00
//Add connected neighbor traversals to the open set
for ( float i = 0 ; i < cl_navmesh_polies [ current_poly ] . connected_traversals_count ; i + + ) {
float neighbor_traversal = cl_navmesh_polies [ current_poly ] . connected_traversals [ i ] ;
// print("Checking poly ");
// print(ftos(current_poly));
// print("'s neighbor traversal ");
// print(ftos(neighbor_traversal));
// print(".\n");
if ( cl_test_pathfind_result - > traversal_set [ neighbor_traversal ] ! = PATHFIND_POLY_SET_CLOSED ) {
//Calculate tentative f score
//Calculate tentative g score (distance from start to current + distance from current to neighbor)
// vector traversal_start_pos = cl_navmesh_traversals[neighbor].start_pos;
vector traversal_end_pos = cl_navmesh_get_traversal_end_pos ( neighbor_traversal ) ;
// TODO - Should I cache traversal length?
float tentative_g_score = cl_test_pathfind_result - > poly_g_score [ current_poly ] + vlen ( traversal_end_pos - cl_navmesh_polies [ current_poly ] . center ) ;
// If not in open-set, or this is a better score, set score for this traversal
if ( cl_test_pathfind_result - > traversal_set [ neighbor_traversal ] ! = PATHFIND_POLY_SET_OPEN | | tentative_g_score < cl_test_pathfind_result - > traversal_g_score [ neighbor_traversal ] ) {
//print("Neighbor is not in open list, or a better g score has been found.\n");
//Adding neighbor to open set
cl_test_pathfind_result - > traversal_set [ neighbor_traversal ] = PATHFIND_POLY_SET_OPEN ;
//Updating scores for neighbor node
float tentative_f_score = tentative_g_score + cl_pathfind_calc_traversal_h_score ( neighbor_traversal , goal_poly ) ;
cl_test_pathfind_result - > traversal_g_score [ neighbor_traversal ] = tentative_g_score ;
cl_test_pathfind_result - > traversal_f_score [ neighbor_traversal ] = tentative_f_score ;
//Linking neighbor node to current node (for tracing path)
cl_test_pathfind_result - > traversal_prev_poly [ neighbor_traversal ] = current_poly ;
// print("Assigning traversal ");
// print(ftos(neighbor_traversal));
// print(" came from poly ");
// print(ftos(current_poly));
// print(".\n");
}
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
}
}
}
else if ( current_traversal ! = - 1 ) {
// print("Checking traversal: ");
// print(ftos(current_traversal));
// print(" with end polygon: ");
// print(ftos(cl_navmesh_traversals[current_traversal].end_poly));
// print("\n");
//Add current traversal to the closed set
cl_test_pathfind_result - > traversal_set [ current_traversal ] = PATHFIND_POLY_SET_CLOSED ;
// Traversals have single neighbor polygons (at the traversal endpoint)
float neighbor_poly = cl_navmesh_traversals [ current_traversal ] . end_poly ;
if ( neighbor_poly ! = - 1 ) {
// print("Checking traversal ");
// print(ftos(current_traversal));
// print("'s neighbor poly ");
// print(ftos(neighbor_poly));
// print(".\n");
if ( cl_test_pathfind_result - > poly_set [ neighbor_poly ] ! = PATHFIND_POLY_SET_CLOSED ) {
//print("Neighbor is not in closed list.\n");
//Calculate tentative f score
//Calculate tentative g score (distance from start to current + distance from current to neighbor)
vector traversal_end_pos = cl_navmesh_get_traversal_end_pos ( current_traversal ) ;
float tentative_g_score = cl_test_pathfind_result - > traversal_g_score [ current_traversal ] + vlen ( cl_navmesh_polies [ neighbor_poly ] . center - traversal_end_pos ) ;
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
// If not in open-set, or this is a better score, set score for this polygon
if ( cl_test_pathfind_result - > poly_set [ neighbor_poly ] ! = PATHFIND_POLY_SET_OPEN | | tentative_g_score < cl_test_pathfind_result - > poly_g_score [ neighbor_poly ] ) {
//print("Neighbor is not in open list, or a better g score has been found.\n");
//Adding neighbor to open set
cl_test_pathfind_result - > poly_set [ neighbor_poly ] = PATHFIND_POLY_SET_OPEN ;
//Updating scores for neighbor node
float tentative_f_score = tentative_g_score + cl_pathfind_calc_poly_h_score ( neighbor_poly , goal_poly ) ;
cl_test_pathfind_result - > poly_g_score [ neighbor_poly ] = tentative_g_score ;
cl_test_pathfind_result - > poly_f_score [ neighbor_poly ] = tentative_f_score ;
//Linking neighbor node to current node (for tracing path)
cl_test_pathfind_result - > poly_prev_poly [ neighbor_poly ] = - 1 ;
cl_test_pathfind_result - > poly_prev_traversal [ neighbor_poly ] = current_traversal ;
// print("Assigning Poly ");
// print(ftos(neighbor_poly));
// print(" came from traversal ");
// print(ftos(current_traversal));
// print(".\n");
}
2023-02-05 10:07:35 +00:00
}
}
2023-08-01 10:20:35 +00:00
}
float best_openset_poly = cl_pathfind_get_lowest_f_score_poly ( ) ;
float best_openset_traversal = cl_pathfind_get_lowest_f_score_traversal ( ) ;
// print("Best openset poly: ");
// print(ftos(best_openset_poly));
// print("\n");
// print("Best openset traversal: ");
// print(ftos(best_openset_traversal));
// print("\n");
// If we have both, compare their f-scores
if ( best_openset_poly ! = - 1 & & best_openset_traversal ! = - 1 ) {
// If traversal score is better, don't consider polygon
if ( cl_test_pathfind_result - > traversal_f_score [ best_openset_poly ] < cl_test_pathfind_result - > poly_f_score [ best_openset_poly ] ) {
best_openset_poly = - 1 ;
}
// If polygon score is better, don't consider traversal
else {
best_openset_traversal = - 1 ;
}
}
// Assign what we found, loop will terminate if we have neither
current_traversal = best_openset_traversal ;
current_poly = best_openset_poly ;
// --------------------------------------------------------------------
2023-02-05 10:07:35 +00:00
}
2023-08-01 10:20:35 +00:00
2023-02-05 10:07:35 +00:00
//Tracing the pathfind results
2023-08-01 10:20:35 +00:00
if ( pathfind_success = = TRUE ) {
cl_pathfind_trace_path ( start_poly , goal_poly ) ;
cl_pathfind_smooth_path ( start_pos , end_pos ) ;
2023-02-05 10:07:35 +00:00
return 1 ;
}
print ( " Pathfind fail " ) ;
return 0 ;
}
//This renders the raw pathfind results (polygon center to polygon center)
2023-08-01 10:20:35 +00:00
void cl_navmesh_pathfind_draw_result_path ( ) {
if ( cl_test_pathfind_result - > path_length < 1 )
2023-02-05 10:07:35 +00:00
return ;
//================= Drawing Polygon Center ======================
//=======================================
//Assigning the shader as something else so that fte doesn't batch the calls (leading to colors not changing between draw calls)
R_BeginPolygon ( " debug/wireframe " , 0 ) ;
R_BeginPolygon ( " debug/solid_nocull " , 0 ) ;
2023-08-01 10:20:35 +00:00
vector ofs = [ 0 , 0 , - 5 ] ;
vector size = [ 5 , 5 , 5 ] ;
vector color = [ 1 , 0.1 , 0.5 ] ;
2023-02-05 10:07:35 +00:00
float alpha = 0.2 ; //0.3
float edge_width = 2 ;
float edge_alpha = 0.2 ; //0.3
2023-08-01 10:20:35 +00:00
vector cur_point = ' 0 0 0 ' ;
vector cur_end_point = ' 0 0 0 ' ;
vector prev_point = ' 0 0 0 ' ;
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < cl_test_pathfind_result - > path_length ; i + + ) {
// Current path entry is a polygon, draw box at polygon center
if ( cl_test_pathfind_result - > path_polygons [ i ] ! = - 1 ) {
cur_point = cl_navmesh_polies [ cl_test_pathfind_result - > path_polygons [ i ] ] . center + ofs ;
cur_end_point = cur_point ;
cl_navmesh_draw_box ( cur_point , size , color , alpha ) ;
}
// Current path entry is a traversal, draw box at start and end
else if ( cl_test_pathfind_result - > path_traversals [ i ] ! = - 1 ) {
cur_point = cl_navmesh_traversals [ cl_test_pathfind_result - > path_traversals [ i ] ] . start_pos + ofs ;
cur_end_point = cl_navmesh_get_traversal_end_pos ( cl_test_pathfind_result - > path_traversals [ i ] ) + ofs ;
cl_navmesh_draw_box ( cur_point , size , color , alpha ) ;
cl_navmesh_draw_box ( cur_end_point , size , color , alpha ) ;
cl_navmesh_draw_line ( cur_end_point , cur_point , edge_width , color , edge_alpha ) ;
2023-02-05 10:07:35 +00:00
}
2023-08-01 10:20:35 +00:00
// Draw an edge connecting i-th node to previous node
if ( i > 0 ) {
cl_navmesh_draw_line ( prev_point , cur_point , edge_width , color , edge_alpha ) ;
}
prev_point = cur_end_point ;
2023-02-05 10:07:35 +00:00
}
}
2023-08-01 10:20:35 +00:00
void cl_navmesh_pathfind_draw_result_portals ( ) {
// TODO - Update this to account for traversals (at least once I implement funnel algorithm for traversals)
2023-02-05 10:07:35 +00:00
if ( cl_test_pathfind_result - > portals_length < 1 )
return ;
vector color ;
color = [ 0.5 , 0.5 , 1.0 ] ;
float edge_width = 4 ;
float edge_alpha = 0.2 ; //0.3
2023-08-01 10:20:35 +00:00
vector portal_left ;
vector portal_right ;
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < cl_test_pathfind_result - > portals_length ; i + + ) {
portal_left = cl_test_pathfind_result - > portals_left_pos [ i ] ;
portal_right = cl_test_pathfind_result - > portals_right_pos [ i ] ;
cl_navmesh_draw_vert ( portal_left + [ 0 , 0 , - 10 ] , [ 0.5 , 0.5 , 1 ] , 0.4 ) ;
cl_navmesh_draw_vert ( portal_right + [ 0 , 0 , - 8 ] , [ 0.5 , 1 , 0.5 ] , 0.4 ) ;
2023-02-05 10:07:35 +00:00
R_BeginPolygon ( " debug/wireframe " , 0 ) ;
R_BeginPolygon ( " debug/solid_nocull " , 0 ) ;
//Draw an edge connecting portals
2023-08-01 10:20:35 +00:00
cl_navmesh_draw_line ( portal_left + [ 0 , 0 , - 10 ] , portal_right + [ 0 , 0 , - 8 ] , edge_width , color , edge_alpha ) ;
2023-02-05 10:07:35 +00:00
}
}
2023-08-01 10:20:35 +00:00
void cl_navmesh_pathfind_draw_result_point_path ( ) {
// TODO - Update this to account for traversals
if ( cl_test_pathfind_result - > point_path_length < 1 )
2023-02-05 10:07:35 +00:00
return ;
vector color ;
color = [ 1.0 , 0.0 , 0.0 ] ;
float edge_width = 4 ;
2023-08-01 10:20:35 +00:00
float alpha = 0.4 ;
2023-02-05 10:07:35 +00:00
float edge_alpha = 0.2 ; //0.3
2023-08-01 10:20:35 +00:00
vector ofs = [ 0 , 0 , 10 ] ;
2023-02-05 10:07:35 +00:00
//Drawing the a line connecting the start and first path node
2023-08-01 10:20:35 +00:00
cl_navmesh_draw_vert ( startent_pos + ofs , color , alpha ) ;
cl_navmesh_draw_line ( startent_pos + ofs , cl_test_pathfind_result - > point_path_points [ 0 ] + ofs , edge_width , color , edge_alpha ) ;
vector cur_point = ' 0 0 0 ' ;
vector prev_point = ' 0 0 0 ' ;
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
for ( float i = 0 ; i < cl_test_pathfind_result - > point_path_length ; i + + ) {
cur_point = cl_test_pathfind_result - > point_path_points [ i ] ;
cl_navmesh_draw_vert ( cur_point + ofs , color , alpha ) ;
// TODO - if traversal, draw endpoint and line?
2023-02-05 10:07:35 +00:00
2023-08-01 10:20:35 +00:00
if ( i > 0 ) {
//Draw an edge connecting path points
cl_navmesh_draw_line ( cur_point + ofs , prev_point + ofs , edge_width , color , edge_alpha ) ;
2023-02-05 10:07:35 +00:00
}
2023-08-01 10:20:35 +00:00
prev_point = cur_point ;
2023-02-05 10:07:35 +00:00
}
}
2023-08-08 05:24:15 +00:00
void cl_toggle_navmesh_editor ( ) {
2023-02-05 10:07:35 +00:00
if ( cvar ( " navmesh_edit_mode " ) ) {
cvar_set ( " navmesh_edit_mode " , " 0 " ) ;
print ( " cl_navmesh editor: 0 \n " ) ;
}
else {
print ( " cl_navmesh editor: 1 \n " ) ;
cvar_set ( " navmesh_edit_mode " , " 1 " ) ;
if ( selected_verts = = 0 ) {
selected_verts = memalloc ( sizeof ( float ) * NAV_MAX_SELECTED_VERTS ) ;
}
if ( cl_navmesh_verts = = 0 ) {
cl_navmesh_verts = memalloc ( sizeof ( navmesh_vertex ) * NAV_MAX_VERTS ) ;
}
if ( cl_navmesh_polies = = 0 ) {
cl_navmesh_polies = memalloc ( sizeof ( navmesh_poly ) * NAV_MAX_POLIES ) ;
}
2023-02-05 15:04:21 +00:00
if ( cl_navmesh_traversals = = 0 ) {
cl_navmesh_traversals = memalloc ( sizeof ( navmesh_traversal ) * NAV_MAX_TRAVERSALS ) ;
}
2023-02-05 10:07:35 +00:00
// Allocate memory for pathfind result
if ( cl_test_pathfind_result = = 0 ) {
2023-08-01 10:20:35 +00:00
cl_test_pathfind_result = memalloc ( sizeof ( navmesh_pathfind_result ) ) ;
2023-02-05 10:07:35 +00:00
cl_pathfind_clear_result_data ( ) ;
}
cl_navmesh_deselect_all ( ) ;
2023-02-05 15:04:21 +00:00
cl_navmesh_selected_traversal = - 1 ;
cl_navmesh_traversal_edit_mode = false ;
cl_navmesh_traversal_editor_cur_point = 0 ;
2023-02-05 10:07:35 +00:00
}
}
2023-08-08 05:24:15 +00:00
void cl_register_navmesh_commands ( ) {
2023-02-05 10:07:35 +00:00
registercommand ( " nav_editor " ) ;
registercommand ( " nav_place_vert " ) ;
registercommand ( " nav_delete_verts " ) ;
registercommand ( " nav_select_vert " ) ;
registercommand ( " nav_deselect_vert " ) ;
registercommand ( " nav_deselect_all " ) ;
registercommand ( " nav_make_poly " ) ;
registercommand ( " nav_delete_poly " ) ;
registercommand ( " nav_resolve_corner " ) ;
registercommand ( " nav_place_corner " ) ;
registercommand ( " nav_cancel_corner " ) ;
registercommand ( " nav_confirm_corner " ) ;
registercommand ( " nav_calc_connected_polies " ) ;
registercommand ( " nav_calc_poly_centers " ) ;
registercommand ( " navtest_place_goal " ) ;
registercommand ( " navtest_place_start " ) ;
registercommand ( " nav_save_navmesh " ) ;
registercommand ( " nav_clear_navmesh " ) ;
registercommand ( " y " ) ;
registercommand ( " yes " ) ;
registercommand ( " nav_load_navmesh " ) ;
registercommand ( " nav_toggle_poly_door " ) ;
registercommand ( " nav_print_poly_door " ) ;
2023-02-05 15:04:21 +00:00
// --- Traversal Commands ---
registercommand ( " nav_place_traversal " ) ;
registercommand ( " nav_select_traversal " ) ;
registercommand ( " nav_clone_traversal " ) ;
registercommand ( " nav_delete_traversal " ) ;
registercommand ( " nav_edit_traversal " ) ;
registercommand ( " nav_next_traversal_type " ) ;
registercommand ( " nav_prev_traversal_type " ) ;
// --- Traversal Edit Mode Commands ---
registercommand ( " nav_trav_next_point " ) ;
registercommand ( " nav_trav_toggle_point " ) ;
registercommand ( " nav_trav_get_point_pos " ) ;
registercommand ( " nav_trav_set_point_pos " ) ;
registercommand ( " nav_trav_get_angle " ) ;
registercommand ( " nav_trav_set_angle " ) ;
2023-08-08 05:24:15 +00:00
registercommand ( " nav_trav_auto_adjust " ) ;
2023-02-05 10:07:35 +00:00
}
float cl_confirm_clear_navmesh ;
void cl_navmesh_editor_toggle_poly_door ( ) {
int selected_polygon = cl_navmesh_get_selected_poly ( ) ;
if ( selected_polygon = = - 1 ) {
print ( " Can't make door polygon. No polygon selected. \n " ) ;
return ;
}
// strcat will copy the string
string editor_active_door = strcat ( cvar_string ( " nav_editor_active_door " ) ) ;
// If current polygon doortarget isn't set to the editor's currently active door, set it
2023-08-06 05:53:44 +00:00
// if(strcmp(cl_navmesh_polies[selected_polygon].doortarget, editor_active_door) != 0) {
if ( cl_navmesh_polies [ selected_polygon ] . doortarget ! = editor_active_door ) {
2023-02-05 10:07:35 +00:00
cl_navmesh_polies [ selected_polygon ] . doortarget = editor_active_door ;
}
// Otherwise, clear it.
else {
cl_navmesh_polies [ selected_polygon ] . doortarget = " " ;
}
print ( " Selected Polygon doortarget set to: \" " ) ;
print ( cl_navmesh_polies [ selected_polygon ] . doortarget ) ;
print ( " \" \n " ) ;
}
// Print the currently selected polygon's doortarget field
void cl_navmesh_editor_print_poly_door ( ) {
int selected_polygon = cl_navmesh_get_selected_poly ( ) ;
if ( selected_polygon = = - 1 ) {
print ( " Can't print door polygon. No polygon selected. \n " ) ;
return ;
}
print ( " Current selected polygon ( " ) ;
print ( itos ( selected_polygon ) ) ;
print ( " ) doortarget: \" " ) ;
print ( cl_navmesh_polies [ selected_polygon ] . doortarget ) ;
print ( " \" \n " ) ;
}
2023-02-05 15:04:21 +00:00
void cl_navmesh_editor_place_traversal ( ) {
cl_navmesh_traversal_edit_mode = false ;
cl_navmesh_traversal_editor_cur_point = 0 ;
if ( cl_navmesh_traversal_count > = NAV_MAX_TRAVERSALS ) {
print ( " Can't add traversal, max traversal count has been reached. \n " ) ;
return ;
}
// Place the traversal at the player origin
vector player_pos = getentity ( player_localentnum , GE_ORIGIN ) ;
vector player_angles = getentity ( player_localentnum , GE_ANGLES ) ;
cl_navmesh_traversals [ cl_navmesh_traversal_count ] . start_pos . x = player_pos . x ;
cl_navmesh_traversals [ cl_navmesh_traversal_count ] . start_pos . y = player_pos . y ;
cl_navmesh_traversals [ cl_navmesh_traversal_count ] . start_pos . z = player_pos . z ;
// Make traversal face same direction as player
cl_navmesh_traversals [ cl_navmesh_traversal_count ] . angle = player_angles . y ;
// -------------------------------
// Set up default traversal (teleport)
// TODO - Move this to a separate function
// -------------------------------
// X - Right, Y - Forward, Z - up
cl_navmesh_traversals [ cl_navmesh_traversal_count ] . midpoint_pos = ' 0 24 24 ' ;
cl_navmesh_traversals [ cl_navmesh_traversal_count ] . use_midpoint = false ;
cl_navmesh_traversals [ cl_navmesh_traversal_count ] . use_midpoint = true ;
cl_navmesh_traversals [ cl_navmesh_traversal_count ] . end_pos = ' 0 48 0 ' ;
// TODO - somehow set traversal type ID?
// -------------------------------
print ( " Traveral " ) ;
print ( ftos ( cl_navmesh_traversal_count ) ) ;
print ( " created at " , vtos ( player_pos ) , " \n " ) ;
print ( " Angles: " ) ;
print ( vtos ( player_angles ) ) ;
// Immediately select it
cl_navmesh_selected_traversal = cl_navmesh_traversal_count ;
cl_navmesh_traversal_count + + ;
} ;
float cl_navmesh_get_nearest_traversal ( ) {
if ( cl_navmesh_traversal_count < = 0 ) {
return - 1 ;
}
vector player_pos = getentity ( player_localentnum , GE_ORIGIN ) ;
float closest_dist = vlen ( player_pos - cl_navmesh_traversals [ 0 ] . start_pos ) ;
float closest_index = 0 ;
float temp_dist ;
2023-08-08 05:24:15 +00:00
for ( float i = 1 ; i < cl_navmesh_traversal_count ; i + + ) {
// Check traversal start position
2023-02-05 15:04:21 +00:00
temp_dist = vlen ( player_pos - cl_navmesh_traversals [ i ] . start_pos ) ;
2023-08-08 05:24:15 +00:00
if ( temp_dist < closest_dist ) {
closest_dist = temp_dist ;
closest_index = i ;
}
// Also check traversal end position
temp_dist = vlen ( player_pos - cl_navmesh_get_traversal_end_pos ( i ) ) ;
if ( temp_dist < closest_dist ) {
2023-02-05 15:04:21 +00:00
closest_dist = temp_dist ;
closest_index = i ;
}
}
return closest_index ;
}
// Copies a traversal from index src to index dest
void cl_navmesh_editor_copy_traversal ( float src , float dest , float copy_pos , float copy_angle ) {
if ( copy_pos ) {
cl_navmesh_traversals [ dest ] . start_pos = cl_navmesh_traversals [ src ] . start_pos ;
}
if ( copy_angle ) {
cl_navmesh_traversals [ dest ] . angle = cl_navmesh_traversals [ src ] . angle ;
}
cl_navmesh_traversals [ dest ] . end_pos = cl_navmesh_traversals [ src ] . end_pos ;
cl_navmesh_traversals [ dest ] . midpoint_pos = cl_navmesh_traversals [ src ] . midpoint_pos ;
cl_navmesh_traversals [ dest ] . use_midpoint = cl_navmesh_traversals [ src ] . use_midpoint ;
}
// Selects the nearest traversal (if nearest traversal is selcted, deselects it)
void cl_navmesh_editor_select_traversal ( ) {
cl_navmesh_traversal_edit_mode = false ;
cl_navmesh_traversal_editor_cur_point = 0 ;
int trav_idx = cl_navmesh_get_nearest_traversal ( ) ;
if ( trav_idx = = - 1 ) {
print ( " No traversals to select " ) ;
return ;
}
if ( trav_idx = = cl_navmesh_selected_traversal ) {
print ( " Travseral deselected. \n " ) ;
cl_navmesh_selected_traversal = - 1 ;
return ;
}
print ( " Traversal " ) ;
print ( ftos ( trav_idx ) ) ;
print ( " selected. \n " ) ;
cl_navmesh_selected_traversal = trav_idx ;
} ;
void cl_navmesh_editor_clone_traversal ( ) {
cl_navmesh_traversal_edit_mode = false ;
cl_navmesh_traversal_editor_cur_point = 0 ;
if ( cl_navmesh_traversal_count > = NAV_MAX_TRAVERSALS ) {
print ( " Can't clone traversal, max traversal count has been reached. \n " ) ;
return ;
}
float prev_trav = cl_navmesh_selected_traversal ;
cl_navmesh_editor_place_traversal ( ) ;
// place_traversal implicitly updates `cl_navmesh_selected_traversal` to the new one
cl_navmesh_editor_copy_traversal ( prev_trav , cl_navmesh_selected_traversal , false , false ) ;
} ;
void cl_navmesh_editor_delete_traversal ( ) {
cl_navmesh_traversal_edit_mode = false ;
cl_navmesh_traversal_editor_cur_point = 0 ;
if ( cl_navmesh_selected_traversal = = - 1 ) {
print ( " No traversal selected. \n " ) ;
return ;
}
print ( " Deleting traversal: " ) ;
print ( ftos ( cl_navmesh_selected_traversal ) ) ;
print ( " . \n " ) ;
//Bringing all traversals down to not leave any holes in the array
//Moving down every index to the right of what is selected to not leave any holes
for ( float i = cl_navmesh_selected_traversal ; i < cl_navmesh_traversal_count - 1 ; i + + ) {
// Copy traversal (i+1) to traversal (i)
cl_navmesh_editor_copy_traversal ( i + 1 , i , true , true ) ;
}
// TOOD - Clear the last one?
cl_navmesh_traversal_count - - ;
// TODO - Fix any references from polygons -> traversals?
cl_navmesh_selected_traversal = - 1 ;
} ;
void cl_navmesh_editor_edit_traversal ( ) {
if ( cl_navmesh_selected_traversal = = - 1 ) {
print ( " No traversal selected. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_edit_mode ) {
cl_navmesh_traversal_edit_mode = false ;
}
else {
cl_navmesh_traversal_edit_mode = true ;
}
cl_navmesh_traversal_editor_cur_point = 0 ;
} ;
void cl_navmesh_editor_next_traversal_type ( ) {
// TODO - Scroll through list once traversals are defined
} ;
void cl_navmesh_editor_prev_traversal_type ( ) {
// TODO - Scroll through list once traversals are defined
} ;
void cl_navmesh_traversal_editor_next_point ( ) {
if ( cl_navmesh_selected_traversal = = - 1 ) {
print ( " No traversal selected. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_edit_mode = = false ) {
print ( " Not in traversal edit mode. \n " ) ;
return ;
}
cl_navmesh_traversal_editor_cur_point = ( cl_navmesh_traversal_editor_cur_point + 1 ) % 3 ;
} ;
void cl_navmesh_traversal_editor_toggle_point ( ) {
if ( cl_navmesh_selected_traversal = = - 1 ) {
print ( " No traversal selected. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_edit_mode = = false ) {
print ( " Not in traversal edit mode. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_editor_cur_point ! = 1 ) {
print ( " Can only toggle midpoint. \n " ) ;
return ;
}
if ( cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . use_midpoint ) {
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . use_midpoint = false ;
}
else {
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . use_midpoint = true ;
}
} ;
void cl_navmesh_traversal_editor_get_point_pos ( ) {
if ( cl_navmesh_selected_traversal = = - 1 ) {
print ( " No traversal selected. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_edit_mode = = false ) {
print ( " Not in traversal edit mode. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_editor_cur_point = = 0 ) {
print ( " Traversal start point: " ) ;
print ( vtos ( cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . start_pos ) ) ;
print ( " . \n " ) ;
}
else if ( cl_navmesh_traversal_editor_cur_point = = 1 ) {
2023-08-01 10:20:35 +00:00
print ( " Traversal mid point: " ) ;
2023-02-05 15:04:21 +00:00
print ( vtos ( cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . midpoint_pos ) ) ;
2023-08-01 10:20:35 +00:00
print ( " . (relative to start point) \n " ) ;
2023-02-05 15:04:21 +00:00
}
else if ( cl_navmesh_traversal_editor_cur_point = = 2 ) {
2023-08-01 10:20:35 +00:00
print ( " Traversal end point: " ) ;
2023-02-05 15:04:21 +00:00
print ( vtos ( cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . end_pos ) ) ;
2023-08-01 10:20:35 +00:00
print ( " . (relative to start point) \n " ) ;
2023-02-05 15:04:21 +00:00
}
} ;
void cl_navmesh_traversal_editor_set_point_pos ( vector pos ) {
if ( cl_navmesh_selected_traversal = = - 1 ) {
print ( " No traversal selected. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_edit_mode = = false ) {
print ( " Not in traversal edit mode. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_editor_cur_point = = 0 ) {
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . start_pos = pos ;
2023-08-01 10:20:35 +00:00
print ( " Traversal start pos set to: " ) ;
2023-02-05 15:04:21 +00:00
print ( vtos ( cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . start_pos ) ) ;
print ( " . \n " ) ;
}
else if ( cl_navmesh_traversal_editor_cur_point = = 1 ) {
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . midpoint_pos = pos ;
2023-08-01 10:20:35 +00:00
print ( " Traversal mid pos set to: " ) ;
2023-02-05 15:04:21 +00:00
print ( vtos ( cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . midpoint_pos ) ) ;
print ( " . (relative to start pos) \n " ) ;
}
else if ( cl_navmesh_traversal_editor_cur_point = = 2 ) {
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . end_pos = pos ;
2023-08-01 10:20:35 +00:00
print ( " Traversal end pos set to: " ) ;
2023-02-05 15:04:21 +00:00
print ( vtos ( cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . end_pos ) ) ;
print ( " . (relative to start pos) \n " ) ;
}
} ;
void cl_navmesh_traversal_editor_get_angle ( ) {
if ( cl_navmesh_selected_traversal = = - 1 ) {
print ( " No traversal selected. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_edit_mode = = false ) {
print ( " Not in traversal edit mode. \n " ) ;
return ;
}
print ( " Traversal angle: " ) ;
print ( ftos ( cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . angle ) ) ;
print ( " . \n " ) ;
} ;
void cl_navmesh_traversal_editor_set_angle ( float angle ) {
if ( cl_navmesh_selected_traversal = = - 1 ) {
print ( " No traversal selected. \n " ) ;
return ;
}
if ( cl_navmesh_traversal_edit_mode = = false ) {
print ( " Not in traversal edit mode. \n " ) ;
return ;
}
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . angle = angle ;
print ( " Traversal angle set to: " ) ;
print ( ftos ( cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . angle ) ) ;
print ( " . \n " ) ;
} ;
2023-08-08 05:24:15 +00:00
void cl_navmesh_traversal_editor_auto_adjust_traversal ( ) {
if ( cl_navmesh_selected_traversal = = - 1 ) {
return ;
}
2023-02-05 15:04:21 +00:00
2023-08-08 05:24:15 +00:00
vector start_pos = cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . start_pos ;
vector midpoint_pos = cl_navmesh_get_traversal_midpoint_pos ( cl_navmesh_selected_traversal ) ;
vector end_pos = cl_navmesh_get_traversal_end_pos ( cl_navmesh_selected_traversal ) ;
2023-02-05 15:04:21 +00:00
2023-08-08 05:24:15 +00:00
vector ofs = ' 0 0 16 ' ;
tracebox ( start_pos + ofs , VEC_HULL_MIN , VEC_HULL_MAX , start_pos - ofs , TRUE , player ) ;
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . start_pos . z = trace_endpos . z ;
tracebox ( end_pos + ofs , VEC_HULL_MIN , VEC_HULL_MAX , end_pos - ofs , TRUE , player ) ;
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . end_pos . z = trace_endpos . z - start_pos . z ;
2023-02-05 15:04:21 +00:00
2023-02-05 10:07:35 +00:00
2023-08-08 05:24:15 +00:00
// FIXME - Pull from traversal struct
string traversal_type = " ledge " ;
// If "ledge" traversal,
if ( traversal_type = = " ledge " ) {
start_pos = cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . start_pos ;
end_pos = cl_navmesh_get_traversal_end_pos ( cl_navmesh_selected_traversal ) ;
vector top_point ;
vector bottom_point ;
if ( start_pos . z < end_pos . z ) {
bottom_point = start_pos ;
top_point = end_pos ;
}
else {
top_point = start_pos ;
bottom_point = end_pos ;
}
// --------------------------------------------------------------------
// Binary search to find the ledge location
// --------------------------------------------------------------------
vector search_start = top_point ;
vector search_midpoint ;
vector search_end = bottom_point ;
search_end . z = search_start . z ;
// What if there's a wall above the bottom point?
for ( float i = 0 ; i < 6 ; i + + ) {
search_midpoint = ( search_start + search_end ) * 0.5 ;
tracebox ( search_midpoint + ofs , VEC_HULL_MIN , VEC_HULL_MAX , search_midpoint - ofs , TRUE , player ) ;
print ( " -------------------------------- \n " ) ;
print ( " iter: " , ftos ( i ) , " start: " , vtos ( search_start ) , " end: " , vtos ( search_end ) , " mid: " ) ;
print ( vtos ( search_midpoint ) , " frac: " , ftos ( trace_fraction ) , " \n " ) ;
if ( trace_fraction > = 1.0 | | trace_startsolid ) {
search_end = search_midpoint ;
}
else {
search_start = search_midpoint ;
}
}
vector ledge_pos = ( search_start + search_end ) * 0.5 ;
// ledge_pos is the center of the bbox, offset to find the corner near the ledge
// Push 16 qu towards the top point
// NOTE: This won't be accurate for sloped ledges, but we're just
// trying to get close-enough so the animations look half-decent
ledge_pos + = normalize ( top_point - [ bottom_point . x , bottom_point . y , top_point . z ] ) * 16 ;
// Lower by the bbox height to the center
ledge_pos . z + = VEC_HULL_MIN . z ;
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// Offset the traversal such that the top point is 10qu away from the ledge
// --------------------------------------------------------------------
float traversal_length = 50 ; // qu
float start_to_ledge = 10 ; // qu
// First, make sure the end_pos is exactly 50qu away from the start
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . end_pos . x = 0 ;
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . end_pos . y = traversal_length ;
// Compute vector pointing from start towards ledge:
vector delta_pos = start_pos - ledge_pos ;
delta_pos . z = 0 ;
float cur_dist = vlen ( delta_pos ) ;
delta_pos = normalize ( delta_pos ) ;
// If traversal start is at the bottom, move traversal start 40qu away from ledge
if ( start_pos . z < end_pos . z ) {
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . start_pos + = delta_pos * ( ( traversal_length - start_to_ledge ) - cur_dist ) ;
}
// If traversal start is at the top, move traversal start 10qu away from ledge
else {
cl_navmesh_traversals [ cl_navmesh_selected_traversal ] . start_pos + = delta_pos * ( start_to_ledge - cur_dist ) ;
}
// Next, adjust distance between the traversal start and the ledge
// so that it's exactly 25 qu away.
// --------------------------------------------------------------------
}
}
2023-02-05 10:07:35 +00:00
float ( string cmd ) cl_navmesh_console_commands =
{
tokenize ( cmd ) ;
switch ( argv ( 0 ) )
{
case " nav_editor " :
cl_toggle_navmesh_editor ( ) ;
return TRUE ;
default :
break ;
}
//nav_clear_navmesh must be run twice consecutively to execute it (for safety)
switch ( argv ( 0 ) )
{
case " nav_clear_navmesh " :
cl_confirm_clear_navmesh = 1 ;
print ( " Are you sure you want to clear the navmesh? All unsaved changes will be lost. \n " ) ;
return TRUE ;
case " y " :
case " yes " :
if ( cl_confirm_clear_navmesh = = 1 )
{
print ( " Navmesh cleared. \n " ) ;
cl_navmesh_editor_clear_navmesh ( ) ;
}
cl_confirm_clear_navmesh = 0 ;
return TRUE ;
default :
cl_confirm_clear_navmesh = 0 ;
break ;
}
if ( ! cvar ( " navmesh_edit_mode " ) )
return FALSE ;
switch ( argv ( 0 ) )
{
case " nav_place_vert " :
cl_navmesh_place_vert ( ) ;
return TRUE ;
case " nav_delete_verts " :
cl_navmesh_delete_verts ( ) ;
return TRUE ;
case " nav_select_vert " :
cl_navmesh_select_vert ( ) ;
return TRUE ;
case " nav_deselect_vert " :
cl_navmesh_deselect_vert ( ) ;
return TRUE ;
case " nav_deselect_all " :
cl_navmesh_deselect_all ( ) ;
print ( " All vertices deselected. \n " ) ;
return TRUE ;
case " nav_make_poly " :
cl_navmesh_make_poly ( ) ;
return TRUE ;
case " nav_delete_poly " :
cl_navmesh_delete_poly ( ) ;
return TRUE ;
case " nav_resolve_corner " :
cl_navmesh_resolve_corner ( ) ;
return TRUE ;
case " nav_place_corner " :
cl_navmesh_place_corner ( ) ;
return TRUE ;
case " nav_cancel_corner " :
cl_navmesh_cancel_corner ( ) ;
return TRUE ;
case " nav_confirm_corner " :
cl_navmesh_confirm_corner ( ) ;
return TRUE ;
case " nav_calc_connected_polies " :
cl_navmesh_calc_connected_polies ( ) ;
return TRUE ;
case " nav_calc_poly_centers " :
cl_navmesh_calc_polies_centers ( ) ;
return TRUE ;
case " navtest_place_goal " :
cl_navmesh_place_test_goalent ( ) ;
return TRUE ;
case " navtest_place_start " :
cl_navmesh_place_test_startent ( ) ;
return TRUE ;
case " nav_save_navmesh " :
cl_navmesh_editor_save_navmesh ( ) ;
return TRUE ;
case " nav_load_navmesh " :
cl_navmesh_editor_load_navmesh ( ) ;
return TRUE ;
case " nav_toggle_poly_door " :
cl_navmesh_editor_toggle_poly_door ( ) ;
return TRUE ;
case " nav_print_poly_door " :
cl_navmesh_editor_print_poly_door ( ) ;
return TRUE ;
2023-02-05 15:04:21 +00:00
case " nav_place_traversal " :
cl_navmesh_editor_place_traversal ( ) ;
return TRUE ;
case " nav_select_traversal " :
cl_navmesh_editor_select_traversal ( ) ;
return TRUE ;
case " nav_clone_traversal " :
cl_navmesh_editor_clone_traversal ( ) ;
return TRUE ;
case " nav_delete_traversal " :
cl_navmesh_editor_delete_traversal ( ) ;
return TRUE ;
case " nav_edit_traversal " :
cl_navmesh_editor_edit_traversal ( ) ;
return TRUE ;
case " nav_next_traversal_type " :
cl_navmesh_editor_next_traversal_type ( ) ;
return TRUE ;
case " nav_prev_traversal_type " :
cl_navmesh_editor_prev_traversal_type ( ) ;
return TRUE ;
case " nav_trav_next_point " :
cl_navmesh_traversal_editor_next_point ( ) ;
return TRUE ;
case " nav_trav_toggle_point " :
cl_navmesh_traversal_editor_toggle_point ( ) ;
return TRUE ;
case " nav_trav_get_point_pos " :
cl_navmesh_traversal_editor_get_point_pos ( ) ;
return TRUE ;
case " nav_trav_set_point_pos " :
cl_navmesh_traversal_editor_set_point_pos ( stov ( argv ( 1 ) ) ) ;
return TRUE ;
case " nav_trav_get_angle " :
cl_navmesh_traversal_editor_get_angle ( ) ;
return TRUE ;
case " nav_trav_set_angle " :
cl_navmesh_traversal_editor_set_angle ( stof ( argv ( 1 ) ) ) ;
return TRUE ;
2023-08-08 05:24:15 +00:00
case " nav_trav_auto_adjust " :
cl_navmesh_traversal_editor_auto_adjust_traversal ( ) ;
return TRUE ;
2023-02-05 10:07:35 +00:00
default :
break ;
}
return FALSE ;
}