You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

258 lines
12 KiB
C#

using UnityEditor;
using UnityEngine;
namespace FluffyGroomingTool {
public class BrushPropertiesUI {
static readonly float CHANGE_BRUSH_SENSITIVITY = 0.005f;
static readonly float SMALL_CHANGE_BRUSH_SENSITIVITY = 0.0001f;
static readonly float BRUSH_FILL_OPACITY = 0.75f;
static readonly float MIN_BRUSH_SIZE = 0.001f;
static readonly float MAX_BRUSH_SIZE = 10f;
private static readonly string HAS_IGNORE_NORMAL_TIP_BEEN_SHOWN = "hasIgnoreNormalTipBeenShown";
private GUIStyle _panelStyle;
private BindPoseHelper bindPoseHelper = new BindPoseHelper();
private static bool hasIgnoreNormalBeenShown;
public void init() {
hasIgnoreNormalBeenShown = PerProjectPreferences.getInt(HAS_IGNORE_NORMAL_TIP_BEEN_SHOWN, -1) != -1;
PerProjectPreferences.setInt(HAS_IGNORE_NORMAL_TIP_BEEN_SHOWN, 1);
}
public GUIStyle PanelStyle {
get {
if (_panelStyle == null) _panelStyle = createDefaultPanelStyle();
return _panelStyle;
}
}
public static GUIStyle createDefaultPanelStyle(string bg = "bg_box") {
return new GUIStyle(GUI.skin.label) {
border = new RectOffset(20, 20, 20, 20), padding = new RectOffset(15, 16, 15, 17),
normal = {background = Resources.Load<Texture2D>(bg)}
};
}
private GUIStyle mirrorPanelStyle;
private GUIStyle learnMoreStyle;
public void drawBrushPropertiesUI(FurCreator furCreator, PainterBrushTypeUI brushMenu, GUIStyle buttonStyle) {
PainterProperties properties = furCreator.getPainterProperties();
var ctrl = getControlText();
properties.isBrushMenuExpanded = EditorGUILayout.Foldout(properties.isBrushMenuExpanded, "Brush Properties");
if (properties.isBrushMenuExpanded) {
EditorGUILayout.Space(PainterResetAndSmoothUI.DEFAULT_CHILD_VERTICAL_MARGIN);
GUILayout.BeginVertical(PanelStyle);
properties.brushSize = furCreator.groomContainer.PainterProperties.undoSlider(
new GUIContent("Brush Size:", "Shortcut: Left mouse button + " + ctrl + ". Changes the size of the brush."), properties.brushSize,
MIN_BRUSH_SIZE, MAX_BRUSH_SIZE);
addIgnoreNormalTooltipClick();
properties.brushOpacity = furCreator.groomContainer.PainterProperties.undoSlider(
new GUIContent("Brush Opacity:", "Shortcut: Left mouse button + Shift. Changes the intensity of the brush."),
properties.brushOpacity);
properties.brushFalloffPercentOfBrushSize = furCreator.groomContainer.PainterProperties.undoSlider(
new GUIContent("Brush Falloff:",
"Shortcut: Left mouse button + Shift & " + ctrl +
". The falloff is the intensity of the brush from the center towards its borders."),
properties.brushFalloffPercentOfBrushSize);
properties.brushFalloff = properties.brushFalloffPercentOfBrushSize * properties.brushSize;
properties.isNormalIgnored = furCreator.toggle(
new GUIContent("Ignore Normal Slope",
"When this is off painting will only affect areas with similar curvature as the center of the brush." +
" A good example is when painting a thin surface like a rabbits ear, when Ignore Normal Slope is checked - both the back and front side of the ear will be affected by the brush."),
properties.isNormalIgnored
);
drawIgnoreNormalTooltip();
properties.isSelectionLocked = furCreator.toggle(
new GUIContent("Lock Selection", "Locks selection to the current selected object."),
properties.isSelectionLocked
);
properties.isGroomAllLayerAtOnce = furCreator.toggle(
new GUIContent("Groom All Layers Mode", "All Layer will be affected by the brush stroke. Not only the current active."),
properties.isGroomAllLayerAtOnce
);
if (properties.isMirrorMode) {
GUILayout.Space(PainterResetAndSmoothUI.DEFAULT_CHILD_VERTICAL_MARGIN);
if (mirrorPanelStyle == null) {
mirrorPanelStyle = new GUIStyle(GUI.skin.label) {
border = new RectOffset(20, 20, 20, 20),
padding = new RectOffset(15, 16, 15, 17),
normal = {background = Resources.Load<Texture2D>("bg_box_cyan")}
};
}
GUILayout.BeginVertical(mirrorPanelStyle);
}
var mirrorMode = furCreator.toggle(
new GUIContent("Mirror Mode", "Mirrors the strokes along x or z axis."),
properties.isMirrorMode
);
if (properties.isMirrorMode) {
GUILayout.Space(PainterResetAndSmoothUI.DEFAULT_CHILD_VERTICAL_MARGIN);
properties.mirrorAxisTab = fluffyToolbar.drawToolbar(
properties.mirrorAxisTab,
new[] {"Mirror X-Axis", "Mirror Z-Axis"},
furCreator
);
bindPoseHelper.updateBindPoseState(furCreator);
if (!bindPoseHelper.isInBindPose) {
GUILayout.Space(PainterResetAndSmoothUI.DEFAULT_CHILD_VERTICAL_MARGIN);
if (GUILayout.Button("Restore Bind Pose", buttonStyle)) {
bindPoseHelper.restoreBindPose(furCreator);
}
}
}
else {
EditorGUILayout.Space(PainterResetAndSmoothUI.DEFAULT_CHILD_VERTICAL_MARGIN);
if (helpStyle == null) helpStyle = getHelpTextStyle();
//Footer
GUILayout.Label(
"Opacity: Left mouse button + Shift\nSize: Left mouse button + " + ctrl + "\nFalloff: Left mouse button + Shift & " + ctrl,
helpStyle
);
}
if (properties.isMirrorMode) GUILayout.EndVertical();
properties.isMirrorMode = mirrorMode;
GUILayout.EndVertical();
EditorGUILayout.Space(PainterResetAndSmoothUI.DEFAULT_MARGIN_TOP);
}
}
private GUIStyle invisibleButtonStyle = new GUIStyle();
private void addIgnoreNormalTooltipClick() {
if (!hasIgnoreNormalBeenShown) {
var lastRect = GUILayoutUtility.GetLastRect();
if (GUI.Button(new Rect(lastRect.x, lastRect.y, 110, 50), "", invisibleButtonStyle)) {
Application.OpenURL("https://danielzeller427.gitbook.io/fluffy-grooming-tool/brush-properties-1/ingore-normal-slope");
hasIgnoreNormalBeenShown = true;
}
}
}
private readonly ImportantButton myObjectHelpText = new ImportantButton() {
positionRect = new Rect(),
resource = "learn_more_frame",
fillColor = new Color(0.0f, 0.0f, 0.0f, 0.85f),
isRippleEnabled = false,
clickAction = delegate { hasIgnoreNormalBeenShown = true; }
};
private EditorDeltaTime editorDeltaTime = new EditorDeltaTime();
private void drawIgnoreNormalTooltip() {
if (!hasIgnoreNormalBeenShown) {
var lastRect = GUILayoutUtility.GetLastRect();
myObjectHelpText.positionRect = new Rect(
lastRect.x,
lastRect.y - myObjectHelpText.getTextureHeight() / 2f,
myObjectHelpText.getTextureWidth() / 2f,
myObjectHelpText.getTextureHeight() / 2f
);
editorDeltaTime.Update();
myObjectHelpText.update(editorDeltaTime.deltaTime);
myObjectHelpText.draw();
if (learnMoreStyle == null)
learnMoreStyle = new GUIStyle(GUI.skin.label) {
alignment = TextAnchor.MiddleLeft
};
GUI.Label(new Rect(lastRect.x + 11, lastRect.y - 82, myObjectHelpText.getTextureWidth(), 50),
"This fella will be you best friend\nwhenever you can't reach some strands.", learnMoreStyle);
}
}
private FluffyToolbar fluffyToolbar = new FluffyToolbar();
private GUIStyle helpStyle;
private string getControlText() {
return Application.platform == RuntimePlatform.OSXEditor ? "Cmnd" : "Ctrl";
}
public float getFalloffBrushDiscSize(int type, PainterProperties p) {
if (type == (int) PaintType.ADD_FUR) return 0;
return p.brushFalloffPercentOfBrushSize * p.brushSize;
}
public float getBrushDiscSize(int type, PainterAddSpacingUI painterAddSpacingUI, PainterProperties p) {
if (type == (int) PaintType.ADD_FUR) return painterAddSpacingUI.paintDistanceBetweenStrands;
return p.brushSize;
}
public static GUIStyle getHelpTextStyle() {
GUIStyle style = new GUIStyle(EditorStyles.label);
style.fontSize = 10;
style.wordWrap = true;
return style;
}
void changeBrushSize(float deltaX, PainterProperties p) {
p.brushSize += deltaX * getChangeBrushSizeSpeed(p);
p.brushSize = Mathf.Clamp(p.brushSize, MIN_BRUSH_SIZE, MAX_BRUSH_SIZE);
p.brushFalloff = Mathf.Clamp(p.brushFalloffPercentOfBrushSize * p.brushSize, MIN_BRUSH_SIZE, p.brushSize);
}
private static float getChangeBrushSizeSpeed(PainterProperties painterProperties) {
float lerpRange = 0.165f - MIN_BRUSH_SIZE;
float lerpPercent = Mathf.Clamp01((painterProperties.brushSize - MIN_BRUSH_SIZE) / lerpRange);
return Mathf.Lerp(SMALL_CHANGE_BRUSH_SENSITIVITY, CHANGE_BRUSH_SENSITIVITY, lerpPercent);
}
void changeBrushOpacity(float deltaX, PainterProperties p) {
p.brushOpacity += deltaX * CHANGE_BRUSH_SENSITIVITY;
p.brushOpacity = Mathf.Clamp01(p.brushOpacity);
}
void changeFalloff(float deltaX, PainterProperties p) {
p.brushFalloffPercentOfBrushSize += deltaX * CHANGE_BRUSH_SENSITIVITY;
p.brushFalloffPercentOfBrushSize = Mathf.Clamp01(p.brushFalloffPercentOfBrushSize);
}
public float getCircleFillColor(PainterProperties p) {
return p.brushOpacity * BRUSH_FILL_OPACITY;
}
public bool handleBrushShortcuts(Event e, PainterProperties p) {
prevMousePosition ??= e.mousePosition;
var delta = e.mousePosition.x - ((Vector2) prevMousePosition).x;
prevMousePosition = e.mousePosition;
if (Mathf.Abs(delta) > 0 && isLeftMousePressed && e.getControlButton() && !e.shift) {
changeBrushSize(delta, p);
return true;
}
if (Mathf.Abs(delta) > 0 && isLeftMousePressed && !e.getControlButton() && e.shift) {
changeBrushOpacity(delta, p);
return true;
}
if (Mathf.Abs(delta) > 0 && isLeftMousePressed && e.getControlButton() && e.shift) {
changeFalloff(delta, p);
return true;
}
return false;
}
public void resetBindPoseHelper() {
bindPoseHelper.reset();
}
private Vector2? prevMousePosition;
internal bool isLeftMousePressed;
public void resetMouseMove(bool isPressed) {
prevMousePosition = null;
isLeftMousePressed = isPressed;
}
}
}