This commit is contained in:
PhoenixARC
2021-08-09 19:05:20 -04:00
parent 6b68cb9b97
commit 10f1c8daa0
320 changed files with 23614 additions and 2195 deletions

View File

@@ -12,21 +12,49 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Debug|x64.ActiveCfg = Debug|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Debug|x64.Build.0 = Debug|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Debug|x86.ActiveCfg = Debug|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Debug|x86.Build.0 = Debug|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Release|Any CPU.Build.0 = Release|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Release|x64.ActiveCfg = Release|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Release|x64.Build.0 = Release|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Release|x86.ActiveCfg = Release|Any CPU
{0ACAAEDE-93F5-4B5D-B8D7-A0C43359C0D6}.Release|x86.Build.0 = Release|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Debug|x64.ActiveCfg = Debug|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Debug|x64.Build.0 = Debug|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Debug|x86.ActiveCfg = Debug|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Debug|x86.Build.0 = Debug|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Release|Any CPU.Build.0 = Release|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Release|x64.ActiveCfg = Release|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Release|x64.Build.0 = Release|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Release|x86.ActiveCfg = Release|Any CPU
{F77A61F1-0C6F-45DC-A5B5-A7BF38D64322}.Release|x86.Build.0 = Release|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Debug|x64.ActiveCfg = Debug|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Debug|x64.Build.0 = Debug|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Debug|x86.ActiveCfg = Debug|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Debug|x86.Build.0 = Debug|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Release|Any CPU.Build.0 = Release|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Release|x64.ActiveCfg = Release|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Release|x64.Build.0 = Release|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Release|x86.ActiveCfg = Release|Any CPU
{3530A9F2-AE0F-44B4-84F9-8FBACB456070}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,41 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO.Compression" publicKeyToken="b77a5c561934e089" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0"/>
<assemblyIdentity name="System.IO.Compression" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0"/>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO.FileSystem" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
<assemblyIdentity name="System.IO.FileSystem" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO.FileSystem.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
<assemblyIdentity name="System.IO.FileSystem.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0"/>
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0"/>
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows.Forms;
@@ -11,6 +9,40 @@ namespace MinecraftUSkinEditor
public class PCK
{
public PCK(string filename)
{
try
{
IsLittleEndian = !(Read(File.ReadAllBytes(filename)));
if(IsLittleEndian)
ReadVita(File.ReadAllBytes(filename));
}
catch
{
}
}
private static byte[] endianReverseUnicode(byte[] str)
{
List<byte> NewBte = new List<byte>();
NewBte.AddRange(str);
NewBte.Reverse();
return NewBte.ToArray();
/* Old Endian Reverse Code
byte[] newStr = new byte[str.Length];
for (int i = 0; i < str.Length; i += 2)
{
newStr[i] = str[i + 1];
newStr[i + 1] = str[i];
}
return newStr;
*/
}
#region Variables
public class MineFile
{
public int filesize;
@@ -26,35 +58,9 @@ namespace MinecraftUSkinEditor
public Dictionary<string, int> typeCodes = new Dictionary<string, int>();
public List<MineFile> mineFiles = new List<MineFile>();
public PCK()
{
#endregion
}
public PCK(string filename)
{
try
{
Read(File.ReadAllBytes(filename));
IsLittleEndian = false;
}
catch
{
ReadVita(File.ReadAllBytes(filename));
IsLittleEndian = true;
}
}
private static byte[] endianReverseUnicode(byte[] str)
{
byte[] newStr = new byte[str.Length];
for (int i = 0; i < str.Length; i += 2)
{
newStr[i] = str[i + 1];
newStr[i + 1] = str[i];
}
return newStr;
}
#region NormalPCK
public static string readMineString(FileData f)
{
@@ -63,6 +69,153 @@ namespace MinecraftUSkinEditor
return Encoding.Unicode.GetString(endianReverseUnicode(f.readBytes(length)));
}
public bool Read(byte[] data)
{
try
{
pckType = 0;
types = new Dictionary<int, string>();
typeCodes = new Dictionary<string, int>();
mineFiles = new List<MineFile>();
FileData fileData = new FileData(data);
fileData.Endian = Endianness.Big;
fileData.readInt();
int entryTypeCount = fileData.readInt();
//int a = 0;
for (int i = 0; i < entryTypeCount; i++)
{
int unk = fileData.readInt();
string text = "";
try
{
text = readMineString(fileData);
//File.WriteAllText(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\exp\\PCKDump" + a + ".bin", text);
//a++;
}
catch
{
text = "Hello!";
}
types.Add(unk, text);
typeCodes.Add(text, unk);
fileData.skip(4);
}
int itemCount = fileData.readInt();
Console.WriteLine(itemCount);
// no metadata
if (entryTypeCount == 0)
{
Console.WriteLine("PckType0");
}
// type 1 or 2
else if (itemCount < 3)
{
pckType = itemCount;
if (pckType == 1)
{
Console.WriteLine("PckType1");
itemCount = fileData.readInt();
}
if (pckType == 2)
Console.WriteLine("PckType2");
}
// regular pck
else
{
Console.WriteLine("NormalPCK");
}
for (int j = 0; j < itemCount; j++)
{
MineFile mineFile = new MineFile();
mineFile.filesize = fileData.readInt();
mineFile.type = fileData.readInt();
int length = fileData.readInt() * 2;
mineFile.name = Encoding.Unicode.GetString(endianReverseUnicode(fileData.readBytes(length)));
fileData.skip(4);
mineFiles.Add(mineFile);
}
foreach (MineFile mineFile2 in mineFiles)
{
int num4 = fileData.readInt();
for (int k = 0; k < num4; k++)
{
object[] array = new object[2];
int key = fileData.readInt();
array[0] = types[key];
array[1] = readMineString(fileData);
fileData.skip(4);
mineFile2.entries.Add(array);
}
mineFile2.data = fileData.readBytes(mineFile2.filesize);
}
return true;
}
catch
{
return false;
}
}
private static void writeMinecraftString(FileOutput f, string str)
{
byte[] bytes = Encoding.Unicode.GetBytes(str);
f.writeInt(bytes.Length / 2);
f.writeBytes(PCK.endianReverseUnicode(bytes));
f.writeInt(0);
}
public byte[] Rebuild()
{
FileOutput fileOutput = new FileOutput();
fileOutput.Endian = Endianness.Big;
fileOutput.writeInt(3);
fileOutput.writeInt(this.types.Count);
foreach (int num in this.types.Keys)
{
fileOutput.writeInt(num);
PCK.writeMinecraftString(fileOutput, this.types[num]);
}
fileOutput.writeInt(this.mineFiles.Count);
foreach (PCK.MineFile mineFile in this.mineFiles)
{
fileOutput.writeInt(mineFile.data.Length);
fileOutput.writeInt(mineFile.type);
PCK.writeMinecraftString(fileOutput, mineFile.name);
}
foreach (PCK.MineFile mineFile2 in this.mineFiles)
{
string str = "";
try
{
fileOutput.writeInt(mineFile2.entries.Count);
foreach (object[] array in mineFile2.entries)
{
str = array[0].ToString();
fileOutput.writeInt(this.typeCodes[(string)array[0]]);
PCK.writeMinecraftString(fileOutput, (string)array[1]);
}
fileOutput.writeBytes(mineFile2.data);
}
catch (Exception)
{
MessageBox.Show(str + " is not in the main metadatabase");
break;
}
}
return fileOutput.getBytes();
}
#endregion
#region VitaPCK
public static string readMineStringVita(FileData f)
{
int length = f.readIntVita() * 2;
@@ -70,90 +223,6 @@ namespace MinecraftUSkinEditor
return Encoding.Unicode.GetString((f.readBytes(length)));
}
public void Read(byte[] data)
{
pckType = 0;
types = new Dictionary<int, string>();
typeCodes = new Dictionary<string, int>();
mineFiles = new List<MineFile>();
FileData fileData = new FileData(data);
fileData.Endian = Endianness.Big;
fileData.readInt();
int entryTypeCount = fileData.readInt();
//int a = 0;
for (int i = 0; i < entryTypeCount; i++)
{
int unk = fileData.readInt();
string text = "";
try
{
text = readMineString(fileData);
//File.WriteAllText(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\exp\\PCKDump" + a + ".bin", text);
//a++;
}
catch
{
text = "Hello!";
}
types.Add(unk, text);
typeCodes.Add(text, unk);
fileData.skip(4);
}
int itemCount = fileData.readInt();
Console.WriteLine(itemCount);
// no metadata
if (entryTypeCount == 0)
{
Console.WriteLine("PckType0");
}
// type 1 or 2
else if (itemCount < 3)
{
pckType = itemCount;
if (pckType == 1)
{
Console.WriteLine("PckType1");
itemCount = fileData.readInt();
}
if (pckType == 2)
Console.WriteLine("PckType2");
}
// regular pck
else
{
Console.WriteLine("NormalPCK");
}
for (int j = 0; j < itemCount; j++)
{
MineFile mineFile = new MineFile();
mineFile.filesize = fileData.readInt();
mineFile.type = fileData.readInt();
int length = fileData.readInt() * 2;
mineFile.name = Encoding.Unicode.GetString(endianReverseUnicode(fileData.readBytes(length)));
fileData.skip(4);
mineFiles.Add(mineFile);
}
foreach (MineFile mineFile2 in mineFiles)
{
int num4 = fileData.readInt();
for (int k = 0; k < num4; k++)
{
object[] array = new object[2];
int key = fileData.readInt();
array[0] = types[key];
array[1] = readMineString(fileData);
fileData.skip(4);
mineFile2.entries.Add(array);
}
mineFile2.data = fileData.readBytes(mineFile2.filesize);
}
}
public void ReadVita(byte[] data)
{
@@ -240,14 +309,6 @@ namespace MinecraftUSkinEditor
}
}
private static void writeMinecraftString(FileOutput f, string str)
{
byte[] bytes = Encoding.Unicode.GetBytes(str);
f.writeInt(bytes.Length / 2);
f.writeBytes(PCK.endianReverseUnicode(bytes));
f.writeInt(0);
}
private static void writeMinecraftStringVita(FileOutput f, string str)
{
Console.WriteLine("WriteVita -- " + str);
@@ -257,47 +318,6 @@ namespace MinecraftUSkinEditor
f.writeIntVita(0);
}
public byte[] Rebuild()
{
FileOutput fileOutput = new FileOutput();
fileOutput.Endian = Endianness.Big;
fileOutput.writeInt(3);
fileOutput.writeInt(this.types.Count);
foreach (int num in this.types.Keys)
{
fileOutput.writeInt(num);
PCK.writeMinecraftString(fileOutput, this.types[num]);
}
fileOutput.writeInt(this.mineFiles.Count);
foreach (PCK.MineFile mineFile in this.mineFiles)
{
fileOutput.writeInt(mineFile.data.Length);
fileOutput.writeInt(mineFile.type);
PCK.writeMinecraftString(fileOutput, mineFile.name);
}
foreach (PCK.MineFile mineFile2 in this.mineFiles)
{
string str = "";
try
{
fileOutput.writeInt(mineFile2.entries.Count);
foreach (object[] array in mineFile2.entries)
{
str = array[0].ToString();
fileOutput.writeInt(this.typeCodes[(string)array[0]]);
PCK.writeMinecraftString(fileOutput, (string)array[1]);
}
fileOutput.writeBytes(mineFile2.data);
}
catch (Exception)
{
MessageBox.Show(str + " is not in the main metadatabase");
break;
}
}
return fileOutput.getBytes();
}
public byte[] RebuildVita()
{
FileOutput fileOutput = new FileOutput();
@@ -338,5 +358,7 @@ namespace MinecraftUSkinEditor
}
return fileOutput.getBytes();
}
#endregion
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace stonevox
{
public class CSM
{
public List<Box> boxes = new List<Box>();
public CSM(string path)
{
Open(path);
}
void Open(string path)
{
string RawCSM = path;
string[] lines = RawCSM.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
int i = 0;
while (i < lines.Length / 11)
{
string PartName = lines[(i*11)+0];
string ParentName = lines[(i*11)+1];
string PartName2 = lines[(i*11)+2];
int x = (int)Math.Round(float.Parse(lines[(i*11)+3]), 1, MidpointRounding.ToEven);
int y = (int)Math.Round(float.Parse(lines[(i*11)+4]), 1, MidpointRounding.ToEven);
int z = (int)Math.Round(float.Parse(lines[(i*11)+5]), 1, MidpointRounding.ToEven);
int Sizex = (int)Math.Round(float.Parse(lines[(i*11)+6]), 1, MidpointRounding.ToEven);
int Sizey = (int)Math.Round(float.Parse(lines[(i*11)+7]), 1, MidpointRounding.ToEven);
int Sizez = (int)Math.Round(float.Parse(lines[(i*11)+8]), 1, MidpointRounding.ToEven);
int uvx = (int)Math.Round(float.Parse(lines[(i*11)+9]), 1, MidpointRounding.ToEven);
int uvy = (int)Math.Round(float.Parse(lines[(i*11)+10]), 1, MidpointRounding.ToEven);
Box box = new Box();
box.PartName = PartName;
box.ParentName = ParentName;
box.x = x;
box.y = y;
box.z = z;
box.SizeX = Sizex;
box.SizeY = Sizey;
box.SizeZ = Sizez;
box.UvX = uvx;
box.UvY = uvy;
boxes.Add(box);
i++;
}
}
}
public class Box
{
public string PartName;
public string ParentName;
public int x;
public int y;
public int z;
public int SizeX;
public int SizeY;
public int SizeZ;
public int UvX;
public int UvY;
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace stonevox.Mod
{
public class Alias
{
public string name;
public FileReference file;
}
public class FunctionAlias
{
public enum endpoint
{
client,
server
}
public string name;
public FileReference controller;
public endpoint enpoint;
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace stonevox.Mod
{
public class FileReference
{
public string file { get; }
public FileReference(string file)
{
this.file = file;
}
}
}

View File

@@ -0,0 +1,88 @@
using Ionic.Zip;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace stonevox.Mod
{
public class Manifest
{
public class info
{
public string name { get; set; }
public string version { get; set; }
}
public class game
{
public FileReference[] script;
}
public FileReference client_init_script;
public FileReference server_init_script;
public List<Alias> aliases;
// don't need the other stuff now, will add
Manifest()
{
aliases = new List<Alias>();
}
Manifest(string modDirectory)
: this()
{
if (Directory.Exists(modDirectory))
{
if (File.Exists(Path.Combine(modDirectory , "manifest.json")))
{
}
else
throw new Exception("No Manifest File found.");
}
else
throw new Exception("Expecting a folder path.");
}
Manifest(ZipFile file)
: this()
{
}
void LoadFromFile(string manifestPath)
{
}
void LoadFromZip(ZipEntry manifestEntry)
{
}
public static Manifest FromDirectory(string modDirectory)
{
return new Manifest(modDirectory);
}
public static Manifest FromSMOD(string smodPath)
{
if (File.Exists(smodPath))
{
if (ZipFile.IsZipFile(smodPath))
{
ZipFile zip = ZipFile.Read(smodPath);
return new Manifest(zip);
}
else
throw new Exception("Not a valid .smod");
}
else
throw new Exception("Expecting *.smod at path, but found nothing");
}
}
}

View File

@@ -0,0 +1,16 @@
using Ionic.Zip;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace stonevox.Mod
{
public class StoneHearthMod
{
public string path { get; }
private ZipFile zip;
}
}

Binary file not shown.

View File

@@ -0,0 +1,90 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
namespace stonevox
{
class Program
{
public static Thread serverthread;
public static Thread clientthread;
[STAThread()]
static void Main(string[] Arg)
{
Console.Title = "CSM Viewer 3D";
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("");
Console.ForegroundColor = ConsoleColor.White;
File.WriteAllText(Environment.CurrentDirectory + "\\test.csm", Arg[0]);
startClient();
}
public static void startClient()
{
Client.beginstonevox();
}
public static void startServer()
{
Server.defaultConfigure();
Server.start();
}
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
static string lol = "i_made_onion_games_in_the_past";
public static string Encrypt(string ip)
{
string e = EncryptOrDecyzpt(ip, lol);
string re = "";
foreach (var c in e)
re += ((int)c).ToString() + '-';
re = re.Remove(re.Length - 1);
return re;
}
public static string Decrypt(string ip)
{
string d = "";
string[] chars = ip.Split('-');
foreach (var c in chars)
d += (char)(Convert.ToInt32(c));
return EncryptOrDecyzpt(d, lol);
}
public static string EncryptOrDecyzpt(string text, string Key)
{
var result = new StringBuilder();
for (int c = 0; c < text.Length; c++)
result.Append((char)((uint)text[c] ^ (uint)Key[c % Key.Length]));
return result.ToString();
}
}
}

View File

@@ -0,0 +1,871 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
namespace QuickFont
{
/// <summary>
/// Class for building a Quick Font, given a Font
/// and a configuration object.
/// </summary>
class Builder
{
private string charSet;
private QFontBuilderConfiguration config;
private Font font;
public Builder(Font font, QFontBuilderConfiguration config)
{
this.charSet = config.charSet;
this.config = config;
this.font = font;
}
private static Dictionary<char, QFontGlyph> CreateCharGlyphMapping(QFontGlyph[] glyphs)
{
var dict = new Dictionary<char, QFontGlyph>();
for (int i = 0; i < glyphs.Length; i++)
dict.Add(glyphs[i].character, glyphs[i]);
return dict;
}
//these do not affect the actual width of glyphs (we measure widths pixel-perfectly ourselves), but is used to detect whether a font is monospaced
private List<SizeF> GetGlyphSizes(Font font)
{
Bitmap bmp = new Bitmap(512, 512, PixelFormat.Format24bppRgb);
Graphics graph = Graphics.FromImage(bmp);
List<SizeF> sizes = new List<SizeF>();
for (int i = 0; i < charSet.Length; i++)
{
var charSize = graph.MeasureString("" + charSet[i], font);
sizes.Add(new SizeF(charSize.Width, charSize.Height));
}
graph.Dispose();
bmp.Dispose();
return sizes;
}
private SizeF GetMaxGlyphSize(List<SizeF> sizes)
{
SizeF maxSize = new SizeF(0f, 0f);
for (int i = 0; i < charSet.Length; i++)
{
if (sizes[i].Width > maxSize.Width)
maxSize.Width = sizes[i].Width;
if (sizes[i].Height > maxSize.Height)
maxSize.Height = sizes[i].Height;
}
return maxSize;
}
private SizeF GetMinGlyphSize(List<SizeF> sizes)
{
SizeF minSize = new SizeF(float.MaxValue, float.MaxValue);
for (int i = 0; i < charSet.Length; i++)
{
if (sizes[i].Width < minSize.Width)
minSize.Width = sizes[i].Width;
if (sizes[i].Height < minSize.Height)
minSize.Height = sizes[i].Height;
}
return minSize;
}
/// <summary>
/// Returns true if all glyph widths are within 5% of each other
/// </summary>
/// <param name="sizes"></param>
/// <returns></returns>
private bool IsMonospaced(List<SizeF> sizes)
{
var min = GetMinGlyphSize(sizes);
var max = GetMaxGlyphSize(sizes);
if (max.Width - min.Width < max.Width * 0.05f)
return true;
return false;
}
/*
private SizeF GetMaxGlyphSize(Font font)
{
Bitmap bmp = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
Graphics graph = Graphics.FromImage(bmp);
SizeF maxSize = new SizeF(0f, 0f);
for (int i = 0; i < charSet.Length; i++)
{
var charSize = graph.MeasureString("" + charSet[i], font);
if (charSize.Width > maxSize.Width)
maxSize.Width = charSize.Width;
if (charSize.Height > maxSize.Height)
maxSize.Height = charSize.Height;
}
graph.Dispose();
bmp.Dispose();
return maxSize;
}*/
//The initial bitmap is simply a long thin strip of all glyphs in a row
private Bitmap CreateInitialBitmap(Font font, SizeF maxSize, int initialMargin, out QFontGlyph[] glyphs, TextGenerationRenderHint renderHint)
{
glyphs = new QFontGlyph[charSet.Length];
int spacing = (int)Math.Ceiling(maxSize.Width) + 2 * initialMargin;
Bitmap bmp = new Bitmap(spacing * charSet.Length, (int)Math.Ceiling(maxSize.Height) + 2 * initialMargin, PixelFormat.Format24bppRgb);
Graphics graph = Graphics.FromImage(bmp);
switch(renderHint){
case TextGenerationRenderHint.SizeDependent:
graph.TextRenderingHint = font.Size <= 12.0f ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAlias;
break;
case TextGenerationRenderHint.AntiAlias:
graph.TextRenderingHint = TextRenderingHint.AntiAlias;
break;
case TextGenerationRenderHint.AntiAliasGridFit:
graph.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
break;
case TextGenerationRenderHint.ClearTypeGridFit:
graph.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
break;
case TextGenerationRenderHint.SystemDefault:
graph.TextRenderingHint = TextRenderingHint.SystemDefault;
break;
}
int xOffset = initialMargin;
for (int i = 0; i < charSet.Length; i++)
{
graph.DrawString("" + charSet[i], font, Brushes.White, xOffset, initialMargin);
var charSize = graph.MeasureString("" + charSet[i], font);
glyphs[i] = new QFontGlyph(0, new Rectangle(xOffset - initialMargin, 0, (int)charSize.Width + initialMargin * 2, (int)charSize.Height + initialMargin * 2), 0, charSet[i]);
xOffset += (int)charSize.Width + initialMargin * 2;
}
graph.Flush();
graph.Dispose();
return bmp;
}
private delegate bool EmptyDel(BitmapData data, int x, int y);
private static void RetargetGlyphRectangleInwards(BitmapData bitmapData, QFontGlyph glyph, bool setYOffset, byte alphaTolerance)
{
int startX, endX;
int startY, endY;
var rect = glyph.rect;
EmptyDel emptyPix;
if (bitmapData.PixelFormat == PixelFormat.Format32bppArgb)
emptyPix = delegate(BitmapData data, int x, int y) { return QBitmap.EmptyAlphaPixel(data, x, y, alphaTolerance); };
else
emptyPix = delegate(BitmapData data, int x, int y) { return QBitmap.EmptyPixel(data, x, y); };
unsafe
{
for (startX = rect.X; startX < bitmapData.Width; startX++)
for (int j = rect.Y; j < rect.Y + rect.Height; j++)
if (!emptyPix(bitmapData, startX, j))
goto Done1;
Done1:
for (endX = rect.X + rect.Width; endX >= 0; endX--)
for (int j = rect.Y; j < rect.Y + rect.Height; j++)
if (!emptyPix(bitmapData, endX, j))
goto Done2;
Done2:
for (startY = rect.Y; startY < bitmapData.Height; startY++)
for (int i = startX; i < endX; i++)
if (!emptyPix(bitmapData, i, startY))
goto Done3;
Done3:
for (endY = rect.Y + rect.Height; endY >= 0; endY--)
for (int i = startX; i < endX; i++)
if (!emptyPix(bitmapData, i, endY))
goto Done4;
Done4:;
}
if (endY < startY)
startY = endY = rect.Y;
if (endX < startX)
startX = endX = rect.X;
glyph.rect = new Rectangle(startX, startY, endX - startX + 1, endY - startY + 1);
if (setYOffset)
glyph.yOffset = glyph.rect.Y;
}
private static void RetargetGlyphRectangleOutwards(BitmapData bitmapData, QFontGlyph glyph, bool setYOffset, byte alphaTolerance)
{
int startX,endX;
int startY,endY;
var rect = glyph.rect;
EmptyDel emptyPix;
if (bitmapData.PixelFormat == PixelFormat.Format32bppArgb)
emptyPix = delegate(BitmapData data, int x, int y) { return QBitmap.EmptyAlphaPixel(data, x, y, alphaTolerance); };
else
emptyPix = delegate(BitmapData data, int x, int y) { return QBitmap.EmptyPixel(data, x, y); };
unsafe
{
for (startX = rect.X; startX >= 0; startX--)
{
bool foundPix = false;
for (int j = rect.Y; j <= rect.Y + rect.Height; j++)
{
if (!emptyPix(bitmapData, startX, j))
{
foundPix = true;
break;
}
}
if (!foundPix)
{
startX++;
break;
}
}
for (endX = rect.X + rect.Width; endX < bitmapData.Width; endX++)
{
bool foundPix = false;
for (int j = rect.Y; j <= rect.Y + rect.Height; j++)
{
if (!emptyPix(bitmapData, endX, j))
{
foundPix = true;
break;
}
}
if (!foundPix)
{
endX--;
break;
}
}
for (startY = rect.Y; startY >= 0; startY--)
{
bool foundPix = false;
for (int i = startX; i <= endX; i++)
{
if (!emptyPix(bitmapData, i, startY))
{
foundPix = true;
break;
}
}
if (!foundPix)
{
startY++;
break;
}
}
for (endY = rect.Y + rect.Height; endY < bitmapData.Height; endY++)
{
bool foundPix = false;
for (int i = startX; i <= endX; i++)
{
if (!emptyPix(bitmapData, i, endY))
{
foundPix = true;
break;
}
}
if (!foundPix)
{
endY--;
break;
}
}
}
glyph.rect = new Rectangle(startX, startY, endX - startX + 1, endY - startY + 1);
if (setYOffset)
glyph.yOffset = glyph.rect.Y;
}
private static List<QBitmap> GenerateBitmapSheetsAndRepack(QFontGlyph[] sourceGlyphs, BitmapData[] sourceBitmaps, int destSheetWidth, int destSheetHeight, out QFontGlyph[] destGlyphs, int destMargin, bool usePowerOfTwo)
{
var pages = new List<QBitmap>();
destGlyphs = new QFontGlyph[sourceGlyphs.Length];
QBitmap currentPage = null;
int maxY = 0;
foreach (var glph in sourceGlyphs)
maxY = Math.Max(glph.rect.Height, maxY);
int finalPageIndex = 0;
int finalPageRequiredWidth = 0;
int finalPageRequiredHeight = 0;
for (int k = 0; k < 2; k++)
{
bool pre = k == 0; //first iteration is simply to determine the required size of the final page, so that we can crop it in advance
int xPos = 0;
int yPos = 0;
int maxYInRow = 0;
int totalTries = 0;
for (int i = 0; i < sourceGlyphs.Length; i++)
{
if(!pre && currentPage == null){
if (finalPageIndex == pages.Count)
{
int width = Math.Min(destSheetWidth, usePowerOfTwo ? PowerOfTwo(finalPageRequiredWidth) : finalPageRequiredWidth);
int height = Math.Min(destSheetHeight, usePowerOfTwo ? PowerOfTwo(finalPageRequiredHeight) : finalPageRequiredHeight);
currentPage = new QBitmap(new Bitmap(width, height, PixelFormat.Format32bppArgb));
currentPage.Clear32(255, 255, 255, 0); //clear to white, but totally transparent
}
else
{
currentPage = new QBitmap(new Bitmap(destSheetWidth, destSheetHeight, PixelFormat.Format32bppArgb));
currentPage.Clear32(255, 255, 255, 0); //clear to white, but totally transparent
}
pages.Add(currentPage);
}
totalTries++;
if (totalTries > 10 * sourceGlyphs.Length)
throw new Exception("Failed to fit font into texture pages");
var rect = sourceGlyphs[i].rect;
if (xPos + rect.Width + 2 * destMargin <= destSheetWidth && yPos + rect.Height + 2 * destMargin <= destSheetHeight)
{
if (!pre)
{
//add to page
if(sourceBitmaps[sourceGlyphs[i].page].PixelFormat == PixelFormat.Format32bppArgb)
QBitmap.Blit(sourceBitmaps[sourceGlyphs[i].page], currentPage.bitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin);
else
QBitmap.BlitMask(sourceBitmaps[sourceGlyphs[i].page], currentPage.bitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin);
destGlyphs[i] = new QFontGlyph(pages.Count - 1, new Rectangle(xPos + destMargin, yPos + destMargin, rect.Width, rect.Height), sourceGlyphs[i].yOffset, sourceGlyphs[i].character);
}
else
{
finalPageRequiredWidth = Math.Max(finalPageRequiredWidth, xPos + rect.Width + 2 * destMargin);
finalPageRequiredHeight = Math.Max(finalPageRequiredHeight, yPos + rect.Height + 2 * destMargin);
}
xPos += rect.Width + 2 * destMargin;
maxYInRow = Math.Max(maxYInRow, rect.Height);
continue;
}
if (xPos + rect.Width + 2 * destMargin > destSheetWidth)
{
i--;
yPos += maxYInRow + 2 * destMargin;
xPos = 0;
if (yPos + maxY + 2 * destMargin > destSheetHeight)
{
yPos = 0;
if (!pre)
{
currentPage = null;
}
else
{
finalPageRequiredWidth = 0;
finalPageRequiredHeight = 0;
finalPageIndex++;
}
}
continue;
}
}
}
return pages;
}
public QFontData BuildFontData()
{
return BuildFontData(null);
}
public QFontData BuildFontData(string saveName)
{
if (config.ForcePowerOfTwo && config.SuperSampleLevels != PowerOfTwo(config.SuperSampleLevels))
{
throw new ArgumentOutOfRangeException("SuperSampleLevels must be a power of two when using ForcePowerOfTwo.");
}
if (config.SuperSampleLevels <= 0 || config.SuperSampleLevels > 8)
{
throw new ArgumentOutOfRangeException("SuperSampleLevels = [" + config.SuperSampleLevels + "] is an unsupported value. Please use values in the range [1,8]");
}
int margin = 2; //margin in initial bitmap (don't bother to make configurable - likely to cause confusion
int pageWidth = config.PageWidth * config.SuperSampleLevels; //texture page width
int pageHeight = config.PageHeight * config.SuperSampleLevels; //texture page height
bool usePowerOfTwo = config.ForcePowerOfTwo;
int glyphMargin = config.GlyphMargin * config.SuperSampleLevels;
QFontGlyph[] initialGlyphs;
var sizes = GetGlyphSizes(font);
var maxSize = GetMaxGlyphSize(sizes);
var initialBmp = CreateInitialBitmap(font, maxSize, margin, out initialGlyphs,config.TextGenerationRenderHint);
var initialBitmapData = initialBmp.LockBits(new Rectangle(0, 0, initialBmp.Width, initialBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
int minYOffset = int.MaxValue;
foreach (var glyph in initialGlyphs){
RetargetGlyphRectangleInwards(initialBitmapData, glyph, true, config.KerningConfig.alphaEmptyPixelTolerance);
minYOffset = Math.Min(minYOffset,glyph.yOffset);
}
minYOffset--; //give one pixel of breathing room?
foreach (var glyph in initialGlyphs)
glyph.yOffset -= minYOffset;
QFontGlyph[] glyphs;
var bitmapPages = GenerateBitmapSheetsAndRepack( initialGlyphs,new BitmapData[1] { initialBitmapData},pageWidth, pageHeight, out glyphs, glyphMargin, usePowerOfTwo);
initialBmp.UnlockBits(initialBitmapData);
initialBmp.Dispose();
if (config.SuperSampleLevels != 1)
{
ScaleSheetsAndGlyphs(bitmapPages, glyphs, 1.0f / config.SuperSampleLevels);
RetargetAllGlyphs(bitmapPages, glyphs,config.KerningConfig.alphaEmptyPixelTolerance);
}
//create list of texture pages
var pages = new List<TexturePage>();
foreach (var page in bitmapPages)
pages.Add(new TexturePage(page.bitmapData));
var fontData = new QFontData();
fontData.CharSetMapping = CreateCharGlyphMapping(glyphs);
fontData.Pages = pages.ToArray();
fontData.CalculateMeanWidth();
fontData.CalculateMaxHeight();
fontData.KerningPairs = KerningCalculator.CalculateKerning(charSet.ToCharArray(), glyphs, bitmapPages,config.KerningConfig);
fontData.naturallyMonospaced = IsMonospaced(sizes);
if (saveName != null)
{
if (bitmapPages.Count == 1)
bitmapPages[0].bitmap.Save(saveName + ".png", System.Drawing.Imaging.ImageFormat.Png);
else
{
for (int i = 0; i < bitmapPages.Count; i++)
bitmapPages[i].bitmap.Save(saveName + "_sheet_" + i + ".png", System.Drawing.Imaging.ImageFormat.Png);
}
}
if (config.ShadowConfig != null)
fontData.dropShadow = BuildDropShadow(bitmapPages, glyphs, config.ShadowConfig, charSet.ToCharArray(),config.KerningConfig.alphaEmptyPixelTolerance);
foreach (var page in bitmapPages)
page.Free();
//validate glyphs
var intercept = FirstIntercept(fontData.CharSetMapping);
if (intercept != null)
throw new Exception("Failed to create glyph set. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. This is could be due to an error in the font, or a bug in Graphics.MeasureString().");
return fontData;
}
private static QFont BuildDropShadow(List<QBitmap> sourceFontSheets, QFontGlyph[] sourceFontGlyphs, QFontShadowConfiguration shadowConfig, char[] charSet, byte alphaTolerance)
{
QFontGlyph[] newGlyphs;
var sourceBitmapData = new List<BitmapData>();
foreach(var sourceSheet in sourceFontSheets)
sourceBitmapData.Add(sourceSheet.bitmapData);
//GenerateBitmapSheetsAndRepack(QFontGlyph[] sourceGlyphs, BitmapData[] sourceBitmaps, int destSheetWidth, int destSheetHeight, out QFontGlyph[] destGlyphs, int destMargin, bool usePowerOfTwo)
var bitmapSheets = GenerateBitmapSheetsAndRepack(sourceFontGlyphs, sourceBitmapData.ToArray(), shadowConfig.PageWidth, shadowConfig.PageHeight, out newGlyphs, shadowConfig.GlyphMargin + shadowConfig.blurRadius*3, shadowConfig.ForcePowerOfTwo);
//scale up in case we wanted bigger/smaller shadows
if (shadowConfig.Scale != 1.0f)
ScaleSheetsAndGlyphs(bitmapSheets, newGlyphs, shadowConfig.Scale); //no point in retargeting yet, since we will do it after blur
//blacken and blur
foreach (var bitmapSheet in bitmapSheets)
{
bitmapSheet.Colour32(0, 0, 0);
bitmapSheet.BlurAlpha(shadowConfig.blurRadius, shadowConfig.blurPasses);
}
//retarget after blur and scale
RetargetAllGlyphs(bitmapSheets, newGlyphs, alphaTolerance);
//create list of texture pages
var newTextureSheets = new List<TexturePage>();
foreach (var page in bitmapSheets)
newTextureSheets.Add(new TexturePage(page.bitmapData));
var fontData = new QFontData();
fontData.CharSetMapping = new Dictionary<char, QFontGlyph>();
for(int i = 0; i < charSet.Length; i++)
fontData.CharSetMapping.Add(charSet[i],newGlyphs[i]);
fontData.Pages = newTextureSheets.ToArray();
fontData.CalculateMeanWidth();
fontData.CalculateMaxHeight();
foreach (var sheet in bitmapSheets)
sheet.Free();
return new QFont(fontData);
}
private static void ScaleSheetsAndGlyphs(List<QBitmap> pages, QFontGlyph[] glyphs, float scale)
{
foreach (var page in pages)
page.DownScale32((int)(page.bitmap.Width * scale), (int)(page.bitmap.Height * scale));
foreach (var glyph in glyphs)
{
glyph.rect = new Rectangle((int)(glyph.rect.X * scale), (int)(glyph.rect.Y * scale), (int)(glyph.rect.Width * scale), (int)(glyph.rect.Height * scale));
glyph.yOffset = (int)(glyph.yOffset * scale);
}
}
private static void RetargetAllGlyphs(List<QBitmap> pages, QFontGlyph[] glyphs, byte alphaTolerance)
{
foreach (var glyph in glyphs)
RetargetGlyphRectangleOutwards(pages[glyph.page].bitmapData, glyph, false, alphaTolerance);
}
public static void SaveQFontDataToFile(QFontData data, string filePath)
{
var lines = data.Serialize();
StreamWriter writer = new StreamWriter(filePath + ".qfont");
foreach (var line in lines)
writer.WriteLine(line);
writer.Close();
}
public static void CreateBitmapPerGlyph(QFontGlyph[] sourceGlyphs, QBitmap[] sourceBitmaps, out QFontGlyph[] destGlyphs, out QBitmap[] destBitmaps){
destBitmaps = new QBitmap[sourceGlyphs.Length];
destGlyphs = new QFontGlyph[sourceGlyphs.Length];
for(int i = 0; i < sourceGlyphs.Length; i++){
var sg = sourceGlyphs[i];
destGlyphs[i] = new QFontGlyph(i,new Rectangle(0,0,sg.rect.Width,sg.rect.Height),sg.yOffset,sg.character);
destBitmaps[i] = new QBitmap(new Bitmap(sg.rect.Width,sg.rect.Height,PixelFormat.Format32bppArgb));
QBitmap.Blit(sourceBitmaps[sg.page].bitmapData,destBitmaps[i].bitmapData,sg.rect,0,0);
}
}
public static QFontData LoadQFontDataFromFile(string filePath, float downSampleFactor, QFontLoaderConfiguration loaderConfig)
{
var lines = new List<String>();
StreamReader reader = new StreamReader(filePath);
string line;
while((line = reader.ReadLine()) != null)
lines.Add(line);
reader.Close();
var data = new QFontData();
int pageCount = 0;
char[] charSet;
data.Deserialize(lines, out pageCount, out charSet);
string namePrefix = filePath.Replace(".qfont","").Replace(" ", "");
var bitmapPages = new List<QBitmap>();
if (pageCount == 1)
{
bitmapPages.Add(new QBitmap(namePrefix + ".png"));
}
else
{
for (int i = 0; i < pageCount; i++)
bitmapPages.Add(new QBitmap(namePrefix + "_sheet_" + i));
}
foreach (var glyph in data.CharSetMapping.Values)
RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].bitmapData, glyph, false, loaderConfig.KerningConfig.alphaEmptyPixelTolerance);
var intercept = FirstIntercept(data.CharSetMapping);
if (intercept != null)
{
throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. If you are texturing your font without locking pixel opacity, then consider using a larger glyph margin. This can be done by setting QFontBuilderConfiguration myQfontBuilderConfig.GlyphMargin, and passing it into CreateTextureFontFiles.");
}
if (downSampleFactor > 1.0f)
{
foreach (var page in bitmapPages)
page.DownScale32((int)(page.bitmap.Width * downSampleFactor), (int)(page.bitmap.Height * downSampleFactor));
foreach (var glyph in data.CharSetMapping.Values)
{
glyph.rect = new Rectangle((int)(glyph.rect.X * downSampleFactor),
(int)(glyph.rect.Y * downSampleFactor),
(int)(glyph.rect.Width * downSampleFactor),
(int)(glyph.rect.Height * downSampleFactor));
glyph.yOffset = (int)(glyph.yOffset * downSampleFactor);
}
}
else if (downSampleFactor < 1.0f )
{
// If we were simply to shrink the entire texture, then at some point we will make glyphs overlap, breaking the font.
// For this reason it is necessary to copy every glyph to a separate bitmap, and then shrink each bitmap individually.
QFontGlyph[] shrunkGlyphs;
QBitmap[] shrunkBitmapsPerGlyph;
CreateBitmapPerGlyph(Helper.ToArray(data.CharSetMapping.Values), bitmapPages.ToArray(), out shrunkGlyphs, out shrunkBitmapsPerGlyph);
//shrink each bitmap
for (int i = 0; i < shrunkGlyphs.Length; i++)
{
var bmp = shrunkBitmapsPerGlyph[i];
bmp.DownScale32(Math.Max((int)(bmp.bitmap.Width * downSampleFactor),1), Math.Max((int)(bmp.bitmap.Height * downSampleFactor),1));
shrunkGlyphs[i].rect = new Rectangle(0, 0, bmp.bitmap.Width, bmp.bitmap.Height);
shrunkGlyphs[i].yOffset = (int)(shrunkGlyphs[i].yOffset * downSampleFactor);
}
var shrunkBitmapData = new BitmapData[shrunkBitmapsPerGlyph.Length];
for(int i = 0; i < shrunkBitmapsPerGlyph.Length; i ++ ){
shrunkBitmapData[i] = shrunkBitmapsPerGlyph[i].bitmapData;
}
//use roughly the same number of pages as before..
int newWidth = (int)(bitmapPages[0].bitmap.Width * (0.1f + downSampleFactor));
int newHeight = (int)(bitmapPages[0].bitmap.Height * (0.1f + downSampleFactor));
//free old bitmap pages since we are about to chuck them away
for (int i = 0; i < pageCount; i++)
bitmapPages[i].Free();
QFontGlyph[] shrunkRepackedGlyphs;
bitmapPages = GenerateBitmapSheetsAndRepack(shrunkGlyphs, shrunkBitmapData, newWidth, newHeight, out shrunkRepackedGlyphs, 4, false);
data.CharSetMapping = CreateCharGlyphMapping(shrunkRepackedGlyphs);
foreach (var bmp in shrunkBitmapsPerGlyph)
bmp.Free();
pageCount = bitmapPages.Count;
}
data.Pages = new TexturePage[pageCount];
for(int i = 0; i < pageCount; i ++ )
data.Pages[i] = new TexturePage(bitmapPages[i].bitmapData);
if (downSampleFactor != 1.0f)
{
foreach (var glyph in data.CharSetMapping.Values)
RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].bitmapData, glyph, false, loaderConfig.KerningConfig.alphaEmptyPixelTolerance);
intercept = FirstIntercept(data.CharSetMapping);
if (intercept != null)
{
throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. This occurred only after resizing your texture font, implying that there is a bug in QFont. ");
}
}
var glyphList = new List<QFontGlyph>();
foreach (var c in charSet)
glyphList.Add(data.CharSetMapping[c]);
if (loaderConfig.ShadowConfig != null)
data.dropShadow = BuildDropShadow(bitmapPages, glyphList.ToArray(), loaderConfig.ShadowConfig, Helper.ToArray(charSet),loaderConfig.KerningConfig.alphaEmptyPixelTolerance);
data.KerningPairs = KerningCalculator.CalculateKerning(Helper.ToArray(charSet), glyphList.ToArray(), bitmapPages, loaderConfig.KerningConfig);
data.CalculateMeanWidth();
data.CalculateMaxHeight();
for (int i = 0; i < pageCount; i++)
bitmapPages[i].Free();
return data;
}
private static char[] FirstIntercept(Dictionary<char,QFontGlyph> charSet)
{
char[] keys = Helper.ToArray(charSet.Keys);
for (int i = 0; i < keys.Length; i++)
{
for (int j = i + 1; j < keys.Length; j++)
{
if (charSet[keys[i]].page == charSet[keys[j]].page && RectangleIntersect(charSet[keys[i]].rect, charSet[keys[j]].rect))
{
return new char[2] { keys[i], keys[j] };
}
}
}
return null;
}
private static bool RectangleIntersect(Rectangle r1, Rectangle r2)
{
return (r1.X < r2.X + r2.Width && r1.X + r1.Width > r2.X &&
r1.Y < r2.Y + r2.Height && r1.Y + r1.Height > r2.Y);
}
/// <summary>
/// Returns the power of 2 that is closest to x, but not smaller than x.
/// </summary>
private static int PowerOfTwo(int x)
{
int shifts = 0;
uint val = (uint)x;
if (x < 0)
return 0;
while (val > 0)
{
val = val >> 1;
shifts++;
}
val = (uint)1 << (shifts - 1);
if (val < x)
{
val = val << 1;
}
return (int)val;
}
}
}

View File

@@ -0,0 +1,30 @@

05/11/2011 - Added TransformToCurrentOrthogProjection option
06/11/2011 - Fixed bug whereby it was not possible to set the opacity of the drop shadow
24/11/2011 - Fixed bug in RetargetGlyphRectangleInwards causing an invalid pointer dereference
17/12/2011 - Added UseDefaultBlendFunction option
18/12/2011 - Added PushOptions(), and PopOptions()
22/12/2011 - Added LockToPixelRatio render option
27/12/2011 - Simplified ProjectionStack
27/12/2011 - Added RefreshViewport
30/12/2011 - Fixed bug where fonts with empty glyphs caused QFont to crash
30/12/2011 - Added TextGenerationRenderHint option to QFontBuilderConfiguration (see below for new default configuration)
30/12/2011 - by default, ttf fonts smaller than 12.0 are now generated with TextRenderingHint.ClearTypeGridFit, giving much sharper results
30/12/2011 - Underscore, quote and double quote characters will now kern properly
30/12/2011 - Added kerning character configuration to manually configure kerning rules for particular characters
30/12/2011 - QFontShadowConfiguration is now a member of QFontBuilderConfiguration, simplifying the QFont constructors
30/12/2011 - QFont.FromQFontFile now takes a QFontLoaderConfiguration. This contains a QFontShadowConfiguration.
30/12/2011 - Added alphaEmptyPixelTolerance config value
31/12/2011 - Fixed limitation on downscaling texture fonts (previously glyphs could overlap, throwing an exception)
----- 1.0.1 released
07/01/2011 - Removed RefreshViewport
07/01/2011 - Added InvalidateViewport(), ForceViewportRefresh(), PushSoftwareViewport(Viewport viewport), PopSoftwareViewport()
14/04/2012 - When TransformToViewport is enabled for a font, but QFont.Begin/End are not called, the font will now be rendered at the correct size (not tested for all alignments!)
18/07/2012 - Measuring text no longer sets the color and binds the texture, nor does it set a blend function
14/08/2012 - Reload() method added for reloading a qfont object.
14/08/2012 - QFont now correctly implements IDisposable
----- 1.0.2 released

View File

@@ -0,0 +1,83 @@
namespace QuickFont
{
public enum TextGenerationRenderHint
{
/// <summary>
/// Use AntiAliasGridFit when rendering the ttf character set to create the QFont texture
/// </summary>
AntiAliasGridFit,
/// <summary>
/// Use AntiAlias when rendering the ttf character set to create the QFont texture
/// </summary>
AntiAlias,
/// <summary>
/// Use ClearTypeGridFit if the font is smaller than 12, otherwise use AntiAlias
/// </summary>
SizeDependent,
/// <summary>
/// Use ClearTypeGridFit when rendering the ttf character set to create the QFont texture
/// </summary>
ClearTypeGridFit,
/// <summary>
/// Use SystemDefault when rendering the ttf character set to create the QFont texture
/// </summary>
SystemDefault
}
/// <summary>
/// What settings to use when building the font
/// </summary>
public class QFontBuilderConfiguration : QFontConfiguration
{
/// <summary>
/// Whether to use super sampling when building font texture pages
///
///
/// </summary>
public int SuperSampleLevels = 1;
/// <summary>
/// The standard width of texture pages (the page will
/// automatically be cropped if there is extra space)
/// </summary>
public int PageWidth = 512;
/// <summary>
/// The standard height of texture pages (the page will
/// automatically be cropped if there is extra space)
/// </summary>
public int PageHeight = 512;
/// <summary>
/// Whether to force texture pages to use a power of two.
/// </summary>
public bool ForcePowerOfTwo = true;
/// <summary>
/// The margin (on all sides) around glyphs when rendered to
/// their texture page
/// </summary>
public int GlyphMargin = 2;
/// <summary>
/// Set of characters to support
/// </summary>
public string charSet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.:,;'\"(!?)+-*/=_{}[]@~#\\<>|^%$£&";
/// <summary>
/// Which render hint to use when rendering the ttf character set to create the QFont texture
/// </summary>
public TextGenerationRenderHint TextGenerationRenderHint = TextGenerationRenderHint.SizeDependent;
public QFontBuilderConfiguration() { }
public QFontBuilderConfiguration(bool addDropShadow) : this(addDropShadow, false) { }
public QFontBuilderConfiguration(bool addDropShadow, bool TransformToOrthogProjection)
{
if (addDropShadow)
this.ShadowConfig = new QFontShadowConfiguration();
this.TransformToCurrentOrthogProjection = TransformToOrthogProjection;
}
}
}

View File

@@ -0,0 +1,14 @@
namespace QuickFont
{
public class QFontConfiguration
{
public QFontShadowConfiguration ShadowConfig = null;
public QFontKerningConfiguration KerningConfig = new QFontKerningConfiguration();
/// <summary>
/// Render the font pixel-prefectly at a size in units of the current orthogonal projection, independent of the viewport pixel size.
/// </summary>
public bool TransformToCurrentOrthogProjection = false;
}
}

View File

@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
namespace QuickFont
{
public enum CharacterKerningRule
{
/// <summary>
/// Ordinary kerning
/// </summary>
Normal,
/// <summary>
/// All kerning pairs involving this character will kern by 0. This will
/// override both Normal and NotMoreThanHalf for any pair.
/// </summary>
Zero,
/// <summary>
/// Any kerning pairs involving this character will not kern
/// by more than half the minimum width of the two characters
/// involved. This will override Normal for any pair.
/// </summary>
NotMoreThanHalf
}
public class QFontKerningConfiguration
{
/// <summary>
/// Kerning rules for particular characters
/// </summary>
private Dictionary<char, CharacterKerningRule> CharacterKerningRules = new Dictionary<char, CharacterKerningRule>();
/// <summary>
/// When measuring the bounds of glyphs, and performing kerning calculations,
/// this is the minimum alpha level that is necessray for a pixel to be considered
/// non-empty. This should be set to a value on the range [0,255]
/// </summary>
public byte alphaEmptyPixelTolerance = 0;
/// <summary>
/// Sets all characters in the given string to the specified kerning rule.
/// </summary>
/// <param name="chars"></param>
/// <param name="rule"></param>
public void BatchSetCharacterKerningRule(String chars, CharacterKerningRule rule)
{
foreach (var c in chars)
{
CharacterKerningRules[c] = rule;
}
}
/// <summary>
/// Sets the specified character kerning rule.
/// </summary>
/// <param name="c"></param>
/// <param name="rule"></param>
public void SetCharacterKerningRule(char c, CharacterKerningRule rule)
{
CharacterKerningRules[c] = rule;
}
public CharacterKerningRule GetCharacterKerningRule(char c)
{
if (CharacterKerningRules.ContainsKey(c))
{
return CharacterKerningRules[c];
}
return CharacterKerningRule.Normal;
}
/// <summary>
/// Given a pair of characters, this will return the overriding
/// CharacterKerningRule.
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public CharacterKerningRule GetOverridingCharacterKerningRuleForPair(String str)
{
if (str.Length < 2)
{
return CharacterKerningRule.Normal;
}
char c1 = str[0];
char c2 = str[1];
if (GetCharacterKerningRule(c1) == CharacterKerningRule.Zero || GetCharacterKerningRule(c2) == CharacterKerningRule.Zero)
{
return CharacterKerningRule.Zero;
}
else if (GetCharacterKerningRule(c1) == CharacterKerningRule.NotMoreThanHalf || GetCharacterKerningRule(c2) == CharacterKerningRule.NotMoreThanHalf)
{
return CharacterKerningRule.NotMoreThanHalf;
}
return CharacterKerningRule.Normal;
}
public QFontKerningConfiguration()
{
BatchSetCharacterKerningRule("_^", CharacterKerningRule.Zero);
SetCharacterKerningRule('\"', CharacterKerningRule.NotMoreThanHalf);
SetCharacterKerningRule('\'', CharacterKerningRule.NotMoreThanHalf);
}
}
}

View File

@@ -0,0 +1,20 @@
namespace QuickFont
{
/// <summary>
/// The configuraiton used when loading a font from a qfont file.
/// </summary>
public class QFontLoaderConfiguration : QFontConfiguration
{
public QFontLoaderConfiguration() { }
public QFontLoaderConfiguration(bool addDropShadow) : this(addDropShadow, false) { }
public QFontLoaderConfiguration(bool addDropShadow, bool TransformToOrthogProjection)
{
if (addDropShadow)
this.ShadowConfig = new QFontShadowConfiguration();
this.TransformToCurrentOrthogProjection = TransformToOrthogProjection;
}
}
}

View File

@@ -0,0 +1,50 @@
namespace QuickFont
{
/// <summary>
/// The configuration used when building a font drop shadow.
/// </summary>
public class QFontShadowConfiguration
{
/// <summary>
/// Scale in relation to the actual font glyphs
/// </summary>
public float Scale = 1.0f;
/// <summary>
/// The blur radius. Caution: high values will greatly impact the
/// time it takes to build a font shadow
/// </summary>
public int blurRadius = 3;
/// <summary>
/// Number of blur passes. Caution: high values will greatly impact the
/// time it takes to build a font shadow
/// </summary>
public int blurPasses = 2;
/// <summary>
/// The standard width of texture pages (the page will
/// automatically be cropped if there is extra space)
/// </summary>
public int PageWidth = 512;
/// <summary>
/// The standard height of texture pages (the page will
/// automatically be cropped if there is extra space)
/// </summary>
public int PageHeight = 512;
/// <summary>
/// Whether to force texture pages to use a power of two.
/// </summary>
public bool ForcePowerOfTwo = true;
/// <summary>
/// The margin (on all sides) around glyphs when rendered to
/// their texture page. Note this is in addition to 3xblurRadius margin
/// which is automatically added.
/// </summary>
public int GlyphMargin = 2;
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Drawing;
namespace QuickFont
{
enum FontLoadMethod { FontObject, FontFile, QFontFile };
/// <summary>
/// Describes how a font was loaded so that it can be reloaded
/// </summary>
class FontLoadDescription
{
public FontLoadMethod Method { get; private set; }
public String Path { get; private set; }
public float Size { get; private set;}
public FontStyle Style {get; private set; }
public QFontBuilderConfiguration BuilderConfig {get; private set;}
public float DownSampleFactor {get;private set;}
public QFontLoaderConfiguration LoaderConfig {get; private set;}
public FontLoadDescription(String Path, float DownSampleFactor, QFontLoaderConfiguration LoaderConfig){
Method = FontLoadMethod.QFontFile;
this.Path = Path;
this.DownSampleFactor = DownSampleFactor;
this.LoaderConfig = LoaderConfig;
}
public FontLoadDescription(String Path, float Size, FontStyle Style, QFontBuilderConfiguration BuilderConfig){
Method = FontLoadMethod.FontFile;
this.Path = Path;
this.Size = Size;
this.Style = Style;
this.BuilderConfig = BuilderConfig;
}
public FontLoadDescription(Font font, QFontBuilderConfiguration config){
Method = FontLoadMethod.FontObject;
//we don't reload fonts loaded direct from a font object...
}
}
}

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace QuickFont
{
class Helper
{
public static T[] ToArray<T>(ICollection<T> collection)
{
T[] output = new T[collection.Count];
collection.CopyTo(output, 0);
return output;
}
}
}

View File

@@ -0,0 +1,971 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
namespace QuickFont
{
/// <summary>
/// A class for basic bitmap manipulation: clearing bitmaps, blitting from one bitmap to another etc.
///
/// Generally methods in this class are quite slow and are only intended for pre-rendering.
///
/// TODO : replace loops with block copies
/// </summary>
public partial class JBitmap
{
public enum JBitmapPixelFormat {Format8bpp=1, Format24bppBGR=3, Format32bppBGRA=4 }
/// <summary>
/// Gets the corresponding C# pixel format
/// </summary>
/// <param name="format"></param>
/// <returns></returns>
private static System.Drawing.Imaging.PixelFormat GetPixFormat(JBitmapPixelFormat format) {
switch (format){
case JBitmapPixelFormat.Format8bpp:
return System.Drawing.Imaging.PixelFormat.Format8bppIndexed;
case JBitmapPixelFormat.Format24bppBGR:
return System.Drawing.Imaging.PixelFormat.Format24bppRgb;
case JBitmapPixelFormat.Format32bppBGRA:
return System.Drawing.Imaging.PixelFormat.Format32bppArgb;
default:
return System.Drawing.Imaging.PixelFormat.Format8bppIndexed;
}
}
public Bitmap bitmap;
public BitmapData bitmapData;
public JBitmapPixelFormat format;
public int h
{
get { return bitmapData.Height; }
}
public int w
{
get { return bitmapData.Width; }
}
public JBitmap(int w, int h, JBitmapPixelFormat _format)
{
format = _format;
bitmap = new Bitmap(w, h, GetPixFormat(format));
bitmapData = bitmap.LockBits(new Rectangle(0, 0, w, h),ImageLockMode.ReadWrite, GetPixFormat(format));
}
public JBitmap(String fileName, JBitmapPixelFormat _format)
{
format = _format;
bitmap = new Bitmap(fileName);
bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, GetPixFormat(format));
}
public JBitmap CreateCopy()
{
JBitmap newBitmap = new JBitmap(w, h, format);
Blit(newBitmap, w, h);
return newBitmap;
}
public void Clear(byte val)
{
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
for (int i = 0; i < bitmapData.Height; i++)
{
for (int j = 0; j < bitmapData.Width; j++)
{
(*sourcePtr) = val;
sourcePtr++;
}
sourcePtr += bitmapData.Stride - bitmapData.Width * 1; //move to the end of the line (past unused space)
}
}
}
public void Clear24(byte r, byte g, byte b)
{
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
for (int i = 0; i < bitmapData.Height; i++)
{
for (int j = 0; j < bitmapData.Width; j++)
{
*(sourcePtr) = b;
*(sourcePtr + 1) = g;
*(sourcePtr + 2) = r;
sourcePtr += 3;
}
sourcePtr += bitmapData.Stride - bitmapData.Width * 3; //move to the end of the line (past unused space)
}
}
}
/// <summary>
/// Blits this bitmap onto the target - clips regions that are out of bounds.
///
/// Only supported for 8bbp
/// </summary>
/// <param name="target"></param>
/// <param name="targetX"></param>
/// <param name="targetY"></param>
public void BlitOLD(JBitmap target, int px, int py){
//currently only supported for 8 bpp source / target
if (bitmapData.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed &&
target.bitmapData.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(px,0);
targetEndX = Math.Min(px + bitmapData.Width, target.bitmapData.Width);
targetStartY = Math.Max(py,0);
targetEndY = Math.Min(py + bitmapData.Height, target.bitmapData.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if(copyW < 0 ){
return;
}
if(copyH < 0){
return;
}
int sourceStartX = targetStartX - px;
int sourceStartY = targetStartY - py;
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
byte* targetPtr = (byte*)(target.bitmapData.Scan0);
byte* targetY = targetPtr + targetStartY * target.bitmapData.Stride;
byte* sourceY = sourcePtr + sourceStartY * bitmapData.Stride;
for (int y = 0; y < copyH; y++, targetY += target.bitmapData.Stride, sourceY += bitmapData.Stride)
{
byte* targetOffset = targetY + targetStartX;
byte* sourceOffset = sourceY + sourceStartX;
for (int x = 0; x < copyW; x++, targetOffset++, sourceOffset++)
{
*(targetOffset) = *(sourceOffset);
}
}
}
}
}
public void Blit(JBitmap target, int srcPx, int srcPy, int srcW, int srcH, int px, int py){
if (target.format != format)
{
throw new Exception("Attempted to blit between bitmaps not of the same format");
}
int bpp = (int)format;
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(px, 0);
targetEndX = Math.Min(px + srcW, target.bitmapData.Width);
targetStartY = Math.Max(py, 0);
targetEndY = Math.Min(py + srcH, target.bitmapData.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if (copyW < 0)
{
return;
}
if (copyH < 0)
{
return;
}
int sourceStartX = srcPx + targetStartX - px;
int sourceStartY = srcPy + targetStartY - py;
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
byte* targetPtr = (byte*)(target.bitmapData.Scan0);
byte* targetY = targetPtr + targetStartY * target.bitmapData.Stride;
byte* sourceY = sourcePtr + sourceStartY * bitmapData.Stride;
for (int y = 0; y < copyH; y++, targetY += target.bitmapData.Stride, sourceY += bitmapData.Stride)
{
byte* targetOffset = targetY + targetStartX * bpp;
byte* sourceOffset = sourceY + sourceStartX * bpp;
for (int x = 0; x < copyW * bpp; x++, targetOffset++, sourceOffset++)
{
*(targetOffset) = *(sourceOffset);
}
}
}
}
public void Blit(JBitmap target, int px, int py)
{
if (target.format != format)
{
throw new Exception("Attempted to blit between bitmaps not of the same format");
}
int bpp = (int)format;
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(px, 0);
targetEndX = Math.Min(px + bitmapData.Width, target.bitmapData.Width);
targetStartY = Math.Max(py, 0);
targetEndY = Math.Min(py + bitmapData.Height, target.bitmapData.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if (copyW < 0)
{
return;
}
if (copyH < 0)
{
return;
}
int sourceStartX = targetStartX - px;
int sourceStartY = targetStartY - py;
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
byte* targetPtr = (byte*)(target.bitmapData.Scan0);
byte* targetY = targetPtr + targetStartY * target.bitmapData.Stride;
byte* sourceY = sourcePtr + sourceStartY * bitmapData.Stride;
for (int y = 0; y < copyH; y++, targetY += target.bitmapData.Stride, sourceY += bitmapData.Stride)
{
byte* targetOffset = targetY + targetStartX*bpp;
byte* sourceOffset = sourceY + sourceStartX*bpp;
for (int x = 0; x < copyW*bpp; x++, targetOffset++, sourceOffset++)
{
*(targetOffset) = *(sourceOffset);
}
}
}
}
/// <summary>
/// Additively blits onto the target.
///
/// Only supported for 8bbp.
/// </summary>
/// <param name="target"></param>
/// <param name="targetX"></param>
/// <param name="targetY"></param>
public void BlitAdditive(JBitmap target, int px, int py, float strength)
{
//currently only supported for 8 bpp source / target
if (bitmapData.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed &&
target.bitmapData.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(px, 0);
targetEndX = Math.Min(px + bitmapData.Width, target.bitmapData.Width);
targetStartY = Math.Max(py, 0);
targetEndY = Math.Min(py + bitmapData.Height, target.bitmapData.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if (copyW < 0)
{
return;
}
if (copyH < 0)
{
return;
}
int sourceStartX = targetStartX - px;
int sourceStartY = targetStartY - py;
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
byte* targetPtr = (byte*)(target.bitmapData.Scan0);
int sum;
byte* targetY = targetPtr + targetStartY * target.bitmapData.Stride;
byte* sourceY = sourcePtr + sourceStartY * bitmapData.Stride;
for (int y = 0; y < copyH; y++, targetY += target.bitmapData.Stride, sourceY += bitmapData.Stride)
{
byte* targetOffset = targetY + targetStartX;
byte* sourceOffset = sourceY + sourceStartX;
for (int x = 0; x < copyW; x++, targetOffset++, sourceOffset++)
{
sum = (int)*(targetOffset) + (int) (*(sourceOffset) * strength);
if (sum > 255)
{
*(targetOffset) = 255;
} else {
*(targetOffset) = (byte)sum;
}
}
}
}
}
}
/// <summary>
/// Additively blits onto the target.
///
/// Supports just 24bpp (source and target)
/// </summary>
/// <param name="target"></param>
/// <param name="targetX"></param>
/// <param name="targetY"></param>
public void BlitAdditive24(JBitmap target, int px, int py, float strength)
{
//currently only supported for 8 bpp source / target
if (bitmapData.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb &&
target.bitmapData.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(px, 0);
targetEndX = Math.Min(px + bitmapData.Width, target.bitmapData.Width);
targetStartY = Math.Max(py, 0);
targetEndY = Math.Min(py + bitmapData.Height, target.bitmapData.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if (copyW < 0)
{
return;
}
if (copyH < 0)
{
return;
}
int sourceStartX = targetStartX - px;
int sourceStartY = targetStartY - py;
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
byte* targetPtr = (byte*)(target.bitmapData.Scan0);
int sum;
byte* targetY = targetPtr + targetStartY * target.bitmapData.Stride;
byte* sourceY = sourcePtr + sourceStartY * bitmapData.Stride;
for (int y = 0; y < copyH; y++, targetY += target.bitmapData.Stride, sourceY += bitmapData.Stride)
{
byte* targetOffset = targetY + targetStartX*3;
byte* sourceOffset = sourceY + sourceStartX*3;
for (int x = 0; x < copyW*3; x++, targetOffset++, sourceOffset++)
{
sum = (int)*(targetOffset) + (int)(*(sourceOffset) * strength);
if (sum > 255)
{
*(targetOffset) = 255;
}
else
{
*(targetOffset) = (byte)sum;
}
}
}
}
}
}
public void BlitMax(JBitmap target, int px, int py, float strength)
{
//currently only supported for 8 bpp source / target
if (bitmapData.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed &&
target.bitmapData.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(px, 0);
targetEndX = Math.Min(px + bitmapData.Width, target.bitmapData.Width);
targetStartY = Math.Max(py, 0);
targetEndY = Math.Min(py + bitmapData.Height, target.bitmapData.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if (copyW < 0)
{
return;
}
if (copyH < 0)
{
return;
}
int sourceStartX = targetStartX - px;
int sourceStartY = targetStartY - py;
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
byte* targetPtr = (byte*)(target.bitmapData.Scan0);
int max;
byte* targetY = targetPtr + targetStartY * target.bitmapData.Stride;
byte* sourceY = sourcePtr + sourceStartY * bitmapData.Stride;
for (int y = 0; y < copyH; y++, targetY += target.bitmapData.Stride, sourceY += bitmapData.Stride)
{
byte* targetOffset = targetY + targetStartX;
byte* sourceOffset = sourceY + sourceStartX;
for (int x = 0; x < copyW; x++, targetOffset++, sourceOffset++)
{
max = Math.Max((int)*(targetOffset), (int) (*(sourceOffset) * strength));
if (*(sourceOffset) * strength > *(targetOffset))
{
max = (int) (*(sourceOffset) * strength );
if (max > 255)
{
*(targetOffset) = 255;
}
else
{
*(targetOffset) = (byte)max;
}
}
}
}
}
}
}
/// <summary>
/// Just unlocks the bits, so that the bitmap can no longer be blitted to / from.
/// </summary>
public void Free(){
bitmap.UnlockBits(bitmapData);
bitmap.Dispose();
}
/*
* Resizes this bitmap to a smaller size using area-weighted averaging.
*
*/
public void DownScale(int newWidth, int newHeight)
{
JBitmap newBitmap = new JBitmap(newWidth, newHeight, format);
float xscale = (float)bitmapData.Width / newWidth;
float yscale = (float)bitmapData.Height / newHeight;
byte r = 0, g = 0, b = 0, a = 0;
float summedR = 0f;
float summedG = 0f;
float summedB = 0f;
float summedA = 0f;
int left, right, top, bottom; //the area of old pixels covered by the new bitmap
float targetStartX, targetEndX;
float targetStartY, targetEndY;
float leftF, rightF, topF, bottomF; //edges of new pixel in old pixel coords
float weight;
float weightScale = xscale * yscale;
float weightTotalTest = 0f;
for (int m = 0; m < newHeight; m++)
{
for (int n = 0; n < newWidth; n++)
{
leftF = n * xscale;
rightF = (n + 1) * xscale;
topF = m * yscale;
bottomF = (m + 1) * yscale;
left = (int)leftF;
right = (int)rightF;
top = (int)topF;
bottom = (int)bottomF;
if (left < 0) left = 0;
if (top < 0) top = 0;
if (right >= bitmapData.Width) right = bitmapData.Width - 1;
if (bottom >= bitmapData.Height) bottom = bitmapData.Height - 1;
summedR = 0f;
summedG = 0f;
summedB = 0f;
summedA = 0f;
weightTotalTest = 0f;
for (int j = top; j <= bottom; j++)
{
for (int i = left; i <= right; i++)
{
targetStartX = Math.Max(leftF, i);
targetEndX = Math.Min(rightF, i + 1);
targetStartY = Math.Max(topF, j);
targetEndY = Math.Min(bottomF, j + 1);
weight = (targetEndX - targetStartX) * (targetEndY - targetStartY);
GetPixelFast(i, j, ref r, ref g, ref b, ref a);
summedR += weight * r;
summedG += weight * g;
summedB += weight * b;
summedA += weight * a;
weightTotalTest += weight;
}
}
summedR /= weightScale;
summedG /= weightScale;
summedB /= weightScale;
summedA /= weightScale;
if (summedR < 0) summedR = 0f;
if (summedG < 0) summedG = 0f;
if (summedB < 0) summedB = 0f;
if (summedA < 0) summedA = 0f;
if (summedR >= 256) summedR = 255;
if (summedG >= 256) summedG = 255;
if (summedB >= 256) summedB = 255;
if (summedA >= 256) summedA = 255;
newBitmap.PutPixelFast(n, m, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA);
}
}
this.Free();
this.bitmap = newBitmap.bitmap;
this.bitmapData = newBitmap.bitmapData;
this.format = newBitmap.format;
}
public byte GetPixel(int px, int py)
{
py = bitmapData.Height - py;
if (px >= 0 && py >= 0 && px < bitmapData.Width && py < bitmapData.Height)
{
unsafe
{
byte* addr = (byte*)(bitmapData.Scan0);
addr += bitmapData.Stride * py + px;
return *addr;
}
}
else
{
return 0;
}
}
public void GetPixel24(int px, int py, out byte r, out byte g, out byte b)
{
py = bitmapData.Height - py;
if (px >= 0 && py >= 0 && px < bitmapData.Width && py < bitmapData.Height)
{
unsafe
{
byte* addr = (byte*)(bitmapData.Scan0);
addr += bitmapData.Stride * py + px * 3;
b = *addr;
g = *(addr + 1);
r = *(addr + 2);
}
}
else
{
r = 0;
g = 0;
b = 0;
}
}
public void GetPixelFast(int px, int py, ref byte r, ref byte g, ref byte b, ref byte a)
{
unsafe
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * (int)format;
if (format == JBitmapPixelFormat.Format8bpp)
{
r = *addr;
}
else if (format == JBitmapPixelFormat.Format24bppBGR)
{
b = *addr;
g = *(addr + 1);
r = *(addr + 2);
}
else if (format == JBitmapPixelFormat.Format32bppBGRA)
{
b = *addr;
g = *(addr + 1);
r = *(addr + 2);
a = *(addr + 3);
}
}
}
public void GetPixelFast(int px, int py, ref byte r, ref byte g, ref byte b)
{
unsafe
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * (int)format;
if (format == JBitmapPixelFormat.Format8bpp)
{
r = *addr;
}
else if (format == JBitmapPixelFormat.Format24bppBGR)
{
b = *addr;
g = *(addr + 1);
r = *(addr + 2);
}
else if (format == JBitmapPixelFormat.Format32bppBGRA)
{
b = *addr;
g = *(addr + 1);
r = *(addr + 2);
}
}
}
/*
* For 24-bit - just returns blue channel
* For 32-bit - returns alpha channel
*/
public void GetPixelFast(int px, int py, ref byte col)
{
unsafe
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * (int)format;
if (format == JBitmapPixelFormat.Format8bpp)
{
col = *addr;
}
else if (format == JBitmapPixelFormat.Format24bppBGR)
{
col = *addr;
}
else if (format == JBitmapPixelFormat.Format32bppBGRA)
{
col = *(addr + 3);
}
}
}
public void PutPixelFast(int px, int py, byte r, byte g, byte b, byte a)
{
unsafe
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * (int)format;
if (format == JBitmapPixelFormat.Format8bpp)
{
*addr = r;
}
else if (format == JBitmapPixelFormat.Format24bppBGR)
{
*addr = b;
*(addr + 1) = g;
*(addr + 2) = r;
}
else if (format == JBitmapPixelFormat.Format32bppBGRA)
{
*addr = b;
*(addr + 1) = g;
*(addr + 2) = r;
*(addr + 3) = a;
}
}
}
public void PutPixelFast(int px, int py, byte r, byte g, byte b)
{
unsafe
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * (int)format;
if (format == JBitmapPixelFormat.Format8bpp)
{
*addr = r;
}
else if (format == JBitmapPixelFormat.Format24bppBGR)
{
*addr = b;
*(addr + 1) = g;
*(addr + 2) = r;
}
else if (format == JBitmapPixelFormat.Format32bppBGRA)
{
*addr = b;
*(addr + 1) = g;
*(addr + 2) = r;
}
}
}
/*
* For 24-bit - just sets blue channel
* For 32-bit - sets alpha channel
*/
public void PutPixelFast(int px, int py, byte col)
{
unsafe
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * (int)format;
if (format == JBitmapPixelFormat.Format8bpp)
{
*addr = col;
}
else if (format == JBitmapPixelFormat.Format24bppBGR)
{
*addr = col;
}
else if (format == JBitmapPixelFormat.Format32bppBGRA)
{
*(addr + 3) = col;
}
}
}
}
public class JBitmapManager
{
List<JBitmap> bitmapList;
public JBitmapManager()
{
bitmapList = new List<JBitmap>();
}
public JBitmap Add(JBitmap bmp)
{
bitmapList.Add(bmp);
return bmp;
}
public void Remove(JBitmap bmpToRemove)
{
bitmapList.Remove(bmpToRemove);
bmpToRemove.Free();
}
public void FreeAll()
{
foreach (JBitmap bmp in bitmapList)
{
bmp.Free();
}
}
}
}

View File

@@ -0,0 +1,136 @@
using System;
namespace QuickFont
{
public class JMath
{
public const float PI = (float)Math.PI;
public static float Cos(float ang)
{
return (float)Math.Cos(ang);
}
public static float Sin(float ang)
{
return (float)Math.Sin(ang);
}
//fast version of gluproject which only works only for scale / translate transformations in gluOrthog - use FastProject2 for even better speed
public static void FastProject(float px, float py, double[] modelView, double[] projection, int[] view, out float scrx, out float scry){
float w = (float) (2 / projection[0]);
float h = (float) (-2 / projection[5]);
scrx = (float)((px * modelView[0] + modelView[12]) * view[2] / w + view[0]);
scry = (float)((1 - (py * modelView[5] + modelView[13]) / h) * view[3] + view[1]);
}
//even faster simplified version of gluproject
public static void FastProject2(float px, float py, out float scrx, out float scry)
{
scrx = px * fastProjectConsts[0] + fastProjectConsts[1];
scry = py * fastProjectConsts[2] + fastProjectConsts[3];
}
private static float[] fastProjectConsts = new float[4];
public static void FastProjectSetConsts(double[] modelView, double[] projection, int[] view){
float w = (float) (2 / projection[0]);
float h = (float) (-2 / projection[5]);
fastProjectConsts[0] = (float)(modelView[0] * view[2] / w);
fastProjectConsts[1] = (float)(modelView[12] * view[2] / w + view[0]);
fastProjectConsts[2] = (float)(-modelView[5] * view[3] / h);
fastProjectConsts[3] = (float)( (1 - modelView[13] /h) * view[3] + view[1]);
}
public static float distSquared(float x1, float y1, float x2, float y2)
{
return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
}
/// <summary>
/// Returns the power of 2 that is closest to x, but not smaller than x.
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
public static int pot(int x){
int shifts = 0;
uint val = (uint)x;
if (x < 0)
{
return 0;
}
while (val > 0)
{
val = val >> 1;
shifts++;
}
val = (uint)1 << (shifts - 1);
if (val < x)
{
val = val << 1;
}
return (int)val;
}
public static double StandardDeviation(double[] data)
{
if (data.Length == 0)
return 0;
double avg = Average(data);
double totalVariance = 0;
foreach (var v in data)
totalVariance += (v - avg) * (v - avg);
return Math.Sqrt(totalVariance / data.Length);
}
private static double Average(double[] data)
{
if (data.Length == 0)
return 0d;
double total = 0;
foreach (var v in data)
total += v;
return total / data.Length;
}
}
}

View File

@@ -0,0 +1,463 @@
using OpenTK;
using OpenTK.Graphics.OpenGL;
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace QuickFont
{
public class JTexture
{
private bool isSubTexture;
private float _h;
public float h
{
get { return _h; }
}
private float _w;
public float w
{
get { return _w; }
}
private int _GLTexID;
public int GLTexID
{
get { return _GLTexID; }
}
private bool _hasAlpha;
public bool hasAlpha
{
get { return _hasAlpha; }
}
private bool _useAlpha;
public bool useAlpha
{
get { return _useAlpha; }
//can only use alpha if the texture has alpha (but can turn alpha off
//when drawing even if the texture has alpha)
set { _useAlpha = value && _hasAlpha; }
}
private Vector2 bottomLeft;
private Vector2 topRight;
public JTexture()
{
_h = _w = 0;
_GLTexID = -1;
_useAlpha = false;
_hasAlpha = false;
}
public void SetDrawSize(float width, float height)
{
_w = width;
_h = height;
}
#region Drawing
public void Draw(Vector2 pos)
{
Draw(pos,1f);
}
public void Draw(Vector2 pos, float scale)
{
Draw(pos, new Vector2(scale,scale));
}
public void Draw(Vector2 pos, Vector2 scale)
{
GL.PushMatrix();
GL.Translate(pos.X, pos.Y, 0f);
GL.Scale(scale.X, scale.Y, 1f);
Draw();
GL.PopMatrix();
}
/// <summary>
/// Draw using JTexture blending settings (ordinary ones).
/// </summary>
public void Draw()
{
if (_GLTexID == -1)
{
return; //no texture loaded yet
}
if (_useAlpha)
{
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
}
else
{
GL.Disable(EnableCap.Blend);
}
DrawBasic();
}
/// <summary>
/// Draw using JTexture blending settings (ordinary ones).
/// </summary>
public void DrawCentred()
{
if (_GLTexID == -1)
{
return; //no texture loaded yet
}
if (_useAlpha)
{
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
}
else
{
GL.Disable(EnableCap.Blend);
}
DrawBasicCentred();
}
public void DrawBasicCentred(Vector2 pos, Vector2 scale,float ang)
{
GL.PushMatrix();
GL.Translate(pos.X, pos.Y, 0f);
GL.Scale(scale.X, scale.Y, 1f);
GL.Rotate(ang, 0f, 0f, 1f);
GL.Translate(-w * 0.5f, -h * 0.5f, 0f);
DrawBasic();
GL.PopMatrix();
}
public void DrawBasicCentred()
{
GL.PushMatrix();
GL.Translate(-w * 0.5f, -h * 0.5f, 0f);
DrawBasic();
GL.PopMatrix();
}
public void DrawBasic(Vector2 pos)
{
DrawBasic(pos, 1f);
}
public void DrawBasic(Vector2 pos, float scale)
{
DrawBasic(pos, new Vector2(scale, scale));
}
public void DrawBasic(Vector2 pos, Vector2 scale)
{
GL.PushMatrix();
GL.Translate(pos.X, pos.Y, 0f);
GL.Scale(scale.X, scale.Y, 1f);
DrawBasic();
GL.PopMatrix();
}
/// <summary>
/// Draws the texture without changing any blending settings. Use this
/// when you wish to set the blending settings yourself.
/// </summary>
public void DrawBasic()
{
GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, _GLTexID);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(bottomLeft); GL.Vertex2(0f, _h);
GL.TexCoord2(bottomLeft.X, topRight.Y); GL.Vertex2(0, 0);
GL.TexCoord2(topRight); GL.Vertex2(_w, 0f);
GL.TexCoord2(topRight.X, bottomLeft.Y); GL.Vertex2(_w, _h);
GL.End();
}
#endregion
public void Free()
{
if (_GLTexID != -1)
{
//only free root textures
if (!isSubTexture)
GL.DeleteTexture(_GLTexID);
}
}
/// <summary>
/// Construct a JTexture when the GLTexture already exists. texW and texH are the actual
/// dimensions of the given opengl texture (usually pots). Width and height are always
/// less than or equal to texW and texH respectively, and are the part of the texture to
/// be used starting in the bottom left corner.
/// </summary>
/// <param name="GLTex"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="alpha"></param>
public JTexture(int GLTex, float width, float height, bool alpha)
{
isSubTexture = false;
_GLTexID = GLTex;
_w = width;
_h = height;
_hasAlpha = _useAlpha = alpha;
bottomLeft = new Vector2(0f, 0f);
topRight = new Vector2(1f, 1f);
}
/// <summary>
/// Constructs a JTexture from a parent texture
/// </summary>
/// <param name="parent"></param>
/// <param name="topLeftX"></param>
/// <param name="topLeftY"></param>
/// <param name="width"></param>
/// <param name="height"></param>
public JTexture(JTexture parent, float topLeftX, float topLeftY, float width, float height, bool alpha)
{
if (topLeftX < 0 || topLeftY < 0 || topLeftX + width > parent.w || topLeftY + height > parent.h)
{
throw new ArgumentException("Attempted to define a sub JTexture outside the bounds of the parent");
}
isSubTexture = true;
_GLTexID = parent.GLTexID;
_hasAlpha = parent.hasAlpha;
useAlpha = alpha;
_w = width;
_h = height;
float parentTextureW = parent.topRight.X - parent.bottomLeft.X;
float parentTextureH = parent.topRight.Y - parent.bottomLeft.Y;
bottomLeft.X = parent.bottomLeft.X + (float)topLeftX * parentTextureW / parent.w;
topRight.X = parent.bottomLeft.X + (float)(topLeftX + width) * parentTextureW / parent.w;
bottomLeft.Y = parent.bottomLeft.Y + (1 - (float)(topLeftY + height) / parent.h) * parentTextureH;
topRight.Y = parent.bottomLeft.Y + (1 - (float)topLeftY / parent.h) * parentTextureH;
}
public JTexture(String fileName, bool alpha)
: this(fileName, alpha, false)
{
}
public JTexture(String fileName, bool alpha, bool padToPowerOfTwo)
{
Bitmap bitmapSource = new Bitmap(fileName);
var format = alpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb;
BitmapData dataSource = bitmapSource.LockBits(new Rectangle(0, 0, bitmapSource.Width, bitmapSource.Height),ImageLockMode.ReadOnly, format);
JTextureConstructorHelper(dataSource, alpha, padToPowerOfTwo);
bitmapSource.UnlockBits(dataSource);
}
public JTexture(JBitmap jbitmap, bool alpha)
{
JTextureConstructorHelper(jbitmap.bitmapData, alpha, false);
}
public JTexture(JBitmap jbitmap, bool alpha, bool padToPowerOfTwo)
{
JTextureConstructorHelper(jbitmap.bitmapData, alpha, padToPowerOfTwo);
}
private void JTextureConstructorHelper(BitmapData dataSource, bool alpha, bool padToPowerOfTwo)
{
int texture;
int bpp;
Bitmap bitmapTarget;
_w = dataSource.Width;
_h = dataSource.Height;
_hasAlpha = _useAlpha = alpha;
isSubTexture = false;
GL.Enable(EnableCap.Texture2D);
GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
GL.GenTextures(1, out texture);
GL.BindTexture(TextureTarget.Texture2D, texture);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Clamp);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Clamp);
System.Drawing.Imaging.PixelFormat format1;
PixelInternalFormat format2;
OpenTK.Graphics.OpenGL.PixelFormat format3;
if (alpha)
{
format1 = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
format2 = PixelInternalFormat.Rgba;
format3 = OpenTK.Graphics.OpenGL.PixelFormat.Bgra;
bpp = 4;
}
else
{
format1 = System.Drawing.Imaging.PixelFormat.Format24bppRgb;
format2 = PixelInternalFormat.Three;
format3 = OpenTK.Graphics.OpenGL.PixelFormat.Bgr;
bpp = 3;
}
int targetW, targetH;
if (!padToPowerOfTwo)
{
targetW = (int)_w;
targetH = (int)_h;
}
else
{
targetW = JMath.pot((int)_w);
targetH = JMath.pot((int)_h);
}
bitmapTarget = new Bitmap(targetW, targetH, format1);
bottomLeft = new Vector2(0f, 0f);
topRight = new Vector2((float)_w / targetW, (float)_h / targetH);
BitmapData dataTarget = bitmapTarget.LockBits(new Rectangle(0, 0, bitmapTarget.Width, bitmapTarget.Height),
ImageLockMode.ReadWrite, format1);
//copy source data into the target data, flipping it vertically in the process
unsafe
{
byte* sourcePtr = (byte*)(dataSource.Scan0);
byte* targetPtr = (byte*)(dataTarget.Scan0);
targetPtr += dataTarget.Stride * (dataSource.Height - 1); //target moves to start of last line
for (int i = 0; i < dataSource.Height; i++)
{
for (int j = 0; j < dataSource.Width; j++)
{
// write the logic implementation here
for (int k = 0; k < bpp; k++)
{
(*targetPtr) = (*sourcePtr);
sourcePtr++;
targetPtr++;
}
}
sourcePtr += dataSource.Stride - dataSource.Width * bpp; //move to the end of the line (past unused space)
targetPtr += dataTarget.Stride - dataSource.Width * bpp; //move to the end of the line (past unused space)
targetPtr -= dataTarget.Stride * 2; //move up a line
}
}
GL.TexImage2D(TextureTarget.Texture2D, 0, format2, dataTarget.Width, dataTarget.Height, 0,
format3, PixelType.UnsignedByte, dataTarget.Scan0);
bitmapTarget.UnlockBits(dataTarget);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
_GLTexID = texture;
}
}
}

View File

@@ -0,0 +1,39 @@
using System.Collections.Generic;
namespace QuickFont
{
/// <summary>
/// A very simple class for managing a collection of textures.
/// </summary>
public class JTextureManager
{
List<JTexture> textureList;
public JTextureManager()
{
textureList = new List<JTexture>();
}
public JTexture Add(JTexture texture)
{
textureList.Add(texture);
return texture;
}
public void Remove(JTexture textureToRemove)
{
textureList.Remove(textureToRemove);
textureToRemove.Free();
}
public void FreeAll()
{
foreach (JTexture tex in textureList)
{
tex.Free();
}
}
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
namespace QuickFont
{
class KerningCalculator
{
private struct XLimits
{
public int Min;
public int Max;
}
private static int Kerning(QFontGlyph g1, QFontGlyph g2, XLimits[] lim1, XLimits[] lim2, QFontKerningConfiguration config)
{
int yOffset1 = g1.yOffset;
int yOffset2 = g2.yOffset;
int startY = Math.Max(yOffset1, yOffset2);
int endY = Math.Min(g1.rect.Height + yOffset1, g2.rect.Height + yOffset2);
int w1 = g1.rect.Width;
int worstCase = w1;
//TODO - offset startY, endY by yOffset1 so that lim1[j-yOffset1] can be written as lim1[j], will need another var for yOffset2
for (int j = startY; j < endY; j++)
worstCase = Math.Min(worstCase, w1 - lim1[j-yOffset1].Max + lim2[j-yOffset2].Min);
worstCase = Math.Min(worstCase, g1.rect.Width);
worstCase = Math.Min(worstCase, g2.rect.Width);
//modify by character kerning rules
CharacterKerningRule kerningRule = config.GetOverridingCharacterKerningRuleForPair(""+g1.character + g2.character);
if (kerningRule == CharacterKerningRule.Zero)
{
return 0;
}
else if (kerningRule == CharacterKerningRule.NotMoreThanHalf)
{
return (int)Math.Min(Math.Min(g1.rect.Width,g2.rect.Width)*0.5f, worstCase);
}
return worstCase;
}
public static Dictionary<String, int> CalculateKerning(char[] charSet, QFontGlyph[] glyphs, List<QBitmap> bitmapPages, QFontKerningConfiguration config)
{
var kerningPairs = new Dictionary<String, int>();
//we start by computing the index of the first and last non-empty pixel in each row of each glyph
XLimits[][] limits = new XLimits[charSet.Length][];
int maxHeight = 0;
for (int n = 0; n < charSet.Length; n++)
{
var rect = glyphs[n].rect;
var page = bitmapPages[glyphs[n].page];
limits[n] = new XLimits[rect.Height];
maxHeight = Math.Max(rect.Height, maxHeight);
int yStart = rect.Y;
int yEnd = rect.Y + rect.Height;
int xStart = rect.X;
int xEnd = rect.X + rect.Width;
for (int j = yStart; j < yEnd; j++)
{
int last = xStart;
bool yetToFindFirst = true;
for (int i = xStart; i < xEnd; i++)
{
if (!QBitmap.EmptyAlphaPixel(page.bitmapData, i, j,config.alphaEmptyPixelTolerance))
{
if (yetToFindFirst)
{
limits[n][j - yStart].Min = i - xStart;
yetToFindFirst = false;
}
last = i;
}
}
limits[n][j - yStart].Max = last - xStart;
if (yetToFindFirst)
limits[n][j - yStart].Min = xEnd - 1;
}
}
//we now bring up each row to the max (or min) of it's two adjacent rows, this is to stop glyphs sliding together too closely
var tmp = new XLimits[maxHeight];
for (int n = 0; n < charSet.Length; n++)
{
//clear tmp
for (int j = 0; j < limits[n].Length; j++)
tmp[j] = limits[n][j];
for (int j = 0; j < limits[n].Length; j++)
{
if(j != 0){
tmp[j].Min = Math.Min(limits[n][j - 1].Min, tmp[j].Min);
tmp[j].Max = Math.Max(limits[n][j - 1].Max, tmp[j].Max);
}
if (j != limits[n].Length - 1)
{
tmp[j].Min = Math.Min(limits[n][j + 1].Min, tmp[j].Min);
tmp[j].Max = Math.Max(limits[n][j + 1].Max, tmp[j].Max);
}
}
for (int j = 0; j < limits[n].Length; j++)
limits[n][j] = tmp[j];
}
for (int i = 0; i < charSet.Length; i++)
for (int j = 0; j < charSet.Length; j++)
kerningPairs.Add("" + charSet[i] + charSet[j], 1-Kerning(glyphs[i], glyphs[j], limits[i], limits[j],config));
return kerningPairs;
}
}
}

View File

@@ -0,0 +1,156 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using System.Collections.Generic;
namespace QuickFont
{
public struct Viewport
{
public int X, Y, Width, Height;
public Viewport(int X, int Y, int Width, int Height) { this.X = X; this.Y = Y; this.Width = Width; this.Height = Height; }
}
public struct TransformViewport
{
public float X, Y, Width, Height;
public TransformViewport(float X, float Y, float Width, float Height) { this.X = X; this.Y = Y; this.Width = Width; this.Height = Height; }
}
class ProjectionStack
{
public static bool Begun { get; private set; }
private static Stack<Viewport?> cachedViewportStack = new Stack<Viewport?>();
static ProjectionStack()
{
Begun = false;
cachedViewportStack.Push(null);
}
//The currently set viewport
public static Viewport? CurrentViewport {
get {
lock (cachedViewportStack)
{
var currentViewport = cachedViewportStack.Peek();
if (currentViewport == null)
{
UpdateCurrentViewportFromHardware();
currentViewport = cachedViewportStack.Peek();
}
return currentViewport;
}
}
}
public static void UpdateCurrentViewportFromHardware()
{
lock (cachedViewportStack)
{
GraphicsContext.Assert();
Viewport viewport = new Viewport();
GL.GetInteger(GetPName.Viewport, out viewport.X);
cachedViewportStack.Pop();
cachedViewportStack.Push(viewport);
}
}
public static void PushSoftwareViewport(Viewport viewport)
{
lock (cachedViewportStack)
{
cachedViewportStack.Push(viewport);
}
}
public static void PopSoftwareViewport()
{
lock (cachedViewportStack)
{
cachedViewportStack.Pop();
}
}
public static void InvalidateViewport()
{
lock (cachedViewportStack)
{
cachedViewportStack.Pop();
cachedViewportStack.Push(null);
}
}
public static void GetCurrentOrthogProjection(out bool isOrthog, out float left, out float right, out float bottom, out float top)
{
Matrix4 matrix = new Matrix4();
GL.GetFloat(GetPName.ProjectionMatrix, out matrix.Row0.X);
if (matrix.M11 == 0 || matrix.M22 == 0)
{
isOrthog = false;
left = right = bottom = top = 0;
return;
}
left = -(1f + matrix.M41) / (matrix.M11);
right = (1f - matrix.M41) / (matrix.M11);
bottom = -(1 + matrix.M42) / (matrix.M22);
top = (1 - matrix.M42) / (matrix.M22);
isOrthog =
matrix.M12 == 0 && matrix.M13 == 0 && matrix.M14 == 0 &&
matrix.M21 == 0 && matrix.M23 == 0 && matrix.M24 == 0 &&
matrix.M31 == 0 && matrix.M32 == 0 && matrix.M34 == 0 &&
matrix.M44 == 1f;
}
public static void Begin()
{
GraphicsContext.Assert();
Viewport currentVp = (Viewport)CurrentViewport;
GL.MatrixMode(MatrixMode.Projection);
GL.PushMatrix(); //push projection matrix
GL.LoadIdentity();
GL.Ortho(currentVp.X, currentVp.Width, currentVp.Height, currentVp.Y, -1.0, 1.0);
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix(); //push modelview matrix
GL.LoadIdentity();
Begun = true;
}
public static void End()
{
GraphicsContext.Assert();
GL.MatrixMode(MatrixMode.Modelview);
GL.PopMatrix(); //pop modelview
GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix(); //pop projection
GL.MatrixMode(MatrixMode.Modelview);
Begun = false;
}
}
}

View File

@@ -0,0 +1,552 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace QuickFont
{
public class QBitmap
{
public Bitmap bitmap;
public BitmapData bitmapData;
public QBitmap(string filePath)
{
LockBits(new Bitmap(filePath));
}
public QBitmap(Bitmap bitmap)
{
LockBits(bitmap);
}
private void LockBits(Bitmap bitmap)
{
this.bitmap = bitmap;
bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
}
public void Clear32(byte r, byte g, byte b, byte a)
{
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
for (int i = 0; i < bitmapData.Height; i++)
{
for (int j = 0; j < bitmapData.Width; j++)
{
*(sourcePtr) = b;
*(sourcePtr + 1) = g;
*(sourcePtr + 2) = r;
*(sourcePtr + 3) = a;
sourcePtr += 4;
}
sourcePtr += bitmapData.Stride - bitmapData.Width * 4; //move to the end of the line (past unused space)
}
}
}
/// <summary>
/// Returns try if the given pixel is empty (i.e. black)
/// </summary>
public static unsafe bool EmptyPixel(BitmapData bitmapData, int px, int py)
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 3;
return (*addr == 0 && *(addr + 1) == 0 && *(addr + 2) == 0);
}
/// <summary>
/// Returns try if the given pixel is empty (i.e. alpha is zero)
/// </summary>
public static unsafe bool EmptyAlphaPixel(BitmapData bitmapData, int px, int py, byte alphaEmptyPixelTolerance)
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4;
return (*(addr + 3) <= alphaEmptyPixelTolerance);
}
/// <summary>
/// Blits a block of a bitmap data from source to destination, using the luminance of the source to determine the
/// alpha of the target. Source must be 24-bit, target must be 32-bit.
/// </summary>
public static void BlitMask(BitmapData source, BitmapData target, int srcPx, int srcPy, int srcW, int srcH, int px, int py)
{
int sourceBpp = 3;
int targetBpp = 4;
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(px, 0);
targetEndX = Math.Min(px + srcW, target.Width);
targetStartY = Math.Max(py, 0);
targetEndY = Math.Min(py + srcH, target.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if (copyW < 0)
{
return;
}
if (copyH < 0)
{
return;
}
int sourceStartX = srcPx + targetStartX - px;
int sourceStartY = srcPy + targetStartY - py;
unsafe
{
byte* sourcePtr = (byte*)(source.Scan0);
byte* targetPtr = (byte*)(target.Scan0);
byte* targetY = targetPtr + targetStartY * target.Stride;
byte* sourceY = sourcePtr + sourceStartY * source.Stride;
for (int y = 0; y < copyH; y++, targetY += target.Stride, sourceY += source.Stride)
{
byte* targetOffset = targetY + targetStartX * targetBpp;
byte* sourceOffset = sourceY + sourceStartX * sourceBpp;
for (int x = 0; x < copyW; x++, targetOffset += targetBpp, sourceOffset += sourceBpp)
{
int lume = *(sourceOffset) + *(sourceOffset + 1) + *(sourceOffset + 2);
lume /= 3;
if (lume > 255)
lume = 255;
*(targetOffset + 3) = (byte)lume;
}
}
}
}
/// <summary>
/// Blits from source to target. Both source and target must be 32-bit
/// </summary>
public static void Blit(BitmapData source, BitmapData target, Rectangle sourceRect, int px, int py)
{
Blit(source, target, sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height, px, py);
}
/// <summary>
/// Blits from source to target. Both source and target must be 32-bit
/// </summary>
public static void Blit(BitmapData source, BitmapData target, int srcPx, int srcPy, int srcW, int srcH, int destX, int destY)
{
int bpp = 4;
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(destX, 0);
targetEndX = Math.Min(destX + srcW, target.Width);
targetStartY = Math.Max(destY, 0);
targetEndY = Math.Min(destY + srcH, target.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if (copyW < 0)
{
return;
}
if (copyH < 0)
{
return;
}
int sourceStartX = srcPx + targetStartX - destX;
int sourceStartY = srcPy + targetStartY - destY;
unsafe
{
byte* sourcePtr = (byte*)(source.Scan0);
byte* targetPtr = (byte*)(target.Scan0);
byte* targetY = targetPtr + targetStartY * target.Stride;
byte* sourceY = sourcePtr + sourceStartY * source.Stride;
for (int y = 0; y < copyH; y++, targetY += target.Stride, sourceY += source.Stride)
{
byte* targetOffset = targetY + targetStartX * bpp;
byte* sourceOffset = sourceY + sourceStartX * bpp;
for (int x = 0; x < copyW*bpp; x++, targetOffset ++, sourceOffset ++)
*(targetOffset) = *(sourceOffset);
}
}
}
public unsafe void PutPixel32(int px, int py, byte r, byte g, byte b, byte a)
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4;
*addr = b;
*(addr + 1) = g;
*(addr + 2) = r;
*(addr + 3) = a;
}
public unsafe void GetPixel32(int px, int py, ref byte r, ref byte g, ref byte b, ref byte a)
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4;
b = *addr;
g = *(addr + 1);
r = *(addr + 2);
a = *(addr + 3);
}
public unsafe void PutAlpha32(int px, int py, byte a)
{
*((byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4 + 3) = a;
}
public unsafe void GetAlpha32(int px, int py, ref byte a)
{
a = *((byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4 + 3);
}
public void DownScale32(int newWidth, int newHeight)
{
QBitmap newBitmap = new QBitmap(new Bitmap(newWidth, newHeight, bitmap.PixelFormat));
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new Exception("DownsScale32 only works on 32 bit images");
float xscale = (float)bitmapData.Width / newWidth;
float yscale = (float)bitmapData.Height / newHeight;
byte r = 0, g = 0, b = 0, a = 0;
float summedR = 0f;
float summedG = 0f;
float summedB = 0f;
float summedA = 0f;
int left, right, top, bottom; //the area of old pixels covered by the new bitmap
float targetStartX, targetEndX;
float targetStartY, targetEndY;
float leftF, rightF, topF, bottomF; //edges of new pixel in old pixel coords
float weight;
float weightScale = xscale * yscale;
float totalColourWeight = 0f;
for (int m = 0; m < newHeight; m++)
{
for (int n = 0; n < newWidth; n++)
{
leftF = n * xscale;
rightF = (n + 1) * xscale;
topF = m * yscale;
bottomF = (m + 1) * yscale;
left = (int)leftF;
right = (int)rightF;
top = (int)topF;
bottom = (int)bottomF;
if (left < 0) left = 0;
if (top < 0) top = 0;
if (right >= bitmapData.Width) right = bitmapData.Width - 1;
if (bottom >= bitmapData.Height) bottom = bitmapData.Height - 1;
summedR = 0f;
summedG = 0f;
summedB = 0f;
summedA = 0f;
totalColourWeight = 0f;
for (int j = top; j <= bottom; j++)
{
for (int i = left; i <= right; i++)
{
targetStartX = Math.Max(leftF, i);
targetEndX = Math.Min(rightF, i + 1);
targetStartY = Math.Max(topF, j);
targetEndY = Math.Min(bottomF, j + 1);
weight = (targetEndX - targetStartX) * (targetEndY - targetStartY);
GetPixel32(i, j, ref r, ref g, ref b, ref a);
summedA += weight * a;
if (a != 0)
{
summedR += weight * r;
summedG += weight * g;
summedB += weight * b;
totalColourWeight += weight;
}
}
}
summedR /= totalColourWeight;
summedG /= totalColourWeight;
summedB /= totalColourWeight;
summedA /= weightScale;
if (summedR < 0) summedR = 0f;
if (summedG < 0) summedG = 0f;
if (summedB < 0) summedB = 0f;
if (summedA < 0) summedA = 0f;
if (summedR >= 256) summedR = 255;
if (summedG >= 256) summedG = 255;
if (summedB >= 256) summedB = 255;
if (summedA >= 256) summedA = 255;
newBitmap.PutPixel32(n, m, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA);
}
}
this.Free();
this.bitmap = newBitmap.bitmap;
this.bitmapData = newBitmap.bitmapData;
}
/// <summary>
/// Sets colour without touching alpha values
/// </summary>
/// <param name="r"></param>
/// <param name="g"></param>
/// <param name="b"></param>
public void Colour32(byte r, byte g, byte b)
{
unsafe
{
byte* addr;
for (int i = 0; i < bitmapData.Width; i++)
{
for (int j = 0; j < bitmapData.Height; j++)
{
addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * j + i * 4;
*addr = b;
*(addr + 1) = g;
*(addr + 2) = r;
}
}
}
}
/*
public void Blur(int radius, int passes)
{
QBitmap tmp = new QBitmap(new Bitmap(this.bitmap.Width, this.bitmap.Height, bitmap.PixelFormat));
byte r=0,g=0,b=0,a=0;
int summedR, summedG, summedB, summedA;
int weight = 0;
int xpos, ypos, x, y, kx, ky;
for (int pass = 0; pass < passes; pass++)
{
//horizontal pass
for (y = 0; y < bitmap.Height; y++)
{
for (x = 0; x < bitmap.Width; x++)
{
summedR = summedG = summedB = summedA = weight = 0;
for (kx = -radius; kx <= radius; kx++)
{
xpos = x + kx;
if (xpos >= 0 && xpos < bitmap.Width)
{
GetPixel32(xpos, y, ref r, ref g, ref b, ref a);
summedR += r;
summedG += g;
summedB += b;
summedA += a;
weight++;
}
}
summedR /= weight;
summedG /= weight;
summedB /= weight;
summedA /= weight;
tmp.PutPixel32(x, y, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA);
}
}
//vertical pass
for (x = 0; x < bitmap.Width; ++x)
{
for (y = 0; y < bitmap.Height; ++y)
{
summedR = summedG = summedB = summedA = weight = 0;
for (ky = -radius; ky <= radius; ky++)
{
ypos = y + ky;
if (ypos >= 0 && ypos < bitmap.Height)
{
tmp.GetPixel32(x, ypos, ref r, ref g, ref b, ref a);
summedR += r;
summedG += g;
summedB += b;
summedA += a;
weight++;
}
}
summedR /= weight;
summedG /= weight;
summedB /= weight;
summedA /= weight;
PutPixel32(x, y, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA);
}
}
}
tmp.Free();
}*/
public void BlurAlpha(int radius, int passes)
{
QBitmap tmp = new QBitmap(new Bitmap(this.bitmap.Width, this.bitmap.Height, bitmap.PixelFormat));
byte a = 0;
int summedA;
int weight = 0;
int xpos, ypos, x, y, kx, ky;
int width = bitmap.Width;
int height = bitmap.Height;
for (int pass = 0; pass < passes; pass++)
{
//horizontal pass
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
summedA = weight = 0;
for (kx = -radius; kx <= radius; kx++)
{
xpos = x + kx;
if (xpos >= 0 && xpos < width)
{
GetAlpha32(xpos, y, ref a);
summedA += a;
weight++;
}
}
summedA /= weight;
tmp.PutAlpha32(x, y, (byte)summedA);
}
}
//vertical pass
for (x = 0; x <width; ++x)
{
for (y = 0; y < height; ++y)
{
summedA = weight = 0;
for (ky = -radius; ky <= radius; ky++)
{
ypos = y + ky;
if (ypos >= 0 && ypos < height)
{
tmp.GetAlpha32(x, ypos,ref a);
summedA += a;
weight++;
}
}
summedA /= weight;
PutAlpha32(x, y, (byte)summedA);
}
}
}
tmp.Free();
}
public void Free()
{
bitmap.UnlockBits(bitmapData);
bitmap.Dispose();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Drawing;
namespace QuickFont
{
class QFontData : IDisposable
{
/// <summary>
/// Mapping from a pair of characters to a pixel offset
/// </summary>
public Dictionary<String, int> KerningPairs;
/// <summary>
/// List of texture pages
/// </summary>
public TexturePage[] Pages;
/// <summary>
/// Mapping from character to glyph index
/// </summary>
public Dictionary<char, QFontGlyph> CharSetMapping;
/// <summary>
/// The average glyph width
/// </summary>
public float meanGlyphWidth;
/// <summary>
/// The maximum glyph height
/// </summary>
public int maxGlyphHeight;
/// <summary>
/// Null if no dropShadow is available
/// </summary>
public QFont dropShadow;
/// <summary>
/// Whether the original font (from ttf) was detected to be monospaced
/// </summary>
public bool naturallyMonospaced = false;
/// <summary>
/// The font scaling due to the font being transformed to the
/// current viewport for consistent pixel-perfect size across
/// any resolution
/// </summary>
public float scaleDueToTransformToViewport = 1.0f;
public bool IsMonospacingActive(QFontRenderOptions options)
{
return (options.Monospacing == QFontMonospacing.Natural && naturallyMonospaced) || options.Monospacing == QFontMonospacing.Yes;
}
public float GetMonoSpaceWidth(QFontRenderOptions options)
{
return (float)Math.Ceiling(1 + (1 + options.CharacterSpacing) * meanGlyphWidth);
}
public List<String> Serialize()
{
var data = new List<String>();
data.Add("" + Pages.Length);
data.Add("" + CharSetMapping.Count);
foreach (var glyphChar in CharSetMapping)
{
var chr = glyphChar.Key;
var glyph = glyphChar.Value;
data.Add("" + chr + " " +
glyph.page + " " +
glyph.rect.X + " " +
glyph.rect.Y + " " +
glyph.rect.Width + " " +
glyph.rect.Height + " " +
glyph.yOffset);
}
return data;
}
public void Deserialize(List<String> input, out int pageCount, out char[] charSet)
{
CharSetMapping = new Dictionary<char, QFontGlyph>();
var charSetList = new List<char>();
try
{
pageCount = int.Parse(input[0]);
int glyphCount = int.Parse(input[1]);
for (int i = 0; i < glyphCount; i++)
{
var vals = input[2 + i].Split(' ');
var glyph = new QFontGlyph(int.Parse(vals[1]), new Rectangle(int.Parse(vals[2]), int.Parse(vals[3]), int.Parse(vals[4]), int.Parse(vals[5])), int.Parse(vals[6]), vals[0][0]);
CharSetMapping.Add(vals[0][0], glyph);
charSetList.Add(vals[0][0]);
}
}
catch (Exception e)
{
throw new Exception("Failed to parse qfont file. Invalid format.",e);
}
charSet = charSetList.ToArray();
}
public void CalculateMeanWidth()
{
meanGlyphWidth = 0f;
foreach (var glyph in CharSetMapping)
meanGlyphWidth += glyph.Value.rect.Width;
meanGlyphWidth /= CharSetMapping.Count;
}
public void CalculateMaxHeight()
{
maxGlyphHeight = 0;
foreach (var glyph in CharSetMapping)
maxGlyphHeight = Math.Max(glyph.Value.rect.Height, maxGlyphHeight);
}
/// <summary>
/// Returns the kerning length correction for the character at the given index in the given string.
/// Also, if the text is part of a textNode list, the nextNode is given so that the following
/// node can be checked incase of two adjacent word nodes.
/// </summary>
/// <param name="index"></param>
/// <param name="text"></param>
/// <param name="textNode"></param>
/// <returns></returns>
public int GetKerningPairCorrection(int index, string text, TextNode textNode)
{
if (KerningPairs == null)
return 0;
var chars = new char[2];
if (index + 1 == text.Length)
{
if (textNode != null && textNode.Next != null && textNode.Next.Type == TextNodeType.Word)
chars[1] = textNode.Next.Text[0];
else
return 0;
}
else
{
chars[1] = text[index + 1];
}
chars[0] = text[index];
String str = new String(chars);
if (KerningPairs.ContainsKey(str))
return KerningPairs[str];
return 0;
}
public virtual void Dispose()
{
foreach (var page in Pages)
page.Dispose();
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Drawing;
namespace QuickFont
{
public class QFontGlyph
{
/// <summary>
/// Which texture page the glyph is on
/// </summary>
public int page;
/// <summary>
/// The rectangle defining the glyphs position on the page
/// </summary>
public Rectangle rect;
/// <summary>
/// How far the glyph would need to be vertically offset to be vertically in line with the tallest glyph in the set of all glyphs
/// </summary>
public int yOffset;
/// <summary>
/// Which character this glyph represents
/// </summary>
public char character;
public QFontGlyph(int page, Rectangle rect, int yOffset, char character)
{
this.page = page;
this.rect = rect;
this.yOffset = yOffset;
this.character = character;
}
}
}

View File

@@ -0,0 +1,257 @@
using OpenTK;
using OpenTK.Graphics;
using System.Drawing;
namespace QuickFont
{
public enum QFontAlignment { Left=0, Right, Centre, Justify }
public enum QFontMonospacing { Natural = 0, Yes, No }
public class QFontRenderOptions
{
/// <summary>
/// The font colour
/// </summary>
public Color4 Colour = Color.FromArgb(255,255,255,255);
/// <summary>
/// Spacing between characters in units of average glyph width
/// </summary>
public float CharacterSpacing = 0.05f;
/// <summary>
/// Spacing between words in units of average glyph width
/// </summary>
public float WordSpacing = 0.9f;
/// <summary>
/// Line spacing in units of max glyph width
/// </summary>
public float LineSpacing = 1.0f;
/// <summary>
/// Whether to draw a drop-shadow. Note: this requires
/// the QFont to have been loaded with a drop shadow to
/// take effect.
/// </summary>
public bool DropShadowActive = false;
/// <summary>
/// Offset of the shadow from the font glyphs in units of average glyph width
/// </summary>
public Vector2 DropShadowOffset = new Vector2(0.16f, 0.16f);
/// <summary>
/// Opacity of drop shadows
/// </summary>
public float DropShadowOpacity = 0.5f;
/// <summary>
/// Whether to render the font in monospaced mode. If set to "Natural", then
/// monospacing will be used if the font loaded font was detected to be monospaced.
/// </summary>
public QFontMonospacing Monospacing = QFontMonospacing.Natural;
/// <summary>
/// This is intended as a means of rendering text pixel-perfectly at a
/// fixed display size (size on screen) independent of the screen resolution.
///
/// Ordinarily it is possible to render pixel-perfect text by calling
/// QFont.Begin() / QFont.End(); however, this means working in a coordinate
/// system corresponding to the current screen resolution. If the screen
/// resolution changes, then the display size of the font will change
/// accordingly which may not be desirable. Many games/applications prefer
/// to use a fixed orthog coordinate system that is independent of screen
/// resolution (e.g. 1000x1000) so that when the screen resolution changes,
/// everything is still the same size on screen, it simply has higher
/// definition - which is what this setting supports.
///
/// One option is simply not to call QFont.Begin() / QFont.End(). This
/// works; however, it becomes impossible to assure that glyphs are
/// rendered pixel-perfectly. Instead they will be scaled in hardware.
/// In most cases this looks fine; however, if you are a perfectionist,
/// you may prefer to use this option to assure pixel perfection.
///
/// Setting this option does two things:
///
/// Rendering to a specified position is transformed
/// Measurements are transformed
///
/// So for example, suppose the screen resolution is 1024x768, but you
/// wish to run orthog mode at 1000x1000. If you set:
///
/// myFont.Options.TransformToViewport = new Viewport(0,0,1000,1000);
///
/// Then, if you render at position 500,500:
///
/// QFont.Begin();
/// myFont.Options.LockToPixel = true;
/// myFont.Print("Hello",new Vector2(500,500));
/// QFont.End();
///
/// This will be printed pixel-pefectly at pixel position 512, 384.
///
/// Additionally the font will be measured in terms of your 500x500
/// coordinate system.
///
/// The only issue is that if you change the resolution, the size of your
/// font will change. You can get around this by loading a font size
/// that is proportional to the screen resolution. This makes sense:
/// if you want a font to be rendered pixel-perfectly at a higher
/// resolution, it will need to be a larger font. At present this
/// needs to be doen manually. E.g:
///
/// float fontScale = (float)Height / 800;
/// compyFontSmall = new QFont("Data/comfy.ttf", 14 * fontScale);
///
///
/// </summary>
public TransformViewport? TransformToViewport = null;
/// <summary>
/// Locks the position to a particular pixel, allowing the text to be rendered pixel-perfectly.
/// You need to turn this off if you wish to move text around the screen smoothly by fractions
/// of a pixel.
/// </summary>
public bool LockToPixel;
/// <summary>
/// Only applies when LockToPixel is true:
/// This is used to transition smoothly between being locked to pixels and not
/// </summary>
public float LockToPixelRatio = 1.0f;
/// <summary>
/// Whether to always set :
/// GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.DstAlpha);
/// before rendering text.
///
/// </summary>
public bool UseDefaultBlendFunction = true;
#region Justify Options
/// <summary>
/// When a line of text is justified, space may be inserted between
/// characters, and between words.
///
/// This parameter determines how this choice is weighted:
///
/// 0.0f => word spacing only
/// 1.0f => "fairly" distributed between both
/// > 1.0 => in favour of character spacing
///
/// This applies to expansions only.
///
/// </summary>
public float JustifyCharacterWeightForExpand
{
get { return justifyCharWeightForExpand; }
set {
justifyCharWeightForExpand = value;
if (justifyCharWeightForExpand < 0f)
justifyCharWeightForExpand = 0f;
else if (justifyCharWeightForExpand > 1.0f)
justifyCharWeightForExpand = 1.0f;
}
}
private float justifyCharWeightForExpand = 0.5f;
/// <summary>
/// When a line of text is justified, space may be removed between
/// characters, and between words.
///
/// This parameter determines how this choice is weighted:
///
/// 0.0f => word spacing only
/// 1.0f => "fairly" distributed between both
/// > 1.0 => in favour of character spacing
///
/// This applies to contractions only.
///
/// </summary>
public float JustifyCharacterWeightForContract
{
get { return justifyCharWeightForContract; }
set
{
justifyCharWeightForContract = value;
if (justifyCharWeightForContract < 0f)
justifyCharWeightForContract = 0f;
else if (justifyCharWeightForContract > 1.0f)
justifyCharWeightForContract = 1.0f;
}
}
private float justifyCharWeightForContract = 0.2f;
/// <summary>
/// Total justification cap as a fraction of the boundary width.
/// </summary>
public float JustifyCapExpand = 0.5f;
/// <summary>
/// Total justification cap as a fraction of the boundary width.
/// </summary>
public float JustifyCapContract = 0.1f;
/// <summary>
/// By what factor justification is penalized for being negative.
///
/// (e.g. if this is set to 3, then a contraction will only happen
/// over an expansion if it is 3 of fewer times smaller than the
/// expansion).
///
///
/// </summary>
public float JustifyContractionPenalty = 2;
#endregion
public QFontRenderOptions CreateClone()
{
var clone = new QFontRenderOptions();
clone.Colour = Colour;
clone.CharacterSpacing = CharacterSpacing;
clone.WordSpacing = WordSpacing;
clone.LineSpacing = LineSpacing;
clone.DropShadowActive = DropShadowActive;
clone.DropShadowOffset = DropShadowOffset;
clone.DropShadowOpacity = DropShadowOpacity;
clone.Monospacing = Monospacing;
clone.TransformToViewport = TransformToViewport;
clone.LockToPixel = LockToPixel;
clone.LockToPixelRatio = LockToPixelRatio;
clone.UseDefaultBlendFunction = UseDefaultBlendFunction;
clone.JustifyCharacterWeightForExpand = JustifyCharacterWeightForExpand;
clone.justifyCharWeightForExpand = justifyCharWeightForExpand;
clone.JustifyCharacterWeightForContract = JustifyCharacterWeightForContract;
clone.justifyCharWeightForContract = justifyCharWeightForContract;
clone.JustifyCapExpand = JustifyCapExpand;
clone.JustifyCapContract = JustifyCapContract;
clone.JustifyContractionPenalty = JustifyContractionPenalty;
return clone;
}
}
}

View File

@@ -0,0 +1,316 @@
using System;
using System.Collections;
using System.Text;
namespace QuickFont
{
enum TextNodeType { Word, LineBreak, Space }
class TextNode
{
public TextNodeType Type;
public string Text;
public float Length; //pixel length (without tweaks)
public float LengthTweak; //length tweak for justification
public float ModifiedLength
{
get { return Length + LengthTweak; }
}
public TextNode(TextNodeType Type, string Text){
this.Type = Type;
this.Text = Text;
}
public TextNode Next;
public TextNode Previous;
}
/// <summary>
/// Class to hide TextNodeList and related classes from
/// user whilst allowing a textNodeList to be passed around.
/// </summary>
public class ProcessedText
{
internal TextNodeList textNodeList;
internal float maxWidth;
internal QFontAlignment alignment;
}
/// <summary>
/// A doubly linked list of text nodes
/// </summary>
class TextNodeList : IEnumerable
{
public TextNode Head;
public TextNode Tail;
/// <summary>
/// Builds a doubly linked list of text nodes from the given input string
/// </summary>
/// <param name="text"></param>
public TextNodeList(string text)
{
#region parse text
text = text.Replace("\r\n", "\r");
bool wordInProgress = false;
StringBuilder currentWord = new StringBuilder();
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\r' || text[i] == '\n' || text[i] == ' ')
{
if (wordInProgress)
{
Add(new TextNode(TextNodeType.Word, currentWord.ToString()));
wordInProgress = false;
}
if (text[i] == '\r' || text[i] == '\n')
Add(new TextNode(TextNodeType.LineBreak, null));
else if (text[i] == ' ')
Add(new TextNode(TextNodeType.Space, null));
}
else
{
if (!wordInProgress)
{
wordInProgress = true;
currentWord = new StringBuilder();
}
currentWord.Append(text[i]);
}
}
if (wordInProgress)
Add(new TextNode(TextNodeType.Word, currentWord.ToString()));
#endregion
}
public void MeasureNodes(QFontData fontData, QFontRenderOptions options){
foreach(TextNode node in this){
if(node.Length == 0f)
node.Length = MeasureTextNodeLength(node,fontData,options);
}
}
private float MeasureTextNodeLength(TextNode node, QFontData fontData, QFontRenderOptions options)
{
bool monospaced = fontData.IsMonospacingActive(options);
float monospaceWidth = fontData.GetMonoSpaceWidth(options);
if (node.Type == TextNodeType.Space)
{
if (monospaced)
return monospaceWidth;
return (float)Math.Ceiling(fontData.meanGlyphWidth * options.WordSpacing);
}
float length = 0f;
if (node.Type == TextNodeType.Word)
{
for (int i = 0; i < node.Text.Length; i++)
{
char c = node.Text[i];
if (fontData.CharSetMapping.ContainsKey(c))
{
if (monospaced)
length += monospaceWidth;
else
length += (float)Math.Ceiling(fontData.CharSetMapping[c].rect.Width + fontData.meanGlyphWidth * options.CharacterSpacing + fontData.GetKerningPairCorrection(i, node.Text, node));
}
}
}
return length;
}
/// <summary>
/// Splits a word into sub-words of size less than or equal to baseCaseSize
/// </summary>
/// <param name="node"></param>
/// <param name="baseCaseSize"></param>
public void Crumble(TextNode node, int baseCaseSize){
//base case
if(node.Text.Length <= baseCaseSize )
return;
var left = SplitNode(node);
var right = left.Next;
Crumble(left,baseCaseSize);
Crumble(right,baseCaseSize);
}
/// <summary>
/// Splits a word node in two, adding both new nodes to the list in sequence.
/// </summary>
/// <param name="node"></param>
/// <returns>The first new node</returns>
public TextNode SplitNode(TextNode node)
{
if (node.Type != TextNodeType.Word)
throw new Exception("Cannot slit text node of type: " + node.Type);
int midPoint = node.Text.Length / 2;
string newFirstHalf = node.Text.Substring(0, midPoint);
string newSecondHalf = node.Text.Substring(midPoint, node.Text.Length - midPoint);
TextNode newFirst = new TextNode(TextNodeType.Word, newFirstHalf);
TextNode newSecond = new TextNode(TextNodeType.Word, newSecondHalf);
newFirst.Next = newSecond;
newSecond.Previous = newFirst;
//node is head
if (node.Previous == null)
Head = newFirst;
else
{
node.Previous.Next = newFirst;
newFirst.Previous = node.Previous;
}
//node is tail
if (node.Next == null)
Tail = newSecond;
else
{
node.Next.Previous = newSecond;
newSecond.Next = node.Next;
}
return newFirst;
}
public void Add(TextNode node){
//new node is head (and tail)
if(Head == null){
Head = node;
Tail = node;
} else {
Tail.Next = node;
node.Previous = Tail;
Tail = node;
}
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
// for (var node = Head; node.Next != null; node = node.Next)
foreach(TextNode node in this)
{
if (node.Type == TextNodeType.Space)
builder.Append(" ");
if (node.Type == TextNodeType.LineBreak)
builder.Append(System.Environment.NewLine);
if (node.Type == TextNodeType.Word)
builder.Append("" + node.Text + "");
}
return builder.ToString();
}
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return new TextNodeListEnumerator(this);
}
#endregion
private class TextNodeListEnumerator : IEnumerator
{
private TextNode currentNode = null;
private TextNodeList targetList;
public TextNodeListEnumerator(TextNodeList targetList)
{
this.targetList = targetList;
}
public object Current
{
get { return currentNode; }
}
public virtual bool MoveNext()
{
if (currentNode == null)
currentNode = targetList.Head;
else
currentNode = currentNode.Next;
return currentNode != null;
}
public void Reset()
{
currentNode = null;
}
public void Dispose()
{
}
}
}
}

View File

@@ -0,0 +1,95 @@
using OpenTK.Graphics.OpenGL;
using System;
using System.Drawing.Imaging;
namespace QuickFont
{
internal class TexturePage : IDisposable
{
int gLTexID;
int width;
int height;
public int GLTexID { get { return gLTexID; } }
public int Width { get { return width; } }
public int Height { get { return height; } }
public TexturePage(string filePath)
{
var bitmap = new QBitmap(filePath);
CreateTexture(bitmap.bitmapData);
bitmap.Free();
}
public TexturePage(BitmapData dataSource)
{
CreateTexture(dataSource);
}
private void CreateTexture(BitmapData dataSource)
{
width = dataSource.Width;
height = dataSource.Height;
GL.Enable(EnableCap.Texture2D);
GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
GL.GenTextures(1, out gLTexID);
GL.BindTexture(TextureTarget.Texture2D, gLTexID);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Clamp);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Clamp);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, dataSource.Scan0);
}
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
//Note: finalizer NOT included. .Net runs finalizer on a separate thread,
//which means that it does not know about the OpenGL context.
/*
~TexturePage()
{
// Finalizer calls Dispose(false)
Dispose(false);
}*/
// The bulk of the clean-up code is implemented in Dispose(bool)
private bool deletedTexture = false;
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
//dispose managed resources here - if there were any!
}
//free managed resources here (if there were any!)
if (!deletedTexture)
{
GL.DeleteTexture(gLTexID);
deletedTexture = true;
}
}
#endregion
}
}

View File

@@ -0,0 +1,125 @@
using OpenTK;
using OpenTK.Input;
using System.Collections.Generic;
using System;
namespace stonevox
{
public class BrushAdd : IVoxelBrush
{
ToolState state = ToolState.Start;
VoxelLocation startPosition;
VoxelLocation endPosition;
VoxelVolume volume;
VoxelVolume lastvolume;
QbMatrix lastmatrix;
Dictionary<double, VoxelUndoData> modifiedVoxels = new Dictionary<double, VoxelUndoData>();
public MouseCursor Cursor { get; set; }
public VoxelBrushType BrushType { get { return VoxelBrushType.Add; } }
public bool Active { get; set; }
public string CursorPath
{
get
{
return "./data/images/target_cursor.png";
}
}
public BrushAdd()
{
}
public bool OnRaycastHitchanged(Input input, QbMatrix matrix, RaycastHit hit, ref Colort color, MouseButtonEventArgs e)
{
return true;
}
public void EnumerateVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
for (int z = currentVolume.minz; z <= currentVolume.maxz; z++)
for (int y = currentVolume.miny; y <= currentVolume.maxy; y++)
for (int x = currentVolume.minx; x <= currentVolume.maxx; x++)
{
if (!volume.ContainsPoint(x,y,z) && !modifiedVoxels.ContainsKey(matrix.GetHash(x,y,z)))
modifiedVoxels.Add(matrix.GetHash(x, y, z), new VoxelUndoData(matrix.Add(x, y, z, color)));
}
}
public void CleanLastVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
if (!currentVolume.ContainsPoint(x, y, z))
{
if (modifiedVoxels[matrix.GetHash(x, y, z)].changed)
{
matrix.Remove(x, y, z, false, false);
modifiedVoxels.Remove(matrix.GetHash(x, y, z));
}
}
}
}
void CleanForToolReset()
{
RemoveVolume(volume, lastmatrix, modifiedVoxels);
modifiedVoxels.Clear();
lastvolume = VoxelVolume.NEGATIVE_ZERO;
}
public void RemoveVolume(VoxelVolume volume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
VoxelUndoData _temp;
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
if (modifiedVoxels.TryGetValue(matrix.GetHash(x, y, z), out _temp) && _temp.changed)
{
matrix.Remove(x, y, z, false, false);
modifiedVoxels.Remove(matrix.GetHash(x, y, z));
}
}
}
public void AddVolume(VoxelVolume volume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
double hash;
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
hash = matrix.GetHash(x, y, z);
if (!modifiedVoxels.ContainsKey(hash))
modifiedVoxels.Add(hash, new VoxelUndoData(matrix.Add(x, y, z, color)));
}
}
enum ToolState
{
Disabled,
Start,
Base,
Limit
}
public void Enable()
{
Active = true;
}
public void Disable()
{
Active = false;
}
public void Dispose()
{
}
}
}

View File

@@ -0,0 +1,109 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using System;
using System.Collections.Generic;
namespace stonevox
{
public class BrushColorSelection : IVoxelBrush
{
public bool Active { get; set; }
public VoxelBrushType BrushType
{
get
{
return VoxelBrushType.ColorSelect;
}
}
public MouseCursor Cursor
{
get; set;
}
public string CursorPath
{
get
{
return "./data/images/cursor_eyedrop.png";
}
}
public BrushColorSelection()
{
Singleton<Input>.INSTANCE.AddHandler(new InputHandler()
{
Keydownhandler = (e) =>
{
if (Singleton<GUI>.INSTANCE.OverWidget) return;
if (!Active && e.Shift)
{
Singleton<BrushManager>.INSTANCE.SetCurrentBrush(BrushType);
}
},
Keyuphandler = (e) =>
{
if (Singleton<GUI>.INSTANCE.OverWidget) return;
if (Active && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight))
{
var clientbrush = Singleton<BrushManager>.INSTANCE;
clientbrush.SetCurrentBrush(clientbrush.previousBrush.BrushType);
}
}
});
}
public bool OnRaycastHitchanged(Input input, QbMatrix matrix, RaycastHit hit, ref Colort color, MouseButtonEventArgs e)
{
if ((e != null && e.IsPressed && e.Button == MouseButton.Left) || (e == null && input.mousedown(MouseButton.Left)))
{
QbMatrix mat = Singleton<QbManager>.INSTANCE.ActiveModel.matrices[hit.matrixIndex];
if (mat != null)
{
Voxel voxel;
if (mat.voxels.TryGetValue(mat.GetHash(hit.x, hit.y, hit.z), out voxel))
{
}
}
return true;
}
return false;
}
public void Disable()
{
Active = false;
Singleton<Raycaster>.INSTANCE.Mode = RaycastMode.ActiveMatrix;
}
public void Enable()
{
Active = true;
Singleton<Raycaster>.INSTANCE.Mode = RaycastMode.MatrixColorSelection;
}
public void AddVolume(VoxelVolume volume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
throw new NotImplementedException();
}
public void CleanLastVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
throw new NotImplementedException();
}
public void EnumerateVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
throw new NotImplementedException();
}
public void RemoveVolume(VoxelVolume volume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,127 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using System;
using System.Collections.Generic;
namespace stonevox
{
public class BrushMatrixSelection : IVoxelBrush
{
public bool Active { get; set; }
public VoxelBrushType BrushType
{
get
{
return VoxelBrushType.MatrixSelect;
}
}
public MouseCursor Cursor
{
get; set;
}
public string CursorPath
{
get
{
return "./data/images/target_cursor.png";
}
}
private QbMatrix lastmatrix;
public BrushMatrixSelection()
{
Singleton<Input>.INSTANCE.AddHandler(new InputHandler()
{
Keydownhandler = (e) =>
{
if (Singleton<GUI>.INSTANCE.OverWidget) return;
if (!Active && e.Key == Key.Space)
{
Singleton<BrushManager>.INSTANCE.SetCurrentBrush(BrushType);
}
},
Keyuphandler = (e) =>
{
if (Singleton<GUI>.INSTANCE.OverWidget) return;
if (Active && (e.Key == Key.Space || e.Key == Key.Space))
{
var clientbrush = Singleton<BrushManager>.INSTANCE;
clientbrush.SetCurrentBrush(clientbrush.previousBrush.BrushType);
if (lastmatrix != null)
{
Singleton<QbManager>.INSTANCE.ActiveMatrix = lastmatrix;
Singleton<Camera>.INSTANCE.TransitionToMatrix();
lastmatrix.highlight = Color4.White;
lastmatrix = null;
}
}
}
});
}
public bool OnRaycastHitchanged(Input input, QbMatrix matrix, RaycastHit hit, ref Colort color, MouseButtonEventArgs e)
{
if (matrix == null)
{
if (lastmatrix != null)
{
lastmatrix.highlight = Color4.White;
lastmatrix = null;
}
return true;
}
if(matrix != lastmatrix)
{
if (lastmatrix != null)
lastmatrix.highlight = Color4.White;
matrix.highlight = new Colort(1.5f, 1.5f, 1.5f);
lastmatrix = matrix;
}
return true;
}
public void Disable()
{
Active = false;
Singleton<Raycaster>.INSTANCE.Mode = RaycastMode.ActiveMatrix;
Singleton<Selection>.INSTANCE.Visible = true;
}
public void Enable()
{
Active = true;
Singleton<Raycaster>.INSTANCE.Mode = RaycastMode.MatrixSelection;
Singleton<Selection>.INSTANCE.Visible = false;
}
public void AddVolume(VoxelVolume volume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
throw new NotImplementedException();
}
public void CleanLastVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
throw new NotImplementedException();
}
public void EnumerateVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
throw new NotImplementedException();
}
public void RemoveVolume(VoxelVolume volume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,202 @@
using OpenTK;
using OpenTK.Input;
using System.Collections.Generic;
namespace stonevox
{
public class BrushRecolor : IVoxelBrush
{
ToolState state = ToolState.Start;
VoxelLocation startPosition;
VoxelLocation endPosition;
VoxelVolume volume;
VoxelVolume lastvolume;
QbMatrix lastmatrix;
Dictionary<double, VoxelUndoData> modifiedVoxels = new Dictionary<double, VoxelUndoData>();
public MouseCursor Cursor { get; set; }
public VoxelBrushType BrushType { get { return VoxelBrushType.Recolor; } }
public bool Active { get; set; }
public string CursorPath
{
get
{
return "./data/images/cursor_paint.png";
}
}
public BrushRecolor()
{
Singleton<Input>.INSTANCE.AddHandler(new InputHandler()
{
mouseuphandler = (e) =>
{
if (!Active) return;
if (e.Button == MouseButton.Right && Singleton<Input>.INSTANCE.Keydown(Key.AltLeft) && state == ToolState.Base)
{
state = ToolState.Disabled;
if (Singleton<Input>.INSTANCE.mouseup(MouseButton.Left))
{
CleanForToolReset();
state = ToolState.Start;
}
}
if (state == ToolState.Disabled && e.Button == MouseButton.Left)
{
state = ToolState.Start;
CleanForToolReset();
}
}
});
}
public bool OnRaycastHitchanged(Input input, QbMatrix matrix, RaycastHit hit, ref Colort color, MouseButtonEventArgs e)
{
lastmatrix = matrix;
switch (state)
{
case ToolState.Start:
if ((e != null && e.IsPressed && e.Button == MouseButton.Left) || (e == null && input.mousedown(MouseButton.Left)))
{
state = ToolState.Base;
Singleton<Raycaster>.INSTANCE.testdirt = true;
startPosition = new VoxelLocation(hit, false);
endPosition = new VoxelLocation(hit, false);
volume = new VoxelVolume(startPosition, endPosition);
modifiedVoxels.Clear();
EnumerateVolume(volume, lastvolume, matrix, ref color, modifiedVoxels);
lastvolume = volume;
return true;
}
break;
case ToolState.Base:
if ((e != null && e.IsPressed && e.Button == MouseButton.Left) || (e == null && input.mousedown(MouseButton.Left)))
{
endPosition = new VoxelLocation(hit, false);
volume = new VoxelVolume(startPosition, endPosition);
EnumerateVolume(volume, lastvolume, matrix, ref color, modifiedVoxels);
CleanLastVolume(lastvolume, volume, matrix, modifiedVoxels);
lastvolume = volume;
return true;
}
else if ((e != null && !e.IsPressed && e.Button == MouseButton.Left) || (e == null && input.mouseup(MouseButton.Left)))
{
state = ToolState.Start;
lastvolume = VoxelVolume.NEGATIVE_ZERO;
Singleton<UndoRedo>.INSTANCE.AddUndo(BrushType, matrix, volume, color, modifiedVoxels);
return true;
}
break;
case ToolState.Limit:
break;
}
return false;
}
public void EnumerateVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
double hash;
Voxel voxel = null;
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
hash = matrix.GetHash(x, y, z);
if (!modifiedVoxels.ContainsKey(hash) && matrix.voxels.TryGetValue(hash, out voxel) && voxel.alphamask > 1)
{
modifiedVoxels.Add(hash, new VoxelUndoData(matrix.voxels[hash].colorindex, 0));
matrix.Color(x, y, z, color);
}
}
}
public void CleanLastVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
double hash;
VoxelUndoData voxel = new VoxelUndoData();
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
if (!currentVolume.ContainsPoint(x, y, z))
{
hash = matrix.GetHash(x, y, z);
if (modifiedVoxels.TryGetValue(hash, out voxel))
{
matrix.Color(x, y, z, voxel.colorindex, false, true);
modifiedVoxels.Remove(hash);
}
}
}
}
void CleanForToolReset()
{
RemoveVolume(volume, lastmatrix, modifiedVoxels);
modifiedVoxels.Clear();
lastvolume = VoxelVolume.NEGATIVE_ZERO;
}
public void RemoveVolume(VoxelVolume volume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
double hash;
VoxelUndoData voxel = new VoxelUndoData();
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
hash = matrix.GetHash(x, y, z);
if (modifiedVoxels.TryGetValue(hash, out voxel))
{
matrix.Color(x, y, z, voxel.colorindex, false, true);
modifiedVoxels.Remove(hash);
}
}
}
public void AddVolume(VoxelVolume volume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
double hash;
Voxel voxel = null;
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
hash = matrix.GetHash(x, y, z);
if (!modifiedVoxels.ContainsKey(hash) && matrix.voxels.TryGetValue(hash, out voxel) && voxel.alphamask > 1)
{
modifiedVoxels.Add(hash, new VoxelUndoData(matrix.voxels[hash].colorindex, 0));
matrix.Color(x, y, z, color);
}
}
}
enum ToolState
{
Disabled,
Start,
Base,
Limit
}
public void Enable()
{
Active = true;
}
public void Disable()
{
Active = false;
}
}
}

View File

@@ -0,0 +1,203 @@
using OpenTK;
using OpenTK.Input;
using System.Collections.Generic;
namespace stonevox
{
public class BrushRemove : IVoxelBrush
{
ToolState state = ToolState.Start;
VoxelLocation startPosition;
VoxelLocation endPosition;
VoxelVolume volume;
VoxelVolume lastvolume;
QbMatrix lastmatrix;
Dictionary<double, VoxelUndoData> modifiedVoxels = new Dictionary<double, VoxelUndoData>();
public MouseCursor Cursor { get; set; }
public VoxelBrushType BrushType { get { return VoxelBrushType.Remove; } }
public bool Active { get; set; }
public string CursorPath
{
get
{
return "./data/images/cursor_remove.png";
}
}
public BrushRemove()
{
Singleton<Input>.INSTANCE.AddHandler(new InputHandler()
{
mouseuphandler = new MouseHandler((MouseButtonEventArgs e) =>
{
if (!Active) return;
if (e.Button == MouseButton.Right && Singleton<Input>.INSTANCE.Keydown(Key.AltLeft) && state == ToolState.Base)
{
state = ToolState.Disabled;
if (Singleton<Input>.INSTANCE.mouseup(MouseButton.Left))
{
CleanForToolReset();
state = ToolState.Start;
}
}
if (state == ToolState.Disabled && e.Button == MouseButton.Left)
{
state = ToolState.Start;
CleanForToolReset();
}
})
});
}
public bool OnRaycastHitchanged(Input input, QbMatrix matrix, RaycastHit hit, ref Colort color, MouseButtonEventArgs e)
{
lastmatrix = matrix;
switch (state)
{
case ToolState.Start:
if ((e != null && e.IsPressed && e.Button == MouseButton.Left) || (e == null && input.mousedown(MouseButton.Left)))
{
state = ToolState.Base;
Singleton<Raycaster>.INSTANCE.testdirt = true;
startPosition = new VoxelLocation(hit, false);
endPosition = new VoxelLocation(hit, false);
volume = new VoxelVolume(startPosition, endPosition);
modifiedVoxels.Clear();
EnumerateVolume(volume, lastvolume, matrix, ref color, modifiedVoxels);
lastvolume = volume;
return true;
}
break;
case ToolState.Base:
if ((e != null && e.IsPressed && e.Button == MouseButton.Left) || (e == null && input.mousedown(MouseButton.Left)))
{
endPosition = new VoxelLocation(hit, false);
volume = new VoxelVolume(startPosition, endPosition);
EnumerateVolume(volume, lastvolume, matrix, ref color, modifiedVoxels);
CleanLastVolume(lastvolume, volume, matrix, modifiedVoxels);
lastvolume = volume;
return true;
}
else if ((e != null && !e.IsPressed && e.Button == MouseButton.Left) || (e == null && input.mouseup(MouseButton.Left)))
{
state = ToolState.Start;
lastvolume = VoxelVolume.NEGATIVE_ZERO;
Singleton<UndoRedo>.INSTANCE.AddUndo(BrushType, matrix, volume, color, modifiedVoxels);
return true;
}
break;
case ToolState.Limit:
break;
}
return false;
}
public void EnumerateVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
VoxelUndoData removed = new VoxelUndoData();
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
if (!modifiedVoxels.ContainsKey(matrix.GetHash(x, y, z)))
{
if (matrix.GetColorIndex_Alphamask(x, y, z, out removed.colorindex, out removed.alphamask))
{
if (matrix.Remove(x, y, z, true, false))
modifiedVoxels.Add(matrix.GetHash(x, y, z), removed);
}
}
}
}
public void CleanLastVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
VoxelUndoData removed = new VoxelUndoData();
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
if (!currentVolume.ContainsPoint(x, y, z))
{
if (modifiedVoxels.TryGetValue(matrix.GetHash(x, y, z), out removed))
{
if (removed.alphamask > 1)
matrix.Add(x, y, z, matrix.colors[removed.colorindex]);
modifiedVoxels.Remove(matrix.GetHash(x, y, z));
}
}
}
}
void CleanForToolReset()
{
RemoveVolume(volume, lastmatrix, modifiedVoxels);
modifiedVoxels.Clear();
lastvolume = VoxelVolume.NEGATIVE_ZERO;
}
public void RemoveVolume(VoxelVolume volume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
VoxelUndoData removed = new VoxelUndoData();
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
if (modifiedVoxels.TryGetValue(matrix.GetHash(x, y, z), out removed))
{
if (removed.alphamask > 1)
matrix.Add(x, y, z, matrix.colors[removed.colorindex]);
modifiedVoxels.Remove(matrix.GetHash(x, y, z));
}
}
}
public void AddVolume(VoxelVolume volume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
VoxelUndoData removed = new VoxelUndoData();
for (int z = volume.minz; z <= volume.maxz; z++)
for (int y = volume.miny; y <= volume.maxy; y++)
for (int x = volume.minx; x <= volume.maxx; x++)
{
if (!modifiedVoxels.ContainsKey(matrix.GetHash(x, y, z)))
{
if (matrix.GetColorIndex_Alphamask(x, y, z, out removed.colorindex, out removed.alphamask))
{
if (matrix.Remove(x, y, z, true, false))
modifiedVoxels.Add(matrix.GetHash(x, y, z), removed);
}
}
}
}
enum ToolState
{
Disabled,
Start,
Base,
Limit
}
public void Enable()
{
Active = true;
}
public void Disable()
{
Active = false;
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK;
using OpenTK.Input;
namespace stonevox
{
public class BrushVoxelSelection : IVoxelBrush
{
private bool active;
public bool Active
{
get
{
return active;
}
set
{
active = value;
}
}
public VoxelBrushType BrushType
{
get
{
return VoxelBrushType.Select;
}
}
public MouseCursor Cursor
{
get;
set;
}
public string CursorPath
{
get
{
return "";
}
}
public void AddVolume(VoxelVolume volume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
}
public void CleanLastVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
}
public void Disable()
{
}
public void Enable()
{
}
public void EnumerateVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
}
public bool OnRaycastHitchanged(Input input, QbMatrix matrix, RaycastHit hit, ref Colort color, MouseButtonEventArgs e)
{
return true;
}
public void RemoveVolume(VoxelVolume volume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
}
}
}

View File

@@ -0,0 +1,25 @@
using OpenTK;
using OpenTK.Input;
using System.Collections.Generic;
namespace stonevox
{
public interface IVoxelBrush
{
VoxelBrushType BrushType { get; }
bool Active { get; set; }
string CursorPath { get; }
MouseCursor Cursor { get; set; }
void Enable();
void Disable();
bool OnRaycastHitchanged(Input input, QbMatrix matrix, RaycastHit hit, ref Colort color, MouseButtonEventArgs e);
void EnumerateVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels);
void CleanLastVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels);
void AddVolume(VoxelVolume volume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels);
void RemoveVolume(VoxelVolume volume, QbMatrix matrix, Dictionary<double, VoxelUndoData> modifiedVoxels);
}
}

View File

@@ -0,0 +1,15 @@
namespace stonevox
{
public enum VoxelBrushType
{
Add,
Remove,
Recolor,
MatrixSelect,
ColorSelect,
Select
//Area_Fill,
//Area_Remove,
//Area_Color,
}
}

View File

@@ -0,0 +1,111 @@
using System.Collections.Generic;
namespace stonevox
{
public enum Message
{
WidgetEnable,
WidgetMouseEnter,
WidgetMouseLeave,
WidgetKeyPress,
WidgetFocus,
WidgetFocusLost,
WidgetStartTranslation,
WidgetEndTranslation,
ColorSelectionChanged,
ColorSelectionUpdate,
ColorSelectionCommit,
TextboxTextCommited,
WindowOpened,
WindowClosed,
StatusStripUpdate,
ModelImported,
ModelRemoved,
ModelRenamed,
ActiveMatrixChanged,
ActiveModelChanged,
WidgetMouseDown = 100,
WidgetMouseDoubleClick = 101,
WidgetMouseUp = 102,
WidgetMouseScroll = 103,
WidgetMouseOver = 104,
WidgetMouseMove = 105
}
// allows widgets or others to handle the broadcast and stop it from carring further
public enum BroadcastMessageReturn
{
Funnel,
Stop
}
public class BroadcastMessage
{
public Message messgae;
public Widget widget;
public object[] args;
public bool HasWidget { get { return widget != null; } }
public T Arg<T>(int index)
{
return (T)args[index];
}
}
public delegate void BroadcastMessageHandler(Message message, Widget windget, object[] args);
public class Broadcaster : Singleton<Broadcaster>
{
GUI gui;
public List<BroadcastMessageHandler> handlers;
public Broadcaster()
: base()
{
handlers = new List<BroadcastMessageHandler>();
}
public void SetGUI(GUI gui)
{
this.gui = gui;
}
public void Broadcast(Message message, params object[] args)
{
Broadcast(message, null, args);
}
public void Broadcast(Message message, Widget widget, params object[] args)
{
if ((int)message < 100)
gui.Dirty = true;
BroadcastMessage m = new BroadcastMessage()
{
messgae = message,
widget = widget,
args = args
};
handlers.ForEach(t => t(message, widget, args));
foreach (var guiWidget in gui.widgets)
{
if (m.HasWidget)
{
if (guiWidget.ID != m.widget.ID)
guiWidget.HandleMessageRecieved(m);
}
else
guiWidget.HandleMessageRecieved(m);
}
}
}
}

View File

@@ -0,0 +1,155 @@
using OpenTK.Input;
using System;
using System.Collections.Generic;
using System.Drawing;
namespace stonevox
{
public class BrushManager : Singleton<BrushManager>
{
public Colort brushColor = new Colort(.3f, .2f, .8f);
public IVoxelBrush currentBrush;
public IVoxelBrush previousBrush;
public Dictionary<VoxelBrushType, IVoxelBrush> brushes;
private GLWindow window;
public BrushManager(GLWindow window, Input input)
: base()
{
this.window = window;
brushes = new Dictionary<VoxelBrushType, IVoxelBrush>();
brushes.Add(VoxelBrushType.Add, new BrushAdd());
brushes.Add(VoxelBrushType.Remove, new BrushRemove());
brushes.Add(VoxelBrushType.Recolor, new BrushRecolor());
brushes.Add(VoxelBrushType.MatrixSelect, new BrushMatrixSelection());
brushes.Add(VoxelBrushType.ColorSelect, new BrushColorSelection());
brushes.Add(VoxelBrushType.Select, new BrushVoxelSelection());
input.AddHandler(new InputHandler()
{
Keydownhandler = (e) =>
{
if (e.Key == Key.Tab)
{
NextBrush();
}
var gui = Singleton<GUI>.INSTANCE;
if (e.Key == Key.B)
SetCurrentBrush(VoxelBrushType.Add);
else if (e.Key == Key.R)
SetCurrentBrush(VoxelBrushType.Recolor);
else if (e.Key == Key.F)
SetCurrentBrush(VoxelBrushType.Remove);
}
});
window.SVReizeEvent += (e, o) =>
{
var values = Enum.GetValues(typeof(VoxelBrushType));
var enumer = values.GetEnumerator();
while (enumer.MoveNext())
{
string path = brushes[(VoxelBrushType)enumer.Current].CursorPath;
if (string.IsNullOrEmpty(path))
{
brushes[(VoxelBrushType)enumer.Current].Cursor = OpenTK.MouseCursor.Default;
continue;
}
Bitmap bitmap = new Bitmap(path);
if (window.Width <= 1280)
bitmap = bitmap.ResizeImage(new Size((int)(bitmap.Width * .75f), (int)(bitmap.Height * .75f)));
else if (window.Width <= 1400)
bitmap = bitmap.ResizeImage(new Size((int)(bitmap.Width * .8f), (int)(bitmap.Height * .8f)));
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
var data = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
// super hacks
if ((VoxelBrushType)enumer.Current == VoxelBrushType.MatrixSelect)
brushes[(VoxelBrushType)enumer.Current].Cursor = new OpenTK.MouseCursor(
data.Width / 2, data.Height / 2, data.Width, data.Height, data.Scan0);
else
brushes[(VoxelBrushType)enumer.Current].Cursor = new OpenTK.MouseCursor(
0, 0, data.Width, data.Height, data.Scan0);
bitmap.Dispose();
}
};
NextBrush();
}
public void SetCurrentBrush(VoxelBrushType type)
{
if (previousBrush != null)
{
currentBrush.Disable();
//super super hacks
if (currentBrush.BrushType != VoxelBrushType.Select || currentBrush.BrushType != VoxelBrushType.MatrixSelect || currentBrush.BrushType != VoxelBrushType.ColorSelect)
previousBrush = currentBrush;
}
else
{
if (currentBrush?.BrushType != VoxelBrushType.Select || currentBrush?.BrushType != VoxelBrushType.MatrixSelect || currentBrush?.BrushType != VoxelBrushType.MatrixSelect)
previousBrush = currentBrush;
}
currentBrush = brushes[type];
currentBrush.Enable();
// ensure onselectionchanged is called when changing brushes
// even if the raycaster isn't over a new voxel
if (Singleton<Raycaster>.INSTANCE != null)
Singleton<Raycaster>.INSTANCE.lastHit = new RaycastHit() { distance = 10000 };
if (Singleton<GUI>.INSTANCE?.OverWidget == false)
window.Cursor = currentBrush.Cursor;
}
public void NextBrush()
{
var values = Enum.GetValues(typeof(VoxelBrushType));
var enumer = values.GetEnumerator();
enumer.MoveNext();
VoxelBrushType first = (VoxelBrushType)enumer.Current;
if (currentBrush != null)
{
do
{
if ((VoxelBrushType)enumer.Current == currentBrush.BrushType)
{
if (enumer.MoveNext())
{
// kinda hacky but so is the matrix selection tool...
if ((VoxelBrushType)enumer.Current == VoxelBrushType.Select || (VoxelBrushType)enumer.Current == VoxelBrushType.MatrixSelect || currentBrush.BrushType == VoxelBrushType.MatrixSelect)
continue;
SetCurrentBrush((VoxelBrushType)enumer.Current);
return;
}
}
} while (enumer.MoveNext());
}
SetCurrentBrush(first);
}
public bool onselectionchanged(Input input, QbMatrix matrix, RaycastHit hit, MouseButtonEventArgs e = null)
{
if (matrix != null && !matrix.Visible) return true;
return currentBrush.OnRaycastHitchanged(input, matrix, hit, ref brushColor, e);
}
}
}

View File

@@ -0,0 +1,396 @@
using OpenTK;
using OpenTK.Input;
using System;
namespace stonevox
{
public class Camera : Singleton<Camera>
{
public Vector3 position;
public Vector3 direction;
public Matrix4 projection;
public Matrix4 view;
public Matrix4 modelviewprojection;
private Input input;
private QbManager manager;
public Vector3 cameraright { get { return Vector3.Cross(direction, VectorUtils.UP); } }
public Vector3 cameraup { get { return Vector3.Cross(cameraright, direction); } }
float fov = 45f;
float nearPlane = 1f;
float farPlane = 300;
bool dotransition;
Vector3 startpos;
Vector3 startdir;
Vector3 _goto;
Vector3 centerposition;
float time = 0;
public bool freecam;
public Camera(GLWindow window, Input input, QbManager manager)
: base()
{
this.input = input;
this.manager = manager;
window.Resize += (e, s) =>
{
projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(fov), (float)window.Width / (float)window.Height, nearPlane, farPlane);
};
position = new Vector3(0f, 0f, 10f);
direction = new Vector3(0f, 0f, 1f);
direction.Normalize();
projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(fov), (float)window.Width / (float)window.Height, nearPlane, farPlane);
view = Matrix4.LookAt(position, position + direction, VectorUtils.UP);
modelviewprojection = projection * view;
InputHandler handler = new InputHandler()
{
Keydownhandler = (e) =>
{
var gui = Singleton<GUI>.INSTANCE;
//freecam is super interesting... but need a bit of work
//if (e.Key == Key.C)
//{
// freecam = !freecam;
// if (freecam)
// {
// Client.window.CursorVisible = false;
// }
// else
// {
// Client.window.CursorVisible = true;
// }
//}
},
mousewheelhandler = (e) =>
{
if (!Singleton<GUI>.INSTANCE.OverWidget)
{
if (e.Delta < 0)
{
position -= direction * 6 * 1f;
}
else if (e.Delta > 0)
{
position += direction * 6 * 1f;
}
}
else if (Singleton<GUI>.INSTANCE.lastWidgetOver.Drag)
{
if (e.Delta < 0)
{
position -= direction * 6 * 1f;
}
else if (e.Delta > 0)
{
position += direction * 6 * 1f;
}
}
}
};
input.AddHandler(handler);
}
public void update(float delta)
{
if (!Singleton<GUI>.INSTANCE.OverWidget)
{
if (input.Keydown(Key.Q))
position += Vector3.UnitY * -15f * delta;
if (input.Keydown(Key.E))
position -= Vector3.UnitY * -15f * delta;
if (input.Keydown(Key.W) || input.Keydown(Key.Up))
{
position += direction * 25f * delta;
}
if (input.Keydown(Key.S) || input.Keydown(Key.Down))
{
position -= direction * 25f * delta;
}
if (input.Keydown(Key.A) || input.Keydown(Key.Left))
{
Vector3 camerar = cameraright;
position.X -= camerar.X * 25f * delta;
position.Z -= camerar.Z * 25f * delta;
}
if (input.Keydown(Key.D) || input.Keydown(Key.Right))
{
Vector3 camerar = cameraright;
position.X += camerar.X * 25f * delta;
position.Z += camerar.Z * 25f * delta;
}
}
if (input.mousedown(MouseButton.Right) || freecam)
{
Vector3 camright = cameraright;
Vector3 camup = cameraup;
float roty = (float)MathHelper.DegreesToRadians(-input.mousedx * .15f);
float rotx = (float)MathHelper.DegreesToRadians(-input.mousedy * .15f);
if (Math.Abs(direction.Y) >= .95f && Math.Sign(rotx) == Math.Sign(direction.Y))
{
camright.Normalize();
camup.Normalize();
Vector3 focus = position - manager.ActiveMatrix.centerposition;
float length = focus.Length;
focus.Normalize();
focus = Vector3.Transform(focus, Quaternion.FromAxisAngle(camup, roty));
position = focus * length + manager.ActiveMatrix.centerposition;
direction = Vector3.Transform(direction, Quaternion.FromAxisAngle(camup, roty));
direction.Normalize();
}
else
{
camright.Normalize();
camup.Normalize();
Vector3 focus = position - manager.ActiveMatrix.centerposition;
float length = focus.Length;
focus.Normalize();
if (!freecam)
{
focus = Vector3.Transform(focus, Quaternion.FromAxisAngle(camup, roty));
focus = Vector3.Transform(focus, Quaternion.FromAxisAngle(camright, rotx));
}
position = focus * length + manager.ActiveMatrix.centerposition;
direction = Vector3.Transform(direction, Quaternion.FromAxisAngle(camup, roty));
direction = Vector3.Transform(direction, Quaternion.FromAxisAngle(camright, rotx));
direction.Normalize();
direction.Y = direction.Y.Clamp(-.95f, .95f);
}
}
if (input.mousedown(MouseButton.Middle))
{
Vector3 camright = Vector3.Cross(direction, Vector3.UnitY);
camright.Normalize();
Vector3 camup = Vector3.Cross(camright, direction);
camup.Normalize();
camright *= -input.mousedx;
camup *= input.mousedy;
camright += camup;
position.X += camright.X * .06f;
position.Y += camright.Y * .06f;
position.Z += camright.Z * .06f;
}
if (dotransition)
{
time += delta;
if (time >= .5f)
{
position = _goto;
direction = (centerposition - position).Normalized();
dotransition = false;
time = 0;
}
else
{
position = Vector3.Lerp(startpos, _goto, time / .5f);
direction = Vector3.Lerp(startdir, (centerposition - position).Normalized(), time / .5f);
direction.Normalize();
}
}
view = Matrix4.LookAt(position, position + direction, Vector3.UnitY);
modelviewprojection = view * projection;
}
public void LookAtModel(bool skipVoxels = false)
{
if (!skipVoxels)
{
int minx = 10000;
int miny = 10000;
int minz = 10000;
int maxx = 0;
int maxy = 0;
int maxz = 0;
int sizex = 0;
int sizey = 0;
int sizez = 0;
foreach (var matrix in manager.ActiveModel.matrices)
{
if (matrix.minx < minx)
minx = matrix.minx;
if (matrix.maxx > maxx)
maxx = matrix.maxx;
if (matrix.miny < miny)
miny = matrix.miny;
if (matrix.maxy > maxy)
maxy = matrix.maxy;
if (matrix.minz < minz)
minz = matrix.minz;
if (matrix.maxz > maxz)
maxz = matrix.maxz;
}
sizex = maxx - minx;
sizey = maxy - miny;
sizez = maxz - minz;
float backup = 0;
if (sizey * 1.5f > 20)
backup = sizey * 1.5f;
else if (sizex * 1.5f > 20)
backup = sizex * 1.5f;
else backup = 20;
var centerpos = new Vector3((minx + ((maxx - minx) / 2)), (miny + ((maxy - miny) / 2)), (minz + ((maxz - minz) / 2)));
position = centerpos + new Vector3(.5f, sizey * .65f, backup);
Vector3.Subtract(ref centerpos, ref position, out direction);
direction.Normalize();
view = Matrix4.LookAt(position, position + direction, cameraup);
modelviewprojection = Matrix4.CreateScale(.1f) * projection * view;
}
else
{
float sizey = manager.ActiveMatrix.sizey;
float sizex = manager.ActiveMatrix.sizex;
float backup = 0;
if (sizey * 1.5f > 20)
backup = sizey * 1.5f;
else if (sizex * 1.5f > 20)
backup = sizex * 1.5f;
else backup = 20;
var centerpos = manager.ActiveMatrix.centerposition;
position = centerpos + new Vector3(0, sizey * .65f, backup*.7f);
Vector3.Subtract(ref centerpos, ref position, out direction);
direction.Normalize();
view = Matrix4.LookAt(position, position + direction, cameraup);
modelviewprojection = Matrix4.CreateScale(.1f) * projection * view;
}
}
public void TransitionToMatrix()
{
startpos = position;
startdir = direction;
var mat = manager.ActiveMatrix;
_goto = mat.centerposition;
centerposition = mat.centerposition;
float height = (mat.maxy - mat.miny);
float width = (mat.maxx - mat.minx);
float length = (mat.maxz - mat.minz);
float distance;
if (height < 10 && width < 10 && length < 10)
{
height = (mat.maxy - mat.miny) * 2.5f;
width = (mat.maxx - mat.minx) * 2.5f;
length = (mat.maxz - mat.minz) * 2.5f;
distance = Math.Max(Math.Max(height, width), length);
}
else if (height < 18 && width < 18 && length < 18)
{
height = (mat.maxy - mat.miny) * 3.5f;
width = (mat.maxx - mat.minx) * 3.5f;
length = (mat.maxz - mat.minz) * 3.5f;
distance = Math.Max(Math.Max(height, width), length);
}
else
{
height = (mat.maxy - mat.miny) * 1.5f;
width = (mat.maxx - mat.minx) * 1.5f;
length = (mat.maxz - mat.minz) * 1.5f;
distance = Math.Max(Math.Max(height, width), length);
}
Vector3 offset = direction;
if (Math.Abs(offset.X) > .5f)
offset.X = 1f * -Math.Sign(direction.X);
else
offset.X = 0;
if (Math.Abs(offset.Z) > .5f)
offset.Z = 1f * -Math.Sign(direction.Z);
else
offset.Z = 0;
if (Math.Abs(offset.Y) > .5f)
offset.Y = 1f * -Math.Sign(direction.Y);
else
offset.Y = 0;
if (offset.Y == 1 && offset.X == 0 && offset.Z == 0)
{
offset = direction;
float starting = .5f;
bool b = true;
while (b)
{
if (Math.Abs(offset.X) > starting)
{
offset.X = 1f * -Math.Sign(direction.X);
b = false;
}
if (Math.Abs(offset.Z) > starting)
{
offset.Z = 1f * -Math.Sign(direction.Z);
b = false;
}
starting -= .07f;
}
offset.Y = 0;
}
offset.Normalize();
_goto += offset * distance;
dotransition = true;
}
}
}

View File

@@ -0,0 +1,28 @@
using OpenTK.Graphics;
namespace stonevox
{
public struct Colort
{
public float R;
public float G;
public float B;
public Colort(float r, float g, float b)
{
this.R = r;
this.G = g;
this.B = b;
}
public static implicit operator Colort(Color4 color)
{
return new Colort(color.R, color.G, color.B);
}
public static implicit operator Color4(Colort color)
{
return new Color4(color.R, color.G, color.B, 1.0f);
}
}
}

View File

@@ -0,0 +1,497 @@
using OpenTK.Graphics.OpenGL4;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
namespace stonevox
{
public static class ConsoleCommands
{
[ConsoleCommand("help", 0)]
[ConsoleCommandDescription("Lists all console commands.")]
public static void help()
{
var cmds = typeof(ConsoleCommands).GetMethods(BindingFlags.Static | BindingFlags.Public);
Console.WriteLine();
foreach (var c in cmds)
{
Console.ForegroundColor = ConsoleColor.Green;
ConsoleCommand command = (ConsoleCommand)c.GetCustomAttribute(typeof(ConsoleCommand));
Console.WriteLine(command.name);
ConsoleCommandDescription description = (ConsoleCommandDescription)c.GetCustomAttribute(typeof(ConsoleCommandDescription));
if (description != null)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine(description.text);
}
ConsoleArgs args = (ConsoleArgs)c.GetCustomAttribute(typeof(ConsoleArgs));
if (args != null)
{
Console.ForegroundColor = ConsoleColor.White;
foreach (var arg in args.args)
Console.WriteLine(" " + arg);
}
Console.WriteLine();
}
Console.ForegroundColor = ConsoleColor.White;
}
[ConsoleCommand("getip", 0)]
[ConsoleCommandDescription("Copies your public ip address to the clipboard.")]
public static void getip()
{
string ip = NetworkUtil.getIP();
//Clippz.PushAnsiStringToClipboard(ip);
Clipboard.SetText(ip);
Console.WriteLine(string.Format("your public ip address is {0}", ip));
}
[ConsoleCommand("getip", 1)]
[ConsoleCommandDescription("Copies your public ip address to the clipboard.")]
[ConsoleArgs("accurate - goes through slower but more accurate means to obtain your public IP.")]
public static void getipact(string act)
{
string ip = NetworkUtil.getIP_WEBREQUEST();
//Clippz.PushAnsiStringToClipboard(ip);
Clipboard.SetText(ip);
Console.WriteLine(string.Format("your accurate public ip address is {0}", ip));
}
[ConsoleCommand("getips", 0)]
[ConsoleCommandDescription("Displays all IP addresses found on your network interface.")]
public static void getips()
{
string ip = NetworkUtil.getIP();
//Clippz.PushAnsiStringToClipboard(ip);
Clipboard.SetText(ip);
Console.WriteLine(string.Format("your public ip address is {0}", ip));
}
[ConsoleCommand("encrypt", 1)]
[ConsoleCommandDescription("Encrypts the give text.")]
[ConsoleArgs("text - value to encrypt.")]
public static void encrypt(string text)
{
string e = Program.Encrypt(text);
//Clippz.PushAnsiStringToClipboard(e);
Clipboard.SetText(e);
Console.WriteLine(e);
}
[ConsoleCommand("decrypt", 1)]
[ConsoleCommandDescription("Decrypts the give text. Value is copied to clipboard.")]
[ConsoleArgs("text - value to encrypt.")]
public static void decrypt(string text)
{
string e = Program.Decrypt(text);
//Clippz.PushAnsiStringToClipboard(e);
Clipboard.SetText(e);
Console.WriteLine(e);
}
[ConsoleCommand("startclient", 0)]
[ConsoleCommandDescription("Starts up client, if a client is currently running it will be closed.")]
public static void startclient()
{
if (Client.net != null && Client.net.Status == Lidgren.Network.NetPeerStatus.Running)
{
Client.net.Shutdown("(Client) Shutting Down");
Thread.Sleep(300);
if (Program.clientthread.IsAlive)
{
Program.clientthread.Abort();
Thread.Sleep(300);
}
}
Program.clientthread = new Thread(_startclient);
Program.clientthread.SetApartmentState(ApartmentState.STA);
Program.clientthread.Start();
}
static void _startclient()
{
Client.defaultConfigure();
Client.start();
Client.beginstonevox();
}
[ConsoleCommand("startserver", 0)]
[ConsoleCommandDescription("Starts up server, if a server is currently running it will be closed.")]
public static void startserver()
{
if (Server.net != null && Server.net.Status == Lidgren.Network.NetPeerStatus.Running)
{
Server.net.Shutdown("(Server) Shutting Down");
Thread.Sleep(300);
if (Program.serverthread.IsAlive)
{
Program.serverthread.Abort();
Thread.Sleep(300);
}
}
Program.serverthread = new Thread(Program.startServer);
Program.serverthread.Start();
Thread.Sleep(20);
}
[ConsoleCommand("connectlocal", 0)]
[ConsoleCommandDescription("Connects the client to server running on your local computer.")]
public static void connectlocal()
{
int count = 0;
while (count < 1000)
{
if (!Client.Initialized)
{
count += 10;
Thread.Sleep(10);
}
else
break;
}
if (Client.net.ConnectionStatus == Lidgren.Network.NetConnectionStatus.Connected)
{
Client.disconnect();
Thread.Sleep(300);
}
Client.connect(NetConfig.NETWORK_LOCALHOST, NetConfig.NETWORK_PORT);
}
[ConsoleCommand("connect", 3)]
[ConsoleCommandDescription("Connect the client to a server running at the specified web address")]
[ConsoleArgs("ip - ip address to connect to",
"port - port on server",
"encrypted - does the ip need to be decrypted : (true | false)")]
public static void connect(string ip, string port, string encrypted)
{
int count = 0;
while (count < 1000)
{
if (!Client.Initialized)
{
count += 10;
Thread.Sleep(10);
}
else
break;
}
if (encrypted.ToLower() == "true")
{
ip = Program.Decrypt(ip);
}
if (Client.net.ConnectionStatus == Lidgren.Network.NetConnectionStatus.Connected)
{
Client.disconnect();
Thread.Sleep(300);
}
int nport = Convert.ToInt32(port);
Client.connect(ip, nport);
}
[ConsoleCommand("disconnect", 0)]
[ConsoleCommandDescription("Disconnects client from server. Value is copied to clipboard.")]
public static void disconnect()
{
if (Client.net.ConnectionStatus == Lidgren.Network.NetConnectionStatus.Connected)
{
Client.disconnect();
Thread.Sleep(300);
}
}
[ConsoleCommand("getclientid", 0)]
[ConsoleCommandDescription("Retreives your specific client ID used to connect with others through StoneVox 3D. Data is copy to the clipboard.")]
public static void getclientid()
{
string ip = Program.Encrypt(NetworkUtil.getIP());
Console.WriteLine(string.Format("you client ID : {0}", ip));
Clipboard.SetText(ip);
}
[ConsoleCommand("getclientstatus", 0)]
[ConsoleCommandDescription("Displays the clients current network status.")]
public static void getclientstatus()
{
if (Client.net != null)
Client.print("info", "Connection Status " + Client.net.ConnectionStatus.ToString());
}
[ConsoleCommand("getserverconnectioncount", 0)]
[ConsoleCommandDescription("If a server is running on this computer, Displays the amount of clients connected to the server")]
public static void getserverconnectioncount()
{
if (Server.net != null)
Server.print("info", "Connection Count " + Server.net.ConnectionsCount.ToString());
}
[ConsoleCommand("loadqb", 1)]
[ConsoleCommandDescription("Loads a .qb file.")]
[ConsoleArgs("filepath - path to file to load")]
public static void loadqb(string path)
{
if (!File.Exists(path))
{
Client.print("error", $"Error : loadqb -File {path}, was not found.");
return;
}
Client.OpenGLContextThread.Add(() =>
{
ImportExportUtil.Import(path);
});
}
[ConsoleCommand("loadqbnetworking", 1)]
[ConsoleCommandDescription("Loads a .qb file internally, then sends the loaded model to all clients connected.")]
[ConsoleArgs("filepath - path to file to load")]
public static void loadqbnetworking(string path)
{
if (Client.net != null && Client.net.ConnectionsCount == 0)
{
Client.print("error", "Error : loadqbnetworking - is meant for network loading of files. Connect your client to a server before running this command, or try using \"loadqb\"");
return;
}
Client.OpenGLContextThread.Add(() =>
{
StopwatchUtil.startclient("internalqbread", "Begin Qb Read");
ImporterQb importer = new ImporterQb();
QbModel model = importer._read(path);
StopwatchUtil.stopclient("internalqbread", "End Qb Read");
StopwatchUtil.startclient("clientwriteqbpacket", "Begin Qb Packet Write");
var packet = PacketWriter.write<Packet_QbImported>(NetEndpoint.CLIENT);
packet.write(model);
packet.send();
StopwatchUtil.stopclient("clientwriteqbpacket", "End qb Packet Write");
});
}
[ConsoleCommand("fps", 1)]
[ConsoleCommandDescription("SV will try to match this frame-rate.")]
[ConsoleArgs("fps - frames per second to shoot for")]
public static void fps(string s_fps)
{
try
{
int fps = Convert.ToInt32(s_fps);
if (fps <= 0)
throw new Exception();
Client.window.targetFps = fps;
Client.window.TargetRenderFrequency = fps;
Client.window.TargetUpdateFrequency = fps;
}
catch
{
Client.print("info", "input value must be non decimal and greater then 0");
}
}
[ConsoleCommand("openglsupport", 0)]
[ConsoleCommandDescription("Gives feedback about what version of OpenGL you GPU supports.")]
public static void openglsupport()
{
Client.OpenGLContextThread.Add(() =>
{
Console.WriteLine();
var osversion= Environment.OSVersion.VersionString;
Client.print("debug", osversion);
string majorminor = GL.GetString(StringName.Version);
Client.print("debug", string.Format("Available OpenGL version : {0}", majorminor));
string vendor = GL.GetString(StringName.Vendor);
Client.print("debug", string.Format("Vendor : {0}", vendor));
string renderer = GL.GetString(StringName.Renderer);
Client.print("debug", string.Format("Available Render version : {0}", renderer));
string glslversion = GL.GetString(StringName.ShadingLanguageVersion);
Client.print("debug", string.Format("Available Shader version : {0}", glslversion));
string numberextensions = GL.GetString(StringName.Extensions);
string[] supports = numberextensions.Split(' ');
Console.WriteLine();
Client.print("debug", string.Format("Available Extensions : {0}", supports.Length));
for (int i = 0; i < supports.Length; i++)
{
Client.print("debug_data", string.Format(" {0}", supports[i]));
}
Client.print("debug", "Shader test");
int shadererrorcount = 0;
string vertexshaderpath = "./data/shaders/voxel.vs";
string fragmentshaderpath = "./data/shaders/voxel.fs";
int vertexshaderid = GL.CreateShader(ShaderType.VertexShader);
int fragmentshaderid = GL.CreateShader(ShaderType.FragmentShader);
using (StreamReader r = new StreamReader(vertexshaderpath))
GL.ShaderSource(vertexshaderid, r.ReadToEnd());
using (StreamReader r = new StreamReader(fragmentshaderpath))
GL.ShaderSource(fragmentshaderid, r.ReadToEnd());
GL.CompileShader(vertexshaderid);
string vertexshadererror = GL.GetShaderInfoLog(vertexshaderid);
GL.CompileShader(fragmentshaderid);
string fragmentshadererror = GL.GetShaderInfoLog(fragmentshaderid);
int shaderid = GL.CreateProgram();
GL.AttachShader(shaderid, vertexshaderid);
GL.AttachShader(shaderid, fragmentshaderid);
GL.LinkProgram(shaderid);
string shadererror = GL.GetProgramInfoLog(shaderid);
if (!string.IsNullOrEmpty(vertexshadererror))
{
Client.print("debug_data", string.Format(" {0}", vertexshadererror));
shadererrorcount++;
}
if (!string.IsNullOrEmpty(fragmentshadererror))
{
Client.print("debug_data", string.Format(" Fragment shader failed compiling : {0}", fragmentshadererror));
shadererrorcount++;
}
if (!string.IsNullOrEmpty(shadererror))
{
Client.print("debug_data", string.Format(" Shader failed linking: {0}", shadererror));
shadererrorcount++;
}
if (shadererrorcount > 0)
{
Client.print("debug", "Shader test failed");
}
else
Client.print("debug", "Shader completed with no errors");
Console.WriteLine();
Client.print("info", "Would you like to export info to desktop. (yes/no)");
string t = Console.ReadLine();
if (t.ToLower().Contains("y") || t.ToLower().Contains("es"))
{
Client.print("debug", "Info exported to desktop.");
using (FileStream file = new FileStream(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "//stonevox_log.txt", FileMode.Create))
{
using (StreamWriter s = new StreamWriter(file))
{
s.WriteLine(osversion);
s.WriteLine(string.Format("Available OpenGL version : {0}", majorminor));
s.WriteLine(string.Format("Vendor : {0}", vendor));
s.WriteLine(string.Format("Available Render version : {0}", renderer));
s.WriteLine(string.Format("Available Shader version : {0}", glslversion));
s.WriteLine();
s.WriteLine(string.Format("Available Extensions : {0}", supports.Length));
for (int i = 0; i < supports.Length; i++)
{
s.WriteLine(string.Format(" {0}", supports[i]));
}
s.WriteLine();
s.WriteLine("Shader test");
if (!string.IsNullOrEmpty(vertexshadererror))
{
s.WriteLine(string.Format(" {0}", vertexshadererror));
}
if (!string.IsNullOrEmpty(fragmentshadererror))
{
s.WriteLine(string.Format(" Fragment shader failed compiling : {0}", fragmentshadererror));
}
if (!string.IsNullOrEmpty(shadererror))
{
s.WriteLine(string.Format(" Shader failed linking: {0}", shadererror));
}
if (shadererrorcount > 0)
{
s.WriteLine("Shader test failed");
}
else
s.WriteLine("Shader completed with no errors");
}
}
}
else
{
}
});
}
}
public class ConsoleCommand : System.Attribute
{
public string name;
public int argcount;
public ConsoleCommand(string cmdname, int argcount)
: base()
{
name = cmdname;
this.argcount = argcount;
}
}
public class ConsoleCommandDescription : System.Attribute
{
public string text;
public ConsoleCommandDescription(string text)
{
this.text = text;
}
}
public class ConsoleArgs : System.Attribute
{
public string[] args;
public ConsoleArgs(params string[] args)
{
this.args = args;
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
namespace stonevox
{
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000122-0000-0000-C000-000000000046")] // This is the value of IID_IDropTarget from the Platform SDK.
[ComImport]
public interface IDropTarget
{
void DragEnter([In] IDataObject dataObject, [In] uint keyState, [In] Point pt, [In, Out] ref uint effect);
void DragOver([In] uint keyState, [In] Point pt, [In, Out] ref uint effect);
void DragLeave();
void Drop([In] IDataObject dataObject, [In] uint keyState, [In] Point pt, [In, Out] ref uint effect);
}
static class NativeMethods
{
public const int CF_HDROP = 15;
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
public static extern int DragQueryFile(HandleRef hDrop, int iFile, [Out] StringBuilder lpszFile, int cch);
[DllImport("ole32.dll")]
internal static extern void ReleaseStgMedium(ref STGMEDIUM medium);
}
public class DragDropTarget : stonevox.IDropTarget
{
public void DragEnter(IDataObject dataObject, uint keyState, Point pt, ref uint effect)
{
}
public void DragOver(uint keyState, Point pt, ref uint effect)
{
}
public void DragLeave()
{
}
public void Drop(IDataObject dataObject, uint keyState, Point pt, ref uint effect)
{
FORMATETC format = new FORMATETC()
{
cfFormat = NativeMethods.CF_HDROP,
dwAspect = DVASPECT.DVASPECT_CONTENT,
tymed = TYMED.TYMED_HGLOBAL
};
STGMEDIUM medium;
string[] files;
dataObject.GetData(ref format, out medium);
try
{
IntPtr dropHandle = medium.unionmember;
int fileCount = NativeMethods.DragQueryFile(new HandleRef(this, dropHandle), -1, null, 0);
files = new string[fileCount];
for (int x = 0; x < fileCount; ++x)
{
int size = NativeMethods.DragQueryFile(new HandleRef(this, dropHandle), x, null, 0);
if (size > 0)
{
StringBuilder fileName = new StringBuilder(size + 1);
if (NativeMethods.DragQueryFile(new HandleRef(this, dropHandle), x, fileName, fileName.Capacity) > 0)
files[x] = fileName.ToString();
}
}
}
finally
{
NativeMethods.ReleaseStgMedium(ref medium);
}
foreach (string file in files)
{
Client.OpenGLContextThread.Add(() =>
{
ImportExportUtil.Import(file);
});
}
}
}
}

View File

@@ -0,0 +1,114 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using System;
namespace stonevox
{
public class Floor : Singleton<Floor>
{
public static int MinSize = 15;
Camera camera;
Broadcaster broadcaster;
public Color4 color = Color4.Transparent;
float width =15;
float length = 15;
public float x = 0;
public float y = 0;
public float z = 0;
Vector3 up = new Vector3(0, 1, 0);
Vector3 h = Vector3.Zero;
public Floor(Camera camera, Broadcaster broadcaster)
: base()
{
this.camera = camera;
this.broadcaster = broadcaster;
broadcaster.handlers.Add((message, e, t) =>
{
if (message == Message.ActiveMatrixChanged)
{
QbMatrix m = t[0] as QbMatrix;
m.MatchFloorToSize();
}
});
}
public void SetFloorSize(float x, float y, float z, float width, float length)
{
this.x = x;
this.y = y;
this.z = z;
this.width = width;
this.length = length;
}
public void Render()
{
float cubesize = .5f;
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref camera.projection);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref camera.view);
GL.Begin(PrimitiveType.Quads);
GL.Color4(color);
GL.Vertex3(-cubesize + x,
-.5F + y,
cubesize*length*2f + z+cubesize);
GL.Vertex3(cubesize*width*2f + x+cubesize,
-.5F + y,
cubesize*length*2f + z+cubesize);
GL.Vertex3(cubesize*width*2f + x+cubesize,
-.5F + y,
-cubesize + z);
GL.Vertex3(-cubesize*2f + x +cubesize,
-.5F + y,
-cubesize + z);
GL.End();
}
public bool RayTest(Raycaster raycaster, ref RaycastHit hit)
{
// 0 1 2
// 0 2 3
float cubesize = .5f;
for (float x = this.x; x <= this.x+width; x++)
for (float z = this.z; z <= this.z + length; z++)
{
if (raycaster.RayTestTriangle(ref up, -cubesize + x, -.5F + y, cubesize + z,
cubesize + x, -.5F + y, cubesize + z,
cubesize + x, -.5F + y, -cubesize + z,
out h) || raycaster.RayTestTriangle(ref up, -cubesize + x, -.5F + y, cubesize + z,
cubesize + x, -.5F + y, -cubesize + z,
-cubesize + x, -.5F + y, -cubesize + z, out h))
{
int hx, hy, hz;
hx = (int)Math.Round(h.X, 0);
hy = (int)Math.Round(h.Y, 0);
hz = (int)Math.Round(h.Z, 0);
hit.distance = raycaster.distance(hx * .5f, hy * .5f + .5f, hz * .5f);
hit.x = hx;
hit.y = (int)y-1;
hit.z = hz;
hit.side = Side.Top;
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,560 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Input;
using QuickFont;
using stonevox.gui.editor;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections.Generic;
namespace stonevox
{
// personally i'm thinking nothing should go here to access stuff, ie backcolor, input, gui, ect
// added singleton classes to start this change
public class GLWindow : SyncWindow
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
public static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("ole32.dll")]
public static extern int OleInitialize(IntPtr pvReserved);
[DllImport("ole32.dll")]
public static extern int RegisterDragDrop(IntPtr hwnd, stonevox.IDropTarget pDropTarget);
// this goes along with the comment below
// these are all singeltons now
Input input;
Camera camera;
Selection selection;
BrushManager brushes;
GUI gui;
Broadcaster broadcaster;
UndoRedo undoredo;
Raycaster raycaster;
Floor floor;
QbManager manager;
IRenderer renderer;
// not sure about these yet, need another place
// ... also the whole QbManager/Importer/Exporter thing i don't like
// thinking some sort of qb manager class should be put in the _client space
// following Singleton<?? "qb manager">
//public QbModel model;
public Shader voxelShader;
public Color4 backcolor;
public bool isfocused = true;
public QFont Qfont;
public QFont Qfont_1280;
public QFont Qfont_1400;
public QFont Qfont_1920;
public event EventHandler SVReizeEvent;
private int lastWidth;
public string CSMText = "";
public GLWindow(int width, int height, GraphicsMode graphicsmode, string CSMTextx)
: base(width, height, graphicsmode)
{
CSMText = CSMTextx;
}
public DragDropTarget dnd;
VoxelLocation startPosition;
VoxelLocation endPosition;
VoxelVolume volume;
VoxelVolume lastvolume;
QbMatrix lastmatrix;
Dictionary<double, VoxelUndoData> modifiedVoxels = new Dictionary<double, VoxelUndoData>();
protected override void OnLoad(EventArgs e)
{
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Title = String.Format("StoneVox 3D - version {0}", version);
GL.Viewport(0, 0, Width, Height);
Qfont_1280 = new QFont("data\\fonts\\Bigfish.ttf", 11.2f, new QFontBuilderConfiguration(true, false));
Qfont_1400 = new QFont("data\\fonts\\Bigfish.ttf", 12f, new QFontBuilderConfiguration(true, false));
Qfont_1920 = new QFont("data\\fonts\\Bigfish.ttf", 15, new QFontBuilderConfiguration(true, false));
if (Width <= 1280)
{
Qfont = Qfont_1280;
}
else if (Width < 1400)
{
Qfont = Qfont_1400;
}
else
{
Qfont = Qfont_1920;
}
this.Qfont.Options.Colour = Color.White;
//this.Qfont.Options.TransformToViewport = new TransformViewport(-1,-1,2,2);
Scale.SetHScaling(0, Width);
Scale.SetVScaling(0, Height);
ShaderUtil.CreateShader("quad_interpolation", "./data/shaders/QuadInterpolation.vs", "./data/shaders/QuadInterpolation.fs");
broadcaster = new Broadcaster();
manager = new QbManager(broadcaster);
input = new Input(this);
camera = new Camera(this, input, manager);
brushes = new BrushManager(this, input);
floor = new Floor(camera, broadcaster);
gui = new GUI(this, manager, input);
selection = new Selection(this,brushes, input, manager, floor, gui);
renderer = new Wireframe(camera, selection, floor, input);
undoredo = new UndoRedo(input);
selection.GenerateVertexArray();
if(!manager.HasModel)
manager.AddEmpty();
camera.LookAtModel(true);
backcolor = new Color4(60, 60, 60, 0);
GL.Enable(EnableCap.DepthTest);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.Enable(EnableCap.CullFace);
GL.CullFace(CullFaceMode.Back);
int ole_hresult = OleInitialize(IntPtr.Zero);
IntPtr handle = FindWindowByCaption(IntPtr.Zero, Title);
dnd = new DragDropTarget();
int dnd_hresult = RegisterDragDrop(handle, dnd);
raycaster = new Raycaster(this, camera, selection, floor, input, manager, gui);
selection.raycaster = raycaster;
Client.Initialized = true;
base.OnLoad(e);
CSM csm = new CSM(CSMText);
foreach(Box box in csm.boxes)
{
CreateBox(box.ParentName, -box.x, -box.y, -box.z, box.SizeX, box.SizeY, box.SizeZ, new Colort(.3f, .5f, .8f));
Console.WriteLine("CreateBox(\"" + box.ParentName + "\", " + -box.x + ", " + -box.y + ", " + -box.z + ", " + box.SizeX + ", " + box.SizeY + ", " + box.SizeZ + ", new Colort(.3f, .5f, .8f));");
}
SetForegroundWindow(WindowInfo.Handle);
}
public void CreateBox(string Parent, int x, int y, int z, int sizeX, int sizeY, int sizeZ, Colort col)
{
sizeX -= 1;
sizeY -= 1;
sizeZ -= 1;
switch (Parent)
{
case ("HEAD"):
y += 16;
col = new Colort(.3f, .5f, .8f);
break;
case ("BODY"):
z += 4;
y += 12;
col = new Colort(.5f, .2f, .8f);
break;
case ("ARM0"):
z += 4;
y += 10;
x -= 3;
col = new Colort(.3f, .2f, .5f);
break;
case ("ARM1"):
z += 4;
y += 10;
x += 11;
col = new Colort(.3f, .5f, .5f);
break;
case ("LEG0"):
x += 2;
z += 4;
col = new Colort(.3f, .2f, .1f);
break;
case ("LEG1"):
x += 6;
z += 4;
col = new Colort(.1f, .2f, .5f);
break;
}
RaycastHit hit = new RaycastHit();
hit.distance = 10000;
hit.x = x;
hit.y = y;
hit.z = z;
RaycastHit hit2 = new RaycastHit();
hit2.distance = hit.distance - (sizeX + sizeZ + sizeY);
hit2.y = y + sizeY;
hit2.x = x + sizeX;
hit2.z = z + sizeZ;
int index = 0;
for (int i = 0; i < manager.ActiveModel.numMatrices; i++)
{
if (!manager.ActiveModel.matrices[i].Visible) continue;
RaycastHit tempHit = raycaster.RaycastTest(camera.position, manager.ActiveModel.matrices[i]);
if (tempHit.distance != 10000 && tempHit.distance < hit.distance && !tempHit.matches(hit))
{
index = i;
}
}
startPosition = new VoxelLocation(hit);
endPosition = new VoxelLocation(hit2);
volume = new VoxelVolume(startPosition, endPosition);
modifiedVoxels.Clear();
EnumerateVolume(lastvolume, volume, manager.ActiveModel.matrices[index], ref col, modifiedVoxels);
}
public void EnumerateVolume(VoxelVolume volume, VoxelVolume currentVolume, QbMatrix matrix, ref Colort color, Dictionary<double, VoxelUndoData> modifiedVoxels)
{
for (int z = currentVolume.minz; z <= currentVolume.maxz; z++)
for (int y = currentVolume.miny; y <= currentVolume.maxy; y++)
for (int x = currentVolume.minx; x <= currentVolume.maxx; x++)
{
if (!volume.ContainsPoint(x, y, z) && !modifiedVoxels.ContainsKey(matrix.GetHash(x, y, z)))
modifiedVoxels.Add(matrix.GetHash(x, y, z), new VoxelUndoData(matrix.Add(x, y, z, color)));
}
}
protected override void OnClosing(CancelEventArgs e)
{
Environment.Exit(0);
base.OnClosing(e);
}
public override void Dispose()
{
Qfont_1280.Dispose();
Qfont_1400.Dispose();
Qfont_1920.Dispose();
manager.Dispose();
base.Dispose();
}
protected override void OnFocusedChanged(EventArgs e)
{
if (!Focused)
{
isfocused = false;
raycaster.Enabled = false;
}
else
{
isfocused = true;
raycaster.Enabled = true;
}
var handle = FindWindowByCaption(IntPtr.Zero, "StoneVox - Open File");
if (handle != IntPtr.Zero)
SetForegroundWindow(handle);
handle = FindWindowByCaption(IntPtr.Zero, "StoneVox - Save File");
if (handle != IntPtr.Zero)
SetForegroundWindow(handle);
base.OnFocusedChanged(e);
}
protected override void OnWindowStateChanged(EventArgs e)
{
if (WindowState == WindowState.Minimized)
{
raycaster.Enabled = false;
}
else if (WindowState == WindowState.Normal || WindowState == WindowState.Maximized)
{
raycaster.Enabled = true;
}
base.OnWindowStateChanged(e);
}
protected override void OnResize(EventArgs e)
{
GL.Viewport(0, 0, Width, Height);
QFont.ForceViewportRefresh();
Scale.SetHScaling(0, Width);
Scale.SetVScaling(0, Height);
if (Width <= 1280 && lastWidth != 1280)
{
lastWidth = 1280;
Qfont = Qfont_1280;
SVReizeEvent?.Invoke(this, EventArgs.Empty);
}
else if (Width >1280 && Width <= 1400 && lastWidth !=1400)
{
lastWidth = 1400;
Qfont = Qfont_1400;
SVReizeEvent?.Invoke(this, EventArgs.Empty);
}
else if (Width > 1400 && lastWidth != 1920)
{
lastWidth = 1920;
Qfont = Qfont_1920;
SVReizeEvent?.Invoke(this, EventArgs.Empty);
}
base.OnResize(e);
}
protected override void OnKeyDown(KeyboardKeyEventArgs e)
{
base.OnKeyDown(e);
input.handleKeydown(e);
if (e.Control && e.Key == Key.O)
{
var open = new OpenFileDialog();
open.Multiselect = false;
open.Title = "StoneVox - Open File";
open.DefaultExt = ".qb";
open.FileOk += (s, o) =>
{
Client.OpenGLContextThread.Add(() =>
{
ImportExportUtil.Import(open.FileName);
});
};
Thread thread = new Thread(() =>
{
open.ShowDialog();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
if (e.Control && e.Key == Key.S)
{
var save = new SaveFileDialog();
save.Title = "StoneVox - Save File";
save.Filter = "StoneVox Project (.svp)|*.svp|Qubicle Binary (.qb)|*.qb|Wavefront OBJ (.obj)|*.obj|All files (*.*)|*.*";
save.DefaultExt = ".svp";
save.FileOk += (s, t) =>
{
QbModel model = manager.ActiveModel;
model.name = save.FileName.Split('\\').Last();
if (model.name.Contains('.'))
model.name = model.name.Split('.').First();
broadcaster.Broadcast(Message.ModelRenamed, model, model.name);
ImportExportUtil.Export(save.FileName.Split('\\').Last().Replace(model.name, ""), model.name, Path.GetDirectoryName(save.FileName), model);
};
Thread thread = new Thread(() =>
{
save.ShowDialog();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
else if (e.Control && e.Key == Key.F12)
{
GUIEditor editor = new GUIEditor();
editor.Show();
}
//else if (e.Key == Key.T)
//{
// Stopwatch w = Stopwatch.StartNew();
// var model = Singleton<QbManager>.INSTANCE.ActiveMatrix;
// Colort color = new Colort(1f, 0f, 0f);
// var volume = new VoxelVolume()
// {
// minx = 0,
// maxx = 100,
// miny = 0,
// maxy = 100,
// minz = 0,
// maxz = 100
// };
// model.Add(volume, ref color);
// w.Stop();
// Console.WriteLine("add " + w.ElapsedMilliseconds.ToString());
//}
//else if (e.Key == Key.T)
//{
// Stopwatch w = Stopwatch.StartNew();
// var model = Singleton<QbManager>.INSTANCE.ActiveMatrix;
// Colort color = new Colort(1f, 0f, 0f);
// for (int x = 0; x < 100; x++)
// for (int z = 0; z < 100; z++)
// for (int y = 0; y< 100; y++)
// model.Add(x, y, z, color);
// w.Stop();
// Console.WriteLine("add " +w.ElapsedMilliseconds.ToString());
//}
//else if (e.Key == Key.Y)
//{
// Stopwatch w = Stopwatch.StartNew();
// var model = Singleton<QbManager>.INSTANCE.ActiveMatrix;
// Colort color = new Colort(1f, 0f, 0f);
// for (int x = 0; x < 100; x++)
// for (int z = 0; z < 100; z++)
// for (int y = 0; y < 100; y++)
// model.Remove(x, y, z);
// w.Stop();
// Console.WriteLine("earse " + w.ElapsedMilliseconds.ToString());
//}
}
protected override void OnKeyUp(KeyboardKeyEventArgs e)
{
input.handleKeyup(e);
base.OnKeyUp(e);
}
protected override void OnKeyPress(OpenTK.KeyPressEventArgs e)
{
input.handleKeypress(e);
base.OnKeyPress(e);
}
// mouse
protected override void OnMouseDown(MouseButtonEventArgs e)
{
input.handlemousedown(e);
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
input.handlemouseup(e);
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseMoveEventArgs e)
{
if (input != null)
{
input.mousex = e.X;
input.mousey = Height - e.Y;
input.mousedx = e.XDelta;
input.mousedy = e.YDelta;
// when i implement something good for following the mouse around quickly
//Raycaster.testlocations.Push(new Vector2(e.X, Height - e.Y));
input.handlemousemove(e);
}
base.OnMouseMove(e);
}
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
input.handlemousewheel(e);
base.OnMouseWheel(e);
}
protected override void OnUpdateFrame(FrameEventArgs e)
{
if (!isfocused)
{
input.update();
base.OnUpdateFrame(e);
return;
}
input.update();
gui.Update(e);
camera.update((float)e.Time);
selection.update();
base.OnUpdateFrame(e);
}
double ee = 0;
int fps = 0;
protected override void OnRenderFrame(FrameEventArgs e)
{
if (!isfocused)
{
Client.update();
base.OnRenderFrame(e);
return;
}
// Console.WriteLine(raycaster.Enabled.ToString());
GL.ClearColor(backcolor.R, backcolor.G, backcolor.B, backcolor.A);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
ee += e.Time;
fps++;
if (ee > 1)
{
ee = 0;
Title = "StoneVox fps : " + fps.ToString();
fps = 0;
}
renderer.Render(manager.ActiveModel);
ShaderUtil.ResetShader();
floor.Render();
gui.Render();
SwapBuffers();
Client.update();
base.OnRenderFrame(e);
}
}
}

View File

@@ -0,0 +1,490 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace stonevox
{
public class GUIID
{
public const int HSV_H = 500;
public const int HSV_S = 501;
public const int HSV_V = 502;
public const int RGB_R = 503;
public const int RGB_G = 504;
public const int RGB_B = 505;
public const int MATRIX_LISTBOX_WINDOW = 700;
public const int MATRIX_SIZE_TEXTBOX = 701;
public const int IO_WINDOW = 900;
public const int COLOR_PICKER_WINDOW = 600;
public const int COLORQUAD = 601;
public const int START_COLOR_SELECTORS = 1000;
public const int STATUS_TEXT = 750;
public const int GRIDOPTIONS = 800;
public const int ACTIVE_MATRIX_NAME = 850;
public const int HACKYSELECTIONTOOL = 950;
}
public class GUI : Singleton<GUI>
{
public float scale = 1.0f;
public List<Widget> widgets;
private Input input;
private GLWindow window;
private QbManager manager;
private int widgetIDs = 100000;
public int NextAvailableWidgeID { get { widgetIDs++; return widgetIDs; } }
private int lastWidgetOverIndex = -1;
public Widget lastWidgetOver { get { return widgets[lastWidgetOverIndex]; } }
private int lastWidgetFocusedID = -1;
public Widget lastWidgetFocused { get { return widgets[lastWidgetFocusedID]; } }
public bool OverWidget { get { return lastWidgetOverIndex != -1; } }
public bool FocusingWidget { get { return lastWidgetFocusedID != -1; } }
public bool Visible = true;
int activecolorindex = 9;
List<Color4> colorpallete = new List<Color4>();
public bool Dirty = true;
int framebuffer;
int color;
public GUI(GLWindow window, QbManager manager, Input input)
: base()
{
this.window = window;
this.manager = manager;
Singleton<Broadcaster>.INSTANCE.SetGUI(this);
framebuffer = GL.GenFramebuffer();
GL.BindFramebuffer(FramebufferTarget.FramebufferExt, framebuffer);
color = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, color);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, window.Width, window.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
GL.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, color, 0);
GL.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
window.Resize += (e, o) =>
{
UISaveState();
ConfigureUI(window.Width);
UILoadState();
GL.BindTexture(TextureTarget.Texture2D, color);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, window.Width, window.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
GL.BindTexture(TextureTarget.Texture2D, 0);
};
this.input = input;
widgets = new List<Widget>();
input.AddHandler(new InputHandler()
{
Keydownhandler = (e) =>
{
if (lastWidgetFocusedID != -1 && lastWidgetFocused.Enable)
{
lastWidgetFocused.HandleKeyDown(e);
}
},
Keypresshandler = (e) =>
{
if (lastWidgetFocusedID != -1 && lastWidgetFocused.Enable)
{
lastWidgetFocused.HandleKeyPress(e);
}
},
mousedownhandler = (e) =>
{
if (lastWidgetOverIndex != -1 && lastWidgetOver.Enable)
{
if (lastWidgetOverIndex != lastWidgetFocusedID)
{
if (lastWidgetFocusedID != -1)
lastWidgetFocused.HandleFocusedLost();
lastWidgetFocusedID = lastWidgetOverIndex;
lastWidgetOver?.HandleFocusedGained();
}
lastWidgetOver.HandleMouseDown(e);
}
else
{
if (lastWidgetFocusedID != -1 && lastWidgetFocused.Enable)
{
lastWidgetFocused.HandleFocusedLost();
lastWidgetFocusedID = -1;
}
}
},
mousemovehandler = (e) =>
{
if (lastWidgetFocusedID != -1 && lastWidgetFocused.Enable)
{
if (lastWidgetFocused.Drag)
{
lastWidgetFocused.HandleMouseMove(e);
}
}
},
mouseuphandler = (e) =>
{
if (lastWidgetOverIndex != -1 && lastWidgetOver.Enable)
{
lastWidgetOver.Drag = false;
lastWidgetOver.HandleMouseUp(e);
//float scaledmouseX = (float)Scale.hPosScale(input.mousex);
//float scaledmousez = (float)Scale.vPosScale(input.mouseY);
//if (!isMouseWithin(scaledmouseX, scaledmousez, lastWidgetOver))
//{
// Console.WriteLine("mouse leasve");
// lastWidgetOver.HandleMouseLeave();
// lastWidgetOverIndex = -1;
//}
}
},
mousewheelhandler = (e) =>
{
if (lastWidgetOverIndex != -1 && lastWidgetOver.Enable)
{
lastWidgetOver.HandleMouseWheel(e);
}
}
});
//SharpSerializer s = new SharpSerializer();
//if (File.Exists(Application.StartupPath + @"\data\gui\Standard.svui"))
//{
// GUIData data = s.Deserialize(Application.StartupPath + @"\data\gui\Standard.svui") as GUIData;
// foreach (var widget in data.widgets)
// {
// //widgets.Add(widget.)
// }
//}
}
public void Update(FrameEventArgs e)
{
for (int i = widgets.Count - 1; i > -1; i--)
{
if (widgets[i].Enable)
widgets[i].Update(e);
}
if (input.mousedx != 0 || input.mousedy != 0)
{
float scaledmouseX = (float)Scale.hPosScale(input.mousex);
float scaledmouseY = (float)Scale.vPosScale(input.mousey);
// we were over a control
if (lastWidgetOverIndex != -1)
{
if (!lastWidgetOver.Drag)
{
bool change = false;
if (!isMouseWithin(scaledmouseX, scaledmouseY, lastWidgetOver) || !lastWidgetOver.Enable)
{
change = true;
}
// are within our lastoverwidget
if (!change)
{
// check widgets above, if we're over then reset lastover
for (int i = widgets.Count - 1; i > lastWidgetOverIndex; i--)
{
if (widgets[i].Enable)
if (isMouseWithin(scaledmouseX, scaledmouseY, widgets[i]))
{
lastWidgetOver.HandleMouseLeave();
lastWidgetOverIndex = i;
lastWidgetOver.HandleMouseEnter();
lastWidgetOver.HandleMouseOver();
change = true;
break;
}
}
}
// not within our lastoverwidget
else
{
change = false;
// check all widgets including lower ones...
for (int i = widgets.Count - 1; i > -1; i--)
{
if (widgets[i].Enable)
if (i != lastWidgetOverIndex && isMouseWithin(scaledmouseX, scaledmouseY, widgets[i]))
{
lastWidgetOver.HandleMouseLeave();
lastWidgetOverIndex = i;
lastWidgetOver.HandleMouseEnter();
lastWidgetOver.HandleMouseOver();
change = true;
break;
}
}
// not over anything
if (!change)
{
lastWidgetOver.HandleMouseLeave();
lastWidgetOverIndex = -1;
}
}
}
else
{
// were dragging... release left mouse to let go
if (!input.mousedown(MouseButton.Left))
{
lastWidgetOver.HandleMouseLeave();
lastWidgetOverIndex = -1;
// if we don't test again we'd have to wait till next frame to mouse over something...
// i think there a potential double mouse over / mouse enter here??
for (int i = widgets.Count - 1; i > -1; i--)
{
if (widgets[i].Enable)
if (isMouseWithin(scaledmouseX, scaledmouseY, widgets[i]))
{
lastWidgetOverIndex = i;
lastWidgetOver.HandleMouseEnter();
lastWidgetOver.HandleMouseOver();
break;
}
}
}
}
}
else
{
for (int i = widgets.Count - 1; i > -1; i--)
{
if (widgets[i].Enable)
if (isMouseWithin(scaledmouseX, scaledmouseY, widgets[i]))
{
lastWidgetOverIndex = i;
lastWidgetOver.HandleMouseEnter();
lastWidgetOver.HandleMouseOver();
break;
}
}
}
if (lastWidgetOverIndex == -1)
{
Singleton<Raycaster>.INSTANCE.Enabled = true;
if (Client.window.Cursor != Singleton<BrushManager>.INSTANCE.currentBrush.Cursor)
Client.window.Cursor = Singleton<BrushManager>.INSTANCE.currentBrush.Cursor;
}
else
{
Singleton<Raycaster>.INSTANCE.Enabled = false;
MouseCursor cursor = lastWidgetOver.cursor != null ? lastWidgetOver.cursor : MouseCursor.Default;
if (Client.window.Cursor != cursor)
Client.window.Cursor = cursor;
}
}
}
public void Render()
{
if (!Visible) return;
GL.Disable(EnableCap.DepthTest);
Setup2D();
if (Dirty)
{
Dirty = false;
//if (lastWidgetOverIndex >-1)
//{
// Label status = Get<Label>(GUIID.STATUS_TEXT);
// string current = status.text;
// string previous = lastWidgetOver.StatusText;
// if (!string.IsNullOrEmpty(previous) && current != previous)
// status.text = previous;
// else
// status.text = "";
// Console.WriteLine("changed text");
//}
GL.BindFramebuffer(FramebufferTarget.FramebufferExt, framebuffer);
GL.DrawBuffers(1, new DrawBuffersEnum[] { DrawBuffersEnum.ColorAttachment0 });
GL.ClearColor(0, 0, 0, 0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
Render2D();
GL.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
}
float x = -1;
float y = -1;
float width = 2;
float height = 2;
GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, color);
GL.Color4(Color4.White);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(0f, 0f);
GL.Vertex2(x, y);
GL.TexCoord2(1f, 0f);
GL.Vertex2(x + width, y);
GL.TexCoord2(1f, 1f);
GL.Vertex2(x + width, y + height);
GL.TexCoord2(0f, 1f);
GL.Vertex2(x, y + height);
GL.End();
GL.BindTexture(TextureTarget.Texture2D, 0);
GL.Disable(EnableCap.Texture2D);
GL.Enable(EnableCap.DepthTest);
}
void Setup2D()
{
ShaderUtil.ResetShader();
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(-1f, 1f, -1f, 1f, -1, 1);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
}
void Render2D()
{
for (int i = 0; i < widgets.Count; i++)
{
widgets[i].Render();
}
}
bool isMouseWithin(float x, float y, Widget widget)
{
// absolute x.z and sizes should alreadz be in scale
float widget_x = widget.Absolute_X;
float widget_y = widget.Absolute_Y;
float widget_width = widget.size.X;
float widget_height = widget.size.Y;
return (x <= widget_x + widget_width && x >= widget_x && y >= widget_y && y <= widget_y + widget_height);
}
public T Get<T>(int ID) where T : Widget
{
var widget = widgets.Where((e) => { return e.ID == ID; });
if (widget.Count() > 0)
return (T)widget.First();
else
{
return null;
}
}
void UISaveState()
{
}
void UILoadState()
{
}
void ConfigureUI(int width)
{
}
void BuildUI()
{
}
void Build_HackySelectionTool()
{
}
void Build_IOWindow()
{
}
void Build_ModelTabs()
{
}
void Build_BrushToolbar()
{
}
void Build_ColorPicker()
{
}
void Build_ColorToolbar()
{
}
void Build_MatrixList()
{
}
void Build_Screenshot()
{
}
private static Bitmap cropImage(Bitmap img, Rectangle cropArea)
{
Bitmap bmpImage = new Bitmap(img);
return bmpImage.Clone(cropArea, bmpImage.PixelFormat);
}
}
}

View File

@@ -0,0 +1,182 @@
using OpenTK;
using OpenTK.Input;
using System.Collections.Generic;
namespace stonevox
{
public delegate void KeyHandler(KeyboardKeyEventArgs e);
public delegate void KeyPressHandler(KeyPressEventArgs e);
public delegate void MouseHandler(MouseButtonEventArgs e);
public delegate void MouseMoveHandler(MouseMoveEventArgs e);
public delegate void MouseWheelHandler(MouseWheelEventArgs e);
public class Input : Singleton<Input>
{
public List<InputHandler> messaginghandlers = new List<InputHandler>();
public List<InputHandler> driverhandlers = new List<InputHandler>();
public int mousedx;
public int mousedy;
private KeyboardState lastKeyboardstate;
private KeyboardState currentKeyboardstate;
private MouseState lastmousestate;
private MouseState currentmousestate;
public int mousex;
public int mousey;
public int mouseX { get { return currentmousestate.X; } }
public int mouseY { get { return currentmousestate.Y; } }
private GLWindow window;
private bool isFocused { get { return window.isfocused; } }
public Input(GLWindow window)
: base()
{
this.window = window;
}
public void update()
{
currentKeyboardstate = Keyboard.GetState();
currentmousestate = Mouse.GetState();
mousedx = currentmousestate.X - lastmousestate.X;
mousedy = currentmousestate.Y - lastmousestate.Y;
lastKeyboardstate = currentKeyboardstate;
lastmousestate = currentmousestate;
}
public void AddHandler(InputHandler handler)
{
messaginghandlers.Add(handler);
}
public void removehandler(InputHandler handler)
{
messaginghandlers.Remove(handler);
}
public bool Keydown(Key Key)
{
return isFocused && currentKeyboardstate.IsKeyDown(Key);
}
public bool Keyup(Key Key)
{
return isFocused && currentKeyboardstate.IsKeyUp(Key);
}
public bool Keypressed(Key Key)
{
return isFocused && currentKeyboardstate.IsKeyDown(Key) && lastKeyboardstate.IsKeyUp(Key);
}
public bool mousedown(MouseButton button)
{
return isFocused && currentmousestate.IsButtonDown(button);
}
public bool mouseup(MouseButton button)
{
return isFocused && currentmousestate.IsButtonUp(button);
}
public bool mousepressed(MouseButton button)
{
return isFocused && currentmousestate.IsButtonDown(button) && lastmousestate.IsButtonUp(button);
}
public void handleKeydown(KeyboardKeyEventArgs e)
{
if (!isFocused) return;
messaginghandlers.ForEach(delegate (InputHandler t)
{
if (t.Keydownhandler != null)
t.Keydownhandler(e);
});
}
public void handleKeyup(KeyboardKeyEventArgs e)
{
//Client.print("info", "Keyup");
if (!isFocused) return;
messaginghandlers.ForEach(delegate (InputHandler t)
{
if (t.Keyuphandler != null)
t.Keyuphandler(e);
});
}
public void handleKeypress(KeyPressEventArgs e)
{
///Client.print("info", "Keypress");
if (!isFocused) return;
messaginghandlers.ForEach(delegate (InputHandler t)
{
if (t.Keypresshandler != null)
t.Keypresshandler(e);
});
}
public void handlemousemove(MouseMoveEventArgs e)
{
if (!isFocused) return;
messaginghandlers.ForEach(delegate (InputHandler t)
{
if (t.mousemovehandler != null)
t.mousemovehandler(e);
});
}
public void handlemousedown(MouseButtonEventArgs e)
{
//Client.print("info", "mousedown");
if (!isFocused) return;
messaginghandlers.ForEach(delegate (InputHandler t)
{
if (t.mousedownhandler != null)
t.mousedownhandler(e);
});
}
public void handlemouseup(MouseButtonEventArgs e)
{
///Client.print("info", "mouseup");
if (!isFocused) return;
messaginghandlers.ForEach(delegate (InputHandler t)
{
if (t.mouseuphandler != null)
t.mouseuphandler(e);
});
}
public void handlemousewheel(MouseWheelEventArgs e)
{
// Client.print("info", "mousewheel");
if (!isFocused) return;
messaginghandlers.ForEach(delegate (InputHandler t)
{
if (t.mousewheelhandler != null)
t.mousewheelhandler(e);
});
}
}
public class InputHandler
{
public KeyHandler Keydownhandler;
public KeyHandler Keyuphandler;
public KeyPressHandler Keypresshandler;
public MouseHandler mousedownhandler;
public MouseHandler mouseuphandler;
public MouseMoveHandler mousemovehandler;
public MouseWheelHandler mousewheelhandler;
}
}

View File

@@ -0,0 +1,122 @@
using System.Collections.Generic;
namespace stonevox
{
public class QbManager : Singleton<QbManager>
{
private int activemodelindex;
public bool HasModel { get { return models.Count > 0; } }
public int ActiveMatrixIndex { get { return ActiveModel.activematrix; } set { SetActiveMatrix(value); } }
public int ActiveModelIndex { get { return activemodelindex; } set { SetActiveModel(value); } }
public List<QbModel> models = new List<QbModel>();
Broadcaster broadcaster;
public QbModel ActiveModel
{
get { return models[activemodelindex]; }
set { SetActiveModel(value); }
}
public QbMatrix ActiveMatrix
{
get { return ActiveModel.getactivematrix; }
set { SetActiveMatrix(value); }
}
public QbManager(Broadcaster broadcaster)
: base()
{
this.broadcaster = broadcaster;
//broadcaster.handlers.Add((message, widge, args) =>
//{
//});
}
void SetActiveModel(int index)
{
activemodelindex = index;
broadcaster.Broadcast(Message.ActiveModelChanged, ActiveModel, ActiveModelIndex);
broadcaster.Broadcast(Message.ActiveMatrixChanged, ActiveMatrix, ActiveMatrixIndex);
}
void SetActiveModel(QbModel model)
{
SetActiveModel(models.IndexOf(model));
}
void SetActiveMatrix(int index)
{
ActiveModel.activematrix = index;
broadcaster.Broadcast(Message.ActiveMatrixChanged, ActiveMatrix, index);
}
void SetActiveMatrix(QbMatrix matrix)
{
SetActiveMatrix(ActiveModel.matrices.IndexOf(matrix));
}
public void Dispose()
{
foreach (var m in models)
m.Dispose();
}
public QbModel AddEmpty()
{
QbModel model = QbModel.EmptyModel();
AddModel(model);
return model;
}
public void AddModel(QbModel model, bool setActive = true)
{
models.Add(model);
if (setActive)
{
ActiveModel = model;
ActiveMatrix.MatchFloorToSize();
}
broadcaster.Broadcast(Message.ModelImported, model, model.name);
}
public void RemoveModel(QbModel model)
{
// stonevox as of now, must have a qbmodel reference...
// this will be cleanup sometime
// it's a dirty fix for now :(
if (ActiveModelIndex == 0 && models.Count == 1)
{
if (models.Remove(model))
{
AddEmpty();
broadcaster.Broadcast(Message.ModelRemoved, model, model.name);
model = null;
}
FixActiveMatrix();
return;
}
if (models.Remove(model))
{
FixActiveMatrix();
broadcaster.Broadcast(Message.ModelRemoved, model, model.name);
model = null;
}
}
void FixActiveMatrix()
{
while (activemodelindex >= models.Count)
activemodelindex--;
// eh :)
ActiveModelIndex = activemodelindex;
}
}
}

View File

@@ -0,0 +1,847 @@
using OpenTK;
using System;
using System.Threading;
namespace stonevox
{
public enum RaycastMode
{
ActiveMatrix,
FullModel,
MatrixSelection, // super hacks
MatrixColorSelection
}
public class Raycaster : Singleton<Raycaster>
{
Vector3 top = new Vector3(0, 1, 0);
Vector3 bottom = new Vector3(0, -1, 0);
Vector3 left = new Vector3(1, 0, 0);
Vector3 right = new Vector3(-1, 0, 0);
Vector3 front = new Vector3(0, 0, 1);
Vector3 back = new Vector3(0, 0, -1);
public Vector3 near = new Vector3();
public Vector3 far = new Vector3();
float clientwidth;
float clientheight;
Camera camera;
Input input;
GLWindow window;
Selection selection;
Floor floor;
QbManager manager;
GUI gui;
public Vector3 rayOrigin;
public Vector3 rayDirection;
public bool HasHit;
bool enabled = true;
public bool Enabled { get { return enabled; } set { enabled = value; if (!value) selection.handledselectionchange = true; } }
public bool testdirt = false;
public RaycastMode Mode = RaycastMode.ActiveMatrix;
float dot;
float t;
float coordRatio;
Vector3 intPoint = new Vector3();
double accuracy = 0.0008d;
double fullArea;
double subTriangle1;
double subTriangle2;
double subTriangle3;
double totalSubAreas;
float cubesize = .5f;
float d;
Matrix4 a;
Vector4 _in = new Vector4();
Vector4 _out;
Vector3 p1;
Vector3 p2;
Vector3 p3;
Vector3 s1;
Vector3 s2;
Vector3 s3;
bool _front;
bool _back;
bool _left;
bool _right;
bool _top;
bool _bottom;
Vector3 testout;
public RaycastHit lastHit = new RaycastHit()
{
distance = 10000
};
Thread thread;
public Raycaster(GLWindow window, Camera camera, Selection selection, Floor floor, Input input, QbManager manager, GUI gui)
{
this.window = window;
this.camera = camera;
this.input = input;
this.floor = floor;
this.selection = selection;
this.manager = manager;
this.gui = gui;
clientwidth = window.Width;
clientheight = window.Height;
window.Resize += (e, o) =>
{
clientwidth = window.Width;
clientheight = window.Height;
};
thread = new Thread(RaycastTask);
thread.Start();
}
void RaycastTask()
{
while (true)
{
if (!enabled || gui.OverWidget)
{
HasHit = false;
lastHit.distance = 10000;
Thread.Sleep(200);
continue;
}
if (!camera.freecam)
ScreenToMouseRay(input.mousex, input.mousey);
else
ScreenToMouseRay(window.Width / 2, window.Height / 2);
RaycastHit hit = new RaycastHit();
hit.distance = 10000;
switch (Mode)
{
case RaycastMode.ActiveMatrix:
if (!manager.ActiveMatrix.Visible) break;
hit = RaycastTest(camera.position, manager.ActiveMatrix);
if (hit.distance != 10000)
{
HasHit = true;
hit.matrixIndex = manager.ActiveMatrixIndex;
if (!hit.matches(lastHit))
{
lastHit = hit;
selection.dirty = true;
}
}
else if (hit.distance == 10000)
{
if (floor.RayTest(this, ref hit))
{
HasHit = true;
hit.matrixIndex = manager.ActiveMatrixIndex;
if (!hit.matches(lastHit))
{
lastHit = hit;
selection.dirty = true;
}
Thread.Sleep(15);
continue;
}
selection.handledselectionchange = true;
HasHit = false;
}
lastHit = hit;
break;
case RaycastMode.FullModel:
for (int i = 0; i < manager.ActiveModel.numMatrices; i++)
{
if (!manager.ActiveModel.matrices[i].Visible) continue;
RaycastHit tempHit = RaycastTest(camera.position, manager.ActiveModel.matrices[i]);
if (tempHit.distance < hit.distance && !tempHit.matches(hit))
{
manager.ActiveModel.activematrix = i;
tempHit.matrixIndex = i;
hit = tempHit;
}
}
if (hit.distance != 10000)
{
HasHit = true;
if (!hit.matches(lastHit))
{
lastHit = hit;
selection.dirty = true;
}
}
else if (hit.distance == 10000)
{
selection.handledselectionchange = true;
HasHit = false;
}
lastHit = hit;
break;
case RaycastMode.MatrixSelection: // super hacks
int index = 0;
for (int i = 0; i < manager.ActiveModel.numMatrices; i++)
{
if (!manager.ActiveModel.matrices[i].Visible) continue;
RaycastHit tempHit = RaycastTest(camera.position, manager.ActiveModel.matrices[i]);
if (tempHit.distance != 10000 && tempHit.distance < hit.distance && !tempHit.matches(hit))
{
index = i;
tempHit.matrixIndex = i;
hit = tempHit;
}
}
if (hit.distance != 10000)
{
HasHit = true;
}
else if (hit.distance == 10000)
{
HasHit = false;
}
if (!hit.matches(lastHit))
{
// ohhh my...
// super super hacks
if (HasHit)
Singleton<BrushManager>.INSTANCE.onselectionchanged(input, manager.ActiveModel.matrices[index], hit);
else
Singleton<BrushManager>.INSTANCE.onselectionchanged(input, null, hit);
}
lastHit = hit;
break;
case RaycastMode.MatrixColorSelection: // super hacks
int indexx = 0;
for (int i = 0; i < manager.ActiveModel.numMatrices; i++)
{
if (!manager.ActiveModel.matrices[i].Visible) continue;
RaycastHit tempHit = RaycastTest(camera.position, manager.ActiveModel.matrices[i]);
if (tempHit.distance != 10000 && tempHit.distance < hit.distance && !tempHit.matches(hit))
{
indexx = i;
tempHit.matrixIndex = i;
hit = tempHit;
}
}
if (hit.distance != 10000)
{
HasHit = true;
}
else if (hit.distance == 10000)
{
HasHit = false;
}
if (!hit.matches(lastHit))
{
Client.OpenGLContextThread.Add(() => Singleton<Selection>.INSTANCE.UpdateVisibleSelection());
// ohhh my...
// super super hacks
if (HasHit)
Singleton<BrushManager>.INSTANCE.onselectionchanged(input, manager.ActiveModel.matrices[indexx], hit);
else
Singleton<BrushManager>.INSTANCE.onselectionchanged(input, null, hit);
}
lastHit = hit;
break;
}
Thread.Sleep(15);
}
}
public void UnProject(float x, float y, float z, ref Matrix4 modelview, ref Matrix4 projection, out Vector3 value)
{
Matrix4.Mult(ref modelview, ref projection, out a);
a.Invert();
_in.X = (x) / clientwidth * 2f - 1f;
_in.Y = (y) / clientheight * 2f - 1f;
_in.Z = 2f * z - 1f;
_in.W = 1;
Vector4.Transform(ref _in, ref a, out _out);
if (_out.W != 0f)
_out.W = 1f / _out.W;
value.X = _out.X * _out.W;
value.Y = _out.Y * _out.W;
value.Z = _out.Z * _out.W;
}
public void ScreenToMouseRay(int mouseX, int mouseY)
{
UnProject(mouseX, mouseY, 0f, ref camera.view, ref camera.projection, out near);
UnProject(mouseX, mouseY, 1f, ref camera.view, ref camera.projection, out far);
Vector3.Subtract(ref far, ref near, out rayDirection);
}
public bool RayTestTriangle(ref Vector3 planeNormal, float p1x, float p1y, float p1z, float p2x, float p2y,
float p2z, float p3x, float p3y, float p3z, out Vector3 _out)
{
dot = rayDirection.X * planeNormal.X + rayDirection.Y * planeNormal.Y + rayDirection.Z * planeNormal.Z;
t = 0;
if (dot == 0)
{
_out = Vector3.Zero;
return false;
}
coordRatio =
p1x * planeNormal.X + p1y * planeNormal.Y + p1z * planeNormal.Z - planeNormal.X * rayOrigin.X
- planeNormal.Y * rayOrigin.Y - planeNormal.Z * rayOrigin.Z;
t = coordRatio / dot;
if (t < 0)
{
_out = Vector3.Zero;
return false;
}
intPoint.X = rayOrigin.X + t * rayDirection.X;
intPoint.Y = rayOrigin.Y + t * rayDirection.Y;
intPoint.Z = rayOrigin.Z + t * rayDirection.Z;
fullArea = CalculateTriangleArea(p1x, p1y, p1z, p2x, p2y, p2z, p3x, p3y, p3z);
subTriangle1 = CalculateTriangleArea(p1x, p1y, p1z, p2x, p2y, p2z, intPoint.X, intPoint.Y, intPoint.Z);
subTriangle2 = CalculateTriangleArea(p2x, p2y, p2z, p3x, p3y, p3z, intPoint.X, intPoint.Y, intPoint.Z);
subTriangle3 = CalculateTriangleArea(p1x, p1y, p1z, p3x, p3y, p3z, intPoint.X, intPoint.Y, intPoint.Z);
totalSubAreas = subTriangle1 + subTriangle2 + subTriangle3;
if (Math.Abs(fullArea - totalSubAreas) < accuracy)
{
_out = intPoint;
return true;
}
else
{
_out = Vector3.Zero;
return false;
}
}
private double CalculateTriangleArea(float p1x, float p1y, float p1z, float p2x, float p2y, float p2z,
float p3x, float p3y, float p3z)
{
// this is about 1/3 faster than all the math.sqrt
// 1/2 | (x₃ - x₁) x (x₃ - x₂) |
p1.X = p1x;
p1.Y = p1y;
p1.Z = p1z;
p2.X = p2x;
p2.Y = p2y;
p2.Z = p2z;
p3.X = p3x;
p3.Y = p3y;
p3.Z = p3z;
Vector3.Subtract(ref p3, ref p1, out s1);
Vector3.Subtract(ref p3, ref p2, out s2);
Vector3.Cross(ref s1, ref s2, out s3);
double value = Math.Abs(s3.X + s3.Y + s3.Z);
return value * .5d;
//double a = Math.Sqrt((p2x - p1x) * (p2x - p1x) + (p2y - p1y) * (p2y - p1y) + (p2z - p1z) * (p2z - p1z));
//double b = Math.Sqrt((p3x - p2x) * (p3x - p2x) + (p3y - p2y) * (p3y - p2y) + (p3z - p2z) * (p3z - p2z));
//double c = Math.Sqrt((p3x - p1x) * (p3x - p1x) + (p3y - p1y) * (p3y - p1y) + (p3z - p1z) * (p3z - p1z));
//double s = (a + b + c) / 2d;
//return Math.Sqrt(s * (s - a) * (s - b) * (s - c));
}
bool RayIntersectsPlane(ref Vector3 normal, ref Vector3 rayVector)
{
float denom = 0;
Vector3.Dot(ref normal, ref rayVector, out denom);
if (denom < .3f)
{
return true;
}
return false;
}
public float distance(float x, float y, float z)
{
float num = rayOrigin.X - x;
float num2 = rayOrigin.Y - y;
float num3 = rayOrigin.Z - z;
float num4 = num * num + num2 * num2 + num3 * num3;
return (float)Math.Sqrt((double)num4);
}
public RaycastHit RaycastTest(Vector3 camerapos, QbMatrix m)
{
RaycastHit hitpoint = new RaycastHit();
hitpoint.distance = 10000;
rayOrigin = camerapos;
_front = RayIntersectsPlane(ref front, ref camera.direction);
_back = RayIntersectsPlane(ref back, ref camera.direction);
_top = RayIntersectsPlane(ref top, ref camera.direction);
_bottom = RayIntersectsPlane(ref bottom, ref camera.direction);
_right = RayIntersectsPlane(ref right, ref camera.direction);
_left = RayIntersectsPlane(ref left, ref camera.direction);
//var outp =string.Format("front {0},back {1},top {2},bottom {3},left {4},right {5},", _front, _back, _top, _bottom, _left, _right);
//Console.WriteLine(outp);
// RayTestTriangle region... kinda think of like quad treeing
//Vector3[] f1 = new Vector3[] { new Vector3(0, 0, 0), new Vector3(m.size.X/2f, m.size.Y /2f, m.size.Z/2f) };
//Vector3[] f2 = new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) };
//Vector3[] f3 = new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) };
//Vector3[] f4 = new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) };
//Vector3[] f5 = new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) };
//Vector3[] f6 = new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) };
//Vector3[] f7 = new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) };
//Vector3[] f8 = new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) };
foreach (var v in m.voxels.Values)
{
if (testdirt)
{
if (v.dirty)
{
////front
if (_front && (RayTestTriangle(ref front, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
out testout) || RayTestTriangle(ref front, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
-cubesize + v.x, cubesize + v.y, cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f, v.z * .5f + .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Front;
}
}
////bavk
if (_back && (RayTestTriangle(ref back, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
out testout) || RayTestTriangle(ref back, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f, v.z * .5f - .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Back;
}
}
//top
if (_top && (RayTestTriangle(ref top, -cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
out testout) || RayTestTriangle(ref top, -cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f + .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Top;
}
}
////bottom
if (_bottom && (RayTestTriangle(ref bottom, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
out testout) || RayTestTriangle(ref bottom, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, -cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f - .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Bottom;
}
}
////left
if (_left && (RayTestTriangle(ref left, cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
out testout) || RayTestTriangle(ref left, cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f + .5f, v.y * .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Left;
}
}
////right
if (_right && (RayTestTriangle(ref right, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, -cubesize + v.y, cubesize + v.z,
-cubesize + v.x, cubesize + v.y, cubesize + v.z,
out testout) || RayTestTriangle(ref right, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, cubesize + v.y, cubesize + v.z,
-cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f - .5f, v.y * .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Right;
}
}
}
else
{
//front
if (_front && ((v.alphamask & 32) == 32))
{
if ((RayTestTriangle(ref front, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
out testout) || RayTestTriangle(ref front, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
-cubesize + v.x, cubesize + v.y, cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f, v.z * .5f + .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Front;
}
}
}
//bavk
if (_back && ((v.alphamask & 64) == 64))
{
if ((RayTestTriangle(ref back, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
out testout) || RayTestTriangle(ref back, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f, v.z * .5f - .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Back;
}
}
}
//top
if (_top && ((v.alphamask & 8) == 8))
{
if ((RayTestTriangle(ref top, -cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
out testout) || RayTestTriangle(ref top, -cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f + .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Top;
}
}
}
//bottom
if (_bottom && ((v.alphamask & 16) == 16))
{
if ((RayTestTriangle(ref bottom, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
out testout) || RayTestTriangle(ref bottom, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, -cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f - .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Bottom;
}
}
}
//left
if (_left && ((v.alphamask & 2) == 2))
{
if ((RayTestTriangle(ref left, cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
out testout) || RayTestTriangle(ref left, cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f + .5f, v.y * .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Left;
}
}
}
//right
if (_right && ((v.alphamask & 4) == 4))
{
if ((RayTestTriangle(ref right, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, -cubesize + v.y, cubesize + v.z,
-cubesize + v.x, cubesize + v.y, cubesize + v.z,
out testout) || RayTestTriangle(ref right, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, cubesize + v.y, cubesize + v.z,
-cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f - .5f, v.y * .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Right;
}
}
}
}
}
else if (v.alphamask > 0)
{
if (v.dirty)
continue;
//front
if (_front && ((v.alphamask & 32) == 32 || m.IsDirty(v.x, v.y, v.z + 1)))
{
if ((RayTestTriangle(ref front, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
out testout) || RayTestTriangle(ref front, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
-cubesize + v.x, cubesize + v.y, cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f, v.z * .5f + .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Front;
}
}
}
//bavk
if (_back && ((v.alphamask & 64) == 64 || m.IsDirty(v.x, v.y, v.z - 1)))
{
if ((RayTestTriangle(ref back, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
out testout) || RayTestTriangle(ref back, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f, v.z * .5f - .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Back;
}
}
}
//top
if (_top && ((v.alphamask & 8) == 8) || m.IsDirty(v.x, v.y + 1, v.z))
{
if ((RayTestTriangle(ref top, -cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
out testout) || RayTestTriangle(ref top, -cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f + .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Top;
}
}
}
//bottom
if (_bottom && ((v.alphamask & 16) == 16) || m.IsDirty(v.x, v.y - 1, v.z))
{
if ((RayTestTriangle(ref bottom, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
out testout) || RayTestTriangle(ref bottom, -cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, -cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f, v.y * .5f - .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Bottom;
}
}
}
//left
if (_left && ((v.alphamask & 2) == 2 || m.IsDirty(v.x + 1, v.y, v.z)))
{
if ((RayTestTriangle(ref left, cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, -cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
out testout) || RayTestTriangle(ref left, cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
cubesize + v.x, cubesize + v.y, cubesize + v.z,
cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f + .5f, v.y * .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Left;
}
}
}
//right
if (_right && ((v.alphamask & 4) == 4 || m.IsDirty(v.x - 1, v.y, v.z)))
{
if ((RayTestTriangle(ref right, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, -cubesize + v.y, cubesize + v.z,
-cubesize + v.x, cubesize + v.y, cubesize + v.z,
out testout) || RayTestTriangle(ref right, -cubesize + v.x, -cubesize + v.y, -cubesize + v.z,
-cubesize + v.x, cubesize + v.y, cubesize + v.z,
-cubesize + v.x, cubesize + v.y, -cubesize + v.z, out testout)))
{
d = distance(v.x * .5f - .5f, v.y * .5f, v.z * .5f);
if (d < hitpoint.distance)
{
hitpoint.distance = d;
hitpoint.x = v.x;
hitpoint.y = v.y;
hitpoint.z = v.z;
hitpoint.side = Side.Right;
}
}
}
}
}
return hitpoint;
}
}
public class RaycastHit
{
public float distance;
public int x;
public int y;
public int z;
public Side side;
public int matrixIndex;
public RaycastHit()
{
}
public RaycastHit(int x, int y, int z, float distance)
{
}
public bool matches(RaycastHit other)
{
return this.x == other.x && this.y == other.y && this.z == other.z && this.side == other.side;
}
public override string ToString()
{
return string.Format("SIDE : {0} \nLocation : {1} : {2} : {3}", side.ToString(), x, y, z);
}
}
}

View File

@@ -0,0 +1,124 @@
using OpenTK;
using System;
namespace stonevox
{
public static class Raycaster2
{
const float SMALL_NUM = 0.00000001f;
const float Epsilon = 0.00000001f;
static Vector3 e1, e2;
static Vector3 p, q, t;
static float det, invDet, u, v;
static Vector3 zero = new Vector3(0, 0, 0);
// this is around .06 faster... meh
public static bool Intersect(ref Vector3 p1, ref Vector3 p2, ref Vector3 p3)
{
Vector3.Subtract(ref p2,ref p1, out e1);
Vector3.Subtract(ref p3, ref p1, out e2);
//Vector3.Cross(ref Raycaster.rayDirection, ref e2, out p);
Vector3.Dot(ref e1, ref p, out det);
if (det > -Epsilon && det < Epsilon) { return false; }
invDet = 1.0f / det;
//Vector3.Subtract(ref Raycaster.near, ref p1, out t);
float temp;
Vector3.Dot(ref t, ref p, out temp);
u = temp * invDet;
if (u < 0 || u > 1) { return false; }
Vector3.Cross(ref t, ref e1, out q);
//Vector3.Dot(ref Raycaster.rayDirection, ref q, out temp);
v = temp * invDet;
if (v < 0 || u + v > 1) { return false; }
Vector3.Dot(ref e2, ref q, out temp);
if (temp * invDet > Epsilon)
{
return true;
}
return false;
}
// slower than what i have now but i'm keeping it here for refference
// Copzright 2001 softSurfer, 2012 Dan Sundaz
// This code maz be freelz used, distributed and modified for anz purpose
// providing that this copzright notice is included with it.
// SoftSurfer makes no warrantz for this code, and cannot be held
// liable for anz real or imagined damage resulting from its use.
// Users of this code must verifz correctness for their application.
public static int intersect3D_RazTriangle(ref Vector3 rp0, ref Vector3 rp1, Vector3 tv0, Vector3 tv1, Vector3 tv2, ref Vector3 intersection)
{
Vector3 u, v, n;
Vector3 dir, w0, w;
float r, a, b;
// get triangle edge vectors and plane normal
u = tv1 - tv0;
v = tv2 - tv0;
n = u * v; // cross product
if (n == zero) // triangle is degenerate
return -1; // do not deal with this case
dir = rp1 - rp0; // raz direction vector
w0 = rp0 - tv0;
Vector3.Dot(ref n, ref w0, out a);
a *= -1f;
Vector3.Dot(ref n, ref dir, out b);
if (Math.Abs(b) < SMALL_NUM)
{ // raz is parallel to triangle plane
if (a == 0) // raz lies in triangle plane
return 2;
else return 0; // raz disjoint from plane
}
// get intersect point of raz with triangle plane
r = a / b;
if (r < 0.0) // raz goes awaz from triangle
return 0; // => no intersect
// for a segment, also test if (r > 1.0) => no intersect
intersection = rp0 + r * dir;
//*I = R.P0 + r * dir; // intersect point of raz and plane
// is I inside T?
float uu, uv, vv, wu, wv, D;
Vector3.Dot(ref u, ref u, out uu);
Vector3.Dot(ref u, ref v, out uv);
Vector3.Dot(ref v, ref v, out vv);
w = intersection - tv0;
Vector3.Dot(ref w, ref u, out wu);
Vector3.Dot(ref w, ref v, out wv);
D = uv * uv - uu * vv;
// get and test parametric coords
float s, t;
s = (uv * wv - vv * wu) / D;
if (s < 0.0 || s > 1.0) // I is outside T
{
return 0;
}
t = (uv * wu - uu * wv) / D;
if (t < 0.0 || (s + t) > 1.0) // I is outside T
{
return 0;
}
return 1; // I is in T
}
}
}

View File

@@ -0,0 +1,270 @@
using OpenTK;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Input;
using System;
namespace stonevox
{
public class Selection : Singleton<Selection>
{
int vertexbuffer;
int vertexArray;
public bool dirty;
public bool Visible = true;
float cubesize = .5f;
float[] buffer;
private BrushManager brushes;
private Input input;
private QbManager manager;
private Floor floor;
private GUI gui;
public Raycaster raycaster;
public bool handledselectionchange = true;
private bool needscleaning;
private RaycastHit lasthit = new RaycastHit()
{
distance = 10000
};
private Colort color;
public Selection(GLWindow window, BrushManager tools, Input input, QbManager manager, Floor floor, GUI gui)
: base()
{
this.brushes = tools;
this.input = input;
this.manager = manager;
this.floor = floor;
this.gui = gui;
window.Resize += (e, a) =>
{
};
color = new Colort(1, 0, 0);
buffer = new float[16];
input.AddHandler(new InputHandler()
{
mousedownhandler = (e) =>
{
if (Singleton<GUI>.INSTANCE.OverWidget) return;
if (e.Button == MouseButton.Left && !dirty && handledselectionchange && Singleton<Raycaster>.INSTANCE.HasHit)
{
handledselectionchange = brushes.onselectionchanged(input, manager.ActiveMatrix, lasthit, e);
if (handledselectionchange)
needscleaning = true;
}
},
mouseuphandler = (e) =>
{
if (e.Button == MouseButton.Left && !dirty && handledselectionchange)
{
handledselectionchange = brushes.onselectionchanged(input, manager.ActiveMatrix, lasthit, e);
if (handledselectionchange)
needscleaning = true;
}
}
});
}
public void GenerateVertexArray()
{
GLUtils.CreateVertexArraysQBF(sizeof(float) * 16, out vertexArray, out vertexbuffer);
}
public void update()
{
if (dirty)
{
dirty = false;
UpdateVisibleSelection();
handledselectionchange = brushes.onselectionchanged(input, manager.ActiveMatrix, lasthit);
if (handledselectionchange)
needscleaning = true;
}
if (!handledselectionchange && raycaster.HasHit)
{
handledselectionchange = brushes.onselectionchanged(input, manager.ActiveMatrix, lasthit);
if (handledselectionchange)
needscleaning = true;
}
if (needscleaning)
{
if (input.mouseup(MouseButton.Left))
{
// change this... have the tool decide to clean or not
needscleaning = false;
manager.ActiveMatrix.Clean();
}
}
}
public void UpdateVisibleSelection()
{
lasthit = Singleton<Raycaster>.INSTANCE.lastHit;
switch (lasthit.side)
{
case Side.Front:
buffer[0] = -cubesize + lasthit.x;
buffer[1] = -cubesize + lasthit.y;
buffer[2] = cubesize + lasthit.z;
buffer[4] = cubesize + lasthit.x;
buffer[5] = -cubesize + lasthit.y;
buffer[6] = cubesize + lasthit.z;
buffer[8] = cubesize + lasthit.x;
buffer[9] = cubesize + lasthit.y;
buffer[10] = cubesize + lasthit.z;
buffer[12] = -cubesize + lasthit.x;
buffer[13] = cubesize + lasthit.y;
buffer[14] = cubesize + lasthit.z;
break;
case Side.Back:
buffer[12] = -cubesize + lasthit.x;
buffer[13] = -cubesize + lasthit.y;
buffer[14] = -cubesize + lasthit.z;
buffer[8] = cubesize + lasthit.x;
buffer[9] = -cubesize + lasthit.y;
buffer[10] = -cubesize + lasthit.z;
buffer[4] = cubesize + lasthit.x;
buffer[5] = cubesize + lasthit.y;
buffer[6] = -cubesize + lasthit.z;
buffer[0] = -cubesize + lasthit.x;
buffer[1] = cubesize + lasthit.y;
buffer[2] = -cubesize + lasthit.z;
break;
case Side.Top:
buffer[0] = -cubesize + lasthit.x;
buffer[1] = cubesize + lasthit.y;
buffer[2] = cubesize + lasthit.z;
buffer[4] = cubesize + lasthit.x;
buffer[5] = cubesize + lasthit.y;
buffer[6] = cubesize + lasthit.z;
buffer[8] = cubesize + lasthit.x;
buffer[9] = cubesize + lasthit.y;
buffer[10] = -cubesize + lasthit.z;
buffer[12] = -cubesize + lasthit.x;
buffer[13] = cubesize + lasthit.y;
buffer[14] = -cubesize + lasthit.z;
break;
case Side.Bottom:
buffer[12] = -cubesize + lasthit.x;
buffer[13] = -cubesize + lasthit.y;
buffer[14] = cubesize + lasthit.z;
buffer[8] = cubesize + lasthit.x;
buffer[9] = -cubesize + lasthit.y;
buffer[10] = cubesize + lasthit.z;
buffer[4] = cubesize + lasthit.x;
buffer[5] = -cubesize + lasthit.y;
buffer[6] = -cubesize + lasthit.z;
buffer[0] = -cubesize + lasthit.x;
buffer[1] = -cubesize + lasthit.y;
buffer[2] = -cubesize + lasthit.z;
break;
case Side.Right:
buffer[0] = -cubesize + lasthit.x;
buffer[1] = -cubesize + lasthit.y;
buffer[2] = -cubesize + lasthit.z;
buffer[4] = -cubesize + lasthit.x;
buffer[5] = -cubesize + lasthit.y;
buffer[6] = cubesize + lasthit.z;
buffer[8] = -cubesize + lasthit.x;
buffer[9] = cubesize + lasthit.y;
buffer[10] = cubesize + lasthit.z;
buffer[12] = -cubesize + lasthit.x;
buffer[13] = cubesize + lasthit.y;
buffer[14] = -cubesize + lasthit.z;
break;
case Side.Left:
buffer[12] = cubesize + lasthit.x;
buffer[13] = -cubesize + lasthit.y;
buffer[14] = -cubesize + lasthit.z;
buffer[8] = cubesize + lasthit.x;
buffer[9] = -cubesize + lasthit.y;
buffer[10] = cubesize + lasthit.z;
buffer[4] = cubesize + lasthit.x;
buffer[5] = cubesize + lasthit.y;
buffer[6] = cubesize + lasthit.z;
buffer[0] = cubesize + lasthit.x;
buffer[1] = cubesize + lasthit.y;
buffer[2] = -cubesize + lasthit.z;
break;
}
buffer[3] = 0;
buffer[7] = 0;
buffer[11] = 0;
buffer[15] = 0;
GL.BindBuffer(BufferTarget.ArrayBuffer, vertexbuffer);
GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, (IntPtr)(sizeof(float) * 16), buffer);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
}
public void render(Shader shader)
{
if (Visible && Singleton<Raycaster>.INSTANCE.lastHit.distance != 10000)
{
shader.WriteUniform("highlight", new Vector3(1, 1, 1));
unsafe
{
fixed (float* pointer = &color.R)
{
shader.WriteUniformArray("colors", 1, pointer);
}
}
GL.BindVertexArray(vertexArray);
GL.DrawArrays(PrimitiveType.Quads, 0, 4);
GL.BindVertexArray(0);
}
}
}
}

View File

@@ -0,0 +1,13 @@
namespace stonevox
{
public class Singleton<T> where T : class
{
private static T Instance;
public static T INSTANCE { get { return Singleton<T>.Instance; } }
public Singleton()
{
Instance = this as T;
}
}
}

View File

@@ -0,0 +1,227 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Platform;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace stonevox
{
public class SyncWindow : GameWindow, IGameWindow, INativeWindow, IDisposable
{
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public int targetFps;
public SyncWindow(int width, int height, GraphicsMode mode)
: base(width, height, mode)
{
}
public void Run_WithErrorCatching(int targetFps)
{
this.targetFps = targetFps;
base.Visible = true;
try
{
TargetUpdateFrequency = this.targetFps;
TargetRenderFrequency = this.targetFps;
FrameEventArgs updateArgs = new FrameEventArgs();
FrameEventArgs renderArgs = new FrameEventArgs();
OnLoad(EventArgs.Empty);
OnResize(EventArgs.Empty);
Stopwatch stopWatch = Stopwatch.StartNew();
int[] sleepTimes = new int[15];
for (int i = 0; i < sleepTimes.Length; i++) sleepTimes[i] = 1000 / this.targetFps;
int frameSleepTime = 0;
int frames = 0;
double previousElapsedSeconds = 0;
while (true)
{
frames++;
double totalElapsedSeconds = stopWatch.Elapsed.TotalSeconds;
double frameElapsedSeconds = totalElapsedSeconds - previousElapsedSeconds;
previousElapsedSeconds = totalElapsedSeconds;
if (totalElapsedSeconds >= 0.25)
{
double fps = frames / totalElapsedSeconds;
if (fps < this.targetFps)
{
int max = 0;
for (int i = 1; i < sleepTimes.Length; i++)
{
if (sleepTimes[i] > sleepTimes[max]) max = i;
}
sleepTimes[max] = System.Math.Max(0, sleepTimes[max] - 1);
}
else
{
int min = 0;
for (int i = 1; i < sleepTimes.Length; i++)
{
if (sleepTimes[i] < sleepTimes[min]) min = i;
}
sleepTimes[min] += 1;
}
stopWatch.Reset();
stopWatch.Start();
frames = 0;
previousElapsedSeconds = 0;
}
ProcessEvents();
updateArgs = new FrameEventArgs(frameElapsedSeconds);
this.OnUpdateFrame(updateArgs);
renderArgs = new FrameEventArgs(frameElapsedSeconds);
this.OnRenderFrame(renderArgs);
System.Threading.Thread.Sleep(sleepTimes[frameSleepTime]);
frameSleepTime = (frameSleepTime + 1) % sleepTimes.Length;
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("");
Console.WriteLine("Exception Caught - exiting main loop.");
string stacktrace = ex.ToString().ToLower();
stacktrace = stacktrace.Replace("c:\\users\\daniel\\documents\\github\\stonevox3d\\", "");
Console.WriteLine(stacktrace);
Console.WriteLine("");
SetForegroundWindow(GetConsoleWindow());
var result = MessageBox.Show("Would you like to copy crash info to the clipboard?", "StoneVox Encountered An Error", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
Clipboard.SetText(stacktrace);
}
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("");
Console.WriteLine("Crash info copied to clipboard.");
Console.WriteLine("");
Console.ForegroundColor = ConsoleColor.White;
}
finally
{
Debug.Print("Restoring priority.");
Thread.CurrentThread.Priority = ThreadPriority.Normal;
OnUnload(EventArgs.Empty);
if (Exists)
{
Context.Dispose();
}
while (this.Exists)
this.ProcessEvents();
}
}
public void Run_NoErrorCatching(int targetFps)
{
this.targetFps = targetFps;
base.Visible = true;
while (!IsExiting)
{
TargetUpdateFrequency = this.targetFps;
TargetRenderFrequency = this.targetFps;
FrameEventArgs updateArgs = new FrameEventArgs();
FrameEventArgs renderArgs = new FrameEventArgs();
OnLoad(EventArgs.Empty);
OnResize(EventArgs.Empty);
Stopwatch stopWatch = Stopwatch.StartNew();
int[] sleepTimes = new int[15];
for (int i = 0; i < sleepTimes.Length; i++) sleepTimes[i] = 1000 / this.targetFps;
int frameSleepTime = 0;
int frames = 0;
double previousElapsedSeconds = 0;
while (true)
{
frames++;
double totalElapsedSeconds = stopWatch.Elapsed.TotalSeconds;
double frameElapsedSeconds = totalElapsedSeconds - previousElapsedSeconds;
previousElapsedSeconds = totalElapsedSeconds;
if (totalElapsedSeconds >= 0.25)
{
double fps = frames / totalElapsedSeconds;
if (fps < this.targetFps)
{
int max = 0;
for (int i = 1; i < sleepTimes.Length; i++)
{
if (sleepTimes[i] > sleepTimes[max]) max = i;
}
sleepTimes[max] = System.Math.Max(0, sleepTimes[max] - 1);
}
else
{
int min = 0;
for (int i = 1; i < sleepTimes.Length; i++)
{
if (sleepTimes[i] < sleepTimes[min]) min = i;
}
sleepTimes[min] += 1;
}
stopWatch.Reset();
stopWatch.Start();
frames = 0;
previousElapsedSeconds = 0;
}
ProcessEvents();
updateArgs = new FrameEventArgs(frameElapsedSeconds);
this.OnUpdateFrame(updateArgs);
renderArgs = new FrameEventArgs(frameElapsedSeconds);
this.OnRenderFrame(renderArgs);
System.Threading.Thread.Sleep(sleepTimes[frameSleepTime]);
frameSleepTime = (frameSleepTime + 1) % sleepTimes.Length;
}
}
Thread.CurrentThread.Priority = ThreadPriority.Normal;
OnUnload(EventArgs.Empty);
if (Exists)
{
Context.Dispose();
}
while (this.Exists)
this.ProcessEvents();
}
}
}

View File

@@ -0,0 +1,157 @@
using System.Collections.Generic;
namespace stonevox
{
public class UndoRedo : Singleton<UndoRedo>
{
public LimitedSizeStack<UndoData> undos;
public LimitedSizeStack<UndoData> redos;
public UndoRedo(Input input)
: base()
{
undos = new LimitedSizeStack<UndoData>(100);
redos = new LimitedSizeStack<UndoData>(100);
input.AddHandler(new InputHandler()
{
Keydownhandler = (e) =>
{
var gui =Singleton<GUI>.INSTANCE;
if (e.Key ==OpenTK.Input.Key.Z)
Undo();
if (e.Key == OpenTK.Input.Key.Y)
Redo();
}
});
}
public void Undo()
{
UndoData undo;
var matrices = Singleton<QbManager>.INSTANCE.ActiveModel.matrices;
// skip all undodata that doesn't have a matrix
// (ie, matrix was removed or something)
while (true)
{
if (undos.Count == 0) return;
undo = undos.Pop();
if (!Singleton<QbManager>.INSTANCE.ActiveModel.matrices.Contains(undo.matrix))
continue;
if (undo.matrix == null)
continue;
else
break;
}
Singleton<BrushManager>.INSTANCE.brushes[undo.brush].RemoveVolume(undo.volume, undo.matrix, undo.data);
undo.matrix.Clean();
redos.Push(undo);
}
public void Redo()
{
UndoData redo;
var matrices = Singleton<QbManager>.INSTANCE.ActiveModel.matrices;
// skip all undodata that doesn't have a matrix
// (ie, matrix was removed or something)
while (true)
{
if (redos.Count == 0) return;
redo = redos.Pop();
if (!Singleton<QbManager>.INSTANCE.ActiveModel.matrices.Contains(redo.matrix))
continue;
if (redo.matrix == null)
continue;
else
break;
}
Singleton<BrushManager>.INSTANCE.brushes[redo.brush].AddVolume(redo.volume, redo.matrix, ref redo.color, redo.data);
redo.matrix.Clean();
undos.Push(redo);
}
public void AddUndo(VoxelBrushType type, QbMatrix matrix, VoxelVolume volume, Colort color, Dictionary<double, VoxelUndoData> data)
{
redos.Clear();
undos.Push(new UndoData(type, matrix, volume, color, data));
}
}
public struct UndoData
{
public QbMatrix matrix;
public Dictionary<double, VoxelUndoData> data;
public VoxelVolume volume;
public Colort color;
public VoxelBrushType brush;
public UndoData(VoxelBrushType type, QbMatrix matrix, VoxelVolume volume, Colort color, Dictionary<double, VoxelUndoData> data)
{
this.brush = type;
this.matrix = matrix;
this.volume = volume;
this.color = color;
this.data = new Dictionary<double, VoxelUndoData>();
foreach (var c in data)
{
this.data.Add(c.Key, c.Value);
}
}
}
public struct VoxelUndoData
{
public int colorindex;
public bool changed;
public byte alphamask;
public VoxelUndoData(bool changed)
{
this.colorindex = 0;
this.changed = changed;
this.alphamask = 0;
}
public VoxelUndoData(int colorindex, byte alphamask)
{
this.colorindex = colorindex;
this.changed = false;
this.alphamask = alphamask;
}
}
public class LimitedSizeStack<T> : LinkedList<T>
{
private readonly int _maxSize;
public LimitedSizeStack(int maxSize)
{
_maxSize = maxSize;
}
public void Push(T item)
{
this.AddFirst(item);
if (this.Count > _maxSize)
this.RemoveLast();
}
public T Pop()
{
var item = this.First.Value;
this.RemoveFirst();
return item;
}
}
}

View File

@@ -0,0 +1 @@


Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Some files were not shown because too many files have changed in this diff Show More