mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-02 11:28:19 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
268 lines
6.4 KiB
C#
268 lines
6.4 KiB
C#
namespace Sandbox;
|
|
|
|
public partial class GameObject
|
|
{
|
|
internal class GameObjectHandle
|
|
{
|
|
public Texture Texture { get; set; }
|
|
public string Icon { get; set; }
|
|
public Color Color { get; set; }
|
|
}
|
|
|
|
GameObjectHandle _handle;
|
|
bool handleBuilt;
|
|
|
|
[Obsolete( "Use HasGizmoHandle" )]
|
|
public bool HasGimzoHandle { get => HasGizmoHandle; private set => HasGizmoHandle = value; }
|
|
public bool HasGizmoHandle { get; private set; }
|
|
|
|
void BuildGizmoDetails()
|
|
{
|
|
if ( handleBuilt )
|
|
return;
|
|
|
|
_handle = default;
|
|
|
|
var handles = Components.GetAll()
|
|
.Where( x => x is not null )
|
|
.Select( x => Game.TypeLibrary.GetType( x.GetType() ) )
|
|
.Where( x => x is not null )
|
|
.SelectMany( x => x.GetAttributes<EditorHandleAttribute>( true ) )
|
|
.FirstOrDefault();
|
|
|
|
if ( handles is null )
|
|
return;
|
|
|
|
_handle = new GameObjectHandle();
|
|
_handle.Icon = handles.Icon;
|
|
_handle.Color = handles.Color;
|
|
|
|
var colorProvider = Components.GetAll<Component.IColorProvider>().FirstOrDefault();
|
|
if ( colorProvider is not null )
|
|
{
|
|
_handle.Color = colorProvider.ComponentColor;
|
|
|
|
// this is mainly for lights, we don't want any black bulbs, but we do want to indicate light color
|
|
// so if anything else starts using this we should probably move this logic into the light component implementation
|
|
_handle.Color = ((Vector3)_handle.Color).Normal * 2;
|
|
}
|
|
|
|
if ( !string.IsNullOrWhiteSpace( handles.Texture ) )
|
|
{
|
|
_handle.Texture = Texture.Load( handles.Texture );
|
|
}
|
|
}
|
|
|
|
void DrawGizmoHandle( ref bool clicked )
|
|
{
|
|
HasGizmoHandle = false;
|
|
|
|
if ( !Gizmo.Settings.GizmosEnabled ) return;
|
|
|
|
BuildGizmoDetails();
|
|
|
|
if ( _handle is null )
|
|
return;
|
|
|
|
bool isSelected = Gizmo.IsSelected;
|
|
bool selected = Gizmo.IsSelected;
|
|
|
|
using ( Gizmo.Scope( "Handle" ) )
|
|
{
|
|
Gizmo.Transform = Gizmo.Transform.WithScale( 1.0f );
|
|
|
|
float size = 32;
|
|
|
|
if ( !selected )
|
|
{
|
|
Gizmo.Hitbox.DepthBias = 0.1f;
|
|
Gizmo.Hitbox.Sprite( 0, size * Gizmo.Settings.GizmoScale, false );
|
|
|
|
clicked = clicked || Gizmo.WasClicked;
|
|
}
|
|
|
|
float opacity = 0.6f;
|
|
|
|
if ( Gizmo.IsHovered ) opacity = 1;
|
|
if ( isSelected ) opacity = 1;
|
|
|
|
if ( Gizmo.IsHovered && Gizmo.Settings.Selection ) size = 40;
|
|
|
|
Gizmo.Draw.IgnoreDepth = true;
|
|
|
|
if ( _handle.Texture is not null )
|
|
{
|
|
//
|
|
// Texture mode
|
|
//
|
|
|
|
Gizmo.Draw.Color = isSelected ? Color.Yellow : _handle.Color;
|
|
Gizmo.Draw.Sprite( Vector3.Zero, size * Gizmo.Settings.GizmoScale, _handle.Texture, false );
|
|
}
|
|
else if ( _handle.Icon is not null )
|
|
{
|
|
//
|
|
// Icon mode
|
|
//
|
|
|
|
var text = new TextRendering.Scope( _handle.Icon, _handle.Color, 64, "Material Icons", 400 );
|
|
text.Shadow = new TextRendering.Shadow { Enabled = true, Color = Color.Black, Offset = 2, Size = 8 };
|
|
var tex = TextRendering.GetOrCreateTexture( text, flag: TextFlag.Center );
|
|
if ( tex is not null )
|
|
{
|
|
Gizmo.Draw.Color = Color.White.WithAlphaMultiplied( opacity );
|
|
Gizmo.Draw.Sprite( Vector3.Zero, size * Gizmo.Settings.GizmoScale, tex, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
HasGizmoHandle = true;
|
|
}
|
|
|
|
internal void DrawGizmos()
|
|
{
|
|
if ( !Active ) return;
|
|
var parentTx = Gizmo.Transform;
|
|
|
|
var tx = LocalTransform;
|
|
|
|
// Absolute gameobject transform need to be converted back to local because it's already in worldspace
|
|
if ( Flags.Contains( GameObjectFlags.Absolute ) )
|
|
{
|
|
tx = parentTx.ToLocal( tx );
|
|
}
|
|
|
|
using ( Gizmo.ObjectScope( this, tx ) )
|
|
{
|
|
bool clicked = Gizmo.WasClicked;
|
|
|
|
if ( Gizmo.Settings.GizmosEnabled )
|
|
{
|
|
DrawGizmoHandle( ref clicked );
|
|
|
|
Components.ForEach( "DrawGizmos", false, c =>
|
|
{
|
|
if ( !c.Flags.Contains( ComponentFlags.Hidden ) )
|
|
{
|
|
using var scope = Gizmo.Scope();
|
|
c.DrawGizmosInternal();
|
|
clicked = clicked || Gizmo.WasClicked;
|
|
}
|
|
} );
|
|
}
|
|
|
|
if ( clicked )
|
|
{
|
|
GizmoSelect();
|
|
}
|
|
|
|
//
|
|
// If we pressed on this, but then moved the mouse a lot, clear the pressed state
|
|
//
|
|
if ( Gizmo.Pressed.This && Gizmo.CursorDragDelta.Length > 10 )
|
|
{
|
|
Gizmo.Pressed.ClearPath();
|
|
}
|
|
|
|
ForEachChild( "DrawGizmos", false, c =>
|
|
{
|
|
if ( !c.Flags.Contains( GameObjectFlags.Hidden ) )
|
|
{
|
|
c.DrawGizmos();
|
|
}
|
|
} );
|
|
|
|
DrawBoneGizmo();
|
|
}
|
|
}
|
|
|
|
void DrawBoneGizmo()
|
|
{
|
|
if ( !Gizmo.Settings.GizmosEnabled )
|
|
return;
|
|
|
|
if ( !Flags.Contains( GameObjectFlags.Bone ) )
|
|
return;
|
|
|
|
if ( !Parent.IsValid() )
|
|
return;
|
|
|
|
if ( !Parent.Flags.Contains( GameObjectFlags.Bone ) )
|
|
return;
|
|
|
|
var distance = Root.WorldPosition.Distance( Gizmo.Camera.Position );
|
|
if ( distance > 500.0f )
|
|
return;
|
|
|
|
var position = WorldTransform.PointToLocal( Parent.WorldPosition );
|
|
var length = position.Length * 0.5f;
|
|
var width = length * 0.1f;
|
|
|
|
if ( width.AlmostEqual( 0.0f ) )
|
|
return;
|
|
|
|
using ( Gizmo.Scope( "Bone" ) )
|
|
{
|
|
Gizmo.Hitbox.DepthBias *= 0.2f;
|
|
Gizmo.Draw.IgnoreDepth = true;
|
|
Gizmo.Draw.Color = Color.White.WithAlpha( 0.2f );
|
|
Gizmo.Draw.LineThickness = 1;
|
|
|
|
Gizmo.Draw.Line( 0, position );
|
|
|
|
Gizmo.Draw.Color = Gizmo.IsSelected ? Gizmo.Colors.Active : Gizmo.IsHovered ? Color.White : Color.White.WithAlpha( 0.2f );
|
|
Gizmo.Draw.LineThickness = Gizmo.IsSelected ? 2 : Gizmo.IsHovered ? 2 : 1;
|
|
|
|
Gizmo.Draw.Sprite( 0, 0.4f, Texture.White );
|
|
|
|
Gizmo.Hitbox.Sphere( new Sphere( 0, 0.4f ) );
|
|
|
|
if ( Gizmo.WasClicked )
|
|
GizmoSelect();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the first GameObject in the ancestor chain that we consider a selection base.
|
|
/// </summary>
|
|
GameObject FindSelectionBase()
|
|
{
|
|
var isSelectionBase = IsNetworkRoot || IsOutermostPrefabInstanceRoot || Components.GetAll().Any( x => Game.TypeLibrary.GetType( x.GetType() ).HasAttribute<SelectionBaseAttribute>() );
|
|
|
|
if ( isSelectionBase ) return this;
|
|
|
|
if ( Parent.IsValid() ) return Parent.FindSelectionBase();
|
|
|
|
return null;
|
|
}
|
|
|
|
void GizmoSelect()
|
|
{
|
|
if ( !Gizmo.Settings.Selection )
|
|
return;
|
|
|
|
// Find the best candidate to select
|
|
var selectionBase = FindSelectionBase();
|
|
|
|
if ( selectionBase != null && selectionBase != this )
|
|
{
|
|
// If the selectionbase is already selected, we don't want to select it again, we want to switch the selection to the child
|
|
// So when you double click an object that is descendant of the selectionbase you will be able to select the nested object.
|
|
if ( !Gizmo.Active.Selection.Contains( selectionBase ) )
|
|
{
|
|
selectionBase.GizmoSelect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
using ( Gizmo.ObjectScope( this, LocalTransform ) )
|
|
{
|
|
using ( Scene.Editor?.UndoScope( $"Select {Name}" ).Push() )
|
|
{
|
|
Gizmo.Select();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|