Outlines & Custom non-entity drawing. (#592)

* Shader work WiP

* Probably implement basic shader param support.

* Really rough and hacky version

* Fix dumb exception.

* Custom fullscreen drawing API prototype.

* Significantly optimize debug colliders drawing.

* Fix drawdepths on clothing & overlays.

* Re-add license to outline shader.

* Update .editorconfig for .gdsl files.

* Fix unit tests.
This commit is contained in:
Pieter-Jan Briers
2018-06-03 14:38:56 +02:00
committed by GitHub
parent 1812e8743d
commit c058c2e3d4
34 changed files with 940 additions and 209 deletions

View File

@@ -9,3 +9,6 @@ charset = utf-8-bom
[*.{csproj,xml,yml,dll.config,targets}]
indent_size = 2
[*.gdsl]
indent_style = tab

View File

@@ -12,6 +12,7 @@
- type: BoundingBox
aabb: "-0.45,-1,0.95,1"
DebugColor: "#0000FF"
- type: Collidable
DebugColor: "#0000FF"

View File

@@ -0,0 +1,7 @@
- type: shader
id: selection_outline
kind: source
path: "/Shaders/outline.gdsl"
params:
outline_width: 1
outline_color: "#FF000055"

View File

@@ -0,0 +1,59 @@
shader_type canvas_item;
uniform float outline_width = 2.0;
uniform vec4 outline_color: hint_color;
void fragment() {
// I, for some reason, cannot put this comment in the top of the file.
// Taken from the godot-demo-projects repo.
// GODOT ENGINE
// http://www.godotengine.org
//
// ************************************************************************
//
// Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//************************************************************************
vec4 col = texture(TEXTURE, UV);
vec2 ps = TEXTURE_PIXEL_SIZE;
float a;
float maxa = col.a;
float mina = col.a;
a = texture(TEXTURE, UV + vec2(0, -outline_width)*ps).a;
maxa = max(a, maxa);
mina = min(a, mina);
a = texture(TEXTURE, UV + vec2(0, outline_width)*ps).a;
maxa = max(a, maxa);
mina = min(a, mina);
a = texture(TEXTURE, UV + vec2(-outline_width,0)*ps).a;
maxa = max(a, maxa);
mina = min(a, mina);
a = texture(TEXTURE, UV + vec2(outline_width, 0)*ps).a;
maxa = max(a, maxa);
mina = min(a, mina);
COLOR = mix(col, outline_color, maxa-mina);
}

View File

@@ -130,11 +130,11 @@ namespace SS14.Client.Console.Commands
}
}
class DebugCollidersCommand : IConsoleCommand
class ShowBoundingBoxesCommand : IConsoleCommand
{
public string Command => "debugcolliders";
public string Command => "showbb";
public string Help => "";
public string Description => "Enables debug drawing over all collidables in the game.";
public string Description => "Enables debug drawing over all bounding boxes in the game, showing their size.";
public bool Execute(IDebugConsole console, params string[] args)
{
@@ -201,6 +201,5 @@ namespace SS14.Client.Console.Commands
IoCManager.Resolve<IStateManager>().RequestStateChange<MainScreen>();
return false;
}
}
}

View File

@@ -1,16 +1,24 @@
using SS14.Client.GameObjects;
using SS14.Client.Graphics.ClientEye;
using SS14.Client.Graphics.Drawing;
using SS14.Client.Graphics.Overlays;
using SS14.Client.Graphics.Shaders;
using SS14.Client.Interfaces.Debugging;
using SS14.Client.Interfaces.Graphics.ClientEye;
using SS14.Client.Interfaces.Graphics.Overlays;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.Interfaces.GameObjects.Components;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Maths;
using SS14.Shared.Prototypes;
namespace SS14.Client.Debugging
{
public class DebugDrawing : IDebugDrawing
{
[Dependency]
readonly IComponentManager componentManager;
readonly IOverlayManager overlayManager;
private bool _debugColliders = false;
public bool DebugColliders
@@ -24,20 +32,69 @@ namespace SS14.Client.Debugging
}
_debugColliders = value;
UpdateDebugColliders();
if (value)
{
overlayManager.AddOverlay(new CollidableOverlay());
}
else
{
overlayManager.RemoveOverlay(nameof(CollidableOverlay));
}
}
}
private void UpdateDebugColliders()
private class CollidableOverlay : Overlay
{
int count = 0;
foreach (var component in componentManager.GetComponents<GodotCollidableComponent>())
[Dependency]
readonly IComponentManager componentManager;
[Dependency]
readonly IEyeManager eyeManager;
[Dependency]
readonly IPrototypeManager prototypeManager;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public CollidableOverlay() : base(nameof(CollidableOverlay))
{
count++;
component.DebugDraw = DebugColliders;
IoCManager.InjectDependencies(this);
Shader = prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
}
Logger.Debug($"Set debugdraw on {count} collidables");
protected override void Draw(DrawingHandle handle)
{
var viewport = eyeManager.GetWorldViewport();
foreach (var boundingBox in componentManager.GetComponents<BoundingBoxComponent>())
{
// all entities have a TransformComponent
var transform = boundingBox.Owner.GetComponent<ITransformComponent>();
// if not on the same map, continue
if (transform.MapID != eyeManager.CurrentMap)
continue;
var colorEdge = boundingBox.DebugColor.WithAlpha(0.33f);
var colorFill = boundingBox.DebugColor.WithAlpha(0.25f);
Box2 worldBox;
if (boundingBox.Owner.TryGetComponent<ICollidableComponent>(out var collision))
{
worldBox = collision.WorldAABB;
}
else
{
worldBox = boundingBox.WorldAABB;
}
// if not on screen, or too small, continue
if (!worldBox.Intersects(viewport) || worldBox.IsEmpty())
continue;
const int ppm = EyeManager.PIXELSPERMETER;
var screenBox = new Box2(worldBox.TopLeft * ppm, worldBox.BottomRight * ppm);
handle.DrawRect(screenBox, colorFill);
handle.DrawRect(screenBox, colorEdge, filled: false);
}
}
}
}
}

View File

@@ -6,6 +6,7 @@ using SS14.Client.Interfaces.GameStates;
using SS14.Client.Interfaces.Graphics;
using SS14.Client.Interfaces.Graphics.ClientEye;
using SS14.Client.Interfaces.Graphics.Lighting;
using SS14.Client.Interfaces.Graphics.Overlays;
using SS14.Client.Interfaces.Input;
using SS14.Client.Interfaces.Map;
using SS14.Client.Interfaces.Placement;
@@ -86,6 +87,8 @@ namespace SS14.Client
readonly IPlacementManager placementManager;
[Dependency]
readonly IClientGameStateManager gameStateManager;
[Dependency]
readonly IOverlayManager overlayManager;
public override void Main(Godot.SceneTree tree)
{
@@ -141,6 +144,7 @@ namespace SS14.Client
lightManager.Initialize();
_entityManager.Initialize();
gameStateManager.Initialize();
overlayManager.Initialize();
_client.Initialize();
@@ -203,6 +207,7 @@ namespace SS14.Client
AssemblyLoader.BroadcastUpdate(AssemblyLoader.UpdateLevel.FramePreEngine, eventArgs.Elapsed);
lightManager.FrameUpdate(eventArgs);
_stateManager.FrameUpdate(eventArgs);
overlayManager.FrameUpdate(eventArgs);
AssemblyLoader.BroadcastUpdate(AssemblyLoader.UpdateLevel.FramePostEngine, eventArgs.Elapsed);
}

View File

@@ -53,6 +53,8 @@ using SS14.Client.Interfaces.Graphics.ClientEye;
using SS14.Client.Graphics.ClientEye;
using SS14.Client.Interfaces.Placement;
using SS14.Client.Placement;
using SS14.Client.Interfaces.Graphics.Overlays;
using SS14.Client.Graphics.Overlays;
namespace SS14.Client
{
@@ -121,6 +123,7 @@ namespace SS14.Client
// Only GameController can acess this because the type is private so it's fine.
IoCManager.Register<GameController.GameTiming, GameController.GameTiming>();
IoCManager.Register<IPlacementManager, PlacementManager>();
IoCManager.Register<IOverlayManager, OverlayManager>();
IoCManager.BuildGraph();
}

View File

@@ -1,5 +1,6 @@
using System;
using SS14.Shared.GameObjects;
using SS14.Shared.GameObjects.Serialization;
using SS14.Shared.Interfaces.GameObjects.Components;
using SS14.Shared.Maths;
@@ -17,6 +18,14 @@ namespace SS14.Client.GameObjects
/// <inheritdoc />
public override uint? NetID => NetIDs.BOUNDING_BOX;
private Color _debugColor;
public Color DebugColor
{
get => _debugColor;
private set => _debugColor = value;
}
/// <summary>
/// Local Axis Aligned Bounding Box of the entity.
/// </summary>
@@ -48,5 +57,12 @@ namespace SS14.Client.GameObjects
{
AABB = ((BoundingBoxComponentState)state).AABB;
}
public override void ExposeData(EntitySerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _debugColor, "DebugColor", Color.Red);
}
}
}

View File

@@ -24,14 +24,6 @@ namespace SS14.Client.GameObjects
// no client side collision support for now
private bool collisionEnabled;
private Color _debugColor;
public Color DebugColor
{
get { return _debugColor; }
private set { _debugColor = value; }
}
/// <inheritdoc />
public override string Name => "Collidable";
@@ -50,13 +42,6 @@ namespace SS14.Client.GameObjects
/// <inheritdoc />
public MapId MapID => Owner.GetComponent<ITransformComponent>().MapID;
protected bool _debugDraw = false;
public virtual bool DebugDraw
{
get => _debugDraw;
set => _debugDraw = value;
}
/// <inheritdoc />
void ICollidable.Bumped(IEntity bumpedby)
{
@@ -92,11 +77,6 @@ namespace SS14.Client.GameObjects
var cm = IoCManager.Resolve<ICollisionManager>();
cm.AddCollidable(this);
}
if (IoCManager.Resolve<IDebugDrawing>().DebugColliders)
{
DebugDraw = true;
}
}
/// <summary>
@@ -179,22 +159,5 @@ namespace SS14.Client.GameObjects
var cm = IoCManager.Resolve<ICollisionManager>();
cm.RemoveCollidable(this);
}
public override void ExposeData(EntitySerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _debugColor, "DebugColor", Color.Red);
}
public override void LoadParameters(YamlMappingNode mapping)
{
base.LoadParameters(mapping);
if (mapping.TryGetNode("DebugColor", out var node))
{
DebugColor = node.AsHexColor();
}
}
}
}

View File

@@ -1,74 +0,0 @@
using SS14.Client.Graphics.ClientEye;
using SS14.Client.Interfaces.GameObjects.Components;
using SS14.Client.Utility;
namespace SS14.Client.GameObjects
{
/// <summary>
/// Supports the debug drawing of colliders.
/// </summary>
public class GodotCollidableComponent : CollidableComponent
{
public override bool DebugDraw
{
set
{
if (value == _debugDraw)
{
return;
}
_debugDraw = value;
debugNode.Update();
}
}
private Godot.Node2D debugNode;
private IGodotTransformComponent transform;
private GodotGlue.GodotSignalSubscriber0 debugDrawSubscriber;
public override void Initialize()
{
transform = Owner.GetComponent<IGodotTransformComponent>();
debugNode = new Godot.Node2D();
debugNode.SetName("Collidable debug");
debugDrawSubscriber = new GodotGlue.GodotSignalSubscriber0();
debugDrawSubscriber.Connect(debugNode, "draw");
debugDrawSubscriber.Signal += DrawDebugRect;
transform.SceneNode.AddChild(debugNode);
base.Initialize();
}
public override void OnRemove()
{
base.OnRemove();
debugDrawSubscriber.Disconnect(debugNode, "draw");
debugDrawSubscriber.Dispose();
debugDrawSubscriber = null;
debugNode.QueueFree();
debugNode.Dispose();
debugNode = null;
}
private void DrawDebugRect()
{
if (!DebugDraw)
{
return;
}
var colorEdge = DebugColor.WithAlpha(0.50f).Convert();
var colorFill = DebugColor.WithAlpha(0.25f).Convert();
var aabb = Owner.GetComponent<BoundingBoxComponent>().AABB;
const int ppm = EyeManager.PIXELSPERMETER;
var rect = new Godot.Rect2(aabb.Left * ppm, aabb.Top * ppm, aabb.Width * ppm, aabb.Height * ppm);
debugNode.DrawRect(rect, colorEdge, filled: false);
rect.Position += new Godot.Vector2(1, 1);
rect.Size -= new Godot.Vector2(2, 2);
debugNode.DrawRect(rect, colorFill, filled: true);
}
}
}

View File

@@ -1,4 +1,5 @@
using SS14.Client.Interfaces.GameObjects;
using SS14.Client.Graphics.Shaders;
using SS14.Client.Interfaces.GameObjects;
using SS14.Client.Interfaces.GameObjects.Components;
using SS14.Shared.GameObjects;
using SS14.Shared.Input;
@@ -27,5 +28,17 @@ namespace SS14.Client.GameObjects
SendMessage(message);
SendNetworkMessage(message);
}
public void OnMouseEnter()
{
var sprite = Owner.GetComponent<ISpriteComponent>();
sprite.LayerSetShader(0, "selection_outline");
}
public void OnMouseLeave()
{
var sprite = Owner.GetComponent<ISpriteComponent>();
sprite.LayerSetShader(0, (Shader)null);
}
}
}

View File

@@ -1,12 +1,5 @@
using SS14.Client.Interfaces.GameObjects;
using SS14.Client.Interfaces.GameObjects.Components;
using SS14.Shared.GameObjects;
using SS14.Client.Interfaces.GameObjects.Components;
using SS14.Shared.Interfaces.GameObjects.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SS14.Client.GameObjects
{
@@ -17,9 +10,6 @@ namespace SS14.Client.GameObjects
Register<GodotTransformComponent>(overwrite: true);
RegisterReference<GodotTransformComponent, ITransformComponent>();
RegisterReference<GodotTransformComponent, IGodotTransformComponent>();
Register<GodotCollidableComponent>(overwrite: true);
RegisterReference<GodotCollidableComponent, ICollidableComponent>();
}
}
}

View File

@@ -48,6 +48,23 @@ namespace SS14.Client.Graphics.ClientEye
public MapId CurrentMap => currentEye.MapId;
public Box2 GetWorldViewport()
{
var vpSize = sceneTree.SceneTree.Root.Size.Convert();
var topLeft = ScreenToWorld(Vector2.Zero);
var topRight = ScreenToWorld(new Vector2(vpSize.X, 0));
var bottomRight = ScreenToWorld(vpSize);
var bottomLeft = ScreenToWorld(new Vector2(0, vpSize.Y));
var left = MathHelper.Min(topLeft.X, topRight.X, bottomRight.X, bottomLeft.X);
var top = MathHelper.Min(topLeft.Y, topRight.Y, bottomRight.Y, bottomLeft.Y);
var right = MathHelper.Max(topLeft.X, topRight.X, bottomRight.X, bottomLeft.X);
var bottom = MathHelper.Max(topLeft.Y, topRight.Y, bottomRight.Y, bottomLeft.Y);
return new Box2(left, top, right, bottom);
}
public void Initialize()
{
defaultEye = new FixedEye();

View File

@@ -0,0 +1,195 @@
using SS14.Client.Graphics.Drawing;
using SS14.Client.Graphics.Shaders;
using SS14.Client.Interfaces.Graphics.Overlays;
using SS14.Shared.IoC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VS = Godot.VisualServer;
namespace SS14.Client.Graphics.Overlays
{
public abstract class Overlay : IOverlay
{
protected IOverlayManager OverlayManager { get; }
public string ID { get; }
public virtual bool AlwaysDirty => false;
public bool IsDirty => AlwaysDirty || _isDirty;
public bool Drawing { get; private set; } = false;
public virtual OverlaySpace Space => OverlaySpace.ScreenSpace;
private Shader _shader;
public Shader Shader
{
get => _shader;
set
{
_shader = value;
if (MainCanvasItem != null)
{
VS.CanvasItemSetMaterial(MainCanvasItem, value?.GodotMaterial?.GetRid());
}
}
}
private int? _zIndex;
public int? ZIndex
{
get => _zIndex;
set
{
if (_zIndex > VS.CanvasItemZMax || _zIndex > VS.CanvasItemZMax)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
_zIndex = value;
UpdateZIndex();
}
}
public virtual bool SubHandlesUseMainShader { get; } = true;
private bool _isDirty = true;
private Godot.RID MainCanvasItem;
private readonly List<Godot.RID> CanvasItems = new List<Godot.RID>();
private readonly List<DrawingHandle> TempHandles = new List<DrawingHandle>();
private bool Disposed = false;
protected Overlay(string id)
{
OverlayManager = IoCManager.Resolve<IOverlayManager>();
ID = id;
}
public void AssignCanvasItem(Godot.RID canvasItem)
{
MainCanvasItem = canvasItem;
if (Shader != null)
{
Shader.ApplyToCanvasItem(MainCanvasItem);
}
UpdateZIndex();
}
public void Dispose()
{
if (Disposed)
{
return;
}
Dispose(true);
Disposed = true;
GC.SuppressFinalize(this);
}
~Overlay()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
ClearDraw();
}
protected abstract void Draw(DrawingHandle handle);
protected DrawingHandle NewHandle(Shader shader = null)
{
if (!Drawing)
{
throw new InvalidOperationException("Can only allocate new handles while drawing.");
}
var item = VS.CanvasItemCreate();
VS.CanvasItemSetParent(item, MainCanvasItem);
CanvasItems.Add(item);
if (shader != null)
{
shader.ApplyToCanvasItem(item);
}
else
{
VS.CanvasItemSetUseParentMaterial(item, SubHandlesUseMainShader);
}
var handle = new DrawingHandle(item);
TempHandles.Add(handle);
return handle;
}
public void Dirty()
{
_isDirty = true;
}
public void FrameUpdate(RenderFrameEventArgs args)
{
if (!IsDirty)
{
return;
}
ClearDraw();
try
{
Drawing = true;
Draw(new DrawingHandle(MainCanvasItem));
}
finally
{
Drawing = false;
foreach (var handle in TempHandles)
{
handle.Dispose();
}
TempHandles.Clear();
}
}
public void ClearCanvasItem()
{
ClearDraw();
MainCanvasItem = null;
}
private void ClearDraw()
{
foreach (var item in CanvasItems)
{
VS.FreeRid(item);
}
VS.CanvasItemClear(MainCanvasItem);
CanvasItems.Clear();
}
private void UpdateZIndex()
{
if (MainCanvasItem == null)
{
return;
}
if (Space != OverlaySpace.WorldSpace || ZIndex == null)
{
VS.CanvasItemSetZIndex(MainCanvasItem, 0);
VS.CanvasItemSetZAsRelativeToParent(MainCanvasItem, true);
}
else
{
VS.CanvasItemSetZIndex(MainCanvasItem, ZIndex.Value);
VS.CanvasItemSetZAsRelativeToParent(MainCanvasItem, false);
}
}
}
}

View File

@@ -0,0 +1,117 @@
using SS14.Client.Interfaces;
using SS14.Client.Interfaces.Graphics.Overlays;
using SS14.Client.Interfaces.UserInterface;
using SS14.Shared.GameObjects;
using SS14.Shared.IoC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VS = Godot.VisualServer;
namespace SS14.Client.Graphics.Overlays
{
internal class OverlayManager : IOverlayManager
{
private Godot.Node2D RootNodeWorld;
private Godot.Node2D RootNodeScreen;
[Dependency]
readonly ISceneTreeHolder sceneTreeHolder;
private readonly Dictionary<string, (IOverlay overlay, Godot.RID canvasItem)> overlays = new Dictionary<string, (IOverlay, Godot.RID)>();
public void Initialize()
{
RootNodeWorld = new Godot.Node2D { Name = "OverlayRoot" };
sceneTreeHolder.WorldRoot.AddChild(RootNodeWorld);
RootNodeWorld.ZIndex = (int)DrawDepth.Overlays;
RootNodeScreen = new Godot.Node2D { Name = "OverlayRoot" };
sceneTreeHolder.SceneTree.Root.GetNode("UILayer").AddChild(RootNodeScreen);
}
public void FrameUpdate(RenderFrameEventArgs args)
{
foreach (var (overlay, _) in overlays.Values)
{
overlay.FrameUpdate(args);
}
}
public void AddOverlay(IOverlay overlay)
{
if (overlays.ContainsKey(overlay.ID))
{
throw new InvalidOperationException($"We already have an overlay with ID '{overlay.ID}'");
}
Godot.RID parent;
switch (overlay.Space)
{
case OverlaySpace.ScreenSpace:
parent = RootNodeScreen.GetCanvasItem();
break;
case OverlaySpace.WorldSpace:
parent = RootNodeWorld.GetCanvasItem();
break;
default:
throw new NotImplementedException($"Unknown overlay space: {overlay.Space}");
}
var item = VS.CanvasItemCreate();
VS.CanvasItemSetParent(item, parent);
overlays.Add(overlay.ID, (overlay, item));
overlay.AssignCanvasItem(item);
}
public IOverlay GetOverlay(string id)
{
return overlays[id].overlay;
}
public T GetOverlay<T>(string id) where T : IOverlay
{
return (T)GetOverlay(id);
}
public bool HasOverlay(string id)
{
return overlays.ContainsKey(id);
}
public void RemoveOverlay(string id)
{
if (!overlays.TryGetValue(id, out var value))
{
return;
}
var (overlay, item) = value;
overlay.ClearCanvasItem();
VS.FreeRid(item);
overlays.Remove(id);
}
public bool TryGetOverlay(string id, out IOverlay overlay)
{
if (overlays.TryGetValue(id, out var value))
{
overlay = value.overlay;
return true;
}
overlay = null;
return false;
}
public bool TryGetOverlay<T>(string id, out T overlay) where T : IOverlay
{
if (overlays.TryGetValue(id, out var value))
{
overlay = (T)value.overlay;
return true;
}
overlay = default(T);
return false;
}
}
}

View File

@@ -0,0 +1,33 @@
namespace SS14.Client.Graphics.Shaders
{
public enum ShaderParamType
{
// Can this even happen?
Void = 0,
Bool,
BVec2,
BVec3,
BVec4,
UInt,
/// <summary>
/// While Godot supports all (u)int vectors,
/// It doesn't specify which is used from get params.
/// So ivec2, ivec3, ivec4... are all this guy.
/// </summary>
IntVec,
Int,
Float,
Vec2,
Vec3,
Vec4,
Mat2,
Mat3,
Mat4,
/// <summary>
/// Godot supports u, i and b samplers too,
/// but we can't tell in code.
/// </summary>
Sampler2D,
SamplerCube
}
}

View File

@@ -1,4 +1,4 @@
namespace SS14.Client.Graphics.Shaders
namespace SS14.Client.Graphics.Shaders
{
public sealed class Shader
{
@@ -8,5 +8,10 @@ namespace SS14.Client.Graphics.Shaders
{
GodotMaterial = godotMaterial;
}
internal void ApplyToCanvasItem(Godot.RID item)
{
Godot.VisualServer.CanvasItemSetMaterial(item, GodotMaterial.GetRid());
}
}
}

View File

@@ -1,12 +1,16 @@
using System;
using SS14.Client.Interfaces.ResourceManagement;
using SS14.Client.Interfaces.ResourceManagement;
using SS14.Client.ResourceManagement;
using SS14.Client.ResourceManagement.ResourceTypes;
using SS14.Client.Utility;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Prototypes;
using SS14.Shared.Utility;
using System;
using System.Collections.Generic;
using YamlDotNet.RepresentationModel;
using LightModeEnum = Godot.CanvasItemMaterial.LightModeEnum;
using BlendModeEnum = Godot.CanvasItemMaterial.BlendModeEnum;
using LightModeEnum = Godot.CanvasItemMaterial.LightModeEnum;
namespace SS14.Client.Graphics.Shaders
{
@@ -17,8 +21,11 @@ namespace SS14.Client.Graphics.Shaders
private ShaderKind Kind;
// Source shader variables.
private ShaderSourceResource Source;
private Dictionary<string, object> ShaderParams;
// Canvas shader variables.
private LightModeEnum LightMode;
private BlendModeEnum BlendMode;
@@ -32,10 +39,18 @@ namespace SS14.Client.Graphics.Shaders
switch (Kind)
{
case ShaderKind.Source:
mat = new Godot.ShaderMaterial
var shaderMat = new Godot.ShaderMaterial
{
Shader = Source.GodotShader
};
mat = shaderMat;
if (ShaderParams != null)
{
foreach (var pair in ShaderParams)
{
shaderMat.SetShaderParam(pair.Key, pair.Value);
}
}
break;
case ShaderKind.Canvas:
mat = new Godot.CanvasItemMaterial
@@ -60,11 +75,7 @@ namespace SS14.Client.Graphics.Shaders
{
case "source":
Kind = ShaderKind.Source;
var path = mapping.GetNode("path").AsResourcePath();
var resc = IoCManager.Resolve<IResourceCache>();
Source = resc.GetResource<ShaderSourceResource>(path);
// TODO: Handle shader parameters!
ReadSourceKind(mapping);
break;
case "canvas":
@@ -74,7 +85,29 @@ namespace SS14.Client.Graphics.Shaders
default:
throw new InvalidOperationException($"Invalid shader kind: '{kind}'");
}
}
private void ReadSourceKind(YamlMappingNode mapping)
{
var path = mapping.GetNode("path").AsResourcePath();
var resc = IoCManager.Resolve<IResourceCache>();
Source = resc.GetResource<ShaderSourceResource>(path);
if (mapping.TryGetNode<YamlMappingNode>("params", out var paramMapping))
{
ShaderParams = new Dictionary<string, object>();
foreach (var item in paramMapping)
{
var name = item.Key.AsString();
if (!Source.TryGetShaderParamType(name, out var type))
{
Logger.ErrorS("shader", "Shader param '{0}' does not exist on shader '{1}'", name, path);
continue;
}
var value = ParseShaderParamFor(item.Value, type);
ShaderParams.Add(name, value);
}
}
}
@@ -131,6 +164,44 @@ namespace SS14.Client.Graphics.Shaders
}
}
private object ParseShaderParamFor(YamlNode node, ShaderParamType type)
{
switch (type)
{
case ShaderParamType.Void:
throw new NotSupportedException();
case ShaderParamType.Bool:
return node.AsBool();
case ShaderParamType.Int:
case ShaderParamType.UInt:
return node.AsInt();
case ShaderParamType.Float:
return node.AsFloat();
case ShaderParamType.Vec2:
return node.AsVector2().Convert();
case ShaderParamType.Vec3:
return node.AsVector3().Convert();
case ShaderParamType.Vec4:
try
{
return node.AsColor().Convert();
}
catch
{
var vec4 = node.AsVector4();
return new Godot.Quat(vec4.X, vec4.Y, vec4.Z, vec4.W);
}
case ShaderParamType.Sampler2D:
var path = node.AsResourcePath();
var resc = IoCManager.Resolve<IResourceCache>();
return resc.GetResource<TextureResource>(path).Texture.GodotTexture;
// If something's not handled here, then that's probably because I was lazy.
default:
throw new NotImplementedException();
}
}
enum ShaderKind
{
Source,

View File

@@ -27,5 +27,15 @@ namespace SS14.Client.Interfaces.GameObjects.Components
/// <param name="userUID">The entity owned by the player that clicked.</param>
/// <param name="clickType">See <see cref="MouseClickType" />.</param>
void DispatchClick(IEntity user, ClickType clickType);
/// <summary>
/// Invoked whenever the mouse hovers over this entity.
/// </summary>
void OnMouseEnter();
/// <summary>
/// Invoked whenever the mouse stops hovering over this entity.
/// </summary>
void OnMouseLeave();
}
}

View File

@@ -10,6 +10,12 @@ namespace SS14.Client.Interfaces.Graphics.ClientEye
/// The ID of the map on which the current eye is "placed".
/// </summary>
MapId CurrentMap { get; }
/// <summary>
/// A world-space box that is at LEAST the area covered by the viewport.
/// May be larger due to say rotation.
/// </summary>
Box2 GetWorldViewport();
void Initialize();
Vector2 WorldToScreen(Vector2 point);

View File

@@ -0,0 +1,32 @@
using System;
namespace SS14.Client.Interfaces.Graphics.Overlays
{
public interface IOverlay : IDisposable
{
string ID { get; }
OverlaySpace Space { get; }
void FrameUpdate(RenderFrameEventArgs args);
void AssignCanvasItem(Godot.RID canvasItem);
void ClearCanvasItem();
}
/// <summary>
/// Determines in which canvas layer an overlay gets drawn.
/// </summary>
public enum OverlaySpace
{
/// <summary>
/// This overlay will be drawn in the UI root, thus being in screen space.
/// </summary>
ScreenSpace = 0,
/// <summary>
/// This overlay will be drawn in the world root, thus being in world space.
/// </summary>
WorldSpace = 1,
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SS14.Client.Interfaces.Graphics.Overlays
{
public interface IOverlayManager
{
void Initialize();
void FrameUpdate(RenderFrameEventArgs args);
void AddOverlay(IOverlay overlay);
void RemoveOverlay(string id);
bool HasOverlay(string id);
IOverlay GetOverlay(string id);
T GetOverlay<T>(string id) where T : IOverlay;
bool TryGetOverlay(string id, out IOverlay overlay);
bool TryGetOverlay<T>(string id, out T overlay) where T : IOverlay;
}
}

View File

@@ -94,7 +94,7 @@ namespace SS14.Client.Player
ControlledEntity.AddComponent<PlayerInputMoverComponent>();
if (!ControlledEntity.HasComponent<ICollidableComponent>())
ControlledEntity.AddComponent<GodotCollidableComponent>();
ControlledEntity.AddComponent<CollidableComponent>();
if (!ControlledEntity.TryGetComponent<EyeComponent>(out var eye))
{

View File

@@ -34,3 +34,5 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: InternalsVisibleTo("SS14.UnitTesting")]

View File

@@ -1,5 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using SS14.Client.Graphics.Shaders;
using SS14.Client.Interfaces.ResourceManagement;
using SS14.Shared.Utility;
@@ -12,6 +16,8 @@ namespace SS14.Client.ResourceManagement.ResourceTypes
{
internal Godot.Shader GodotShader { get; private set; }
private readonly Dictionary<string, ShaderParamType> Parameters = new Dictionary<string, ShaderParamType>();
public override void Load(IResourceCache cache, ResourcePath path)
{
using (var stream = cache.ContentFileRead(path))
@@ -23,6 +29,88 @@ namespace SS14.Client.ResourceManagement.ResourceTypes
Code = code,
};
}
var properties = Godot.VisualServer.ShaderGetParamList(GodotShader.GetRid());
foreach (var dict in properties.Cast<Dictionary<object, object>>())
{
Parameters.Add((string)dict["name"], DetectParamType(dict));
}
}
internal bool TryGetShaderParamType(string paramName, out ShaderParamType shaderParamType)
{
return Parameters.TryGetValue(paramName, out shaderParamType);
}
private ShaderParamType DetectParamType(Dictionary<object, object> dict)
{
var type = (Godot.Variant.Type)dict["type"];
var hint = (Godot.PropertyHint)dict["hint"];
var hint_string = (string)dict["hint_string"];
switch (type)
{
// See RasterizerStorageGLES3::shader_get_param_list for how I figured this out.
case Godot.Variant.Type.Nil:
return ShaderParamType.Void;
case Godot.Variant.Type.Bool:
return ShaderParamType.Bool;
case Godot.Variant.Type.Int:
if (hint == Godot.PropertyHint.Flags)
{
// The bvecs are a flags variable,
// Hint string is in the form x,y,z,w,
// So these length matches check WHICH it is.
if (hint_string.Length == 3)
{
return ShaderParamType.BVec2;
}
else if (hint_string.Length == 5)
{
return ShaderParamType.BVec3;
}
else if (hint_string.Length == 7)
{
return ShaderParamType.BVec4;
}
else
{
throw new NotImplementedException("Hint string is not the right length?");
}
}
else
{
return ShaderParamType.Int;
}
case Godot.Variant.Type.Real:
return ShaderParamType.Float;
case Godot.Variant.Type.Vector2:
return ShaderParamType.Vec2;
case Godot.Variant.Type.Vector3:
return ShaderParamType.Vec3;
case Godot.Variant.Type.Transform:
case Godot.Variant.Type.Basis:
case Godot.Variant.Type.Transform2d:
throw new NotImplementedException("Matrices are not implemented yet.");
case Godot.Variant.Type.Plane:
case Godot.Variant.Type.Color:
return ShaderParamType.Vec4;
case Godot.Variant.Type.Object:
if (hint_string == "Texture")
{
return ShaderParamType.Sampler2D;
}
else
{
throw new NotImplementedException();
}
case Godot.Variant.Type.IntArray:
// Godot merges all the integral vectors into this.
// Can't be more specific sadly.
return ShaderParamType.IntVec;
default:
throw new NotSupportedException($"Unknown variant type: {type}");
}
}
}
}

View File

@@ -108,7 +108,6 @@
<Compile Include="GameObjects\ClientEntityManager.cs" />
<Compile Include="GameObjects\ClientEntityNetworkManager.cs" />
<Compile Include="GameObjects\Components\BoundingBox\BoundingBoxComponent.cs" />
<Compile Include="GameObjects\Components\Collidable\GodotCollidableComponent.cs" />
<Compile Include="GameObjects\Components\Collidable\CollidableComponent.cs" />
<Compile Include="GameObjects\Components\Icon\IconComponent.cs" />
<Compile Include="GameObjects\Components\Input\ClickableComponent.cs" />
@@ -132,6 +131,8 @@
<Compile Include="Graphics\Lighting\LightManager.Occluder.cs" />
<Compile Include="Graphics\Lighting\LightManager.cs" />
<Compile Include="Graphics\Lighting\LightMode\LightModeConstant.cs" />
<Compile Include="Graphics\Overlays\Overlay.cs" />
<Compile Include="Graphics\Overlays\OverlayManager.cs" />
<Compile Include="Graphics\Shaders\ShaderPrototype.cs" />
<Compile Include="Graphics\Shaders\Shader.cs" />
<Compile Include="Interfaces\Console\IConsoleCommand.cs" />
@@ -149,6 +150,8 @@
<Compile Include="Interfaces\Graphics\Lighting\ILightManager.cs" />
<Compile Include="Interfaces\Graphics\Lighting\ILightMode.cs" />
<Compile Include="Interfaces\Graphics\Lighting\IOccluder.cs" />
<Compile Include="Interfaces\Graphics\Overlays\IOverlay.cs" />
<Compile Include="Interfaces\Graphics\Overlays\IOverlayManager.cs" />
<Compile Include="Interfaces\IBaseClient.cs" />
<Compile Include="Interfaces\ISceneTreeHolder.cs" />
<Compile Include="Interfaces\Map\IClientTileDefinitionManager.cs" />
@@ -281,6 +284,7 @@
<EmbeddedResource Include="Graphics\RSI\RSISchema.json" />
<None Include="packages.config" />
<Compile Include="GameObjects\PlayerMessages.cs" />
<Compile Include="Graphics\Shaders\Params.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\MSBuild\SS14.Engine.targets" />

View File

@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SS14.Client.Console;
using SS14.Client.GameObjects.EntitySystems;
using SS14.Client.Console;
using SS14.Client.Graphics.Shaders;
using SS14.Client.Input;
using SS14.Client.Interfaces.GameObjects;
using SS14.Client.Interfaces.GameObjects.Components;
@@ -11,22 +8,16 @@ using SS14.Client.Interfaces.Input;
using SS14.Client.Interfaces.Placement;
using SS14.Client.Interfaces.Player;
using SS14.Client.Interfaces.UserInterface;
using SS14.Client.UserInterface;
using SS14.Client.UserInterface.Controls;
using SS14.Client.UserInterface.CustomControls;
using SS14.Shared;
using SS14.Shared.Configuration;
using SS14.Shared.GameObjects;
using SS14.Shared.Input;
using SS14.Shared.Interfaces.Configuration;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.Interfaces.GameObjects.Components;
using SS14.Shared.Interfaces.Map;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Map;
using SS14.Shared.Network;
using System.Collections.Generic;
using System.Linq;
namespace SS14.Client.State.States
{
@@ -57,6 +48,8 @@ namespace SS14.Client.State.States
private Chatbox _gameChat;
private IEntity lastHoveredEntity;
public override void Startup()
{
IoCManager.InjectDependencies(this);
@@ -133,6 +126,26 @@ namespace SS14.Client.State.States
{
placementManager.FrameUpdate(e);
_entityManager.FrameUpdate(e.Elapsed);
var map = playerManager.LocalPlayer.ControlledEntity.GetComponent<ITransformComponent>().MapID;
var mousePosWorld = eyeManager.ScreenToWorld(new ScreenCoordinates(inputManager.MouseScreenPosition, map));
IEntity entityToClick = GetEntityUnderPosition(mousePosWorld);
if (entityToClick == lastHoveredEntity)
{
return;
}
if (lastHoveredEntity != null)
{
lastHoveredEntity.GetComponent<IClientClickableComponent>().OnMouseLeave();
}
lastHoveredEntity = entityToClick;
if (lastHoveredEntity != null)
{
lastHoveredEntity.GetComponent<IClientClickableComponent>().OnMouseEnter();
}
}
public override void MouseDown(MouseButtonEventArgs eventargs)
@@ -142,37 +155,7 @@ namespace SS14.Client.State.States
var map = playerManager.LocalPlayer.ControlledEntity.GetComponent<ITransformComponent>().MapID;
var mousePosWorld = eyeManager.ScreenToWorld(new ScreenCoordinates(eventargs.Position, map));
// Find all the entities intersecting our click
var entities =
_entityManager.GetEntitiesIntersecting(mousePosWorld.MapID, mousePosWorld.Position);
// Check the entities against whether or not we can click them
var clickedEntities = new List<(IEntity clicked, int drawDepth)>();
foreach (IEntity entity in entities)
{
if (entity.TryGetComponent<IClientClickableComponent>(out var component)
&& entity.GetComponent<ITransformComponent>().IsMapTransform
&& component.CheckClick(mousePosWorld, out int drawdepthofclicked))
{
clickedEntities.Add((entity, drawdepthofclicked));
}
}
IEntity entityToClick;
if (clickedEntities.Any())
{
entityToClick = (from cd in clickedEntities
orderby cd.drawDepth ascending,
cd.clicked.GetComponent<ITransformComponent>().LocalPosition
.Y ascending
select cd.clicked).Last();
}
else
{
entityToClick = null;
}
IEntity entityToClick = GetEntityUnderPosition(mousePosWorld);
//First possible exit point for click, acceptable due to being clientside
if (entityToClick != null && placementManager.Eraser && placementManager.IsActive)
@@ -215,9 +198,50 @@ namespace SS14.Client.State.States
}
}
private IEntity GetEntityUnderPosition(LocalCoordinates coordinates)
{
// Find all the entities intersecting our click
var entities =
_entityManager.GetEntitiesIntersecting(coordinates.MapID, coordinates.Position);
// Check the entities against whether or not we can click them
var foundEntities = new List<(IEntity clicked, int drawDepth)>();
foreach (IEntity entity in entities)
{
if (entity.TryGetComponent<IClientClickableComponent>(out var component)
&& entity.GetComponent<ITransformComponent>().IsMapTransform
&& component.CheckClick(coordinates, out int drawdepthofclicked))
{
foundEntities.Add((entity, drawdepthofclicked));
}
}
if (foundEntities.Count != 0)
{
foundEntities.Sort(new ClickableEntityComparer());
return foundEntities[foundEntities.Count - 1].clicked;
}
return null;
}
public override void MouseUp(MouseButtonEventArgs e)
{
placementManager.MouseUp(e);
}
internal class ClickableEntityComparer : IComparer<(IEntity clicked, int depth)>
{
public int Compare((IEntity clicked, int depth) x, (IEntity clicked, int depth) y)
{
var val = x.depth.CompareTo(y.depth);
if (val != 0)
{
return val;
}
var transx = x.clicked.GetComponent<ITransformComponent>();
var transy = y.clicked.GetComponent<ITransformComponent>();
return transx.LocalPosition.Y.CompareTo(transy.LocalPosition.Y);
}
}
}
}

View File

@@ -14,6 +14,16 @@ namespace SS14.Client.Utility
return new Godot.Vector2(vector2.X, vector2.Y);
}
public static Vector3 Convert(this Godot.Vector3 vector3)
{
return new Vector3(vector3.x, vector3.y, vector3.z);
}
public static Godot.Vector3 Convert(this Vector3 vector3)
{
return new Godot.Vector3(vector3.X, vector3.Y, vector3.Z);
}
public static Color Convert(this Godot.Color color)
{
return new Color(color.r, color.g, color.b, color.a);

View File

@@ -1,4 +1,5 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -37,3 +38,5 @@ using System.Runtime.InteropServices;
[assembly: AssemblyVersion("0.1.*")]
[assembly: AssemblyFileVersion("0.1")]
[assembly: InternalsVisibleTo("SS14.UnitTesting")]

View File

@@ -32,5 +32,6 @@
Walls = 7,
WallMountedItems = 8,
WallTops = 9,
Overlays = 10,
}
}

View File

@@ -4,11 +4,11 @@
* Copyright (c) 2006-2008 the OpenTK Team.
* This notice may not be removed from any source distribution.
* See license.txt for licensing detailed licensing details.
*
*
* Contributions by Andy Gill, James Talton and Georg Wächter.
*/
#endregion
#endregion --- License ---
using System;
@@ -71,7 +71,7 @@ namespace SS14.Shared.Maths
/// </summary>
public const float Log2E = 1.442695041f;
#endregion
#endregion Fields
#region Public Members
@@ -85,7 +85,7 @@ namespace SS14.Shared.Maths
public static long NextPowerOfTwo(long n)
{
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), "Must be positive.");
return (long) Math.Pow(2, Math.Ceiling(Math.Log(n, 2)));
return (long)Math.Pow(2, Math.Ceiling(Math.Log(n, 2)));
}
/// <summary>
@@ -96,7 +96,7 @@ namespace SS14.Shared.Maths
public static int NextPowerOfTwo(int n)
{
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), "Must be positive.");
return (int) Math.Pow(2, Math.Ceiling(Math.Log(n, 2)));
return (int)Math.Pow(2, Math.Ceiling(Math.Log(n, 2)));
}
/// <summary>
@@ -107,7 +107,7 @@ namespace SS14.Shared.Maths
public static float NextPowerOfTwo(float n)
{
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), "Must be positive.");
return (float) Math.Pow(2, Math.Ceiling(Math.Log(n, 2)));
return (float)Math.Pow(2, Math.Ceiling(Math.Log(n, 2)));
}
/// <summary>
@@ -121,7 +121,7 @@ namespace SS14.Shared.Maths
return Math.Pow(2, Math.Ceiling(Math.Log(n, 2)));
}
#endregion
#endregion NextPowerOfTwo
#region Factorial
@@ -139,7 +139,7 @@ namespace SS14.Shared.Maths
return result;
}
#endregion
#endregion Factorial
#region BinomialCoefficient
@@ -154,7 +154,7 @@ namespace SS14.Shared.Maths
return Factorial(n) / (Factorial(k) * Factorial(n - k));
}
#endregion
#endregion BinomialCoefficient
#region DegreesToRadians
@@ -165,7 +165,7 @@ namespace SS14.Shared.Maths
/// <returns>The angle expressed in radians</returns>
public static float DegreesToRadians(float degrees)
{
const float degToRad = (float) Math.PI / 180.0f;
const float degToRad = (float)Math.PI / 180.0f;
return degrees * degToRad;
}
@@ -176,7 +176,7 @@ namespace SS14.Shared.Maths
/// <returns>The angle expressed in degrees</returns>
public static float RadiansToDegrees(float radians)
{
const float radToDeg = 180.0f / (float) Math.PI;
const float radToDeg = 180.0f / (float)Math.PI;
return radians * radToDeg;
}
@@ -202,7 +202,7 @@ namespace SS14.Shared.Maths
return radians * radToDeg;
}
#endregion
#endregion DegreesToRadians
#region Swap
@@ -230,8 +230,22 @@ namespace SS14.Shared.Maths
b = temp;
}
#endregion
#endregion Swap
#endregion
#region MinMax
public static float Min(float a, float b, float c, float d)
{
return Math.Min(a, Math.Min(b, Math.Min(c, d)));
}
public static float Max(float a, float b, float c, float d)
{
return Math.Max(a, Math.Max(b, Math.Max(c, d)));
}
#endregion MinMax
#endregion Public Members
}
}

View File

@@ -104,6 +104,8 @@ namespace SS14.Shared.GameObjects
/// </summary>
public YamlMappingNode DataNode { get; set; }
private readonly HashSet<Type> ReferenceTypes = new HashSet<Type>();
public void LoadFrom(YamlMappingNode mapping)
{
TypeString = mapping.GetNode("type").ToString();
@@ -138,9 +140,24 @@ namespace SS14.Shared.GameObjects
// COMPONENTS
if (mapping.TryGetNode<YamlSequenceNode>("components", out var componentsequence))
{
var factory = IoCManager.Resolve<IComponentFactory>();
foreach (var componentMapping in componentsequence.Cast<YamlMappingNode>())
{
ReadComponent(componentMapping);
ReadComponent(componentMapping, factory);
}
// Assert that there are no conflicting component references.
foreach (var componentName in Components.Keys)
{
var registration = factory.GetRegistration(componentName);
foreach (var type in registration.References)
{
if (ReferenceTypes.Contains(type))
{
throw new InvalidOperationException($"Duplicate component reference in prototype: '{type}'");
}
ReferenceTypes.Add(type);
}
}
}
@@ -240,6 +257,7 @@ namespace SS14.Shared.GameObjects
private static void PushInheritance(EntityPrototype source, EntityPrototype target)
{
// Copy component data over.
foreach (KeyValuePair<string, YamlMappingNode> component in source.Components)
{
if (target.Components.TryGetValue(component.Key, out YamlMappingNode targetComponent))
@@ -256,10 +274,22 @@ namespace SS14.Shared.GameObjects
else
{
// Copy component into the target, since it doesn't have it yet.
// Unless it'd cause a conflict.
var factory = IoCManager.Resolve<IComponentFactory>();
foreach (var refType in factory.GetRegistration(component.Key).References)
{
if (target.ReferenceTypes.Contains(refType))
{
// yeah nope the child's got it!! NEXT!!
goto next;
}
}
target.Components[component.Key] = new YamlMappingNode(component.Value.AsEnumerable());
}
next:;
}
// Copy all simple data over.
if (!target._placementOverriden)
{
target.PlacementMode = source.PlacementMode;
@@ -357,9 +387,8 @@ namespace SS14.Shared.GameObjects
return !entities.SelectMany(e => e.Prototype._snapFlags).Any(f => _snapFlags.Contains(f));
}
private void ReadComponent(YamlMappingNode mapping)
private void ReadComponent(YamlMappingNode mapping, IComponentFactory factory)
{
var factory = IoCManager.Resolve<IComponentFactory>();
string type = mapping.GetNode("type").AsString();
// See if type exists to detect errors.
switch (factory.GetComponentAvailability(type))

View File

@@ -77,6 +77,8 @@ using SS14.Shared.Log;
using SS14.Server.Prototypes;
using SS14.Client.Interfaces.GameStates;
using SS14.Client.GameStates;
using SS14.Client.Interfaces.Graphics.Overlays;
using SS14.Client.Graphics.Overlays;
namespace SS14.UnitTesting
{
@@ -222,6 +224,8 @@ namespace SS14.UnitTesting
IoCManager.Register<IDisplayManager, DisplayManager>();
//IoCManager.Register<IEyeManager, EyeManager>();
IoCManager.Register<IPrototypeManager, PrototypeManager>();
IoCManager.Register<IOverlayManager, OverlayManager>();
IoCManager.Register<ISceneTreeHolder, SceneTreeHolder>();
break;
case UnitTestProject.Server: