diff --git a/compile_navmesh.py b/compile_navmesh.py index 2091722..d2826d5 100644 --- a/compile_navmesh.py +++ b/compile_navmesh.py @@ -5,8 +5,14 @@ _, inpath, outpath, basename = sys.argv tree = lxml.etree.parse(inpath) -tris = [] -edge2tri = {} +polys = [] +edge2poly = {} + +# Polygons must be convex to work correctly, but we don't verify that + +def points_to_edges(points): + points = points + [points[0]] + return zip(points[:-1], points[1:]) for polygon in tree.findall("objectgroup[@name='navmesh']/object/polygon"): basex = int(polygon.getparent().attrib["x"]) @@ -26,33 +32,32 @@ for polygon in tree.findall("objectgroup[@name='navmesh']/object/polygon"): cross = abx*acy-acx*aby # positive for clockwise winding order. Example clockwise winding order: (0,0), (1,0), (0,1) if cross < 0: - points = points[0],points[2],points[1] - bx,by,cx,cy=cx,cy,bx,by - cross = -cross + points = points[::-1] - assert len(points)==3, "triangles only" - tri_id = len(tris) - tris.append(points) + poly_id = len(polys) + polys.append(points) - for edge in [(points[0],points[1]), (points[1],points[2]), (points[2],points[0])]: + print(points) + for edge in points_to_edges(points): edge_id = tuple(sorted(edge)) - if edge_id not in edge2tri: edge2tri[edge_id] = [] - edge2tri[edge_id].append(tri_id) - assert len(edge2tri[edge_id]) <= 2, "more than 2 triangles share edge "+str(edge_id) + print(edge, edge_id) + if edge_id not in edge2poly: edge2poly[edge_id] = [] + edge2poly[edge_id].append(poly_id) + assert len(edge2poly[edge_id]) <= 2, "more than 2 polygons share edge "+str(edge_id) -pathfind_edges = [[] for _ in range(len(tris))] -for edgetris in edge2tri.values(): - assert len(edgetris) in (1,2) - if len(edgetris) == 1: continue - t1,t2 = edgetris - pathfind_edges[t1].append(t2) - pathfind_edges[t2].append(t1) +pathfind_edges = [[] for _ in range(len(polys))] +for edgepolys in edge2poly.values(): + assert len(edgepolys) in (1,2) + if len(edgepolys) == 1: continue + p1,p2 = edgepolys + pathfind_edges[p1].append(p2) + pathfind_edges[p2].append(p1) -pathfind_matrix = [[255 for _1 in range(len(tris))] for _2 in range(len(tris))] +pathfind_matrix = [[255 for _1 in range(len(polys))] for _2 in range(len(polys))] # pathfind_matrix[source][dest] is how to get to dest from source -for dest in range(len(tris)): - # Find paths to tri by breadth-first search. We don't take the size of the triangle into account yet. +for dest in range(len(polys)): + # Find paths to poly by breadth-first search. We don't take the size of the polygon into account yet. openset = [dest] while len(openset)>0: cur, openset = openset[0], openset[1:] @@ -66,11 +71,13 @@ for dest in range(len(tris)): out = open(outpath,"w") out.write("#include \"compiled_structures.h\"\n") -out.write(f"static const struct navmesh_tri {basename}_triangles[{len(tris)}] = {{\n"); -for tri_id,points in enumerate(tris): - out.write("\t{\n\t\t{\n"); - for a,b,c in [(points[0],points[1],points[2]), (points[1],points[2],points[0]), (points[2],points[0],points[1])]: - (ax,ay),(bx,by),(cx,cy) = a,b,c +out.write(f"static const struct navmesh_tri {basename}_triangles[{len(polys)}] = {{\n"); +for poly_id,points in enumerate(polys): + out.write("\t{\n") + out.write("\t\t"+str(len(points))+",\n") + out.write("\t\t(const struct navmesh_tri_edge[]){\n") + for a,b in points_to_edges(points): + (ax,ay),(bx,by) = a,b # edge a-b and c is the other point # Line equation Px+Qy+R=0 from https://math.stackexchange.com/questions/422602/convert-two-points-to-line-eq-ax-by-c-0 P,Q,R=ay-by,bx-ax,ax*by-bx*ay @@ -82,29 +89,29 @@ for tri_id,points in enumerate(tris): R *= norm edge_id = tuple(sorted((a,b))) - tris_on_edge = edge2tri[edge_id] - assert len(tris_on_edge) in (1,2) - assert tri_id in tris_on_edge - if len(tris_on_edge) == 2: - other_tri_id = [x for x in tris_on_edge if x!=tri_id][0] + polys_on_edge = edge2poly[edge_id] + assert len(polys_on_edge) in (1,2) + assert poly_id in polys_on_edge + if len(polys_on_edge) == 2: + other_poly_id = [x for x in polys_on_edge if x!=poly_id][0] else: - other_tri_id = -1 + other_poly_id = -1 centx, centy = (ax+bx)/2, (ay+by)/2 - out.write(f"\t\t\t{{{P},{Q},{R},{other_tri_id},{{{int(centx)},{int(centy)}}}}},\n") + out.write(f"\t\t\t{{{P},{Q},{R},{other_poly_id},{{{int(centx)},{int(centy)}}}}},\n") out.write("\t\t},\n") - out.write("\t\t{" + ",".join(f"{{{x},{y}}}" for x,y in points) + "},\n") + out.write("\t\t(const struct navmesh_point[]){" + ",".join(f"{{{x},{y}}}" for x,y in points) + "},\n") out.write("\t},\n"); out.write("};\n") -out.write(f"static const unsigned char {basename}_pathfind[{len(tris)*len(tris)}] = {{\n") -for dest in range(len(tris)): +out.write(f"static const unsigned char {basename}_pathfind[{len(polys)*len(polys)}] = {{\n") +for dest in range(len(polys)): out.write("\t") - for source in range(len(tris)): + for source in range(len(polys)): out.write(str(pathfind_matrix[source][dest])+",") out.write("\n") out.write("};\n") -out.write(f"extern const struct navmesh {basename} = {{{len(tris)}, {basename}_triangles, {basename}_pathfind}};\n") +out.write(f"extern const struct navmesh {basename} = {{{len(polys)}, {basename}_triangles, {basename}_pathfind}};\n") diff --git a/compiled_structures.h b/compiled_structures.h index d772d2c..14d0e55 100644 --- a/compiled_structures.h +++ b/compiled_structures.h @@ -11,8 +11,10 @@ struct navmesh_tri_edge { struct navmesh_point center; // intermediate point for use in routing. could be improved later. }; struct navmesh_tri { - struct navmesh_tri_edge edges[3]; - struct navmesh_point points[3]; + int num_verts; + const struct navmesh_tri_edge *edges; + const struct navmesh_point *points; + // For a triangle: // edges[0]: points[0]-points[1] // edges[1]: points[1]-points[2] // edges[2]: points[2]-points[0] diff --git a/game.cpp b/game.cpp index e8c72ca..d7abd6c 100644 --- a/game.cpp +++ b/game.cpp @@ -44,20 +44,22 @@ static void transition_scene_on_walk_finish(struct script *scr, int wakeupMode, assert(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED); } +static void start_player_walk_to_point_then_transition_scene(int x, int y, int scene) { + start_player_walk_to_point(x, y); + + struct script *scr = scene_add_script(OBJID_PLAYER_WALK_TO_DOOR_SCRIPT, true); + scr->wakeupMode = SCRIPT_WAKEUP_OTHER_SCRIPT; + scr->wakeupArg1 = OBJID_PLAYER_WALK_SCRIPT; + scr->wakeupFn = transition_scene_on_walk_finish; + scr->vars[0] = scene; +} + void onclick(int curscene, int objid) { switch(curscene) { case SCENE_LOBBY: switch(objid) { case OBJID_DOOR_TO_MANAGERS_OFFICE_FROM_LOBBY: - { - start_player_walk_to_point(312, 441); - - struct script *scr = scene_add_script(OBJID_PLAYER_WALK_TO_DOOR_SCRIPT, true); - scr->wakeupMode = SCRIPT_WAKEUP_OTHER_SCRIPT; - scr->wakeupArg1 = OBJID_PLAYER_WALK_SCRIPT; - scr->wakeupFn = transition_scene_on_walk_finish; - scr->vars[0] = SCENE_MANAGERS_OFFICE; - } + start_player_walk_to_point_then_transition_scene(312, 441, SCENE_MANAGERS_OFFICE); return; } break; diff --git a/navmesh/lobby.tmx b/navmesh/lobby.tmx index a1682c4..8e70227 100644 --- a/navmesh/lobby.tmx +++ b/navmesh/lobby.tmx @@ -41,28 +41,22 @@ - - - - + - + - - - - + - + - + - + diff --git a/navmesh/managers_office.tmx b/navmesh/managers_office.tmx new file mode 100644 index 0000000..ce971d6 --- /dev/null +++ b/navmesh/managers_office.tmx @@ -0,0 +1,86 @@ + + + + +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pinetab2_framework.cpp b/pinetab2_framework.cpp index 63861bf..446b7b6 100644 --- a/pinetab2_framework.cpp +++ b/pinetab2_framework.cpp @@ -228,7 +228,7 @@ static void closest_point_inside_tri(int *x, int *y, const struct navmesh_tri *t // If this happens, we must be in a corner region. // Repeated normal-vector moves don't necessarily bring us to the corner, but we can just hardcode the corner. for(int j = 0; j < 2; j++) { - for(int i = 0; i < 3; i++) { + for(int i = 0; i < tri->num_verts; i++) { double edgeval = fx*tri->edges[i].a + fy*tri->edges[i].b + tri->edges[i].c; if(edgeval <= 0) { // edge.a,b is normalized so that moving 1 unit of a and 1 unit of b changes edgeval by 1. @@ -239,6 +239,16 @@ static void closest_point_inside_tri(int *x, int *y, const struct navmesh_tri *t } } + for(int i = 0; i < tri->num_verts; i++) { + int next = (i+1) % tri->num_verts; + int mask = (1 << i) | (1 << next); + if((edgeHitMask & mask) == mask) { + *x = tri->points[next].x; + *y = tri->points[next].y; + return; + } + } +/* switch(edgeHitMask) { case 3: // hit edge 0-1 and 1-2 *x = tri->points[1].x; @@ -252,7 +262,7 @@ static void closest_point_inside_tri(int *x, int *y, const struct navmesh_tri *t *x = tri->points[2].x; *y = tri->points[2].y; return; - } + }*/ // may be slightly outside of triangle due to rounding errors, but good enough for us *x = (int)(fx + 0.5); @@ -267,16 +277,33 @@ static double point_to_point_dist(int x, int y, const struct navmesh_point *pt) // Returns distance from triangle, 0 if inside. static double is_point_in_tri(int x, int y, const struct navmesh_tri *tri) { - double edgedist[3]; // positive if outside + /*double edgedist[3]; // positive if outside for(int edge = 0; edge < 3; edge++) edgedist[edge] = -(x*tri->edges[edge].a + y*tri->edges[edge].b + tri->edges[edge].c); if(edgedist[0] < 0 && edgedist[1] < 0 && edgedist[2] < 0) return 0; // inside - if(edgedist[0] >= 0 && edgedist[1] >= 0) return point_to_point_dist(x, y, &tri->points[0]); + if(edgedist[0] >= 0 && edgedist[1] >= 0) return point_to_point_dist(x, y, &tri->points[0]); // are these correct? shouldn't it be points 1,2,0? if(edgedist[1] >= 0 && edgedist[2] >= 0) return point_to_point_dist(x, y, &tri->points[1]); if(edgedist[2] >= 0 && edgedist[0] >= 0) return point_to_point_dist(x, y, &tri->points[2]); if(edgedist[0] >= 0) return edgedist[0]; if(edgedist[1] >= 0) return edgedist[1]; - if(edgedist[2] >= 0) return edgedist[2]; + if(edgedist[2] >= 0) return edgedist[2];*/ + + double edgedist[tri->num_verts]; + bool all_inside = true; + for(int edge = 0; edge < tri->num_verts; edge++) { + edgedist[edge] = -(x*tri->edges[edge].a + y*tri->edges[edge].b + tri->edges[edge].c); + if(edgedist[edge] > 0) + all_inside = false; + } + if(all_inside) return 0; + for(int edge = 0; edge < tri->num_verts; edge++) { + int next = (edge+1)%tri->num_verts; + if(edgedist[edge] >= 0 && edgedist[next] >= 0) return point_to_point_dist(x, y, &tri->points[next]); + } + for(int edge = 0; edge < tri->num_verts; edge++) + if(edgedist[edge] >= 0) + return edgedist[edge]; + return 0; // branch should be unreachable; maybe within epsilon of triangle border? } static int find_navmesh_tri(int x, int y, int tolerance) { @@ -378,7 +405,7 @@ static void update_player_walk_script_on_frame(struct script *scr, int wakeupMod } else { nextTri = cur_navmesh->pathfindgrid[player_walk_script->targetNavmeshTri * cur_navmesh->num_tris + nmtri]; nextX = -1; - for(int edge = 0; edge < 3; edge++) { + for(int edge = 0; edge < cur_navmesh->tris[nmtri].num_verts; edge++) { if(cur_navmesh->tris[nmtri].edges[edge].other_tri == nextTri) { nextX = cur_navmesh->tris[nmtri].edges[edge].center.x; nextY = cur_navmesh->tris[nmtri].edges[edge].center.y;