using UnityEngine;
using UnityEditor;
public class DiscreteColorPicker : EditorWindow
{
//Create and open a discrete color picker
[MenuItem(“Window/Open Discrete color picker”)]
public static void OpenDiscreteColorPicker()
{
DiscreteColorPicker windowInstance = ScriptableObject.CreateInstance<DiscreteColorPicker>();
windowInstance.Init();
windowInstance.Show();
}
int division = 1; //Base value to calculate amount of available colors
int colorAmount
{
//There are {pickerTextureHeight} base color to choose from, each generating
// a grid of {luminosityTextureSize}*{luminosityTextureSize} tints, with
// the last line being only black squares.
get { return pickerTextureHeight * luminosityTextureSize * luminosityTextureSize – luminosityTextureSize + 1; }
}
Color currentBaseColor; //Current selected base color
Vector2 currentLuminosityCoordinates; //Coordinates of selected tint in luminosity texture
float currentMainColorY = -1f; //Coordinate of selected base color
Texture2D pickerTexture; //Texture containing the base colors
Texture2D luminosityTexture; //Texture based on the current base color with luminosity variations
Texture2D cursorTexture; //Texture used to display cursors
Texture2D sampleColorTexture; //Texture containing final color
int pickerTextureHeight
{
get { return division * 6; }
}
int luminosityTextureSize
{
get { return division * 3; }
}
int pickerSize = 300; //GUI object size
bool draggingLuminosity; //Is user dragging from luminosity square
bool draggingColor; //Is user dragging from color picker
void OnGUI()
{
EditorGUILayout.LabelField(“Discrete color”);
EditorGUI.indentLevel++;
//First, a slider to decide the amount of colors
bool needsUpdate = false;
int newDivision = EditorGUILayout.IntSlider(“Color levels:”, division, 1, 30, GUILayout.MaxWidth(pickerSize));
if(newDivision != division)
{
//If division changed, update luminosity coordinates to
adapt to new grid
currentLuminosityCoordinates *= ((float) newDivision) / ((float) division);
division = newDivision;
//Then regenerate textures, since their size has changed
RegenerateTextures();
needsUpdate = true;
}
EditorGUILayout.LabelField(“=> “ + colorAmount + ” colors”);
GUILayout.Space(10);
//Reserve a rect in the windows, where we can draw with GUI functions, and that
// GUILayout functions will consider as already filled
Rect colorPickerRect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, pickerSize);
//Draw luminosity texture
Rect luminosityRect = new Rect(colorPickerRect);
luminosityRect.x += 5;
luminosityRect.width = pickerSize;
GUI.DrawTexture(luminosityRect, luminosityTexture, ScaleMode.StretchToFill, false, colorPickerRect.width/((float) luminosityTextureSize));
//Draw small black dot to show which color is selected
Rect luminosityCursorRect = new Rect(luminosityRect);
luminosityCursorRect.position += new Vector2((int) currentLuminosityCoordinates.x, (int) currentLuminosityCoordinates.y) * pickerSize / ((float) luminosityTextureSize);
luminosityCursorRect.y = luminosityRect.height – luminosityCursorRect.y + 2*luminosityRect.y;
luminosityCursorRect.position += new Vector2(0, -5f);
luminosityCursorRect.size = new Vector2(5, 5);
GUI.DrawTexture(luminosityCursorRect, cursorTexture);
//Draw color picker
colorPickerRect.x += luminosityRect.width + 10;
colorPickerRect.width = 20;
GUI.DrawTexture(colorPickerRect, pickerTexture);
//Draw small black line to show which color is selected
Rect colorPickCursorRect = new Rect(colorPickerRect);
colorPickCursorRect.y = colorPickerRect.y + currentMainColorY * colorPickerRect.height;
colorPickCursorRect.height = 1;
GUI.DrawTexture(colorPickCursorRect, cursorTexture);
//Check if user is beginning or ending a drag in one of the pickers
if(Event.current.type == EventType.MouseDown)
{
if(luminosityRect.Contains(Event.current.mousePosition))
{
draggingLuminosity = true;
}
else if(colorPickerRect.Contains(Event.current.mousePosition))
{
draggingColor = true;
}
else
{
draggingLuminosity = false;
draggingColor = false;
}
}
else if(Event.current.type == EventType.MouseUp)
{
draggingLuminosity = false;
draggingColor = false;
}
//Warning: OnGUI can be called for different event types; you only want to use some of them, since
// during others the layout is being constructed and values will be wrong
bool input = Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseDrag;
if(input)
{
Vector2 mousePosition = Event.current.mousePosition;
if(draggingLuminosity)
{
//Calculate new coordinates in luminosity texture
Restrain(ref mousePosition, luminosityRect);
Vector2 coordinates = mousePosition – luminosityRect.position;
coordinates *= luminosityTextureSize / luminosityRect.width;
currentLuminosityCoordinates = new Vector2((int) coordinates.x, luminosityTextureSize – 1 – ((int) coordinates.y));
needsUpdate = true;
}
else if(draggingColor)
{
//Calculate new base color and regenerate luminosity texture
Restrain(ref mousePosition, colorPickerRect);
currentMainColorY = (mousePosition.y – colorPickerRect.position.y) / colorPickerRect.height;
int textureY = (int) ((1f – currentMainColorY) * pickerTextureHeight);
currentBaseColor = pickerTexture.GetPixel(0, textureY);
RecalculateLuminosityTexture();
needsUpdate = true;
}
}
if(needsUpdate)
{
//A change has been made, sample texture has to be updated
sampleColorTexture.SetPixel(0, 0, luminosityTexture.GetPixel((int)
currentLuminosityCoordinates.x, (int) currentLuminosityCoordinates.y));
sampleColorTexture.Apply();
Repaint();
}
GUILayout.Space(10);
//Draw currently selected color to sample texture
Rect sampleColorRect = GUILayoutUtility.GetRect(1, 20);
sampleColorRect.x += 5;
sampleColorRect.width = pickerSize + 20;
GUI.DrawTexture(sampleColorRect, sampleColorTexture);
EditorGUI.indentLevel–;
}
//Simple helper function
void Restrain(ref Vector2 v, Rect r)
{
v.x = Mathf.Min(r.x + r.width – 1f, Mathf.Max(r.x + 1f, v.x));
v.y = Mathf.Min(r.y + r.height – 1f, Mathf.Max(r.y + 1f, v.y));
}
//Each time the base color changes, the luminosity texture has to be updated
void RecalculateLuminosityTexture()
{
Color rowColor;
Color rowGreyColor;
for(int i = 0; i < luminosityTextureSize; i++)
{
//Row by row, simple lerp of two vertical gradients (white to black on the left,
// base color to black on the right)
rowColor = currentBaseColor * ((float) (luminosityTextureSize – i – 1)) / ((float) (luminosityTextureSize – 1));
rowGreyColor = Color.white * ((float) (luminosityTextureSize – i – 1)) / ((float) (luminosityTextureSize – 1));
rowColor.a = 1;
rowGreyColor.a = 1;
for(int j = 0; j < luminosityTextureSize; j++)
{
luminosityTexture.SetPixel(j, luminosityTextureSize – i – 1, Color.Lerp(rowGreyColor, rowColor, ((float) j)/((float) (luminosityTextureSize – 1))));
}
}
//Don’t forget to call Apply after you change a texture, or it won’t
// have any effect
luminosityTexture.Apply();
}
//Regenerate all the texture, because they don’t exist yet or because
// their size was changed
void RegenerateTextures()
{
//Just create a texture and fill it with basic rainbow
// There must be a name for that, sorry I don’t know it
// (…and there must be a better way to do it, but it’s not the point)
pickerTexture = new Texture2D(1, pickerTextureHeight);
pickerTexture.filterMode = FilterMode.Point;
Color colorInProgress = Color.red;
float increment = 6f / ((float) pickerTextureHeight);
for(int i = 0; i < 6; i++)
{
for(int j = 0; j < pickerTextureHeight / 6; j++)
{
int index = i * pickerTextureHeight / 6 + j;
pickerTexture.SetPixel(0, index, colorInProgress);
//Grab current base color to setup cursor in color picker at init
if(colorInProgress == currentBaseColor && currentMainColorY < 0)
{
currentMainColorY = 1f – (1f / (2f * pickerTextureHeight) + ((float) index) / ((float) pickerTextureHeight));
}
switch(i)
{
case 0:
colorInProgress.g += increment;
break;
case 1:
colorInProgress.r -= increment;
break;
case 2:
colorInProgress.b += increment;
break;
case 3:
colorInProgress.g -= increment;
break;
case 4:
colorInProgress.r += increment;
break;
case 5:
colorInProgress.b -= increment;
break;
}
}
}
pickerTexture.Apply();
int textureY = (int) ((1f – currentMainColorY) * pickerTextureHeight);
currentBaseColor = pickerTexture.GetPixel(0, textureY);
luminosityTexture = new Texture2D(luminosityTextureSize, luminosityTextureSize);
luminosityTexture.filterMode = FilterMode.Point;
RecalculateLuminosityTexture();
sampleColorTexture = new Texture2D(1, 1);
sampleColorTexture.SetPixel(0, 0, luminosityTexture.GetPixel((int)
currentLuminosityCoordinates.x, (int) currentLuminosityCoordinates.y));
sampleColorTexture.Apply();
}
//Init default values and create cursor texture, since it won’t be updated after
void Init()
{
currentBaseColor = Color.red;
currentLuminosityCoordinates = new Vector2(luminosityTextureSize – 1, luminosityTextureSize – 1);
RegenerateTextures();
cursorTexture = new Texture2D(1, 1);
cursorTexture.SetPixel(0, 0, Color.black);
cursorTexture.Apply();
}
}
|