update
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
67
MinecraftUSkinEditor/Classes/StoneVOX/CSM.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
27
MinecraftUSkinEditor/Classes/StoneVOX/Mod/Alias.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
18
MinecraftUSkinEditor/Classes/StoneVOX/Mod/FileReference.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
88
MinecraftUSkinEditor/Classes/StoneVOX/Mod/Manifest.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
16
MinecraftUSkinEditor/Classes/StoneVOX/Mod/StoneHearthMod.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
BIN
MinecraftUSkinEditor/Classes/StoneVOX/OpenTK.dll
Normal file
90
MinecraftUSkinEditor/Classes/StoneVOX/Program.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
871
MinecraftUSkinEditor/Classes/StoneVOX/QFont/Builder.cs
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
30
MinecraftUSkinEditor/Classes/StoneVOX/QFont/ChangeLog.txt
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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...
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
14
MinecraftUSkinEditor/Classes/StoneVOX/QFont/Helper.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
971
MinecraftUSkinEditor/Classes/StoneVOX/QFont/JBitmap.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
136
MinecraftUSkinEditor/Classes/StoneVOX/QFont/JMath.cs
Normal 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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
463
MinecraftUSkinEditor/Classes/StoneVOX/QFont/JTexture.cs
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
140
MinecraftUSkinEditor/Classes/StoneVOX/QFont/KerningCalculator.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
156
MinecraftUSkinEditor/Classes/StoneVOX/QFont/ProjectionStack.cs
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
552
MinecraftUSkinEditor/Classes/StoneVOX/QFont/QBitmap.cs
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
1321
MinecraftUSkinEditor/Classes/StoneVOX/QFont/QFont.cs
Normal file
192
MinecraftUSkinEditor/Classes/StoneVOX/QFont/QFontData.cs
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
36
MinecraftUSkinEditor/Classes/StoneVOX/QFont/QFontGlyph.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
316
MinecraftUSkinEditor/Classes/StoneVOX/QFont/TextNodeList.cs
Normal 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()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
95
MinecraftUSkinEditor/Classes/StoneVOX/QFont/TexturePage.cs
Normal 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
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
125
MinecraftUSkinEditor/Classes/StoneVOX/brush/BrushAdd.cs
Normal 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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
202
MinecraftUSkinEditor/Classes/StoneVOX/brush/BrushRecolor.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
203
MinecraftUSkinEditor/Classes/StoneVOX/brush/BrushRemove.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
25
MinecraftUSkinEditor/Classes/StoneVOX/brush/IVoxelBrush.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace stonevox
|
||||
{
|
||||
public enum VoxelBrushType
|
||||
{
|
||||
Add,
|
||||
Remove,
|
||||
Recolor,
|
||||
MatrixSelect,
|
||||
ColorSelect,
|
||||
Select
|
||||
//Area_Fill,
|
||||
//Area_Remove,
|
||||
//Area_Color,
|
||||
}
|
||||
}
|
||||
111
MinecraftUSkinEditor/Classes/StoneVOX/client/Broadcaster.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
155
MinecraftUSkinEditor/Classes/StoneVOX/client/BrushManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
396
MinecraftUSkinEditor/Classes/StoneVOX/client/Camera.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
MinecraftUSkinEditor/Classes/StoneVOX/client/Colort.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
497
MinecraftUSkinEditor/Classes/StoneVOX/client/ConsoleCommands.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
114
MinecraftUSkinEditor/Classes/StoneVOX/client/Floor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
560
MinecraftUSkinEditor/Classes/StoneVOX/client/GLWindow.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
490
MinecraftUSkinEditor/Classes/StoneVOX/client/GUI.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
182
MinecraftUSkinEditor/Classes/StoneVOX/client/Input.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
122
MinecraftUSkinEditor/Classes/StoneVOX/client/QbManger.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
847
MinecraftUSkinEditor/Classes/StoneVOX/client/Raycaster.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
124
MinecraftUSkinEditor/Classes/StoneVOX/client/Raycaster2.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
270
MinecraftUSkinEditor/Classes/StoneVOX/client/Selection.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
MinecraftUSkinEditor/Classes/StoneVOX/client/Singleton.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
227
MinecraftUSkinEditor/Classes/StoneVOX/client/SyncWindow.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
157
MinecraftUSkinEditor/Classes/StoneVOX/client/UndoRedo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/fonts/Bigfish.ttf
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/add.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/add.psd
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/addmatrix.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/addmatrix.psd
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/arrowSide.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/arrowSide.psd
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/arrowUp.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/brush.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/button.psd
Normal file
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/camera.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/camera.psd
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/cursor_add.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
MinecraftUSkinEditor/Classes/StoneVOX/data/images/cursor_add.psd
Normal file
|
After Width: | Height: | Size: 3.8 KiB |