mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-07-19 12:34:53 -04:00
Removed NioTSO client and server
- NioTSO client isn't needed because we're using RayLib - Added FreeSO's API server to handle most backend operations
This commit is contained in:
parent
f12ba1502b
commit
22191ce648
591 changed files with 53264 additions and 3362 deletions
88
server/tso.common/Rendering/CachableTexture2D.cs
Executable file
88
server/tso.common/Rendering/CachableTexture2D.cs
Executable file
|
@ -0,0 +1,88 @@
|
|||
using FSO.Common.Utils;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace FSO.Common.Rendering
|
||||
{
|
||||
public class CachableTexture2D : Texture2D, ITimedCachable
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new texture of the given size
|
||||
/// </summary>
|
||||
/// <param name="graphicsDevice"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
public CachableTexture2D(GraphicsDevice graphicsDevice, int width, int height)
|
||||
: base(graphicsDevice, width, height)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new texture of a given size with a surface format and optional mipmaps
|
||||
/// </summary>
|
||||
/// <param name="graphicsDevice"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="mipmap"></param>
|
||||
/// <param name="format"></param>
|
||||
public CachableTexture2D(GraphicsDevice graphicsDevice, int width, int height, bool mipmap, SurfaceFormat format)
|
||||
: base(graphicsDevice, width, height, mipmap, format)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new texture array of a given size with a surface format and optional mipmaps.
|
||||
/// Throws ArgumentException if the current GraphicsDevice can't work with texture arrays
|
||||
/// </summary>
|
||||
/// <param name="graphicsDevice"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="mipmap"></param>
|
||||
/// <param name="format"></param>
|
||||
/// <param name="arraySize"></param>
|
||||
public CachableTexture2D(GraphicsDevice graphicsDevice, int width, int height, bool mipmap, SurfaceFormat format, int arraySize)
|
||||
: base(graphicsDevice, width, height, mipmap, format, arraySize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private bool WasReferenced = true;
|
||||
public bool BeingDisposed = false;
|
||||
private bool Resurrect = true;
|
||||
public object Parent; //set if you want a parent object to be tied to this object (don't kill parent til we die)
|
||||
~CachableTexture2D() {
|
||||
if (!IsDisposed && !BeingDisposed)
|
||||
{
|
||||
//if we are disposed, there's no need to do anything.
|
||||
if (WasReferenced)
|
||||
{
|
||||
TimedReferenceController.KeepAlive(this, KeepAliveType.DEREFERENCED);
|
||||
WasReferenced = false;
|
||||
GC.ReRegisterForFinalize(this);
|
||||
Resurrect = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
BeingDisposed = true;
|
||||
GameThread.NextUpdate(x => this.Dispose());
|
||||
GC.ReRegisterForFinalize(this);
|
||||
Resurrect = true; //one more final
|
||||
}
|
||||
}
|
||||
else { Resurrect = false; }
|
||||
}
|
||||
|
||||
public void Rereferenced(bool saved)
|
||||
{
|
||||
WasReferenced = saved;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing) base.Dispose(disposing);
|
||||
else if (Resurrect)
|
||||
{
|
||||
//in finalizer
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
103
server/tso.common/Rendering/Emoji/EmojiCache.cs
Executable file
103
server/tso.common/Rendering/Emoji/EmojiCache.cs
Executable file
|
@ -0,0 +1,103 @@
|
|||
using FSO.Common.Utils;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace FSO.Common.Rendering.Emoji
|
||||
{
|
||||
public class EmojiCache
|
||||
{
|
||||
public string Source = "https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/";
|
||||
public int DefaultRes = 24;
|
||||
public int Width = 32;
|
||||
|
||||
public int NextIndex = 0;
|
||||
public List<string> Emojis = new List<string>();
|
||||
public Dictionary<string, int> EmojiToIndex = new Dictionary<string, int>();
|
||||
public RenderTarget2D EmojiTex;
|
||||
public SpriteBatch EmojiBatch;
|
||||
public HashSet<int> IncompleteSpaces = new HashSet<int>();
|
||||
public HashSet<int> ErrorSpaces = new HashSet<int>();
|
||||
private GraphicsDevice GD;
|
||||
private bool needClear = true;
|
||||
|
||||
public EmojiCache(GraphicsDevice gd)
|
||||
{
|
||||
GD = gd;
|
||||
EmojiBatch = new SpriteBatch(gd);
|
||||
|
||||
EmojiTex = new RenderTarget2D(gd, Width * DefaultRes, Width * DefaultRes, false, SurfaceFormat.Color, DepthFormat.None, 0, RenderTargetUsage.PreserveContents);
|
||||
ServicePointManager.Expect100Continue = true;
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
|
||||
}
|
||||
|
||||
public void ExpandIfNeeded()
|
||||
{
|
||||
//todo
|
||||
}
|
||||
|
||||
public Rectangle GetEmoji(string emojiID) {
|
||||
|
||||
int index;
|
||||
if (EmojiToIndex.TryGetValue(emojiID, out index))
|
||||
{
|
||||
return RectForIndex(index);
|
||||
} else
|
||||
{
|
||||
index = NextIndex++;
|
||||
ExpandIfNeeded();
|
||||
lock (IncompleteSpaces) IncompleteSpaces.Add(index);
|
||||
var client = new WebClient();
|
||||
client.DownloadDataCompleted += (object sender, DownloadDataCompletedEventArgs e) =>
|
||||
{
|
||||
if (e.Cancelled || e.Error != null || e.Result == null)
|
||||
{
|
||||
lock (ErrorSpaces) ErrorSpaces.Add(index);
|
||||
} else
|
||||
{
|
||||
GameThread.NextUpdate(x =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var mem = new MemoryStream(e.Result))
|
||||
{
|
||||
var tex = Texture2D.FromStream(GD, mem);
|
||||
|
||||
var decimated = TextureUtils.Decimate(tex, GD, 72 / DefaultRes, true);
|
||||
//blit this into our emoji buffer
|
||||
GD.SetRenderTarget(EmojiTex);
|
||||
if (needClear)
|
||||
{
|
||||
GD.Clear(Color.TransparentBlack);
|
||||
needClear = false;
|
||||
}
|
||||
EmojiBatch.Begin(blendState: BlendState.NonPremultiplied, sortMode: SpriteSortMode.Immediate);
|
||||
EmojiBatch.Draw(decimated, RectForIndex(index), Color.White);
|
||||
EmojiBatch.End();
|
||||
GD.SetRenderTarget(null);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
lock (ErrorSpaces) ErrorSpaces.Add(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
lock (IncompleteSpaces) IncompleteSpaces.Remove(index);
|
||||
};
|
||||
client.DownloadDataAsync(new Uri((emojiID[0] == '!')?(emojiID.Substring(1)):(Source + emojiID + ".png")));
|
||||
Emojis.Add(emojiID);
|
||||
EmojiToIndex[emojiID] = index;
|
||||
return RectForIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
private Rectangle RectForIndex(int index)
|
||||
{
|
||||
return new Rectangle((index % Width) * DefaultRes, (index / Width) * DefaultRes, DefaultRes, DefaultRes);
|
||||
}
|
||||
}
|
||||
}
|
95
server/tso.common/Rendering/Emoji/EmojiDictionary.cs
Executable file
95
server/tso.common/Rendering/Emoji/EmojiDictionary.cs
Executable file
|
@ -0,0 +1,95 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace FSO.Common.Rendering.Emoji
|
||||
{
|
||||
public class EmojiDictionary
|
||||
{
|
||||
public Dictionary<string, string> NameToEmojis = new Dictionary<string, string>();
|
||||
public Dictionary<string, List<string>> KeywordToCandidates = new Dictionary<string, List<string>>();
|
||||
public Dictionary<string, List<string>> CandidatesToKeywords = new Dictionary<string, List<string>>();
|
||||
|
||||
public EmojiDictionary()
|
||||
{
|
||||
JObject emojis;
|
||||
using (var emojiDict = new StreamReader(
|
||||
new FileStream(Path.Combine(FSOEnvironment.ContentDir, "UI/emojis.json"), FileMode.Open, FileAccess.Read, FileShare.Read)))
|
||||
{
|
||||
emojis = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(emojiDict.ReadToEnd());
|
||||
}
|
||||
|
||||
foreach (var token in emojis)
|
||||
{
|
||||
var charValue = token.Value.Value<string>("char");
|
||||
var twemojiID = ToCodePoint(charValue);
|
||||
NameToEmojis[token.Key] = twemojiID;
|
||||
var keys = token.Value.Value<JArray>("keywords");
|
||||
foreach (var key in keys)
|
||||
AddKeyword(key.Value<string>(), token.Key);
|
||||
}
|
||||
|
||||
using (var emojiDict = new StreamReader(
|
||||
new FileStream(Path.Combine(FSOEnvironment.ContentDir, "UI/customemojis.json"), FileMode.Open, FileAccess.Read, FileShare.Read)))
|
||||
{
|
||||
emojis = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(emojiDict.ReadToEnd());
|
||||
}
|
||||
|
||||
foreach (var token in emojis)
|
||||
{
|
||||
NameToEmojis[token.Key] = "!" + token.Value.Value<string>("url");
|
||||
var keys = token.Value.Value<JArray>("keywords");
|
||||
foreach (var key in keys)
|
||||
AddKeyword(key.Value<string>(), token.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddKeyword(string keyword, string candidate)
|
||||
{
|
||||
List<string> cand;
|
||||
if (!KeywordToCandidates.TryGetValue(keyword, out cand))
|
||||
{
|
||||
cand = new List<string>();
|
||||
KeywordToCandidates[keyword] = cand;
|
||||
}
|
||||
cand.Add(candidate);
|
||||
|
||||
if (!CandidatesToKeywords.TryGetValue(candidate, out cand))
|
||||
{
|
||||
cand = new List<string>();
|
||||
CandidatesToKeywords[candidate] = cand;
|
||||
}
|
||||
cand.Add(keyword);
|
||||
}
|
||||
|
||||
public string ToCodePoint(string str)
|
||||
{
|
||||
var cs = str.ToCharArray();
|
||||
var i = 0;
|
||||
var c = 0;
|
||||
var p = 0;
|
||||
var r = new List<string>();
|
||||
var zeroWidth = str.Any(x => x == '\x200D');
|
||||
while (i < cs.Length)
|
||||
{
|
||||
c = cs[i++];
|
||||
if (c == 0xfe0f && !zeroWidth) continue; //"as image", just ignore this
|
||||
if (p > 0)
|
||||
{
|
||||
r.Add((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).ToString("x"));
|
||||
p = 0;
|
||||
}
|
||||
else if (0xD800 <= c && c <= 0xDBFF)
|
||||
{
|
||||
p = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.Add(c.ToString("x"));
|
||||
}
|
||||
}
|
||||
return string.Join("-", r);
|
||||
}
|
||||
}
|
||||
}
|
232
server/tso.common/Rendering/Emoji/EmojiProvider.cs
Executable file
232
server/tso.common/Rendering/Emoji/EmojiProvider.cs
Executable file
|
@ -0,0 +1,232 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FSO.Common.Rendering.Emoji
|
||||
{
|
||||
public class EmojiProvider
|
||||
{
|
||||
public EmojiDictionary Dict;
|
||||
public EmojiCache Cache;
|
||||
public Random DictRand = new Random();
|
||||
|
||||
public Dictionary<string, string> TranslateShortcuts = new Dictionary<string, string>()
|
||||
{
|
||||
{ "i", "eye" },
|
||||
{ "you", "point_right" },
|
||||
{ "your", "point_right" },
|
||||
{ "you're", "point_right" },
|
||||
{ "me", "relieved: :point_left" },
|
||||
{ "i'm", "relieved: :point_left" },
|
||||
{ "us", "couple" },
|
||||
{ "we", "couple" },
|
||||
{ "our", "couple" },
|
||||
{ "what", "woman_shrugging" },
|
||||
{ "be", "b" },
|
||||
{ "to", "two" },
|
||||
{ "and", "symbols" },
|
||||
{ "that", "point_up" },
|
||||
{ "for", "four" },
|
||||
{ "not", "exclamation" },
|
||||
{ "this", "point_up" },
|
||||
{ "but", "exclamation" },
|
||||
{ "his", "man" },
|
||||
{ "her", "woman" },
|
||||
{ "him", "man" },
|
||||
{ "he", "man" },
|
||||
{ "she", "woman" },
|
||||
{ "from", "postbox" },
|
||||
{ "they", "family_man_woman_girl_boy" },
|
||||
{ "them", "family_man_woman_girl_boy" },
|
||||
{ "or", "thinking" },
|
||||
{ "an", "a" },
|
||||
{ "will", "thinking" },
|
||||
{ "my", "relieved: :point_left" },
|
||||
{ "all", "rainbow_flag" },
|
||||
{ "would", "tree" },
|
||||
{ "so", "woman_shrugging" },
|
||||
{ "out", "outbox_tray" },
|
||||
{ "if", "thinking" },
|
||||
{ "about", "arrows_counterclockwise" },
|
||||
{ "who", "thinking: :family_man_woman_girl_boy" },
|
||||
{ "get", "gift" },
|
||||
{ "which", "woman_shrugging" },
|
||||
{ "go", "door" },
|
||||
{ "when", "watch" },
|
||||
{ "make", "toolbox" },
|
||||
{ "know", "brain" },
|
||||
{ "take", "takeout_box" },
|
||||
{ "into", "arrow_heading_down" },
|
||||
{ "year", "calendar" },
|
||||
{ "because", "woman_shrugging" },
|
||||
{ "hmm", "thinking" },
|
||||
{ "yo", "wave" },
|
||||
{ "hey", "wave" },
|
||||
{ "sup", "wave" },
|
||||
};
|
||||
|
||||
public EmojiProvider(GraphicsDevice gd)
|
||||
{
|
||||
Dict = new EmojiDictionary();
|
||||
Cache = new EmojiCache(gd);
|
||||
}
|
||||
|
||||
public string EmojiFromName(string name)
|
||||
{
|
||||
string result;
|
||||
if (Dict.NameToEmojis.TryGetValue(name, out result))
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
private string StripPunctuation(string word, out string punctuation)
|
||||
{
|
||||
for (int i=word.Length-1; i>=0; i--)
|
||||
{
|
||||
if (!char.IsPunctuation(word[i]))
|
||||
{
|
||||
punctuation = word.Substring(i + 1);
|
||||
return word.Substring(0, i + 1);
|
||||
}
|
||||
}
|
||||
punctuation = "";
|
||||
return word;
|
||||
}
|
||||
|
||||
public string SearchForWordHit(string word)
|
||||
{
|
||||
string direct;
|
||||
if (TranslateShortcuts.TryGetValue(word, out direct))
|
||||
{
|
||||
return ":" + direct + ":";
|
||||
}
|
||||
|
||||
if (Dict.NameToEmojis.TryGetValue(word, out direct))
|
||||
{
|
||||
return ":" + word + ":";
|
||||
}
|
||||
|
||||
List<string> options;
|
||||
if (Dict.KeywordToCandidates.TryGetValue(word, out options))
|
||||
{
|
||||
return ":" + options[DictRand.Next(options.Count)] + ":";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string TranslateWordToEmoji(string word)
|
||||
{
|
||||
string punctuation;
|
||||
var lower = StripPunctuation(word.ToLowerInvariant(), out punctuation);
|
||||
string result;
|
||||
result = SearchForWordHit(lower);
|
||||
if (result != null) return result + ' ' + punctuation;
|
||||
if (lower.EndsWith("s"))
|
||||
{
|
||||
result = SearchForWordHit(lower.Substring(0, lower.Length-1));
|
||||
if (result != null) return result + ' ' + punctuation;
|
||||
}
|
||||
if (lower.EndsWith("ing"))
|
||||
{
|
||||
result = SearchForWordHit(lower.Substring(0, lower.Length - 3));
|
||||
if (result != null) return result + ' ' + punctuation;
|
||||
}
|
||||
return word.Substring(0, lower.Length) + punctuation;
|
||||
}
|
||||
|
||||
public Tuple<Texture2D, Rectangle> GetEmoji(string id)
|
||||
{
|
||||
var rect = Cache.GetEmoji(id);
|
||||
return new Tuple<Texture2D, Rectangle>(Cache.EmojiTex, rect);
|
||||
}
|
||||
|
||||
public string EmojiToBB(string input)
|
||||
{
|
||||
//search through the string for emojis to turn to BBcode
|
||||
int index = 0;
|
||||
int lastColon = -1;
|
||||
var result = new StringBuilder();
|
||||
while (true)
|
||||
{
|
||||
var nColon = input.IndexOf(':', index);
|
||||
if (nColon == -1) break;
|
||||
if (lastColon == -1) result.Append(input.Substring(index, nColon - index)); //add up to the colon
|
||||
else
|
||||
{
|
||||
//is the string between the two colons an emoji?
|
||||
var emoji = EmojiFromName(input.Substring(lastColon + 1, nColon - (lastColon + 1)));
|
||||
if (emoji == null)
|
||||
{
|
||||
result.Append(":"+input.Substring(index, nColon - index)); //add up to the colon (include the last colon we skipped)
|
||||
} else
|
||||
{
|
||||
result.Append("[emoji=" + emoji + "] ");
|
||||
lastColon = -1;
|
||||
index = nColon + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
index = nColon + 1;
|
||||
lastColon = nColon;
|
||||
}
|
||||
result.Append(((lastColon == -1) ? "" : ":") + input.Substring(index));
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public string EmojiTranslate(string input)
|
||||
{
|
||||
//search for replacement candidates for each word in input
|
||||
var words = input.Split(' ');
|
||||
|
||||
for (int i=0; i<words.Length; i++)
|
||||
{
|
||||
var word = words[i];
|
||||
if (word == "") continue;
|
||||
if (word.Length > 2 && word.StartsWith(":") && word.EndsWith(":"))
|
||||
{
|
||||
//is this already an emoji? if so, skip it
|
||||
var existing = EmojiFromName(word.Substring(1, word.Length-2));
|
||||
if (existing == null) continue;
|
||||
}
|
||||
words[i] = TranslateWordToEmoji(word);
|
||||
}
|
||||
return String.Join(" ", words);
|
||||
}
|
||||
|
||||
public string EmojiOnly(string input, int mode)
|
||||
{
|
||||
if (mode == 2) return EmojiTranslate(input);
|
||||
//search through the string for emojis to keep
|
||||
int index = 0;
|
||||
int lastColon = -1;
|
||||
var result = new StringBuilder();
|
||||
while (true)
|
||||
{
|
||||
var nColon = input.IndexOf(':', index);
|
||||
if (nColon == -1) break;
|
||||
else
|
||||
{
|
||||
//is the string between the two colons an emoji?
|
||||
var name = input.Substring(lastColon + 1, nColon - (lastColon + 1));
|
||||
var emoji = EmojiFromName(name);
|
||||
if (emoji == null)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append(":" + name + ": ");
|
||||
lastColon = -1;
|
||||
index = nColon + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
index = nColon + 1;
|
||||
lastColon = nColon;
|
||||
}
|
||||
//result.Append(((lastColon == -1) ? "" : ":"));
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
83
server/tso.common/Rendering/Framework/3DAbstract.cs
Executable file
83
server/tso.common/Rendering/Framework/3DAbstract.cs
Executable file
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using FSO.Common.Rendering.Framework.Camera;
|
||||
using FSO.Common.Rendering.Framework.Model;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for scenes with 3D elements.
|
||||
/// </summary>
|
||||
public abstract class _3DAbstract : IDisposable
|
||||
{
|
||||
public ICamera Camera;
|
||||
public string ID;
|
||||
public bool Visible = true;
|
||||
public abstract List<_3DComponent> GetElements();
|
||||
public abstract void Add(_3DComponent item);
|
||||
public abstract void Update(UpdateState Time);
|
||||
public abstract void Draw(GraphicsDevice device);
|
||||
|
||||
protected _3DLayer Parent;
|
||||
private EventHandler<EventArgs> ResetEvent;
|
||||
|
||||
public virtual void PreDraw(GraphicsDevice device)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Initialize(_3DLayer layer)
|
||||
{
|
||||
Parent = layer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new _3DAbstract instance.
|
||||
/// </summary>
|
||||
/// <param name="Device">A GraphicsDevice instance.</param>
|
||||
public _3DAbstract(GraphicsDevice Device)
|
||||
{
|
||||
m_Device = Device;
|
||||
ResetEvent = new EventHandler<EventArgs>(m_Device_DeviceReset);
|
||||
m_Device.DeviceReset += ResetEvent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when m_Device is reset.
|
||||
/// </summary>
|
||||
private void m_Device_DeviceReset(object sender, EventArgs e)
|
||||
{
|
||||
DeviceReset(m_Device);
|
||||
}
|
||||
|
||||
protected GraphicsDevice m_Device;
|
||||
|
||||
public abstract void DeviceReset(GraphicsDevice Device);
|
||||
public static bool IsInvalidated;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public object Controller { get; internal set; }
|
||||
|
||||
public void SetController(object controller)
|
||||
{
|
||||
this.Controller = controller;
|
||||
}
|
||||
|
||||
public T FindController<T>()
|
||||
{
|
||||
if(Controller is T)
|
||||
{
|
||||
return (T)Controller;
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (m_Device != null) m_Device.DeviceReset -= ResetEvent;
|
||||
}
|
||||
}
|
||||
}
|
161
server/tso.common/Rendering/Framework/3DComponent.cs
Executable file
161
server/tso.common/Rendering/Framework/3DComponent.cs
Executable file
|
@ -0,0 +1,161 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using FSO.Common.Rendering.Framework.Model;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
public abstract class _3DComponent
|
||||
{
|
||||
public _3DScene Scene;
|
||||
|
||||
public _3DComponent()
|
||||
{
|
||||
}
|
||||
|
||||
private Vector3 m_Position = Vector3.Zero;
|
||||
private Vector3 m_Scale = Vector3.One;
|
||||
private float m_RotateX = 0.0f;
|
||||
private float m_RotateY = 0.0f;
|
||||
private float m_RotateZ = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or the GraphicsDevice instance for this component.
|
||||
/// </summary>
|
||||
public GraphicsDevice Device
|
||||
{
|
||||
get
|
||||
{
|
||||
return Scene.Parent.Device;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The X component of this 3DComponent's rotation.
|
||||
/// </summary>
|
||||
public float RotationX
|
||||
{
|
||||
get { return m_RotateX; }
|
||||
set
|
||||
{
|
||||
m_RotateX = value;
|
||||
m_WorldDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Y component of this 3DComponent's rotation.
|
||||
/// </summary>
|
||||
public float RotationY
|
||||
{
|
||||
get { return m_RotateY; }
|
||||
set
|
||||
{
|
||||
m_RotateY = value;
|
||||
m_WorldDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Z component of this 3DComponent's rotation.
|
||||
/// </summary>
|
||||
public float RotationZ
|
||||
{
|
||||
get { return m_RotateZ; }
|
||||
set
|
||||
{
|
||||
m_RotateZ = value;
|
||||
m_WorldDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This 3DComponent's position.
|
||||
/// </summary>
|
||||
public Vector3 Position
|
||||
{
|
||||
get { return m_Position; }
|
||||
set
|
||||
{
|
||||
m_Position = value;
|
||||
m_WorldDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This 3DComponent's scale.
|
||||
/// </summary>
|
||||
public Vector3 Scale
|
||||
{
|
||||
get { return m_Scale; }
|
||||
set
|
||||
{
|
||||
m_Scale = value;
|
||||
m_WorldDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private Matrix m_World = Matrix.Identity;
|
||||
private bool m_WorldDirty = false;
|
||||
public Matrix World
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_WorldDirty)
|
||||
{
|
||||
m_World = Matrix.CreateRotationX(m_RotateX) * Matrix.CreateRotationY(m_RotateY) * Matrix.CreateRotationZ(m_RotateZ) * Matrix.CreateScale(m_Scale) * Matrix.CreateTranslation(m_Position);
|
||||
m_WorldDirty = false;
|
||||
}
|
||||
return m_World;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_StringID;
|
||||
public string ID
|
||||
{
|
||||
get { return m_StringID; }
|
||||
set { m_StringID = value; }
|
||||
}
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract void Update(UpdateState state);
|
||||
public abstract void Draw(GraphicsDevice device);
|
||||
/// <summary>
|
||||
/// GraphicsDevice was reset.
|
||||
/// </summary>
|
||||
public abstract void DeviceReset(GraphicsDevice Device);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (m_StringID != null)
|
||||
{
|
||||
return m_StringID;
|
||||
}
|
||||
return base.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This 3DComponent's camera's view.
|
||||
/// </summary>
|
||||
protected Matrix View
|
||||
{
|
||||
get
|
||||
{
|
||||
return Scene.Camera.View;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This 3DComponent's camera's projection.
|
||||
/// </summary>
|
||||
protected Matrix Projection
|
||||
{
|
||||
get
|
||||
{
|
||||
return Scene.Camera.Projection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
99
server/tso.common/Rendering/Framework/3DLayer.cs
Executable file
99
server/tso.common/Rendering/Framework/3DLayer.cs
Executable file
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
public class _3DLayer : IGraphicsLayer
|
||||
{
|
||||
public GraphicsDevice Device;
|
||||
public List<_3DAbstract> Scenes = new List<_3DAbstract>();
|
||||
public List<_3DAbstract> External = new List<_3DAbstract>();
|
||||
|
||||
#region IGraphicsLayer Members
|
||||
|
||||
public void Update(FSO.Common.Rendering.Framework.Model.UpdateState state)
|
||||
{
|
||||
foreach (var scene in Scenes)
|
||||
{
|
||||
scene.Update(state);
|
||||
}
|
||||
foreach (var scene in External)
|
||||
{
|
||||
scene.Update(state);
|
||||
}
|
||||
}
|
||||
|
||||
public void PreDraw(Microsoft.Xna.Framework.Graphics.GraphicsDevice device)
|
||||
{
|
||||
foreach (var scene in Scenes)
|
||||
{
|
||||
if (scene.Visible) scene.PreDraw(device);
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(Microsoft.Xna.Framework.Graphics.GraphicsDevice device)
|
||||
{
|
||||
foreach (var scene in Scenes)
|
||||
{
|
||||
if (scene.Visible) scene.Draw(device);
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(Microsoft.Xna.Framework.Graphics.GraphicsDevice device)
|
||||
{
|
||||
this.Device = device;
|
||||
foreach (var scene in Scenes)
|
||||
{
|
||||
scene.Initialize(this);
|
||||
}
|
||||
foreach (var scene in External)
|
||||
{
|
||||
scene.Initialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(_3DAbstract scene)
|
||||
{
|
||||
Scenes.Add(scene);
|
||||
if (this.Device != null)
|
||||
{
|
||||
scene.Initialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(_3DAbstract scene)
|
||||
{
|
||||
Scenes.Remove(scene);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var scene in Scenes)
|
||||
{
|
||||
if (scene is IDisposable) ((IDisposable)scene).Dispose();
|
||||
}
|
||||
Scenes.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a scene to the draw stack. The system will not call
|
||||
/// Draw on the scene but it will be initialized and given updates
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
public void AddExternal(_3DAbstract scene){
|
||||
External.Add(scene);
|
||||
if (this.Device != null)
|
||||
{
|
||||
scene.Initialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveExternal(_3DAbstract scene)
|
||||
{
|
||||
External.Remove(scene);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
125
server/tso.common/Rendering/Framework/3DScene.cs
Executable file
125
server/tso.common/Rendering/Framework/3DScene.cs
Executable file
|
@ -0,0 +1,125 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FSO.Common.Rendering.Framework.Camera;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using FSO.Common.Rendering.Framework.Model;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
/// <summary>
|
||||
/// A scene capable of rendering 3D elements.
|
||||
/// </summary>
|
||||
public class _3DScene : _3DAbstract
|
||||
{
|
||||
private List<_3DComponent> m_Elements = new List<_3DComponent>();
|
||||
|
||||
public new _3DLayer Parent;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new _3DScene instance.
|
||||
/// </summary>
|
||||
/// <param name="Device">A GraphicsDevice instance used for rendering.</param>
|
||||
/// <param name="camera">A camera inheriting from ICamera used for rendering.</param>
|
||||
public _3DScene(GraphicsDevice Device, ICamera camera) : base(Device)
|
||||
{
|
||||
this.Camera = camera;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new _3DScene instance.
|
||||
/// </summary>
|
||||
/// <param name="Device">A GraphicsDevice instance used for rendering.</param>
|
||||
public _3DScene(GraphicsDevice Device) : base(Device)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Graphics device was reset (happens when scene is updated or minimized.)
|
||||
/// </summary>
|
||||
void m_Device_DeviceReset(object sender, EventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the _3DComponents that make up this scene.
|
||||
/// </summary>
|
||||
/// <returns>A List of _3DComponent instances.</returns>
|
||||
public override List<_3DComponent> GetElements()
|
||||
{
|
||||
return m_Elements;
|
||||
}
|
||||
|
||||
public override void Initialize(_3DLayer layer)
|
||||
{
|
||||
this.Parent = layer;
|
||||
|
||||
foreach (var element in m_Elements)
|
||||
{
|
||||
element.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(UpdateState state)
|
||||
{
|
||||
for (int i = 0; i < m_Elements.Count; i++)
|
||||
{
|
||||
m_Elements[i].Update(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a 3D element from this 3DScene.
|
||||
/// </summary>
|
||||
/// <param name="item">The _3DComponent instance to remove.</param>
|
||||
public void Remove(_3DComponent item)
|
||||
{
|
||||
m_Elements.Remove(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a 3D element to this 3DScene.
|
||||
/// </summary>
|
||||
/// <param name="item">The _3DComponent instance to add.</param>
|
||||
public override void Add(_3DComponent item)
|
||||
{
|
||||
m_Elements.Add(item);
|
||||
item.Scene = this;
|
||||
if (this.Parent != null)
|
||||
{
|
||||
item.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public override void PreDraw(GraphicsDevice device){
|
||||
}
|
||||
|
||||
public override void Draw(GraphicsDevice device)
|
||||
{
|
||||
for (int i = 0; i < m_Elements.Count; i++)
|
||||
{
|
||||
m_Elements[i].Draw(device);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (ID != null)
|
||||
{
|
||||
return ID;
|
||||
}
|
||||
|
||||
return base.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GraphicsDevice was reset.
|
||||
/// </summary>
|
||||
/// <param name="Device">The GraphicsDevice instance.</param>
|
||||
public override void DeviceReset(GraphicsDevice Device)
|
||||
{
|
||||
for (int i = 0; i < m_Elements.Count; i++)
|
||||
m_Elements[i].DeviceReset(Device);
|
||||
}
|
||||
}
|
||||
}
|
44
server/tso.common/Rendering/Framework/3DTargetScene.cs
Executable file
44
server/tso.common/Rendering/Framework/3DTargetScene.cs
Executable file
|
@ -0,0 +1,44 @@
|
|||
using FSO.Common.Rendering.Framework.Camera;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
public class _3DTargetScene : _3DScene
|
||||
{
|
||||
public RenderTarget2D Target;
|
||||
private GraphicsDevice Device;
|
||||
private int Multisample = 0;
|
||||
public Color ClearColor = Color.Transparent;
|
||||
public _3DTargetScene(GraphicsDevice device, ICamera camera, Point size, int multisample) : this(device, size, multisample) { Camera = camera; }
|
||||
public _3DTargetScene(GraphicsDevice device, Point size, int multisample) : base(device)
|
||||
{
|
||||
Device = device;
|
||||
Multisample = multisample;
|
||||
SetSize(size);
|
||||
}
|
||||
|
||||
public void SetSize(Point size)
|
||||
{
|
||||
if (Target != null) Target.Dispose();
|
||||
Target = new RenderTarget2D(Device, size.X, size.Y, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8, Multisample, RenderTargetUsage.PreserveContents);
|
||||
}
|
||||
|
||||
public override void Draw(GraphicsDevice device)
|
||||
{
|
||||
var oldTargets = device.GetRenderTargets();
|
||||
device.SetRenderTarget(Target);
|
||||
device.Clear(ClearColor);
|
||||
device.DepthStencilState = DepthStencilState.Default;
|
||||
Camera.ProjectionDirty();
|
||||
base.Draw(device);
|
||||
device.SetRenderTargets(oldTargets);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
Target.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
273
server/tso.common/Rendering/Framework/Camera/BasicCamera.cs
Executable file
273
server/tso.common/Rendering/Framework/Camera/BasicCamera.cs
Executable file
|
@ -0,0 +1,273 @@
|
|||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework.Camera
|
||||
{
|
||||
/// <summary>
|
||||
/// A basic camera for the game.
|
||||
/// </summary>
|
||||
[DisplayName("BasicCamera")]
|
||||
public class BasicCamera : ICamera
|
||||
{
|
||||
public float NearPlane { get; set; }
|
||||
public float FarPlane { get; set; }
|
||||
|
||||
public float AspectRatioMultiplier { get; set; }
|
||||
|
||||
protected Vector3 m_Position;
|
||||
protected Vector3 m_Target;
|
||||
protected Vector3 m_Up;
|
||||
protected GraphicsDevice m_Device;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new BasicCamera instance. Assumes projection is full screen!
|
||||
/// </summary>
|
||||
/// <param name="device">A GraphicsDevice instance used for rendering.</param>
|
||||
/// <param name="Position">Camera's initial position.</param>
|
||||
/// <param name="Target">Camera's initial target.</param>
|
||||
/// <param name="Up">Camera's initial up vector.</param>
|
||||
public BasicCamera(GraphicsDevice device, Vector3 Position, Vector3 Target, Vector3 Up)
|
||||
{
|
||||
m_Device = device;
|
||||
AspectRatioMultiplier = 1.0f;
|
||||
NearPlane = 1.0f;
|
||||
FarPlane = 800.0f;
|
||||
|
||||
m_Position = Position;
|
||||
m_Target = Target;
|
||||
m_Up = Up;
|
||||
|
||||
m_ViewDirty = true;
|
||||
|
||||
/**
|
||||
* Assume the projection is full screen, center origin
|
||||
*/
|
||||
ProjectionOrigin = new Vector2(
|
||||
m_Device.Viewport.Width / 2.0f,
|
||||
m_Device.Viewport.Height / 2.0f
|
||||
);
|
||||
}
|
||||
|
||||
protected Vector2 m_ProjectionOrigin = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets this BasicCamera's projection origin.
|
||||
/// </summary>
|
||||
public Vector2 ProjectionOrigin
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_ProjectionOrigin;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_ProjectionOrigin = value;
|
||||
m_ProjectionDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected Matrix m_Projection;
|
||||
protected bool m_ProjectionDirty;
|
||||
|
||||
public void ProjectionDirty()
|
||||
{
|
||||
m_ProjectionDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets this camera's projection.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public Matrix Projection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ProjectionDirty)
|
||||
{
|
||||
CalculateProjection();
|
||||
m_ProjectionDirty = false;
|
||||
}
|
||||
return m_Projection;
|
||||
}
|
||||
}
|
||||
|
||||
private float _FOV = (float)Math.PI / 4f;
|
||||
public float FOV
|
||||
{
|
||||
get
|
||||
{
|
||||
return _FOV;
|
||||
}
|
||||
set
|
||||
{
|
||||
_FOV = value;
|
||||
ProjectionDirty();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void CalculateProjection()
|
||||
{
|
||||
var device = m_Device;
|
||||
var aspect = device.Viewport.AspectRatio * AspectRatioMultiplier;
|
||||
|
||||
var ratioX = m_ProjectionOrigin.X / device.Viewport.Width;
|
||||
var ratioY = m_ProjectionOrigin.Y / device.Viewport.Height;
|
||||
|
||||
var projectionX = 0.0f - (1.0f * ratioX);
|
||||
var projectionY = (1.0f * ratioY);
|
||||
|
||||
m_Projection = Matrix.CreatePerspectiveFieldOfView(FOV, aspect, NearPlane, FarPlane);
|
||||
|
||||
/*m_Projection = Matrix.CreatePerspectiveOffCenter(
|
||||
projectionX, projectionX + 1.0f,
|
||||
((projectionY-1.0f) / aspect), (projectionY) / aspect,
|
||||
NearPlane, FarPlane
|
||||
);*/
|
||||
|
||||
m_Projection = Matrix.CreateScale(Zoom, Zoom, 1.0f) * m_Projection;
|
||||
}
|
||||
|
||||
protected virtual void CalculateView()
|
||||
{
|
||||
var translate = Matrix.CreateTranslation(m_Translation);
|
||||
var position = Vector3.Transform(m_Position, translate);
|
||||
var target = Vector3.Transform(m_Target, translate);
|
||||
|
||||
m_View = Matrix.CreateLookAt(position, target, m_Up);
|
||||
}
|
||||
|
||||
protected bool m_ViewDirty = false;
|
||||
protected Matrix m_View = Matrix.Identity;
|
||||
[Browsable(false)]
|
||||
public Matrix View
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ViewDirty)
|
||||
{
|
||||
m_ViewDirty = false;
|
||||
CalculateView();
|
||||
}
|
||||
return m_View;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected float m_Zoom = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets this BasicCamera's zoom level.
|
||||
/// </summary>
|
||||
public float Zoom
|
||||
{
|
||||
get { return m_Zoom; }
|
||||
set
|
||||
{
|
||||
m_Zoom = value;
|
||||
m_ViewDirty = true;
|
||||
m_ProjectionDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector3 m_Translation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets this BasicCamera's translation.
|
||||
/// </summary>
|
||||
public Vector3 Translation
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Translation;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_Translation = value;
|
||||
m_ViewDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets this BasicCamera's position.
|
||||
/// </summary>
|
||||
public Vector3 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_Position = value;
|
||||
m_ViewDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets this BasicCamera's target.
|
||||
/// </summary>
|
||||
public Vector3 Target
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Target;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_Target = value;
|
||||
m_ViewDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets this BasicCamera's up vector.
|
||||
/// </summary>
|
||||
public Vector3 Up
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Up;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_Up = value;
|
||||
m_ViewDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DrawCamera = false;
|
||||
|
||||
public void Draw(GraphicsDevice device)
|
||||
{
|
||||
/*
|
||||
device.RasterizerState.PointSize = 30.0f;
|
||||
device.VertexDeclaration = new VertexDeclaration(device, VertexPositionColor.VertexElements);
|
||||
|
||||
var effect = new BasicEffect(device);
|
||||
|
||||
effect.World = Matrix.Identity;
|
||||
effect.View = View;
|
||||
effect.Projection = Projection;
|
||||
effect.VertexColorEnabled = true;
|
||||
|
||||
foreach (var pass in effect.Techniques[0].Passes)
|
||||
{
|
||||
pass.Apply();
|
||||
|
||||
var vertex = new VertexPositionColor(Position, Color.Green);
|
||||
var vertexList = new VertexPositionColor[1] { vertex };
|
||||
device.DrawUserPrimitives(PrimitiveType.PointList, vertexList, 0, 1);
|
||||
|
||||
vertex.Color = Color.Red;
|
||||
vertex.Position = Target;
|
||||
device.DrawUserPrimitives(PrimitiveType.PointList, vertexList, 0, 1);
|
||||
|
||||
}
|
||||
* XNA4 no longer has support for point primitives.
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
}
|
24
server/tso.common/Rendering/Framework/Camera/ICamera.cs
Executable file
24
server/tso.common/Rendering/Framework/Camera/ICamera.cs
Executable file
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework.Camera
|
||||
{
|
||||
public interface ICamera
|
||||
{
|
||||
Matrix View { get; }
|
||||
Matrix Projection { get; }
|
||||
|
||||
Vector3 Position { get; set; }
|
||||
Vector3 Target { get; set; }
|
||||
Vector3 Up { get; set; }
|
||||
Vector3 Translation { get; set; }
|
||||
|
||||
Vector2 ProjectionOrigin { get; set; }
|
||||
float NearPlane { get; set; }
|
||||
float FarPlane { get; set; }
|
||||
float Zoom { get; set; }
|
||||
float AspectRatioMultiplier { get; set; }
|
||||
|
||||
void ProjectionDirty();
|
||||
|
||||
}
|
||||
}
|
35
server/tso.common/Rendering/Framework/Camera/ManualCamera.cs
Executable file
35
server/tso.common/Rendering/Framework/Camera/ManualCamera.cs
Executable file
|
@ -0,0 +1,35 @@
|
|||
namespace FSO.Common.Rendering.Framework.Camera
|
||||
{
|
||||
public class ManualCamera : ICamera
|
||||
{
|
||||
#region ICamera Members
|
||||
|
||||
public Microsoft.Xna.Framework.Matrix View { get; set; }
|
||||
|
||||
public Microsoft.Xna.Framework.Matrix Projection { get; set; }
|
||||
|
||||
public Microsoft.Xna.Framework.Vector3 Position { get; set; }
|
||||
|
||||
public Microsoft.Xna.Framework.Vector3 Target { get; set; }
|
||||
|
||||
public Microsoft.Xna.Framework.Vector3 Up { get; set; }
|
||||
|
||||
public Microsoft.Xna.Framework.Vector3 Translation { get; set; }
|
||||
|
||||
public Microsoft.Xna.Framework.Vector2 ProjectionOrigin { get; set; }
|
||||
|
||||
public float NearPlane { get; set; }
|
||||
|
||||
public float FarPlane { get; set; }
|
||||
|
||||
public float Zoom { get; set; }
|
||||
|
||||
public float AspectRatioMultiplier { get; set; }
|
||||
|
||||
public void ProjectionDirty()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
37
server/tso.common/Rendering/Framework/Camera/OrthographicCamera.cs
Executable file
37
server/tso.common/Rendering/Framework/Camera/OrthographicCamera.cs
Executable file
|
@ -0,0 +1,37 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework.Camera
|
||||
{
|
||||
/// <summary>
|
||||
/// Orthographic camera for the game. Used for rendering lots.
|
||||
/// </summary>
|
||||
public class OrthographicCamera : BasicCamera
|
||||
{
|
||||
public OrthographicCamera(GraphicsDevice device, Vector3 Position, Vector3 Target, Vector3 Up)
|
||||
: base(device, Position, Target, Up)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void CalculateProjection()
|
||||
{
|
||||
var device = m_Device;
|
||||
var aspect = device.Viewport.AspectRatio * AspectRatioMultiplier;
|
||||
|
||||
var ratioX = m_ProjectionOrigin.X / device.Viewport.Width;
|
||||
var ratioY = m_ProjectionOrigin.Y / device.Viewport.Height;
|
||||
|
||||
var projectionX = 0.0f - (1.0f * ratioX);
|
||||
var projectionY = (1.0f * ratioY);
|
||||
|
||||
m_Projection = Matrix.CreateOrthographicOffCenter(
|
||||
projectionX, projectionX + 1.0f,
|
||||
((projectionY - 1.0f) / aspect), (projectionY) / aspect,
|
||||
NearPlane, FarPlane
|
||||
);
|
||||
|
||||
var zoom = 1 / m_Zoom;
|
||||
m_Projection = m_Projection * Matrix.CreateScale(zoom);
|
||||
}
|
||||
}
|
||||
}
|
137
server/tso.common/Rendering/Framework/CursorManager.cs
Executable file
137
server/tso.common/Rendering/Framework/CursorManager.cs
Executable file
|
@ -0,0 +1,137 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using FSO.Common.Utils;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
public enum CursorType
|
||||
{
|
||||
Normal,
|
||||
ArrowUp,
|
||||
ArrowUpLeft,
|
||||
ArrowUpRight,
|
||||
ArrowDown,
|
||||
ArrowDownLeft,
|
||||
ArrowDownRight,
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
LiveNothing,
|
||||
LiveObjectUnavail,
|
||||
LivePerson,
|
||||
IBeam,
|
||||
|
||||
SimsRotate,
|
||||
SimsRotateNE,
|
||||
SimsRotateSE,
|
||||
SimsRotateSW,
|
||||
SimsRotateNW,
|
||||
|
||||
SimsMove,
|
||||
SimsPlace,
|
||||
|
||||
Hourglass,
|
||||
|
||||
LiveObjectAvail,
|
||||
LiveObject1Star,
|
||||
LiveObject2Star,
|
||||
LiveObject3Star,
|
||||
LiveObject4Star,
|
||||
LiveObject5Star,
|
||||
LiveObjectSpecial,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages cursors in the game.
|
||||
/// </summary>
|
||||
public class CursorManager
|
||||
{
|
||||
public static CursorManager INSTANCE;
|
||||
|
||||
private Dictionary<CursorType, MouseCursor> m_CursorMap;
|
||||
private GraphicsDevice GD;
|
||||
public CursorType CurrentCursor { get; internal set;} = CursorType.Normal;
|
||||
|
||||
public CursorManager(GraphicsDevice gd)
|
||||
{
|
||||
INSTANCE = this;
|
||||
m_CursorMap = new Dictionary<CursorType, MouseCursor>();
|
||||
this.GD = gd;
|
||||
}
|
||||
|
||||
public void SetCursor(CursorType type)
|
||||
{
|
||||
if (m_CursorMap.ContainsKey(type))
|
||||
{
|
||||
CurrentCursor = type;
|
||||
Mouse.SetCursor(m_CursorMap[type]);
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<CursorType, string> GenMap()
|
||||
{
|
||||
return new Dictionary< CursorType, string> (){
|
||||
//{CursorType.Normal, "arrow.cur"},
|
||||
{ CursorType.ArrowUp, "up.cur"},
|
||||
{ CursorType.ArrowUpLeft, "upleft.cur"},
|
||||
{ CursorType.ArrowUpRight, "upright.cur"},
|
||||
{ CursorType.ArrowDown, "down.cur"},
|
||||
{ CursorType.ArrowDownLeft, "downleft.cur"},
|
||||
{ CursorType.ArrowDownRight, "downright.cur"},
|
||||
{ CursorType.ArrowLeft, "left.cur"},
|
||||
{ CursorType.ArrowRight, "right.cur"},
|
||||
{ CursorType.LiveNothing, "livenothing.cur"},
|
||||
{ CursorType.LiveObjectAvail, "liveobjectavail.cur"},
|
||||
{ CursorType.LiveObjectUnavail, "liveobjectunavail.cur"},
|
||||
{ CursorType.LivePerson, "liveperson.cur"},
|
||||
|
||||
{ CursorType.SimsRotate, "simsrotate.cur" },
|
||||
{ CursorType.SimsRotateNE, "simsrotatene.cur" },
|
||||
{ CursorType.SimsRotateNW, "simsrotatenw.cur" },
|
||||
{ CursorType.SimsRotateSE, "simsrotatese.cur" },
|
||||
{ CursorType.SimsRotateSW, "simsrotatesw.cur" },
|
||||
|
||||
{ CursorType.SimsMove, "simsmove.cur" },
|
||||
{ CursorType.SimsPlace, "simsplace.cur" },
|
||||
|
||||
{ CursorType.Hourglass, "hourglass.cur" }
|
||||
};
|
||||
}
|
||||
|
||||
public void Init(string basepath, bool ts1)
|
||||
{
|
||||
var map = GenMap();
|
||||
var curPath = "UIGraphics/Shared/cursors/";
|
||||
if (!ts1) curPath = curPath.ToLowerInvariant();
|
||||
foreach (var item in map)
|
||||
{
|
||||
m_CursorMap.Add(item.Key,
|
||||
LoadCustomCursor(
|
||||
Path.Combine(basepath, curPath, item.Value)
|
||||
));
|
||||
}
|
||||
|
||||
var starMax = 5;
|
||||
var stars = LoadUpgradeCursors(Path.Combine(basepath, curPath, "liveobjectavail.cur"), starMax);
|
||||
for (int i=0; i<starMax; i++)
|
||||
{
|
||||
m_CursorMap.Add(CursorType.LiveObject1Star + i, stars[i]);
|
||||
}
|
||||
|
||||
m_CursorMap.Add(CursorType.IBeam, MouseCursor.IBeam);
|
||||
//m_CursorMap.Add(CursorType.Hourglass, MouseCursor.Wait);
|
||||
m_CursorMap.Add(CursorType.Normal, MouseCursor.Arrow);
|
||||
}
|
||||
|
||||
private MouseCursor[] LoadUpgradeCursors(string path, int maxStars)
|
||||
{
|
||||
return CurLoader.LoadUpgradeCursors(GD, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read), maxStars);
|
||||
}
|
||||
|
||||
private MouseCursor LoadCustomCursor(string path)
|
||||
{
|
||||
return CurLoader.LoadMonoCursor(GD, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read));
|
||||
}
|
||||
}
|
||||
}
|
30
server/tso.common/Rendering/Framework/Game.cs
Executable file
30
server/tso.common/Rendering/Framework/Game.cs
Executable file
|
@ -0,0 +1,30 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
public abstract class Game : Microsoft.Xna.Framework.Game
|
||||
{
|
||||
protected GraphicsDeviceManager Graphics;
|
||||
protected GameScreen Screen;
|
||||
|
||||
public Game() : base()
|
||||
{
|
||||
Graphics = new GraphicsDeviceManager(this);
|
||||
}
|
||||
|
||||
protected override void Initialize(){
|
||||
base.Initialize();
|
||||
|
||||
Screen = new GameScreen(GraphicsDevice);
|
||||
}
|
||||
|
||||
protected override void Update(GameTime gameTime){
|
||||
Screen.Update(gameTime, IsActive);
|
||||
}
|
||||
|
||||
protected override void Draw(GameTime gameTime){
|
||||
base.Draw(gameTime);
|
||||
Screen.Draw(gameTime);
|
||||
}
|
||||
}
|
||||
}
|
225
server/tso.common/Rendering/Framework/GameScreen.cs
Executable file
225
server/tso.common/Rendering/Framework/GameScreen.cs
Executable file
|
@ -0,0 +1,225 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using FSO.Common.Rendering.Framework.Model;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Input.Touch;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
/// <summary>
|
||||
/// A screen used for drawing.
|
||||
/// </summary>
|
||||
public class GameScreen
|
||||
{
|
||||
public List<IGraphicsLayer> Layers = new List<IGraphicsLayer>();
|
||||
public GraphicsDevice Device;
|
||||
public UpdateState State;
|
||||
|
||||
public static Color ClearColor = new Color(0x72, 0x72, 0x72);
|
||||
|
||||
private int touchedFrames;
|
||||
private int lastTouchCount;
|
||||
private MouseState lastMouseState;
|
||||
private Vector2? prevTouchAvg;
|
||||
private const int TOUCH_ACCEPT_TIME = 5;
|
||||
|
||||
public GameScreen(GraphicsDevice device)
|
||||
{
|
||||
this.Device = device;
|
||||
|
||||
State = new UpdateState();
|
||||
}
|
||||
|
||||
private static List<char> TextCharacters = new List<char>();
|
||||
public static void TextInput(object sender, TextInputEventArgs e)
|
||||
{
|
||||
TextCharacters.Add(e.Character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a graphical element to this scene.
|
||||
/// </summary>
|
||||
/// <param name="layer">Element inheriting from IGraphicsLayer.</param>
|
||||
public void Add(IGraphicsLayer layer)
|
||||
{
|
||||
layer.Initialize(Device);
|
||||
Layers.Add(layer);
|
||||
}
|
||||
|
||||
public void Update(GameTime time, bool hasFocus)
|
||||
{
|
||||
State.Time = time;
|
||||
State.PreviousKeyboardState = State.KeyboardState;
|
||||
State.FrameTextInput = TextCharacters;
|
||||
|
||||
var touchMode = FSOEnvironment.SoftwareKeyboard;
|
||||
|
||||
if (touchMode)
|
||||
{
|
||||
if (FSOEnvironment.SoftwareDepth) State.KeyboardState = new KeyboardState();
|
||||
TouchCollection touches = TouchPanel.GetState();
|
||||
|
||||
var missing = new HashSet<MultiMouse>(State.MouseStates);
|
||||
//relate touches to their last virtual mouse
|
||||
foreach (var touch in touches)
|
||||
{
|
||||
var mouse = State.MouseStates.FirstOrDefault(x => x.ID == touch.Id);
|
||||
if (mouse == null)
|
||||
{
|
||||
mouse = new MultiMouse { ID = touch.Id };
|
||||
State.MouseStates.Add(mouse);
|
||||
}
|
||||
missing.Remove(mouse);
|
||||
|
||||
mouse.MouseState = new MouseState(
|
||||
(int)touch.Position.X, (int)touch.Position.Y, 0,
|
||||
ButtonState.Pressed,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released
|
||||
);
|
||||
}
|
||||
|
||||
//if virtual mouses no longer have their touch, they are performing a "mouse up"
|
||||
//if the state has mouseovers, we should record the mouse state as being lifted.
|
||||
foreach (var miss in missing)
|
||||
{
|
||||
if (miss.LastMouseOver == null && miss.LastMouseDown == null)
|
||||
{
|
||||
State.MouseStates.Remove(miss);
|
||||
} else
|
||||
{
|
||||
miss.MouseState = new MouseState(miss.MouseState.X, miss.MouseState.Y, 0, ButtonState.Released, ButtonState.Released, ButtonState.Released, ButtonState.Released, ButtonState.Released);
|
||||
miss.Dead = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//single mouse state
|
||||
if (hasFocus)
|
||||
{
|
||||
State.MouseState = Mouse.GetState();
|
||||
State.KeyboardState = Keyboard.GetState();
|
||||
}
|
||||
else
|
||||
{
|
||||
State.MouseState = new MouseState();
|
||||
State.KeyboardState = new KeyboardState();
|
||||
}
|
||||
|
||||
if (State.KeyboardState.IsKeyDown(Keys.LeftAlt) && State.MouseState.LeftButton == ButtonState.Pressed)
|
||||
{
|
||||
//emulated middle click with alt
|
||||
var ms = State.MouseState;
|
||||
State.MouseState = new MouseState(ms.X, ms.Y, ms.ScrollWheelValue, ButtonState.Released, ButtonState.Pressed, ms.RightButton, ms.XButton1, ms.XButton2);
|
||||
}
|
||||
|
||||
if (State.MouseStates.Count == 0)
|
||||
{
|
||||
State.MouseStates.Add(new MultiMouse { ID = 1 });
|
||||
}
|
||||
|
||||
State.MouseStates[0].MouseState = State.MouseState;
|
||||
}
|
||||
|
||||
|
||||
State.SharedData.Clear();
|
||||
State.Update();
|
||||
|
||||
foreach (var layer in Layers){
|
||||
layer.Update(State);
|
||||
}
|
||||
|
||||
TextCharacters.Clear();
|
||||
}
|
||||
|
||||
private void TouchStub(UpdateState state)
|
||||
{
|
||||
var test = TouchPanel.EnableMouseTouchPoint;
|
||||
TouchCollection touches = TouchPanel.GetState();
|
||||
if (touches.Count != lastTouchCount) touchedFrames = 0;
|
||||
lastTouchCount = touches.Count;
|
||||
if (touches.Count > 0)
|
||||
{
|
||||
Vector2 avg = new Vector2();
|
||||
for (int i = 0; i < touches.Count; i++)
|
||||
{
|
||||
avg += touches[i].Position;
|
||||
}
|
||||
avg /= touches.Count;
|
||||
|
||||
if (touchedFrames < TOUCH_ACCEPT_TIME)
|
||||
{
|
||||
avg = prevTouchAvg ?? avg;
|
||||
state.MouseState = new MouseState(
|
||||
(int)avg.X, (int)avg.Y, state.MouseState.ScrollWheelValue,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released
|
||||
);
|
||||
touchedFrames++;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.MouseState = new MouseState(
|
||||
(int)avg.X, (int)avg.Y, state.MouseState.ScrollWheelValue,
|
||||
(touches.Count > 1) ? ButtonState.Released : ButtonState.Pressed,
|
||||
(touches.Count > 1) ? ButtonState.Pressed : ButtonState.Released,
|
||||
(touches.Count > 1) ? ButtonState.Pressed : ButtonState.Released,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released
|
||||
);
|
||||
prevTouchAvg = avg;
|
||||
|
||||
state.TouchMode = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prevTouchAvg = null;
|
||||
touchedFrames = 0;
|
||||
if (state.TouchMode) state.MouseState = new MouseState(
|
||||
lastMouseState.X, lastMouseState.Y, state.MouseState.ScrollWheelValue,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released,
|
||||
ButtonState.Released
|
||||
);
|
||||
//state.TouchMode = false;
|
||||
}
|
||||
lastMouseState = state.MouseState;
|
||||
}
|
||||
|
||||
public void Draw(GameTime time)
|
||||
{
|
||||
lock (Device)
|
||||
{
|
||||
foreach (var layer in Layers.Reverse<IGraphicsLayer>())
|
||||
{
|
||||
layer.PreDraw(Device);
|
||||
}
|
||||
}
|
||||
|
||||
Device.SetRenderTarget(null);
|
||||
Device.BlendState = BlendState.AlphaBlend;
|
||||
Device.Clear(ClearColor);
|
||||
//Device.RasterizerState.AlphaBlendEnable = true;
|
||||
//Device.DepthStencilState.DepthBufferEnable = true;
|
||||
|
||||
lock (Device)
|
||||
{
|
||||
foreach (var layer in Layers)
|
||||
{
|
||||
layer.Draw(Device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
server/tso.common/Rendering/Framework/I3DGeometry.cs
Executable file
8
server/tso.common/Rendering/Framework/I3DGeometry.cs
Executable file
|
@ -0,0 +1,8 @@
|
|||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
public interface I3DGeometry {
|
||||
void DrawGeometry(GraphicsDevice gd);
|
||||
}
|
||||
}
|
13
server/tso.common/Rendering/Framework/IGraphicsLayer.cs
Executable file
13
server/tso.common/Rendering/Framework/IGraphicsLayer.cs
Executable file
|
@ -0,0 +1,13 @@
|
|||
using FSO.Common.Rendering.Framework.Model;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
public interface IGraphicsLayer
|
||||
{
|
||||
void Initialize(GraphicsDevice device);
|
||||
void Update(UpdateState state);
|
||||
void PreDraw(GraphicsDevice device);
|
||||
void Draw(GraphicsDevice device);
|
||||
}
|
||||
}
|
10
server/tso.common/Rendering/Framework/IO/ClipboardHandler.cs
Executable file
10
server/tso.common/Rendering/Framework/IO/ClipboardHandler.cs
Executable file
|
@ -0,0 +1,10 @@
|
|||
namespace FSO.Common.Rendering.Framework.IO
|
||||
{
|
||||
public class ClipboardHandler
|
||||
{
|
||||
public static ClipboardHandler Default = new ClipboardHandler();
|
||||
|
||||
public virtual string Get() { return ""; }
|
||||
public virtual void Set(string text) { }
|
||||
}
|
||||
}
|
10
server/tso.common/Rendering/Framework/IO/IDepthProvider.cs
Executable file
10
server/tso.common/Rendering/Framework/IO/IDepthProvider.cs
Executable file
|
@ -0,0 +1,10 @@
|
|||
namespace FSO.Common.Rendering.Framework.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an object that has depth
|
||||
/// </summary>
|
||||
public interface IDepthProvider
|
||||
{
|
||||
float Depth { get; }
|
||||
}
|
||||
}
|
13
server/tso.common/Rendering/Framework/IO/IFocusableUI.cs
Executable file
13
server/tso.common/Rendering/Framework/IO/IFocusableUI.cs
Executable file
|
@ -0,0 +1,13 @@
|
|||
namespace FSO.Common.Rendering.Framework.IO
|
||||
{
|
||||
public interface IFocusableUI
|
||||
{
|
||||
void OnFocusChanged(FocusEvent newFocus);
|
||||
}
|
||||
|
||||
public enum FocusEvent
|
||||
{
|
||||
FocusIn,
|
||||
FocusOut
|
||||
}
|
||||
}
|
571
server/tso.common/Rendering/Framework/IO/InputManager.cs
Executable file
571
server/tso.common/Rendering/Framework/IO/InputManager.cs
Executable file
|
@ -0,0 +1,571 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System.Runtime.InteropServices;
|
||||
using FSO.Common.Rendering.Framework.Model;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages input for the game.
|
||||
/// </summary>
|
||||
public class InputManager
|
||||
{
|
||||
private IFocusableUI LastFocus;
|
||||
public bool RequireWindowFocus = false;
|
||||
|
||||
public void SetFocus(IFocusableUI ui)
|
||||
{
|
||||
/** No change **/
|
||||
if (ui == LastFocus) { return; }
|
||||
|
||||
if (LastFocus != null)
|
||||
{
|
||||
LastFocus.OnFocusChanged(FocusEvent.FocusOut);
|
||||
}
|
||||
|
||||
LastFocus = ui;
|
||||
if (ui != null)
|
||||
{
|
||||
LastFocus.OnFocusChanged(FocusEvent.FocusIn);
|
||||
}
|
||||
}
|
||||
|
||||
public IFocusableUI GetFocus()
|
||||
{
|
||||
return LastFocus;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern int MapVirtualKey(uint uCode, uint uMapType);
|
||||
|
||||
/// <summary>
|
||||
/// Utility to apply the result of pressing keys against a buffer
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="keys"></param>
|
||||
public KeyboardInputResult ApplyKeyboardInput(StringBuilder m_SBuilder, UpdateState state, int cursorIndex, int cursorEndIndex, bool allowInput)
|
||||
{
|
||||
if (!state.WindowFocused && RequireWindowFocus) { return null; }
|
||||
|
||||
var PressedKeys = state.KeyboardState.GetPressedKeys();
|
||||
int charCount = 0;
|
||||
if (state.FrameTextInput == null) charCount = 0;
|
||||
else charCount = state.FrameTextInput.Count;
|
||||
|
||||
if (PressedKeys.Length + charCount == 0) { return null; }
|
||||
//bit of a legacy thing going on here
|
||||
//we support both "pressed keys" and the keyboard event system.
|
||||
//todo: clean up a bit
|
||||
|
||||
var didChange = false;
|
||||
var result = new KeyboardInputResult();
|
||||
|
||||
var m_CurrentKeyState = state.KeyboardState;
|
||||
var m_OldKeyState = state.PreviousKeyboardState;
|
||||
|
||||
|
||||
result.ShiftDown = PressedKeys.Contains(Keys.LeftShift) || PressedKeys.Contains(Keys.RightShift);
|
||||
result.CapsDown = state.KeyboardState.CapsLock;
|
||||
result.NumLockDown = state.KeyboardState.NumLock;
|
||||
// Right alt aka AltGr is treated as Ctrl+Alt. It is used to type accented letters and other unusual characters so pressing that key cannot cause special actions.
|
||||
result.CtrlDown = (PressedKeys.Contains(Keys.LeftControl) && !PressedKeys.Contains(Keys.RightAlt)) || PressedKeys.Contains(Keys.RightControl);
|
||||
|
||||
for (int j = 0; j < state.NewKeys.Count + charCount; j++)
|
||||
{
|
||||
var key = (j<state.NewKeys.Count)?state.NewKeys[j]:Keys.None;
|
||||
bool processChar = true;
|
||||
|
||||
if (key != Keys.None)
|
||||
{
|
||||
processChar = false;
|
||||
if (key == Keys.Back || key == Keys.Delete)
|
||||
{
|
||||
if (m_SBuilder.Length > 0)
|
||||
{
|
||||
/**
|
||||
* Delete previous character or delete selection
|
||||
*/
|
||||
if (cursorEndIndex == -1 && result.CtrlDown)
|
||||
{
|
||||
/** Delete up until the previous whitespace char **/
|
||||
int newEndIndex = cursorIndex;
|
||||
if (newEndIndex == -1)
|
||||
{
|
||||
newEndIndex = m_SBuilder.Length;
|
||||
cursorIndex = m_SBuilder.Length;
|
||||
}
|
||||
int dir = (key == Keys.Delete) ? 1 : -1;
|
||||
int ws = (key == Keys.Delete) ? 0 : -1;
|
||||
while (newEndIndex+ws >= 0 && newEndIndex+ws < m_SBuilder.Length)
|
||||
{
|
||||
if (Char.IsWhiteSpace(m_SBuilder[newEndIndex+ws]))
|
||||
{
|
||||
/** Keep the whitespace char **/
|
||||
break;
|
||||
}
|
||||
newEndIndex += dir;
|
||||
}
|
||||
if (cursorIndex > newEndIndex)
|
||||
{
|
||||
cursorEndIndex = cursorIndex;
|
||||
cursorIndex = newEndIndex;
|
||||
}
|
||||
else
|
||||
cursorEndIndex = newEndIndex;
|
||||
if (cursorEndIndex == cursorIndex)
|
||||
cursorIndex = cursorEndIndex = -1;
|
||||
}
|
||||
|
||||
if (cursorEndIndex == -1)
|
||||
{
|
||||
/** Previous character **/
|
||||
var index = cursorIndex == -1 ? m_SBuilder.Length : cursorIndex;
|
||||
if ((key == Keys.Back) && (index > 0))
|
||||
{
|
||||
var numToDelete = 1;
|
||||
if (index > 1 && m_SBuilder[index - 1] == '\n' && m_SBuilder[index - 2] == '\r')
|
||||
{
|
||||
numToDelete = 2;
|
||||
}
|
||||
|
||||
|
||||
m_SBuilder.Remove(index - numToDelete, numToDelete);
|
||||
result.NumDeletes += numToDelete;
|
||||
|
||||
if (cursorIndex != -1)
|
||||
{
|
||||
cursorIndex -= numToDelete;
|
||||
}
|
||||
}
|
||||
else if ((key == Keys.Delete) && (index < m_SBuilder.Length))
|
||||
{
|
||||
/** Guys, delete removes the next character, not the last!! **/
|
||||
var numToDelete = 1;
|
||||
if ((index < m_SBuilder.Length - 1) && m_SBuilder[index] == '\r' && m_SBuilder[index + 1] == '\n')
|
||||
{
|
||||
numToDelete = 2;
|
||||
}
|
||||
|
||||
m_SBuilder.Remove(index, numToDelete);
|
||||
result.NumDeletes += numToDelete;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteSelectedText(m_SBuilder, ref cursorIndex, ref cursorEndIndex, ref didChange, result);
|
||||
}
|
||||
result.SelectionChanged = true;
|
||||
didChange = true;
|
||||
}
|
||||
}
|
||||
else if (key == Keys.Enter)
|
||||
{
|
||||
if (allowInput)
|
||||
{
|
||||
/** Line break **/
|
||||
if (cursorEndIndex != -1)
|
||||
{
|
||||
/** Delete selected text **/
|
||||
DeleteSelectedText(m_SBuilder, ref cursorIndex, ref cursorEndIndex, ref didChange, result);
|
||||
}
|
||||
|
||||
if (cursorIndex == -1)
|
||||
{
|
||||
m_SBuilder.Append("\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
cursorIndex = Math.Min(m_SBuilder.Length, cursorIndex);
|
||||
m_SBuilder.Insert(cursorIndex, "\n");
|
||||
cursorIndex += 1;
|
||||
}
|
||||
result.NumInsertions += 1;
|
||||
didChange = true;
|
||||
result.EnterPressed = true;
|
||||
}
|
||||
}
|
||||
else if (key == Keys.Tab)
|
||||
{
|
||||
result.TabPressed = true;
|
||||
}
|
||||
else if (result.CtrlDown)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case Keys.A:
|
||||
/** Select all **/
|
||||
cursorIndex = 0;
|
||||
cursorEndIndex = m_SBuilder.Length;
|
||||
result.SelectionChanged = true;
|
||||
break;
|
||||
|
||||
case Keys.C:
|
||||
case Keys.X:
|
||||
/** Copy text to clipboard **/
|
||||
if (cursorEndIndex > 0)
|
||||
{
|
||||
var selectionStart = Math.Max(0, cursorIndex);
|
||||
var selectionEnd = cursorEndIndex;
|
||||
GetSelectionRange(ref selectionStart, ref selectionEnd);
|
||||
|
||||
var str = m_SBuilder.ToString().Substring(selectionStart, selectionEnd - selectionStart);
|
||||
|
||||
ClipboardHandler.Default.Set(str);
|
||||
|
||||
if (key == Keys.X)
|
||||
{
|
||||
DeleteSelectedText(m_SBuilder, ref cursorIndex, ref cursorEndIndex, ref didChange, result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Keys.V:
|
||||
/** Paste text in **/
|
||||
var clipboardText = ClipboardHandler.Default.Get();
|
||||
|
||||
if (clipboardText != null)
|
||||
{
|
||||
/** TODO: Cleanup the clipboard text to make sure its valid **/
|
||||
|
||||
/** If i have selection, delete it **/
|
||||
if (cursorEndIndex != -1)
|
||||
{
|
||||
DeleteSelectedText(m_SBuilder, ref cursorIndex, ref cursorEndIndex, ref didChange, result);
|
||||
}
|
||||
|
||||
/** Paste **/
|
||||
if (cursorIndex == -1)
|
||||
{
|
||||
m_SBuilder.Append(clipboardText);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SBuilder.Insert(Math.Min(cursorIndex, m_SBuilder.Length), clipboardText);
|
||||
cursorIndex += clipboardText.Length;
|
||||
}
|
||||
result.NumInsertions += clipboardText.Length;
|
||||
didChange = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
result.UnhandledKeys.Add(key);
|
||||
processChar = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (processChar)
|
||||
{
|
||||
char value;
|
||||
if (j >= state.NewKeys.Count) value = state.FrameTextInput[j - state.NewKeys.Count];
|
||||
else if (state.FrameTextInput != null) continue;
|
||||
else value = TranslateChar(key, result.ShiftDown, result.CapsDown, result.NumLockDown);
|
||||
/** For now we dont support tabs in text **/
|
||||
|
||||
if (!char.IsControl(value) && value != '\0' && value != '\t' && value != '\b' && value != '\r')
|
||||
{
|
||||
if (allowInput)
|
||||
{
|
||||
if (cursorEndIndex != -1)
|
||||
{
|
||||
/** Delete selected text **/
|
||||
DeleteSelectedText(m_SBuilder, ref cursorIndex, ref cursorEndIndex, ref didChange, result);
|
||||
}
|
||||
|
||||
if (cursorIndex == -1)
|
||||
{
|
||||
m_SBuilder.Append(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SBuilder.Insert(cursorIndex, value);
|
||||
cursorIndex++;
|
||||
}
|
||||
result.NumInsertions++;
|
||||
didChange = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.UnhandledKeys.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.SelectionStart = cursorIndex;
|
||||
result.SelectionEnd = cursorEndIndex;
|
||||
|
||||
result.ContentChanged = didChange;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void DeleteSelectedText(StringBuilder m_SBuilder, ref int cursorIndex, ref int cursorEndIndex, ref bool didChange, KeyboardInputResult result)
|
||||
{
|
||||
/** Remove selected text **/
|
||||
var index = cursorIndex == -1 ? m_SBuilder.Length : cursorIndex;
|
||||
var end = cursorEndIndex;
|
||||
if (end < index)
|
||||
{
|
||||
var temp = index;
|
||||
index = end;
|
||||
end = temp;
|
||||
}
|
||||
m_SBuilder.Remove(index, end - index);
|
||||
|
||||
cursorIndex = index;
|
||||
if (cursorIndex >= m_SBuilder.Length)
|
||||
{
|
||||
cursorIndex = -1;
|
||||
}
|
||||
cursorEndIndex = -1;
|
||||
result.SelectionChanged = true;
|
||||
didChange = true;
|
||||
}
|
||||
|
||||
public void GetSelectionRange(ref int start, ref int end)
|
||||
{
|
||||
if (end < start)
|
||||
{
|
||||
var temp = start;
|
||||
start = end;
|
||||
end = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public static char TranslateChar(Keys key, bool shift, bool capsLock, bool numLock)
|
||||
{
|
||||
|
||||
switch (key)
|
||||
{
|
||||
|
||||
case Keys.A: return TranslateAlphabetic('a', shift, capsLock);
|
||||
|
||||
case Keys.B: return TranslateAlphabetic('b', shift, capsLock);
|
||||
|
||||
case Keys.C: return TranslateAlphabetic('c', shift, capsLock);
|
||||
|
||||
case Keys.D: return TranslateAlphabetic('d', shift, capsLock);
|
||||
|
||||
case Keys.E: return TranslateAlphabetic('e', shift, capsLock);
|
||||
|
||||
case Keys.F: return TranslateAlphabetic('f', shift, capsLock);
|
||||
|
||||
case Keys.G: return TranslateAlphabetic('g', shift, capsLock);
|
||||
|
||||
case Keys.H: return TranslateAlphabetic('h', shift, capsLock);
|
||||
|
||||
case Keys.I: return TranslateAlphabetic('i', shift, capsLock);
|
||||
|
||||
case Keys.J: return TranslateAlphabetic('j', shift, capsLock);
|
||||
|
||||
case Keys.K: return TranslateAlphabetic('k', shift, capsLock);
|
||||
|
||||
case Keys.L: return TranslateAlphabetic('l', shift, capsLock);
|
||||
|
||||
case Keys.M: return TranslateAlphabetic('m', shift, capsLock);
|
||||
|
||||
case Keys.N: return TranslateAlphabetic('n', shift, capsLock);
|
||||
|
||||
case Keys.O: return TranslateAlphabetic('o', shift, capsLock);
|
||||
|
||||
case Keys.P: return TranslateAlphabetic('p', shift, capsLock);
|
||||
|
||||
case Keys.Q: return TranslateAlphabetic('q', shift, capsLock);
|
||||
|
||||
case Keys.R: return TranslateAlphabetic('r', shift, capsLock);
|
||||
|
||||
case Keys.S: return TranslateAlphabetic('s', shift, capsLock);
|
||||
|
||||
case Keys.T: return TranslateAlphabetic('t', shift, capsLock);
|
||||
|
||||
case Keys.U: return TranslateAlphabetic('u', shift, capsLock);
|
||||
|
||||
case Keys.V: return TranslateAlphabetic('v', shift, capsLock);
|
||||
|
||||
case Keys.W: return TranslateAlphabetic('w', shift, capsLock);
|
||||
|
||||
case Keys.X: return TranslateAlphabetic('x', shift, capsLock);
|
||||
|
||||
case Keys.Y: return TranslateAlphabetic('y', shift, capsLock);
|
||||
|
||||
case Keys.Z: return TranslateAlphabetic('z', shift, capsLock);
|
||||
|
||||
case Keys.D0: return (shift) ? ')' : '0';
|
||||
|
||||
case Keys.D1: return (shift) ? '!' : '1';
|
||||
|
||||
case Keys.D2: return (shift) ? '@' : '2';
|
||||
|
||||
case Keys.D3: return (shift) ? '#' : '3';
|
||||
|
||||
case Keys.D4: return (shift) ? '$' : '4';
|
||||
|
||||
case Keys.D5: return (shift) ? '%' : '5';
|
||||
|
||||
case Keys.D6: return (shift) ? '^' : '6';
|
||||
|
||||
case Keys.D7: return (shift) ? '&' : '7';
|
||||
|
||||
case Keys.D8: return (shift) ? '*' : '8';
|
||||
|
||||
case Keys.D9: return (shift) ? '(' : '9';
|
||||
|
||||
case Keys.Add: return '+';
|
||||
|
||||
case Keys.Divide: return '/';
|
||||
|
||||
case Keys.Multiply: return '*';
|
||||
|
||||
case Keys.Subtract: return '-';
|
||||
|
||||
case Keys.Space: return ' ';
|
||||
|
||||
case Keys.Tab: return '\t';
|
||||
|
||||
case Keys.Decimal: if (numLock && !shift) return '.'; break;
|
||||
|
||||
case Keys.NumPad0: if (numLock && !shift) return '0'; break;
|
||||
|
||||
case Keys.NumPad1: if (numLock && !shift) return '1'; break;
|
||||
|
||||
case Keys.NumPad2: if (numLock && !shift) return '2'; break;
|
||||
|
||||
case Keys.NumPad3: if (numLock && !shift) return '3'; break;
|
||||
|
||||
case Keys.NumPad4: if (numLock && !shift) return '4'; break;
|
||||
|
||||
case Keys.NumPad5: if (numLock && !shift) return '5'; break;
|
||||
|
||||
case Keys.NumPad6: if (numLock && !shift) return '6'; break;
|
||||
|
||||
case Keys.NumPad7: if (numLock && !shift) return '7'; break;
|
||||
|
||||
case Keys.NumPad8: if (numLock && !shift) return '8'; break;
|
||||
|
||||
case Keys.NumPad9: if (numLock && !shift) return '9'; break;
|
||||
|
||||
case Keys.OemBackslash: return shift ? '|' : '\\';
|
||||
|
||||
case Keys.OemCloseBrackets: return shift ? '}' : ']';
|
||||
|
||||
case Keys.OemComma: return shift ? '<' : ',';
|
||||
|
||||
case Keys.OemMinus: return shift ? '_' : '-';
|
||||
|
||||
case Keys.OemOpenBrackets: return shift ? '{' : '[';
|
||||
|
||||
case Keys.OemPeriod: return shift ? '>' : '.';
|
||||
|
||||
case Keys.OemPipe: return shift ? '|' : '\\';
|
||||
|
||||
case Keys.OemPlus: return shift ? '+' : '=';
|
||||
|
||||
case Keys.OemQuestion: return shift ? '?' : '/';
|
||||
|
||||
case Keys.OemQuotes: return shift ? '"' : '\'';
|
||||
|
||||
case Keys.OemSemicolon: return shift ? ':' : ';';
|
||||
|
||||
case Keys.OemTilde: return shift ? '~' : '`';
|
||||
}
|
||||
|
||||
return (char)0;
|
||||
}
|
||||
|
||||
public static char TranslateAlphabetic(char baseChar, bool shift, bool capsLock)
|
||||
{
|
||||
return (capsLock ^ shift) ? char.ToUpper(baseChar) : baseChar;
|
||||
}
|
||||
|
||||
public void HandleMouseEvents(UpdateState state)
|
||||
{
|
||||
foreach (var mouse in state.MouseStates) {
|
||||
var mouseBtnDown = mouse.MouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed;
|
||||
var mouseDif = mouseBtnDown != mouse.LastMouseDownState;
|
||||
if (mouse.NewMultiMouse) mouse.NewMultiMouse = false;
|
||||
else
|
||||
mouse.LastMouseDownState = mouseBtnDown;
|
||||
state.MouseState = mouse.MouseState; //make sure each event uses the mouse state for this mouse.
|
||||
state.CurrentMouseID = mouse.ID;
|
||||
//if anyone accesses vanilla mouse state during the update loop, it will be the last mouse that was present.
|
||||
|
||||
var topMost =
|
||||
state.MouseEvents.Where(x => x.Item1 == mouse.ID).OrderByDescending(x => x.Item2.Element.Depth).FirstOrDefault();
|
||||
|
||||
if (topMost != null && !mouse.Dead)
|
||||
{
|
||||
|
||||
/** different element? **/
|
||||
if (mouse.LastMouseOver != topMost.Item2)
|
||||
{
|
||||
|
||||
if (mouse.LastMouseOver != null)
|
||||
{
|
||||
mouse.LastMouseOver.Callback(UIMouseEventType.MouseOut, state);
|
||||
}
|
||||
|
||||
topMost.Item2.Callback(UIMouseEventType.MouseOver, state);
|
||||
mouse.LastMouseOver = topMost.Item2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mouse.LastMouseOver != null)
|
||||
{
|
||||
mouse.LastMouseOver.Callback(UIMouseEventType.MouseOut, state);
|
||||
mouse.LastMouseOver = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (mouseDif)
|
||||
{
|
||||
if (mouseBtnDown)
|
||||
{
|
||||
if (mouse.LastMouseDown != null)
|
||||
{
|
||||
/** We already have mouse down on an object **/
|
||||
return;
|
||||
}
|
||||
if (mouse.LastMouseOver != null)
|
||||
{
|
||||
mouse.LastMouseDown = mouse.LastMouseOver;
|
||||
mouse.LastMouseDown.Callback(UIMouseEventType.MouseDown, state);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mouse.LastMouseDown != null)
|
||||
{
|
||||
mouse.LastMouseDown.Callback(UIMouseEventType.MouseUp, state);
|
||||
mouse.LastMouseDown = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class KeyboardInputResult
|
||||
{
|
||||
public List<Keys> UnhandledKeys = new List<Keys>();
|
||||
public bool ContentChanged;
|
||||
public bool ShiftDown;
|
||||
public bool CapsDown;
|
||||
public bool NumLockDown;
|
||||
public bool CtrlDown;
|
||||
public bool EnterPressed;
|
||||
public bool TabPressed;
|
||||
|
||||
public int NumDeletes;
|
||||
public int NumInsertions;
|
||||
|
||||
public int SelectionStart;
|
||||
public int SelectionEnd;
|
||||
public bool SelectionChanged;
|
||||
}
|
||||
}
|
25
server/tso.common/Rendering/Framework/IO/MouseEvent.cs
Executable file
25
server/tso.common/Rendering/Framework/IO/MouseEvent.cs
Executable file
|
@ -0,0 +1,25 @@
|
|||
using FSO.Common.Rendering.Framework.Model;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework.IO
|
||||
{
|
||||
public enum UIMouseEventType
|
||||
{
|
||||
MouseOver,
|
||||
MouseOut,
|
||||
MouseDown,
|
||||
MouseUp
|
||||
}
|
||||
|
||||
public delegate void UIMouseEvent(UIMouseEventType type, UpdateState state);
|
||||
|
||||
public class UIMouseEventRef
|
||||
{
|
||||
public UIMouseEvent Callback;
|
||||
public Rectangle Region;
|
||||
//public UIElement Element;
|
||||
public UIMouseEventType LastState;
|
||||
|
||||
public IDepthProvider Element;
|
||||
}
|
||||
}
|
21
server/tso.common/Rendering/Framework/Model/UIState.cs
Executable file
21
server/tso.common/Rendering/Framework/Model/UIState.cs
Executable file
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework.Model
|
||||
{
|
||||
public class UIState
|
||||
{
|
||||
public int Width;
|
||||
public int Height;
|
||||
public UITooltipProperties TooltipProperties = new UITooltipProperties();
|
||||
public string Tooltip;
|
||||
}
|
||||
|
||||
public class UITooltipProperties
|
||||
{
|
||||
public float Opacity;
|
||||
public Vector2 Position;
|
||||
public bool Show;
|
||||
public Color Color = Color.Black;
|
||||
public bool UpdateDead;
|
||||
}
|
||||
}
|
131
server/tso.common/Rendering/Framework/Model/UpdateState.cs
Executable file
131
server/tso.common/Rendering/Framework/Model/UpdateState.cs
Executable file
|
@ -0,0 +1,131 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using FSO.Common.Rendering.Framework.IO;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework.Model
|
||||
{
|
||||
public class MultiMouse
|
||||
{
|
||||
public int ID;
|
||||
public MouseState MouseState;
|
||||
public UIMouseEventRef LastMouseDown;
|
||||
public UIMouseEventRef LastMouseOver;
|
||||
public bool LastMouseDownState = false;
|
||||
public bool NewMultiMouse = true;
|
||||
public bool Dead = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains common information used in the update loop
|
||||
/// </summary>
|
||||
public class UpdateState
|
||||
{
|
||||
public GameTime Time;
|
||||
public List<MultiMouse> MouseStates = new List<MultiMouse>();
|
||||
public MouseState MouseState;
|
||||
public int CurrentMouseID;
|
||||
public KeyboardState KeyboardState;
|
||||
public bool ShiftDown
|
||||
{
|
||||
get { return KeyboardState.IsKeyDown(Keys.LeftShift) || KeyboardState.IsKeyDown(Keys.RightShift); }
|
||||
}
|
||||
/// <summary>
|
||||
/// Right alt is treated as LeftCtrl+RightAlt so while right Alt is down, you cannot predict if left Ctrl is also down.
|
||||
/// For that reason, this variable is false when left Ctrl and right Alt are down, and right Ctrl is not down.
|
||||
/// </summary>
|
||||
public bool CtrlDown
|
||||
{
|
||||
get { return (KeyboardState.IsKeyDown(Keys.LeftControl) && !KeyboardState.IsKeyDown(Keys.RightAlt)) || KeyboardState.IsKeyDown(Keys.RightControl); }
|
||||
}
|
||||
public bool AltDown
|
||||
{
|
||||
get { return KeyboardState.IsKeyDown(Keys.LeftAlt) || KeyboardState.IsKeyDown(Keys.RightAlt); }
|
||||
}
|
||||
|
||||
public UIState UIState = new UIState();
|
||||
public InputManager InputManager;
|
||||
public bool TouchMode;
|
||||
|
||||
public KeyboardState PreviousKeyboardState;
|
||||
public List<char> FrameTextInput;
|
||||
|
||||
/** A Place to keep shared variables, clears every update cycle **/
|
||||
public Dictionary<string, object> SharedData = new Dictionary<string, object>();
|
||||
public List<Tuple<int, UIMouseEventRef>> MouseEvents = new List<Tuple<int, UIMouseEventRef>>();
|
||||
|
||||
private Dictionary<Keys, long> KeyDownTime = new Dictionary<Keys, long>();
|
||||
private List<Keys> KeyInRepeatMode = new List<Keys>();
|
||||
|
||||
public List<Keys> NewKeys = new List<Keys>();
|
||||
public int Depth;
|
||||
|
||||
public bool WindowFocused;
|
||||
public bool MouseOverWindow;
|
||||
|
||||
public bool ProcessMouseEvents
|
||||
{
|
||||
get
|
||||
{
|
||||
return WindowFocused && MouseOverWindow;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
NewKeys.Clear();
|
||||
Depth = 0;
|
||||
|
||||
/**
|
||||
* If a key has been held down for X duration, treat it as if it is newly
|
||||
* pressed
|
||||
*/
|
||||
for(var i=0; i < KeyInRepeatMode.Count; i++){
|
||||
|
||||
if (!KeyboardState.IsKeyDown(KeyInRepeatMode[i]))
|
||||
{
|
||||
KeyInRepeatMode.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
var now = Time.TotalGameTime.Ticks;
|
||||
var keys = KeyboardState.GetPressedKeys();
|
||||
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var newPress = PreviousKeyboardState.IsKeyUp(key);
|
||||
if (newPress)
|
||||
{
|
||||
KeyDownTime[key] = now;
|
||||
NewKeys.Add(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (KeyInRepeatMode.Contains(key))
|
||||
{
|
||||
|
||||
/** How long has it been down? **/
|
||||
if (now - KeyDownTime[key] > 400000)
|
||||
{
|
||||
/** Its been down long enough, consider it a new key **/
|
||||
KeyDownTime[key] = now;
|
||||
NewKeys.Add(key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/** How long has it been down? **/
|
||||
if (now - KeyDownTime[key] > 9000000)
|
||||
{
|
||||
/** Its been down long enough, consider it in repeat mode **/
|
||||
KeyDownTime[key] = now;
|
||||
KeyInRepeatMode.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
server/tso.common/Rendering/Framework/Shapes/3DCube.cs
Executable file
86
server/tso.common/Rendering/Framework/Shapes/3DCube.cs
Executable file
|
@ -0,0 +1,86 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework.Shapes
|
||||
{
|
||||
public class _3DCube : _3DComponent
|
||||
{
|
||||
private BasicEffect Effect;
|
||||
|
||||
private VertexPositionColor[] Geom;
|
||||
private List<VertexPositionColor> GeomList;
|
||||
|
||||
private Color Color;
|
||||
private Vector3 Size;
|
||||
|
||||
public _3DCube(Color color, Vector3 size)
|
||||
{
|
||||
this.Color = color;
|
||||
this.Size = size;
|
||||
}
|
||||
|
||||
public override void Initialize(){
|
||||
|
||||
Effect = new BasicEffect(Device);
|
||||
|
||||
/** Bottom Face **/
|
||||
var btmTL = new Vector3(0.0f, 0.0f, 0.0f);
|
||||
var btmTR = new Vector3(Size.X, 0.0f, 0.0f);
|
||||
var btmBR = new Vector3(Size.X, 0.0f, Size.Z);
|
||||
var btmBL = new Vector3(0.0f, 0.0f, Size.Z);
|
||||
|
||||
/** Top face **/
|
||||
var topTL = new Vector3(0.0f, Size.Y, 0.0f);
|
||||
var topTR = new Vector3(Size.X, Size.Y, 0.0f);
|
||||
var topBR = new Vector3(Size.X, Size.Y, Size.Z);
|
||||
var topBL = new Vector3(0.0f, Size.Y, Size.Z);
|
||||
|
||||
|
||||
GeomList = new List<VertexPositionColor>();
|
||||
AddQuad(Color, topTL, topTR, topBR, topBL);
|
||||
AddQuad(Color.Yellow, btmTL, btmTR, btmBR, btmBL);
|
||||
AddQuad(Color.Green, topTL, topTR, btmTR, btmTL);
|
||||
AddQuad(Color.Blue, topBL, topTL, btmTL, btmBL);
|
||||
AddQuad(Color.Orange, topBR, topTR, btmTR, btmBR);
|
||||
AddQuad(Color.White, topBL, topBR, btmBR, btmBL);
|
||||
|
||||
Geom = GeomList.ToArray();
|
||||
}
|
||||
|
||||
private void AddQuad(Color color, Vector3 tl, Vector3 tr, Vector3 br, Vector3 bl)
|
||||
{
|
||||
GeomList.Add(new VertexPositionColor(tl, color));
|
||||
GeomList.Add(new VertexPositionColor(tr, color));
|
||||
GeomList.Add(new VertexPositionColor(br, color));
|
||||
|
||||
GeomList.Add(new VertexPositionColor(br, color));
|
||||
GeomList.Add(new VertexPositionColor(bl, color));
|
||||
GeomList.Add(new VertexPositionColor(tl, color));
|
||||
}
|
||||
|
||||
public override void Draw(GraphicsDevice device)
|
||||
{
|
||||
|
||||
Effect.World = World;
|
||||
Effect.View = View;
|
||||
Effect.Projection = Projection;
|
||||
Effect.VertexColorEnabled = true;
|
||||
//Effect.EnableDefaultLighting();
|
||||
|
||||
foreach (var pass in Effect.CurrentTechnique.Passes)
|
||||
{
|
||||
pass.Apply();
|
||||
device.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, Geom, 0, Geom.Length / 3);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(FSO.Common.Rendering.Framework.Model.UpdateState state)
|
||||
{
|
||||
}
|
||||
|
||||
public override void DeviceReset(GraphicsDevice Device)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
6
server/tso.common/Rendering/Framework/UpdateHookDelegate.cs
Executable file
6
server/tso.common/Rendering/Framework/UpdateHookDelegate.cs
Executable file
|
@ -0,0 +1,6 @@
|
|||
using FSO.Common.Rendering.Framework.Model;
|
||||
|
||||
namespace FSO.Common.Rendering.Framework
|
||||
{
|
||||
public delegate void UpdateHookDelegate(UpdateState state);
|
||||
}
|
21
server/tso.common/Rendering/TextureInfo.cs
Executable file
21
server/tso.common/Rendering/TextureInfo.cs
Executable file
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace FSO.Common.Rendering
|
||||
{
|
||||
public class TextureInfo
|
||||
{
|
||||
public Vector2 UVScale;
|
||||
public Point Size;
|
||||
public Point Diff;
|
||||
|
||||
public TextureInfo() { }
|
||||
|
||||
public TextureInfo(Texture2D tex, int width, int height)
|
||||
{
|
||||
Size = new Point(width, height);
|
||||
Diff = new Point(tex.Width, tex.Height) - Size;
|
||||
UVScale = Size.ToVector2() / new Vector2(tex.Width, tex.Height);
|
||||
}
|
||||
}
|
||||
}
|
69
server/tso.common/Rendering/TimeOfDayConfig.cs
Executable file
69
server/tso.common/Rendering/TimeOfDayConfig.cs
Executable file
|
@ -0,0 +1,69 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace FSO.Common.Rendering
|
||||
{
|
||||
public static class TimeOfDayConfig
|
||||
{
|
||||
public static float DarknessMultiplier = 0.8f;
|
||||
private static Color[] m_TimeColors = new Color[]
|
||||
{
|
||||
new Color(50, 70, 122)*1.25f,
|
||||
new Color(50, 70, 122)*1.25f,
|
||||
new Color(55, 75, 111)*1.25f,
|
||||
new Color(70, 70, 70)*1.25f,
|
||||
new Color(217, 109, 50), //sunrise
|
||||
new Color(255, 255, 255),
|
||||
new Color(255, 255, 255), //peak
|
||||
new Color(255, 255, 255), //peak
|
||||
new Color(255, 255, 255),
|
||||
new Color(255, 255, 255),
|
||||
new Color(217, 109, 50), //sunset
|
||||
new Color(70, 70, 70)*1.25f,
|
||||
new Color(55, 75, 111)*1.25f,
|
||||
new Color(50, 70, 122)*1.25f,
|
||||
};
|
||||
|
||||
private static float[] m_SkyColors = new float[]
|
||||
{
|
||||
4/8f,
|
||||
4/8f,
|
||||
4/8f,
|
||||
5/8f,
|
||||
6/8f, //sunrise
|
||||
7/8f,
|
||||
8/8f, //peak
|
||||
0/8f, //peak
|
||||
0/8f,
|
||||
0/8f,
|
||||
1/8f, //sunset
|
||||
2/8f,
|
||||
3/8f,
|
||||
4/8f,
|
||||
};
|
||||
|
||||
private static Color PowColor(Color col, float pow)
|
||||
{
|
||||
var vec = col.ToVector4();
|
||||
vec.X = (float)Math.Pow(vec.X, pow);
|
||||
vec.Y = (float)Math.Pow(vec.Y, pow);
|
||||
vec.Z = (float)Math.Pow(vec.Z, pow);
|
||||
|
||||
return new Color(vec);
|
||||
}
|
||||
|
||||
public static Color ColorFromTime(double time)
|
||||
{
|
||||
Color col1 = m_TimeColors[(int)Math.Floor(time * (m_TimeColors.Length - 1))]; //first colour
|
||||
Color col2 = m_TimeColors[(int)Math.Floor(time * (m_TimeColors.Length - 1)) + 1]; //second colour
|
||||
if (DarknessMultiplier != 1.0)
|
||||
{
|
||||
col1 = Color.Lerp(Color.White, col1, DarknessMultiplier);
|
||||
col2 = Color.Lerp(Color.White, col2, DarknessMultiplier);
|
||||
}
|
||||
double Progress = (time * (m_TimeColors.Length - 1)) % 1; //interpolation progress (mod 1)
|
||||
|
||||
return PowColor(Color.Lerp(col1, col2, (float)Progress), 2.2f); //linearly interpolate between the two colours for this specific time.
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue