using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Runtime.InteropServices; using UnityEngine; public class LevelLoader : MonoBehaviour { public string LevelName = "COLD1"; private string _resoucresDir = @"C:\Users\l-pro\OneDrive\Рабочий стол\HogsOfWar\Processed"; private string PmgPath { get { return Path.Combine(_resoucresDir, LevelName + ".PMG"); } } private string PtgPath { get { return Path.Combine(_resoucresDir, LevelName + ".PTG"); } } private string PogPath { get { return Path.Combine(_resoucresDir, LevelName + ".POG"); } } public static uint TimPixelToRgba32(ushort timPixel) { uint r = (uint)(timPixel & 0x1f); uint g = (uint)(((timPixel) >> 5) & 0x1f); uint b = (uint)(((timPixel) >> 10) & 0x1f); r <<= 3; g <<= 3; b <<= 3; bool black = (timPixel & 0x7fff) == 0; bool stpOn = (timPixel & 0x8000) != 0; uint alpha = ((black && stpOn) || (!black && !stpOn)) ? (byte)0xff : (byte)0x00; return r | (g << 8) | (b << 16) | (alpha << 24); } public Material Mat; private static ImageRGBA32 TimToImage(TimFile tim) { uint[] data = new uint[tim.ImageHeight * tim.ImageWidth]; var format = tim.header.Format; if (format == TimFile.Format.Bpp4Bit) { for (int i = 0; i < tim.image.data.Length; i++) { var ip = tim.image.data[i]; var i0 = (ip >> 0) & 0x0f; var i1 = (ip >> 4) & 0x0f; var i2 = (ip >> 8) & 0x0f; var i3 = (ip >> 12) & 0x0f; data[i * 4 + 0] = TimPixelToRgba32(tim.lut.data[i0]); data[i * 4 + 1] = TimPixelToRgba32(tim.lut.data[i1]); data[i * 4 + 2] = TimPixelToRgba32(tim.lut.data[i2]); data[i * 4 + 3] = TimPixelToRgba32(tim.lut.data[i3]); } } ImageRGBA32 img = new ImageRGBA32((uint)tim.ImageWidth, (uint)tim.ImageHeight, data); return img; } public InspectorButton Rebuild_; class ImageRGBA32 { public uint Width; public uint Height; public uint[] Data; public ImageRGBA32(uint width, uint height) { Width = width; Height = height; Data = new uint[Width * Height]; } public ImageRGBA32(uint width, uint height, uint[] data) { Width = width; Height = height; Data = data; } public void Fill(uint color) { for(int i = 0; i < Data.Length; ++i) { Data[i] = color; } } public void Blit(ImageRGBA32 image, uint x, uint y) { for(uint iy = 0; iy < image.Height; ++iy) { uint ry = iy + y; if(ry >= Height) { break; } for (uint ix = 0; ix < image.Width; ++ix) { uint rx = ix + x; if (rx >= Width) { break; } var color = image.Data[iy * image.Width + ix]; Data[ry * Width + rx] = color; } } } public Texture2D ToTexture2D() { Texture2D tex = new Texture2D((int)Width, (int)Height, TextureFormat.RGBA32, false); var handle = GCHandle.Alloc(Data, GCHandleType.Pinned); tex.LoadRawTextureData(handle.AddrOfPinnedObject(), Data.Length * 4); handle.Free(); tex.Apply(); return tex; } public ImageRGBA32 FlipX() { ImageRGBA32 result = new ImageRGBA32(Width, Height); for (int y = 0; y < Height; ++y) { for (int x = 0; x < Width; ++x) { var vy = y; var vx = Width - x - 1; result.Data[vy * result.Width + vx] = Data[y * Width + x]; } } return result; } public ImageRGBA32 Rotate90() { ImageRGBA32 result = new ImageRGBA32(Height, Width); for(int y = 0; y < Height; ++y) { for (int x = 0; x < Width; ++x) { var vy = x; var vx = Height - y - 1; result.Data[vy * result.Width + vx] = Data[y * Width + x]; } } return result; } } public Texture2D Atlas; public bool ApplyTransform = false; public void BuildMapTexture(PmgFile pmg, PtgFile ptg) { var textures = ptg.Textures.Select(v => TimToImage(v)).ToArray(); var sideBlocks = (int)Math.Sqrt(pmg.Blocks.Length); Debug.Log("Side blocks: " + sideBlocks); uint atlasSize = (uint)(sideBlocks * 4 * ptg.Textures[0].ImageWidth); uint textureBlockSize = textures[0].Width; ImageRGBA32 atlas = new ImageRGBA32(atlasSize, atlasSize); atlas.Fill(0xffffffff); Debug.Log("Atlas size: " + atlasSize); for (int y = 0; y < sideBlocks; ++y) { for (int x = 0; x < sideBlocks; ++x) { int blockId = y * sideBlocks + x; var block = pmg.Blocks[blockId]; for (int tileY = 0; tileY < 4; ++tileY) { for (int tileX = 0; tileX < 4; ++tileX) { int tileId = tileY * 4 + tileX; var tile = block.tiles[tileId]; var texId = tile.TextureIndex; /* if(tile.RotationFlip != PmgFile.PmgBlock.Tile.Transform.None) { Debug.Log("Transform = " + tile.RotationFlip.ToString()); }*/ var tex = textures[texId]; if (ApplyTransform) { if ((tile.RotationFlip & PmgFile.PmgBlock.Tile.Transform.FlipX) == PmgFile.PmgBlock.Tile.Transform.FlipX) { tex = tex.FlipX(); } if ((tile.RotationFlip & PmgFile.PmgBlock.Tile.Transform.Rotate90) == PmgFile.PmgBlock.Tile.Transform.Rotate90) { tex = tex.Rotate90(); } if ((tile.RotationFlip & PmgFile.PmgBlock.Tile.Transform.Rotate180) == PmgFile.PmgBlock.Tile.Transform.Rotate180) { tex = tex.Rotate90(); tex = tex.Rotate90(); } } var ty = y * 4 + tileY; var tx = x * 4 + tileX; atlas.Blit(tex, (uint)(tx * textureBlockSize), (uint)(ty * textureBlockSize)); } } } } Atlas = atlas.ToTexture2D(); Atlas.filterMode = FilterMode.Point; if (Mat != null) { Mat.mainTexture = Atlas; } } public PogFile.ObjectRecord[] Objects; private float unitScale = 1.0f / 512.0f; private void PlaceObjects() { var pogBytes = File.ReadAllBytes(PogPath); Objects = PogFile.Parse(pogBytes).Objects; var parent = transform.GetChild(0); var oldChildsCount = parent.childCount; List children = new List(); for(int i = 0; i < oldChildsCount; ++i) { children.Add(parent.GetChild(i)); } foreach(var child in children) { UnityEngine.Object.DestroyImmediate(child.gameObject); } foreach (var obj in Objects) { var transform = new Vector3(-obj.XOffset, obj.YOffset, -obj.ZOffset) * unitScale; GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.parent = parent; cube.transform.position = transform; cube.transform.localScale = Vector3.one; cube.transform.localRotation = Quaternion.identity; cube.name = obj.Name; } } public void Rebuild() { PlaceObjects(); var pmgBytes = File.ReadAllBytes(PmgPath); var ptgBytes = File.ReadAllBytes(PtgPath); var pmg = PmgFile.Parse(pmgBytes); var ptg = PtgFile.Parse(ptgBytes); var pmgBlocks = pmg.Blocks; List result = new List(); List indices = new List(); Debug.Log("pmgBlocks count: " + pmgBlocks.Length); TriangleMeshConstructor mc = new TriangleMeshConstructor(); var uvs = mc.UvChannel(0); int blockSideCount = (int)Math.Sqrt(pmgBlocks.Length); int blockx = 0; int blocky = 0; List colors = new List(); short zoffset = -32001; var cornerX = -8 * 4 * 512; var cornerY = 8 * 4 * 512; var cornerDX = cornerX - pmgBlocks[0].xOffset; var cornerDY = cornerY - pmgBlocks[0].zOffset; foreach (var block in pmgBlocks) { float x = block.xOffset + cornerDX; float z = block.zOffset+ cornerDY; if(zoffset != block.zOffset) { zoffset = block.zOffset; Debug.Log("zoffset: " + zoffset); } var vertices = block.vertices; int indexOffset = result.Count; for (int i = 0; i < vertices.Length; ++i) { var v = vertices[i]; int vx = (i % 5) * 512; int vy = (i / 5) * 512; int vXId = blockx * 4 + (i % 5); int vYId = blocky * 4 + (i / 5); uvs.Add(new Vector2(vXId / (float)(blockSideCount * 4), vYId / (float)(blockSideCount * 4))); result.Add(new Vector3(-(x + vx), v.height, (-z + vy)) * unitScale); float l = v.lighting / 255.0f; colors.Add(new Color(l, l, l, 1.0f)); } for (int yVertex = 0; yVertex < 4; yVertex++) { for (int xVertex = 0; xVertex < 4; xVertex++) { int i0 = (yVertex + 0) * 5 + (xVertex + 0); int i2 = (yVertex + 1) * 5 + (xVertex + 0); int i1 = (yVertex + 0) * 5 + (xVertex + 1); int i3 = (yVertex + 0) * 5 + (xVertex + 1); int i5 = (yVertex + 1) * 5 + (xVertex + 0); int i4 = (yVertex + 1) * 5 + (xVertex + 1); indices.Add(i0 + indexOffset); indices.Add(i1 + indexOffset); indices.Add(i2 + indexOffset); indices.Add(i3 + indexOffset); indices.Add(i4 + indexOffset); indices.Add(i5 + indexOffset); } } blockx++; if(blockx == blockSideCount) { blockx = 0; blocky++; } } mc.Vertices = result; mc.Indices = indices.ToArray(); mc.Colors = colors; // mc.AssingColors(Color.white); mc.BreakVertices(); mc.RecalculateFlatNormals(); var pm = GetComponent(); pm.Rebuild(mc); BuildMapTexture(pmg, ptg); GetComponent().sharedMaterial.mainTexture = Atlas; } }