From ecc6eb21383e2c4bec02e09738be39f6bf78ce17 Mon Sep 17 00:00:00 2001 From: Tim Sarbin Date: Sat, 24 Nov 2018 10:54:02 -0500 Subject: [PATCH] Added start of cell renderer. Still trying to fix map load issue --- OpenDiablo2.Common/Enums/eRenderCellType.cs | 14 ++++ .../Interfaces/IRenderWindow.cs | 4 +- OpenDiablo2.Common/Models/MPQDT1.cs | 79 +++++++++---------- OpenDiablo2.Common/OpenDiablo2.Common.csproj | 1 + OpenDiablo2.Core/GameState/GameState.cs | 21 ++++- OpenDiablo2.SDL2/SDL2RenderWindow.cs | 4 + OpenDiablo2.SDL2/SDL2Sprite.cs | 64 ++++++++++++++- OpenDiablo2.Scenes/Game.cs | 22 +++++- OpenDiablo2.Scenes/MainMenu.cs | 2 + 9 files changed, 161 insertions(+), 50 deletions(-) create mode 100644 OpenDiablo2.Common/Enums/eRenderCellType.cs diff --git a/OpenDiablo2.Common/Enums/eRenderCellType.cs b/OpenDiablo2.Common/Enums/eRenderCellType.cs new file mode 100644 index 00000000..50efe550 --- /dev/null +++ b/OpenDiablo2.Common/Enums/eRenderCellType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Enums +{ + public enum eRenderCellType + { + Floor, + Wall + } +} diff --git a/OpenDiablo2.Common/Interfaces/IRenderWindow.cs b/OpenDiablo2.Common/Interfaces/IRenderWindow.cs index 041aaf0e..76bdd266 100644 --- a/OpenDiablo2.Common/Interfaces/IRenderWindow.cs +++ b/OpenDiablo2.Common/Interfaces/IRenderWindow.cs @@ -1,4 +1,5 @@ -using OpenDiablo2.Common.Models; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Models; using System; using System.Collections.Generic; using System.Drawing; @@ -18,6 +19,7 @@ namespace OpenDiablo2.Common.Interfaces void Quit(); ISprite LoadSprite(string resourcePath, string palette, Point location); ISprite LoadSprite(string resourcePath, string palette); + ISprite GenerateMapCell(MPQDS1 mapData, int x, int y, eRenderCellType cellType, Palette palette); IFont LoadFont(string resourcePath, string palette); ILabel CreateLabel(IFont font); ILabel CreateLabel(IFont font, string text); diff --git a/OpenDiablo2.Common/Models/MPQDT1.cs b/OpenDiablo2.Common/Models/MPQDT1.cs index a7cdb634..ea6fb798 100644 --- a/OpenDiablo2.Common/Models/MPQDT1.cs +++ b/OpenDiablo2.Common/Models/MPQDT1.cs @@ -15,7 +15,7 @@ namespace OpenDiablo2.Common.Models public byte GridY { get; internal set; } public UInt16 Format { get; internal set; } public UInt32 Length { get; internal set; } - public Int32 FileOffset { get; internal set; } + public UInt32 FileOffset { get; internal set; } public Int16[] PixelData { get; internal set; } } @@ -55,57 +55,53 @@ namespace OpenDiablo2.Common.Models for (int tileIndex = 0; tileIndex < NumberOfTiles; tileIndex++) { + stream.Seek(tileHeaderOffset + (96 * tileIndex), SeekOrigin.Begin); Tiles[tileIndex] = new MPQDT1Tile(); - Tiles[tileIndex].Direction = br.ReadUInt32(); - Tiles[tileIndex].RoofHeight = br.ReadUInt16(); - Tiles[tileIndex].SoundIndex = br.ReadByte(); - Tiles[tileIndex].Animated = br.ReadByte() == 1; - Tiles[tileIndex].Height = br.ReadInt32(); - Tiles[tileIndex].Width = br.ReadInt32(); + var tile = Tiles[tileIndex]; + + tile.Direction = br.ReadUInt32(); + tile.RoofHeight = br.ReadUInt16(); + tile.SoundIndex = br.ReadByte(); + tile.Animated = br.ReadByte() == 1; + tile.Height = br.ReadInt32(); + tile.Width = br.ReadInt32(); br.ReadBytes(4); - Tiles[tileIndex].Orientation = br.ReadUInt32(); - Tiles[tileIndex].MainIndex = br.ReadUInt32(); - Tiles[tileIndex].SubIndex = br.ReadUInt32(); - Tiles[tileIndex].RarityOrFrameIndex = br.ReadUInt32(); + tile.Orientation = br.ReadUInt32(); + tile.MainIndex = br.ReadUInt32(); + tile.SubIndex = br.ReadUInt32(); + tile.RarityOrFrameIndex = br.ReadUInt32(); br.ReadBytes(4); for (int i = 0; i < 25; i++) - Tiles[tileIndex].SubTileFlags[i] = br.ReadByte(); + tile.SubTileFlags[i] = br.ReadByte(); br.ReadBytes(7); - Tiles[tileIndex].BlockHeadersPointer = br.ReadUInt32(); - Tiles[tileIndex].BlockDataLength = br.ReadUInt32(); - Tiles[tileIndex].NumberOfBlocks = br.ReadUInt32(); + tile.BlockHeadersPointer = br.ReadUInt32(); + tile.BlockDataLength = br.ReadUInt32(); + tile.NumberOfBlocks = br.ReadUInt32(); br.ReadBytes(12); - } - - for (int tileIndex = 0; tileIndex < NumberOfTiles; tileIndex++) - { - var tile = Tiles[tileIndex]; - if (tile.BlockHeadersPointer == 0) + if (tile.BlockHeadersPointer == 0 || tile.Width == 0 || tile.Height == 0) { tile.Blocks = new MPQDT1Block[0]; continue; } - stream.Seek(tile.BlockHeadersPointer, SeekOrigin.Begin); tile.Blocks = new MPQDT1Block[tile.NumberOfBlocks]; for (int blockIndex = 0; blockIndex < tile.NumberOfBlocks; blockIndex++) { + stream.Seek(tile.BlockHeadersPointer + (20 * blockIndex), SeekOrigin.Begin); tile.Blocks[blockIndex] = new MPQDT1Block(); - tile.Blocks[blockIndex].PositionX = br.ReadUInt16(); - tile.Blocks[blockIndex].PositionY = br.ReadUInt16(); - br.ReadBytes(2); - tile.Blocks[blockIndex].GridX = br.ReadByte(); - tile.Blocks[blockIndex].GridX = br.ReadByte(); - tile.Blocks[blockIndex].Format = br.ReadUInt16(); - tile.Blocks[blockIndex].Length = br.ReadUInt32(); - br.ReadBytes(2); - tile.Blocks[blockIndex].FileOffset = br.ReadInt32(); - } - - for (int blockIndex = 0; blockIndex < tile.NumberOfBlocks; blockIndex++) - { var block = tile.Blocks[blockIndex]; + + block.PositionX = br.ReadUInt16(); + block.PositionY = br.ReadUInt16(); + br.ReadBytes(2); + block.GridX = br.ReadByte(); + block.GridX = br.ReadByte(); + block.Format = br.ReadUInt16(); + block.Length = br.ReadUInt32(); + br.ReadBytes(2); + block.FileOffset = br.ReadUInt32(); + stream.Seek(tile.BlockHeadersPointer + block.FileOffset, SeekOrigin.Begin); if (block.Format == 1) @@ -136,18 +132,18 @@ namespace OpenDiablo2.Common.Models } } - else + else { - // TODO: This doesn't work.. memory pointer issues? - continue; - // RLE block + /* TODO: BROKEN var length = block.Length; byte b1; byte b2; int x = 0; int y = 0; - block.PixelData = new Int16[32 * 16]; + int width = (block.Format >> 8); + int height = (block.Format & 0xFF); + block.PixelData = new Int16[width * height]; while (length > 0) { b1 = br.ReadByte(); @@ -157,7 +153,7 @@ namespace OpenDiablo2.Common.Models { x += b1; length -= b2; - while(b2 > 0) + while (b2 > 0) { block.PixelData[x + (y * 32)] = br.ReadByte(); br.ReadByte(); @@ -171,6 +167,7 @@ namespace OpenDiablo2.Common.Models y++; } } + */ } } } diff --git a/OpenDiablo2.Common/OpenDiablo2.Common.csproj b/OpenDiablo2.Common/OpenDiablo2.Common.csproj index c2db1c4d..78f520b0 100644 --- a/OpenDiablo2.Common/OpenDiablo2.Common.csproj +++ b/OpenDiablo2.Common/OpenDiablo2.Common.csproj @@ -73,6 +73,7 @@ + diff --git a/OpenDiablo2.Core/GameState/GameState.cs b/OpenDiablo2.Core/GameState/GameState.cs index 7ccab842..c119eef7 100644 --- a/OpenDiablo2.Core/GameState/GameState.cs +++ b/OpenDiablo2.Core/GameState/GameState.cs @@ -14,18 +14,33 @@ namespace OpenDiablo2.Core.GameState_ { private readonly ISceneManager sceneManager; private readonly IResourceManager resourceManager; - MPQDS1 mapData; + private readonly IPaletteProvider paletteProvider; - public GameState(ISceneManager sceneManager, IResourceManager resourceManager) + public MPQDS1 MapData { get; set; } + public bool MapDirty { get; set; } + public int Act { get; private set; } + public string MapName { get; set; } + public Palette CurrentPalette => paletteProvider.PaletteTable[$"ACT{Act}"]; + + public GameState(ISceneManager sceneManager, IResourceManager resourceManager, IPaletteProvider paletteProvider) { this.sceneManager = sceneManager; this.resourceManager = resourceManager; + this.paletteProvider = paletteProvider; } public void Initialize(string characterName, eHero hero) { sceneManager.ChangeScene("Game"); - mapData = resourceManager.GetMPQDS1(ResourcePaths.MapAct1TownE1, -1, 1); + ChangeMap(ResourcePaths.MapAct1TownE1, 1); + } + + public void ChangeMap(string mapName, int act) + { + MapName = mapName; + Act = act; + MapDirty = true; + MapData = resourceManager.GetMPQDS1(mapName, -1, act); } } } diff --git a/OpenDiablo2.SDL2/SDL2RenderWindow.cs b/OpenDiablo2.SDL2/SDL2RenderWindow.cs index a4be0464..d4d4eacd 100644 --- a/OpenDiablo2.SDL2/SDL2RenderWindow.cs +++ b/OpenDiablo2.SDL2/SDL2RenderWindow.cs @@ -10,6 +10,7 @@ using System.Drawing; using OpenDiablo2.Common.Models; using Autofac; using System.Runtime.InteropServices; +using OpenDiablo2.Common.Enums; namespace OpenDiablo2.SDL2_ { @@ -266,5 +267,8 @@ namespace OpenDiablo2.SDL2_ SDL.SDL_RenderCopy(renderer, lbl.texture, IntPtr.Zero, ref destRect); } + + public ISprite GenerateMapCell(MPQDS1 mapData, int x, int y, eRenderCellType cellType, Palette palette) + => new SDL2Sprite(this.renderer, palette, mapData, x, y, cellType); } } diff --git a/OpenDiablo2.SDL2/SDL2Sprite.cs b/OpenDiablo2.SDL2/SDL2Sprite.cs index b7b20ee3..85a09c2a 100644 --- a/OpenDiablo2.SDL2/SDL2Sprite.cs +++ b/OpenDiablo2.SDL2/SDL2Sprite.cs @@ -2,6 +2,7 @@ using System.Drawing; using System.Linq; using System.Runtime.InteropServices; +using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Models; using SDL2; @@ -15,10 +16,12 @@ namespace OpenDiablo2.SDL2_ internal readonly ImageSet source; private readonly IntPtr renderer; internal IntPtr texture = IntPtr.Zero; + private Point globalFrameOffset = new Point(0, 0); public Point Location { get; set; } = new Point(); public Size FrameSize { get; set; } = new Size(); + private bool darken; public bool Darken { @@ -80,11 +83,63 @@ namespace OpenDiablo2.SDL2_ FrameSize = new Size(Pow2((int)source.Frames.Max(x => x.Width)), Pow2((int)source.Frames.Max(x => x.Height))); } + public unsafe SDL2Sprite(IntPtr renderer, Palette palette, MPQDS1 mapData, int x, int y, eRenderCellType cellType) + { + this.renderer = renderer; + // TODO: Cell types + + // Floor cell types + // Todo: multiple floor layers + var floorLayer = mapData.FloorLayers.First(); + var floor = floorLayer.Props[x + (y * mapData.Width)]; + + if (floor.Prop1 == 0) + return; + + var sub_index = floor.Prop2; + var main_index = (floor.Prop3 / 16) + ((floor.Prop4 & 0x0F) * 16); + var tile = mapData.DT1s[main_index].Tiles[sub_index]; + + FrameSize = new Size(tile.Width, Math.Abs(tile.Height)); + TotalFrames = 1; + frame = 0; + IntPtr pixels; + int pitch; + + texture = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_ARGB8888, (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, FrameSize.Width, FrameSize.Height); + + if (texture == IntPtr.Zero) + throw new ApplicationException($"Unaple to initialize texture: {SDL.SDL_GetError()}"); + + SDL.SDL_LockTexture(texture, IntPtr.Zero, out pixels, out pitch); + try + { + UInt32* data = (UInt32*)pixels; + for (var i = 0; i < FrameSize.Width * FrameSize.Height; i++) + data[i] = 0; + + for (var subtileX = 0; subtileX < 5; subtileX++) + { + for (var subtileY = 0; subtileY < 5; subtileY++) + { + var subtileFlags = tile.SubTileFlags[subtileX + (subtileY * 5)]; + + } + } + + } + finally + { + SDL.SDL_UnlockTexture(texture); + } + } + internal Point GetRenderPoint() - => new Point( - Location.X + source.Frames[Frame].OffsetX, - (Location.Y - FrameSize.Height) + source.Frames[Frame].OffsetY - ); + { + return source == null + ? globalFrameOffset + : new Point(Location.X + source.Frames[Frame].OffsetX, (Location.Y - FrameSize.Height) + source.Frames[Frame].OffsetY); + } public Size LocalFrameSize => new Size((int)source.Frames[Frame].Width, (int)source.Frames[Frame].Height); @@ -152,5 +207,6 @@ namespace OpenDiablo2.SDL2_ { SDL.SDL_DestroyTexture(texture); } + } } diff --git a/OpenDiablo2.Scenes/Game.cs b/OpenDiablo2.Scenes/Game.cs index a18af3bf..cce20d86 100644 --- a/OpenDiablo2.Scenes/Game.cs +++ b/OpenDiablo2.Scenes/Game.cs @@ -6,7 +6,9 @@ using System.Text; using System.Threading.Tasks; using OpenDiablo2.Common; using OpenDiablo2.Common.Attributes; +using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Interfaces; +using OpenDiablo2.Core.GameState_; namespace OpenDiablo2.Scenes { @@ -15,13 +17,17 @@ namespace OpenDiablo2.Scenes { private readonly IRenderWindow renderWindow; private readonly IResourceManager resourceManager; + private GameState gameState; + + private ISprite testSprite; private ISprite panelSprite, healthManaSprite, gameGlobeOverlapSprite; - public Game(IRenderWindow renderWindow, IResourceManager resourceManager) + public Game(IRenderWindow renderWindow, IResourceManager resourceManager, GameState gameState) { this.renderWindow = renderWindow; this.resourceManager = resourceManager; + this.gameState = gameState; panelSprite = renderWindow.LoadSprite(ResourcePaths.GamePanels, Palettes.Act1); healthManaSprite = renderWindow.LoadSprite(ResourcePaths.HealthMana, Palettes.Act1); @@ -30,6 +36,10 @@ namespace OpenDiablo2.Scenes public void Render() { + if (gameState.MapDirty) + RedrawMap(); + + renderWindow.Draw(testSprite); DrawPanel(); @@ -64,5 +74,15 @@ namespace OpenDiablo2.Scenes { } + + private void RedrawMap() + { + gameState.MapDirty = false; + + var x = 0; + var y = 0; + testSprite = renderWindow.GenerateMapCell(gameState.MapData, 0, 0, eRenderCellType.Floor, gameState.CurrentPalette); + testSprite.Location = new Point(((x * 80) - (y * 80)) + 100, ((x * 40) + (y * 40)) + 100); + } } } diff --git a/OpenDiablo2.Scenes/MainMenu.cs b/OpenDiablo2.Scenes/MainMenu.cs index 6eb78653..11882404 100644 --- a/OpenDiablo2.Scenes/MainMenu.cs +++ b/OpenDiablo2.Scenes/MainMenu.cs @@ -50,6 +50,8 @@ namespace OpenDiablo2.Scenes this.mouseInfoProvider = mouseInfoProvider; this.sceneManager = sceneManager; + resourceManager.GetMPQDS1(ResourcePaths.MapAct1TownE1, -1, 1); + backgroundSprite = renderWindow.LoadSprite(ResourcePaths.GameSelectScreen, Palettes.Sky); diabloLogoLeft = renderWindow.LoadSprite(ResourcePaths.Diablo2LogoFireLeft, Palettes.Units, new Point(400, 120)); diabloLogoLeft.Blend = true;