├── .gitignore
├── .gitmodules
├── Content
├── Fonts
│ └── default.xnb
├── HLSL
│ ├── Common.fxh
│ ├── Header.fxh
│ ├── PostProcessEffect.fx
│ ├── SkinnedEffect.fx
│ ├── SpectrumEffect.fx
│ ├── TerrainEffect.fx
│ └── WaterEffect.fx
└── Textures
│ ├── blank.png
│ └── missing.png
├── Entry.cs
├── Framework
├── Audio
│ ├── AudioManager.cs
│ ├── AudioPlayer.cs
│ ├── Emitter.cs
│ ├── MusicPlayer.cs
│ └── SoundEffect.cs
├── Color.cs
├── Content
│ ├── ContentHelper.cs
│ ├── ContentParser.cs
│ ├── EffectParser.cs
│ ├── HeightmapParser.cs
│ ├── ImageAsset.cs
│ ├── ImageAssetParser.cs
│ ├── InitDataParser.cs
│ ├── MGCParser.cs
│ ├── MemberContentAttribute.cs
│ ├── ModelParsing
│ │ ├── AnimationParser.cs
│ │ ├── G3DJReader.cs
│ │ ├── ModelParser.cs
│ │ └── OBJReader.cs
│ ├── Plugin.cs
│ ├── SVGParser.cs
│ ├── ScreenParser.cs
│ ├── ScriptAsset.cs
│ ├── ScriptParser.cs
│ ├── Scripting
│ │ └── CSScript.cs
│ ├── SoundParser.cs
│ └── Texture2DParser.cs
├── Context.cs
├── Debug
│ ├── DebugPrinter.cs
│ └── DebugTiming.cs
├── DefaultDict.cs
├── Entities
│ ├── Component.cs
│ ├── Entity.cs
│ ├── EntityCollection.cs
│ ├── EntityManager.cs
│ ├── GameObject.cs
│ ├── GameObject2D.cs
│ ├── InitData.cs
│ └── Interpolator.cs
├── FunctionalExtensions.cs
├── Graphics
│ ├── Animation
│ │ ├── AnimationClip.cs
│ │ ├── AnimationData.cs
│ │ ├── AnimationPlayer.cs
│ │ ├── Bone.cs
│ │ ├── Keyframe.cs
│ │ └── SkinningData.cs
│ ├── Batch3D.cs
│ ├── Billboard.cs
│ ├── Camera.cs
│ ├── DrawablePart.cs
│ ├── Effects
│ │ ├── PostProcessEffect.cs
│ │ ├── SkinnedEffect.cs
│ │ ├── SpectrumEffect.cs
│ │ ├── TerrainEffect.cs
│ │ └── WaterEffect.cs
│ ├── GraphicsEngine.cs
│ ├── RenderTask.cs
│ ├── Settings.cs
│ ├── SpecModel.cs
│ ├── VertexHelper.cs
│ ├── VertexTypes.cs
│ └── Water.cs
├── IDebug.cs
├── Input
│ ├── Axis.cs
│ ├── Gamepad.cs
│ ├── InputLayout.cs
│ ├── InputState.cs
│ ├── KeyBinding.cs
│ ├── PlayerInformation.cs
│ ├── RawMouse.cs
│ ├── SpectrumMouse.cs
│ └── VR
│ │ ├── VRBinding.cs
│ │ ├── VRController.cs
│ │ └── VRHmd.cs
├── JConvert.cs
├── JSON
│ ├── JInitDataConverter.cs
│ ├── JMatrixConverter.cs
│ ├── JPointConverter.cs
│ ├── JShapeConverter.cs
│ ├── JVectorConverter.cs
│ └── SimpleConverter.cs
├── LSystem
│ ├── LNode.cs
│ └── LTree.cs
├── LoadHelper.cs
├── Matrix.cs
├── Network
│ ├── Connection.cs
│ ├── Handshake.cs
│ ├── MultiplayerReceiver.cs
│ ├── MultiplayerSender.cs
│ ├── MultiplayerService.cs
│ ├── NetID.cs
│ ├── NetMessage.cs
│ ├── NetworkMutex.cs
│ ├── ReplicationData.cs
│ ├── ReplyWaiter.cs
│ ├── Serialization.cs
│ ├── SteamP2PReceiver.cs
│ ├── Surrogates
│ │ ├── EntitySurrogate.cs
│ │ ├── FloatArraySurrogate.cs
│ │ ├── JSONSurrogate.cs
│ │ ├── ModelSurrogate.cs
│ │ ├── PrimitiveSurrogate.cs
│ │ └── StreamSurrogate.cs
│ ├── UDPReceiver.cs
│ └── UDPSender.cs
├── Physics
│ ├── Collision
│ │ ├── CollisionIsland.cs
│ │ ├── CollisionSystem.cs
│ │ ├── CollisionSystemPersistentSAP.cs
│ │ ├── DynamicTree.cs
│ │ ├── EPACollide.cs
│ │ ├── GJKCollide.cs
│ │ ├── GroundInfo.cs
│ │ ├── ICollidable.cs
│ │ ├── IslandManager.cs
│ │ ├── Octree.cs
│ │ └── Shapes
│ │ │ ├── BoxShape.cs
│ │ │ ├── ConeShape.cs
│ │ │ ├── ConvexHullShape.cs
│ │ │ ├── CylinderShape.cs
│ │ │ ├── ISupportMappable.cs
│ │ │ ├── ListMultishape.cs
│ │ │ ├── Multishape.cs
│ │ │ ├── Shape.cs
│ │ │ ├── SphereShape.cs
│ │ │ └── TerrainShape.cs
│ ├── Dynamics
│ │ ├── Arbiter.cs
│ │ ├── ArbiterMap.cs
│ │ ├── Constraint.cs
│ │ ├── Contact.cs
│ │ └── Material.cs
│ ├── LinearMath
│ │ ├── JBBox.cs
│ │ └── JMath.cs
│ ├── PhysicsEngine.cs
│ └── ThreadManager.cs
├── Point.cs
├── Quaternion.cs
├── Ray.cs
├── Rectangle.cs
├── ResourcePool.cs
├── Schedule.cs
├── Screens
│ ├── DialogScreen.cs
│ ├── Element.cs
│ ├── ElementSelector.cs
│ ├── ElementSize.cs
│ ├── ElementStyle.cs
│ ├── GridLayout.cs
│ ├── InGameScreen.cs
│ ├── InputElements
│ │ ├── Button.cs
│ │ ├── Checkbox.cs
│ │ ├── Dropdown.cs
│ │ ├── InputElement.cs
│ │ ├── ListOption.cs
│ │ ├── ListSelector.cs
│ │ ├── SelectionWheel.cs
│ │ ├── Slider.cs
│ │ ├── TextElement.cs
│ │ └── TextInput.cs
│ ├── LayoutManager.cs
│ ├── LinearLayout.cs
│ ├── MenuScreen.cs
│ ├── RectOffset.cs
│ ├── RootElement.cs
│ └── SceneScreen.cs
├── SpectrumGame.cs
├── StateMachine.cs
├── Transform.cs
├── TypeHelper.cs
├── Utility
│ ├── ArgParseHelper.cs
│ ├── ExtensionMethods.cs
│ ├── MathHelper.cs
│ ├── MatrixHelper.cs
│ ├── Point3.cs
│ ├── RectangleExtensions.cs
│ ├── SpriteBatchExtensions.cs
│ ├── VectorExtensions.cs
│ └── WinUtil.cs
├── VR
│ ├── SpecVR.cs
│ └── VRMenu.cs
├── Vector2.cs
├── Vector3.cs
└── Vector4.cs
├── LICENSE
├── Spectrum.csproj
├── SpectrumTest.sln
├── Test
├── BenchmarkTests.cs
├── ElementTest.cs
├── InitDataTests.cs
├── JSONTests.cs
├── MathExtensionTests.cs
├── PhysicsTests.cs
├── SerializationTests.cs
└── SpectrumTest.csproj
├── mgfx.targets.xml
├── openvr_api.cs
└── openvr_api.dll
/.gitignore:
--------------------------------------------------------------------------------
1 | *.cachefile
2 | *.zip
3 | *.mgfx
4 | *.vs
5 | bin
6 | obj
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Dependencies"]
2 | path = Dependencies
3 | url = https://github.com/alex-sherman/spectrumdependencies
4 | [submodule "Replicate"]
5 | path = Replicate
6 | url = https://github.com/alex-sherman/Replicate
7 |
--------------------------------------------------------------------------------
/Content/Fonts/default.xnb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alex-sherman/Spectrum/3030b61f5eb961815100b23dcceb99adfff22be9/Content/Fonts/default.xnb
--------------------------------------------------------------------------------
/Content/HLSL/SkinnedEffect.fx:
--------------------------------------------------------------------------------
1 | #include "Common.fxh"
2 |
3 | #define SKINNED_EFFECT_MAX_BONES 64
4 | float4x3 Bones[SKINNED_EFFECT_MAX_BONES];
5 |
6 | void Skin(inout CommonVSInput vin, float4 Indices, float4 Weights, uniform int boneCount)
7 | {
8 | float4x3 skinning = 0;
9 | [unroll]
10 | for (int i = 0; i < boneCount; i++)
11 | {
12 | skinning += Bones[Indices[i]] * Weights[i];
13 | }
14 |
15 | vin.Position.xyz = mul(vin.Position, skinning);
16 | vin.normal = mul(vin.normal, (float3x3) skinning);
17 | vin.tangent = mul(vin.tangent, (float3x3) skinning);
18 | }
19 |
20 | CommonVSOut SkinnedVS(CommonVSInput input, float4 Indices : BLENDINDICES0, float4 Weights : BLENDWEIGHT0)
21 | {
22 | Skin(input, Indices, Weights, 4);
23 | return Transform(input);
24 | }
25 |
26 | CommonVSOut DepthTransform(CommonVSInput vin, float4 Indices : BLENDINDICES0, float4 Weights : BLENDWEIGHT0)
27 | {
28 | Skin(vin, Indices, Weights, 4);
29 | CommonVSOut Out = (CommonVSOut) 0;
30 | float4 worldPosition = CommonVS((CommonVSInput) vin, (CommonVSOut) Out);
31 | return Out;
32 | }
33 | float4 Depth(CommonVSOut vsout) : COLOR0
34 | {
35 | return float4(1 - vsout.Pos2DAsSeenByLight.z / vsout.Pos2DAsSeenByLight.w, 0, 0, 1);
36 | }
37 | technique Standard
38 | {
39 | pass P0
40 | {
41 | vertexShader = compile vs_4_0 SkinnedVS();
42 | pixelShader = compile ps_4_0 ApplyTexture();
43 | }
44 | }
45 | technique ShadowMap
46 | {
47 | pass P0
48 | {
49 | vertexShader = compile vs_4_0 DepthTransform();
50 | pixelShader = compile ps_4_0 Depth();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Content/HLSL/SpectrumEffect.fx:
--------------------------------------------------------------------------------
1 | #include "Common.fxh"
2 |
3 | float4 Depth(CommonVSOut vsout) : COLOR0 {
4 | return float4(1 - vsout.Pos2DAsSeenByLight.z / vsout.Pos2DAsSeenByLight.w, 0, 0, 1);
5 | }
6 |
7 | technique Standard
8 | {
9 | pass P0
10 | {
11 | vertexShader = compile vs_4_0 Transform();
12 | pixelShader = compile ps_4_0 ApplyTexture();
13 | }
14 | }
15 | technique StandardInstance
16 | {
17 | pass P0
18 | {
19 | vertexShader = compile vs_4_0 InstanceTransform();
20 | pixelShader = compile ps_4_0 ApplyTexture();
21 | }
22 | }
23 | technique ShadowMap
24 | {
25 | pass P0
26 | {
27 | vertexShader = compile vs_4_0 Transform();
28 | pixelShader = compile ps_4_0 Depth();
29 | }
30 | }
31 | technique ShadowMapInstance
32 | {
33 | pass P0
34 | {
35 | vertexShader = compile vs_4_0 InstanceTransform();
36 | pixelShader = compile ps_4_0 Depth();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Content/Textures/blank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alex-sherman/Spectrum/3030b61f5eb961815100b23dcceb99adfff22be9/Content/Textures/blank.png
--------------------------------------------------------------------------------
/Content/Textures/missing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alex-sherman/Spectrum/3030b61f5eb961815100b23dcceb99adfff22be9/Content/Textures/missing.png
--------------------------------------------------------------------------------
/Entry.cs:
--------------------------------------------------------------------------------
1 | using Spectrum.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum
9 | {
10 | ///
11 | /// The main class.
12 | ///
13 | public static class Entry where T : SpectrumGame, new()
14 | {
15 | ///
16 | /// The main entry point for the application.
17 | ///
18 | [STAThread]
19 | public static void Main(string[] args)
20 | {
21 | LoadHelper.SetMainAssembly();
22 | #if !DEBUG
23 | try
24 | {
25 | #endif
26 | using (var game = new T())
27 | game.Run();
28 | #if !DEBUG
29 | }
30 | catch (Exception e)
31 | {
32 | DebugPrinter.Print(e.ToString());
33 | }
34 | #endif
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Framework/Audio/AudioManager.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using SharpDX.Mathematics.Interop;
3 | using SharpDX.X3DAudio;
4 | using SharpDX.XAudio2;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 |
10 | namespace Spectrum.Framework.Audio
11 | {
12 | public class AudioManager
13 | {
14 | internal static RawVector3 V3ToV3(Vector3 vector)
15 | {
16 | return new RawVector3(vector.X, vector.Y, vector.Z);
17 | }
18 | internal static Vector3 V3ToV3(RawVector3 vector)
19 | {
20 | return new Vector3(vector.X, vector.Y, vector.Z);
21 | }
22 | internal static Listener Listener;
23 | internal static readonly XAudio2 _xaudio2 = new XAudio2();
24 | internal static readonly X3DAudio X3DAudio = new X3DAudio(SharpDX.Multimedia.Speakers.Stereo);
25 | internal static MasteringVoice MasteringVoice = new MasteringVoice(_xaudio2);
26 | public static int DestinationChannels { get { return 2; } }
27 |
28 | public static Vector3 ListenerPosition
29 | {
30 | get => V3ToV3(Listener.Position);
31 | set => Listener.Position = V3ToV3(value);
32 | }
33 | public static Vector3 ListenerForward
34 | {
35 | get => V3ToV3(Listener.OrientFront);
36 | set => Listener.OrientFront = V3ToV3(value);
37 | }
38 | public static Vector3 ListenerUp
39 | {
40 | get => V3ToV3(Listener.OrientTop);
41 | set => Listener.OrientTop = V3ToV3(value);
42 | }
43 |
44 | public static void Init()
45 | {
46 | Listener = new Listener()
47 | {
48 | Position = new RawVector3(0, 0, 0),
49 | OrientFront = new RawVector3(0, 0, 1),
50 | OrientTop = new RawVector3(0, 1, 0),
51 | Velocity = new RawVector3(0, 0, 0)
52 | };
53 | }
54 |
55 | public static void Shutdown()
56 | {
57 | _xaudio2.Dispose();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Framework/Audio/Emitter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using SharpDX.Mathematics.Interop;
3 | using SharpDX.X3DAudio;
4 | using Spectrum.Framework.Entities;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 |
10 | namespace Spectrum.Framework.Audio
11 | {
12 | public class SoundEmitter : AudioPlayer
13 | {
14 | private Emitter _emitter;
15 |
16 | public Vector3 Position
17 | {
18 | get { return AudioManager.V3ToV3(_emitter.Position); }
19 | set { _emitter.Position = AudioManager.V3ToV3(value); }
20 | }
21 |
22 | public Vector3 Forward
23 | {
24 | get { return AudioManager.V3ToV3(_emitter.OrientFront); }
25 | set { _emitter.OrientFront = AudioManager.V3ToV3(value); }
26 | }
27 |
28 | public Vector3 Up
29 | {
30 | get { return AudioManager.V3ToV3(_emitter.OrientTop); }
31 | set { _emitter.OrientTop = AudioManager.V3ToV3(value); }
32 | }
33 |
34 | public SoundEmitter()
35 | {
36 | _emitter = new Emitter()
37 | {
38 | ChannelCount = 1,
39 | CurveDistanceScaler = 20,
40 | OrientFront = new RawVector3(0, 0, 1),
41 | OrientTop = new RawVector3(0, 1, 0),
42 | Position = new RawVector3(0, 0, 0),
43 | Velocity = new RawVector3(0, 0, 0)
44 | };
45 | }
46 |
47 | public void Update(GameObject emitted)
48 | {
49 | Position = emitted.position;
50 | Up = Vector3.Up;
51 | Forward = Vector3.Forward;
52 | if (voice != null)
53 | {
54 | DspSettings dspSettings = new DspSettings(1, AudioManager.DestinationChannels);
55 | AudioManager.X3DAudio.Calculate(AudioManager.Listener, _emitter, CalculateFlags.Matrix, dspSettings);
56 | voice.SetOutputMatrix(1, AudioManager.DestinationChannels, dspSettings.MatrixCoefficients);
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Framework/Audio/MusicPlayer.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Input;
3 | using Spectrum.Framework.Entities;
4 | using Spectrum.Framework.Input;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace Spectrum.Framework.Audio
12 | {
13 | public class MusicPlayer : Entity
14 | {
15 | AudioPlayer player = new AudioPlayer();
16 | public IReadOnlyList Tracks => tracks;
17 | private int trackIndex = 0;
18 | public SoundEffect CurrentTrack => Tracks.FirstOrDefault();
19 | List tracks = new List();
20 | public bool Repeat;
21 |
22 | public float Volume
23 | {
24 | get => player.Volume;
25 | set => player.Volume = value;
26 | }
27 |
28 | float fadeVolumeInitial;
29 | float fadeTimeInitial;
30 | float fadeTime;
31 | float fadeTarget;
32 |
33 | public MusicPlayer()
34 | {
35 | AllowReplicate = false;
36 | player.Loop = false;
37 | player.OnTrackEnd += Next;
38 | }
39 | public void SetTracks(IEnumerable tracks)
40 | {
41 | this.tracks = tracks.ToList();
42 | player.SoundEffect = CurrentTrack;
43 | }
44 | public void Next()
45 | {
46 | trackIndex++;
47 | if (trackIndex >= tracks.Count)
48 | {
49 | if (Repeat)
50 | trackIndex = 0;
51 | else
52 | return;
53 | }
54 | player.SoundEffect = tracks[trackIndex];
55 | player.State = AudioState.Playing;
56 | }
57 | public void Previous()
58 | {
59 | trackIndex--;
60 | if (trackIndex < 0)
61 | {
62 | if (Repeat)
63 | trackIndex = tracks.Count - 1;
64 | else
65 | trackIndex = 0;
66 | }
67 | player.SoundEffect = tracks[trackIndex];
68 | }
69 | public void FadeTo(float volumeTarget, float time)
70 | {
71 | fadeTarget = volumeTarget;
72 | fadeVolumeInitial = Volume;
73 | fadeTimeInitial = fadeTime = time;
74 | }
75 | public AudioState State
76 | {
77 | get => player.State;
78 | set
79 | {
80 | player.Volume = Volume;
81 | player.State = value;
82 | }
83 | }
84 | public override void Update(float dt)
85 | {
86 | base.Update(dt);
87 | if (fadeTimeInitial != 0)
88 | {
89 | fadeTime -= dt;
90 | if (fadeTime > 0)
91 | {
92 | var w = fadeTime / fadeTimeInitial;
93 | Volume = fadeVolumeInitial * w + fadeTarget * (1 - w);
94 | }
95 | else
96 | {
97 | fadeTimeInitial = 0;
98 | Volume = fadeTarget;
99 | }
100 | }
101 | var track = tracks.FirstOrDefault();
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Framework/Audio/SoundEffect.cs:
--------------------------------------------------------------------------------
1 | using SharpDX;
2 | using SharpDX.MediaFoundation;
3 | using SharpDX.Multimedia;
4 | using SharpDX.XAudio2;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text;
10 |
11 | namespace Spectrum.Framework.Audio
12 | {
13 | public class SoundEffect
14 | {
15 | public List Samples;
16 | public WaveFormat WaveFormat;
17 |
18 | public SoundEffect(Stream stream)
19 | {
20 | AudioDecoder audioDecoder = new AudioDecoder(stream);
21 | WaveFormat = audioDecoder.WaveFormat;
22 | // TODO: This is super slow but copying the stream/list doesn't seem to satisfy SharpDX
23 | Samples = audioDecoder.GetSamples().Select(sample =>
24 | {
25 | var output = new DataPointer(SharpDX.Utilities.AllocateMemory(sample.Size), sample.Size);
26 | SharpDX.Utilities.CopyMemory(output.Pointer, sample.Pointer, sample.Size);
27 | return output;
28 | }).ToList();
29 | }
30 | ~SoundEffect()
31 | {
32 | if (Samples != null)
33 | foreach (var sample in Samples)
34 | SharpDX.Utilities.FreeMemory(sample.Pointer);
35 |
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Framework/Color.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework
8 | {
9 | public struct Color
10 | {
11 | public byte R { get => (byte)(storage & 0x000000ff); set { storage = (storage & 0xffffff00) | value; } }
12 | public byte G { get => (byte)((storage & 0x0000ff00) >> 8); set { storage = (storage & 0xffff00ff) | (uint)value << 8; } }
13 | public byte B { get => (byte)((storage & 0x00ff0000) >> 16); set { storage = (storage & 0xff00ffff) | (uint)value << 16; } }
14 | public byte A { get => (byte)((storage & 0xff000000) >> 24); set { storage = (storage & 0x00ffffff) | (uint)value << 24; } }
15 | // ABGR storage
16 | // 0xAA BB GG RR
17 | private uint storage;
18 | public Color(byte r, byte g, byte b, byte a) { storage = 0; R = r; G = g; B = b; A = a; }
19 | public Color(Color c, byte a) { storage = 0; R = c.R; G = c.G; B = c.B; A = a; }
20 | public Color(Color c, float a) { storage = 0; R = c.R; G = c.G; B = c.B; A = (byte)(a * 255); }
21 | public Color(Vector4 v) : this((byte)(v.X * 255), (byte)(v.Y * 255), (byte)(v.Z * 255), (byte)(v.W * 255)) { }
22 | public Color(Vector3 v) : this((byte)(v.X * 255), (byte)(v.Y * 255), (byte)(v.Z * 255), 255) { }
23 | public Color(float r, float g, float b) : this((byte)(r * 255), (byte)(g * 255), (byte)(b * 255), 255) { }
24 | public Vector4 ToVector4() => new Vector4(R / 255f, G / 255f, B / 255f, A / 255f);
25 | public Vector3 ToVector3() => new Vector3(R / 255f, G / 255f, B / 255f);
26 | public static Color FromString(string value)
27 | {
28 | try
29 | {
30 | System.Drawing.Color color = System.Drawing.ColorTranslator.FromHtml(value);
31 | return new Color((byte)(color.R * color.A / 255), (byte)(color.G * color.A / 255), (byte)(color.B * color.A / 255), color.A);
32 | }
33 | catch (Exception)
34 | {
35 | return Black;
36 | }
37 | }
38 | public static implicit operator Microsoft.Xna.Framework.Color(Color color) => new Microsoft.Xna.Framework.Color(color.R, color.G, color.B, color.A);
39 | public static implicit operator Color(Microsoft.Xna.Framework.Color color) => new Color(color.R, color.G, color.B, color.A);
40 | public static implicit operator Color(string color) => FromString(color);
41 | public static Color Transparent => new Color(0, 0, 0, 0);
42 | public static Color Black => new Color(0, 0, 0, 255);
43 | public static Color Red => new Color(255, 0, 0, 255);
44 | public static Color Green => new Color(0, 255, 0, 255);
45 | public static Color Blue => new Color(0, 0, 255, 255);
46 | public static Color White => new Color(255, 255, 255, 255);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Framework/Content/EffectParser.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework.Graphics;
2 | using Spectrum.Framework.Graphics;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Spectrum.Framework.Content
11 | {
12 | class EffectParser : CachedContentParser
13 | {
14 | public EffectParser() : base("mgfx")
15 | {
16 | Prefix = "HLSL";
17 | }
18 | protected override Effect LoadData(string path, string name)
19 | {
20 | using (var f = new FileStream(path, FileMode.Open, FileAccess.Read))
21 | {
22 | byte[] output = new byte[f.Length];
23 | f.Read(output, 0, (int)f.Length);
24 | return new Effect(SpectrumGame.Game.GraphicsDevice, output);
25 | }
26 | }
27 |
28 | protected override Effect SafeCopy(Effect toClone)
29 | {
30 | return toClone.Clone();
31 | }
32 | }
33 | class SpectrumEffectParser : EffectParser
34 | {
35 | protected override Effect SafeCopy(Effect toClone)
36 | {
37 | return new SpectrumEffect(toClone);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Framework/Content/ImageAsset.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Svg;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Spectrum.Framework.Content
11 | {
12 | public class ImageAsset
13 | {
14 | public static readonly ImageAsset Blank;
15 | public static readonly ImageAsset Missing;
16 | static ImageAsset()
17 | {
18 | Blank = new ImageAsset(ContentHelper.Blank);
19 | Missing = new ImageAsset(ContentHelper.Missing);
20 | }
21 | public SvgDocument SVG = null;
22 | public Texture2D Texture = null;
23 | private Texture2D rasterized = null;
24 | // Marking as public requires a reference to SVG to resolve method
25 | public ImageAsset(SvgDocument svg) { SVG = svg; }
26 | public ImageAsset(Texture2D texture) { Texture = texture; }
27 | public static implicit operator ImageAsset(Texture2D texture) => new ImageAsset(texture);
28 | public static implicit operator ImageAsset(string path)
29 | {
30 | return ContentHelper.Load(path) ?? Missing;
31 | }
32 | public ImageAsset() { }
33 | public void Rasterize(int width, int height)
34 | {
35 | System.Drawing.Bitmap bitmap = SVG.Draw(width, height);
36 | rasterized?.Dispose();
37 | rasterized = bitmap.GetTexture2DFromBitmap(SpectrumGame.Game.GraphicsDevice);
38 | }
39 | public Texture2D GetTexture(Rectangle rect)
40 | {
41 | if (SVG != null)
42 | {
43 | if (rasterized == null || rasterized.Bounds.Width != rect.Width || rasterized.Bounds.Height != rect.Height)
44 | {
45 | Rasterize(rect.Width, rect.Height);
46 | }
47 | return rasterized;
48 | }
49 | else if (Texture != null)
50 | {
51 | return Texture;
52 | }
53 | return null;
54 | }
55 | public ImageAsset Clone()
56 | {
57 | if (SVG != null)
58 | return new ImageAsset(SVG);
59 | return new ImageAsset(Texture);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Framework/Content/ImageAssetParser.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework.Graphics;
2 | using Svg;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Spectrum.Framework.Content
11 | {
12 | public class ImageAssetParser : CachedContentParser
13 | {
14 | private static Dictionary additionalAssets = new Dictionary();
15 | public static void Add(string path, ImageAsset asset) => additionalAssets[path] = asset;
16 | public ImageAssetParser() : base("svg", "png", "jpg")
17 | {
18 | Prefix = "Textures";
19 | }
20 | protected override string ResolvePath(string path, string name)
21 | {
22 | if (additionalAssets.ContainsKey(name)) return path;
23 | return base.ResolvePath(path, name);
24 | }
25 | protected override ImageAsset LoadData(string path, string name)
26 | {
27 | if (additionalAssets.TryGetValue(name, out var result)) return result;
28 | var extension = Path.GetExtension(path).Substring(1);
29 | if (extension == "svg")
30 | return new ImageAsset(SvgDocument.Open(path));
31 | else
32 | return new ImageAsset(Texture2DParser.LoadFromPath(path));
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Framework/Content/InitDataParser.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using Spectrum.Framework.Entities;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace Spectrum.Framework.Content
13 | {
14 | public class InitDataParser : CachedContentParser
15 | {
16 | public InitDataParser() : base("json")
17 | {
18 | Prefix = "InitData";
19 | }
20 | protected override InitData LoadData(string path, string name)
21 | {
22 | using (var reader = new StreamReader(File.OpenRead(path)))
23 | {
24 | var output = JConvert.Deserialize(reader.ReadToEnd()).ToImmutable();
25 | if (output.Name == null) output.Name = name;
26 | output.Path = name;
27 | output.FullPath = path;
28 | return output;
29 | }
30 | }
31 |
32 | protected override InitData SafeCopy(InitData data)
33 | {
34 | return data;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Framework/Content/MGCParser.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework.Content;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework.Content
9 | {
10 | public class MGCParser : IContentParser
11 | {
12 | public string Prefix { get; set; }
13 | string Suffix;
14 | public MGCParser(string prefix, string suffix)
15 | {
16 | Prefix = prefix;
17 | Suffix = suffix;
18 | }
19 | public object Load(string path, string name, bool skipCache)
20 | {
21 | return SpectrumGame.Game.Content.Load(path + Suffix);
22 | }
23 |
24 | public void Clear() { }
25 |
26 | public IEnumerable FindAll(string directory, string glob = "*", bool recursive = true)
27 | {
28 | return Enumerable.Empty();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Framework/Content/MemberContentAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Security.Cryptography.X509Certificates;
5 | using System.Text;
6 |
7 | namespace Spectrum.Framework.Content
8 | {
9 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
10 | public class MemberContentAttribute : Attribute
11 | {
12 | public string Path { get; private set; }
13 | public MemberContentAttribute(string path)
14 | {
15 | Path = path;
16 | }
17 | }
18 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
19 | public class ClassContentAttribute : Attribute
20 | {
21 | public string Member { get; private set; }
22 | public string Path { get; private set; }
23 | public ClassContentAttribute(string member, string path)
24 | {
25 | Member = member;
26 | Path = path;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Framework/Content/ModelParsing/AnimationParser.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using Spectrum.Framework.Graphics.Animation;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text;
10 |
11 | namespace Spectrum.Framework.Content.ModelParsing
12 | {
13 | class AnimationParser : MultiContentParser
14 | {
15 | public AnimationParser() : base(new Dictionary() {
16 | { "g3dj", G3DJReader.LoadAnimation },
17 | })
18 | {
19 | Prefix = "Models";
20 | }
21 |
22 | protected override AnimationData SafeCopy(AnimationData data)
23 | {
24 | return data;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Framework/Content/ModelParsing/ModelParser.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Newtonsoft.Json;
4 | using Newtonsoft.Json.Linq;
5 | using Spectrum.Framework.Content.ModelParsing;
6 | using Spectrum.Framework.Graphics;
7 | using Spectrum.Framework.Graphics.Animation;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.IO;
11 | using System.Linq;
12 | using System.Text;
13 | using System.Text.RegularExpressions;
14 |
15 | namespace Spectrum.Framework.Content.ModelParsing
16 | {
17 | interface IModelReader
18 | {
19 | ModelParserCache LoadData(string path, string name);
20 | }
21 |
22 | class ModelParserCache
23 | {
24 | public SkinningData skinningData;
25 | public Dictionary parts = new Dictionary();
26 | public Dictionary materials = new Dictionary();
27 | public AnimationData animations = new AnimationData();
28 | public List vertexAttributes = new List();
29 | public string Directory;
30 | public string FileName;
31 | public string Name;
32 |
33 | public ModelParserCache(string name, string path)
34 | {
35 | Directory = Path.GetDirectoryName(path);
36 | FileName = path;
37 | Name = name;
38 | }
39 | }
40 | struct BoneWeight
41 | {
42 | public int Index;
43 | public float Weight;
44 | public BoneWeight(int index, float weight)
45 | {
46 | Index = index;
47 | Weight = weight;
48 | }
49 | }
50 | class ModelParser : MultiContentParser
51 | {
52 | public ModelParser() : base(new Dictionary() {
53 | { "g3dj", G3DJReader.LoadModel },
54 | { "obj", OBJReader.LoadData },
55 | })
56 | {
57 | Prefix = "Models";
58 | }
59 |
60 | protected override SpecModel SafeCopy(ModelParserCache data)
61 | {
62 | Dictionary parts = new Dictionary();
63 | foreach (KeyValuePair part in data.parts)
64 | {
65 | parts[part.Key] = part.Value.CreateReference();
66 | // TODO: If SkinnedEffect handled instancing correctly this could be avoided
67 | if (data.skinningData != null)
68 | parts[part.Key].effect = parts[part.Key].effect.Clone() as SpectrumEffect;
69 | }
70 | SpecModel model = new SpecModel(data.Name, data.FileName, parts, data.materials, data.skinningData?.Clone())
71 | {
72 | Animations = data.animations
73 | };
74 | return model;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Framework/Content/SVGParser.cs:
--------------------------------------------------------------------------------
1 | using Svg;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework.Content
9 | {
10 | class SVGParser : CachedContentParser
11 | {
12 | protected override SvgDocument LoadData(string path, string name)
13 | {
14 | return SvgDocument.Open(path);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Framework/Content/ScreenParser.cs:
--------------------------------------------------------------------------------
1 | using Spectrum.Framework.Screens;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Text.RegularExpressions;
8 |
9 | namespace Spectrum.Framework.Content
10 | {
11 | //class ScreenParser : CachedContentParser
12 | //{
13 | // protected override string LoadData(string path)
14 | // {
15 | // FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
16 | // return "";
17 | // }
18 |
19 | // protected override GameScreen SafeCopy(string data)
20 | // {
21 | // Regex regex = new Regex("");
22 | // Match match = regex.Match(data);
23 | // match.
24 |
25 | // return null;
26 | // }
27 | //}
28 | }
29 |
--------------------------------------------------------------------------------
/Framework/Content/ScriptAsset.cs:
--------------------------------------------------------------------------------
1 | using Spectrum.Framework.Content.Scripting;
2 | using Spectrum.Framework.Entities;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Runtime.Remoting;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace Spectrum.Framework.Content
13 | {
14 | public class ScriptAsset
15 | {
16 | public readonly CSScript Script;
17 | public string Path { get; private set; }
18 | public ScriptAsset(string path, CSScript script)
19 | {
20 | Path = path;
21 | Script = script;
22 | }
23 | public static implicit operator Component(ScriptAsset script)
24 | {
25 | return script.Script.GetComponent();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Framework/Content/ScriptParser.cs:
--------------------------------------------------------------------------------
1 | using Spectrum.Framework.Content.Scripting;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Spectrum.Framework.Content
10 | {
11 | public class ScriptParser : CachedContentParser
12 | {
13 | public ScriptParser() : base("cs")
14 | {
15 | Prefix = "Scripts";
16 | }
17 | protected override ScriptAsset LoadData(string path, string name)
18 | {
19 | return new ScriptAsset(path, new CSScript(path));
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Framework/Content/Scripting/CSScript.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CSharp;
2 | using Spectrum.Framework.Entities;
3 | using System;
4 | using System.CodeDom.Compiler;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace Spectrum.Framework.Content.Scripting
12 | {
13 | public class CSScript
14 | {
15 | Assembly assembly;
16 | ConstructorInfo constructor;
17 | public CSScript(params string[] filenames)
18 | {
19 | CSharpCodeProvider provider = new CSharpCodeProvider();
20 | CompilerParameters parameters = new CompilerParameters()
21 | {
22 | GenerateInMemory = true,
23 | GenerateExecutable = false,
24 | };
25 | parameters.ReferencedAssemblies.Add("Spectrum.dll");
26 | parameters.ReferencedAssemblies.Add("System.dll");
27 | parameters.ReferencedAssemblies.Add("System.Linq.dll");
28 | parameters.ReferencedAssemblies.Add("MonoGame.Framework.dll");
29 | parameters.IncludeDebugInformation = true;
30 | parameters.CompilerOptions = "/d:DEBUG";
31 | var result = provider.CompileAssemblyFromFile(parameters, filenames);
32 | if (result.Errors.HasErrors)
33 | {
34 | var errors = result.Errors;
35 | var text = Enumerable.Range(0, errors.Count).Select(i => errors[i].ErrorText).ToList();
36 | throw new Exception(string.Join("\n", text));
37 | }
38 | assembly = result.CompiledAssembly;
39 | var candidateTypes = assembly.DefinedTypes
40 | .Where(typeof(Component).IsAssignableFrom).ToList();
41 | var d = AppDomain.CurrentDomain;
42 | if (candidateTypes.Count != 1)
43 | throw new InvalidOperationException(
44 | $"C# scripts should contain exactly 1 Component type, found {candidateTypes.Count}");
45 | constructor = candidateTypes.First().GetConstructor(new Type[] { });
46 | }
47 |
48 | public Component GetComponent()
49 | {
50 | return constructor.Invoke(new object[] { }) as Component;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Framework/Content/SoundParser.cs:
--------------------------------------------------------------------------------
1 | using SharpDX.IO;
2 | using SharpDX.MediaFoundation;
3 | using SharpDX.Multimedia;
4 | using Spectrum.Framework.Audio;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text;
10 |
11 | namespace Spectrum.Framework.Content
12 | {
13 | class SoundParser : CachedContentParser
14 | {
15 | public SoundParser() : base("wav", "m4a")
16 | {
17 | Prefix = "Sounds";
18 | }
19 |
20 | protected override SoundEffect LoadData(string path, string name)
21 | {
22 | var nativefilestream = new NativeFileStream(
23 | path,
24 | NativeFileMode.Open,
25 | NativeFileAccess.Read,
26 | NativeFileShare.Read);
27 | return new SoundEffect(nativefilestream);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Framework/Content/Texture2DParser.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 |
9 | namespace Spectrum.Framework.Content
10 | {
11 | public class Texture2DData
12 | {
13 | public bool HasAlpha = false;
14 | }
15 | public class Texture2DParser : CachedContentParser
16 | {
17 | public Texture2DParser() : base("jpg", "png")
18 | {
19 | Prefix = "Textures";
20 | }
21 | static bool IsPowerOfTwo(int x)
22 | {
23 | return (x & (x - 1)) == 0;
24 | }
25 | public static Texture2D LoadFromPath(string full_path)
26 | {
27 | Texture2D loaded = Texture2D.FromStream(SpectrumGame.Game.GraphicsDevice, new FileStream(full_path, FileMode.Open, FileAccess.Read));
28 | //Of course you have to generate your own mip maps when you import from a file
29 | //why not. Thanks Monogame.
30 | bool mipMap = IsPowerOfTwo(loaded.Width) && IsPowerOfTwo(loaded.Height);
31 | Texture2D output = new Texture2D(SpectrumGame.Game.GraphicsDevice, loaded.Width, loaded.Height, mipMap, loaded.Format);
32 | Texture2DData outputTag = new Texture2DData();
33 | output.Tag = outputTag;
34 | Color[] data = new Color[loaded.Height * loaded.Width];
35 | Color[] lastLevelData = data;
36 | loaded.GetData(data);
37 | for (int level = 0; level < output.LevelCount; level++)
38 | {
39 | int stride = 1 << level;
40 | int levelHeight = loaded.Height / stride;
41 | int levelWidth = loaded.Width / stride;
42 | Color[] levelData = level == 0 ? data : new Color[levelHeight * levelWidth];
43 | if (level > 0)
44 | {
45 | for (int x = 0; x < levelWidth; x++)
46 | {
47 | for (int y = 0; y < levelHeight; y++)
48 | {
49 | Vector4 sum = new Vector4();
50 | for (int ix = 0; ix < 2; ix++)
51 | {
52 | for (int iy = 0; iy < 2; iy++)
53 | {
54 | Color c = lastLevelData[(x * 2 + ix) + (y * 2 + iy) * levelWidth * 2];
55 | if (c.A < 255)
56 | {
57 | outputTag.HasAlpha = true;
58 | }
59 | sum += c.ToVector4();
60 | }
61 | }
62 | levelData[x + y * levelWidth] = new Color(sum / 4);
63 | }
64 | }
65 | }
66 | output.SetData(level, null, levelData, 0, levelHeight * levelWidth);
67 | lastLevelData = levelData;
68 | }
69 | return output;
70 | }
71 | protected override Texture2D LoadData(string path, string name)
72 | {
73 | return LoadFromPath(path);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Framework/Context.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | public class Context : Context
11 | {
12 | private Context() { }
13 | private T Previous;
14 | public override void Dispose()
15 | {
16 | Current = Previous;
17 | base.Dispose();
18 | }
19 | private static AsyncLocal current = new AsyncLocal();
20 | public static T Current { get => current.Value; private set => current.Value = value; }
21 | public static Context Create(T value)
22 | {
23 | var result = new Context() { Previous = Current };
24 | Current = value;
25 | return result;
26 | }
27 | }
28 | public class Context : IDisposable
29 | {
30 | private IDisposable next;
31 | public virtual void Dispose()
32 | {
33 | next?.Dispose();
34 | }
35 | public static Context Create(U value) => Context.Create(value);
36 | public Context Inject(U value)
37 | {
38 | var result = Create(value);
39 | result.next = this;
40 | return result;
41 | }
42 | public IDisposable Include(IDisposable disposable)
43 | {
44 | if (next != null) throw new InvalidOperationException("Next is already set");
45 | next = disposable;
46 | return this;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Framework/Entities/Component.cs:
--------------------------------------------------------------------------------
1 | using Replicate;
2 | using Spectrum.Framework.Input;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Spectrum.Framework.Entities
10 | {
11 | public interface IUpdate
12 | {
13 | void Update(float dt);
14 | }
15 | public interface IFullUpdate : IUpdate
16 | {
17 | void PreStep(float step);
18 | void PostStep(float step);
19 | }
20 | public class Component
21 | {
22 | [ReplicateIgnore]
23 | public Entity E { get; set; }
24 | public virtual void Initialize(Entity e) { E = e; }
25 | }
26 | public class Component : Component
27 | {
28 | [ReplicateIgnore]
29 | public T P { get; set; }
30 | public override void Initialize(Entity e)
31 | {
32 | base.Initialize(e);
33 | if (!(e is T p)) throw new InvalidCastException($"Cannot convert from {e.GetType()} to {typeof(T)}");
34 | P = p;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Framework/Entities/EntityCollection.cs:
--------------------------------------------------------------------------------
1 | using Spectrum.Framework.Graphics;
2 | using Spectrum.Framework.Physics;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Collections;
8 |
9 | namespace Spectrum.Framework.Entities
10 | {
11 | public class EntityCollection
12 | {
13 | public readonly Dictionary Map = new Dictionary();
14 | public readonly Dictionary Compacted = new Dictionary();
15 | private List updatedSorted = new List();
16 | private List drawSorted = new List();
17 | public event Action OnEntityAdded;
18 | public event Action OnEntityRemoved;
19 | public IEnumerable UpdateSorted { get { lock (this) { return updatedSorted.Where(e => !e.Destroying); } } }
20 | public IEnumerable DrawSorted { get { lock (this) { return drawSorted.Where(e => !e.Destroying); } } }
21 | public IEnumerable Destroying { get { lock (this) { return Map.Values.Where(e => e.Destroying); } } }
22 | public IEnumerable All { get { lock (this) { return Map.Values; } } }
23 |
24 | public void Add(Entity entity)
25 | {
26 | lock (this)
27 | {
28 | if (Map.ContainsKey(entity.ID))
29 | throw new InvalidOperationException("An Entity with that ID has already been added to the collection");
30 | Map[entity.ID] = entity;
31 | int updateIndex = updatedSorted.TakeWhile(e => e.UpdateOrder < entity.UpdateOrder).Count();
32 | updatedSorted.Insert(updateIndex, entity);
33 | int drawIndex = drawSorted.TakeWhile(e => e.DrawOrder < entity.DrawOrder).Count();
34 | drawSorted.Insert(drawIndex, entity);
35 | OnEntityAdded?.Invoke(entity);
36 | }
37 | }
38 | public void Compact(Entity entity)
39 | {
40 | lock (this)
41 | {
42 | if (Map.ContainsKey(entity.ID))
43 | RemoveFromMap(entity.ID);
44 | Compacted[entity.ID] = entity;
45 | }
46 | }
47 | public void Uncompact(Entity entity)
48 | {
49 | lock (this)
50 | {
51 | Compacted.Remove(entity.ID);
52 | Add(entity);
53 | }
54 | }
55 | private Entity RemoveFromMap(Guid entityID)
56 | {
57 | var entity = Map[entityID];
58 | Map.Remove(entityID);
59 | updatedSorted.Remove(entity);
60 | drawSorted.Remove(entity);
61 | OnEntityRemoved?.Invoke(entity);
62 | return entity;
63 | }
64 | public Entity Remove(Guid entityID)
65 | {
66 | lock (this)
67 | {
68 | Entity entity = null;
69 | if (Compacted.ContainsKey(entityID))
70 | {
71 | entity = Compacted[entityID];
72 | Compacted.Remove(entityID);
73 | }
74 | else if (Map.ContainsKey(entityID))
75 | entity = RemoveFromMap(entityID);
76 | return entity;
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Framework/Entities/GameObject2D.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Replicate;
3 | using Spectrum.Framework.Content;
4 | using Spectrum.Framework.Graphics;
5 | using Spectrum.Framework.Screens;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace Spectrum.Framework.Entities
13 | {
14 | [ReplicateType]
15 | public class GameObject2D : Entity
16 | {
17 | [ReplicateIgnore]
18 | public static readonly DrawablePart GameObject2DPart;
19 | static GameObject2D()
20 | {
21 | GameObject2DPart = DrawablePart.From(new List()
22 | {
23 | new CommonTex(new Vector3(0, 1, 0), Vector3.UnitZ, new Vector2(0, 0)),
24 | new CommonTex(new Vector3(1, 1, 0), Vector3.UnitZ, new Vector2(1, 0)),
25 | new CommonTex(new Vector3(0, 0, 0), Vector3.UnitZ, new Vector2(0, 1)),
26 | new CommonTex(new Vector3(1, 0, 0), Vector3.UnitZ, new Vector2(1, 1))
27 | }, new List() { 0, 1, 2, 1, 2, 3 });
28 | GameObject2DPart.effect = new SpectrumEffect() { LightingEnabled = false };
29 | }
30 | public ImageAsset Texture;
31 | public Color Color = Color.White;
32 | public Rectangle Bounds;
33 | public Vector2 Position;
34 | public float Layer;
35 | public Matrix World => Matrix.CreateRotationZ(Rotation) * Matrix.CreateTranslation(Position.X, Position.Y, Layer);
36 | public float Rotation = 0;
37 | public Quaternion Orientation => Quaternion.CreateFromAxisAngle(Vector3.Up, Rotation);
38 | public override void Draw(float gameTime)
39 | {
40 | base.Draw(gameTime);
41 | if (Texture != null)
42 | {
43 | Batch3D.Current.DrawPart(GameObject2DPart, CreateTexTransform(Bounds) * World, new MaterialData() { DiffuseTexture = Texture.GetTexture(Bounds), DiffuseColor = Color, DisableLighting = true });
44 | }
45 | }
46 |
47 | public static Matrix CreateTexTransform(Rectangle rect)
48 | {
49 | return Matrix.CreateScale(rect.Width, rect.Height, 0) * Matrix.CreateTranslation(rect.X, rect.Y, 0);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Framework/Entities/Interpolator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework.Entities
8 | {
9 | public class Interpolator : Interpolator
10 | {
11 | Func interpolator;
12 | public Interpolator(Func interpolator) => this.interpolator = interpolator;
13 | public override object GetValue(float weight, object currentValue, object target)
14 | {
15 | return interpolator(weight, (T)currentValue, (T)target);
16 | }
17 | }
18 | public abstract class Interpolator
19 | {
20 | private float period;
21 | private float periodRemaining;
22 | private object target;
23 | public bool NeedsUpdate => periodRemaining > 0;
24 | public void BeginInterpolate(float period, object target)
25 | {
26 | this.target = target;
27 | this.period = period;
28 | periodRemaining = period;
29 | }
30 | public virtual object Update(float elapsed, object currentValue)
31 | {
32 | if (periodRemaining > 0)
33 | {
34 | periodRemaining -= elapsed;
35 | return GetValue(1 - periodRemaining / period, currentValue, target);
36 | }
37 | return null;
38 | }
39 | public abstract object GetValue(float weight, object currentValue, object target);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Framework/FunctionalExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | public static class FunctionalExtensions
11 | {
12 | public static object GetConstantValue(this Expression exp)
13 | {
14 | return Expression.Lambda>(Expression.Convert(exp, typeof(object))).Compile()();
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Framework/Graphics/Animation/AnimationClip.cs:
--------------------------------------------------------------------------------
1 | #region File Description
2 | //-----------------------------------------------------------------------------
3 | // AnimationClip.cs
4 | //
5 | // Microsoft XNA Community Game Platform
6 | // Copyright (C) Microsoft Corporation. All rights reserved.
7 | //-----------------------------------------------------------------------------
8 | #endregion
9 |
10 | #region Using Statements
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | #endregion
15 |
16 | namespace Spectrum.Framework.Graphics.Animation
17 | {
18 | ///
19 | /// An animation clip is the runtime equivalent of the
20 | /// Microsoft.Xna.Framework.Content.Pipeline.Graphics.AnimationContent type.
21 | /// It holds all the keyframes needed to describe a single animation.
22 | ///
23 | public class AnimationClip
24 | {
25 | ///
26 | /// Constructs a new animation clip object.
27 | ///
28 | public AnimationClip(string name, float duration, Dictionary> translations, Dictionary> rotations)
29 | {
30 | Name = name;
31 | Duration = duration;
32 | Translations = translations;
33 | Rotations = rotations;
34 | }
35 |
36 |
37 | ///
38 | /// Private constructor for use by the XNB deserializer.
39 | ///
40 | private AnimationClip()
41 | {
42 | }
43 |
44 | public string Name { get; private set; }
45 |
46 | ///
47 | /// Gets the total length of the animation.
48 | ///
49 | public float Duration { get; private set; }
50 |
51 |
52 | ///
53 | /// Gets a combined list containing all the keyframes for all bones,
54 | /// sorted by time.
55 | ///
56 | public Dictionary> Translations { get; private set; }
57 | public Dictionary> Rotations { get; private set; }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Framework/Graphics/Animation/AnimationData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework.Graphics.Animation
8 | {
9 | public class AnimationData : DefaultDict { }
10 | }
11 |
--------------------------------------------------------------------------------
/Framework/Graphics/Animation/Bone.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Spectrum.Framework.Graphics.Animation
8 | {
9 | public class Bone : ITransform
10 | {
11 | public string Id;
12 | public List Children;
13 | public Bone Parent;
14 | public Quaternion DefaultRotation;
15 | public Vector3 DefaultTranslation;
16 | public Matrix BindPose;
17 | public Matrix InverseBindPose;
18 | private Quaternion rotation;
19 | public Quaternion Rotation
20 | {
21 | get => rotation;
22 | set { SetDirty(); rotation = value; }
23 | }
24 | private Vector3 translation;
25 | public Vector3 Translation
26 | {
27 | get => translation;
28 | set { SetDirty(); translation = value; }
29 | }
30 | Vector3 ITransform.Position { get { ResolveDirty(); return (withParentTransform).Translation; } }
31 | // TODO: This could be like the above and pull out a rotation from the matrix
32 | Quaternion ITransform.Orientation => Parent != null ? Rotation.Concat(((ITransform)Parent).Orientation) : Rotation;
33 | Vector3 ITransform.Scale => Vector3.One;
34 | private Matrix transform;
35 |
36 | private Matrix withParentTransform;
37 | private bool dirty = true;
38 | private void SetDirty()
39 | {
40 | dirty = true;
41 | foreach (var child in Children)
42 | child.SetDirty();
43 | }
44 | private void ResolveDirty()
45 | {
46 | if (dirty)
47 | {
48 | // TODO: I think this can be combined into one operation
49 | transform = rotation.ToMatrix() * Matrix.CreateTranslation(translation);
50 | withParentTransform = Parent == null ? transform : transform * Parent.WithParentTransform;
51 | dirty = false;
52 | }
53 | }
54 | public Matrix WithParentTransform
55 | {
56 | get
57 | {
58 | ResolveDirty();
59 | return withParentTransform;
60 | }
61 | }
62 | public Matrix DeltaTransform => InverseBindPose * WithParentTransform;
63 |
64 | public Bone(string id, Bone parent)
65 | {
66 | Parent = parent;
67 | Children = new List();
68 | DefaultRotation = Quaternion.Identity;
69 | DefaultTranslation = Vector3.Zero;
70 | BindPose = Matrix.Identity;
71 | InverseBindPose = Matrix.Identity;
72 | Id = id;
73 | }
74 |
75 | public Bone Clone(Dictionary bones, Bone parent = null)
76 | {
77 | Bone output = new Bone(Id, parent);
78 | output.DefaultRotation = DefaultRotation;
79 | output.DefaultTranslation = DefaultTranslation;
80 | output.BindPose = BindPose;
81 | output.InverseBindPose = InverseBindPose;
82 | output.transform = transform;
83 | bones[Id] = output;
84 | foreach (var child in Children)
85 | {
86 | output.Children.Add(child.Clone(bones, output));
87 | }
88 | return output;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Framework/Graphics/Animation/Keyframe.cs:
--------------------------------------------------------------------------------
1 | #region File Description
2 | //-----------------------------------------------------------------------------
3 | // Keyframe.cs
4 | //
5 | // Microsoft XNA Community Game Platform
6 | // Copyright (C) Microsoft Corporation. All rights reserved.
7 | //-----------------------------------------------------------------------------
8 | #endregion
9 |
10 | #region Using Statements
11 | using System;
12 | using Microsoft.Xna.Framework;
13 | #endregion
14 |
15 | namespace Spectrum.Framework.Graphics.Animation
16 | {
17 | ///
18 | /// Describes the position of a single bone at a single point in time.
19 | ///
20 | public class Keyframe
21 | {
22 | ///
23 | /// Constructs a new keyframe object.
24 | ///
25 | public Keyframe(float time, T transform)
26 | {
27 | Time = time;
28 | Value = transform;
29 | }
30 |
31 |
32 | ///
33 | /// Private constructor for use by the XNB deserializer.
34 | ///
35 | private Keyframe()
36 | {
37 | }
38 |
39 | ///
40 | /// Gets the time offset from the start of the animation to this keyframe.
41 | ///
42 | public float Time { get; private set; }
43 |
44 |
45 | ///
46 | /// Gets the bone transform for this keyframe.
47 | ///
48 | public T Value { get; private set; }
49 | public Keyframe Next;
50 | }
51 | public static class KeyframeExtensions
52 | {
53 | public static T LerpTo(this Keyframe head, float time, T start, Func lerp)
54 | {
55 | while (head.Next != null && time > head.Next.Time)
56 | head = head.Next;
57 | if (head?.Next != null)
58 | {
59 | float w = (time - head.Time) / (head.Next.Time - head.Time);
60 | return lerp(head.Value, head.Next.Value, w);
61 | }
62 | return start;
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Framework/Graphics/Animation/SkinningData.cs:
--------------------------------------------------------------------------------
1 | #region File Description
2 | //-----------------------------------------------------------------------------
3 | // SkinningData.cs
4 | //
5 | // Microsoft XNA Community Game Platform
6 | // Copyright (C) Microsoft Corporation. All rights reserved.
7 | //-----------------------------------------------------------------------------
8 | #endregion
9 |
10 | #region Using Statements
11 | using System.Collections.Generic;
12 | using Spectrum.Framework.Graphics.Animation;
13 | using Microsoft.Xna.Framework;
14 | using Newtonsoft.Json.Linq;
15 | #endregion
16 |
17 | namespace Spectrum.Framework.Graphics.Animation
18 | {
19 | ///
20 | /// Combines all the data needed to render and animate a skinned object.
21 | /// This is typically stored in the Tag property of the Model being animated.
22 | ///
23 | public class SkinningData
24 | {
25 | ///
26 | /// Constructs a new skinning data object.
27 | ///
28 | public SkinningData(Bone root, Dictionary bones)
29 | {
30 | Bones = bones;
31 | Root = root;
32 | }
33 |
34 | public void ToDefault()
35 | {
36 | foreach (Bone bone in Bones.Values)
37 | {
38 | bone.Translation = bone.DefaultTranslation;
39 | bone.Rotation = bone.DefaultRotation;
40 | }
41 | }
42 |
43 | public Bone Root { get; private set; }
44 |
45 | public Dictionary Bones { get; private set; }
46 |
47 | public SkinningData Clone()
48 | {
49 | Dictionary outBones = new Dictionary();
50 | return new SkinningData(Root.Clone(outBones), outBones);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Framework/Graphics/Billboard.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using SharpDX.Direct2D1.Effects;
4 | using Spectrum.Framework.Entities;
5 | using Spectrum.Framework.Input;
6 | using Spectrum.Framework.Physics.Collision.Shapes;
7 | using Spectrum.Framework.Screens;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 |
14 | namespace Spectrum.Framework.Graphics
15 | {
16 | public static class Billboard
17 | {
18 | public static readonly DrawablePart BillboardPart;
19 | static Billboard()
20 | {
21 | BillboardPart = DrawablePart.From(new List()
22 | {
23 | new CommonTex(new Vector3(-0.5f, -0.5f, 0), Vector3.Backward, new Vector2(0,1)),
24 | new CommonTex(new Vector3(0.5f, -0.5f, 0), Vector3.Backward, new Vector2(1, 1)),
25 | new CommonTex(new Vector3(-0.5f, 0.5f, 0), Vector3.Backward, new Vector2(0, 0)),
26 | new CommonTex(new Vector3(0.5f, 0.5f, 0), Vector3.Backward, new Vector2(1, 0))
27 | });
28 | }
29 | public static void Draw(Matrix world, Vector2 size, MaterialData material)
30 | {
31 | Batch3D.Current.DrawPart(
32 | BillboardPart,
33 | Matrix.CreateScale(size.X, size.Y, 1) * world,
34 | material
35 | );
36 | }
37 | public static void Draw(Vector3 position, Vector2 size,
38 | MaterialData material, Vector3 offset = default)
39 | {
40 | Batch3D.Current.DrawPart(
41 | BillboardPart,
42 | Matrix.Create(offset, new Vector3(size.X, size.Y, 1)),
43 | material,
44 | options: new Batch3D.DrawOptions()
45 | {
46 | DisableInstancing = true,
47 | DynamicDraw = (args) =>
48 | {
49 | args.Group.Properties.Effect.World = args.World *
50 | Matrix.CreateRotationY(-args.Phase.View.ToQuaternion().Yaw()) *
51 | Matrix.CreateTranslation(position);
52 | }
53 | }
54 | );
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Framework/Graphics/Effects/SkinnedEffect.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework.Graphics;
2 | using Spectrum.Framework.Content;
3 | using Spectrum.Framework.Graphics.Animation;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Spectrum.Framework.Graphics
11 | {
12 | public class SpectrumSkinnedEffect : SpectrumEffect
13 | {
14 | private string[] BoneNames;
15 | private Microsoft.Xna.Framework.Matrix[] BoneTransforms;
16 | public SpectrumSkinnedEffect() : base(ContentHelper.Load("SkinnedEffect")) { }
17 | private SpectrumSkinnedEffect(SpectrumSkinnedEffect clone) : base(clone) { }
18 | public override bool CanInstance
19 | {
20 | get
21 | {
22 | return false;
23 | }
24 | }
25 | protected override void OnApply()
26 | {
27 | if (BoneTransforms != null)
28 | Parameters["Bones"].SetValue(BoneTransforms);
29 | base.OnApply();
30 | }
31 | public void SetBoneNames(params string[] boneNames)
32 | {
33 | BoneTransforms = new Microsoft.Xna.Framework.Matrix[boneNames.Count()];
34 | for (int i = 0; i < boneNames.Count(); i++)
35 | {
36 | BoneTransforms[i] = Matrix.Identity;
37 | }
38 | BoneNames = boneNames;
39 | }
40 | public void UpdateBoneTransforms(SkinningData SkinningData)
41 | {
42 | for (int i = 0; i < BoneTransforms.Count(); i++)
43 | {
44 | BoneTransforms[i] = SkinningData.Bones[BoneNames[i]].DeltaTransform;
45 | }
46 | }
47 | public override Effect Clone()
48 | {
49 | var result = new SpectrumSkinnedEffect(this);
50 | result.SetBoneNames(BoneNames);
51 | return result;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Framework/Graphics/Effects/TerrainEffect.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework.Graphics;
2 | using Spectrum.Framework.Content;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace Spectrum.Framework.Graphics
9 | {
10 | public class TerrainEffect : SpectrumEffect
11 | {
12 | public float VertexBlend
13 | {
14 | get { return Parameters["VertexBlend"].GetValueSingle(); }
15 | set { Parameters["VertexBlend"].SetValue(value); }
16 | }
17 |
18 | public TerrainEffect(string textureA, string textureB, string textureC, string textureD)
19 | : base(ContentHelper.Load("TerrainEffect"))
20 | {
21 | Parameters["UseTexture"].SetValue(true);
22 | Parameters["MultiTextureA"].SetValue(ContentHelper.Load(textureA));
23 | Parameters["MultiTextureB"].SetValue(ContentHelper.Load(textureB));
24 | Parameters["MultiTextureC"].SetValue(ContentHelper.Load(textureC));
25 | Parameters["MultiTextureD"].SetValue(ContentHelper.Load(textureD));
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Framework/Graphics/Effects/WaterEffect.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Spectrum.Framework.Content;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 |
9 | namespace Spectrum.Framework.Graphics
10 | {
11 | class WaterEffect : SpectrumEffect
12 | {
13 | public static float WaterPerturbation = 1.4f;
14 | public static Matrix ReflectionView;
15 | public static Matrix ReflectionProj;
16 | public static float WaterTime;
17 | protected override void OnApply()
18 | {
19 | base.OnApply();
20 | Parameters["waterPerturbCoef"].SetValue(WaterPerturbation);
21 | Parameters["reflView"].SetValue(ReflectionView);
22 | Parameters["reflProj"].SetValue(ReflectionProj);
23 | Parameters["waterTime"].SetValue(WaterTime);
24 | Texture2D faff = Parameters["Reflection"].GetValueTexture2D();
25 | Texture2D test = Parameters["Refraction"].GetValueTexture2D();
26 | Parameters["Reflection"].SetValue(Water.reflectionRenderTarget);
27 | Parameters["Refraction"].SetValue(Water.refractionRenderTarget);
28 | }
29 | public WaterEffect(string waterBumpMap1, string waterBumpMap2)
30 | : base(ContentHelper.Load("WaterEffect"))
31 | {
32 |
33 | Parameters["WaterBumpBase"].SetValue(ContentHelper.Load(waterBumpMap1));
34 | Parameters["WaterBump"].SetValue(ContentHelper.Load(waterBumpMap2));
35 | Vector2 windDirection = (new Vector2(1, 2));
36 | windDirection.Normalize();
37 | Parameters["windDirection"].SetValue(windDirection);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Framework/Graphics/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Microsoft.Xna.Framework;
6 | using Microsoft.Xna.Framework.Graphics;
7 | using Microsoft.Xna.Framework.Content;
8 | using Spectrum.Framework.Content;
9 |
10 | namespace Spectrum.Framework.Graphics
11 | {
12 | public static class Settings
13 | {
14 | public static Matrix reflectionProjection;
15 | public static Matrix lightProjection;
16 | public static bool enableWater = false;
17 | public static int waterQuality = 0;
18 | public const string WaterBumpMapBase = "waterbump";
19 | public const string WaterBumpMap = "waterbump1";
20 | public static Vector2 ScreenSize;
21 | static Settings()
22 | {
23 | lightProjection = Matrix.CreatePerspectiveFOV(
24 | (float)Math.PI / 20f,
25 | 1,
26 | 100, 1100f);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Framework/Graphics/SpecModel.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Newtonsoft.Json.Linq;
4 | using Spectrum.Framework.Content;
5 | using Spectrum.Framework.Graphics.Animation;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Collections;
11 | using Spectrum.Framework.Physics.LinearMath;
12 |
13 | namespace Spectrum.Framework.Graphics
14 | {
15 | public class SpecModel
16 | {
17 | public string Path { get; private set; }
18 | public string Name { get; private set; }
19 | public AnimationData Animations { get; set; }
20 | public SkinningData SkinningData { get; protected set; }
21 | private static int _modelIndex = 0;
22 | private int _partIndex = 0;
23 |
24 | public Dictionary MeshParts { get; private set; }
25 | public Dictionary Materials { get; private set; }
26 | public static implicit operator SpecModel(string path)
27 | {
28 | return ContentHelper.Load(path);
29 | }
30 | public SpecModel()
31 | {
32 | Name = $"model_{_modelIndex++}";
33 | Path = null;
34 | MeshParts = new Dictionary();
35 | Materials = new Dictionary();
36 | SkinningData = null;
37 | }
38 | public SpecModel(string name, string path,
39 | Dictionary meshParts, Dictionary materials, SkinningData skinningData)
40 | {
41 | Name = name;
42 | Path = path;
43 | MeshParts = meshParts;
44 | Materials = materials;
45 | SkinningData = skinningData;
46 | }
47 | public JBBox Bounds
48 | {
49 | get
50 | {
51 | JBBox output = new JBBox(Vector3.Zero, Vector3.Zero);
52 | foreach (var part in this)
53 | {
54 | output.AddPoint(part.Bounds.Min);
55 | output.AddPoint(part.Bounds.Max);
56 | }
57 | return output;
58 | }
59 | }
60 | ///
61 | /// Be careful using this because the GameObject's RenderTasks will not get updated
62 | ///
63 | ///
64 | public void Add(DrawablePart part)
65 | {
66 | MeshParts[$"part_{_partIndex++}"] = part;
67 | }
68 | public void Update(float dt)
69 | {
70 | if (SkinningData != null)
71 | foreach (var part in MeshParts.Values)
72 | (part.effect as SpectrumSkinnedEffect)?.UpdateBoneTransforms(SkinningData);
73 | }
74 |
75 | public IEnumerator GetEnumerator()
76 | {
77 | return MeshParts.Values.GetEnumerator();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Framework/Graphics/Water.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Microsoft.Xna.Framework.Graphics;
6 | using Spectrum.Framework.Graphics;
7 | using Microsoft.Xna.Framework;
8 | using Spectrum.Framework;
9 | using Spectrum.Framework.Network;
10 | using Spectrum.Framework.Physics;
11 | using Spectrum.Framework.Entities;
12 |
13 | namespace Spectrum.Framework.Graphics
14 | {
15 | public class Water : GameObject
16 | {
17 | public const string waterBump1 = "waterbump";
18 | public const string waterBump2 = "waterbump1";
19 | public const float waterHeight = 0;
20 | public static RenderTarget2D refractionRenderTarget;
21 | public static RenderTarget2D reflectionRenderTarget;
22 | public VertexBuffer waterV;
23 | public int size;
24 | int numVertices = 32;
25 | public Water()
26 | {
27 | isStatic = true;
28 | AllowReplicate = false;
29 | }
30 | public override void Initialize()
31 | {
32 | Model = new SpecModel();
33 | base.Initialize();
34 | waterV = new VertexBuffer(SpectrumGame.Game.GraphicsDevice, VertexPositionTexture.VertexDeclaration, numVertices * numVertices, BufferUsage.WriteOnly);
35 | float[,] heights = new float[numVertices, numVertices];
36 | VertexPositionTexture[] verts = new VertexPositionTexture[numVertices * numVertices];
37 | for (int x = 0; x < numVertices; x++)
38 | {
39 | for (int y = 0; y < numVertices; y++)
40 | {
41 | verts[x + y * numVertices] = (VertexPositionTexture)VertexHelper.getVertex(x, y, heights, size * 1.0f / (numVertices - 1), Constructor);
42 | }
43 | }
44 | waterV.SetData(verts);
45 | IndexBuffer iBuffer = VertexHelper.MakeIndexBuffer(VertexHelper.getIndexList(numVertices, numVertices).ToList());
46 | DrawablePart p = new DrawablePart(waterV, iBuffer);
47 | p.effect = new WaterEffect(waterBump1, waterBump2);
48 | Model.Add(p);
49 | }
50 | public static void ResetRenderTargets()
51 | {
52 | refractionRenderTarget?.Dispose();
53 | refractionRenderTarget = new RenderTarget2D(SpectrumGame.Game.GraphicsDevice, (int)(2048.0 / Math.Pow(2, Settings.waterQuality)),
54 | (int)(2048.0 / Math.Pow(2, Settings.waterQuality)), false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);
55 | reflectionRenderTarget?.Dispose();
56 | reflectionRenderTarget = new RenderTarget2D(SpectrumGame.Game.GraphicsDevice, (int)(2048.0 / Math.Pow(2, Settings.waterQuality)),
57 | (int)(2048.0 / Math.Pow(2, Settings.waterQuality)), false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);
58 | }
59 | private static VertexPositionTexture Constructor(VertexArgs args)
60 | {
61 | return new VertexPositionTexture(args.pos, args.texturePos);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Framework/IDebug.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | public interface IDebug
11 | {
12 | string Debug();
13 | void DebugDraw(float gameTime);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Framework/Input/Axis.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Input;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace Spectrum.Framework.Input
9 | {
10 | public interface IAxis
11 | {
12 | float Value(InputState input, PlayerInformation player);
13 | }
14 | public struct KeyboardAxis : IAxis
15 | {
16 | public Keys Positive;
17 | public Keys Negative;
18 | public float Scalar;
19 | public KeyboardAxis(Keys positive, Keys negative, float scalar = 1)
20 | {
21 | Positive = positive;
22 | Negative = negative;
23 | Scalar = scalar;
24 | }
25 |
26 | public float Value(InputState input, PlayerInformation player)
27 | {
28 | if (!player.UsesKeyboard) return 0;
29 | float output = 0;
30 | if (input.IsKeyDown(Positive))
31 | output += 1;
32 | if (input.IsKeyDown(Negative))
33 | output -= 1;
34 | return output * Scalar;
35 | }
36 | }
37 | public struct GamepadAxis : IAxis
38 | {
39 | public GamepadAxisType Axis;
40 | public float Scalar;
41 | public GamepadAxis(GamepadAxisType axis, float scalar = 1)
42 | {
43 | Axis = axis;
44 | Scalar = scalar;
45 | }
46 | public float Value(InputState input, PlayerInformation player)
47 | {
48 | float output = 0;
49 | foreach (int gamepadIndex in player.UsedGamepads)
50 | {
51 | output += input.Gamepads[gamepadIndex].Axis(Axis);
52 | }
53 | return output * Scalar;
54 | }
55 | }
56 | public struct MouseAxis : IAxis
57 | {
58 | public bool horizontal;
59 | public float scalar;
60 | public MouseAxis(bool horizontal, float scalar = 0.001f)
61 | {
62 | this.horizontal = horizontal;
63 | this.scalar = scalar;
64 | }
65 |
66 | public float Value(InputState input, PlayerInformation player)
67 | {
68 | if (horizontal)
69 | return input.CursorState.DX * scalar;
70 | else
71 | return input.CursorState.DY * scalar;
72 | }
73 | }
74 | public struct Axis1
75 | {
76 | public List Axes;
77 | public Axis1(IAxis axis)
78 | {
79 | Axes = new List();
80 | Axes.Add(axis);
81 | }
82 | public float Value(InputState input, PlayerInformation player)
83 | {
84 | float output = 0;
85 | foreach (IAxis axis in Axes)
86 | {
87 | output += axis.Value(input, player);
88 | }
89 | return Math.Min(Math.Max(output, -1), 1);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Framework/Input/InputLayout.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Microsoft.Xna.Framework.Input;
6 |
7 | namespace Spectrum.Framework.Input
8 | {
9 | public class InputLayout
10 | {
11 | public static Dictionary Profiles = new Dictionary();
12 | public static InputLayout Default;
13 |
14 | public DefaultDict KeyBindings = new DefaultDict(() => new KeyBinding(), true);
15 | public Dictionary Axes1 = new Dictionary();
16 |
17 | public static void Init()
18 | {
19 | Default = new InputLayout("Default");
20 | Default.Add("MenuLeft", new KeyBind(Keys.Left));
21 | Default.Add("MenuRight", new KeyBind(Keys.Right));
22 | Default.Add("MenuUp", new KeyBind(Keys.Up));
23 | Default.Add("MenuDown", new KeyBind(Keys.Down));
24 | Default.Add("MenuCycleF", new KeyBind(Keys.Tab));
25 | Default.Add("MenuCycleB", new KeyBind(Keys.Tab, modifiers: new KeyBind(Keys.LeftShift)));
26 | Default.Add("GoBack", new KeyBind(Keys.Escape));
27 | Default.Add("Continue", new KeyBind(Keys.Enter));
28 | }
29 |
30 | public InputLayout(string name)
31 | {
32 | if (Profiles.ContainsKey(name)) throw new ArgumentException("An input profile with that name already exists");
33 | Profiles[name] = this;
34 | }
35 |
36 | public void Add(string binding, KeyBind option)
37 | {
38 | KeyBindings[binding].Options.Add(option);
39 | }
40 | public static void AddBind(string binding, KeyBind option, string profile = "Default")
41 | {
42 | Profiles[profile].Add(binding, option);
43 | }
44 |
45 | public void AddBindClass(Type type)
46 | {
47 | foreach (var field in type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static))
48 | {
49 | if (field.FieldType != typeof(KeyBinding)) continue;
50 | KeyBindings[field.Name] = (KeyBinding)field.GetValue(null);
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Framework/Input/PlayerInformation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Spectrum.Framework.Input
7 | {
8 | [Flags]
9 | public enum PlayerInputs
10 | {
11 | Keyboard = 1,
12 | Gamepad1 = 2,
13 | Gamepad2 = 4,
14 | Gamepad3 = 8,
15 | Gamepad4 = 16,
16 | Joystick1 = 32,
17 | Joystick2 = 64,
18 | Joystick3 = 128,
19 | Joystick4 = 256,
20 | }
21 | public class PlayerInformation
22 | {
23 | public static PlayerInformation Default = new PlayerInformation(PlayerInputs.Keyboard | PlayerInputs.Gamepad1, "Default");
24 |
25 | public PlayerInputs PlayerType;
26 | public InputLayout Layout;
27 |
28 | public bool UsesKeyboard;
29 | public List UsedGamepads;
30 | public List UsedJoysticks;
31 |
32 | public PlayerInformation(PlayerInputs playerType, string inputLayout)
33 | {
34 | PlayerType = playerType;
35 | Layout = InputLayout.Profiles[inputLayout];
36 | UsedGamepads = new List();
37 | UsesKeyboard = false;
38 | UsedJoysticks = new List();
39 | foreach (PlayerInputs input in Enum.GetValues(typeof(PlayerInputs)))
40 | {
41 | switch (input)
42 | {
43 | case PlayerInputs.Keyboard:
44 | UsesKeyboard = true;
45 | break;
46 | case PlayerInputs.Gamepad1:
47 | UsedGamepads.Add(0);
48 | break;
49 | case PlayerInputs.Gamepad2:
50 | UsedGamepads.Add(1);
51 | break;
52 | case PlayerInputs.Gamepad3:
53 | UsedGamepads.Add(2);
54 | break;
55 | case PlayerInputs.Gamepad4:
56 | UsedGamepads.Add(3);
57 | break;
58 | case PlayerInputs.Joystick1:
59 | UsedJoysticks.Add(0);
60 | break;
61 | case PlayerInputs.Joystick2:
62 | UsedJoysticks.Add(1);
63 | break;
64 | case PlayerInputs.Joystick3:
65 | UsedJoysticks.Add(2);
66 | break;
67 | case PlayerInputs.Joystick4:
68 | UsedJoysticks.Add(3);
69 | break;
70 | default:
71 | break;
72 | }
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Framework/Input/SpectrumMouse.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Windows.Forms;
8 |
9 | namespace Spectrum.Framework.Input
10 | {
11 | public class CursorState
12 | {
13 | public bool[] buttons;
14 | public Point P;
15 | public float DX;
16 | public float DY;
17 | public int ScrollX;
18 | public int ScrollY;
19 | public CursorState()
20 | {
21 | P = new Point { X = -1, Y = -1 };
22 | buttons = new bool[16];
23 | }
24 | }
25 |
26 | public class SpectrumMouse
27 | {
28 | public static bool UseRaw = true;
29 | Point mousePosition;
30 | public SpectrumMouse()
31 | {
32 | SpectrumGame.Game.WindowForm.MouseMove += WindowForm_MouseMove;
33 | }
34 |
35 | private void WindowForm_MouseMove(object sender, MouseEventArgs e)
36 | {
37 | mousePosition.X = e.X;
38 | mousePosition.Y = e.Y;
39 | }
40 |
41 | public CursorState GetCurrentState(CursorState previous = null)
42 | {
43 | bool[] buttons = new bool[16];
44 | RawMouse.buttons.CopyTo(buttons, 0);
45 | return new CursorState()
46 | {
47 | buttons = buttons,
48 | P = mousePosition,
49 | DX = RawMouse.lastX / 2.0f,
50 | DY = RawMouse.lastY / 2.0f,
51 | ScrollY = RawMouse.lastZ,
52 | };
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Framework/Input/VR/VRBinding.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework.Input
8 | {
9 | public enum VRButton
10 | {
11 | System = 0,
12 | ApplicationMenu = 1,
13 | Grip = 2,
14 | DPad_Left = 3,
15 | DPad_Up = 4,
16 | DPad_Right = 5,
17 | DPad_Down = 6,
18 | A = 7,
19 | ProximitySensor = 31,
20 | Axis0 = 32,
21 | Axis1 = 33,
22 | Axis2 = 34,
23 | Axis3 = 35,
24 | Axis4 = 36,
25 | SteamVR_Touchpad = 32,
26 | SteamVR_Trigger = 33,
27 | Max = 64,
28 | }
29 | [Flags]
30 | public enum VRHand
31 | {
32 | Left = 1,
33 | Right = 2
34 | }
35 | [Flags]
36 | public enum VRPressType
37 | {
38 | Pressed = 1,
39 | Touched = 2
40 | }
41 | public struct VRBinding
42 | {
43 | public VRHand Hand;
44 | public VRButton Button;
45 | public VRPressType PressType;
46 | public VRBinding(VRButton button, VRHand hand = VRHand.Left | VRHand.Right, VRPressType pressType = VRPressType.Pressed)
47 | {
48 | Hand = hand;
49 | Button = button;
50 | PressType = pressType;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Framework/Input/VR/VRHmd.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Spectrum.Framework.VR;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Spectrum.Framework.Input
10 | {
11 | public struct VRHMD
12 | {
13 | public Vector3 Position;
14 | public Vector3 PositionDelta;
15 | public Vector3 Direction;
16 | public Quaternion Rotation;
17 | public Quaternion RotationDelta;
18 | public void Update()
19 | {
20 | var position = SpecVR.HeadPose.Translation;
21 | PositionDelta = position - Position;
22 | Position = position;
23 | var rotation = SpecVR.HeadPose.ToQuaternion();
24 | RotationDelta = rotation * Rotation.Inverse();
25 | Rotation = rotation;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Framework/JConvert.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using Replicate.MetaData;
5 | using Spectrum.Framework.Content;
6 | using Spectrum.Framework.Entities;
7 | using Spectrum.Framework.Graphics;
8 | using Spectrum.Framework.JSON;
9 | using Spectrum.Framework.Physics.Collision.Shapes;
10 | using System;
11 | using System.Collections.Generic;
12 | using System.IO;
13 | using System.Linq;
14 | using System.Text;
15 |
16 | namespace Spectrum.Framework
17 | {
18 | public class JConvert
19 | {
20 | public static List Converters = new List();
21 | static JsonSerializerSettings Settings {
22 | get
23 | {
24 | var settings = new JsonSerializerSettings();
25 | foreach (var converter in Converters)
26 | {
27 | settings.Converters.Add(converter);
28 | }
29 | return settings;
30 | }
31 | }
32 | static JConvert()
33 | {
34 | Converters.Add(new JVectorConverter());
35 | Converters.Add(new JPointConverter());
36 | Converters.Add(new JMatrixConverter());
37 | Converters.Add(new JShapeConverter());
38 | Converters.Add(new SimpleConverter(
39 | model => model.Name,
40 | token => ContentHelper.Load((string)token)));
41 | Converters.Add(new JInitDataConverter());
42 | //Converters.Add(new SimpleConverter(
43 | // typeData => typeData.TypeData.Name,
44 | // token => TypeHelper.Model.Types[(string)token]);
45 | }
46 | public static object Deserialize(JToken token, Type targetType)
47 | {
48 | return token?.ToObject(targetType, JsonSerializer.Create(Settings));
49 | }
50 | public static T Deserialize(JToken token)
51 | {
52 | return (T)(Deserialize(token, typeof(T)) ?? default(T));
53 | }
54 | public static T Deserialize(string json)
55 | {
56 | return (T)JsonConvert.DeserializeObject(json, typeof(T), Settings);
57 | }
58 | public static T DeserializeFile(string path)
59 | {
60 | using (var reader = new StreamReader(File.OpenRead(path)))
61 | return Deserialize(reader.ReadToEnd());
62 | }
63 | public static string Serialize(object obj)
64 | {
65 | return JsonConvert.SerializeObject(obj, Formatting.Indented, Settings);
66 | }
67 | public static void SerializeFile(object obj, string path)
68 | {
69 | using (var f = File.Open(path, FileMode.OpenOrCreate))
70 | {
71 | f.SetLength(0);
72 | using (var sw = new StreamWriter(f))
73 | sw.Write(Serialize(obj));
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Framework/JSON/JMatrixConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.Xna.Framework;
7 | using Newtonsoft.Json;
8 | using Newtonsoft.Json.Linq;
9 |
10 | namespace Spectrum.Framework.JSON
11 | {
12 | class JMatrixConverter : JsonConverter
13 | {
14 | public override bool CanConvert(Type objectType)
15 | {
16 | return objectType == typeof(Matrix);
17 | }
18 |
19 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
20 | {
21 | var token = JToken.ReadFrom(reader);
22 | if (token is JArray array)
23 | {
24 | return MatrixHelper.FromArray(array.Select(_token => (float)_token).ToArray());
25 | }
26 | if (token is JObject obj)
27 | {
28 | Vector3 rotation = Vector3.Zero;
29 | Vector3 scale = Vector3.One;
30 | Vector3 translation = Vector3.Zero;
31 | if (obj["rotation"] != null)
32 | rotation = JConvert.Deserialize(obj["rotation"]) * (float)(Math.PI / 180);
33 | if (obj["scale"] != null)
34 | {
35 | if (obj["scale"].Type == JTokenType.Array)
36 | scale = JConvert.Deserialize(obj["scale"]);
37 | if (obj["scale"].Type == JTokenType.Float)
38 | scale = new Vector3((float)obj["scale"]);
39 | }
40 | if (obj["translation"] != null)
41 | translation = JConvert.Deserialize(obj["translation"]);
42 | return Matrix.CreateFromYawPitchRoll(rotation.Y, rotation.X, rotation.Z) * Matrix.CreateScale(scale) * Matrix.CreateTranslation(translation);
43 | }
44 | return null;
45 | }
46 |
47 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
48 | {
49 | var oldF = writer.Formatting;
50 | writer.Formatting = Formatting.None;
51 | serializer.Serialize(writer, ((Matrix)value).ToArray());
52 | writer.Formatting = oldF;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Framework/JSON/JPointConverter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using Replicate;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace Spectrum.Framework.JSON
12 | {
13 | class JPointConverter : JsonConverter
14 | {
15 | public static bool IsNumeric(JToken token)
16 | {
17 | return token.Type == JTokenType.Float || token.Type == JTokenType.Integer;
18 | }
19 |
20 | public override bool CanConvert(Type objectType)
21 | {
22 | objectType = objectType.DeNullable();
23 | return objectType == typeof(Point3) || objectType == typeof(Point);
24 | }
25 |
26 | public override object ReadJson(JsonReader reader, Type targetType, object existingValue, JsonSerializer serializer)
27 | {
28 | var token = JToken.ReadFrom(reader);
29 | targetType = targetType.DeNullable();
30 | if (token is JArray array)
31 | {
32 | if (targetType == typeof(Point3) && array.Count == 3 && array.All(IsNumeric))
33 | return new Point3((int)array[0], (int)array[1], (int)array[2]);
34 | if (targetType == typeof(Point) && array.Count == 2 && array.All(IsNumeric))
35 | return new Point((int)array[0], (int)array[1]);
36 | }
37 | return null;
38 | }
39 |
40 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
41 | {
42 | JArray array = null;
43 | if (value is Point3 point3)
44 | array = new JArray(point3.X, point3.Y, point3.Z);
45 | if (value is Point point2)
46 | array = new JArray(point2.X, point2.Y);
47 | writer.Formatting = Formatting.None;
48 | array.WriteTo(writer);
49 | writer.Formatting = Formatting.Indented;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Framework/JSON/JShapeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.Xna.Framework;
7 | using Newtonsoft.Json;
8 | using Newtonsoft.Json.Linq;
9 | using Spectrum.Framework.Physics.Collision.Shapes;
10 |
11 | namespace Spectrum.Framework.JSON
12 | {
13 | class JShapeConverter : JsonConverter
14 | {
15 | public override bool CanConvert(Type objectType)
16 | {
17 | return objectType == typeof(Shape) || objectType == typeof(BoxShape) || objectType == typeof(ListMultishape);
18 | }
19 |
20 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
21 | {
22 | var token = JToken.ReadFrom(reader);
23 | if (token is JObject obj)
24 | {
25 | var shapeType = (string)obj["type"];
26 | switch (shapeType)
27 | {
28 | case "box":
29 | return new BoxShape(JConvert.Deserialize(obj["size"]),
30 | JConvert.Deserialize(obj["position"]));
31 | case "list":
32 | return new ListMultishape(((JArray)obj["shapes"])
33 | .Select(shape => JConvert.Deserialize(shape)).ToList());
34 | default:
35 | break;
36 | }
37 | }
38 | return null;
39 | }
40 |
41 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
42 | {
43 | if (value is BoxShape box)
44 | serializer.Serialize(writer, new
45 | {
46 | type = "box",
47 | size = box.Size,
48 | position = box.Position,
49 | });
50 | else if (value is ListMultishape listShape)
51 | {
52 | serializer.Serialize(writer, new
53 | {
54 | type = "list",
55 | shapes = listShape.Shapes
56 | });
57 | }
58 | else
59 | throw new NotImplementedException();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Framework/JSON/JVectorConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.Xna.Framework;
7 | using Newtonsoft.Json;
8 | using Newtonsoft.Json.Linq;
9 | using Replicate;
10 |
11 | namespace Spectrum.Framework.JSON
12 | {
13 | class JVectorConverter : JsonConverter
14 | {
15 | public static bool IsNumeric(JToken token)
16 | {
17 | return token.Type == JTokenType.Float || token.Type == JTokenType.Integer;
18 | }
19 |
20 | public override bool CanConvert(Type objectType)
21 | {
22 | objectType = objectType.DeNullable();
23 | return objectType == typeof(Vector2) || objectType == typeof(Vector3) || objectType == typeof(Vector4);
24 | }
25 |
26 | public override object ReadJson(JsonReader reader, Type targetType, object existingValue, JsonSerializer serializer)
27 | {
28 | var token = JToken.ReadFrom(reader);
29 | targetType = targetType.DeNullable();
30 | if (token is JArray array)
31 | {
32 | if (targetType == typeof(Vector3) && array.Count == 3 && array.All(IsNumeric))
33 | return new Vector3((float)array[0], (float)array[1], (float)array[2]);
34 | if (targetType == typeof(Vector2) && array.Count == 2 && array.All(IsNumeric))
35 | return new Vector2((float)array[0], (float)array[1]);
36 | if (targetType == typeof(Vector4) && array.Count == 4 && array.All(IsNumeric))
37 | return new Vector4((float)array[0], (float)array[1], (float)array[2], (float)array[3]);
38 | }
39 | return null;
40 | }
41 |
42 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
43 | {
44 | JArray array = null;
45 | if (value is Vector3 vector3)
46 | array = new JArray(vector3.X, vector3.Y, vector3.Z);
47 | if (value is Vector2 vector2)
48 | array = new JArray(vector2.X, vector2.Y);
49 | if (value is Vector4 vector4)
50 | array = new JArray(vector4.X, vector4.Y, vector4.Z, vector4.W);
51 | var oldF = writer.Formatting;
52 | writer.Formatting = Formatting.None;
53 | array.WriteTo(writer);
54 | writer.Formatting = oldF;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Framework/JSON/SimpleConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Newtonsoft.Json;
7 | using Newtonsoft.Json.Linq;
8 |
9 | namespace Spectrum.Framework.JSON
10 | {
11 | class SimpleConverter : JsonConverter
12 | {
13 | readonly Func Writer;
14 | readonly Func Reader;
15 | public SimpleConverter(Func writer, Func reader)
16 | {
17 | Writer = writer;
18 | Reader = reader;
19 | }
20 |
21 | public override bool CanConvert(Type objectType)
22 | {
23 | return objectType == typeof(T);
24 | }
25 |
26 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
27 | {
28 | return Reader(JToken.ReadFrom(reader));
29 | }
30 |
31 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
32 | {
33 | Writer((T)value).WriteTo(writer);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Framework/LSystem/LNode.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Spectrum.Framework.LSystem
8 | {
9 | public class LNode
10 | {
11 | public Matrix Transform { get { return Matrix.CreateTranslation(Vector3.Up) * Matrix.CreateFromAxisAngle(new Vector3((float)Math.Cos(R2), 0, (float)Math.Sin(R2)), R1) * Matrix.CreateScale(D) * root; } }
12 | public List Children = new List();
13 | public float D;
14 | public float R1;
15 | public float R2;
16 | private Matrix root;
17 | public LNode(float D, float R1, float R2, Matrix root)
18 | {
19 | this.root = root;
20 | this.D = D;
21 | this.R1 = R1;
22 | this.R2 = R2;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Framework/Network/Handshake.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Spectrum.Framework.Network
7 | {
8 | public enum HandshakeStage
9 | {
10 | Wait = 0,
11 | Begin = 1,
12 | AckBegin = 2,
13 | PartialRequest = 3,
14 | PartialResponse = 4,
15 | Completed = 5
16 | }
17 |
18 | public struct HandshakeHandler
19 | {
20 | public Action Receive;
21 | public Action Write;
22 | public HandshakeHandler(Action write, Action receive)
23 | {
24 | Write = write;
25 | Receive = receive;
26 | }
27 | }
28 |
29 | public class Handshake
30 | {
31 | private static Dictionary> handshakeHandlers = new Dictionary>();
32 | public static void RegisterHandshakeHandler(HandshakeStage stage, HandshakeHandler handler)
33 | {
34 | if (!handshakeHandlers.ContainsKey(stage)) { handshakeHandlers[stage] = new List(); }
35 | handshakeHandlers[stage].Add(handler);
36 | }
37 | public static void WriteHandshake(HandshakeStage stage, NetID peer, NetMessage message)
38 | {
39 | List handlers;
40 | if (!handshakeHandlers.TryGetValue(stage, out handlers)) { return; }
41 | foreach (HandshakeHandler handler in handlers)
42 | {
43 | handler.Write(peer, message);
44 | }
45 | }
46 | public static void ReadHandshake(HandshakeStage stage, NetID peer, NetMessage message)
47 | {
48 | List handlers;
49 | if (!handshakeHandlers.TryGetValue(stage, out handlers)) { return; }
50 | foreach (HandshakeHandler handler in handlers)
51 | {
52 | handler.Receive(peer, message);
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Framework/Network/MultiplayerReceiver.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Net.Sockets;
8 | using System.Text;
9 | using System.Threading;
10 |
11 | namespace Spectrum.Framework.Network
12 | {
13 | public class MultiplayerReceiver
14 | {
15 | private NetworkStream NetStream;
16 | private TcpClient Client;
17 | private Connection Connection;
18 | public bool Running { get; private set; }
19 | public MultiplayerReceiver(Connection conn)
20 | {
21 | Connection = conn;
22 | Client = conn.Client;
23 | NetStream = Client.GetStream();
24 | //new AsyncListenData(ListenForControlData).BeginInvoke(null, this);
25 | Thread receiverThread = new Thread(new ThreadStart(ListenForControlData))
26 | {
27 | IsBackground = true
28 | };
29 | Running = true;
30 | receiverThread.Start();
31 | }
32 | public void ListenForControlData()
33 | {
34 | try
35 | {
36 | DateTime lastHeard = DateTime.Now;
37 | NetStream.ReadTimeout = 10000;
38 | byte[] guidBuffer = new byte[16];
39 | while (Running && Client.Connected)
40 | {
41 | byte comType = (byte)NetStream.ReadByte();
42 | NetMessage header = new NetMessage(NetStream);
43 | Connection.PeerID = header.Read();
44 | NetMessage message = ReadFromControlStream(NetStream);
45 | switch (comType)
46 | {
47 | case 255:
48 | throw new InvalidDataException($"Bad data from host: {Connection.PeerID}");
49 | default:
50 | Connection.MPService.ReceiveMessage(comType, Connection.PeerID, message);
51 | break;
52 | }
53 | lastHeard = DateTime.Now;
54 | }
55 | }
56 | catch (InvalidDataException) { }
57 | catch (IOException) { }
58 | catch (NullReferenceException) { }
59 | finally
60 | {
61 | Running = false;
62 | }
63 | }
64 |
65 | public NetMessage ReadFromControlStream(Stream netStream)
66 | {
67 | NetMessage messageOut = new NetMessage(netStream);
68 | byte testBytes = (byte)netStream.ReadByte();
69 | if (testBytes != 255)
70 | {
71 | DebugPrinter.Print($"Bad data from host: {Connection.PeerID}");
72 | Connection.Terminate();
73 | }
74 | return messageOut;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Framework/Network/NetID.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using Replicate;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace Spectrum.Framework.Network
9 | {
10 | [ReplicateType]
11 | public struct NetID : IComparable
12 | {
13 | public ulong? SteamID;
14 | public Guid? Guid;
15 |
16 | public static bool operator >(NetID lhs, NetID rhs)
17 | {
18 | return lhs.CompareTo(rhs) > 0;
19 | }
20 | public static bool operator <(NetID lhs, NetID rhs)
21 | {
22 | return lhs.CompareTo(rhs) < 0;
23 | }
24 |
25 | public static bool operator ==(NetID lhs, NetID rhs)
26 | {
27 | return lhs.Equals(rhs);
28 | }
29 |
30 | public static bool operator !=(NetID lhs, NetID rhs)
31 | {
32 | return !lhs.Equals(rhs);
33 | }
34 |
35 | public NetID(Guid guid)
36 | {
37 | this.Guid = guid;
38 | SteamID = null;
39 | }
40 |
41 | public NetID(ulong steamID)
42 | {
43 | SteamID = steamID;
44 | Guid = null;
45 | }
46 |
47 | public override bool Equals(object obj)
48 | {
49 | if(obj is NetID)
50 | {
51 | NetID other = (NetID)obj;
52 | return other.Guid == Guid && other.SteamID == SteamID;
53 | }
54 | return false;
55 | }
56 |
57 | public override int GetHashCode() => Guid?.GetHashCode() ?? SteamID.GetHashCode();
58 |
59 | public int CompareTo(object obj)
60 | {
61 | if(obj is NetID)
62 | {
63 | NetID otherNetID = (NetID)obj;
64 | if (SteamID != null)
65 | {
66 | if (otherNetID.SteamID == null)
67 | return 1;
68 | return SteamID.Value.CompareTo(otherNetID.SteamID.Value);
69 | }
70 | else if(Guid != null)
71 | {
72 | if (otherNetID.Guid == null)
73 | return 1;
74 | return Guid.Value.CompareTo(otherNetID.Guid.Value);
75 | }
76 | return 0;
77 | }
78 | throw new ArgumentException("Cannot compare to anything but NetID");
79 | }
80 | public override string ToString()
81 | {
82 | if (Guid.HasValue) return Guid.Value.ToString();
83 | else if (SteamID.HasValue) return $"Steam: {SteamID.Value}";
84 | return "null";
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Framework/Network/ReplicationData.cs:
--------------------------------------------------------------------------------
1 | using Replicate.MetaData;
2 | using Spectrum.Framework.Entities;
3 | using Spectrum.Framework.Network.Surrogates;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace Spectrum.Framework.Network
12 | {
13 | public interface IReplicatable
14 | {
15 | InitData InitData { get; set; }
16 | ReplicationData ReplicationData { get; set; }
17 | }
18 | public class ReplicationData
19 | {
20 | public const float DefaultReplicationPeriod = .2f;
21 | public TypeAccessor TypeData { get; private set; }
22 | public IReplicatable Replicated { get; private set; }
23 | public ReplicationData(TypeAccessor typeData, IReplicatable replicated)
24 | {
25 | TypeData = typeData;
26 | Replicated = replicated;
27 | }
28 | private Dictionary interpolators = new Dictionary();
29 |
30 | public void SetInterpolator(string attributeName, Func interpolator)
31 | {
32 | interpolators[attributeName] = new Interpolator(interpolator);
33 | }
34 |
35 | public void HandleRPC(string name, object[] args)
36 | {
37 | if (TypeData.Methods.ContainsKey(name))
38 | TypeData.Methods[name].Invoke(Replicated, args);
39 | }
40 |
41 | public virtual NetMessage WriteReplicationData(NetMessage output)
42 | {
43 | Primitive[] fields = TypeData.Members.Values.ToList().ConvertAll(x => new Primitive(x.GetValue(Replicated))).ToArray();
44 | output.Write(fields);
45 | return output;
46 | }
47 | public virtual void ReadReplicationData(NetMessage input)
48 | {
49 | Primitive[] fields = input.Read();
50 | var properties = TypeData.Members.Values.ToList();
51 | for (int i = 0; i < fields.Count(); i++)
52 | {
53 | var replicate = properties[i];
54 | if (interpolators.ContainsKey(replicate.Info.Name))
55 | interpolators[replicate.Info.Name].BeginInterpolate(DefaultReplicationPeriod * 2, fields[i].Object);
56 | else
57 | replicate.SetValue(Replicated, fields[i].Object);
58 | }
59 | }
60 | public void Interpolate(float dt)
61 | {
62 | foreach (var interpolator in interpolators)
63 | {
64 | if (!interpolator.Value.NeedsUpdate)
65 | continue;
66 | var member = TypeData[interpolator.Key];
67 | object value = interpolator.Value.Update(dt, member.GetValue(Replicated));
68 | if (value != null)
69 | member.SetValue(Replicated, value);
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Framework/Network/ReplyWaiter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 |
7 | namespace Spectrum.Framework.Network
8 | {
9 | ///
10 | /// Allows you to block and wait for all current peers to respond to your request
11 | ///
12 | public class ReplyWaiter
13 | {
14 | private bool noUpdateOnRemove;
15 | private volatile List replies = new List();
16 | private List requiredReplies = new List();
17 | private static List waiters = new List();
18 |
19 | public ReplyWaiter(bool noUpdateOnRemove, params NetID[] requiredGuids)
20 | {
21 | this.noUpdateOnRemove = noUpdateOnRemove;
22 | requiredReplies = requiredGuids.ToList();
23 | lock (waiters) { waiters.Add(this); }
24 | }
25 |
26 | public ReplyWaiter(params NetID[] requiredGuids)
27 | : this(false, requiredGuids) { }
28 |
29 | public void AddReply(NetID id)
30 | {
31 | lock (this)
32 | {
33 | if (!replies.Contains(id))
34 | {
35 | replies.Add(id);
36 | Monitor.Pulse(this);
37 | }
38 | }
39 | }
40 | private bool allReplied
41 | {
42 | get
43 | {
44 | foreach (NetID peer in requiredReplies)
45 | {
46 | if (!replies.Contains(peer))
47 | {
48 | return false;
49 | }
50 | }
51 | return true;
52 | }
53 | }
54 | public void WaitReplies()
55 | {
56 | lock (this)
57 | {
58 | while (!allReplied)
59 | {
60 | Monitor.Wait(this);
61 | }
62 | }
63 | lock (waiters) { waiters.Remove(this); }
64 | }
65 | public static void PeerRemoved(NetID removedPeer)
66 | {
67 | lock (waiters)
68 | {
69 | foreach (ReplyWaiter waiter in waiters)
70 | {
71 | lock (waiter)
72 | {
73 | if (waiter.noUpdateOnRemove)
74 | continue;
75 | waiter.requiredReplies.Remove(removedPeer);
76 | Monitor.Pulse(waiter);
77 | }
78 | }
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Framework/Network/Serialization.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Newtonsoft.Json.Linq;
3 | using ProtoBuf;
4 | using ProtoBuf.Meta;
5 | using Replicate;
6 | using Replicate.MetaData;
7 | using Replicate.Serialization;
8 | using Spectrum.Framework.Entities;
9 | using Spectrum.Framework.Graphics;
10 | using Spectrum.Framework.Network.Surrogates;
11 | using Spectrum.Framework.Physics.Collision.Shapes;
12 | using System;
13 | using System.Collections.Generic;
14 | using System.IO;
15 | using System.Linq;
16 | using System.Runtime.Serialization;
17 | using System.Runtime.Serialization.Formatters.Binary;
18 | using System.Text;
19 | using System.Threading.Tasks;
20 |
21 | namespace Spectrum.Framework.Network
22 | {
23 | public class Serialization
24 | {
25 | public static readonly BinarySerializer BinarySerializer = new BinarySerializer(TypeHelper.Model);
26 | private static bool Initialized = false;
27 | public static void Initialize()
28 | {
29 | if (Initialized)
30 | return;
31 | Initialized = true;
32 | TypeHelper.Model.Add(typeof(Vector2)).AddMember("X").AddMember("Y");
33 | TypeHelper.Model.Add(typeof(Vector3)).AddMember("X").AddMember("Y").AddMember("Z");
34 | TypeHelper.Model.Add(typeof(Rectangle)).AddMember("X").AddMember("Y").AddMember("Width").AddMember("Height");
35 | TypeHelper.Model.Add(typeof(MemoryStream)).SetSurrogate(new Surrogate(typeof(StreamSurrogate)));
36 | TypeHelper.Model.Add(typeof(float[,])).SetSurrogate(new Surrogate(typeof(FloatArraySurrogate)));
37 | TypeHelper.Model.Add(typeof(Primitive)).SetSurrogate(typeof(PrimitiveSurrogate));
38 | TypeHelper.Model.Add(typeof(SpecModel)).SetSurrogate(typeof(ModelSurrogate));
39 | TypeHelper.Model.Add(typeof(Shape));
40 | TypeHelper.Model.Add(typeof(BoxShape));
41 | TypeHelper.Model.Add(typeof(CylinderShape));
42 | }
43 | public static T Copy(T obj) where T : class, new()
44 | {
45 | if (obj == null) return null;
46 | var accessor = TypeHelper.Model.GetTypeAccessor(obj.GetType());
47 | return TypeUtil.CopyToRaw(obj, accessor, null, accessor) as T;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Framework/Network/SteamP2PReceiver.cs:
--------------------------------------------------------------------------------
1 | using Steamworks;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Net.Sockets;
8 | using System.Text;
9 | using System.Threading;
10 |
11 | namespace Spectrum.Framework.Network
12 | {
13 | class SteamP2PReceiver
14 | {
15 | private MultiplayerService mpService;
16 | private volatile bool Running;
17 |
18 | public SteamP2PReceiver(MultiplayerService mpService)
19 | {
20 | this.mpService = mpService;
21 | Thread receiverThread = new Thread(new ThreadStart(ListenForData));
22 | Running = true;
23 | receiverThread.IsBackground = true;
24 | receiverThread.Start();
25 | }
26 |
27 | private void ListenForData()
28 | {
29 | uint messageSize;
30 | while (Running)
31 | {
32 | try
33 | {
34 | while (Steamworks.SteamNetworking.IsP2PPacketAvailable(out messageSize))
35 | {
36 | byte[] buffer = new byte[messageSize];
37 | CSteamID steamID;
38 | SteamNetworking.ReadP2PPacket(buffer, messageSize, out messageSize, out steamID);
39 | MemoryStream data = new MemoryStream(buffer);
40 |
41 | byte comType = (byte)data.ReadByte();
42 | NetMessage header = new NetMessage(data);
43 | header.Read();
44 |
45 | NetMessage messageOut = new NetMessage(data);
46 | byte testBytes = (byte)data.ReadByte();
47 | if (testBytes != 255)
48 | {
49 | }
50 |
51 | mpService.ReceiveMessage(comType, new NetID(steamID.m_SteamID), messageOut);
52 | }
53 | Thread.Sleep(1);
54 | }
55 | catch (Exception e)
56 | {
57 | DebugPrinter.Print(e.Message);
58 | }
59 | }
60 | }
61 | public void Terminate()
62 | {
63 | Running = false;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Framework/Network/Surrogates/EntitySurrogate.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using Spectrum.Framework.Entities;
3 | using Spectrum.Framework.Network.Surrogates;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Spectrum.Framework.Network.Surrogates
11 | {
12 | [ProtoContract]
13 | public class EntitySurrogate where T : Entity
14 | {
15 | [ProtoMember(1)]
16 | Guid id;
17 | public static implicit operator EntitySurrogate(T entity)
18 | {
19 | if (entity == null) return null;
20 | return new EntitySurrogate() { id = entity.ID };
21 | }
22 | public static implicit operator T(EntitySurrogate entity)
23 | {
24 | if (entity == null) return null;
25 | return SpectrumGame.Game.EntityManager.Find(entity.id) as T;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Framework/Network/Surrogates/FloatArraySurrogate.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using Replicate;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Spectrum.Framework.Network.Surrogates
10 | {
11 | [ProtoContract]
12 | [ReplicateType]
13 | public class FloatArraySurrogate
14 | {
15 | [Replicate]
16 | int width;
17 | [Replicate]
18 | int height;
19 | [Replicate]
20 | float[] buffer;
21 | private FloatArraySurrogate() { }
22 | public FloatArraySurrogate(int width, int height)
23 | {
24 | this.width = width;
25 | this.height = height;
26 | buffer = new float[width * height];
27 | }
28 | public float this[int i1, int i2]
29 | {
30 | get { return buffer[i1 + width * i2]; }
31 | set { buffer[i1 + width * i2] = value; }
32 | }
33 | public static implicit operator FloatArraySurrogate(float[,] array)
34 | {
35 | if (array == null) return null;
36 | float[] buffer = new float[array.GetLength(0) * array.GetLength(1)];
37 | for (int i = 0; i < array.GetLength(1); i++)
38 | {
39 | for (int j = 0; j < array.GetLength(0); j++)
40 | {
41 | buffer[j + i * array.GetLength(0)] = array[j, i];
42 | }
43 | }
44 | return new FloatArraySurrogate(array.GetLength(0), array.GetLength(1)) { buffer = buffer };
45 | }
46 | public static implicit operator float[,] (FloatArraySurrogate stream)
47 | {
48 | if (stream == null) return null;
49 | float[,] output = new float[stream.width, stream.height];
50 | for (int i = 0; i < stream.height; i++)
51 | {
52 | for (int j = 0; j < stream.width; j++)
53 | {
54 | output[j, i] = stream.buffer[j + i * stream.width];
55 | }
56 | }
57 | return output;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Framework/Network/Surrogates/JSONSurrogate.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Linq;
3 | using ProtoBuf;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Spectrum.Framework.Network.Surrogates
11 | {
12 | [ProtoContract]
13 | class JSONSurrogate
14 | {
15 | [ProtoMember(1)]
16 | string json;
17 | public static implicit operator JToken(JSONSurrogate surrogate)
18 | {
19 | return JToken.Parse(surrogate.json);
20 | }
21 | public static implicit operator JSONSurrogate(JToken token)
22 | {
23 | if (token == null) return null;
24 | return new JSONSurrogate() { json = token.ToString(Formatting.None) };
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Framework/Network/Surrogates/ModelSurrogate.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using Spectrum.Framework.Content;
3 | using Spectrum.Framework.Graphics;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Spectrum.Framework.Network.Surrogates
11 | {
12 | [ProtoContract]
13 | public class ModelSurrogate
14 | {
15 | [ProtoMember(1)]
16 | string ModelName;
17 | public static implicit operator SpecModel(ModelSurrogate surrogate)
18 | {
19 | if (surrogate.ModelName == null) return null;
20 | return ContentHelper.Load(surrogate.ModelName);
21 | }
22 | public static implicit operator ModelSurrogate(SpecModel model)
23 | {
24 | return new ModelSurrogate() { ModelName = model?.Name };
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Framework/Network/Surrogates/PrimitiveSurrogate.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Linq;
2 | using ProtoBuf;
3 | using Replicate;
4 | using Replicate.MetaData;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Reflection;
10 | using System.Runtime.Serialization.Formatters.Binary;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 |
14 | namespace Spectrum.Framework.Network.Surrogates
15 | {
16 | public class Primitive
17 | {
18 | public object Object;
19 | public Primitive() { }
20 | public Primitive(object obj)
21 | {
22 | if (obj is Enum)
23 | obj = (int)obj;
24 | Object = obj;
25 | }
26 | }
27 | [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
28 | [ReplicateType]
29 | public class PrimitiveSurrogate
30 | {
31 | public TypeId type;
32 | public byte[] buffer;
33 |
34 | private object Object
35 | {
36 | get
37 | {
38 | if (type.Id.IsEmpty) return null;
39 | MemoryStream stream = new MemoryStream(buffer);
40 | Type objType = TypeHelper.Model.GetType(type);
41 | if (objType == typeof(JToken))
42 | return (JToken)(JSONSurrogate)Serializer.NonGeneric.Deserialize(typeof(JSONSurrogate), stream);
43 | return Serialization.BinarySerializer.Deserialize(objType, stream);
44 | }
45 | }
46 | public static implicit operator PrimitiveSurrogate(Primitive obj)
47 | {
48 | if (obj == null) return null;
49 | if (obj.Object != null)
50 | {
51 | TypeId type = TypeHelper.Model.GetId(obj.Object.GetType());
52 | MemoryStream buffer = new MemoryStream();
53 | Serialize(buffer, obj.Object);
54 | return new PrimitiveSurrogate() { type = type, buffer = buffer.ToArray() };
55 | }
56 | return new PrimitiveSurrogate();
57 | }
58 | public static implicit operator Primitive(PrimitiveSurrogate obj)
59 | {
60 | if (obj == null) return null;
61 | return new Primitive(obj.Object);
62 | }
63 | public static MemoryStream Serialize(MemoryStream stream, object obj)
64 | {
65 | if (obj is JToken)
66 | obj = (JSONSurrogate)(JToken)obj;
67 | Serialization.BinarySerializer.Serialize(obj.GetType(), obj, stream);
68 | return stream;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Framework/Network/Surrogates/StreamSurrogate.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using Replicate;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Spectrum.Framework.Network.Surrogates
11 | {
12 | [ProtoContract]
13 | [ReplicateType]
14 | public class StreamSurrogate
15 | {
16 | [ProtoMember(1)]
17 | public byte[] buffer;
18 | public static implicit operator StreamSurrogate(MemoryStream stream)
19 | {
20 | if (stream == null) return null;
21 | return new StreamSurrogate() { buffer = stream.ToArray() };
22 | }
23 | public static implicit operator MemoryStream(StreamSurrogate stream)
24 | {
25 | if (stream == null) return null;
26 | return new MemoryStream(stream.buffer);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Framework/Network/UDPReceiver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.Sockets;
7 | using System.Text;
8 | using System.Threading;
9 |
10 | namespace Spectrum.Framework.Network
11 | {
12 | class UDPReceiver
13 | {
14 | private MultiplayerService mpService;
15 | private UdpClient client;
16 | private IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 0);
17 |
18 | public UDPReceiver(MultiplayerService mpService, UdpClient client)
19 | {
20 | this.mpService = mpService;
21 | this.client = client;
22 | Thread receiverThread = new Thread(new ThreadStart(ListenForData));
23 | receiverThread.IsBackground = true;
24 | receiverThread.Start();
25 | }
26 |
27 | private void ListenForData()
28 | {
29 | while (true)
30 | {
31 | try
32 | {
33 | NetMessage data = new NetMessage(client.Receive(ref endpoint));
34 | byte comType = data.Read();
35 | NetID peerGuid = data.Read();
36 | NetMessage userMessage = data.Read();
37 | switch (comType)
38 | {
39 | case FrameworkMessages.KeepAlive:
40 | break;
41 | default:
42 | mpService.ReceiveMessage(comType, peerGuid, userMessage);
43 | break;
44 | }
45 |
46 | }
47 | catch (Exception e)
48 | {
49 | DebugPrinter.Print(e.Message);
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Framework/Network/UDPSender.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.Sockets;
7 | using System.Text;
8 | using System.Threading;
9 |
10 | namespace Spectrum.Framework.Network
11 | {
12 | class UDPSender
13 | {
14 | struct QueueItem
15 | {
16 | public byte MessageType;
17 | public IPEndPoint dest;
18 | public NetMessage toWrite;
19 | }
20 | private Semaphore writeSem = new Semaphore(0, 10);
21 | private UdpClient client;
22 | private Queue dataQueue;
23 | private NetID mpID;
24 |
25 | public UDPSender(NetID mpID, UdpClient client)
26 | {
27 | this.mpID = mpID;
28 | dataQueue = new Queue();
29 | this.client = client;
30 | new AsyncSendData(SendData).BeginInvoke(null, this);
31 | }
32 | private delegate void AsyncSendData();
33 | private void SendData()
34 | {
35 | try
36 | {
37 | while (true)
38 | {
39 | if (!writeSem.WaitOne(100)) { continue; };
40 | QueueItem item;
41 | lock (dataQueue)
42 | {
43 | if (dataQueue.Count == 0) { throw new Exception("Write semaphore was signaled without anything to dequeue"); }
44 | item = dataQueue.Dequeue();
45 | }
46 | lock (client)
47 | {
48 | NetMessage data = new NetMessage();
49 | data.Write(item.MessageType);
50 | data.Write(mpID);
51 | data.Write(item.toWrite);
52 | byte[] dataArray = data.stream.ToArray();
53 | client.Send(dataArray, dataArray.Length, item.dest);
54 | }
55 | }
56 | }
57 | catch (Exception e)
58 | {
59 | DebugPrinter.Print(e.Message);
60 | }
61 | }
62 | public void SendMessage(IPEndPoint dest, byte messageType, NetMessage message)
63 | {
64 | WriteToStream(messageType, dest, message);
65 | }
66 | private void WriteToStream(byte EventType, IPEndPoint dest, NetMessage toWrite)
67 | {
68 | if (toWrite == null)
69 | {
70 | toWrite = new NetMessage();
71 | }
72 | QueueItem item = new QueueItem();
73 | item.MessageType = EventType;
74 | item.toWrite = toWrite;
75 | item.dest = dest;
76 | lock (dataQueue)
77 | {
78 | try
79 | {
80 | writeSem.Release();
81 | dataQueue.Enqueue(item);
82 | }
83 | catch (SemaphoreFullException)
84 | {
85 | DebugPrinter.Print("Write queue is full");
86 | }
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Framework/Physics/Collision/GroundInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Spectrum.Framework.Entities;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace Spectrum.Framework.Physics.Collision
9 | {
10 | public class CollisionInfo
11 | {
12 | public GameObject Object { get; private set; }
13 | public Vector3 Normal { get; private set; }
14 | public Vector3 Point { get; private set; }
15 | private CollisionInfo() { }
16 | public CollisionInfo(GameObject other, Vector3 point, Vector3 normal)
17 | {
18 | Point = point;
19 | Object = other;
20 | Normal = normal;
21 | }
22 | public void Update(Vector3 point, Vector3 normal)
23 | {
24 | Point = point;
25 | Normal = normal;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Framework/Physics/Collision/ICollidable.cs:
--------------------------------------------------------------------------------
1 | using Spectrum.Framework.Physics.Collision.Shapes;
2 | using Spectrum.Framework.Physics.LinearMath;
3 | using Microsoft.Xna.Framework;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 |
9 | namespace Spectrum.Framework.Physics.Collision
10 | {
11 | public interface ICollidable
12 | {
13 | Shape Shape { get; }
14 | Vector3 Position { get; }
15 | Vector3 Velocity { get; }
16 | Quaternion Orientation { get; }
17 | Quaternion InvOrientation { get; }
18 | JBBox BoundingBox { get; }
19 | }
20 | public static class ICollidableExtension
21 | {
22 | public static JBBox SweptBoundingBox(this ICollidable col)
23 | {
24 | JBBox box = col.BoundingBox;
25 | box.AddPoint(box.Max + col.Velocity);
26 | box.AddPoint(box.Min + col.Velocity);
27 | return box;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Framework/Physics/Collision/Shapes/ISupportMappable.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Spectrum.Framework.Physics.Collision.Shapes
8 | {
9 | ///
10 | /// The implementation of the ISupportMappable interface defines the form
11 | /// of a shape.
12 | ///
13 | public interface ISupportMappable
14 | {
15 |
16 | ///
17 | /// SupportMapping. Finds the point in the shape furthest away from the given direction.
18 | /// Imagine a plane with a normal in the search direction. Now move the plane along the normal
19 | /// until the plane does not intersect the shape. The last intersection point is the result.
20 | ///
21 | /// The direction.
22 | /// The result.
23 | /// Set to true when searching for the collision normal and penetration,
24 | /// useful to read in multishapes where sub shapes may have edges that produce wonky collision normals.
25 | void SupportMapping(ref Vector3 direction, out Vector3 result, bool retrievingInformation);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Framework/Physics/Collision/Shapes/ListMultishape.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using ProtoBuf;
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 |
9 | namespace Spectrum.Framework.Physics.Collision.Shapes
10 | {
11 | [ProtoContract]
12 | public class ListMultishape : Multishape
13 | {
14 | [ProtoMember(1)]
15 | public List Shapes;
16 | private int currentShape;
17 |
18 | public ListMultishape() : this(new List()) { }
19 |
20 | public ListMultishape(List shapes)
21 | {
22 | if (shapes.Any((Shape shape) => (shape is Multishape)))
23 | {
24 | throw new ArgumentException("Cannot use Multishapes in a ListMultishape");
25 | }
26 | Shapes = shapes;
27 | }
28 |
29 | public void Add(Shape shape)
30 | {
31 | if (shape == null) throw new ArgumentNullException(nameof(shape));
32 | Shapes.Add(shape);
33 | UpdateShape();
34 | }
35 |
36 | public void Remove(Shape shape)
37 | {
38 | Shapes.Remove(shape);
39 | UpdateShape();
40 | }
41 |
42 | public override void SetCurrentShape(int index)
43 | {
44 | currentShape = index;
45 | }
46 | //TODO: These could be optimized
47 | public override int Prepare(ref LinearMath.JBBox box)
48 | {
49 | return Shapes.Count;
50 | }
51 |
52 | public override int Prepare(ref Vector3 rayOrigin, ref Vector3 rayDelta)
53 | {
54 | return Shapes.Count;
55 | }
56 |
57 | protected override Multishape CreateWorkingClone()
58 | {
59 | ListMultishape output = new ListMultishape();
60 | output.Shapes = Shapes;
61 | output.currentShape = currentShape;
62 |
63 | return output;
64 | }
65 |
66 | public override void SupportMapping(ref Vector3 direction, out Vector3 result)
67 | {
68 | Shapes[currentShape].SupportMapping(ref direction, out result);
69 | }
70 |
71 | public override void SetScale(float scale)
72 | {
73 | foreach (var shape in Shapes)
74 | shape.SetScale(scale);
75 | UpdateShape();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Framework/Physics/Dynamics/Material.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Spectrum.Framework.Physics.Dynamics
6 | {
7 |
8 | // TODO: Check values, Documenation
9 | // Maybe some default materials, aka Material.Soft?
10 | public class Material
11 | {
12 | public float kineticFriction = 0.1f;
13 | public float staticFriction = 0.6f;
14 | public float restitution = 0.2f;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Framework/Point.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework
8 | {
9 | public struct Point : IEquatable
10 | {
11 | public int X { get; set; }
12 | public int Y { get; set; }
13 | public static Point operator +(Point a, Point b) => new Point(a.X + b.X, a.Y + b.Y);
14 | public static Point operator -(Point a, Point b) => new Point(a.X - b.X, a.Y - b.Y);
15 | public Point(int x, int y) { X = x; Y = y; }
16 | public static explicit operator Vector2(Point point) => new Vector2(point.X, point.Y);
17 | public static explicit operator Point(Vector2 vector) => new Point() { X = (int)Math.Floor(vector.X), Y = (int)Math.Floor(vector.Y) };
18 |
19 | public override string ToString()
20 | {
21 | return $"{X},{Y}";
22 | }
23 |
24 | #region Equality
25 | public static bool operator ==(Point left, Point right)
26 | {
27 | return left.Equals(right);
28 | }
29 |
30 | public static bool operator !=(Point left, Point right)
31 | {
32 | return !(left == right);
33 | }
34 |
35 | public override bool Equals(object obj)
36 | {
37 | return obj is Point point && Equals(point);
38 | }
39 |
40 | public bool Equals(Point other)
41 | {
42 | return X == other.X &&
43 | Y == other.Y;
44 | }
45 |
46 | public override int GetHashCode()
47 | {
48 | int hashCode = 1861411795;
49 | hashCode = hashCode * -1521134295 + X.GetHashCode();
50 | hashCode = hashCode * -1521134295 + Y.GetHashCode();
51 | return hashCode;
52 | }
53 | #endregion
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Framework/Ray.cs:
--------------------------------------------------------------------------------
1 | using Spectrum.Framework.Physics.LinearMath;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | public struct Ray
11 | {
12 | public Ray(Vector3 position, Vector3 direction) { Direction = direction; Position = position; }
13 | public Vector3 Direction { get; set; }
14 | public Vector3 Position { get; set; }
15 | public float? Intersects(JBBox box)
16 | {
17 | return box.RayIntersect(Position, Direction);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Framework/Rectangle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | [DebuggerDisplay("[X:{X} Y:{Y} Width:{Width} Height:{Height}]")]
11 | public struct Rectangle
12 | {
13 | public Rectangle(int x, int y, int width, int height) { X = x; Y = y; Width = width; Height = height; }
14 | public int X { get; set; }
15 | public int Y { get; set; }
16 | public int Width { get; set; }
17 | public int Height { get; set; }
18 | public int Top => Y;
19 | public int YMax => Y + Height;
20 | public int YMin => Y;
21 | public int Right => X + Width;
22 | public int Bottom => Y + Height;
23 | public int Left => X;
24 | public Point TopLeft => new Point(X, Y);
25 | public Rectangle Clip(Rectangle other)
26 | {
27 | var x = Math.Max(X, other.X);
28 | var y = Math.Max(Y, other.Y);
29 | return new Rectangle(x, y,
30 | Math.Min(Right, other.Right) - x, Math.Min(Bottom, other.Bottom) - y);
31 | }
32 | public bool Intersects(Rectangle other)
33 | {
34 | return other.X < X + Width && other.X + other.Width > X
35 | && other.Y < Y + Height && other.Y + other.Height > Y;
36 | }
37 | public bool Contains(Point p) => Left <= p.X && p.X <= Right && Top <= p.Y && p.Y <= Bottom;
38 | public Rectangle Translate(Point p) => new Rectangle(X + p.X, Y + p.Y, Width, Height);
39 | ///
40 | /// Scales the source rectangle (and optionally centers it) to the destination rectangle while maintaining the original aspect ratio.
41 | /// The crop paremeter determines whether to scale up (overflowing and requiring a crop) or down (requiring no crop).
42 | ///
43 | ///
44 | ///
45 | /// If true will scale the source up, else down
46 | ///
47 | ///
48 | public Rectangle FitTo(Rectangle destination, bool crop = true, bool center = true)
49 | {
50 | bool scaleX = (!crop) ^ (destination.Width * 1.0 / Width * Height > destination.Height);
51 | double scale = scaleX ? destination.Width * 1.0 / Width : destination.Height * 1.0 / Height;
52 | var fittedX = X;
53 | var fittedY = Y;
54 | var fittedWidth = (int)(Width * scale);
55 | var fittedHeight = (int)(Height * scale);
56 | if (center)
57 | {
58 | fittedX = (destination.Width - fittedWidth) / 2;
59 | fittedY = (destination.Height - fittedHeight) / 2;
60 | }
61 | return new Rectangle(fittedX, fittedY, fittedWidth, fittedHeight);
62 | }
63 | public static implicit operator Microsoft.Xna.Framework.Rectangle(Rectangle r) => new Microsoft.Xna.Framework.Rectangle(r.X, r.Y, r.Width, r.Height);
64 | public static implicit operator Rectangle(Microsoft.Xna.Framework.Rectangle r) => new Rectangle(r.X, r.Y, r.Width, r.Height);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Framework/Schedule.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | public class Scheduler
11 | {
12 | public static Scheduler Current => Context.Current;
13 | public double Time = 0;
14 | SortedList> timers = new SortedList>();
15 | const double BestEffortBudget = 0.001;
16 | ConcurrentQueue> bestEffort = new ConcurrentQueue>();
17 | public void Wait(float scheduleAt, Action done) => timers.Add(Time + scheduleAt, done);
18 | // TODO: This could run immediate if `canBestEffort`
19 | public void RunBestEffort(Action done) => bestEffort.Enqueue(done);
20 | public void Update(float dt)
21 | {
22 | Time += dt;
23 | while (timers.Any() && timers.First().Key <= Time)
24 | {
25 | timers.First().Value(dt);
26 | timers.RemoveAt(0);
27 | }
28 | var start = DebugTiming.Now();
29 | while (BestEffortBudget > (DebugTiming.Now() - start) && bestEffort.TryDequeue(out var action))
30 | action(dt);
31 | }
32 | }
33 | public static class Schedule
34 | {
35 | public static void Wait(float scheduleAt, Action done) => Scheduler.Current.Wait(scheduleAt, done);
36 | public static void RunBestEffort(Action done) => Scheduler.Current.RunBestEffort(done);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Framework/Screens/DialogScreen.cs:
--------------------------------------------------------------------------------
1 | using Spectrum.Framework.Input;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework.Screens
9 | {
10 | public class DialogScreen : InGameScreen
11 | {
12 | public event Action OnClose;
13 | public KeyBind CloseKey;
14 |
15 | public override bool HandleInput(bool otherTookInput, InputState input)
16 | {
17 | return base.HandleInput(otherTookInput, input);
18 | }
19 |
20 | public override void Close()
21 | {
22 | OnClose?.Invoke(default(T));
23 | base.Close();
24 | }
25 |
26 | public void Close(T choice)
27 | {
28 | OnClose?.Invoke(choice);
29 | base.Close();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Framework/Screens/ElementSelector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework.Screens
8 | {
9 | public class Selector
10 | {
11 | // TODO: Implement properly?
12 | public static Selector Parse(string selector)
13 | {
14 | return new Selector(e => e?.HasTag(selector) ?? false);
15 | }
16 | private Func _selector;
17 | public Selector(Func selector) { _selector = selector; }
18 | public static implicit operator Selector(string selector) => Parse(selector);
19 | public static Selector operator &(Selector a, Selector b) => new Selector((e) => a.Matches(e) && b.Matches(e));
20 | public static Selector operator |(Selector a, Selector b) => new Selector((e) => a.Matches(e) || b.Matches(e));
21 | public static Selector operator !(Selector a) => new Selector((e) => !a.Matches(e));
22 | public static Selector Parent(Selector selector, bool recursive = false)
23 | {
24 | bool parent(Element e) => selector.Matches(e.Parent) || (recursive && (e.Parent != null && parent(e.Parent)));
25 | return new Selector(parent);
26 | }
27 | public static Selector IsVR = Parent(Parse("vrmenu"), true);
28 | public virtual bool Matches(Element element)
29 | {
30 | return _selector(element);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Framework/Screens/ElementSize.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Spectrum.Framework.Screens
8 | {
9 | public enum SizeType
10 | {
11 | WrapContent = 0,
12 | Flat,
13 | MatchParent
14 | }
15 | public struct ElementSize
16 | {
17 | public bool WrapContent;
18 | public static ElementSize Zero = new ElementSize(0);
19 | public static ElementSize WrapFill = new ElementSize(0, 1, true);
20 | public static ElementSize Wrap = new ElementSize(0, 0, true);
21 | public double Relative;
22 | public int Flat;
23 | public ElementSize(int flat = 0, double relative = 0, bool wrapContent = false)
24 | {
25 | Flat = flat;
26 | Relative = relative;
27 | WrapContent = wrapContent;
28 | }
29 | public int Measure(int parent, int content = 0)
30 | {
31 | if (WrapContent)
32 | return Math.Max(content, (int)(parent * Relative) + Flat);
33 | else
34 | return (int)(parent * Relative) + Flat;
35 | }
36 | public static implicit operator ElementSize(int size)
37 | {
38 | return new ElementSize(size);
39 | }
40 | public static implicit operator ElementSize(double size)
41 | {
42 | return new ElementSize(relative: size);
43 | }
44 | #region Equality
45 | public static bool operator ==(ElementSize a, ElementSize b)
46 | {
47 | return a.Equals(b);
48 | }
49 | public static bool operator !=(ElementSize a, ElementSize b)
50 | {
51 | return !(a == b);
52 | }
53 |
54 | public override bool Equals(object obj)
55 | {
56 | if (!(obj is ElementSize))
57 | {
58 | return false;
59 | }
60 |
61 | var size = (ElementSize)obj;
62 | return WrapContent == size.WrapContent &&
63 | Flat == size.Flat &&
64 | Relative == size.Relative;
65 | }
66 |
67 | public override int GetHashCode()
68 | {
69 | var hashCode = 76549531;
70 | hashCode = hashCode * -1521134295 + WrapContent.GetHashCode();
71 | hashCode = hashCode * -1521134295 + Flat.GetHashCode();
72 | hashCode = hashCode * -1521134295 + Relative.GetHashCode();
73 | return hashCode;
74 | }
75 | #endregion
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Framework/Screens/ElementStyle.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Replicate;
4 | using Replicate.MetaData;
5 | using Spectrum.Framework.Content;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace Spectrum.Framework.Screens
13 | {
14 | [ReplicateType]
15 | public struct ElementStyle
16 | {
17 | struct TagOverride
18 | {
19 | public Selector Selector;
20 | public ElementStyle Style;
21 |
22 | public TagOverride(Selector selector, ElementStyle style)
23 | {
24 | Selector = selector;
25 | Style = style;
26 | }
27 | }
28 | static TypeAccessor typeData;
29 | static ElementStyle()
30 | {
31 | typeData = TypeHelper.Model.GetTypeAccessor(typeof(ElementStyle));
32 | }
33 | private static List TagOverrides = new List();
34 | public static void OverrideTag(ElementStyle style)
35 | => TagOverrides.Add(new TagOverride(null, style));
36 | public static void OverrideTag(string selector, ElementStyle style)
37 | => OverrideTag(Selector.Parse(selector), style);
38 | public static void OverrideTag(Selector selector, ElementStyle style)
39 | => TagOverrides.Add(new TagOverride(selector, style));
40 | public static ElementStyle GetStyle(Element element)
41 | {
42 | ElementStyle output = new ElementStyle();
43 | foreach (var overriden in TagOverrides)
44 | {
45 | if(overriden.Selector?.Matches(element) ?? true)
46 | output.Apply(overriden.Style);
47 | }
48 | return output;
49 | }
50 | public ImageAsset Texture;
51 | public Color? TextureColor;
52 | public ImageAsset Background;
53 | public Color? FontColor;
54 | public Color? BackgroundColor;
55 | public Color? FillColor;
56 | public SpriteFont Font;
57 | public ElementSize? Width;
58 | public ElementSize? Height;
59 | public RectOffset? Padding;
60 | public RectOffset? Margin;
61 | public static T Value(T? value, T? parent = null) where T : struct
62 | {
63 | return value ?? parent ?? default(T);
64 | }
65 | public static T Value(T value, T parent) where T : class
66 | {
67 | return value ?? parent ?? default(T);
68 | }
69 | public void Apply(ElementStyle newStyle)
70 | {
71 | this = TypeUtil.CopyTo(newStyle, this);
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Framework/Screens/GridLayout.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework.Screens
9 | {
10 | public class GridLayout : Element
11 | {
12 | public GridLayout(int cols)
13 | {
14 | LayoutManager = new GridLayoutManager(cols);
15 | }
16 | }
17 | public class GridLayoutManager : LayoutManager
18 | {
19 | private int Cols;
20 | public GridLayoutManager(int cols) { Cols = cols; }
21 | public void OnLayout(Element element, Rectangle bounds)
22 | {
23 | int curRow = 0;
24 | int curCol = 0;
25 | foreach (var item in element.Children)
26 | {
27 | item.Layout(new Rectangle(colWidths.Where((h, i) => i < curCol).Sum(), rowHeights.Where((h, i) => i < curRow).Sum(), colWidths[curCol], rowHeights[curRow]));
28 | curCol += 1;
29 | if (curCol == Cols)
30 | {
31 | curRow += 1;
32 | curCol = 0;
33 | }
34 | }
35 | }
36 | private List rowHeights = new List();
37 | private List colWidths = new List();
38 | public void OnMeasure(Element element, int width, int height)
39 | {
40 | rowHeights.Clear();
41 | colWidths.Clear();
42 | colWidths.AddRange(Enumerable.Repeat(0, Cols));
43 | int curRow = 0;
44 | int curCol = 0;
45 | int cellSize = element.PreChildWidth(width) / Cols;
46 | int curRowHeight = 0;
47 | int childHeight = height;
48 | foreach (var child in element.Children)
49 | {
50 | // Without passing in content dimensions children cannot have a parent percentage
51 | // that is affected by other childrens' dimensions. Might be fine for grids?
52 | child.Measure(cellSize, cellSize);
53 | curRowHeight = Math.Max(child.MeasuredHeight, curRowHeight);
54 | colWidths[curCol] = Math.Max(colWidths[curCol], child.MeasuredWidth);
55 | curCol += 1;
56 | if (curCol == Cols)
57 | {
58 | rowHeights.Add(curRowHeight);
59 | curRowHeight = 0;
60 | curRow += 1;
61 | curCol = 0;
62 | }
63 | }
64 | rowHeights.Add(curRowHeight);
65 | element.MeasuredHeight = element.MeasureHeight(height, rowHeights.DefaultIfEmpty(0).Sum());
66 | element.MeasuredWidth = element.MeasureWidth(width, colWidths.DefaultIfEmpty(0).Sum());
67 | }
68 |
69 | public int ContentWidth(Element element)
70 | {
71 | return colWidths.DefaultIfEmpty(0).Sum();
72 | }
73 |
74 | public int ContentHeight(Element element)
75 | {
76 | return rowHeights.DefaultIfEmpty(0).Sum();
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Framework/Screens/InputElements/Button.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Microsoft.Xna.Framework.Input;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 |
9 | namespace Spectrum.Framework.Screens.InputElements
10 | {
11 | public class Button : InputElement
12 | {
13 | public readonly TextElement TextElement;
14 | public string Text
15 | {
16 | get => TextElement.Text;
17 | set => TextElement.Text = value;
18 | }
19 | public Button(string text)
20 | {
21 | TextElement = AddElement(new TextElement(text) { Positioning = PositionType.Center });
22 | }
23 | }
24 | public class ToggleButton : Button
25 | {
26 | public bool Value
27 | {
28 | get => HasTag("highlight");
29 | set
30 | {
31 | if (value)
32 | AddTag("highlight");
33 | else
34 | RemoveTag("highlight");
35 | OnValueChanged?.Invoke(value);
36 | }
37 | }
38 | public event Action OnValueChanged;
39 | public ToggleButton(string text) : base(text)
40 | {
41 | OnClick += (_) => Value ^= true;
42 | }
43 | }
44 | public class CycleButton : Button
45 | {
46 | public readonly List> Options;
47 | private readonly List values;
48 | public T Value { get; private set; }
49 | public int Index { get; private set; }
50 | public CycleButton(List> options) : base(options[0].Item2)
51 | {
52 | Options = options;
53 | Value = options[0].Item1;
54 | values = options.Select(t => t.Item1).ToList();
55 | OnClick += (_) => Cycle();
56 | }
57 | public void Cycle(int amount = 1)
58 | {
59 | SetIndex((Index + amount) % Options.Count);
60 | }
61 | public void SetIndex(int index)
62 | {
63 | this.Index = index;
64 | Value = Options[index].Item1;
65 | Text = Options[index].Item2;
66 | }
67 | public bool SetValue(T value)
68 | {
69 | var i = values.IndexOf(value);
70 | if (i >= 0)
71 | {
72 | SetIndex(i);
73 | return true;
74 | }
75 | return false;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Framework/Screens/InputElements/Checkbox.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework.Screens.InputElements
9 | {
10 | public delegate void OnToggleChanged(bool value);
11 | public class Checkbox : InputElement
12 | {
13 | Element valueIndicator;
14 | public bool Value
15 | {
16 | get => valueIndicator.Display;
17 | set => valueIndicator.Toggle(value);
18 | }
19 | public event OnToggleChanged OnValueChanged = null;
20 | public Checkbox()
21 | {
22 | Width = 20; Height = 20;
23 | }
24 | public bool ToggleValue(bool? value = null)
25 | {
26 | bool newValue = value ?? !Value;
27 | if (newValue != Value && OnValueChanged != null)
28 | OnValueChanged(newValue);
29 | Value = newValue;
30 | return Value;
31 | }
32 | public override void Initialize()
33 | {
34 | base.Initialize();
35 | valueIndicator = new Element();
36 | valueIndicator.Positioning = PositionType.Center;
37 | valueIndicator.Width = 6;
38 | valueIndicator.Height = 6;
39 | valueIndicator.AddTag("toggle-indicator");
40 | Value = false;
41 | AddElement(valueIndicator);
42 | OnClick += (_) => ToggleValue();
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Framework/Screens/InputElements/InputElement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Microsoft.Xna.Framework.Input;
4 | using Spectrum.Framework.Input;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 |
10 | namespace Spectrum.Framework.Screens.InputElements
11 | {
12 | public class InputElement : Element
13 | {
14 | public event Action OnClick;
15 | public event Action OnRightClick;
16 | public object Data;
17 | public string HoverText;
18 | private TextElement hoverElement;
19 | public InputElement()
20 | {
21 | RegisterHandler(new KeyBind(0), (input) =>
22 | {
23 | if (OnClick != null && MouseInside(input))
24 | {
25 | OnClick(this);
26 | input.ConsumeInput(new KeyBind(0), true);
27 | return true;
28 | }
29 | return false;
30 | });
31 | RegisterHandler(new KeyBind(1), (input) =>
32 | {
33 | if (OnRightClick != null && MouseInside(input))
34 | {
35 | OnRightClick(this);
36 | input.ConsumeInput(new KeyBind(1), true);
37 | return true;
38 | }
39 | return false;
40 | });
41 | }
42 | private void ClearHoverText() { hoverElement?.Parent?.RemoveElement(hoverElement); hoverElement = null; }
43 | public override bool HandleInput(bool otherTookInput, InputState input)
44 | {
45 | if (Display && MouseInside(input))
46 | {
47 | if (HoverText != null && hoverElement == null)
48 | {
49 | hoverElement = Root.AddElement(new TextElement(HoverText)
50 | {
51 | Positioning = PositionType.Absolute,
52 | BackgroundColor = Color.White,
53 | Z = 1000,
54 | ZDetach = true
55 | });
56 | }
57 | }
58 | else if (hoverElement != null) { ClearHoverText(); }
59 | return base.HandleInput(otherTookInput, input);
60 | }
61 | public override void Draw(float gameTime, SpriteBatch spritebatch)
62 | {
63 | if (hoverElement != null)
64 | {
65 | hoverElement.X = InputState.Current.MousePosition.X;
66 | hoverElement.Y = InputState.Current.MousePosition.Y - hoverElement.MeasuredHeight;
67 | }
68 | base.Draw(gameTime, spritebatch);
69 | }
70 | public void Click() => OnClick?.Invoke(this);
71 | public void RightClick() => OnRightClick?.Invoke(this);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Framework/Screens/InputElements/ListOption.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Spectrum.Framework.Screens.InputElements
8 | {
9 | public class ListOption : InputElement
10 | {
11 | public int Id { get; set; }
12 | public T Option { get; set; }
13 | public string Text { get { return text?.Text; } set { text.Text = value; } }
14 | private TextElement text = new TextElement("");
15 |
16 | public ListOption() : this(null, default(T)) { }
17 | public ListOption(string text, T option) : this(0, text, option) { }
18 | public ListOption(int id, string text, T tag)
19 | {
20 | Text = text;
21 | Id = id;
22 | Option = tag;
23 | }
24 | public override void Initialize()
25 | {
26 | base.Initialize();
27 | AddElement(text);
28 | text.Center();
29 | Width = ElementSize.WrapFill;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Framework/Screens/InputElements/ListSelector.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Microsoft.Xna.Framework.Input;
4 | using Spectrum.Framework.Input;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 |
10 | namespace Spectrum.Framework.Screens.InputElements
11 | {
12 | public delegate void PickedEventHandler(int id, T picked);
13 | public class ListSelector : InputElement
14 | {
15 | public event PickedEventHandler OnPick;
16 | private List> options = new List>();
17 | private int stringHeight;
18 | //The list selector's _rect is in absolute coordinates unlike other interface elements
19 | public ListSelector(Element parent, int x, int y, int width)
20 | {
21 | Positioning = PositionType.Absolute;
22 | X = x;
23 | Y = y;
24 | Width = new ElementSize() { Flat = width, WrapContent = true };
25 | LayoutManager = new LinearLayoutManager();
26 | }
27 | public override void Initialize()
28 | {
29 | base.Initialize();
30 | stringHeight = (int)Font.LineSpacing;
31 | }
32 | public void AddOption(string text, T tag)
33 | {
34 | int id = Children.Select(ele => ele is ListOption ? (ele as ListOption).Id : 0).DefaultIfEmpty(0).Max() + 1;
35 | AddOption(id, text, tag);
36 | }
37 | public void AddOption(int id, string text, T tag = default(T))
38 | {
39 | int optionHeight = stringHeight;
40 | ListOption option = new ListOption(id, text, tag);
41 | option.OnClick += optionClicked;
42 | options.Add(option);
43 | AddElement(option);
44 | }
45 | private void optionClicked(InputElement clicked)
46 | {
47 | if (OnPick != null)
48 | {
49 | var option = clicked as ListOption;
50 | OnPick(option.Id, option.Option);
51 | }
52 | Close();
53 | }
54 | public override bool HandleInput(bool otherTookInput, InputState input)
55 | {
56 | if (!Display)
57 | return false;
58 | otherTookInput |= base.HandleInput(otherTookInput, input);
59 | if (otherTookInput || input.IsNewMousePress(0) && !Rect.Contains(input.MousePosition))
60 | {
61 | Close();
62 | }
63 | return true;
64 | }
65 | public void Close()
66 | {
67 | Parent?.RemoveElement(this);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Framework/Screens/InputElements/Slider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.Xna.Framework;
7 | using Spectrum.Framework.Input;
8 |
9 | namespace Spectrum.Framework.Screens.InputElements
10 | {
11 | public class Slider : InputElement
12 | {
13 | InputElement sliderPull;
14 | InputElement sliderTrack;
15 | bool dragging = false;
16 | float sliderValue = 0;
17 | public event Action OnValueChanged = null;
18 | public event Action OnSliderFinished = null;
19 | public float Value
20 | {
21 | get { return sliderValue; }
22 | set
23 | {
24 | sliderValue = Math.Min(1, Math.Max(0, value));
25 | }
26 | }
27 | public Slider()
28 | {
29 | AddTag("slider");
30 | Width = 100;
31 | Height = 20;
32 | }
33 | public override void Initialize()
34 | {
35 | base.Initialize();
36 | sliderTrack = new InputElement();
37 | sliderTrack.AddTag("slider-track");
38 | sliderTrack.Height = 2;
39 | sliderTrack.Positioning = PositionType.Relative;
40 | sliderTrack.OnClick += (_) => dragging = true;
41 | AddElement(sliderTrack);
42 |
43 | sliderPull = new InputElement();
44 | sliderPull.AddTag("slider-pull");
45 | sliderPull.Height = 0.5f;
46 | sliderPull.Positioning = PositionType.Relative;
47 | sliderPull.OnClick += (_) => dragging = true;
48 | AddElement(sliderPull);
49 | }
50 | public override void OnMeasure(int width, int height)
51 | {
52 | base.OnMeasure(width, height);
53 | sliderPull.Width = sliderPull.MeasuredHeight;
54 | sliderTrack.Width = MeasuredWidth - Padding.WidthTotal(width) - sliderPull.MeasuredHeight;
55 | }
56 | public override void Layout(Rectangle bounds)
57 | {
58 | sliderTrack.Y = Rect.Height / 2 - sliderTrack.MeasuredHeight / 2;
59 | sliderTrack.X = Rect.Width / 2 - sliderTrack.MeasuredWidth / 2;
60 | sliderPull.X = (int)(sliderTrack.MeasuredWidth * sliderValue) + sliderTrack.X - sliderPull.MeasuredWidth / 2;
61 | sliderPull.Y = Rect.Height / 2 - sliderPull.MeasuredHeight / 2;
62 | base.Layout(bounds);
63 | }
64 | public override bool HandleInput(bool otherTookInput, InputState input)
65 | {
66 | if (dragging && !input.IsMouseDown(0))
67 | {
68 | OnSliderFinished?.Invoke(Value);
69 | dragging = false;
70 | }
71 | otherTookInput |= base.HandleInput(otherTookInput, input);
72 | if (dragging)
73 | {
74 | otherTookInput = true;
75 | Value = (input.MousePosition.X - sliderTrack.Rect.Left) * 1.0f / sliderTrack.MeasuredWidth;
76 | OnValueChanged?.Invoke(Value);
77 | }
78 | return otherTookInput;
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Framework/Screens/InputElements/TextElement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace Spectrum.Framework.Screens.InputElements
9 | {
10 | public class TextElement : Element
11 | {
12 | private int contentWidth;
13 | private int contentHeight;
14 | private bool _dirty = false;
15 | private string _text;
16 | public string Text
17 | {
18 | get => _text;
19 | set
20 | {
21 | if (_text != value)
22 | {
23 | _text = value;
24 | _dirty = true;
25 | }
26 | }
27 | }
28 | public Func TextSource;
29 |
30 | public TextElement(string text = null) { Text = text; }
31 | private void CacheDims()
32 | {
33 | if (_dirty)
34 | {
35 | _dirty = false;
36 | if (Text == null)
37 | {
38 | contentWidth = 0;
39 | contentHeight = (int)Font.MeasureString("a").Y;
40 | }
41 | else
42 | {
43 | contentWidth = (int)Font.MeasureString(Text).X;
44 | contentHeight = (int)Math.Max(Font.MeasureString("a").Y, Font.MeasureString(Text).Y);
45 | }
46 | }
47 | }
48 | public override int ContentWidth { get { CacheDims(); return contentWidth; } }
49 | public override int ContentHeight { get { CacheDims(); return contentHeight; } }
50 |
51 | public override void OnMeasure(int width, int height)
52 | {
53 | if (TextSource != null)
54 | Text = TextSource();
55 | CacheDims();
56 | MeasuredWidth = MeasureWidth(width, contentWidth);
57 | MeasuredHeight = MeasureHeight(height, contentHeight);
58 | }
59 |
60 | public override void Draw(float time, SpriteBatch spritebatch)
61 | {
62 | base.Draw(time, spritebatch);
63 | if (Text != null)
64 | spritebatch.DrawString(Font, Text, new Vector2(Rect.X, Rect.Y), FontColor, LayerDepth, Parent?.Clipped);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Framework/Screens/InputElements/TextInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.Xna.Framework;
7 | using Microsoft.Xna.Framework.Graphics;
8 | using Spectrum.Framework.Input;
9 | using Spectrum.Framework.VR;
10 |
11 | namespace Spectrum.Framework.Screens.InputElements
12 | {
13 | public class TextInput : InputElement
14 | {
15 | public event Action OnFocusChanged;
16 | private bool _focused = false;
17 | public bool Focused
18 | {
19 | get => _focused;
20 | set
21 | {
22 | if (value != _focused)
23 | {
24 | _focused = value;
25 | OnFocusChanged?.Invoke(value);
26 | }
27 | }
28 | }
29 | public int CursorPosition = 0;
30 | private string text = "";
31 | public string Text { get => text; set => text = value ?? ""; }
32 | private bool isVR = false;
33 | public TextInput()
34 | {
35 | Width = 250;
36 | OnClick += (_) =>
37 | {
38 | if (isVR)
39 | if (!SpecVR.IsKeyboardVisible)
40 | SpecVR.ShowKeyboard(text);
41 | Focused = true;
42 | };
43 | OnDisplayChanged += (display) => Focused &= display;
44 | }
45 | public override void Initialize()
46 | {
47 | isVR = Selector.IsVR.Matches(this);
48 | OnFocusChanged += (focus) =>
49 | {
50 | if (!focus && isVR)
51 | SpecVR.HideKeyboard();
52 | };
53 | }
54 | public override bool HandleInput(bool otherTookInput, InputState input)
55 | {
56 | if (!Display)
57 | return false;
58 | otherTookInput = base.HandleInput(otherTookInput, input);
59 | if (Focused)
60 | {
61 | if (input.IsNewMousePress(0) && !MouseInside(input))
62 | {
63 | Focused = false;
64 | return false;
65 | }
66 | if (isVR)
67 | text = SpecVR.GetKeyBoardText();
68 | else
69 | input.TakeKeyboardInput(ref CursorPosition, ref text);
70 | return true;
71 | }
72 | return otherTookInput;
73 | }
74 | public override void OnMeasure(int width, int height)
75 | {
76 | MeasuredWidth = MeasureWidth(width, (int)Font.MeasureString(text).X);
77 | MeasuredHeight = MeasureHeight(height, (int)Math.Max(Font.MeasureString("a").Y, Font.MeasureString(text).Y));
78 | }
79 | public override void Draw(float time, SpriteBatch spritebatch)
80 | {
81 | base.Draw(time, spritebatch);
82 | spritebatch.DrawString(Font, text, new Vector2(Rect.X, Rect.Y), FontColor, LayerDepth);
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Framework/Screens/LayoutManager.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework.Screens
9 | {
10 | public interface LayoutManager
11 | {
12 | void OnMeasure(Element element, int width, int height);
13 | void OnLayout(Element element, Rectangle bounds);
14 | int ContentWidth(Element element);
15 | int ContentHeight(Element element);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Framework/Screens/MenuScreen.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Microsoft.Xna.Framework;
6 | using Microsoft.Xna.Framework.Input;
7 | using Microsoft.Xna.Framework.Graphics;
8 | using Spectrum.Framework.Screens.InputElements;
9 | using Spectrum.Framework.Input;
10 |
11 | namespace Spectrum.Framework.Screens
12 | {
13 | public class MenuScreen : Element
14 | {
15 | private string MenuTitle;
16 | public MenuScreen(string menuTitle)
17 | {
18 | MenuTitle = menuTitle;
19 | }
20 | public override void Initialize()
21 | {
22 | base.Initialize();
23 | TextElement Title = new TextElement(MenuTitle);
24 | AddElement(Title);
25 | Title.Center();
26 | }
27 |
28 | public override bool HandleInput(bool otherTookInput, InputState input)
29 | {
30 | otherTookInput |= base.HandleInput(otherTookInput, input);
31 | if (!otherTookInput)
32 | {
33 | if (input.IsNewKeyPress("GoBack"))
34 | {
35 | otherTookInput = true;
36 | Parent.RemoveElement(this);
37 | }
38 | }
39 | return true;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Framework/Screens/RectOffset.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Spectrum.Framework.Screens
7 | {
8 | public struct RectOffset
9 | {
10 | public ElementSize Left;
11 | public ElementSize Right;
12 | public ElementSize Top;
13 | public ElementSize Bottom;
14 | public int WidthTotal(int parentWidth) => Left.Measure(parentWidth) + Right.Measure(parentWidth);
15 | public int HeightTotal(int parentHeight) => Top.Measure(parentHeight) + Bottom.Measure(parentHeight);
16 | public static implicit operator RectOffset(int size)
17 | {
18 | return new RectOffset()
19 | {
20 | Left = size,
21 | Right = size,
22 | Top = size,
23 | Bottom = size,
24 | };
25 | }
26 | public static implicit operator RectOffset(float size)
27 | {
28 | return new RectOffset()
29 | {
30 | Left = size,
31 | Right = size,
32 | Top = size,
33 | Bottom = size,
34 | };
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Framework/StateMachine.cs:
--------------------------------------------------------------------------------
1 | using Replicate;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | [ReplicateType]
11 | public class StateMachine
12 | {
13 | public delegate int Handler(int current, object e);
14 | public int State { get; private set; }
15 |
16 | private Dictionary Defaults = new Dictionary();
17 | private Dictionary<(int, Type), Handler> Handlers
18 | = new Dictionary<(int, Type), Handler>();
19 | private StateMachine() { }
20 | public StateMachine(int initial)
21 | {
22 | State = initial;
23 | }
24 | public void Add(Func handler)
25 | {
26 | Defaults[typeof(TEvent)] = (s, e) => handler(s, (TEvent)e);
27 | }
28 | public void Add(int state, Func handler)
29 | {
30 | Handlers[(state, typeof(TEvent))] = (s, e) => handler((TEvent)e);
31 | }
32 | public int ProcessEvent(TEvent e)
33 | {
34 | if (Handlers.TryGetValue((State, typeof(TEvent)), out var handler)
35 | || Defaults.TryGetValue(typeof(TEvent), out handler))
36 | return State = handler(State, e);
37 | return State;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Framework/Transform.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework
8 | {
9 | public interface ITransform
10 | {
11 | Vector3 Position { get; }
12 | Quaternion Orientation { get; }
13 | Vector3 Scale { get; }
14 | }
15 | public class Transform : ITransform
16 | {
17 | private ITransform source;
18 | public Transform() { }
19 | public Transform(Vector3 position)
20 | {
21 | Translation = position;
22 | }
23 | public Transform(Vector3 position, Quaternion rotation)
24 | {
25 | Translation = position;
26 | Rotation = rotation;
27 | }
28 | public Transform(ITransform source) { this.source = source; }
29 | public ITransform Parent;
30 | private Vector3 translation;
31 | public Vector3 Translation
32 | {
33 | get => source != null ? translation + source.Position : translation;
34 | set => translation = value;
35 | }
36 | public Vector3 Position
37 | {
38 | get
39 | {
40 | if (Parent != null)
41 | {
42 | return Parent.Apply(Translation);
43 | }
44 | return Translation;
45 | }
46 | }
47 | private Quaternion rotation = Quaternion.Identity;
48 | public Quaternion Rotation
49 | {
50 | get => source != null ? rotation.Concat(source.Orientation) : rotation;
51 | set => rotation = value;
52 | }
53 | public Vector3 Scale = Vector3.One;
54 | Vector3 ITransform.Scale => Scale;
55 | public Quaternion Orientation => Parent != null ? Rotation.Concat(Parent.Orientation) : Rotation;
56 | public static Transform Copy(ITransform transform)
57 | => new Transform(transform.Position, transform.Orientation);
58 | public Matrix LocalWorld => Matrix.CreateScale(Scale) * Rotation.ToMatrix() * Matrix.CreateTranslation(Translation);
59 |
60 | }
61 | public static class TransformExtension
62 | {
63 | public static Matrix World(this ITransform transform)
64 | => Matrix.CreateScale(transform.Scale) * transform.Orientation.ToMatrix() * Matrix.CreateTranslation(transform.Position);
65 | public static Vector3 Apply(this ITransform transform, Vector3 point)
66 | => transform.World() * point;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Framework/TypeHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Reflection;
6 | using System.IO;
7 | using Spectrum.Framework.Network;
8 | using Spectrum.Framework.Content;
9 | using Replicate.MetaData;
10 | using Replicate;
11 | using Spectrum.Framework.Entities;
12 |
13 | namespace Spectrum.Framework
14 | {
15 | public class TypeHelper
16 | {
17 | // TODO: Is using this model a good idea?
18 | public static ReplicationModel Model = TypeUtil.Model;
19 | private static DefaultDict plugins = new DefaultDict();
20 | static ReplicateTypeAttribute MakeReplicateAttribute() => new ReplicateTypeAttribute() { AutoMembers = AutoAdd.None };
21 | public static TypeData RegisterType(Type type, Plugin plugin)
22 | {
23 | // Laziness to avoid marking up every type with ReplicateType
24 | var typeData = Model.Add(type, type.GetCustomAttribute(false) ?? MakeReplicateAttribute());
25 | // Reinitialize if it was added by another call and has no ReplicateType
26 | if (typeData.TypeAttribute == null)
27 | {
28 | typeData.TypeAttribute = MakeReplicateAttribute();
29 | typeData.InitializeMembers();
30 | Model.ClearTypeAccessorCache();
31 | }
32 | plugins[type] = plugin;
33 | return typeData;
34 | }
35 | public static IEnumerable GetTypes(Type type)
36 | {
37 | return Model.Types.Values
38 | .Where(t => t.GenericTypeParameters == null && type.IsAssignableFrom(t.Type))
39 | .Select(t => Model.GetTypeAccessor(t.Type));
40 | }
41 | public static Plugin GetPlugin(Type type)
42 | {
43 | return plugins[type];
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Framework/Utility/ArgParseHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework.Utility
8 | {
9 | public class ArgParseResult
10 | {
11 | public List Positional = new List();
12 | public HashSet Flags = new HashSet();
13 | public Dictionary Arguments = new Dictionary();
14 | public T Get(string key = null, int? position = null, T missing = default(T))
15 | {
16 | if (key != null && Arguments.TryGetValue(key, out string value))
17 | return (T)Convert.ChangeType(value, typeof(T));
18 | if (position != null && Positional.Count > position.Value)
19 | return (T)Convert.ChangeType(Positional[position.Value], typeof(T));
20 | return missing;
21 | }
22 | public bool HasFlag(string key) => Flags.Contains(key);
23 | }
24 | public class ArgParseHelper
25 | {
26 | public static ArgParseResult Parse(string[] args)
27 | {
28 | var result = new ArgParseResult();
29 | for(int i = 0; i < args.Length; i++)
30 | {
31 | var arg = args[i];
32 | if (arg.Substring(0, 2) == "--")
33 | {
34 | if (i + 1 >= args.Length || args[i + 1].Substring(0, 1) == "-")
35 | result.Flags.Add(arg.Substring(2));
36 | else
37 | {
38 | result.Arguments.Add(arg.Substring(2), args[++i]);
39 | }
40 | }
41 | else if (arg.Substring(0, 1) == "-")
42 | result.Flags.Add(arg.Substring(1));
43 | else
44 | result.Positional.Add(arg);
45 | }
46 | return result;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Framework/Utility/ExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Spectrum.Framework.Content;
4 | using Spectrum.Framework.Graphics;
5 | using Spectrum.Framework.Screens;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Runtime.InteropServices;
10 | using System.Text;
11 |
12 | namespace Spectrum.Framework
13 | {
14 | public static class ExtensionMethods
15 | {
16 | public static IEnumerable Union(this IEnumerable source, T item)
17 | {
18 | return source.Union(Enumerable.Repeat(item, 1));
19 | }
20 | public static IEnumerable NotNull(this IEnumerable source) where T : struct
21 | {
22 | return source.Where(t => t.HasValue).Cast();
23 | }
24 | public static IEnumerable NotNull(this IEnumerable source) where T : class
25 | {
26 | return source.Where(t => t != null).Cast();
27 | }
28 | public static float DT(this GameTime time)
29 | {
30 | return (float)(time.ElapsedGameTime.TotalMilliseconds / 1000);
31 | }
32 |
33 | public static T Pop(this List list)
34 | {
35 | var ele = list[0];
36 | list.RemoveAt(0);
37 | return ele;
38 | }
39 | public static float NextFloat(this Random r, float start, float end)
40 | => (float)(r.NextDouble() * (end - start) + start);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Framework/Utility/MathHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework
8 | {
9 | public static class MathHelper
10 | {
11 | public static float Lerp(float a, float b, float w)
12 | {
13 | return a * (1 - w) + b * w;
14 | }
15 | public static double Lerp(double a, double b, double w)
16 | {
17 | return a * (1 - w) + b * w;
18 | }
19 | public static float Clamp(float v, float min, float max)
20 | {
21 | return Math.Min(Math.Max(v, min), max);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Framework/Utility/MatrixHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Newtonsoft.Json.Linq;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | // TODO: Probably remove this
11 | public static class MatrixHelper
12 | {
13 | public static float[] ToArray(this Matrix matrix)
14 | {
15 | return new float[]
16 | {
17 | matrix.M11, matrix.M12, matrix.M13, matrix.M14,
18 | matrix.M21, matrix.M22, matrix.M23, matrix.M24,
19 | matrix.M31, matrix.M32, matrix.M33, matrix.M34,
20 | matrix.M41, matrix.M42, matrix.M43, matrix.M44,
21 | };
22 | }
23 | public static Matrix FromArray(float[] array)
24 | {
25 | return new Matrix(
26 | array[0], array[1], array[2], array[3],
27 | array[4], array[5], array[6], array[7],
28 | array[8], array[9], array[10], array[11],
29 | array[12], array[13], array[14], array[15]
30 | );
31 | }
32 | public static Matrix YUpToZUp()
33 | {
34 | var output = Matrix.Identity;
35 | output[1, 1] = 0;
36 | output[1, 2] = 1;
37 | output[2, 2] = 0;
38 | output[2, 1] = -1;
39 | return output;
40 | }
41 | public static Vector3 ToVector3(this JToken jobj)
42 | {
43 | return new Vector3(
44 | (float)jobj[0],
45 | (float)jobj[1],
46 | (float)jobj[2]);
47 | }
48 | public static Matrix ToTranslationMatrix(this JToken jobj)
49 | {
50 | return Matrix.CreateTranslation(jobj.ToVector3());
51 | }
52 | public static Matrix ToRotationMatrix(this JToken jobj)
53 | {
54 | return jobj.ToQuaternion().ToMatrix();
55 | }
56 | public static Quaternion ToQuaternion(this JToken jobj)
57 | {
58 | return new Quaternion(
59 | (float)jobj[0],
60 | (float)jobj[1],
61 | (float)jobj[2],
62 | (float)jobj[3]);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Framework/Utility/Point3.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | public struct Point3 : IEquatable
11 | {
12 | public int X;
13 | public int Y;
14 | public int Z;
15 |
16 | public Point3(int x, int y, int z)
17 | {
18 | X = x; Y = y; Z = z;
19 | }
20 |
21 | public override bool Equals(object obj)
22 | {
23 | if (obj is Point3 point)
24 | return this.Equals(point);
25 | return false;
26 | }
27 |
28 | public override int GetHashCode()
29 | {
30 | var hashCode = -307843816;
31 | hashCode = hashCode * -1521134295 + X.GetHashCode();
32 | hashCode = hashCode * -1521134295 + Y.GetHashCode();
33 | hashCode = hashCode * -1521134295 + Z.GetHashCode();
34 | return hashCode;
35 | }
36 | public static Point3 Zero => new Point3();
37 | public static explicit operator Vector3(Point3 point) => new Vector3(point.X, point.Y, point.Z);
38 | public static explicit operator Point3(Vector3 vector) => new Point3() { X = (int)Math.Floor(vector.X), Y = (int)Math.Floor(vector.Y), Z = (int)Math.Floor(vector.Z) };
39 | public static Point3 operator +(Point3 a, Point3 b) => new Point3() { X = a.X + b.X, Y = a.Y + b.Y, Z = a.Z + b.Z };
40 | public static Point3 operator -(Point3 a, Point3 b) => new Point3() { X = a.X - b.X, Y = a.Y - b.Y, Z = a.Z - b.Z };
41 | public static Point3 operator *(Point3 a, int s) => new Point3() { X = a.X * s, Y = a.Y * s, Z = a.Z * s };
42 | public static bool operator ==(Point3 a, Point3 b) => a.Equals(b);
43 | public static bool operator !=(Point3 a, Point3 b) => !a.Equals(b);
44 | public static Point3 Round(Vector3 vector) => new Point3() { X = (int)Math.Round(vector.X), Y = (int)Math.Round(vector.Y), Z = (int)Math.Round(vector.Z) };
45 | public override string ToString()
46 | {
47 | return $"{X},{Y},{Z}";
48 | }
49 |
50 | public bool Equals(Point3 other)
51 | {
52 | return X == other.X &&
53 | Y == other.Y &&
54 | Z == other.Z;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Framework/Utility/RectangleExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework.Utility
9 | {
10 | using Rectangle = Microsoft.Xna.Framework.Rectangle;
11 | public static class RectangleExtensions
12 | {
13 | ///
14 | /// Scales the source rectangle (and optionally centers it) to the destination rectangle.
15 | /// The crop paremeter determines whether to scale up (overflowing and requiring a crop) or down (requiring no crop).
16 | ///
17 | ///
18 | ///
19 | /// If true will scale the source up, else down
20 | ///
21 | ///
22 | public static Rectangle FitTo(this Rectangle source, Rectangle destination, bool crop = true, bool center = true)
23 | {
24 | bool scaleX = (!crop) ^ (destination.Width * 1.0 / source.Width * source.Height > destination.Height);
25 | double scale = scaleX ? destination.Width * 1.0 / source.Width : destination.Height * 1.0 / source.Height;
26 | var fittedX = source.X;
27 | var fittedY = source.Y;
28 | var fittedWidth = (int)(source.Width * scale);
29 | var fittedHeight = (int)(source.Height * scale);
30 | if (center)
31 | {
32 | fittedX = (destination.Width - fittedWidth) / 2;
33 | fittedY = (destination.Height - fittedHeight) / 2;
34 | }
35 | return new Rectangle(fittedX, fittedY, fittedWidth, fittedHeight);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Framework/Utility/VectorExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | public static class VectorExtensions
11 | {
12 | public static string FixedLenString(this Vector3 vector)
13 | {
14 | return "<" + vector.X.ToString("0.00") + ", " + vector.Y.ToString("0.00") + ", " + vector.Z.ToString("0.00") + ">";
15 | }
16 | public static Vector3 Homogeneous(this Vector4 vector)
17 | {
18 | return new Vector3(vector.X, vector.Y, vector.Z) / vector.W;
19 | }
20 | public static bool IsInSameDirection(this Vector3 vector, Vector3 otherVector)
21 | {
22 | return vector.Dot(otherVector) > 0;
23 | }
24 | public static bool IsInOppositeDirection(this Vector3 vector, Vector3 otherVector)
25 | {
26 | return vector.Dot(otherVector) < 0;
27 | }
28 | public static Vector3 Project(this Vector3 source, Vector3 normal)
29 | {
30 | return source.Dot(normal) * normal;
31 | }
32 | public static Vector3 ProjectUnto(this Vector3 source, Vector3 planeNormal)
33 | {
34 | return source - source.Project(planeNormal);
35 | }
36 | public static float Roll(this Quaternion quaternion)
37 | {
38 | // yaw (z-axis rotation)
39 | double siny = +2.0 * (quaternion.W * quaternion.Z + quaternion.X * quaternion.Y);
40 | double cosy = +1.0 - 2.0 * (quaternion.X * quaternion.X + quaternion.Z * quaternion.Z);
41 | return (float)Math.Atan2(siny, cosy);
42 | }
43 | public static float Yaw(this Quaternion quaternion)
44 | {
45 | // roll (y-axis rotation)
46 | double sinr = +2.0 * (quaternion.W * quaternion.Y + quaternion.X * quaternion.Z);
47 | double cosr = +1.0 - 2.0 * (quaternion.X * quaternion.X + quaternion.Y * quaternion.Y);
48 | return (float)Math.Atan2(sinr, cosr);
49 | //// pitch (y-axis rotation)
50 | //double sinp = +2.0 * (quaternion.W * quaternion.Y - quaternion.Z * quaternion.X);
51 | //if (Math.Abs(sinp) >= 1)
52 | // return (float)(Math.Sign(sinp) * Math.PI / 2); // use 90 degrees if out of range
53 | //return (float)Math.Asin(sinp);
54 | }
55 | public static float Pitch(this Quaternion quaternion)
56 | {
57 | // pitch (x-axis rotation)
58 | double sinp = +2.0 * (quaternion.W * quaternion.X - quaternion.Z * quaternion.Y);
59 | if (Math.Abs(sinp) >= 1)
60 | return (float)(Math.Sign(sinp) * Math.PI / 2); // use 90 degrees if out of range
61 | return (float)Math.Asin(sinp);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Framework/Utility/WinUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.InteropServices;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework.Utility
9 | {
10 | public static class WinUtil
11 | {
12 | [DllImport("shcore.dll")]
13 | public static extern int SetProcessDpiAwareness(ProcessDPIAwareness value);
14 |
15 | public enum ProcessDPIAwareness
16 | {
17 | DPI_Unaware = 0,
18 | System_DPI_Aware = 1,
19 | Per_Monitor_DPI_Aware = 2
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Framework/Vector2.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Spectrum.Framework
9 | {
10 | public struct Vector2 : IEquatable
11 | {
12 | public static Vector2 Zero => new Vector2();
13 | public static Vector2 UnitX => new Vector2(1, 0);
14 | public static Vector2 UnitY => new Vector2(0, 1);
15 | public static Vector2 One => new Vector2(1, 1);
16 | public Vector2(float x, float y) { X = x; Y = y; }
17 | public Vector2(float d) { X = d; Y = d; }
18 | public float X { get; set; }
19 | public float Y { get; set; }
20 | public void Normalize()
21 | {
22 | var length = Length;
23 | X /= length; Y /= length;
24 | }
25 | public Vector2 Normal() => this / Length;
26 | public Vector2 Transform(Matrix matrix) => new Vector2((X * matrix.M11) + (Y * matrix.M21) + matrix.M41, (X * matrix.M12) + (Y * matrix.M22) + matrix.M42);
27 | public Vector2 Rotate(float r)
28 | {
29 | var sin = Math.Sin(r); var cos = Math.Cos(r);
30 | return new Vector2((float)(X * cos - Y * sin), (float)(X * sin + Y * cos));
31 | }
32 | [Obsolete]
33 | public static float Dot(Vector2 a, Vector2 b) => a.Dot(b);
34 | public float Dot(Vector2 b) => X * b.X + Y * b.Y;
35 | public override bool Equals(object obj) => obj is Vector2 vector && Equals(vector);
36 | public bool Equals(Vector2 other) => X == other.X && Y == other.Y;
37 | public override int GetHashCode()
38 | {
39 | var hashCode = 1861411795;
40 | hashCode = hashCode * -1521134295 + X.GetHashCode();
41 | hashCode = hashCode * -1521134295 + Y.GetHashCode();
42 | return hashCode;
43 | }
44 | public static Vector2 operator -(Vector2 a) => new Vector2(-a.X, -a.Y);
45 | public static Vector2 operator -(Vector2 a, Vector2 b) => new Vector2(a.X - b.X, a.Y - b.Y);
46 | public static Vector2 operator +(Vector2 a, Vector2 b) => new Vector2(a.X + b.X, a.Y + b.Y);
47 | public static Vector2 operator /(Vector2 a, int l) => new Vector2(a.X / l, a.Y / l);
48 | public static Vector2 operator /(Vector2 a, float l) => new Vector2(a.X / l, a.Y / l);
49 | public static Vector2 operator *(Vector2 a, int l) => new Vector2(a.X * l, a.Y * l);
50 | public static Vector2 operator *(Vector2 a, float l) => new Vector2(a.X * l, a.Y * l);
51 | public static bool operator ==(Vector2 left, Vector2 right) => left.Equals(right);
52 | public static bool operator !=(Vector2 left, Vector2 right) => !(left == right);
53 | public float Length => (float)Math.Pow(LengthSquared, 0.5);
54 | public float LengthSquared => (float)(Math.Pow(X, 2) + Math.Pow(Y, 2));
55 | public static implicit operator Microsoft.Xna.Framework.Vector2(Vector2 vector)
56 | {
57 | return new Microsoft.Xna.Framework.Vector2(vector.X, vector.Y);
58 | }
59 | public static implicit operator Vector2(Microsoft.Xna.Framework.Vector2 vector)
60 | {
61 | return new Vector2(vector.X, vector.Y);
62 | }
63 | public override string ToString()
64 | {
65 | return $"{X:0.00},{Y:0.00}";
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Framework/Vector4.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Spectrum.Framework
8 | {
9 | public struct Vector4
10 | {
11 | public static Vector4 Zero => new Vector4();
12 | public static Vector4 UnitX => new Vector4() { X = 1 };
13 | public static Vector4 UnitY => new Vector4() { Y = 1 };
14 | public static Vector4 UnitZ => new Vector4() { Z = 1 };
15 | public static Vector4 UnitW => new Vector4() { W = 1 };
16 | public static Vector4 One => new Vector4(1);
17 | public Vector4(float x, float y, float z, float w) { X = x; Y = y; Z = z; W = w; }
18 | public Vector4(float s) { X = s; Y = s; Z = s; W = s; }
19 | public float X { get; set; }
20 | public float Y { get; set; }
21 | public float Z { get; set; }
22 | public float W { get; set; }
23 | public static Vector4 operator -(Vector4 a) => new Vector4(-a.X, -a.Y, -a.Z, -a.W);
24 | public static Vector4 operator -(Vector4 a, Vector4 b) => new Vector4(a.X - b.X, a.Y - b.Y, a.Z - b.Z, a.W - b.W);
25 | public static Vector4 operator +(Vector4 a, Vector4 b) => new Vector4(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W);
26 | public static Vector4 operator /(Vector4 a, int d) => new Vector4(a.X / d, a.Y / d, a.Z / d, a.W / d);
27 | public static Vector4 operator /(Vector4 a, float d) => new Vector4(a.X / d, a.Y / d, a.Z / d, a.W / d);
28 | public static Vector4 operator *(Vector4 a, int d) => new Vector4(a.X * d, a.Y * d, a.Z * d, a.W * d);
29 | public static Vector4 operator *(Vector4 a, float d) => new Vector4(a.X * d, a.Y * d, a.Z * d, a.W * d);
30 | public static Vector4 operator *(int d, Vector4 a) => new Vector4(a.X * d, a.Y * d, a.Z * d, a.W * d);
31 | public static Vector4 operator *(float d, Vector4 a) => new Vector4(a.X * d, a.Y * d, a.Z * d, a.W * d);
32 | public static implicit operator Microsoft.Xna.Framework.Vector4(Vector4 vector) => new Microsoft.Xna.Framework.Vector4(vector.X, vector.Y, vector.Z, vector.W);
33 | public static implicit operator Vector4(Microsoft.Xna.Framework.Vector4 vector) => new Vector4(vector.X, vector.Y, vector.Z, vector.W);
34 | public Vector4 Normal() => new Vector4(X / Length, Y / Length, Z / Length, W / Length);
35 | public float Length => (float)Math.Pow(LengthSquared, 0.5);
36 | public float LengthSquared => (float)(Math.Pow(X, 2) + Math.Pow(Y, 2) + Math.Pow(Z, 2) + Math.Pow(W, 2));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Alex Sherman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Test/BenchmarkTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using Spectrum.Framework.Entities;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace SpectrumTest
11 | {
12 | public struct TimingResult
13 | {
14 | public double T;
15 | public double N;
16 | public override string ToString()
17 | {
18 |
19 | if (T >= 1)
20 | return $"{T} s";
21 | if (T >= 0.001)
22 | return $"{T * 1e3} ms";
23 | return $"{T * 1e6} us";
24 | }
25 | }
26 | [TestFixture]
27 | public class BenchmarkTests
28 | {
29 | public TimingResult Time(Action del, int n = 1_000, double t = 2)
30 | {
31 | Stopwatch s = new Stopwatch();
32 | s.Start();
33 | for (int i = 0; i < n; i++)
34 | {
35 | del();
36 | }
37 | s.Stop();
38 | return new TimingResult() { T = s.ElapsedTicks * 1.0 / Stopwatch.Frequency / n, N = n };
39 | }
40 | [Test]
41 | public void LargeUpdateBatch()
42 | {
43 | var manager = new EntityManager();
44 | for (int i = 0; i < 1e3; i++)
45 | {
46 | manager.AddEntity(new GameObject() { ID = Guid.NewGuid() });
47 | }
48 | var time = Time(() => manager.Update(0.16666f));
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Test/ElementTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using Spectrum.Framework;
3 | using Spectrum.Framework.Screens;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace SpectrumTest
11 | {
12 | [TestFixture]
13 | public class ElementTest
14 | {
15 | [Test]
16 | public void DefaultHorizontalLayout()
17 | {
18 | var root = new Element();
19 | var ele1 = root.AddElement(new Element() { Width = 10, Height = 20 });
20 | var ele2 = root.AddElement(new Element() { Width = 20, Height = 10 });
21 | root.ClearMeasure();
22 | root.Measure(100, 100);
23 | root.Layout(new Rectangle(0, 0, 100, 100));
24 | Assert.AreEqual(ele1.Rect.Left, 0);
25 | Assert.AreEqual(ele1.Rect.Right, 10);
26 | Assert.AreEqual(ele1.Rect.Top, 0);
27 | Assert.AreEqual(ele1.Rect.Bottom, 20);
28 | Assert.AreEqual(ele2.Rect.Left, 10);
29 | Assert.AreEqual(ele2.Rect.Right, 30);
30 | Assert.AreEqual(ele2.Rect.Top, 0);
31 | Assert.AreEqual(ele2.Rect.Bottom, 10);
32 | }
33 | [Test]
34 | public void CenterHorizontally()
35 | {
36 | var root = new Element { Width = 100, Height = 100 };
37 | var ele1 = root.AddElement(new Element() { Margin = new RectOffset() { Left = new ElementSize(-5, 0.5) }, Width = 10, Height = 20 });
38 | root.ClearMeasure();
39 | root.Measure(100, 100);
40 | root.Layout(new Rectangle(0, 0, 100, 100));
41 | Assert.AreEqual(ele1.Rect.Left, 45);
42 | Assert.AreEqual(ele1.Rect.Right, 55);
43 | Assert.AreEqual(ele1.Rect.Top, 0);
44 | Assert.AreEqual(ele1.Rect.Bottom, 20);
45 | }
46 | [Test]
47 | public void GridLayoutRelativeChildren()
48 | {
49 | var root = new GridLayout(4) { Width = 100, Height = 100 };
50 | var ele0 = root.AddElement(new Element() { Width = 1.0, Height = 1.0 });
51 | var ele1 = root.AddElement(new Element() { Width = 1.0, Height = 1.0 });
52 | var ele2 = root.AddElement(new Element() { Width = 1.0, Height = 1.0 });
53 | var ele3 = root.AddElement(new Element() { Width = 1.0, Height = 1.0 });
54 | var ele4 = root.AddElement(new Element() { Width = 1.0, Height = 1.0 });
55 | root.ClearMeasure();
56 | root.Measure(100, 100);
57 | root.Layout(new Rectangle(0, 0, 100, 100));
58 | Assert.AreEqual(ele0.Rect.Left, 0);
59 | Assert.AreEqual(ele0.Rect.Right, 25);
60 | Assert.AreEqual(ele0.Rect.Top, 0);
61 | Assert.AreEqual(ele0.Rect.Bottom, 25);
62 | Assert.AreEqual(ele1.Rect.Left, 25);
63 | Assert.AreEqual(ele1.Rect.Right, 50);
64 | Assert.AreEqual(ele1.Rect.Top, 0);
65 | Assert.AreEqual(ele1.Rect.Bottom, 25);
66 | Assert.AreEqual(ele4.Rect.Left, 0);
67 | Assert.AreEqual(ele4.Rect.Right, 25);
68 | Assert.AreEqual(ele4.Rect.Top, 25);
69 | Assert.AreEqual(ele4.Rect.Bottom, 50);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Test/InitDataTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using Spectrum.Framework;
3 | using Spectrum.Framework.Content;
4 | using Spectrum.Framework.Entities;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Reflection;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace SpectrumTest
13 | {
14 | [LoadableType]
15 | public class ClassWithArgument
16 | {
17 | public string Field;
18 | public int Property { get; set; }
19 | public float Derp { get; private set; }
20 | public ClassWithArgument(float derp)
21 | {
22 | Derp = derp;
23 | }
24 | }
25 | [TestFixture]
26 | public class InitDataTests
27 | {
28 | [SetUp]
29 | public void Initialize()
30 | {
31 | var plugin = Plugin.CreatePlugin("Main", null, LoadHelper.SpectrumAssembly);
32 | LoadHelper.RegisterTypes(plugin);
33 | }
34 | [Test]
35 | public void InitDataUpdateOnSet()
36 | {
37 | var idata = new InitData();
38 | Assert.AreEqual(idata, idata.Set("Position", null));
39 | }
40 | [Test]
41 | public void InitDataCopyOnSet()
42 | {
43 | InitData idata = new InitData().ToImmutable();
44 | Assert.AreNotEqual(idata, idata.Set("ID", null));
45 | }
46 | [Test]
47 | public void SetEntityDataValid()
48 | {
49 | Entity entity = new InitData().SetData("Test", "herp").Construct();
50 | Assert.IsNotNull(entity);
51 | Assert.AreEqual(entity.Data("Test"), "herp");
52 | }
53 | [Test]
54 | public void FunctionalInspection()
55 | {
56 | TypeHelper.RegisterType(typeof(ClassWithArgument), null);
57 | var obj = new InitData