Adaug proiectul Unity

This commit is contained in:
2026-11-01 23:39:55 +02:00
parent cb14d9d37d
commit 863eac8f2c
23463 changed files with 5412601 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
*.psd filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text
*.blend filter=lfs diff=lfs merge=lfs -text
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
repository "My project 2026-11-01_15-52-52/Ill Omen@4672926770058@cloud"
path "/"
smartbranch "/main"
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
My project 2026-11-01_15-52-52_Ill Omen
9a93bac0-3e2d-4caf-952f-65f9fbf3aec5
Standard
+6
View File
@@ -0,0 +1,6 @@
{
"version": "1.0",
"components": [
"Microsoft.VisualStudio.Workload.ManagedGame"
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e7fef3b495f1b47428ec59beb59e14fb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5a4c20bf7fca7034bab3200890dd6e38
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+27
View File
@@ -0,0 +1,27 @@
## Palette Importing & Creation
A palette can be imported from the following file formats (simply drag the file into the asset folder somewhere):
* **.ase** — Adobe Photoshop ASE file
* **.gpl** — GIMP Palette file
* **.hex** — Hex color list
* **.txt** — Paint.NET palette format
* **.pal** — PAL file (supports both JASC and RPAL formats)
### Creating a Palette
You can create a palette in several ways:
* **Manually:**
Right-click in the Project window and select:
`Create → VNTG → Palette Asset`
* **From a Texture:**
Right-click on a texture and select:
`Create → VNTG → Convert Texture To Palette Asset`
### Palette Settings
Within a Palette Asset, you can adjust the **color distance metric** used to generate the LUT (Look-Up Table). This setting affects how colors are matched and applied.
The default metric is **CIE76**, but depending on your palette and desired visual style, other metrics may produce better results. Experimenting with different options is recommended.
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 37e4c4f84f8884f44a788611b54cf32e
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4a5832e93be44d44f84653d4f133ad5c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c8e0f9242c3aa7d4398bd44ff82b83b0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,765 @@
using ColbyO.VNTG.PSX.Editor;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: CRTSettingsEditor.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.CRT.Editor
{
[CustomEditor(typeof(CRTSettings))]
public class CRTSettingsEditor : VolumeComponentEditor
{
private static readonly string[] _subPixelModeOptions = new string[] { "None", "Shadow", "Grille", "Spot" };
private bool _showScreen = true;
private bool _showScreenShape = true;
private bool _showVignette = true;
private bool _showScanlines = true;
private bool _showNoise = true;
private bool _showVhs = true;
private bool _showTracking = true;
private bool _showGlitches = true;
private bool _showSignalInterference = true;
private bool _showChromaticAberration = true;
private bool _showSubpixels = true;
private bool _showUnsharp = true;
private bool _showClampColor = true;
private bool _showColorAdjustment = true;
// Settings
private SerializedDataParameter _Enabled;
private SerializedDataParameter _ShowInSceneView;
// Screen
private SerializedDataParameter _EnableScreenSettings;
private SerializedDataParameter _ScreenResolution;
private SerializedDataParameter _UseMaxFPS;
private SerializedDataParameter _RefreshRate;
private SerializedDataParameter _DecayRate;
private SerializedDataParameter _EnableInterlacedRendering;
// Screen Shape
private SerializedDataParameter _EnableScreenBend;
private SerializedDataParameter _ScreenBend;
// Vignette
private SerializedDataParameter _EnableVignette;
private SerializedDataParameter _ScreenRoundness;
private SerializedDataParameter _VignetteOpacity;
// Scanlines
private SerializedDataParameter _EnableScanlines;
private SerializedDataParameter _ScanLineVerticalOpacity;
private SerializedDataParameter _ScanLineHorizontalOpacity;
private SerializedDataParameter _ScanLineVerticalSpeed;
private SerializedDataParameter _ScanLineHorizontalSpeed;
private SerializedDataParameter _ScanLineStrength;
// Noise
private SerializedDataParameter _EnableNoise;
private SerializedDataParameter _NoiseScale;
private SerializedDataParameter _NoiseRBGOffsetX;
private SerializedDataParameter _NoiseRBGOffsetY;
private SerializedDataParameter _NoiseSpeed;
private SerializedDataParameter _NoiseFade;
// VHS
private SerializedDataParameter _EnableVhs;
private SerializedDataParameter _VhsSmear;
// Tracking
private SerializedDataParameter _EnableTrackerLine;
private SerializedDataParameter _TrackingSpeed;
private SerializedDataParameter _TrackingJitter;
// Glitches
private SerializedDataParameter _EnableGlitch;
private SerializedDataParameter _GlitchChance;
private SerializedDataParameter _GlitchLength;
// Signal Interference
private SerializedDataParameter _EnableSignalInterference;
private SerializedDataParameter _InterferenceFrequency;
private SerializedDataParameter _InterferenceAmplitude;
// Chromatic Aberration
private SerializedDataParameter _EnableChromaticAberration;
private SerializedDataParameter _ChromaticOffset;
private SerializedDataParameter _ChromaticOffsetSpeed;
// Subpixels
private SerializedDataParameter _EnableSubPixels;
private SerializedDataParameter _SubPixelMode;
private SerializedDataParameter _SubPixelDensity;
// Unsharp
private SerializedDataParameter _EnableUnsharp;
private SerializedDataParameter _UnsharpAmount;
private SerializedDataParameter _UnsharpRadius;
private SerializedDataParameter _UnsharpThreshold;
// Color Clamp
private SerializedDataParameter _EnableClampColor;
private SerializedDataParameter _ClampBlack;
private SerializedDataParameter _ClampWhite;
private SerializedDataParameter _ShadowTint;
// Color Adjustment
private SerializedDataParameter _EnableColorAdjustment;
private SerializedDataParameter _Gamma;
private SerializedDataParameter _Brightness;
private SerializedDataParameter _Contrast;
private SerializedDataParameter _Saturation;
private SerializedDataParameter _Hue;
private SerializedDataParameter _RedShift;
private SerializedDataParameter _BlueShift;
private SerializedDataParameter _GreenShift;
private SerializedDataParameter _IsMonochrome;
public override void OnEnable()
{
if (target == null || targets == null || targets.Length == 0) return;
base.OnEnable();
var o = new PropertyFetcher<CRTSettings>(serializedObject);
_Enabled = Unpack(o.Find(x => x.Enabled));
_ShowInSceneView = Unpack(o.Find(x => x.ShowInSceneView));
_EnableScreenSettings = Unpack(o.Find(x => x.EnableScreenSettings));
_ScreenResolution = Unpack(o.Find(x => x.ScreenResolution));
_UseMaxFPS = Unpack(o.Find(x => x.UseMaxFPS));
_RefreshRate = Unpack(o.Find(x => x.RefreshRate));
_DecayRate = Unpack(o.Find(x => x.DecayRate));
_EnableInterlacedRendering = Unpack(o.Find(x => x.EnableInterlacedRendering));
_EnableScreenBend = Unpack(o.Find(x => x.EnableScreenBend));
_ScreenBend = Unpack(o.Find(x => x.ScreenBend));
_EnableVignette = Unpack(o.Find(x => x.EnableVignette));
_VignetteOpacity = Unpack(o.Find(x => x.VignetteOpacity));
_ScreenRoundness = Unpack(o.Find(x => x.ScreenRoundness));
_EnableScanlines = Unpack(o.Find(x => x.EnableScanlines));
_ScanLineVerticalOpacity = Unpack(o.Find(x => x.ScanLineVerticalOpacity));
_ScanLineHorizontalOpacity = Unpack(o.Find(x => x.ScanLineHorizontalOpacity));
_ScanLineVerticalSpeed = Unpack(o.Find(x => x.ScanLineVerticalSpeed));
_ScanLineHorizontalSpeed = Unpack(o.Find(x => x.ScanLineHorizontalSpeed));
_ScanLineStrength = Unpack(o.Find(x => x.ScanLineStrength));
_EnableNoise = Unpack(o.Find(x => x.EnableNoise));
_NoiseScale = Unpack(o.Find(x => x.NoiseScale));
_NoiseRBGOffsetX = Unpack(o.Find(x => x.NoiseRBGOffsetX));
_NoiseRBGOffsetY = Unpack(o.Find(x => x.NoiseRBGOffsetY));
_NoiseSpeed = Unpack(o.Find(x => x.NoiseSpeed));
_NoiseFade = Unpack(o.Find(x => x.NoiseFade));
_EnableVhs = Unpack(o.Find(x => x.EnableVhs));
_VhsSmear = Unpack(o.Find(x => x.VhsSmear));
_EnableTrackerLine = Unpack(o.Find(x => x.EnableTrackerLine));
_TrackingSpeed = Unpack(o.Find(x => x.TrackingSpeed));
_TrackingJitter = Unpack(o.Find(x => x.TrackingJitter));
_EnableGlitch = Unpack(o.Find(x => x.EnableGlitch));
_GlitchChance = Unpack(o.Find(x => x.GlitchChance));
_GlitchLength = Unpack(o.Find(x => x.GlitchLength));
_EnableSignalInterference = Unpack(o.Find(x => x.EnableSignalInterference));
_InterferenceFrequency = Unpack(o.Find(x => x.InterferenceFrequency));
_InterferenceAmplitude = Unpack(o.Find(x => x.InterferenceAmplitude));
_EnableChromaticAberration = Unpack(o.Find(x => x.EnableChromaticAberration));
_ChromaticOffset = Unpack(o.Find(x => x.ChromaticOffset));
_ChromaticOffsetSpeed = Unpack(o.Find(x => x.ChromaticOffsetSpeed));
_EnableSubPixels = Unpack(o.Find(x => x.EnableSubPixels));
_SubPixelMode = Unpack(o.Find(x => x.SubPixelMode));
_SubPixelDensity = Unpack(o.Find(x => x.SubPixelDensity));
_EnableUnsharp = Unpack(o.Find(x => x.EnableUnsharp));
_UnsharpAmount = Unpack(o.Find(x => x.UnsharpAmount));
_UnsharpRadius = Unpack(o.Find(x => x.UnsharpRadius));
_UnsharpThreshold = Unpack(o.Find(x => x.UnsharpThreshold));
_EnableClampColor = Unpack(o.Find(x => x.EnableClampColor));
_ClampBlack = Unpack(o.Find(x => x.ClampBlack));
_ClampWhite = Unpack(o.Find(x => x.ClampWhite));
_ShadowTint = Unpack(o.Find(x => x.ShadowTint));
_EnableColorAdjustment = Unpack(o.Find(x => x.EnableColorAdjustment));
_Gamma = Unpack(o.Find(x => x.Gamma));
_Brightness = Unpack(o.Find(x => x.Brightness));
_Contrast = Unpack(o.Find(x => x.Contrast));
_Saturation = Unpack(o.Find(x => x.Saturation));
_Hue = Unpack(o.Find(x => x.Hue));
_RedShift = Unpack(o.Find(x => x.RedShift));
_BlueShift = Unpack(o.Find(x => x.BlueShift));
_GreenShift = Unpack(o.Find(x => x.GreenShift));
_IsMonochrome = Unpack(o.Find(x => x.IsMonochrome));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.BeginVertical();
DrawSettingsSection();
EditorGUILayout.EndVertical();
EditorGUILayout.Space(2);
_showScreen = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showScreen,
"Screen",
_EnableScreenSettings,
ResetScreenSection
);
if (_showScreen && _EnableScreenSettings.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawScreenSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showScreenShape = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showScreenShape,
"Screen Shape",
_EnableScreenBend,
ResetScreenShapeSection
);
if (_showScreenShape && _EnableScreenBend.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawScreenShapeSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showVignette = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showVignette,
"Vignette",
_EnableVignette,
ResetVignetteSection
);
if (_showVignette && _EnableVignette.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawVignetteSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showScanlines = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showScanlines,
"Scanlines",
_EnableScanlines,
ResetScanlinesSection
);
if (_showScanlines && _EnableScanlines.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawScanlinesSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showNoise = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showNoise,
"Noise",
_EnableNoise,
ResetNoiseSection
);
if (_showNoise && _EnableNoise.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawNoiseSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showVhs = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showVhs,
"VHS",
_EnableVhs,
ResetVhsSection
);
if (_showVhs && _EnableVhs.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawVhsSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showTracking = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showTracking,
"Tracking",
_EnableTrackerLine,
ResetTrackingSection
);
if (_showTracking && _EnableTrackerLine.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawTrackingSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showGlitches = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showGlitches,
"Glitches",
_EnableGlitch,
ResetGlitchesSection
);
if (_showGlitches && _EnableGlitch.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawGlitchesSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showSignalInterference = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showSignalInterference,
"Interference",
_EnableSignalInterference,
ResetSignalInterferenceSection
);
if (_showSignalInterference && _EnableSignalInterference.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawSignalInterferenceSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showSubpixels = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showChromaticAberration,
"Aberration",
_EnableChromaticAberration,
ResetChromaticAberrationSection
);
if (_showChromaticAberration && _EnableChromaticAberration.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawChromaticAberrationSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showSubpixels = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showSubpixels,
"Subpixels",
_EnableSubPixels,
ResetSubpixelsSection
);
if (_showSubpixels && _EnableSubPixels.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawSubpixelsSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showUnsharp = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showUnsharp,
"Unsharp",
_EnableUnsharp,
ResetUnsharpSection
);
if (_showUnsharp && _EnableUnsharp.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawUnsharpSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showClampColor = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showClampColor,
"Color Clamping",
_EnableClampColor,
ResetClampColorSection
);
if (_showClampColor && _EnableClampColor.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawClampColorSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showColorAdjustment = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(
_showColorAdjustment,
"Color Grading",
_EnableColorAdjustment,
ResetColorAdjustmentSection
);
if (_showColorAdjustment && _EnableColorAdjustment.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawColorAdjustmentSection();
EditorGUILayout.EndVertical();
}
serializedObject.ApplyModifiedProperties();
}
private void DrawSettingsSection()
{
EditorGUILayout.Space(4);
EditorGUILayout.BeginHorizontal();
VNTGEditorGUIUtils.DrawToggleButton(_Enabled, "Enabled", inline: true);
GUILayout.Space(10);
VNTGEditorGUIUtils.DrawToggleButton(_ShowInSceneView, "Scene View", inline: true);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(4);
}
private void ResetScreenSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_ScreenResolution.value.vector2Value = def.ScreenResolution.value;
_UseMaxFPS.value.boolValue = def.UseMaxFPS.value;
_RefreshRate.value.intValue = def.RefreshRate.value;
_DecayRate.value.floatValue = def.DecayRate.value;
_EnableInterlacedRendering.value.boolValue = def.EnableInterlacedRendering.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetScreenShapeSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_EnableScreenBend.value.boolValue = def.EnableScreenBend.value;
_ScreenBend.value.floatValue = def.ScreenBend.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetVignetteSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_VignetteOpacity.value.floatValue = def.VignetteOpacity.value;
_ScreenRoundness.value.floatValue = def.ScreenRoundness.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetScanlinesSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_ScanLineVerticalOpacity.value.floatValue = def.ScanLineVerticalOpacity.value;
_ScanLineHorizontalOpacity.value.floatValue = def.ScanLineHorizontalOpacity.value;
_ScanLineVerticalSpeed.value.floatValue = def.ScanLineVerticalSpeed.value;
_ScanLineHorizontalSpeed.value.floatValue = def.ScanLineVerticalSpeed.value;
_ScanLineStrength.value.floatValue = def.ScanLineStrength.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetNoiseSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_NoiseScale.value.floatValue = def.NoiseScale.value;
_NoiseRBGOffsetX.value.floatValue = def.NoiseRBGOffsetX.value;
_NoiseRBGOffsetY.value.floatValue = def.NoiseRBGOffsetY.value;
_NoiseSpeed.value.floatValue = def.NoiseSpeed.value;
_NoiseFade.value.floatValue = def.NoiseFade.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetVhsSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_VhsSmear.value.floatValue = def.VhsSmear.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetTrackingSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_EnableTrackerLine.value.boolValue = def.EnableTrackerLine.value;
_TrackingSpeed.value.floatValue = def.TrackingSpeed.value;
_TrackingJitter.value.floatValue = def.TrackingJitter.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetGlitchesSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_GlitchChance.value.floatValue = def.GlitchChance.value;
_GlitchLength.value.floatValue = def.GlitchLength.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetSignalInterferenceSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_EnableSignalInterference.value.boolValue = def.EnableSignalInterference.value;
_InterferenceFrequency.value.floatValue = def.InterferenceFrequency.value;
_InterferenceAmplitude.value.floatValue = def.InterferenceAmplitude.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetChromaticAberrationSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_ChromaticOffset.value.floatValue = def.ChromaticOffset.value;
_ChromaticOffsetSpeed.value.floatValue = def.ChromaticOffsetSpeed.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetSubpixelsSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_SubPixelMode.value.enumValueIndex = System.Convert.ToInt32(def.SubPixelMode.value);
_SubPixelDensity.value.floatValue = def.SubPixelDensity.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetUnsharpSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_UnsharpAmount.value.floatValue = def.UnsharpAmount.value;
_UnsharpRadius.value.floatValue = def.UnsharpRadius.value;
_UnsharpThreshold.value.floatValue = def.UnsharpThreshold.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetClampColorSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_ClampBlack.value.floatValue = def.ClampBlack.value;
_ClampWhite.value.floatValue = def.ClampWhite.value;
_ShadowTint.value.colorValue = def.ShadowTint.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetColorAdjustmentSection()
{
var def = ScriptableObject.CreateInstance<CRTSettings>();
_Gamma.value.floatValue = def.Gamma.value;
_Brightness.value.floatValue = def.Brightness.value;
_Contrast.value.floatValue = def.Contrast.value;
_Saturation.value.floatValue = def.Saturation.value;
_Hue.value.floatValue = def.Hue.value;
_RedShift.value.floatValue = def.RedShift.value;
_BlueShift.value.floatValue = def.BlueShift.value;
_GreenShift.value.floatValue = def.GreenShift.value;
_IsMonochrome.value.boolValue = def.IsMonochrome.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void DrawScreenSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawInlineVector2Param(_ScreenResolution);
VNTGEditorGUIUtils.DrawToggleButton(_UseMaxFPS, "Use Max FPS");
if (!_UseMaxFPS.value.boolValue)
{
EditorGUI.indentLevel++;
VNTGEditorGUIUtils.DrawClampedSlider(_RefreshRate, 0, 360);
EditorGUI.indentLevel--;
}
VNTGEditorGUIUtils.DrawClampedSlider(_DecayRate, 0f, 1f);
VNTGEditorGUIUtils.DrawToggleButton(_EnableInterlacedRendering, "Interlaced Rendering");
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawScreenShapeSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_ScreenBend, 0f, 100f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawVignetteSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_VignetteOpacity, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_ScreenRoundness, 0f, 1f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawScanlinesSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_ScanLineVerticalOpacity, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_ScanLineHorizontalOpacity, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_ScanLineVerticalSpeed, -1f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_ScanLineHorizontalSpeed, -1f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_ScanLineStrength, 0f, 1f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawNoiseSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_NoiseScale, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_NoiseRBGOffsetX, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_NoiseRBGOffsetY, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_NoiseSpeed, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_NoiseFade, 0f, 1f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawVhsSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_VhsSmear, 0.01f, 1f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawTrackingSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_TrackingSpeed, 1f, 20f);
VNTGEditorGUIUtils.DrawClampedSlider(_TrackingJitter, 0f, 50f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawGlitchesSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_GlitchChance, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_GlitchLength, 0f, 30f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawSignalInterferenceSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_InterferenceFrequency, 0f, 200f);
VNTGEditorGUIUtils.DrawClampedSlider(_InterferenceAmplitude, 0f, 1f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawChromaticAberrationSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_ChromaticOffset, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_ChromaticOffsetSpeed, 0f, 1f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawSubpixelsSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawEnumToolbarParameter<CRTSubPixelMode>(_SubPixelMode, "Sub Pixel Mode", _subPixelModeOptions);
VNTGEditorGUIUtils.DrawClampedSlider(_SubPixelDensity, 100f, 400f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawUnsharpSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_UnsharpAmount, 0f, 5f);
VNTGEditorGUIUtils.DrawClampedSlider(_UnsharpRadius, 0f, 5f);
VNTGEditorGUIUtils.DrawClampedSlider(_UnsharpThreshold, 0f, 0.5f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawClampColorSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_ClampBlack, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_ClampWhite, 0f, 1f);
VNTGEditorGUIUtils.DrawParamField(_ShadowTint);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawColorAdjustmentSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_Gamma, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_Brightness, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_Contrast, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_Saturation, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_Hue, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_RedShift, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_BlueShift, 0f, 1f);
VNTGEditorGUIUtils.DrawClampedSlider(_GreenShift, 0f, 1f);
VNTGEditorGUIUtils.DrawToggleButton(_IsMonochrome, "Is Monochrome");
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3d6c8eeb43adca9448bbfa45c8186d08
@@ -0,0 +1,21 @@
{
"name": "ColbyO.VNTG.Editor",
"rootNamespace": "ColbyO.VNTG.Editor",
"references": [
"GUID:15fc0a57446b3144c949da3e2b9737a9",
"GUID:df380645f10b7bc4b97d4f5eb6303d95",
"GUID:3eae0364be2026648bf74846acb8a731",
"GUID:2973bd4f1dd86854c9dd22ea5ef314a6"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: cc239eaf481676f428b673aca21d4bbb
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e4ec80f4fbcbdf409d6b0cf5378a801
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 695c13ebaf99b7e4db2ae2779f931d11
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,161 @@
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: ColorControlCache.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class ColorControlCache
{
private ColorCache _cache = new ColorCache();
public void Clear()
{
_cache.LastColor = new Color(-1f, -1f, -1f, -1f);
_cache.HasCachedData = false;
}
public void GetHSV(Color inputColor, out float h, out float s, out float v)
{
if (_cache.HasCachedData && ColorsMatch(inputColor, _cache.LastColor))
{
h = _cache.Hue;
s = _cache.Saturation;
v = _cache.Value;
return;
}
Color.RGBToHSV(inputColor, out h, out s, out v);
_cache.Hue = h;
_cache.Saturation = s;
_cache.Value = v;
_cache.LastColor = inputColor;
_cache.HasCachedData = true;
}
public void UpdateHSV(float h, float s, float v, Color resultingColor)
{
_cache.Hue = h;
_cache.Saturation = s;
_cache.Value = v;
_cache.LastColor = resultingColor;
_cache.HasCachedData = true;
}
public void GetHSL(Color inputColor, out float h, out float s, out float l)
{
if (_cache.HasCachedData && ColorsMatch(inputColor, _cache.LastColor))
{
h = _cache.HslHue;
s = _cache.HslSaturation;
l = _cache.Ligntness;
return;
}
inputColor.RGBToHSL(out h, out s, out l);
_cache.HslHue = h;
_cache.HslSaturation = s;
_cache.Ligntness = l;
_cache.LastColor = inputColor;
_cache.HasCachedData = true;
}
public void UpdateHSL(float h, float s, float l, Color resultingColor)
{
_cache.HslHue = h;
_cache.HslSaturation = s;
_cache.Ligntness = l;
_cache.LastColor = resultingColor;
_cache.HasCachedData = true;
}
public void GetLab(Color inputColor, out float l, out float a, out float b)
{
if (_cache.HasCachedData && ColorsMatch(inputColor, _cache.LastColor))
{
l = _cache.LabL;
a = _cache.LabA;
b = _cache.LabB;
return;
}
inputColor.RGBToLab(out l, out a, out b);
_cache.LabL = l;
_cache.LabA = a;
_cache.LabB = b;
_cache.LastColor = inputColor;
_cache.HasCachedData = true;
}
public void UpdateLab(float l, float a, float b, Color resultingColor)
{
_cache.LabL = l;
_cache.LabA = a;
_cache.LabB = b;
_cache.LastColor = resultingColor;
_cache.HasCachedData = true;
}
public void GetOklab(Color inputColor, out float l, out float a, out float b)
{
if (_cache.HasCachedData && ColorsMatch(inputColor, _cache.LastColor))
{
l = _cache.OkLabL;
a = _cache.OkLabA;
b = _cache.OkLabB;
return;
}
inputColor.RGBToOklab(out l, out a, out b);
_cache.OkLabL = l;
_cache.OkLabA = a;
_cache.OkLabB = b;
_cache.LastColor = inputColor;
_cache.HasCachedData = true;
}
public void UpdateOklab(float l, float a, float b, Color resultingColor)
{
_cache.OkLabL = l;
_cache.OkLabA = a;
_cache.OkLabB = b;
_cache.LastColor = resultingColor;
_cache.HasCachedData = true;
}
private bool ColorsMatch(Color c1, Color c2)
{
return Mathf.Abs(c1.r - c2.r) < 0.001f &&
Mathf.Abs(c1.g - c2.g) < 0.001f &&
Mathf.Abs(c1.b - c2.b) < 0.001f &&
Mathf.Abs(c1.a - c2.a) < 0.001f;
}
private struct ColorCache
{
public Color LastColor;
public bool HasCachedData;
public float Hue;
public float Saturation;
public float Value;
public float HslHue;
public float HslSaturation;
public float Ligntness;
public float LabL;
public float LabA;
public float LabB;
public float OkLabL;
public float OkLabA;
public float OkLabB;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f2aa9d4a88a2bfc48880b4580a290d36
@@ -0,0 +1,15 @@
//-----------------------------------------------------------------------
// Author: Colby-O
// File: ColorFieldsMode.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public enum ColorFieldsMode
{
RGB,
HSV,
HSL,
LAB,
Hex
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ed186460ee411b548998a483bd40379f
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 284399d1564781d4fbaaf01d357be17b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,110 @@
using System.Collections.Generic;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: ColorWheelCache.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class ColorWheelCache
{
private readonly List<Vector3> _hsvCache = new List<Vector3>();
private readonly List<Color> _rgbCache = new List<Color>();
public int CachedCount => _hsvCache.Count;
public Vector3 GetHSV(int index) => _hsvCache[index];
public Color GetRGB(int index) => _rgbCache[index];
public void SetHSV(int index, Vector3 value)
{
_hsvCache[index] = value;
_rgbCache[index] = Color.HSVToRGB(value.x, value.y, value.z);
}
public void Sync(List<Color> colors)
{
if (colors == null) return;
if (_hsvCache.Count != colors.Count)
{
_hsvCache.Clear();
_rgbCache.Clear();
for (int i = 0; i < colors.Count; i++)
{
Color col = colors[i];
Color.RGBToHSV(col, out float h, out float s, out float v);
_hsvCache.Add(new Vector3(h, s, v));
_rgbCache.Add(col);
}
return;
}
for (int i = 0; i < colors.Count; i++)
{
Color currentRGB = colors[i];
if (currentRGB != _rgbCache[i])
{
Color.RGBToHSV(currentRGB, out float h, out float s, out float v);
if (v == 0)
{
h = _hsvCache[i].x;
s = _hsvCache[i].y;
}
else if (s == 0)
{
h = _hsvCache[i].x;
}
_hsvCache[i] = new Vector3(h, s, v);
_rgbCache[i] = currentRGB;
}
}
}
public Vector2 GetWheelPosition(int index, Rect targetRect, bool isValueWheel)
{
Vector3 hsv = _hsvCache[index];
float angle = hsv.x * 2.0f * Mathf.PI;
float magnitude = isValueWheel ? hsv.z : hsv.y;
float radius = magnitude * ((targetRect.width * 0.5f) - 1f);
return targetRect.center + new Vector2(Mathf.Cos(angle) * radius, -Mathf.Sin(angle) * radius);
}
public bool TryFindMarkerAtPosition(
Vector2 mousePos,
Rect targetRect,
bool isValueWheel,
int selectedIndex,
float radius,
out int foundIndex
)
{
if (selectedIndex >= 0 && selectedIndex < _hsvCache.Count)
{
if (Vector2.Distance(mousePos, GetWheelPosition(selectedIndex, targetRect, isValueWheel)) <= radius)
{
foundIndex = selectedIndex;
return true;
}
}
for (int i = 0; i < _hsvCache.Count; i++)
{
if (i == selectedIndex) continue;
if (Vector2.Distance(mousePos, GetWheelPosition(i, targetRect, isValueWheel)) <= radius)
{
foundIndex = i;
return true;
}
}
foundIndex = -1;
return false;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: aa7177c2f5b1fad409a0703dabd9f9fa
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 52a15faa6dbaeae48bb49b90a9ce14df
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,532 @@
using System;
using System.Collections.Generic;
using System.IO;
using Unity.Collections;
using UnityEditor;
using UnityEditor.AssetImporters;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PaletteEditor.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
[CustomEditor(typeof(Palette))]
public class PaletteEditor : UnityEditor.Editor
{
#if UNITY_6000_4_OR_NEWER
private static readonly HashSet<EntityId> PendingAutoBakes = new HashSet<EntityId>();
#else
private static readonly HashSet<int> PendingAutoBakes = new HashSet<int>();
#endif
private VisualElement _root;
private UIPaletteColorPicker _colorWheelUI;
private UIColorControlElement _colorControlUI;
private UIPaletteGridElement _paletteGridUI;
private UIPaletteView _paletteViewUI;
private Palette _palette;
private string _initialDiskSnapshotStr = string.Empty;
private bool _hasUnbakedChanges;
private bool _isBaking;
private Button _bakeButton;
private Button _revertButton;
private Button _exportButton;
private SelectionState _masterSelectedColorsList = new SelectionState();
public void OnEnable()
{
_palette = target as Palette;
if (_palette == null) return;
for (int i = 0; i < _palette.colors.Count; i++)
{
_masterSelectedColorsList.Add(i);
}
Undo.undoRedoPerformed += OnUndoRedoPerformed;
if (!CheckIfImportedPalette(_palette))
{
EditorApplication.delayCall += () =>
{
if (_palette != null && !CheckIfImportedPalette(_palette))
{
CheckAndBakeMissingLUT(_palette);
CachePristineState(_palette);
_hasUnbakedChanges = false;
}
};
}
CachePristineState(_palette);
}
public void OnDisable()
{
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
_colorWheelUI?.Cleanup();
_colorControlUI?.Cleanup();
if (_palette != null && _hasUnbakedChanges && !_isBaking)
{
#if UNITY_6000_4_OR_NEWER
EntityId paletteId = _palette.GetEntityId();
#else
int paletteId = _palette.GetInstanceID();
#endif
if (!PendingAutoBakes.Contains(paletteId))
{
PendingAutoBakes.Add(paletteId);
_hasUnbakedChanges = false;
var targetPalette = _palette;
EditorApplication.delayCall += () =>
{
PendingAutoBakes.Remove(paletteId);
if (targetPalette != null)
{
BakeInternalForPalette(targetPalette);
}
};
}
}
}
public override VisualElement CreateInspectorGUI()
{
_root = new VisualElement();
if (_palette == null) return _root;
if (CheckIfImportedPalette(_palette))
{
_root.Add(new IMGUIContainer(() => DrawDefaultInspector()));
return _root;
}
#if !UNITY_6000_3_OR_NEWER
// Some part of the UIToolkit is causing the color wheel to break on Unity version 6.2 or lower (unsupported feature).
BuildHeaderAndProperties();
var colorsProp = serializedObject.FindProperty("colors");
if (colorsProp != null)
{
var propertyField = new PropertyField(colorsProp);
propertyField.style.marginBottom = 15;
propertyField.Bind(serializedObject);
_root.TrackPropertyValue(colorsProp, (prop) =>
{
serializedObject.ApplyModifiedProperties();
EvaluateChangeState();
});
_root.Add(propertyField);
}
BuildControlFooter();
BuildPreview();
return _root;
#else
BuildHeaderAndProperties();
InitializeSubElements();
BindEventPipelines();
BuildControlFooter();
BuildPreview();
return _root;
#endif
}
private void BuildHeaderAndProperties()
{
var distanceMetricProp = serializedObject.FindProperty("distanceMetric");
if (distanceMetricProp != null)
{
var propertyField = new PropertyField(distanceMetricProp);
propertyField.style.marginBottom = 15;
propertyField.Bind(serializedObject);
propertyField.RegisterValueChangeCallback((evt) =>
{
serializedObject.ApplyModifiedProperties();
EvaluateChangeState();
if (_hasUnbakedChanges)
{
BakeInternal();
}
});
_root.Add(propertyField);
}
}
private void InitializeSubElements()
{
_colorWheelUI = new UIPaletteColorPicker { Selected = _masterSelectedColorsList };
_colorWheelUI.BindPalette(_palette);
_root.Add(_colorWheelUI);
_colorControlUI = new UIColorControlElement();
_colorControlUI.BindPalette(_palette, _masterSelectedColorsList.GetPrimarySelection());
_root.Add(_colorControlUI);
_paletteGridUI = new UIPaletteGridElement();
_paletteGridUI.BindPalette(_palette, _masterSelectedColorsList);
_root.Add(_paletteGridUI);
}
private void BindEventPipelines()
{
_paletteGridUI.OnSelectionChanged += (newIndex) =>
{
                _colorWheelUI.RefreshCacheAndTextures();
_colorControlUI.BindPalette(_palette, newIndex);
};
_paletteGridUI.OnSwatchHoverChanged += (hoveredIndex) =>
{
if (_colorWheelUI.HoveringIndex != hoveredIndex)
{
_colorWheelUI.HoveringIndex = hoveredIndex;
_colorWheelUI.MarkDirtyRepaint();
}
};
_paletteGridUI.OnPaletteUpdated += () => SyncStateChanges(refreshWheel: true, refreshGrid: false, refreshControls: true);
_colorWheelUI.OnPaletteUpdated += () => SyncStateChanges(refreshWheel: false, refreshGrid: true, refreshControls: true);
_colorControlUI.OnPaletteUpdated += () => SyncStateChanges(refreshWheel: true, refreshGrid: true, refreshControls: false);
}
private void SyncStateChanges(bool refreshWheel, bool refreshGrid, bool refreshControls)
{
serializedObject.Update();
EvaluateChangeState();
if (refreshWheel) _colorWheelUI.RefreshCacheAndTextures();
if (refreshGrid) _paletteGridUI.BindPalette(_palette, _masterSelectedColorsList);
if (refreshControls) _colorControlUI.BindPalette(_palette, _masterSelectedColorsList.GetPrimarySelection());
}
private void BuildControlFooter()
{
_bakeButton = new Button(BakePaletteData)
{
text = "Bake Palette",
style = { height = 30, marginTop = 15 }
};
_root.Add(_bakeButton);
_revertButton = new Button(RevertPaletteData)
{
text = "Revert Palette",
style = { height = 30, marginTop = 4, backgroundColor = new Color(0.75f, 0.35f, 0.35f, 1f) }
};
_root.Add(_revertButton);
_exportButton = new Button(ExportPaletteData)
{
text = "Export Palette",
style = { height = 30, marginTop = 4 }
};
_root.Add(_exportButton);
UpdateActionButtonVisuals();
}
private void BuildPreview()
{
VisualElement divider = new VisualElement();
divider.style.height = 1;
divider.style.backgroundColor = new Color(0.2f, 0.2f, 0.2f, 1f);
divider.style.marginTop = 8;
divider.style.marginBottom = 8;
_root.Add(divider);
_paletteViewUI = new UIPaletteView();
_paletteViewUI.BindPaletteContext(_palette);
_root.Add(_paletteViewUI);
}
private void ExportPaletteData()
{
if (_palette == null || _palette.colors == null || _palette.colors.Count == 0)
{
EditorUtility.DisplayDialog("Export Error", "The current palette contains no color data to export.", "OK");
return;
}
GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent("Raw Hex Text (*.hex)"), false, () => OpenSavePanel("hex"));
menu.AddItem(new GUIContent("Paint.NET Palette (*.txt)"), false, () => OpenSavePanel("txt"));
menu.AddItem(new GUIContent("GIMP Palette (*.gpl)"), false, () => OpenSavePanel("gpl"));
menu.AddItem(new GUIContent("Adobe Swatch Exchange (*.ase)"), false, () => OpenSavePanel("ase"));
menu.AddItem(new GUIContent("JASC PAL (*.pal)"), false, () => OpenSavePanel("pal"));
menu.ShowAsContext();
}
private void OpenSavePanel(string extension)
{
string chosenPath = EditorUtility.SaveFilePanel(
$"Export Palette as {extension.ToUpper()}",
Application.dataPath,
_palette.name,
extension
);
if (string.IsNullOrEmpty(chosenPath)) return;
try
{
switch (extension)
{
case "hex":
PaletteWriter.WriteHex(chosenPath, _palette.colors);
break;
case "txt":
PaletteWriter.WritePaintNet(chosenPath, _palette.name, _palette.colors);
break;
case "gpl":
PaletteWriter.WriteGpl(chosenPath, _palette.name, _palette.colors);
break;
case "ase":
PaletteWriter.WriteAse(chosenPath, _palette.colors);
break;
case "pal":
PaletteWriter.WriteJascPal(chosenPath, _palette.colors);
break;
}
Debug.Log($"<color=#5fc976><b>[VNTG]</b></color> Successfully exported palette down to: {chosenPath}");
}
catch (Exception ex)
{
EditorUtility.DisplayDialog("Export Failed", $"An error occurred while saving the palette:\n{ex.Message}", "OK");
}
}
private void EvaluateChangeState()
{
string currentStateStr = JsonUtility.ToJson(_palette);
_hasUnbakedChanges = (currentStateStr != _initialDiskSnapshotStr);
UpdateActionButtonVisuals();
}
private void UpdateActionButtonVisuals()
{
if (_bakeButton == null || _revertButton == null) return;
if (_hasUnbakedChanges)
{
_bakeButton.text = "Bake Palette (Unsaved Changes)";
_bakeButton.style.backgroundColor = new Color(0.35f, 0.75f, 0.35f, 1f);
_revertButton.style.display = DisplayStyle.Flex;
}
else
{
_bakeButton.text = "Bake Palette";
_bakeButton.style.backgroundColor = StyleKeyword.Null;
_revertButton.style.display = DisplayStyle.None;
}
}
private void BakePaletteData()
{
if (_palette == null) return;
BakeInternalForPalette(_palette);
UpdateActionButtonVisuals();
}
private void BakeInternal()
{
BakeInternalForPalette(_palette);
}
private void BakeInternalForPalette(Palette palette)
{
if (_isBaking || palette == null)
return;
try
{
_isBaking = true;
_hasUnbakedChanges = false;
EditorUtility.SetDirty(palette);
LUTBaker.Rebake(palette);
EditorApplication.delayCall += () =>
{
if (palette != null)
{
AssetDatabase.SaveAssetIfDirty(palette);
}
};
CachePristineState(palette);
UpdateActionButtonVisuals();
}
finally
{
_isBaking = false;
}
}
private void RevertPaletteData()
{
if (_palette == null) return;
Undo.RegisterCompleteObjectUndo(_palette, "Revert Palette");
JsonUtility.FromJsonOverwrite(_initialDiskSnapshotStr, _palette);
BakeInternal();
CachePristineState(_palette);
_hasUnbakedChanges = false;
serializedObject.Update();
RefreshInspectorLayout();
UpdateActionButtonVisuals();
}
private void RefreshInspectorLayout()
{
if (_palette == null) return;
int activeIndex = _colorWheelUI != null ? _colorWheelUI.Selected.GetPrimarySelection() : 0;
if (activeIndex >= _palette.colors.Count) activeIndex = _palette.colors.Count - 1;
_colorWheelUI?.RefreshCacheAndTextures();
_colorWheelUI?.MarkDirtyRepaint();
_paletteGridUI?.BindPalette(_palette, _masterSelectedColorsList);
_colorControlUI?.BindPalette(_palette, _masterSelectedColorsList.GetPrimarySelection());
}
private void OnUndoRedoPerformed()
{
EditorApplication.delayCall += () =>
{
if (_palette == null) return;
serializedObject.Update();
EvaluateChangeState();
RefreshInspectorLayout();
};
}
private void CachePristineState(Palette palette)
{
if (palette != null)
{
_initialDiskSnapshotStr = JsonUtility.ToJson(palette);
}
}
private bool CheckIfImportedPalette(Palette palette)
{
string assetPath = AssetDatabase.GetAssetPath(palette);
if (string.IsNullOrEmpty(assetPath)) return false;
string ext = Path.GetExtension(assetPath).ToLower().Replace(".", "");
AssetImporter importer = AssetImporter.GetAtPath(assetPath);
return AssetDatabase.IsSubAsset(palette) || importer is ScriptedImporter || ext != "asset";
}
private void CheckAndBakeMissingLUT(Palette palette)
{
string path = AssetDatabase.GetAssetPath(palette);
if ((!string.IsNullOrEmpty(path) || EditorUtility.IsPersistent(palette)) && !palette.HasLUT())
{
LUTBaker.Rebake(palette);
}
}
public override Texture2D RenderStaticPreview(string assetPath, UnityEngine.Object[] subAssets, int width, int height)
{
if (target is not Palette palette) return null;
IconSettings iconSettings = new IconSettings(width, height);
int totalPixels = width * height;
Texture2D previewTexture = new Texture2D(width, height, TextureFormat.RGBA32, false);
NativeArray<Color32> previewPixelBuffer = new NativeArray<Color32>(totalPixels, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
Vector2 center = new Vector2(width, height) * 0.5f;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int index = y * width + x;
Vector2 delta = new Vector2(x + 0.5f, y + 0.5f) - center;
float distFromCenter = delta.magnitude;
float sdf = TextureGenerationUtility.ChamferedRectSDF(
delta,
iconSettings.CardHalfSize,
iconSettings.CardCornerRadius,
iconSettings.CardFoldSize
);
if (sdf <= 0)
{
previewPixelBuffer[index] = (sdf > -iconSettings.OutlineSize)
? iconSettings.OutlineColor
: TextureGenerationUtility.SamplePreviewColorWheel(
delta, distFromCenter, iconSettings.WheelRadius, iconSettings.OutlineSize,
iconSettings.OutlineColor, iconSettings.FileColor, palette.colors
);
}
else
{
previewPixelBuffer[index] = iconSettings.BackgroundColor;
}
}
}
previewTexture.SetPixelData(previewPixelBuffer, 0);
previewPixelBuffer.Dispose();
previewTexture.Apply();
return previewTexture;
}
private struct IconSettings
{
public Color FileColor;
public Color OutlineColor;
public Color BackgroundColor;
public float OutlineSize;
public float WheelRadius;
public Vector2 CardHalfSize;
public float CardCornerRadius;
public float CardFoldSize;
public IconSettings(int width, int height)
{
FileColor = new Color(0.2f, 0.2f, 0.2f, 1f);
OutlineColor = new Color(0.1f, 0.1f, 0.1f, 1f);
BackgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
OutlineSize = 2.5f;
WheelRadius = width * 0.28f;
CardHalfSize = new Vector2(width * 0.38f, height * 0.42f);
CardCornerRadius = width * 0.06f;
CardFoldSize = width * 0.22f;
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fc3c7678442325a48ae390392b08e6b1
@@ -0,0 +1,69 @@
using System.IO;
using UnityEditor;
using UnityEditor.AssetImporters;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PaletteImporterEditor.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
[CustomEditor(typeof(PaletteImporter))]
public class PaletteImporterEditor : ScriptedImporterEditor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
EditorGUILayout.Space(15);
if (GUILayout.Button("Extract to Editable Palette", GUILayout.Height(30)))
{
ExtractToStandaloneAsset();
}
ApplyRevertGUI();
}
private void ExtractToStandaloneAsset()
{
ScriptedImporter importer = target as ScriptedImporter;
if (importer == null) return;
Palette sourceAsset = AssetDatabase.LoadAssetAtPath<Palette>(importer.assetPath);
if (sourceAsset == null)
{
Debug.LogError("Could not find a valid Palette to duplicate.");
return;
}
string directory = Path.GetDirectoryName(importer.assetPath);
string fileName = Path.GetFileNameWithoutExtension(importer.assetPath);
string targetPath = Path.Combine(directory, $"{fileName}_Clone.asset");
targetPath = AssetDatabase.GenerateUniqueAssetPath(targetPath);
Palette editableClone = Instantiate(sourceAsset);
AssetDatabase.CreateAsset(editableClone, targetPath);
if (sourceAsset.LUT != null)
{
Texture3D lutClone = Instantiate(sourceAsset.LUT);
lutClone.name = sourceAsset.LUT.name;
AssetDatabase.AddObjectToAsset(lutClone, editableClone);
editableClone.UpdateLUT(lutClone, sourceAsset.RawLUT);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
ProjectWindowUtil.ShowCreatedAsset(editableClone);
Debug.Log($"Successfully created editable asset at: {targetPath}");
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e18fa9c4af72d6b4c9598357237ebf97
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e8b2c7a33ebc91942815accb3d6ef09f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,33 @@
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: GUIHelpers.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public static class GUIHelpers
{
public static Rect PadRect
(
Rect rect,
float left,
float right,
float top,
float bottom
)
{
return new Rect(
rect.x + left,
rect.y + top,
rect.width - left - right,
rect.height - top - bottom
);
}
public static Rect PadRect(Rect rect, float padding)
{
return PadRect(rect, padding, padding, padding, padding);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 34d24a3c5535ee7409545930fdfa3202
@@ -0,0 +1,150 @@
using UnityEngine;
using System.Collections.Generic;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: TextureGenerationUtility.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public static class TextureGenerationUtility
{
public static float GetAngleAt(Vector2 delta)
{
float angle = Mathf.Atan2(delta.y, delta.x) * Mathf.Rad2Deg;
if (angle < 0) angle += 360.0f;
return angle;
}
public static Color GetWheelColorAt(Vector2 delta, float saturation = 0f, float brightness = 1f, List<Color> palette = null)
{
float angle = GetAngleAt(delta);
if (palette != null && palette.Count > 0)
{
float sliceSize = 360.0f / palette.Count;
int colorIndex = Mathf.FloorToInt(angle / sliceSize);
colorIndex = Mathf.Clamp(colorIndex, 0, palette.Count - 1);
Color paletteColor = palette[colorIndex];
paletteColor.a = 1.0f;
return paletteColor;
}
return Color.HSVToRGB(angle / 360.0f, saturation, brightness);
}
private static Color GetSliceColor(Vector2 delta, float distFromCenter, List<Color> palette)
{
if (palette == null || palette.Count <= 1)
{
return GetWheelColorAt(delta, 0.6f, 0.7f, palette);
}
float angle = GetAngleAt(delta);
float sliceSize = 360.0f / palette.Count;
float angularPixelWidth = (1.0f / (distFromCenter + 0.001f)) * Mathf.Rad2Deg;
float remainder = angle % sliceSize;
float distToEdge = Mathf.Min(remainder, sliceSize - remainder);
if (distToEdge < angularPixelWidth * 0.5f)
{
float t = Mathf.Clamp01((distToEdge + (angularPixelWidth * 0.5f)) / angularPixelWidth);
int currentIndex = Mathf.FloorToInt(angle / sliceSize) % palette.Count;
int neighborIndex = (remainder < sliceSize * 0.5f)
? (currentIndex - 1 + palette.Count) % palette.Count
: (currentIndex + 1) % palette.Count;
Color c1 = palette[neighborIndex];
Color c2 = palette[currentIndex];
c1.a = 1.0f;
c2.a = 1.0f;
return Color.Lerp(c1, c2, t);
}
return GetWheelColorAt(delta, 0.6f, 0.7f, palette);
}
public static void GenerateColorWheel(Color32[] buffer, int width, int height, float brightness = 1f)
{
Vector2 center = new Vector2(width, height) * 0.5f;
float radius = (Mathf.Min(width, height) * 0.5f) - 1f;
float aaWidth = 1.0f;
float halfAA = aaWidth * 0.5f;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int index = y * width + x;
Vector2 delta = new Vector2(x, y) - center;
float dist = delta.magnitude;
if (dist > radius + halfAA)
{
buffer[index] = new Color32(0, 0, 0, 0);
}
else
{
Color sampledColor = GetWheelColorAt(delta, Mathf.Clamp01(dist / radius), brightness);
if (dist > radius - halfAA)
{
float alphaCoverage = Mathf.Clamp01((radius + halfAA - dist) / aaWidth);
sampledColor.a *= alphaCoverage;
}
buffer[index] = sampledColor;
}
}
}
}
public static Color SamplePreviewColorWheel
(
Vector2 delta,
float distFromCenter,
float wheelRadius,
float outlineSize,
Color outlineColor,
Color fallbackColor,
List<Color> palette = null
)
{
float aaWidth = 1.0f;
float innerEdge = wheelRadius;
float outerEdge = wheelRadius + outlineSize;
if (distFromCenter >= outerEdge + aaWidth) return fallbackColor;
Color wheelColor = GetSliceColor(delta, distFromCenter, palette);
float toOutline = Mathf.Clamp01((distFromCenter - (innerEdge - aaWidth * 0.5f)) / aaWidth);
float toBackground = Mathf.Clamp01((distFromCenter - (outerEdge - aaWidth * 0.5f)) / aaWidth);
Color mixedColor = Color.Lerp(wheelColor, outlineColor, toOutline);
return Color.Lerp(mixedColor, fallbackColor, toBackground);
}
public static float ChamferedRectSDF(Vector2 p, Vector2 size, float radius, float cutSize)
{
float absX = Mathf.Abs(p.x);
float absY = Mathf.Abs(p.y);
float dx = Mathf.Max(absX - size.x + radius, 0.0f);
float dy = Mathf.Max(absY - size.y + radius, 0.0f);
float boxSDF = Mathf.Sqrt(dx * dx + dy * dy) - radius;
float cutPlane = (p.x - (size.x - cutSize)) + (p.y - (size.y - cutSize)) - cutSize;
return Mathf.Max(boxSDF, cutPlane);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: df9eb207e8e0de4429847db025be98cb
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 398c90a01abbff440af85fd479059a88
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PaintNetTextPalettePostprocessor.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class PaintNetTextPalettePostprocessor : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (string assetPath in importedAssets)
{
if (!assetPath.EndsWith(".txt", StringComparison.OrdinalIgnoreCase)) continue;
try
{
using (StreamReader reader = new StreamReader(assetPath))
{
string firstLine = reader.ReadLine();
if (firstLine != null && firstLine.Trim().StartsWith(";paint.net Palette File"))
{
ConvertTxtToPaletteAsset(assetPath);
}
}
}
catch (Exception e)
{
Debug.LogError($"VNTG Postprocessor failed checking text asset: {assetPath}. Error: {e.Message}");
}
}
}
private static void ConvertTxtToPaletteAsset(string assetPath)
{
List<Color> parsedColors = PaletteReader.ParsePaintNet(assetPath);
Palette paletteAsset = ScriptableObject.CreateInstance<Palette>();
paletteAsset.colors = parsedColors;
paletteAsset.distanceMetric = PaletteDistanceMetric.CIE76;
LUTBaker.BakePaletteTo3DLUT(parsedColors, paletteAsset.distanceMetric, ref paletteAsset.RawLUT, ref paletteAsset.LUT);
if (paletteAsset.LUT != null)
{
paletteAsset.LUT.name = "Baked_LUT";
}
string newAssetPath = Path.ChangeExtension(assetPath, ".asset");
AssetDatabase.CreateAsset(paletteAsset, newAssetPath);
if (paletteAsset.LUT != null)
{
AssetDatabase.AddObjectToAsset(paletteAsset.LUT, paletteAsset);
}
AssetDatabase.SaveAssets();
AssetDatabase.DeleteAsset(assetPath);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 686caff471059644aaa2de8d64547ea2
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor.AssetImporters;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PaletteImporter.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
[ScriptedImporter(1, new[] { "hex", "gpl", "ase", "pal" })]
public class PaletteImporter : ScriptedImporter
{
[Header("Palette Conversion Table Generation Settings")]
[SerializeField] private PaletteDistanceMetric distanceMetric = PaletteDistanceMetric.CIE76;
public override void OnImportAsset(AssetImportContext ctx)
{
string ext = Path.GetExtension(ctx.assetPath).ToLower().Replace(".", "");
List<Color> parsedColors = new List<Color>();
try
{
if (ext == "hex")
{
parsedColors = PaletteReader.ParseHex(ctx.assetPath);
}
else if (ext == "gpl")
{
parsedColors = PaletteReader.ParseGpl(ctx.assetPath);
}
else if (ext == "ase")
{
parsedColors = PaletteReader.ParseAse(ctx.assetPath);
}
else if (ext == "pal")
{
parsedColors = PaletteReader.ParsePal(ctx.assetPath);
}
}
catch (Exception e)
{
ctx.LogImportError($"Failed to parse palette file: {ctx.assetPath}. Error: {e.Message}");
}
Palette paletteAsset = ScriptableObject.CreateInstance<Palette>();
paletteAsset.colors = parsedColors;
paletteAsset.distanceMetric = distanceMetric;
LUTBaker.BakePaletteTo3DLUT(parsedColors, distanceMetric, ref paletteAsset.RawLUT, ref paletteAsset.LUT);
if (paletteAsset.LUT != null)
{
paletteAsset.LUT.name = "Baked_LUT";
ctx.AddObjectToAsset("LUT texture", paletteAsset.LUT);
}
ctx.AddObjectToAsset("main obj", paletteAsset);
ctx.SetMainObject(paletteAsset);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1d61ca7de3b48854c97fe4a77c57855a
@@ -0,0 +1,292 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PaletteReader.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public static class PaletteReader
{
public static List<Color> ParseHex(string path)
{
List<Color> colors = new List<Color>();
string text = File.ReadAllText(path);
string[] tokens = text.Split(new char[] { ',', '\n', '\r', ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string token in tokens)
{
string cleanHex = token.Trim().Replace("#", "");
if (ColorUtility.TryParseHtmlString("#" + cleanHex, out Color color))
{
colors.Add(color);
}
}
return colors;
}
public static List<Color> ParsePaintNet(string path)
{
List<Color> colors = new List<Color>();
string[] lines = File.ReadAllLines(path);
foreach (string line in lines)
{
string trimmed = line.Trim();
if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith(";"))
{
continue;
}
string cleanHex = trimmed.Replace("#", "");
if (cleanHex.Length == 8)
{
string alpha = cleanHex.Substring(0, 2);
string rgb = cleanHex.Substring(2, 6);
cleanHex = rgb + alpha;
}
if (ColorUtility.TryParseHtmlString("#" + cleanHex, out Color color))
{
colors.Add(color);
}
}
return colors;
}
public static List<Color> ParseGpl(string path)
{
List<Color> colors = new List<Color>();
string[] lines = File.ReadAllLines(path);
foreach (string line in lines)
{
string trimmed = line.Trim();
if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith("GIMP") || trimmed.StartsWith("Name:") || trimmed.StartsWith("Columns:") || trimmed.StartsWith("#"))
{
continue;
}
string[] tokens = trimmed.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length >= 3 && int.TryParse(tokens[0], out int r) && int.TryParse(tokens[1], out int g) && int.TryParse(tokens[2], out int b))
{
colors.Add(new Color(r / 255f, g / 255f, b / 255f, 1f));
}
}
return colors;
}
public static List<Color> ParseAse(string path)
{
List<Color> colors = new List<Color>();
if (!File.Exists(path)) return colors;
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fs))
{
byte[] signatureBytes = br.ReadBytes(4);
string signature = System.Text.Encoding.ASCII.GetString(signatureBytes);
if (signature != "ASEF")
{
throw new Exception("Not a valid Adobe Swatch Exchange (.ase) file.");
}
ushort versionMajor = ReadBigEndianUInt16(br);
ushort versionMinor = ReadBigEndianUInt16(br);
uint blockCount = ReadBigEndianUInt32(br);
for (int i = 0; i < blockCount; i++)
{
if (fs.Position >= fs.Length) break;
ushort blockType = ReadBigEndianUInt16(br);
uint blockLength = ReadBigEndianUInt32(br);
long blockStartPos = fs.Position;
if (blockType == 0x0001)
{
ushort nameLength = ReadBigEndianUInt16(br);
fs.Position += (nameLength * 2);
byte[] modelBytes = br.ReadBytes(4);
string colorModel = System.Text.Encoding.ASCII.GetString(modelBytes).Trim();
if (colorModel == "RGB")
{
float r = ReadBigEndianFloat(br);
float g = ReadBigEndianFloat(br);
float b = ReadBigEndianFloat(br);
colors.Add(new Color(r, g, b, 1f));
}
else if (colorModel == "CMYK")
{
float c = ReadBigEndianFloat(br);
float m = ReadBigEndianFloat(br);
float y = ReadBigEndianFloat(br);
float k = ReadBigEndianFloat(br);
float r = (1f - c) * (1f - k);
float g = (1f - m) * (1f - k);
float b = (1f - y) * (1f - k);
colors.Add(new Color(r, g, b, 1f));
}
else if (colorModel == "GRAY")
{
float gray = ReadBigEndianFloat(br);
colors.Add(new Color(gray, gray, gray, 1f));
}
}
fs.Position = blockStartPos + blockLength;
}
}
return colors;
}
public static List<Color> ParsePal(string path)
{
if (!File.Exists(path)) return new List<Color>();
byte[] sigBytes = new byte[4];
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
if (fs.Length >= 4)
{
fs.Read(sigBytes, 0, 4);
}
}
string sig = System.Text.Encoding.ASCII.GetString(sigBytes);
if (sig == "RIFF")
{
return ParseRiffPal(path);
}
return ParseJascPal(path);
}
private static List<Color> ParseJascPal(string path)
{
List<Color> colors = new List<Color>();
string[] lines = File.ReadAllLines(path);
if (lines.Length < 3 || !lines[0].ToUpper().Contains("JASC-PAL"))
{
throw new Exception("Invalid JASC PAL file header.");
}
int countLineIndex = 2;
while (countLineIndex < lines.Length && string.IsNullOrEmpty(lines[countLineIndex].Trim()))
{
countLineIndex++;
}
if (countLineIndex >= lines.Length || !int.TryParse(lines[countLineIndex].Trim(), out int targetColorCount))
{
throw new Exception("Could not parse color count from JASC PAL file.");
}
for (int i = countLineIndex + 1; i < lines.Length; i++)
{
string trimmed = lines[i].Trim();
if (string.IsNullOrEmpty(trimmed)) continue;
string[] tokens = trimmed.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length >= 3 &&
int.TryParse(tokens[0], out int r) &&
int.TryParse(tokens[1], out int g) &&
int.TryParse(tokens[2], out int b))
{
colors.Add(new Color(r / 255f, g / 255f, b / 255f, 1f));
}
if (colors.Count >= targetColorCount) break;
}
return colors;
}
private static List<Color> ParseRiffPal(string path)
{
List<Color> colors = new List<Color>();
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fs))
{
br.ReadBytes(4);
br.ReadUInt32();
string type = System.Text.Encoding.ASCII.GetString(br.ReadBytes(4));
if (type != "PAL ") throw new Exception("RIFF container type is not 'PAL '.");
while (fs.Position < fs.Length - 8)
{
string chunkID = System.Text.Encoding.ASCII.GetString(br.ReadBytes(4));
uint chunkSize = br.ReadUInt32();
long chunkStart = fs.Position;
if (chunkID == "data")
{
br.ReadUInt16();
ushort numEntries = br.ReadUInt16();
for (int i = 0; i < numEntries; i++)
{
if (fs.Position >= fs.Length) break;
byte r = br.ReadByte();
byte g = br.ReadByte();
byte b = br.ReadByte();
br.ReadByte();
colors.Add(new Color(r / 255f, g / 255f, b / 255f, 1f));
}
break;
}
else if (chunkID == "LIST")
{
br.ReadBytes(4);
continue;
}
uint paddedSize = chunkSize % 2 == 1 ? chunkSize + 1 : chunkSize;
fs.Position = chunkStart + paddedSize;
}
}
return colors;
}
private static ushort ReadBigEndianUInt16(BinaryReader br)
{
byte[] bytes = br.ReadBytes(2);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
return BitConverter.ToUInt16(bytes, 0);
}
private static uint ReadBigEndianUInt32(BinaryReader br)
{
byte[] bytes = br.ReadBytes(4);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
return BitConverter.ToUInt32(bytes, 0);
}
private static float ReadBigEndianFloat(BinaryReader br)
{
byte[] bytes = br.ReadBytes(4);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
return BitConverter.ToSingle(bytes, 0);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 254e0092301946c47a1f8f2df1010cc2
@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PaletteWriter.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public static class PaletteWriter
{
public static void WriteHex(string path, List<Color> colors)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < colors.Count; i++)
{
sb.Append(ColorUtility.ToHtmlStringRGB(colors[i]));
if (i < colors.Count - 1) sb.Append("\n");
}
File.WriteAllText(path, sb.ToString());
}
public static void WritePaintNet(string path, string paletteName, List<Color> colors)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(";paint.net Palette File");
sb.AppendLine($";Palette Name: {paletteName}");
sb.AppendLine($";Description:");
sb.AppendLine($";Colors: {colors.Count}");
foreach (var color in colors)
{
Color32 c32 = color;
sb.AppendLine($"{c32.a:X2}{c32.r:x2}{c32.g:x2}{c32.b:x2}");
}
File.WriteAllText(path, sb.ToString());
}
public static void WriteGpl(string path, string paletteName, List<Color> colors)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("GIMP Palette");
sb.AppendLine($"Name: {paletteName}");
sb.AppendLine("Columns: 4");
sb.AppendLine("#");
foreach (var color in colors)
{
Color32 c32 = color;
sb.AppendLine($"{c32.r,3} {c32.g,3} {c32.b,3}\tUntitled Swatch");
}
File.WriteAllText(path, sb.ToString());
}
public static void WriteJascPal(string path, List<Color> colors)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("JASC-PAL");
sb.AppendLine("0100");
sb.AppendLine(colors.Count.ToString());
foreach (var color in colors)
{
Color32 c32 = color;
sb.AppendLine($"{c32.r} {c32.g} {c32.b}");
}
File.WriteAllText(path, sb.ToString());
}
public static void WriteAse(string path, List<Color> colors)
{
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
using (BinaryWriter bw = new BinaryWriter(fs))
{
bw.Write(Encoding.ASCII.GetBytes("ASEF"));
WriteBigEndianUInt16(bw, 1);
WriteBigEndianUInt16(bw, 0);
WriteBigEndianUInt32(bw, (uint)colors.Count);
for (int i = 0; i < colors.Count; i++)
{
WriteBigEndianUInt16(bw, 0x0001);
string name = $"Color {i}\0";
uint blockLength = (uint)(2 + (name.Length * 2) + 4 + 12 + 2);
WriteBigEndianUInt32(bw, blockLength);
WriteBigEndianUInt16(bw, (ushort)name.Length);
foreach (char c in name)
{
WriteBigEndianUInt16(bw, (ushort)c);
}
bw.Write(Encoding.ASCII.GetBytes("RGB "));
WriteBigEndianFloat(bw, colors[i].r);
WriteBigEndianFloat(bw, colors[i].g);
WriteBigEndianFloat(bw, colors[i].b);
WriteBigEndianUInt16(bw, 0);
}
}
}
private static void WriteBigEndianUInt16(BinaryWriter bw, ushort value)
{
byte[] bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
bw.Write(bytes);
}
private static void WriteBigEndianUInt32(BinaryWriter bw, uint value)
{
byte[] bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
bw.Write(bytes);
}
private static void WriteBigEndianFloat(BinaryWriter bw, float value)
{
byte[] bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
bw.Write(bytes);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 324deb382e1bb5543b4681079650f91e
@@ -0,0 +1,110 @@
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: TextureToPaletteConverter.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public static class TextureToPaletteConverter
{
[MenuItem("Assets/Create/VNTG/Convert Tex to Palette Asset", false, 20)]
private static void ConvertSelectedTextureToPalette()
{
Texture2D sourceTex = Selection.activeObject as Texture2D;
if (sourceTex == null)
{
EditorUtility.DisplayDialog("Conversion Failed", "Please select a valid Texture2D asset first.", "OK");
return;
}
string assetPath = AssetDatabase.GetAssetPath(sourceTex);
Texture2D readableTex = sourceTex;
bool wasUnreadable = !sourceTex.isReadable;
if (wasUnreadable)
{
RenderTexture tmp = RenderTexture.GetTemporary(sourceTex.width, sourceTex.height, 0, RenderTextureFormat.Default, RenderTextureReadWrite.sRGB);
Graphics.Blit(sourceTex, tmp);
RenderTexture previous = RenderTexture.active;
RenderTexture.active = tmp;
readableTex = new Texture2D(sourceTex.width, sourceTex.height);
readableTex.ReadPixels(new Rect(0, 0, tmp.width, tmp.height), 0, 0);
readableTex.Apply();
RenderTexture.active = previous;
RenderTexture.ReleaseTemporary(tmp);
}
int width = readableTex.width;
int height = readableTex.height;
Color32[] rawPixels = readableTex.GetPixels32();
List<Color> paletteColors = new List<Color>();
HashSet<Color32> encounteredColors = new HashSet<Color32>();
int targetY = height / 2;
for (int x = 0; x < width; x++)
{
int pixelIndex = targetY * width + x;
Color32 c = rawPixels[pixelIndex];
if (c.a == 0) continue;
if (!encounteredColors.Contains(c))
{
encounteredColors.Add(c);
paletteColors.Add(c);
}
}
if (wasUnreadable)
{
UnityEngine.Object.DestroyImmediate(readableTex);
}
if (paletteColors.Count == 0)
{
EditorUtility.DisplayDialog("Conversion Failed", "No valid colors were found on the horizontal centerline of this texture.", "OK");
return;
}
Palette paletteAsset = ScriptableObject.CreateInstance<Palette>();
paletteAsset.colors = paletteColors;
paletteAsset.distanceMetric = PaletteDistanceMetric.CIE76;
LUTBaker.BakePaletteTo3DLUT(paletteColors, paletteAsset.distanceMetric, ref paletteAsset.RawLUT, ref paletteAsset.LUT);
if (paletteAsset.LUT != null)
{
paletteAsset.LUT.name = "Baked_LUT";
}
string directory = Path.GetDirectoryName(assetPath);
string filename = Path.GetFileNameWithoutExtension(assetPath);
string targetAssetPath = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(directory, $"{filename}_Palette.asset"));
AssetDatabase.CreateAsset(paletteAsset, targetAssetPath);
if (paletteAsset.LUT != null)
{
AssetDatabase.AddObjectToAsset(paletteAsset.LUT, paletteAsset);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorGUIUtility.PingObject(paletteAsset);
Selection.activeObject = paletteAsset;
}
[MenuItem("Assets/Create/VNTG/Convert Tex to Palette Asset", true)]
private static bool ConvertSelectedTextureToPaletteValidate()
{
return Selection.activeObject is Texture2D;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 26d827e1d0c95c54b8fef4ec1b622d70
@@ -0,0 +1,145 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: LUTBaker.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public static class LUTBaker
{
private const int LUT_DIM = 32;
public static void BakePaletteTo3DLUT(List<Color> palette, PaletteDistanceMetric metric, ref Color32[] rawColors, ref Texture3D lut)
{
if (palette == null || palette.Count == 0)
{
rawColors = null;
return;
}
if (lut == null || lut.width != LUT_DIM || lut.height != LUT_DIM || lut.depth != LUT_DIM)
{
lut = new Texture3D(LUT_DIM, LUT_DIM, LUT_DIM, TextureFormat.RGBA32, false);
}
lut.filterMode = FilterMode.Point;
lut.wrapMode = TextureWrapMode.Clamp;
Color32[] lutColors = new Color32[LUT_DIM * LUT_DIM * LUT_DIM];
Color32[] palette32 = new Color32[palette.Count];
for (int i = 0; i < palette.Count; i++) palette32[i] = palette[i];
Color paletteCol = new Color();
Color target = new Color();
try
{
int totalSteps = LUT_DIM * LUT_DIM;
for (int r = 0; r < LUT_DIM; r++)
{
for (int g = 0; g < LUT_DIM; g++)
{
int currentStep = (r * LUT_DIM) + g;
float progress = (float)currentStep / totalSteps;
EditorUtility.DisplayProgressBar(
"Baking LUT",
$"Processing color space... ({currentStep}/{totalSteps})",
progress
);
for (int b = 0; b < LUT_DIM; b++)
{
target.r = (float)r / (LUT_DIM - 1);
target.g = (float)g / (LUT_DIM - 1);
target.b = (float)b / (LUT_DIM - 1);
int bestIndex = 0;
float minDistance = float.MaxValue;
for (int i = 0; i < palette32.Length; i++)
{
paletteCol.r = palette32[i].r / 255f;
paletteCol.g = palette32[i].g / 255f;
paletteCol.b = palette32[i].b / 255f;
float distSq = float.MaxValue;
switch (metric)
{
case PaletteDistanceMetric.EuclideanRGB: distSq = ColorDistanceCalculator.GetRBGDistance(target, paletteCol); break;
case PaletteDistanceMetric.Redmean: distSq = ColorDistanceCalculator.GetRedmeanDistance(target, paletteCol); break;
case PaletteDistanceMetric.Luminance: distSq = ColorDistanceCalculator.GetLuminanceDistance(target, paletteCol); break;
case PaletteDistanceMetric.Hue: distSq = ColorDistanceCalculator.GetHueDistance(target, paletteCol); break;
case PaletteDistanceMetric.CIE76: distSq = ColorDistanceCalculator.GetCIE76(target, paletteCol); break;
case PaletteDistanceMetric.CIE94: distSq = ColorDistanceCalculator.GetCIE94(target, paletteCol); break;
case PaletteDistanceMetric.CMC1984: distSq = ColorDistanceCalculator.GetCMClc(target, paletteCol); break;
case PaletteDistanceMetric.CIEDE2000: distSq = ColorDistanceCalculator.GetCIEDE2000(target, paletteCol); break;
case PaletteDistanceMetric.HyAB: distSq = ColorDistanceCalculator.GetHybridABDistance(target, paletteCol); break;
case PaletteDistanceMetric.BT2124_ITP: distSq = ColorDistanceCalculator.GetDeltaEITP(target, paletteCol); break;
}
if (distSq < minDistance)
{
minDistance = distSq;
bestIndex = i;
}
}
int index = r + (g * LUT_DIM) + (b * LUT_DIM * LUT_DIM);
lutColors[index] = palette32[bestIndex];
}
}
}
lut.SetPixels32(lutColors);
lut.Apply();
rawColors = lutColors;
}
finally
{
EditorUtility.ClearProgressBar();
}
}
public static void Rebake(Palette palette)
{
if (palette == null) return;
bool isNewTexture = (palette.LUT == null);
BakePaletteTo3DLUT(palette.colors, palette.distanceMetric, ref palette.RawLUT, ref palette.LUT);
if (palette.LUT == null) return;
palette.LUT.name = "Baked_LUT";
palette.LUT.hideFlags = HideFlags.None;
if (isNewTexture)
{
string parentPath = AssetDatabase.GetAssetPath(palette);
if (!string.IsNullOrEmpty(parentPath))
{
EditorApplication.delayCall += () =>
{
if (palette == null || palette.LUT == null || string.IsNullOrEmpty(parentPath)) return;
AssetDatabase.AddObjectToAsset(palette.LUT, palette);
EditorUtility.SetDirty(palette);
AssetDatabase.SaveAssets();
AssetDatabase.ImportAsset(parentPath);
};
}
}
else
{
EditorUtility.SetDirty(palette);
EditorUtility.SetDirty(palette.LUT);
AssetDatabase.SaveAssets();
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 196d2a8c3a7b82e4ea3c4fbb343ccb5c
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 754f760dbf921af4193f7f73c773cda3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,126 @@
using System;
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: BoxSelectionManipulator.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class BoxSelectionManipulator : Manipulator
{
private readonly Action<Rect, Vector2, SelectionMode, bool, bool> _onSelectionUpdated;
private UISelectionBox _selectionBox;
private Vector2 _startPosition;
private bool _isActive;
private bool _shouldRenderRegion;
public enum SelectionMode
{
StartedDrag,
ActiveDrag,
SelectionConfirmed
}
public BoxSelectionManipulator(Action<Rect, Vector2, SelectionMode, bool, bool> onSelectionUpdated, bool shouldRenderRegion = true)
{
_onSelectionUpdated = onSelectionUpdated;
_shouldRenderRegion = shouldRenderRegion;
}
protected override void RegisterCallbacksOnTarget()
{
target.RegisterCallback<PointerDownEvent>(OnPointerDown);
target.RegisterCallback<PointerMoveEvent>(OnPointerMove);
target.RegisterCallback<PointerUpEvent>(OnPointerUp);
}
protected override void UnregisterCallbacksFromTarget()
{
target.UnregisterCallback<PointerDownEvent>(OnPointerDown);
target.UnregisterCallback<PointerMoveEvent>(OnPointerMove);
target.UnregisterCallback<PointerUpEvent>(OnPointerUp);
}
private void OnPointerDown(PointerDownEvent evt)
{
if (evt.button != 0) return;
_isActive = true;
_startPosition = evt.localPosition;
bool isAdditive = evt.ctrlKey || evt.actionKey;
bool isRangeSelect = evt.shiftKey;
if (!isRangeSelect && _shouldRenderRegion)
{
_selectionBox = new UISelectionBox();
target.Add(_selectionBox);
_selectionBox.UpdatePosition(_startPosition, _startPosition);
}
Vector2 currentPos = evt.localPosition;
_onSelectionUpdated?.Invoke(CalculateSelectionRect(_startPosition, currentPos), currentPos, SelectionMode.StartedDrag, isAdditive, isRangeSelect);
target.CapturePointer(evt.pointerId);
evt.StopPropagation();
}
private void OnPointerMove(PointerMoveEvent evt)
{
if (!_isActive) return;
Vector2 currentPos = evt.localPosition;
if (_selectionBox != null)
{
_selectionBox.UpdatePosition(_startPosition, currentPos);
}
Rect currentRect = CalculateSelectionRect(_startPosition, currentPos);
bool isAdditive = evt.ctrlKey || evt.actionKey;
bool isRangeSelect = evt.shiftKey;
_onSelectionUpdated?.Invoke(currentRect, currentPos, SelectionMode.ActiveDrag, isAdditive, isRangeSelect);
evt.StopPropagation();
}
private void OnPointerUp(PointerUpEvent evt)
{
if (!_isActive) return;
_isActive = false;
Vector2 currentPos = evt.localPosition;
Rect finalRect = CalculateSelectionRect(_startPosition, currentPos);
bool isAdditive = evt.ctrlKey || evt.actionKey;
bool isRangeSelect = evt.shiftKey;
_onSelectionUpdated?.Invoke(finalRect, currentPos, SelectionMode.SelectionConfirmed, isAdditive, isRangeSelect);
if (_selectionBox != null)
{
_selectionBox.RemoveFromHierarchy();
_selectionBox = null;
}
if (target.HasPointerCapture(evt.pointerId))
{
target.ReleasePointer(evt.pointerId);
}
evt.StopPropagation();
}
private Rect CalculateSelectionRect(Vector2 start, Vector2 current)
{
float x = Mathf.Min(start.x, current.x);
float y = Mathf.Min(start.y, current.y);
float w = Mathf.Abs(start.x - current.x);
float h = Mathf.Abs(start.y - current.y);
return new Rect(x, y, w, h);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9392f551f752a45489713c50d95537d9
@@ -0,0 +1,30 @@
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PaletteEditorStyle.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public static class PaletteEditorStyle
{
public static readonly Color HighlightColor = new Color(0.4f, 0.8f, 1f, 1f);
public static readonly Color BorderColor = new Color(0.15f, 0.15f, 0.15f, 1f);
public static readonly Color FoldColorUpper = new Color(0.1f, 0.1f, 0.1f, 1.0f);
public static readonly Color FoldColorLower = new Color(0.4f, 0.8f, 1f, 1f);
public static readonly Color HandleRectColor = new Color(0.25f, 0.25f, 0.25f, 0.4f);
public static readonly Color HandleIconColor = new Color(0.6f, 0.6f, 0.6f, 1f);
public const float RectSize = 36.0f;
public const float BorderSize = 1.0f;
public const float HighlightBorderSize = 2.0f;
public const float FoldSize = 8.0f;
public const float FoldPadding = 2.0f;
public const float WheelSize = 300.0f;
public const float BarWidth = 22.5f;
public const float Spacing = 15.0f;
public const float MarkerSize = 8.0f;
public const float KnobSize = 16.0f;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a4a7a0fac74f8214fa028dc700d75798
@@ -0,0 +1,56 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using ColbyO.VNTG.ColorPalette;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PalettePropertyDrawer.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.Editor
{
[CustomPropertyDrawer(typeof(Palette))]
public class PalettePropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
Palette currentPalette = property.objectReferenceValue as Palette;
if (Event.current.type == EventType.MouseDown && position.Contains(Event.current.mousePosition))
{
PopupWindow.Show(position, new PaletteSelectorPopup(property));
Event.current.Use();
}
if (currentPalette != null && currentPalette.colors != null && currentPalette.colors.Count > 0)
{
DrawPaletteBar(position, currentPalette.colors);
GUIStyle textStyle = new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter };
EditorGUI.DropShadowLabel(position, currentPalette.name, textStyle);
}
else
{
GUI.Box(position, currentPalette != null ? currentPalette.name : "None (Click to select Palette)");
}
EditorGUI.EndProperty();
}
public static void DrawPaletteBar(Rect position, List<Color> colors)
{
if (colors == null || colors.Count == 0) return;
float blockWidth = position.width / colors.Count;
for (int i = 0; i < colors.Count; i++)
{
Rect colorRect = new Rect(position.x + (i * blockWidth), position.y, blockWidth, position.height);
EditorGUI.DrawRect(colorRect, colors[i]);
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e109e6a9463cef742910769efb005c01
@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using ColbyO.VNTG.ColorPalette;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PaletteSelectorPopup.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.Editor
{
public class PaletteSelectorPopup : PopupWindowContent
{
private readonly SerializedProperty property;
private readonly Action<Palette> onPaletteSelected;
private List<Palette> cachedPalettes;
private Vector2 scrollPos;
private string searchFilter = "";
private bool isFocusSet = false;
public PaletteSelectorPopup(SerializedProperty property)
{
this.property = property;
CachePalettes();
}
public PaletteSelectorPopup(Action<Palette> onPaletteSelected)
{
this.onPaletteSelected = onPaletteSelected;
CachePalettes();
}
private void CachePalettes()
{
string[] guids = AssetDatabase.FindAssets("t:Palette");
cachedPalettes = new List<Palette>(guids.Length);
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
Palette p = AssetDatabase.LoadAssetAtPath<Palette>(path);
if (p != null) cachedPalettes.Add(p);
}
}
public override Vector2 GetWindowSize()
{
int itemCount = cachedPalettes != null ? cachedPalettes.Count : 0;
return new Vector2(300, Mathf.Min(itemCount * 26 + 65, 300));
}
public override void OnGUI(Rect rect)
{
Event e = Event.current;
if (e.type == EventType.MouseMove)
{
editorWindow.Repaint();
}
GUILayout.Space(2);
GUI.SetNextControlName("PaletteSearchField");
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField);
if (!isFocusSet)
{
EditorGUI.FocusTextInControl("PaletteSearchField");
isFocusSet = true;
}
GUILayout.Space(2);
scrollPos = GUILayout.BeginScrollView(scrollPos);
if (string.IsNullOrEmpty(searchFilter))
{
Rect noneRect = GUILayoutUtility.GetRect(new GUIContent("None"), EditorStyles.miniButton, GUILayout.Height(20));
if (noneRect.Contains(e.mousePosition))
{
EditorGUI.DrawRect(noneRect, new Color(0.3f, 0.5f, 0.8f, 0.35f));
if (e.type == EventType.MouseDown && e.button == 0)
{
SelectPalette(null);
e.Use();
}
}
GUI.Label(noneRect, "None", EditorStyles.centeredGreyMiniLabel);
}
for (int i = 0; i < cachedPalettes.Count; i++)
{
Palette palette = cachedPalettes[i];
if (palette == null) continue;
if (!string.IsNullOrEmpty(searchFilter) &&
!palette.name.ToLower().Contains(searchFilter.ToLower()))
{
continue;
}
Rect rowRect = GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.label, GUILayout.Height(24));
bool isHovered = rowRect.Contains(e.mousePosition);
if (isHovered)
{
EditorGUI.DrawRect(rowRect, new Color(0.25f, 0.55f, 0.95f, 0.35f));
if (e.type == EventType.MouseDown && e.button == 0)
{
SelectPalette(palette);
e.Use();
}
}
Rect labelRect = new Rect(rowRect.x + 5, rowRect.y, 110, rowRect.height);
GUI.Label(labelRect, palette.name, EditorStyles.miniBoldLabel);
Rect paletteBarRect = new Rect(rowRect.x + 120, rowRect.y + 3, rowRect.width - 125, rowRect.height - 6);
PalettePropertyDrawer.DrawPaletteBar(paletteBarRect, palette.colors);
}
GUILayout.EndScrollView();
}
private void SelectPalette(Palette palette)
{
if (property != null)
{
property.objectReferenceValue = palette;
property.serializedObject.ApplyModifiedProperties();
}
onPaletteSelected?.Invoke(palette);
editorWindow.Close();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 23e439b99128a974ca49f4b3535735e7
@@ -0,0 +1,89 @@
using System.Collections.Generic;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: SelectionState.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class SelectionState
{
private readonly List<int> _orderedIndices = new List<int> { 0 };
private readonly HashSet<int> _selectedIndices = new HashSet<int> { 0 };
public HashSet<int> Indices => _selectedIndices;
public int GetPrimarySelection()
{
if (IsEmpty()) return -1;
return _orderedIndices[0];
}
public int SetPrimarySelection(int index)
{
if (!Contains(index))
{
Add(index);
}
_orderedIndices.Remove(index);
_orderedIndices.Insert(0, index);
return index;
}
public int Add(int index)
{
if (_selectedIndices.Add(index))
{
_orderedIndices.Add(index);
}
return GetPrimarySelection();
}
public int Remove(int index)
{
if (_selectedIndices.Remove(index))
{
_orderedIndices.Remove(index);
}
return GetPrimarySelection();
}
public int UnionWith(IEnumerable<int> other)
{
List<int> currentRange = new List<int>(_orderedIndices);
_selectedIndices.Clear();
_orderedIndices.Clear();
foreach (int index in other)
{
Add(index);
}
foreach (int index in currentRange)
{
Add(index);
}
return GetPrimarySelection();
}
public void Clear()
{
_selectedIndices.Clear();
_orderedIndices.Clear();
}
public bool Contains(int index)
{
return _selectedIndices.Contains(index);
}
public bool IsEmpty()
{
return _orderedIndices.Count == 0;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5592473ce1377534e96b87a9bc25615f
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7750e5ed12983e74f8b2266c558add93
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e80fc8cd64547224784c4b6f8669461c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,122 @@
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIBlitSliderBase.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public abstract class UIBlitSliderBase : VisualElement
{
private const string COLOR_BAR_SHADER_PATH = "Hidden/ColbyO/VNTG/Editor/ColorBar";
private static StyleSheet _cachedStyleSheet;
protected Material SliderMaterial;
protected RenderTexture TrackTexture;
protected VisualElement TrackElement;
protected VisualElement KnobElement;
public event Action<float> OnValueChanged;
protected UIBlitSliderBase()
{
ApplySharedSliderStyles();
Shader shader = Shader.Find(COLOR_BAR_SHADER_PATH);
if (shader != null) SliderMaterial = new Material(shader);
}
private void ApplySharedSliderStyles()
{
if (_cachedStyleSheet != null)
{
styleSheets.Add(_cachedStyleSheet);
return;
}
string[] guids = AssetDatabase.FindAssets("SliderStyles t:StyleSheet");
if (guids.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
_cachedStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(path);
if (_cachedStyleSheet != null)
{
styleSheets.Add(_cachedStyleSheet);
}
}
}
protected void InitializeTrack(VisualElement track, float knobSize)
{
TrackElement = track;
KnobElement = CreateKnobElement(knobSize);
TrackElement.Add(KnobElement);
}
protected void BlitTrackTexture(int width, int height, Vector4 inColor, int shaderModeId, bool isHorizontal)
{
if (SliderMaterial == null || width <= 0 || height <= 0) return;
if (TrackTexture == null || TrackTexture.width != width || TrackTexture.height != height)
{
if (TrackTexture != null) TrackTexture.Release();
TrackTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32)
{
hideFlags = HideFlags.HideAndDontSave
};
TrackElement.style.backgroundImage = Background.FromRenderTexture(TrackTexture);
}
SliderMaterial.SetVector("_InColor", inColor);
SliderMaterial.SetVector("_Dimensions", new Vector4(width, height, 0, 0));
SliderMaterial.SetFloat("_Radius", (isHorizontal ? height : width) * 0.5f);
SliderMaterial.SetInt("_SliderMode", shaderModeId);
SliderMaterial.SetInt("_IsHorizontal", isHorizontal ? 1 : 0);
RenderTexture oldActive = RenderTexture.active;
RenderTexture.active = TrackTexture;
GL.Clear(true, true, Color.clear);
Graphics.Blit(Texture2D.whiteTexture, TrackTexture, SliderMaterial);
RenderTexture.active = oldActive;
TrackElement.MarkDirtyRepaint();
}
protected void FireValueChanged(float rawValue)
{
OnValueChanged?.Invoke(rawValue);
}
private VisualElement CreateKnobElement(float knobSize)
{
var knob = new VisualElement();
knob.AddToClassList("blit-slider__knob");
knob.style.width = knobSize;
knob.style.height = knobSize;
float outerRadius = knobSize * 0.5f;
SetBorderRadius(knob, outerRadius);
VisualElement knobCore = new VisualElement { name = "Knob" };
knobCore.AddToClassList("blit-slider__knob-core");
SetBorderRadius(knobCore, outerRadius);
knob.Add(knobCore);
return knob;
}
private void SetBorderRadius(VisualElement element, float radius)
{
element.style.borderTopLeftRadius = radius; element.style.borderTopRightRadius = radius;
element.style.borderBottomLeftRadius = radius; element.style.borderBottomRightRadius = radius;
}
public virtual void Cleanup()
{
if (SliderMaterial != null) { UnityEngine.Object.DestroyImmediate(SliderMaterial); SliderMaterial = null; }
if (TrackTexture != null) { TrackTexture.Release(); UnityEngine.Object.DestroyImmediate(TrackTexture); TrackTexture = null; }
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 22dc34bce58b5494c963056be5f8a54c
@@ -0,0 +1,168 @@
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIChannelSliderElement.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UIChannelSliderElement : UIBlitSliderBase
{
private readonly Label _prefixLabel;
private readonly FloatField _valueField;
private readonly IntegerField _intValueField;
private float _min;
private float _max;
private float _currentRawValue;
private bool _asInt;
private int _shaderModeId;
private Vector4 _currentInColor;
private const float TRACK_HEIGHT = 20f;
private const float KNOB_SIZE = 14f;
public UIChannelSliderElement() : base()
{
AddToClassList("channel-slider__row");
_prefixLabel = new Label();
_prefixLabel.AddToClassList("channel-slider__label");
Add(_prefixLabel);
var track = new VisualElement();
track.AddToClassList("channel-slider__track");
track.style.height = TRACK_HEIGHT;
track.RegisterCallback<PointerDownEvent>(OnTrackPointerDown);
track.RegisterCallback<PointerMoveEvent>(OnTrackPointerMove);
track.RegisterCallback<PointerUpEvent>(OnTrackPointerUp);
track.RegisterCallback<GeometryChangedEvent>(OnTrackGeometryChanged);
Add(track);
InitializeTrack(track, KNOB_SIZE);
KnobElement.style.top = (TRACK_HEIGHT - KNOB_SIZE) / 2f;
_valueField = new FloatField();
_valueField.AddToClassList("channel-slider__field");
_valueField.RegisterValueChangedCallback(evt => UpdateFromField(evt.newValue));
Add(_valueField);
_intValueField = new IntegerField();
_intValueField.AddToClassList("channel-slider__field");
_intValueField.RegisterValueChangedCallback(evt => UpdateFromField(evt.newValue));
Add(_intValueField);
}
public void Setup(string label, float value, float min, float max, int shaderModeId, bool asInt)
{
_prefixLabel.text = label;
_min = min;
_max = max;
_shaderModeId = shaderModeId;
_asInt = asInt;
_valueField.style.display = _asInt ? DisplayStyle.None : DisplayStyle.Flex;
_intValueField.style.display = _asInt ? DisplayStyle.Flex : DisplayStyle.None;
_currentInColor = Vector4.one;
SetRawValueWithoutNotify(value);
}
public void UpdateShaderTrack(Vector4 inColor)
{
_currentInColor = inColor;
float width = TrackElement.layout.width;
if (width <= 0f || float.IsNaN(width)) return;
BlitTrackTexture((int)width, (int)TRACK_HEIGHT, _currentInColor, _shaderModeId, isHorizontal: true);
}
public void SetRawValueWithoutNotify(float value)
{
_currentRawValue = Mathf.Clamp(value, _min, _max);
if (_asInt) _intValueField.SetValueWithoutNotify(Mathf.RoundToInt(_currentRawValue));
else _valueField.SetValueWithoutNotify(_currentRawValue);
RepositionKnob();
}
private void OnTrackGeometryChanged(GeometryChangedEvent evt)
{
float width = evt.newRect.width;
if (width <= 1f) return;
BlitTrackTexture((int)width, (int)TRACK_HEIGHT, _currentInColor, _shaderModeId, isHorizontal: true);
RepositionKnob();
}
private void RepositionKnob()
{
float trackWidth = TrackElement.layout.width;
if (trackWidth <= 0f) return;
float percentage = Mathf.InverseLerp(_min, _max, _currentRawValue);
float targetLeft = Mathf.Lerp(0f, trackWidth - KNOB_SIZE, percentage);
KnobElement.style.left = targetLeft;
}
private void UpdateFromDragPosition(Vector2 localPos)
{
float trackWidth = TrackElement.layout.width;
if (trackWidth <= 0f) return;
float pct = Mathf.Clamp01(localPos.x / trackWidth);
float newValue = Mathf.Lerp(_min, _max, pct);
if (_asInt) newValue = Mathf.RoundToInt(newValue);
if (!Mathf.Approximately(_currentRawValue, newValue))
{
_currentRawValue = newValue;
if (_asInt) _intValueField.SetValueWithoutNotify(Mathf.RoundToInt(_currentRawValue));
else _valueField.SetValueWithoutNotify(_currentRawValue);
RepositionKnob();
FireValueChanged(_currentRawValue);
}
}
private void UpdateFromField(float value)
{
float clamped = Mathf.Clamp(value, _min, _max);
if (!Mathf.Approximately(_currentRawValue, clamped))
{
_currentRawValue = clamped;
RepositionKnob();
FireValueChanged(_currentRawValue);
}
}
private void OnTrackPointerDown(PointerDownEvent evt)
{
if (evt.button != 0) return;
TrackElement.CapturePointer(evt.pointerId);
UpdateFromDragPosition(evt.localPosition);
evt.StopPropagation();
}
private void OnTrackPointerMove(PointerMoveEvent evt)
{
if (TrackElement.HasPointerCapture(evt.pointerId))
{
UpdateFromDragPosition(evt.localPosition);
evt.StopPropagation();
}
}
private void OnTrackPointerUp(PointerUpEvent evt)
{
if (TrackElement.HasPointerCapture(evt.pointerId))
{
TrackElement.ReleasePointer(evt.pointerId);
evt.StopPropagation();
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7df1b69e3ca909e4e9dcbe92301153fd
@@ -0,0 +1,85 @@
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIColorBar.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UIColorBar : UIBlitSliderBase
{
private bool _isDragging;
public UIColorBar() : base()
{
AddToClassList("color-bar__container");
style.width = PaletteEditorStyle.BarWidth;
style.height = Length.Percent(100);
style.marginLeft = PaletteEditorStyle.Spacing;
style.flexShrink = 0;
InitializeTrack(this, PaletteEditorStyle.KnobSize);
KnobElement.style.left = (PaletteEditorStyle.BarWidth - PaletteEditorStyle.KnobSize) / 2f;
RegisterCallback<PointerDownEvent>(OnPointerDown);
RegisterCallback<PointerMoveEvent>(OnPointerMove);
RegisterCallback<PointerUpEvent>(OnPointerUp);
}
public void UpdateVisuals(Vector3 hsv, float lightnessValue, bool hasSelection)
{
if (!hasSelection)
{
KnobElement.style.display = DisplayStyle.None;
return;
}
KnobElement.style.display = DisplayStyle.Flex;
int currentWidth = (int)Mathf.Max(1f, layout.width > 0 ? layout.width : PaletteEditorStyle.BarWidth);
int currentHeight = (int)Mathf.Max(1f, layout.height > 0 ? layout.height : PaletteEditorStyle.WheelSize);
Vector4 inColor = new Vector4(hsv.x, hsv.y, hsv.z, 1.0f);
BlitTrackTexture(currentWidth, currentHeight, inColor, 7, isHorizontal: false);
float trackHeight = layout.height > 0 ? layout.height : PaletteEditorStyle.WheelSize;
float knobTopY = Mathf.Lerp(trackHeight - 12f, 0f, lightnessValue);
KnobElement.style.top = knobTopY;
}
private void OnPointerDown(PointerDownEvent evt)
{
if (evt.button != 0) return;
this.CapturePointer(evt.pointerId);
_isDragging = true;
ProcessDrag(evt.localPosition);
evt.StopPropagation();
}
private void OnPointerMove(PointerMoveEvent evt)
{
if (!_isDragging || !this.HasPointerCapture(evt.pointerId)) return;
ProcessDrag(evt.localPosition);
evt.StopPropagation();
}
private void OnPointerUp(PointerUpEvent evt)
{
if (this.HasPointerCapture(evt.pointerId))
{
this.ReleasePointer(evt.pointerId);
_isDragging = false;
evt.StopPropagation();
}
}
private void ProcessDrag(Vector2 localPos)
{
float value = Mathf.InverseLerp(layout.height, 0f, localPos.y);
float targetL = Mathf.Clamp(value, 0.0001f, 0.9999f);
FireValueChanged(targetL);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6f4cf9a4b8afb8c43b7be8573aa17a4a
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a51f3a91792a26448aef94ea61dc114a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,153 @@
using System;
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIColorSwatch.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UIColorSwatch : VisualElement
{
public int Index { get; private set; }
private bool _isSelected;
private readonly VisualElement _innerColorDisplay;
public event Action<int> OnSwatchHoverChanged;
public class UIColorSwatchDragEventArgs : EventArgs { public UIColorSwatch Swatch { get; set; } }
public event Action<UIColorSwatch> OnSwatchDragStarted;
private bool _isPrimarySelectedSwatch;
public UIColorSwatch(Color colorData, int associatedIndex, bool startingSelectionState, StyleSheet styleSheet)
{
Index = associatedIndex;
_isSelected = startingSelectionState;
if (styleSheet != null)
{
styleSheets.Add(styleSheet);
}
AddToClassList("color-swatch");
_innerColorDisplay = new VisualElement
{
pickingMode = PickingMode.Position
};
_innerColorDisplay.AddToClassList("color-swatch__inner-display");
_innerColorDisplay.style.backgroundColor = colorData;
Add(_innerColorDisplay);
RegisterCallback<PointerDownEvent>(OnPointerDown);
RegisterCallback<PointerEnterEvent>(e => OnSwatchHoverChanged?.Invoke(Index));
RegisterCallback<PointerLeaveEvent>(OnPointerLeave);
RegisterCallback<PointerMoveEvent>(OnPointerMove);
_innerColorDisplay.generateVisualContent += DrawSelectionFoldTriangle;
}
public void UpdateSwatchData(Color targetColor, int newIndex, bool selectionState, bool isPrimarySelectedSwatch)
{
Index = newIndex;
_innerColorDisplay.style.backgroundColor = targetColor;
_isPrimarySelectedSwatch = isPrimarySelectedSwatch;
if (_isSelected != selectionState)
{
_isSelected = selectionState;
_innerColorDisplay.MarkDirtyRepaint();
}
else if (_isSelected)
{
_innerColorDisplay.MarkDirtyRepaint();
}
}
private bool IsInFoldArea(Vector2 localPos)
{
float maxCoord = PaletteEditorStyle.FoldSize + PaletteEditorStyle.FoldPadding;
return _isPrimarySelectedSwatch && localPos.x <= maxCoord && localPos.y <= maxCoord;
}
private void OnPointerMove(PointerMoveEvent evt)
{
if (_isSelected && IsInFoldArea(evt.localPosition))
{
if (!_innerColorDisplay.ClassListContains("swatch-fold-hover"))
{
_innerColorDisplay.AddToClassList("swatch-fold-hover");
_innerColorDisplay.MarkDirtyRepaint();
}
}
else
{
if (_innerColorDisplay.ClassListContains("swatch-fold-hover"))
{
_innerColorDisplay.RemoveFromClassList("swatch-fold-hover");
_innerColorDisplay.MarkDirtyRepaint();
}
}
}
private void OnPointerDown(PointerDownEvent evt)
{
if (evt.button != 0) return;
if (_isSelected && IsInFoldArea(evt.localPosition))
{
OnSwatchDragStarted?.Invoke(this);
evt.StopPropagation();
}
}
private void OnPointerLeave(PointerLeaveEvent evt)
{
OnSwatchHoverChanged?.Invoke(-1);
_innerColorDisplay.RemoveFromClassList("swatch-fold-hover");
_innerColorDisplay.MarkDirtyRepaint();
}
public void UpdateContourBorders(bool showTop, bool showBottom, bool showLeft, bool showRight)
{
ToggleClass(this, "swatch--out-top", showTop);
ToggleClass(this, "swatch--out-bottom", showBottom);
ToggleClass(this, "swatch--out-left", showLeft);
ToggleClass(this, "swatch--out-right", showRight);
ToggleClass(_innerColorDisplay, "swatch--in-top", showTop);
ToggleClass(_innerColorDisplay, "swatch--in-bottom", showBottom);
ToggleClass(_innerColorDisplay, "swatch--in-left", showLeft);
ToggleClass(_innerColorDisplay, "swatch--in-right", showRight);
}
private void ToggleClass(VisualElement element, string className, bool enable)
{
if (enable) element.AddToClassList(className);
else element.RemoveFromClassList(className);
}
private void DrawSelectionFoldTriangle(MeshGenerationContext mgc)
{
if (!_isSelected || !_isPrimarySelectedSwatch) return;
Rect bounds = _innerColorDisplay.contentRect;
float size = PaletteEditorStyle.FoldSize;
var mesh = mgc.Allocate(6, 6);
Vertex t1_v0 = new Vertex { position = new Vector3(bounds.x, bounds.y, Vertex.nearZ), tint = PaletteEditorStyle.FoldColorUpper };
Vertex t1_v1 = new Vertex { position = new Vector3(bounds.x + size, bounds.y, Vertex.nearZ), tint = PaletteEditorStyle.FoldColorUpper };
Vertex t1_v2 = new Vertex { position = new Vector3(bounds.x, bounds.y + size, Vertex.nearZ), tint = PaletteEditorStyle.FoldColorUpper };
Vertex t2_v0 = new Vertex { position = new Vector3(bounds.x + size, bounds.y, Vertex.nearZ), tint = PaletteEditorStyle.FoldColorLower };
Vertex t2_v1 = new Vertex { position = new Vector3(bounds.x + size, bounds.y + size, Vertex.nearZ), tint = PaletteEditorStyle.FoldColorLower };
Vertex t2_v2 = new Vertex { position = new Vector3(bounds.x, bounds.y + size, Vertex.nearZ), tint = PaletteEditorStyle.FoldColorLower };
mesh.SetAllVertices(new[] { t1_v0, t1_v1, t1_v2, t2_v0, t2_v1, t2_v2 });
mesh.SetAllIndices(new ushort[] { 0, 1, 2, 3, 4, 5 });
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 88aef59a912cc804785eda4bea03c1e9
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e683a9e85cbbcf64190652be57e0ed1b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,291 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIBaseColorWheel.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public abstract class UIBaseColorWheel : VisualElement
{
private const string COLOR_WHEEL_SHADER_PATH = "Hidden/ColbyO/VNTG/Editor/ColorWheel";
protected readonly ColorWheelCache Cache;
private Material _wheelMaterial;
private RenderTexture _wheelTexture;
private IList<Color> _currentColors;
private SelectionState _selectedIndices;
private int _selectedIndex = -1;
private int _draggedColorIndex = -1;
private Vector2 _dragMousePosStart;
private float _dragStartValue;
private float _dragStartSaturation;
public event Action<int, Vector3> OnColorModified;
public event Action<int> OnSelectionChanged;
public event Action<int> OnHoverChanged;
private int _hoveringIndex = -1;
public int HoveringIndex
{
get => _hoveringIndex;
set
{
if (_hoveringIndex != value)
{
_hoveringIndex = value;
MarkDirtyRepaint();
}
}
}
protected abstract float ShaderWheelTypeValue { get; }
protected abstract Vector2 GetMarkerPosition(int index, Rect wheelRect);
protected abstract bool TryFindMarker(Vector2 localPos, Rect wheelRect, int selectedIndex, float radius, out int foundIndex);
protected abstract void ProcessDragInput(Vector2 offset, float magnitude, float hue, ref Vector3 hsv, float startSaturation);
protected abstract void ProcessShiftDragInput(float delta, ref Vector3 hsv, float startSaturation, float startValue);
protected UIBaseColorWheel(ColorWheelCache cache)
{
Cache = cache;
AddToClassList("color-picker__wheel");
InitializeResources();
generateVisualContent += DrawWheelMarkers;
RegisterCallback<PointerDownEvent>(OnPointerDown);
RegisterCallback<PointerMoveEvent>(OnPointerMove);
RegisterCallback<PointerUpEvent>(OnPointerUp);
RegisterCallback<GeometryChangedEvent>(OnWheelGeometryChanged);
RegisterCallback<DetachFromPanelEvent>(e => Cleanup());
}
private void OnWheelGeometryChanged(GeometryChangedEvent evt)
{
if (Mathf.Approximately(evt.newRect.width, evt.oldRect.width) &&
Mathf.Approximately(evt.newRect.height, evt.oldRect.height)) return;
RecreateRenderTexture((int)Mathf.Max(1f, evt.newRect.width), (int)Mathf.Max(1f, evt.newRect.height));
MarkDirtyRepaint();
}
private void RecreateRenderTexture(int width, int height)
{
if (_wheelTexture != null && (_wheelTexture.width != width || _wheelTexture.height != height))
{
_wheelTexture.Release();
UnityEngine.Object.DestroyImmediate(_wheelTexture);
_wheelTexture = null;
}
if (_wheelTexture == null && width > 0 && height > 0)
{
_wheelTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32)
{
hideFlags = HideFlags.HideAndDontSave
};
}
}
private void InitializeResources()
{
if (_wheelMaterial == null)
{
Shader shader = Shader.Find(COLOR_WHEEL_SHADER_PATH);
if (shader != null) _wheelMaterial = new Material(shader);
}
if (_wheelTexture == null)
{
_wheelTexture = new RenderTexture((int)PaletteEditorStyle.WheelSize, (int)PaletteEditorStyle.WheelSize, 0, RenderTextureFormat.ARGB32)
{
hideFlags = HideFlags.HideAndDontSave
};
}
}
public void UpdateVisuals(IList<Color> colors, int selectedIndex, float modifier, SelectionState selectedIndices = null)
{
_currentColors = colors;
_selectedIndex = selectedIndex;
_selectedIndices = selectedIndices;
InitializeResources();
if (_wheelMaterial != null && _wheelTexture != null)
{
_wheelMaterial.SetFloat("_WheelType", ShaderWheelTypeValue);
_wheelMaterial.SetFloat("_Modifier", modifier);
RenderTexture previousActive = RenderTexture.active;
RenderTexture.active = _wheelTexture;
GL.Clear(true, true, Color.clear);
Graphics.Blit(Texture2D.whiteTexture, _wheelTexture, _wheelMaterial);
style.backgroundImage = Background.FromRenderTexture(_wheelTexture);
RenderTexture.active = previousActive;
}
MarkDirtyRepaint();
}
private void DrawWheelMarkers(MeshGenerationContext mgc)
{
if (_currentColors == null || layout.width < 1f) return;
Rect wheelRect = new Rect(0f, 0f, layout.width, layout.height);
Painter2D painter = mgc.painter2D;
for (int i = 0; i < _currentColors.Count; i++)
{
if (_selectedIndices != null && !_selectedIndices.Contains(i)) continue;
if (_selectedIndex == i || HoveringIndex == i) continue;
DrawVisualMarker(painter, i, wheelRect, Color.white, 1.0f);
}
if (_selectedIndex >= 0 && _selectedIndex < _currentColors.Count)
{
if (_selectedIndices == null || _selectedIndices.Contains(_selectedIndex))
{
DrawVisualMarker(painter, _selectedIndex, wheelRect, Color.cyan, 1.5f);
}
}
if (HoveringIndex >= 0 && HoveringIndex < _currentColors.Count && HoveringIndex != _selectedIndex)
{
if (_selectedIndices == null || _selectedIndices.Contains(HoveringIndex))
{
DrawVisualMarker(painter, HoveringIndex, wheelRect, Color.white, 1.5f);
}
}
}
private void DrawVisualMarker(Painter2D painter, int index, Rect wheelRect, Color outerRingColor, float scale)
{
Vector2 position = GetMarkerPosition(index, wheelRect);
float radius = PaletteEditorStyle.MarkerSize * scale;
Color darkOutlineColor = new Color(0.12f, 0.12f, 0.12f, 1f);
painter.lineWidth = 1.5f;
painter.strokeColor = outerRingColor;
painter.fillColor = darkOutlineColor;
painter.BeginPath();
painter.Arc(position, radius, 0f, 360f);
painter.ClosePath();
painter.Fill();
painter.Stroke();
painter.strokeColor = darkOutlineColor;
painter.fillColor = Cache.GetRGB(index);
painter.BeginPath();
painter.Arc(position, Mathf.Max(0.5f, radius - 1.0f), 0f, 360f);
painter.ClosePath();
painter.Fill();
painter.Stroke();
}
private void OnPointerDown(PointerDownEvent evt)
{
if (evt.button != 0 || _currentColors == null) return;
Vector2 localPos = evt.localPosition;
Rect localRect = new Rect(0f, 0f, layout.width, layout.height);
if (localRect.Contains(localPos))
{
this.CapturePointer(evt.pointerId);
if (TryFindMarker(localPos, localRect, _selectedIndex, PaletteEditorStyle.MarkerSize + 4f, out _draggedColorIndex))
{
if (_selectedIndices != null && !_selectedIndices.Contains(_draggedColorIndex))
{
_draggedColorIndex = -1;
this.ReleasePointer(evt.pointerId);
return;
}
OnSelectionChanged?.Invoke(_draggedColorIndex);
_dragMousePosStart = localPos;
Vector3 hsv = Cache.GetHSV(_draggedColorIndex);
_dragStartValue = hsv.z;
_dragStartSaturation = hsv.y;
}
evt.StopPropagation();
}
}
private void OnPointerMove(PointerMoveEvent evt)
{
if (!this.HasPointerCapture(evt.pointerId) || _currentColors == null)
{
Rect localRect = new Rect(0f, 0f, layout.width, layout.height);
int targetHoverIndex = -1;
if (localRect.Contains(evt.localPosition))
{
TryFindMarker(evt.localPosition, localRect, _selectedIndex, PaletteEditorStyle.MarkerSize + 4f, out targetHoverIndex);
if (_selectedIndices != null && !_selectedIndices.Contains(targetHoverIndex))
{
targetHoverIndex = -1;
}
}
OnHoverChanged?.Invoke(targetHoverIndex);
return;
}
if (_draggedColorIndex >= 0 && _draggedColorIndex < _currentColors.Count)
{
Vector3 hsv = Cache.GetHSV(_draggedColorIndex);
Rect localRect = new Rect(0f, 0f, layout.width, layout.height);
if (evt.shiftKey)
{
float delta = (evt.localPosition.y - _dragMousePosStart.y) / (0.8f * localRect.height);
ProcessShiftDragInput(delta, ref hsv, _dragStartSaturation, _dragStartValue);
}
else
{
Vector2 offset = evt.localPosition - (Vector3)localRect.center;
float maxRadius = (localRect.width * 0.5f) - 1f;
float magnitude = Mathf.Clamp01(offset.magnitude / maxRadius);
float angle = Mathf.Atan2(-offset.y, offset.x);
float hue = angle / (2.0f * Mathf.PI);
if (hue < 0.0f) hue += 1.0f;
ProcessDragInput(offset, magnitude, hue, ref hsv, _dragStartSaturation);
}
OnColorModified?.Invoke(_draggedColorIndex, hsv);
evt.StopPropagation();
}
}
private void OnPointerUp(PointerUpEvent evt)
{
if (this.HasPointerCapture(evt.pointerId))
{
this.ReleasePointer(evt.pointerId);
_draggedColorIndex = -1;
evt.StopPropagation();
}
}
public void Cleanup()
{
if (_wheelMaterial != null) { UnityEngine.Object.DestroyImmediate(_wheelMaterial); _wheelMaterial = null; }
if (_wheelTexture != null) { _wheelTexture.Release(); UnityEngine.Object.DestroyImmediate(_wheelTexture); _wheelTexture = null; }
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a291f9584e7d4d04d86da633c1e1b166
@@ -0,0 +1,39 @@
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UISaturationColorWheel.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UISaturationColorWheel : UIBaseColorWheel
{
protected override float ShaderWheelTypeValue => 0.0f;
public UISaturationColorWheel(ColorWheelCache cache) : base(cache) { }
protected override Vector2 GetMarkerPosition(int index, Rect wheelRect)
{
return Cache.GetWheelPosition(index, wheelRect, isValueWheel: false);
}
protected override bool TryFindMarker(Vector2 localPos, Rect wheelRect, int selectedIndex, float radius, out int foundIndex)
{
return Cache.TryFindMarkerAtPosition(localPos, wheelRect, isValueWheel: false, selectedIndex, radius, out foundIndex);
}
protected override void ProcessDragInput(Vector2 offset, float magnitude, float hue, ref Vector3 hsv, float startSaturation)
{
if (magnitude > 0.0001f)
{
hsv.x = hue;
}
hsv.y = magnitude;
}
protected override void ProcessShiftDragInput(float delta, ref Vector3 hsv, float startSaturation, float startValue)
{
hsv.y = Mathf.Clamp(startSaturation - delta, 0.0001f, 0.9999f);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b011e5ddd5aeac249b2b12e08c0259f1
@@ -0,0 +1,44 @@
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIValueColorWheel.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UIValueColorWheel : UIBaseColorWheel
{
protected override float ShaderWheelTypeValue => 1.0f;
public UIValueColorWheel(ColorWheelCache cache) : base(cache)
{
AddToClassList("color-picker__wheel--value-mode");
style.marginLeft = PaletteEditorStyle.Spacing;
}
protected override Vector2 GetMarkerPosition(int index, Rect wheelRect)
{
return Cache.GetWheelPosition(index, wheelRect, isValueWheel: true);
}
protected override bool TryFindMarker(Vector2 localPos, Rect wheelRect, int selectedIndex, float radius, out int foundIndex)
{
return Cache.TryFindMarkerAtPosition(localPos, wheelRect, isValueWheel: true, selectedIndex, radius, out foundIndex);
}
protected override void ProcessDragInput(Vector2 offset, float magnitude, float hue, ref Vector3 hsv, float startSaturation)
{
if (magnitude > 0.0001f)
{
hsv.x = hue;
hsv.y = startSaturation;
}
hsv.z = Mathf.Clamp(magnitude, 0.0001f, 1.0f);
}
protected override void ProcessShiftDragInput(float delta, ref Vector3 hsv, float startSaturation, float startValue)
{
hsv.z = Mathf.Clamp(startValue - delta, 0.0001f, 0.9999f);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b941172303a824d4b9f2550c020993e6
@@ -0,0 +1,320 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIColorControlElement.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UIColorControlElement : VisualElement
{
private Palette _palette;
private int _selectedIndex = -1;
private StyleSheet _cachedStyleSheet;
private readonly Toolbar _modeToolbar;
private readonly List<Button> _tabButtons = new List<Button>();
private readonly VisualElement _slidersContainer;
private readonly UIChannelSliderElement[] _sliders;
private readonly TextField _hexField;
private ColorFieldsMode _currentFieldsMode = ColorFieldsMode.RGB;
private readonly string[] _modeTabs = { "RGB", "HSV", "HSL", "LAB", "Hex" };
private readonly ColorControlCache _sliderCache = new ColorControlCache();
private bool _isUpdatingInternally;
public event Action OnPaletteUpdated;
public UIColorControlElement()
{
LoadAndApplyStyleSheet();
AddToClassList("color-control__main-container");
_modeToolbar = new Toolbar();
_modeToolbar.AddToClassList("color-control__mode-toolbar");
for (int i = 0; i < _modeTabs.Length; i++)
{
int modeIndex = i;
Button tabButton = new Button(() => SwitchMode((ColorFieldsMode)modeIndex))
{
text = _modeTabs[i]
};
tabButton.AddToClassList("color-control__mode-tab-button");
_modeToolbar.Add(tabButton);
_tabButtons.Add(tabButton);
}
Add(_modeToolbar);
_slidersContainer = new VisualElement();
Add(_slidersContainer);
_sliders = new UIChannelSliderElement[4];
for (int i = 0; i < 4; i++)
{
int sliderIndex = i;
_sliders[i] = new UIChannelSliderElement();
_sliders[i].OnValueChanged += (val) => OnSliderMoved(sliderIndex, val);
}
_hexField = new TextField("Hex Color Value");
_hexField.AddToClassList("color-control__hex-field");
_hexField.RegisterValueChangedCallback(evt => OnHexChanged(evt.newValue));
}
private void LoadAndApplyStyleSheet()
{
if (_cachedStyleSheet != null) return;
string[] guids = AssetDatabase.FindAssets("ColorControlStyles t:StyleSheet");
if (guids.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
_cachedStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(path);
if (_cachedStyleSheet != null)
{
styleSheets.Add(_cachedStyleSheet);
}
}
}
public void BindPalette(Palette palette, int selectedIndex)
{
if (_selectedIndex != selectedIndex || _palette != palette)
{
_sliderCache.Clear();
}
_palette = palette;
_selectedIndex = selectedIndex;
RefreshLayoutStructure();
}
private void SwitchMode(ColorFieldsMode newMode)
{
if (_currentFieldsMode == newMode) return;
_sliderCache.Clear();
_currentFieldsMode = newMode;
UpdateTabVisualSelection();
RefreshLayoutStructure();
}
private void UpdateTabVisualSelection()
{
for (int i = 0; i < _tabButtons.Count; i++)
{
if (i == (int)_currentFieldsMode)
{
_tabButtons[i].AddToClassList("color-control__mode-tab-button--active");
}
else
{
_tabButtons[i].RemoveFromClassList("color-control__mode-tab-button--active");
}
}
}
private void RefreshLayoutStructure()
{
_slidersContainer.Clear();
if (_palette == null || _selectedIndex < 0 || _selectedIndex >= _palette.colors.Count) return;
Color currentColor = _palette.colors[_selectedIndex];
if (_currentFieldsMode == ColorFieldsMode.Hex)
{
_isUpdatingInternally = true;
_hexField.SetValueWithoutNotify("#" + ColorUtility.ToHtmlStringRGB(currentColor));
_isUpdatingInternally = false;
_slidersContainer.Add(_hexField);
return;
}
RebuildSliderData(currentColor);
int activeSlidersCount = (_currentFieldsMode == ColorFieldsMode.RGB) ? 4 : 3;
for (int i = 0; i < activeSlidersCount; i++)
{
_slidersContainer.Add(_sliders[i]);
}
UpdateShaderTracks(currentColor);
}
private void RebuildSliderData(Color c)
{
_isUpdatingInternally = true;
switch (_currentFieldsMode)
{
case ColorFieldsMode.RGB:
_sliderCache.GetHSV(c, out _, out _, out float v);
_sliders[0].Setup("R", c.r * 255f, 0, 255, 0, asInt: true);
_sliders[1].Setup("G", c.g * 255f, 0, 255, 1, asInt: true);
_sliders[2].Setup("B", c.b * 255f, 0, 255, 2, asInt: true);
_sliders[3].Setup("Brightness", v * 100f, 0, 100, 3, asInt: true);
break;
case ColorFieldsMode.HSV:
_sliderCache.GetHSV(c, out float h, out float s, out float hsvV);
_sliders[0].Setup("H", h * 360f, 0, 360, 4, asInt: true);
_sliders[1].Setup("S", s * 100f, 0, 100, 5, asInt: true);
_sliders[2].Setup("V", hsvV * 100f, 0, 100, 6, asInt: true);
break;
case ColorFieldsMode.LAB:
_sliderCache.GetLab(c, out float L, out float a, out float b);
_sliders[0].Setup("L", L, 0, 100, 8, asInt: true);
_sliders[1].Setup("a", a, -128, 127, 9, asInt: true);
_sliders[2].Setup("b", b, -128, 127, 10, asInt: true);
break;
case ColorFieldsMode.HSL:
_sliderCache.GetHSL(c, out float hslH, out float hslS, out float hslL);
_sliders[0].Setup("H", hslH * 360f, 0, 360, 11, asInt: true);
_sliders[1].Setup("S", hslS * 100f, 0, 100, 12, asInt: true);
_sliders[2].Setup("L", hslL * 100f, 0, 100, 13, asInt: true);
break;
}
_isUpdatingInternally = false;
}
private void UpdateShaderTracks(Color c)
{
Vector4 trackContext = GetColorSpaceVector(c, _currentFieldsMode, out bool useDirectColor);
int activeCount = (_currentFieldsMode == ColorFieldsMode.RGB) ? 4 : 3;
for (int i = 0; i < activeCount; i++)
{
bool useRawColor = useDirectColor && i < 3;
_sliders[i].UpdateShaderTrack(useRawColor ? (Vector4)c : trackContext);
}
}
private void OnSliderMoved(int sliderIndex, float value)
{
if (_isUpdatingInternally || _palette == null) return;
Color targetColor = _palette.colors[_selectedIndex];
switch (_currentFieldsMode)
{
case ColorFieldsMode.RGB:
if (sliderIndex < 3)
{
targetColor[sliderIndex] = value / 255f;
}
else
{
_sliderCache.GetHSV(targetColor, out float h, out float s, out _);
targetColor = Color.HSVToRGB(h, s, value / 100f);
_sliderCache.UpdateHSV(h, s, value / 100f, targetColor);
}
break;
case ColorFieldsMode.HSV:
_sliderCache.GetHSV(targetColor, out float currentH, out float currentS, out float currentV);
float nextH = (sliderIndex == 0) ? value / 360f : currentH;
float nextS = (sliderIndex == 1) ? value / 100f : currentS;
float nextV = (sliderIndex == 2) ? value / 100f : currentV;
targetColor = Color.HSVToRGB(nextH, nextS, nextV);
_sliderCache.UpdateHSV(nextH, nextS, nextV, targetColor);
break;
case ColorFieldsMode.LAB:
_sliderCache.GetLab(targetColor, out float labL, out float labA, out float labB);
if (sliderIndex == 0) labL = value;
if (sliderIndex == 1) labA = value;
if (sliderIndex == 2) labB = value;
targetColor = VNTGColorSpace.LabToRGB(labL, labA, labB).Clamp01();
_sliderCache.UpdateLab(labL, labA, labB, targetColor);
break;
case ColorFieldsMode.HSL:
_sliderCache.GetHSL(targetColor, out float hslH, out float hslS, out float hslL);
if (sliderIndex == 0) hslH = value / 360f;
if (sliderIndex == 1) hslS = value / 100f;
if (sliderIndex == 2) hslL = value / 100f;
targetColor = VNTGColorSpace.HslToRGB(hslH, hslS, hslL).Clamp01();
_sliderCache.UpdateHSL(hslH, hslS, hslL, targetColor);
break;
}
ApplyColorMutation(targetColor);
_isUpdatingInternally = true;
RebuildSliderData(targetColor);
UpdateShaderTracks(targetColor);
_isUpdatingInternally = false;
}
private Vector4 GetColorSpaceVector(Color c, ColorFieldsMode mode, out bool isRgbBase)
{
isRgbBase = false;
if (mode == ColorFieldsMode.RGB)
{
isRgbBase = true;
_sliderCache.GetHSV(c, out float h, out float s, out float v);
return new Vector4(h, s, v, 1f);
}
if (mode == ColorFieldsMode.HSV)
{
_sliderCache.GetHSV(c, out float h, out float s, out float v);
return new Vector4(h, s, v, 1f);
}
if (mode == ColorFieldsMode.LAB)
{
_sliderCache.GetLab(c, out float L, out float a, out float b);
return new Vector4(L, a, b, 1f);
}
if (mode == ColorFieldsMode.HSL)
{
_sliderCache.GetHSL(c, out float h, out float s, out float l);
return new Vector4(h, s, l, 1f);
}
return Vector4.one;
}
private void OnHexChanged(string newHex)
{
if (_isUpdatingInternally || _palette == null) return;
if (ColorUtility.TryParseHtmlString(newHex, out Color parsedColor))
{
_sliderCache.Clear();
ApplyColorMutation(parsedColor);
}
}
private void ApplyColorMutation(Color finalColor)
{
finalColor.a = _palette.colors[_selectedIndex].a;
Undo.RecordObject(_palette, "Changed Color");
_palette.colors[_selectedIndex] = finalColor;
EditorUtility.SetDirty(_palette);
OnPaletteUpdated?.Invoke();
}
public void Cleanup()
{
for (int i = 0; i < 4; i++)
{
_sliders[i]?.Cleanup();
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 41baf90b9984b384d84c24cd4db85f8c
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIColorPicker.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UIColorPicker : VisualElement
{
private readonly UIBaseColorWheel _wheel;
private readonly ColorWheelCache _cache = new ColorWheelCache();
private List<Color> _colors = new List<Color>() { Color.red };
public event Action<int, Color> OnColorChanged;
public UIColorPicker()
{
_wheel = new UISaturationColorWheel(_cache);
Add(_wheel);
_wheel.OnColorModified += (index, hsv) =>
{
_cache.SetHSV(index, hsv);
Color updatedColor = _cache.GetRGB(index);
_colors[index] = updatedColor;
OnColorChanged?.Invoke(index, updatedColor);
Refresh();
};
_wheel.OnHoverChanged += index => _wheel.HoveringIndex = index;
RegisterCallback<GeometryChangedEvent>(e => Refresh());
Refresh();
}
public void SetColors(List<Color> colors)
{
_colors = colors;
Refresh();
}
private void Refresh()
{
_cache.Sync(_colors);
Vector3 hsv = _cache.GetHSV(0);
_wheel.UpdateVisuals(_colors, 0, hsv.z);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: eece6c1aa43759442b2dd3ed4ea4ab30
@@ -0,0 +1,172 @@
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIPaletteColorPicker.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
[UxmlElement]
public partial class UIPaletteColorPicker : VisualElement
{
private readonly UIBaseColorWheel _satWheel;
private readonly UIColorBar _lightnessBar;
private readonly UIBaseColorWheel _valWheel;
private Palette _palette;
private readonly ColorWheelCache _cache = new ColorWheelCache();
private static StyleSheet _cachedStyleSheet;
public SelectionState Selected { get; set; } = new SelectionState();
public event Action OnPaletteUpdated;
public int HoveringIndex
{
get => _satWheel.HoveringIndex;
set
{
_satWheel.HoveringIndex = value;
_valWheel.HoveringIndex = value;
}
}
public UIPaletteColorPicker()
{
LoadAndApplyStyleSheet();
AddToClassList("color-picker__orchestrator");
style.flexDirection = FlexDirection.Row;
style.alignItems = Align.Center;
style.justifyContent = Justify.Center;
_satWheel = new UISaturationColorWheel(_cache);
_lightnessBar = new UIColorBar();
_valWheel = new UIValueColorWheel(_cache);
Add(_satWheel);
Add(_lightnessBar);
Add(_valWheel);
_satWheel.OnColorModified += HandleColorModification;
_valWheel.OnColorModified += HandleColorModification;
_lightnessBar.OnValueChanged += HandleLightnessModification;
_satWheel.OnSelectionChanged += HandleSelectionChanged;
_valWheel.OnSelectionChanged += HandleSelectionChanged;
_satWheel.OnHoverChanged += index => HoveringIndex = index;
_valWheel.OnHoverChanged += index => HoveringIndex = index;
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
}
private void LoadAndApplyStyleSheet()
{
if (_cachedStyleSheet != null)
{
styleSheets.Add(_cachedStyleSheet);
return;
}
string[] guids = AssetDatabase.FindAssets("ColorWheelStyles t:StyleSheet");
if (guids.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
_cachedStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(path);
if (_cachedStyleSheet != null)
{
styleSheets.Add(_cachedStyleSheet);
}
}
}
private void OnGeometryChanged(GeometryChangedEvent evt)
{
RegenerateTextures();
}
public void BindPalette(Palette palette)
{
_palette = palette;
RefreshCacheAndTextures();
}
public void RefreshCacheAndTextures()
{
if (_palette == null) return;
_cache.Sync(_palette.colors);
RegenerateTextures();
}
public void RegenerateTextures()
{
if (_palette == null) return;
bool hasSelected = !Selected.IsEmpty();
int primarySelection = Selected.GetPrimarySelection();
if (primarySelection < 0 || primarySelection >= _palette.colors.Count) return;
Vector3 selectedHSV = hasSelected ? _cache.GetHSV(primarySelection) : new Vector3(0f, 1f, 1f);
_satWheel.UpdateVisuals(_palette.colors, primarySelection, selectedHSV.z, Selected);
_valWheel.UpdateVisuals(_palette.colors, primarySelection, selectedHSV.y, Selected);
if (hasSelected)
{
VNTGColorSpace.HSVToHSL(selectedHSV.x, selectedHSV.y, selectedHSV.z, out _, out _, out float lightness);
_lightnessBar.UpdateVisuals(selectedHSV, lightness, true);
}
else
{
_lightnessBar.UpdateVisuals(selectedHSV, 0.5f, false);
}
}
private void HandleSelectionChanged(int index)
{
Selected.SetPrimarySelection(index);
OnPaletteUpdated?.Invoke();
RegenerateTextures();
}
private void HandleColorModification(int index, Vector3 newHSV)
{
if (_palette == null || index < 0 || index >= _palette.colors.Count) return;
_cache.SetHSV(index, newHSV);
Color newColor = Color.HSVToRGB(newHSV.x, newHSV.y, newHSV.z);
Undo.RecordObject(_palette, "Changed Color");
_palette.colors[index] = newColor;
EditorUtility.SetDirty(_palette);
OnPaletteUpdated?.Invoke();
RegenerateTextures();
}
private void HandleLightnessModification(float newLightness)
{
if (_palette == null || Selected.IsEmpty()) return;
int primaryIndex = Selected.GetPrimarySelection();
Vector3 hsv = _cache.GetHSV(primaryIndex);
VNTGColorSpace.HSVToHSL(hsv.x, hsv.y, hsv.z, out float h, out float s, out _);
VNTGColorSpace.HSLToHSV(h, s, newLightness, out _, out float newS, out float newV);
HandleColorModification(primaryIndex, new Vector3(hsv.x, newS, newV));
}
public void Cleanup()
{
_satWheel.Cleanup();
_lightnessBar.Cleanup();
_valWheel.Cleanup();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: deda3835bb15bd945bf74b818799ee31
@@ -0,0 +1,884 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIPaletteGridElement.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UIPaletteGridElement : VisualElement
{
private Palette _palette;
private StyleSheet _cachedStyleSheet;
private readonly VisualElement _gridContainer;
private readonly List<UIColorSwatch> _swatchElements = new List<UIColorSwatch>();
private VisualElement _resizeHandle;
private VisualElement _buttonContainer;
private VisualElement _plusButton;
private VisualElement _minusButton;
private bool _isDraggingSizeHandle;
private int _draggedSwatchIndex = -1;
private float _dragAccumulatorX;
private float _dragAccumulatorY;
private const float ResizeThreshold = 36.0f;
private int _originalPaletteSizeBeforeDrag = -1;
private readonly List<Color> _colorHistoryCache = new List<Color>();
public event Action<int> OnSelectionChanged;
public event Action<int> OnSwatchHoverChanged;
public event Action OnPaletteUpdated;
private HashSet<int> _initialSelectionState = new HashSet<int>();
private int _lastSelectedIndex;
private int _shiftDragAnchorIndex = -1;
public SelectionState Selection { get; set; } = new SelectionState();
public UIPaletteGridElement()
{
style.paddingLeft = style.paddingRight = style.marginTop = style.marginBottom = 4f;
_buttonContainer = new VisualElement
{
style =
{
flexDirection = FlexDirection.Row,
width = Length.Percent(100),
marginBottom = 4f
}
};
Add(_buttonContainer);
_gridContainer = new VisualElement();
_gridContainer.AddToClassList("palette-grid__container");
Add(_gridContainer);
focusable = true;
RegisterCallback<KeyDownEvent>(OnKeyDown);
this.AddManipulator(new BoxSelectionManipulator(ProcessSelection, shouldRenderRegion: false));
this.AddManipulator(new ContextualMenuManipulator((ContextualMenuPopulateEvent evt) =>
{
if (_palette == null) return;
bool hasSelection = !Selection.IsEmpty();
int selectedCount = Selection.Indices.Count;
if (hasSelection)
{
string deleteLabel = selectedCount > 1 ? $"Delete {selectedCount} Selected Items" : "Delete Selected Item";
evt.menu.AppendAction(deleteLabel, (action) => DeleteSelectedSwatches());
string duplicateLabel = selectedCount > 1 ? $"Duplicate {selectedCount} Selected Items" : "Duplicate Selected Item";
evt.menu.AppendAction(duplicateLabel, (action) => DuplicateSelectedSwatches());
evt.menu.AppendSeparator();
string copyLabel = selectedCount > 1 ? $"Copy {selectedCount} Colors" : "Copy Color";
evt.menu.AppendAction(copyLabel, (action) => CopySelectedToClipboard());
string cutLabel = selectedCount > 1 ? $"Cut {selectedCount} Colors" : "Cut Color";
evt.menu.AppendAction(cutLabel, (action) =>
{
CopySelectedToClipboard();
DeleteSelectedSwatches();
});
}
DropdownMenuAction.Status pasteStatus = IsClipboardValidHex()
? DropdownMenuAction.Status.Normal
: DropdownMenuAction.Status.Disabled;
evt.menu.AppendAction("Paste Color(s)", (action) => PasteFromClipboard(), pasteStatus);
}));
RegisterCallback<PointerMoveEvent>(OnPointerMove);
RegisterCallback<PointerUpEvent>(OnPointerUp);
}
private void OnKeyDown(KeyDownEvent evt)
{
if (evt.keyCode == KeyCode.Delete)
{
if (_palette != null && !Selection.IsEmpty())
{
DeleteSelectedSwatches();
evt.StopPropagation();
}
return;
}
if (evt.actionKey)
{
if (evt.keyCode == KeyCode.C)
{
if (_palette != null && !Selection.IsEmpty())
{
CopySelectedToClipboard();
evt.StopPropagation();
}
}
else if (evt.keyCode == KeyCode.V)
{
if (_palette != null && IsClipboardValidHex())
{
PasteFromClipboard();
evt.StopPropagation();
}
}
if (evt.keyCode == KeyCode.X)
{
if (_palette != null && !Selection.IsEmpty())
{
CopySelectedToClipboard();
DeleteSelectedSwatches();
evt.StopPropagation();
}
}
else if (evt.keyCode == KeyCode.A)
{
if (_palette != null && _palette.colors.Count > 0)
{
SelectAllSwatches();
evt.StopPropagation();
}
}
}
}
private void SelectAllSwatches()
{
if (_palette == null) return;
Selection.Clear();
for (int i = 0; i < _palette.colors.Count; i++)
{
Selection.Add(i);
}
_lastSelectedIndex = _palette.colors.Count - 1;
RefreshSwatchVisuals();
OnSelectionChanged?.Invoke(Selection.GetPrimarySelection());
}
private void DuplicateSelectedSwatches()
{
if (_palette == null || Selection.IsEmpty()) return;
Undo.RecordObject(_palette, "Duplicate Colors");
List<int> sortedSelected = new List<int>(Selection.Indices);
sortedSelected.Sort();
List<Color> colorsToDuplicate = new List<Color>();
foreach (int idx in sortedSelected)
{
colorsToDuplicate.Add(_palette.colors[idx]);
}
int insertionIndex = sortedSelected[sortedSelected.Count - 1] + 1;
_palette.colors.InsertRange(insertionIndex, colorsToDuplicate);
Selection.Clear();
for (int i = 0; i < colorsToDuplicate.Count; i++)
{
Selection.Add(insertionIndex + i);
}
_lastSelectedIndex = insertionIndex;
FinalizePaletteMutation();
}
private void CopySelectedToClipboard()
{
if (_palette == null || Selection.IsEmpty()) return;
List<int> sortedSelected = new List<int>(Selection.Indices);
sortedSelected.Sort();
List<string> hexStrings = new List<string>();
foreach (int idx in sortedSelected)
{
hexStrings.Add("#" + ColorUtility.ToHtmlStringRGBA(_palette.colors[idx]));
}
EditorGUIUtility.systemCopyBuffer = string.Join(", ", hexStrings);
}
private void PasteFromClipboard()
{
if (_palette == null) return;
string clipboard = EditorGUIUtility.systemCopyBuffer;
if (string.IsNullOrEmpty(clipboard)) return;
string[] tokens = clipboard.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
List<Color> parsedColors = new List<Color>();
foreach (string token in tokens)
{
string cleanToken = token.Trim();
if (ColorUtility.TryParseHtmlString(cleanToken, out Color parsedColor))
{
parsedColors.Add(parsedColor);
}
}
if (parsedColors.Count == 0) return;
Undo.RecordObject(_palette, "Paste Colors from Clipboard");
int insertionIndex;
if (!Selection.IsEmpty())
{
List<int> sortedSelected = new List<int>(Selection.Indices);
sortedSelected.Sort();
insertionIndex = sortedSelected[sortedSelected.Count - 1] + 1;
_palette.colors.InsertRange(insertionIndex, parsedColors);
}
else
{
insertionIndex = _palette.colors.Count;
_palette.colors.AddRange(parsedColors);
}
Selection.Clear();
for (int i = 0; i < parsedColors.Count; i++)
{
Selection.Add(insertionIndex + i);
}
_lastSelectedIndex = insertionIndex;
FinalizePaletteMutation();
}
private bool IsClipboardValidHex()
{
string clipboard = EditorGUIUtility.systemCopyBuffer;
if (string.IsNullOrEmpty(clipboard)) return false;
string firstToken = clipboard.Split(',')[0].Trim();
return ColorUtility.TryParseHtmlString(firstToken, out _);
}
private void DeleteSelectedSwatches()
{
if (_palette == null || Selection.IsEmpty()) return;
Undo.RecordObject(_palette, "Delete Selected Swatches");
List<int> indicesToDelete = new List<int>(Selection.Indices);
indicesToDelete.Sort((a, b) => b.CompareTo(a));
foreach (int index in indicesToDelete)
{
if (index >= 0 && index < _palette.colors.Count)
{
_palette.colors.RemoveAt(index);
}
}
Selection.Clear();
if (_palette.colors.Count > 0)
{
Selection.Add(0);
_lastSelectedIndex = 0;
}
else
{
_lastSelectedIndex = -1;
}
FinalizePaletteMutation();
}
private void ProcessSelection(Rect selectionRect, Vector2 pointerPos, BoxSelectionManipulator.SelectionMode mode, bool isAdditive, bool isRangeSelect)
{
if (_isDraggingSizeHandle || _palette == null) return;
if (mode == BoxSelectionManipulator.SelectionMode.StartedDrag)
{
_initialSelectionState = new HashSet<int>(Selection.Indices);
_shiftDragAnchorIndex = isRangeSelect && _shiftDragAnchorIndex == -1 ? _lastSelectedIndex : -1;
if (!isRangeSelect && !isAdditive)
{
Selection.Clear();
_initialSelectionState.Clear();
}
return;
}
if (isRangeSelect) HandleRangeSelection(pointerPos, mode, isAdditive);
else HandleBoxOrClickSelection(selectionRect, isAdditive);
if (mode == BoxSelectionManipulator.SelectionMode.SelectionConfirmed)
{
if (Selection.IsEmpty())
{
//Selection.Add(0);
SelectAllSwatches();
_lastSelectedIndex = 0;
}
_shiftDragAnchorIndex = -1;
_initialSelectionState?.Clear();
}
RefreshSwatchVisuals();
OnSelectionChanged?.Invoke(Selection.GetPrimarySelection());
}
private void HandleRangeSelection(Vector2 pointerPos, BoxSelectionManipulator.SelectionMode mode, bool isAdditive)
{
Vector2 currentGridPos = this.ChangeCoordinatesTo(_gridContainer, pointerPos);
int currentHoveredIndex = _swatchElements.FindIndex(swatch => swatch.layout.Contains(currentGridPos));
if (currentHoveredIndex == -1 || _shiftDragAnchorIndex == -1) return;
int targetIndex = Selection.IsEmpty() ? _shiftDragAnchorIndex : Selection.GetPrimarySelection();
Selection.Clear();
int start = Mathf.Min(targetIndex, currentHoveredIndex);
int end = Mathf.Max(targetIndex, currentHoveredIndex);
for (int i = start; i <= end; i++) Selection.Add(i);
if (isAdditive) Selection.UnionWith(_initialSelectionState);
if (mode == BoxSelectionManipulator.SelectionMode.SelectionConfirmed) _lastSelectedIndex = currentHoveredIndex;
}
private void HandleBoxOrClickSelection(Rect selectionRect, bool isAdditive)
{
Vector2 gridSpaceMin = this.ChangeCoordinatesTo(_gridContainer, selectionRect.min);
Vector2 gridSpaceMax = this.ChangeCoordinatesTo(_gridContainer, selectionRect.max);
Rect gridSpaceRect = Rect.MinMaxRect(gridSpaceMin.x, gridSpaceMin.y, gridSpaceMax.x, gridSpaceMax.y);
bool isSingleClick = gridSpaceRect.size.sqrMagnitude == 0f;
int primaryHitIndex = -1;
for (int i = 0; i < _swatchElements.Count; i++)
{
bool isHit = isSingleClick
? _swatchElements[i].layout.Contains(gridSpaceRect.min)
: _swatchElements[i].layout.Overlaps(gridSpaceRect);
if (isHit)
{
if (isSingleClick) primaryHitIndex = i;
if (isAdditive && _initialSelectionState.Contains(i))
Selection.Remove(i);
else
Selection.Add(i);
}
else
{
if (isAdditive && _initialSelectionState.Contains(i))
Selection.Add(i);
else
Selection.Remove(i);
}
}
if (isSingleClick && primaryHitIndex != -1) _lastSelectedIndex = primaryHitIndex;
}
public void BindPalette(Palette palette, SelectionState selection)
{
if (selection != null) Selection = selection;
if (_palette == palette && _palette != null && _swatchElements.Count == _palette.colors.Count)
{
UpdateHistoryCache();
RefreshSwatchVisuals();
return;
}
_palette = palette;
UpdateHistoryCache();
RebuildGridStructure();
RefreshSwatchVisuals();
}
private void UpdateHistoryCache()
{
if (_palette?.colors == null) return;
if (_colorHistoryCache.Count != _palette.colors.Count)
{
_colorHistoryCache.Clear();
_colorHistoryCache.AddRange(_palette.colors);
return;
}
for (int i = 0; i < _palette.colors.Count; i++)
{
_colorHistoryCache[i] = _palette.colors[i];
}
}
private void RefreshSwatchVisuals()
{
if (_palette == null) return;
//bool isActivelyInteracting = _isDraggingSizeHandle || _draggedSwatchIndex >= 0;
//DisplayStyle buttonVisibility = isActivelyInteracting ? DisplayStyle.None : DisplayStyle.Flex;
//if (_plusButton != null) _plusButton.style.display = buttonVisibility;
//if (_minusButton != null) _minusButton.style.display = buttonVisibility;
float gridWidth = _gridContainer.resolvedStyle.width > 0 ? _gridContainer.resolvedStyle.width : 300f;
int columns = Mathf.Max(1, Mathf.FloorToInt(gridWidth / ResizeThreshold));
for (int i = 0; i < _swatchElements.Count; i++)
{
bool isSelected = Selection.Contains(i);
_swatchElements[i].UpdateSwatchData(_palette.colors[i], i, isSelected, i == Selection.GetPrimarySelection());
bool topSelected = i >= columns && Selection.Contains(i - columns);
bool bottomSelected = i + columns < _swatchElements.Count && Selection.Contains(i + columns);
bool leftSelected = i % columns != 0 && Selection.Contains(i - 1);
bool rightSelected = (i + 1) % columns != 0 && Selection.Contains(i + 1);
_swatchElements[i].UpdateContourBorders(
showTop: isSelected && !topSelected,
showBottom: isSelected && !bottomSelected,
showLeft: isSelected && !leftSelected,
showRight: isSelected && !rightSelected
);
}
}
private StyleSheet GetOrLoadStyleSheet()
{
if (_cachedStyleSheet != null) return _cachedStyleSheet;
string[] guids = AssetDatabase.FindAssets("SwatchStyles t:StyleSheet");
if (guids.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
_cachedStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(path);
if (_cachedStyleSheet != null) styleSheets.Add(_cachedStyleSheet);
}
return _cachedStyleSheet;
}
private void RebuildGridStructure()
{
_gridContainer.Clear();
_swatchElements.Clear();
if (_palette == null) return;
StyleSheet standardStyleSheet = GetOrLoadStyleSheet();
for (int i = 0; i < _palette.colors.Count; i++)
{
var swatch = CreateSwatchElement(_palette.colors[i], i, standardStyleSheet);
_swatchElements.Add(swatch);
_gridContainer.Add(swatch);
}
InitializeAddButton();
InitializeRemoveButton();
InitializeResizeHandle();
_gridContainer.Add(_resizeHandle);
_gridContainer.RegisterCallback<GeometryChangedEvent>(OnGridGeometryChanged);
}
private void OnGridGeometryChanged(GeometryChangedEvent evt)
{
if (evt.newRect.width <= 0 || evt.oldRect.width == evt.newRect.width)
return;
RefreshSwatchVisuals();
}
private UIColorSwatch CreateSwatchElement(Color color, int index, StyleSheet sheet)
{
var swatch = new UIColorSwatch(color, index, Selection.Contains(index), sheet);
swatch.OnSwatchDragStarted += StartSwatchDrag;
swatch.OnSwatchHoverChanged += (hoveredIndex) => OnSwatchHoverChanged?.Invoke(hoveredIndex);
return swatch;
}
private void InitializeAddButton()
{
if (_plusButton != null) return;
_plusButton = new Button(() => AlterPaletteSizeByAmount(1))
{
text = "+",
style =
{
width = PaletteEditorStyle.RectSize,
height = PaletteEditorStyle.RectSize,
fontSize = 14f,
unityFontStyleAndWeight = FontStyle.Bold,
marginRight = 4f
}
};
_plusButton.AddToClassList("palette-modifier-btn");
_buttonContainer.Add(_plusButton);
}
private void InitializeRemoveButton()
{
if (_minusButton != null) return;
_minusButton = new Button(() => AlterPaletteSizeByAmount(-1))
{
text = "-",
style =
{
width = PaletteEditorStyle.RectSize,
height = PaletteEditorStyle.RectSize,
fontSize = 14f,
unityFontStyleAndWeight = FontStyle.Bold,
marginRight = 2f
}
};
_minusButton.AddToClassList("palette-modifier-btn");
_buttonContainer.Add(_minusButton);
}
private void InitializeResizeHandle()
{
if (_resizeHandle != null) return;
StyleSheet standardStyleSheet = GetOrLoadStyleSheet();
_resizeHandle = new VisualElement
{
pickingMode = PickingMode.Position,
style =
{
width = PaletteEditorStyle.RectSize,
height = PaletteEditorStyle.RectSize,
backgroundColor = PaletteEditorStyle.HandleRectColor,
justifyContent = Justify.Center,
alignItems = Align.Center
}
};
_resizeHandle.AddToClassList("resize-handle");
_resizeHandle.AddToClassList("swatch-grid-resizing");
Label handleLabel = new Label("↔")
{
pickingMode = PickingMode.Ignore,
style = { color = PaletteEditorStyle.HandleIconColor }
};
_resizeHandle.Add(handleLabel);
_resizeHandle.RegisterCallback<PointerDownEvent>(e =>
{
if (e.button != 0) return;
_isDraggingSizeHandle = true;
_dragAccumulatorX = _dragAccumulatorY = 0f;
AddToClassList("swatch-grid-resizing");
RefreshSwatchVisuals();
this.CapturePointer(e.pointerId);
e.StopPropagation();
});
}
private void AlterPaletteSizeByAmount(int amount)
{
if (_palette == null) return;
int newTotalCount = Mathf.Max(1, _palette.colors.Count + amount);
if (newTotalCount == _palette.colors.Count) return;
Undo.RecordObject(_palette, amount > 0 ? "Added Color" : "Removed Color");
while (_palette.colors.Count < newTotalCount)
{
_palette.colors.Add(GetFillerColor(_palette.colors.Count, false));
}
while (_palette.colors.Count > newTotalCount)
{
int targetIdx = _palette.colors.Count - 1;
_palette.colors.RemoveAt(targetIdx);
if (Selection.Contains(targetIdx))
{
Selection.Remove(targetIdx);
if (targetIdx > 0) Selection.Add(targetIdx - 1);
}
}
FinalizePaletteMutation();
}
private void StartSwatchDrag(UIColorSwatch swatch)
{
_draggedSwatchIndex = swatch.Index;
if (_palette != null) _originalPaletteSizeBeforeDrag = _palette.colors.Count;
RefreshSwatchVisuals();
this.CapturePointer(PointerId.mousePointerId);
}
private void OnPointerMove(PointerMoveEvent evt)
{
if (_isDraggingSizeHandle)
{
ProcessResizeDelta(evt.deltaPosition);
evt.StopPropagation();
return;
}
if (_draggedSwatchIndex >= 0)
{
ProcessReorderingTarget(evt.localPosition);
evt.StopPropagation();
}
}
private void OnPointerUp(PointerUpEvent evt)
{
if (_isDraggingSizeHandle)
{
_isDraggingSizeHandle = false;
RemoveFromClassList("swatch-grid-resizing");
RefreshSwatchVisuals();
if (this.HasPointerCapture(evt.pointerId)) this.ReleasePointer(evt.pointerId);
evt.StopPropagation();
}
if (_draggedSwatchIndex >= 0)
{
bool structureChanged = false;
while (_palette.colors.Count > _originalPaletteSizeBeforeDrag && _swatchElements.Count > 1)
{
int lastIndex = _palette.colors.Count - 1;
if (Selection.Contains(lastIndex)) break;
Undo.RecordObject(_palette, "Resized Palette");
_palette.colors.RemoveAt(lastIndex);
var visualToRemove = _swatchElements[lastIndex];
if (_gridContainer.Contains(visualToRemove)) _gridContainer.Remove(visualToRemove);
_swatchElements.RemoveAt(lastIndex);
structureChanged = true;
}
if (structureChanged)
{
FinalizePaletteMutation();
}
_draggedSwatchIndex = -1;
_originalPaletteSizeBeforeDrag = -1;
RefreshSwatchVisuals();
if (this.HasPointerCapture(evt.pointerId)) this.ReleasePointer(evt.pointerId);
UpdateHistoryCache();
evt.StopPropagation();
}
}
private Color GetFillerColor(int index, bool ignoreCache = false)
{
return (!ignoreCache && index < _colorHistoryCache.Count) ? _colorHistoryCache[index] : Color.black;
}
private void FinalizePaletteMutation()
{
EditorUtility.SetDirty(_palette);
OnPaletteUpdated?.Invoke();
RebuildGridStructure();
RefreshSwatchVisuals();
}
private void ProcessReorderingTarget(Vector2 localizedMousePos)
{
Vector2 gridSpaceMousePos = this.ChangeCoordinatesTo(_gridContainer, localizedMousePos);
float gridWidth = _gridContainer.resolvedStyle.width > 0 ? _gridContainer.resolvedStyle.width : 300f;
int columns = Mathf.Max(1, Mathf.FloorToInt(gridWidth / ResizeThreshold));
bool isMultiDrag = Selection.Indices.Count > 1 && Selection.Contains(_draggedSwatchIndex);
List<int> sortedIndices = isMultiDrag ? new List<int>(Selection.Indices) : new List<int> { _draggedSwatchIndex };
sortedIndices.Sort();
int handleOffset = 0;
if (_swatchElements.Count > 0)
{
var lastSwatch = _swatchElements[_swatchElements.Count - 1];
bool isBelowGrid = gridSpaceMousePos.y > lastSwatch.layout.yMax;
bool isPastLastElementSameRow = gridSpaceMousePos.y > lastSwatch.layout.yMin && gridSpaceMousePos.x > lastSwatch.layout.xMax;
if (isBelowGrid || isPastLastElementSameRow)
{
int targetColumn = Mathf.FloorToInt(gridSpaceMousePos.x / ResizeThreshold);
targetColumn = Mathf.Clamp(targetColumn, 0, columns - 1);
float swatchHeight = lastSwatch.layout.height > 0 ? lastSwatch.layout.height : ResizeThreshold;
int targetRow = Mathf.FloorToInt(gridSpaceMousePos.y / swatchHeight);
int desiredFlatIndex = (targetRow * columns) + targetColumn;
int gridCountWithoutDragged = _palette.colors.Count - sortedIndices.Count;
int targetInsertIndex = desiredFlatIndex - handleOffset;
if (targetInsertIndex > gridCountWithoutDragged)
{
int slotsToCreate = targetInsertIndex - gridCountWithoutDragged;
int currentCount = _palette.colors.Count;
List<Color> newFillerColors = new List<Color>();
StyleSheet activeSheet = GetOrLoadStyleSheet();
for (int s = 0; s < slotsToCreate; s++)
newFillerColors.Add(GetFillerColor(currentCount + s, true));
int insertIndex = gridCountWithoutDragged + slotsToCreate;
MoveColorsAndVisuals(sortedIndices, insertIndex, handleOffset, newFillerColors, activeSheet);
return;
}
}
}
for (int i = 0; i < _swatchElements.Count; i++)
{
if (i == _draggedSwatchIndex) continue;
if (_swatchElements[i].layout.Contains(gridSpaceMousePos))
{
int insertIndex = Mathf.Clamp(i - handleOffset, 0, _palette.colors.Count - sortedIndices.Count);
if (insertIndex == sortedIndices[0]) return;
MoveColorsAndVisuals(sortedIndices, insertIndex, handleOffset, null, null);
break;
}
}
}
private void MoveColorsAndVisuals(List<int> sortedIndices, int insertIndex, int handleOffset, List<Color> fillerColors, StyleSheet activeSheet)
{
Undo.RecordObject(_palette, fillerColors != null ? "Expanded Palette" : "Reordered Color");
List<Color> targetColors = new List<Color>();
List<UIColorSwatch> visualsToMove = new List<UIColorSwatch>();
foreach (int idx in sortedIndices)
{
targetColors.Add(_palette.colors[idx]);
visualsToMove.Add(_swatchElements[idx]);
}
for (int k = sortedIndices.Count - 1; k >= 0; k--)
{
_palette.colors.RemoveAt(sortedIndices[k]);
_swatchElements.RemoveAt(sortedIndices[k]);
}
foreach (var visual in visualsToMove)
{
if (_gridContainer.Contains(visual)) _gridContainer.Remove(visual);
}
if (fillerColors != null)
{
_palette.colors.AddRange(fillerColors);
_palette.colors.InsertRange(insertIndex, targetColors);
for (int s = 0; s < fillerColors.Count; s++)
{
var newSwatch = CreateSwatchElement(fillerColors[s], _palette.colors.Count - fillerColors.Count + s, activeSheet);
_swatchElements.Add(newSwatch);
_gridContainer.Insert(_swatchElements.Count - 1, newSwatch);
}
}
else
{
_palette.colors.InsertRange(insertIndex, targetColors);
}
for (int m = 0; m < visualsToMove.Count; m++)
{
_swatchElements.Insert(insertIndex + m, visualsToMove[m]);
_gridContainer.Insert(insertIndex + m, visualsToMove[m]);
}
Selection.Clear();
for (int m = 0; m < targetColors.Count; m++) Selection.Add(insertIndex + m);
_draggedSwatchIndex = insertIndex + handleOffset;
_resizeHandle.BringToFront();
EditorUtility.SetDirty(_palette);
OnSelectionChanged?.Invoke(_draggedSwatchIndex);
OnPaletteUpdated?.Invoke();
RefreshSwatchVisuals();
}
private void ProcessResizeDelta(Vector3 delta)
{
_dragAccumulatorX += delta.x;
_dragAccumulatorY += delta.y;
int itemMutationAmount = 0;
if (Mathf.Abs(_dragAccumulatorX) >= ResizeThreshold)
{
itemMutationAmount += Mathf.RoundToInt(_dragAccumulatorX / ResizeThreshold);
_dragAccumulatorX %= ResizeThreshold;
}
if (Mathf.Abs(_dragAccumulatorY) >= ResizeThreshold)
{
float computedWidth = resolvedStyle.width > 0 ? resolvedStyle.width : 300f;
int elementsPerRow = Mathf.Max(1, Mathf.FloorToInt(computedWidth / PaletteEditorStyle.RectSize));
itemMutationAmount += Mathf.RoundToInt(_dragAccumulatorY / ResizeThreshold) * elementsPerRow;
_dragAccumulatorY %= ResizeThreshold;
}
if (itemMutationAmount != 0)
{
int newTotalCount = Mathf.Max(1, _palette.colors.Count + itemMutationAmount);
if (newTotalCount != _palette.colors.Count)
{
Undo.RecordObject(_palette, "Resized Palette");
while (_palette.colors.Count < newTotalCount)
{
_palette.colors.Add(GetFillerColor(_palette.colors.Count, false));
}
while (_palette.colors.Count > newTotalCount)
{
int targetIdx = _palette.colors.Count - 1;
_palette.colors.RemoveAt(targetIdx);
if (Selection.Contains(targetIdx))
{
Selection.Remove(targetIdx);
if (targetIdx > 0) Selection.Add(targetIdx - 1);
}
}
EditorUtility.SetDirty(_palette);
FinalizePaletteMutation();
}
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a9e61469286d36141b3656352f20e883
@@ -0,0 +1,34 @@
using UnityEngine;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UISelectionBox.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UISelectionBox : VisualElement
{
public UISelectionBox()
{
style.position = Position.Absolute;
style.backgroundColor = new Color(0.2f, 0.5f, 1.0f, 0.15f);
style.borderLeftColor = style.borderRightColor = style.borderTopColor = style.borderBottomColor = new Color(0.2f, 0.6f, 1f, 0.8f);
style.borderLeftWidth = style.borderRightWidth = style.borderTopWidth = style.borderBottomWidth = 1f;
pickingMode = PickingMode.Ignore;
}
public void UpdatePosition(Vector2 start, Vector2 current)
{
float x = Mathf.Min(start.x, current.x);
float y = Mathf.Min(start.y, current.y);
float w = Mathf.Abs(start.x - current.x);
float h = Mathf.Abs(start.y - current.y);
style.left = x;
style.top = y;
style.width = w;
style.height = h;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6aa6b99deefa820498b4057bf0745b0d
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 99c29fe503af11047bf26e71a305b8d1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,468 @@
using ColbyO.VNTG.CRT;
using ColbyO.VNTG.PSX;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.UIElements;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: UIPaletteView.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.ColorPalette.Editor
{
public class UIPaletteView : VisualElement
{
private readonly DropdownField _cameraDropdown;
private readonly VisualElement _viewportContainer;
private readonly VisualElement _baseImage;
private readonly VisualElement _filteredMask;
private readonly VisualElement _filteredImage;
private readonly VisualElement _sliderHandle;
private Camera _previewCamera;
private Volume _localVolume;
private VolumeProfile _internalRuntimeCloneProfile;
private RenderTexture _renderTextureBase;
private RenderTexture _renderTextureFiltered;
private readonly List<Camera> _foundCameras = new List<Camera>();
private Camera _explicitSelectedCamera;
private bool _useSceneViewFallback = true;
private Palette _currentPaletteAsset;
private bool _isDraggingSlider;
private float _splitPercentage = 0.5f;
private Rect _imageBounds;
public UIPaletteView()
{
style.flexGrow = 1;
style.flexShrink = 1;
style.flexBasis = new StyleLength(StyleKeyword.Auto);
style.minHeight = 400;
style.marginTop = 10;
style.marginBottom = 10;
VisualElement headerControls = new VisualElement();
headerControls.style.flexDirection = FlexDirection.Row;
headerControls.style.alignItems = Align.Center;
headerControls.style.justifyContent = Justify.SpaceBetween;
headerControls.style.marginBottom = 4;
Label titleLabel = new Label("Palette Preview");
titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
titleLabel.style.fontSize = 14;
headerControls.Add(titleLabel);
_cameraDropdown = new DropdownField("")
{
style = { width = 250 }
};
_cameraDropdown.RegisterCallback<PointerDownEvent>(evt => RefreshCameraDropdownOptions());
_cameraDropdown.RegisterValueChangedCallback(OnDropdownValueChanged);
headerControls.Add(_cameraDropdown);
Add(headerControls);
_viewportContainer = new VisualElement();
_viewportContainer.style.flexGrow = 1;
_viewportContainer.style.position = Position.Relative;
_viewportContainer.style.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 1f);
_viewportContainer.style.overflow = Overflow.Hidden;
_viewportContainer.RegisterCallback<GeometryChangedEvent>(OnViewportGeometryChanged);
Add(_viewportContainer);
_baseImage = new VisualElement();
_baseImage.style.position = Position.Absolute;
_baseImage.style.backgroundSize = new BackgroundSize(BackgroundSizeType.Contain);
_viewportContainer.Add(_baseImage);
_filteredMask = new VisualElement();
_filteredMask.style.position = Position.Absolute;
_filteredMask.style.overflow = Overflow.Hidden;
_viewportContainer.Add(_filteredMask);
_filteredImage = new VisualElement();
_filteredImage.style.position = Position.Absolute;
_filteredImage.style.backgroundSize = new BackgroundSize(BackgroundSizeType.Contain);
_filteredMask.Add(_filteredImage);
_sliderHandle = new VisualElement();
_sliderHandle.style.position = Position.Absolute;
_sliderHandle.style.top = 0; _sliderHandle.style.bottom = 0;
_sliderHandle.style.width = 4;
_sliderHandle.style.marginLeft = -2;
_sliderHandle.style.backgroundColor = Color.white;
_sliderHandle.style.borderLeftColor = Color.black;
_sliderHandle.style.borderRightColor = Color.black;
_sliderHandle.style.borderLeftWidth = 1;
_sliderHandle.style.borderRightWidth = 1;
_sliderHandle.RegisterCallback<PointerDownEvent>(OnPointerDown);
_sliderHandle.RegisterCallback<PointerMoveEvent>(OnPointerMove);
_sliderHandle.RegisterCallback<PointerUpEvent>(OnPointerUp);
_sliderHandle.RegisterCallback<PointerCaptureOutEvent>(OnPointerCaptureOut);
_viewportContainer.Add(_sliderHandle);
_viewportContainer.RegisterCallback<PointerDownEvent>(OnPointerDown);
_viewportContainer.RegisterCallback<PointerMoveEvent>(OnPointerMove);
_viewportContainer.RegisterCallback<PointerUpEvent>(OnPointerUp);
_viewportContainer.RegisterCallback<PointerCaptureOutEvent>(OnPointerCaptureOut);
RegisterCallback<AttachToPanelEvent>(OnAttached);
RegisterCallback<DetachFromPanelEvent>(OnDetached);
InitializeRendering();
}
private void OnViewportGeometryChanged(GeometryChangedEvent evt)
{
UpdateRenderResolution();
UpdateSplitterPositions();
}
private void UpdateSplitterPositions()
{
if (_imageBounds.width <= 0) return;
float splitXWithinImage = _imageBounds.width * _splitPercentage;
_filteredMask.style.left = _imageBounds.x;
_filteredMask.style.top = _imageBounds.y;
_filteredMask.style.width = splitXWithinImage;
_filteredMask.style.height = _imageBounds.height;
_filteredImage.style.width = _imageBounds.width;
_filteredImage.style.height = _imageBounds.height;
_filteredImage.style.left = 0;
_sliderHandle.style.left = _imageBounds.x + splitXWithinImage;
_sliderHandle.style.top = _imageBounds.y;
_sliderHandle.style.height = _imageBounds.height;
}
private void OnPointerDown(PointerDownEvent evt)
{
_isDraggingSlider = true;
_viewportContainer.CapturePointer(evt.pointerId);
UpdateSplitFromPointer(evt.position);
evt.StopPropagation();
}
private void OnPointerMove(PointerMoveEvent evt)
{
if (!_isDraggingSlider) return;
UpdateSplitFromPointer(evt.position);
evt.StopPropagation();
}
private void OnPointerUp(PointerUpEvent evt)
{
if (_isDraggingSlider)
{
_isDraggingSlider = false;
_viewportContainer.ReleasePointer(evt.pointerId);
}
}
private void OnPointerCaptureOut(PointerCaptureOutEvent evt)
{
_isDraggingSlider = false;
}
private void UpdateSplitFromPointer(Vector2 pointerPosition)
{
Vector2 localMousePos = _viewportContainer.WorldToLocal(pointerPosition);
if (_imageBounds.width > 0)
{
float relativeX = localMousePos.x - _imageBounds.x;
_splitPercentage = Mathf.Clamp01(relativeX / _imageBounds.width);
UpdateSplitterPositions();
}
}
public void BindPaletteContext(Palette palette)
{
_currentPaletteAsset = palette;
UpdateVolumeWithPalette(null);
}
private void UpdateVolumeWithPalette(Camera camera)
{
if (_currentPaletteAsset == null || _localVolume == null) return;
if (_internalRuntimeCloneProfile == null)
{
_internalRuntimeCloneProfile = ScriptableObject.CreateInstance<VolumeProfile>();
_internalRuntimeCloneProfile.name = "VNTG_Override_Profile";
_localVolume.profile = _internalRuntimeCloneProfile;
}
if (!_internalRuntimeCloneProfile.TryGet(out PSXEffectSettings psxSettings))
{
psxSettings = _internalRuntimeCloneProfile.Add<PSXEffectSettings>(true);
}
if (camera != null)
{
UniversalAdditionalCameraData cameraData = camera.GetUniversalAdditionalCameraData();
LayerMask layerMask = cameraData != null ? cameraData.volumeLayerMask : (LayerMask)(-1);
Transform triggerTransform = (cameraData != null && cameraData.volumeTrigger != null)
? cameraData.volumeTrigger
: camera.transform;
VolumeManager.instance.Update(triggerTransform, layerMask);
VolumeStack stack = VolumeManager.instance.stack;
if (stack != null)
{
PSXEffectSettings activePSXSettings = stack.GetComponent<PSXEffectSettings>();
if (activePSXSettings != null && psxSettings != null)
{
UnityEditor.EditorUtility.CopySerialized(activePSXSettings, psxSettings);
}
}
}
psxSettings.EnableColorPalette.overrideState = true;
psxSettings.EnableColorPalette.value = true;
psxSettings.PaletteAsset.overrideState = true;
psxSettings.PaletteAsset.value = _currentPaletteAsset;
}
private void OnAttached(AttachToPanelEvent evt)
{
RefreshCameraDropdownOptions();
TogglePreview(true);
}
private void OnDetached(DetachFromPanelEvent evt)
{
TogglePreview(false);
Cleanup();
}
private void InitializeRendering()
{
GameObject camGO = new GameObject("VNTG_Preview_Camera") { hideFlags = HideFlags.HideAndDontSave };
_previewCamera = camGO.AddComponent<Camera>();
_previewCamera.clearFlags = CameraClearFlags.Skybox;
_previewCamera.forceIntoRenderTexture = true;
_previewCamera.enabled = false;
var urpCamData = _previewCamera.GetComponent<UniversalAdditionalCameraData>();
if (urpCamData == null) urpCamData = _previewCamera.gameObject.AddComponent<UniversalAdditionalCameraData>();
urpCamData.renderPostProcessing = true;
_localVolume = camGO.AddComponent<Volume>();
_localVolume.isGlobal = true;
_localVolume.priority = 9999;
_localVolume.enabled = false;
UpdateRenderResolution();
}
private void RefreshCameraDropdownOptions()
{
_foundCameras.Clear();
List<string> options = new List<string> { "Active Scene View Camera" };
#if UNITY_6000_4_OR_NEWER
Camera[] allCams = Object.FindObjectsByType<Camera>();
#else
Camera[] allCams = Object.FindObjectsByType<Camera>(FindObjectsSortMode.None);
#endif
foreach (Camera cam in allCams)
{
if (cam.gameObject.name == "VNTG_Preview_Camera") continue;
_foundCameras.Add(cam);
options.Add($"{cam.gameObject.name} ({cam.tag})");
}
_cameraDropdown.choices = options;
if (_useSceneViewFallback || _explicitSelectedCamera == null)
{
_cameraDropdown.SetValueWithoutNotify("Active Scene View Camera");
}
}
private void OnDropdownValueChanged(ChangeEvent<string> evt)
{
if (evt.newValue == "Active Scene View Camera" || string.IsNullOrEmpty(evt.newValue))
{
_useSceneViewFallback = true;
_explicitSelectedCamera = null;
}
else
{
int selectedIndex = _cameraDropdown.choices.IndexOf(evt.newValue) - 1;
if (selectedIndex >= 0 && selectedIndex < _foundCameras.Count)
{
_explicitSelectedCamera = _foundCameras[selectedIndex];
_useSceneViewFallback = false;
}
}
}
private void UpdateRenderResolution()
{
float layoutWidth = float.IsNaN(_viewportContainer.layout.width) ? 256f : _viewportContainer.layout.width;
float layoutHeight = float.IsNaN(_viewportContainer.layout.height) ? 256f : _viewportContainer.layout.height;
if (layoutWidth <= 0 || layoutHeight <= 0) return;
float targetAspect = 16f / 9f;
float containerAspect = layoutWidth / layoutHeight;
float finalWidth = layoutWidth;
float finalHeight = layoutHeight;
if (containerAspect > targetAspect)
{
finalWidth = layoutHeight * targetAspect;
}
else
{
finalHeight = layoutWidth / targetAspect;
}
float leftOffset = (layoutWidth - finalWidth) / 2f;
float topOffset = (layoutHeight - finalHeight) / 2f;
_imageBounds = new Rect(leftOffset, topOffset, finalWidth, finalHeight);
_baseImage.style.width = finalWidth;
_baseImage.style.height = finalHeight;
_baseImage.style.left = leftOffset;
_baseImage.style.top = topOffset;
float scaleModifier = 1.0f;
int targetWidth = Mathf.Max(64, Mathf.RoundToInt(finalWidth * scaleModifier));
int targetHeight = Mathf.Max(64, Mathf.RoundToInt(finalHeight * scaleModifier));
if (_renderTextureBase == null || _renderTextureBase.width != targetWidth || _renderTextureBase.height != targetHeight)
{
if (_renderTextureBase != null) _renderTextureBase.Release();
if (_renderTextureFiltered != null) _renderTextureFiltered.Release();
_renderTextureBase = new RenderTexture(targetWidth, targetHeight, 24, RenderTextureFormat.ARGB32);
_renderTextureBase.Create();
_renderTextureFiltered = new RenderTexture(targetWidth, targetHeight, 24, RenderTextureFormat.ARGB32);
_renderTextureFiltered.Create();
_baseImage.style.backgroundImage = Background.FromRenderTexture(_renderTextureBase);
_filteredImage.style.backgroundImage = Background.FromRenderTexture(_renderTextureFiltered);
}
}
private void TogglePreview(bool enable)
{
if (enable)
EditorApplication.update += Render;
else
EditorApplication.update -= Render;
}
private void Render()
{
if (_previewCamera == null || _renderTextureBase == null || _renderTextureFiltered == null) return;
if (!_useSceneViewFallback && _explicitSelectedCamera != null)
{
_previewCamera.transform.position = _explicitSelectedCamera.transform.position;
_previewCamera.transform.rotation = _explicitSelectedCamera.transform.rotation;
_previewCamera.fieldOfView = _explicitSelectedCamera.fieldOfView;
_previewCamera.orthographic = _explicitSelectedCamera.orthographic;
_previewCamera.orthographicSize = _explicitSelectedCamera.orthographicSize;
_previewCamera.cullingMask = _explicitSelectedCamera.cullingMask;
UpdateVolumeWithPalette(_explicitSelectedCamera);
}
else if (SceneView.lastActiveSceneView != null)
{
_previewCamera.transform.position = SceneView.lastActiveSceneView.camera.transform.position;
_previewCamera.transform.rotation = SceneView.lastActiveSceneView.camera.transform.rotation;
_previewCamera.fieldOfView = 60.0f;
_previewCamera.orthographic = SceneView.lastActiveSceneView.orthographic;
_previewCamera.orthographicSize = SceneView.lastActiveSceneView.camera.orthographicSize;
_previewCamera.cullingMask = SceneView.lastActiveSceneView.camera.cullingMask;
UpdateVolumeWithPalette(SceneView.lastActiveSceneView.camera);
}
if (!_internalRuntimeCloneProfile.TryGet(out PSXEffectSettings psxSettings))
{
psxSettings = _internalRuntimeCloneProfile.Add<PSXEffectSettings>(true);
}
if (!_internalRuntimeCloneProfile.TryGet(out CRTSettings crtSettings))
{
crtSettings = _internalRuntimeCloneProfile.Add<CRTSettings>(true);
}
crtSettings.Enabled.value = false;
_previewCamera.targetTexture = _renderTextureBase;
_localVolume.enabled = true;
psxSettings.EnableColorPalette.value = false;
psxSettings.EnableFog.value = false;
_previewCamera.Render();
_previewCamera.targetTexture = _renderTextureFiltered;
psxSettings.EnableColorPalette.value = true;
psxSettings.EnableFog.value = false;
psxSettings.Enabled.value = true;
psxSettings.ShowInSceneView.value = true;
_previewCamera.Render();
_localVolume.enabled = false;
_viewportContainer.MarkDirtyRepaint();
}
private void Cleanup()
{
if (_internalRuntimeCloneProfile != null)
{
Object.DestroyImmediate(_internalRuntimeCloneProfile);
_internalRuntimeCloneProfile = null;
}
if (_previewCamera != null)
{
Object.DestroyImmediate(_previewCamera.gameObject);
_previewCamera = null;
}
if (_renderTextureBase != null)
{
_renderTextureBase.Release();
Object.DestroyImmediate(_renderTextureBase);
_renderTextureBase = null;
}
if (_renderTextureFiltered != null)
{
_renderTextureFiltered.Release();
Object.DestroyImmediate(_renderTextureFiltered);
_renderTextureFiltered = null;
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0b109e44eb217fd43b656fecc7a924cc
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1763a72bf0204754ebc708de2ae563cd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,345 @@
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PSXEffectSettingsEditor.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.PSX.Editor
{
[CustomEditor(typeof(PSXEffectSettings))]
public class PSXEffectSettingsEditor : VolumeComponentEditor
{
private static readonly string[] _ditherModeOptions = new string[] { "Mult Lum", "Mult RGB", "Additive" };
private static readonly string[] _ditherDisplayModeOptions = new string[] { "Abs", "Rel", "Pixel Rel" };
private bool _showLighting = true;
private bool _showPixelation = true;
private bool _showColorPrecision = true;
private bool _showDither = true;
private bool _showColorPalette = true;
private bool _showFog = true;
private SerializedDataParameter _Enabled;
private SerializedDataParameter _ShowInSceneView;
private SerializedDataParameter _AmbientColor;
private SerializedDataParameter _EnablePixelation;
private SerializedDataParameter _EnableLetterbox;
private SerializedDataParameter _PixelResolution;
private SerializedDataParameter _EnableColorPrecision;
private SerializedDataParameter _ColorPrecision;
private SerializedDataParameter _EnableDither;
private SerializedDataParameter _DitherMode;
private SerializedDataParameter _DitherPattern;
private SerializedDataParameter _DitherDisplayMode;
private SerializedDataParameter _DitherReferenceResolution;
private SerializedDataParameter _DitherScale;
private SerializedDataParameter _DitherThreshold;
private SerializedDataParameter _EnableColorPalette;
private SerializedDataParameter _NormalizeLuminanceBeforeSampling;
private SerializedDataParameter _PreserveLighting;
private SerializedDataParameter _PaletteFileAsset;
private SerializedDataParameter _EnableFog;
private SerializedDataParameter _IgnoreSkybox;
private SerializedDataParameter _FogColor;
private SerializedDataParameter _FogDensity;
private SerializedDataParameter _FogNoiseStrength;
private SerializedDataParameter _FogEdgeSmoothness;
private SerializedDataParameter _FogNoiseScale;
private SerializedDataParameter _FogNoiseStart;
public override void OnEnable()
{
if (target == null || targets == null || targets.Length == 0) return;
base.OnEnable();
var o = new PropertyFetcher<PSXEffectSettings>(serializedObject);
_Enabled = Unpack(o.Find(x => x.Enabled));
_ShowInSceneView = Unpack(o.Find(x => x.ShowInSceneView));
_AmbientColor = Unpack(o.Find(x => x.AmbientColor));
_EnablePixelation = Unpack(o.Find(x => x.EnablePixelation));
_EnableLetterbox = Unpack(o.Find(x => x.EnableLetterbox));
_PixelResolution = Unpack(o.Find(x => x.PixelResolution));
_EnableColorPrecision = Unpack(o.Find(x => x.EnableColorPrecision));
_ColorPrecision = Unpack(o.Find(x => x.ColorPrecision));
_EnableDither = Unpack(o.Find(x => x.EnableDither));
_DitherMode = Unpack(o.Find(x => x.DitherMode));
_DitherPattern = Unpack(o.Find(x => x.DitherPattern));
_DitherDisplayMode = Unpack(o.Find(x => x.DitherDisplayMode));
_DitherReferenceResolution = Unpack(o.Find(x => x.DitherReferenceRes));
_DitherScale = Unpack(o.Find(x => x.DitherScale));
_DitherThreshold = Unpack(o.Find(x => x.DitherThreshold));
_EnableColorPalette = Unpack(o.Find(x => x.EnableColorPalette));
_NormalizeLuminanceBeforeSampling = Unpack(o.Find(x => x.NormalizeLuminanceBeforeSampling));
_PreserveLighting = Unpack(o.Find(x => x.PreserveLighting));
_PaletteFileAsset = Unpack(o.Find(x => x.PaletteAsset));
_EnableFog = Unpack(o.Find(x => x.EnableFog));
_IgnoreSkybox = Unpack(o.Find(x => x.IgnoreSkybox));
_FogColor = Unpack(o.Find(x => x.FogColor));
_FogDensity = Unpack(o.Find(x => x.FogDensity));
_FogNoiseStrength = Unpack(o.Find(x => x.FogNoiseStrength));
_FogEdgeSmoothness = Unpack(o.Find(x => x.FogEdgeSmoothness));
_FogNoiseScale = Unpack(o.Find(x => x.FogNoiseScale));
_FogNoiseStart = Unpack(o.Find(x => x.FogNoiseStart));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.BeginVertical();
DrawSettingsSection();
EditorGUILayout.EndVertical();
EditorGUILayout.Space(2);
_showLighting = VNTGEditorGUIUtils.DrawSectionHeaderWithOverrideState(_showLighting, "Lighting", _AmbientColor, ResetLightingSection);
if (_showLighting && _AmbientColor.overrideState.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawLightingSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showPixelation = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(_showPixelation, "Pixelation", _EnablePixelation, ResetPixelationSection);
if (_showPixelation && _EnablePixelation.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawPixelationSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showColorPrecision = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(_showColorPrecision, "Color Precision", _EnableColorPrecision, ResetColorPrecisionSection);
if (_showColorPrecision && _EnableColorPrecision.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawColorPrecisionSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showDither = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(_showDither, "Dither", _EnableDither, ResetDitherSection);
if (_showDither && _EnableDither.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawDitherSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showColorPalette = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(_showColorPalette, "Color Palette", _EnableColorPalette, ResetColorPaletteSection);
if (_showColorPalette && _EnableColorPalette.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawColorPaletteSection();
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
_showFog = VNTGEditorGUIUtils.DrawSectionHeaderWithToggle(_showFog, "Fog", _EnableFog, ResetFogSection);
if (_showFog && _EnableFog.value.boolValue)
{
EditorGUILayout.BeginVertical("HelpBox");
DrawFogSection();
EditorGUILayout.EndVertical();
}
serializedObject.ApplyModifiedProperties();
}
private void DrawSettingsSection()
{
EditorGUILayout.Space(4);
EditorGUILayout.BeginHorizontal();
VNTGEditorGUIUtils.DrawToggleButton(_Enabled, "Enabled", inline: true);
GUILayout.Space(10);
VNTGEditorGUIUtils.DrawToggleButton(_ShowInSceneView, "Scene View", inline: true);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(4);
}
private void ResetLightingSection()
{
var def = ScriptableObject.CreateInstance<PSXEffectSettings>();
_AmbientColor.value.colorValue = def.AmbientColor.value;
_AmbientColor.overrideState.boolValue = def.AmbientColor.overrideState;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetPixelationSection()
{
var def = ScriptableObject.CreateInstance<PSXEffectSettings>();
_EnablePixelation.value.boolValue = def.EnablePixelation.value;
_EnableLetterbox.value.boolValue = def.EnableLetterbox.value;
_PixelResolution.value.vector2Value = def.PixelResolution.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetColorPrecisionSection()
{
var def = ScriptableObject.CreateInstance<PSXEffectSettings>();
_EnableColorPrecision.value.boolValue = def.EnableColorPrecision.value;
_ColorPrecision.value.floatValue = def.ColorPrecision.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetDitherSection()
{
var def = ScriptableObject.CreateInstance<PSXEffectSettings>();
_EnableDither.value.boolValue = def.EnableDither.value;
_DitherMode.value.enumValueIndex = System.Convert.ToInt32(def.DitherMode.value);
_DitherPattern.value.intValue = def.DitherPattern.value;
_DitherDisplayMode.value.enumValueIndex = System.Convert.ToInt32(def.DitherDisplayMode.value);
_DitherReferenceResolution.value.vector2Value = def.DitherReferenceRes.value;
_DitherScale.value.floatValue = def.DitherScale.value;
_DitherThreshold.value.floatValue = def.DitherThreshold.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetColorPaletteSection()
{
var def = ScriptableObject.CreateInstance<PSXEffectSettings>();
_EnableColorPalette.value.boolValue = def.EnableColorPalette.value;
_NormalizeLuminanceBeforeSampling.value.boolValue = def.NormalizeLuminanceBeforeSampling.value;
_PreserveLighting.value.boolValue = def.PreserveLighting.value;
_PaletteFileAsset.value.objectReferenceValue = def.PaletteAsset.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void ResetFogSection()
{
var def = ScriptableObject.CreateInstance<PSXEffectSettings>();
_EnableFog.value.boolValue = def.EnableFog.value;
_IgnoreSkybox.value.boolValue = def.IgnoreSkybox.value;
_FogColor.value.colorValue = def.FogColor.value;
_FogDensity.value.floatValue = def.FogDensity.value;
_FogNoiseStrength.value.floatValue = def.FogNoiseStrength.value;
_FogEdgeSmoothness.value.floatValue = def.FogEdgeSmoothness.value;
_FogNoiseScale.value.floatValue = def.FogNoiseScale.value;
_FogNoiseStart.value.floatValue = def.FogNoiseStart.value;
DestroyImmediate(def);
serializedObject.ApplyModifiedProperties();
}
private void DrawLightingSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawParamField(_AmbientColor);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawPixelationSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawInlineVector2Param(_PixelResolution);
EditorGUI.indentLevel++;
EditorGUILayout.Space(1);
VNTGEditorGUIUtils.DrawToggleButton(_EnableLetterbox, "Keep Aspect Ratio");
EditorGUI.indentLevel--;
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawColorPrecisionSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawClampedSlider(_ColorPrecision, 1.0f, 256.0f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawDitherSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
DrawDitherDisplayModeToolbar(_DitherDisplayMode, "Dither Display Mode", _EnablePixelation.value.boolValue);
EditorGUI.indentLevel++;
VNTGEditorGUIUtils.DrawClampedSlider(_DitherScale, 0.0f, 1.0f);
if (_DitherDisplayMode.value.intValue == (int)PSXDitherDisplayMode.RelativeScale)
{
VNTGEditorGUIUtils.DrawInlineVector2Param(_DitherReferenceResolution);
}
EditorGUI.indentLevel--;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawEnumToolbarParameter<PSXDitherMode>(_DitherMode, "Dither Mode", _ditherModeOptions);
EditorGUI.indentLevel++;
VNTGEditorGUIUtils.DrawClampedSlider(_DitherPattern, 0, 10);
VNTGEditorGUIUtils.DrawClampedSlider(_DitherThreshold, 0.0f, 1.0f);
EditorGUI.indentLevel--;
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawColorPaletteSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawParamField(_PaletteFileAsset);
VNTGEditorGUIUtils.DrawToggleButton(_PreserveLighting, "Preserve Lighting");
VNTGEditorGUIUtils.DrawToggleButton(_NormalizeLuminanceBeforeSampling, "Normalize Luminance");
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawFogSection()
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawToggleButton(_IgnoreSkybox, "Ignore Skybox");
VNTGEditorGUIUtils.DrawParamField(_FogColor);
VNTGEditorGUIUtils.DrawClampedSlider(_FogDensity, 0.0f, 1.0f);
VNTGEditorGUIUtils.DrawClampedSlider(_FogNoiseStrength, 0.0f, 1.0f);
VNTGEditorGUIUtils.DrawClampedSlider(_FogEdgeSmoothness, 0.01f, 1.0f);
VNTGEditorGUIUtils.DrawClampedSlider(_FogNoiseScale, 0.0f, 1.0f);
VNTGEditorGUIUtils.DrawClampedSlider(_FogNoiseStart, 0.0f, 1.0f);
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawDitherDisplayModeToolbar(SerializedDataParameter param, string label, bool enablePixelation)
{
if (param == null) return;
SerializedProperty enumProp = param.value;
if (!enablePixelation && enumProp.intValue == (int)PSXDitherDisplayMode.PixelResolutionRelative)
{
enumProp.intValue = (int)PSXDitherDisplayMode.RelativeScale;
}
VNTGEditorGUIUtils.DrawEnumToolbarParameter<PSXDitherDisplayMode>(
param,
label,
_ditherDisplayModeOptions,
index => index == (int)PSXDitherDisplayMode.PixelResolutionRelative && !enablePixelation
);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6baa05bd2e164c74b988243d5382f695
@@ -0,0 +1,808 @@
using ColbyO.VNTG.ColorPalette;
using ColbyO.VNTG.Editor;
using ColbyO.VNTG.PSX;
using ColbyO.VNTG.PSX.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PSXMaterialEditor.cs
//-----------------------------------------------------------------------
#if UNITY_EDITOR
public class PSXMaterialEditor : ShaderGUI
{
private static readonly string _terrianShaderName = "VNTG/PSXTerrian_URP";
private static readonly string _decalShaderName = "VNTG/PSXDecal_URP";
private static readonly string _skyboxShaderName = "VNTG/PSXSkybox_URP";
private static readonly string _waterShaderName = "VNTG/PSXWater_URP";
private static readonly string[] _cullOptions = new string[] { "Off", "Front", "Back" };
private static readonly string[] _surfaceOptions = new string[] { "Opaque", "Transparent" };
private static readonly string[] _textureSamplingOptions = new string[] { "Point", "Bilinear", "N64" };
private static readonly string[] _jitterOptions = new string[] { "Disabled", "View", "Screen" };
private static readonly string[] _lightingOptions = new string[] { "Unlit", "Lit", "Texel Lit", "Vertex Lit" };
private static readonly string[] _shadingOptions = new string[] { "Default", "Flat" };
private static readonly string[] _ditherMethodOptions = new string[] { "Full", "Texture Rel" };
private static readonly string[] _skyboxModeOptions = new string[] { "Cubemap", "Panoramic", "Procedual" };
private static readonly string[] _procedualSkyboxModeOptions = new string[] { "Flat Gradient", "Space" };
private bool _showSurfaceInputs = true;
private bool _showRetroEffects = true;
private bool _showLighting = true;
private bool _showWater = true;
private bool _showSkybox = true;
private bool _showFlatGradientSkybox = true;
private bool _showSpaceSkybox = true;
private bool _showAdvancedOptions = false;
MaterialProperty _surfaceType;
MaterialProperty _cullMode;
MaterialProperty _mainTex;
MaterialProperty _textureSampleMode;
MaterialProperty _baseColor;
MaterialProperty _tiling;
MaterialProperty _offset;
MaterialProperty _hueShift;
MaterialProperty _lightnessMultiplier;
MaterialProperty _saturationMultiplier;
MaterialProperty _enableTextureDownsampling;
MaterialProperty _textureResolution;
MaterialProperty _enableDithering;
MaterialProperty _ditheringMethod;
MaterialProperty _ditherStrength;
MaterialProperty _ditherScale;
MaterialProperty _usePalette;
MaterialProperty _paletteLUT;
MaterialProperty _useColorQuantization;
MaterialProperty _colorDepth;
MaterialProperty _useVertexColor;
MaterialProperty _enableAffineTextureMapping;
MaterialProperty _affineWrapStrength;
MaterialProperty _lightingMethod;
MaterialProperty _shadingMode;
MaterialProperty _metalness;
MaterialProperty _useSpecular;
MaterialProperty _smoothness;
MaterialProperty _specularColor;
MaterialProperty _enableShadows;
MaterialProperty _shadowTint;
MaterialProperty _shadowDistCutoff;
MaterialProperty _shadowOffset;
MaterialProperty _vertexJitterMode;
MaterialProperty _vertexPrecision;
MaterialProperty _alphaClipThreshold;
MaterialProperty _waterColor;
MaterialProperty _waterCrestColor;
MaterialProperty _waterSharpness;
MaterialProperty _waterScale;
MaterialProperty _waterSpeed;
MaterialProperty _maxDepth;
MaterialProperty _depthDecay;
MaterialProperty _skyboxMode;
MaterialProperty _procedualSkyboxMode;
MaterialProperty _skyboxCubemap;
MaterialProperty _skyboxRotation;
MaterialProperty _flatGradientTopColor;
MaterialProperty _flatGradientBottomColor;
MaterialProperty _flatGradientTopAngle;
MaterialProperty _flatGradientBottomAngle;
MaterialProperty _enableNebula;
MaterialProperty _nebulaPeriod;
MaterialProperty _nebulaPower;
MaterialProperty _nebulaColor;
MaterialProperty _starDensity;
MaterialProperty _starSharpness;
Material targetMat;
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
GetProperties(properties);
targetMat = (materialEditor.target as Material);
EditorGUI.BeginChangeCheck();
if (targetMat.shader == null || targetMat.shader.name == _skyboxShaderName)
{
_showSkybox = VNTGEditorGUIUtils.DrawSectionHeader(_showSurfaceInputs, "Skybox Options", () => ResetSkybox(materialEditor));
if (_showSkybox)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
DrawSkybox(materialEditor);
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
if (_skyboxMode != null && _skyboxMode.floatValue == 2.0f)
{
if (_procedualSkyboxMode != null && _procedualSkyboxMode.floatValue == 0.0f)
{
_showFlatGradientSkybox = VNTGEditorGUIUtils.DrawSectionHeader(_showFlatGradientSkybox, "Flat Gradient Skybox", () => ResetFlatGradientSkybox(materialEditor));
if (_showFlatGradientSkybox)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
DrawFlatGradientSkybox(materialEditor);
EditorGUILayout.EndVertical();
}
}
else if (_procedualSkyboxMode != null && _procedualSkyboxMode.floatValue == 1.0f)
{
_showSpaceSkybox = VNTGEditorGUIUtils.DrawSectionHeader(_showSpaceSkybox, "Space Skybox", () => ResetSpaceSkybox(materialEditor)); ;
if (_showSpaceSkybox)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
DrawSpaceSkybox(materialEditor);
EditorGUILayout.EndVertical();
}
}
}
}
if (
targetMat.shader != null &&
targetMat.shader.name != _waterShaderName &&
(
targetMat.shader.name != _skyboxShaderName ||
_skyboxMode == null ||
_skyboxMode.floatValue != 2.0f
)
)
{
_showSurfaceInputs = VNTGEditorGUIUtils.DrawSectionHeader(_showSurfaceInputs, "Surface Options", () => ResetSurfaceOptions(materialEditor));
if (_showSurfaceInputs)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
if (
targetMat.shader != null &&
targetMat.shader.name == _skyboxShaderName &&
_skyboxMode != null &&
_skyboxMode.floatValue == 0.0f
) DrawSurfaceOptions(materialEditor, _skyboxCubemap);
else DrawSurfaceOptions(materialEditor, _mainTex);
EditorGUILayout.EndVertical();
}
}
if (targetMat.shader != null && targetMat.shader.name == _waterShaderName)
{
EditorGUILayout.Space(2);
_showWater = VNTGEditorGUIUtils.DrawSectionHeader(_showWater, "Water", () => ResetWater(materialEditor));
if (_showWater)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
DrawWater(materialEditor);
EditorGUILayout.EndVertical();
}
}
EditorGUILayout.Space(2);
_showRetroEffects = VNTGEditorGUIUtils.DrawSectionHeader(_showRetroEffects, "Retro Effects", () => ResetRetroEffects(materialEditor));
if (_showRetroEffects)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
DrawPSXEffects(materialEditor);
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space(2);
if (targetMat.shader != null && targetMat.shader.name != _skyboxShaderName)
{
_showLighting = VNTGEditorGUIUtils.DrawSectionHeader(_showLighting, "Lighting", () => ResetLightingOptions(materialEditor));
if (_showLighting)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
DrawLightingOptions(materialEditor);
EditorGUILayout.EndVertical();
}
}
EditorGUILayout.Space(10);
_showAdvancedOptions = VNTGEditorGUIUtils.DrawSectionHeader(_showAdvancedOptions, "Advanced Options");
if (_showAdvancedOptions)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
DrawAdvancedOptions(materialEditor);
EditorGUILayout.EndVertical();
}
if (EditorGUI.EndChangeCheck())
{
materialEditor.RegisterPropertyChangeUndo("Modify Material Properties");
materialEditor.PropertiesChanged();
foreach (Object target in materialEditor.targets)
{
if (target is Material mat)
{
SetupMaterialBlendMode(mat);
}
}
EditorUtility.SetDirty(materialEditor.target);
}
}
private void DrawSurfaceOptions(MaterialEditor materialEditor, MaterialProperty textureProp = null)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
if (_surfaceType != null && targetMat.shader != null && targetMat.shader.name != _decalShaderName && targetMat.shader.name != _skyboxShaderName) VNTGEditorGUIUtils.DrawEnumToolbar<SurfaceType>(materialEditor, _surfaceType, "Surface Type", _surfaceOptions);
if (_cullMode != null && targetMat.shader != null && targetMat.shader.name != _decalShaderName && targetMat.shader.name != _skyboxShaderName) VNTGEditorGUIUtils.DrawEnumToolbar<CullMode>(materialEditor, _cullMode, "Face Culling", _cullOptions);
if (_alphaClipThreshold != null) materialEditor.ShaderProperty(_alphaClipThreshold, "Alpha Clip Threshold");
if (targetMat.shader != null && targetMat.shader.name != _decalShaderName && targetMat.shader.name != _skyboxShaderName && targetMat.shader.name != _terrianShaderName) EditorGUILayout.Space(16);
if (textureProp != null)
{
GUIContent mainTexLabel = new GUIContent("Main Texture", "The primary color texture map.");
materialEditor.TexturePropertySingleLine(mainTexLabel, textureProp, _baseColor);
}
else if (_baseColor != null)
{
materialEditor.ShaderProperty(_baseColor, "Base Color");
}
EditorGUI.indentLevel++;
EditorGUI.indentLevel++;
VNTGEditorGUIUtils.DrawEnumToolbar<PSXTextureSampleMode>(materialEditor, _textureSampleMode, "Texture Sampling Mode", _textureSamplingOptions);
if (_textureSampleMode.floatValue == 2f && targetMat.shader != null && targetMat.shader.name == _terrianShaderName)
{
EditorGUILayout.HelpBox("Warning: N64 texture sampling is not supported on terrian shaders. Reverting to Bilinear.", MessageType.Warning);
}
if (_hueShift != null) materialEditor.ShaderProperty(_hueShift, "Hue Shift");
if (_saturationMultiplier != null) materialEditor.ShaderProperty(_saturationMultiplier, "Saturation Multiplier");
if (_lightnessMultiplier != null) materialEditor.ShaderProperty(_lightnessMultiplier, "Lightness Multiplier");
if (
_mainTex != null &&
_mainTex.textureValue != null &&
_tiling != null &&
_offset != null &&
(targetMat.shader != null && targetMat.shader.name != _skyboxShaderName)
)
{
EditorGUILayout.BeginVertical();
materialEditor.ShaderProperty(_tiling, "Tiling");
GUILayout.Space(-16);
materialEditor.ShaderProperty(_offset, "Offset");
EditorGUILayout.EndVertical();
}
EditorGUI.indentLevel--;
EditorGUI.indentLevel--;
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawPSXEffects(MaterialEditor materialEditor)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawPropertyToggle(materialEditor, _enableTextureDownsampling, "Texture Downsampling");
if (_enableTextureDownsampling != null && _enableTextureDownsampling.floatValue == 1.0f && _textureResolution != null)
{
EditorGUI.indentLevel++;
materialEditor.ShaderProperty(_textureResolution, "Target Resolution");
EditorGUI.indentLevel--;
}
VNTGEditorGUIUtils.DrawPropertyToggle(materialEditor, _enableAffineTextureMapping, "Affine Texture Wraping");
if (_enableAffineTextureMapping != null && _enableAffineTextureMapping.floatValue == 1.0f && _affineWrapStrength != null)
{
EditorGUI.indentLevel++;
materialEditor.ShaderProperty(_affineWrapStrength, "Warp Strength");
EditorGUI.indentLevel--;
}
VNTGEditorGUIUtils.DrawPropertyToggle(materialEditor, _usePalette, "Palette");
if (_usePalette != null && _usePalette.floatValue == 1.0f && _paletteLUT != null)
{
EditorGUI.indentLevel++;
DrawPaletteMaterialProperty(materialEditor, _paletteLUT);
EditorGUI.indentLevel--;
}
VNTGEditorGUIUtils.DrawPropertyToggle(materialEditor, _useColorQuantization, "Color Quantization");
if (_useColorQuantization != null && _useColorQuantization.floatValue == 1.0f && _colorDepth != null)
{
EditorGUI.indentLevel++;
materialEditor.ShaderProperty(_colorDepth, "Color Depth");
EditorGUI.indentLevel--;
}
VNTGEditorGUIUtils.DrawPropertyToggle(materialEditor, _enableDithering, "Dithering");
if (_enableDithering != null && _enableDithering.floatValue == 1.0f)
{
EditorGUI.indentLevel++;
if (_ditheringMethod != null && _enableTextureDownsampling != null && _enableTextureDownsampling.floatValue == 1) VNTGEditorGUIUtils.DrawEnumToolbar<DitherMode>(materialEditor, _ditheringMethod, "Dither Mode", _ditherMethodOptions);
if (_ditherStrength != null) materialEditor.ShaderProperty(_ditherStrength, "Dither Strength");
if (_ditherScale != null) materialEditor.ShaderProperty(_ditherScale, "Dither Scale");
EditorGUI.indentLevel--;
}
VNTGEditorGUIUtils.DrawPropertyToggle(materialEditor, _useVertexColor, "Use Vertex Colors");
VNTGEditorGUIUtils.DrawEnumToolbar<PSXVertexJitterMode>(materialEditor, _vertexJitterMode, "Vertex Jitter Mode", _jitterOptions);
if (_vertexJitterMode != null && Mathf.RoundToInt(_vertexJitterMode.floatValue) != 0)
{
EditorGUI.indentLevel++;
if (_vertexPrecision != null) materialEditor.ShaderProperty(_vertexPrecision, "Vertex Precision");
EditorGUI.indentLevel--;
}
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawLightingOptions(MaterialEditor materialEditor)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawEnumToolbar<PSXLightingMethod>(materialEditor, _lightingMethod, "Lighting Method", _lightingOptions, (int newMode) =>
{
if (_shadingMode == null) return;
if (newMode != 1)
{
VNTGEditorGUIUtils.UpdateShaderGraphEnumKeyword(materialEditor, _shadingMode, 0, _shadingOptions);
}
else
{
VNTGEditorGUIUtils.UpdateShaderGraphEnumKeyword(materialEditor, _shadingMode, Mathf.RoundToInt(_shadingMode.floatValue), _shadingOptions);
}
});
bool isUnlit = _lightingMethod != null && Mathf.RoundToInt(_lightingMethod.floatValue) == 0;
bool isLit = _lightingMethod != null && Mathf.RoundToInt(_lightingMethod.floatValue) == 1;
if (!isUnlit)
{
if (isLit && targetMat.shader != null && targetMat.shader.name != _decalShaderName)
{
VNTGEditorGUIUtils.DrawEnumToolbar<PSXShadingMode>(materialEditor, _shadingMode, "Shading Mode", _shadingOptions);
}
if (_metalness != null && targetMat.shader != null && targetMat.shader.name != _decalShaderName) materialEditor.ShaderProperty(_metalness, "Metalness");
VNTGEditorGUIUtils.DrawPropertyToggle(materialEditor, _useSpecular, "Enable Specular");
if (_useSpecular != null && _useSpecular.floatValue == 1.0f)
{
EditorGUI.indentLevel++;
if (_specularColor != null) materialEditor.ShaderProperty(_specularColor, "Specular Color");
if (_smoothness != null) materialEditor.ShaderProperty(_smoothness, "Smoothness");
EditorGUI.indentLevel--;
}
EditorGUILayout.Space(4);
VNTGEditorGUIUtils.DrawPropertyToggle(materialEditor, _enableShadows, "Enable Custom Shadows");
if (_enableShadows != null && _enableShadows.floatValue == 1.0f)
{
EditorGUI.indentLevel++;
if (_shadowTint != null) materialEditor.ShaderProperty(_shadowTint, "Shadow Tint");
if (_shadowDistCutoff != null) materialEditor.ShaderProperty(_shadowDistCutoff, "Distance Cutoff");
if (_shadowOffset != null) materialEditor.ShaderProperty(_shadowOffset, "Shadow Bias");
EditorGUI.indentLevel--;
}
}
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawWater(MaterialEditor materialEditor)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
if (_waterColor != null) materialEditor.ShaderProperty(_waterColor, "Water Color");
if (_waterCrestColor != null) materialEditor.ShaderProperty(_waterCrestColor, "Crest Color");
if (_waterSharpness != null) materialEditor.ShaderProperty(_waterSharpness, "Water Sharpness");
if (_waterScale != null) materialEditor.ShaderProperty(_waterScale, "Water Scale");
if (_waterSpeed != null) materialEditor.ShaderProperty(_waterSpeed, "Water Speed");
EditorGUILayout.Space(4);
if (_maxDepth != null) materialEditor.ShaderProperty(_maxDepth, "Max Depth");
if (_depthDecay != null) materialEditor.ShaderProperty(_depthDecay, "Depth Decay");
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawSkybox(MaterialEditor materialEditor)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
if (_skyboxMode != null)
{
VNTGEditorGUIUtils.DrawEnumToolbar<SkyboxMode>(materialEditor, _skyboxMode, "Skybox Mode", _skyboxModeOptions);
if (_procedualSkyboxMode != null && _skyboxMode.floatValue == 2.0f)
{
VNTGEditorGUIUtils.DrawEnumToolbar<ProcedualSkyboxMode>(materialEditor, _procedualSkyboxMode, "Procedual Mode", _procedualSkyboxModeOptions);
}
if (
_skyboxRotation != null &&
(
_skyboxMode.floatValue != 2.0f ||
(_procedualSkyboxMode != null && _procedualSkyboxMode.floatValue != 0.0f)
)
) materialEditor.ShaderProperty(_skyboxRotation, "Rotation");
}
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawFlatGradientSkybox(MaterialEditor materialEditor)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
if (_flatGradientTopColor != null) materialEditor.ShaderProperty(_flatGradientTopColor, "Top Color");
if (_flatGradientBottomColor != null) materialEditor.ShaderProperty(_flatGradientBottomColor, "Bottom Color");
if (_flatGradientTopAngle != null) materialEditor.ShaderProperty(_flatGradientTopAngle, "Top Angle (Degrees)");
if (_flatGradientBottomAngle != null) materialEditor.ShaderProperty(_flatGradientBottomAngle, "Bottom Angle (Degrees)");
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawSpaceSkybox(MaterialEditor materialEditor)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
if (_starDensity != null) materialEditor.ShaderProperty(_starDensity, "Star Density");
if (_starSharpness != null) materialEditor.ShaderProperty(_starSharpness, "Star Sharpness");
if (_enableNebula != null) VNTGEditorGUIUtils.DrawPropertyToggle(materialEditor, _enableNebula, "Enable Nebula");
if (_enableNebula != null && _enableNebula.floatValue > 0.0f)
{
EditorGUI.indentLevel++;
if (_nebulaColor != null) materialEditor.ShaderProperty(_nebulaColor, "Nebula Color");
if (_nebulaPeriod != null) materialEditor.ShaderProperty(_nebulaPeriod, "Nebula Period");
if (_nebulaPower != null) materialEditor.ShaderProperty(_nebulaPower, "Nebula Power");
EditorGUI.indentLevel--;
}
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void DrawAdvancedOptions(MaterialEditor materialEditor)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space(4);
materialEditor.RenderQueueField();
materialEditor.EnableInstancingField();
materialEditor.DoubleSidedGIField();
EditorGUILayout.Space(4);
EditorGUI.indentLevel--;
}
private void ResetSurfaceOptions(MaterialEditor materialEditor)
{
materialEditor.RegisterPropertyChangeUndo("Reset Surface Properties");
if (_surfaceType != null) _surfaceType.floatValue = 0f;
if (_cullMode != null) _cullMode.floatValue = 2f;
if (_alphaClipThreshold != null) _alphaClipThreshold.floatValue = 0.5f;
if (_textureSampleMode != null) _textureSampleMode.floatValue = 0f;
if (_hueShift != null) _hueShift.floatValue = 0f;
if (_saturationMultiplier != null) _saturationMultiplier.floatValue = 1f;
if (_lightnessMultiplier != null) _lightnessMultiplier.floatValue = 1f;
if (_tiling != null) _tiling.vectorValue = new Vector4(1, 1, 0, 0);
if (_offset != null) _offset.vectorValue = Vector4.zero;
RegisterReset(materialEditor);
}
private void ResetRetroEffects(MaterialEditor materialEditor)
{
materialEditor.RegisterPropertyChangeUndo("Reset Retro Properties");
if (_enableTextureDownsampling != null) _enableTextureDownsampling.floatValue = 0f;
if (_textureResolution != null) _textureResolution.floatValue = 240f;
if (_enableAffineTextureMapping != null) _enableAffineTextureMapping.floatValue = 0f;
if (_affineWrapStrength != null) _affineWrapStrength.floatValue = 1f;
if (_usePalette != null) _usePalette.floatValue = 0f;
if (_paletteLUT != null) _paletteLUT.textureValue = null;
if (_useColorQuantization != null) _useColorQuantization.floatValue = 0f;
if (_colorDepth != null) _colorDepth.floatValue = 32f;
if (_enableDithering != null) _enableDithering.floatValue = 0f;
if (_ditheringMethod != null) _ditheringMethod.floatValue = 1f;
if (_ditherStrength != null) _ditherStrength.floatValue = 1f;
if (_ditherScale != null) _ditherScale.floatValue = 1f;
if (_useVertexColor != null) _useVertexColor.floatValue = 1f;
if (_vertexJitterMode != null) _vertexJitterMode.floatValue = 0f;
if (_vertexPrecision != null) _vertexPrecision.floatValue = 32f;
RegisterReset(materialEditor);
}
private void ResetLightingOptions(MaterialEditor materialEditor)
{
materialEditor.RegisterPropertyChangeUndo("Reset Lighting Properties");
if (_lightingMethod != null) _lightingMethod.floatValue = 1f;
if (_shadingMode != null) _shadingMode.floatValue = 0f;
if (_metalness != null) _metalness.floatValue = 0f;
if (_useSpecular != null) _useSpecular.floatValue = 0f;
if (_smoothness != null) _smoothness.floatValue = 0.5f;
if (_specularColor != null) _specularColor.colorValue = Color.white;
if (_enableShadows != null) _enableShadows.floatValue = 0f;
if (_shadowTint != null) _shadowTint.colorValue = Color.black;
if (_shadowDistCutoff != null) _shadowDistCutoff.floatValue = 50f;
if (_shadowOffset != null) _shadowOffset.floatValue = 0.05f;
RegisterReset(materialEditor);
}
private void ResetWater(MaterialEditor materialEditor)
{
materialEditor.RegisterPropertyChangeUndo("Reset Water Properties");
if (_waterColor != null) _waterColor.colorValue = Color.blue;
if (_waterCrestColor != null) _waterCrestColor.colorValue = Color.white;
if (_waterSharpness != null) _waterSharpness.floatValue = 5.0f;
if (_waterScale != null) _waterScale.floatValue = 256.0f;
if (_waterSpeed != null) _waterSpeed.floatValue = 1.0f;
if (_maxDepth != null) _maxDepth.floatValue = 0.0f;
if (_depthDecay != null) _depthDecay.floatValue = 2.0f;
RegisterReset(materialEditor);
}
private void ResetSkybox(MaterialEditor materialEditor)
{
materialEditor.RegisterPropertyChangeUndo("Skybox Properties");
if (_skyboxMode != null) _skyboxMode.floatValue = 2.0f;
if (_procedualSkyboxMode != null) _procedualSkyboxMode.floatValue = 0.0f;
if (_skyboxRotation != null) _skyboxRotation.floatValue = 0.0f;
RegisterReset(materialEditor);
}
private void ResetFlatGradientSkybox(MaterialEditor materialEditor)
{
materialEditor.RegisterPropertyChangeUndo("Flat Gradient Skybox Properties");
if (_flatGradientTopColor != null) _flatGradientTopColor.colorValue = Color.lightSkyBlue;
if (_flatGradientBottomColor != null) _flatGradientBottomColor.colorValue = Color.orangeRed;
if (_flatGradientTopAngle != null) _flatGradientTopAngle.floatValue = 45.0f;
if (_flatGradientBottomAngle != null) _flatGradientBottomAngle.floatValue = -15.0f;
RegisterReset(materialEditor);
}
private void ResetSpaceSkybox(MaterialEditor materialEditor)
{
materialEditor.RegisterPropertyChangeUndo("Space Skybox Properties");
if (_enableNebula != null) _enableNebula.floatValue = 0;
if (_nebulaPeriod != null) _nebulaPeriod.floatValue = 30f;
if (_nebulaPower != null) _nebulaPower.floatValue = 5.0f;
if (_nebulaColor != null) _nebulaColor.colorValue = new Color(0.4471f, 0.2706f, 0.5098f, 1.0f);
if (_starDensity != null) _starDensity.floatValue = 0.5f;
if (_starSharpness != null) _starSharpness.floatValue = 0.5f;
RegisterReset(materialEditor);
}
private void RegisterReset(MaterialEditor materialEditor)
{
materialEditor.PropertiesChanged();
foreach (UnityEngine.Object target in materialEditor.targets)
{
if (target is Material material)
{
MaterialEditor.ApplyMaterialPropertyDrawers(material);
}
}
}
private void SetupMaterialBlendMode(Material material)
{
if (_surfaceType == null) return;
int surfaceTypeValue = Mathf.RoundToInt(_surfaceType.floatValue);
if (surfaceTypeValue == 0)
{
material.SetOverrideTag("RenderType", "Opaque");
material.DisableKeyword("_SURFACE_TYPE_TRANSPARENT");
material.DisableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.SetInt("_SrcBlend", (int)BlendMode.One);
material.SetInt("_DstBlend", (int)BlendMode.Zero);
material.SetInt("_ZWrite", 1);
material.renderQueue = (int)RenderQueue.Geometry;
}
else
{
material.SetOverrideTag("RenderType", "Transparent");
material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
material.EnableKeyword("_ALPHABLEND_ON");
material.SetInt("_SrcBlend", (int)BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)BlendMode.OneMinusSrcAlpha);
material.SetInt("_ZWrite", 0);
material.renderQueue = (int)RenderQueue.Transparent;
}
}
private void DrawPaletteMaterialProperty(MaterialEditor materialEditor, MaterialProperty lutProperty)
{
Texture3D currentLUT = lutProperty.textureValue as Texture3D;
Palette currentPalette = FindPaletteByLUT(currentLUT);
GUIContent label = new GUIContent("Palette Asset", "Select a Palette asset.");
Rect lineRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
Rect labelRect = new Rect(lineRect.x, lineRect.y, EditorGUIUtility.labelWidth, lineRect.height);
Rect fieldRect = new Rect(lineRect.x + EditorGUIUtility.labelWidth, lineRect.y, lineRect.width - EditorGUIUtility.labelWidth, lineRect.height);
EditorGUI.LabelField(labelRect, label);
if (Event.current.type == EventType.MouseDown && fieldRect.Contains(Event.current.mousePosition))
{
PopupWindow.Show(fieldRect, new PaletteSelectorPopup(selectedPalette =>
{
materialEditor.RegisterPropertyChangeUndo("Select Palette Asset");
lutProperty.textureValue = (selectedPalette != null) ? selectedPalette.LUT : null;
}));
Event.current.Use();
}
if (currentPalette != null && currentPalette.colors != null && currentPalette.colors.Count > 0)
{
PalettePropertyDrawer.DrawPaletteBar(fieldRect, currentPalette.colors);
GUIStyle textStyle = new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter };
EditorGUI.DropShadowLabel(fieldRect, currentPalette.name, textStyle);
}
else
{
GUI.Box(fieldRect, currentLUT != null ? currentLUT.name : "None (Click to select Palette)");
}
}
private Palette FindPaletteByLUT(Texture3D lut)
{
if (lut == null) return null;
string[] guids = AssetDatabase.FindAssets("t:Palette");
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
Palette p = AssetDatabase.LoadAssetAtPath<Palette>(path);
if (p != null && p.LUT == lut)
{
return p;
}
}
return null;
}
private void GetProperties(MaterialProperty[] properties)
{
_surfaceType = FindProperty("_SurfaceType", properties, false);
_cullMode = FindProperty("_Cull", properties, false);
_mainTex = FindProperty("_MainTex", properties, false);
_textureSampleMode = FindProperty("_TEXTURESAMPLEMODE", properties, false);
_baseColor = FindProperty("_BaseColor", properties, false);
_tiling = FindProperty("_Tiling", properties, false);
_offset = FindProperty("_Offset", properties, false);
_hueShift = FindProperty("_HueShift", properties, false);
_lightnessMultiplier = FindProperty("_LightnessMultiplier", properties, false);
_saturationMultiplier = FindProperty("_SaturationMultiplier", properties, false);
_enableTextureDownsampling = FindProperty("_EnableTextureDownsampling", properties, false);
_textureResolution = FindProperty("_TextureResolution", properties, false);
_enableDithering = FindProperty("_EnableDithering", properties, false);
_ditheringMethod = FindProperty("_DITHERINGMETHOD", properties, false);
_ditherStrength = FindProperty("_DitherStrength", properties, false);
_ditherScale = FindProperty("_DitherScale", properties, false);
_usePalette = FindProperty("_UsePalette", properties, false);
_paletteLUT = FindProperty("_PaletteLUT", properties, false);
_useColorQuantization = FindProperty("_UseColorQuantization", properties, false);
_colorDepth = FindProperty("_ColorDepth", properties, false);
_useVertexColor = FindProperty("_UseVertexColor", properties, false);
_enableAffineTextureMapping = FindProperty("_EnableAffineTextureMapping", properties, false);
_affineWrapStrength = FindProperty("_AffineWrapStrength", properties, false);
_lightingMethod = FindProperty("_LIGHTINGMETHOD", properties, false);
_shadingMode = FindProperty("_SHADINGMETHOD", properties, false);
_metalness = FindProperty("_Metalness", properties, false);
_useSpecular = FindProperty("_UseSpecular", properties, false);
_smoothness = FindProperty("_Smoothness", properties, false);
_specularColor = FindProperty("_SpecularColor", properties, false);
_enableShadows = FindProperty("_EnableShadows", properties, false);
_shadowTint = FindProperty("_ShadowTint", properties, false);
_shadowDistCutoff = FindProperty("_ShadowDistCutoff", properties, false);
_shadowOffset = FindProperty("_ShadowOffset", properties, false);
_vertexJitterMode = FindProperty("_VERTEXJITTERMODE", properties, false);
_vertexPrecision = FindProperty("_VertexPrecision", properties, false);
_alphaClipThreshold = FindProperty("_AlphaClipThreshold", properties, false);
_waterColor = FindProperty("_WaterColor", properties, false);
_waterCrestColor = FindProperty("_WaterCrestColor", properties, false);
_waterSharpness = FindProperty("_WaterSharpness", properties, false);
_waterScale = FindProperty("_WaterScale", properties, false);
_waterSpeed = FindProperty("_WaterSpeed", properties, false);
_maxDepth = FindProperty("_MaxDepth", properties, false);
_depthDecay = FindProperty("_DepthDecay", properties, false);
_skyboxMode = FindProperty("_SKYBOXMODE", properties, false);
_procedualSkyboxMode = FindProperty("_PROCEDUALSKYBOXMODE", properties, false);
_skyboxCubemap = FindProperty("_SkyboxCubemap", properties, false);
_skyboxRotation = FindProperty("_SkyboxRotation", properties, false);
_flatGradientTopColor = FindProperty("_FlatGradientTopColor", properties, false);
_flatGradientBottomColor = FindProperty("_FlatGradientBottomColor", properties, false);
_flatGradientTopAngle = FindProperty("_FlatGradientTopAngle", properties, false);
_flatGradientBottomAngle = FindProperty("_FlatGradientBottomAngle", properties, false);
_enableNebula = FindProperty("_EnableNebula", properties, false);
_nebulaPeriod = FindProperty("_NebulaPeriod", properties, false);
_nebulaPower = FindProperty("_NebulaPower", properties, false);
_nebulaColor = FindProperty("_NebulaColor", properties, false);
_starDensity = FindProperty("_StarDensity", properties, false);
_starSharpness = FindProperty("_StarSharpness", properties, false);
}
private enum SurfaceType
{
Opaque,
Transparent
}
private enum CullOptions
{
Off,
Front,
Back
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: dcc9fd37c11a83947a0ec5138a664ce4
@@ -0,0 +1,205 @@
using System.IO;
using UnityEditor;
using UnityEngine;
//-----------------------------------------------------------------------
// Author: Colby-O
// File: PSXMaterialFactory.cs
//-----------------------------------------------------------------------
namespace ColbyO.VNTG.PSX.Editor
{
public static class PSXMaterialFactory
{
private const string PSX_SHADER_NAME = "VNTG/PSXMaster_URP";
private const string PSX_TERRIAN_SHADER_NAME = "VNTG/PSXTerrian_URP";
private const string PSX_WATER_SHADER_NAME = "VNTG/PSXWater_URP";
private const string PSX_DECAL_SHADER_NAME = "VNTG/PSXDecal_URP";
private const string PSX_SKYBOX_SHADER_NAME = "VNTG/PSXSkybox_URP";
[MenuItem("Assets/Create/VNTG/PSX Material", false)]
public static void CreatePSXMaterial()
{
CreateMateral(PSX_SHADER_NAME);
}
#if UNITY_6000_3_OR_NEWER
[MenuItem("Assets/Create/VNTG/PSX Terrian Material", false)]
public static void CreateTerrianMaterial()
{
CreateMateral(PSX_TERRIAN_SHADER_NAME);
}
#endif
[MenuItem("Assets/Create/VNTG/PSX Water Material", false)]
public static void CreateWaterMaterial()
{
CreateMateral(PSX_WATER_SHADER_NAME);
}
[MenuItem("Assets/Create/VNTG/PSX Decal Material", false)]
public static void CreateDecalMaterial()
{
CreateMateral(PSX_DECAL_SHADER_NAME);
}
[MenuItem("Assets/Create/VNTG/PSX Skybox Material", false)]
public static void CreateSkyboxMaterial()
{
CreateMateral(PSX_SKYBOX_SHADER_NAME);
}
[MenuItem("Assets/VNTG/Convert to PSX Material", false)]
public static void ConvertToPSXMaterial()
{
Material selectedMaterial = Selection.activeObject as Material;
Shader psxShader = Shader.Find(PSX_SHADER_NAME);
if (psxShader == null)
{
Debug.LogError($"PSXFactory: Could not find shader: '{PSX_SHADER_NAME}'.");
return;
}
string originalShaderName = selectedMaterial.shader.name;
bool isLit = false;
if (originalShaderName.Contains("Lit") && !originalShaderName.Contains("Unlit"))
{
isLit = true;
}
string texturePropertyName = "_MainTex";
Vector2 originalTiling = Vector2.one;
Vector2 originalOffset = Vector2.zero;
if (selectedMaterial.HasProperty(texturePropertyName))
{
originalTiling = selectedMaterial.GetTextureScale(texturePropertyName);
originalOffset = selectedMaterial.GetTextureOffset(texturePropertyName);
}
float originalCutoff = 0.5f;
bool isAlphaClipEnabled = false;
if (selectedMaterial.HasProperty("_Cutoff"))
{
originalCutoff = selectedMaterial.GetFloat("_Cutoff");
}
if (selectedMaterial.IsKeywordEnabled("_ALPHATEST_ON"))
{
isAlphaClipEnabled = true;
}
bool receiveShadows = true;
if (selectedMaterial.IsKeywordEnabled("_RECEIVE_SHADOWS_OFF"))
{
receiveShadows = false;
}
else if (selectedMaterial.HasProperty("_ReceiveShadows"))
{
receiveShadows = selectedMaterial.GetFloat("_ReceiveShadows") == 1.0f;
}
float originalSmoothness = 0.0f;
if (selectedMaterial.HasProperty("_Smoothness"))
{
originalSmoothness = selectedMaterial.GetFloat("_Smoothness");
}
else if (selectedMaterial.HasProperty("_Glossiness"))
{
originalSmoothness = selectedMaterial.GetFloat("_Glossiness");
}
float originalMetallic = 0.0f;
if (selectedMaterial.HasProperty("_Metallic"))
{
originalMetallic = selectedMaterial.GetFloat("_Metallic");
}
Undo.RecordObject(selectedMaterial, "Convert Material to PSX");
selectedMaterial.shader = psxShader;
if (isLit)
{
selectedMaterial.SetLightingMethod(PSXLightingMethod.Lit);
selectedMaterial.SetFloat("_UseSpecular", 1.0f);
selectedMaterial.SetFloat("_Smoothness", originalSmoothness);
selectedMaterial.SetFloat("_Metalness", originalMetallic);
}
else
{
selectedMaterial.SetLightingMethod(PSXLightingMethod.Unlit);
}
selectedMaterial.SetVector("_Tiling", originalTiling);
selectedMaterial.SetVector("_Offset", originalOffset);
selectedMaterial.SetFloat("_AlphaClipThreshold", isAlphaClipEnabled ? originalCutoff : 0.0f);
selectedMaterial.SetFloat("_EnableShadows", receiveShadows ? 1.0f : 0.0f);
EditorUtility.SetDirty(selectedMaterial);
AssetDatabase.SaveAssets();
Debug.Log($"Successfully converted '{selectedMaterial.name}' to PSX shader.", selectedMaterial);
}
[MenuItem("Assets/VNTG/Convert to PSX Material", true)]
public static bool ValidateConvertToPSXMaterial()
{
if (Selection.activeObject is Material mat)
{
return mat.shader.name != PSX_SHADER_NAME;
}
return false;
}
private static string GetDefaultMaterialName(string shaderName)
{
return shaderName switch
{
"VNTG/PSXMaster_URP" => "New PSX Material",
"VNTG/PSXTerrian_URP" => "New PSX Terrain Material",
"VNTG/PSXWater_URP" => "New PSX Water Material",
"VNTG/PSXDecal_URP" => "New PSX Decal Material",
"VNTG/PSXSkybox_URP" => "New PSX Skybox Material",
_ => "New Material"
};
}
private static void CreateMateral(string shaderName)
{
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (string.IsNullOrEmpty(path))
{
path = "Assets";
}
else if (!Directory.Exists(path))
{
path = Path.GetDirectoryName(path);
}
string fullPath = Path.Combine(path, $"{GetDefaultMaterialName(shaderName)}.mat");
fullPath = AssetDatabase.GenerateUniqueAssetPath(fullPath);
Shader psxShader = Shader.Find(shaderName);
if (psxShader == null)
{
Debug.LogError($"PSXFactory: Could not find shader: '{shaderName}'. Please check the shader path string in the script.");
return;
}
Material newMat = new Material(psxShader);
AssetDatabase.CreateAsset(newMat, fullPath);
AssetDatabase.SaveAssets();
EditorUtility.FocusProjectWindow();
Selection.activeObject = newMat;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6371d18c5c122c342ae07d2242997ae7
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 31968fe139c6d5e428170523a924079e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More