mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
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:
committed by
GitHub
parent
1812e8743d
commit
c058c2e3d4
@@ -9,3 +9,6 @@ charset = utf-8-bom
|
||||
|
||||
[*.{csproj,xml,yml,dll.config,targets}]
|
||||
indent_size = 2
|
||||
|
||||
[*.gdsl]
|
||||
indent_style = tab
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
- type: BoundingBox
|
||||
aabb: "-0.45,-1,0.95,1"
|
||||
DebugColor: "#0000FF"
|
||||
|
||||
- type: Collidable
|
||||
DebugColor: "#0000FF"
|
||||
|
||||
|
||||
7
Resources/Prototypes/Shaders/outline.yml
Normal file
7
Resources/Prototypes/Shaders/outline.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
- type: shader
|
||||
id: selection_outline
|
||||
kind: source
|
||||
path: "/Shaders/outline.gdsl"
|
||||
params:
|
||||
outline_width: 1
|
||||
outline_color: "#FF000055"
|
||||
59
Resources/Shaders/outline.gdsl
Normal file
59
Resources/Shaders/outline.gdsl
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
195
SS14.Client/Graphics/Overlays/Overlay.cs
Normal file
195
SS14.Client/Graphics/Overlays/Overlay.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
SS14.Client/Graphics/Overlays/OverlayManager.cs
Normal file
117
SS14.Client/Graphics/Overlays/OverlayManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
SS14.Client/Graphics/Shaders/Params.cs
Normal file
33
SS14.Client/Graphics/Shaders/Params.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
32
SS14.Client/Interfaces/Graphics/Overlays/IOverlay.cs
Normal file
32
SS14.Client/Interfaces/Graphics/Overlays/IOverlay.cs
Normal 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,
|
||||
}
|
||||
}
|
||||
24
SS14.Client/Interfaces/Graphics/Overlays/IOverlayManager.cs
Normal file
24
SS14.Client/Interfaces/Graphics/Overlays/IOverlayManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -32,5 +32,6 @@
|
||||
Walls = 7,
|
||||
WallMountedItems = 8,
|
||||
WallTops = 9,
|
||||
Overlays = 10,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user