Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Editor][Plugin] Expose and cleanup gizmos #2127

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Eideren
Copy link
Collaborator

@Eideren Eideren commented Jan 26, 2024

PR Details

This PR provides support for user-defined gizmos, most of the logic was already there, I just had to clean out the editor only stuff from the api.

Here's a simple example:

[GizmoComponent(typeof(TestClass), isMainGizmo:false/*When true, only the first gizmo on an entity with true is visible, false means that it is always visible*/)]
public class Gizmo : IEntityGizmo
{
    private bool _selected, _enabled;
    private TestClass _component;
    private ModelComponent _model;
    private Material _material, _materialOnSelect;

    public bool IsEnabled
    {
        get
        {
            return _enabled;
        }
        set
        {
            _enabled = value;
            _model.Enabled = _enabled;
        }
    }

    public float SizeFactor { get; set; }

    public bool IsSelected
    {
        get
        {
            return _selected;
        }
        set
        {
            _selected = value;
            _model.Materials[0] = _selected ? _materialOnSelect : _material;
            // The logic below shows gizmos for all components when they are on in the gizmo settings, and when off, only shows the one from the selected entity
            // Removing the line hides gizmos even when selected when the gizmo settings is off
            _model.Enabled = _selected || _enabled;
        }
    }

    public Gizmo(TestClass component) // This constructor is called by the editor
    {
        _component = component;
    }

    public bool HandlesComponentId(OpaqueComponentId pickedComponentId, out Entity? selection)
    {
        // This function is called when scene picking/mouse clicking in the scene on a gizmo
        // The engine calls this function on each gizmos, gizmos in term notify the engine
        // when the given component comes from them, and provide the editor with the corresponding entity this gizmo represents
        if (pickedComponentId.Match(_model))
        {
            selection = _component.Entity;
            return true;
        }
        selection = null;
        return false;
    }

    public void Initialize(IServiceRegistry services, Scene editorScene)
    {
        var graphicsDevice = services.GetSafeServiceAs<IGraphicsDeviceService>().GraphicsDevice;

        var sphere = GeometricPrimitive.Sphere.New(graphicsDevice);

        var vertexBuffer = sphere.VertexBuffer;
        var indexBuffer = sphere.IndexBuffer;
        var vertexBufferBinding = new VertexBufferBinding(vertexBuffer, new VertexPositionNormalTexture().GetLayout(), vertexBuffer.ElementCount);
        var indexBufferBinding = new IndexBufferBinding(indexBuffer, sphere.IsIndex32Bits, indexBuffer.ElementCount);

        _material = Material.New(graphicsDevice, new MaterialDescriptor
        {
            Attributes =
            {
                Emissive = new MaterialEmissiveMapFeature(new ComputeColor(new Color4(0.25f,0.75f,0.25f,0.05f).ToColorSpace(graphicsDevice.ColorSpace))) { UseAlpha = true },
                Transparency = new MaterialTransparencyBlendFeature()
            },
        });
        _materialOnSelect = Material.New(graphicsDevice, new MaterialDescriptor
        {
            Attributes =
            {
                Emissive = new MaterialEmissiveMapFeature(new ComputeColor(new Color4(0.25f,0.75f,0.25f,0.5f).ToColorSpace(graphicsDevice.ColorSpace))) { UseAlpha = true },
                Transparency = new MaterialTransparencyBlendFeature()
            },
        });

        _model = new ModelComponent
        {
            Model = new Model
            {
                (_selected ? _materialOnSelect : _material),
                new Mesh
                {
                    Draw = new MeshDraw
                    {
                        StartLocation = 0,
                        // You can swap to LineList or LineStrip to show the model in wireframe mode, you'll have to adapt your index buffer to that new type though
                        PrimitiveType = PrimitiveType.TriangleList,
                        VertexBuffers = new[] { vertexBufferBinding },
                        IndexBuffer = indexBufferBinding,
                        DrawCount = indexBuffer.ElementCount,
                    }
                }
            },
            RenderGroup = IEntityGizmo.PickingRenderGroup, // This RenderGroup allows scene picking/selection, use a different one if you don't want selection
            Enabled = _selected || _enabled
        };

        var entity = new Entity($"{nameof(Gizmo)} for {_component.Entity.Name}"){ _model };
        entity.Transform.UseTRS = false; // We're controlling the matrix directly in this case
        entity.Scene = editorScene;

        vertexBuffer.DisposeBy(entity);
        indexBuffer.DisposeBy(entity); // Attach buffers to the entity for manual disposal later
    }

    public void Dispose()
    {
        _model.Entity.Scene = null;
        _model.Entity.Dispose(); // Clear the two buffers we attached above
    }

    public void Update()
    {
        // Ensures the gizmo follows the entity it is representing, note that UseTRS is disabled above to improve performance and ensure that there are no world space issues
        _model.Entity.Transform.LocalMatrix = _component.Entity.Transform.WorldMatrix;
    }
}

image

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • My change requires a change to the documentation. - I'll take care of that once this is in
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have built and run the editor to try this change out.

@VaclavElias
Copy link
Contributor

Ohh, does it mean, I can access it through code only and add to any entity as it is just a regular component?

@Eideren
Copy link
Collaborator Author

Eideren commented Jan 26, 2024

@VaclavElias It's not really a component, it only inherits from the IEntityGizmo interface.
The editor has facilities to automate the spawning and management of those gizmos when creating and destroying components with gizmos. The player as no such thing built in, but it's not that hard to implement on your side: create an instance alongside the component you want to have a gizmo on, call Initialize on the instance after creation, then Update on every update, and dispose when the component is removed from the scene. Once I'm looking at creating documentation for this I'll see if I can setup a processor or similar to automatically toggle gizmos on and off in a running game.

@VaclavElias
Copy link
Contributor

Thanks for the hints as usually. I will try it.

Yes, it could be useful to enable/disable it through running game as I found it still very useful even now, to orient in the 3D, when debugging/testing running game.

Also, if I may suggest. Adding optionally text X,Y,Z would be also helpful.

@Kryptos-FR Kryptos-FR self-requested a review January 26, 2024 17:07
@Eideren Eideren mentioned this pull request Jan 28, 2024
19 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants