192 lines
7.7 KiB
C#
192 lines
7.7 KiB
C#
// SPDX-License-Identifier: MIT
|
|
|
|
using System;
|
|
using GaussianSplatting.Runtime;
|
|
using UnityEditor;
|
|
using UnityEditor.EditorTools;
|
|
using UnityEngine;
|
|
|
|
namespace GaussianSplatting.Editor
|
|
{
|
|
[EditorToolContext("GaussianSplats", typeof(GaussianSplatRenderer)), Icon(k_IconPath)]
|
|
class GaussianToolContext : EditorToolContext
|
|
{
|
|
const string k_IconPath = "Packages/org.nesnausk.gaussian-splatting/Editor/Icons/GaussianContext.png";
|
|
|
|
Vector2 m_MouseStartDragPos;
|
|
|
|
protected override Type GetEditorToolType(Tool tool)
|
|
{
|
|
if (tool == Tool.Move)
|
|
return typeof(GaussianMoveTool);
|
|
//if (tool == Tool.Rotate)
|
|
// return typeof(GaussianRotateTool); // not correctly working yet
|
|
//if (tool == Tool.Scale)
|
|
// return typeof(GaussianScaleTool); // not working correctly yet when the GS itself has scale
|
|
return null;
|
|
}
|
|
|
|
public override void OnWillBeDeactivated()
|
|
{
|
|
var gs = target as GaussianSplatRenderer;
|
|
if (!gs)
|
|
return;
|
|
gs.EditDeselectAll();
|
|
}
|
|
|
|
static void HandleKeyboardCommands(Event evt, GaussianSplatRenderer gs)
|
|
{
|
|
if (evt.type != EventType.ValidateCommand && evt.type != EventType.ExecuteCommand)
|
|
return;
|
|
bool execute = evt.type == EventType.ExecuteCommand;
|
|
switch (evt.commandName)
|
|
{
|
|
// ugh, EventCommandNames string constants is internal :(
|
|
case "SoftDelete":
|
|
case "Delete":
|
|
if (execute)
|
|
{
|
|
gs.EditDeleteSelected();
|
|
GaussianSplatRendererEditor.RepaintAll();
|
|
}
|
|
evt.Use();
|
|
break;
|
|
case "SelectAll":
|
|
if (execute)
|
|
{
|
|
gs.EditSelectAll();
|
|
GaussianSplatRendererEditor.RepaintAll();
|
|
}
|
|
evt.Use();
|
|
break;
|
|
case "DeselectAll":
|
|
if (execute)
|
|
{
|
|
gs.EditDeselectAll();
|
|
GaussianSplatRendererEditor.RepaintAll();
|
|
}
|
|
evt.Use();
|
|
break;
|
|
case "InvertSelection":
|
|
if (execute)
|
|
{
|
|
gs.EditInvertSelection();
|
|
GaussianSplatRendererEditor.RepaintAll();
|
|
}
|
|
evt.Use();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool IsViewToolActive()
|
|
{
|
|
return Tools.viewToolActive || Tools.current == Tool.View || (Event.current != null && Event.current.alt);
|
|
}
|
|
|
|
public override void OnToolGUI(EditorWindow window)
|
|
{
|
|
if (!(window is SceneView sceneView))
|
|
return;
|
|
var gs = target as GaussianSplatRenderer;
|
|
if (!gs)
|
|
return;
|
|
|
|
GaussianSplatRendererEditor.BumpGUICounter();
|
|
|
|
int id = GUIUtility.GetControlID(FocusType.Passive);
|
|
Event evt = Event.current;
|
|
HandleKeyboardCommands(evt, gs);
|
|
var evtType = evt.GetTypeForControl(id);
|
|
switch (evtType)
|
|
{
|
|
case EventType.Layout:
|
|
// make this be the default tool, so that we get focus when user clicks on nothing else
|
|
HandleUtility.AddDefaultControl(id);
|
|
break;
|
|
case EventType.MouseDown:
|
|
if (IsViewToolActive())
|
|
break;
|
|
if (HandleUtility.nearestControl == id && evt.button == 0)
|
|
{
|
|
// shift/command adds to selection, ctrl removes from selection: if none of these
|
|
// are present, start a new selection
|
|
if (!evt.shift && !EditorGUI.actionKey && !evt.control)
|
|
gs.EditDeselectAll();
|
|
|
|
// record selection state at start
|
|
gs.EditStoreSelectionMouseDown();
|
|
GaussianSplatRendererEditor.RepaintAll();
|
|
|
|
GUIUtility.hotControl = id;
|
|
m_MouseStartDragPos = evt.mousePosition;
|
|
evt.Use();
|
|
}
|
|
break;
|
|
case EventType.MouseDrag:
|
|
if (GUIUtility.hotControl == id && evt.button == 0)
|
|
{
|
|
Rect rect = FromToRect(m_MouseStartDragPos, evt.mousePosition);
|
|
Vector2 rectMin = HandleUtility.GUIPointToScreenPixelCoordinate(rect.min);
|
|
Vector2 rectMax = HandleUtility.GUIPointToScreenPixelCoordinate(rect.max);
|
|
gs.EditUpdateSelection(rectMin, rectMax, sceneView.camera, evt.control);
|
|
GaussianSplatRendererEditor.RepaintAll();
|
|
evt.Use();
|
|
}
|
|
break;
|
|
case EventType.MouseUp:
|
|
if (GUIUtility.hotControl == id && evt.button == 0)
|
|
{
|
|
m_MouseStartDragPos = Vector2.zero;
|
|
GUIUtility.hotControl = 0;
|
|
evt.Use();
|
|
}
|
|
break;
|
|
case EventType.Repaint:
|
|
// draw cutout gizmos
|
|
Handles.color = new Color(1,0,1,0.7f);
|
|
var prevMatrix = Handles.matrix;
|
|
foreach (var cutout in gs.m_Cutouts)
|
|
{
|
|
if (!cutout)
|
|
continue;
|
|
Handles.matrix = cutout.transform.localToWorldMatrix;
|
|
if (cutout.m_Type == GaussianCutout.Type.Ellipsoid)
|
|
{
|
|
Handles.DrawWireDisc(Vector3.zero, Vector3.up, 1.0f);
|
|
Handles.DrawWireDisc(Vector3.zero, Vector3.right, 1.0f);
|
|
Handles.DrawWireDisc(Vector3.zero, Vector3.forward, 1.0f);
|
|
}
|
|
if (cutout.m_Type == GaussianCutout.Type.Box)
|
|
Handles.DrawWireCube(Vector3.zero, Vector3.one * 2);
|
|
}
|
|
|
|
Handles.matrix = prevMatrix;
|
|
// draw selection bounding box
|
|
if (gs.editSelectedSplats > 0)
|
|
{
|
|
var selBounds = GaussianSplatRendererEditor.TransformBounds(gs.transform, gs.editSelectedBounds);
|
|
Handles.DrawWireCube(selBounds.center, selBounds.size);
|
|
}
|
|
// draw drag rectangle
|
|
if (GUIUtility.hotControl == id && evt.mousePosition != m_MouseStartDragPos)
|
|
{
|
|
GUIStyle style = "SelectionRect";
|
|
Handles.BeginGUI();
|
|
style.Draw(FromToRect(m_MouseStartDragPos, evt.mousePosition), false, false, false, false);
|
|
Handles.EndGUI();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// build a rect that always has a positive size
|
|
static Rect FromToRect(Vector2 from, Vector2 to)
|
|
{
|
|
if (from.x > to.x)
|
|
(from.x, to.x) = (to.x, from.x);
|
|
if (from.y > to.y)
|
|
(from.y, to.y) = (to.y, from.y);
|
|
return new Rect(from.x, from.y, to.x - from.x, to.y - from.y);
|
|
}
|
|
}
|
|
} |