Skip to content

Commit

Permalink
my way for a skybox
Browse files Browse the repository at this point in the history
  • Loading branch information
LoneWandererProductions committed Jan 27, 2025
1 parent 442c446 commit 4bd92b0
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 0 deletions.
113 changes: 113 additions & 0 deletions RenderEngine/WorldCamera.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;

namespace RenderEngine
{

public class WorldCamera
{
/// <summary>
/// X position on the map.
/// </summary>
public int X { get; set; }

/// <summary>
/// Y position on the map.
/// </summary>
public int Y { get; set; }

/// <summary>
/// Z position:
/// Z = −CellSize / 2: Bottom of the cell.
/// Z = +CellSize / 2: Top of the cell.
/// </summary>
public int Z { get; set; }

/// <summary>
/// Offset of the horizon position (looking up-down).
/// </summary>
public int Horizon { get; set; }

/// <summary>
/// Distance of the camera looking forward.
/// </summary>
public float ZFar { get; init; }

/// <summary>
/// Camera angle (radians, clockwise).
/// </summary>
public float Angle { get; set; }

/// <summary>
/// Camera pitch (vertical angle, radians, looking up-down).
/// </summary>
public float Pitch { get; set; }

/// <summary>
/// Field of view in degrees.
/// </summary>
public float FieldOfView { get; set; } = 90f;

/// <summary>
/// Cell size (dimensions of each unit cube in the map).
/// </summary>
public int CellSize { get; init; } = 1;

/// <summary>
/// Calculates the visible bounds in the 3D world based on camera settings.
/// </summary>
public (float left, float right, float top, float bottom) GetViewFrustum()
{
// Compute half-width/height of the frustum at ZFar
float halfHeight = (float)(Math.Tan(FieldOfView * Math.PI / 360) * ZFar);
float halfWidth = halfHeight;

return (
left: X - halfWidth,
right: X + halfWidth,
top: Y + halfHeight,
bottom: Y - halfHeight
);
}

/// <summary>
/// Moves the camera in the given direction.
/// </summary>
public void Move(float deltaX, float deltaY, float deltaZ)
{
X += (int)deltaX;
Y += (int)deltaY;
Z += (int)deltaZ;
}

/// <summary>
/// Rotates the camera by the specified angle (in radians).
/// </summary>
public void Rotate(float deltaAngle)
{
Angle += deltaAngle;
Angle %= (float)(2 * Math.PI); // Keep angle within 0 to 2π
}

/// <summary>
/// Adjusts the pitch of the camera.
/// </summary>
public void AdjustPitch(float deltaPitch)
{
Pitch += deltaPitch;
Pitch = Math.Clamp(Pitch, -MathF.PI / 2, MathF.PI / 2); // Clamp between -90° and +90°
}

/// <summary>
/// Simulates rendering based on the camera's field of view and position.
/// </summary>
public void Render()
{
var frustum = GetViewFrustum();
Console.WriteLine($"Rendering frustum:");
Console.WriteLine($"Left: {frustum.left}, Right: {frustum.right}, Top: {frustum.top}, Bottom: {frustum.bottom}");
Console.WriteLine($"Camera Position: ({X}, {Y}, {Z})");
Console.WriteLine($"Angle: {Angle} radians, Pitch: {Pitch} radians");
}
}

}
132 changes: 132 additions & 0 deletions RenderEngine/WorldRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RenderEngine
{
public class WorldRenderer
{
public int CellSize { get; set; }
public int WorldLength { get; set; }
public int WorldWidth { get; set; }
public int WorldHeight { get; set; }

// Simulates the world as a 3D array of colors (can be replaced with a more complex system)
private readonly Color[,,] _worldData;

public WorldRenderer(int length, int width, int height, int cellSize)
{
WorldLength = length;
WorldWidth = width;
WorldHeight = height;
CellSize = cellSize;

// Initialize the world data with random colors for demonstration
_worldData = new Color[WorldLength, WorldWidth, WorldHeight];
var random = new Random();
for (int x = 0; x < WorldLength; x++)
{
for (int y = 0; y < WorldWidth; y++)
{
for (int z = 0; z < WorldHeight; z++)
{
_worldData[x, y, z] = Color.FromArgb(
random.Next(256), random.Next(256), random.Next(256));
}
}
}
}

public Bitmap Render(WorldCamera camera, int resolutionWidth, int resolutionHeight)
{
var image = new Bitmap(resolutionWidth, resolutionHeight);

// Precompute frustum parameters
float halfFOV = camera.FieldOfView * (float)Math.PI / 360; // FOV/2 in radians
float aspectRatio = (float)resolutionWidth / resolutionHeight;
float tanHalfFOV = (float)Math.Tan(halfFOV);

// Loop through each pixel in the image
for (int px = 0; px < resolutionWidth; px++)
{
for (int py = 0; py < resolutionHeight; py++)
{
// Normalize pixel coordinates to [-1, 1] range
float nx = (2f * px / resolutionWidth - 1f) * aspectRatio;
float ny = 1f - 2f * py / resolutionHeight;

// Calculate ray direction in world space
float dx = nx * tanHalfFOV;
float dy = ny * tanHalfFOV;
float dz = 1f; // Assume forward direction

// Apply camera rotation
(dx, dy, dz) = RotateRay(dx, dy, dz, (int)camera.Angle, (int)camera.Pitch);

// Cast the ray and get the color
Color color = CastRay(camera.X, camera.Y, camera.Z, dx, dy, dz);

// Set the pixel color
image.SetPixel(px, py, color);
}
}

return image;
}

private Color CastRay(float startX, float startY, float startZ, float dx, float dy, float dz)
{
var t = 0f;
while (t < 1000) // Arbitrary large number to prevent infinite loops
{
// Calculate current position along the ray
float x = startX + t * dx;
float y = startY + t * dy;
float z = startZ + t * dz;

// Convert to world grid coordinates
int cellX = (int)Math.Floor(x / CellSize);
int cellY = (int)Math.Floor(y / CellSize);
int cellZ = (int)Math.Floor(z / CellSize);

// Check if the ray is outside the world bounds
if (cellX < 0 || cellX >= WorldLength ||
cellY < 0 || cellY >= WorldWidth ||
cellZ < 0 || cellZ >= WorldHeight)
{
return Color.Black; // Background color
}

// Return the color of the intersected cell
return _worldData[cellX, cellY, cellZ];
}

return Color.Black; // Default to black if no intersection
}

private (float dx, float dy, float dz) RotateRay(float dx, float dy, float dz, int angle, int pitch)
{
// Convert angles to radians
float angleRad = angle * (float)Math.PI / 180;
float pitchRad = pitch * (float)Math.PI / 180;

// Apply rotation around Y-axis (horizontal angle)
float cosA = (float)Math.Cos(angleRad);
float sinA = (float)Math.Sin(angleRad);
float newDx = dx * cosA - dz * sinA;
float newDz = dx * sinA + dz * cosA;

// Apply rotation around X-axis (vertical pitch)
float cosP = (float)Math.Cos(pitchRad);
float sinP = (float)Math.Sin(pitchRad);
float newDy = dy * cosP - newDz * sinP;
float finalDz = dy * sinP + newDz * cosP;

return (newDx, newDy, finalDz);
}
}

}

0 comments on commit 4bd92b0

Please sign in to comment.