Upgrade to MSI Installation and MSI based update

This commit is contained in:
Felix Miller
2022-02-17 00:05:53 -05:00
parent 3b3c9b385d
commit 0d114e471b
322 changed files with 489 additions and 33135 deletions
+97
View File
@@ -0,0 +1,97 @@
using System;
using System.Net;
using System.Windows.Forms;
namespace PckStudio.Classes
{
class Network
{
public static string Version = "6.4";
public static bool Beta = true;
public static bool NeedsUpdate = false;
public static string MainURL = "https://www.pckstudio.xyz/";
public static string BackURL = "http://phoenixarc.ddns.net/";
static string UpdateURL = "studio/PCK/api/updatePCKStudio.txt";
static string BetaUpdateURL = "studio/PCK/api/updatePCKStudioB.txt";
public static void CheckUpdate()
{
WebClient wc = new WebClient();
string docuDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
try
{
Console.WriteLine(MainURL + UpdateURL);
switch (Beta)
{
case false:
if (float.Parse(Version) < float.Parse(wc.DownloadString(MainURL + UpdateURL)))
{
if (MessageBox.Show("An update is available! do you want to update?\nYour Version:" + Version + "\nAvailable version:" + wc.DownloadString(MainURL + UpdateURL), "Update Available", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
Classes.Update.UpdateProgram(Beta);
}
else
{
NeedsUpdate = true;
}
}
break;
case true:
if (float.Parse(Version) < float.Parse(wc.DownloadString(MainURL + BetaUpdateURL)))
{
if (MessageBox.Show("An update is available! do you want to update?\nYour Version:" + Version + "\nAvailable version:" + wc.DownloadString(MainURL + BetaUpdateURL), "Update Available", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
Classes.Update.UpdateProgram(Beta);
}
else
{
NeedsUpdate = true;
}
}
break;
}
}
catch
{
try
{
switch (Beta)
{
case false:
if (float.Parse(Version) < float.Parse(wc.DownloadString(BackURL + UpdateURL)))
{
if (MessageBox.Show("An update is available! do you want to update?\nYour Version:" + Version + "\nAvailable version:" + wc.DownloadString(BackURL + UpdateURL), "Update Available", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
Classes.Update.UpdateProgram(Beta);
}
else
{
NeedsUpdate = true;
}
}
break;
case true:
if (float.Parse(Version) < float.Parse(wc.DownloadString(BackURL + BetaUpdateURL)))
{
if (MessageBox.Show("An update is available! do you want to update?\nYour Version:" + Version + "\nAvailable version:" + wc.DownloadString(BackURL + UpdateURL), "Update Available", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
Classes.Update.UpdateProgram(Beta);
}
else
{
NeedsUpdate = true;
}
}
break;
}
}
catch(Exception ex)
{
MessageBox.Show("Server unavailabe", "Cannot connect to the server!");
}
}
}
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ namespace PckStudio
{
public static string baseurl = "https://www.pckstudio.xyz/studio/PCK/api/";
public static string backurl = "https://raw.githubusercontent.com/PhoenixARC/pckstudio.tk/main/studio/PCK/api/";
public static string Appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
public static string Appdata = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "/PCK Studio/";
public static bool IsDev = false;
public static FormMain formMain;
/// <summary>
@@ -1,67 +0,0 @@
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;
}
}
@@ -1,27 +0,0 @@
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;
}
}
@@ -1,18 +0,0 @@
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;
}
}
}
@@ -1,88 +0,0 @@
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");
}
}
}
@@ -1,16 +0,0 @@
using Ionic.Zip;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace stonevox.Mod
{
public class StoneHearthMod
{
public string path { get; }
private ZipFile zip;
}
}
Binary file not shown.
@@ -1,90 +0,0 @@
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();
}
}
}
@@ -1,871 +0,0 @@
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;
}
}
}
@@ -1,30 +0,0 @@
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
@@ -1,83 +0,0 @@
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;
}
}
}
@@ -1,14 +0,0 @@
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;
}
}
@@ -1,112 +0,0 @@
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);
}
}
}
@@ -1,20 +0,0 @@
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;
}
}
}
@@ -1,50 +0,0 @@
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;
}
}
@@ -1,46 +0,0 @@
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...
}
}
}
@@ -1,14 +0,0 @@
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;
}
}
}
@@ -1,971 +0,0 @@
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();
}
}
}
}
@@ -1,136 +0,0 @@
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;
}
}
}
@@ -1,463 +0,0 @@
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;
}
}
}
@@ -1,39 +0,0 @@
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();
}
}
}
}
@@ -1,140 +0,0 @@
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;
}
}
}
@@ -1,156 +0,0 @@
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;
}
}
}
@@ -1,552 +0,0 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace QuickFont
{
public class QBitmap
{
public Bitmap bitmap;
public BitmapData bitmapData;
public QBitmap(string filePath)
{
LockBits(new Bitmap(filePath));
}
public QBitmap(Bitmap bitmap)
{
LockBits(bitmap);
}
private void LockBits(Bitmap bitmap)
{
this.bitmap = bitmap;
bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
}
public void Clear32(byte r, byte g, byte b, byte a)
{
unsafe
{
byte* sourcePtr = (byte*)(bitmapData.Scan0);
for (int i = 0; i < bitmapData.Height; i++)
{
for (int j = 0; j < bitmapData.Width; j++)
{
*(sourcePtr) = b;
*(sourcePtr + 1) = g;
*(sourcePtr + 2) = r;
*(sourcePtr + 3) = a;
sourcePtr += 4;
}
sourcePtr += bitmapData.Stride - bitmapData.Width * 4; //move to the end of the line (past unused space)
}
}
}
/// <summary>
/// Returns try if the given pixel is empty (i.e. black)
/// </summary>
public static unsafe bool EmptyPixel(BitmapData bitmapData, int px, int py)
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 3;
return (*addr == 0 && *(addr + 1) == 0 && *(addr + 2) == 0);
}
/// <summary>
/// Returns try if the given pixel is empty (i.e. alpha is zero)
/// </summary>
public static unsafe bool EmptyAlphaPixel(BitmapData bitmapData, int px, int py, byte alphaEmptyPixelTolerance)
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4;
return (*(addr + 3) <= alphaEmptyPixelTolerance);
}
/// <summary>
/// Blits a block of a bitmap data from source to destination, using the luminance of the source to determine the
/// alpha of the target. Source must be 24-bit, target must be 32-bit.
/// </summary>
public static void BlitMask(BitmapData source, BitmapData target, int srcPx, int srcPy, int srcW, int srcH, int px, int py)
{
int sourceBpp = 3;
int targetBpp = 4;
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(px, 0);
targetEndX = Math.Min(px + srcW, target.Width);
targetStartY = Math.Max(py, 0);
targetEndY = Math.Min(py + srcH, target.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if (copyW < 0)
{
return;
}
if (copyH < 0)
{
return;
}
int sourceStartX = srcPx + targetStartX - px;
int sourceStartY = srcPy + targetStartY - py;
unsafe
{
byte* sourcePtr = (byte*)(source.Scan0);
byte* targetPtr = (byte*)(target.Scan0);
byte* targetY = targetPtr + targetStartY * target.Stride;
byte* sourceY = sourcePtr + sourceStartY * source.Stride;
for (int y = 0; y < copyH; y++, targetY += target.Stride, sourceY += source.Stride)
{
byte* targetOffset = targetY + targetStartX * targetBpp;
byte* sourceOffset = sourceY + sourceStartX * sourceBpp;
for (int x = 0; x < copyW; x++, targetOffset += targetBpp, sourceOffset += sourceBpp)
{
int lume = *(sourceOffset) + *(sourceOffset + 1) + *(sourceOffset + 2);
lume /= 3;
if (lume > 255)
lume = 255;
*(targetOffset + 3) = (byte)lume;
}
}
}
}
/// <summary>
/// Blits from source to target. Both source and target must be 32-bit
/// </summary>
public static void Blit(BitmapData source, BitmapData target, Rectangle sourceRect, int px, int py)
{
Blit(source, target, sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height, px, py);
}
/// <summary>
/// Blits from source to target. Both source and target must be 32-bit
/// </summary>
public static void Blit(BitmapData source, BitmapData target, int srcPx, int srcPy, int srcW, int srcH, int destX, int destY)
{
int bpp = 4;
int targetStartX, targetEndX;
int targetStartY, targetEndY;
int copyW, copyH;
targetStartX = Math.Max(destX, 0);
targetEndX = Math.Min(destX + srcW, target.Width);
targetStartY = Math.Max(destY, 0);
targetEndY = Math.Min(destY + srcH, target.Height);
copyW = targetEndX - targetStartX;
copyH = targetEndY - targetStartY;
if (copyW < 0)
{
return;
}
if (copyH < 0)
{
return;
}
int sourceStartX = srcPx + targetStartX - destX;
int sourceStartY = srcPy + targetStartY - destY;
unsafe
{
byte* sourcePtr = (byte*)(source.Scan0);
byte* targetPtr = (byte*)(target.Scan0);
byte* targetY = targetPtr + targetStartY * target.Stride;
byte* sourceY = sourcePtr + sourceStartY * source.Stride;
for (int y = 0; y < copyH; y++, targetY += target.Stride, sourceY += source.Stride)
{
byte* targetOffset = targetY + targetStartX * bpp;
byte* sourceOffset = sourceY + sourceStartX * bpp;
for (int x = 0; x < copyW*bpp; x++, targetOffset ++, sourceOffset ++)
*(targetOffset) = *(sourceOffset);
}
}
}
public unsafe void PutPixel32(int px, int py, byte r, byte g, byte b, byte a)
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4;
*addr = b;
*(addr + 1) = g;
*(addr + 2) = r;
*(addr + 3) = a;
}
public unsafe void GetPixel32(int px, int py, ref byte r, ref byte g, ref byte b, ref byte a)
{
byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4;
b = *addr;
g = *(addr + 1);
r = *(addr + 2);
a = *(addr + 3);
}
public unsafe void PutAlpha32(int px, int py, byte a)
{
*((byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4 + 3) = a;
}
public unsafe void GetAlpha32(int px, int py, ref byte a)
{
a = *((byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4 + 3);
}
public void DownScale32(int newWidth, int newHeight)
{
QBitmap newBitmap = new QBitmap(new Bitmap(newWidth, newHeight, bitmap.PixelFormat));
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new Exception("DownsScale32 only works on 32 bit images");
float xscale = (float)bitmapData.Width / newWidth;
float yscale = (float)bitmapData.Height / newHeight;
byte r = 0, g = 0, b = 0, a = 0;
float summedR = 0f;
float summedG = 0f;
float summedB = 0f;
float summedA = 0f;
int left, right, top, bottom; //the area of old pixels covered by the new bitmap
float targetStartX, targetEndX;
float targetStartY, targetEndY;
float leftF, rightF, topF, bottomF; //edges of new pixel in old pixel coords
float weight;
float weightScale = xscale * yscale;
float totalColourWeight = 0f;
for (int m = 0; m < newHeight; m++)
{
for (int n = 0; n < newWidth; n++)
{
leftF = n * xscale;
rightF = (n + 1) * xscale;
topF = m * yscale;
bottomF = (m + 1) * yscale;
left = (int)leftF;
right = (int)rightF;
top = (int)topF;
bottom = (int)bottomF;
if (left < 0) left = 0;
if (top < 0) top = 0;
if (right >= bitmapData.Width) right = bitmapData.Width - 1;
if (bottom >= bitmapData.Height) bottom = bitmapData.Height - 1;
summedR = 0f;
summedG = 0f;
summedB = 0f;
summedA = 0f;
totalColourWeight = 0f;
for (int j = top; j <= bottom; j++)
{
for (int i = left; i <= right; i++)
{
targetStartX = Math.Max(leftF, i);
targetEndX = Math.Min(rightF, i + 1);
targetStartY = Math.Max(topF, j);
targetEndY = Math.Min(bottomF, j + 1);
weight = (targetEndX - targetStartX) * (targetEndY - targetStartY);
GetPixel32(i, j, ref r, ref g, ref b, ref a);
summedA += weight * a;
if (a != 0)
{
summedR += weight * r;
summedG += weight * g;
summedB += weight * b;
totalColourWeight += weight;
}
}
}
summedR /= totalColourWeight;
summedG /= totalColourWeight;
summedB /= totalColourWeight;
summedA /= weightScale;
if (summedR < 0) summedR = 0f;
if (summedG < 0) summedG = 0f;
if (summedB < 0) summedB = 0f;
if (summedA < 0) summedA = 0f;
if (summedR >= 256) summedR = 255;
if (summedG >= 256) summedG = 255;
if (summedB >= 256) summedB = 255;
if (summedA >= 256) summedA = 255;
newBitmap.PutPixel32(n, m, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA);
}
}
this.Free();
this.bitmap = newBitmap.bitmap;
this.bitmapData = newBitmap.bitmapData;
}
/// <summary>
/// Sets colour without touching alpha values
/// </summary>
/// <param name="r"></param>
/// <param name="g"></param>
/// <param name="b"></param>
public void Colour32(byte r, byte g, byte b)
{
unsafe
{
byte* addr;
for (int i = 0; i < bitmapData.Width; i++)
{
for (int j = 0; j < bitmapData.Height; j++)
{
addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * j + i * 4;
*addr = b;
*(addr + 1) = g;
*(addr + 2) = r;
}
}
}
}
/*
public void Blur(int radius, int passes)
{
QBitmap tmp = new QBitmap(new Bitmap(this.bitmap.Width, this.bitmap.Height, bitmap.PixelFormat));
byte r=0,g=0,b=0,a=0;
int summedR, summedG, summedB, summedA;
int weight = 0;
int xpos, ypos, x, y, kx, ky;
for (int pass = 0; pass < passes; pass++)
{
//horizontal pass
for (y = 0; y < bitmap.Height; y++)
{
for (x = 0; x < bitmap.Width; x++)
{
summedR = summedG = summedB = summedA = weight = 0;
for (kx = -radius; kx <= radius; kx++)
{
xpos = x + kx;
if (xpos >= 0 && xpos < bitmap.Width)
{
GetPixel32(xpos, y, ref r, ref g, ref b, ref a);
summedR += r;
summedG += g;
summedB += b;
summedA += a;
weight++;
}
}
summedR /= weight;
summedG /= weight;
summedB /= weight;
summedA /= weight;
tmp.PutPixel32(x, y, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA);
}
}
//vertical pass
for (x = 0; x < bitmap.Width; ++x)
{
for (y = 0; y < bitmap.Height; ++y)
{
summedR = summedG = summedB = summedA = weight = 0;
for (ky = -radius; ky <= radius; ky++)
{
ypos = y + ky;
if (ypos >= 0 && ypos < bitmap.Height)
{
tmp.GetPixel32(x, ypos, ref r, ref g, ref b, ref a);
summedR += r;
summedG += g;
summedB += b;
summedA += a;
weight++;
}
}
summedR /= weight;
summedG /= weight;
summedB /= weight;
summedA /= weight;
PutPixel32(x, y, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA);
}
}
}
tmp.Free();
}*/
public void BlurAlpha(int radius, int passes)
{
QBitmap tmp = new QBitmap(new Bitmap(this.bitmap.Width, this.bitmap.Height, bitmap.PixelFormat));
byte a = 0;
int summedA;
int weight = 0;
int xpos, ypos, x, y, kx, ky;
int width = bitmap.Width;
int height = bitmap.Height;
for (int pass = 0; pass < passes; pass++)
{
//horizontal pass
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
summedA = weight = 0;
for (kx = -radius; kx <= radius; kx++)
{
xpos = x + kx;
if (xpos >= 0 && xpos < width)
{
GetAlpha32(xpos, y, ref a);
summedA += a;
weight++;
}
}
summedA /= weight;
tmp.PutAlpha32(x, y, (byte)summedA);
}
}
//vertical pass
for (x = 0; x <width; ++x)
{
for (y = 0; y < height; ++y)
{
summedA = weight = 0;
for (ky = -radius; ky <= radius; ky++)
{
ypos = y + ky;
if (ypos >= 0 && ypos < height)
{
tmp.GetAlpha32(x, ypos,ref a);
summedA += a;
weight++;
}
}
summedA /= weight;
PutAlpha32(x, y, (byte)summedA);
}
}
}
tmp.Free();
}
public void Free()
{
bitmap.UnlockBits(bitmapData);
bitmap.Dispose();
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,192 +0,0 @@
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();
}
}
}
@@ -1,36 +0,0 @@
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;
}
}
}
@@ -1,257 +0,0 @@
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;
}
}
}
@@ -1,316 +0,0 @@
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()
{
}
}
}
}
@@ -1,95 +0,0 @@
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
}
}
@@ -1,125 +0,0 @@
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()
{
}
}
}
@@ -1,109 +0,0 @@
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();
}
}
}
@@ -1,127 +0,0 @@
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();
}
}
}
@@ -1,202 +0,0 @@
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;
}
}
}
@@ -1,203 +0,0 @@
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;
}
}
}
@@ -1,79 +0,0 @@
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)
{
}
}
}
@@ -1,25 +0,0 @@
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);
}
}
@@ -1,15 +0,0 @@
namespace stonevox
{
public enum VoxelBrushType
{
Add,
Remove,
Recolor,
MatrixSelect,
ColorSelect,
Select
//Area_Fill,
//Area_Remove,
//Area_Color,
}
}
@@ -1,111 +0,0 @@
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);
}
}
}
}
@@ -1,155 +0,0 @@
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);
}
}
}
@@ -1,396 +0,0 @@
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;
}
}
}
@@ -1,28 +0,0 @@
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);
}
}
}
@@ -1,497 +0,0 @@
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;
}
}
}
@@ -1,87 +0,0 @@
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);
});
}
}
}
}
@@ -1,114 +0,0 @@
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;
}
}
}
@@ -1,560 +0,0 @@
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);
}
}
}
@@ -1,490 +0,0 @@
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);
}
}
}
@@ -1,182 +0,0 @@
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;
}
}
@@ -1,122 +0,0 @@
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;
}
}
}
@@ -1,847 +0,0 @@
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);
}
}
}
@@ -1,124 +0,0 @@
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
}
}
}
@@ -1,270 +0,0 @@
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);
}
}
}
}
@@ -1,13 +0,0 @@
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;
}
}
}
@@ -1,227 +0,0 @@
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();
}
}
}
@@ -1,157 +0,0 @@
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;
}
}
}
@@ -1 +0,0 @@

@@ -1 +0,0 @@
␍␍␍©2009 ßoodfonts, designed by Felix Braden.␍␍You are allowed to use BIGFISH for every kind of publication (electronic/print), it doesn't matter if its commercial or not. You can copy and give it away to your friends as long as the font-info-file is included with the postscript or true type data. ␍␍It may not be remade or redistributed in any way: it may not be made available for download, it may not be sold, it may not be included in any kind of software collection without the express written permission of the designer.␍␍Felix Braden shall, in no event, be liable for any damages arising out of the use or inability to use this font.␍␍␍␍www.ßoodfonts.com␍[email protected]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

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