Urho3D/bin/Data/Scripts/Editor/EditorViewSelectableOrigins.as

435 lines
14 KiB
ActionScript

const bool DEFAULT_SHOW_NAMES_FOR_ALL = false;
const int ORIGIN_STEP_UPDATE = 10;
const int NAMES_SIZE = 11;
const StringHash ORIGIN_NODEID_VAR("OriginNodeID");
const Color ORIGIN_COLOR(1.0f,1.0f,1.0f,1.0f);
const Color ORIGIN_COLOR_SELECTED(0.0f,1.0f,1.0f,1.0f);
const Color ORIGIN_COLOR_DISABLED(1.0f,0.0f,0.0f,1.0f);
const Color ORIGIN_COLOR_TEXT(1.0f,1.0f,1.0f,0.3f);
const Color ORIGIN_COLOR_SELECTED_TEXT(1.0f,1.0f,1.0f,1.0f);
const IntVector2 ORIGIN_ICON_SIZE(14,14);
const IntVector2 ORIGIN_ICON_SIZE_SELECTED(18,18);
const float ORIGINS_VISIBLITY_RANGE = 32.0f;
const IntVector2 ORIGINOFFSETICON(8,8);
const IntVector2 ORIGINOFFSETICONSELECTED(10,8);
bool showNamesForAll = DEFAULT_SHOW_NAMES_FOR_ALL;
bool EditorOriginShow = false;
bool rebuildSceneOrigins = true;
bool isOriginsHovered = false;
int EditorOriginUITimeToUpdate = 0;
int EditorOriginUITimeToSceneNodeRead = 0;
int prevSelectedID;
int selectedNodeInfoState = 0;
int originHoveredIndex = -1;
UIElement@ EditorOriginUIContainer = null;
Text@ selectedNodeName = null;
BorderImage@ selectedNodeOrigin = null;
Array<BorderImage@> selectedNodeOriginChilds;
Array<Text@> selectedNodeNameChilds;
Array<Node@> originsNodes;
Array<BorderImage@> originsIcons;
Array<Text@> originsNames;
void CreateOriginsContainer()
{
if (editorScene is null) return;
EditorOriginUIContainer = UIElement();
EditorOriginUIContainer.position = IntVector2(0,0);
EditorOriginUIContainer.size = IntVector2(graphics.width,graphics.height);
EditorOriginUIContainer.priority = -1000;
EditorOriginUIContainer.focusMode = FM_NOTFOCUSABLE;
EditorOriginUIContainer.bringToBack = true;
EditorOriginUIContainer.name ="DebugOriginsContainer";
EditorOriginUIContainer.temporary = true;
ui.root.AddChild(EditorOriginUIContainer);
}
void HandleOriginToggled(StringHash eventType, VariantMap& eventData)
{
UIElement@ origin = eventData["Element"].GetPtr();
if (origin is null) return;
if (EditorPaintSelectionShow) return;
if (IsSceneOrigin(origin))
{
int nodeID = origin.vars[ORIGIN_NODEID_VAR].GetInt();
if (editorScene !is null)
{
bool goBackAndSelectNodeParent = input.qualifierDown[QUAL_CTRL];
bool multiSelect = input.qualifierDown[QUAL_SHIFT];
WeakHandle handle = editorScene.GetNode(nodeID);
if (handle.Get() !is null) {
Node@ selectedNodeByOrigin = handle.Get();
if (selectedNodeByOrigin !is null)
{
if (goBackAndSelectNodeParent)
SelectNode(selectedNodeByOrigin.parent, false);
else
SelectNode(selectedNodeByOrigin, multiSelect);
}
}
}
}
}
void ShowOrigins(bool isVisible = true)
{
EditorOriginShow = isVisible;
if (EditorOriginUIContainer is null)
CreateOriginsContainer();
EditorOriginUIContainer.visible = isVisible;
}
void UpdateOrigins()
{
// Early out if Origins are disabled
if (!EditorOriginShow) return;
CheckKeyboardQualifers();
if (editorScene is null || EditorOriginUITimeToUpdate > time.systemTime) return;
EditorOriginUIContainer = ui.root.GetChild("DebugOriginsContainer");
// Since editor not clear UIs then do loading new scenes, this creation called once per Editor's starting event
// for other scenes we use the same container
if (EditorOriginUIContainer is null)
{
CreateOriginsContainer();
}
if (EditorOriginUIContainer !is null)
{
// Set visibility for all origins
EditorOriginUIContainer.visible = EditorOriginShow;
if (viewportMode!=VIEWPORT_SINGLE)
EditorOriginUIContainer.visible = false;
// Forced read nodes for some reason:
if ((originsNodes.length < 1) || rebuildSceneOrigins)
{
originsNodes = editorScene.GetChildren(true);
// If we are hot have free origins icons in arrays, resize x 2
if (originsIcons.length < originsNodes.length)
{
EditorOriginUIContainer.RemoveAllChildren();
originsIcons.Clear();
originsNames.Clear();
originsIcons.Resize(originsNodes.length * 2);
originsNames.Resize(originsNodes.length * 2);
if (originsIcons.length > 0)
{
for (int i=0; i < originsIcons.length; i++)
{
CreateOrigin(i, false);
}
}
}
// If this rebuild pass after new scene loading or add/delete node - reset flag to default
if (rebuildSceneOrigins)
rebuildSceneOrigins = false;
}
if (originsNodes.length > 0)
{
// Get selected node for feeding proper arrray's UIElements with slyte colorig and additional info on ALT
Node@ selectedNode = null;
if (selectedNodes.length > 0)
{
selectedNode = selectedNodes[0];
}
else if (selectedComponents.length > 0)
{
selectedNode = selectedComponents[0].node;
}
// Update existed origins (every 10 ms)
if (originsNodes.length > 0 )
{
for (int i=0; i < originsNodes.length; i++)
{
Vector3 eyeDir = originsNodes[i].worldPosition - cameraNode.worldPosition;
float distance = (eyeDir).length;
eyeDir.Normalize();
Vector3 cameraDir = (cameraNode.worldRotation * Vector3(0.0f, 0.0f, 1.0f)).Normalized();
float angleCameraDirVsDirToNode = eyeDir.DotProduct(cameraDir);
// if node in range and in camera view (clip back sibe)
if (distance < ORIGINS_VISIBLITY_RANGE && angleCameraDirVsDirToNode > 0.7f)
{
// turn on origin and move
MoveOrigin(i, true);
if (isThisNodeOneOfSelected(originsNodes[i]))
{
ShowSelectedNodeOrigin(originsNodes[i], i);
originsNames[i].visible = true;
}
else
{
if (showNamesForAll || (isOriginsHovered && originHoveredIndex == i))
originsNames[i].text = NodeInfo(originsNodes[i], selectedNodeInfoState);
}
}
else
{
// turn-off origin
VisibilityOrigin(i, false);
}
}
// Hide non used origins
for (int j=originsNodes.length; j < originsIcons.length; j++)
{
VisibilityOrigin(j, false);
}
}
}
}
EditorOriginUITimeToUpdate = time.systemTime + ORIGIN_STEP_UPDATE;
}
bool isThisNodeOneOfSelected(Node@ node)
{
if (selectedNodes.length < 1) return false;
for (int i = 0; i < selectedNodes.length; i++)
{
if (node is selectedNodes[i])
return true;
}
return false;
}
void ShowSelectedNodeOrigin(Node@ node, int index)
{
if (node !is null)
{
// just keep node's text and node's origin icon position in actual view
Viewport@ vp = activeViewport.viewport;
Vector2 sp = activeViewport.camera.WorldToScreenPoint(node.worldPosition);
//originsIcons[index].position = IntVector2(10+int(vp.rect.left + sp.x * vp.rect.right), -5 + int(vp.rect.top + sp.y* vp.rect.bottom));
originsIcons[index].position = IntVector2(int(vp.rect.left + sp.x * vp.rect.right) - ORIGINOFFSETICONSELECTED.x, int(vp.rect.top + sp.y* vp.rect.bottom) - ORIGINOFFSETICONSELECTED.y);
originsNames[index].color = ORIGIN_COLOR_SELECTED_TEXT;
if (originsNodes[index].enabled)
originsIcons[index].color = ORIGIN_COLOR_SELECTED;
else
originsIcons[index].color = ORIGIN_COLOR_DISABLED;
originsIcons[index].SetFixedSize(ORIGIN_ICON_SIZE_SELECTED.x,ORIGIN_ICON_SIZE_SELECTED.y);
// if selected node chaged, reset some vars
if (prevSelectedID != node.id)
{
prevSelectedID = node.id;
selectedNodeInfoState = 0;
originsIcons[index].vars[ORIGIN_NODEID_VAR] = node.id;
}
// We always update to keep and feed alt-info with actual info about node components
Array<Component@> components = node.GetComponents();
Array<String> componentsShortInfo;
Array<String> componentsDetailInfo;
componentsShortInfo.Resize(components.length);
componentsDetailInfo.Resize(components.length);
// Add std info node name + tags
originsNames[index].text = NodeInfo(node, selectedNodeInfoState) + "\n";
}
}
void CreateOrigin(int index, bool isVisible = false)
{
if (originsIcons.length < index) return;
originsIcons[index] = BorderImage("Icon");
originsIcons[index].temporary = true;
originsIcons[index].SetFixedSize(ORIGIN_ICON_SIZE.x,ORIGIN_ICON_SIZE.y);
originsIcons[index].texture = cache.GetResource("Texture2D", "Textures/Editor/EditorIcons.png");
originsIcons[index].imageRect = IntRect(0,0,14,14);
originsIcons[index].priority = -1000;
originsIcons[index].color = ORIGIN_COLOR;
originsIcons[index].bringToBack = true;
originsIcons[index].enabled = true;
originsIcons[index].selected = true;
originsIcons[index].visible = isVisible;
EditorOriginUIContainer.AddChild(originsIcons[index]);
originsNames[index] = Text();
originsNames[index].visible = false;
originsNames[index].SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), NAMES_SIZE);
originsNames[index].color = ORIGIN_COLOR_TEXT;
//originsNames[index].textEffect = TE_STROKE;
originsNames[index].temporary = true;
originsNames[index].bringToBack = true;
originsNames[index].priority = -1000;
originsNames[index].enabled = false;
EditorOriginUIContainer.AddChild(originsNames[index]);
}
void MoveOrigin(int index, bool isVisible = false)
{
if (originsIcons.length < index) return;
if (originsIcons[index] is null) return;
if (originsNodes[index].temporary)
{
originsIcons[index].visible = false;
originsNames[index].visible = false;
return;
}
Viewport@ vp = activeViewport.viewport;
Vector2 sp = activeViewport.camera.WorldToScreenPoint(originsNodes[index].worldPosition);
originsIcons[index].SetFixedSize(ORIGIN_ICON_SIZE.x,ORIGIN_ICON_SIZE.y);
if (originsNodes[index].enabled)
originsIcons[index].color = ORIGIN_COLOR;
else
originsIcons[index].color = ORIGIN_COLOR_DISABLED;
originsIcons[index].position = IntVector2(int(vp.rect.left + sp.x * vp.rect.right) - ORIGINOFFSETICON.x, int(vp.rect.top + sp.y* vp.rect.bottom) - ORIGINOFFSETICON.y);
originsIcons[index].visible = isVisible;
originsIcons[index].vars[ORIGIN_NODEID_VAR] = originsNodes[index].id;
originsNames[index].position = IntVector2(10+int(vp.rect.left + sp.x * vp.rect.right), -5 + int(vp.rect.top + sp.y* vp.rect.bottom));
if (isOriginsHovered && originHoveredIndex == index)
{
originsNames[index].visible = true;
originsNames[index].color = ORIGIN_COLOR_SELECTED_TEXT;
}
else
{
originsNames[index].visible = showNamesForAll ? isVisible : false;
originsNames[index].color = ORIGIN_COLOR_TEXT;
}
}
void VisibilityOrigin(int index, bool isVisible = false)
{
originsIcons[index].visible = isVisible;
originsNames[index].visible = isVisible;
}
bool IsSceneOrigin(UIElement@ element)
{
if (originsIcons.length < 1) return false;
for (int i=0; i < originsIcons.length; i++)
{
if (element is originsIcons[i])
{
originHoveredIndex = i;
return true;
}
}
originHoveredIndex = -1;
return false;
}
void CheckKeyboardQualifers()
{
// if pressed alt we inc state for info
bool showAltInfo = input.keyPress[KEY_ALT];
if (showAltInfo)
if (selectedNodeInfoState < 3) selectedNodeInfoState += 1;
// if pressed ctrl we reset info state
bool hideAltInfo = input.qualifierDown[QUAL_CTRL];
if (hideAltInfo)
selectedNodeInfoState = 0;
bool showNameForOther = false;
// In-B.mode Key_Space are busy by quick menu, so we use other key for B.mode
if (hotKeyMode == HOTKEYS_MODE_BLENDER)
showNameForOther = (input.keyPress[KEY_TAB] && ui.focusElement is null);
else
showNameForOther = (input.keyPress[KEY_SPACE] && ui.focusElement is null);
if (showNameForOther)
showNamesForAll =!showNamesForAll;
}
String NodeInfo(Node& node, int st)
{
String result = "";
if (node !is editorScene)
{
if (node.name.empty)
result = "Node";
else
result = node.name;
// Add node's tags if wey are exist
if (st > 0 && node.tags.length > 0)
{
result = result + "\n[";
for (int i=0;i<node.tags.length; i++)
{
result = result + " " + node.tags[i];
}
result = result + " ] ";
}
}
else
result = "Scene Origin";
return result;
}
void HandleSceneLoadedForOrigins()
{
rebuildSceneOrigins = true;
}
void HandleOriginsHoverBegin(StringHash eventType, VariantMap& eventData)
{
UIElement@ origin = eventData["Element"].GetPtr();
if (origin is null)
return;
if (IsSceneOrigin(origin))
{
VariantMap data;
data["Element"] = originsIcons[originHoveredIndex];
data["Id"] = originHoveredIndex;
data["NodeId"] = originsIcons[originHoveredIndex].vars[ORIGIN_NODEID_VAR].GetInt();
SendEvent(EDITOR_EVENT_ORIGIN_START_HOVER, data);
isOriginsHovered = true;
}
}
void HandleOriginsHoverEnd(StringHash eventType, VariantMap& eventData)
{
UIElement@ origin = eventData["Element"].GetPtr();
if (origin is null)
return;
if (IsSceneOrigin(origin))
{
VariantMap data;
data["Element"] = originsIcons[originHoveredIndex];
data["Id"] = originHoveredIndex;
data["NodeId"] = originsIcons[originHoveredIndex].vars[ORIGIN_NODEID_VAR].GetInt();
SendEvent(EDITOR_EVENT_ORIGIN_END_HOVER, data);
isOriginsHovered = false;
}
}