X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_map.pas;h=e439d17db2b9643ea8451966dc98354690f15be7;hb=32339d5cde92f848fde363aeb38b6bbbf4d7c35c;hp=6b343b325fa8913fa7f3cae31a27e42418be4659;hpb=0d83f3f36628c487c98470a87440c87b3467f5e4;p=d2df-sdl.git diff --git a/src/game/g_map.pas b/src/game/g_map.pas index 6b343b3..e439d17 100644 --- a/src/game/g_map.pas +++ b/src/game/g_map.pas @@ -13,15 +13,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *) -{$MODE DELPHI} -{$modeswitch nestedprocvars} +{$INCLUDE ../shared/a_modes.inc} unit g_map; interface uses e_graphics, g_basic, MAPSTRUCT, g_textures, Classes, - g_phys, wadreader, BinEditor, g_panel, g_grid, md5; + g_phys, wadreader, BinEditor, g_panel, g_grid, z_aabbtree, md5, xprofiler; type TMapInfo = record @@ -91,6 +90,9 @@ procedure g_Map_LoadState(Var Mem: TBinMemoryReader); procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer); +procedure g_Map_ProfilersBegin (); +procedure g_Map_ProfilersEnd (); + const RESPAWNPOINT_PLAYER1 = 1; RESPAWNPOINT_PLAYER2 = 2; @@ -132,6 +134,13 @@ var BackID: DWORD = DWORD(-1); gExternalResources: TStringList; + gdbg_map_use_accel_render: Boolean = true; + gdbg_map_use_accel_coldet: Boolean = true; + gdbg_map_use_tree_draw: Boolean = false; + gdbg_map_use_tree_coldet: Boolean = false; + gdbg_map_dump_coldet_tree_queries: Boolean = false; + profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY! + implementation uses @@ -139,7 +148,7 @@ uses GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG, g_options, MAPREADER, g_triggers, g_player, MAPDEF, Math, g_monsters, g_saveload, g_language, g_netmsg, - utils, sfs, + utils, sfs, binheap, ImagingTypes, Imaging, ImagingUtility, ImagingGif, ImagingNetworkGraphics; @@ -148,20 +157,31 @@ const MUSIC_SIGNATURE = $4953554D; // 'MUSI' FLAG_SIGNATURE = $47414C46; // 'FLAG' + GridTagInvalid = 0; + GridTagWallDoor = $0001; + GridTagBack = $0002; + GridTagFore = $0004; + GridTagWater = $0008; + GridTagAcid1 = $0010; + GridTagAcid2 = $0020; + GridTagStep = $0040; + GridTagLift = $0080; + GridTagBlockMon = $0100; + function panelTypeToTag (panelType: Word): Integer; begin case panelType of - PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := 0; // gWalls - PANEL_BACK: result := 1; // gRenderBackgrounds - PANEL_FORE: result := 2; // gRenderForegrounds - PANEL_WATER: result := 3; // gWater - PANEL_ACID1: result := 4; // gAcid1 - PANEL_ACID2: result := 5; // gAcid2 - PANEL_STEP: result := 6; // gSteps - PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := 7; // gLifts -- this is for all lifts - PANEL_BLOCKMON: result := 8; // gBlockMon -- this is for all blockmons - else result := -1; + PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagWallDoor; // gWalls + PANEL_BACK: result := GridTagBack; // gRenderBackgrounds + PANEL_FORE: result := GridTagFore; // gRenderForegrounds + PANEL_WATER: result := GridTagWater; // gWater + PANEL_ACID1: result := GridTagAcid1; // gAcid1 + PANEL_ACID2: result := GridTagAcid2; // gAcid2 + PANEL_STEP: result := GridTagStep; // gSteps + PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts + PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons + else result := GridTagInvalid; end; end; @@ -172,6 +192,26 @@ type PArrID: Integer; end; +type + TDynAABBTreeMap = class(TDynAABBTree) + function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean; override; + end; + +function TDynAABBTreeMap.getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean; +var + pan: TPanel; +begin + result := false; + if (flesh = nil) then begin aabb := AABB2D.Create(0, 0, 0, 0); exit; end; + pan := (flesh as TPanel); + aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width, pan.Y+pan.Height); + if (pan.Width < 1) or (pan.Height < 1) then exit; + //if (pan.Width = 1) then aabb.maxX += 1; + //if (pan.Height = 1) then aabb.maxY += 1; + if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!'); + result := aabb.valid; +end; + var PanelById: array of TPanelID; Textures: TLevelTextureArray; @@ -179,6 +219,27 @@ var FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint; //DOMFlagPoints: Array of TFlagPoint; gMapGrid: TBodyGrid = nil; + mapTree: TDynAABBTree = nil; + + +procedure g_Map_ProfilersBegin (); +begin + if (profMapCollision = nil) then profMapCollision := TProfiler.Create('MAP COLLISION', g_profile_history_size); + profMapCollision.mainBegin(g_profile_collision); + // create sections + if g_profile_collision then + begin + profMapCollision.sectionBegin('wall coldet'); + profMapCollision.sectionEnd(); + profMapCollision.sectionBegin('liquid coldet'); + profMapCollision.sectionEnd(); + end; +end; + +procedure g_Map_ProfilersEnd (); +begin + if (profMapCollision <> nil) then profMapCollision.mainEnd(); +end; function g_Map_IsSpecialTexture(Texture: String): Boolean; @@ -390,6 +451,7 @@ begin panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures); panels^[len].ArrIdx := len; + panels^[len].tag := panelTypeToTag(PanelRec.PanelType); if sav then panels^[len].SaveIt := True; @@ -946,13 +1008,17 @@ var tag := panelTypeToTag(tag); for idx := High(panels) downto 0 do begin + panels[idx].tag := tag; gMapGrid.insertBody(panels[idx], panels[idx].X, panels[idx].Y, panels[idx].Width, panels[idx].Height, tag); + mapTree.insertObject(panels[idx], tag, true); // as static object end; end; begin gMapGrid.Free(); gMapGrid := nil; + mapTree.Free(); + mapTree := nil; fixMinMax(gWalls); fixMinMax(gRenderBackgrounds); @@ -971,6 +1037,7 @@ begin end; gMapGrid := TBodyGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1); + mapTree := TDynAABBTreeMap.Create(); addPanelsToGrid(gWalls, PANEL_WALL); // and PANEL_CLOSEDOOR addPanelsToGrid(gRenderBackgrounds, PANEL_BACK); @@ -983,6 +1050,8 @@ begin addPanelsToGrid(gBlockMon, PANEL_BLOCKMON); gMapGrid.dumpStats(); + e_WriteLog(Format('tree depth: %d; %d nodes used, %d nodes allocated', [mapTree.computeTreeHeight, mapTree.nodeCount, mapTree.nodeAlloced]), MSG_NOTIFY); + mapTree.forEachLeaf(nil); end; function g_Map_Load(Res: String): Boolean; @@ -1019,6 +1088,8 @@ var begin gMapGrid.Free(); gMapGrid := nil; + mapTree.Free(); + mapTree := nil; Result := False; gMapInfo.Map := Res; @@ -1827,13 +1898,13 @@ procedure g_Map_DrawPanels(x0, y0, wdt, hgt: Integer; PanelType: Word); var ptag: Integer; - function qq (obj: TObject; tag: Integer): Boolean; + function checker (obj: TObject; tag: Integer): Boolean; var pan: TPanel; begin result := false; // don't stop, ever + //e_WriteLog(Format(' *body: tag:%d; ptag:%d; pantype=%d', [tag, ptag, PanelType]), MSG_NOTIFY); if (tag <> ptag) then exit; - //e_WriteLog(Format(' *body: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY); if obj = nil then begin e_WriteLog(Format(' !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY); exit; end; if not (obj is TPanel) then begin e_WriteLog(Format(' !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY); exit; end; @@ -1859,10 +1930,13 @@ var if not (drawDoors xor panels[idx].Door) then begin pan := panels[idx]; + { if (pan.Width < 1) or (pan.Height < 1) then continue; if (pan.X+pan.Width <= x0) or (pan.Y+pan.Height <= y0) then continue; if (pan.X >= x0+wdt) or (pan.Y >= y0+hgt) then continue; - e_WriteLog(Format(' *body hit: (%d,%d)-(%dx%d) tag: %d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, PanelType, PanelType]), MSG_NOTIFY); + } + pan.Draw(); + //e_WriteLog(Format(' *body hit: (%d,%d)-(%dx%d) tag: %d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, PanelType, PanelType]), MSG_NOTIFY); end; end; end; @@ -1874,46 +1948,63 @@ begin //e_WriteLog(Format('***QQQ: qtag:%d', [PanelType]), MSG_NOTIFY); dplClear(); ptag := panelTypeToTag(PanelType); - gMapGrid.forEachInAABB(x0, y0, wdt, hgt, qq); - - // debug - { - e_WriteLog(Format('+++QQQ: qtag:%d', [PanelType]), MSG_NOTIFY); - case PanelType of - PANEL_WALL: DrawPanels(0, gWalls); - PANEL_CLOSEDOOR: DrawPanels(0, gWalls, True); - PANEL_BACK: DrawPanels(1, gRenderBackgrounds); - PANEL_FORE: DrawPanels(2, gRenderForegrounds); - PANEL_WATER: DrawPanels(3, gWater); - PANEL_ACID1: DrawPanels(4, gAcid1); - PANEL_ACID2: DrawPanels(5, gAcid2); - PANEL_STEP: DrawPanels(6, gSteps); - end; - e_WriteLog('==================', MSG_NOTIFY); - } - // sort and draw the list (we need to sort it, or rendering is fucked) - while gDrawPanelList.count > 0 do + if gdbg_map_use_accel_render then + begin + if gdbg_map_use_tree_draw then + begin + mapTree.aabbQuery(x0, y0, wdt, hgt, checker, ptag); + end + else + begin + gMapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, ptag); + end; + // sort and draw the list (we need to sort it, or rendering is fucked) + while gDrawPanelList.count > 0 do + begin + (gDrawPanelList.front() as TPanel).Draw(); + gDrawPanelList.popFront(); + end; + end + else begin - (gDrawPanelList.front() as TPanel).Draw(); - gDrawPanelList.popFront(); + //e_WriteLog(Format('+++QQQ: qtag:%d', [PanelType]), MSG_NOTIFY); + case PanelType of + PANEL_WALL: DrawPanels(0, gWalls); + PANEL_CLOSEDOOR: DrawPanels(0, gWalls, True); + PANEL_BACK: DrawPanels(1, gRenderBackgrounds); + PANEL_FORE: DrawPanels(2, gRenderForegrounds); + PANEL_WATER: DrawPanels(3, gWater); + PANEL_ACID1: DrawPanels(4, gAcid1); + PANEL_ACID2: DrawPanels(5, gAcid2); + PANEL_STEP: DrawPanels(6, gSteps); + end; end; + + //e_WriteLog('==================', MSG_NOTIFY); end; procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer); - function qq (obj: TObject; tag: Integer): Boolean; + function checker (obj: TObject; tag: Integer): Boolean; var pan: TPanel; begin result := false; // don't stop, ever - if (tag <> 0 {panelTypeToTag(PANEL_WALL)}) then exit; // only walls + if (tag <> GridTagWallDoor) then exit; // only walls pan := (obj as TPanel); pan.DrawShadowVolume(lightX, lightY, radius); end; begin - gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, qq); + if gdbg_map_use_tree_draw then + begin + mapTree.aabbQuery(lightX-radius, lightY-radius, radius*2, radius*2, checker, GridTagWallDoor); + end + else + begin + gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, GridTagWallDoor); + end; end; @@ -2045,9 +2136,40 @@ begin end; end; +function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD; +var + texid: DWORD; + + function checkPanels (var panels: TPanelArray): Boolean; + var + a: Integer; + begin + result := false; + if panels = nil then exit; + for a := 0 to High(panels) do + begin + if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then + begin + result := true; + texid := panels[a].GetTextureID(); + exit; + end; + end; + end; + +begin + texid := TEXTURE_NONE; + result := texid; + if not checkPanels(gWater) then + if not checkPanels(gAcid1) then + if not checkPanels(gAcid2) then exit; + result := texid; +end; + + function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean; - function qq (obj: TObject; tag: Integer): Boolean; + function checker (obj: TObject; tag: Integer): Boolean; var pan: TPanel; a: Integer; @@ -2069,7 +2191,7 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; pan := (obj as TPanel); a := pan.ArrIdx; - if WordBool(PanelType and PANEL_WALL) and (tag = panelTypeToTag(PANEL_WALL)) then + if WordBool(PanelType and PANEL_WALL) and (tag = GridTagWallDoor) then begin if gWalls[a].Enabled and g_Collide(X, Y, Width, Height, gWalls[a].X, gWalls[a].Y, gWalls[a].Width, gWalls[a].Height) then begin @@ -2078,7 +2200,7 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; end; end; - if WordBool(PanelType and PANEL_WATER) and (tag = panelTypeToTag(PANEL_WATER)) then + if WordBool(PanelType and PANEL_WATER) and (tag = GridTagWater) then begin if g_Collide(X, Y, Width, Height, gWater[a].X, gWater[a].Y, gWater[a].Width, gWater[a].Height) then begin @@ -2087,7 +2209,7 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; end; end; - if WordBool(PanelType and PANEL_ACID1) and (tag = panelTypeToTag(PANEL_ACID1)) then + if WordBool(PanelType and PANEL_ACID1) and (tag = GridTagAcid1) then begin if g_Collide(X, Y, Width, Height, gAcid1[a].X, gAcid1[a].Y, gAcid1[a].Width, gAcid1[a].Height) then begin @@ -2096,7 +2218,7 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; end; end; - if WordBool(PanelType and PANEL_ACID2) and (tag = panelTypeToTag(PANEL_ACID2)) then + if WordBool(PanelType and PANEL_ACID2) and (tag = GridTagAcid2) then begin if g_Collide(X, Y, Width, Height, gAcid2[a].X, gAcid2[a].Y, gAcid2[a].Width, gAcid2[a].Height) then begin @@ -2105,7 +2227,7 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; end; end; - if WordBool(PanelType and PANEL_STEP) and (tag = panelTypeToTag(PANEL_STEP)) then + if WordBool(PanelType and PANEL_STEP) and (tag = GridTagStep) then begin if g_Collide(X, Y, Width, Height, gSteps[a].X, gSteps[a].Y, gSteps[a].Width, gSteps[a].Height) then begin @@ -2114,7 +2236,7 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; end; end; - if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) and (tag = panelTypeToTag(PANEL_LIFTUP)) then + if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) and (tag = GridTagLift) then begin if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or @@ -2127,7 +2249,7 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; end; end; - if WordBool(PanelType and PANEL_BLOCKMON)and (tag = panelTypeToTag(PANEL_BLOCKMON)) then + if WordBool(PanelType and PANEL_BLOCKMON) and (tag = GridTagBlockMon) then begin if ((not b1x3) or ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64)) and g_Collide(X, Y, Width, Height, gBlockMon[a].X, gBlockMon[a].Y, gBlockMon[a].Width, gBlockMon[a].Height) then @@ -2139,57 +2261,100 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; end; begin - result := gMapGrid.forEachInAABB(X, Y, Width, Height, qq); + //TODO: detailed profile + if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('wall coldet'); + if gdbg_map_use_accel_coldet then + begin + if gdbg_map_use_tree_coldet then + begin + result := (mapTree.aabbQuery(X, Y, Width, Height, checker, (GridTagWallDoor or GridTagWater or GridTagAcid1 or GridTagAcid2 or GridTagStep or GridTagLift or GridTagBlockMon)) <> nil); + if (gdbg_map_dump_coldet_tree_queries) and (mapTree.nodesVisited <> 0) then + begin + //e_WriteLog(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]), MSG_NOTIFY); + g_Console_Add(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited])); + end; + end + else + begin + result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWallDoor or GridTagWater or GridTagAcid1 or GridTagAcid2 or GridTagStep or GridTagLift or GridTagBlockMon)); + end; + end + else + begin + result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3); + end; + if (profMapCollision <> nil) then profMapCollision.sectionEnd(); end; function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD; var - a, h: Integer; -begin - Result := TEXTURE_NONE; + cctype: Integer = 3; // priority: 0: water, 1: acid1, 2: acid2; 3: others (nothing) + texid: DWORD; - if gWater <> nil then + // slightly different from the old code, but meh... + function checker (obj: TObject; tag: Integer): Boolean; + var + pan: TPanel; + a: Integer; begin - h := High(gWater); - - for a := 0 to h do - if g_Collide(X, Y, Width, Height, - gWater[a].X, gWater[a].Y, - gWater[a].Width, gWater[a].Height) then + result := false; // don't stop, ever + if (tag <> GridTagWater) and (tag <> GridTagAcid1) and (tag <> GridTagAcid2) then exit; + pan := (obj as TPanel); + a := pan.ArrIdx; + // water + if (tag = GridTagWater) then + begin + if g_Collide(X, Y, Width, Height, gWater[a].X, gWater[a].Y, gWater[a].Width, gWater[a].Height) then begin - Result := gWater[a].GetTextureID(); - Exit; + result := true; // water has highest priority, so stop right here + texid := gWater[a].GetTextureID(); + exit; end; - end; - - if gAcid1 <> nil then - begin - h := High(gAcid1); - - for a := 0 to h do - if g_Collide(X, Y, Width, Height, - gAcid1[a].X, gAcid1[a].Y, - gAcid1[a].Width, gAcid1[a].Height) then + end; + // acid1 + if (cctype > 1) and (tag = GridTagAcid1) then + begin + if g_Collide(X, Y, Width, Height, gAcid1[a].X, gAcid1[a].Y, gAcid1[a].Width, gAcid1[a].Height) then begin - Result := gAcid1[a].GetTextureID(); - Exit; + cctype := 1; + texid := gAcid1[a].GetTextureID(); + exit; end; + end; + // acid2 + if (cctype > 2) and (tag = GridTagAcid2) then + begin + if g_Collide(X, Y, Width, Height, gAcid2[a].X, gAcid2[a].Y, gAcid2[a].Width, gAcid2[a].Height) then + begin + cctype := 2; + texid := gAcid2[a].GetTextureID(); + exit; + end; + end; end; - if gAcid2 <> nil then +begin + //TODO: detailed profile? + if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquid coldet'); + if gdbg_map_use_accel_coldet then begin - h := High(gAcid2); - - for a := 0 to h do - if g_Collide(X, Y, Width, Height, - gAcid2[a].X, gAcid2[a].Y, - gAcid2[a].Width, gAcid2[a].Height) then - begin - Result := gAcid2[a].GetTextureID(); - Exit; - end; + texid := TEXTURE_NONE; + if gdbg_map_use_tree_coldet then + begin + mapTree.aabbQuery(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2)); + end + else + begin + gMapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2)); + end; + result := texid; + end + else + begin + result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height); end; + if (profMapCollision <> nil) then profMapCollision.sectionEnd(); end; procedure g_Map_EnableWall(ID: DWORD);