/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_debug.c * * desc: AAS debug code * * $Archive: /MissionPack/code/botlib/be_aas_debug.c $ * *****************************************************************************/ #include "../qcommon/q_shared.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_libvar.h" #include "aasfile.h" #include "botlib.h" #include "be_aas.h" #include "be_interface.h" #include "be_aas_funcs.h" #include "be_aas_def.h" #define MAX_DEBUGLINES 1024 #define MAX_DEBUGPOLYGONS 8192 static int debuglines[MAX_DEBUGLINES]; static int debuglinevisible[MAX_DEBUGLINES]; static int numdebuglines; static int debugpolygons[MAX_DEBUGPOLYGONS]; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ClearShownPolygons(void) { int i; //* for (i = 0; i < MAX_DEBUGPOLYGONS; i++) { if (debugpolygons[i]) botimport.DebugPolygonDelete(debugpolygons[i]); debugpolygons[i] = 0; } //end for //*/ /* for (i = 0; i < MAX_DEBUGPOLYGONS; i++) { botimport.DebugPolygonDelete(i); debugpolygons[i] = 0; } //end for */ } //end of the function AAS_ClearShownPolygons //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_ShowPolygon(int color, int numpoints, vec3_t *points) { int i; for (i = 0; i < MAX_DEBUGPOLYGONS; i++) { if (!debugpolygons[i]) { debugpolygons[i] = botimport.DebugPolygonCreate(color, numpoints, points); break; } //end if } //end for } //end of the function AAS_ShowPolygon //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ClearShownDebugLines(void) { int i; //make all lines invisible for (i = 0; i < MAX_DEBUGLINES; i++) { if (debuglines[i]) { //botimport.DebugLineShow(debuglines[i], NULL, NULL, LINECOLOR_NONE); botimport.DebugLineDelete(debuglines[i]); debuglines[i] = 0; debuglinevisible[i] = qfalse; } //end if } //end for } //end of the function AAS_ClearShownDebugLines //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DebugLine(vec3_t start, vec3_t end, int color) { int line; for (line = 0; line < MAX_DEBUGLINES; line++) { if (!debuglines[line]) { debuglines[line] = botimport.DebugLineCreate(); debuglinevisible[line] = qfalse; numdebuglines++; } //end if if (!debuglinevisible[line]) { botimport.DebugLineShow(debuglines[line], start, end, color); debuglinevisible[line] = qtrue; return; } //end else } //end for } //end of the function AAS_DebugLine //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_PermanentLine(vec3_t start, vec3_t end, int color) { int line; line = botimport.DebugLineCreate(); botimport.DebugLineShow(line, start, end, color); } //end of the function AAS_PermenentLine //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DrawPermanentCross(vec3_t origin, float size, int color) { int i, debugline; vec3_t start, end; for (i = 0; i < 3; i++) { VectorCopy(origin, start); start[i] += size; VectorCopy(origin, end); end[i] -= size; AAS_DebugLine(start, end, color); debugline = botimport.DebugLineCreate(); botimport.DebugLineShow(debugline, start, end, color); } //end for } //end of the function AAS_DrawPermanentCross //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color) { int n0, n1, n2, j, line, lines[2]; vec3_t start1, end1, start2, end2; //make a cross in the hit plane at the hit point VectorCopy(point, start1); VectorCopy(point, end1); VectorCopy(point, start2); VectorCopy(point, end2); n0 = type % 3; n1 = (type + 1) % 3; n2 = (type + 2) % 3; start1[n1] -= 6; start1[n2] -= 6; end1[n1] += 6; end1[n2] += 6; start2[n1] += 6; start2[n2] -= 6; end2[n1] -= 6; end2[n2] += 6; start1[n0] = (dist - (start1[n1] * normal[n1] + start1[n2] * normal[n2])) / normal[n0]; end1[n0] = (dist - (end1[n1] * normal[n1] + end1[n2] * normal[n2])) / normal[n0]; start2[n0] = (dist - (start2[n1] * normal[n1] + start2[n2] * normal[n2])) / normal[n0]; end2[n0] = (dist - (end2[n1] * normal[n1] + end2[n2] * normal[n2])) / normal[n0]; for (j = 0, line = 0; j < 2 && line < MAX_DEBUGLINES; line++) { if (!debuglines[line]) { debuglines[line] = botimport.DebugLineCreate(); lines[j++] = debuglines[line]; debuglinevisible[line] = qtrue; numdebuglines++; } //end if else if (!debuglinevisible[line]) { lines[j++] = debuglines[line]; debuglinevisible[line] = qtrue; } //end else } //end for botimport.DebugLineShow(lines[0], start1, end1, color); botimport.DebugLineShow(lines[1], start2, end2, color); } //end of the function AAS_DrawPlaneCross //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs) { vec3_t bboxcorners[8]; int lines[3]; int i, j, line; //upper corners bboxcorners[0][0] = origin[0] + maxs[0]; bboxcorners[0][1] = origin[1] + maxs[1]; bboxcorners[0][2] = origin[2] + maxs[2]; // bboxcorners[1][0] = origin[0] + mins[0]; bboxcorners[1][1] = origin[1] + maxs[1]; bboxcorners[1][2] = origin[2] + maxs[2]; // bboxcorners[2][0] = origin[0] + mins[0]; bboxcorners[2][1] = origin[1] + mins[1]; bboxcorners[2][2] = origin[2] + maxs[2]; // bboxcorners[3][0] = origin[0] + maxs[0]; bboxcorners[3][1] = origin[1] + mins[1]; bboxcorners[3][2] = origin[2] + maxs[2]; //lower corners Com_Memcpy(bboxcorners[4], bboxcorners[0], sizeof(vec3_t) * 4); for (i = 0; i < 4; i++) bboxcorners[4 + i][2] = origin[2] + mins[2]; //draw bounding box for (i = 0; i < 4; i++) { for (j = 0, line = 0; j < 3 && line < MAX_DEBUGLINES; line++) { if (!debuglines[line]) { debuglines[line] = botimport.DebugLineCreate(); lines[j++] = debuglines[line]; debuglinevisible[line] = qtrue; numdebuglines++; } //end if else if (!debuglinevisible[line]) { lines[j++] = debuglines[line]; debuglinevisible[line] = qtrue; } //end else } //end for //top plane botimport.DebugLineShow(lines[0], bboxcorners[i], bboxcorners[(i+1)&3], LINECOLOR_RED); //bottom plane botimport.DebugLineShow(lines[1], bboxcorners[4+i], bboxcorners[4+((i+1)&3)], LINECOLOR_RED); //vertical lines botimport.DebugLineShow(lines[2], bboxcorners[i], bboxcorners[4+i], LINECOLOR_RED); } //end for } //end of the function AAS_ShowBoundingBox //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowFace(int facenum) { int i, color, edgenum; aas_edge_t *edge; aas_face_t *face; aas_plane_t *plane; vec3_t start, end; color = LINECOLOR_YELLOW; //check if face number is in range if (facenum >= aasworld.numfaces) { botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); } //end if face = &aasworld.faces[facenum]; //walk through the edges of the face for (i = 0; i < face->numedges; i++) { //edge number edgenum = abs(aasworld.edgeindex[face->firstedge + i]); //check if edge number is in range if (edgenum >= aasworld.numedges) { botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); } //end if edge = &aasworld.edges[edgenum]; if (color == LINECOLOR_RED) color = LINECOLOR_GREEN; else if (color == LINECOLOR_GREEN) color = LINECOLOR_BLUE; else if (color == LINECOLOR_BLUE) color = LINECOLOR_YELLOW; else color = LINECOLOR_RED; AAS_DebugLine(aasworld.vertexes[edge->v[0]], aasworld.vertexes[edge->v[1]], color); } //end for plane = &aasworld.planes[face->planenum]; edgenum = abs(aasworld.edgeindex[face->firstedge]); edge = &aasworld.edges[edgenum]; VectorCopy(aasworld.vertexes[edge->v[0]], start); VectorMA(start, 20, plane->normal, end); AAS_DebugLine(start, end, LINECOLOR_RED); } //end of the function AAS_ShowFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_ShowFacePolygon(int facenum, int color, int flip) { int i, edgenum, numpoints; vec3_t points[128]; aas_edge_t *edge; aas_face_t *face; //check if face number is in range if (facenum >= aasworld.numfaces) { botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); } //end if face = &aasworld.faces[facenum]; //walk through the edges of the face numpoints = 0; if (flip) { for (i = face->numedges-1; i >= 0; i--) { //edge number edgenum = aasworld.edgeindex[face->firstedge + i]; edge = &aasworld.edges[abs(edgenum)]; VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); numpoints++; } //end for } //end if else { for (i = 0; i < face->numedges; i++) { //edge number edgenum = aasworld.edgeindex[face->firstedge + i]; edge = &aasworld.edges[abs(edgenum)]; VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); numpoints++; } //end for } //end else AAS_ShowPolygon(color, numpoints, points); } //end of the function AAS_ShowFacePolygon //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowArea(int areanum, int groundfacesonly) { int areaedges[MAX_DEBUGLINES]; int numareaedges, i, j, n, color = 0, line; int facenum, edgenum; aas_area_t *area; aas_face_t *face; aas_edge_t *edge; // numareaedges = 0; // if (areanum < 0 || areanum >= aasworld.numareas) { botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", areanum, aasworld.numareas); return; } //end if //pointer to the convex area area = &aasworld.areas[areanum]; //walk through the faces of the area for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); //check if face number is in range if (facenum >= aasworld.numfaces) { botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); } //end if face = &aasworld.faces[facenum]; //ground faces only if (groundfacesonly) { if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; } //end if //walk through the edges of the face for (j = 0; j < face->numedges; j++) { //edge number edgenum = abs(aasworld.edgeindex[face->firstedge + j]); //check if edge number is in range if (edgenum >= aasworld.numedges) { botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); } //end if //check if the edge is stored already for (n = 0; n < numareaedges; n++) { if (areaedges[n] == edgenum) break; } //end for if (n == numareaedges && numareaedges < MAX_DEBUGLINES) { areaedges[numareaedges++] = edgenum; } //end if } //end for //AAS_ShowFace(facenum); } //end for //draw all the edges for (n = 0; n < numareaedges; n++) { for (line = 0; line < MAX_DEBUGLINES; line++) { if (!debuglines[line]) { debuglines[line] = botimport.DebugLineCreate(); debuglinevisible[line] = qfalse; numdebuglines++; } //end if if (!debuglinevisible[line]) { break; } //end else } //end for if (line >= MAX_DEBUGLINES) return; edge = &aasworld.edges[areaedges[n]]; if (color == LINECOLOR_RED) color = LINECOLOR_BLUE; else if (color == LINECOLOR_BLUE) color = LINECOLOR_GREEN; else if (color == LINECOLOR_GREEN) color = LINECOLOR_YELLOW; else color = LINECOLOR_RED; botimport.DebugLineShow(debuglines[line], aasworld.vertexes[edge->v[0]], aasworld.vertexes[edge->v[1]], color); debuglinevisible[line] = qtrue; } //end for*/ } //end of the function AAS_ShowArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly) { int i, facenum; aas_area_t *area; aas_face_t *face; // if (areanum < 0 || areanum >= aasworld.numareas) { botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", areanum, aasworld.numareas); return; } //end if //pointer to the convex area area = &aasworld.areas[areanum]; //walk through the faces of the area for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); //check if face number is in range if (facenum >= aasworld.numfaces) { botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); } //end if face = &aasworld.faces[facenum]; //ground faces only if (groundfacesonly) { if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; } //end if AAS_ShowFacePolygon(facenum, color, face->frontarea != areanum); } //end for } //end of the function AAS_ShowAreaPolygons //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DrawCross(vec3_t origin, float size, int color) { int i; vec3_t start, end; for (i = 0; i < 3; i++) { VectorCopy(origin, start); start[i] += size; VectorCopy(origin, end); end[i] -= size; AAS_DebugLine(start, end, color); } //end for } //end of the function AAS_DrawCross //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_PrintTravelType(int traveltype) { #ifdef DEBUG char *str; // switch(traveltype & TRAVELTYPE_MASK) { case TRAVEL_INVALID: str = "TRAVEL_INVALID"; break; case TRAVEL_WALK: str = "TRAVEL_WALK"; break; case TRAVEL_CROUCH: str = "TRAVEL_CROUCH"; break; case TRAVEL_BARRIERJUMP: str = "TRAVEL_BARRIERJUMP"; break; case TRAVEL_JUMP: str = "TRAVEL_JUMP"; break; case TRAVEL_LADDER: str = "TRAVEL_LADDER"; break; case TRAVEL_WALKOFFLEDGE: str = "TRAVEL_WALKOFFLEDGE"; break; case TRAVEL_SWIM: str = "TRAVEL_SWIM"; break; case TRAVEL_WATERJUMP: str = "TRAVEL_WATERJUMP"; break; case TRAVEL_TELEPORT: str = "TRAVEL_TELEPORT"; break; case TRAVEL_ELEVATOR: str = "TRAVEL_ELEVATOR"; break; case TRAVEL_ROCKETJUMP: str = "TRAVEL_ROCKETJUMP"; break; case TRAVEL_BFGJUMP: str = "TRAVEL_BFGJUMP"; break; case TRAVEL_GRAPPLEHOOK: str = "TRAVEL_GRAPPLEHOOK"; break; case TRAVEL_JUMPPAD: str = "TRAVEL_JUMPPAD"; break; case TRAVEL_FUNCBOB: str = "TRAVEL_FUNCBOB"; break; default: str = "UNKNOWN TRAVEL TYPE"; break; } //end switch botimport.Print(PRT_MESSAGE, "%s", str); #endif } //end of the function AAS_PrintTravelType //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor) { vec3_t dir, cross, p1, p2, up = {0, 0, 1}; float dot; VectorSubtract(end, start, dir); VectorNormalize(dir); dot = DotProduct(dir, up); if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0); else CrossProduct(dir, up, cross); VectorMA(end, -6, dir, p1); VectorCopy(p1, p2); VectorMA(p1, 6, cross, p1); VectorMA(p2, -6, cross, p2); AAS_DebugLine(start, end, linecolor); AAS_DebugLine(p1, end, arrowcolor); AAS_DebugLine(p2, end, arrowcolor); } //end of the function AAS_DrawArrow //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowReachability(aas_reachability_t *reach) { vec3_t dir, cmdmove, velocity; float speed, zvel; aas_clientmove_t move; AAS_ShowAreaPolygons(reach->areanum, 5, qtrue); //AAS_ShowArea(reach->areanum, qtrue); AAS_DrawArrow(reach->start, reach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); // if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP || (reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) { AAS_HorizontalVelocityForJump(aassettings.phys_jumpvel, reach->start, reach->end, &speed); // VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; VectorNormalize(dir); //set the velocity VectorScale(dir, speed, velocity); //set the command movement VectorClear(cmdmove); cmdmove[2] = aassettings.phys_jumpvel; // AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 3, 30, 0.1f, SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE, 0, qtrue); // if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) { AAS_JumpReachRunStart(reach, dir); AAS_DrawCross(dir, 4, LINECOLOR_BLUE); } //end if } //end if else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) { zvel = AAS_RocketJumpZVelocity(reach->start); AAS_HorizontalVelocityForJump(zvel, reach->start, reach->end, &speed); // VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; VectorNormalize(dir); //get command movement VectorScale(dir, speed, cmdmove); VectorSet(velocity, 0, 0, zvel); // AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 30, 30, 0.1f, SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE| SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); } //end else if else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) { VectorSet(cmdmove, 0, 0, 0); // VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; VectorNormalize(dir); //set the velocity //NOTE: the edgenum is the horizontal velocity VectorScale(dir, reach->edgenum, velocity); //NOTE: the facenum is the Z velocity velocity[2] = reach->facenum; // AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 30, 30, 0.1f, SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE| SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); } //end else if } //end of the function AAS_ShowReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowReachableAreas(int areanum) { aas_areasettings_t *settings; static aas_reachability_t reach; static int index, lastareanum; static float lasttime; if (areanum != lastareanum) { index = 0; lastareanum = areanum; } //end if settings = &aasworld.areasettings[areanum]; // if (!settings->numreachableareas) return; // if (index >= settings->numreachableareas) index = 0; // if (AAS_Time() - lasttime > 1.5) { Com_Memcpy(&reach, &aasworld.reachability[settings->firstreachablearea + index], sizeof(aas_reachability_t)); index++; lasttime = AAS_Time(); AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); botimport.Print(PRT_MESSAGE, "\n"); } //end if AAS_ShowReachability(&reach); } //end of the function AAS_ShowReachableAreas static void AAS_FloodAreas_r(int areanum, int cluster, int *done) { int nextareanum, i, facenum; aas_area_t *area; aas_face_t *face; aas_areasettings_t *settings; aas_reachability_t *reach; AAS_ShowAreaPolygons(areanum, 1, qtrue); //pointer to the convex area area = &aasworld.areas[areanum]; settings = &aasworld.areasettings[areanum]; //walk through the faces of the area for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); face = &aasworld.faces[facenum]; if (face->frontarea == areanum) nextareanum = face->backarea; else nextareanum = face->frontarea; if (!nextareanum) continue; if (done[nextareanum]) continue; done[nextareanum] = qtrue; if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) continue; if (AAS_AreaCluster(nextareanum) != cluster) continue; AAS_FloodAreas_r(nextareanum, cluster, done); } //end for // for (i = 0; i < settings->numreachableareas; i++) { reach = &aasworld.reachability[settings->firstreachablearea + i]; nextareanum = reach->areanum; if (!nextareanum) continue; if (done[nextareanum]) continue; done[nextareanum] = qtrue; if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) continue; if (AAS_AreaCluster(nextareanum) != cluster) continue; /* if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) { AAS_DebugLine(reach->start, reach->end, 1); } */ AAS_FloodAreas_r(nextareanum, cluster, done); } } void AAS_FloodAreas(vec3_t origin) { int areanum, cluster, *done; done = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); areanum = AAS_PointAreaNum(origin); cluster = AAS_AreaCluster(areanum); AAS_FloodAreas_r(areanum, cluster, done); FreeMemory(done); }