Skip to content

Commit 1f8628a

Browse files
committed
feat(graphgen): yet, another 30% faster
Optimized a lot of the doors code. The doors themselves are 250% faster, webgraph will depend on the doors available but it's around 30% faster
1 parent 4190ec4 commit 1f8628a

File tree

5 files changed

+148
-105
lines changed

5 files changed

+148
-105
lines changed

osrs/map/mapdebugger.simba

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ begin
221221

222222
Self.Graph.WalkableSpace := Self.GetLocal(loader^.Graph.WalkableSpace);
223223

224+
225+
for i := 0 to High(Self.Graph.Doors) do
226+
begin
227+
Self.Graph.Doors[i].Before := Self.GetLocal([Self.Graph.Doors[i].Before])[0];
228+
Self.Graph.Doors[i].After := Self.GetLocal([Self.Graph.Doors[i].After])[0];
229+
Self.Graph.Doors[i].Center := Self.GetLocal([Self.Graph.Doors[i].Center])[0];
230+
end;
231+
224232
for i := 0 to High(loader^.Graph.WalkableClusters) do
225233
Self.Graph.WalkableClusters += Self.GetLocal(loader^.Graph.WalkableClusters[i]);
226234
for i := 0 to High(loader^.Graph.ObjectClusters) do

osrs/map/maploader.simba

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -409,42 +409,49 @@ begin
409409

410410
Result := TWebGraph.BuildGraph(name, map);
411411

412-
if (Result.Nodes <> []) and not FileWriteBytes(path + 'nodes.txt', Result.Nodes.ToBytes()) then
413-
raise GetDebugLn('MapLoader', 'Failed to create cache for graph nodes: ' + path);
414-
if (Result.Paths <> []) and not FileWriteBytes(path + 'paths.txt', Result.Paths.ToBytes()) then
415-
raise GetDebugLn('MapLoader', 'Failed to create cache for graph paths: ' + path);
416-
if (Result.Names <> []) and not FileWriteBytes(path + 'names.txt', Result.Names.ToBytes()) then
417-
raise GetDebugLn('MapLoader', 'Failed to create cache for graph names: ' + path);
418-
419-
if (Result.Doors <> []) and not FileWriteBytes(path + 'doors.txt', Result.Doors.ToBytes()) then
420-
raise GetDebugLn('MapLoader', 'Failed to create cache for graph doors: ' + path);
421-
422-
if (Result.WalkableSpace <> []) and not FileWriteBytes(path + 'walkablespace.txt', Result.WalkableSpace.ToBytes()) then
423-
raise GetDebugLn('MapLoader', 'Failed to create cache for graph WalkableSpace: ' + path);
424-
if (Result.WalkableClusters <> []) and not FileWriteBytes(path + 'walkableclusters.txt', Result.WalkableClusters.ToBytes()) then
425-
raise GetDebugLn('MapLoader', 'Failed to create cache for graph WalkableClusters: ' + path);
426-
if (Result.ObjectClusters <> []) and not FileWriteBytes(path + 'objectclusters.txt', Result.ObjectClusters.ToBytes()) then
427-
raise GetDebugLn('MapLoader', 'Failed to create cache for graph ObjectClusters: ' + path);
428-
412+
if (Result.Nodes <> []) then
413+
if not FileWriteBytes(path + 'nodes.txt', CompressBytes(Result.Nodes.ToBytes())) then
414+
raise GetDebugLn('MapLoader', 'Failed to create cache for graph nodes: ' + path);
415+
416+
if (Result.Paths <> []) then
417+
if not FileWriteBytes(path + 'paths.txt', CompressBytes(Result.Paths.ToBytes())) then
418+
raise GetDebugLn('MapLoader', 'Failed to create cache for graph paths: ' + path);
419+
if (Result.Names <> []) then
420+
if not FileWriteBytes(path + 'names.txt', CompressBytes(Result.Names.ToBytes())) then
421+
raise GetDebugLn('MapLoader', 'Failed to create cache for graph names: ' + path);
422+
423+
if (Result.Doors <> []) then
424+
if not FileWriteBytes(path + 'doors.txt', CompressBytes(Result.Doors.ToBytes())) then
425+
raise GetDebugLn('MapLoader', 'Failed to create cache for graph doors: ' + path);
426+
427+
if (Result.WalkableSpace <> []) then
428+
if not FileWriteBytes(path + 'walkablespace.txt', CompressBytes(Result.WalkableSpace.ToBytes())) then
429+
raise GetDebugLn('MapLoader', 'Failed to create cache for graph WalkableSpace: ' + path);
430+
if (Result.WalkableClusters <> []) then
431+
if not FileWriteBytes(path + 'walkableclusters.txt', CompressBytes(Result.WalkableClusters.ToBytes())) then
432+
raise GetDebugLn('MapLoader', 'Failed to create cache for graph WalkableClusters: ' + path);
433+
if (Result.ObjectClusters <> []) then
434+
if not FileWriteBytes(path + 'objectclusters.txt', CompressBytes(Result.ObjectClusters.ToBytes())) then
435+
raise GetDebugLn('MapLoader', 'Failed to create cache for graph ObjectClusters: ' + path);
429436
Exit;
430437
end;
431438

432439
if FileExists(path + 'nodes.txt') then
433-
Result.Nodes := TPointArray.CreateFromBytes(FileReadBytes(path + 'nodes.txt'));
440+
Result.Nodes := TPointArray.CreateFromBytes(DecompressBytes(FileReadBytes(path + 'nodes.txt')));
434441
if FileExists(path + 'paths.txt') then
435-
Result.Paths := T2DIntegerArray.CreateFromBytes(FileReadBytes(path + 'paths.txt'));
442+
Result.Paths := T2DIntegerArray.CreateFromBytes(DecompressBytes(FileReadBytes(path + 'paths.txt')));
436443
if FileExists(path + 'names.txt') then
437-
Result.Names := TStringArray.CreateFromBytes(FileReadBytes(path + 'names.txt'));
444+
Result.Names := TStringArray.CreateFromBytes(DecompressBytes(FileReadBytes(path + 'names.txt')));
438445

439446
if FileExists(path + 'doors.txt') then
440-
Result.Doors := TRSDoorArray.CreateFromBytes(FileReadBytes(path + 'doors.txt'));
447+
Result.Doors := TRSDoorArray.CreateFromBytes(DecompressBytes(FileReadBytes(path + 'doors.txt')));
441448

442449
if FileExists(path + 'walkablespace.txt') then
443-
Result.WalkableSpace := TPointArray.CreateFromBytes(FileReadBytes(path + 'walkablespace.txt'));
450+
Result.WalkableSpace := TPointArray.CreateFromBytes(DecompressBytes(FileReadBytes(path + 'walkablespace.txt')));
444451
if FileExists(path + 'walkableclusters.txt') then
445-
Result.WalkableClusters := T2DPointArray.CreateFromBytes(FileReadBytes(path + 'walkableclusters.txt'));
452+
Result.WalkableClusters := T2DPointArray.CreateFromBytes(DecompressBytes(FileReadBytes(path + 'walkableclusters.txt')));
446453
if FileExists(path + 'objectclusters.txt') then
447-
Result.ObjectClusters := T2DPointArray.CreateFromBytes(FileReadBytes(path + 'objectclusters.txt'));
454+
Result.ObjectClusters := T2DPointArray.CreateFromBytes(DecompressBytes(FileReadBytes(path + 'objectclusters.txt')));
448455

449456
SetLength(Result.Paths, Length(Result.Nodes));
450457
SetLength(Result.Names, Length(Result.Nodes));

osrs/walker.simba

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,9 +1155,6 @@ var
11551155
quad: TQuad;
11561156
tpa: TPointArray;
11571157
begin
1158-
if door.DoorType = ERSDoorType.UNKNOWN then
1159-
Exit;
1160-
11611158
angle := door.GetVisibleAngle(Minimap.GetCompassAngle() , 0 , False);
11621159
Minimap.SetCompassAngleEx(angle, 15);
11631160
SleepUntil(not minimap.IsPlayerMoving, 50,5000);

utils/webgraph.simba

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@ differently to handle different scenarios, e.g. doors.
1111
{$ENDIF}
1212

1313
type
14-
ERSDoorType = (UNKNOWN, NORMAL, WIDE);
14+
ERSDoorType = (NORMAL, WIDE);
1515

1616
TRSDoor = record
1717
Before, After, Center, Direction: TPoint;
1818
DoorType: ERSDoorType;
19-
SEPARATING: Boolean;
2019
end;
2120

2221
TRSDoorArray = array of TRSDoor;
@@ -92,14 +91,48 @@ begin
9291
for door in Self do
9392
begin
9493
case door.DoorType of
95-
ERSDoorType.NORMAL: if door.SEPARATING then img.DrawColor := $FF else img.DrawColor := $FFFF00;
96-
ERSDoorType.UNKNOWN: img.DrawColor := $00;
97-
ERSDoorType.WIDE: img.DrawColor := $FF00FF;
94+
ERSDoorType.NORMAL: img.DrawColor := $FF0000;
95+
ERSDoorType.WIDE: img.DrawColor := $FFFF00;
9896
end;
99-
img.DrawCross(door.Center, 3);
97+
img.DrawCross(door.Center, 4);
10098
end;
10199
end;
102100

101+
procedure TRSDoorArray.Draw(canvas: TImageBoxCanvas); overload;
102+
var
103+
door: TRSDoor;
104+
begin
105+
for door in Self do
106+
case door.DoorType of
107+
ERSDoorType.NORMAL: canvas.DrawCross(door.Center, 4, $FF0000);
108+
ERSDoorType.WIDE: canvas.DrawCross(door.Center, 4, $FFFF00);
109+
end;
110+
end;
111+
112+
113+
function TRSDoorArray.ToBytes(): TByteArray;
114+
var
115+
len: Integer;
116+
begin
117+
len := Length(Self) * SizeOf(TRSDoor);
118+
if len = 0 then Exit;
119+
SetLength(Result, len);
120+
Move(Self[0], Result[0], len);
121+
end;
122+
123+
function TRSDoorArray.CreateFromBytes(bytes: TByteArray): TRSDoorArray; static;
124+
var
125+
len: Integer;
126+
begin
127+
len := Length(bytes);
128+
if len = 0 then Exit;
129+
len := Length(bytes);
130+
SetLength(Result, len div SizeOf(TRSDoor));
131+
Move(bytes[0], Result[0], len);
132+
end;
133+
134+
135+
103136
type
104137
TWebGraph = record
105138
Nodes: TPointArray;
@@ -642,6 +675,8 @@ begin
642675
if Self.Names[i] = '' then img.DrawColor := $0101CC else img.DrawColor := $FF7F00;
643676
img.DrawBox(TBox.Create(Self.Nodes[i], 1, 1));
644677
end;
678+
679+
Self.Doors.Draw(img);
645680
end;
646681

647682
procedure TWebGraph.Draw(canvas: TImageBoxCanvas); overload;
@@ -661,29 +696,7 @@ begin
661696
else
662697
canvas.DrawBox(TBox.Create(Self.Nodes[i], 1, 1), $FF7F00);
663698
end;
664-
end;
665-
666-
667-
668-
function TRSDoorArray.ToBytes(): TByteArray;
669-
var
670-
len: Integer;
671-
begin
672-
len := Length(Self) * SizeOf(TRSDoor);
673-
if len = 0 then Exit;
674-
SetLength(Result, len);
675-
Move(Self[0], Result[0], len);
676-
end;
677699

678-
function TRSDoorArray.CreateFromBytes(bytes: TByteArray): TRSDoorArray; static;
679-
var
680-
len: Integer;
681-
begin
682-
len := Length(bytes);
683-
if len = 0 then Exit;
684-
len := Length(bytes);
685-
SetLength(Result, len div SizeOf(TRSDoor));
686-
Move(bytes[0], Result[0], len);
700+
Self.Doors.Draw(canvas);
687701
end;
688702

689-

utils/webgraphgen.simba

Lines changed: 67 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -35,48 +35,75 @@ begin
3535
end;
3636

3737

38-
function TWebGraph._FindDoors(doors: T2DPointArray; white: TPointArray): TRSDoorArray; static;
38+
function TWebGraph._FindDoors(doors: T2DPointArray; img: TImage): TRSDoorArray; static;
3939
var
40-
direction, center: TPoint;
41-
i: Integer;
40+
direction, inverted: TPoint;
41+
i, idx, j: Integer;
42+
doorType: ERSDoorType;
43+
splitA, splitB: Boolean;
4244
begin
43-
SetLength(Result, Length(doors));
44-
4545
for i := 0 to High(doors) do
4646
begin
47-
center := doors[i].Mean();
48-
Result[i].Center := center;
49-
50-
if doors[i].Contains(center + [1,0]) and doors[i].Contains(center + [-1,0]) then
51-
direction := [0,1]
52-
else if doors[i].Contains(center + [0,1]) and doors[i].Contains(center + [0,-1]) then
53-
direction := [1,0]
54-
else if doors[i].Contains(center + [1,1]) and doors[i].Contains(center + [-1,-1]) then
55-
direction := [1,-1]
56-
else if doors[i].Contains(center + [1,-1]) and doors[i].Contains(center + [-1,1]) then
57-
direction := [1,1]
58-
else
59-
Continue;
47+
case Length(doors[i]) of
48+
4: doorType := ERSDoorType.NORMAL;
49+
8: doorType := ERSDoorType.WIDE;
50+
else Continue;
51+
end;
6052

61-
//check if pixels around the door are walkable to exclude weird objects colored red on the map as being doors
62-
//also excludes open doors to be detected as doors needed to be passed through
63-
if ((center + direction) in white) and ((center + direction.Rotate(180, [0,0])) in white) then
53+
with doors[i].Mean() do
6454
begin
65-
Result[i].Direction := direction;
66-
case Length(doors[i]) of
67-
4: Result[i].DoorType := ERSDoorType.NORMAL;
68-
8: Result[i].DoorType := ERSDoorType.WIDE;
69-
end;
70-
end;
55+
if (img.Pixel[X+1, Y] = $0000FF) or (img.Pixel[X-1, Y] = $0000FF) then
56+
direction := [0,1]
57+
else if (img.Pixel[X, Y+1] = $0000FF) or (img.Pixel[X, Y-1] = $0000FF) then
58+
direction := [1,0]
59+
else if (img.Pixel[X+1, Y+1] = $0000FF) or (img.Pixel[X-1, Y-1] = $0000FF) then
60+
direction := [1,-1]
61+
else if (img.Pixel[X+1, Y-1] = $0000FF) or (img.Pixel[X-1, Y+1] = $0000FF) then
62+
direction := [1,1]
63+
else
64+
Continue;
65+
66+
if (img.Pixel[X+direction.X, Y+direction.Y] <> $FFFFFF) or
67+
(img.Pixel[X + (direction.X*-1), Y+ (direction.Y*-1)] <> $FFFFFF) then
68+
Continue;
69+
70+
inverted := direction.Rotate(PI/2, [0,0]);
71+
splitA := False;
72+
splitB := False;
73+
74+
for j := 1 to 6 do
75+
with Point(X,Y) + (inverted * j) do
76+
case img.Pixel[X, Y] of
77+
$FFFFFF: Break;
78+
$333333, $0:
79+
begin
80+
splitA := True;
81+
Break;
82+
end;
83+
end;
7184

72-
if Result[i].DoorType = ERSDoorType.WIDE then
73-
case Result[i].Direction of
74-
[0,1]: Result[i].Center.X -= 2;
75-
[1,0]: Result[i].Center.Y -= 2;
76-
end;
85+
for j := 1 to 6 do
86+
with Point(X,Y) - (inverted * j) do
87+
case img.Pixel[X, Y] of
88+
$FFFFFF: Break;
89+
$333333, $0:
90+
begin
91+
splitB := True;
92+
Break;
93+
end;
94+
end;
7795

78-
Result[i].Before += Result[i].Center + Result[i].Direction * 2;
79-
Result[i].After += Result[i].Center - Result[i].Direction * 2;
96+
if not splitA or not splitB then Continue;
97+
98+
idx := Length(Result);
99+
SetLength(Result, idx+1);
100+
Result[idx].Center := [X,Y];
101+
end;
102+
103+
Result[idx].DoorType := doorType;
104+
Result[idx].Direction := direction;
105+
Result[idx].Before := Result[idx].Center + direction * 2;
106+
Result[idx].After := Result[idx].Center - direction * 2;
80107
end;
81108
end;
82109

@@ -101,7 +128,7 @@ This is an internal method. Don't use it if you don't know what you are doing.
101128
*)
102129
function TWebGraph._BuildGraph(map: TImage; white, red: TPointArray): TWebGraph; static;
103130
var
104-
a, b, i, j, n, len, hi, nonSepDoors: Integer;
131+
a, b, i, j, n, len, hi: Integer;
105132
atpa, parts: T2DPointArray;
106133
skeleton, nodes, tpa, doorsInTpa, doorNodes: TPointArray;
107134
bounds: TBox;
@@ -114,24 +141,15 @@ var
114141
doorPaths: T2DIntegerArray;
115142
begin
116143
atpa := white.ClusterEx(1).SortBySize(True);
117-
doors := TWebGraph._FindDoors(red.ClusterEx(1), white);
144+
doors := TWebGraph._FindDoors(red.ClusterEx(1), map);
118145

119146
for i := 0 to High(doors) do
120147
begin
121-
door := doors[i];
122-
if (door.DoorType = ERSDoorType.UNKNOWN) or atpa.InSameTPA(door.Before, door.After) then
123-
begin
124-
nonSepDoors += 1;
125-
Continue;
126-
end;
127-
128-
door.SEPARATING := True;
129-
130-
Result.Doors += door;
131-
doorNodes += [door.Before, door.After];
148+
Result.Doors += doors[i];
149+
doorNodes += [doors[i].Before, doors[i].After];
132150
SetLength(doorPaths, Length(doorNodes));
133-
doorPaths[(i-nonSepDoors)*2] += (i-nonSepDoors)*2+1;
134-
doorPaths[(i-nonSepDoors)*2+1] += (i-nonSepDoors)*2;
151+
doorPaths[i*2] += i*2+1;
152+
doorPaths[i*2+1] += i*2;
135153
end;
136154

137155
for i := 0 to High(atpa) do

0 commit comments

Comments
 (0)