1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-12-25 19:46:50 -05:00

Added map caching. Improved map rendering a little bit.

This commit is contained in:
Tim Sarbin 2018-11-25 18:37:53 -05:00
parent 256ccbc7c2
commit 04f1238f2c
7 changed files with 203 additions and 86 deletions

View File

@ -29,6 +29,6 @@ namespace OpenDiablo2.Common.Interfaces
void Draw(ISprite sprite, int frame);
void Draw(ISprite sprite, int xSegments, int ySegments, int offset);
void Draw(ILabel label);
void DrawMapCell(int xCell, int yCell, int xPixel, int yPixel, MPQDS1 mapData, int main_index, int sub_index, Palette palette, MPQDS1WallOrientationTileProps orientation);
void DrawMapCell(int xCell, int yCell, int xPixel, int yPixel, MPQDS1 mapData, int main_index, int sub_index, Palette palette, int orientation = -1);
}
}

View File

@ -73,6 +73,7 @@ namespace OpenDiablo2.Common.Models
{
static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public Guid Id { get; } = Guid.NewGuid();
public Int32 Version { get; internal set; }
public Int32 Width { get; internal set; }
public Int32 Height { get; internal set; }

View File

@ -21,6 +21,7 @@ namespace OpenDiablo2.Common.Models
public sealed class MPQDT1Tile
{
public Guid Id { get; set; } = Guid.NewGuid();
public Int32 Direction { get; internal set; }
public Int16 RoofHeight { get; internal set; }
public byte SoundIndex { get; internal set; }
@ -40,6 +41,8 @@ namespace OpenDiablo2.Common.Models
public sealed class MPQDT1
{
public Int32 X1 { get; private set; }
public Int32 X2 { get; private set; }
public Int32 NumberOfTiles { get; private set; }
public MPQDT1Tile[] Tiles { get; private set; }
private Int32 tileHeaderOffset;
@ -48,7 +51,11 @@ namespace OpenDiablo2.Common.Models
{
var br = new BinaryReader(stream);
X1 = br.ReadInt32();
X2 = br.ReadInt32();
stream.Seek(268, SeekOrigin.Begin); // Skip useless header info
NumberOfTiles = br.ReadInt32();
tileHeaderOffset = br.ReadInt32();
@ -62,6 +69,7 @@ namespace OpenDiablo2.Common.Models
Tiles = new MPQDT1Tile[NumberOfTiles];
for (int tileIndex = 0; tileIndex < NumberOfTiles; tileIndex++)
{
br.BaseStream.Seek(tileHeaderOffset + (tileIndex * 96), SeekOrigin.Begin);
Tiles[tileIndex] = new MPQDT1Tile();
var tile = Tiles[tileIndex];
@ -93,11 +101,12 @@ namespace OpenDiablo2.Common.Models
for (int tileIndex = 0; tileIndex < NumberOfTiles; tileIndex++)
{
var tile = Tiles[tileIndex];
br.BaseStream.Seek(tile.BlockHeadersPointer, SeekOrigin.Begin);
tile.Blocks = new MPQDT1Block[tile.NumberOfBlocks];
for (int blockIndex = 0; blockIndex < tile.NumberOfBlocks; blockIndex++)
{
br.BaseStream.Seek(tile.BlockHeadersPointer + (blockIndex * 20), SeekOrigin.Begin);
tile.Blocks[blockIndex] = new MPQDT1Block();
var block = tile.Blocks[blockIndex];

View File

@ -10,7 +10,7 @@ namespace OpenDiablo2.Common.Models
public struct Palette
{
public string Name { get; set; }
public UInt32[] Colors;
public UInt32[] Colors { get; set; }
public static Palette LoadFromStream(Stream stream, string paletteName)
{

View File

@ -48,7 +48,7 @@ namespace OpenDiablo2.Core.GameState_
Seed = random.Next();
sceneManager.ChangeScene("Game");
ChangeMap(eLevelId.Act5_BaalEntrance);
ChangeMap(eLevelId.Act3_MephistoComplex);
}
public void ChangeMap(eLevelId levelId)
@ -71,6 +71,7 @@ namespace OpenDiablo2.Core.GameState_
MapName = level.Name;
Act = levelType.Act;
MapData = resourceManager.GetMPQDS1(mapName, level, levelDetails, levelType);
getMapEngine().NotifyMapChanged();
}
}

View File

@ -6,6 +6,7 @@ using System.Text;
using System.Threading.Tasks;
using OpenDiablo2.Common;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Models;
namespace OpenDiablo2.Core.Map_Engine
{
@ -65,83 +66,126 @@ namespace OpenDiablo2.Core.Map_Engine
}
public void Render()
{
// Lower Walls, Floors, and Shadows
// Shadows of objects
// Objects with OrderFlag = 1
// Upper Walls and objects with ORderFlag = 0 or 2
// Roofs
for (int y = 0; y < gameState.MapData.Width; y++)
for (int x = 0; x < gameState.MapData.Height; x++)
for (int y = 0; y < gameState.MapData.Height; y++)
{
for (int x = 0; x < gameState.MapData.Width; x++)
{
var visualX = ((x - y) * (cellSizeX / 2)) - cOffX;
var visualY = ((x + y) * (cellSizeY / 2)) - cOffY;
if (visualX < -160 || visualX > 800 || visualY < -120 || visualY > 650)
continue;
// Render the floor
foreach (var floorLayer in gameState.MapData.FloorLayers)
{
var idx = x + (y * gameState.MapData.Width);
if (idx >= floorLayer.Props.Length)
break;
var floor = floorLayer.Props[idx];
if (floor.Prop1 == 0)
continue;
var sub_index = floor.Prop2;
var main_index = (floor.Prop3 >> 4) + ((floor.Prop4 & 0x03) << 4);
renderWindow.DrawMapCell(x, y, ((x - y) * 80) - cOffX, ((x + y) * 40) - cOffY, gameState.MapData, main_index, sub_index, gameState.CurrentPalette, null);
}
DrawWall(x, y, visualX, visualY, false);
DrawFloor(x, y, visualX, visualY);
DrawWall(x, y, visualX, visualY, true);
DrawRoof(x, y, visualX, visualY);
// //DrawShadow(x, y, visualX, visualY);
}
/*
}
}
private void DrawRoof(int x, int y, int visualX, int visualY)
{
var cx = ((x - y) * 80) - cOffX;
var cy = ((x + y) * 40) - cOffY;
// Render the walls
foreach (var wallLayer in gameState.MapData.WallLayers)
{
var wall = wallLayer.Props[x + (y * gameState.MapData.Width)];
var orientation = wallLayer.Orientations[x + (y * gameState.MapData.Width)].Orientation1;
for (int y = 0; y < gameState.MapData.Width; y++)
for (int x = 0; x < gameState.MapData.Height; x++)
{
if (orientation != 15) // Only 15 (roof)
return;
var visualX = ((x - y) * (cellSizeX / 2)) - cOffX;
var visualY = ((x + y) * (cellSizeY / 2)) - cOffY;
if (wall.Prop1 == 0)
continue;
if (visualX < -160 || visualX > 800 || visualY < -120 || visualY > 650)
continue;
if ((wall.Prop4 & 0x80) > 0)
{
if (orientation != 10 && orientation != 11)
return;
}
var idx = x + (y * gameState.MapData.Width);
if (idx >= wallLayer.Props.Length)
continue;
var wall = wallLayer.Props[idx];
if (wall.Prop1 == 0)
continue;
var sub_index = wall.Prop2;
var main_index = (wall.Prop3 >> 4) + ((wall.Prop4 & 0x03) << 4);
var orientation = wallLayer.Orientations[x + (y * gameState.MapData.Width)];
renderWindow.DrawMapCell(x, y, ((x - y) * 80) - cOffX, ((x + y) * 40) - cOffY + 80, gameState.MapData, main_index, sub_index, gameState.CurrentPalette, orientation);
}
var sub_index = wall.Prop2;
var main_index = (wall.Prop3 >> 4) + ((wall.Prop4 & 0x03) << 4);
var lt = gameState.MapData.LookupTable.First(z => z.MainIndex == main_index && z.SubIndex == sub_index && z.Orientation == orientation);
renderWindow.DrawMapCell(x, y, cx, cy - lt.TileRef.RoofHeight, gameState.MapData, main_index, sub_index, gameState.CurrentPalette, orientation);
}
*/
}
private void DrawShadow(int x, int y, int visualX, int visualY)
{
}
private void DrawFloor(int x, int y, int visualX, int visualY)
{
if (visualX < -160 || visualX > 800 || visualY < -120 || visualY > 650)
return;
var cx = ((x - y) * 80) - cOffX;
var cy = ((x + y) * 40) - cOffY;
// Render the floor
foreach (var floorLayer in gameState.MapData.FloorLayers)
{
var idx = x + (y * gameState.MapData.Width);
if (idx >= floorLayer.Props.Length)
break;
var floor = floorLayer.Props[idx];
if (floor.Prop1 == 0)
continue;
var sub_index = floor.Prop2;
var main_index = (floor.Prop3 >> 4) + ((floor.Prop4 & 0x03) << 4);
renderWindow.DrawMapCell(x, y, cx, cy, gameState.MapData, main_index, sub_index, gameState.CurrentPalette);
}
}
private void DrawWall(int x, int y, int visualX, int visualY, bool upper)
{
var cx = ((x - y) * 80) - cOffX;
var cy = ((x + y) * 40) - cOffY;
foreach (var wallLayer in gameState.MapData.WallLayers)
{
var wall = wallLayer.Props[x + (y * gameState.MapData.Width)];
var orientation = wallLayer.Orientations[x + (y * gameState.MapData.Width)].Orientation1;
if (wall.Prop1 == 0)
continue;
if (upper && orientation <= 15)
return;
if (orientation == 10 || orientation == 11)
return; // TODO: Support special walls
if ((wall.Prop4 & 0x80) > 0)
{
if (orientation != 10 && orientation != 11)
return;
}
var sub_index = wall.Prop2;
var main_index = (wall.Prop3 >> 4) + ((wall.Prop4 & 0x03) << 4);
var lt = gameState.MapData.LookupTable.First(z => z.MainIndex == main_index && z.SubIndex == sub_index && z.Orientation == orientation);
renderWindow.DrawMapCell(x, y, cx, cy + 80, gameState.MapData, main_index, sub_index, gameState.CurrentPalette, orientation);
}
}
public void Update(long ms)

View File

@ -34,8 +34,6 @@ namespace OpenDiablo2.SDL2_
private readonly IResourceManager resourceManager;
private readonly IGameState gameState;
private IntPtr cellTexture;
public SDL2RenderWindow(
IMPQProvider mpqProvider,
IPaletteProvider paletteProvider,
@ -64,10 +62,6 @@ namespace OpenDiablo2.SDL2_
SDL.SDL_SetRenderDrawBlendMode(renderer, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
SDL.SDL_ShowCursor(0);
cellTexture = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_ARGB8888, (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, 256, 1024);
SDL.SDL_SetTextureBlendMode(cellTexture, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
IsRunning = true;
}
@ -280,17 +274,31 @@ namespace OpenDiablo2.SDL2_
SDL.SDL_RenderCopy(renderer, lbl.texture, IntPtr.Zero, ref destRect);
}
public unsafe void DrawMapCell(int xCell, int yCell, int xPixel, int yPixel, MPQDS1 mapData, int main_index, int sub_index, Palette palette, MPQDS1WallOrientationTileProps orientation)
// TODO: Clean this up
class _MapDataLookup
{
public Guid TileId;
public int OffX;
public int OffY;
public int FrameWidth;
public int FrameHeight;
public SDL.SDL_Rect SrcRect;
public IntPtr Texture;
}
private Dictionary<Guid, List<_MapDataLookup>> mapDataLookup = new Dictionary<Guid, List<_MapDataLookup>>();
public unsafe void DrawMapCell(int xCell, int yCell, int xPixel, int yPixel, MPQDS1 mapData, int main_index, int sub_index, Palette palette, int orientation)
{
var tiles = mapData.LookupTable.Where(x =>
x.MainIndex == main_index &&
x.SubIndex == sub_index &&
(orientation == null || x.Orientation == orientation.Orientation1)).Select(x => x.TileRef);
(orientation == -1 || x.Orientation == orientation)).Select(x => x.TileRef);
if (!tiles.Any())
return; // TODO: Why does this happen....
return;
//throw new ApplicationException("Invalid tile id found!");
// TODO: This isn't good.. should be remembered in the map engine layer
MPQDT1Tile tile = null;
if (tiles.Count() > 0)
{
@ -309,10 +317,45 @@ namespace OpenDiablo2.SDL2_
}
} else tile = tiles.First();
var frameSize = new Size(tile.Width, Math.Abs(tile.Height));
var srcRect = new SDL.SDL_Rect { x = 0, y = 0, w = frameSize.Width, h = frameSize.Height };
var frameSizeMax = frameSize.Width * frameSize.Height;
if (SDL.SDL_LockTexture(cellTexture, IntPtr.Zero, out IntPtr pixels, out int pitch) != 0)
// This WILL happen to you
if (tile.Width == 0 || tile.Height == 0)
return;
if (mapDataLookup.ContainsKey(mapData.Id))
{
var lookupDetails = mapDataLookup[mapData.Id].FirstOrDefault(x => x.TileId == tile.Id);
if (lookupDetails != null)
{
var dx = new SDL.SDL_Rect { x = xPixel - lookupDetails.OffX, y = yPixel - lookupDetails.OffY, w = lookupDetails.FrameWidth, h = lookupDetails.FrameHeight };
SDL.SDL_RenderCopy(renderer, lookupDetails.Texture, ref lookupDetails.SrcRect, ref dx);
return;
}
}
var minX = tile.Blocks.Min(x => x.PositionX);
var minY = tile.Blocks.Min(x => x.PositionY);
var maxX = tile.Blocks.Max(x => x.PositionX + 32);
var maxY = tile.Blocks.Max(x => x.PositionY + 32);
var diffX = maxX - minX;
var diffY = maxY - minY;
var offX = -minX;
var offy = -minY;
var frameSize = new Size(diffX, Math.Abs(diffY));
var srcRect = new SDL.SDL_Rect { x = 0, y = 0, w = frameSize.Width, h = Math.Abs(frameSize.Height) };
var frameSizeMax = diffX * Math.Abs(diffY);
var texId = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_ARGB8888, (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, frameSize.Width, frameSize.Height);
SDL.SDL_SetTextureBlendMode(texId, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
if (SDL.SDL_LockTexture(texId, IntPtr.Zero, out IntPtr pixels, out int pitch) != 0)
{
log.Error("Could not lock texture for map rendering");
return;
@ -320,14 +363,16 @@ namespace OpenDiablo2.SDL2_
try
{
UInt32* data = (UInt32*)pixels;
for (var i = 0; i < frameSizeMax; i++)
data[i] = 0x0;
var pitchChange = (pitch / 4);
for (var i = 0; i < frameSize.Height * pitchChange; i++)
data[i] = 0x0;
foreach (var block in tile.Blocks)
{
var index = block.PositionX + ((block.PositionY) * pitchChange);
var index = block.PositionX + offX + ((block.PositionY + offy) * pitchChange);
var xx = 0;
var yy = 0;
foreach (var colorIndex in block.PixelData)
@ -338,7 +383,7 @@ namespace OpenDiablo2.SDL2_
continue;
var color = palette.Colors[colorIndex];
if ((color & 0xFFFFFF) > 0)
if (color > 0)
data[index] = color;
}
@ -359,13 +404,30 @@ namespace OpenDiablo2.SDL2_
}
finally
{
SDL.SDL_UnlockTexture(cellTexture);
SDL.SDL_UnlockTexture(texId);
}
SDL.SDL_SetRenderDrawBlendMode(renderer, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
SDL.SDL_SetTextureBlendMode(cellTexture, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
var dstRect = new SDL.SDL_Rect { x = xPixel, y = yPixel - tile.RoofHeight, w = frameSize.Width, h = frameSize.Height };
SDL.SDL_RenderCopy(renderer, cellTexture, ref srcRect, ref dstRect);
if (!mapDataLookup.ContainsKey(mapData.Id))
mapDataLookup[mapData.Id] = new List<_MapDataLookup>();
var lookup = new _MapDataLookup
{
FrameHeight = frameSize.Height,
FrameWidth = frameSize.Width,
OffX = offX,
OffY = offy,
SrcRect = srcRect,
TileId = tile.Id,
Texture = texId
};
mapDataLookup[mapData.Id].Add(lookup);
var dr = new SDL.SDL_Rect { x = xPixel - lookup.OffX, y = yPixel - lookup.OffY, w = lookup.FrameWidth, h = lookup.FrameHeight };
SDL.SDL_RenderCopy(renderer, lookup.Texture, ref lookup.SrcRect, ref dr);
}
}