using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Runtime.InteropServices; using VL.Pipeline.Physics; using VL.Pipeline.Rendering; using VL.Pipeline.Window; using static VL.Pipeline.Rendering.SectorRenderer; namespace VL.Main { class Program { [DllImport("user32.dll")] private static extern short GetAsyncKeyState(int vKey); const int VK_W = 0x57; const int VK_S = 0x53; const int VK_A = 0x41; const int VK_D = 0x44; const int VK_LEFT = 0x25; const int VK_RIGHT = 0x27; const int VK_SPACE = 0x20; const int VK_F1 = 0x70; const int VK_F2 = 0x71; const int VK_F3 = 0x72; private static int frameCounter = 0; private static long lastFpsTime = 0; private static int currentFps = 0; private static Bitmap renderTarget; private static Graphics renderGraphics; private static int renderWidth = 320; private static int renderHeight = 240; static void Main() { Console.Clear(); Console.OutputEncoding = System.Text.Encoding.UTF8; Console.WriteLine("======================================"); Console.WriteLine(" vector's renderer booted "); Console.WriteLine("======================================"); renderTarget = new Bitmap(renderWidth, renderHeight); renderGraphics = Graphics.FromImage(renderTarget); renderGraphics.SmoothingMode = SmoothingMode.None; renderGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; var textureManager = new TextureManager(); var renderer = new SectorRenderer(renderWidth, renderHeight, textureManager); renderer.height = 24; var lighting = new LightingRenderer(renderWidth, renderHeight); lighting.SetAmbientLighting(0.2f, Color.FromArgb(30, 40, 60)); lighting.SetSectorLighting(0.3f, Color.FromArgb(120, 130, 140)); lighting.AddTorchLight(-50, -50, 0.8f); lighting.AddTorchLight(50, 50, 0.8f); lighting.AddSpotlight(0, -75, 1.2f); CreatePS3DoomTextures(textureManager); var stopwatch = Stopwatch.StartNew(); long lastTime = stopwatch.ElapsedMilliseconds; CreateTestMap(renderer); renderer.playerx = 0; renderer.playery = 1; renderer.angle = 0; var collider = new Collider2D(renderer.world, playerRadius: 4f); using var window = new SimpleWindowCreator(800, 600, "PS3 DOOM STYLE - vector's renderer"); window.Run(g => { long now = stopwatch.ElapsedMilliseconds; float delta = Math.Min((now - lastTime) / 1000f, 0.033f); lastTime = now; lighting.Update(now); frameCounter++; if (now - lastFpsTime >= 1000) { currentFps = frameCounter; frameCounter = 0; lastFpsTime = now; } float moveSpeed = 50f * delta; float turnSpeed = 90f * delta; string input = HandleInput(renderer, collider, lighting, moveSpeed, turnSpeed); renderGraphics.Clear(Color.FromArgb(10, 15, 25)); RenderPS3DoomStyle(renderGraphics, renderer, lighting); g.InterpolationMode = InterpolationMode.NearestNeighbor; g.PixelOffsetMode = PixelOffsetMode.None; g.DrawImage(renderTarget, 0, 0, 800, 600); if (frameCounter % 10 == 0) { Console.SetCursorPosition(0, 4); Console.WriteLine("PS3 DOOM STYLE RENDERER".PadRight(30)); Console.WriteLine("input: ".PadRight(10) + input.PadRight(20)); Console.WriteLine($"pos: ({renderer.playerx:0.0}, {renderer.playery:0.0})".PadRight(30)); Console.WriteLine($"angle: {MathF.Round(renderer.angle * 180f / MathF.PI):0} deg".PadRight(20)); Console.WriteLine($"fps: {currentFps}".PadRight(10)); Console.WriteLine($"is colliding: {collider.IsCollidingNow}"); Console.WriteLine($"resolution: {renderWidth}x{renderHeight}".PadRight(20)); Console.WriteLine($"lighting: {lighting.GetLightingInfo()}".PadRight(30)); if (now % 2000 < 16) GC.Collect(0, GCCollectionMode.Optimized); } //foreach (var wall in renderer.render()) //{// //Console.WriteLine($"Wall: x={wall.x}, top={wall.top}, bottom={wall.bottom}, textureId={wall.textureId}"); //} }); renderGraphics?.Dispose(); renderTarget?.Dispose(); Console.WriteLine(); Console.WriteLine("game over man"); Console.ReadKey(); } private static void RenderPS3DoomStyle(Graphics g, SectorRenderer renderer, LightingRenderer lighting) { int[] pixelBuffer = new int[renderer.w * renderer.h]; for (int i = 0; i < pixelBuffer.Length; i++) { pixelBuffer[i] = Color.FromArgb(20, 20, 30).ToArgb(); } foreach (var wall in renderer.render()) { Color lightColor = lighting.CalculateLighting(wall.wallX, wall.wallY); float lightR = lightColor.R / 255.0f; float lightG = lightColor.G / 255.0f; float lightB = lightColor.B / 255.0f; float distanceFactor = Math.Min(1.0f, 30.0f / (wall.distance * wall.distance * 0.1f)); distanceFactor = Math.Max(0.1f, distanceFactor); int yStart = Math.Max(0, wall.top); int yEnd = Math.Min(renderer.h, wall.bottom); for (int y = yStart; y < yEnd; y++) { if (wall.x < 0 || wall.x >= renderer.w || y < 0 || y >= renderer.h) continue; int baseARGB = renderer.GetWallPixelColor(wall, y, renderer.h); int baseR = (baseARGB >> 16) & 0xFF; int baseG = (baseARGB >> 8) & 0xFF; int baseB = baseARGB & 0xFF; int litR = Math.Min(255, (int)(baseR * lightR * 1.2f)); int litG = Math.Min(255, (int)(baseG * lightG * 1.2f)); int litB = Math.Min(255, (int)(baseB * lightB * 1.2f)); int finalR = Math.Max(0, (int)(litR * distanceFactor)); int finalG = Math.Max(0, (int)(litG * distanceFactor)); int finalB = Math.Max(0, (int)(litB * distanceFactor)); if (y % 2 == 0 && y < renderer.h / 2) { finalR = (int)(finalR * 0.95f); finalG = (int)(finalG * 0.95f); finalB = (int)(finalB * 0.95f); } int finalARGB = (255 << 24) | (finalR << 16) | (finalG << 8) | finalB; pixelBuffer[y * renderer.w + wall.x] = finalARGB; } } using (var bitmap = new Bitmap(renderer.w, renderer.h)) { var bitmapData = bitmap.LockBits(new Rectangle(0, 0, renderer.w, renderer.h), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); System.Runtime.InteropServices.Marshal.Copy(pixelBuffer, 0, bitmapData.Scan0, pixelBuffer.Length); bitmap.UnlockBits(bitmapData); g.DrawImage(bitmap, 0, 0); } } private static void CreatePS3DoomTextures(TextureManager textureManager) { textureManager.LoadTexture(1, "E:\\!!- Other Cool Stuff -!!\\VS-Studio\\VectorsRenderer\\VectorsRenderer\\Textures\\brickslmao.png"); textureManager.LoadTexture(2, "E:\\!!- Other Cool Stuff -!!\\VS-Studio\\VectorsRenderer\\VectorsRenderer\\Textures\\MissingTexture.png"); textureManager.LoadTexture(3, "E:\\!!- Other Cool Stuff -!!\\VS-Studio\\VectorsRenderer\\VectorsRenderer\\Textures\\Tile.png"); } private static string HandleInput(SectorRenderer renderer, Collider2D collider, LightingRenderer lighting, float moveSpeed, float turnSpeed) { float dx = 0f; float dy = 0f; float angle = renderer.angle; float forwardX = MathF.Sin(angle); float forwardY = MathF.Cos(angle); float rightX = MathF.Cos(angle); float rightY = -MathF.Sin(angle); string action = "none"; if (IsHeld(VK_W)) { dx += forwardX * moveSpeed; dy += forwardY * moveSpeed; action = "forward"; } if (IsHeld(VK_S)) { dx -= forwardX * moveSpeed; dy -= forwardY * moveSpeed; action = "backward"; } if (IsHeld(VK_A)) { dx -= rightX * moveSpeed; dy -= rightY * moveSpeed; action = "strafe left"; } if (IsHeld(VK_D)) { dx += rightX * moveSpeed; dy += rightY * moveSpeed; action = "strafe right"; } if (IsHeld(VK_SPACE)) { lighting.AddLight(renderer.playerx, renderer.playery, 15000, Color.FromArgb(255, 200, 150)); return "light added"; } if (dx != 0f || dy != 0f) { float oldX = renderer.playerx; float oldY = renderer.playery; renderer.move(dx, dy); if (collider.IsColliding(renderer.playerx, renderer.playery)) { renderer.playerx = oldX; renderer.playery = oldY; } return $"{action} - moving ({dx:0.2f}, {dy:0.2f}) - angle: {angle * 180f / MathF.PI:0.1f}°"; } if (IsHeld(VK_RIGHT)) { renderer.rotate(-turnSpeed); return "→ (turn right)"; } if (IsHeld(VK_LEFT)) { renderer.rotate(turnSpeed); return "← (turn left)"; } return "none"; } private static void CreateTestMap(SectorRenderer renderer) { int wallTex = 2; Vertex v(float x, float y) => new Vertex(x, y); var world = new List(); var room = new Sector { floor = 0, ceil = 80 }; //room.flatPieces.Add(new SectorRenderer.FlatPiece(new SectorRenderer.Vertex(0, 80), new SectorRenderer.Vertex(0, 80), 2)); room.walls.Add(new Wall(v(-100, -100), v(100, -100), wallTex)); room.walls.Add(new Wall(v(100, -100), v(100, 100), wallTex)); room.walls.Add(new Wall(v(100, 100), v(-100, 100), wallTex)); room.walls.Add(new Wall(v(-100, 100), v(-100, -100), wallTex)); room.UpdateBounds(); world.Add(room); renderer.world.Clear(); foreach (var sector in world) { renderer.world.Add(sector); } } private static bool IsHeld(int key) { return (GetAsyncKeyState(key) & 0x8000) != 0; } } }