1026 lines
46 KiB
C#
1026 lines
46 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using FlatKit;
|
|
using FlatKit.StylizedSurface;
|
|
using FlatKit.Editor;
|
|
using JetBrains.Annotations;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using Object = UnityEngine.Object;
|
|
|
|
// ReSharper disable Unity.PreferAddressByIdToGraphicsParams
|
|
// ReSharper disable StringLiteralTypo
|
|
[HelpURL("https://flatkit.dustyroom.com/")]
|
|
public class StylizedSurfaceEditor : BaseShaderGUI {
|
|
private Material _target;
|
|
private MaterialProperty[] _properties;
|
|
private int _celShadingNumSteps = 0;
|
|
private AnimationCurve _gradient = new AnimationCurve(new Keyframe(0, 0), new Keyframe(1, 1));
|
|
private bool? _smoothNormalsEnabled;
|
|
|
|
private static GUIStyle _boxStyle;
|
|
private static GUIStyle _richHelpBoxStyle;
|
|
private static GUIStyle _foldoutStyle;
|
|
private static readonly Dictionary<string, bool> FoldoutStates = new() { { RenderingOptionsName, false } };
|
|
|
|
private static readonly Color HashColor = new Color(0.85023f, 0.85034f, 0.85045f, 0.85056f);
|
|
private static readonly int ColorPropertyName = Shader.PropertyToID("_BaseColor");
|
|
private static readonly int LightmapDirection = Shader.PropertyToID("_LightmapDirection");
|
|
private static readonly int LightmapDirectionPitch = Shader.PropertyToID("_LightmapDirectionPitch");
|
|
private static readonly int LightmapDirectionYaw = Shader.PropertyToID("_LightmapDirectionYaw");
|
|
private const string OutlineSmoothNormalsKeyword = "DR_OUTLINE_SMOOTH_NORMALS";
|
|
private const string RenderingOptionsName = "Rendering Options";
|
|
private const string UnityVersion = "NLC6GP";
|
|
|
|
private void DrawStandard(MaterialProperty property) {
|
|
// Remove everything in square brackets.
|
|
var cleanName = Regex.Replace(property.displayName, @" ?\[.*?\]", string.Empty);
|
|
|
|
if (!Tooltips.Map.TryGetValue(property.displayName, out string tooltip)) {
|
|
Tooltips.Map.TryGetValue(cleanName, out tooltip);
|
|
}
|
|
|
|
var guiContent = new GUIContent(cleanName, tooltip);
|
|
if (property.GetShaderPropertyType() == ShaderPropertyType.Texture) {
|
|
if (!property.name.Contains("_BaseMap")) {
|
|
EditorGUILayout.Space(10);
|
|
}
|
|
|
|
materialEditor.TexturePropertySingleLine(guiContent, property);
|
|
} else {
|
|
materialEditor.ShaderProperty(property, guiContent);
|
|
}
|
|
}
|
|
|
|
private MaterialProperty FindProperty(string name) {
|
|
return FindProperty(name, _properties);
|
|
}
|
|
|
|
private bool HasProperty(string name) {
|
|
return _target != null && _target.HasProperty(name);
|
|
}
|
|
|
|
#if UNITY_2021_2_OR_NEWER
|
|
[Obsolete("MaterialChanged has been renamed ValidateMaterial", false)]
|
|
#endif
|
|
public override void MaterialChanged(Material material) { }
|
|
|
|
public override void OnGUI(MaterialEditor editor, MaterialProperty[] properties) {
|
|
materialEditor = editor;
|
|
_properties = properties;
|
|
_target = editor.target as Material;
|
|
Debug.Assert(_target != null, "_target != null");
|
|
|
|
if (_target.shader.name.Equals("FlatKit/Stylized Surface With Outline")) {
|
|
EditorGUILayout.HelpBox(
|
|
"'Stylized Surface with Outline' shader has been deprecated. Please use the outline section in the 'Stylized Surface' shader.",
|
|
MessageType.Warning);
|
|
}
|
|
|
|
if (!Application.unityVersion.Contains(Rev(UnityVersion)) &&
|
|
!Application.unityVersion.Contains('b') &&
|
|
!Application.unityVersion.Contains('a') &&
|
|
!Application.unityVersion.Replace('.', '^').Contains("23^2")) {
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
// Icon.
|
|
{
|
|
EditorGUILayout.BeginVertical();
|
|
GUILayout.FlexibleSpace();
|
|
var icon = EditorGUIUtility.IconContent("console.erroricon@2x").image;
|
|
var iconSize = Mathf.Min(Mathf.Min(60, icon.width), EditorGUIUtility.currentViewWidth - 100);
|
|
GUILayout.Label(icon, new GUIStyle {
|
|
alignment = TextAnchor.MiddleCenter,
|
|
imagePosition = ImagePosition.ImageLeft,
|
|
fixedWidth = iconSize,
|
|
fixedHeight = iconSize,
|
|
padding = new RectOffset(0, 0, 5, 5),
|
|
margin = new RectOffset(0, 0, 0, 0),
|
|
});
|
|
GUILayout.FlexibleSpace();
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
var unityMajorVersion = Application.unityVersion.Substring(0, Application.unityVersion.LastIndexOf('.'));
|
|
var m = $"This version of <b>Flat Kit</b> is designed for <b>Unity {Rev(UnityVersion)}</b>. " +
|
|
$"You are currently using <b>Unity {unityMajorVersion}</b>.\n" +
|
|
"<i>The shader and the UI below may not work correctly.</i>\n" +
|
|
"Please <b>re-download Flat Kit</b> to get the compatible version.";
|
|
var style = new GUIStyle(EditorStyles.wordWrappedLabel) {
|
|
alignment = TextAnchor.MiddleLeft,
|
|
richText = true,
|
|
fontSize = 12,
|
|
padding = new RectOffset(0, 5, 5, 5),
|
|
margin = new RectOffset(0, 0, 0, 0),
|
|
};
|
|
EditorGUILayout.LabelField(m, style);
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
// Unity version help buttons.
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
const float buttonWidth = 120;
|
|
|
|
if (GUILayout.Button("Package Manager", EditorStyles.miniButton, GUILayout.Width(buttonWidth))) {
|
|
const string packageName = "Flat Kit: Toon Shading and Water";
|
|
|
|
var type = typeof(UnityEditor.PackageManager.UI.Window);
|
|
var method = type.GetMethod("OpenFilter", BindingFlags.NonPublic | BindingFlags.Static);
|
|
if (method != null) {
|
|
method.Invoke(null, new object[] { $"AssetStore/{packageName}" });
|
|
} else {
|
|
UnityEditor.PackageManager.UI.Window.Open(packageName);
|
|
}
|
|
}
|
|
|
|
if (GUILayout.Button("Asset Store", EditorStyles.miniButton, GUILayout.Width(buttonWidth))) {
|
|
const string assetStoreUrl = "https://u3d.as/1uVy";
|
|
Application.OpenURL(assetStoreUrl);
|
|
}
|
|
|
|
if (GUILayout.Button("Support", EditorStyles.miniButton, GUILayout.Width(buttonWidth))) {
|
|
const string contactUrl = "https://flatkit.dustyroom.com/contact-details/";
|
|
Application.OpenURL(contactUrl);
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.Space(2);
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
EditorGUILayout.Space();
|
|
|
|
foreach (var property in properties) {
|
|
// materialEditor.ShaderProperty(property, property.displayName);
|
|
DrawStandard(property);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (_target.IsKeywordEnabled("DR_OUTLINE_ON") && _target.IsKeywordEnabled("_ALPHATEST_ON")) {
|
|
EditorGUILayout.HelpBox(
|
|
"The 'Outline' and 'Alpha Clip' features are usually incompatible. The outline shader pass will not be using alpha clipping.",
|
|
MessageType.Warning);
|
|
}
|
|
|
|
int originalIntentLevel = EditorGUI.indentLevel;
|
|
string foldoutName = string.Empty;
|
|
int foldoutRemainingItems = 0;
|
|
bool latestFoldoutState = false;
|
|
_foldoutStyle ??= new GUIStyle(EditorStyles.foldout) {
|
|
margin = {
|
|
left = -5
|
|
},
|
|
padding = {
|
|
left = 20
|
|
},
|
|
};
|
|
_boxStyle ??= new GUIStyle(EditorStyles.helpBox);
|
|
_richHelpBoxStyle ??= new GUIStyle(EditorStyles.helpBox) {
|
|
richText = true
|
|
};
|
|
bool vGroupStarted = false;
|
|
|
|
foreach (MaterialProperty property in properties) {
|
|
string displayName = property.displayName;
|
|
|
|
if (displayName.Contains("[") && !displayName.Contains("FOLDOUT")) {
|
|
EditorGUI.indentLevel += 1;
|
|
}
|
|
|
|
bool skipProperty = false;
|
|
skipProperty |= displayName.Contains("[_CELPRIMARYMODE_SINGLE]") &&
|
|
!_target.IsKeywordEnabled("_CELPRIMARYMODE_SINGLE");
|
|
skipProperty |= displayName.Contains("[_CELPRIMARYMODE_STEPS]") &&
|
|
!_target.IsKeywordEnabled("_CELPRIMARYMODE_STEPS");
|
|
skipProperty |= displayName.Contains("[_CELPRIMARYMODE_CURVE]") &&
|
|
!_target.IsKeywordEnabled("_CELPRIMARYMODE_CURVE");
|
|
skipProperty |= displayName.Contains("[DR_CEL_EXTRA_ON]") &&
|
|
!property.name.Equals("_CelExtraEnabled") &&
|
|
!_target.IsKeywordEnabled("DR_CEL_EXTRA_ON");
|
|
skipProperty |= displayName.Contains("[DR_SPECULAR_ON]") &&
|
|
!property.name.Equals("_SpecularEnabled") &&
|
|
!_target.IsKeywordEnabled("DR_SPECULAR_ON");
|
|
skipProperty |= displayName.Contains("[DR_RIM_ON]") &&
|
|
!property.name.Equals("_RimEnabled") &&
|
|
!_target.IsKeywordEnabled("DR_RIM_ON");
|
|
skipProperty |= displayName.Contains("[DR_GRADIENT_ON]") &&
|
|
!property.name.Equals("_GradientEnabled") &&
|
|
!_target.IsKeywordEnabled("DR_GRADIENT_ON");
|
|
skipProperty |= displayName.Contains("[_UNITYSHADOWMODE_MULTIPLY]") &&
|
|
!_target.IsKeywordEnabled("_UNITYSHADOWMODE_MULTIPLY");
|
|
skipProperty |= displayName.Contains("[_UNITYSHADOWMODE_COLOR]") &&
|
|
!_target.IsKeywordEnabled("_UNITYSHADOWMODE_COLOR");
|
|
skipProperty |= displayName.Contains("[DR_ENABLE_LIGHTMAP_DIR]") &&
|
|
!_target.IsKeywordEnabled("DR_ENABLE_LIGHTMAP_DIR");
|
|
skipProperty |= displayName.Contains("[DR_OUTLINE_ON]") &&
|
|
!_target.IsKeywordEnabled("DR_OUTLINE_ON");
|
|
skipProperty |= displayName.Contains("[_EMISSION]") &&
|
|
!_target.IsKeywordEnabled("_EMISSION");
|
|
skipProperty |= displayName.Contains("[_OUTLINESPACE_SCREEN]") &&
|
|
!_target.IsKeywordEnabled("_OUTLINESPACE_SCREEN");
|
|
|
|
bool isOverrideLightDirectionToggle = displayName.ToLower().Contains("override light direction");
|
|
if (Lightmapping.lightingDataAsset != null) {
|
|
// Lightmapping is enabled.
|
|
_target.EnableKeyword("DR_ENABLE_LIGHTMAP_DIR");
|
|
if (isOverrideLightDirectionToggle) {
|
|
skipProperty = true;
|
|
|
|
if (latestFoldoutState && foldoutName.Contains("Advanced Lighting")) {
|
|
const string m =
|
|
"<b>Lightmap Direction Override</b> is required because the scene is using baked lighting.";
|
|
EditorGUILayout.LabelField(m, new GUIStyle(EditorStyles.label) {
|
|
richText = true,
|
|
wordWrap = true,
|
|
padding = new RectOffset(16, 0, 8, 0)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_target.IsKeywordEnabled("DR_ENABLE_LIGHTMAP_DIR") && isOverrideLightDirectionToggle) {
|
|
var dirPitch = _target.GetFloat(LightmapDirectionPitch);
|
|
var dirYaw = _target.GetFloat(LightmapDirectionYaw);
|
|
|
|
var dirPitchRad = dirPitch * Mathf.Deg2Rad;
|
|
var dirYawRad = dirYaw * Mathf.Deg2Rad;
|
|
|
|
var direction = new Vector4(Mathf.Sin(dirPitchRad) * Mathf.Sin(dirYawRad), Mathf.Cos(dirPitchRad),
|
|
Mathf.Sin(dirPitchRad) * Mathf.Cos(dirYawRad), 0.0f);
|
|
_target.SetVector(LightmapDirection, direction);
|
|
}
|
|
|
|
if (displayName.Contains("FOLDOUT")) {
|
|
foldoutName = displayName.Split('(', ')')[1];
|
|
string foldoutItemCount = displayName.Split('{', '}')[1];
|
|
foldoutRemainingItems = Convert.ToInt32(foldoutItemCount);
|
|
FoldoutStates.TryAdd(property.name, false);
|
|
|
|
EditorGUILayout.Space(10);
|
|
FoldoutStates[property.name] =
|
|
EditorGUILayout.Foldout(FoldoutStates[property.name], foldoutName, _foldoutStyle);
|
|
latestFoldoutState = FoldoutStates[property.name];
|
|
if (latestFoldoutState) {
|
|
BeginBox();
|
|
}
|
|
}
|
|
|
|
if (foldoutRemainingItems > 0) {
|
|
skipProperty = skipProperty || !latestFoldoutState;
|
|
EditorGUI.indentLevel += 1;
|
|
--foldoutRemainingItems;
|
|
} else if (!skipProperty) {
|
|
if (EditorGUI.indentLevel > 0 && !vGroupStarted) {
|
|
BeginBox();
|
|
vGroupStarted = true;
|
|
}
|
|
|
|
if (EditorGUI.indentLevel <= 0 && vGroupStarted) {
|
|
EndBox();
|
|
vGroupStarted = false;
|
|
}
|
|
}
|
|
|
|
if (_target.IsKeywordEnabled("_CELPRIMARYMODE_STEPS") && displayName.Contains("[LAST_PROP_STEPS]")) {
|
|
EditorGUILayout.HelpBox(
|
|
"This mode creates a step texture that control the light/shadow transition. To use:\n" +
|
|
"1. Set the number of steps (e.g. 3 means three steps between lit and shaded regions), \n" +
|
|
"2. Save the steps as a texture - 'Save Ramp Texture' button",
|
|
MessageType.Info);
|
|
int currentNumSteps = _target.GetInt("_CelNumSteps");
|
|
if (currentNumSteps != _celShadingNumSteps) {
|
|
if (GUILayout.Button("Save Ramp Texture")) {
|
|
_celShadingNumSteps = currentNumSteps;
|
|
PromptTextureSave(editor, GenerateStepTexture, "_CelStepTexture", FilterMode.Point);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_target.IsKeywordEnabled("_CELPRIMARYMODE_CURVE") && displayName.Contains("[LAST_PROP_CURVE]")) {
|
|
EditorGUILayout.HelpBox(
|
|
"This mode uses arbitrary curves to control the light/shadow transition. How to use:\n" +
|
|
"1. Set shading curve (generally from 0.0 to 1.0)\n" +
|
|
"2. [Optional] Save the curve preset\n" +
|
|
"3. Save the curve as a texture.",
|
|
MessageType.Info);
|
|
_gradient = EditorGUILayout.CurveField("Shading curve", _gradient);
|
|
|
|
if (GUILayout.Button("Save Ramp Texture")) {
|
|
PromptTextureSave(editor, GenerateCurveTexture, "_CelCurveTexture",
|
|
FilterMode.Trilinear);
|
|
}
|
|
}
|
|
|
|
if (!skipProperty &&
|
|
property.GetShaderPropertyType() == ShaderPropertyType.Color &&
|
|
property.colorValue == HashColor) {
|
|
property.colorValue = _target.GetColor(ColorPropertyName);
|
|
}
|
|
|
|
if (!skipProperty && property.name.Contains("_EmissionMap")) {
|
|
EditorGUILayout.Space(10);
|
|
bool emission = editor.EmissionEnabledProperty();
|
|
EditorGUILayout.Space(-10);
|
|
EditorGUI.indentLevel += 1;
|
|
if (emission) {
|
|
_target.EnableKeyword("_EMISSION");
|
|
} else {
|
|
_target.DisableKeyword("_EMISSION");
|
|
}
|
|
}
|
|
|
|
if (!skipProperty && property.name.Contains("_EmissionColor")) {
|
|
EditorGUI.indentLevel += 1;
|
|
}
|
|
|
|
bool hideInInspector = (property.GetShaderPropertyFlags() & ShaderPropertyFlags.HideInInspector) != 0;
|
|
if (!hideInInspector && !skipProperty) {
|
|
EditorGUI.BeginChangeCheck();
|
|
DrawStandard(property);
|
|
|
|
// Toggle per-object outlines.
|
|
if (property.name.Equals("_OutlineEnabled")) {
|
|
var outlineEnabled = _target.IsKeywordEnabled("DR_OUTLINE_ON");
|
|
|
|
if (_target.GetShaderPassEnabled("SRPDEFAULTUNLIT")) {
|
|
// Legacy per-object outlines.
|
|
const string m = "Using legacy per-object outlines. Please update to Unity 2022.3+ to use " +
|
|
"the new Renderer Feature outlines.";
|
|
EditorGUILayout.HelpBox(m, MessageType.Info);
|
|
} else {
|
|
// Per-object outlines are now handled by a Renderer Feature.
|
|
var renderer = ObjectOutlineEditorUtils.GetRendererData();
|
|
if (renderer != null) {
|
|
GUILayout.Space(-EditorGUIUtility.standardVerticalSpacing -
|
|
EditorGUIUtility.singleLineHeight);
|
|
GUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button("Select Renderer Feature", EditorStyles.miniButtonRight)) {
|
|
Selection.activeObject = renderer;
|
|
}
|
|
|
|
GUILayout.EndHorizontal();
|
|
}
|
|
}
|
|
|
|
if (EditorGUI.EndChangeCheck()) {
|
|
// Outline toggle changed state.
|
|
#if UNITY_2022_3_OR_NEWER
|
|
ObjectOutlineEditorUtils.SetActive(_target, outlineEnabled);
|
|
#else
|
|
_target.SetShaderPassEnabled("SRPDEFAULTUNLIT", outlineEnabled);
|
|
#endif
|
|
}
|
|
|
|
#if UNITY_2022_3_OR_NEWER
|
|
// Switch from legacy shader pass per-object outlines (pre-4.7.0) to new Renderer Feature outlines.
|
|
const string legacyPassName = "SRPDEFAULTUNLIT";
|
|
if (_target.GetShaderPassEnabled(legacyPassName) && outlineEnabled) {
|
|
_target.SetShaderPassEnabled(legacyPassName, false);
|
|
var m = $"<color=grey>[Flat Kit]</color> <b>Per-object outlines</b> are now handled by a " +
|
|
$"<b>Renderer Feature</b>. The material <color=green><b>{_target.name}</b></color> " +
|
|
$"and the <b>URP Renderer</b> currently in use will be updated now.";
|
|
Debug.Log(m);
|
|
ObjectOutlineEditorUtils.SetActive(_target, true);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (!skipProperty && displayName.Contains("Detail Impact")) {
|
|
DrawTileOffset(editor, FindProperty("_DetailMap"));
|
|
}
|
|
|
|
// Horizontal line separators.
|
|
{
|
|
if (FoldoutStates.TryGetValue("_BaseMap", out bool baseMapFoldoutState) && baseMapFoldoutState) {
|
|
if (displayName.Contains("Texture Impact") ||
|
|
displayName.Contains("Detail Impact") ||
|
|
displayName.Contains("Normal Map") ||
|
|
displayName.Contains("Emission Color")) {
|
|
var indent = EditorGUI.indentLevel;
|
|
EditorGUI.indentLevel = originalIntentLevel + 1;
|
|
EditorGUILayout.Space(5);
|
|
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
|
|
EditorGUILayout.Space(-5);
|
|
EditorGUI.indentLevel = indent;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!skipProperty && property.name.Contains("_EmissionColor")) {
|
|
EditorGUILayout.Space(10);
|
|
EditorGUI.indentLevel -= 1;
|
|
EditorGUILayout.LabelField("Applied to All Maps", EditorStyles.boldLabel);
|
|
DrawTileOffset(editor, FindProperty("_BaseMap"));
|
|
}
|
|
|
|
if (!skipProperty && property.displayName.Contains("Smooth Normals")) {
|
|
SmoothNormalsUI();
|
|
}
|
|
|
|
EditorGUI.indentLevel = originalIntentLevel;
|
|
|
|
if (foldoutRemainingItems == 0 && latestFoldoutState) {
|
|
EndBox();
|
|
latestFoldoutState = false;
|
|
if (foldoutName.Contains("Advanced Lighting")) {
|
|
const string m =
|
|
"<b>Advanced Lighting</b> features have significantly different impact in real-time and baked lighting.";
|
|
EditorGUILayout.LabelField(m, _richHelpBoxStyle);
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.Space(10);
|
|
var renderingOptionsName = RenderingOptionsName;
|
|
FoldoutStates[renderingOptionsName] =
|
|
EditorGUILayout.Foldout(FoldoutStates[renderingOptionsName], renderingOptionsName, _foldoutStyle);
|
|
if (FoldoutStates[renderingOptionsName]) {
|
|
EditorGUI.indentLevel += 1;
|
|
BeginBox();
|
|
HandleUrpSettings(_target, editor);
|
|
EndBox();
|
|
}
|
|
|
|
if (_target.IsKeywordEnabled("_UNITYSHADOWMODE_NONE")) {
|
|
_target.EnableKeyword("_RECEIVE_SHADOWS_OFF");
|
|
} else {
|
|
_target.DisableKeyword("_RECEIVE_SHADOWS_OFF");
|
|
}
|
|
|
|
// Toggle the outline pass. Disabling by name `Outline` doesn't work.
|
|
// _target.SetShaderPassEnabled("SRPDEFAULTUNLIT", _target.IsKeywordEnabled("DR_OUTLINE_ON"));
|
|
|
|
/*
|
|
if (HasProperty("_MainTex")) {
|
|
TransferToBaseMap();
|
|
}
|
|
*/
|
|
}
|
|
|
|
private void BeginBox() {
|
|
EditorGUILayout.BeginVertical(_boxStyle);
|
|
EditorGUILayout.Space(3);
|
|
}
|
|
|
|
private void EndBox() {
|
|
EditorGUILayout.Space(3);
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private void SmoothNormalsUI() {
|
|
var keywordEnabled = _target.IsKeywordEnabled(OutlineSmoothNormalsKeyword);
|
|
_smoothNormalsEnabled ??= keywordEnabled;
|
|
|
|
if (keywordEnabled != _smoothNormalsEnabled) {
|
|
var mesh = GetMeshFromSelection();
|
|
if (mesh == null) {
|
|
return;
|
|
}
|
|
|
|
if (keywordEnabled) {
|
|
if (!MeshSmoother.HasSmoothNormals(mesh)) {
|
|
SmoothNormals(mesh);
|
|
}
|
|
}
|
|
} else if (keywordEnabled) {
|
|
var mesh = GetMeshFromSelection();
|
|
if (mesh == null) {
|
|
return;
|
|
}
|
|
|
|
if (!MeshSmoother.HasSmoothNormals(mesh)) {
|
|
EditorGUILayout.HelpBox(
|
|
"Mesh does not have smooth normals. Please use the 'Smooth Normals' button to generate them.",
|
|
MessageType.Warning);
|
|
|
|
if (mesh.subMeshCount > 1) {
|
|
EditorGUILayout.HelpBox(
|
|
"Mesh smoothing is not supported for meshes with multiple sub-meshes. " +
|
|
"Please combine sub-meshes into a single sub-mesh before smoothing.",
|
|
MessageType.Warning);
|
|
}
|
|
|
|
GUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button("Smooth Normals", GUILayout.ExpandWidth(false))) {
|
|
SmoothNormals(mesh);
|
|
}
|
|
|
|
GUILayout.EndHorizontal();
|
|
}
|
|
}
|
|
|
|
_smoothNormalsEnabled = keywordEnabled;
|
|
}
|
|
|
|
private void SmoothNormals(Mesh mesh) {
|
|
if (mesh.isReadable) {
|
|
MeshSmoother.SmoothNormals(mesh);
|
|
var path = AssetDatabase.GetAssetPath(mesh);
|
|
var pathSmoothed =
|
|
$"{Path.GetDirectoryName(path)}\\{Path.GetFileNameWithoutExtension(path)} Smooth Normals.asset";
|
|
|
|
var fileExists = File.Exists(pathSmoothed);
|
|
if (fileExists) {
|
|
var action1 = EditorUtility.DisplayDialogComplex("Smoothing normals",
|
|
"Asset already exists. Do you want to overwrite it?",
|
|
"Overwrite", "Cancel", "Open asset");
|
|
switch (action1) {
|
|
case 0: {
|
|
// Overwrite
|
|
AssetDatabase.DeleteAsset(pathSmoothed);
|
|
break;
|
|
}
|
|
case 1: {
|
|
// Cancel
|
|
_target.DisableKeyword(OutlineSmoothNormalsKeyword);
|
|
return;
|
|
}
|
|
case 2: {
|
|
// Open asset
|
|
AssetDatabase.OpenAsset(AssetDatabase.LoadAssetAtPath<Object>(pathSmoothed));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
var newMesh = Object.Instantiate(mesh);
|
|
try {
|
|
AssetDatabase.CreateAsset(newMesh, pathSmoothed);
|
|
Debug.Log($"<b>[Flat Kit]</b> Created asset <i>{pathSmoothed}</i>.");
|
|
}
|
|
catch (Exception) {
|
|
Debug.Log("<b>[Flat Kit]</b> Please ignore the error above. It is caused by a Unity bug.");
|
|
var action2 = EditorUtility.DisplayDialogComplex("Error",
|
|
$"Could not create asset at path '{pathSmoothed}'. Please check the Console for more information.",
|
|
"OK", "Show Console", "Save to 'Assets' folder");
|
|
switch (action2) {
|
|
case 0: {
|
|
// OK
|
|
_target.DisableKeyword(OutlineSmoothNormalsKeyword);
|
|
return;
|
|
}
|
|
case 1: {
|
|
// Show Console
|
|
EditorApplication.ExecuteMenuItem("Window/General/Console");
|
|
return;
|
|
}
|
|
case 2: {
|
|
// Save to Assets folder
|
|
pathSmoothed = $"Assets/{Path.GetFileName(pathSmoothed)}";
|
|
|
|
// Make sure the name is a valid file name.
|
|
pathSmoothed = Regex.Replace(pathSmoothed, @"[<>:""/\\|?*]", "_");
|
|
|
|
// Re-name if the file already exists.
|
|
if (File.Exists(pathSmoothed)) {
|
|
var baseName = Path.GetFileNameWithoutExtension(pathSmoothed);
|
|
var extension = Path.GetExtension(pathSmoothed);
|
|
var directory = Path.GetDirectoryName(pathSmoothed);
|
|
int copyIndex = 1;
|
|
string newPath;
|
|
do {
|
|
newPath = $"{directory}\\{baseName} ({copyIndex}){extension}";
|
|
copyIndex++;
|
|
|
|
if (copyIndex > 160) {
|
|
Debug.LogError(
|
|
"<b>[Flat Kit]</b> Could not find a valid file name to save the smoothed mesh.");
|
|
_target.DisableKeyword(OutlineSmoothNormalsKeyword);
|
|
|
|
// Show the user a message.
|
|
EditorUtility.DisplayDialog("Error",
|
|
"Could not find a valid file name to save the smoothed mesh. " +
|
|
"Please try renaming the mesh or saving it to a different folder.",
|
|
"OK");
|
|
|
|
return;
|
|
}
|
|
} while (File.Exists(newPath));
|
|
|
|
pathSmoothed = newPath;
|
|
}
|
|
|
|
try {
|
|
AssetDatabase.CreateAsset(newMesh, pathSmoothed);
|
|
Debug.Log($"<b>[Flat Kit]</b> Created asset <i>{pathSmoothed}</i>.");
|
|
}
|
|
catch (Exception e) {
|
|
Debug.LogError($"<b>[Flat Kit]</b> Could not create asset at path '{pathSmoothed}'. " +
|
|
$"Please check the Console for more information.\n{e}");
|
|
|
|
// Show the user a message to select a folder.
|
|
var m =
|
|
$"Could not create asset at path '{pathSmoothed}'. Please select the folder and file name.";
|
|
if (EditorUtility.DisplayDialog("Error", m, "Select Folder", "Cancel")) {
|
|
var userPath = EditorUtility.SaveFilePanelInProject("Save Smoothed Mesh",
|
|
Path.GetFileName(pathSmoothed), "asset",
|
|
"Please select a folder and file name to save the smoothed mesh.",
|
|
Path.GetDirectoryName(pathSmoothed));
|
|
if (!string.IsNullOrEmpty(userPath)) {
|
|
try {
|
|
AssetDatabase.CreateAsset(newMesh, userPath);
|
|
Debug.Log($"<b>[Flat Kit]</b> Created asset <i>{userPath}</i>.");
|
|
}
|
|
catch (Exception ex) {
|
|
Debug.LogError(
|
|
$"<b>[Flat Kit]</b> Could not create asset at path '{userPath}'. " +
|
|
$"Please check the Console for more information.\n{ex}");
|
|
_target.DisableKeyword(OutlineSmoothNormalsKeyword);
|
|
}
|
|
} else {
|
|
_target.DisableKeyword(OutlineSmoothNormalsKeyword);
|
|
}
|
|
} else {
|
|
_target.DisableKeyword(OutlineSmoothNormalsKeyword);
|
|
}
|
|
|
|
_target.DisableKeyword(OutlineSmoothNormalsKeyword);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
var newAsset = AssetDatabase.LoadAssetAtPath<Mesh>(pathSmoothed);
|
|
if (newAsset != null) {
|
|
SetMeshToSelection(newAsset);
|
|
}
|
|
} else {
|
|
var action = EditorUtility.DisplayDialogComplex("Smoothing normals",
|
|
"Mesh is not readable. Please enable 'Read/Write Enabled' in the mesh import settings.",
|
|
"Set readable", "Open import settings", "Cancel");
|
|
switch (action) {
|
|
case 0: {
|
|
// Set readable
|
|
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(mesh));
|
|
if (importer != null) {
|
|
var modelImporter = importer as ModelImporter;
|
|
if (modelImporter != null) {
|
|
modelImporter.isReadable = true;
|
|
modelImporter.SaveAndReimport();
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 1: {
|
|
// Open import settings
|
|
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(mesh));
|
|
if (importer != null) {
|
|
AssetDatabase.OpenAsset(importer);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 2: {
|
|
// Cancel
|
|
_target.DisableKeyword(OutlineSmoothNormalsKeyword);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Mesh GetMeshFromSelection() {
|
|
var go = Selection.activeGameObject;
|
|
if (go == null) {
|
|
EditorGUILayout.HelpBox(
|
|
"All meshes using smooth normals need processing. To process a mesh, select it in a scene and " +
|
|
"re-enable 'Smooth Normals' in the inspector.",
|
|
MessageType.Info);
|
|
return null;
|
|
}
|
|
|
|
var meshFilter = go.GetComponent<MeshFilter>();
|
|
if (meshFilter != null) {
|
|
return meshFilter.sharedMesh;
|
|
}
|
|
|
|
var skinnedMeshRenderer = go.GetComponent<SkinnedMeshRenderer>();
|
|
if (skinnedMeshRenderer != null) {
|
|
return skinnedMeshRenderer.sharedMesh;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static void SetMeshToSelection(Mesh mesh) {
|
|
var go = Selection.activeGameObject;
|
|
if (go == null) {
|
|
return;
|
|
}
|
|
|
|
var meshFilter = go.GetComponent<MeshFilter>();
|
|
if (meshFilter != null) {
|
|
meshFilter.sharedMesh = mesh;
|
|
return;
|
|
}
|
|
|
|
var skinnedMeshRenderer = go.GetComponent<SkinnedMeshRenderer>();
|
|
if (skinnedMeshRenderer != null) {
|
|
skinnedMeshRenderer.sharedMesh = mesh;
|
|
}
|
|
}
|
|
|
|
// Adapted from BaseShaderGUI.cs.
|
|
private void HandleUrpSettings(Material material, MaterialEditor editor) {
|
|
queueOffsetProp = FindProperty("_QueueOffset");
|
|
|
|
bool alphaClip = false;
|
|
if (material.HasProperty("_AlphaClip")) {
|
|
alphaClip = material.GetFloat("_AlphaClip") >= 0.5;
|
|
}
|
|
|
|
if (alphaClip) {
|
|
material.EnableKeyword("_ALPHATEST_ON");
|
|
} else {
|
|
material.DisableKeyword("_ALPHATEST_ON");
|
|
}
|
|
|
|
if (HasProperty("_Surface")) {
|
|
EditorGUI.BeginChangeCheck();
|
|
var surfaceProp = FindProperty("_Surface");
|
|
EditorGUI.showMixedValue = surfaceProp.hasMixedValue;
|
|
var surfaceType = (SurfaceType)surfaceProp.floatValue;
|
|
surfaceType = (SurfaceType)EditorGUILayout.EnumPopup("Surface Type", surfaceType);
|
|
if (EditorGUI.EndChangeCheck()) {
|
|
editor.RegisterPropertyChangeUndo("Surface Type");
|
|
surfaceProp.floatValue = (float)surfaceType;
|
|
}
|
|
|
|
if (surfaceType == SurfaceType.Opaque) {
|
|
if (alphaClip) {
|
|
material.renderQueue = (int)RenderQueue.AlphaTest;
|
|
material.SetOverrideTag("RenderType", "TransparentCutout");
|
|
} else {
|
|
material.renderQueue = (int)RenderQueue.Geometry;
|
|
material.SetOverrideTag("RenderType", "Opaque");
|
|
}
|
|
|
|
material.renderQueue += queueOffsetProp != null ? (int)queueOffsetProp.floatValue : 0;
|
|
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
|
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
|
|
material.SetInt("_ZWrite", 1);
|
|
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
|
material.SetShaderPassEnabled("ShadowCaster", true);
|
|
} else // Transparent
|
|
{
|
|
BlendMode blendMode = (BlendMode)material.GetFloat("_Blend");
|
|
|
|
// Specific Transparent Mode Settings
|
|
switch (blendMode) {
|
|
case BlendMode.Alpha:
|
|
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
|
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
|
break;
|
|
case BlendMode.Premultiply:
|
|
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
|
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
|
|
break;
|
|
case BlendMode.Additive:
|
|
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
|
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
|
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
|
break;
|
|
case BlendMode.Multiply:
|
|
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.DstColor);
|
|
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
|
|
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
|
material.EnableKeyword("_ALPHAMODULATE_ON");
|
|
break;
|
|
}
|
|
|
|
// General Transparent Material Settings
|
|
material.SetOverrideTag("RenderType", "Transparent");
|
|
material.SetInt("_ZWrite", 0);
|
|
material.renderQueue = (int)RenderQueue.Transparent;
|
|
material.renderQueue += queueOffsetProp != null ? (int)queueOffsetProp.floatValue : 0;
|
|
material.SetShaderPassEnabled("ShadowCaster", false);
|
|
}
|
|
|
|
// DR: draw popup.
|
|
if (surfaceType == SurfaceType.Transparent && HasProperty("_Blend")) {
|
|
EditorGUI.BeginChangeCheck();
|
|
var blendModeProperty = FindProperty("_Blend");
|
|
EditorGUI.showMixedValue = blendModeProperty.hasMixedValue;
|
|
var blendMode = (BlendMode)blendModeProperty.floatValue;
|
|
blendMode = (BlendMode)EditorGUILayout.EnumPopup("Blend Mode", blendMode);
|
|
if (EditorGUI.EndChangeCheck()) {
|
|
editor.RegisterPropertyChangeUndo("Blend Mode");
|
|
blendModeProperty.floatValue = (float)blendMode;
|
|
}
|
|
}
|
|
}
|
|
|
|
DrawQueueOffsetField();
|
|
|
|
// DR: draw popup.
|
|
if (HasProperty("_Cull")) {
|
|
EditorGUI.BeginChangeCheck();
|
|
var cullProp = FindProperty("_Cull");
|
|
EditorGUI.showMixedValue = cullProp.hasMixedValue;
|
|
var culling = (RenderFace)cullProp.floatValue;
|
|
culling = (RenderFace)EditorGUILayout.EnumPopup("Render Faces", culling);
|
|
if (EditorGUI.EndChangeCheck()) {
|
|
editor.RegisterPropertyChangeUndo("Render Faces");
|
|
cullProp.floatValue = (float)culling;
|
|
material.doubleSidedGI = (RenderFace)cullProp.floatValue != RenderFace.Front;
|
|
}
|
|
}
|
|
|
|
if (HasProperty("_AlphaClip")) {
|
|
EditorGUI.BeginChangeCheck();
|
|
var clipProp = FindProperty("_AlphaClip");
|
|
EditorGUI.showMixedValue = clipProp.hasMixedValue;
|
|
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
|
var alphaClipEnabled = EditorGUILayout.Toggle("Alpha Clipping", clipProp.floatValue == 1);
|
|
if (EditorGUI.EndChangeCheck())
|
|
clipProp.floatValue = alphaClipEnabled ? 1 : 0;
|
|
EditorGUI.showMixedValue = false;
|
|
|
|
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
|
if (clipProp.floatValue == 1 && HasProperty("_Cutoff")) {
|
|
var cutoffProp = FindProperty("_Cutoff");
|
|
editor.ShaderProperty(cutoffProp, "Threshold", 1);
|
|
}
|
|
}
|
|
|
|
editor.EnableInstancingField();
|
|
}
|
|
|
|
private void PromptTextureSave(MaterialEditor editor, Func<Texture2D> generate, string propertyName,
|
|
FilterMode filterMode) {
|
|
var rampTexture = generate();
|
|
var pngNameNoExtension = $"{editor.target.name}{propertyName}-ramp";
|
|
var fullPath =
|
|
EditorUtility.SaveFilePanel("Save Ramp Texture", "Assets", pngNameNoExtension, "png");
|
|
if (fullPath.Length > 0) {
|
|
SaveTextureAsPng(rampTexture, fullPath, filterMode);
|
|
var loadedTexture = LoadTexture(fullPath);
|
|
if (loadedTexture != null) {
|
|
_target.SetTexture(propertyName, loadedTexture);
|
|
} else {
|
|
Debug.LogWarning("Could not save the texture. Make sure the destination is in the Assets folder.");
|
|
}
|
|
}
|
|
}
|
|
|
|
private Texture2D GenerateStepTexture() {
|
|
int numSteps = _celShadingNumSteps;
|
|
var t2d = new Texture2D(numSteps + 1, /*height=*/1, TextureFormat.R8, /*mipChain=*/false) {
|
|
filterMode = FilterMode.Point,
|
|
wrapMode = TextureWrapMode.Clamp
|
|
};
|
|
for (int i = 0; i < numSteps + 1; i++) {
|
|
var color = Color.white * i / numSteps;
|
|
t2d.SetPixel(i, 0, color);
|
|
}
|
|
|
|
t2d.Apply();
|
|
return t2d;
|
|
}
|
|
|
|
private Texture2D GenerateCurveTexture() {
|
|
const int width = 256;
|
|
const int height = 1;
|
|
var lut = new Texture2D(width, height, TextureFormat.R8, /*mipChain=*/false) {
|
|
alphaIsTransparency = false,
|
|
wrapMode = TextureWrapMode.Clamp,
|
|
hideFlags = HideFlags.HideAndDontSave,
|
|
filterMode = FilterMode.Trilinear
|
|
};
|
|
|
|
for (float x = 0; x < width; x++) {
|
|
float value = _gradient.Evaluate(x / width);
|
|
for (float y = 0; y < height; y++) {
|
|
var color = Color.white * value;
|
|
lut.SetPixel(Mathf.CeilToInt(x), Mathf.CeilToInt(y), color);
|
|
}
|
|
}
|
|
|
|
return lut;
|
|
}
|
|
|
|
private static void SaveTextureAsPng(Texture2D texture, string fullPath, FilterMode filterMode) {
|
|
byte[] bytes = texture.EncodeToPNG();
|
|
File.WriteAllBytes(fullPath, bytes);
|
|
AssetDatabase.Refresh();
|
|
Debug.Log($"<b>[Flat Kit]</b> Texture saved as: {fullPath}");
|
|
|
|
string pathRelativeToAssets = ConvertFullPathToAssetPath(fullPath);
|
|
TextureImporter importer = (TextureImporter)AssetImporter.GetAtPath(pathRelativeToAssets);
|
|
if (importer != null) {
|
|
importer.filterMode = filterMode;
|
|
importer.textureType = TextureImporterType.SingleChannel;
|
|
importer.textureCompression = TextureImporterCompression.Uncompressed;
|
|
importer.mipmapEnabled = false;
|
|
var textureSettings = new TextureImporterPlatformSettings {
|
|
format = TextureImporterFormat.R8
|
|
};
|
|
importer.SetPlatformTextureSettings(textureSettings);
|
|
EditorUtility.SetDirty(importer);
|
|
importer.SaveAndReimport();
|
|
}
|
|
|
|
// 22b5f7ed-989d-49d1-90d9-c62d76c3081a
|
|
|
|
Debug.Assert(importer,
|
|
string.Format("[FlatKit] Could not change import settings of {0} [{1}]",
|
|
fullPath, pathRelativeToAssets));
|
|
}
|
|
|
|
private static Texture2D LoadTexture(string fullPath) {
|
|
string pathRelativeToAssets = ConvertFullPathToAssetPath(fullPath);
|
|
if (pathRelativeToAssets.Length == 0) {
|
|
return null;
|
|
}
|
|
|
|
var loadedTexture = AssetDatabase.LoadAssetAtPath(pathRelativeToAssets, typeof(Texture2D)) as Texture2D;
|
|
if (loadedTexture == null) {
|
|
Debug.LogError(string.Format("[FlatKit] Could not load texture from {0} [{1}].", fullPath,
|
|
pathRelativeToAssets));
|
|
return null;
|
|
}
|
|
|
|
loadedTexture.filterMode = FilterMode.Point;
|
|
loadedTexture.wrapMode = TextureWrapMode.Clamp;
|
|
|
|
return loadedTexture;
|
|
}
|
|
|
|
private static string ConvertFullPathToAssetPath(string fullPath) {
|
|
int count = (Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar).Length;
|
|
return fullPath.Remove(0, count);
|
|
}
|
|
|
|
private static string Rev(string a) {
|
|
StringBuilder b = new StringBuilder(a.Length);
|
|
int i = 0;
|
|
|
|
foreach (char c in a) {
|
|
b.Append((char)(c - "8<3F9="[i] % 32));
|
|
i = (i + 1) % 6;
|
|
}
|
|
|
|
return b.ToString();
|
|
}
|
|
|
|
#if !UNITY_2020_3_OR_NEWER
|
|
private new void DrawQueueOffsetField() {
|
|
GUIContent queueSlider = new GUIContent(" Priority",
|
|
"Determines the chronological rendering order for a Material. High values are rendered first.");
|
|
const int queueOffsetRange = 50;
|
|
MaterialProperty queueOffsetProp = FindProperty("_QueueOffset", _properties, false);
|
|
if (queueOffsetProp == null) return;
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUI.showMixedValue = queueOffsetProp.hasMixedValue;
|
|
var queue = EditorGUILayout.IntSlider(queueSlider, (int) queueOffsetProp.floatValue, -queueOffsetRange,
|
|
queueOffsetRange);
|
|
if (EditorGUI.EndChangeCheck())
|
|
queueOffsetProp.floatValue = queue;
|
|
EditorGUI.showMixedValue = false;
|
|
|
|
_target.renderQueue = (int)RenderQueue.Transparent + queue;
|
|
}
|
|
#endif
|
|
|
|
[UsedImplicitly]
|
|
private void TransferToBaseMap() {
|
|
var baseMapProperty = FindProperty("_MainTex");
|
|
var baseColorProperty = FindProperty("_Color");
|
|
_target.SetTexture("_BaseMap", baseMapProperty.textureValue);
|
|
var baseMapTiling = baseMapProperty.textureScaleAndOffset;
|
|
_target.SetTextureScale("_BaseMap", new Vector2(baseMapTiling.x, baseMapTiling.y));
|
|
_target.SetTextureOffset("_BaseMap", new Vector2(baseMapTiling.z, baseMapTiling.w));
|
|
_target.SetColor("_BaseColor", baseColorProperty.colorValue);
|
|
}
|
|
|
|
[UsedImplicitly]
|
|
private void TransferToMainTex() {
|
|
var baseMapProperty = FindProperty("_BaseMap");
|
|
var baseColorProperty = FindProperty("_BaseColor");
|
|
_target.SetTexture("_MainTex", baseMapProperty.textureValue);
|
|
var baseMapTiling = baseMapProperty.textureScaleAndOffset;
|
|
_target.SetTextureScale("_MainTex", new Vector2(baseMapTiling.x, baseMapTiling.y));
|
|
_target.SetTextureOffset("_MainTex", new Vector2(baseMapTiling.z, baseMapTiling.w));
|
|
_target.SetColor("_Color", baseColorProperty.colorValue);
|
|
}
|
|
} |