Browse Source

Gizmo docs

Eideren 1 year ago
parent
commit
2944a99932
2 changed files with 175 additions and 0 deletions
  1. 172 0
      en/manual/scripts/gizmos.md
  2. 3 0
      en/manual/scripts/media/gizmo.png

+ 172 - 0
en/manual/scripts/gizmos.md

@@ -0,0 +1,172 @@
+# Gizmos
+
+<span class="badge text-bg-primary">Intermediate</span>
+<span class="badge text-bg-success">Programmer</span>
+
+**Gizmos** are a tool which you can implement over your components to provide visual assistance for designers when manipulating component values.
+
+Here's an exhaustive example one could implement:
+```cs
+using Stride.Core;
+using Stride.Core.Mathematics;
+using Stride.Engine;
+using Stride.Engine.Gizmos;
+using Stride.Graphics;
+using Stride.Graphics.GeometricPrimitives;
+using Stride.Rendering;
+using Stride.Rendering.Materials;
+using Stride.Rendering.Materials.ComputeColors;
+
+// We will be implementing a Gizmo for the following class
+public class MyScript : StartupScript
+{
+    
+}
+
+// This attribute specifies to the engine that the following gizmo class is bound to 'MyScript', 
+// the game studio will pick that up and spawn an instance of that class for each 'MyScript' in the scene
+[GizmoComponent(typeof(MyScript), 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 MyScript _component;
+    private ModelComponent _model;
+    private Material _material, _materialOnSelect;
+
+    // This property is set based on whether the gizmo is globally turned on or off in the editor's view settings
+    public bool IsEnabled
+    {
+        get
+        {
+            return _enabled;
+        }
+        set
+        {
+            _enabled = value;
+            _model.Enabled = _enabled;
+        }
+    }
+
+    // The size slider value in the view settings pane
+    public float SizeFactor { get; set; }
+
+    // The editor will set this property whenever the entity the component is on is selected
+    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;
+        }
+    }
+
+    // This constructor is called by the editor, 
+    // A gizmo class MUST contain a constructor with a single parameter of the component's type
+    public Gizmo(MyScript component)
+    {
+        _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 by returning true, 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)
+    {
+        // As part of initialization, we create a model in the editor scene to visualize the gizmo
+        var graphicsDevice = services.GetSafeServiceAs<IGraphicsDeviceService>().GraphicsDevice;
+
+        // In our case we'll just rely on the GeometricPrimitive API to create a sphere for us
+        // You don't have to create one right away, you can delay it until the component is in the appropriate state
+        // You can also dynamically create and update one in the Update() function further below
+        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()
+    {
+        // This is where you'll update how the gizmo looks based on MyScript's state
+        // Here we'll just ensure the gizmo follows the entity it is representing whenever that entity moves, 
+        // 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;
+    }
+}
+```
+And the result:
+
+![Green sphere gizmo](media/gizmo.png)
+
+Do note that you may have to restart the editor if it was open while you introduced this new gizmo.

+ 3 - 0
en/manual/scripts/media/gizmo.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0a62f0db688ba96190cb147747afcb9c43818717b62eb334f86ca6a0dceb0c86
+size 92679