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

Gizmos #308

Merged
merged 3 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions en/manual/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ These pages contain information about how to use Stride, an open-source C# game
## Latest documentation

### Recent updates
- <span class="badge text-bg-info">New</span> [Scripts - Gizmos](scripts/gizmos.md) - Description and example of the Flexible Processing system
- <span class="badge text-bg-info">New</span> [ECS - Flexible Processing](engine/entity-component-system/flexible-processing.md) - Description and example of the Flexible Processing system
- <span class="badge text-bg-info">Updated</span> [Linux - Setup and requirements](platforms/linux/setup-and-requirements.md) - Fedora OS option added
- <span class="badge text-bg-success">New</span> [Scripts - Serialization](scripts/serialization.md) - Explanation of serialization
Expand Down
172 changes: 172 additions & 0 deletions en/manual/scripts/gizmos.md
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 1 deletion en/manual/scripts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ You can still use standard C# classes in Stride, but these aren't called scripts
* [Events](events.md)
* [Debugging](debugging.md)
* [Preprocessor variables](preprocessor-variables.md)
* [Create a model from code](create-a-model-from-code.md)
* [Create a model from code](create-a-model-from-code.md)
* [Create Gizmos for you components](gizmos.md)
3 changes: 3 additions & 0 deletions en/manual/scripts/media/gizmo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions en/manual/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,8 @@ items:
href: scripts/preprocessor-variables.md
- name: Create a model from code
href: scripts/create-a-model-from-code.md
- name: Create Gizmos for you components
href: scripts/gizmos.md

- name: Sprites
href: sprites/index.md
Expand Down