////////////////////////////////////////////////////// // MicroSplat // Copyright (c) Jason Booth ////////////////////////////////////////////////////// using System.Collections; using System.Collections.Generic; using UnityEngine; // layout // 0 perTex UV Scale and Offset // 1 PerTex Tint and interpolation contrast (a) // 2 Normal Strength, Smoothness, AO, Metallic // 3 Brightness, Contrast, porosity, foam // 4 DetailNoiseStrength, distance Noise Strength, Distance Resample, DisplaceMip // 5 geoTex, tint strength, normal strength, SmoothAOMetal strength // 6 displace, bias, offset, global emis strength // 7 Noise 0, Noise 1, Noise 2, Wind Particulate Strength // 8 Snow (R), Glitter (G), GeoHeightFilter(B), (A) GeoHeightFilterStrength // 9 Triplanar, trplanar contrast, stochastic enabled, (A) Saturation // 10 Texture Cluster Contrast, boost, Height Offset, Height Contrast // 11 Open // 12 Open // 13 Open // 14 AntiTileArray (R)Normal Str, (G) Detail Strength, (B) Distance Strength (A) DisplaceShaping // 15 Reserved for initialization marking // 16 UV Rotation, Triplanar rot, triplanar rot, (A) GlobalSpecularStrength // 17 FuzzyShading Core, Edge, Power, (A) MicroShadow // 18 SSS Tint, (A) SSS thickness // 19 (R) Curve Interpolator // 20 Trax Dig Depth, opacity, normal strength (A) Open // 21 Trax Tint (A) open // 22 Noise Height Frequency, Amplitude, NoiseUV Frequency, Amplitude // 23 ColorIntensity(R) // 24 Scatter UV Scale (RG), blendMode (B), Alpha Mult (A) // 25 Scatter Height Filter (RG), Scatter SlopeFilter (BA) // 26 Scatter Distance Fade (R), RimLight Power (G), ScatterWeight (B) // 27 Rim Light Color, intensity (A) // 28 Outline Color and intensity // 29 Slope Texture Angle (R), Contrast (G) namespace JBooth.MicroSplat { // because unity's HDR import pipeline is broke (assumes gamma, so breaks data in textures) public class MicroSplatPropData : ScriptableObject { public enum PerTexVector2 { SplatUVScale = 0, SplatUVOffset = 2, } public enum PerTexColor { Tint = 1 * 4, SSSRTint = 18 * 4, TraxTint = 21 * 4, RimLightColor = 27*4, OutlineColor = 28*4, } public enum PerTexFloat { InterpolationContrast = 1 * 4 + 1, NormalStrength = 2 * 4, Smoothness = 2 * 4 + 1, AO = 2 * 4 + 2, Metallic = 2 * 4 + 3, Brightness = 3 * 4, Contrast = 3 * 4 + 1, Porosity = 3 * 4 + 2, Foam = 3 * 4 + 3, DetailNoiseStrength = 4 * 4, DistanceNoiseStrength = 4 * 4 + 1, DistanceResample = 4 * 4 + 2, DisplacementMip = 4 * 4 + 3, GeoTexStrength = 5 * 4, GeoTintStrength = 5 * 4 + 1, GeoNormalStrength = 5 * 4 + 2, GlobalSmoothMetalAOStength = 5 * 4 + 3, DisplacementStength = 6 * 4, DisplacementBias = 6 * 4 + 1, DisplacementOffset = 6 * 4 + 2, GlobalEmisStength = 6 * 4 + 3, NoiseNormal0Strength = 7 * 4, NoiseNormal1Strength = 7 * 4 + 1, NoiseNormal2Strength = 7 * 4 + 2, WindParticulateStrength = 7 * 4 + 3, SnowAmount = 8 * 4, GlitterAmount = 8 * 4 + 1, GeoHeightFilter = 8 * 4 + 2, GeoHeightFilterStrength = 8 * 4 + 3, TriplanarMode = 9 * 4, TriplanarContrast = 9 * 4 + 1, StochatsicEnabled = 9 * 4 + 2, Saturation = 9 * 4 + 3, TextureClusterContrast = 10 * 4, TextureClusterBoost = 10 * 4 + 1, HeightOffset = 10 * 4 + 2, HeightContrast = 10 * 4 + 3, AntiTileArrayNormalStrength = 14 * 4, AntiTileArrayDetailStrength = 14 * 4 + 1, AntiTileArrayDistanceStrength = 14 * 4 + 2, DisplaceShaping = 14 * 4 + 3, UVRotation = 16 * 4, TriplanarRotationX = 16 * 4 + 1, TriplanarRotationY = 16 * 4 + 2, FuzzyShadingCore = 17 * 4, FuzzyShadingEdge = 17 * 4 + 1, FuzzyShadingPower = 17 * 4 + 2, SSSThickness = 18 * 4 + 3, CurveInterpolator = 19 * 4, TraxDigDepth = 20 * 4, TraxOpacity = 20 * 4 + 1, TraxNormalStrength = 20 * 4 + 2, NoiseHeightFrequency = 22 * 4, NoiseHeightAmplitude = 22 * 4 + 1, NoiseUVFrequency = 22 * 4 + 2, NoiseUVAmplitude = 22 * 4 + 3, ColorIntensity = 23 * 4, ScatterBlendMode = 24 * 4 + 2, ScatterAlphaMult = 24 * 4 + 3, ScatterDistanceFade = 26 * 4, RimPower = 26 * 4 + 1, RimIntensity = 27 * 4 + 3, OutlineIntensity = 28 * 4 + 3, SlopeTextureAngle = 29 * 4, SlopeTextureContrast = 29 * 4 + 1, } public const int sMaxAttributes = 32; [HideInInspector] public Color[] values = new Color[32 * sMaxAttributes]; [HideInInspector] public Texture2D propTex; [HideInInspector] public AnimationCurve geoCurve = AnimationCurve.Linear(0, 0.0f, 0, 0.0f); [HideInInspector] public Texture2D geoTex; [HideInInspector] public AnimationCurve geoSlopeFilter = AnimationCurve.Linear(0, 0.2f, 0.4f, 1.0f); [HideInInspector] public Texture2D geoSlopeTex; [HideInInspector] public AnimationCurve globalSlopeFilter = AnimationCurve.Linear(0, 0.2f, 0.4f, 1.0f); [HideInInspector] public Texture2D globalSlopeTex; void ClearPropTex() { #if UNITY_EDITOR Object[] objs = UnityEditor.AssetDatabase.LoadAllAssetRepresentationsAtPath(UnityEditor.AssetDatabase.GetAssetPath(this)); for (int i = 0; i < objs.Length; ++i) { if (objs[i] != null && objs[i].name == "PropTex") { DestroyImmediate(objs[i], true); } } propTex = null; #else if (propTex != null) { DestroyImmediate(propTex); } #endif } [HideInInspector] public int maxTextures = 32; // can be set to 256 or 32 public void RevisionData() { Debug.Assert(maxTextures == 32 || maxTextures == 256); // revision from 16 to 32 max textures if (values.Length == (16 * 16)) { ClearPropTex(); Color[] c = new Color[maxTextures * sMaxAttributes]; for (int x = 0; x < 16; ++x) { for (int y = 0; y < 16; ++y) { c[y * maxTextures + x] = values[y * sMaxAttributes + x]; } } values = c; #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif } else if (values.Length == (32 * 16)) { ClearPropTex(); Color[] c = new Color[maxTextures * sMaxAttributes]; for (int x = 0; x < 32; ++x) { for (int y = 0; y < 16; ++y) { c[y * maxTextures + x] = values[y * sMaxAttributes + x]; } } values = c; #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif } // handle switch from mega -> regular else if (values.Length == 32 * 256 && maxTextures == 32) { ClearPropTex(); Color[] c = new Color[maxTextures * sMaxAttributes]; for (int x = 0; x < 32; ++x) { for (int y = 0; y < 32; ++y) { c[y * maxTextures + x] = values[y * sMaxAttributes + x]; } } values = c; #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif } // switch from regular -> mega else if (values.Length == 32 * 32 && maxTextures == 256) { ClearPropTex(); Color [] c = new Color [maxTextures * sMaxAttributes]; for (int x = 0; x < 32; ++x) { for (int y = 0; y < 32; ++y) { c [y * maxTextures + x] = values [y * sMaxAttributes + x]; } } // copy first pixel into remaining. for (int x = 32; x < 256; ++x) { for (int y = 0; y < 32; ++y) { c[y * maxTextures + x] = values[y * sMaxAttributes]; } } values = c; #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty (this); #endif } } public Color GetValue(int x, int y) { RevisionData(); return values[y * maxTextures + x]; } public void SetValue(int x, int y, Color c) { RevisionData(); #if UNITY_EDITOR UnityEditor.Undo.RecordObject(this, "Changed Value"); #endif values[y * maxTextures + x] = c; #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif } public void SetValue(int x, int y, int channel, float value) { RevisionData(); #if UNITY_EDITOR UnityEditor.Undo.RecordObject(this, "Changed Value"); #endif int index = y * maxTextures + x; Color c = values[index]; c[channel] = value; values[index] = c; #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif } public void SetValue(int x, int y, int channel, Vector2 value) { RevisionData(); #if UNITY_EDITOR UnityEditor.Undo.RecordObject(this, "Changed Value"); #endif int index = y * maxTextures + x; Color c = values[index]; if (channel == 0) { c.r = value.x; c.g = value.y; } else { c.b = value.x; c.a = value.y; } values[index] = c; #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif } public void SetValue(int textureIndex, PerTexFloat channel, float value) { float e = ((float)channel) / 4.0f; int x = (int)e; int y = Mathf.RoundToInt((e - x) * 4); SetValue(textureIndex, x, y, value); } public void SetValue(int textureIndex, PerTexColor channel, Color value) { float e = ((float)channel) / 4.0f; int x = (int)e; SetValue(textureIndex, x, value); } public void SetValue(int textureIndex, PerTexVector2 channel, Vector2 value) { float e = ((float)channel) / 4.0f; int x = (int)e; int y = Mathf.RoundToInt((e - x) * 4); SetValue(textureIndex, x, y, value); } public Color[] GetAllValues(int textureIndex) { RevisionData(); Color[] c = new Color[sMaxAttributes]; for (int i = 0; i < sMaxAttributes; ++i) { c[i] = GetValue(textureIndex, i); } return c; } public void SetAllValues( int textureIndex, Color[] c) { RevisionData(); #if UNITY_EDITOR UnityEditor.Undo.RecordObject(this, "Changed Value"); #endif for (int i = 0; i < c.Length; ++i) { SetValue(textureIndex, i, c[i]); } #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif } public Texture2D GetTexture() { RevisionData(); if (propTex == null) { if (SystemInfo.SupportsTextureFormat(TextureFormat.RGBAFloat)) { propTex = new Texture2D(maxTextures, sMaxAttributes, TextureFormat.RGBAFloat, false, true); } else if (SystemInfo.SupportsTextureFormat(TextureFormat.RGBAHalf)) { propTex = new Texture2D(maxTextures, sMaxAttributes, TextureFormat.RGBAHalf, false, true); } else { Debug.LogError("Could not create RGBAFloat or RGBAHalf format textures, per texture properties will be clamped to 0-1 range, which will break things"); propTex = new Texture2D(maxTextures, sMaxAttributes, TextureFormat.RGBA32, false, true); } //tex.hideFlags = HideFlags.HideAndDontSave; propTex.wrapMode = TextureWrapMode.Clamp; propTex.filterMode = FilterMode.Point; propTex.hideFlags = HideFlags.None; #if UNITY_EDITOR propTex.name = "PropTex"; UnityEditor.AssetDatabase.AddObjectToAsset(propTex, this); UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(propTex)); #endif } propTex.SetPixels(values); propTex.Apply(); #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(propTex); #endif return propTex; } public Texture2D GetGeoCurve() { if (geoTex == null) { geoTex = new Texture2D(256, 1, TextureFormat.RHalf, false, true); #if UNITY_EDITOR geoTex.name = "geoTex"; UnityEditor.AssetDatabase.AddObjectToAsset(geoTex, this); UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(geoTex)); #endif } for (int i = 0; i < 256; ++i) { float v = geoCurve.Evaluate((float)i / 255.0f); geoTex.SetPixel(i, 0, new Color(v, v, v, v)); } geoTex.Apply(); #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(geoTex); #endif return geoTex; } public Texture2D GetGeoSlopeFilter() { if (geoSlopeTex == null) { geoSlopeTex = new Texture2D(256, 1, TextureFormat.Alpha8, false, true); #if UNITY_EDITOR geoSlopeTex.name = "geoSlopeTex"; UnityEditor.AssetDatabase.AddObjectToAsset(geoSlopeTex, this); UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(geoSlopeTex)); #endif } for (int i = 0; i < 256; ++i) { float v = geoSlopeFilter.Evaluate((float)i / 255.0f); geoSlopeTex.SetPixel(i, 0, new Color(v, v, v, v)); } geoSlopeTex.Apply(); #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(geoSlopeTex); #endif return geoSlopeTex; } public Texture2D GetGlobalSlopeFilter() { if (globalSlopeTex == null) { globalSlopeTex = new Texture2D(256, 1, TextureFormat.Alpha8, false, true); #if UNITY_EDITOR globalSlopeTex.name = "globalSlopeTex"; UnityEditor.AssetDatabase.AddObjectToAsset(globalSlopeTex, this); UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(globalSlopeTex)); #endif } for (int i = 0; i < 256; ++i) { float v = globalSlopeFilter.Evaluate((float)i / 255.0f); globalSlopeTex.SetPixel(i, 0, new Color(v, v, v, v)); } globalSlopeTex.Apply(); #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(globalSlopeTex); #endif return globalSlopeTex; } } }