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("Decal Tool"); window._material = m; window._gizmoMaterial = new Material(AssetDatabase.LoadAssetAtPath(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; } } }