Modify: 닐로툰 스크립트 최신버전으로 업데이트

This commit is contained in:
Yamo4490 2025-05-15 00:25:26 +09:00
parent 0405f6ccf0
commit 85a03a3cbe
152 changed files with 14708 additions and 2291 deletions

View File

@ -96,16 +96,12 @@ namespace MagicaCloth2
//=========================================================================================
[MenuItem("Tools/Magica Cloth2/Manager information", false)]
static void DispClothManagerInfo()
static async void DispClothManagerInfo()
{
if (MagicaManager.IsPlaying() == false)
{
Debug.Log("This feature is run-time only.");
return;
}
StringBuilder allsb = new StringBuilder();
await ClothEditorManager.InformationLog(allsb);
var timeManager = MagicaManager.Time;
if (timeManager == null)
{
@ -186,8 +182,15 @@ namespace MagicaCloth2
renderManager.InformationLog(allsb);
}
// clipboard
//GUIUtility.systemCopyBuffer = allsb.ToString();
var preBuildManager = MagicaManager.PreBuild;
if (preBuildManager == null)
{
Debug.LogWarning("PreBuild Manager is null!");
}
else
{
preBuildManager.InformationLog(allsb);
}
// file
DateTime dt = DateTime.Now;
@ -197,5 +200,24 @@ namespace MagicaCloth2
sw.Flush();
sw.Close();
}
//=========================================================================================
// インスペクターのコンテキストメニュー
[MenuItem("CONTEXT/MagicaCloth/Rebuild InitData")]
private static void SampleMenu(MenuCommand menuCommand)
{
// 初期化データをクリアして再構築する
var cloth = menuCommand.context as MagicaCloth;
if (cloth)
{
cloth.GetSerializeData2().initData.Clear();
EditorUtility.SetDirty(cloth);
// 編集用メッシュの再構築
ClothEditorManager.RegisterComponent(cloth, GizmoType.Active, true); // 強制更新
Develop.Log($"[{cloth.name}] Initialization data rebuilt.");
}
}
}
}

View File

@ -1,6 +1,7 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
@ -10,6 +11,7 @@ namespace MagicaCloth2
{
// ギズモカラー定義
public static readonly Color ColorCollider = new Color(0.0f, 1.0f, 0.0f);
public static readonly Color ColorSymmetryCollider = new Color(0.0f, 1.0f, 1.0f);
public static readonly Color ColorNonSelectedCollider = new Color(0.5f, 0.3f, 0.0f);
public static readonly Color ColorSkinningBone = new Color(1.0f, 0.5f, 0.0f);
public static readonly Color ColorWindZone = new Color(1f, 1f, 1f);
@ -162,56 +164,142 @@ namespace MagicaCloth2
}
//=========================================================================================
public static void DrawCollider(ColliderComponent collider, Quaternion camRot, bool useHandles, bool selected)
public static void DrawCollider(ColliderComponent collider, Quaternion camRot, bool selected)
{
if (collider == null)
return;
var cpos = collider.transform.TransformPoint(collider.center);
var crot = collider.transform.rotation;
var cscl = Vector3.one * collider.GetScale(); // スケールはx軸のみつまり均等スケールのみ
Handles.color = selected ? ColorCollider : ColorCollider * 0.5f;
// Main
var ct = collider.transform;
//var cpos = ct.TransformPoint(collider.center);
float3 cpos = ct.position;
quaternion crot = ct.rotation;
float3 cscl = ct.lossyScale;
// マイナススケール
float3 sclSign = math.sign(cscl);
// オフセット
cpos += math.mul(crot, collider.center * sclSign) * cscl * sclSign;
// カメラ回転をコライダーのローカル回転に変換
camRot = Quaternion.Inverse(crot) * camRot;
var camRotN = Quaternion.Inverse(crot) * camRot;
DrawColliderInternal(collider, camRotN, cpos, crot, cscl, 1.0f);
// サイズ
var size = collider.GetSize();
if (useHandles)
// Symmetry
// 実行時と同じ計算をして表示
ColliderSymmetryMode? smode = ColliderSymmetryMode.None;
Transform symmetryParent = null;
if (EditorApplication.isPlaying)
{
Handles.matrix = Matrix4x4.TRS(cpos, crot, cscl);
Handles.color = selected ? ColorCollider : ColorCollider * 0.5f;
switch (collider.GetColliderType())
{
case ColliderManager.ColliderType.Sphere:
DrawWireSphere(Vector3.zero, Quaternion.identity, size.x, camRot, true);
break;
case ColliderManager.ColliderType.CapsuleX_Center:
DrawWireCapsule(Vector3.zero, Quaternion.identity, Vector3.right, Vector3.up, size.x, size.y, size.z, true, camRot, true);
break;
case ColliderManager.ColliderType.CapsuleY_Center:
DrawWireCapsule(Vector3.zero, Quaternion.identity, Vector3.up, Vector3.right, size.x, size.y, size.z, true, camRot, true);
break;
case ColliderManager.ColliderType.CapsuleZ_Center:
DrawWireCapsule(Vector3.zero, Quaternion.identity, Vector3.forward, Vector3.up, size.x, size.y, size.z, true, camRot, true);
break;
case ColliderManager.ColliderType.CapsuleX_Start:
DrawWireCapsule(Vector3.zero, Quaternion.identity, Vector3.right, Vector3.up, size.x, size.y, size.z, false, camRot, true);
break;
case ColliderManager.ColliderType.CapsuleY_Start:
DrawWireCapsule(Vector3.zero, Quaternion.identity, Vector3.up, Vector3.right, size.x, size.y, size.z, false, camRot, true);
break;
case ColliderManager.ColliderType.CapsuleZ_Start:
DrawWireCapsule(Vector3.zero, Quaternion.identity, Vector3.forward, Vector3.up, size.x, size.y, size.z, false, camRot, true);
break;
case ColliderManager.ColliderType.Plane:
DrawWireCube(Vector3.zero, Quaternion.identity, new Vector3(1.0f, 0.0f, 1.0f) * 1.0f, true);
break;
}
smode = collider.ActiveSymmetryMode;
symmetryParent = collider.ActiveSymmetryTarget;
}
else
if (smode.HasValue == false || smode == ColliderSymmetryMode.None)
smode = collider.CalcSymmetryMode(out symmetryParent);
if (smode != ColliderSymmetryMode.None && symmetryParent)
{
float3 lpos = ct.localPosition;
//float3 lerot = ct.localEulerAngles;
float3 lerot = MathUtility.ToEuler(ct.localRotation);
float3 lscl = ct.localScale;
float3 center = collider.center;
switch (smode)
{
case ColliderSymmetryMode.X_Symmetry:
lpos.x = -lpos.x;
center.x = -center.x;
lerot.y = -lerot.y;
lerot.z = -lerot.z;
break;
case ColliderSymmetryMode.Y_Symmetry:
lpos.y = -lpos.y;
center.y = -center.y;
lerot.x = -lerot.x;
lerot.z = -lerot.z;
break;
case ColliderSymmetryMode.Z_Symmetry:
lpos.z = -lpos.z;
center.z = -center.z;
lerot.x = -lerot.x;
lerot.y = -lerot.y;
break;
case ColliderSymmetryMode.XYZ_Symmetry:
lpos = -lpos;
center = -center;
break;
default:
return;
}
// 方向性
float direction = 1.0f;
if (collider is MagicaCapsuleCollider)
{
var ccol = collider as MagicaCapsuleCollider;
if (smode == ColliderSymmetryMode.X_Symmetry && ccol.direction == MagicaCapsuleCollider.Direction.X)
direction = -1.0f;
else if (smode == ColliderSymmetryMode.Y_Symmetry && ccol.direction == MagicaCapsuleCollider.Direction.Y)
direction = -1.0f;
else if (smode == ColliderSymmetryMode.Z_Symmetry && ccol.direction == MagicaCapsuleCollider.Direction.Z)
direction = -1.0f;
else if (smode == ColliderSymmetryMode.XYZ_Symmetry)
direction = -1.0f;
}
else if (collider is MagicaPlaneCollider)
{
switch (smode)
{
case ColliderSymmetryMode.Y_Symmetry:
case ColliderSymmetryMode.XYZ_Symmetry:
direction = -1.0f;
break;
}
}
// シンメトリーの親
float3 ppos = symmetryParent.position;
quaternion prot = symmetryParent.rotation;
float3 pscl = symmetryParent.lossyScale;
// マイナススケール
sclSign = math.sign(pscl);
float3 sclEulerSign = 1;
if (pscl.x < 0 || pscl.y < 0 || pscl.z < 0)
sclEulerSign = sclSign * -1;
// シンメトリーコライダーの姿勢
float3 wpos = MathUtility.TransformPoint(lpos, ppos, prot, pscl);
quaternion wrot = math.mul(prot, quaternion.Euler(math.radians(lerot * sclEulerSign)));
float3 wscl = pscl * lscl;
wpos += math.mul(wrot, center * sclSign) * wscl * sclSign;
// カメラ回転をコライダーのローカル回転に変換
var camRotS = Quaternion.Inverse(wrot) * camRot;
Handles.color = selected ? ColorSymmetryCollider : ColorSymmetryCollider * 0.5f;
DrawColliderInternal(collider, camRotS, wpos, wrot, wscl, direction);
}
}
static void DrawColliderInternal(ColliderComponent collider, Quaternion camRot, Vector3 cpos, Quaternion crot, Vector3 cscl, float direction)
{
var size = collider.GetSize();
Handles.matrix = Matrix4x4.TRS(cpos, crot, cscl);
if (collider is MagicaSphereCollider)
{
DrawWireSphere(Vector3.zero, Quaternion.identity, size.x, camRot, true);
}
else if (collider is MagicaPlaneCollider)
{
DrawWireCube(Vector3.zero, Quaternion.identity, new Vector3(1.0f, 0.0f, 1.0f) * 1.0f, true);
DrawLine(Vector3.zero, Vector3.up * 0.25f * direction, true);
}
else if (collider is MagicaCapsuleCollider)
{
var c = collider as MagicaCapsuleCollider;
var ldir = c.GetLocalDir() * direction;
var lup = c.GetLocalUp();
DrawWireCapsule(Vector3.zero, Quaternion.identity, ldir, lup, size.x, size.y, size.z, c.alignedOnCenter, camRot, true);
}
}
@ -300,23 +388,6 @@ namespace MagicaCloth2
Gizmos.matrix = Matrix4x4.identity;
}
//public static void DrawWireSphere(
// Vector3 pos, Quaternion rot, Vector3 scl, float radius,
// Quaternion camRot, bool useHandles
// )
//{
// if(useHandles)
// {
// Handles.matrix = Matrix4x4.TRS(pos, rot, scl);
// }
// else
// {
// }
//}
#if false
/// <summary>
/// ワイヤーボックスを描画する

BIN
Assets/External/NiloToonURP/CHANGELOG.md (Stored with Git LFS) vendored

Binary file not shown.

View File

@ -10,7 +10,8 @@ namespace NiloToon.NiloToonURP
{
public class NiloToonEditor_AutoGeneratePrefabVariantAndAssets : Editor
{
[MenuItem("Window/NiloToonURP/Create Nilo Prefab Variant and Materials", priority = 0)]
[MenuItem("Window/NiloToonURP/[Prefab] Create NiloToon Prefab Variant and Materials", priority = 0)]
[MenuItem("Assets/NiloToon/[Prefab] Create NiloToon Prefab Variant and Materials", priority = 1100 + 0)]
static void CreatePrefabVariantAndCloneMaterials()
{
// Get the selected prefab
@ -186,7 +187,8 @@ namespace NiloToon.NiloToonURP
EditorUtility.DisplayDialog("Success", "Prefab variant created with cloned materials.", "OK");
}
[MenuItem("Window/NiloToonURP/Create Nilo Prefab Variant and Materials", priority = 0, validate = true)]
[MenuItem("Window/NiloToonURP/[Prefab] Create NiloToon Prefab Variant and Materials", priority = 0, validate = true)]
[MenuItem("Assets/NiloToon/[Prefab] Create NiloToon Prefab Variant and Materials", priority = 1100 + 0, validate = true)]
public static bool ValidateCreatePrefabVariantAndCloneMaterials()
{
var allselectedObjects = Selection.objects;

View File

@ -307,45 +307,41 @@ namespace NiloToon.NiloToonURP
matOriginal.shader.name.Contains("Overlay");
bool isOutl = matOriginal.shader.name.Contains("Outline");
bool isTransparentQueue = matOriginal.renderQueue > 2500;
bool isZWrite = matOriginal.GetFloat("_ZWrite") > 0.5f;
if (!isCutout && !isTransparent && !isOutl && !isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Opaque;
else if (!isCutout && !isTransparent && !isOutl && isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] =
NiloToonSurfaceTypePreset.TransparentQueueTransparent_ZWrite;
else if (!isCutout && !isTransparent && isOutl && !isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Opaque_Outline;
else if (!isCutout && !isTransparent && isOutl && isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent_ZWrite_Outline;
else if (!isCutout && isTransparent && !isOutl && !isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent_ZWrite;
else if (!isCutout && isTransparent && !isOutl && isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent;
else if (!isCutout && isTransparent && isOutl && !isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent_ZWrite_Outline;
else if (!isCutout && isTransparent && isOutl && isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] =
NiloToonSurfaceTypePreset.TransparentQueueTransparent_ZWrite;
else if (isCutout && !isTransparent && !isOutl && !isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutOpaque;
else if (isCutout && !isTransparent && !isOutl && isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutOpaque;
else if (isCutout && !isTransparent && isOutl && !isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutOpaque_Outline;
else if (isCutout && !isTransparent && isOutl && isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] =
NiloToonSurfaceTypePreset.CutoutTransparentQueueTransparent_ZWrite_Outline;
else if (isCutout && isTransparent && !isOutl && !isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] =
NiloToonSurfaceTypePreset.CutoutTransparentQueueTransparent_ZWrite;
else if (isCutout && isTransparent && !isOutl && isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent;
else if (isCutout && isTransparent && isOutl && !isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] =
NiloToonSurfaceTypePreset.CutoutTransparent_ZWrite_Outline;
else if (isCutout && isTransparent && isOutl && isTransparentQueue)
niloToonSurfaceTypePresetIDArray[i] =
NiloToonSurfaceTypePreset.CutoutTransparentQueueTransparent_ZWrite_Outline;
// if ZWrite is off, we treat the material as transparent
if (!isCutout && !isTransparent && !isOutl && !isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Opaque; // 1
else if (!isCutout && !isTransparent && !isOutl && !isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent; // 2
else if (!isCutout && !isTransparent && !isOutl && isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.TransparentQueueTransparent_ZWrite; // 3
else if (!isCutout && !isTransparent && !isOutl && isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent; // 4
else if (!isCutout && !isTransparent && isOutl && !isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Opaque_Outline; // 5
else if (!isCutout && !isTransparent && isOutl && !isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent; // 6
else if (!isCutout && !isTransparent && isOutl && isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent_ZWrite_Outline; // 7
else if (!isCutout && !isTransparent && isOutl && isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent; // 8
else if (!isCutout && isTransparent && !isOutl && !isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent_ZWrite; // 9
else if (!isCutout && isTransparent && !isOutl && !isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent; // 10
else if (!isCutout && isTransparent && !isOutl && isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent_ZWrite; // 11
else if (!isCutout && isTransparent && !isOutl && isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent; // 12
else if (!isCutout && isTransparent && isOutl && !isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent_ZWrite_Outline; // 13
else if (!isCutout && isTransparent && isOutl && !isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent; // 14
else if (!isCutout && isTransparent && isOutl && isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.TransparentQueueTransparent_ZWrite; // 15
else if (!isCutout && isTransparent && isOutl && isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.Transparent; // 16
else if ( isCutout && !isTransparent && !isOutl && !isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutOpaque; // 17
else if ( isCutout && !isTransparent && !isOutl && !isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent; // 18
else if ( isCutout && !isTransparent && !isOutl && isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutOpaque; // 19
else if ( isCutout && !isTransparent && !isOutl && isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent; // 20
else if ( isCutout && !isTransparent && isOutl && !isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutOpaque_Outline; // 21
else if ( isCutout && !isTransparent && isOutl && !isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent; // 22
else if ( isCutout && !isTransparent && isOutl && isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparentQueueTransparent_ZWrite_Outline; // 23
else if ( isCutout && !isTransparent && isOutl && isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent; // 24
else if ( isCutout && isTransparent && !isOutl && !isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparentQueueTransparent_ZWrite; // 25
else if ( isCutout && isTransparent && !isOutl && !isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent; // 26
else if ( isCutout && isTransparent && !isOutl && isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent_ZWrite; // 27
else if ( isCutout && isTransparent && !isOutl && isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent; // 28
else if ( isCutout && isTransparent && isOutl && !isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent_ZWrite_Outline; // 29
else if ( isCutout && isTransparent && isOutl && !isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent; // 30
else if ( isCutout && isTransparent && isOutl && isTransparentQueue && isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparentQueueTransparent_ZWrite_Outline; //31
else if ( isCutout && isTransparent && isOutl && isTransparentQueue && !isZWrite) niloToonSurfaceTypePresetIDArray[i] = NiloToonSurfaceTypePreset.CutoutTransparent; // 32
}
// since there are many model's texture that alpha is not used for transparency(e.g. store special data that is not alpha),
@ -516,7 +512,7 @@ namespace NiloToon.NiloToonURP
};
string[] IsEyeBanNames = { "brown" }; // avoid "brown" treated as eye due to "brow"
string[] IsMouthTargetNames = { "mouth", "oral", "tongue", "kuchi", ".ha" };
string[] IsMouthTargetNames = { "mouth", "oral", "tongue", "kuchi", ".ha", "kounai", "shita" };
string[] IsMouthBanNames = { ".hada" };
string[] IsTeethTargetNames = { "teeth", "tooth" };
@ -540,6 +536,8 @@ namespace NiloToon.NiloToonURP
IsFaceFinalTargetNames = IsFaceFinalTargetNames.Concat(IsMouthTargetNames).ToArray();
IsFaceFinalTargetNames = IsFaceFinalTargetNames.Concat(IsTeethTargetNames).ToArray();
string[] IsFaceExactTargetNames = new string[]{ "me", "ha"};
string[] IsFaceFinalBanNames = new string[] { };
IsFaceFinalBanNames = IsFaceFinalBanNames.Concat(IsFaceBanNames).ToArray();
IsFaceFinalBanNames = IsFaceFinalBanNames.Concat(IsEyeBanNames).ToArray();
@ -561,6 +559,9 @@ namespace NiloToon.NiloToonURP
IsFaceFinalTargetNames.Where(x => x != "face" && x != "head").ToList();
IsNoOutlineFinalTargetNames.Concat(IsNoOutlineTargetNames);
List<string> IsNoOutlineExactTargetNames = new List<string>();
IsNoOutlineExactTargetNames.Concat(IsFaceExactTargetNames);
List<string> IsNoOutlineFinalBanNames = IsFaceFinalBanNames.ToList();
IsNoOutlineFinalBanNames.Concat(IsNoOutlineBanNames);
@ -573,6 +574,11 @@ namespace NiloToon.NiloToonURP
isNoOutline |= NiloToonUtils.NameHasKeyword(mat.name, keyword);
}
foreach (var keyword in IsNoOutlineExactTargetNames)
{
isNoOutline |= NiloToonUtils.NameEqualsKeywordIgnoreCase(mat.name, keyword);
}
foreach (var keyword in IsNoOutlineFinalBanNames)
{
isNoOutline &= !NiloToonUtils.NameHasKeyword(mat.name, keyword);
@ -642,11 +648,11 @@ namespace NiloToon.NiloToonURP
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (originalMatClone.shader == VRMBRPMToon00Shader)
{
const float niloToonWidthRelativeToMToon00 = 4;
const float niloToonWidthRelativeToMToon00 = 12; //12~18 is good
mat.SetFloat("_OutlineWidthExtraMultiplier", niloToonWidthRelativeToMToon00);
// extra check to limit any unexpected large outline width
float maxFinalWidth = 0.6f; // 0.6 is NiloToon's default outline width
float maxFinalWidth = 0.5f; // 0.5 is NiloToon's default outline width
float finalWidth = Mathf.Min(maxFinalWidth,
mat.GetFloat("_OutlineWidth") * niloToonWidthRelativeToMToon00);
mat.SetFloat("_OutlineWidth", finalWidth / niloToonWidthRelativeToMToon00);
@ -662,7 +668,7 @@ namespace NiloToon.NiloToonURP
mat.SetFloat("_OutlineWidthExtraMultiplier", niloToonWidthRelativeToMToon10);
// extra check to limit any unexpected large outline width
float maxFinalWidth = 0.6f; // 0.6 is NiloToon's default outline width
float maxFinalWidth = 0.5f; // 0.5 is NiloToon's default outline width
float finalWidth = Mathf.Min(maxFinalWidth,
mat.GetFloat("_OutlineWidth") * niloToonWidthRelativeToMToon10);
mat.SetFloat("_OutlineWidth", finalWidth / niloToonWidthRelativeToMToon10);
@ -865,6 +871,11 @@ namespace NiloToon.NiloToonURP
isFace |= NiloToonUtils.NameHasKeyword(mat.name, keyword);
}
foreach (var keyword in IsFaceExactTargetNames)
{
isFace |= NiloToonUtils.NameEqualsKeywordIgnoreCase(mat.name,keyword);
}
foreach (var keyword in IsFaceFinalBanNames)
{
isFace &= !NiloToonUtils.NameHasKeyword(mat.name, keyword);
@ -1377,7 +1388,7 @@ namespace NiloToon.NiloToonURP
toNiloToonMat.SetFloat("_OutlineWidthExtraMultiplier", niloToonWidthRelativeTolilToon);
// extra check to limit any unexpected large outline width
float maxFinalWidth = 0.6f; // 0.6 is NiloToon's default outline width
float maxFinalWidth = 0.5f; // 0.5 is NiloToon's default outline width
float finalWidth = Mathf.Min(maxFinalWidth, _OutlineWidth * niloToonWidthRelativeTolilToon);
toNiloToonMat.SetFloat("_OutlineWidth", finalWidth / niloToonWidthRelativeTolilToon);
@ -1797,7 +1808,8 @@ namespace NiloToon.NiloToonURP
}
#if UNITY_EDITOR
[MenuItem("Window/NiloToonURP/Convert Selected Materials to NiloToon", priority = 2)]
[MenuItem("Window/NiloToonURP/[Material] Convert Selected Materials to NiloToon", priority = 2)]
[MenuItem("Assets/NiloToon/[Material] Convert Selected Materials to NiloToon", priority = 1100 + 2)]
public static void ConvertSelectedMaterialsToNiloToon_Character()
{
var allselectedObjects = Selection.objects;
@ -1817,7 +1829,8 @@ namespace NiloToon.NiloToonURP
AutoConvertMaterialsToNiloToon(allInputMaterials);
}
[MenuItem("Window/NiloToonURP/Convert Selected Materials to NiloToon", priority = 2, validate = true)]
[MenuItem("Window/NiloToonURP/[Material] Convert Selected Materials to NiloToon", priority = 2, validate = true)]
[MenuItem("Assets/NiloToon/[Material] Convert Selected Materials to NiloToon", priority = 1100 + 2, validate = true)]
public static bool ValidateConvertSelectedMaterialsToNiloToon_Character()
{
var allselectedObjects = Selection.objects;

View File

@ -7,7 +7,9 @@ using System.IO;
using System.Collections;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System;
namespace NiloToon.NiloToonURP
{
@ -15,16 +17,30 @@ namespace NiloToon.NiloToonURP
{
private string ffmpegPath;
private string inputFilePath = string.Empty;
private string customSuffix = "(BakedMotionBlur)_<fps>_<shutterspeed>"; // Custom suffix for the output file
private int crf = 0; // Default CRF value
private string customSuffix = "(BakedMotionBlur)_<fps>_<shutterspeed>_<codec>"; // Custom suffix for the output file
private int crf = 0; // Default CRF value (0 is lossless for CRF)
private List<float> fpsOptions = new List<float>(); // List of possible FPS options
private int selectedFPSIndex = 0; // Default FPS index
private float inputVideoFPS = 0.0f; // FPS of the input video
private double inputVideoDuration = 0.0; // Duration of the input video in seconds
private int totalFrames = 0; // Total number of frames in the output video
private float cameraExposureDuration = 1f / 48f; // 180 shutter angle in terms of 24fps
private float motionBlurAmount = 1;
private string ffmpegArgumentsTemplate =
"-vf \"{0}format=yuv420p\" -r {1} -c:v libx264 -preset veryslow -crf {2} -pix_fmt yuv420p -x264opts \"keyint=12:min-keyint=1:ref=1:bframes=0:qcomp=0.8:aq-strength=0.5:direct=auto:fast-pskip=0:deblock=-2,-2\"";
private string ffmpegArgumentsTemplate_H264 =
"-vf \"{0}format=yuv420p\" -r {1} -c:v libx264 -preset veryslow -crf {2} -pix_fmt yuv420p -x264opts \"keyint=1:ref=4:bframes=0\"";
private string ffmpegArgumentsTemplate_H265 =
"-vf \"{0}format=yuv420p\" -r {1} -c:v libx265 -preset veryslow -x265-params \"crf={2}:keyint=1:no-open-gop=1\" -pix_fmt yuv420p";
private string ffmpegArgumentsTemplate_H265_10bit =
"-vf \"{0}format=yuv420p10le\" -r {1} -c:v libx265 -preset veryslow -x265-params \"crf={2}:keyint=1:no-open-gop=1\" -pix_fmt yuv420p10le";
private string ffmpegArgumentsTemplate_ProRes_422HQ =
"-vf \"{0}\" -r {1} -c:v prores_ks -profile:v 3 -pix_fmt yuv422p10le"; // ProRes 422 HQ
private string ffmpegArgumentsTemplate_ProRes =
"-vf \"{0}\" -r {1} -c:v prores_ks -profile:v 5 -pix_fmt yuv444p10le"; // ProRes 4444 XQ
private Process ffmpegProcess;
private Coroutine coroutine;
@ -36,6 +52,13 @@ namespace NiloToon.NiloToonURP
private Vector2 scrollPosition;
private string[] codecOptions = new string[] { "H.264", "H.265 (8-bit)", "H.265 (10-bit)", "ProRes 422 HQ", "ProRes 4444 XQ" };
private int selectedCodecIndex = 3; // Default to ProRes 422 HQ
// Added variables for logging interval and process timing
private DateTime lastLogTime = DateTime.MinValue;
private DateTime processStartTime;
[MenuItem("Window/NiloToonURP/MotionBlur Video Baker", priority = 10000)]
public static void ShowWindow()
{
@ -43,20 +66,44 @@ namespace NiloToon.NiloToonURP
window.LoadFFmpegPath(); // Load the FFmpeg path when the window is opened
}
private bool ClickableLink(string text, string url)
{
GUIStyle linkStyle = new GUIStyle(GUI.skin.label)
{
richText = true,
normal = { textColor = Color.cyan },
hover = { textColor = Color.cyan },
alignment = TextAnchor.MiddleLeft
};
Rect rect = GUILayoutUtility.GetRect(new GUIContent(text), linkStyle, GUILayout.Height(20));
EditorGUIUtility.AddCursorRect(rect, MouseCursor.Link);
if (GUI.Button(rect, $"{text}", linkStyle))
{
Application.OpenURL(url);
return true;
}
return false;
}
private void OnGUI()
{
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
// 960fps will make cinemachine not working correctly, so we suggest max at 900fps
// 960fps may make cinemachine not working correctly, so we suggest max at 900fps
EditorGUILayout.HelpBox(
"[Purpose of this tool]\n" +
"Bake 480-900 fps video with zero or little motion blur -> 24/30/60 fps video with cinematic motion blur & AA produced by sub frame merging\n\n" +
"[How to use?]\n" +
"1.Locate your own ffmpeg.exe (download from ffmpeg.org)\n" +
"2.Prepare a video with 480~900FPS (e.g., Record using Unity's Recorder with a high custom FPS)\n" +
"3.Select that video as Input Video, wait for analysis\n" +
"4.(optional)Adjust other settings if needed\n" +
"5.Click 'Bake now!', this tool will bake cinematic motion blur & AA to a 24/30/60 fps output video (H.264)", MessageType.Info);
"[Purpose of this tool]\n" +
"Import a 480-960 fps short video, bake to a 24/30/60 fps video with high-quality motion blur & AA produced by sub-frame accumulation, similar to UnrealEngine Movie Render Queue's temporal sampling AA\n\n" +
"[How to use?]\n" +
"1. Locate your own ffmpeg.exe (download the latest from ffmpeg.org)\n" +
"2. Select a 480~960FPS video as Input Video(e.g., set NiloToonMotionBlurVolume = 0.5~0.25 intensity, then record using Unity's Recorder with 480~960 FPS into ProRes422LT or higher)\n" +
"3. Click 'Bake now!', this tool will bake motion blur & AA into a result 24/30/60 fps output video", MessageType.Info);
// add here
ClickableLink("Online Document", "https://docs.google.com/document/d/1iEh1E5xLXnXuICM0ElV3F3x_J2LND9Du2SSKzcIuPXw/edit?tab=t.0#heading=h.tkl2vd8auzt9");
ClickableLink("Download FFmpeg", "https://ffmpeg.org");
GUI.enabled = ffmpegProcess == null;
@ -65,7 +112,7 @@ namespace NiloToon.NiloToonURP
////////////////////////////////////////////////////////////////////////
GUILayout.Label("FFmpeg Path", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
ffmpegPath = EditorGUILayout.TextField("Path",ffmpegPath);
ffmpegPath = EditorGUILayout.TextField("Path", ffmpegPath);
if (GUILayout.Button("...", GUILayout.Width(60)))
{
string selectedPath = EditorUtility.OpenFilePanel("Select FFmpeg Executable", "", "exe");
@ -84,21 +131,21 @@ namespace NiloToon.NiloToonURP
GUILayout.Label("Input Video", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
inputFilePath = EditorGUILayout.TextField("Path",inputFilePath);
inputFilePath = EditorGUILayout.TextField("Path", inputFilePath);
if (GUILayout.Button("...", GUILayout.Width(30)) && ffmpegProcess == null)
{
string newPath = EditorUtility.OpenFilePanel("Select Input File", "", "mov,mp4");
if (!string.IsNullOrEmpty(newPath))
{
inputFilePath = newPath;
ExtractVideoFPS(inputFilePath); // Extract the FPS of the selected video
ExtractVideoInfo(inputFilePath); // Extract the FPS and duration of the selected video
}
}
EditorGUILayout.EndHorizontal();
if (inputVideoFPS < 480)
{
EditorGUILayout.HelpBox("Expect a video with 480~900FPS", MessageType.Info);
EditorGUILayout.HelpBox("Expect a video with 480~960FPS", MessageType.Info);
}
// Display error message if input video path is invalid
@ -108,7 +155,7 @@ namespace NiloToon.NiloToonURP
}
////////////////////////////////////////////////////////////////////////
// input video fps
// Input Video FPS
////////////////////////////////////////////////////////////////////////
if (inputVideoFPS > 0 && File.Exists(inputFilePath))
{
@ -118,11 +165,14 @@ namespace NiloToon.NiloToonURP
GUILayout.Space(25);
////////////////////////////////////////////////////////////////////////
// output video (suffix,fps,CRF)
// Output Video Settings
////////////////////////////////////////////////////////////////////////
GUILayout.Label("Output Video", EditorStyles.boldLabel);
// Codec Selection
selectedCodecIndex = EditorGUILayout.Popup(new GUIContent("Codec"), selectedCodecIndex, codecOptions);
if (fpsOptions.Count > 0)
{
selectedFPSIndex = Mathf.Clamp(selectedFPSIndex, 0, fpsOptions.Count - 1);
@ -130,7 +180,6 @@ namespace NiloToon.NiloToonURP
if (newSelectedFPSIndex != selectedFPSIndex)
{
selectedFPSIndex = newSelectedFPSIndex;
UnityEngine.Debug.Log($"Selected FPS: {GetOutputFPS()}"); // Log the selected FPS
}
}
@ -140,16 +189,42 @@ namespace NiloToon.NiloToonURP
}
motionBlurAmount = EditorGUILayout.Slider("Motion Blur", motionBlurAmount, 0f, 4f);
crf = EditorGUILayout.IntSlider("CRF", crf, 0, 51);
EditorGUILayout.HelpBox(
"CRF (Constant Rate Factor) controls the quality of the video. Lower values mean higher quality and larger file sizes. The range is from 0 (lossless) to 51 (worst quality).",
MessageType.Info);
// CRF Slider (Only for codecs that support it)
if (selectedCodecIndex != 3 && selectedCodecIndex != 4) // Not ProRes
{
crf = EditorGUILayout.IntSlider(new GUIContent("CRF", "For lossless encoding, set CRF to 0. Higher values reduce quality."), crf, 0, 51);
if (crf == 0)
{
EditorGUILayout.HelpBox("CRF is set to 0: Lossless encoding. This results in the highest quality and largest file size.", MessageType.Info);
}
else
{
EditorGUILayout.HelpBox("CRF controls the quality of the video. Lower values mean higher quality and larger file sizes.", MessageType.Info);
}
}
customSuffix = EditorGUILayout.TextField("Suffix", customSuffix);
// Open Destination Folder Button
GUI.enabled = !string.IsNullOrEmpty(inputFilePath) && File.Exists(inputFilePath);
if (GUILayout.Button("Open Destination Folder"))
{
string currentOutputFilePath = GenerateOutputFilePath(inputFilePath, customSuffix);
string folder = Path.GetDirectoryName(currentOutputFilePath);
if (Directory.Exists(folder))
{
OpenFolder(folder);
}
else
{
EditorUtility.DisplayDialog("Folder Not Found", "The destination folder does not exist.", "OK");
}
}
GUI.enabled = ffmpegProcess == null;
////////////////////////////////////////////////////////////////////////
// output video info
// Output Video Info
////////////////////////////////////////////////////////////////////////
if (fpsOptions.Count > 0)
{
@ -158,30 +233,18 @@ namespace NiloToon.NiloToonURP
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Output info:", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(
$"- TMix Frame Count: {currenttmixFrameCount}\n" +
$"- Shutter Angle in terms of 24fps: {GetShutterAngleInTermsOf24fps()} degrees (~180 is a good default for cinematic output)\n" +
$"- Shutter Speed: {GetShutterSpeedDisplayString()} (~1/48 is a good default for cinematic output)"
$"- Temporal Sample(TMix) Count: {currenttmixFrameCount} (>=16 is good for cinematic output)\n" +
$"- Shutter Angle in terms of 24fps: {GetShutterAngleInTermsOf24fps()} degrees (~180 is a good default for cinematic output)\n" +
$"- Shutter Speed: {GetShutterSpeedDisplayString()} (~1/48 is a good default for cinematic output)"
, MessageType.Info);
// check if shutter angle is too small
if (currenttmixFrameCount < Mathf.CeilToInt(inputVideoFPS / GetOutputFPS()/ 2))
// Check if shutter angle is too small
if (currenttmixFrameCount < Mathf.CeilToInt(inputVideoFPS / GetOutputFPS() / 2))
{
EditorGUILayout.HelpBox(
$"Current Shutter Angle for {GetOutputFPS()}fps is < 180, while it is not wrong, it may produce not enough motion blur"
, MessageType.Warning);
}
// it is ok to go over 360, so don't give warning.
// for example, 60fps output with 1/48th shutter speed
/*
// check if shutter angle is too big
if (currenttmixFrameCount > (inputVideoFPS / GetOutputFPS()))
{
EditorGUILayout.HelpBox(
$"Current Shutter Angle is too large, it may produce too much motion blur"
, MessageType.Warning);
}
*/
}
GUILayout.Space(25);
@ -189,12 +252,13 @@ namespace NiloToon.NiloToonURP
EditorGUILayout.LabelField("Extra note:", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(
$"- Make sure Project Settings > VFX > Fixed Time Step is using '1/fps of recorder' when recording\n" +
$"- Do not record in 960fps or higher, it may break cinemachine's camera movement"
$"- If fps is high (e.g., 900fps or higher), it may break cinemachine's camera movement\n" +
$"- Example input video: 1440p 960fps, or 2160p 480fps"
, MessageType.Info);
GUI.enabled = true;
////////////////////////////////////////////////////////////////////////
// Bake button
// Bake Button
////////////////////////////////////////////////////////////////////////
// Disable the "Bake Now!" button if any condition is not met
@ -279,6 +343,7 @@ namespace NiloToon.NiloToonURP
{
return (float)currenttmixFrameCount / (inputVideoFPS / 24f) * 360f;
}
private float GetShutterSpeed()
{
return (1f / 24f) * (GetShutterAngleInTermsOf24fps() / 360f);
@ -286,17 +351,38 @@ namespace NiloToon.NiloToonURP
private string GetShutterSpeedDisplayString()
{
return $"1/{1 / GetShutterSpeed()}s";
return $"1/{FormatNumber(1 / GetShutterSpeed())}s";
}
private string GetShutterSpeedFileNameString()
{
return $"OneOver{1 / GetShutterSpeed()}s";
return $"OneOver{FormatNumber(1 / GetShutterSpeed())}s";
}
// Helper method to format numbers without unnecessary trailing zeros
private string FormatNumber(double number)
{
if (number % 1 == 0)
return number.ToString("0");
else
return number.ToString("0.##");
}
private string GenerateOutputFilePath(string inputPath, string suffix)
{
suffix = suffix.Replace("<fps>", $"{GetOutputFPS()}fps");
suffix = suffix.Replace("<shutterspeed>", $"{GetShutterSpeedFileNameString()}");
// Handle codec placeholder
if (suffix.Contains("<codec>"))
{
// Get codec name and format it for filename
string codecName = codecOptions[selectedCodecIndex];
codecName = codecName.Replace(" ", "").Replace("(", "").Replace(")", "").Replace(".", "").Replace("-", "");
suffix = suffix.Replace("<codec>", codecName);
}
string directory = Path.GetDirectoryName(inputPath);
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(inputPath);
string extension = Path.GetExtension(inputPath);
@ -306,30 +392,62 @@ namespace NiloToon.NiloToonURP
private IEnumerator StartFFmpegProcessCoroutine(string outputFilePath, float selectedFPS)
{
string tmixFilters = Generate_tmixFilters(inputVideoFPS);
string ffmpegArguments = string.Format(ffmpegArgumentsTemplate, tmixFilters, selectedFPS, crf);
// Record start time
processStartTime = DateTime.Now;
// Calculate totalFrames here, based on the selected output FPS and the input video duration
if (selectedFPS > 0 && inputVideoDuration > 0)
{
totalFrames = (int)(selectedFPS * inputVideoDuration);
UnityEngine.Debug.Log($"Calculated Total Output Frames (using output video FPS {selectedFPS}): {totalFrames}");
}
else
{
totalFrames = 0;
UnityEngine.Debug.LogError("Failed to calculate total frames due to invalid output FPS or video duration.");
}
// Log start of baking process with output file name
UnityEngine.Debug.Log($"Baking started. Output file: {outputFilePath}");
// Select the FFmpeg arguments template based on the selected codec
string ffmpegArgumentsTemplate = GetFFmpegArgumentsTemplate();
// Prepare the FFmpeg arguments
string ffmpegArguments = "";
if (selectedCodecIndex == 0 || selectedCodecIndex == 1 || selectedCodecIndex == 2) // H.264, H.265
{
ffmpegArguments = string.Format(ffmpegArgumentsTemplate, tmixFilters, selectedFPS, crf);
}
else if (selectedCodecIndex == 3 || selectedCodecIndex == 4) // ProRes
{
ffmpegArguments = string.Format(ffmpegArgumentsTemplate, tmixFilters, selectedFPS);
}
// Use '-hide_banner' to suppress unnecessary output
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
FileName = ffmpegPath,
Arguments = $"-i \"{inputFilePath}\" {ffmpegArguments} \"{outputFilePath}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false, // must be false
Arguments = $"-hide_banner -i \"{inputFilePath}\" {ffmpegArguments} \"{outputFilePath}\"",
RedirectStandardOutput = false, // Do not redirect standard output
RedirectStandardError = true, // Redirect standard error to capture errors and progress
UseShellExecute = false, // Must be false
CreateNoWindow = true
};
ffmpegProcess = new Process
{
StartInfo = processStartInfo
StartInfo = processStartInfo,
EnableRaisingEvents = true
};
ffmpegProcess.OutputDataReceived += (sender, args) => HandleFFmpegOutput(args.Data);
ffmpegProcess.ErrorDataReceived += (sender, args) => HandleFFmpegOutput(args.Data);
ffmpegProcess.ErrorDataReceived += FfmpegProcess_ErrorDataReceived;
ffmpegProcess.Start();
ffmpegProcess.BeginOutputReadLine();
ffmpegProcess.BeginErrorReadLine();
// Wait for the process to exit while updating the progress bar
// Wait for the process to exit
while (!ffmpegProcess.HasExited)
{
yield return null;
@ -338,13 +456,233 @@ namespace NiloToon.NiloToonURP
ffmpegProcess.WaitForExit();
ffmpegProcess = null;
// Close the progress bar when the process exits
EditorUtility.ClearProgressBar();
// Calculate elapsed time
TimeSpan elapsedTime = DateTime.Now - processStartTime;
// Log completion message
string timeSpent = $"Time spent = {elapsedTime.Hours} hours {elapsedTime.Minutes} minutes {elapsedTime.Seconds} seconds, video completed ({Path.GetFileName(outputFilePath)}).";
UnityEngine.Debug.Log(timeSpent);
}
private void FfmpegProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
string data = e.Data;
HandleFFmpegOutput(data);
}
}
private void HandleFFmpegOutput(string data)
{
// Parse FFmpeg output to extract meaningful information
if (IsCriticalFfmpegMessage(data))
{
UnityEngine.Debug.LogError("FFmpeg Error: " + data);
}
else
{
// Parse progress information
// FFmpeg outputs progress in lines like: "frame=123 fps=45 q=28.0 size=1234kB time=00:00:05.12 bitrate=1976.3kbits/s speed=1.23x"
if (data.StartsWith("frame="))
{
// Regular expression to parse the progress line
var progressMatch = Regex.Match(data, @"frame=\s*(\d+)\s.*?time=\s*(\S+)\s.*?speed=\s*([\d\.]+)x");
if (progressMatch.Success)
{
string frameStr = progressMatch.Groups[1].Value;
string timeStr = progressMatch.Groups[2].Value;
string speedStr = progressMatch.Groups[3].Value;
if (int.TryParse(frameStr, out int currentFrame))
{
float percentage = 0f;
if (totalFrames > 0)
{
percentage = (float)currentFrame / totalFrames * 100f;
percentage = Mathf.Clamp(percentage, 0f, 100f);
}
// Only log if at least 2 seconds have passed since last log
if ((DateTime.Now - lastLogTime).TotalSeconds >= 2)
{
TimeSpan elapsedTime = DateTime.Now - processStartTime;
string estimatedTimeRemainingString = "Unknown";
if (percentage > 0)
{
double estimatedTotalSeconds = elapsedTime.TotalSeconds / (percentage / 100);
TimeSpan estimatedRemainingTime = TimeSpan.FromSeconds(estimatedTotalSeconds - elapsedTime.TotalSeconds);
estimatedTimeRemainingString = $"{(int)estimatedRemainingTime.TotalHours}h {estimatedRemainingTime.Minutes}m {estimatedRemainingTime.Seconds}s";
}
string elapsedTimeString = $"{(int)elapsedTime.TotalHours}h {elapsedTime.Minutes}m {elapsedTime.Seconds}s";
string logMessage = $"Frame {currentFrame}/{totalFrames} ({percentage:F1}%), Processed time: {elapsedTimeString}, Estimated time remaining: {estimatedTimeRemainingString}, Speed {speedStr}x";
UnityEngine.Debug.Log(logMessage);
lastLogTime = DateTime.Now;
}
}
// Optionally, handle other outputs or suppress less important messages
}
}
}
}
private bool IsCriticalFfmpegMessage(string message)
{
if (string.IsNullOrEmpty(message))
return false;
// Check if message contains 'Error' or 'Invalid' etc.
string lowerMessage = message.ToLowerInvariant();
return lowerMessage.Contains("error") || lowerMessage.Contains("invalid") || lowerMessage.Contains("failed");
}
private void CancelFFmpegProcess()
{
if (ffmpegProcess != null && !ffmpegProcess.HasExited)
{
ffmpegProcess.Kill();
ffmpegProcess.WaitForExit(); // Ensure the process has completely exited
ffmpegProcess = null;
if (coroutine != null)
{
EditorCoroutine.StopCoroutine(coroutine);
coroutine = null;
}
UnityEngine.Debug.Log("FFmpeg process cancelled.");
// Delete the incomplete output file
if (File.Exists(outputFilePath))
{
try
{
File.Delete(outputFilePath);
UnityEngine.Debug.Log("Incomplete output file deleted.");
}
catch (IOException e)
{
UnityEngine.Debug.LogError($"Failed to delete incomplete output file: {e.Message}");
}
}
}
}
private void ExtractVideoInfo(string videoPath)
{
inputVideoFPS = 0f;
inputVideoDuration = 0.0;
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
FileName = ffmpegPath,
Arguments = $"-hide_banner -i \"{videoPath}\"",
RedirectStandardOutput = false, // FFmpeg outputs video information to standard error stream
RedirectStandardError = true, // Redirect standard error stream
UseShellExecute = false,
CreateNoWindow = true
};
Process process = new Process
{
StartInfo = processStartInfo
};
process.ErrorDataReceived += ProcessOutputHandler;
process.Start();
process.BeginErrorReadLine();
process.WaitForExit();
// After processing, check if Duration was successfully extracted
if (inputVideoDuration <= 0)
{
UnityEngine.Debug.LogError("Failed to extract Duration from the input video.");
}
}
private void ProcessOutputHandler(object sender, DataReceivedEventArgs e)
{
if (string.IsNullOrEmpty(e.Data))
return;
// UnityEngine.Debug.Log("FFmpeg Output: " + e.Data); // Optionally log output
// Extract FPS (input video FPS)
var matchFPS = Regex.Match(e.Data, @"Video:.*?(\d+(\.\d+)?) fps");
if (matchFPS.Success)
{
if (float.TryParse(matchFPS.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out float fps))
{
inputVideoFPS = fps;
UnityEngine.Debug.Log("Input Video FPS: " + inputVideoFPS);
UpdateFPSOptions(inputVideoFPS);
}
}
// Extract duration
var matchDuration = Regex.Match(e.Data, @"Duration: (\d+):(\d+):(\d+(\.\d+)?)");
if (matchDuration.Success)
{
int hours = int.Parse(matchDuration.Groups[1].Value);
int minutes = int.Parse(matchDuration.Groups[2].Value);
double seconds = double.Parse(matchDuration.Groups[3].Value, CultureInfo.InvariantCulture);
inputVideoDuration = hours * 3600 + minutes * 60 + seconds;
UnityEngine.Debug.Log("Input Video Duration: " + inputVideoDuration + " seconds");
}
}
private void UpdateFPSOptions(float inputFPS)
{
fpsOptions.Clear();
float[] supportedOutputFPS = { 23.976f, 24f, 25f, 29.97f, 30f, 48f, 50f, 59.94f, 60f, 72f, 90f, 100f, 120f, 144f, 240f };
foreach (var fps in supportedOutputFPS)
{
if (inputFPS >= fps * 2)
{
fpsOptions.Add(fps);
}
}
// Find index of 60 fps or closest lower value if 60 is not available
int index60 = fpsOptions.FindIndex(fps => fps >= 60);
selectedFPSIndex = index60 >= 0 ? index60 : fpsOptions.Count - 1;
}
private string GetFFmpegArgumentsTemplate()
{
switch (selectedCodecIndex)
{
case 0: // H.264
return ffmpegArgumentsTemplate_H264;
case 1: // H.265 (8-bit)
return ffmpegArgumentsTemplate_H265;
case 2: // H.265 (10-bit)
return ffmpegArgumentsTemplate_H265_10bit;
case 3: // ProRes 422 HQ
return ffmpegArgumentsTemplate_ProRes_422HQ;
case 4: // ProRes 4444 XQ
return ffmpegArgumentsTemplate_ProRes;
default:
return ffmpegArgumentsTemplate_H264;
}
}
private string Generate_tmixFilters(float inputVideoFPS)
{
int numberOfTMixFrames = Mathf.FloorToInt(inputVideoFPS * cameraExposureDuration * motionBlurAmount);
// ensure at least 1 frame when motionBlurAmount is 0
numberOfTMixFrames = Mathf.Max(numberOfTMixFrames, 1);
currenttmixFrameCount = numberOfTMixFrames;
if (currenttmixFrameCount == 0)
@ -352,11 +690,11 @@ namespace NiloToon.NiloToonURP
return "";
}
#if METHOD1
#if METHOD1
string weightString = string.Join(" ", Enumerable.Repeat("1", numberOfTMixFrames));
#endif
#endif
#if METHOD2
#if METHOD2
// [2-way expo falloff]
// Generate exponential falloff weights
@ -376,10 +714,9 @@ namespace NiloToon.NiloToonURP
string weightString = string.Join(" ", weights);
//-------------------------------------------------------------------------------------------------------
#endif
#endif
#if METHOD3
#if METHOD3
//-------------------------------------------------------------------------------------------------------
// [1-way expo falloff]
@ -405,11 +742,11 @@ namespace NiloToon.NiloToonURP
string weightString = string.Join(" ", weights);
//-------------------------------------------------------------------------------------------------------
#endif
#endif
#if METHOD4
#if METHOD4
string weightString = GenerateGaussianWeights(numberOfTMixFrames);
#endif
#endif
return $"tmix=frames={numberOfTMixFrames}:weights='{weightString}',";
}
@ -443,104 +780,19 @@ namespace NiloToon.NiloToonURP
return string.Join(" ", scaledWeights);
}
private void CancelFFmpegProcess()
// New method to open the folder directly
private void OpenFolder(string folderPath)
{
if (ffmpegProcess != null && !ffmpegProcess.HasExited)
{
ffmpegProcess.Kill();
ffmpegProcess.WaitForExit(); // Ensure the process has completely exited
ffmpegProcess = null;
if (coroutine != null)
{
EditorCoroutine.StopCoroutine(coroutine);
coroutine = null;
}
EditorUtility.ClearProgressBar();
UnityEngine.Debug.Log("FFmpeg process cancelled.");
// Delete the incomplete output file
if (File.Exists(outputFilePath))
{
try
{
File.Delete(outputFilePath);
UnityEngine.Debug.Log("Incomplete output file deleted.");
}
catch (IOException e)
{
UnityEngine.Debug.LogError($"Failed to delete incomplete output file: {e.Message}");
}
}
}
}
private void HandleFFmpegOutput(string data)
{
UnityEngine.Debug.Log(data);
}
private void ExtractVideoFPS(string videoPath)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
FileName = ffmpegPath,
Arguments = $"-i \"{videoPath}\" -vcodec copy -f null -",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
Process process = new Process
{
StartInfo = processStartInfo
};
process.OutputDataReceived += ProcessOutputHandler;
process.ErrorDataReceived += ProcessOutputHandler;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
}
private void ProcessOutputHandler(object sender, DataReceivedEventArgs e)
{
if (string.IsNullOrEmpty(e.Data))
return;
UnityEngine.Debug.Log("FFmpeg Output: " + e.Data); // Log the output to debug
// Example of how to parse the FPS from FFmpeg output
// Look for a line containing "fps" and extract the value
var match = Regex.Match(e.Data, @"(\d+(\.\d+)?) fps");
if (match.Success)
{
if (float.TryParse(match.Groups[1].Value, out float fps))
{
inputVideoFPS = fps;
UnityEngine.Debug.Log("Extracted FPS: " + inputVideoFPS); // Log the extracted FPS
UpdateFPSOptions(inputVideoFPS); // Update FPS options based on extracted FPS
}
}
}
private void UpdateFPSOptions(float inputFPS)
{
fpsOptions.Clear();
float[] supportedOutputFPS = { 24, 25, 30, 50, 60 };
foreach (var fps in supportedOutputFPS)
{
if (inputFPS >= fps * 2)
{
fpsOptions.Add(fps);
}
}
selectedFPSIndex = fpsOptions.Count - 1; // Set the highest FPS
#if UNITY_EDITOR_WIN
folderPath = folderPath.Replace('/', '\\'); // Ensure correct path separator on Windows
System.Diagnostics.Process.Start("explorer.exe", folderPath);
#elif UNITY_EDITOR_OSX
System.Diagnostics.Process.Start("open", folderPath);
#elif UNITY_EDITOR_LINUX
System.Diagnostics.Process.Start("xdg-open", folderPath);
#else
Application.OpenURL("file://" + folderPath);
#endif
}
}

View File

@ -85,7 +85,11 @@ namespace NiloToon.NiloToonURP
Mesh mesh = null;
switch (renderer)
{
case MeshRenderer mr: mesh = mr.GetComponent<MeshFilter>().sharedMesh; break;
case MeshRenderer mr:
MeshFilter mf = mr.GetComponent<MeshFilter>();
if(!mf) continue;
mesh = mf.sharedMesh;
break;
case SkinnedMeshRenderer smr: mesh = smr.sharedMesh; ; break;
default:
break; // do nothing if not a supported renderer(e.g. particle system's renderer)
@ -112,7 +116,11 @@ namespace NiloToon.NiloToonURP
Mesh mesh = null;
switch (renderer)
{
case MeshRenderer mr: mesh = mr.GetComponent<MeshFilter>().sharedMesh; break;
case MeshRenderer mr:
MeshFilter mf = mr.GetComponent<MeshFilter>();
if(!mf) continue;
mesh = mf.sharedMesh;
break;
case SkinnedMeshRenderer smr: mesh = smr.sharedMesh; break;
default:
break; // do nothing if not a supported renderer(e.g. particle system's renderer)
@ -381,7 +389,8 @@ namespace NiloToon.NiloToonURP
public class NiloToonEditorAutoSetupCharacter
{
#if UNITY_EDITOR
[MenuItem("Window/NiloToonURP/Convert selected GameObjects to NiloToon", priority = 1)]
[MenuItem("Window/NiloToonURP/[GameObject] Convert Selected GameObjects to NiloToon", priority = 1)]
[MenuItem("Assets/NiloToon/[GameObject] Convert Selected GameObjects to NiloToon", priority = 1100 + 1)]
public static void SetupSelectedCharacterGameObject()
{
var allselectedObjects = Selection.objects;
@ -394,7 +403,8 @@ namespace NiloToon.NiloToonURP
}
}
[MenuItem("Window/NiloToonURP/Convert selected GameObjects to NiloToon", priority = 1, validate = true)]
[MenuItem("Window/NiloToonURP/[GameObject] Convert Selected GameObjects to NiloToon", priority = 1, validate = true)]
[MenuItem("Assets/NiloToon/[GameObject] Convert Selected GameObjects to NiloToon", priority = 1100 + 1, validate = true)]
public static bool ValidateSetupSelectedCharacterGameObject()
{
var allselectedObjects = Selection.objects;

View File

@ -108,6 +108,7 @@ namespace NiloToon.NiloToonURP
return true;
}
// [It seems that we can't strip a local keyword based on local keyword..?]
// In Unity2021.3's build, this section will incorrectly strip some keyword(e.g. _MATCAP_ADD _MATCAP_BLEND _RAMP_LIGHTING_SAMPLE_UVY_TEX _SKIN_MASK_ON _SPECULARHIGHLIGHTS),
// we now disable this section for "Unity 2021.3 or later" temporary until bug is fixed.
/*
@ -242,7 +243,8 @@ namespace NiloToon.NiloToonURP
}
// use targetResultSetting to add keywords that we want to strip to our ignore list
private static List<ShaderKeyword> GetStripKeywordList(Shader shader, NiloToonShaderStrippingSettingSO.Settings targetResultSetting)
private static List<ShaderKeyword> GetStripKeywordList(Shader shader,
NiloToonShaderStrippingSettingSO.Settings targetResultSetting)
{
List<ShaderKeyword> haveToStripList = new List<ShaderKeyword>();
@ -251,10 +253,12 @@ namespace NiloToon.NiloToonURP
{
haveToStripList.Add(new ShaderKeyword("_NILOTOON_DEBUG_SHADING"));
}
if (!targetResultSetting.include_NILOTOON_FORCE_MINIMUM_SHADER)
{
haveToStripList.Add(new ShaderKeyword("_NILOTOON_FORCE_MINIMUM_SHADER"));
}
if (!targetResultSetting.include_NILOTOON_GLOBAL_ENABLE_SCREENSPACE_OUTLINE)
{
haveToStripList.Add(new ShaderKeyword("_NILOTOON_GLOBAL_ENABLE_SCREENSPACE_OUTLINE"));
@ -264,10 +268,12 @@ namespace NiloToon.NiloToonURP
{
haveToStripList.Add(new ShaderKeyword("_NILOTOON_GLOBAL_ENABLE_SCREENSPACE_OUTLINE_V2"));
}
if (!targetResultSetting.include_NILOTOON_RECEIVE_URP_SHADOWMAPPING)
{
haveToStripList.Add(new ShaderKeyword("_NILOTOON_RECEIVE_URP_SHADOWMAPPING"));
}
if (!targetResultSetting.include_NILOTOON_RECEIVE_SELF_SHADOW)
{
haveToStripList.Add(new ShaderKeyword("_NILOTOON_RECEIVE_SELF_SHADOW"));
@ -298,15 +304,58 @@ namespace NiloToon.NiloToonURP
{
haveToStripList.Add(new ShaderKeyword(shader, "_NILOTOON_DITHER_FADEOUT"));
}
if (!targetResultSetting.include_NILOTOON_DISSOLVE)
{
haveToStripList.Add(new ShaderKeyword(shader, "_NILOTOON_DISSOLVE"));
}
if (!targetResultSetting.include_NILOTOON_PERCHARACTER_BASEMAP_OVERRIDE)
{
haveToStripList.Add(new ShaderKeyword(shader, "_NILOTOON_PERCHARACTER_BASEMAP_OVERRIDE"));
}
//----------------------------------------------------------------------------------------------------------------------------------
// [strip Fog]
// https://docs.unity3d.com/6000.2/Documentation/Manual/urp/shader-stripping-fog.html
// We forced dynamic_branch for fog in NiloToonCharacter.shader for Unity6.1 or higher, so no need to strip them now.
//haveToStripList.Add(new ShaderKeyword(shader, "FOG_EXP"));
//haveToStripList.Add(new ShaderKeyword(shader, "FOG_EXP2"));
//haveToStripList.Add(new ShaderKeyword(shader, "FOG_LINEAR"));
// [strip XR]
// By default, Unity adds this set of keywords to all graphics shader programs:
// - STEREO_INSTANCING_ON
// - STEREO_MULTIVIEW_ON
// - STEREO_CUBEMAP_RENDER_ON
// - UNITY_SINGLE_PASS_STEREO
// we by default strip these keywords since most user are not using NiloToon for XR.
// XR user should enable these keyword manually
// https://docs.unity3d.com/6000.2/Documentation/Manual/shader-keywords-default.html
if (!targetResultSetting.include_STEREO_INSTANCING_ON)
{
haveToStripList.Add(new ShaderKeyword(shader, "STEREO_INSTANCING_ON")); // safe to strip
}
if (!targetResultSetting.include_STEREO_MULTIVIEW_ON)
{
haveToStripList.Add(new ShaderKeyword(shader, "STEREO_MULTIVIEW_ON")); // safe to strip
}
if (!targetResultSetting.include_STEREO_CUBEMAP_RENDER_ON)
{
haveToStripList.Add(new ShaderKeyword(shader, "STEREO_CUBEMAP_RENDER_ON")); // safe to strip
}
if (!targetResultSetting.include_UNITY_SINGLE_PASS_STEREO)
{
haveToStripList.Add(new ShaderKeyword(shader, "UNITY_SINGLE_PASS_STEREO")); // safe to strip
}
//haveToStripList.Add(new ShaderKeyword(shader, "_RECEIVE_URP_SHADOW")); // confirm not safe to strip a local material keyword
//haveToStripList.Add(new ShaderKeyword(shader, "_SCREENSPACE_OUTLINE")); // confirm not safe to strip a local material keyword
// [URP multi_compile]
//haveToStripList.Add(new ShaderKeyword(shader, "_MAIN_LIGHT_SHADOWS_CASCADE")); // safe to strip?
//haveToStripList.Add(new ShaderKeyword(shader, "...")); // there are a lot of other keyword
//----------------------------------------------------------------------------------------------------------------------------------
return haveToStripList;
}

View File

@ -1,12 +1,16 @@
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
// using System.Collections.Generic;
// using System.IO;
// using UnityEditor;
// using UnityEditor.Build;
// using UnityEditor.Build.Reporting;
// using UnityEngine;
namespace LWGUI
{
/*
/// <summary>
/// Used to exclude textures referenced by ImageDrawer in Build
/// </summary>
public class ExcludeFromBuild : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
public static List<string> excludeAssetPaths = new List<string>();
@ -106,4 +110,5 @@ namespace LWGUI
OutputLogs();
}
}
*/
}

View File

@ -6,6 +6,9 @@ using UnityEngine;
namespace LWGUI
{
/// <summary>
/// Used to listen for Shader updates and flush the LWGUI caches
/// </summary>
public class ShaderModifyListener : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)

View File

@ -15,7 +15,8 @@ namespace LWGUI.CustomGUISample
[InitializeOnLoadMethod]
private static void RegisterEvent()
{
LWGUI.onDrawCustomFooter += DoCustomFooter;
// Register Event
// LWGUI.onDrawCustomFooter += DoCustomFooter;
}
}
}

View File

@ -15,7 +15,8 @@ namespace LWGUI.CustomGUISample
[InitializeOnLoadMethod]
private static void RegisterEvent()
{
LWGUI.onDrawCustomHeader += DoCustomHeader;
// Register Event
// LWGUI.onDrawCustomHeader += DoCustomHeader;
}
}
}

View File

@ -0,0 +1,48 @@
using System.Linq;
using UnityEngine;
using UnityEditor;
namespace LWGUI
{
public static class CustomMaterialAssetFinder
{
public static Material FindMaterialAssetInRendererByMaterialInstance(Renderer renderer, Material materialInstance)
{
Material materialAsset = null;
// Find the material asset by name
// if (materialAsset == null)
// {
// var name = materialInstance.name.Replace(" (Instance)", "");
// var guids = AssetDatabase.FindAssets("t:Material " + name, new[] { "Assets" }).Select(guid =>
// {
// var assetPath = AssetDatabase.GUIDToAssetPath(guid);
// if (string.IsNullOrEmpty(assetPath) || !assetPath.EndsWith(".mat"))
// return null;
// else
// return guid;
// }).ToArray();
//
// if (guids != null && guids.Length > 0)
// {
// var matPath = AssetDatabase.GUIDToAssetPath(guids[0]);
// Selection.activeObject = AssetDatabase.LoadAssetAtPath<Material>(matPath);
//
// if (guids.Length > 1)
// {
// Debug.LogWarning($"LWGUI: Multiple materials with the same name were found, and the first one was selected: { matPath }");
// }
// }
// }
return materialAsset;
}
[InitializeOnLoadMethod]
private static void RegisterEvent()
{
// Register Event
// Helper.onFindMaterialAssetInRendererByMaterialInstance = FindMaterialAssetInRendererByMaterialInstance;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d476ea1a49c64eb4a29bca60d1e97c01
timeCreated: 1732872239

View File

@ -177,57 +177,22 @@ namespace LWGUI
// Tips: Use properties to fix null reference errors
private static GUIStyle _guiStyles_IconButton;
public static GUIStyle guiStyles_IconButton
{
get
{
if (_guiStyles_IconButton == null)
{
_guiStyles_IconButton = new GUIStyle(EditorStyles.iconButton) { fixedHeight = 0, fixedWidth = 0 };
}
return _guiStyles_IconButton;
}
}
public static GUIStyle guiStyles_IconButton => _guiStyles_IconButton ?? new GUIStyle(EditorStyles.iconButton) { fixedHeight = 0, fixedWidth = 0 };
private static GUIStyle _guiStyle_Foldout;
public static GUIStyle guiStyle_Foldout
public static GUIStyle guiStyle_Foldout => _guiStyle_Foldout ?? new GUIStyle(EditorStyles.miniButton)
{
get
{
if (_guiStyle_Foldout == null)
{
_guiStyle_Foldout =
new GUIStyle(EditorStyles.miniButton)
{
contentOffset = new Vector2(22, 0),
fixedHeight = 27,
alignment = TextAnchor.MiddleLeft,
font = EditorStyles.boldLabel.font,
fontSize = EditorStyles.boldLabel.fontSize + 1,
};
}
return _guiStyle_Foldout;
}
}
contentOffset = new Vector2(22, 0),
fixedHeight = 27,
alignment = TextAnchor.MiddleLeft,
font = EditorStyles.boldLabel.font,
fontSize = EditorStyles.boldLabel.fontSize + 1
};
private static GUIStyle _guiStyle_Helpbox;
public static GUIStyle guiStyle_Helpbox
{
get
{
if (_guiStyle_Helpbox == null)
{
_guiStyle_Helpbox = new GUIStyle(EditorStyles.helpBox) { fontSize = 12 };
}
return _guiStyle_Helpbox;
}
}
public static GUIStyle guiStyle_Helpbox => _guiStyle_Helpbox ?? new GUIStyle(EditorStyles.helpBox) { fontSize = 12 };
private static GUIStyle _guiStyles_ToolbarSearchTextFieldPopup;
public static GUIStyle guiStyles_ToolbarSearchTextFieldPopup
{
get
@ -359,8 +324,10 @@ namespace LWGUI
}
}
private static Texture _logo = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("26b9d845eb7b1a747bf04dc84e5bcc2c"));
private static GUIContent _logoGuiContent = new GUIContent(string.Empty, _logo,
private static Texture _logoCache;
private static GUIContent _logoGuiContentCache;
private static Texture _logo => _logoCache = _logoCache ?? AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("26b9d845eb7b1a747bf04dc84e5bcc2c"));
private static GUIContent _logoGuiContent => _logoGuiContentCache = _logoGuiContentCache ?? new GUIContent(string.Empty, _logo,
"LWGUI (Light Weight Shader GUI)\n\n"
+ "A Lightweight, Flexible, Powerful Unity Shader GUI system.\n\n"
+ "Copyright (c) Jason Ma");
@ -386,23 +353,38 @@ namespace LWGUI
private static Material _copiedMaterial;
private static List<string> _copiedProps = new List<string>();
private static Texture _iconCopy = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("9cdef444d18d2ce4abb6bbc4fed4d109"));
private static Texture _iconPaste = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("8e7a78d02e4c3574998524a0842a8ccb"));
private static Texture _iconSelect = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("6f44e40b24300974eb607293e4224ecc"));
private static Texture _iconCheckout = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("72488141525eaa8499e65e52755cb6d0"));
private static Texture _iconExpand = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("2382450e7f4ddb94c9180d6634c41378"));
private static Texture _iconCollapse = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("929b6e5dfacc42b429d715a3e1ca2b57"));
private static Texture _iconVisibility = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("9576e23a695b35d49a9fc55c9a948b4f"));
private const string _iconCopyGUID = "9cdef444d18d2ce4abb6bbc4fed4d109";
private const string _iconPasteGUID = "8e7a78d02e4c3574998524a0842a8ccb";
private const string _iconSelectGUID = "6f44e40b24300974eb607293e4224ecc";
private const string _iconCheckoutGUID = "72488141525eaa8499e65e52755cb6d0";
private const string _iconExpandGUID = "2382450e7f4ddb94c9180d6634c41378";
private const string _iconCollapseGUID = "929b6e5dfacc42b429d715a3e1ca2b57";
private const string _iconVisibilityGUID = "9576e23a695b35d49a9fc55c9a948b4f";
private static GUIContent _guiContentCopy = new GUIContent("", _iconCopy, "Copy Material Properties");
private static GUIContent _guiContentPaste = new GUIContent("", _iconPaste, "Paste Material Properties\n\nRight-click to paste values by type.");
private static GUIContent _guiContentSelect = new GUIContent("", _iconSelect, "Select the Material Asset\n\nUsed to jump from a Runtime Material Instance to a Material Asset.");
private static GUIContent _guiContentChechout = new GUIContent("", _iconCheckout, "Checkout selected Material Assets");
private static GUIContent _guiContentExpand = new GUIContent("", _iconExpand, "Expand All Groups");
private static GUIContent _guiContentCollapse = new GUIContent("", _iconCollapse, "Collapse All Groups");
private static GUIContent _guiContentVisibility = new GUIContent("", _iconVisibility, "Display Mode");
private const string _iconCopyTooltip = "Copy Material Properties";
private const string _iconPasteTooltip = "Paste Material Properties\n\nRight-click to paste values by type.";
private const string _iconSelectTooltip = "Select the Material Asset\n\nUsed to jump from a Runtime Material Instance to a Material Asset.";
private const string _iconCheckoutTooltip = "Checkout selected Material Assets";
private const string _iconExpandTooltip = "Expand All Groups";
private const string _iconCollapseTooltip = "Collapse All Groups";
private const string _iconVisibilityTooltip = "Display Mode";
private static GUIContent _guiContentCopyCache;
private static GUIContent _guiContentPasteCache;
private static GUIContent _guiContentSelectCache;
private static GUIContent _guiContentChechoutCache;
private static GUIContent _guiContentExpandCache;
private static GUIContent _guiContentCollapseCache;
private static GUIContent _guiContentVisibilityCache;
private static GUIContent _guiContentCopy => _guiContentCopyCache = _guiContentCopyCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconCopyGUID)), _iconCopyTooltip);
private static GUIContent _guiContentPaste => _guiContentPasteCache = _guiContentPasteCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconPasteGUID)), _iconPasteTooltip);
private static GUIContent _guiContentSelect => _guiContentSelectCache = _guiContentSelectCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconSelectGUID)), _iconSelectTooltip);
private static GUIContent _guiContentChechout => _guiContentChechoutCache = _guiContentChechoutCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconCheckoutGUID)), _iconCheckoutTooltip);
private static GUIContent _guiContentExpand => _guiContentExpandCache = _guiContentExpandCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconExpandGUID)), _iconExpandTooltip);
private static GUIContent _guiContentCollapse => _guiContentCollapseCache = _guiContentCollapseCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconCollapseGUID)), _iconCollapseTooltip);
private static GUIContent _guiContentVisibility => _guiContentVisibilityCache = _guiContentVisibilityCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconVisibilityGUID)), _iconVisibilityTooltip);
private static string[] _materialInstanceNameEnd = new[] { "_Instantiated (Instance)", " (Instance)", "_Instantiated" };
private enum CopyMaterialValueMask
{
@ -547,44 +529,9 @@ namespace LWGUI
}
else
{
// Get Material Asset name
var name = material.name;
foreach (var nameEnd in _materialInstanceNameEnd)
if (FindMaterialAssetByMaterialInstance(material, metaDatas, out var materialAsset))
{
if (name.EndsWith(nameEnd))
{
name = name.Substring(0, name.Length - nameEnd.Length);
break;
}
}
// Get path
var guids = AssetDatabase.FindAssets("t:Material " + name);
var paths = guids.Select(((guid, i) =>
{
var filePath = AssetDatabase.GUIDToAssetPath(guid);
var fileName = System.IO.Path.GetFileNameWithoutExtension(filePath);
return (fileName == name && filePath.EndsWith(".mat")) ? filePath : null;
})).Where((s => !string.IsNullOrEmpty(s))).ToArray();
// Select Asset
if (paths.Length == 0)
{
Debug.LogError("LWGUI: Can not find Material Assets with name: " + name);
}
else if (paths.Length > 1)
{
var str = string.Empty;
foreach (string path in paths)
{
str += "\n" + path;
}
Debug.LogWarning("LWGUI: Multiple Material Assets with the same name have been found, select only the first one:" + str);
Selection.activeObject = AssetDatabase.LoadAssetAtPath<Material>(paths[0]);
}
else
{
Selection.activeObject = AssetDatabase.LoadAssetAtPath<Material>(paths[0]);
Selection.activeObject = materialAsset;
}
}
}
@ -631,16 +578,16 @@ namespace LWGUI
if (GUI.Button(buttonRect, _guiContentVisibility, Helper.guiStyles_IconButton))
{
// Build Display Mode Menu Items
string[] displayModeMenus = new[]
var displayModeMenus = new[]
{
"Show All Advanced Properties (" + displayModeData.advancedCount + " of " + perShaderData.propStaticDatas.Count + ")",
"Show All Hidden Properties (" + displayModeData.hiddenCount + " of " + perShaderData.propStaticDatas.Count + ")",
"Show Only Modified Properties (" + perMaterialData.modifiedCount + " of " + perShaderData.propStaticDatas.Count + ")",
"Show Only Modified Properties by Group (" + perMaterialData.modifiedCount + " of " + perShaderData.propStaticDatas.Count + ")",
};
bool[] enabled = new[] { true, true, true, true };
bool[] separator = new bool[4];
int[] selected = new[]
var enabled = new[] { true, true, true, true };
var separator = new bool[4];
var selected = new[]
{
displayModeData.showAllAdvancedProperties ? 0 : -1,
displayModeData.showAllHiddenProperties ? 1 : -1,
@ -682,20 +629,46 @@ namespace LWGUI
toolBarRect.xMin += 2;
}
public static Func<Renderer, Material, Material> onFindMaterialAssetInRendererByMaterialInstance;
private static bool FindMaterialAssetByMaterialInstance(Material material, LWGUIMetaDatas metaDatas, out Material materialAsset)
{
materialAsset = null;
var renderers = metaDatas.perInspectorData.materialEditor.GetMeshRenderersByMaterialEditor();
foreach (var renderer in renderers)
{
if (onFindMaterialAssetInRendererByMaterialInstance != null)
{
materialAsset = onFindMaterialAssetInRendererByMaterialInstance(renderer, material);
}
if (materialAsset == null)
{
int index = renderer.materials.ToList().FindIndex(materialInstance => materialInstance == material);
if (index >= 0 && index < renderer.sharedMaterials.Length)
{
materialAsset = renderer.sharedMaterials[index];
}
}
if (materialAsset != null && AssetDatabase.Contains(materialAsset))
return true;
}
Debug.LogError("LWGUI: Can not find the Material Assets of: " + material.name);
return false;
}
#endregion
#region Search Field
private static readonly int s_TextFieldHash = "EditorTextField".GetHashCode();
private static readonly GUIContent[] _searchModeMenus =
(new GUIContent[(int)SearchMode.Num]).Select(((guiContent, i) =>
{
if (i == (int)SearchMode.Num)
return null;
return new GUIContent(((SearchMode)i).ToString());
})).ToArray();
private static readonly GUIContent[] _searchModeMenus = Enumerable.Range(0, (int)SearchMode.Num - 1).Select(i =>
new GUIContent(((SearchMode)i).ToString())).ToArray();
/// <returns>is has changed?</returns>
public static bool DrawSearchField(Rect rect, LWGUIMetaDatas metaDatas)
@ -926,11 +899,16 @@ namespace LWGUI
menus.AddSeparator("");
foreach (var activePresetData in perMaterialData.activePresetDatas)
{
// Cull self
if (activePresetData.property == prop) continue;
var activePreset = activePresetData.preset;
var presetAsset = perShaderData.propStaticDatas[activePresetData.property.name].propertyPresetAsset;
var presetPropDisplayName = perShaderData.propStaticDatas[activePresetData.property.name].displayName;
var (presetPropStaticData, presetPropDynamicData) = metaDatas.GetPropDatas(activePresetData.property);
var presetAsset = presetPropStaticData.propertyPresetAsset;
var presetPropDisplayName = presetPropStaticData.displayName;
// Cull invisible presets
if (!presetPropDynamicData.isShowing) continue;
if (activePreset.GetPropertyValue(prop.name) != null)
{

View File

@ -16,6 +16,7 @@ namespace LWGUI
#region Get Prop Data
public PropertyStaticData GetPropStaticData(string propName) => perShaderData?.GetPropStaticData(propName);
public PropertyStaticData GetPropStaticData(MaterialProperty prop) => GetPropStaticData(prop.name);
@ -27,15 +28,18 @@ namespace LWGUI
public MaterialProperty GetProperty(string propName) => GetPropDynamicData(propName)?.property;
public MaterialProperty GetDefaultProperty(string propName) => GetPropDynamicData(propName)?.defualtProperty;
#endregion
#region Get Data Tuple
// var (perShaderData, perMaterialData, perInspectorData) =
public (PerShaderData, PerMaterialData, PerInspectorData) GetDatas() => (perShaderData, perMaterialData, perInspectorData);
// var (propStaticData, propDynamicData) =
public (PropertyStaticData, PropertyDynamicData) GetPropDatas(MaterialProperty prop) =>
(GetPropStaticData(prop), GetPropDynamicData(prop));
#endregion
public MaterialProperty[] GetProps() => perMaterialData.props;
@ -45,12 +49,6 @@ namespace LWGUI
public Shader GetShader() => perShaderData.shader;
public MaterialEditor GetMaterialEditor() => perInspectorData.materialEditor;
public void OnValidate()
{
MaterialEditor.ApplyMaterialPropertyDrawers(GetMaterialEditor()?.targets);
MetaDataHelper.ForceUpdateMaterialsMetadataCache(GetMaterialEditor()?.targets);
}
}
public class MetaDataHelper
@ -167,24 +165,6 @@ namespace LWGUI
return str;
}
private static readonly string _tooltipString = "#";
private static readonly string _helpboxString = "%";
public static string GetPropertyDisplayName(Shader shader, MaterialProperty prop)
{
var tooltipIndex = prop.displayName.IndexOf(_tooltipString, StringComparison.Ordinal);
var helpboxIndex = prop.displayName.IndexOf(_helpboxString, StringComparison.Ordinal);
var minIndex = tooltipIndex == -1 ? helpboxIndex : tooltipIndex;
if (tooltipIndex != -1 && helpboxIndex != -1)
minIndex = Mathf.Min(minIndex, helpboxIndex);
if (minIndex == -1)
return prop.displayName;
else if (minIndex == 0)
return string.Empty;
else
return prop.displayName.Substring(0, minIndex);
}
public static bool GetPropertyVisibility(MaterialProperty prop, Material material, LWGUIMetaDatas metaDatas)
{
bool result = true;
@ -218,16 +198,14 @@ namespace LWGUI
public static bool GetParentPropertyVisibility(PropertyStaticData parentPropStaticData, Material material, LWGUIMetaDatas metaDatas)
{
bool result = true;
if (parentPropStaticData != null
&& (!metaDatas.GetPropStaticData(parentPropStaticData.name).isExpanding
|| !MetaDataHelper.GetPropertyVisibility(metaDatas.GetProperty(parentPropStaticData.name), material, metaDatas)))
|| !GetPropertyVisibility(metaDatas.GetProperty(parentPropStaticData.name), material, metaDatas)))
{
result = false;
return false;
}
return result;
return true;
}
}
}

View File

@ -45,6 +45,9 @@ namespace LWGUI
public static ShaderPropertyPreset GetPresetFile(string presetFileName)
{
if (string.IsNullOrEmpty(presetFileName))
return null;
if (!_loadedPresets.ContainsKey(presetFileName) || !_loadedPresets[presetFileName])
ForceInit();
@ -60,11 +63,10 @@ namespace LWGUI
// For Developers: Call this after a material has modified in code
public static void ApplyPresetsInMaterial(Material material)
{
var props = MaterialEditor.GetMaterialProperties(new[] { material });
var props = MaterialEditor.GetMaterialProperties(new UnityEngine.Object[] { material });
foreach (var prop in props)
{
List<MaterialPropertyDrawer> decoratorDrawers;
var drawer = ReflectionHelper.GetPropertyDrawer(material.shader, prop, out decoratorDrawers);
var drawer = ReflectionHelper.GetPropertyDrawer(material.shader, prop, out _);
// Apply active preset
if (drawer != null && drawer is IBasePresetDrawer)
@ -73,9 +75,8 @@ namespace LWGUI
if (activePreset != null)
activePreset.ApplyToDefaultMaterial(material);
}
}
MaterialEditor.ApplyMaterialPropertyDrawers(material);
UnityEditorExtension.ApplyMaterialPropertyAndDecoratorDrawers(material);
}
}
}

View File

@ -2,27 +2,20 @@
using System;
using System.IO;
using System.Linq;
using LWGUI.LwguiGradientEditor;
using LWGUI.Runtime.LwguiGradient;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace LWGUI
{
public class RampHelper
public static class RampHelper
{
#region RampEditor
public static readonly string projectPath = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
public static string lastSavePath
{
get { return EditorPrefs.GetString("LWGUI_GradientSavePath_" + Application.version, Application.dataPath); }
set
{
if (value.Contains(projectPath))
EditorPrefs.SetString("LWGUI_GradientSavePath_" + Application.version, value);
}
}
private static readonly GUIContent _iconAdd = new GUIContent(EditorGUIUtility.IconContent("d_Toolbar Plus").image, "Add"),
_iconEdit = new GUIContent(EditorGUIUtility.IconContent("editicon.sml").image, "Edit"),
@ -30,22 +23,27 @@ namespace LWGUI
_iconSave = new GUIContent(EditorGUIUtility.IconContent("SaveActive").image, "Save");
public static bool RampEditor(
Rect buttonRect,
MaterialProperty prop,
SerializedProperty serializedProperty,
bool isLinear,
bool isDirty,
string defaultFileName,
string rootPath,
int defaultWidth,
int defaultHeight,
out Texture2D newTexture,
out bool doSave,
out bool doDiscard)
Rect buttonRect,
MaterialProperty prop,
ref LwguiGradient gradient,
ColorSpace colorSpace,
LwguiGradient.ChannelMask viewChannelMask,
LwguiGradient.GradientTimeRange timeRange,
bool isDirty,
string defaultFileName,
string rootPath,
int defaultWidth,
int defaultHeight,
out bool doRegisterUndo,
out Texture2D newTexture,
out bool doSave,
out bool doDiscard
)
{
newTexture = null;
var hasChange = false;
var shouldCreate = false;
var doOpenWindow = false;
var singleButtonWidth = buttonRect.width * 0.25f;
var editRect = new Rect(buttonRect.x + singleButtonWidth * 0, buttonRect.y, singleButtonWidth, buttonRect.height);
var saveRect = new Rect(buttonRect.x + singleButtonWidth * 1, buttonRect.y, singleButtonWidth, buttonRect.height);
@ -53,38 +51,30 @@ namespace LWGUI
var discardRect = new Rect(buttonRect.x + singleButtonWidth * 3, buttonRect.y, singleButtonWidth, buttonRect.height);
// Edit button event
var currEvent = Event.current;
if (currEvent.type == EventType.MouseDown && editRect.Contains(currEvent.mousePosition))
{
// if the current edited texture is null, create new one
if (prop.textureValue == null)
{
shouldCreate = true;
currEvent.Use();
}
else
{
// Undo.RecordObject(prop.textureValue, "Edit Gradient");
}
}
// Gradient Editor
if (GUI.enabled)
{
var gradientPropertyRect = new Rect(editRect.x + 2, editRect.y + 2, editRect.width - 2, editRect.height - 2);
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(gradientPropertyRect, serializedProperty, GUIContent.none);
LwguiGradientEditorHelper.GradientEditButton(editRect, _iconEdit, gradient, colorSpace, viewChannelMask, timeRange, () =>
{
// if the current edited texture is null, create new one
if (prop.textureValue == null)
{
shouldCreate = true;
Event.current.Use();
return false;
}
else
{
doOpenWindow = true;
return true;
}
});
if (EditorGUI.EndChangeCheck())
{
hasChange = true;
gradient = LwguiGradientWindow.instance.lwguiGradient;
}
}
// Edit button overlay
if (currEvent.type == EventType.Repaint)
{
var isHover = editRect.Contains(currEvent.mousePosition);
(new GUIStyle("button")).Draw(editRect, _iconEdit, isHover, false, false, false);
doRegisterUndo = doOpenWindow;
}
// Create button
@ -101,7 +91,7 @@ namespace LWGUI
{
//Create texture and save PNG
var saveUnityPath = absPath.Replace(projectPath, String.Empty);
CreateAndSaveNewGradientTexture(defaultWidth, defaultHeight, saveUnityPath, isLinear);
CreateAndSaveNewGradientTexture(defaultWidth, defaultHeight, saveUnityPath, colorSpace == ColorSpace.Linear);
// VersionControlHelper.Add(saveUnityPath);
//Load created texture
newTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(saveUnityPath);
@ -120,10 +110,12 @@ namespace LWGUI
}
// Save button
var color = GUI.color;
if (isDirty) GUI.color = Color.yellow;
doSave = GUI.Button(saveRect, _iconSave);
GUI.color = color;
{
var color = GUI.color;
if (isDirty) GUI.color = Color.yellow;
doSave = GUI.Button(saveRect, _iconSave);
GUI.color = color;
}
// Discard button
doDiscard = GUI.Button(discardRect, _iconDiscard);
@ -133,17 +125,20 @@ namespace LWGUI
public static bool HasGradient(AssetImporter assetImporter) { return assetImporter.userData.Contains("#");}
public static Gradient GetGradientFromTexture(Texture texture, out bool isDirty, bool doReimport = false)
public static LwguiGradient GetGradientFromTexture(Texture texture, out bool isDirty, bool doDiscard = false, bool doRegisterUndo = false)
{
isDirty = false;
if (texture == null) return null;
var assetImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture));
if (doRegisterUndo)
{
LwguiGradientWindow.RegisterRampMapUndo(texture, assetImporter);
}
if (assetImporter != null && HasGradient(assetImporter))
{
GradientObject savedGradientObject, editingGradientObject;
isDirty = DecodeGradientFromJSON(assetImporter.userData, out savedGradientObject, out editingGradientObject);
var outGradient = doReimport ? savedGradientObject.gradient : editingGradientObject.gradient;
isDirty = DecodeGradientFromJSON(assetImporter.userData, out var savedGradient, out var editingGradient);
var outGradient = doDiscard ? savedGradient : editingGradient;
return outGradient;
}
else
@ -156,25 +151,25 @@ namespace LWGUI
}
}
public static void SetGradientToTexture(Texture texture, GradientObject gradientObject, bool doSaveToDisk = false)
public static void SetGradientToTexture(Texture texture, LwguiGradient gradient, bool doSaveToDisk = false)
{
if (texture == null || gradientObject.gradient == null) return;
if (texture == null || gradient == null) return;
var texture2D = (Texture2D)texture;
var path = AssetDatabase.GetAssetPath(texture);
var assetImporter = AssetImporter.GetAtPath(path);
VersionControlHelper.Checkout(texture2D);
Undo.RecordObject(texture2D, "LWGUI: Set Gradient To Texture");
LwguiGradientWindow.RegisterRampMapUndo(texture2D, assetImporter);
// Save to texture
var path = AssetDatabase.GetAssetPath(texture);
var pixels = GetPixelsFromGradient(gradientObject.gradient, texture.width, texture.height);
texture2D.SetPixels32(pixels);
var pixels = gradient.GetPixels(texture.width, texture.height);
texture2D.SetPixels(pixels);
texture2D.Apply();
// Save gradient JSON to userData
var assetImporter = AssetImporter.GetAtPath(path);
GradientObject savedGradientObject, editingGradientObject;
DecodeGradientFromJSON(assetImporter.userData, out savedGradientObject, out editingGradientObject);
assetImporter.userData = EncodeGradientToJSON(doSaveToDisk ? gradientObject : savedGradientObject, gradientObject);
DecodeGradientFromJSON(assetImporter.userData, out var savedGradient, out _);
assetImporter.userData = EncodeGradientToJSON(doSaveToDisk ? gradient : savedGradient, gradient);
// Save texture to disk
if (doSaveToDisk)
@ -186,39 +181,52 @@ namespace LWGUI
}
}
private static string EncodeGradientToJSON(GradientObject savedGradientObject, GradientObject editingGradientObject)
private static string EncodeGradientToJSON(LwguiGradient savedGradient, LwguiGradient editingGradient)
{
string savedJSON = " ", editingJSON = " ";
if (savedGradientObject != null)
savedJSON = EditorJsonUtility.ToJson(savedGradientObject);
if (editingGradientObject != null)
editingJSON = EditorJsonUtility.ToJson(editingGradientObject);
if (savedGradient != null)
savedJSON = EditorJsonUtility.ToJson(savedGradient);
if (editingGradient != null)
editingJSON = EditorJsonUtility.ToJson(editingGradient);
return savedJSON + "#" + editingJSON;
}
private static bool DecodeGradientFromJSON(string json, out GradientObject savedGradientObject, out GradientObject editingGradientObject)
private static bool DecodeGradientFromJSON(string json, out LwguiGradient savedGradient, out LwguiGradient editingGradient)
{
savedGradient = new LwguiGradient();
editingGradient = new LwguiGradient();
var isLegacyJSON = json.Contains("MonoBehaviour");
var subJSONs = json.Split('#');
savedGradientObject = ScriptableObject.CreateInstance<GradientObject>();
if (subJSONs[0] != " ")
EditorJsonUtility.FromJsonOverwrite(subJSONs[0], savedGradientObject);
editingGradientObject = ScriptableObject.CreateInstance<GradientObject>();
if (subJSONs[1] != " ")
EditorJsonUtility.FromJsonOverwrite(subJSONs[1], editingGradientObject);
// Upgrading from deprecated GradientObject to LwguiGradient
if (isLegacyJSON)
{
var savedGradientLegacy = ScriptableObject.CreateInstance<GradientObject>();
var editingGradientLegacy = ScriptableObject.CreateInstance<GradientObject>();
EditorJsonUtility.FromJsonOverwrite(subJSONs[0], savedGradientLegacy);
EditorJsonUtility.FromJsonOverwrite(subJSONs[1], editingGradientLegacy);
savedGradient = LwguiGradient.FromGradient(savedGradientLegacy.gradient);
editingGradient = LwguiGradient.FromGradient(editingGradientLegacy.gradient);
}
else
{
EditorJsonUtility.FromJsonOverwrite(subJSONs[0], savedGradient);
EditorJsonUtility.FromJsonOverwrite(subJSONs[1], editingGradient);
}
return subJSONs[0] != subJSONs[1];
}
public static bool CreateAndSaveNewGradientTexture(int width, int height, string unityPath, bool isLinear)
{
var gradientObject = ScriptableObject.CreateInstance<GradientObject>();
gradientObject.gradient = new Gradient();
gradientObject.gradient.colorKeys = new[] { new GradientColorKey(Color.black, 0.0f), new GradientColorKey(Color.white, 1.0f) };
gradientObject.gradient.alphaKeys = new[] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(1f, 1f) };
var gradient = new LwguiGradient();
var ramp = CreateGradientTexture(gradientObject.gradient, width, height);
var ramp = gradient.GetPreviewRampTexture(width, height, ColorSpace.Linear);
var png = ramp.EncodeToPNG();
Object.DestroyImmediate(ramp);
var systemPath = projectPath + unityPath;
File.WriteAllBytes(systemPath, png);
@ -238,44 +246,18 @@ namespace LWGUI
textureImporter.SetPlatformTextureSettings(platformTextureSettings);
//Gradient data embedded in userData
textureImporter.userData = EncodeGradientToJSON(gradientObject, gradientObject);
textureImporter.userData = EncodeGradientToJSON(gradient, gradient);
textureImporter.SaveAndReimport();
return true;
}
private static Texture2D CreateGradientTexture(Gradient gradient, int width, int height)
{
var ramp = new Texture2D(width, height, TextureFormat.RGBA32, true, true);
var colors = GetPixelsFromGradient(gradient, width, height);
ramp.SetPixels32(colors);
ramp.Apply();
return ramp;
}
private static Color32[] GetPixelsFromGradient(Gradient gradient, int width, int height)
{
var pixels = new Color32[width * height];
for (var x = 0; x < width; x++)
{
var delta = x / (float)width;
if (delta < 0) delta = 0;
if (delta > 1) delta = 1;
var col = gradient.Evaluate(delta);
for (int i = 0; i < height; i++)
{
pixels[x + i * width] = col;
}
}
return pixels;
}
#endregion
#region RampSelector
public delegate void SwitchRampMapEvent(Texture2D selectedRamp);
public static void RampSelector(Rect rect, string rootPath, SwitchRampMapEvent switchRampMapEvent)
public static void RampSelector(Rect rect, string rootPath, Action<Texture2D> switchRampMapEvent)
{
var e = Event.current;
if (e.type == UnityEngine.EventType.MouseDown && rect.Contains(e.mousePosition))
@ -301,11 +283,11 @@ namespace LWGUI
public class RampSelectorWindow : EditorWindow
{
private Texture2D[] _rampMaps;
private Vector2 _scrollPosition;
private RampHelper.SwitchRampMapEvent _switchRampMapEvent;
private Texture2D[] _rampMaps;
private Vector2 _scrollPosition;
private Action<Texture2D> _switchRampMapEvent;
public static void ShowWindow(Rect rect, Texture2D[] rampMaps, RampHelper.SwitchRampMapEvent switchRampMapEvent)
public static void ShowWindow(Rect rect, Texture2D[] rampMaps, Action<Texture2D> switchRampMapEvent)
{
RampSelectorWindow window = ScriptableObject.CreateInstance<RampSelectorWindow>();
window.titleContent = new GUIContent("Ramp Selector");

View File

@ -1,158 +0,0 @@
// Copyright (c) Jason Ma
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace LWGUI
{
public class ReflectionHelper
{
private static Assembly UnityEditor_Assembly = Assembly.GetAssembly(typeof(Editor));
#region MaterialPropertyHandler
private static Type MaterialPropertyHandler_Type = UnityEditor_Assembly.GetType("UnityEditor.MaterialPropertyHandler");
private static MethodInfo MaterialPropertyHandler_GetHandler_Method = MaterialPropertyHandler_Type.GetMethod("GetHandler", BindingFlags.Static | BindingFlags.NonPublic);
private static PropertyInfo MaterialPropertyHandler_PropertyDrawer_Property = MaterialPropertyHandler_Type.GetProperty("propertyDrawer");
private static FieldInfo MaterialPropertyHandler_DecoratorDrawers_Field = MaterialPropertyHandler_Type.GetField("m_DecoratorDrawers", BindingFlags.NonPublic | BindingFlags.Instance);
public static MaterialPropertyDrawer GetPropertyDrawer(Shader shader, MaterialProperty prop, out List<MaterialPropertyDrawer> decoratorDrawers)
{
decoratorDrawers = new List<MaterialPropertyDrawer>();
var handler = MaterialPropertyHandler_GetHandler_Method.Invoke(null, new System.Object[] { shader, prop.name });
if (handler != null && handler.GetType() == MaterialPropertyHandler_Type)
{
decoratorDrawers = MaterialPropertyHandler_DecoratorDrawers_Field.GetValue(handler) as List<MaterialPropertyDrawer>;
return MaterialPropertyHandler_PropertyDrawer_Property.GetValue(handler, null) as MaterialPropertyDrawer;
}
return null;
}
public static MaterialPropertyDrawer GetPropertyDrawer(Shader shader, MaterialProperty prop)
{
List<MaterialPropertyDrawer> decoratorDrawers;
return GetPropertyDrawer(shader, prop, out decoratorDrawers);
}
#endregion
#region MaterialEditor
private static Type MaterialEditor_Type = typeof(MaterialEditor);
private static MethodInfo MaterialEditor_DoPowerRangeProperty_Method = MaterialEditor_Type.GetMethod("DoPowerRangeProperty", BindingFlags.Static | BindingFlags.NonPublic);
private static MethodInfo MaterialEditor_DefaultShaderPropertyInternal_Method = MaterialEditor_Type.GetMethod("DefaultShaderPropertyInternal", BindingFlags.NonPublic | BindingFlags.Instance, null,
new[] { typeof(Rect), typeof(MaterialProperty), typeof(GUIContent) }, null);
public static float DoPowerRangeProperty(Rect position, MaterialProperty prop, GUIContent label, float power)
{
return (float)MaterialEditor_DoPowerRangeProperty_Method.Invoke(null, new System.Object[] { position, prop, label, power });
}
public static void DefaultShaderPropertyInternal(MaterialEditor editor, Rect position, MaterialProperty prop, GUIContent label)
{
MaterialEditor_DefaultShaderPropertyInternal_Method.Invoke(editor, new System.Object[] { position, prop, label });
}
#endregion
#region EditorUtility
private static Type EditorUtility_Type = typeof(EditorUtility);
private static MethodInfo EditorUtility_DisplayCustomMenuWithSeparators_Method = EditorUtility_Type.GetMethod("DisplayCustomMenuWithSeparators", BindingFlags.NonPublic | BindingFlags.Static, null,
new []{typeof(Rect), typeof(string[]), typeof(bool[]), typeof(bool[]), typeof(int[]), typeof(EditorUtility.SelectMenuItemFunction), typeof(object), typeof(bool)}, null);
public static void DisplayCustomMenuWithSeparators(Rect position, string[] options, bool[] enabled, bool[] separator, int[] selected, EditorUtility.SelectMenuItemFunction callback, object userData = null, bool showHotkey = false)
{
EditorUtility_DisplayCustomMenuWithSeparators_Method.Invoke(null, new System.Object[] { position, options, enabled, separator, selected, callback, userData, showHotkey });
}
#endregion
#region EditorGUI
private static Type EditorGUI_Type = typeof(EditorGUI);
private static PropertyInfo EditorGUI_Indent_Property = EditorGUI_Type.GetProperty("indent", BindingFlags.NonPublic | BindingFlags.Static);
public static float EditorGUI_Indent { get { return (float)EditorGUI_Indent_Property.GetValue(null, null); } }
#endregion
#region EditorGUILayout
private static Type EditorGUILayout_Type = typeof(EditorGUILayout);
private static PropertyInfo EditorGUILayout_kLabelFloatMinW_Property = EditorGUILayout_Type.GetProperty("kLabelFloatMinW", BindingFlags.NonPublic | BindingFlags.Static);
public static float EditorGUILayout_kLabelFloatMinW { get { return (float)EditorGUILayout_kLabelFloatMinW_Property.GetValue(null, null); } }
#endregion
#region MaterialEnumDrawer
// UnityEditor.MaterialEnumDrawer(string enumName)
private static System.Type[] _types;
public static System.Type[] GetAllTypes()
{
if (_types == null)
{
_types = ((IEnumerable<Assembly>)AppDomain.CurrentDomain.GetAssemblies())
.SelectMany<Assembly, System.Type>((Func<Assembly, IEnumerable<System.Type>>)
(assembly =>
{
if (assembly == null)
return (IEnumerable<System.Type>)(new System.Type[0]);
try
{
return (IEnumerable<System.Type>)assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
Debug.LogError(ex);
return (IEnumerable<System.Type>)(new System.Type[0]);
}
})).ToArray<System.Type>();
}
return _types;
}
#endregion
#region MaterialProperty.PropertyData
#if UNITY_2022_1_OR_NEWER
private static Type MaterialProperty_Type = typeof(MaterialProperty);
private static Type PropertyData_Type = MaterialProperty_Type.GetNestedType("PropertyData", BindingFlags.NonPublic);
// MergeStack(out bool lockedInChildren, out bool lockedByAncestor, out bool overriden)
private static MethodInfo PropertyData_MergeStack_Method = PropertyData_Type.GetMethod("MergeStack", BindingFlags.Static | BindingFlags.NonPublic);
// HandleApplyRevert(GenericMenu menu, bool singleEditing, UnityEngine.Object[] targets)
private static MethodInfo PropertyData_HandleApplyRevert_Method = PropertyData_Type.GetMethod("HandleApplyRevert", BindingFlags.Static | BindingFlags.NonPublic);
public static void HandleApplyRevert(GenericMenu menu, MaterialProperty prop)
{
System.Object[] parameters = new System.Object[3];
ReflectionHelper.PropertyData_MergeStack_Method.Invoke(null, parameters);
bool lockedInChildren = (bool)parameters[0];
bool lockedByAncestor = (bool)parameters[1];
bool overriden = (bool)parameters[2];
bool singleEditing = prop.targets.Length == 1;
if (overriden)
{
ReflectionHelper.PropertyData_HandleApplyRevert_Method.Invoke(null, new System.Object[]{menu, singleEditing, prop.targets});
menu.AddSeparator("");
}
}
#endif
#endregion
}
}

View File

@ -160,7 +160,8 @@ namespace LWGUI
}
}
private static readonly Texture _icon = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("e7bc1130858d984488bca32b8512ca96"));
private static Texture _iconCache;
private static Texture _icon => _iconCache = _iconCache ?? AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath("e7bc1130858d984488bca32b8512ca96"));
public static bool DrawRevertButton(Rect rect)
{

View File

@ -11,7 +11,7 @@ namespace LWGUI
{
public class VersionControlHelper
{
public static bool isVCEnabled { get { return Provider.enabled && Provider.isActive; } }
public static bool isVCEnabled => Provider.enabled && Provider.isActive;
public static bool Checkout(UnityEngine.Object obj)
{

View File

@ -1,7 +1,10 @@
{
"name": "LWGUI",
"rootNamespace": "",
"references": [],
"references": [
"LWGUI.Runtime",
"Unity.InternalAPIEditorBridge.020"
],
"includePlatforms": [
"Editor"
],

View File

@ -132,10 +132,11 @@ namespace LWGUI
private void DrawAdvancedHeader(PropertyStaticData propStaticData, MaterialProperty prop)
{
EditorGUILayout.Space(3);
var rect = EditorGUILayout.GetControlRect();
var revertButtonRect = RevertableHelper.SplitRevertButtonRect(ref rect);
var label = string.IsNullOrEmpty(propStaticData.advancedHeaderString) ? "Advanced" : propStaticData.advancedHeaderString;
propStaticData.isExpanding = EditorGUI.Foldout(rect, propStaticData.isExpanding, label);
propStaticData.isExpanding = EditorGUI.Foldout(rect, propStaticData.isExpanding, label, EditorStyles.foldoutHeader);
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && rect.Contains(Event.current.mousePosition))
propStaticData.isExpanding = !propStaticData.isExpanding;
RevertableHelper.DrawRevertableProperty(revertButtonRect, prop, metaDatas, true);
@ -147,6 +148,9 @@ namespace LWGUI
var (propStaticData, propDynamicData) = metaDatas.GetPropDatas(prop);
var materialEditor = metaDatas.GetMaterialEditor();
if (propStaticData.isAdvancedHeaderProperty)
EditorGUILayout.Space(3);
Helper.DrawHelpbox(propStaticData, propDynamicData);
var label = new GUIContent(propStaticData.displayName, MetaDataHelper.GetPropertyTooltip(propStaticData, propDynamicData));
@ -181,11 +185,32 @@ namespace LWGUI
MetaDataHelper.ReleaseMaterialMetadataCache(material);
}
// Called after editing the material
public static void OnValidate(Object[] materials)
{
UnityEditorExtension.ApplyMaterialPropertyAndDecoratorDrawers(materials);
MetaDataHelper.ForceUpdateMaterialsMetadataCache(materials);
}
// Called after edit in code
public static void OnValidate(LWGUIMetaDatas metaDatas)
{
OnValidate(metaDatas?.GetMaterialEditor()?.targets);
}
// Called after edit or undo
public override void ValidateMaterial(Material material)
{
base.ValidateMaterial(material);
metaDatas?.OnValidate();
// Undo
if (metaDatas == null)
{
OnValidate(new Object[] { material });
}
// Edit
else
{
OnValidate(metaDatas);
}
}
}
} //namespace LWGUI

View File

@ -62,10 +62,15 @@ namespace LWGUI
// Get active presets
foreach (var prop in props)
{
var activePreset = perShaderData.propStaticDatas[prop.name].presetDrawer
?.GetActivePreset(prop, perShaderData.propStaticDatas[prop.name].propertyPresetAsset);
if (activePreset != null)
var propStaticData = perShaderData.propStaticDatas[prop.name];
var activePreset = propStaticData.presetDrawer?.GetActivePreset(prop, propStaticData.propertyPresetAsset);
if (activePreset != null
// Filter invisible preset properties
&& (propStaticData.showIfDatas.Count == 0
|| ShowIfDecorator.GetShowIfResultFromMaterial(propStaticData.showIfDatas, this.material)))
{
activePresetDatas.Add(new PersetDynamicData(activePreset, prop));
}
}
{
@ -85,16 +90,6 @@ namespace LWGUI
var defaultProperties = MaterialEditor.GetMaterialProperties(new[] { defaultMaterial });
Debug.Assert(defaultProperties.Length == props.Length);
// Override default value
for (int i = 0; i < props.Length; i++)
{
Debug.Assert(props[i].name == defaultProperties[i].name);
Debug.Assert(!propDynamicDatas.ContainsKey(props[i].name));
perShaderData.propStaticDatas[props[i].name].baseDrawers
?.ForEach(baseDrawer => baseDrawer.OverrideDefaultValue(shader, props[i], defaultProperties[i], perShaderData));
}
// Init propDynamicDatas
for (int i = 0; i < props.Length; i++)
{
@ -132,7 +127,7 @@ namespace LWGUI
}
}
// Store Show Modified Props Only Cache
// Store "Show Modified Props Only" Caches
{
if (perShaderData.displayModeData.showOnlyModifiedGroups || perShaderData.displayModeData.showOnlyModifiedProperties)
{

View File

@ -31,7 +31,7 @@ namespace LWGUI
public bool IsDefaultDisplayMode() { return !(showAllAdvancedProperties || showAllHiddenProperties || showOnlyModifiedProperties || showOnlyModifiedGroups); }
}
public class PropertyStaticData
public partial class PropertyStaticData
{
public string name = string.Empty;
public string displayName = string.Empty; // Decoded displayName (Helpbox and Tooltip are encoded in displayName)
@ -105,6 +105,7 @@ namespace LWGUI
propStaticDatas[prop.name] = propStaticData;
// Get Drawers and Build Drawer StaticMetaData
bool hasDecodedStaticMetaData = false;
{
var drawer = ReflectionHelper.GetPropertyDrawer(shader, prop, out var decoratorDrawers);
@ -116,6 +117,7 @@ namespace LWGUI
{
propStaticData.baseDrawers = new List<IBaseDrawer>() { baseDrawer };
baseDrawer.BuildStaticMetaData(shader, prop, props, propStaticData);
hasDecodedStaticMetaData = true;
}
decoratorDrawers?.ForEach(decoratorDrawer =>
@ -133,7 +135,8 @@ namespace LWGUI
});
}
DecodeMetaDataFromDisplayName(prop, propStaticData);
if (!hasDecodedStaticMetaData)
DecodeMetaDataFromDisplayName(prop, propStaticData);
}
// Check Data
@ -245,7 +248,7 @@ namespace LWGUI
private static readonly string _helpboxSplitter = "%";
public void DecodeMetaDataFromDisplayName(MaterialProperty prop, PropertyStaticData propStaticData)
public static void DecodeMetaDataFromDisplayName(MaterialProperty prop, PropertyStaticData propStaticData)
{
var tooltips = prop.displayName.Split(new String[] { _tooltipSplitter }, StringSplitOptions.None);
if (tooltips.Length > 1)

View File

@ -1,13 +1,14 @@
// Copyright (c) Jason Ma
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using Object = UnityEngine.Object;
namespace LWGUI
{
[CreateAssetMenu(fileName = "LWGUI_ShaderPropertyPreset.asset", menuName = "LWGUI/Shader Property Preset")]
[CreateAssetMenu(fileName = "LWGUI_ShaderPropertyPreset.asset", menuName = "LWGUI/Shader Property Preset", order = 84)]
public class ShaderPropertyPreset : ScriptableObject
{
public enum PropertyType
@ -75,7 +76,7 @@ namespace LWGUI
break;
}
MaterialEditor.ApplyMaterialPropertyDrawers(material);
UnityEditorExtension.ApplyMaterialPropertyAndDecoratorDrawers(material);
}
// is Property Primary Material
else if (perMaterialData != null)
@ -145,6 +146,8 @@ namespace LWGUI
public List<PropertyValue> propertyValues = new List<PropertyValue>();
public List<string> enabledKeywords = new List<string>();
public List<string> disabledKeywords = new List<string>();
public List<string> enabledPasses = new List<string>();
public List<string> disabledPasses = new List<string>();
public int renderQueue = -1;
@ -156,11 +159,15 @@ namespace LWGUI
material.EnableKeyword(enabledKeyword);
foreach (var disabledKeyword in disabledKeywords)
material.DisableKeyword(disabledKeyword);
Helper.SetShaderPassEnabled(new Object[] { material }, enabledPasses.Select(s => s.ToUpper()).ToArray(), true);
Helper.SetShaderPassEnabled(new Object[] { material }, disabledPasses.Select(s => s.ToUpper()).ToArray(), false);
if (renderQueue >= 0)
material.renderQueue = renderQueue;
}
public void ApplyToEditingMaterial(UnityEngine.Object[] materials, PerMaterialData perMaterialData)
public void ApplyToEditingMaterial(Object[] materials, PerMaterialData perMaterialData)
{
for (int i = 0; i < materials.Length; i++)
{
@ -171,12 +178,16 @@ namespace LWGUI
material.EnableKeyword(enabledKeyword);
foreach (var disabledKeyword in disabledKeywords)
material.DisableKeyword(disabledKeyword);
if (renderQueue >= 0)
material.renderQueue = renderQueue;
}
Helper.SetShaderPassEnabled(materials, enabledPasses.Select(s => s.ToUpper()).ToArray(), true);
Helper.SetShaderPassEnabled(materials, disabledPasses.Select(s => s.ToUpper()).ToArray(), false);
}
public void ApplyKeywordsToMaterials(UnityEngine.Object[] materials)
public void ApplyKeywordsAndPassesToMaterials(Object[] materials)
{
for (int i = 0; i < materials.Length; i++)
{
@ -186,6 +197,9 @@ namespace LWGUI
foreach (var disabledKeyword in disabledKeywords)
material.DisableKeyword(disabledKeyword);
}
Helper.SetShaderPassEnabled(materials, enabledPasses.Select(s => s.ToUpper()).ToArray(), true);
Helper.SetShaderPassEnabled(materials, disabledPasses.Select(s => s.ToUpper()).ToArray(), false);
}
public PropertyValue GetPropertyValue(string propName)

Binary file not shown.

View File

@ -1,9 +1,9 @@
fileFormatVersion: 2
guid: b3a047b5aeeebeb409a7ed0856959109
guid: 5876caab7dc67af4d8e92317782da76f
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
@ -20,11 +20,12 @@ TextureImporter:
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
@ -34,7 +35,7 @@ TextureImporter:
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 2
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
@ -63,10 +64,12 @@ TextureImporter:
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 8192
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
@ -74,11 +77,12 @@ TextureImporter:
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 8192
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
@ -86,35 +90,12 @@ TextureImporter:
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
@ -122,6 +103,7 @@ TextureImporter:
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
@ -138,9 +120,8 @@ TextureImporter:
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
mipmapLimitGroupName:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: d728a6f19010f8747b8ac956860a5089
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 56332e65f9dc7b44f83296f4b88f0f23
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,123 @@
fileFormatVersion: 2
guid: d09eaf1bb9d214040a6cb0d350439f7f
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,123 @@
fileFormatVersion: 2
guid: 43264f7caf023f740b5cc4dd66fa768b
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,153 @@
fileFormatVersion: 2
guid: 0b6df23f9d012a74abed6b4f422d9442
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 25
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: VisionOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,153 @@
fileFormatVersion: 2
guid: cc772ea50f72a6e44b0f6190010a6424
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 25
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: VisionOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,153 @@
fileFormatVersion: 2
guid: c387c6327985b4f419caee4e22ffbb9b
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 25
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: VisionOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: b307e0433bc4e724180c49d205d9060e
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -1,5 +1,6 @@
fileFormatVersion: 2
guid: d0ddbd505f5f46444b98a9285b7cdb8a
guid: 9c80aad1bb2dd5348bc922988445a5fb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:

View File

@ -0,0 +1,3 @@
{
"name": "LWGUI.Runtime"
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 959b4969da7632b4e9d4dc209cce9cd6
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 756c9f5a40564b56bc263009a656acba
timeCreated: 1720764695

View File

@ -0,0 +1,500 @@
// Copyright (c) Jason Ma
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace LWGUI.Runtime.LwguiGradient
{
[Serializable]
public class LwguiGradient : IDisposable
{
#region Channel Enum
public enum Channel
{
Red = 0,
Green = 1,
Blue = 2,
Alpha = 3,
Num = 4
}
[Flags]
public enum ChannelMask
{
None = 0,
Red = 1 << 0,
Green = 1 << 1,
Blue = 1 << 2,
Alpha = 1 << 3,
RGB = Red | Green | Blue,
All = RGB | Alpha
}
public enum GradientTimeRange
{
One = 1,
TwentyFour = 24,
TwentyFourHundred = 2400
}
public static bool HasChannelMask(ChannelMask channelMaskA, ChannelMask channelMaskB) => ((uint)channelMaskA & (uint)channelMaskB) > 0;
public static bool IsChannelIndexInMask(int channelIndex, ChannelMask channelMask) => ((uint)channelMask & (uint)(1 << channelIndex)) > 0;
public static ChannelMask ChannelIndexToMask(int channelIndex) => (ChannelMask)(1 << channelIndex);
#endregion
#region Const
public static readonly Color[] channelColors = new[] { Color.red, Color.green, Color.blue, Color.white };
public static readonly char[] channelNames = new[] { 'r', 'g', 'b', 'a' };
public static AnimationCurve defaultCurve => new (new Keyframe(0, 1).SetLinearTangentMode(), new Keyframe(1, 1).SetLinearTangentMode());
#endregion
#region Data
// The complete data is stored by RGBA Curves and can be converted into Texture
[SerializeField] private List<AnimationCurve> _curves;
#endregion
#region Constructor
public LwguiGradient()
{
_curves = new List<AnimationCurve>();
for (int c = 0; c < (int)Channel.Num; c++)
_curves.Add(defaultCurve);
}
public LwguiGradient(LwguiGradient src)
{
DeepCopyFrom(src);
}
public LwguiGradient(params Keyframe[] keys)
{
_curves = new List<AnimationCurve>();
for (int c = 0; c < (int)Channel.Num; c++)
_curves.Add(new AnimationCurve());
if (keys?.Length > 0)
{
AddKeys(keys, ChannelMask.All);
}
}
public LwguiGradient(Color[] colors, float[] times)
{
_curves = new List<AnimationCurve>();
for (int c = 0; c < (int)Channel.Num; c++)
_curves.Add(new AnimationCurve());
if (colors == null || times == null)
return;
for (int i = 0; i < Mathf.Min(colors.Length, times.Length); i++)
{
for (int c = 0; c < (int)Channel.Num; c++)
{
_curves[c].AddKey(new Keyframe(times[i], colors[i][c]).SetLinearTangentMode());
}
}
SetLinearTangentMode();
}
public LwguiGradient(List<AnimationCurve> inRgbaCurves) => SetRgbaCurves(inRgbaCurves);
public static LwguiGradient white
{
get => new ();
}
public static LwguiGradient gray
{
get => new (new []{Color.gray, Color.gray}, new []{0.0f, 1.0f});
}
public static LwguiGradient black
{
get => new (new []{Color.black, Color.black}, new []{0.0f, 1.0f});
}
public static LwguiGradient red
{
get => new (new []{Color.red, Color.red}, new []{0.0f, 1.0f});
}
public static LwguiGradient green
{
get => new (new []{Color.green, Color.green}, new []{0.0f, 1.0f});
}
public static LwguiGradient blue
{
get => new (new []{Color.blue, Color.blue}, new []{0.0f, 1.0f});
}
public static LwguiGradient cyan
{
get => new (new []{Color.cyan, Color.cyan}, new []{0.0f, 1.0f});
}
public static LwguiGradient magenta
{
get => new (new []{Color.magenta, Color.magenta}, new []{0.0f, 1.0f});
}
public static LwguiGradient yellow
{
get => new (new []{Color.yellow, Color.yellow}, new []{0.0f, 1.0f});
}
#endregion
public int GetValueBasedHashCode()
{
var hash = 17;
if (_curves != null)
{
foreach (var curve in _curves)
{
if (curve != null)
{
hash = hash * 23 + curve.GetHashCode();
}
}
}
return hash;
}
public void Dispose()
{
_curves?.Clear();
}
public void Clear(ChannelMask channelMask = ChannelMask.All)
{
_curves ??= new List<AnimationCurve>();
for (int c = 0; c < (int)Channel.Num; c++)
{
if (!IsChannelIndexInMask(c, channelMask)) continue;
if (_curves.Count > c) _curves[c].keys = Array.Empty<Keyframe>();
else _curves.Add(new AnimationCurve());
}
}
public void DeepCopyFrom(LwguiGradient src)
{
_curves ??= new List<AnimationCurve>();
for (int c = 0; c < (int)Channel.Num; c++)
{
if (_curves.Count == c)
_curves.Add(new AnimationCurve());
_curves[c].keys = Array.Empty<Keyframe>();
}
for (int c = 0; c < src._curves.Count; c++)
{
foreach (var key in src._curves[c].keys)
{
_curves[c].AddKey(key);
}
}
}
public void SetCurve(AnimationCurve curve, ChannelMask channelMask)
{
curve ??= defaultCurve;
for (int c = 0; c < (int)Channel.Num; c++)
{
if (!IsChannelIndexInMask(c, channelMask)) continue;
_curves[c] = curve;
}
}
public void SetRgbaCurves(List<AnimationCurve> inRgbaCurves)
{
_curves = new List<AnimationCurve>();
for (int c = 0; c < (int)Channel.Num; c++)
{
if (inRgbaCurves?.Count > c && inRgbaCurves[c]?.keys.Length > 0)
{
_curves.Add(inRgbaCurves[c]);
}
else
{
_curves.Add(defaultCurve);
}
}
}
public void AddKey(Keyframe key, ChannelMask channelMask)
{
for (int c = 0; c < (int)Channel.Num; c++)
{
if (!IsChannelIndexInMask(c, channelMask))
continue;
_curves[c].AddKey(key);
}
}
public void AddKeys(Keyframe[] keys, ChannelMask channelMask)
{
for (int i = 0; i < keys?.Length; i++)
{
AddKey(keys[i], channelMask);
}
}
public List<AnimationCurve> rawCurves
{
get => _curves;
set => SetRgbaCurves(value);
}
public AnimationCurve redCurve
{
get => _curves[(int)Channel.Red] ?? defaultCurve;
set => SetCurve(value, ChannelMask.Red);
}
public AnimationCurve greenCurve
{
get => _curves[(int)Channel.Green] ?? defaultCurve;
set => SetCurve(value, ChannelMask.Green);
}
public AnimationCurve blueCurve
{
get => _curves[(int)Channel.Blue] ?? defaultCurve;
set => SetCurve(value, ChannelMask.Blue);
}
public AnimationCurve alphaCurve
{
get => _curves[(int)Channel.Alpha] ?? defaultCurve;
set => SetCurve(value, ChannelMask.Alpha);
}
public Color Evaluate(float time, ChannelMask channelMask = ChannelMask.All, GradientTimeRange timeRange = GradientTimeRange.One)
{
time /= (int)timeRange;
if (channelMask == ChannelMask.Alpha)
{
var alpha = _curves[(int)Channel.Alpha].Evaluate(time);
return new Color(alpha, alpha, alpha, 1);
}
return new Color(
IsChannelIndexInMask((int)Channel.Red, channelMask) ? _curves[(int)Channel.Red].Evaluate(time) : 0,
IsChannelIndexInMask((int)Channel.Green, channelMask) ? _curves[(int)Channel.Green].Evaluate(time) : 0,
IsChannelIndexInMask((int)Channel.Blue, channelMask) ? _curves[(int)Channel.Blue].Evaluate(time) : 0,
IsChannelIndexInMask((int)Channel.Alpha, channelMask) ? _curves[(int)Channel.Alpha].Evaluate(time) : 1);
}
public void SetLinearTangentMode()
{
for (int c = 0; c < (int)Channel.Num; c++)
{
_curves[c].SetLinearTangents();
}
}
#region LwguiGradient <=> Ramp Texture
public Color[] GetPixels(int width, int height, ChannelMask channelMask = ChannelMask.All)
{
var pixels = new Color[width * height];
for (var x = 0; x < width; x++)
{
var u = x / (float)width;
var col = Evaluate(u, channelMask);
for (int i = 0; i < height; i++)
{
pixels[x + i * width] = col;
}
}
return pixels;
}
public Texture2D GetPreviewRampTexture(int width = 256, int height = 1, ColorSpace colorSpace = ColorSpace.Gamma, ChannelMask channelMask = ChannelMask.All)
{
if (LwguiGradientHelper.TryGetRampPreview(this, width, height, colorSpace, channelMask, out var cachedPreview))
return cachedPreview;
var rampPreview = new Texture2D(width, height, TextureFormat.RGBA32, false, colorSpace == ColorSpace.Linear);
var pixels = GetPixels(width, height, channelMask);
rampPreview.SetPixels(pixels);
rampPreview.wrapMode = TextureWrapMode.Clamp;
rampPreview.name = "LWGUI Gradient Preview";
rampPreview.Apply();
LwguiGradientHelper.SetRampPreview(this, width, height, colorSpace, channelMask, rampPreview);
return rampPreview;
}
#endregion
#region LwguiGradient <=> Gradient
public struct LwguiKeyframe
{
public float time;
public float value;
public int index;
public LwguiKeyframe(float time, float value, int index)
{
this.time = time;
this.value = value;
this.index = index;
}
}
public class LwguiMergedColorCurves : IDisposable
{
public List<List<LwguiKeyframe>> curves = new ();
public LwguiMergedColorCurves()
{
for (int c = 0; c < (int)Channel.Num; c++)
curves.Add(new List<LwguiKeyframe>());
}
public LwguiMergedColorCurves(List<AnimationCurve> rgbaCurves)
{
for (int c = 0; c < (int)Channel.Num; c++)
curves.Add(new List<LwguiKeyframe>());
// Get color keys
{
var timeColorDic = new Dictionary<float, List<(float value, int index)>>();
for (int c = 0; c < (int)Channel.Num - 1; c++)
{
var keys = rgbaCurves[c].keys;
for (int j = 0; j < keys.Length; j++)
{
var keyframe = keys[j];
if (timeColorDic.ContainsKey(keyframe.time))
{
timeColorDic[keyframe.time].Add((keyframe.value, j));
}
else
{
timeColorDic.Add(keyframe.time, new List<(float value, int index)> { (keyframe.value, j) });
}
}
}
foreach (var kwPair in timeColorDic)
{
if (kwPair.Value.Count == (int)Channel.Num - 1)
{
for (int c = 0; c < (int)Channel.Num - 1; c++)
{
curves[c].Add(new LwguiKeyframe(kwPair.Key, kwPair.Value[c].value, kwPair.Value[c].index));
}
}
}
}
// Get alpha keys
for (int i = 0; i < rgbaCurves[(int)Channel.Alpha].keys.Length; i++)
{
var alphaKey = rgbaCurves[(int)Channel.Alpha].keys[i];
curves[(int)Channel.Alpha].Add(new LwguiKeyframe(alphaKey.time, alphaKey.value, i));
}
}
public LwguiMergedColorCurves(Gradient gradient)
{
for (int c = 0; c < (int)Channel.Num; c++)
curves.Add(new List<LwguiKeyframe>());
foreach (var colorKey in gradient.colorKeys)
{
for (int c = 0; c < (int)Channel.Num - 1; c++)
{
curves[c].Add(new LwguiKeyframe(colorKey.time, colorKey.color[c], 0));
}
}
foreach (var alphaKey in gradient.alphaKeys)
{
curves[(int)Channel.Alpha].Add(new LwguiKeyframe(alphaKey.time, alphaKey.alpha, 0));
}
}
public Gradient ToGradient(int maxGradientKeyCount = 8) => new Gradient
{
colorKeys = curves[(int)Channel.Red].Select((keyframe, i) => new GradientColorKey(
new Color(
curves[(int)Channel.Red][i].value,
curves[(int)Channel.Green][i].value,
curves[(int)Channel.Blue][i].value),
curves[(int)Channel.Red][i].time))
.Where((key, i) => i < maxGradientKeyCount).ToArray(),
alphaKeys = curves[(int)Channel.Alpha].Select(alphaKey => new GradientAlphaKey(alphaKey.value, alphaKey.time))
.Where((key, i) => i < maxGradientKeyCount).ToArray()
};
public List<AnimationCurve> ToAnimationCurves()
{
var outCurves = new List<AnimationCurve>();
for (int c = 0; c < (int)Channel.Num; c++)
{
var curve = new AnimationCurve();
foreach (var key in curves[c])
{
curve.AddKey(new Keyframe(key.time, key.value).SetLinearTangentMode());
}
curve.SetLinearTangents();
outCurves.Add(curve);
}
return outCurves;
}
public LwguiGradient ToLwguiGradient()
{
return new LwguiGradient(ToAnimationCurves());
}
public void Dispose()
{
curves?.Clear();
}
}
public static LwguiGradient FromGradient(Gradient gradient)
{
return new LwguiMergedColorCurves(gradient).ToLwguiGradient();
}
public Gradient ToGradient(int maxGradientKeyCount = 8)
{
return new LwguiMergedColorCurves(_curves).ToGradient(maxGradientKeyCount);
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a1c1406f2f784af489fb3758fb334d07
timeCreated: 1716793021

View File

@ -0,0 +1,91 @@
// Copyright (c) Jason Ma
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
// Disable Keyframe.tangentMode obsolete warning
#pragma warning disable 0618
namespace LWGUI.Runtime.LwguiGradient
{
public static class LwguiGradientHelper
{
#region Extended Methods
public static Keyframe SetLinearTangentMode(this Keyframe key)
{
key.tangentMode = 69;
return key;
}
public static void SetLinearTangents(this AnimationCurve curve)
{
for (int i = 1; i < curve.keys.Length; i++)
{
var keyStart = curve.keys[i - 1];
var keyEnd = curve.keys[i];
float tangent = (keyEnd.value - keyStart.value) / (keyEnd.time - keyStart.time);
keyStart.outTangent = tangent;
keyEnd.inTangent = tangent;
curve.MoveKey(i - 1, keyStart);
curve.MoveKey(i, keyEnd);
}
}
public static void DestroyInEditorOrRuntime(this UnityEngine.Object obj)
{
#if UNITY_EDITOR
Object.DestroyImmediate(obj);
#else
Object.Destroy(obj);
#endif
}
#endregion
#region Ramp Preview Caches
private static Dictionary<int/* LwguiGradient Value Based Hash */,
Dictionary<(int, int, ColorSpace, LwguiGradient.ChannelMask), Texture2D>> _gradientToPreviewDic;
public static bool TryGetRampPreview(LwguiGradient gradient, int width, int height, ColorSpace colorSpace, LwguiGradient.ChannelMask channelMask,
out Texture2D cachedPreview)
{
cachedPreview = _gradientToPreviewDic?.GetValueOrDefault(gradient.GetValueBasedHashCode())?.GetValueOrDefault((width, height, colorSpace, channelMask));
return cachedPreview;
}
public static void SetRampPreview(LwguiGradient gradient, int width, int height, ColorSpace colorSpace, LwguiGradient.ChannelMask channelMask, Texture2D newPreviewTex)
{
_gradientToPreviewDic ??= new Dictionary<int, Dictionary<(int, int, ColorSpace, LwguiGradient.ChannelMask), Texture2D>>();
var hash = gradient.GetValueBasedHashCode();
if (!_gradientToPreviewDic.ContainsKey(hash))
{
_gradientToPreviewDic[hash] = new Dictionary<(int, int, ColorSpace, LwguiGradient.ChannelMask), Texture2D>();
}
if (_gradientToPreviewDic[hash].ContainsKey((width, height, colorSpace, channelMask)))
{
_gradientToPreviewDic[hash][(width, height, colorSpace, channelMask)].DestroyInEditorOrRuntime();
}
_gradientToPreviewDic[hash][(width, height, colorSpace, channelMask)] = newPreviewTex;
}
public static void ClearRampPreviewCaches()
{
if (_gradientToPreviewDic == null) return;
foreach (var paramsToPreviewTexDic in _gradientToPreviewDic.Values)
{
paramsToPreviewTexDic.Values.ToList().ForEach(previewTex => previewTex.DestroyInEditorOrRuntime());
}
_gradientToPreviewDic.Clear();
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dacb09adca6c4f07b8f5fa5acdc7cbfd
timeCreated: 1720172986

View File

@ -10,7 +10,7 @@ MonoBehaviour:
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 28fbcbca3fb14507af6ed5c104c40b84, type: 3}
m_Name: LWGUI_BlendModePreset
m_Name: LWGUI_Preset_BlendMode
m_EditorClassIdentifier:
presets:
- presetName: Opaque

View File

@ -0,0 +1,29 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 28fbcbca3fb14507af6ed5c104c40b84, type: 3}
m_Name: LWGUI_Preset_Toggle
m_EditorClassIdentifier:
presets:
- presetName: Disable
propertyValues: []
enabledKeywords: []
disabledKeywords: []
enabledPasses: []
disabledPasses: []
renderQueue: 2000
- presetName: Enable
propertyValues: []
enabledKeywords: []
disabledKeywords: []
enabledPasses: []
disabledPasses: []
renderQueue: 2001

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c3b3b9433213824419f41452830ee4ea
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -9,16 +9,20 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: LWGUI_SampleDrawer 1
m_Shader: {fileID: 4800000, guid: 7ee048c9536c0344bb8b4860595a4d9b, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords:
- _GROUP_ON
- _KEY2
- _KEY3
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 3000
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:

View File

@ -20,12 +20,11 @@ TextureImporter:
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 1
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
@ -64,7 +63,6 @@ TextureImporter:
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
@ -77,7 +75,6 @@ TextureImporter:
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
@ -90,7 +87,6 @@ TextureImporter:
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
@ -103,7 +99,6 @@ TextureImporter:
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
@ -120,8 +115,9 @@ TextureImporter:
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
spritePackingTag:
pSDRemoveMatte: 0
userData: '{"MonoBehaviour":{"m_Enabled":true,"m_EditorHideFlags":0,"m_Name":"","m_EditorClassIdentifier":"","gradient":{"serializedVersion":"2","key0":{"r":0.0,"g":0.0,"b":0.0,"a":1.0},"key1":{"r":1.0,"g":1.0,"b":1.0,"a":1.0},"key2":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key3":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key4":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key5":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key6":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key7":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"ctime0":0,"ctime1":65535,"ctime2":0,"ctime3":0,"ctime4":0,"ctime5":0,"ctime6":0,"ctime7":0,"atime0":0,"atime1":65535,"atime2":0,"atime3":0,"atime4":0,"atime5":0,"atime6":0,"atime7":0,"m_Mode":0,"m_ColorSpace":1,"m_NumColorKeys":2,"m_NumAlphaKeys":2}}}#{"MonoBehaviour":{"m_Enabled":true,"m_EditorHideFlags":0,"m_Name":"","m_EditorClassIdentifier":"","gradient":{"serializedVersion":"2","key0":{"r":0.0,"g":0.0,"b":0.0,"a":1.0},"key1":{"r":1.0,"g":1.0,"b":1.0,"a":1.0},"key2":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key3":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key4":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key5":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key6":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"key7":{"r":0.0,"g":0.0,"b":0.0,"a":0.0},"ctime0":0,"ctime1":65535,"ctime2":0,"ctime3":0,"ctime4":0,"ctime5":0,"ctime6":0,"ctime7":0,"atime0":0,"atime1":65535,"atime2":0,"atime3":0,"atime4":0,"atime5":0,"atime6":0,"atime7":0,"m_Mode":0,"m_ColorSpace":1,"m_NumColorKeys":2,"m_NumAlphaKeys":2}}}'
pSDShowRemoveMatteOption: 0
userData: '{"_curves":[{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":1.0,"inSlope":0.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":0.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4}]}#{"_curves":[{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":1.0,"inSlope":0.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":0.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4}]}'
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 1522a7c4f7317044ab912724c1594427
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 1
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: 4
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData: '{"_curves":[{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":1.0,"inSlope":0.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":0.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4}]}#{"_curves":[{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":0.0,"inSlope":0.0,"outSlope":1.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":1.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4},{"serializedVersion":"2","m_Curve":[{"serializedVersion":"3","time":0.0,"value":1.0,"inSlope":0.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0},{"serializedVersion":"3","time":1.0,"value":1.0,"inSlope":0.0,"outSlope":0.0,"tangentMode":69,"weightedMode":0,"inWeight":0.0,"outWeight":0.0}],"m_PreInfinity":2,"m_PostInfinity":2,"m_RotationOrder":4}]}'
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -12,6 +12,7 @@
[Main(Group1, _KEYWORD, on)] _group1 ("Group - Default Open", float) = 1
[Preset(Group1, LWGUI_ShaderPropertyPreset)] _preset ("Preset Sample", float) = 0
[Preset(Group1, LWGUI_ShaderPropertyPreset1)] _preset1 ("Preset Sample 1", float) = 0
[SubToggle(Group1, _, LWGUI_Preset_Toggle)] _preset_toggle ("Preset Toggle Sample", float) = 0
[Sub(Group1)] _float1 ("Sub Float", float) = 0
[Sub(Group1)] _vector1 ("Sub Vector", vector) = (1, 1, 1, 1)
[Sub(Group1)] [HDR] _color1 ("Sub HDR Color", color) = (0.7, 0.7, 1, 1)
@ -36,7 +37,7 @@
[Advanced][Tex(Group2, _AdvancedColor0)] _AdvancedTex1 ("Advanced Tex 1", 2D) = "white" { }
[Advanced][HideInInspector] _AdvancedColor0 ("Advanced Color 0", Color) = (1, 1, 1, 1)
[AdvancedHeaderProperty][Sub(Group2)] _AdvancedFloat ("Advanced Image", float) = 0
[Advanced][Image(Group2)] _AdvancedImage ("Advanced Image", 2D) = "white" { }
[Advanced][Image(Group2)] _AdvancedImage ("../image-20220828003810353.png", float) = 0
[Title(Channel Samples)]
[Channel] _textureChannelMask ("Texture Channel Mask (Default G)", Vector) = (0, 1, 0, 0)
@ -76,6 +77,9 @@
[MinMaxSlider(_rangeStart, _rangeEnd)] _minMaxSlider ("Min Max Slider (0 - 1)", Range(0.0, 1.0)) = 1.0
_rangeStart ("Range Start", Range(0.0, 0.5)) = 0.0
[PowerSlider(10)] _rangeEnd ("Range End PowerSlider", Range(0.5, 1.0)) = 1.0
[Title(Button Samples)]
[Button(_)] _button0 ("URL Button@URL:https://github.com/JasonMa0012/LWGUI@C# Button@C#:LWGUI.ButtonDrawer.TestMethod(1234, abcd)", Float) = 0
}
HLSLINCLUDE

View File

@ -44,7 +44,7 @@
[Main(Preset, _, on, off)] _PresetGroup ("Preset Samples", float) = 0
[Preset(Preset, LWGUI_BlendModePreset)] _BlendMode ("Blend Mode Preset", float) = 0
[Preset(Preset, LWGUI_Preset_BlendMode)] _BlendMode ("Blend Mode Preset", float) = 0
[SubEnum(Preset, UnityEngine.Rendering.CullMode)] _Cull ("Cull", Float) = 2
[SubEnum(Preset, UnityEngine.Rendering.BlendMode)] _SrcBlend ("SrcBlend", Float) = 1
[SubEnum(Preset, UnityEngine.Rendering.BlendMode)] _DstBlend ("DstBlend", Float) = 0

View File

@ -1,5 +1,6 @@
fileFormatVersion: 2
guid: 6ea7dfa994ebd3c42aed7934d8377314
guid: 56e3a65fe6e61904a94a3cd1ea19a8ee
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 76783890193e4d1aaab781515ec60548
timeCreated: 1720764555

View File

@ -0,0 +1,58 @@
// Copyright (c) Jason Ma
using System;
using UnityEngine;
using UnityEditor;
using LWGUI.Runtime.LwguiGradient;
namespace LWGUI.LwguiGradientEditor
{
[System.AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public sealed class LwguiGradientUsageAttribute : PropertyAttribute
{
public ColorSpace colorSpace;
public LwguiGradient.ChannelMask viewChannelMask;
public LwguiGradient.GradientTimeRange timeRange;
public LwguiGradientUsageAttribute(ColorSpace colorSpace = ColorSpace.Gamma, LwguiGradient.ChannelMask viewChannelMask = LwguiGradient.ChannelMask.All, LwguiGradient.GradientTimeRange timeRange = LwguiGradient.GradientTimeRange.One)
{
this.colorSpace = colorSpace;
this.viewChannelMask = viewChannelMask;
this.timeRange = timeRange;
}
}
[CustomPropertyDrawer(typeof(LwguiGradientUsageAttribute))]
internal sealed class LwguiGradientUsageDrawer : LwguiGradientPropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var colorUsage = (LwguiGradientUsageAttribute)attribute;
colorSpace = colorUsage.colorSpace;
viewChannelMask = colorUsage.viewChannelMask;
timeRange = colorUsage.timeRange;
base.OnGUI(position, property, label);
}
}
[CustomPropertyDrawer(typeof(LwguiGradient))]
public class LwguiGradientPropertyDrawer : PropertyDrawer
{
public ColorSpace colorSpace = ColorSpace.Gamma;
public LwguiGradient.ChannelMask viewChannelMask = LwguiGradient.ChannelMask.All;
public LwguiGradient.GradientTimeRange timeRange = LwguiGradient.GradientTimeRange.One;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginChangeCheck();
var gradient = (LwguiGradient)fieldInfo.GetValue(property.serializedObject.targetObject);
LwguiGradientEditorHelper.GradientField(position, label, property, gradient, colorSpace, viewChannelMask, timeRange);
if (EditorGUI.EndChangeCheck())
{
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9672b824fb314326a5fd4078a7068d89
timeCreated: 1716793950

View File

@ -0,0 +1,851 @@
// Copyright (c) Jason Ma
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using LWGUI.Runtime.LwguiGradient;
namespace LWGUI.LwguiGradientEditor
{
public class LwguiGradientEditor
{
private class CurveSelectionInfo
{
public List<AnimationCurve> selectedAnimationCurves;
public Vector4 selectedVectorValue = Vector4.negativeInfinity;
public float selectedFloatValue = float.NegativeInfinity;
public float selectedTime = float.NegativeInfinity;
public bool isOnlyColorKeySelected;
public LwguiGradient.LwguiMergedColorCurves mergedCurves;
public bool hasMixedTime;
public bool hasMixedFloatValue; // Selected multiple keys with different values in a same Curve
public bool hasMixedColorValue; // Selected multiple color keys with different values in the RGB Curves
public List<bool> hasMixedChannelValue = Enumerable.Repeat(false, (int)LwguiGradient.Channel.Num).ToList();
public CurveSelectionInfo(CurveEditor curveEditor)
{
selectedAnimationCurves = new List<AnimationCurve>();
for (int c = 0; c < (int)LwguiGradient.Channel.Num; c++)
selectedAnimationCurves.Add(new AnimationCurve());
if (curveEditor?.selectedCurves is { Count: > 0 })
{
foreach (var curveSelection in curveEditor.selectedCurves.Where(selection => selection != null))
{
var channelID = curveSelection.curveID;
var key = curveEditor.GetKeyframeFromSelection(curveSelection);
selectedAnimationCurves[channelID].AddKey(key);
if (selectedTime != float.NegativeInfinity && selectedTime != key.time)
hasMixedTime = true;
selectedTime = key.time;
if (selectedVectorValue[channelID] != Vector4.negativeInfinity[channelID] && selectedVectorValue[channelID] != key.value)
hasMixedChannelValue[channelID] = true;
selectedVectorValue[channelID] = key.value;
if (selectedFloatValue != float.NegativeInfinity && selectedFloatValue != key.value)
hasMixedFloatValue = true;
selectedFloatValue = key.value;
}
for (int i = 0; i < 4; i++)
{
if (selectedVectorValue[i] == Vector4.negativeInfinity[i])
selectedVectorValue[i] = 0;
}
if (selectedFloatValue == float.NegativeInfinity)
selectedFloatValue = 0;
mergedCurves = new LwguiGradient.LwguiMergedColorCurves(selectedAnimationCurves);
var noAlphaKeySelected = mergedCurves.curves[(int)LwguiGradient.Channel.Alpha].Count == 0;
hasMixedColorValue = noAlphaKeySelected && hasMixedChannelValue. Where((_, c) => c < (int)LwguiGradient.Channel.Alpha).Any(b => b);
isOnlyColorKeySelected = noAlphaKeySelected && selectedAnimationCurves.Where((_, c) => c < (int)LwguiGradient.Channel.Alpha).All(curve => curve.length == mergedCurves.curves[(int)LwguiGradient.Channel.Red].Count);
}
else
{
mergedCurves = new LwguiGradient.LwguiMergedColorCurves();
}
}
}
#region UI Layout
private bool _useSignalLineEditField => _position.width > 1150;
private float _gradientEditorHeisht => _useSignalLineEditField ? 160 : 160 + _editFieldHeight;
private float _margin => 8;
private float _editFieldHeight => EditorGUIUtility.singleLineHeight; // 18
private float _editFieldMarginsHeight => _margin * (_useSignalLineEditField ? 2 : 3);
private float _gradientAndSwatchHeight => _gradientEditorHeisht - _margin - _editFieldMarginsHeight - _editFieldHeight - _secondEditFieldRect.height;
private float _swatchHeisht => _gradientAndSwatchHeight * 0.15f;
private float _alphaGradientHeisht => _rgbGradientHeisht * 0.5f;
private float _rgbGradientHeisht => (_gradientAndSwatchHeight - _swatchHeisht * 2) * 0.66666f;
private Rect _gradientEditorRect => new Rect(0, 0, _position.width, _gradientEditorHeisht);
private Rect _editFieldRect => new Rect(_curveEditor.leftmargin, _margin, _position.width - _curveEditor.leftmargin - _curveEditor.rightmargin, _editFieldHeight);
private Rect _secondEditFieldRect => _useSignalLineEditField ? Rect.zero : new Rect(_curveEditor.leftmargin, _margin * 2 + _editFieldHeight, _position.width - _curveEditor.leftmargin - _curveEditor.rightmargin, _editFieldHeight);
private Rect _alphaSwatchesRect => new Rect(_curveEditor.leftmargin, _editFieldMarginsHeight + _editFieldHeight + _secondEditFieldRect.height, _position.width - _curveEditor.leftmargin - _curveEditor.rightmargin, _swatchHeisht);
private Rect _rgbSwatchesRect => new Rect(_curveEditor.leftmargin, _editFieldMarginsHeight + _editFieldHeight + _secondEditFieldRect.height + _swatchHeisht + _alphaGradientHeisht + _rgbGradientHeisht, _position.width - _curveEditor.leftmargin - _curveEditor.rightmargin, _swatchHeisht);
private Rect _alphaGradientRect => new Rect(_curveEditor.leftmargin, _editFieldMarginsHeight + _editFieldHeight + _secondEditFieldRect.height + _swatchHeisht, _position.width - _curveEditor.leftmargin - _curveEditor.rightmargin, _alphaGradientHeisht);
private Rect _rgbGradientRect => new Rect(_curveEditor.leftmargin, _editFieldMarginsHeight + _editFieldHeight + _secondEditFieldRect.height + _swatchHeisht + _alphaGradientHeisht, _position.width - _curveEditor.leftmargin - _curveEditor.rightmargin, _rgbGradientHeisht);
private Rect _curveEditorRect => new Rect(0, _gradientEditorHeisht, _position.width, _position.height - _gradientEditorHeisht);
private static readonly GUIContent[] s_XYZWLabels = {EditorGUIUtility.TextContent("R"), EditorGUIUtility.TextContent("G"), EditorGUIUtility.TextContent("B"), EditorGUIUtility.TextContent("A")};
private static GUIStyle _style_PopupCurveEditorBackground;
public static GUIStyle style_PopupCurveEditorBackground => _style_PopupCurveEditorBackground ??= "PopupCurveEditorBackground";
#endregion
#region Math
private const float TOLERANCE = 0.00001f;
private static bool Equal(float a, float b) => Math.Abs(a - b) < TOLERANCE;
private static bool Equal(GradientEditor.Swatch a, GradientEditor.Swatch b) =>
a == b
|| (a != null && b != null && a.m_Time == b.m_Time && a.m_Value == b.m_Value && a.m_IsAlpha == b.m_IsAlpha);
#endregion
#region Fields
#region Inputs
private Rect _position;
internal LwguiGradient lwguiGradient;
internal ColorSpace colorSpace;
internal LwguiGradient.ChannelMask viewChannelMask;
internal LwguiGradient.GradientTimeRange gradientTimeRange;
private Action<LwguiGradient> _onChange;
#endregion
private GradientEditor _gradientEditor;
private CurveEditor _curveEditor;
private bool _viewSettingschanged;
private bool _curveEditorContextMenuChanged;
private bool _changed;
private bool _lastChanged;
#region Gradient Editor
private static readonly string[] timeRangeMenuNames = new string[] { "0-1", "0-24", "0-2400" };
private static readonly List<LwguiGradient.GradientTimeRange> timeRangeMenuValues = new () { LwguiGradient.GradientTimeRange.One, LwguiGradient.GradientTimeRange.TwentyFour, LwguiGradient.GradientTimeRange.TwentyFourHundred };
private GradientEditor.Swatch _selectedGradientKey
{
get => _gradientEditor.GetSelectedSwatch();
set => _gradientEditor.SetSelectedSwatch(value);
}
private GradientEditor.Swatch _lastSelectedGradientKey;
private GradientEditor.Swatch _deletedGradientKey;
private List<GradientEditor.Swatch> _gradientRGBSwatches => _gradientEditor.GetRGBdSwatches();
private List<GradientEditor.Swatch> _gradientAlphaSwatches => _gradientEditor.GetAlphaSwatches();
private int _lastGradientRGBSwatchesCount;
private int _lastGradientAlphaSwatchesCount;
private float _lastEditingTime = float.NegativeInfinity;
private static bool _isAddGradientKeyFailure;
#endregion
#region Curve Editor
private List<CurveSelection> _selectedCurves
{
get => _curveEditor.selectedCurves;
set => _curveEditor.selectedCurves = value;
}
private List<Keyframe> _deletedCurveKeys;
private bool _shouldSyncSelectionFromCurveToGradient;
#endregion
#endregion
#region Gradient Editor
private void InitGradientEditor(bool force = false)
{
if (_gradientEditor != null && !force) return;
var lastSelectedGradientKey = _gradientEditor != null && _selectedGradientKey != null ? new GradientEditor.Swatch(_selectedGradientKey.m_Time, _selectedGradientKey.m_Value, _selectedGradientKey.m_IsAlpha) : null;
var lwguiMergedCurves = new LwguiGradient.LwguiMergedColorCurves(lwguiGradient.rawCurves);
_gradientEditor ??= new GradientEditor();
_gradientEditor.Init(lwguiMergedCurves.ToGradient(ReflectionHelper.maxGradientKeyCount), 1024, colorSpace == ColorSpace.Linear, colorSpace);
// When Curve has only one key, Gradient Editor will automatically add a key
{
void FixAutoAddedGradientKey(List<GradientEditor.Swatch> swatches, LwguiGradient.Channel channel)
{
if (swatches.Count == 2 && lwguiMergedCurves.curves[(int)channel].Count == 1)
{
swatches.Clear();
var curveKey = lwguiMergedCurves.curves[(int)channel][0];
swatches.Add(channel == LwguiGradient.Channel.Alpha
? new GradientEditor.Swatch(curveKey.time, new Color(curveKey.value, curveKey.value, curveKey.value, curveKey.value), true)
: new GradientEditor.Swatch(curveKey.time, new Color(
lwguiMergedCurves.curves[(int)LwguiGradient.Channel.Red][0].value,
lwguiMergedCurves.curves[(int)LwguiGradient.Channel.Green][0].value,
lwguiMergedCurves.curves[(int)LwguiGradient.Channel.Blue][0].value,
1), false));
}
}
FixAutoAddedGradientKey(_gradientRGBSwatches, LwguiGradient.Channel.Red);
FixAutoAddedGradientKey(_gradientAlphaSwatches, LwguiGradient.Channel.Alpha);
}
// Keep selected key
if (lastSelectedGradientKey != null)
{
_selectedGradientKey = (lastSelectedGradientKey.m_IsAlpha ? _gradientAlphaSwatches : _gradientRGBSwatches)
.Find(swatch => Equal(swatch.m_Time, lastSelectedGradientKey.m_Time));
}
else
{
_selectedGradientKey = null;
}
}
private void OnGradientEditorGUI()
{
OnGradientEditFieldGUI();
EditorGUI.DrawPreviewTexture(_alphaGradientRect, lwguiGradient.GetPreviewRampTexture(1024, 1, colorSpace, LwguiGradient.ChannelMask.Alpha & viewChannelMask));
EditorGUI.DrawPreviewTexture(_rgbGradientRect, lwguiGradient.GetPreviewRampTexture(1024, 1, colorSpace, LwguiGradient.ChannelMask.RGB & viewChannelMask));
// Swatch Array
{
PrepareSyncSelectionFromGradientToCurve();
EditorGUI.BeginChangeCheck();
ShowGradientSwatchArray(_alphaSwatchesRect, _gradientAlphaSwatches, LwguiGradient.ChannelMask.Alpha);
ShowGradientSwatchArray(_rgbSwatchesRect, _gradientRGBSwatches, LwguiGradient.ChannelMask.RGB);
if (EditorGUI.EndChangeCheck())
{
_changed = true;
ApplyGradientChangesToCurve();
}
SyncSelectionFromGradientToCurveWithoutChanges();
}
}
private void OnGradientEditFieldGUI()
{
// Prevent interrupting mouse drag event
if (Event.current.type is EventType.MouseDrag or EventType.MouseDown or EventType.MouseMove or EventType.MouseUp
&& (!_editFieldRect.Contains(Event.current.mousePosition)
&& !_secondEditFieldRect.Contains(Event.current.mousePosition)))
return;
float space = 20;
float vectorValueWidth = 270;
float locationWidth = 70;
float locationTextWidth = 35;
float alphaOrColorTextWidth = 40;
float colorSpaceTextWidth = 87;
float colorSpaceToggleWidth = 16;
float channelsTextWidth = 60;
float channelsMenuWidth = 85;
float timeRangeTextWidth = 75;
float timeRangeMenuWidth = 70;
Rect rect = _editFieldRect;
// Color Space
{
rect.x = rect.xMax - colorSpaceToggleWidth - colorSpaceTextWidth;
rect.width = colorSpaceTextWidth + colorSpaceToggleWidth;
var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = colorSpaceTextWidth;
EditorGUI.BeginChangeCheck();
colorSpace = EditorGUI.Toggle(rect, "sRGB Preview", colorSpace == ColorSpace.Gamma) ? ColorSpace.Gamma : ColorSpace.Linear;
if (EditorGUI.EndChangeCheck())
{
_viewSettingschanged = true;
InitGradientEditor(true);
}
EditorGUIUtility.labelWidth = labelWidth;
}
// View Channel Mask
{
rect.x -= space + channelsMenuWidth + channelsTextWidth;
rect.width = channelsMenuWidth + channelsTextWidth;
var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = channelsTextWidth;
EditorGUI.BeginChangeCheck();
viewChannelMask = (LwguiGradient.ChannelMask)EditorGUI.EnumFlagsField(rect, "Channels", viewChannelMask);
if (EditorGUI.EndChangeCheck())
{
_viewSettingschanged = true;
InitGradientEditor(true);
InitCurveEditor(true);
}
EditorGUIUtility.labelWidth = labelWidth;
}
// Gradient Time Range
{
rect.x -= space + timeRangeMenuWidth + timeRangeTextWidth;
rect.width = timeRangeMenuWidth + timeRangeTextWidth;
var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = timeRangeTextWidth;
EditorGUI.BeginChangeCheck();
gradientTimeRange = timeRangeMenuValues[EditorGUI.Popup(rect, "Time Range", timeRangeMenuValues.IndexOf(gradientTimeRange), timeRangeMenuNames)];
if (EditorGUI.EndChangeCheck())
{
_viewSettingschanged = true;
InitGradientEditor(true);
InitCurveEditor(true);
}
EditorGUIUtility.labelWidth = labelWidth;
}
// Key edit field (GradientEditor.OnGUI())
if (_selectedCurves is { Count: > 0 })
{
var labelWidth = EditorGUIUtility.labelWidth;
var showMixedValue = EditorGUI.showMixedValue;
var selectionInfo = new CurveSelectionInfo(_curveEditor);
// Time
{
if (_useSignalLineEditField)
{
rect.x -= space + locationTextWidth + locationWidth;
}
else
{
rect = _secondEditFieldRect;
rect.x = rect.xMax - (locationTextWidth + locationWidth);
}
rect.width = locationTextWidth + locationWidth;
EditorGUIUtility.labelWidth = locationTextWidth;
EditorGUI.showMixedValue = selectionInfo.hasMixedTime;
EditorGUI.BeginChangeCheck();
var newTime = EditorGUI.FloatField(rect, "Time", selectionInfo.selectedTime * (int)gradientTimeRange) / (int)gradientTimeRange;
// When two keys have the same time, they will be merged, so avoid modifying the time in real time and only apply the changes at the end of the change
var hasChange = EditorGUI.EndChangeCheck();
if (hasChange) _lastEditingTime = newTime;
if (_lastEditingTime != selectionInfo.selectedTime
&& _lastEditingTime != float.NegativeInfinity
// End editing text
&& (EditorGUI.IsEditingTextField() && Event.current.keyCode is KeyCode.Return or KeyCode.KeypadEnter
// Mouse drag
|| !EditorGUI.IsEditingTextField() && hasChange))
{
_changed = true;
_curveEditor.SetSelectedKeyPositions(Mathf.Clamp01(_lastEditingTime), 0, true, false);
InitGradientEditor(true);
SyncSelectionFromCurveToGradient(true);
}
}
// Vector Value
{
rect.x -= space + vectorValueWidth;
rect.width = vectorValueWidth;
// EditorGUI.VectorField()
{
int channelCount = (int)LwguiGradient.Channel.Num;
float w = (rect.width - (channelCount - 1) * EditorGUI.kSpacingSubLabel) / channelCount;
Rect nr = new Rect(rect) {width = w};
int l = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
for (int c = 0; c < channelCount; c++)
{
EditorGUIUtility.labelWidth = EditorGUI.GetLabelWidth(s_XYZWLabels[c], 0);
EditorGUI.showMixedValue = selectionInfo.hasMixedChannelValue[c];
EditorGUI.BeginChangeCheck();
var newValue = EditorGUI.FloatField(nr, s_XYZWLabels[c], selectionInfo.selectedVectorValue[c]);
if (EditorGUI.EndChangeCheck())
{
_changed = true;
// Apply a curve's modification
foreach (var selection in _selectedCurves.Where(selection => selection.curveID == c))
{
var cw = _curveEditor.animationCurves[selection.curveID];
var key = cw.curve.keys[selection.key];
key.value = newValue;
cw.MoveKey(selection.key, ref key);
cw.changed = true;
AnimationUtility.UpdateTangentsFromMode(cw.curve);
}
_curveEditor.InvalidateSelectionBounds();
InitGradientEditor(true);
SyncSelectionFromCurveToGradient(true);
}
nr.x += w + EditorGUI.kSpacingSubLabel;
}
EditorGUI.indentLevel = l;
}
}
// Alpha or Color field
{
rect = new Rect(_editFieldRect.x, rect.y, rect.x - _editFieldRect.x - space, rect.height);
EditorGUIUtility.labelWidth = alphaOrColorTextWidth;
EditorGUI.BeginChangeCheck();
if (selectionInfo.isOnlyColorKeySelected)
{
EditorGUI.showMixedValue = selectionInfo.hasMixedColorValue;
var newColorValue = new Vector4(selectionInfo.selectedVectorValue.x, selectionInfo.selectedVectorValue.y, selectionInfo.selectedVectorValue.z, 1);
newColorValue = EditorGUI.ColorField(rect, new GUIContent("Value"), newColorValue, true, false, colorSpace == ColorSpace.Linear);
newColorValue.w = selectionInfo.selectedVectorValue.w;
if (EditorGUI.EndChangeCheck())
{
_changed = true;
// Use EventCommandNames.ColorPickerChanged event to avoid ShowSwatchArray() Errors
Event.current.Use();
// Apply RGB curve's modification
foreach (var selection in _selectedCurves.Where(selection => selection.curveID < (int)LwguiGradient.Channel.Alpha))
{
var channelID = selection.curveID;
var cw = _curveEditor.animationCurves[channelID];
var key = cw.curve.keys[selection.key];
key.value = newColorValue[channelID];
cw.MoveKey(selection.key, ref key);
cw.changed = true;
AnimationUtility.UpdateTangentsFromMode(cw.curve);
}
_curveEditor.InvalidateSelectionBounds();
InitGradientEditor(true);
}
}
else
{
EditorGUI.showMixedValue = selectionInfo.hasMixedFloatValue;
var newValue = EditorGUI.IntSlider(rect, "Value", (int)(selectionInfo.selectedFloatValue * 255), 0, 255) / 255f;
if (EditorGUI.EndChangeCheck())
{
_changed = true;
_curveEditor.SetSelectedKeyPositions(0, newValue, false, true);
InitGradientEditor(true);
}
}
}
EditorGUIUtility.labelWidth = labelWidth;
EditorGUI.showMixedValue = showMixedValue;
}
}
private void ShowGradientSwatchArray(Rect rect, List<GradientEditor.Swatch> swatches, LwguiGradient.ChannelMask drawingChannelMask)
{
// GradientEditor.ShowSwatchArray()
ReflectionHelper.GradientEditor_SetStyles();
_isAddGradientKeyFailure = false;
_gradientEditor.ShowSwatchArray(rect, (viewChannelMask & drawingChannelMask) != drawingChannelMask ? new List<GradientEditor.Swatch>() : swatches, drawingChannelMask == LwguiGradient.ChannelMask.Alpha);
// Since the maximum number of Gradient Keys is hard-coded in the engine, keys that exceed the limit can only be displayed and edited in the Curve Editor
if (_isAddGradientKeyFailure)
{
_changed = true;
_curveEditor.SelectNone();
float mouseSwatchTime = _gradientEditor.GetTime((Event.current.mousePosition.x - rect.x) / rect.width);
for (int c = 0; c < (int)LwguiGradient.Channel.Num; c++)
{
if (!LwguiGradient.IsChannelIndexInMask(c, drawingChannelMask))
continue;
var curveSelection = _curveEditor.AddKeyAtTime(_curveEditor.animationCurves[c], mouseSwatchTime);
_curveEditor.AddSelection(curveSelection);
}
_curveEditor.InvalidateSelectionBounds();
_selectedGradientKey = null;
}
}
private void ApplyGradientChangesToCurve()
{
var selectedKeyEqual = _selectedGradientKey == _lastSelectedGradientKey || Equal(_selectedGradientKey, _lastSelectedGradientKey);
var rgbKeyCountEqual = _gradientRGBSwatches.Count == _lastGradientRGBSwatchesCount;
var alphaKeyCountEqual = _gradientAlphaSwatches.Count == _lastGradientAlphaSwatchesCount;
var addRGBKey = _gradientRGBSwatches.Count > _lastGradientRGBSwatchesCount;
var addAlphaKey = _gradientAlphaSwatches.Count > _lastGradientAlphaSwatchesCount;
var delRGBKey = _gradientRGBSwatches.Count < _lastGradientRGBSwatchesCount;
var delAlphaKey = _gradientAlphaSwatches.Count < _lastGradientAlphaSwatchesCount;
if (selectedKeyEqual && rgbKeyCountEqual && alphaKeyCountEqual)
{
return;
}
// Change time or value
if ((!selectedKeyEqual && rgbKeyCountEqual && alphaKeyCountEqual)
// Del a key
|| (delRGBKey || delAlphaKey))
{
foreach (var curveSelection in _selectedCurves.Where(selection => LwguiGradient.IsChannelIndexInMask(selection.curveID, viewChannelMask)))
{
var cw = _curveEditor.animationCurves[curveSelection.curveID];
var selectedKey = cw.curve.keys[curveSelection.key];
if (rgbKeyCountEqual && alphaKeyCountEqual)
{
var newKey = selectedKey;
// Change a key time
if (_selectedGradientKey.m_Time != _lastSelectedGradientKey.m_Time)
{
newKey.time = _selectedGradientKey.m_Time;
}
// Change a key value
else if (_selectedGradientKey.m_Value != _lastSelectedGradientKey.m_Value)
{
newKey.value = _selectedGradientKey.m_IsAlpha ? _selectedGradientKey.m_Value.r : _selectedGradientKey.m_Value[curveSelection.curveID];
}
newKey = CheckNewKeyTime(cw, newKey, selectedKey.time);
curveSelection.key = cw.MoveKey(curveSelection.key, ref newKey);
}
else
{
// Mouse drag out of the swatch rect, save the key
if (_selectedGradientKey != null)
{
_deletedGradientKey = new GradientEditor.Swatch(_selectedGradientKey.m_Time, _selectedGradientKey.m_Value, _selectedGradientKey.m_IsAlpha);
_deletedCurveKeys ??= new List<Keyframe>(new Keyframe[(int)LwguiGradient.Channel.Num]);
_deletedCurveKeys[curveSelection.curveID] = selectedKey;
}
// Del a key
cw.curve.RemoveKey(curveSelection.key);
}
AnimationUtility.UpdateTangentsFromMode(cw.curve);
cw.changed = true;
}
if (delRGBKey || delAlphaKey)
_curveEditor.SelectNone();
}
else
{
var curveSelections = new List<CurveSelection>();
for (int c = 0; c < (int)LwguiGradient.Channel.Num; c++)
{
if (!LwguiGradient.IsChannelIndexInMask(c, viewChannelMask))
continue;
// Add a RGB Key
if ((c < (int)LwguiGradient.Channel.Alpha && addRGBKey)
// Add an Alpha Key
|| (c == (int)LwguiGradient.Channel.Alpha && addAlphaKey))
{
var cw = _curveEditor.animationCurves[c];
// Mouse drag back to the swatch rect, restore the key
if (_deletedGradientKey != null && _deletedCurveKeys != null
&& _selectedGradientKey?.m_Value == _deletedGradientKey?.m_Value
&& _selectedGradientKey?.m_IsAlpha == _deletedGradientKey?.m_IsAlpha)
{
var deletedKey = _deletedCurveKeys[c];
var newKey = deletedKey;
newKey.time = _selectedGradientKey.m_Time;
newKey = CheckNewKeyTime(cw, newKey, deletedKey.time);
var addedKeyIndex = cw.AddKey(newKey);
curveSelections.Add(new CurveSelection(c, addedKeyIndex, CurveSelection.SelectionType.Key));
}
// Add a new key
else
{
var curveSelection = _curveEditor.AddKeyAtTime(cw, _selectedGradientKey.m_Time);
curveSelections.Add(curveSelection);
}
cw.selected = CurveWrapper.SelectionMode.Selected;
cw.changed = true;
}
}
_deletedGradientKey = null;
_deletedCurveKeys = null;
_curveEditor.SelectNone();
curveSelections.ForEach(selection => _curveEditor.AddSelection(selection));
InitGradientEditor(true);
}
_curveEditor.InvalidateSelectionBounds();
InitCurveEditor(true);
// Cannot overlap with the Time of an existing Key when adding or moving Keys
Keyframe CheckNewKeyTime(CurveWrapper cw, Keyframe newKey, float oldKeyTime = 0)
{
try
{
var sameTimeKey = cw.curve.keys.First(keyframe => keyframe.time == newKey.time);
if (newKey.time > oldKeyTime)
newKey.time += 0.00001f;
else
newKey.time -= 0.00001f;
}
catch (InvalidOperationException) { }
return newKey;
}
}
private void PrepareSyncSelectionFromGradientToCurve()
{
_lastSelectedGradientKey = _selectedGradientKey != null ? new GradientEditor.Swatch(_selectedGradientKey.m_Time, _selectedGradientKey.m_Value, _selectedGradientKey.m_IsAlpha) : null;
_lastGradientRGBSwatchesCount = _gradientRGBSwatches.Count;
_lastGradientAlphaSwatchesCount = _gradientAlphaSwatches.Count;
}
private void SyncSelectionFromGradientToCurveWithoutChanges()
{
// Only detect when switching selected Key without modifying it
if (!_gradientEditorRect.Contains(Event.current.mousePosition)
// || Event.current.type != EventType.MouseDown
|| _changed
|| _lastChanged
|| Equal(_lastSelectedGradientKey, _selectedGradientKey))
return;
if (_selectedGradientKey == null)
{
_curveEditor.SelectNone();
return;
}
// Get selected gradient key index
var selectedGradientKeyIndexes = Enumerable.Repeat(-1, (int)LwguiGradient.Channel.Num).ToArray();
if (_selectedGradientKey.m_IsAlpha)
{
selectedGradientKeyIndexes[(int)LwguiGradient.Channel.Alpha] = _gradientAlphaSwatches.FindIndex(swatch => swatch == _selectedGradientKey);
}
else
{
for (int c = 0; c < (int)LwguiGradient.Channel.Num - 1; c++)
{
selectedGradientKeyIndexes[c] = _gradientRGBSwatches.FindIndex(swatch => swatch == _selectedGradientKey);
}
}
// Get curve key index
_curveEditor.SelectNone();
var lwguiMergedCurves = new LwguiGradient.LwguiMergedColorCurves(lwguiGradient.rawCurves);
for (int c = 0; c < (int)LwguiGradient.Channel.Num; c++)
{
if (selectedGradientKeyIndexes[c] < 0)
continue;
var curveKeyIndex = lwguiMergedCurves.curves[c][selectedGradientKeyIndexes[c]].index;
_selectedCurves.Add(new CurveSelection(c, curveKeyIndex));
}
_curveEditor.InvalidateSelectionBounds();
}
#endregion
#region Curve Editor
private void InitCurveEditor(bool force = false)
{
if (_curveEditor != null && !force)
return;
var firstOpenWindow = _curveEditor == null;
_curveEditor = CurveEditorWindow.instance.GetCurveEditor();
var cws = new CurveWrapper[(int)LwguiGradient.Channel.Num];
for (int c = 0; c < (int)LwguiGradient.Channel.Num; c++)
{
var curve = lwguiGradient.rawCurves[c];
var cw = new CurveWrapper();
cw.id = c;
if (LwguiGradient.IsChannelIndexInMask(c, viewChannelMask))
{
cw.color = LwguiGradient.channelColors[c];
cw.renderer = new NormalCurveRenderer(curve);
cw.renderer.SetWrap(curve.preWrapMode, curve.postWrapMode);
}
else
{
cw.renderer = new NormalCurveRenderer(new AnimationCurve());
}
cws[c] = cw;
}
_curveEditor.animationCurves = cws;
_curveEditor.curvesUpdated = () =>
{
_curveEditorContextMenuChanged = true;
};
SyncCurveEditorRect();
if (firstOpenWindow)
{
_curveEditor.Frame(new Bounds(new Vector2(0.5f, 0.5f), Vector2.one), true, true);
_curveEditor.SelectNone();
}
}
private void OnCurveEditorGUI()
{
GUI.Label(_curveEditorRect, GUIContent.none, style_PopupCurveEditorBackground);
EditorGUI.DrawRect(new Rect(_curveEditorRect.x, _curveEditorRect.y, _curveEditorRect.width, 1), new Color(0.5f, 0.5f, 0.5f, 0.5f));
PrepareSyncSelectionFromCurveToGradient();
EditorGUI.BeginChangeCheck();
_curveEditor.OnGUI();
bool curveEditorChanged = EditorGUI.EndChangeCheck() || _curveEditorContextMenuChanged;
_changed |= curveEditorChanged;
if (curveEditorChanged)
{
InitGradientEditor(true);
foreach (var cw in _curveEditor.animationCurves)
{
cw.changed = false;
}
_curveEditorContextMenuChanged = false;
}
SyncSelectionFromCurveToGradient();
}
private void SyncCurveEditorRect()
{
_curveEditor.rect = _curveEditorRect;
}
private void PrepareSyncSelectionFromCurveToGradient()
{
// var eventType = Event.current.GetTypeForControl(GUIUtility.GetControlID(897560, FocusType.Passive)); // CurveEditor.SelectPoints()
var eventType = Event.current.type;
if (_curveEditorRect.Contains(Event.current.mousePosition)
&& eventType is EventType.MouseDown or EventType.MouseDrag)
_shouldSyncSelectionFromCurveToGradient = true;
}
private void SyncSelectionFromCurveToGradient(bool force = false)
{
if (!_shouldSyncSelectionFromCurveToGradient && !force)
return;
_shouldSyncSelectionFromCurveToGradient = false;
_selectedGradientKey = null;
var selectedGradientKeys = new List<GradientEditor.Swatch>();
var mergedCurves = new CurveSelectionInfo(_curveEditor).mergedCurves;
FindSelectedGradientKey((int)LwguiGradient.Channel.Red, _gradientRGBSwatches);
FindSelectedGradientKey((int)LwguiGradient.Channel.Alpha, _gradientAlphaSwatches);
// Sync selection to Gradient Editor only when single selection
if (selectedGradientKeys.Count == 1)
_selectedGradientKey = selectedGradientKeys[0];
return;
void FindSelectedGradientKey(int channel, List<GradientEditor.Swatch> list)
{
foreach (var lwguiKeyframe in mergedCurves.curves[channel])
{
if (selectedGradientKeys.Count > 1) return;
var key = list.Find(swatch => Equal(swatch.m_Time, lwguiKeyframe.time));
if (key != null)
selectedGradientKeys.Add(key);
}
}
}
#endregion
#region Events
public void Init(Rect position, LwguiGradient gradient, ColorSpace colorSpace = ColorSpace.Gamma, LwguiGradient.ChannelMask viewChannelMask = LwguiGradient.ChannelMask.All, LwguiGradient.GradientTimeRange timeRange = LwguiGradient.GradientTimeRange.One, Action<LwguiGradient> onChange = null)
{
Clear();
this._position = position;
this.lwguiGradient = gradient;
this.colorSpace = colorSpace;
this.viewChannelMask = viewChannelMask;
this.gradientTimeRange = timeRange;
this._onChange = onChange;
}
public void OnGUI(Rect position)
{
if (lwguiGradient == null)
return;
// Debug.Log(JsonUtility.ToJson(lwguiGradient));
this._position = position;
InitGradientEditor();
InitCurveEditor();
// Gradient Editor
OnGradientEditorGUI();
// Curve Editor
SyncCurveEditorRect();
OnCurveEditorGUI();
_lastChanged = _changed;
_changed = false;
if (_lastChanged)
{
if (!EditorApplication.isPlaying)
UnityEditor.SceneManagement.EditorSceneManager.MarkAllScenesDirty();
GUI.changed = true;
_onChange?.Invoke(lwguiGradient);
}
if (_viewSettingschanged)
{
_viewSettingschanged = false;
GUI.changed = true;
}
}
public void Clear()
{
lwguiGradient = null;
_gradientEditor = null;
_curveEditor?.OnDisable();
_curveEditor = null;
_lastChanged = false;
_lastEditingTime = float.NegativeInfinity;
}
public static void CheckAddGradientKeyFailureLog(string logString, string stackTrace, LogType type)
{
if (type == LogType.Warning
&& logString == "Max " + ReflectionHelper.maxGradientKeyCount + " color keys and " + ReflectionHelper.maxGradientKeyCount + " alpha keys are allowed in a gradient.")
{
_isAddGradientKeyFailure = true;
}
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0a472009f0e9429c8d503438262f3d34
timeCreated: 1720769127

View File

@ -0,0 +1,195 @@
// Copyright (c) Jason Ma
using System;
using UnityEditor;
using UnityEngine;
using LWGUI.Runtime.LwguiGradient;
namespace LWGUI.LwguiGradientEditor
{
public static class LwguiGradientEditorHelper
{
private static readonly int s_LwguiGradientHash = "s_LwguiGradientHash".GetHashCode();
private static int s_LwguiGradientID;
// GradientEditor.DrawGradientWithBackground()
public static void DrawGradientWithBackground(Rect position, LwguiGradient gradient, ColorSpace colorSpace, LwguiGradient.ChannelMask viewChannelMask)
{
Texture2D gradientTexture = gradient.GetPreviewRampTexture(256, 1, colorSpace, viewChannelMask);
Rect r2 = new Rect(position.x + 1, position.y + 1, position.width - 2, position.height - 2);
// Background checkers
Texture2D backgroundTexture = GradientEditor.GetBackgroundTexture();
Rect texCoordsRect = new Rect(0, 0, r2.width / backgroundTexture.width, r2.height / backgroundTexture.height);
GUI.DrawTextureWithTexCoords(r2, backgroundTexture, texCoordsRect, false);
// Outline for Gradinet Texture, used to be Frame over texture.
// LWGUI: GUI.Box() will cause subsequent attributes to be unable to be selected
// GUI.Box(position, GUIContent.none);
// Gradient texture
Color oldColor = GUI.color;
GUI.color = Color.white; //Dont want the Playmode tint to be applied to gradient textures.
if (gradientTexture != null)
GUI.DrawTexture(r2, gradientTexture, ScaleMode.StretchToFill, true);
GUI.color = oldColor;
// HDR label
// float maxColorComponent = GetMaxColorComponent(gradient);
// if (maxColorComponent > 1.0f)
// {
// GUI.Label(new Rect(position.x, position.y, position.width - 3, position.height), "HDR", EditorStyles.centeredGreyMiniLabel);
// }
}
public static void DrawGradientWithSeparateAlphaChannel(Rect position, LwguiGradient gradient, ColorSpace colorSpace, LwguiGradient.ChannelMask viewChannelMask)
{
if (!LwguiGradient.HasChannelMask(viewChannelMask, LwguiGradient.ChannelMask.Alpha) || viewChannelMask == LwguiGradient.ChannelMask.Alpha)
{
DrawGradientWithBackground(position, gradient, colorSpace, viewChannelMask);
}
else
{
var r2 = new Rect(position.x + 1, position.y + 1, position.width - 2, position.height - 2);
var rgbRect = new Rect(r2.x, r2.y, r2.width, r2.height * 0.8f);
var alphaRect = new Rect(rgbRect.x, rgbRect.yMax, r2.width, r2.height * 0.2f);
var rgbTexture = gradient.GetPreviewRampTexture(256, 1, colorSpace, viewChannelMask ^ LwguiGradient.ChannelMask.Alpha);
var alphaTexture = gradient.GetPreviewRampTexture(256, 1, colorSpace, LwguiGradient.ChannelMask.Alpha);
Color oldColor = GUI.color;
GUI.color = Color.white; //Dont want the Playmode tint to be applied to gradient textures.
GUI.DrawTexture(rgbRect, rgbTexture, ScaleMode.StretchToFill, false);
GUI.DrawTexture(alphaRect, alphaTexture, ScaleMode.StretchToFill, false);
GUI.color = oldColor;
}
}
public static void GradientField(Rect position, GUIContent label, LwguiGradient gradient,
ColorSpace colorSpace = ColorSpace.Gamma,
LwguiGradient.ChannelMask viewChannelMask = LwguiGradient.ChannelMask.All,
LwguiGradient.GradientTimeRange timeRange = LwguiGradient.GradientTimeRange.One,
Action onOpenWindow = null)
{
int id = GUIUtility.GetControlID(s_LwguiGradientHash, FocusType.Keyboard, position);
var rect = EditorGUI.PrefixLabel(position, id, label);
var evt = Event.current;
// internal static Gradient DoGradientField(Rect position, int id, Gradient value, SerializedProperty property, bool hdr, ColorSpace space)
switch (evt.GetTypeForControl(id))
{
case EventType.MouseDown:
if (rect.Contains(evt.mousePosition))
{
if (evt.button == 0)
{
s_LwguiGradientID = id;
GUIUtility.keyboardControl = id;
LwguiGradientWindow.Show(gradient, colorSpace, viewChannelMask, timeRange, GUIView.current);
onOpenWindow?.Invoke();
GUIUtility.ExitGUI();
}
else if (evt.button == 1)
{
// if (property != null)
// GradientContextMenu.Show(property.Copy());
// // TODO: make work for Gradient value
}
}
break;
case EventType.KeyDown:
if (GUIUtility.keyboardControl == id && (evt.keyCode == KeyCode.Space || evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter))
{
evt.Use();
LwguiGradientWindow.Show(gradient, colorSpace, viewChannelMask, timeRange, GUIView.current);
onOpenWindow?.Invoke();
GUIUtility.ExitGUI();
}
break;
case EventType.Repaint:
DrawGradientWithSeparateAlphaChannel(rect, gradient, colorSpace, viewChannelMask);
break;
case EventType.ExecuteCommand:
// When drawing the modifying Gradient Field and it has changed
if ((GUIUtility.keyboardControl == id || s_LwguiGradientID == id)
&& (evt.commandName is LwguiGradientWindow.LwguiGradientChangedCommand))
{
GUI.changed = true;
LwguiGradientHelper.ClearRampPreviewCaches();
HandleUtility.Repaint();
}
break;
case EventType.ValidateCommand:
// Sync Undo/Redo result to editor window
if (s_LwguiGradientID == id && evt.commandName == "UndoRedoPerformed")
{
LwguiGradientWindow.UpdateCurrentGradient(gradient);
}
break;
}
}
/// Lwgui Gradient Field with full Undo/Redo/ContextMenu functions
public static void GradientField(Rect position, GUIContent label, SerializedProperty property, LwguiGradient gradient,
ColorSpace colorSpace = ColorSpace.Gamma,
LwguiGradient.ChannelMask viewChannelMask = LwguiGradient.ChannelMask.All,
LwguiGradient.GradientTimeRange timeRange = LwguiGradient.GradientTimeRange.One)
{
label = EditorGUI.BeginProperty(position, label, property);
EditorGUI.BeginChangeCheck();
GradientField(position, label, gradient, colorSpace, viewChannelMask, timeRange,
() => LwguiGradientWindow.RegisterSerializedObjectUndo(property.serializedObject.targetObject));
if (EditorGUI.EndChangeCheck())
{
GUI.changed = true;
LwguiGradientWindow.RegisterSerializedObjectUndo(property.serializedObject.targetObject);
}
EditorGUI.EndProperty();
}
public static bool GradientEditButton(Rect position, GUIContent icon, LwguiGradient gradient,
ColorSpace colorSpace = ColorSpace.Gamma,
LwguiGradient.ChannelMask viewChannelMask = LwguiGradient.ChannelMask.All,
LwguiGradient.GradientTimeRange timeRange = LwguiGradient.GradientTimeRange.One,
Func<bool> shouldOpenWindowAfterClickingEvent = null)
{
int id = GUIUtility.GetControlID(s_LwguiGradientHash, FocusType.Keyboard, position);
var evt = Event.current;
// When drawing the modifying Gradient Field and it has changed
if ((GUIUtility.keyboardControl == id || s_LwguiGradientID == id)
&& evt.GetTypeForControl(id) == EventType.ExecuteCommand
&& evt.commandName == LwguiGradientWindow.LwguiGradientChangedCommand)
{
GUI.changed = true;
HandleUtility.Repaint();
}
// Sync Undo/Redo result to editor window
if (s_LwguiGradientID == id
&& evt.commandName == "UndoRedoPerformed")
{
LwguiGradientWindow.UpdateCurrentGradient(gradient);
}
// Open editor window
var clicked = ReflectionHelper.GUI_Button(position, id, icon, GUI.skin.button);
if (clicked)
{
if (shouldOpenWindowAfterClickingEvent == null || shouldOpenWindowAfterClickingEvent.Invoke())
{
s_LwguiGradientID = id;
GUIUtility.keyboardControl = id;
LwguiGradientWindow.Show(gradient, colorSpace, viewChannelMask, timeRange, GUIView.current);
GUIUtility.ExitGUI();
}
}
return clicked;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 016014c82cb94b5d9f271b1b06986541
timeCreated: 1720424032

View File

@ -0,0 +1,117 @@
// Copyright (c) Jason Ma
using System.Collections.Generic;
using LWGUI.Runtime.LwguiGradient;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;
namespace LWGUI.LwguiGradientEditor
{
[ExcludeFromPreset]
class LwguiGradientPresetLibrary : PresetLibrary
{
[SerializeField]
List<LwguiGradientPreset> m_Presets = new List<LwguiGradientPreset>();
public override int Count()
{
return m_Presets.Count;
}
public override object GetPreset(int index)
{
return m_Presets[index].lwguiGradient;
}
public override void Add(object presetObject, string presetName)
{
LwguiGradient gradient = presetObject as LwguiGradient;
if (gradient == null)
{
Debug.LogError("Wrong type used in LwguiGradientPresetLibrary");
return;
}
m_Presets.Add(new LwguiGradientPreset(new LwguiGradient(gradient), presetName));
}
public override void Replace(int index, object newPresetObject)
{
LwguiGradient gradient = newPresetObject as LwguiGradient;
if (gradient == null)
{
Debug.LogError("Wrong type used in LwguiGradientPresetLibrary");
return;
}
m_Presets[index].lwguiGradient = new LwguiGradient(gradient);
}
public override void Remove(int index)
{
m_Presets.RemoveAt(index);
}
public override void Move(int index, int destIndex, bool insertAfterDestIndex)
{
PresetLibraryHelpers.MoveListItem(m_Presets, index, destIndex, insertAfterDestIndex);
}
public override void Draw(Rect rect, int index)
{
Draw(rect, m_Presets[index].lwguiGradient, ColorSpace.Gamma, LwguiGradient.ChannelMask.All);
}
public override void Draw(Rect rect, object presetObject)
{
Draw(rect, presetObject as LwguiGradient, ColorSpace.Gamma, LwguiGradient.ChannelMask.All);
}
public void Draw(Rect rect, LwguiGradient gradient, ColorSpace colorSpace, LwguiGradient.ChannelMask viewChannelMask)
{
if (gradient == null)
return;
LwguiGradientEditorHelper.DrawGradientWithSeparateAlphaChannel(rect, gradient, colorSpace, viewChannelMask);
}
public override string GetName(int index)
{
return m_Presets[index].name;
}
public override void SetName(int index, string presetName)
{
m_Presets[index].name = presetName;
}
[System.Serializable]
class LwguiGradientPreset
{
[SerializeField]
string m_Name;
[SerializeField]
LwguiGradient m_LwguiGradient;
public LwguiGradientPreset(LwguiGradient preset, string presetName)
{
lwguiGradient = preset;
name = presetName;
}
public LwguiGradient lwguiGradient
{
get => m_LwguiGradient;
set => m_LwguiGradient = value;
}
public string name
{
get => m_Name;
set => m_Name = value;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6aa6b41078c94b978a355082d28769f0
timeCreated: 1721013180

View File

@ -0,0 +1,40 @@
// Copyright (c) Jason Ma
using LWGUI.Runtime.LwguiGradient;
using UnityEditor;
using UnityEngine;
namespace LWGUI.LwguiGradientEditor
{
[CustomEditor(typeof(LwguiGradientPresetLibrary))]
internal class LwguiGradientPresetLibraryEditor : Editor
{
private GenericPresetLibraryInspector<LwguiGradientPresetLibrary> m_GenericPresetLibraryInspector;
public void OnEnable()
{
m_GenericPresetLibraryInspector = new GenericPresetLibraryInspector<LwguiGradientPresetLibrary>(target, "Lwgui Gradient Preset Library", OnEditButtonClicked)
{
presetSize = new Vector2(72, 16),
lineSpacing = 4f
};
}
public void OnDestroy()
{
m_GenericPresetLibraryInspector?.OnDestroy();
}
public override void OnInspectorGUI()
{
m_GenericPresetLibraryInspector.itemViewMode = PresetLibraryEditorState.GetItemViewMode("LwguiGradient"); // ensure in-sync
m_GenericPresetLibraryInspector?.OnInspectorGUI();
}
private void OnEditButtonClicked(string libraryPath)
{
LwguiGradientWindow.Show(new LwguiGradient());
LwguiGradientWindow.instance.currentPresetLibrary = libraryPath;
}
}
} // namespace

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c5050da63672448d8f12cf3c82e12a6a
timeCreated: 1721013226

View File

@ -0,0 +1,318 @@
// Copyright (c) Jason Ma
using System;
using UnityEngine;
using UnityEditor;
using LWGUI.Runtime.LwguiGradient;
using UnityEngine.Serialization;
using Object = UnityEngine.Object;
namespace LWGUI.LwguiGradientEditor
{
internal class PresetLibraryLwguiGradientEditor : PresetLibraryEditor<LwguiGradientPresetLibrary>
{
public PresetLibraryLwguiGradientEditor(ScriptableObjectSaveLoadHelper<LwguiGradientPresetLibrary> helper,
PresetLibraryEditorState state,
Action<int, object> itemClickedCallback
) : base(helper, state, itemClickedCallback)
{}
public ColorSpace colorSpace { get; set; }
public LwguiGradient.ChannelMask viewChannelMask { get; set; }
protected override void DrawPreset(PresetLibrary lib, Rect rect, object presetObject)
{
((LwguiGradientPresetLibrary)lib).Draw(rect, presetObject as LwguiGradient, colorSpace, viewChannelMask);
}
}
public class LwguiGradientWindow : EditorWindow
{
#region Fields
private static LwguiGradientWindow _lwguiGradientWindow;
public const string presetsEditorPrefID = "LwguiGradient";
private LwguiGradientEditor _lwguiGradientEditor;
private PresetLibraryLwguiGradientEditor _lwguiGradientLibraryEditor;
[SerializeField] private PresetLibraryEditorState _LwguiGradientLibraryEditorState;
[NonSerialized] public LwguiGradient lwguiGradient;
[NonSerialized] public ColorSpace colorSpace;
[NonSerialized] public LwguiGradient.ChannelMask viewChannelMask;
[NonSerialized] public LwguiGradient.GradientTimeRange gradientTimeRange;
private GUIView _viewToUpdate;
private Action<LwguiGradient> _onChange;
#endregion
#region GUI Layout
private static readonly Vector2 _minWindowSize = new (750, 500);
private static readonly float _presetLibraryHeight = 100;
private Rect _gradientEditorRect => new Rect(0, 0, position.width, position.height - _presetLibraryHeight);
private Rect _presetLibraryRect => new Rect(0, position.height - _presetLibraryHeight, position.width, _presetLibraryHeight);
#endregion
public static LwguiGradientWindow instance
{
get
{
if (!_lwguiGradientWindow)
Debug.LogError("Lwgui Gradient Window not initalized, did you call Show first?");
return _lwguiGradientWindow;
}
}
public string currentPresetLibrary
{
get
{
Init(false);
return _lwguiGradientLibraryEditor.currentLibraryWithoutExtension;
}
set
{
Init(false);
_lwguiGradientLibraryEditor.currentLibraryWithoutExtension = value;
}
}
public static bool visible => _lwguiGradientWindow != null;
public void Init(bool force = true, bool forceRecreate = false)
{
if (_lwguiGradientEditor == null || force || forceRecreate)
{
if (_lwguiGradientEditor == null || forceRecreate)
{
_lwguiGradientEditor = new LwguiGradientEditor();
}
_lwguiGradientEditor.Init(_gradientEditorRect, lwguiGradient, colorSpace, viewChannelMask, gradientTimeRange, _onChange);
}
if (_LwguiGradientLibraryEditorState == null || forceRecreate)
{
_LwguiGradientLibraryEditorState = new PresetLibraryEditorState(presetsEditorPrefID);
_LwguiGradientLibraryEditorState.TransferEditorPrefsState(true);
}
if (_lwguiGradientLibraryEditor == null || force || forceRecreate)
{
if (_lwguiGradientLibraryEditor == null || forceRecreate)
{
var saveLoadHelper = new ScriptableObjectSaveLoadHelper<LwguiGradientPresetLibrary>("lwguigradients", SaveType.Text);
_lwguiGradientLibraryEditor = new PresetLibraryLwguiGradientEditor(saveLoadHelper, _LwguiGradientLibraryEditorState, PresetClickedCallback);
UpdatePresetLibraryViewSettings();
}
_lwguiGradientLibraryEditor.showHeader = true;
_lwguiGradientLibraryEditor.minMaxPreviewHeight = new Vector2(14f, 14f);
}
}
void UpdatePresetLibraryViewSettings()
{
_lwguiGradientLibraryEditor.colorSpace = _lwguiGradientEditor.colorSpace;
_lwguiGradientLibraryEditor.viewChannelMask = _lwguiGradientEditor.viewChannelMask;
}
/// Used to modify the LwguiGradient value externally, such as: Undo/Redo/Select Preset
public static void UpdateCurrentGradient(LwguiGradient newGradient, bool doDeepCopy = false)
{
if (_lwguiGradientWindow == null)
return;
if (doDeepCopy)
{
_lwguiGradientWindow.lwguiGradient.DeepCopyFrom(newGradient);
}
else
{
_lwguiGradientWindow.lwguiGradient = newGradient;
}
// Debug.Log("Update");
_lwguiGradientWindow.Init();
_lwguiGradientWindow.Repaint();
GUI.changed = true;
LwguiGradientHelper.ClearRampPreviewCaches();
}
private static LwguiGradientWindow GetWindow(bool focus = true) => (LwguiGradientWindow)GetWindow(typeof(LwguiGradientWindow), true, "LWGUI Gradient Editor", focus);
internal static void Show(LwguiGradient gradient, ColorSpace colorSpace = ColorSpace.Gamma, LwguiGradient.ChannelMask viewChannelMask = LwguiGradient.ChannelMask.All, LwguiGradient.GradientTimeRange timeRange = LwguiGradient.GradientTimeRange.One, GUIView viewToUpdate = null, Action<LwguiGradient> onChange = null)
{
if (_lwguiGradientWindow == null)
{
_lwguiGradientWindow = GetWindow();
_lwguiGradientWindow.minSize = _minWindowSize;
_lwguiGradientWindow.RegisterEvents();
}
else
{
_lwguiGradientWindow = GetWindow();
}
_lwguiGradientWindow.lwguiGradient = gradient;
_lwguiGradientWindow.colorSpace = colorSpace;
_lwguiGradientWindow.viewChannelMask = viewChannelMask;
_lwguiGradientWindow.gradientTimeRange = timeRange;
_lwguiGradientWindow._viewToUpdate = viewToUpdate;
_lwguiGradientWindow._onChange = onChange;
_lwguiGradientWindow.Init();
_lwguiGradientWindow.Show();
// window.ShowAuxWindow();
LwguiGradientHelper.ClearRampPreviewCaches();
}
public static void CloseWindow()
{
if (_lwguiGradientWindow == null)
return;
_lwguiGradientWindow.UnregisterEvents();
_lwguiGradientWindow.Close();
// GUIUtility.ExitGUI();
}
public static void RepaintWindow()
{
if (_lwguiGradientWindow == null)
return;
_lwguiGradientWindow.Repaint();
}
public static void RegisterSerializedObjectUndo(Object targetObject)
{
Undo.RegisterCompleteObjectUndo(targetObject, "Lwgui Gradient Editor");
EditorUtility.SetDirty(targetObject);
}
public static void RegisterRampMapUndo(Object texture, Object assetImporter)
{
Undo.RecordObjects(new Object[]{ texture, assetImporter }, "Set Lwgui Gradient To Texture");
EditorUtility.SetDirty(texture);
EditorUtility.SetDirty(assetImporter);
}
private void OnGUI()
{
if (lwguiGradient == null)
return;
Init(false);
// Separator
EditorGUI.DrawRect(new Rect(_presetLibraryRect.x, _presetLibraryRect.y - 1, _presetLibraryRect.width, 1), new Color(0, 0, 0, 0.3f));
EditorGUI.DrawRect(new Rect(_presetLibraryRect.x, _presetLibraryRect.y, _presetLibraryRect.width, 1), new Color(1, 1, 1, 0.1f));
EditorGUI.BeginChangeCheck();
_lwguiGradientEditor.OnGUI(_gradientEditorRect);
_lwguiGradientLibraryEditor.OnGUI(_presetLibraryRect, lwguiGradient);
if (EditorGUI.EndChangeCheck())
{
LwguiGradientHelper.ClearRampPreviewCaches();
UpdatePresetLibraryViewSettings();
SendEvent(true);
}
}
public const string LwguiGradientChangedCommand = "LwguiGradientChanged";
void SendEvent(bool exitGUI)
{
if (_viewToUpdate != null)
{
Event e = EditorGUIUtility.CommandEvent(LwguiGradientChangedCommand);
Repaint();
_viewToUpdate.SendEvent(e);
if (exitGUI)
GUIUtility.ExitGUI();
}
if (_onChange != null)
{
_onChange(lwguiGradient);
}
}
private void OnEnable()
{
Application.logMessageReceived += LwguiGradientEditor.CheckAddGradientKeyFailureLog;
hideFlags = HideFlags.DontSave;
}
private void OnDisable()
{
Application.logMessageReceived -= LwguiGradientEditor.CheckAddGradientKeyFailureLog;
_LwguiGradientLibraryEditorState?.TransferEditorPrefsState(false);
UnregisterEvents();
Clear();
}
private void OnDestroy()
{
UnregisterEvents();
_lwguiGradientLibraryEditor?.UnloadUsedLibraries();
Clear();
}
private void Clear()
{
_lwguiGradientEditor = null;
_lwguiGradientWindow = null;
_lwguiGradientLibraryEditor = null;
}
private void RegisterEvents()
{
#if UNITY_2022_2_OR_NEWER
Undo.undoRedoEvent += OnUndoPerformed;
#endif
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
}
private void UnregisterEvents()
{
#if UNITY_2022_2_OR_NEWER
Undo.undoRedoEvent -= OnUndoPerformed;
#endif
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
}
#region Call Backs
#if UNITY_2022_2_OR_NEWER
private void OnUndoPerformed(in UndoRedoInfo info)
{
// Debug.Log("Init");
_lwguiGradientWindow.Init();
_lwguiGradientWindow.Repaint();
}
#endif
void OnPlayModeStateChanged(PlayModeStateChange state)
{
Close();
}
void PresetClickedCallback(int clickCount, object presetObject)
{
LwguiGradient gradient = presetObject as LwguiGradient;
if (gradient == null)
Debug.LogError("Incorrect object passed " + presetObject);
UpdateCurrentGradient(gradient, true);
// UnityEditorInternal.GradientPreviewCache.ClearCache();
// LwguiGradientHelper.ClearRampPreviewCaches();
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 097db44e7f52445b80daf9c3c3e9f26b
timeCreated: 1716795885

Binary file not shown.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5d0d1d146a4108b4ebb49ce891131f50
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,242 @@
// Copyright (c) Jason Ma
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace LWGUI
{
public static class ReflectionHelper
{
#region MaterialPropertyHandler
private static readonly Type MaterialPropertyHandler_Type = Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.MaterialPropertyHandler");
private static readonly MethodInfo MaterialPropertyHandler_GetHandler_Method = MaterialPropertyHandler_Type.GetMethod("GetHandler", BindingFlags.Static | BindingFlags.NonPublic);
private static readonly PropertyInfo MaterialPropertyHandler_PropertyDrawer_Property = MaterialPropertyHandler_Type.GetProperty("propertyDrawer");
private static readonly FieldInfo MaterialPropertyHandler_DecoratorDrawers_Field = MaterialPropertyHandler_Type.GetField("m_DecoratorDrawers", BindingFlags.NonPublic | BindingFlags.Instance);
public static MaterialPropertyDrawer GetPropertyDrawer(Shader shader, MaterialProperty prop, out List<MaterialPropertyDrawer> decoratorDrawers)
{
decoratorDrawers = new List<MaterialPropertyDrawer>();
var handler = MaterialPropertyHandler_GetHandler_Method.Invoke(null, new object[] { shader, prop.name });
if (handler != null && handler.GetType() == MaterialPropertyHandler_Type)
{
decoratorDrawers = MaterialPropertyHandler_DecoratorDrawers_Field.GetValue(handler) as List<MaterialPropertyDrawer>;
return MaterialPropertyHandler_PropertyDrawer_Property.GetValue(handler, null) as MaterialPropertyDrawer;
}
return null;
}
public static MaterialPropertyDrawer GetPropertyDrawer(Shader shader, MaterialProperty prop)
{
return GetPropertyDrawer(shader, prop, out _);
}
#endregion
#region MaterialEditor
public static float DoPowerRangeProperty(Rect position, MaterialProperty prop, GUIContent label, float power)
{
return MaterialEditor.DoPowerRangeProperty(position, prop, label, power);
}
public static void DefaultShaderPropertyInternal(this MaterialEditor editor, Rect position, MaterialProperty prop, GUIContent label)
{
editor.DefaultShaderPropertyInternal(position, prop, label);
}
public static List<Renderer> GetMeshRenderersByMaterialEditor(this MaterialEditor materialEditor)
{
var outRenderers = new List<Renderer>();
// MaterialEditor.ShouldEditorBeHidden()
PropertyEditor property = materialEditor.propertyViewer as PropertyEditor;
if (property)
{
GameObject gameObject = property.tracker.activeEditors[0].target as GameObject;
if (gameObject)
{
outRenderers.AddRange(gameObject.GetComponents<MeshRenderer>());
outRenderers.AddRange(gameObject.GetComponents<SkinnedMeshRenderer>());
}
}
return outRenderers;
}
#endregion
#region EditorUtility
public static void DisplayCustomMenuWithSeparators(Rect position, string[] options, bool[] enabled, bool[] separator, int[] selected, EditorUtility.SelectMenuItemFunction callback, object userData = null, bool showHotkey = false)
{
EditorUtility.DisplayCustomMenuWithSeparators(position, options, enabled, separator, selected, callback, userData, showHotkey);
}
#endregion
#region EditorGUI
public static float EditorGUI_Indent => EditorGUI.indentLevel;
#endregion
#region EditorGUILayout
public static float EditorGUILayout_kLabelFloatMinW => EditorGUILayout.kLabelFloatMinW;
#endregion
#region MaterialEnumDrawer
// UnityEditor.MaterialEnumDrawer(string enumName)
private static Type[] _types;
public static Type[] GetAllTypes()
{
if (_types == null)
{
_types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly =>
{
if (assembly == null)
return Type.EmptyTypes;
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
Debug.LogError(ex);
return Type.EmptyTypes;
}
}).ToArray();
}
return _types;
}
#endregion
#region MaterialProperty.PropertyData
#if UNITY_2022_1_OR_NEWER
private static readonly Type MaterialProperty_Type = typeof(MaterialProperty);
private static readonly Type PropertyData_Type = MaterialProperty_Type.GetNestedType("PropertyData", BindingFlags.NonPublic);
private static readonly MethodInfo PropertyData_MergeStack_Method = PropertyData_Type.GetMethod("MergeStack", BindingFlags.Static | BindingFlags.NonPublic);
private static readonly MethodInfo PropertyData_HandleApplyRevert_Method = PropertyData_Type.GetMethod("HandleApplyRevert", BindingFlags.Static | BindingFlags.NonPublic);
public static void HandleApplyRevert(GenericMenu menu, MaterialProperty prop)
{
var parameters = new object[3];
PropertyData_MergeStack_Method.Invoke(null, parameters);
var overriden = (bool)parameters[2];
var singleEditing = prop.targets.Length == 1;
if (overriden)
{
PropertyData_HandleApplyRevert_Method.Invoke(null, new object[] { menu, singleEditing, prop.targets });
menu.AddSeparator("");
}
}
#endif
#endregion
#region GUI
private static readonly MethodInfo gui_Button_Method = typeof(GUI).GetMethod("Button", BindingFlags.Static | BindingFlags.NonPublic);
public static bool GUI_Button(Rect position, int id, GUIContent content, GUIStyle style)
{
return (bool)gui_Button_Method.Invoke(null, new object[] { position, id, content, style });
}
#endregion
#region GradientEditor
private static readonly FieldInfo k_MaxNumKeys_Field = typeof(GradientEditor).GetField("k_MaxNumKeys", BindingFlags.Static | BindingFlags.NonPublic);
public static readonly int maxGradientKeyCount = (int)k_MaxNumKeys_Field.GetValue(null);
private static readonly FieldInfo m_SelectedSwatch_Field = typeof(GradientEditor).GetField("m_SelectedSwatch", BindingFlags.Instance | BindingFlags.NonPublic);
internal static GradientEditor.Swatch GetSelectedSwatch(this GradientEditor gradientEditor)
{
return m_SelectedSwatch_Field.GetValue(gradientEditor) as GradientEditor.Swatch;
}
internal static void SetSelectedSwatch(this GradientEditor gradientEditor, GradientEditor.Swatch swatch)
{
m_SelectedSwatch_Field.SetValue(gradientEditor, swatch);
}
private static readonly FieldInfo m_RGBSwatches_Field = typeof(GradientEditor).GetField("m_RGBSwatches", BindingFlags.Instance | BindingFlags.NonPublic);
internal static List<GradientEditor.Swatch> GetRGBdSwatches(this GradientEditor gradientEditor)
{
return m_RGBSwatches_Field.GetValue(gradientEditor) as List<GradientEditor.Swatch>;
}
private static readonly FieldInfo m_AlphaSwatches_Field = typeof(GradientEditor).GetField("m_AlphaSwatches", BindingFlags.Instance | BindingFlags.NonPublic);
internal static List<GradientEditor.Swatch> GetAlphaSwatches(this GradientEditor gradientEditor)
{
return m_AlphaSwatches_Field.GetValue(gradientEditor) as List<GradientEditor.Swatch>;
}
private static object s_Styles_Value;
private static readonly FieldInfo s_Styles_Field = typeof(GradientEditor).GetField("s_Styles", BindingFlags.Static | BindingFlags.NonPublic);
public static void GradientEditor_SetStyles()
{
s_Styles_Value ??= Activator.CreateInstance(typeof(GradientEditor).GetNestedType("Styles", BindingFlags.NonPublic));
s_Styles_Field.SetValue(null, s_Styles_Value);
}
private static readonly MethodInfo ShowSwatchArray_Method = typeof(GradientEditor)
.GetMethod("ShowSwatchArray", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Rect), typeof(List<GradientEditor.Swatch>), typeof(bool) }, null);
internal static void ShowSwatchArray(this GradientEditor gradientEditor, Rect position, List<GradientEditor.Swatch> swatches, bool isAlpha)
{
ShowSwatchArray_Method.Invoke(gradientEditor, new object[] { position, swatches, isAlpha });
}
private static readonly MethodInfo GetTime_Method = typeof(GradientEditor).GetMethod("GetTime", BindingFlags.Instance | BindingFlags.NonPublic);
internal static float GetTime(this GradientEditor gradientEditor, float actualTime)
{
return (float)GetTime_Method.Invoke(gradientEditor, new object[] { actualTime });
}
#endregion
#region CurveEditor
private static readonly FieldInfo m_CurveEditor_Field = typeof(CurveEditorWindow).GetField("m_CurveEditor", BindingFlags.Instance | BindingFlags.NonPublic);
internal static CurveEditor GetCurveEditor(this CurveEditorWindow curveEditorWindow)
{
return m_CurveEditor_Field.GetValue(curveEditorWindow) as CurveEditor;
}
private static readonly MethodInfo AddKeyAtTime_Method = typeof(CurveEditor).GetMethod("AddKeyAtTime", BindingFlags.Instance | BindingFlags.NonPublic);
internal static CurveSelection AddKeyAtTime(this CurveEditor curveEditor, CurveWrapper cw, float time)
{
return AddKeyAtTime_Method.Invoke(curveEditor, new object[] { cw, time }) as CurveSelection;
}
#endregion
}
}

View File

@ -0,0 +1,18 @@
{
"name": "Unity.InternalAPIEditorBridge.020",
"rootNamespace": "",
"references": [
"LWGUI.Runtime"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d40b1b73ac7645c43af711c92abd00b3
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
// Copyright (c) Jason Ma
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace LWGUI
{
public static class UnityEditorExtension
{
#region MaterialEditor
// For Developers: Call this after a material has modified in code
public static void ApplyMaterialPropertyAndDecoratorDrawers(Material material)
{
var objs = new Object[] { material };
ApplyMaterialPropertyAndDecoratorDrawers(objs);
}
// Called after edit or undo
public static void ApplyMaterialPropertyAndDecoratorDrawers(Object[] targets)
{
if (!EditorMaterialUtility.disableApplyMaterialPropertyDrawers)
{
if (targets == null || targets.Length == 0)
return;
var target = targets[0] as Material;
if (target == null)
return;
var shader = target.shader;
string[] propNames = MaterialEditor.GetMaterialPropertyNames(targets);
for (int i = 0; i < propNames.Length; i++)
{
var prop = MaterialEditor.GetMaterialProperty(targets, i);
var drawer = ReflectionHelper.GetPropertyDrawer(shader, prop, out var decoratorDrawers);
if (drawer != null)
{
drawer.Apply(prop);
}
if (decoratorDrawers != null)
{
foreach (var decoratorDrawer in decoratorDrawers)
{
decoratorDrawer.Apply(prop);
}
}
}
}
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c461331302de45948d270b0842238473
timeCreated: 1726212440

Binary file not shown.

View File

@ -18,9 +18,11 @@ namespace NiloToon.NiloToonURP
{
List<string> messages = new List<string>();
messages.Add("- Like URP14's Bloom but with more controls.\n" +
"- Can supplement or replace URP's Bloom.\n" +
"* This Bloom is slower than URP's Bloom (GPU)");
messages.Add("- Similar to URP's Bloom but with more controls\n" +
"- Great for preventing character become over bloom\n" +
"- Allows different setting for Character & non-Character pixels\n" +
"- Can supplement or replace URP's Bloom\n" +
"* This Bloom is slower than URP's Bloom (GPU), only recommended for PC/Console");
messages.Add(IsPostProcessMessage);

View File

@ -19,7 +19,7 @@ namespace NiloToon.NiloToonURP
List<string> messages = new List<string>();
messages.Add(
"- Great for helping NiloToon characters blend into any environment\n" +
"- Great for helping NiloToon characters to blend into any environment\n" +
"- Great for artist-controlled lighting and rim light on NiloToon characters");
messages.Add(NonPostProcess_NotAffectPerformance_Message);

View File

@ -19,10 +19,10 @@ namespace NiloToon.NiloToonURP
List<string> messages = new List<string>();
messages.Add(
"Make NiloToon characters convert all received additional light into rim light.\n" +
"Useful if you want characters to receive many bright additional lights,\n" +
"yet maintain a pleasing lighting effect, without becoming overly bright or unappealing.\n" +
"(e.g., ideal for concert stage live performances or cinematic cut scenes)"
"- Converts all received additional light into rim light for NiloToon characters\n" +
"- Useful if you want characters to receive many bright additional lights,\n" +
" yet maintain a pleasing lighting effect, without becoming overly bright or unappealing.\n" +
"- Ideal for concert stage live performances or cinematic cut scenes"
);
messages.Add(NonPostProcess_NotAffectPerformance_Message);

View File

@ -18,6 +18,8 @@ namespace NiloToon.NiloToonURP
{
List<string> messages = new List<string>();
messages.Add("- For extra color control of NiloToon_Environment shader\n" +
"- Or for debug which material is using NiloToon_Environment shader" );
messages.Add(NonPostProcess_NotAffectPerformance_Message);
return messages;

View File

@ -0,0 +1,30 @@
using System.Collections.Generic;
using NiloToon.NiloToonURP;
using UnityEditor;
using UnityEditor.Rendering;
namespace NiloToon.NiloToonURP
{
[CanEditMultipleObjects]
#if UNITY_2022_2_OR_NEWER
[CustomEditor(typeof(NiloToonMotionBlurVolume))]
#else
[VolumeComponentEditor(typeof(NiloToonMotionBlurVolume))]
#endif
public class NiloToonMotionBlurVolumeEditor : NiloToonVolumeComponentEditor<NiloToonMotionBlurVolume>
{
// Override GetHelpBoxContent to provide specific help box content
protected override List<string> GetHelpBoxContent()
{
List<string> messages = new List<string>();
messages.Add(
"[Requires Unity2022.3 or above]\n" +
"- Cinematic object motion blur, usually used for character dance animations in music videos (MVs). It is better than URP's object motion blur but comes with a much higher GPU cost.\n" +
"- Recommended for PC/Console only");
messages.Add(IsPostProcessMessage);
return messages;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2706839ba1d17584890ca7454f75e0ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -18,7 +18,12 @@ namespace NiloToon.NiloToonURP
{
List<string> messages = new List<string>();
messages.Add(NonPostProcess_NotAffectPerformance_Message);
messages.Add(
"- For producing NiloToon Character or Environment's screen space outline\n" +
"- Requires enabling 'Enable ScreenSpace Outline' in NiloToonAllInOne renderer feature\n" +
"- Requires temporal AA like TAA/STP/DLSS/XeSS/FSR... to produce stable result");
messages.Add(NonPostProcess_MayAffectPerformance_Message);
return messages;
}

View File

@ -19,8 +19,9 @@ namespace NiloToon.NiloToonURP
List<string> messages = new List<string>();
messages.Add(
"- If overridden, will use settings here instead of NiloToonAllInOneRendererFeature.\n" +
"- If not overridden, will use NiloToonAllInOneRendererFeature's settings.");
"- Great for controlling NiloToon characters' shadow results (e.g., shadow tint color)\n" +
"- When overridden, uses settings from here instead of NiloToonAllInOneRendererFeature\n" +
"- When not overridden, uses NiloToonAllInOneRendererFeature's settings");
messages.Add(NonPostProcess_MayAffectPerformance_Message);

Binary file not shown.

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: a0890936ec94ed749881904e977d840d
guid: faa27ea2c11a8a34a99a3c5a2288129b
folderAsset: yes
DefaultImporter:
externalObjects: {}

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