177 lines
8.3 KiB
C#
Executable file
177 lines
8.3 KiB
C#
Executable file
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace Thry
|
|
{
|
|
public class DecalTool : EditorWindow
|
|
{
|
|
const string GUID_GIZMO_SHADER = "94a63a84353e517488db284dc8fab0ca";
|
|
|
|
private MaterialProperty _propPosition;
|
|
private MaterialProperty _propRotation;
|
|
private MaterialProperty _propScale;
|
|
private MaterialProperty _propOffset;
|
|
private MaterialProperty _propUVChannel;
|
|
private Material _material;
|
|
private Material _gizmoMaterial;
|
|
|
|
public static DecalTool OpenDecalTool(Material m)
|
|
{
|
|
var window = EditorWindow.GetWindow<DecalTool>("Decal Tool");
|
|
window._material = m;
|
|
window._gizmoMaterial = new Material(AssetDatabase.LoadAssetAtPath<Shader>(AssetDatabase.GUIDToAssetPath(GUID_GIZMO_SHADER)));
|
|
window._gizmoMaterial.color = Color.white;
|
|
return window;
|
|
}
|
|
|
|
public void SetMaterialProperties(MaterialProperty decalProp, MaterialProperty uvProp, MaterialProperty positionProp, MaterialProperty rotationProp, MaterialProperty scaleProp, MaterialProperty offsetProp)
|
|
{
|
|
_propPosition = positionProp;
|
|
_propRotation = rotationProp;
|
|
_propScale = scaleProp;
|
|
_propOffset = offsetProp;
|
|
_propUVChannel = uvProp;
|
|
_gizmoMaterial.SetTexture("_DecalTex", decalProp.textureValue);
|
|
this.Repaint();
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
if(_propPosition == null)
|
|
{
|
|
return;
|
|
}
|
|
HandleInput();
|
|
// EditorGUI.DrawPreviewTexture(new Rect(0, 0, position.width, position.height), _material.mainTexture, _material);
|
|
EditorGUI.DrawTextureTransparent(new Rect(0, 0, position.width, position.height), _material.mainTexture, ScaleMode.StretchToFill);
|
|
_gizmoMaterial.SetVector("_Position", _propPosition.vectorValue);
|
|
_gizmoMaterial.SetVector("_Scale", _propScale.vectorValue);
|
|
_gizmoMaterial.SetFloat("_Rotation", _propRotation.floatValue);
|
|
_gizmoMaterial.SetVector("_Offset", _propOffset.vectorValue);
|
|
_gizmoMaterial.SetFloat("_UVChannel", _propUVChannel.floatValue);
|
|
EditorGUI.DrawPreviewTexture(new Rect(0, 0, position.width, position.height), Texture2D.whiteTexture, _gizmoMaterial);
|
|
}
|
|
|
|
private Vector2 _lastMousePosition;
|
|
private Vector2 _initialMousePosition;
|
|
private Vector4 _initalScale;
|
|
private float _initialRotation;
|
|
private bool _isInsideAction;
|
|
private bool _isOutsideAction;
|
|
private Vector2 _grabbedSnappoint;
|
|
private Vector2 _lastDecalMouseUV;
|
|
private Vector2 _initalDecalMouseUV;
|
|
private Vector2 _initalMouseUV;
|
|
private void HandleInput()
|
|
{
|
|
Vector2 pivot = _propPosition.vectorValue * position.size;
|
|
Vector2 pivotUV = pivot / position.size;
|
|
|
|
Vector2 mouseUV = Event.current.mousePosition / position.size;
|
|
mouseUV.y = 1 - mouseUV.y;
|
|
Vector2 decalMouseUV = DecalUV(mouseUV, _propPosition.vectorValue, _propRotation.floatValue, _propScale.vectorValue, _propOffset.vectorValue);
|
|
|
|
Event e = Event.current;
|
|
bool isMouseDrag = e.type == EventType.MouseDrag;
|
|
|
|
Vector2 delta = e.mousePosition - _lastMousePosition;
|
|
Vector2 deltaDecalUV = decalMouseUV - _lastDecalMouseUV;
|
|
if(isMouseDrag)
|
|
{
|
|
_lastMousePosition = e.mousePosition;
|
|
}
|
|
|
|
|
|
if (e.type == EventType.MouseDown && e.button == 0)
|
|
{
|
|
_initialMousePosition = e.mousePosition;
|
|
_lastMousePosition = e.mousePosition;
|
|
|
|
_initalMouseUV = mouseUV;
|
|
|
|
_initalDecalMouseUV = decalMouseUV;
|
|
_lastDecalMouseUV = decalMouseUV;
|
|
|
|
_initalScale = _propScale.vectorValue;
|
|
_initialRotation = _propRotation.floatValue;
|
|
_isInsideAction = decalMouseUV.x >= 0.1f && decalMouseUV.x <= 0.9f && decalMouseUV.y >= 0.1f && decalMouseUV.y <= 0.9f;
|
|
_isOutsideAction = decalMouseUV.x < -0.1f || decalMouseUV.x > 1.1f || decalMouseUV.y < -0.1f || decalMouseUV.y > 1.1f;
|
|
_grabbedSnappoint = new Vector2(Mathf.Round(decalMouseUV.x * 2), Mathf.Round(decalMouseUV.y * 2)) / 2;
|
|
}
|
|
// Translate
|
|
if (_isInsideAction && isMouseDrag && !e.alt)
|
|
{
|
|
delta = delta / position.size;
|
|
Vector2 pos = _propPosition.vectorValue;
|
|
pos.x += delta.x;
|
|
pos.y -= delta.y;
|
|
_propPosition.vectorValue = pos;
|
|
this.Repaint();
|
|
}
|
|
// Rotate
|
|
else if(_isOutsideAction && isMouseDrag && !e.alt)
|
|
{
|
|
Vector2 vecInital = _initalMouseUV - pivotUV;
|
|
Vector2 vecLast = mouseUV - pivotUV;
|
|
float angle = Vector2.SignedAngle(vecInital, vecLast);
|
|
SetClampedRotation(_propRotation, _initialRotation - angle);
|
|
this.Repaint();
|
|
}
|
|
// Scale
|
|
else if(isMouseDrag && e.alt)
|
|
{
|
|
Vector2 vecInital = _initalMouseUV - pivotUV;
|
|
Vector2 vecLast = mouseUV - pivotUV;
|
|
float vecLastIntoInitalDir = Vector2.Dot(vecLast, vecInital.normalized);
|
|
float uniform = vecInital.magnitude / vecLastIntoInitalDir;
|
|
_propScale.vectorValue = _initalScale / uniform;
|
|
this.Repaint();
|
|
}
|
|
// Offset
|
|
else if(isMouseDrag && !_isInsideAction && !_isOutsideAction)
|
|
{
|
|
Vector4 offset = _propOffset.vectorValue;
|
|
if(_grabbedSnappoint.x == 0)
|
|
offset.x = Mathf.Max(-_propScale.vectorValue.x / 2, offset.x - deltaDecalUV.x * _propScale.vectorValue.y);
|
|
if(_grabbedSnappoint.x == 1)
|
|
offset.y = Mathf.Max(-_propScale.vectorValue.x / 2, offset.y + deltaDecalUV.x * _propScale.vectorValue.y);
|
|
if(_grabbedSnappoint.y == 0)
|
|
offset.z = Mathf.Max(-_propScale.vectorValue.y / 2, offset.z - deltaDecalUV.y * _propScale.vectorValue.y);
|
|
if(_grabbedSnappoint.y == 1)
|
|
offset.w = Mathf.Max(-_propScale.vectorValue.y / 2, offset.w + deltaDecalUV.y * _propScale.vectorValue.y);
|
|
_propOffset.vectorValue = offset;
|
|
this.Repaint();
|
|
}
|
|
|
|
if(isMouseDrag)
|
|
{
|
|
_lastDecalMouseUV = DecalUV(mouseUV, _propPosition.vectorValue, _propRotation.floatValue, _propScale.vectorValue, _propOffset.vectorValue);
|
|
}
|
|
}
|
|
|
|
static Vector2 Remap(Vector2 x, Vector2 minOld, Vector2 maxOld, Vector2 minNew, Vector2 maxNew)
|
|
{
|
|
return minNew + (x - minOld) * (maxNew - minNew) / (maxOld - minOld);
|
|
}
|
|
|
|
static Vector2 DecalUV(Vector2 uv, Vector2 position, float rotation, Vector2 scale, Vector4 scaleOffset)
|
|
{
|
|
scaleOffset = new Vector4(-scaleOffset.x, scaleOffset.y, -scaleOffset.z, scaleOffset.w);
|
|
Vector2 centerOffset = new Vector2((scaleOffset.x + scaleOffset.y)/2, (scaleOffset.z + scaleOffset.w)/2);
|
|
Vector2 decalCenter = position + centerOffset;
|
|
float theta = Mathf.Deg2Rad * rotation;
|
|
float cs = Mathf.Cos(theta);
|
|
float sn = Mathf.Sin(theta);
|
|
uv = new Vector2((uv.x - decalCenter.x) * cs - (uv.y - decalCenter.y) * sn + decalCenter.x, (uv.x - decalCenter.x) * sn + (uv.y - decalCenter.y) * cs + decalCenter.y);
|
|
uv = Remap(uv, new Vector2(0, 0) - scale / 2 + position + new Vector2(scaleOffset.x, scaleOffset.z), scale / 2 + position + new Vector2(scaleOffset.y,scaleOffset.w), Vector2.zero, Vector2.one);
|
|
return uv;
|
|
}
|
|
|
|
public static void SetClampedRotation(MaterialProperty property, float value)
|
|
{
|
|
Vector2 limits = property.rangeLimits;
|
|
value = Helper.Mod(value - limits.x, limits.y - limits.x) + limits.x;
|
|
property.floatValue = value;
|
|
}
|
|
}
|
|
} |