Fixed unity terrain generation bug.

This commit is contained in:
Tiberiu Chibici 2015-03-19 12:34:58 +02:00
parent 8f9f935796
commit 68140b11a7
40 changed files with 326 additions and 245 deletions

6
.gitignore vendored
View File

@ -1,5 +1,7 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# Logs
Game/Logs/
Game/[Oo]bj/
Game/[Tt]emp/
# User-specific files
*.suo

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<biome>
<name>Grassland</name>
<heightRange min="0" max="10" />
<moisture min=".1" max=".5"/>
<vegetationDensity min=".2" max=".5" />
<?xml version="1.0" encoding="utf-8" ?>
<biome>
<name>Grassland</name>
<height>100</height>
<moisture min=".1" max=".5"/>
<vegetationDensity min=".2" max=".5" />
</biome>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<biome>
<name>Mountain</name>
<heightRange min="0" max="500" />
<moisture min=".1" max=".3"/>
<vegetationDensity min=".5" max=".9" />
<?xml version="1.0" encoding="utf-8" ?>
<biome>
<name>Mountain</name>
<height>500</height>
<moisture min=".1" max=".3"/>
<vegetationDensity min=".5" max=".9" />
</biome>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<terrgenConfig>
<noiseOctaves>8</noiseOctaves>
<noiseNonLinearPower>2</noiseNonLinearPower>
<waterNonLinearPower>1.3</waterNonLinearPower>
<elevationScale>0.01</elevationScale>
<noiseOctaves>9</noiseOctaves>
<noiseNonLinearPower>2.4</noiseNonLinearPower>
<waterNonLinearPower>1.9</waterNonLinearPower>
<elevationScale>0.0005</elevationScale>
</terrgenConfig>

Binary file not shown.

View File

@ -45,10 +45,14 @@ namespace TransportGame.Generator
// Generate water level
float waterAmount = random.NextSingle(map.Biome.Moisture.Minimum, map.Biome.Moisture.Maximum);
map.WaterLevel = Mathf.Pow(waterAmount, ConfigurationManager.TerrGenConfig.WaterNonLinearPower)
* (map.Biome.HeightRange.Maximum - map.Biome.HeightRange.Minimum) + map.Biome.HeightRange.Minimum;
map.WaterLevel = Mathf.Pow(waterAmount, ConfigurationManager.TerrGenConfig.WaterNonLinearPower) * map.Biome.Height;
DumpData(map, "1generated.map");
// Simulate water erosion
//Erode(map);
//DumpData(map, "2eroded.map");
DumpData(map);
return map;
}
@ -64,14 +68,21 @@ namespace TransportGame.Generator
{
for (int x = 0; x < map.Width; ++x)
for (int y = 0; y < map.Height; ++y)
map[x, y] = Noise.Generate(x, y, map.Biome.HeightRange.Minimum, map.Biome.HeightRange.Maximum);
map[x, y] = Noise.Generate(x, y, 0, 1);
}
private void DumpData(Map map)
//private void Erode(Map map)
//{
// float[,] water = new float[map.Width, map.Height];
//}
private void DumpData(Map map, string filename)
{
XmlSerializer serializer = new XmlSerializer(typeof(Map));
using (StreamWriter writer = new StreamWriter(Path.Combine(Logger.LogsDirectory, "mapdump.map")))
using (StreamWriter writer = new StreamWriter(Path.Combine(Logger.LogsDirectory, filename)))
{
serializer.Serialize(writer, map);
writer.Close();

View File

@ -1,44 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using TransportGame.Utils;
namespace TransportGame.Model
{
[XmlRoot("biome")]
public class Biome
{
/// <summary>
/// Gets or sets the name of the biome
/// </summary>
[XmlElement("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the height range of the biome.
/// </summary>
/// <remarks>
/// 1 unit = 100 meters.
/// </remarks>
[XmlElement("heightRange")]
public Range HeightRange { get; set; }
/// <summary>
/// Gets or sets the moisture range.
/// </summary>
/// <remarks>
/// Moisture is the amount of water on a map.
/// Value is a probability, should be between 0 and 1.
/// </remarks>
[XmlElement("moisture")]
public Range Moisture { get; set; }
/// <summary>
/// Gets or sets the vegetation density of the biome
/// </summary>
[XmlElement("vegetationDensity")]
public Range VegetationDensity { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using TransportGame.Utils;
namespace TransportGame.Model
{
[XmlRoot("biome")]
public class Biome
{
/// <summary>
/// Gets or sets the name of the biome
/// </summary>
[XmlElement("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the maximum height of the biome.
/// </summary>
/// <remarks>
/// 1 unit = 100 meters.
/// </remarks>
[XmlElement("height")]
public float Height { get; set; }
/// <summary>
/// Gets or sets the moisture range.
/// </summary>
/// <remarks>
/// Moisture is the amount of water on a map.
/// Value is a probability, should be between 0 and 1.
/// </remarks>
[XmlElement("moisture")]
public Range Moisture { get; set; }
/// <summary>
/// Gets or sets the vegetation density of the biome
/// </summary>
[XmlElement("vegetationDensity")]
public Range VegetationDensity { get; set; }
}
}

View File

@ -140,7 +140,7 @@ namespace TransportGame.Model
/// <returns></returns>
public bool IsWater(int x, int y)
{
return grid[x, y] <= WaterLevel;
return grid[x, y] * Biome.Height <= WaterLevel;
}
}
}

View File

@ -1,87 +1,87 @@
using UnityEngine;
using System.Collections;
using TransportGame.Utils;
using System.Threading;
using TransportGame.Generator;
using TransportGame.Model;
using TransportGame.Business;
public class TerrainGeneratorScript : MonoBehaviour
{
private Map map = null;
public int TerrainWidth = 256;
public int TerrainHeight = 256;
public GameObject WaterObject;
// Use this for initialization
void Start()
{
StartCoroutine(GenerateMap());
}
private void GenerateMapThread()
{
TerrainGenerator generator = new TerrainGenerator();
map = generator.Generate(TerrainWidth, TerrainHeight);
}
private Mesh GenerateWater()
{
Mesh water = new Mesh();
water.name = "water";
water.vertices = new[] {
new Vector3(0, map.WaterLevel, 0),
new Vector3(0, map.WaterLevel, map.Height),
new Vector3(map.Width, map.WaterLevel, 0),
new Vector3(map.Width, map.WaterLevel, map.Height)
};
water.triangles = new[] { 0, 1, 2, 2, 1, 3 };
water.uv = new[] {
new Vector2(0, 0),
new Vector2(0, 1),
new Vector2(1, 0),
new Vector2(1, 1)
};
water.RecalculateNormals();
return water;
}
private IEnumerator GenerateMap()
{
// Wait for the map generation thread
foreach (var i in Task.RunAsync(GenerateMapThread))
yield return i;
// Generate terrain data
TerrainData terrainData = new TerrainData();
terrainData.heightmapResolution = Mathf.Max(map.Height, map.Width);
terrainData.size = new Vector3(map.Width, map.Biome.HeightRange.Maximum, map.Height);
terrainData.SetDetailResolution(1024, 8);
terrainData.SetHeights(0, 0, map.Heights);
terrainData.name = "Generated Terrain Data";
yield return null;
// Create terrain object
GameObject terrain = Terrain.CreateTerrainGameObject(terrainData);
terrain.name = "Generated Terrain";
yield return null;
Terrain terrainComp = terrain.GetComponent<Terrain>();
terrainComp.heightmapPixelError = 1;
yield return null;
// Set water
if (WaterObject != null)
{
MeshFilter waterMesh = WaterObject.GetComponent<MeshFilter>();
waterMesh.mesh = GenerateWater();
}
}
// Update is called once per frame
void Update()
{
}
}
using UnityEngine;
using System.Collections;
using TransportGame.Utils;
using System.Threading;
using TransportGame.Generator;
using TransportGame.Model;
using TransportGame.Business;
public class TerrainGeneratorScript : MonoBehaviour
{
private Map map = null;
public int TerrainWidth = 256;
public int TerrainHeight = 256;
public GameObject WaterObject;
// Use this for initialization
void Start()
{
StartCoroutine(GenerateMap());
}
private void GenerateMapThread()
{
TerrainGenerator generator = new TerrainGenerator();
map = generator.Generate(TerrainWidth, TerrainHeight);
}
private Mesh GenerateWater()
{
Mesh water = new Mesh();
water.name = "water";
water.vertices = new[] {
new Vector3(0, map.WaterLevel, 0),
new Vector3(0, map.WaterLevel, map.Height),
new Vector3(map.Width, map.WaterLevel, 0),
new Vector3(map.Width, map.WaterLevel, map.Height)
};
water.triangles = new[] { 0, 1, 2, 2, 1, 3 };
water.uv = new[] {
new Vector2(0, 0),
new Vector2(0, 1),
new Vector2(1, 0),
new Vector2(1, 1)
};
water.RecalculateNormals();
return water;
}
private IEnumerator GenerateMap()
{
// Wait for the map generation thread
foreach (var i in Task.RunAsync(GenerateMapThread))
yield return i;
// Generate terrain data
TerrainData terrainData = new TerrainData();
terrainData.heightmapResolution = Mathf.Max(map.Height, map.Width) + 1;
terrainData.size = new Vector3(map.Width, map.Biome.Height, map.Height);
terrainData.SetDetailResolution(1024, 8);
terrainData.SetHeights(0, 0, map.Heights);
terrainData.name = "Generated Terrain Data";
yield return null;
// Create terrain object
GameObject terrain = Terrain.CreateTerrainGameObject(terrainData);
terrain.name = "Generated Terrain";
yield return null;
Terrain terrainComp = terrain.GetComponent<Terrain>();
terrainComp.heightmapPixelError = 1;
yield return null;
// Set water
if (WaterObject != null)
{
MeshFilter waterMesh = WaterObject.GetComponent<MeshFilter>();
waterMesh.mesh = GenerateWater();
}
}
// Update is called once per frame
void Update()
{
}
}

View File

@ -23,7 +23,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = Assembly-CSharp.csproj
Policies = $0
$0.TextStylePolicy = $1

View File

@ -29,7 +29,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = Assembly-CSharp.csproj
Policies = $0
$0.TextStylePolicy = $1

Binary file not shown.

Binary file not shown.

View File

@ -1,9 +1,3 @@
Base path: E:/SDKs/Unity/Editor/Data
Pipe name: \\.\pipe\UnityShaderCompiler-32bit-1-3856
Cmd: getPlatforms
Unhandled exception: Readfile from pipe failed. GLE=The pipe has been ended.
Quitting shader compiler process
Crashed!
Base path: E:/SDKs/Unity/Editor/Data
Pipe name: \\.\pipe\UnityShaderCompiler-32bit-1-4308
Cmd: getPlatforms

View File

@ -1,3 +1,7 @@
Base path: E:/SDKs/Unity/Editor/Data
Pipe name: \\.\pipe\UnityShaderCompiler-64bit-1-3856
Pipe name: \\.\pipe\UnityShaderCompiler-64bit-1-4308
Cmd: getPlatforms
Cmd: compileSnippet
api=4 type=0 insize=14871 outsize=3492 kw=DIRECTIONAL SHADOWS_OFF LIGHTMAP_OFF DIRLIGHTMAP_OFF DYNAMICLIGHTMAP_OFF ok=1
Cmd: compileSnippet
api=4 type=1 insize=14871 outsize=10090 kw=DIRECTIONAL SHADOWS_OFF LIGHTMAP_OFF DIRLIGHTMAP_OFF DYNAMICLIGHTMAP_OFF ok=1

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,13 +1,13 @@
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\Assembly-CSharp.dll
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\Assembly-CSharp.pdb
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEditor.dll
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEngine.dll
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEngine.UI.dll
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEditor.xml
E:\Facultate\$ Licenta\Game\obj\Debug\UnityVS.Game.CSharp.csprojResolveAssemblyReference.cache
E:\Facultate\$ Licenta\Game\obj\Debug\Assembly-CSharp.dll
E:\Facultate\$ Licenta\Game\obj\Debug\Assembly-CSharp.pdb
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEngine.xml
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEngine.UI.xml
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\Assembly-CSharp-firstpass.pdb
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\Assembly-UnityScript-firstpass.dll
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\Assembly-CSharp.dll
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\Assembly-CSharp.pdb
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEditor.dll
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEngine.dll
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEngine.UI.dll
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEditor.xml
E:\Facultate\$ Licenta\Game\obj\Debug\UnityVS.Game.CSharp.csprojResolveAssemblyReference.cache
E:\Facultate\$ Licenta\Game\obj\Debug\Assembly-CSharp.dll
E:\Facultate\$ Licenta\Game\obj\Debug\Assembly-CSharp.pdb
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEngine.xml
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\UnityEngine.UI.xml
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\Assembly-CSharp-firstpass.pdb
E:\Facultate\$ Licenta\Game\Temp\UnityVS_bin\Debug\Assembly-UnityScript-firstpass.dll

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using TransportGame.MapViewer.Model;
namespace TransportGame.MapViewer.Business
{
public static class BitmapExtensions
{
/// <summary>
/// Gets a wpf bitmap source from a bitmap.
/// </summary>
/// <param name="this">The bitmap</param>
/// <returns>The bitmap source</returns>
public static BitmapSource ToBitmapSource(this Bitmap24 @this)
{
return BitmapSource.Create(@this.Width, @this.Height,
96, 96, PixelFormats.Rgb24, null, @this.Raw, @this.Width * 3);
}
}
}

View File

@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using TransportGame.MapViewer.Model;
using TransportGame.Model;
namespace TransportGame.MapViewer
@ -23,8 +18,8 @@ namespace TransportGame.MapViewer
/// <summary>
/// Gets or sets the scale
/// </summary>
private int _scale;
public int Scale
private float _scale;
public float Scale
{
get
{
@ -45,7 +40,7 @@ namespace TransportGame.MapViewer
public MapRenderer()
{
Scale = 4;
Scale = 1;
}
///<summary>
@ -54,72 +49,46 @@ namespace TransportGame.MapViewer
///<param name="file"></param>
///<param name="map"></param>
///<param name="layers"></param>
public Color[,] Render(Map map, Layers layers = Layers.All)
public Bitmap24 Render(Map map, Layers layers = Layers.All)
{
lock (this)
{
// Create texture on which to draw
Color[,] pixels = new Color[map.Width * Scale, map.Height * Scale];
Bitmap24 bitmap = new Bitmap24(Convert.ToInt32(map.Width * Scale), Convert.ToInt32(map.Height * Scale));
// First layer - cells
DrawCells(pixels, map, (layers & Layers.Elevation) > 0);
DrawCells(bitmap, map, (layers & Layers.Elevation) > 0);
// Done
return pixels;
return bitmap;
}
}
private void DrawCells(Color[,] pixels, Map map, bool elevation)
private void DrawCells(Bitmap24 bitmap, Map map, bool elevation)
{
for (int x = 0; x < pixels.GetLength(0); x++)
for (int y = 0; y < pixels.GetLength(1); y++)
for (int x = 0; x < bitmap.Width; x++)
for (int y = 0; y < bitmap.Height; y++)
{
int mapX = x / Scale;
int mapY = y / Scale;
int mapX = Convert.ToInt32(x / Scale);
int mapY = Convert.ToInt32(y / Scale);
// Draw water
if (map.IsWater(mapX, mapY))
pixels[x, y] = WaterColor;
bitmap[x, y] = WaterColor;
// Draw elevation
else if (elevation)
{
float alpha = (map[mapX, mapY] - map.Biome.HeightRange.Minimum) / (map.Biome.HeightRange.Maximum - map.Biome.HeightRange.Minimum);
pixels[x, y] = Color.Multiply(ElevationTerrainColor, alpha);
float alpha = map[mapX, mapY]; // map height range is [0,1]
bitmap[x, y] = Color.Multiply(ElevationTerrainColor, alpha);
}
// Draw terrain
else
{
pixels[x, y] = TerrainColor;
bitmap[x, y] = TerrainColor;
}
}
}
public async Task<BitmapSource> RenderToImageSource(Map map, Layers layers = Layers.All)
{
var colors = await Task.Run(() => Render(map, layers));
colors[0, 0] = Colors.Red;
colors[colors.GetLength(0) - 1, 0] = Colors.Red;
// Convert to raw pixels
byte[] raw = new byte[colors.GetLength(0) * colors.GetLength(1) * 3];
for (int x = 0; x < colors.GetLength(0); x++)
for (int y = 0; y < colors.GetLength(1); y++)
{
int pixelIndex = x + y * colors.GetLength(0);
raw[3 * pixelIndex] = colors[x, y].R;
raw[3 * pixelIndex + 1] = colors[x, y].G;
raw[3 * pixelIndex + 2] = colors[x, y].B;
}
// Create bitmap source
BitmapSource bmp = BitmapSource.Create(colors.GetLength(0), colors.GetLength(1),
96, 96, PixelFormats.Rgb24, null, raw, colors.GetLength(0) * 3);
return bmp;
}
}
}

View File

@ -21,6 +21,7 @@
<Button Name="buttonSave" ToolTip="Saves as image" Click="buttonSave_Click">
<Image Source="pack://application:,,,/Resources/picture_save.png" />
</Button>
<Separator />
<Button Name="buttonZoomIn" ToolTip="Zoom in" Click="buttonZoomIn_Click">
@ -31,7 +32,7 @@
</Button>
<TextBlock VerticalAlignment="Center">
<Run Text="{Binding ZoomLevel}" /><Run>00%</Run>
<Run Text="{Binding ZoomLevel}" /><Run>%</Run>
</TextBlock>
</ToolBar>
@ -114,8 +115,8 @@
<TextBlock Grid.Row="0" Grid.Column="0" >Name:</TextBlock>
<TextBox Grid.Row="0" Grid.Column="1" IsReadOnly="True" Text="{Binding Map.Biome.Name,Mode=OneWay}" />
<TextBlock Grid.Row="1" Grid.Column="0" >Height range:</TextBlock>
<TextBox Grid.Row="1" Grid.Column="1" IsReadOnly="True" Text="{Binding Map.Biome.HeightRange,Mode=OneWay}" />
<TextBlock Grid.Row="1" Grid.Column="0" >Height:</TextBlock>
<TextBox Grid.Row="1" Grid.Column="1" IsReadOnly="True" Text="{Binding Map.Biome.Height,Mode=OneWay}" />
<TextBlock Grid.Row="2" Grid.Column="0" >Moisture:</TextBlock>
<TextBox Grid.Row="2" Grid.Column="1" IsReadOnly="True" Text="{Binding Map.Biome.Moisture,Mode=OneWay}" />

View File

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using TransportGame.MapViewer.Business;
using TransportGame.MapViewer.Storage;
using TransportGame.Model;
@ -152,15 +153,15 @@ namespace TransportGame.MapViewer
/// <summary>
/// Gets or sets the zoom level
/// </summary>
public int ZoomLevel
public float ZoomLevel
{
get
{
return Renderer.Scale;
return Renderer.Scale * 100f;
}
set
{
Renderer.Scale = value;
Renderer.Scale = value / 100f;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("ZoomLevel"));
}
@ -215,7 +216,8 @@ namespace TransportGame.MapViewer
try
{
RenderedMap = await Renderer.RenderToImageSource(Map, _layers);
var renderResult = await Task.Run(() => Renderer.Render(Map, _layers));
RenderedMap = renderResult.ToBitmapSource();
}
catch (Exception ex)
{
@ -260,10 +262,10 @@ namespace TransportGame.MapViewer
private async void buttonZoomIn_Click(object sender, RoutedEventArgs e)
{
// Zoom
ZoomLevel++;
ZoomLevel *= 2;
// Update IsEnabled
if (ZoomLevel >= 10)
if (ZoomLevel >= 400)
buttonZoomIn.IsEnabled = false;
buttonZoomOut.IsEnabled = true;
@ -275,10 +277,10 @@ namespace TransportGame.MapViewer
private async void buttonZoomOut_Click(object sender, RoutedEventArgs e)
{
// Zoom
ZoomLevel--;
ZoomLevel /=2;
// Update IsEnabled
if (ZoomLevel <= 1)
if (ZoomLevel <= 10)
buttonZoomOut.IsEnabled = false;
buttonZoomIn.IsEnabled = true;
@ -332,8 +334,7 @@ namespace TransportGame.MapViewer
filename = dialog.FileName;
return true;
}
#endregion
}
}

View File

@ -59,6 +59,8 @@
<Compile Include="..\..\..\Game\Assets\Scripts\Model\Map.cs">
<Link>Model\Map.cs</Link>
</Compile>
<Compile Include="Business\BitmapExtensions.cs" />
<Compile Include="Model\Bitmap24.cs" />
<Compile Include="Storage\MapStorage.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace TransportGame.MapViewer.Model
{
public class Bitmap24
{
/// <summary>
/// Gets the width of the bitmap
/// </summary>
public int Width { get; private set; }
/// <summary>
/// Gets the height of the bitmap
/// </summary>
public int Height { get; private set; }
/// <summary>
/// Gets the raw bytes of the bitmap
/// </summary>
public byte[] Raw { get; private set; }
/// <summary>
/// Initializes a bitmap
/// </summary>
/// <param name="w"></param>
/// <param name="h"></param>
public Bitmap24(int w, int h)
{
Width = w;
Height = h;
Raw = new byte[w * h * 3];
}
/// <summary>
/// Gets or sets a pixel
/// </summary>
/// <param name="x">x coordinate of pixel</param>
/// <param name="y">y coordinate of pixel</param>
public Color this[int x, int y]
{
get
{
int index = RawIndexOf(x, y);
return Color.FromRgb(Raw[index], Raw[index + 1], Raw[index + 2]);
}
set
{
int index = RawIndexOf(x, y);
Raw[index] = value.R;
Raw[index + 1] = value.G;
Raw[index + 2] = value.B;
}
}
private int RawIndexOf(int x, int y)
{
if (x < 0 || x >= Width)
throw new ArgumentOutOfRangeException("x is out of range");
if (y < 0 || y >= Height)
throw new ArgumentOutOfRangeException("x is out of range");
return 3 * (x + y * Width);
}
}
}