关于翻录

一些论坛:

1、https://cs.rin.ru/forum/viewtopic.php?t=100672
这个帖子是用来查询和请求解压虚幻文件的AES KEY。
2、https://www.gildor.org/smf/index.php
这个是Umoder下兴者自己建立的论坛,主要是用来讨论 (寻找方法)解决一些有特殊加密的虚幻文件
3、https://forum.xentax.com/index.php
这是一个论坛,主要是解决各种非“通用”引擎的游戏文件
4、https://discord.gg/7yixAVhS
这是个Discord聊天群,主要用来解决一些有特殊加密的Unity3D的文件

 

 

        [Main(Rain, _USERAIN, off)] _RainGroup("下雨", float) = 0
        [Title(Rain, Puddle)]
        [Sub(Rain)] [HDR] _PuddleColor("Puddle Color", Color) = (1,1,1,1)
        [Sub(Rain)] _PuddleWeight("Puddle Weight", Range(0, 1)) = 0
        
        [Title(Rain, Ripple)]
        [Sub(Rain)] [NoScaleOffset] _RippleMaskMap("Ripple Mask Map", 2D) = "black" {}
        [Sub(Rain)] _RippleTilling("Ripple Tilling", float) = 0.05
        [Sub(Rain)] _RippleSpeed("Ripple Speed", float) = 1
        [Sub(Rain)] _RippleNormalStrength("Ripple Normal Strength", Range(0, 1)) = 1




    // 下雨
    #if _USERAIN
    // pubble
    float finalPuddleWeight = _PuddleWeight * puddleWeight;
    albedo = lerp(albedo, _PuddleColor.rgb, saturate(finalPuddleWeight + _PuddleColor.a - 1));
    roughnessRst = saturate(roughnessRst - 0.3);
    roughnessRst = lerp(roughnessRst, 0, finalPuddleWeight);
    finalNormalTS = lerp(finalNormalTS, float3(0,0,1), finalPuddleWeight);
    
    // ripple 1
    float rippleMask1;
    float3 rippleNormal1 = CalcRippleNormal(positionWS, 0, rippleMask1);
    finalNormalTS = lerp(finalNormalTS, rippleNormal1, rippleMask1 * finalPuddleWeight);
    
    // ripple 2
    float rippleMask2;
    float3 rippleNormal2 = CalcRippleNormal(positionWS, 0.5, rippleMask2);
    finalNormalTS = lerp(finalNormalTS, rippleNormal2, rippleMask2 * finalPuddleWeight);
    #endif




float3 CalcRippleNormal(float3 positionWS, float offset, out float rippleMask)
{
    half4 rippleMaskMap = SAMPLE_TEXTURE2D(_RippleMaskMap, sampler_RippleMaskMap, positionWS.xz * _RippleTilling + offset.xx);
    rippleMask = rippleMaskMap.r * saturate(1 - abs(rippleMaskMap.r + frac(_Time.y * _RippleSpeed + offset) - 1) * 10);
    
    float2 rippleNormalXY = rippleMaskMap.gb * 2 - 1;
    float rippleNormalZ = sqrt(1 - dot(rippleNormalXY, rippleNormalXY));
    float3 rippleNormal = normalize(float3(rippleNormalXY * _RippleNormalStrength, rippleNormalZ));
    return rippleNormal;
}
pubble and ripple

 

#ifndef CUSTOM_WEATHER_INCLUDED
#define CUSTOM_WEATHER_INCLUDED

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl"

uniform float _GlobalRoughnessOffset;
uniform float _SnowWeight;
uniform half4 _SnowColor;

uniform TEXTURE2D(_SnowMaskMap);
SAMPLER(sampler_SnowMaskMap);


float CalcSnowMask(float3 positionWS, float3 NModel, out float3 snowNormalTS, out float snowAO)
{
    // snow
    half NoUP = dot(NModel, float3(0,1,0));
    half snowMask = saturate(NoUP + 0.2) * _SnowWeight;
                
    //
    half4 snowMaskMap = SAMPLE_TEXTURE2D(_SnowMaskMap, sampler_SnowMaskMap, positionWS.xz * 0.02);
                
    // normal
    float2 snowNormalXY = snowMaskMap.rg * 2 - 1;
    snowNormalXY *= 0.2;
    float snowNormalZ = sqrt(1 - dot(snowNormalXY, snowNormalXY));
    snowNormalTS = normalize(float3(snowNormalXY, snowNormalZ));
                
    // ao
    snowAO = snowMaskMap.b;
                
    return snowMask;
}

void BlendSnow(inout SurfaceData surfaceData, float3 positionWS, float3 NModel)
{
    float3 snowNormalTS;
    float snowAO;
    half snowMask = CalcSnowMask(positionWS, NModel, snowNormalTS, snowAO);
    
    surfaceData.albedo = lerp(surfaceData.albedo, _SnowColor.rgb, snowMask);
    surfaceData.metallic = lerp(surfaceData.metallic, 0, snowMask);
    surfaceData.smoothness = lerp(surfaceData.smoothness, 0, snowMask);
    surfaceData.occlusion = lerp(surfaceData.occlusion, snowAO, snowMask);
    surfaceData.normalTS = lerp(surfaceData.normalTS, snowNormalTS, snowMask);
}



#endif

 

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using TEngine;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;

namespace GameLogic.TimeOfDay
{
    [ExecuteInEditMode]
    public partial class TimeOfDay : MonoBehaviour
    {
        enum EDayPhaseStage
        {
            Transition,
            ToStay,
            Stay,
        }

        [Header("日阶段"), SerializeField]
        private float m_DayPhaseTransitionDuration = 10f;

        [SerializeField]
        private TimeOfDay_DayPhase[] m_DayPhases;

        [Header("天气"), SerializeField]
        private TimeOfDay_WeatherInfo m_WeatherInfo;


        private int m_ShaderID_Cubemap = Shader.PropertyToID("_Cubemap");
        private int m_ShaderID_UseToggle = Shader.PropertyToID("_UseToggle");
        private int m_ShaderID_Cubemap2 = Shader.PropertyToID("_Cubemap2");
        private int m_ShaderID_Toggle = Shader.PropertyToID("_Toggle");
        private string m_Key_USETOGGLE_ON = "_USETOGGLE_ON";

        private Dictionary<EDayPhase, TimeOfDay_DayPhase> m_DicDayPhase;
        private EDayPhase m_CurrentDayPhase = EDayPhase.Day;
        private EWeatherType m_CurrentWeather = EWeatherType.SunnyDay;
        private EDayPhase m_PreviousDayPhase = EDayPhase.Day;
        private EWeatherType m_PreviousWeather = EWeatherType.SunnyDay;

        private Material m_MatSkybox;
        private Light m_MainLight;
        private TimeOfDay_Weather m_Weather;
        private EDayPhaseStage m_Stage;
        private float m_Timer;


        public static TimeOfDay Instance { get; private set; }
        private bool IsGameRunning => Application.isPlaying && ClientTimeHelper.HasSync;
        private TimeOfDay_DayPhase CurrentPoint => GetPoint(m_CurrentDayPhase);


        void Awake()
        {
            Instance = this;
            GameEvent.AddEventListener<int, int, int, int>(EventType.WorldStateSync, OnWorldStateSync);
        }

        void OnDestroy()
        {
            Instance = null;
            GameEvent.RemoveEventListener<int, int, int, int>(EventType.WorldStateSync, OnWorldStateSync);
        }

        private void OnEnable()
        {
            if (IsGameRunning)
            {
                // InitConfig();
                // SyncTime();
                m_CurrentDayPhase = (EDayPhase)DataCacheModule.Instance.WorldStateDataCache.DayPhaseId;
                m_CurrentWeather = (EWeatherType)DataCacheModule.Instance.WorldStateDataCache.WeatherId;
            }

            m_MatSkybox = RenderSettings.skybox;
            m_MainLight = RenderSettings.sun;

            if (m_DicDayPhase == null)
            {
                m_DicDayPhase = new();
                foreach (var p in m_DayPhases)
                {
                    m_DicDayPhase[p.m_DayPhase] = p;
                }
            }

            if (m_Weather == null)
            {
                m_Weather = new(this, m_WeatherInfo);
            }

            ClearWeather();
            BeginWeather(m_CurrentWeather);

            m_Stage = EDayPhaseStage.ToStay;
        }

        void Update()
        {
            if (IsGameRunning)
            {
                UpdateDayPhase();
                m_Weather.Update();
            }
#if UNITY_EDITOR
            else
            {
                UpdateDebugMode();
            }
#endif
        }

        TimeOfDay_DayPhase GetPoint(EDayPhase dayPhase)
        {
            m_DicDayPhase.TryGetValue(dayPhase, out var p);
            return p;
        }

        TimeOfDay_EnvData GetEnvData(EDayPhase dayPhase, EWeatherType weatherType)
        {
            TimeOfDay_DayPhase point = GetPoint(dayPhase);
            TimeOfDay_EnvData envData = null;
            if (weatherType == EWeatherType.LightRain || weatherType == EWeatherType.RainStorm)
            {
                var weatherEnvData = m_WeatherInfo.m_RainEnvData.FirstOrDefault(a => a.m_DayPhase == dayPhase);
                if (weatherEnvData != null)
                {
                    envData = weatherEnvData.m_EnvData;
                }
            }
            else if (weatherType == EWeatherType.Snow)
            {
                var weatherEnvData = m_WeatherInfo.m_SnowEnvData.FirstOrDefault(a => a.m_DayPhase == dayPhase);
                if (weatherEnvData != null)
                {
                    envData = weatherEnvData.m_EnvData;
                }
            }

            if (envData == null)
                envData = point.m_EnvData;

            return envData;
        }

        void UpdateStaying()
        {
            TimeOfDay_EnvData envData = GetEnvData(m_CurrentDayPhase, m_CurrentWeather);
            UpdateSkybox(envData.m_SkyCubeMap);

            m_MainLight.intensity = envData.m_LightIntensity;
            m_MainLight.color = envData.m_LightColor;

            ApplyGI(envData.m_GIInfo);
        }

        void UpdateToggleing(float weight)
        {
            TimeOfDay_EnvData fromEnv = GetEnvData(m_PreviousDayPhase, m_PreviousWeather);
            TimeOfDay_EnvData toEnv = GetEnvData(m_CurrentDayPhase, m_CurrentWeather);
            if (toEnv.m_SkyCubeMap != fromEnv.m_SkyCubeMap)
            {
                UpdateSkyboxToggleing(fromEnv.m_SkyCubeMap, toEnv.m_SkyCubeMap, weight);
            }
            else
            {
                UpdateSkybox(toEnv.m_SkyCubeMap);
            }

            m_MainLight.intensity = math.lerp(fromEnv.m_LightIntensity, toEnv.m_LightIntensity, weight);
            m_MainLight.color = Color.Lerp(fromEnv.m_LightColor, toEnv.m_LightColor, weight);
        }

        void UpdateDayPhase()
        {
            if (m_Stage == EDayPhaseStage.Transition)
            {
                m_Timer += Time.deltaTime;

                float weight = Mathf.Clamp01(m_Timer / m_DayPhaseTransitionDuration);
                UpdateToggleing(weight);
                if (weight >= 1)
                {
                    m_Stage = EDayPhaseStage.ToStay;
                }
            }
            else if (m_Stage == EDayPhaseStage.ToStay)
            {
                UpdateStaying();
                m_Stage = EDayPhaseStage.Stay;
            }
            else if (m_Stage == EDayPhaseStage.Stay)
            {
            }
        }


        #region sky

        void UpdateSkybox(Cubemap cubemap)
        {
            m_MatSkybox.SetTexture(m_ShaderID_Cubemap, cubemap);
            m_MatSkybox.SetFloat(m_ShaderID_UseToggle, 0);
            m_MatSkybox.DisableKeyword(m_Key_USETOGGLE_ON);
        }

        void UpdateSkyboxToggleing(Cubemap from, Cubemap to, float weight)
        {
            m_MatSkybox.SetTexture(m_ShaderID_Cubemap, from);
            m_MatSkybox.SetTexture(m_ShaderID_Cubemap2, to);
            m_MatSkybox.SetFloat(m_ShaderID_Toggle, weight);
            m_MatSkybox.SetFloat(m_ShaderID_UseToggle, 1);
            m_MatSkybox.EnableKeyword(m_Key_USETOGGLE_ON);
        }

        #endregion


        #region GI

        public static void ApplyGI(TimeOfDay_GIInfo giInfo)
        {
            if (giInfo == null)
                return;

            SetEnvironmentSettings(giInfo.m_EnvironmentSettings);

            SetReflectionProbes(giInfo);
        }

        private static void SetEnvironmentSettings(TimeOfDay_GIInfo.EnvironmentSettings es)
        {
            // 环境光设置
            RenderSettings.ambientMode = es.m_AmbientMode;
            RenderSettings.ambientSkyColor = es.m_SkyColor;
            RenderSettings.ambientEquatorColor = es.m_EquatorColor;
            RenderSettings.ambientGroundColor = es.m_GroundColor;

            // 天空盒和环境光强度
            RenderSettings.skybox = es.m_SkyboxMaterial;
            RenderSettings.ambientIntensity = es.m_EnvironmentIntensity;

            RenderSettings.subtractiveShadowColor = es.m_RealtimeColor;
            RenderSettings.ambientProbe = es.m_AmbientProbe;

            // reflection probe
            RenderSettings.defaultReflectionMode = DefaultReflectionMode.Custom;
            RenderSettings.reflectionBounces = es.m_ReflectionBounces;
            RenderSettings.reflectionIntensity = es.m_ReflectionIntensity;
            RenderSettings.defaultReflectionResolution = es.m_DefaultReflectionResolution;
            RenderSettings.customReflectionTexture = es.m_CustomReflectionTexture;

            // 雾效设置
            RenderSettings.fog = es.m_Fog;
            RenderSettings.fogMode = es.m_FogMode;
            RenderSettings.fogColor = es.m_FogColor;
            RenderSettings.fogDensity = es.m_FogDensity;
            RenderSettings.fogStartDistance = es.m_FogStartDistance;
            RenderSettings.fogEndDistance = es.m_FogEndDistance;

            // 其他设置
            RenderSettings.haloStrength = es.m_Halo;
            RenderSettings.flareFadeSpeed = es.m_FlareFadeSpeed;
            RenderSettings.flareStrength = es.m_FlareStrength;
        }

        static void SetReflectionProbes(TimeOfDay_GIInfo giInfo)
        {
            for (int i = 0; i < giInfo.m_ReflectionProbes.Length; i++)
            {
                var probeItem = giInfo.m_ReflectionProbes[i];
                GameObject goProbe = GameObject.Find(probeItem.m_Path);
                if (!goProbe)
                    continue;
                ReflectionProbe probe = goProbe.GetComponent<ReflectionProbe>();
                if (!probe)
                    continue;

                probe.customBakedTexture = probeItem.m_Cubemap;
            }
        }

        #endregion


        #region weather

        private void OnWorldStateSync(int oldDayPhaseId, int newDayPhaseId, int oldWeatherId, int newWeatherId)
        {
            m_PreviousDayPhase = (EDayPhase)oldDayPhaseId;
            m_CurrentDayPhase = (EDayPhase)newDayPhaseId;
            m_PreviousWeather = (EWeatherType)oldWeatherId;
            m_CurrentWeather = (EWeatherType)newWeatherId;
            m_Timer = 0;
            m_Stage = EDayPhaseStage.Transition;

            BeginWeather(m_CurrentWeather);
        }

        void BeginWeather(EWeatherType tarWeather)
        {
            m_Weather.BeginWeather(tarWeather);
        }

        [Button("清除天气")]
        private void ClearWeather()
        {
            m_Weather.Clear();
        }

        #endregion
    }
}
TimeOfDay
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace GameLogic.TimeOfDay
{
    [Serializable]
    public class TimeOfDay_DayPhase
    {
        public EDayPhase m_DayPhase;
        public float m_StartMinutes;
        public TimeOfDay_EnvData m_EnvData;
    }

    [Serializable]
    public class TimeOfDay_EnvData
    {
        public Cubemap m_SkyCubeMap;
        public Color m_LightColor;
        public float m_LightIntensity;
        public TimeOfDay_GIInfo m_GIInfo;
    }

    public enum EDayPhase
    {
        Day = 1,
        Duck,
        Night,
    }
}
View Code
//#define DEBUGMODE

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using TEngine;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;

namespace GameLogic.TimeOfDay
{
    public partial class TimeOfDay
    {
        void UpdateDebugMode()
        {
#if UNITY_EDITOR && DEBUGMODE
            UpdateDebugMode2();
#endif
        }

#if UNITY_EDITOR && DEBUGMODE
        [Header("昼夜变化"), SerializeField, Range(0.0f, 1440.0f)]
        private float m_TimelineMinutes = 360.0f;
        
        [SerializeField, ReadOnly]
        private string m_CurrentTimeString;

        [SerializeField, Tooltip("速度:每秒多少分钟")]
        private float m_TimelineSpeedMinutes = 1f;
        
        private float m_OldTimeline = -1;

        void UpdateDebugMode2()
        {
            if (Application.isPlaying)
            {
                m_TimelineMinutes += m_TimelineSpeedMinutes * Time.deltaTime;
                // Change to the next day
                if (m_TimelineMinutes > 1440)
                    m_TimelineMinutes = 0;

                EditorUpdate();

                m_Weather.Update();
            }
            else
            {
                if (m_OldTimeline != m_TimelineMinutes)
                {
                    m_OldTimeline = m_TimelineMinutes;
                    EditorUpdate();
                }
            }
        }

        EDayPhase GetCurrentDayPhase()
        {
            int lastIndex = m_DayPhases.Length - 1;
            for (int i = lastIndex; i >= 0; --i)
            {
                var p = m_DayPhases[i];
                if (m_TimelineMinutes > p.m_StartMinutes)
                {
                    return p.m_DayPhase;
                }
            }

            return EDayPhase.Night;
        }

        void EditorUpdate()
        {
            EDayPhase curDayPhase = GetCurrentDayPhase();
            if (m_CurrentDayPhase != curDayPhase)
            {
                m_PreviousDayPhase = m_CurrentDayPhase;
                m_CurrentDayPhase = curDayPhase;
            }

            m_CurrentDayPhase = curDayPhase;
            TimeOfDay_DayPhase curPoint = GetPoint(curDayPhase);
            TimeOfDay_DayPhase prePoint = m_CurrentDayPhase == EDayPhase.Day ? m_DayPhases.LastOrDefault() : GetPoint((EDayPhase)((int)m_CurrentDayPhase - 1));

            float toggleEndMinutes = curPoint.m_StartMinutes + m_DayPhaseTransitionDuration;
            if (m_TimelineMinutes > curPoint.m_StartMinutes && m_TimelineMinutes < toggleEndMinutes) //在切换中
            {
                float weight = Mathf.Clamp01((m_TimelineMinutes - curPoint.m_StartMinutes) / m_DayPhaseTransitionDuration);
                UpdateToggleing(weight);

                int curHours = GetCurrentHours(out int curMinutes);
                m_CurrentTimeString = $"{prePoint.m_DayPhase} -> {curPoint.m_DayPhase} {weight:p0} {curHours:d2}:{curMinutes:d2} {m_Weather.CurrentWeather}";
            }
            else
            {
                UpdateStaying();

                int curHours = GetCurrentHours(out int curMinutes);
                m_CurrentTimeString = $"{curPoint.m_DayPhase} {curHours:d2}:{curMinutes:d2} {m_Weather.CurrentWeather}";
            }
        }

        int GetCurrentHours(out int curMinutes)
        {
            int currentHours = Mathf.FloorToInt(m_TimelineMinutes / 60);
            curMinutes = Mathf.RoundToInt(m_TimelineMinutes) % 60;
            return currentHours;
        }

        [Button("天晴")]
        private void BeginSunnyDay()
        {
            BeginWeather(EWeatherType.SunnyDay);
        }

        [Button("下小雨")]
        private void BeginLightRain()
        {
            BeginWeather(EWeatherType.LightRain);
        }

        [Button("下暴雨")]
        private void BeginRainStorm()
        {
            BeginWeather(EWeatherType.RainStorm);
        }

        [Button("下雪")]
        private void BeginSnow()
        {
            BeginWeather(EWeatherType.Snow);
        }

        [Button("彩虹")]
        private void BeginCaiHong()
        {
            BeginWeather(EWeatherType.CaiHong);
        }

        [Button("极光")]
        private void BeginJiGuang()
        {
            BeginWeather(EWeatherType.JiGuang);
        }

        [Button("霓虹")]
        private void BeginLiHong()
        {
            BeginWeather(EWeatherType.LiHong);
        }

        [Button("流星雨")]
        private void BeginLiuXinYu()
        {
            BeginWeather(EWeatherType.LiuXinYu);
        }
        
        /*
        void InitConfig()
        {
            if (!IsGameRunning)
                return;

            for (int i = 0; i < m_DayPhases.Length; i++)
            {
                var p = m_DayPhases[i];
                ConfigSystem.Instance.Tables.TbDayPhase.DataMap.TryGetValue((int)p.m_DayPhase, out var config);
                if (config != null)
                {
                    p.m_StartMinutes = config.StartTime;
                }
                else
                {
                    Debug.LogError($"Cann't find config, day phase:{p.m_DayPhase}, table:DayPhase");
                }
            }
        }

        [Button("同步至服务器时间")]
        void SyncTime()
        {
            if (!IsGameRunning)
                return;

            m_TimelineMinutes = HomeUI.GetDayMinutes();
        }
        */
#endif
    }
}
View Code
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

namespace GameLogic.TimeOfDay
{
    public class TimeOfDay_GIInfo : ScriptableObject
    {
        [Serializable]
        public class EnvironmentSettings
        {
            // Environment 标签设置
            [Header("Environment")]
            public Material m_SkyboxMaterial;

            public AmbientMode m_AmbientMode;

            public Color m_SkyColor = Color.gray;
            public Color m_EquatorColor = Color.gray;
            public Color m_GroundColor = Color.gray;
            public float m_EnvironmentIntensity = 1.0f;
            public Color m_RealtimeColor = Color.white;

            public SphericalHarmonicsL2 m_AmbientProbe;

            // reflection
            [Header("Reflection")]
            public int m_ReflectionBounces;
            public float m_ReflectionIntensity;
            public int m_DefaultReflectionResolution;
            public Texture m_CustomReflectionTexture;

            // Fog 设置
            [Header("Fog Settings")]
            public bool m_Fog = false;

            public FogMode m_FogMode = FogMode.Exponential;
            public Color m_FogColor = Color.gray;
            public float m_FogDensity = 0.01f;
            public float m_FogStartDistance = 0.0f;
            public float m_FogEndDistance = 300.0f;

            // Halo 和 Flare 设置
            [Header("Other Settings")]
            public float m_Halo = 0;

            public float m_FlareFadeSpeed = 3.0f;
            public float m_FlareStrength = 1.0f;
        }

        [Serializable]
        public class ReflectionProbeItem
        {
            public string m_Path;
            public Texture m_Cubemap;
        }

        public EnvironmentSettings m_EnvironmentSettings;
        public ReflectionProbeItem[] m_ReflectionProbes;
    }
}
View Code
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

namespace GameLogic.TimeOfDay
{
    public class TimeOfDay_Weather
    {
        enum EStage
        {
            UnStart,
            Begining,
            Staying,
            Fading,
        }

        private int m_ShaderID_GlobalRoughnessOffset = Shader.PropertyToID("_GlobalRoughnessOffset");
        private int m_ShaderID_PuddleWeight = Shader.PropertyToID("_PuddleWeight");
        private int m_ShaderID_RippleSpeed = Shader.PropertyToID("_RippleSpeed");
        private int m_ShaderID_RippleNormalStrength = Shader.PropertyToID("_RippleNormalStrength");
        private int m_ShaderID_SnowWeight = Shader.PropertyToID("_SnowWeight");
        private int m_ShaderID_SnowMaskMap = Shader.PropertyToID("_SnowMaskMap");
        private int m_ShaderID_SnowColor = Shader.PropertyToID("_SnowColor");
        private GlobalKeyword m_Key_IsRaining, m_Key_UseSnowOn;


        private EStage m_Stage;
        private TimeOfDay m_TimeOfDay;
        private TimeOfDay_WeatherInfo m_Info;
        private float m_Timer;
        private float m_tarRainVFXCount, m_tarRippleNormalStrength, m_tarWetRoughnessOffset, m_tarPuddleWeight;


        public EWeatherType CurrentWeather { get; private set; }
        public EWeatherType PreviousWeather { get; private set; }


        public TimeOfDay_Weather(TimeOfDay timeOfDay, TimeOfDay_WeatherInfo info)
        {
            m_TimeOfDay = timeOfDay;
            m_Info = info;

            m_Key_IsRaining = GlobalKeyword.Create("_IS_RAINING");
            m_Key_UseSnowOn = GlobalKeyword.Create("_USE_SNOW_ON");
        }

        bool IsRain(EWeatherType weatherType)
        {
            return weatherType == EWeatherType.LightRain || weatherType == EWeatherType.RainStorm;
        }

        public void Update()
        {
            if (IsRain(CurrentWeather) || (m_Stage == EStage.Fading && IsRain(PreviousWeather)))
            {
                UpdateRain();
            }

            if (CurrentWeather == EWeatherType.Snow || (m_Stage == EStage.Fading && PreviousWeather == EWeatherType.Snow))
            {
                UpdateSnow();
            }
        }

        #region 下雨

        void UpdateRain()
        {
            if (m_Stage == EStage.UnStart)
            {
            }
            else if (m_Stage == EStage.Begining)
            {
                UpdateRainBegining();
            }
            else if (m_Stage == EStage.Staying)
            {
            }
            else if (m_Stage == EStage.Fading)
            {
                UpdateRainFading();
            }
            else
            {
                Debug.LogError("Unknown stage: " + m_Stage);
            }

            m_Info.m_RainVFX.transform.position = GetRainVFXPosition(out var rot);
            m_Info.m_RainVFX.transform.rotation = rot;
        }

        Vector3 GetRainVFXPosition(out Quaternion rot)
        {
            var mainCam = Camera.main;
            if (mainCam == null)
            {
                rot = Quaternion.identity;
                return Vector3.zero;
            }

            rot = Quaternion.Euler(0, mainCam.transform.rotation.eulerAngles.y, 0);

            Vector3 forward = mainCam.transform.transform.forward;
            forward.y = 0;
            forward.Normalize();
            Vector3 p = mainCam.transform.position + Vector3.up * 1 + forward * 10;
            return p;
        }


        void UpdateRainBegining()
        {
            m_Timer += Time.deltaTime;
            m_Info.m_RainProgress = Mathf.Clamp01(m_Timer / 4.2f);

            // rain vfx
            if (m_Timer <= 2.1f)
            {
                float rainVFXCtrlWeight = Mathf.Clamp01(m_Timer / 2f);
                var emission = m_Info.m_RainVFX.emission;
                emission.rateOverTimeMultiplier = rainVFXCtrlWeight * m_tarRainVFXCount;
                m_Info.m_RainVFX.Play();

                Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, m_tarRippleNormalStrength * rainVFXCtrlWeight);
                Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, rainVFXCtrlWeight * m_tarWetRoughnessOffset);
            }

            // puddle
            if (m_Timer >= 1f && m_Timer < 4.1f)
            {
                float puddleWeight = Mathf.Clamp01((m_Timer - 1) / 3f);
                Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, puddleWeight * m_tarPuddleWeight);
            }

            if (m_Timer > 4.2f)
            {
                m_Stage = EStage.Staying;
                Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, m_tarRippleNormalStrength);
                Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, m_tarWetRoughnessOffset);
                Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, m_tarPuddleWeight);
            }
        }

        void UpdateRainFading()
        {
            m_Timer += Time.deltaTime;
            m_Info.m_RainProgress = 1 - Mathf.Clamp01(m_Timer / 2f);

            // rain vfx
            if (m_Timer <= 1.1f)
            {
                float rainVFXCtrlWeight = 1 - Mathf.Clamp01(m_Timer / 1f);
                var emission = m_Info.m_RainVFX.emission;
                emission.rateOverTimeMultiplier = rainVFXCtrlWeight * m_tarRainVFXCount;
                m_Info.m_RainVFX.Play();

                Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, m_tarRippleNormalStrength * rainVFXCtrlWeight);
            }
            else if (m_Info.m_RainVFX.gameObject.activeSelf)
            {
                m_Info.m_RainVFX.Stop();
                m_Info.m_RainVFX.gameObject.SetActive(false);
            }

            // global wet
            if (m_Timer >= 1 && m_Timer <= 3.1f)
            {
                float globalWetWeight = 1 - Mathf.Clamp01((m_Timer - 1) / 2f);
                Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, globalWetWeight * m_tarWetRoughnessOffset);
            }

            // puddle
            if (m_Timer >= 3f && m_Timer <= 8.1f)
            {
                float puddleWeight = 1 - Mathf.Clamp01((m_Timer - 3) / 5f);
                Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, puddleWeight * m_tarPuddleWeight);
            }

            if (m_Timer > 8.2f)
            {
                m_Stage = EStage.UnStart;
                m_Info.m_RainProgress = 0;
                m_Info.m_RainVFX.Stop();
                Shader.SetKeyword(m_Key_IsRaining, false);
                Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, 0);
                Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, 0);
                Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, 0);
            }
        }

        #endregion

        #region 下雪

        void UpdateSnow()
        {
            if (m_Stage == EStage.UnStart)
            {
            }
            else if (m_Stage == EStage.Begining)
            {
                UpdateSnowBegining();
            }
            else if (m_Stage == EStage.Staying)
            {
            }
            else if (m_Stage == EStage.Fading)
            {
                UpdateSnowFading();
            }
            else
            {
                Debug.LogError("Unknown stage: " + m_Stage);
            }
        }

        void UpdateSnowBegining()
        {
            m_Timer += Time.deltaTime;
            m_Info.m_SnowProgress = Mathf.Clamp01(m_Timer / 15.2f);

            if (m_Timer >= 5 && m_Timer < 15.1f)
            {
                float snowMaskWeight = Mathf.Clamp01((m_Timer - 5) / 10f);
                Shader.SetGlobalFloat(m_ShaderID_SnowWeight, snowMaskWeight);
            }

            if (m_Timer > 15.2f)
            {
                m_Stage = EStage.Staying;
                Shader.SetGlobalFloat(m_ShaderID_SnowWeight, 1);
            }
        }

        void UpdateSnowFading()
        {
            m_Timer += Time.deltaTime;
            m_Info.m_SnowProgress = 1 - Mathf.Clamp01(m_Timer / 10.2f);

            // snow vfx
            m_Info.m_SnowVFX.Stop();

            if (m_Timer >= 5 && m_Timer < 10.1f)
            {
                float snowMaskWeight = 1 - Mathf.Clamp01((m_Timer - 2) / 5f);
                Shader.SetGlobalFloat(m_ShaderID_SnowWeight, snowMaskWeight);
            }

            if (m_Timer > 10.2f)
            {
                m_Stage = EStage.UnStart;
                m_Info.m_SnowProgress = 0;
                m_Info.m_SnowVFX.Stop();
                m_Info.m_SnowVFX.gameObject.SetActive(false);
                Shader.SetKeyword(m_Key_UseSnowOn, false);
                Shader.SetGlobalFloat(m_ShaderID_SnowWeight, 0);
            }
        }

        #endregion


        public void BeginWeather(EWeatherType tarWeather)
        {
            if (CurrentWeather == tarWeather)
            {
                return;
            }
            
            m_Timer = 0;

            PreviousWeather = CurrentWeather;
            CurrentWeather = tarWeather;

            if (tarWeather == EWeatherType.LightRain || tarWeather == EWeatherType.RainStorm)
            {
                Clear();

                m_Stage = EStage.Begining;

                bool isRainStorm = tarWeather == EWeatherType.RainStorm;
                m_tarRainVFXCount = isRainStorm ? 2000 : 500f;
                float tarRippleSpeed = isRainStorm ? 2 : 1;
                m_tarRippleNormalStrength = 1;
                m_tarWetRoughnessOffset = isRainStorm ? m_Info.m_RainGlobalRoughnessOffset : m_Info.m_RainGlobalRoughnessOffset * 0.5f;
                m_tarPuddleWeight = isRainStorm ? 1 : 0.5f;

                m_Info.m_RainVFX.gameObject.SetActive(false);
                m_Info.m_RainVFX.gameObject.SetActive(true);
                Shader.SetKeyword(m_Key_IsRaining, true);
                Shader.SetGlobalFloat(m_ShaderID_RippleSpeed, tarRippleSpeed);
            }
            else if (tarWeather == EWeatherType.Snow)
            {
                Clear();

                m_Stage = EStage.Begining;
                m_Info.m_SnowVFX.gameObject.SetActive(false);
                m_Info.m_SnowVFX.gameObject.SetActive(true);
                var emission = m_Info.m_SnowVFX.emission;
                emission.rateOverTimeMultiplier = 500;
                m_Info.m_SnowVFX.Play();

                Shader.SetKeyword(m_Key_UseSnowOn, true);
                Shader.SetGlobalTexture(m_ShaderID_SnowMaskMap, m_Info.m_SnowMaskMap);
                Shader.SetGlobalColor(m_ShaderID_SnowColor, m_Info.m_SnowColor);
            }
            else if (tarWeather == EWeatherType.CaiHong)
            {
                if (m_Info.m_CaiHongVFX)
                    m_Info.m_CaiHongVFX.SetActive(true);
            }
            else if (tarWeather == EWeatherType.JiGuang)
            {
                if (m_Info.m_JiGuangVFX)
                    m_Info.m_JiGuangVFX.SetActive(true);
            }
            else if (tarWeather == EWeatherType.LiHong)
            {
                if (m_Info.m_LiHongVFX)
                    m_Info.m_LiHongVFX.SetActive(true);
            }
            else if (tarWeather == EWeatherType.LiuXinYu)
            {
                if (m_Info.m_LiuXinYuVFX)
                    m_Info.m_LiuXinYuVFX.SetActive(true);
            }
            else if (tarWeather == EWeatherType.SunnyDay)
            {
                if (PreviousWeather == EWeatherType.Snow || IsRain(PreviousWeather))
                {
                    m_Stage = EStage.Fading;
                }
                else
                {
                    if (m_Info.m_CaiHongVFX)
                        m_Info.m_CaiHongVFX.SetActive(false);
                    if (m_Info.m_JiGuangVFX)
                        m_Info.m_JiGuangVFX.SetActive(false);
                    if (m_Info.m_LiHongVFX)
                        m_Info.m_LiHongVFX.SetActive(false);
                    if (m_Info.m_LiuXinYuVFX)
                        m_Info.m_LiuXinYuVFX.SetActive(false);
                }
            }
        }

        public void Clear()
        {
            m_Info.m_RainProgress = 0;
            m_Info.m_RainVFX.Stop();
            Shader.SetKeyword(m_Key_IsRaining, false);
            Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, 0);
            Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, 0);
            Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, 0);

            m_Info.m_SnowProgress = 0;
            m_Info.m_SnowVFX.Stop();
            Shader.SetKeyword(m_Key_UseSnowOn, false);
            Shader.SetGlobalFloat(m_ShaderID_SnowWeight, 0);
        }
    }
}
View Code
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

namespace GameLogic.TimeOfDay
{
    [Serializable]
    public class TimeOfDay_WeatherInfo
    {
        [Header("下雨"), Range(0, 1)]
        public float m_RainProgress;

        public ParticleSystem m_RainVFX;

        public float m_RainGlobalRoughnessOffset = -0.3f;
        public WeatherEnvData[] m_RainEnvData;

        [Header("下雪"), Range(0, 1)]
        public float m_SnowProgress;

        public ParticleSystem m_SnowVFX;
        public WeatherEnvData[] m_SnowEnvData;
        public Texture m_SnowMaskMap;
        public Color m_SnowColor;

        [Header("彩虹")]
        public GameObject m_CaiHongVFX;

        [Header("极光")]
        public GameObject m_JiGuangVFX;

        [Header("霓虹")]
        public GameObject m_LiHongVFX;

        [Header("流星雨")]
        public GameObject m_LiuXinYuVFX;
    }

    [Serializable]
    public class WeatherEnvData
    {
        public EDayPhase m_DayPhase;
        public TimeOfDay_EnvData m_EnvData;
    }

    public enum EWeatherType
    {
        SunnyDay = 1,
        LightRain,
        RainStorm,
        Snow,
        CaiHong,
        JiGuang,
        LiHong,
        LiuXinYu,
    }
}
View Code

 

 

using System.Collections;
using System.Collections.Generic;
using GameLogic;
using GameLogic.TimeOfDay;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace ArtTools
{
    public class TimeOfDayGIGeneratorWnd : EditorWindow
    {
        private string m_GIName = "GI_New";
        private TimeOfDay_GIInfo m_TimeOfDayGIInfo;

        [MenuItem("Omnee美术/昼夜变化/保存GI 配置")]
        private static void CreateWnd()
        {
            EditorWindow.CreateWindow<TimeOfDayGIGeneratorWnd>("保存昼夜变化GI配置");
        }

        private void OnGUI()
        {
            m_TimeOfDayGIInfo = EditorGUILayout.ObjectField(m_TimeOfDayGIInfo, typeof(TimeOfDay_GIInfo), false) as TimeOfDay_GIInfo;
            if (m_TimeOfDayGIInfo == null)
            {
                m_GIName = EditorGUILayout.TextField("新GI配置名称", m_GIName);
            }
            
            GUILayout.Space(20);

            if (m_TimeOfDayGIInfo)
            {
                if (GUILayout.Button("替换GI数据"))
                {
                    TimeOfDayGIGenerator.SaveGIConfig(m_TimeOfDayGIInfo);
                    EditorUtility.DisplayDialog("", "完成操作", "OK");
                }
                GUILayout.Space(20);
                if (GUILayout.Button("编辑器应用此GI"))
                {
                    TimeOfDayGIGenerator.ApplyGIConfig(m_TimeOfDayGIInfo);
                    EditorUtility.DisplayDialog("", "完成操作", "OK");
                }
            }
            else
            {
                if (GUILayout.Button("生成新GI配置"))
                {
                    m_TimeOfDayGIInfo = TimeOfDayGIGenerator.CreateNewGIConfig(m_GIName);
                    EditorUtility.DisplayDialog("", "完成操作", "OK");
                }
            }
        }
    }
}
View Code
using System.Collections;
using System.Collections.Generic;
using GameLogic;
using GameLogic.TimeOfDay;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

namespace ArtTools
{
    public static class TimeOfDayGIGenerator
    {
        public static void ApplyGIConfig(TimeOfDay_GIInfo config)
        {
            TimeOfDay.ApplyGI(config);

            RenderSettings.skybox.SetFloat("_UseToggle", 0);
            RenderSettings.skybox.DisableKeyword("_USETOGGLE_ON");
            RenderSettings.skybox.SetTexture("_Cubemap", config.m_EnvironmentSettings.m_CustomReflectionTexture);

            RenderSettings.defaultReflectionMode = DefaultReflectionMode.Skybox;
            RenderSettings.customReflectionTexture = null;
        }

        public static TimeOfDay_GIInfo CreateNewGIConfig(string fileName)
        {
            Scene curScene = SceneManager.GetActiveScene();

            string sceneFolder = curScene.path.Replace(".unity", "");
            string newFolderName = "TimeOfDay";
            string newFolder = $"{sceneFolder}/{newFolderName}";
            if (!AssetDatabase.IsValidFolder(newFolder))
            {
                AssetDatabase.CreateFolder(sceneFolder, newFolderName);
            }

            string configPath = $"{newFolder}/{fileName}.asset";
            TimeOfDay_GIInfo config = AssetDatabase.LoadAssetAtPath<TimeOfDay_GIInfo>(configPath);
            if (config == null)
            {
                config = ScriptableObject.CreateInstance<TimeOfDay_GIInfo>();
                AssetDatabase.CreateAsset(config, configPath);
            }

            return SaveGIConfig(config);
        }

        public static TimeOfDay_GIInfo SaveGIConfig(TimeOfDay_GIInfo config)
        {
            Lightmapping.Bake();
            
            SaveEnvironmentSettings(config.m_EnvironmentSettings);
            SaveReflectionProbeSettings(config);

            EditorUtility.SetDirty(config);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();

            Debug.Log("SaveGIConfig done!!!");

            return config;
        }


        private static void SaveEnvironmentSettings(TimeOfDay_GIInfo.EnvironmentSettings es)
        {
            // 环境光设置
            es.m_AmbientMode = RenderSettings.ambientMode;
            es.m_SkyColor = RenderSettings.ambientSkyColor;
            es.m_EquatorColor = RenderSettings.ambientEquatorColor;
            es.m_GroundColor = RenderSettings.ambientGroundColor;

            // 天空盒和环境光强度
            es.m_SkyboxMaterial = RenderSettings.skybox;
            es.m_EnvironmentIntensity = RenderSettings.ambientIntensity;

            es.m_RealtimeColor = RenderSettings.subtractiveShadowColor;
            es.m_AmbientProbe = RenderSettings.ambientProbe;

            // reflection probe
            es.m_ReflectionBounces = RenderSettings.reflectionBounces;
            es.m_ReflectionIntensity = RenderSettings.reflectionIntensity;
            es.m_DefaultReflectionResolution = RenderSettings.defaultReflectionResolution;
            es.m_CustomReflectionTexture = RenderSettings.skybox.GetTexture("_Cubemap");

            // 雾效设置
            es.m_Fog = RenderSettings.fog;
            es.m_FogMode = RenderSettings.fogMode;
            es.m_FogColor = RenderSettings.fogColor;
            es.m_FogDensity = RenderSettings.fogDensity;
            es.m_FogStartDistance = RenderSettings.fogStartDistance;
            es.m_FogEndDistance = RenderSettings.fogEndDistance;

            // 其他设置
            es.m_Halo = RenderSettings.haloStrength;
            es.m_FlareFadeSpeed = RenderSettings.flareFadeSpeed;
            es.m_FlareStrength = RenderSettings.flareStrength;
        }

        static string GetGameObjectPath(GameObject go)
        {
            string path = go.name;
            Transform p = go.transform.parent;
            while (p)
            {
                path = $"{p.name}/{path}";
                p = p.parent;
            }

            return path;
        }

        private static void SaveReflectionProbeSettings(TimeOfDay_GIInfo es)
        {
            List<TimeOfDay_GIInfo.ReflectionProbeItem> list = new();
            ReflectionProbe[] probes = GameObject.FindObjectsByType<ReflectionProbe>(FindObjectsSortMode.None);
            foreach (ReflectionProbe probe in probes)
            {
                TimeOfDay_GIInfo.ReflectionProbeItem item = new()
                {
                    m_Path = GetGameObjectPath(probe.gameObject),
                    m_Cubemap = probe.customBakedTexture,
                };
                list.Add(item);
            }

            es.m_ReflectionProbes = list.ToArray();
        }
    }
}

 

 

fur

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UObj = UnityEngine.Object;

namespace ArtTools
{
    public abstract class FurMeshGeneratorBase
    {
        public const int MAX_VERTICES_FOR_16BITS_MESH = 50000; //NOT change this

        protected UObj m_SelectedObj;
        protected int m_LayerCount = 20;
        protected bool m_IsOptimize;

        private Mesh m_FurMesh;


        public abstract string GetFurMeshSavePath();
        public abstract string GenerateFurMesh(string furMeshSavePath);


        public static FurMeshGeneratorBase Create(UObj selectedObj, int layerCount, bool isOptimize)
        {
            if (selectedObj is SkinnedMeshRenderer)
            {
                return new FurSMRGenerator(selectedObj, layerCount, isOptimize);
            }
            else if (selectedObj is MeshRenderer)
            {
                return new FurMRGenerator(selectedObj, layerCount, isOptimize);
            }
            else
            {
                Debug.LogError($"Unknown renderer:{selectedObj.name}");
                return null;
            }
        }


        public FurMeshGeneratorBase(UObj selectedObj, int layerCount, bool isOptimize)
        {
            m_SelectedObj = selectedObj;
            m_LayerCount = layerCount;
            m_IsOptimize = isOptimize;
        }

        protected void SaveMesh(Mesh newMesh, string newPath)
        {
            string saveFolder = Path.GetDirectoryName(newPath);
            if (!AssetDatabase.IsValidFolder(saveFolder))
            {
                string parentFolder = Path.GetDirectoryName(saveFolder);
                string folderName = Path.GetFileNameWithoutExtension(saveFolder);
                AssetDatabase.CreateFolder(parentFolder, folderName);
            }

            AssetDatabase.CreateAsset(newMesh, newPath);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            m_FurMesh = AssetDatabase.LoadAssetAtPath<Mesh>(newPath);
        }

        public bool UndoGenerateFurMesh()
        {
            if (m_FurMesh)
            {
                PrefabUtility.RevertObjectOverride(m_SelectedObj, InteractionMode.UserAction);
                string assetPath = AssetDatabase.GetAssetPath(m_FurMesh);
                AssetDatabase.DeleteAsset(assetPath);
                return true;
            }

            return false;
        }
        
        protected string GetFurMeshSavePath(Mesh originMesh)
        {
            string path = AssetDatabase.GetAssetPath(originMesh);
            string newPath;
            if (path.EndsWith(".fbx", StringComparison.OrdinalIgnoreCase))
            {
                string folderPath = Path.GetDirectoryName(path).Replace("\\", "/");
                newPath = $"{folderPath}/{originMesh.name}_Fur.asset";
            }
            else if (path.EndsWith(".asset"))
            {
                newPath = path.Replace(".asset", "_Fur.asset");
            }
            else
            {
                throw new InvalidOperationException("Unknown mesh path:" + path);
            }

            return newPath;
        }
    }
}
View Code
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UObj = UnityEngine.Object;

namespace ArtTools
{
    public class FurMeshGeneratorWnd : EditorWindow
    {
        private UObj m_SelectedObj;
        private int m_LayerCount = 20;
        private bool m_IsOptimize;
        private string m_NewMeshSavePath;

        private FurMeshGeneratorBase m_Generator;


        [MenuItem("Omnee美术/毛皮网格生成工具")]
        private static void CreateWnd()
        {
            EditorWindow.CreateWindow<FurMeshGeneratorWnd>("毛皮网格生成工具");
        }

        private void OnGUI()
        {
            UObj oldSelectedObj = m_SelectedObj;
            m_SelectedObj = EditorGUILayout.ObjectField("模型:", m_SelectedObj, typeof(Renderer), true);
            bool selectedObjChange = m_SelectedObj != oldSelectedObj;
            if (m_SelectedObj != null && (selectedObjChange || m_Generator == null))
            {
                m_Generator = FurMeshGeneratorBase.Create(m_SelectedObj, m_LayerCount, m_IsOptimize);
                m_NewMeshSavePath = m_Generator?.GetFurMeshSavePath();
            }

            m_LayerCount = EditorGUILayout.IntField("层数:", m_LayerCount);
            m_IsOptimize = EditorGUILayout.Toggle("是否优化mesh", m_IsOptimize);

            GUILayout.BeginHorizontal();

            m_NewMeshSavePath = EditorGUILayout.TextField("毛皮网格保存路径:", m_NewMeshSavePath);
            if (GUILayout.Button("...", GUILayout.Width(40)))
            {
                string fileName = Path.GetFileNameWithoutExtension(m_NewMeshSavePath);
                m_NewMeshSavePath = EditorUtility.SaveFilePanelInProject("保存", fileName, "asset", "设置毛皮网格保存路径");
            }

            GUILayout.EndHorizontal();

            if (GUILayout.Button("生成毛皮"))
            {
                string error = m_Generator.GenerateFurMesh(m_NewMeshSavePath);
                if (!string.IsNullOrEmpty(error))
                {
                    EditorUtility.DisplayDialog("错误", $"生成毛皮网格出错,信息:{error}", "确定");
                }
                else
                {
                    EditorUtility.DisplayDialog("成功", $"生成毛皮网格成功!", "确定");
                }
            }

            if (GUILayout.Button("撤销生成毛皮"))
            {
                if (m_Generator.UndoGenerateFurMesh())
                    EditorUtility.DisplayDialog("成功", $"撤销生成毛皮网格成功!", "确定");
                m_Generator = null;
            }
        }
    }
}
View Code
using System;
using System.Collections.Generic;
using System.IO;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UObj = UnityEngine.Object;

namespace ArtTools
{
    public class FurMRGenerator : FurMeshGeneratorBase
    {
        private MeshFilter m_MeshFilter;

        public FurMRGenerator(UObj selectedObj, int layerCount, bool isOptimize) :
            base(selectedObj, layerCount, isOptimize)
        {
            m_MeshFilter = m_SelectedObj.GetComponent<MeshFilter>();
        }

        public override string GetFurMeshSavePath()
        {
            return GetFurMeshSavePath(m_MeshFilter.sharedMesh);
        }

        public override string GenerateFurMesh(string furMeshSavePath)
        {
            MeshFilter mf = m_MeshFilter;
            if (mf == null)
            {
                return "请选择MeshFilter";
            }

            if (mf.sharedMesh.subMeshCount > 1)
            {
                return "目标不能有多个子网格";
            }

            Mesh newMesh = GenerateFurMesh(mf);
            SaveMesh(newMesh, furMeshSavePath);

            mf.sharedMesh = newMesh;
            EditorUtility.SetDirty(mf);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();

            Debug.Log($"生成毛皮网格:{mf.name}", mf);
            return null;
        }

        private Mesh GenerateFurMesh(MeshFilter mf)
        {
            Vector3 oldPos = mf.transform.position;
            Quaternion oldRot = mf.transform.rotation;
            mf.transform.position = Vector3.zero;
            mf.transform.rotation = Quaternion.identity;

            CombineInstance[] combinesToMerge = new CombineInstance[m_LayerCount];
            //Matrix4x4[] bindPosesToMerge = new Matrix4x4[smr.bones.Length * m_layerCount];
            //bonesToMerge = new Transform[smr.bones.Length * m_layerCount];
            Vector2[] newUVs = new Vector2[mf.sharedMesh.uv.Length * m_LayerCount];
            Color[] newColors = new Color[mf.sharedMesh.vertexCount * m_LayerCount];
            //BoneWeight[] boneWeights = new BoneWeight[smr.sharedMesh.boneWeights.Length * m_layerCount];
            int indexUV = 0, indexColor = 0;

            for (int i = 0; i < m_LayerCount; i++)
            {
                combinesToMerge[i] = new()
                {
                    mesh = mf.sharedMesh,
                    subMeshIndex = 0,
                    transform = mf.transform.localToWorldMatrix,
                };

                for (int j = 0; j < mf.sharedMesh.uv.Length; j++)
                {
                    newUVs[indexUV] = mf.sharedMesh.uv[j];
                    ++indexUV;
                }

                Color tarColor = new Color((float)i / m_LayerCount, 0, 0);
                for (int j = 0; j < mf.sharedMesh.vertexCount; j++)
                {
                    newColors[indexColor] = tarColor;
                    ++indexColor;
                }
            }

            // Create mesh
            int verticesCount = mf.sharedMesh.vertexCount * m_LayerCount;
            Mesh finalMesh = new();
            finalMesh.indexFormat = verticesCount > MAX_VERTICES_FOR_16BITS_MESH ? IndexFormat.UInt32 : IndexFormat.UInt16;
            finalMesh.name = $"{mf.sharedMesh.name}_Fur";
            finalMesh.CombineMeshes(combinesToMerge, true, true);
            finalMesh.RecalculateBounds();
            finalMesh.uv = newUVs;
            finalMesh.colors = newColors;

            if (m_IsOptimize)
                finalMesh.Optimize();

            mf.transform.position = oldPos;
            mf.transform.rotation = oldRot;
            return finalMesh;
        }
    }
}
View Code
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UObj = UnityEngine.Object;

namespace ArtTools
{
    public class FurSMRGenerator : FurMeshGeneratorBase
    {
        private SkinnedMeshRenderer m_SkinnedMeshRenderer;

        public FurSMRGenerator(UObj selectedObj, int layerCount, bool isOptimize)
            : base(selectedObj, layerCount, isOptimize)
        {
            m_SkinnedMeshRenderer = (SkinnedMeshRenderer)m_SelectedObj;
        }

        public override string GenerateFurMesh(string furMeshSavePath)
        {
            SkinnedMeshRenderer smr = m_SkinnedMeshRenderer;
            if (smr == null)
            {
                return "请选择SkinnedMeshRenderer";
            }

            if (smr.sharedMesh.subMeshCount > 1)
            {
                return "目标不能有多个子网格";
            }

            Mesh newMesh = GenerateFurMesh(out var bonesToMerge);
            SaveMesh(newMesh, furMeshSavePath);

            smr.sharedMesh = newMesh;
            smr.bones = bonesToMerge.ToArray();
            EditorUtility.SetDirty(smr);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();

            Debug.Log($"生成毛皮网格:{smr.name}", smr);
            return null;
        }

        private Mesh GenerateFurMesh(out List<Transform> bonesToMerge)
        {
            SkinnedMeshRenderer smr = m_SkinnedMeshRenderer;
            Vector3 oldPos = smr.transform.position;
            Quaternion oldRot = smr.transform.rotation;
            smr.transform.position = Vector3.zero;
            smr.transform.rotation = Quaternion.identity;

            CombineInstance[] combinesToMerge = new CombineInstance[m_LayerCount];
            //Matrix4x4[] bindPosesToMerge = new Matrix4x4[smr.bones.Length * m_layerCount];
            //bonesToMerge = new Transform[smr.bones.Length * m_layerCount];
            Vector2[] newUVs = new Vector2[smr.sharedMesh.uv.Length * m_LayerCount];
            Color[] newColors = new Color[smr.sharedMesh.vertexCount * m_LayerCount];
            //BoneWeight[] boneWeights = new BoneWeight[smr.sharedMesh.boneWeights.Length * m_layerCount];
            int indexUV = 0, indexColor = 0;
            bonesToMerge = new();
            List<Matrix4x4> bindPosesToMerge = new();
            List<BoneWeight> boneWeights = new();

            for (int i = 0; i < m_LayerCount; i++)
            {
                combinesToMerge[i] = new()
                {
                    mesh = smr.sharedMesh,
                    subMeshIndex = 0,
                    transform = smr.transform.localToWorldMatrix,
                };

                for (int j = 0; j < smr.bones.Length; j++)
                {
                    var curBone = smr.bones[j];
                    if (!bonesToMerge.Contains(curBone))
                    {
                        bonesToMerge.Add(curBone);
                        bindPosesToMerge.Add(smr.sharedMesh.bindposes[j] * smr.transform.worldToLocalMatrix);
                    }
                }

                for (int j = 0; j < smr.sharedMesh.uv.Length; j++)
                {
                    newUVs[indexUV] = smr.sharedMesh.uv[j];
                    ++indexUV;
                }

                Color tarColor = new Color((float)i / m_LayerCount, 0, 0);
                for (int j = 0; j < smr.sharedMesh.vertexCount; j++)
                {
                    newColors[indexColor] = tarColor;
                    ++indexColor;
                }

                for (int j = 0; j < smr.sharedMesh.boneWeights.Length; j++)
                {
                    BoneWeight bw = smr.sharedMesh.boneWeights[j];
                    bw.boneIndex0 = bonesToMerge.FindIndex(a => a == smr.bones[bw.boneIndex0]);
                    bw.boneIndex1 = bonesToMerge.FindIndex(a => a == smr.bones[bw.boneIndex1]);
                    bw.boneIndex2 = bonesToMerge.FindIndex(a => a == smr.bones[bw.boneIndex2]);
                    bw.boneIndex3 = bonesToMerge.FindIndex(a => a == smr.bones[bw.boneIndex3]);
                    boneWeights.Add(bw);
                }
            }

            // Create mesh
            int verticesCount = smr.sharedMesh.vertexCount * m_LayerCount;
            Mesh finalMesh = new();
            finalMesh.indexFormat = verticesCount > MAX_VERTICES_FOR_16BITS_MESH ? IndexFormat.UInt32 : IndexFormat.UInt16;
            finalMesh.name = $"{smr.sharedMesh.name}_Fur";
            finalMesh.CombineMeshes(combinesToMerge, true, true);
            finalMesh.RecalculateBounds();
            finalMesh.bindposes = bindPosesToMerge.ToArray();
            finalMesh.boneWeights = boneWeights.ToArray();
            finalMesh.uv = newUVs;
            finalMesh.colors = newColors;

            if (m_IsOptimize)
                finalMesh.Optimize();

            smr.transform.position = oldPos;
            smr.transform.rotation = oldRot;
            return finalMesh;
        }

        public override string GetFurMeshSavePath()
        {
            return GetFurMeshSavePath(m_SkinnedMeshRenderer.sharedMesh);
        }
    }
}
View Code

 

posted @ 2023-07-04 13:38  太乙_真人  阅读(144)  评论(0)    收藏  举报